游戏必须使用16位ms-dos程序集完成,我使用NASM VAL DosBox创建它,我用以下代码编译它:
- nasm -f obj test.asm
- val test.obj
游戏只是使用键盘箭头在固定屏幕上移动球拍,您也可以通过按下退出退出游戏.
这是一切都还好:https://puu.sh/yeKtG/affc912d4b.png,当程序溢出时它看起来像这样:http://puu.sh/yeKEy/caeef089d1.png或http://puu.sh/yeKJH/1106e1e823.png
我注意到奇怪的行为只发生在我移动桨时它会随机发生,例如现在我从程序中删除了几乎所有其他东西,它可能需要几次尝试来获取bug.
这是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
这是完整的代码,它使用键盘处理程序读取输入,并使用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],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,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,ax
- ;guardando el modo de video y aplicando el nuevo
- xor ax,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],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,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,9h
- int 21h
- mov al,0
- mov ah,4ch
- int 21h
谢谢阅读!
^^^这是答案(导致你的错误的原因),而不仅仅是一些建议
这里有一些建议如下:
在中断中有任何循环(动态延迟)也是错误的,中断应该尽可能快地进行.
我不记得从头部读取键盘的0x6X端口的正确方法(我只记得它有点棘手,让它完全正确),所以我不打算检查特定的输入/输出序列及其正确性.
但是如果你将在实际当前状态中设置XXXpressed中断,并且主循环将太慢,则可能看不到非常短的按键(因为输入没有被缓冲).对于像arkanoid clone一样的简单游戏,这是可以的,我根本不会被这个打扰,听起来像是正确的行为(你需要实际上非常快地把钥匙保持得这么短).
您还可以通过在中断代码处理程序附近保留一些数据空间(在iret之后将escpressed dw 0移动到代码部分中)来避免中断中的ds设置,然后将其用作mov word [cs:escpressed],1等.如果您实际上以更有效的方式设置内存标志和短中断代码(可以简化很多),那么使用cs的代价:内部中断寻址将低于ds设置.
你有多广泛地将slow loop
instruction用于所有主循环,但是在延迟子程序中你做了更快的dec cx jnz …替代方案.
我确实检查了如何编写DOS键盘处理程序,所以这是我的建议(不幸的是我没有测试它,如果它工作):
- segment mycode code
- escpressed db 0
- leftpressed db 0
- rightpressed db 0
- KeybInt:
- cli
- push ax ;guardamos ax
- ; when IRQ1 is fired,int 9 is called to handle it and the input
- ; already waits on port 0x60,no need to validate IBF flag on 0x64
- in al,60h ;obtenemos el codigo make o break de la tecla leida
- mov ah,al
- and al,0x7F ; AL = scan code without pressed/released flag
- shr ah,7
- xor ah,1 ; AH = 1/0 pressed/released
- cmp al,01h ;revisamos si es escape
- jne .checkLeft
- mov [cs:escpressed],ah
- jmp .kbread
- .checkLeft:
- cmp al,4bh ;revisamos si es la flecha izquierda
- jne .checkRight
- mov [cs:leftpressed],ah
- jmp .kbread
- .checkRight:
- cmp al,4dh ;revisamos si es la flecha derecha
- jne .kbread
- mov [cs:rightpressed],ah
- .kbread:
- in al,61h
- mov ah,al ; store original value
- or al,al ; set "enable kbd" bit
- mov al,ah
- out 61h,al ; set original value back
- mov al,al ; send end-of-interrupt signal to 8259 IC
- pop ax ;recuperamos ax
- sti ; not needed in real x86 real mode,IRET restores flags
- iret ; but explicit STI paired with CLI may help some VMs
…然后在游戏代码中,要检查密钥的状态,你也必须使用cs:
- ...
- cmp byte [cs:escpressed],1
- ...