I'm working on an assignment for the univesity, we need to create a simple breakout/arkanoid clone, it is going pretty well but I found a bug that would delete everything on the screen, this bug is random but I suspect its related to my DrawPaddle function. Maybe you can spot the bug or have the knowledge about why is the video memory doing that.
The game has to be done with 16 bit ms-dos assembly, I'm using NASM + VAL + Dosbox to create it, I compile it with:
nasm -f obj test.asm
val test.obj
The game justs moves the paddle in a fixed screen using the keyboard arrows, you can also quit the game by pressing escape.
This is the while everything is still alright: https://puu.sh/yeKtG/affc912d4b.png and it looks like this when the program overflows: http://puu.sh/yeKEy/caeef089d1.png or http://puu.sh/yeKJH/1106e1e823.png
I noticed that the weird behaviour only happens when I move the paddle and it will happen at random, for example now that I removed almost everything else from the program it can take a few tries to get the bug.
This is the DrawPaddle code:
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
And this is the complete code, it uses a keyboard handler to read the input and will write directly into the video memory using 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
Thanks for reading!
See Question&Answers more detail:
os