Assembly 在8086实模式下绘制图形时避免闪烁/闪烁

Assembly 在8086实模式下绘制图形时避免闪烁/闪烁,assembly,x86,x86-16,Assembly,X86,X86 16,我不熟悉汇编语言8086。我用的是masm611。我正在制作恐龙游戏(谷歌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

我不熟悉汇编语言8086。我用的是masm611。我正在制作恐龙游戏(谷歌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 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

你所描述的叫做。直接引用维基百科:

例如,在帧缓冲器中直接对一个区域进行消隐,然后在其上画上“空白”,使得空白区域在屏幕上瞬时出现。 这是由于写入帧缓冲区和视频子系统刷新周期之间缺乏同步造成的。
为了保持一致的图形状态,您应该仅在回程期间修改帧缓冲区,通常是由于其较长的扩展。
在视频子系统读取帧缓冲区时写入帧缓冲区会产生不一致的状态,因为只有当前扫描线下方的屏幕部分会被更新

这个问题的解决方案都有相同的轮廓——在回溯期间一次性写入帧缓冲区

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

另一种方法是使用一个与帧缓冲区一样大的后缓冲区,以写入常用的绘图原语。
然后显示后缓冲区,即在下一次可用的回溯期间,一次复制()到帧缓冲区

软件架构的确切编写范围太广,有很多方法可以实现这一点,每种方法都有不同的技能要求。
最简单的方法是使用同步表示-代码在后台缓冲区上执行所有图形操作,然后等待下一次垂直回程来表示它。
如果您的代码足够快,那么等待回溯所浪费的时间就无关紧要了。
但是请记住,渲染循环将以监视器刷新率的速度运行(这在游戏中通常是一件好事,因为它是一个用于计时的免费挂钟)

不幸的是,您使用BIOS服务作为图形原语,它们只在帧缓冲区中写入(实际上是活动页面,但只有一个)

请注意,另一种方法是直接写入帧缓冲区-您选择的视频模式不太容易使用,因为它涉及位平面。相反,模式13h将帧缓冲区作为连续的内存区域公开-非常类似于矩阵

如果您不想重写绘制的代码,可以尝试使用一些管道胶带进行调整。
可以通过测试的位3来判断监视器何时处于垂直回溯状态-当该位为1时,监视器处于垂直回溯状态

在开始绘制新帧之前,先等待垂直回程。
这可能与您编写渲染循环的方式兼容,也可能与您编写渲染循环的方式不兼容。此外,我从未将这种混合方法用于BIOS调用和垂直回溯逻辑。
由于与视频子系统的交互方式,我不能排除BIOS实现使等待回溯无效的可能性。
特别指出,BIOS调用将不可避免地导致闪烁

为了避免在近端回溯时开始帧的风险,最好等待新的回溯

我尚未保存注册表
dx
ax
,您可能需要根据需要添加一对
push/pop



1根据可能实现的情况。

因此,我可以通过执行以下两个步骤来解决此“闪烁”问题:

  • 通过在绘制画布后添加时间延迟(睡眠)
  • 将DOSBox的CPU周期增加到3400000000

  • 谢谢大家的回答。

    定义闪烁?有人在模仿8086显卡的实际硬件闪烁(我告诉你,当扫描进行时,你访问了视频ram,你会弄乱像素/字符,屏幕上到处都会出现小闪烁)。我想这不是你所说的。你没有提供一个完整的例子,把它缩小到一些有足够代码的东西,我们可以理解问题可能在哪里,不是说发布你的整个项目,而是制作一个只演示问题的专用程序,一个测试程序…这里是描述我问题的视频链接[link]使用分页。在一个屏幕上绘制(同时显示另一个屏幕),绘制完成后,即可切换。int 10h具有切换活动页面的功能(AH=5)。尝试查找有关CRT(阴极射线管)有多旧的文章将图片绘制到屏幕上。VGA卡发出的信号是这样工作的,包括与光束同步实时读取VRAM,即如果VRAM中有“黑屏”,并且只将光束前面的几个像素变为白色,然后再将它们设回黑色,然后对整个屏幕执行此操作,用户将看到监视器上的“白屏”,即使任何其他监视VRAM数据的人都会看到“黑屏,几乎没有白像素移动”。您所说的一切都是100%正确的,而且信息很好,但这里真正的问题是使用BIOS中断10h,功能0Ch(绘制单像素)。这是一个极其缓慢的操作,必然会产生闪烁和滞后的绘图。您无法将其用于绘图目的。使用翻页或双缓冲是解决此问题的一种方法。更好的替代方法是直接写入视频缓冲区。也许可以选择
    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