comportamento estranho do código (desenho corrompido) ao usar o próprio manipulador de interrupção do teclado `int 09h`
Estou trabalhando em uma tarefa para a universidade, precisamos criar um simples clone de breakout / arkanoid, está indo muito bem, mas encontrei um bug que excluiria tudo na tela, esse bug é aleatório, mas suspeito que esteja relacionado a minha função DrawPaddle. Talvez você consiga detectar o bug ou tenha o conhecimento sobre o motivo pelo qual a memória de vídeo está fazendo isso.
O jogo tem que ser feito com assembly ms-dos de 16 bits, estou usando NASM + VAL + Dosbox para criá-lo, eu o compilo com:
nasm -f obj test.asm
val test.obj
O jogo apenas move a raquete em uma tela fixa usando as setas do teclado; você também pode sair do jogo pressionando a tecla Escape.
Este é o momento em que tudo ainda está bem:https://puu.sh/yeKtG/affc912d4b.png e fica assim quando o programa estourar:http://puu.sh/yeKEy/caeef089d1.png ouhttp://puu.sh/yeKJH/1106e1e823.png
Percebi que o comportamento estranho só acontece quando eu movo a raquete e isso acontece aleatoriamente, por exemplo, agora que removi quase todo o resto do programa, pode levar algumas tentativas para obter o bug.
Este é o código do DrawPaddle:
DrawPaddle:
push di
mov di, [paddleposition]
mov cx, 5 ;the paddle will be 5 pixels tall
.p0:
push cx
mov cx, paddlesize
.p1:
mov byte [es:di], bl
inc di
loop .p1
add di, screenweight - paddlesize
pop cx
loop .p0
pop di
ret
E este é o código completo, ele usa um manipulador de teclado para ler a entrada e grava diretamente na memória de vídeo usando 320x200x256.
BITS 16
stacksize EQU 0200h
;Constantes
;Direccion de inicio de la memoria de video
videobase EQU 0a000h
;Definicion de colores
black EQU 0
green EQU 00110000b
;Screen data
screenweight EQU 320
;Paddle data
startx EQU 140
starty EQU 170
paddlesize EQU 40
paddlecolor EQU 00101010b
;Paddle movement limits
leftlimit EQU starty * screenweight + 1 + 10 + 1
rightlimit EQU ((starty + 1) * screenweight) - paddlesize - 10 - 1
segment mystack stack
resb stacksize
stacktop:
segment mydata data
;Variables
escpressed dw 0
leftpressed dw 0
rightpressed dw 0
oldintseg resw 1
oldintoff resw 1
originalVideoMode resb 1
paddleposition resw 1
segment mycode code
;Subrutinas
KeybInt:
push ds ;guardamos ds:ax
push ax
mov ax, mydata ;los re-inicializamos
mov ds, ax
cli
.getstatus:
in al, 64h
test al, 02h
loopnz .getstatus ;esperando a que el puerto esté listo
in al,60h ;obtenemos el codigo make o break de la tecla leida
cmp al, 01h ;revisamos si es escape
jne .revEsc
mov word [escpressed], 1
jmp .kbread
.revEsc:
cmp al, 81h ;revisamos si el escape fue soltado
jne .revIzq
mov word [escpressed], 0
jmp .kbread
.revIzq:
cmp al, 4bh ;revisamos si es la flecha izquierda
jne .revDer
mov word [leftpressed], 1
jmp .kbread
.revDer:
cmp al, 4dh ;revisamos si es la flecha derecha
jne .revIzq2
mov word [rightpressed], 1
jmp .kbread
.revIzq2:
cmp al, 0cbh ;si se solto la flecha izquierda
jne .revDer2
mov word [leftpressed], 0
jmp .kbread
.revDer2:
cmp al, 0cdh ;o la derecha
jne .kbread
mov word [rightpressed], 0
jmp .kbread
.kbread:
in al, 61h
or al, 10000000b
out 61h, al
and al, 01111111b
out 61h, al
mov al, 20h
out 20h, al
sti
pop ax ;recuperamos ds:ax
pop ds
iret
DrawStage:
push di
push bx
;movemos el cursor a la posicion 10,10
;que seria en realidad 10*320+10
mov di, (10 * screenweight) + 10
;ahora repetiremos esto 320-20 veces
mov cx, 300
.h1:
mov byte [es:di], green
inc di
loop .h1
mov di, (190 * screenweight) + 10
;ahora repetiremos esto 320-20 veces
mov cx, 301
.h2:
mov byte [es:di], green
inc di
loop .h2
;ahora volveremos al primer punto
;y dibujaremos hacia abajo
mov di, (10 * screenweight) + 10
;y lo repetiremos 200-20 veces
mov cx, 180
.v1:
mov byte [es:di], green
add di, screenweight
loop .v1
mov di, (10 * screenweight) + 310
mov cx, 180
.v2:
mov byte [es:di], green
add di, screenweight
loop .v2
pop bx
pop di
ret
;Rutina para dibujar el palo
;Recibe en bl el color del mismo
DrawPaddle:
push di
mov di, [paddleposition]
mov cx, 5 ;the paddle will be 5 pixels tall
.p0:
push cx
mov cx, paddlesize
.p1:
mov byte [es:di], bl
inc di
loop .p1
add di, screenweight - paddlesize
pop cx
loop .p0
pop di
ret
Delay1:
mov dx, 4
sub dx, 3
.pause1:
mov cx, 6000
.pause2:
dec cx
jne .pause2
dec dx
jne .pause1
ret
..start:
mov ax, mydata
mov ds, ax
mov ax, mystack
mov ss, ax
mov sp, stacktop
;guardando el manejador actual
mov ah, 35h
mov al, 9h
int 21h
mov [oldintseg], es
mov [oldintoff], bx
;instalando el manejador nuevo
mov ax, mycode
mov es, ax
mov dx, KeybInt
mov ax, cs
mov ds, ax
mov ah, 25h
mov al, 9h
int 21h
;restaurando el segmento de datos
mov ax, mydata
mov ds, ax
;guardando el modo de video y aplicando el nuevo
xor ax, ax
mov ah, 0fh
int 10h
mov [originalVideoMode], al
mov ah, 00h
mov al, 13h
int 10h
;coordenada de inicio para el palo
mov ax, (screenweight * starty) + startx
mov word [paddleposition], ax
mov ax, videobase
mov es, ax
call DrawStage
mov bl, paddlecolor
call DrawPaddle
jmp .main
.main:
call Delay1
;leemos las entradas
cmp word [escpressed], 1
je .dosexit
cmp word [rightpressed], 1
je .movRight
cmp word [leftpressed], 1
je .movLeft
jmp .main
.movRight:
mov bl, black
call DrawPaddle
cmp word [paddleposition], rightlimit
je .ending
inc word [paddleposition]
jmp .ending
.movLeft:
mov bl, black
call DrawPaddle
cmp word [paddleposition], leftlimit
je .ending
dec word [paddleposition]
jmp .ending
.ending:
mov bl, paddlecolor
call DrawPaddle
jmp .main
.dosexit:
;restaurando el modo de video original
mov ah, 00h
mov byte al, [originalVideoMode]
int 10h
;restaurando el manejador de teclado original
mov dx, [oldintoff]
mov ax, [oldintseg]
mov ds, ax
mov ah, 25h
mov al, 9h
int 21h
mov al, 0
mov ah, 4ch
int 21h
Obrigado pela leitura!