当垂直回扫位清零时,VGA卡是否在像素缓冲区中读取?

时间:2018-05-22 06:21:45

标签: assembly x86-16 vga

我正在使用视频模式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是唯一安全的

还是我完全错了?

1 个答案:

答案 0 :(得分:4)

VGA卡没有单独的缓冲区。 (请记住,当VGA是新的时,甚至32kiB的DRAM也很昂贵。而且,内存带宽很低。有些视频卡过去常常使用dual-ported RAM,因此CPU的访问不会干扰扫描;它可以在CRTC / RAMDAC读取像素数据时,可以在一个端口上读/写。)

vertical-blanking interval期间,视频卡根本无法读取或写入视频RAM;它存在,因此CRT可以将电子束偏转板的电压改变回屏幕顶部,而不会在屏幕上划线。然后VGA硬件再次开始读取视频RAM,以便下一次扫描出下一帧。

(现代硬件当然不会驱动CRT,但是按照&#34;消隐间隔&#34;顺序读取VRAM仍然是一件事。)

等待设置然后清除有助于使代码在消隐间隔开始时开始运行,而不是可能接近消隐间隔的结束。

如果修改视频RAM的代码运行得足够快,那么在硬件再次开始阅读之前就已经完成了,所以你不会撕裂。 (实际上,因为您以扫描输出顺序编写屏幕,只需要足够快以保持光栅扫描之前,因此屏幕输出无法通过memcpy并在帧的后面显示一些&#34; old&#34;像素。)

在旧硬件上,rep movsw速度不够快,无法在VBI期间复制整个数据帧,尤其是在通过ISA总线写入内存映射I / O时。相反,您通常会在VBI期间通过将VGA基准更改为指向已绘制的帧来double-buffer。所以你在一个缓冲区中绘制而另一个缓冲区被扫描出来,给你一个整个帧间隔来更新它,而不仅仅是VBI。

rep movsw在实际的现代CPU上快速运行非常(例如,如果您以实模式启动现代PC)。如果VRAM映射为WC(又名USWC:不可缓存的推测性写入组合),那么rep movsw将一次复制16或32个字节(快速字符串模式或甚至ERMSB(增强型Rep Mov / Stos B)),受益于写入组合缓冲区。 (WC内存上的常规存储就像普通WB(写回)内存上的NT存储一样)。英特尔勘误表(like IvyBridge BU2)表明WC内存上的REP MOVS确实以这种方式工作:如果你将一个页面从WC跨越到UC内存,一些商店到UC内存可能会发生宽带快速字符串存储而不是单独存储rep movsw的比特商店。这意味着CPU必须在WC内存中进行广泛存储。

如果源数据在L1d或L2缓存中很热,因为您刚刚编写它,并且目标是USWC视频RAM,那么在VBI期间应该很容易用rep movsw进行blitting。如果它被映射为UC(当WC是一个相对较新的功能时,至少在奔腾III /早期的K8板上,这曾经是BIOS选项),那么现代的多GHz PC可能仍然很快。 / p>

(BTW,repne cmpsb仍然很慢,但是rep movs / stos很快。)

BTW,即使是集成显卡,其中&#34;视频RAM&#34;仍然只是常规DRAM的一部分,它将是UC(不可缓存)或WC(不可缓存的写入组合))。当然,这些天大多数VGA接口都是模拟的。 VGA内存可能是您的图形硬件使用的真实帧缓冲区(如果在裸机上运行,​​而不是DOSBOX或其他模拟器)。

无论如何,在低rez的现代硬件上,你可能只检查被清除的位是否正常,因为与刷新率相比,复制运行速度非常快,因此得到任何撕裂。或者也许第一个或第一个像素可能来自旧框架。

在DOSBOX上模拟具有真实时钟速度的真正旧PC

@Ped7G says rep movsw在VBI期间复制帧速度不够快,除非您将DOSBOX设置为在~70MHz处模拟486,或者&#34;动态/最大&#34 ;速度。