For a bonus assignment at college, we are required to make a game in assembly. My idea was to recreate the first level of mario NES, but while coding I could not make the emulator I am using (qemu) display pixels on screen.
I am coding on a Linux Debian distribution and compiling my code with NASM. I am using this bootloader: ( https://github.com/Pusty/realmode-assembly/blob/master/part6/bootloader.asm ) to make the game bootable and am coding the kernel myself.
To draw to the screen I am using 320x200 VGA mode (int 0x10, 0x13) and use a double buffer method to write to screen to make it run smoother.
I am currently using this code to draw the double buffer
; si = image to draw, ax = x location offset of image, bx = y location offset of image
drawBuffer:
pusha
push es
xor di, di
imul di, bx, 320 ; translate the y offset to y position in buffer
add di, ax ; adds the x offset to buffer to obtain (x,y) of image in buffer
mov es, word [bufferPos] ; moves the address of the buffer to es
mov bp, ax ; saves the x offset for later use
; image is in binary and first two words contain width and height of image
xor ax, ax
lodsb
mov cx, ax ; first byte of img is the width of the image
lodsb
mov dx, ax ; second byte of img is height of the image
.forY:
; check if within screen box
mov bx, di
add bx, cx ; adds length of image to offset to get top right pixel
cmp bx, 0 ; if top right pixel is less than 0 it is outside top screen
jl .skipX ; if less then 0 skip the x pixels row
sub bx, cx
sub bx, 320*200 ; subtracts total screen pixels off of image to get left botom pixel
jge .skipX ; if greater then 0 it is outside of bottom of screen
xor bx, bx
.forX:
; check if within left and right of screen
cmp bx, 320
jg .skip
sub bx, bp
cmp bx, 0
jl .skip
; if more bx (x position) is more >320 or <0 it is outside of screen
mov al, byte [si + bx] ; moves the color of the next pixel to al
test al, al ; if al is 0x0 it is a 'transparant' pixel
jz .skip
mov byte [es:di], al ; move the pixel to the buffer
.skip:
inc bx ; move to next pixel in image
cmp bx, cx ; check if all pixels in x row of image are done
jl .forX ; if not all images are done repeat forX
.skipX:
add di, 320 ; if forX is done move to next y position
add si, cx ; moves to the next y row in image
dec dx ; decrements yloop counter
jnz .forY
pop es
popa
ret
bufferPos dw 0x7E0 ; address at which to store the second buffer
This code is stored in a separate file called buffer.s and is included in the kernel.s using
%include buffer.s
The kernel is located at 0x8000 and all it does is contain the image to draw with the x and y offset and it calls the double buffer
org 0x8000
bits 16
setup:
mov ah, 0
mov al, 0x13
int 0x10
main:
call resetBuffer ; method to set all pixels in buffer to light blue
mov ax, 10 ; x position of image
mov bx, 100 ; y position of image
mov si, [mario] ; moves the image location to si
call drawBuffer
call writeVideoMem ; simply stores all bytes in the buffer to the videoMemory
jmp main
jmp $
mario
dw mario_0
mario_0 incbin "images/mario_right_0.bin"
times (512*16)-($-$$) db 0
What I would expect is for it to draw mario, but when I run it on qemu using
nasm -fbin bootloader.s -o bootloader.bin
nasm -fbin kernel.s -o kernel.bin
cat bootloader.bin kernel.bin > game.bin
qemu-system-i386 game.bin
it just shows a black screen and nothing is drawn.
The only thing I can think of is that to access all the pixels 16 bit registers don't have enough bits and that is why it is not working.
PS. If any more information is needed, I'll be happy to provide