程序集 – 使用自己的键盘中断`int 09h`处理程序时代码的奇怪行为(损坏的绘制)

前端之家收集整理的这篇文章主要介绍了程序集 – 使用自己的键盘中断`int 09h`处理程序时代码的奇怪行为(损坏的绘制)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在为univesity工作,我们需要创建一个简单的breakout / arkanoid克隆,它很顺利但是我发现了一个错误删除屏幕上的所有内容,这个bug是随机的,但我怀疑它与我的DrawPaddle功能.也许你可以发现错误或了解为什么视频内存会这样做.

游戏必须使用16位ms-dos程序集完成,我使用NASM VAL DosBox创建它,我用以下代码编译它:

  1. nasm -f obj test.asm
  2. val test.obj

游戏只是使用键盘箭头在固定屏幕上移动球拍,您也可以通过按下退出退出游戏.

这是一切都还好:https://puu.sh/yeKtG/affc912d4b.png,当程序溢出时它看起来像这样:http://puu.sh/yeKEy/caeef089d1.pnghttp://puu.sh/yeKJH/1106e1e823.png

我注意到奇怪的行为只发生在我移动桨时它会随机发生,例如现在我从程序中删除了几乎所有其他东西,它可能需要几次尝试来获取bug.

这是DrawPaddle代码

  1. DrawPaddle:
  2. push di
  3. mov di,[paddleposition]
  4. mov cx,5 ;the paddle will be 5 pixels tall
  5. .p0:
  6. push cx
  7. mov cx,paddlesize
  8. .p1:
  9. mov byte [es:di],bl
  10. inc di
  11. loop .p1
  12. add di,screenweight - paddlesize
  13. pop cx
  14. loop .p0
  15. pop di
  16. ret

