To bring back some memories I decided to sit down and code a little assembler game in VGA mode 13h - until the point I realized the visual output is flickering as hell.
At first I suspected it might be my clearscreen routine. Indeed by using a STOSW instead of writing a single byte to the video memory a time the flickering is less annoying but still present.
Digging some further I recalled I might have to wait for the vertical retrace and update my screen right after but that didn't make things much better.
So the final solution I'm aware of goes a little like this:
- do all graphical operations - clearing the screen, setting pixels - on a separate memory region
- wait for the vertical retrace
- copy the memory over to the video memory
The theory is of course simple but I just can't figure out how to do my writes to the buffer and ultimately blit it into the video memory!
Here's a striped-down - though working - snippet of my code written for TASM:
VGA256 EQU 13h
TEXTMODE EQU 3h
VIDEOMEMORY EQU 0a000h
RETRACE EQU 3dah
.MODEL LARGE
.STACK 100h
.DATA
spriteColor DW ?
spriteOffset DW ?
spriteWidth DW ?
spriteHeight DW ?
enemyOneA DB 0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0
spriteToDraw DW ?
buffer DB 64000 dup (0) ; HERE'S MY BUFFER
.CODE
Main:
MOV AX,@DATA;
MOV DS,AX
MOV AH,0
MOV AL,VGA256
INT 10h
CLI
MainLoop:
MOV DX,RETRACE
Vsync1:
IN AL,DX
TEST AL,8
JZ Vsync1
Vsync2:
IN AL,DX
TEST AL,8
JNZ Vsync2
CALL clearScreen
CALL updateSprites
JMP MainLoop
mov AH,1
int 21h
mov AH,0
mov AL,TEXTMODE
int 10h
; program end
clearScreen PROC NEAR
MOV BX,VIDEOMEMORY
MOV ES,BX
XOR DI,DI
MOV CX,320*200/2
MOV AL,12
MOV AH,AL
REP STOSW
RET
clearScreen ENDP
drawSprite PROC NEAR
MOV DI,0
MOV CX,0
ForLoopA:
PUSH CX
MOV SI,CX
MOV CX,0
ForLoopB:
MOV BX,spriteToDraw
MOV AL,[BX+DI]
CMP AL,0
JE DontDraw
MOV BX,spriteColor
MUL BX
PUSH SI
PUSH DI
PUSH AX
MOV AX,SI
MOV BX,320
MUL BX
MOV BX,AX
POP AX
POP DI
ADD BX,CX
ADD BX,spriteOffset
MOV SI,BX
MOV BX,VIDEOMEMORY
MOV ES,BX
MOV ES:[SI],AL
POP SI
DontDraw:
INC DI
INC CX
CMP CX,spriteWidth
JNE ForLoopB
POP CX
INC CX
CMP CX,spriteHeight
JNE ForLoopA
RET
drawSprite ENDP
updateSprites PROC NEAR
MOV spriteOffset,0
MOV spriteColor,15
MOV spriteWidth,16
MOV spriteHeight,8
MOV spriteOffset,0
MOV spriteToDraw, OFFSET enemyOneA
CALL drawSprite
RET
updateSprites ENDP
END Main