Assembly 如何在16位汇编中将320x200像素映射到VGA视频内存

Assembly 如何在16位汇编中将320x200像素映射到VGA视频内存,assembly,graphics,x86-16,bootloader,Assembly,Graphics,X86 16,Bootloader,为了获得大学的奖金,我们必须在集合中做一个游戏。我的想法是重新创建mario NES的第一级,但在编写代码时,我无法使我使用的模拟器(qemu)在屏幕上显示像素 我正在Linux Debian发行版上编写代码,并使用NASM编译代码。我正在使用这个引导加载程序:()使游戏可引导,并自己编写内核。 为了绘制屏幕,我使用320x200 VGA模式(int 0x10,0x13),并使用双缓冲区方法写入屏幕,使其运行更平滑 我目前正在使用这段代码来绘制双缓冲区 ; si = image to draw,

为了获得大学的奖金,我们必须在集合中做一个游戏。我的想法是重新创建mario NES的第一级,但在编写代码时,我无法使我使用的模拟器(qemu)在屏幕上显示像素

我正在Linux Debian发行版上编写代码,并使用NASM编译代码。我正在使用这个引导加载程序:()使游戏可引导,并自己编写内核。
为了绘制屏幕,我使用320x200 VGA模式(int 0x10,0x13),并使用双缓冲区方法写入屏幕,使其运行更平滑

我目前正在使用这段代码来绘制双缓冲区

; 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
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
内核位于0x8000,它所做的只是包含要用x和y偏移量绘制的图像,并调用双缓冲区

; 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
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
我希望它能画马里奥,但当我在qemu上使用

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
它只显示了一个黑屏,什么也没有画出来

我唯一能想到的是,要访问所有像素,16位寄存器没有足够的位,这就是它无法工作的原因

如果需要更多的信息,我很乐意提供

写入缓冲区会覆盖内核! 您使用的引导加载程序将内核存储在线性地址0x00008000处。用于加载它设置
ES:BX==0x0000:0x8000
。您已经在内核的源代码顶部正确地放置了一个
org0x8000

但是您已经在线性地址0x00007E00(
ES
*16)定义了双缓冲区。您已经设置了
ES==0x07E0

320x200x8视频模式的合适缓冲区应具有64000字节。因此,在线性地址0x00010000处设置双缓冲区,需要您写入:

bufferPos dw 0x1000
这为32KB的内核留出了空间。目前,内核仅使用8KB


用于检查图片是否保留在屏幕/缓冲区内的代码看起来非常虚假。我建议您现在将其移除。
处理1条水平线的内部循环忘记递增
DI
寄存器

.forY:
    PUSH DI              <******************
    xor bx, bx
    .forX:
        mov al, byte [si + bx]
        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 DI           <******************
        inc bx                ; move to next pixel in image
        cmp bx, cx            ; check if all pixels in row are done
        jl .forX                 ; if not all images are done repeat forX
    .skipX:
    POP DI               <******************
    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

这不是一个好主意。请注意,如果你甚至没有得到浅蓝色的背景,那么就不要关注马里奥的图像,因为你有更深层次的问题。另外,学习使用调试器。您使用的320x200x8bpp VGA模式是每像素8比特,而不是16比特!!!此外,我没有看到任何目标段0xA000,因此我希望您正在复制屏幕缓冲区。看看我的这个旧MS-DOS游戏,它可能会对你的项目有所帮助。。。同样,如果我是对的,我在将寄存器al中的数据移动到缓冲区中的内存位置时,每像素使用8位。这只是将它写入屏幕的双缓冲区,我确实在将它复制到0xA000段。我是否也应该提供将缓冲区复制到视频内存以使其更清晰的代码?@LazyL0tus如果您确定它可以正常工作,则无需。。。我能想到的可能还有更多的问题。1.我不熟悉qemu和debian,但您的模拟器可能希望某些VGA访问方式刷新屏幕,比如访问一些VGA寄存器或调用特定的BIOS调用。2.另一件事是,由于你的东西是可引导的,你的可执行文件真的是16位x86还是32/64位?IIRC后者无法访问BIOS功能。。。3.你们有液晶显示器吗?有些无法正确处理旧的VGA低分辨率,并且不同步。。。4.你的精灵真的加载到内存中了吗?试着渲染一些图案,比如
pixel[y*320+x]=x+y要检查渲染效果。。。像这样的东西可以排除1,2,3个问题如果工作至少解决了一个问题,我的屏幕现在显示一个全浅蓝色的屏幕!但是仍然没有雪碧上膛,所以我还是要调查一下that@LazyL0tus请从代码中删除
jmp main
。仅保留
jmp$
。你在画完这幅画后马上就把它抹掉了。也许这就是为什么你只看到蓝色的背景!移除
jmp main
后,它仍然只显示一个蓝屏,因为重置缓冲区只重置缓冲区中的所有像素,然后它将精灵重新绘制到缓冲区。问题在于缓冲区没有绘制精灵,或者没有正确复制
.forY:
    xor bx, bx
    .forX:
        mov al, byte [si + bx]
        test al, al           ; if al is 0x0 it is a 'transparant' pixel
        jz .skip
        mov byte [es:di+BX], al  ; move the pixel to the buffer
      .skip:
        inc bx                ; move to next pixel in image
        cmp bx, cx            ; check if all pixels in row 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