这是完整的代码,它使用键盘处理程序读取输入,并使用320x200x256直接写入视频内存.

  1. BITS 16
  2.  
  3. stacksize EQU 0200h
  4.  
  5. ;Constantes
  6. ;Direccion de inicio de la memoria de video
  7. videobase EQU 0a000h
  8.  
  9. ;Definicion de colores
  10. black EQU 0
  11. green EQU 00110000b
  12.  
  13. ;Screen data
  14. screenweight EQU 320
  15.  
  16.  
  17. ;Paddle data
  18. startx EQU 140
  19. starty EQU 170
  20. paddlesize EQU 40
  21. paddlecolor EQU 00101010b
  22.  
  23. ;Paddle movement limits
  24. leftlimit EQU starty * screenweight + 1 + 10 + 1
  25. rightlimit EQU ((starty + 1) * screenweight) - paddlesize - 10 - 1
  26.  
  27. segment mystack stack
  28. resb stacksize
  29. stacktop:
  30.  
  31. segment mydata data
  32.  
  33. ;Variables
  34. escpressed dw 0
  35. leftpressed dw 0
  36. rightpressed dw 0
  37. oldintseg resw 1
  38. oldintoff resw 1
  39. originalVideoMode resb 1
  40. paddleposition resw 1
  41.  
  42. segment mycode code
  43. ;Subrutinas
  44.  
  45. KeybInt:
  46. push ds ;guardamos ds:ax
  47. push ax
  48.  
  49. mov ax,mydata ;los re-inicializamos
  50. mov ds,ax
  51.  
  52. cli
  53.  
  54. .getstatus:
  55. in al,64h
  56. test al,02h
  57. loopnz .getstatus ;esperando a que el puerto esté listo
  58.  
  59. in al,60h ;obtenemos el codigo make o break de la tecla leida
  60.  
  61. cmp al,01h ;revisamos si es escape
  62. jne .revEsc
  63. mov word [escpressed],1
  64. jmp .kbread
  65. .revEsc:
  66. cmp al,81h ;revisamos si el escape fue soltado
  67. jne .revIzq
  68. mov word [escpressed],0
  69. jmp .kbread
  70. .revIzq:
  71. cmp al,4bh ;revisamos si es la flecha izquierda
  72. jne .revDer
  73. mov word [leftpressed],1
  74. jmp .kbread
  75. .revDer:
  76. cmp al,4dh ;revisamos si es la flecha derecha
  77. jne .revIzq2
  78. mov word [rightpressed],1
  79. jmp .kbread
  80. .revIzq2:
  81. cmp al,0cbh ;si se solto la flecha izquierda
  82. jne .revDer2
  83. mov word [leftpressed],0
  84. jmp .kbread
  85. .revDer2:
  86. cmp al,0cdh ;o la derecha
  87. jne .kbread
  88. mov word [rightpressed],0
  89. jmp .kbread
  90. .kbread:
  91. in al,61h
  92. or al,10000000b
  93. out 61h,al
  94. and al,01111111b
  95. out 61h,al
  96. mov al,20h
  97. out 20h,al
  98.  
  99. sti
  100.  
  101. pop ax ;recuperamos ds:ax
  102. pop ds
  103. iret
  104.  
  105. DrawStage:
  106. push di
  107. push bx
  108. ;movemos el cursor a la posicion 10,10
  109. ;que seria en realidad 10*320+10
  110. mov di,(10 * screenweight) + 10
  111. ;ahora repetiremos esto 320-20 veces
  112. mov cx,300
  113. .h1:
  114. mov byte [es:di],green
  115. inc di
  116. loop .h1
  117.  
  118. mov di,(190 * screenweight) + 10
  119. ;ahora repetiremos esto 320-20 veces
  120. mov cx,301
  121. .h2:
  122. mov byte [es:di],green
  123. inc di
  124. loop .h2
  125.  
  126. ;ahora volveremos al primer punto
  127. ;y dibujaremos hacia abajo
  128. mov di,(10 * screenweight) + 10
  129. ;y lo repetiremos 200-20 veces
  130. mov cx,180
  131. .v1:
  132. mov byte [es:di],green
  133. add di,screenweight
  134. loop .v1
  135.  
  136. mov di,(10 * screenweight) + 310
  137. mov cx,180
  138. .v2:
  139. mov byte [es:di],screenweight
  140. loop .v2
  141.  
  142. pop bx
  143. pop di
  144. ret
  145.  
  146. ;Rutina para dibujar el palo
  147. ;Recibe en bl el color del mismo
  148. DrawPaddle:
  149. push di
  150. mov di,screenweight - paddlesize
  151. pop cx
  152. loop .p0
  153. pop di
  154. ret
  155.  
  156. Delay1:
  157. mov dx,4
  158. sub dx,3
  159. .pause1:
  160. mov cx,6000
  161. .pause2:
  162. dec cx
  163. jne .pause2
  164. dec dx
  165. jne .pause1
  166. ret
  167.  
  168. ..start:
  169. mov ax,mydata
  170. mov ds,ax
  171. mov ax,mystack
  172. mov ss,ax
  173. mov sp,stacktop
  174.  
  175. ;guardando el manejador actual
  176. mov ah,35h
  177. mov al,9h
  178. int 21h
  179. mov [oldintseg],es
  180. mov [oldintoff],bx
  181.  
  182. ;instalando el manejador nuevo
  183. mov ax,mycode
  184. mov es,ax
  185. mov dx,KeybInt
  186. mov ax,cs
  187. mov ds,ax
  188. mov ah,25h
  189. mov al,9h
  190. int 21h
  191.  
  192. ;restaurando el segmento de datos
  193. mov ax,ax
  194.  
  195. ;guardando el modo de video y aplicando el nuevo
  196. xor ax,0fh
  197. int 10h
  198. mov [originalVideoMode],al
  199. mov ah,00h
  200. mov al,13h
  201. int 10h
  202. ;coordenada de inicio para el palo
  203. mov ax,(screenweight * starty) + startx
  204. mov word [paddleposition],videobase
  205. mov es,ax
  206.  
  207. call DrawStage
  208. mov bl,paddlecolor
  209. call DrawPaddle
  210. jmp .main
  211.  
  212. .main:
  213. call Delay1
  214.  
  215. ;leemos las entradas
  216. cmp word [escpressed],1
  217. je .dosexit
  218. cmp word [rightpressed],1
  219. je .movRight
  220. cmp word [leftpressed],1
  221. je .movLeft
  222. jmp .main
  223. .movRight:
  224. mov bl,black
  225. call DrawPaddle
  226. cmp word [paddleposition],rightlimit
  227. je .ending
  228. inc word [paddleposition]
  229. jmp .ending
  230. .movLeft:
  231. mov bl,leftlimit
  232. je .ending
  233. dec word [paddleposition]
  234. jmp .ending
  235. .ending:
  236. mov bl,paddlecolor
  237. call DrawPaddle
  238. jmp .main
  239.  
  240. .dosexit:
  241. ;restaurando el modo de video original
  242. mov ah,00h
  243. mov byte al,[originalVideoMode]
  244. int 10h
  245.  
  246. ;restaurando el manejador de teclado original
  247. mov dx,[oldintoff]
  248. mov ax,[oldintseg]
  249. mov ds,9h
  250. int 21h
  251. mov al,0
  252. mov ah,4ch
  253. int 21h

