Nowadays, most x86 Assembly programs are intended to run in 64 bit or at least 32 bit environments. This means that most features specific to 16 bit development are now no longer supported. Thus, some workarounds related to this were needed in the development of Kapow.

What is Real Mode?

Whenever your x86 cpu boots up, it always starts up in Real Mode. In this mode, your cpu has access to only 16 bit instructions and registers, as well as one megabyte of RAM. In other words, the CPU behaves like an 1978 Intel 8086 with a much higher clock speed.

Memory Segmentation

You might notice that in Real Mode we have access to a whole megabyte of RAM, but yet we are using 16 bit registers to address it. How is it possible to address one million bytes, if in 16 bits we can only represent 64 thousand distinct values? The answer to this question lies in Intel's solution to this problem, Memory Segmentation. This is where another register is used to represent an additional 4 bits for addressing. This gives us a total of 20 bits, enough to address one megabyte. In this model, every 64 kilobytes of RAM is referred to as one segment.

Addressing Labels

The main problem with using NASM in 16 bit code starts when you are trying to modify the value at a certain label. For example, consider the following code:

var: dw 0x123
mov ax, [var]
mul 20
mov [var], ax

Normally, you would expect this to work. You could use the location at var effectively as a variable, addressing and using its contents. However, when dealing with 16 bit code NASM refuses to do this. Upon further digging, it seemed possible to use seg in order to load the segment of the label, and from this be able to address. But this is impossible to do without using Microsoft COM files. Thus, in order to preserve the debugging advantages of using a modern ELF format, I had to find another solution.

Bootlegged Effective Addressing

In order to solve this problem, it was necessary to define an area in memory where variables would be stored. This was done in the game/var_locs.asm file. Here, a base offset is defined, in this case 0xC000 (Which is an offset from the beginning of the segment where the game's executable resides), and subsequently the location of every single variable used by the game is defined. This also necessitates the initialization of each defined memory location, which is done in the respective file where the variables are used.

Loading Sprites

The easiest solution to loading and using sprites would be something like this:

sprite: incbin "./mysprite.bin"
mov ax, seg sprite ; Moves the segment the sprite is in to ax
mov es, ax
mov ax, sprite
call my_draw_routine

However, due to the aforementioned problems, this is simply not possible. Instead, a similar approach must be taken to the one used with variables, and the locations used are stored within the file game/sprite_constants.asm. This time, the sprites are stored and loaded in a different segment.

In order to do this, the first thing that is done is to fill up the rest of the code segment with 0's. This is done with the following line in /game/game.asm:

times 0xFFFF - ($-$$) db 0

Doing this greatly aids in the loading process, as we know where the sprites are going to be, exactly 64 kilobytes from the start of the code segment, or in other words in the following segment.

The Makefile then takes care of writing the assets to the binary executable, as can be seen from this snippet:

        mkdir -p out/assets
        gcc -o out/extract_palette.o src/assets/extract_palette.c
        $(CONV) -compress none assets/PALETTE.PCX out/assets/PALETTE.bmp
        out/extract_palette.o out/assets/PALETTE.bmp out/assets/palette.bin
        cat out/assets/palette.bin >> out/HD.img

        gcc -o out/conv_asset.o src/assets/conv_asset.c
        $(call add_asset,BOMB,16,32)
        $(call add_asset,BOMB8S,8,8)
        $(call add_asset,BOMBER,32,32)
        $(call add_asset,PADDLE,32,8)
        $(call add_asset,CHAR0,12,12)
        $(call add_asset,CHAR1,12,12)
        $(call add_asset,CHAR2,12,12)
        $(call add_asset,CHAR3,12,12)
        $(call add_asset,CHAR4,12,12)
        $(call add_asset,CHAR5,12,12)
        $(call add_asset,CHAR6,12,12)
        $(call add_asset,CHAR7,12,12)
        $(call add_asset,CHAR8,12,12)
        $(call add_asset,CHAR9,12,12)
        $(call add_asset,TOPSC,40,12)
        $(call add_asset,EXP1,8,8)
        $(call add_asset,EXP2,8,8)
        $(call add_asset,EXP3,8,8)
        $(call add_asset,EXP4,8,8)
        $(call add_asset,CHALKBOA,100,32)
        $(call add_asset,ENTER,96,20)

Finally, upon booting, bootup/booter.asm takes care of loading all of the sprite data at the correct segment, where asset_storage is the segment where sprites are to be loaded to. This can be seen here:

mov ax, asset_storage
mov es, ax
mov ah, 0x2
mov al, 128
mov ch, 0
mov dh, 2
mov cl, 4
mov dl, 0x80

mov bx, 0               ; address to copy to
int 0x13