我正在开发一款使用视频模式 13h 的 DOS 游戏。
我一直遇到屏幕撕裂的问题,但直到今天我一直忽略这个问题。我认为修复这将是一个挑战,因为它将涉及延迟像素写入一段精确的时间。但这实际上是一个非常简单的修复。
您所要做的就是等待重新设置 VGA 状态字节的垂直回扫位(位 3),该位在彩色模式下可在端口 0x3da 上使用。
所以我只需要修改这个旧程序,它将我的帧缓冲区写入从 A000:0000 开始的 VGA 像素缓冲区:
WRITE_FRAME PROC
;WRITES ALL 64,000 PIXELS (32,000 WORDS) IN THE FRAME BUFFER TO VIDEO MEMORY
push es
push di
push ds
push si
push cx
mov cx, frame
mov ds, cx
xor si, si ;ds:si -> frame buffer (source)
mov cx, vidMemSeg
mov es, cx
xor di, di ;es:di -> video memory (destination)
mov cx, (scrArea)/2 ;writing 32,000 words of pixels
rep movsw ;write the frame
pop cx
pop si
pop ds
pop di
pop es
ret
WRITE_FRAME ENDP
这是等待新设置垂直回扫位的修改程序:
WRITE_FRAME PROC
;WRITES ALL 64,000 PIXELS (32,000 WORDS) IN THE FRAME BUFFER TO VIDEO MEMORY
push es
push di
push ds
push si
push ax
push cx
push dx
mov cx, frame
mov ds, cx
xor si, si ;ds:si -> frame buffer (source)
mov cx, vidMemSeg
mov es, cx
xor di, di ;es:di -> video memory (destination)
mov cx, (scrArea)/2 ;writing 32,000 words of pixels
;If vert. retrace bit is set, wait for it to clear
mov dx, 3dah ;dx <- VGA status register
VRET_SET:
in al, dx ;al <- status byte
and al, 8 ;is bit 3 (vertical retrace bit) set
jnz VRET_SET ;If so, wait for it to clear
VRET_CLR: ;When it's cleared, wait for it to be set
in al, dx
and al, 8
jz VRET_CLR ;loop back till vert. retrace bit is newly set
rep movsw ;write the frame
pop dx
pop cx
pop ax
pop si
pop ds
pop di
pop es
ret
WRITE_FRAME ENDP
它并不完全完美。仍然有一点抖动,尤其是当精灵后面的背景向上或向下滚动时,但不再有什么问题了。
我的问题是,为什么这有效?
我的猜测是,当设置垂直回扫位时,像素已经被读入VGA卡的内存中,并且当前正在写入已经加载的像素。然而,当垂直回扫位被清除时,它正在将像素从A000:0000加载到本地存储器中。它使用 DMA 来实现这一点,对吗?
因此,只有当 VGA 卡正在写入像素(位设置)并且不加载像素(位清除)时,写入 A000:0000 才是安全的
还是我完全错了?