为什么在VGA模式下写入图形内存时写入模式不会改变?



我正在尝试使用NASM在DOSBOX中绘制VGA图形到屏幕。我的代码能够完美地写到一个黑屏,但是如果我编辑一个像素,已经是一个特定的颜色,输出颜色最终是以前的颜色与我的新颜色。例如,如果我填充一个红色的屏幕,然后再填充它为蓝色,最后的屏幕将不是蓝色,而是品红。

这是我的代码,它应该填充屏幕蓝色然后绿色,而是填充屏幕蓝色,然后用绿色填充蓝色内存,导致青色填充屏幕。我可以通过降低cpu速度在DOSBOX中看到这种情况。

org 100h
section .text
start:
mov ax, 4F02h
mov bx, 102h
int 10h       ; enter 800x600 16 color VGA mode
push 0x01     ; blue
call fill_screen
push 0x2      ; green
call fill_screen
jmp $
fill_screen:
; multiply screen width by height then divide by 8 to get number of times to repeat write operation, push to stack
mov ax, [screen_width]
mov dx, [screen_height]
mul dx
mov cx, 8
div cx
push ax
; point es to video memory
mov ax, 0A000h
mov es, ax
pop cx  ; times to repeat write operation
pop dx  ; return address
pop ax  ; color 
push dx ; put return address back on stack

mov dx, 03c4h ; register selection port
shl ax, 8     ; color is one byte, it was in al but we need it in ah so shift left one byte
mov al, 02h   ; map mask
out dx, ax    ; write all the bitplanes
xor di, di    ; video memory pointer to start
mov ax, 0ffh  ; write to every pixel
rep stosb
ret
section .data
screen_width dw 320h
screen_height dw 258h

在第5节中,通过写入端口03CFh,您可以将写入模式更改为替换或xor或and。它们包括切换到异或模式的示例代码:

mov ax,1803h
mov dx,03CEh
out dx,ax

当我在第6行(int 10h)之后添加这个时,什么也没有发生,代码的输出仍然是纯白色的。由于蓝色或绿色与蓝色或绿色相同,我将绿色输入更改为品红色(使第9行push 0x5)。输出现在是洋红色的,这意味着内存仍然处于或模式,而不是在异或模式。

下面是最终的测试代码:
org 100h
section .text
start:
mov ax, 4F02h
mov bx, 102h
int 10h       ; enter 800x600 16 color VGA mode
; switch to XOR write mode (?)
mov ax, 1803h
mov dx, 03CEh
out dx, ax
push 0x01 ; blue
call fill_screen
push 0x5 ; magenta
call fill_screen
jmp $
fill_screen:
; multiply screen width by height then divide by 8 to get number of times to repeat write operation, push to stack
mov ax, [screen_width]
mov dx, [screen_height]
mul dx
mov cx, 8
div cx
push ax
; point es to video memory
mov ax, 0A000h
mov es, ax
pop cx  ; times to repeat write operation
pop dx  ; return address
pop ax  ; color 
push dx ; put return address back on stack
mov dx, 03c4h ; register selection port
shl ax, 8     ; color is one byte, it was in al but we need it in ah so shift left one byte
mov al, 02h   ; map mask
out dx, ax    ; write all the bitplanes
xor di, di    ; video memory pointer to start
mov ax, 0ffh  ; write to every pixel
rep stosb
ret
section .data
screen_width dw 320h
screen_height dw 258h

我怎么能解决这个问题,让我进入不同的写模式?是否有一种不同的更好的方式来写入视频内存,放弃我应该使用的写入模式?如何进入替换模式?

关于VGA硬件的一些参考:https://wiki.osdev.org/VGA_Hardware, https://web.stanford.edu/class/cs140/projects/pintos/specs/freevga/vga/vgamem.htm

首先,异或模式(和其他模式)不与已经存在于显存中的数据异或。它们与锁存寄存器异或,锁存寄存器在从显存读取时加载。锁存寄存器保持它们的值,直到你从显存中读取另一个值。如果不从显存中读取,则锁存寄存器的值为未定义值。

要保持在正常模式(NOP模式),只需将模式设置为0:

mov ax,0003h
mov dx,03CEh
out dx,ax

同样,当写入到03c4h/reg 2时,您设置了一个掩码,该掩码将应用于所有对显存的写入,过滤您写入的每个位。将掩码设置为0Fh将允许按预期写入所有位。如果你不使用掩码值0Fh(这是你的情况,你用你的颜色填充它),那么掩码的0位将从锁存寄存器中取出,因为你不读取,所以你不设置。

要写没有遮罩的颜色,将遮罩设置为0Fh,并写入实际颜色而不是FFh:

mov dx,03C4h
mov ax,0F02h
out dx,ax
mov es:[di], <color> ; example write

如果您希望对每个像素使用异或和掩码操作,那么您需要在每次写入之前通过执行虚拟读取来正确填充锁存器:

mov bl, es:[di] ; ignore bl if not needed
mov es:[di], <color> ; example write with VGA operations

请注意,另一种选择,使用bl并自己执行xor/掩码,并不复杂得多。

最新更新