谢谢阅读!

您可以在键盘中断中修改cx而不保留它.

^^^这是答案(导致你的错误的原因),而不仅仅是一些建议

这里有一些建议如下:

在中断中有任何循环(动态延迟)也是错误的,中断应该尽可能快地进行.

我不记得从头部读取键盘的0x6X端口的正确方法(我只记得它有点棘手,让它完全正确),所以我不打算检查特定的输入/输出序列及其正确性.

但是如果你将在实际当前状态中设置XXXpressed中断,并且主循环将太慢,则可能看不到非常短的按键(因为输入没有被缓冲).对于像arkanoid clone一样的简单游戏,这是可以的,我根本不会被这个打扰,听起来像是正确的行为(你需要实际上非常快地把钥匙保持得这么短).

您还可以通过在中断代码处理程序附近保留一些数据空间(在iret之后将escpressed dw 0移动到代码部分中)来避免中断中的ds设置,然后将其用作mov word [cs:escpressed],1等.如果您实际上以更有效的方式设置内存标志和短中断代码(可以简化很多),那么使用cs的代价:内部中断寻址将低于ds设置.

你有多广泛地将slow loop instruction用于所有主循环,但是在延迟子程序中你做了更快的dec cx jnz …替代方案.

我确实检查了如何编写DOS键盘处理程序,所以这是我的建议(不幸的是我没有测试它,如果它工作):

  1. segment mycode code
  2.  
  3. escpressed db 0
  4. leftpressed db 0
  5. rightpressed db 0
  6.  
  7. KeybInt:
  8. cli
  9. push ax ;guardamos ax
  10.  
  11. ; when IRQ1 is fired,int 9 is called to handle it and the input
  12. ; already waits on port 0x60,no need to validate IBF flag on 0x64
  13.  
  14. in al,60h ;obtenemos el codigo make o break de la tecla leida
  15. mov ah,al
  16. and al,0x7F ; AL = scan code without pressed/released flag
  17. shr ah,7
  18. xor ah,1 ; AH = 1/0 pressed/released
  19.  
  20. cmp al,01h ;revisamos si es escape
  21. jne .checkLeft
  22. mov [cs:escpressed],ah
  23. jmp .kbread
  24. .checkLeft:
  25. cmp al,4bh ;revisamos si es la flecha izquierda
  26. jne .checkRight
  27. mov [cs:leftpressed],ah
  28. jmp .kbread
  29. .checkRight:
  30. cmp al,4dh ;revisamos si es la flecha derecha
  31. jne .kbread
  32. mov [cs:rightpressed],ah
  33. .kbread:
  34.  
  35. in al,61h
  36. mov ah,al ; store original value
  37. or al,al ; set "enable kbd" bit
  38. mov al,ah
  39. out 61h,al ; set original value back
  40.  
  41. mov al,al ; send end-of-interrupt signal to 8259 IC
  42.  
  43. pop ax ;recuperamos ax
  44. sti ; not needed in real x86 real mode,IRET restores flags
  45. iret ; but explicit STI paired with CLI may help some VMs

…然后在游戏代码中,要检查密钥的状态,你也必须使用cs:

  1. ...
  2. cmp byte [cs:escpressed],1
  3. ...

猜你在找的Windows相关文章