代码之家  ›  专栏  ›  技术社区  ›  bad

当垂直重传位清除时,VGA卡是否在像素缓冲区中读取?

  •  2
  • bad  · 技术社区  · 7 年前

    我正在开发一款使用视频模式13H的DOS游戏。

    我一直有屏幕撕裂的问题,但直到今天我都忽略了这个问题。我认为这将是一个修复的挑战,因为它将涉及延迟像素写入一段精确的时间。但这实际上是一个非常简单的修复方法。

    您所要做的就是等待VGA状态字节的垂直重传位(第3位),该位在颜色模式下的端口0x3da处可用。

    所以我只需要修改这个旧过程,它将我的帧缓冲区写入vga像素缓冲区,从a00:0000开始:

    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 回复  |  直到 7 年前
        1
  •  4
  •   Peter Cordes    7 年前

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

    在A期间 vertical-blanking interval 视频卡根本不读写视频RAM;它的存在使得CRT可以将电子束偏转板的电压改变回屏幕顶部,而不需要在屏幕上画一条线。然后,VGA硬件开始读取视频RAM,以便下次扫描出下一帧。

    (当然,现代硬件不驱动CRT,但按“空白间隔”顺序读取VRAM仍然是一回事)。


    等待位被设置然后清除,有助于使代码在空白间隔开始时开始运行,而不是在空白间隔结束时开始运行。

    如果修改视频RAM的代码运行得足够快,那么在硬件再次开始读取之前就已经完成了,这样就不会被撕破。(实际上,因为你是按扫描顺序写屏幕的, 它只需要足够快来保持在光栅扫描之前 ,因此屏幕输出不会通过memcpy,稍后在帧中显示一些“旧”像素。)

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


    代表MOVSW 非常 在实际的现代CPU上运行速度很快(例如,如果您以实模式启动现代PC)。如果VRAM映射为wc(aka uswc:uncacheable推测性写入组合),则 代表MOVSW 将一次复制16或32个字节(快速字符串模式或甚至ERMSB(增强的rep mov/stos b)),这得益于写入组合缓冲区。(wc内存上的常规存储类似于普通wb(写回)内存上的NT存储)。英特尔勘误表( like IvyBridge BU2 )指示wc内存上的rep mov确实是这样工作的:如果您将一个页面从wc跨过到uc内存中,则可能会发生一些存储到uc内存的情况,使用宽快速字符串存储,而不是单独的16位存储 代表MOVSW . 这意味着CPU必须对wc内存进行广泛的存储。

    如果源数据在l1d或l2缓存中是热的,因为您刚刚写入了它,而目的地是uswc视频RAM,则使用 代表MOVSW 应在VBI期间轻松完成。如果它被映射为UC(至少在Pentium III/早期的K8板上,WC是一个相对较新的功能时,这曾经是一个BIOS选项),那么现代的多GHz PC可能仍然足够快。

    (顺便说一句, repne cmpsb 仍然很慢,但rep movs/stos很快)。

    顺便说一句,即使集成图形“视频RAM”仍然只是常规DRAM的一部分,它也将是UC(不可缓存)或WC(不可缓存写入组合))。当然,现在大部分的VGA接口都是模拟的。不过,VGA内存可能是图形硬件使用的真正帧缓冲区(如果在裸机上运行,则不是Dosbox或其他模拟器)。

    不管怎样,在低REZ的现代硬件上,您可能只需要检查清除的位就可以了,因为拷贝的运行速度比刷新速度快,几乎没有任何撕裂的机会。或者第一个或两个像素可能来自旧帧。


    在Dosbox上用真实的时钟速度模拟真实的旧PC :

    @Ped7G says 代表MOVSW 在VBI中复制帧的速度不够快,除非您将Dosbox设置为在~70MHz或“动态/最大”速度下模拟486。

    推荐文章