Asteroids: movimiento de los sprites por toda la pantalla (paso I) #Programación retro del Commodore 64

¡¡Por fin llegó el día!! ¡¡Desde hoy vamos a mover los sprites por toda la pantalla!! Deberíamos haber empezado por aquí hace mucho tiempo…

La principal limitación para impedir que los sprites se muevan por toda la pantalla reside en la librería "LibSprites.asm" y, más en particular, en su rutina "posicionaSprite". Esta rutina actualmente es así:

Asteroids - Posicionar sprite

Observad que:

  • Para empezar, la rutina sólo utiliza un byte ("psCoordX") para la coordenada X. Por tanto, difícilmente vamos a superar el pixel 255.
  • Para seguir, el registro MSIGX = $d010, que es el que controla si los sprites están en la zona X 255, siempre toma el valor $00, es decir, que se fuerza a que los sprites estén en la zona X <= 255.

Por tanto, tenemos que levantar estas limitaciones. Y lo primero para ello es que la coordenada X se pueda expresar con dos bytes: "psCoordXLo" y "ps CoordXHi".

Pero, lógicamente, lo anterior no es suficiente. Además, hay que hacer un tratamiento correcto de "psCoordXLo" y "psCoordXHi" y, en función de los valores que tomen, actuar correctamente sobre el registro MISGX.

Dado que MISGX tiene un bit para cada sprite, tendremos que hacer operaciones al nivel de bit, para lo cual ya sabemos que son muy útiles las instrucciones "and", "ora" y "eor". Concretamente:

  • "and Mascara" con una máscara que vale todo unos y un cero (ej. %11111110) sirve para desactivar el bit del acumulador que ocupa la posición del cero. Los demás bits quedan intactos.
  • "ora Mascara" con una máscara que vale todo ceros y un uno (ej. %00000001) sirve para activar el bit del acumulador que ocupa la posición del uno. Los demás bits quedan intactos.
  • "eor Mascara" con una máscara que vale todo (%11111111) unos sirve para cambiar el valor de todos los bits del acumulador. Esto equivale a hacer la operación lógica NOT.

Recordado esto, la nueva rutina "posicionaSprite" queda así:

Asteroids - Posicionar sprite 2

Es decir:

  • La coordenada X tiene dos bytes ("psCoordXLo" y "psCoordXHi") y la coordenada Y sigue teniendo un único byte ("psCoordY"), porque no necesita más.
  • El número de sprite (psNumero) se multiplica por dos con "asl a" y se pasa al registro X con "tax". Esto es así porque cada sprite tiene dos coordenadas (X e Y) y, por tanto, hay que actuar sobre SP0X + 0 y SP0Y + 0 para el sprite 0, SP0X + 2 y SP0Y + 2 para el sprite 1, SP0X + 4 y SP0Y + 4 para el sprite 2, etc. En general, SP0X + 2 x número de sprite y SP0Y + 2 x número de sprite.
  • Directamente, fija la coordenada Y del sprite con "lda psCoordY" y "sta SP0Y,x". No hay más que hacer porque es un único byte.
  • Fija la coordenada X del sprite con "lda psCoordXLo" y "sta SP0X,x". Ahora bien, con esto no hemos terminado, porque todavía nos falta "psCoordXHi".
  • Nos quedamos sólo con el bit 0 de "psCoordXHi" porque, en realidad, más que dos bytes lo que hace falta para el movimiento en el sentido X son nueve bits, los ocho bits de "psCoordXLo" y el bit 0 de "psCoordXHi". Esto lo hacemos con "lda psCoordXHi", "and #%00000001" y "sta psCoordXHi".
  • Ahora, valoramos si el bit 0 de "psCoordXHi" vale 0 o 1. Si vale 0, es porque el sprite está en la zona izquierda de la pantalla; si vale 1 es porque el sprite está en la zona derecha. Bifurcamos en función de esta condición con "beq psXHiCero".
  • Si el bit 0 vale 1 continuamos por la etiqueta "psXHiUno". Como el sprite está en la zona derecha, tenemos que activar su bit en MSIGX. Moviendo el número de sprite al registro X, y usando X como índice sobre la tabla "tablaSpr", pasamos del número de sprite (0, 1, 2, …, 7) a un byte (%00000001 = 1, %00000010 = 2, %00000100 = 4, …, %10000000 = 128) que sólo tiene activado el bit correspondiente a ese sprite. Finalmente, hacemos "ora MSIGX" y "sta MSIGX" para activar ese bit en MSIGX, sin modificar la situación de los otros bits / sprites.
  • Si el bit 0 vale 0 saltamos a la etiqueta "psXHiCero". Como el sprite está en la zona izquierda, tenemos que desactivar su bit en MSIGX. Nuevamente, moviendo el número de sprite al registro X, y usando X como índice sobre la tabla "tablaSpr", pasamos del número de sprite (0, 1, 2, …, 7) a un byte (%00000001 = 1, %00000010 = 2, %00000100 = 4, …, %10000000 = 128) que sólo tiene activado el bit correspondiente a ese sprite. Pero para desactivar ese bit en MSIGX, tenemos que hacer un "and Mascara", donde máscara tiene todos los bits a 1 menos el bit que se quiere desactivar. Por tanto, tenemos que hacer un NOT del valor anterior, lo que se consigue con "eor %11111111"; otra alternativa habría sido definir una tabla complementaria a "tablaSpr" con los valores NOT. Finalmente, hacemos "and MSIGX" y "sta MSIGX" para desactivar ese bit en MSIGX, sin modificar la situación de los otros bits / sprites.

Con esta nueva rutina "posicionaSprite" ya somos capaces de posicionar y mover un sprite por toda la pantalla. Para ello, usamos dos bytes para la coordenada X, aunque, en la práctica, sólo se utilizan nueve bits.

De este cambio (usar dos bytes para la coordenada X) se derivan un montón de cambios para el jugador / nave, los disparos y los asteroides. Básicamente, todo lo que tenga que ver con una coordenada X (si se mide en pixels, si se mide en caracteres no es necesario), hay que revisarlo para usar dos bytes.

Por eso decíamos que es mejor hacer las cosas bien desde el comienzo que no cambiarlas luego. Si hubiéramos empezado con una rutina "posicionaSprite" plenamente funcional, no habríamos tenido que hacer tanta reforma posterior.

Los cambios en el jugador / nave, los disparos y los asteroides los dejamos para entradas posteriores. De momento, podéis ir echando un vistazo a la nueva rutina "posicionaSprite" en la versión 18 del proyecto.


Código del proyecto: Asteroids18


Editar

Josepzin

No hay comentarios:

Publicar un comentario