Чтобы вернуть некоторые воспоминания, я решил сесть и написать небольшую игру на ассемблере в режиме VGA 13h - пока не понял, что визуальный вывод чертовски мерцает.
Сначала я подозревал, что это может быть моя процедура очистки экрана. Действительно, при использовании STOSW вместо записи одного байта в видеопамять время мерцание становится менее раздражающим, но все же присутствует.
Покопавшись немного дальше, я вспомнил, что, возможно, мне придется дождаться вертикального обратного хода и обновить экран сразу после него, но это не сильно улучшило ситуацию.
Итак, окончательное решение, о котором я знаю, выглядит примерно так:
- делать все графические операции - очистку экрана, настройку пикселей - на отдельной области памяти
- дождитесь вертикального отката
- скопировать память в видеопамять
Теория, конечно, проста, но я никак не могу понять, как сделать запись в буфер и, в конечном счете, записать ее в видеопамять!
Вот урезанный, хотя и рабочий, фрагмент моего кода, написанного для 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
rep movsb
илиw
должны быть очень быстрыми на современном процессоре, даже если назначением является видеопамять (сопоставленный WC), достаточно быстрыми, чтобы работать во время vblank. (И, вероятно, закончить до сканирования первой строки, хотя фактическое требование состоит в том, чтобы быть достаточно быстрым, чтобы сканирование не успевало за копированием.) - person Peter Cordes   schedule 15.01.2021