在8086 Realode中绘制图形时,请避免闪烁/闪烁



我是汇编语言的新手8086。我正在使用MASM611。我正在制作恐龙游戏(Google Chrome离线游戏(。我绘制的越多,屏幕就越开始闪烁。我尝试了一切,但找不到任何东西。代码完美地编译而没有错误。这是代码

MainLoop:
    ;call clearRegisters
    mov ah,01h
    int 16h
    mov bh,ah
    mov bl,0
    mov al,0
    mov ah,0Ch
    int 21h
    cmp bh,12h
    je Exit
    call SelectScreen
    mov ax,dinoObj.x
    mov x,ax
    mov ax,dinoObj.y
    mov y,ax
    call clearRegisters
    call DrawBackground
    call clearRegisters
    call drawDinoBase
    call clearRegisters
    call drawDino
    call clearRegisters
jmp MainLoop`

selectscreen funtion如下

SelectScreen proc
    mov ah,0
    mov al,12h
    int 10h
    Terminate:
    ret
SelectScreen Endp`

drawdino函数的片段

drawDino proc
mov ax,y
mov tempY,ax
mov cx,8
outer1:
    push cx
    mov ax,x
    mov tempX,ax
    mov cx,20
    inner1:
        push cx
        mov ah,0ch
        mov al,10
        mov cx,tempX
        mov dx,tempY
        int 10h
        inc tempX
        pop cx
    loop inner1
    inc tempY
    pop cx
    loop outer1
    mov ax,y
    add ax,8
    mov tempY,ax
    mov cx,12
    outer2:
        push cx
        mov ax,x
        mov tempX,ax
        mov cx,38
        inner2:
            push cx
            mov ah,0ch
            mov al,10
            mov cx,tempX
            mov dx,tempY
            int 10h
            inc tempX
            pop cx
        loop inner2
        inc tempY
        pop cx
        loop outer2
        mov ax,y
        add ax,21
        mov tempY,ax
        mov cx,8
        outer3:
            push cx
            mov ax,x
            mov tempX,ax
            mov cx,20
            inner3:
                push cx
                mov ah,0ch
                mov al,10
                mov cx,tempX
                mov dx,tempY
                int 10h
                inc tempX
                pop cx
            loop inner3
            inc tempY
            pop cx
            loop outer3

您所描述的称为闪烁。直接引用Wikipedia:

例如,直接在框架缓冲区中掩盖区域的实践,然后在其"顶部"绘制,使空白区域可以暂时出现在屏幕上。

这是由于您对框架缓冲区的写入与视频子系统刷新周期之间缺乏同步引起的。
为了维持一致的图形状态,您应仅在反弯曲期间修改框架缓冲区,通常由于其更长的延伸而进行垂直反转。
在视频子系统正在阅读时写入框架缓冲区会产生不一致的状态,因为只有当前扫描线以下的屏幕部分才会更新。

此问题的解决方案全部共享相同的轮廓 - 回顾期间立即写下框架缓冲区。

最佳解决方案是页面翻转,并已在评论中建议。
它包括在显示适配器内存的两个不同区域之间切换活动帧缓冲区。
不幸的是,VGA适配器不支持您选择 1 的视频模式 - 只有一个页面。

替代方案是双重缓冲,其中包括使用后部缓冲区(与框架缓冲区一样大(与通常的图形启动。
然后呈现后缓冲区,在下一次可用回溯期间,它将被复制(位混合(到框架缓冲区中。

软件体系结构的确切写作太广泛了,有很多方法可以实现这一目标,并且每个方法都有不同的必需技能。
最简单的方法是使用同步介绍 - 代码在后退缓冲区上执行所有图形操作,然后等待下一个垂直撤回曲线以展示它。
如果您的代码足够快,浪费的时间等待回顾就无关紧要。
但是请记住,渲染循环将以显示器刷新速度的速度运行(在游戏中通常是一件好事,因为它是时间安排的免费壁钟(。

不幸的是,您将BIOS服务用作图形原则,并且仅在框架缓冲区中写入(实际上是活动页面,但只有一个(。

正如CodyGray所提醒的那样,替代方案是直接写入框架缓冲区 - 您选择的视频模式并不容易使用,因为它涉及位平面。模式13H取而代之的是将框架缓冲区视为内存的连续区域 - 几乎就像矩阵。

如果您不想沿着重写绘制的代码的道路,可以尝试使用一些胶带进行调整。
可以通过测试VGA适配器的输入状态#1寄存器的位3来确定显示器何时处于垂直回溯 - 当该位是一个位置时,监视器是在垂直回溯中的。

的想法是在开始绘制新框架之前等待垂直回溯。
这可能与您编写渲染循环的方式兼容或可能不兼容 - 此外,我从未使用过BIOS调用和垂直回溯逻辑的混合方法。
由于它与视频子系统的互动方式,我无法排除BIOS实施的可能性。
尤其是,科迪格雷指出,BIOS呼叫将无情地导致闪烁。

为了避免在接近端撤回上启动框架的风险,最好等待a new retrace。

waitForNewVR:
 mov dx, 3dah
 ;Wait for bit 3 to be zero (not in VR).
 ;We want to detect a 0->1 transition.
_waitForEnd:
 in al, dx
 test al, 08h
jnz _waitForEnd
 ;Wait for bit 3 to be one (in VR)
_waitForNew:
 in al, dx
 test al, 08h
jz _waitForNew
 ret

我没有保存寄存器dxax,您可能需要根据您的需求添加一对push/pop


1 根据PED7G,可以直接实施页面直接操纵硬件。

,因此我能够通过以下2个步骤来解决这个'闪烁'问题:

  1. 通过添加 timedelay (睡觉(绘制画布。
  2. 增加 dosbox的CPU周期 到340000000。

谢谢大家的回答。

最新更新