Reputation: 51
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
Reputation: 51
So I was able to solve this 'Flickering' issue by doing following 2 steps:
Thank you all for you answers.
Upvotes: 0
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