Muh. Abdullah
Muh. Abdullah

Reputation: 51

Avoid blinking/flickering when drawing graphics in 8086 real-mode

I'm new to Assembly Language 8086. I'm using masm611. I am making dinosaur game (Google Chrome offline game). The more i draw the more screen starts blinking. I've tried everything but can't find anything. code compiles perfectly without error. Here is the code

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 is as follows

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

Snippet of DrawDino function

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

Upvotes: 2

Views: 2011

Answers (2)

Muh. Abdullah
Muh. Abdullah

Reputation: 51

So I was able to solve this 'Flickering' issue by doing following 2 steps:

  1. By adding TimeDelay (sleep) after drawing canvas.
  2. Increasing CPU cycles of DOSBox to 340000000.

Thank you all for you answers.

Upvotes: 0

Margaret Bloom
Margaret Bloom

Reputation: 44046

What you describe is called Flickering. Quoting Wikipedia directly:

For example, the practice of blanking an area directly in the frame buffer, then drawing 'on top' of it, makes it possible for the blank region to appear momentarily onscreen.

It is caused by a lack of synchronisation between your writes to the frame buffer and the video subsystem refresh cycle.
In order to maintain a consistent graphical state, you should modify the frame buffer only during a retrace, usually the vertical retrace due to its longer extension.
Writing to the frame buffer while the video subsystem is reading it creates an inconsistent state because only the part of the screen below the current scan line will be updated.

The solutions to this problem all share the same outline - write the frame buffer all at once during a retrace.

The optimal solution is Page flipping and has been suggested in the comments.
It consists of switching the active frame buffer between two different area in the display adapter's memory.
Unfortunately, the VGA adapter doesn't support that for the video mode you chose1 - there is only one page.

The alternative is double buffering that consists in using a back buffer - as large as the frame buffer - to write in with the usual drawing primitives.
The back buffer is then presented, that is copied (Bit blited) to the frame buffer all at once during the next available retracing.

The exact writing of the software architecture is too broad, there are many ways to accomplish this and each one has a different set of required skills.
The simplest approach is to use synchronous presentation - the code performs all the graphical operations on the back buffer and then waits for the next vertical retrace to present it.
If your code is fast enough the wasted time waiting for the retrace won't matter.
Remember however that the render loop will run at the speed of the monitor refresh rate (which is usually a good thing in games because it's a free wall clock for timings).

Unfortunately, you use the BIOS services as the graphic primitives and they write in the frame buffer only (the active page actually but there's only one).

As CodyGray kindly reminded, the alternative is writing directly into the frame buffer - the video mode you chose is not very easy to use because it involves bit-planes. Mode 13h instead exposes the frame buffer as a continuous region of memory - pretty much like a matrix.

If you don't want to go down the road of rewriting the code that draws you can try to adjust it with some duct tape.
It's possible to tell when the monitor is in vertical retrace by testing the bit 3 of the Input status #1 register of the VGA adapter - when that bit is one the monitor is in a vertical retrace.

The idea being to wait for a vertical retrace before starting the drawing of a new frame.
This may or may not be compatible with the way you wrote your render loop - further, I've never used this hybrid approach with the BIOS calls and the vertical retrace logic.
I cannot exclude the possibility of the BIOS implementation making the wait for the retrace useless due to the way it interacts with the video subsystem.
Particularly, CodyGray pointed out that the BIOS calls will inexorably lead to flickering.

In order to avoid the risk of starting the frame on a near-to-end retrace, it's best to wait for 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

I haven't saved the register dx and ax, you may want to add a pair of push/pop depending on your needs.


1 According to Ped7g it's possible to implement Page flipping manipulating the hardware directly.

Upvotes: 4

Related Questions