0x04 Ensamblador ZX Spectrum Marciano – Nave
En este capítulo de Ensamblador ZX Spectrum Marciano, vamos a implementar la nave, su movimiento y, por tanto, los controles.
Antes de nada, creamos la carpeta Paso04 y copiamos, desde la carpeta Paso03, los archivos Const.asm, Graph.asm, Main.asm, Print.asm y Var.asm.
Tabla de contenidos
- Posicionamiento en pantalla
- Pintamos la nave
- Movemos la nave
- Ensamblador ZX Spectrum, conclusión
- Enlaces de interés
Posicionamiento en pantalla
Hasta ahora, hemos usado el carácter de control de la instrucción AT y las coordenadas para posicionarnos por la pantalla, pero esto resulta lento.
Abrimos Graph.asm y al inicio del archivo vamos a implementar una rutina que hace lo mismo, pero es más rápida.
; -----------------------------------------------------------------------------
; Posiciona el cursor en las coordenadas especificadas.
;
; Entrada: B = Coordenada Y (24 a 3).
; C = Coordenada X (32 a 1).
; Altera el valor de los registros AF y BC.
; -----------------------------------------------------------------------------
At:
push de ; Preserva el valor de DE
push hl ; Preserva el valor de HL
call $0a23 ; Llama a la rutina de la ROM
pop hl ; Recupera el valor de HL
pop de ; Recupera el valor de DE
ret
En esta rutina hacemos uso de la rutina de la ROM que posiciona el cursor. Preservamos el valor de DE, PUSH DE, y el de HL, PUSH HL. Una vez preservados estos valores, llamamos a la rutina de la ROM, CALL $0a23, y recuperamos los valores de HL, POP HL, y también el de DE, POP DE. Finalmente, salimos, RET.
En los comentarios podéis observar que esta rutina, en realidad la de la ROM, también altera el valor de AF y BC, pero no los preservamos; no nos va a afectar que lo altere y por eso nos ahorramos dos PUSH y dos POP.
Otra cosa a tener muy en cuenta, y una pista os la dan los comentarios, es que para la rutina de la ROM la esquina superior izquierda está en las coordenadas Y=24 y X=32, por lo que trabajaremos con las coordenadas invertidas con respecto a la instrucción AT.
Vamos a abrir el archivo Const.asm y añadimos las constantes de las coordenadas.
; Coordenadas de la pantalla para la rutina de la ROM que posiciona el cursor,
; relativas al área de juego (el marco).
COR_X: EQU $20 ; Coordenada X de la esquina superior izquierda
COR_Y: EQU $18 ; Coordenada Y de la esquina superior izquierda
MIN_X: EQU $00 ; A restar de COR_X para X esquina superior izquierda
MIN_Y: EQU $00 ; A restar de COR_Y para Y esquina superior izquierda
MAX_X: EQU $1f ; A restar de COR_X para X esquina inferior derecha
MAX_Y: EQU $15 ; A restar de COR_Y para Y esquina inferior derecha
Recordemos que la directiva EQU no se compila, por lo que no aumenta el tamaño del binario, lo que hace es sustituir la etiqueta por el valor en aquellos lugares en dónde se encuentre.
Pintamos la nave
Lo primero que vamos a hacer es poner la nave en nuestra zona de juego; la nave se va a mover de izquierda a derecha en la parte inferior de la zona de juego.
Al ser la nave una parte móvil, necesitamos saber en todo momento en que posición está, y la posición inicial, tal y como vimos con las palas en PorompomPong.
Abrimos el archivo Var.asm (el que hay en la carpeta Paso04), y añadimos, tras las declaraciones de los títulos de información de la partida, las líneas siguientes:
; ----------------------------------------------------------------------------
; Declaraciones de los gráficos de los distintos personajes
; y la configuración de coordenadas (Y, X)
; ----------------------------------------------------------------------------
; ----------------------------------------------------------------------------
; Nave
; ----------------------------------------------------------------------------
shipPos:
dw $0511
En el caso de la nave, solo vamos a definir la posición, DW $0511, un valor de dos bytes, primero la coordenada Y y luego la X, que durante la partida cargaremos en BC para posicionar la nave. La posición $0511 es el resultado de restar 19 ($13) y 15 ($0f) de las coordenadas de la esquina superior derecha que usa la rutina de la ROM ($1820).
Abrimos el archivo Const.asm e incluimos constantes para el carácter de la nave, la posición inicial y los topes a izquierda y derecha.
; Código de carácter de la nave, posición inicial y topes
SHIP_GRAPH: EQU $90
SHIP_INI: EQU $0511
SHIP_TOP_L: EQU $1e
SHIP_TOP_R: EQU $01
Para pintar los colores correctos, y para no repetir el código una y otra vez, vamos a implementar una rutina para cambiar el color de tinta, la vamos a implementar en el archivo Graph.asm. Esta rutina recibe en A el valor de la tinta.
Antes de implementar la rutina, abrimos el archivo Const.asm y añadimos la constante de la posición de memoria donde se guardan los atributos de color actuales. Estos atributos son los usados por RST $10 para asignar el color al carácter que pinta.
; Variable de sistema donde están los atributos de color actuales
ATTR_T: EQU $5c8f
Y ahora sí, abrimos el archivo Graph.asm e implementamos la rutina Ink.
; -----------------------------------------------------------------------------
; Cambia la tinta
;
; Entrada: A -> Color de la tinta
; Altera el valor del registro A.
; -----------------------------------------------------------------------------
Ink:
exx ; Preserva el valor de BC, DE y HL
ld b, a ; Carga la tinta en B
ld a, (ATTR_T) ; Carga los atributos actuales en A
and $f8 ; Desecha los bits de la tinta
or b ; Añade la tinta
ld (ATTR_T), a ; Carga los atributos actuales
exx ; Recupera el valor de BC, DE y HL
ret
Dado que necesitamos apoyarnos en el registro B, lo primero que hacemos es preservar su valor con la instrucción EXX, que lo que hace es intercambiar el valor de los registros BC, DE y HL con los registros alternativos ‘BC, ‘DE y ‘HL con tan solo un byte y cuatro ciclos de reloj, siendo más rápido y ocupando menos que si hubiéramos usado la pila.
Cargamos en B el valor de la tinta, LD B, A, cargamos en A los atributos actuales, LD A, (ATTR_T), desechamos la tinta, AND $F8, añadimos la tinta, OR B, y cargamos el resultado en los atributos actuales, LD (ATTR_T), A. Por último, recuperamos el valor de los registros BC, DE y HL, EXX, y salimos, RET.
Ahora necesitamos una rutina que pinte la nave; la vamos a implementar en el archivo Print.asm.
PrintShip:
ld a, $07
call Ink
Cargamos en A la tinta blanca, LD A, $07, y llamamos a cambiar la tinta, CALL Ink.
ld bc, (shipPos)
call At
Cargamos en BC la posición actual de la nave, LD BC, (shipPos), y llamamos a posicionar el cursor, CALL At.
ld a, SHIP_GRAPH
rst $10
ret
Cargamos en A el código de carácter de la nave, LD A, SHIP_GRAPH, la pintamos, RST $10, y salimos, RET.
Ya tenemos lista la rutina que pinta la nave, cuyo aspecto final es el siguiente:
; ----------------------------------------------------------------------------
; Pinta la nave en la posición actual.
; Altera el valor de los registros A y BC.
; ----------------------------------------------------------------------------
PrintShip:
ld a, $07 ; Carga en A la tinta blanca
call Ink ; Llama al cambio de tinta
ld bc, (shipPos) ; Carga en BC la posición actual de la nave
call At ; Llama a posicionar el cursor
ld a, SHIP_GRAPH ; Carga en A el carácter de la nave
rst $10 ; La pinta
ret
Antes de dejar el archivo Print.asm, vamos a volver sobre la rutina que pinta el marco, en concreto a la parte en la que se hacemos un bucle para pintar los laterales.
printFrame_loop:
ld a, $16 ; Carga en A el carácter de control de AT
rst $10 ; Lo "pinta"
ld a, b ; Carga en A la línea
rst $10 ; La "pinta"
ld a, $00 ; Carga en A la columna
rst $10 ; La "pinta"
ld a, $99 ; Carga en A el carácter lateral izquierdo
rst $10 ; Lo pinta
ld a, $16 ; Carga en A el carácter de control de AT
rst $10 ; Lo "pinta"
ld a, b ; Carga en A la línea
rst $10 ; La "pinta"
ld a, $1f ; Carga en A la columna
rst $10 ; La "pinta"
ld a, $9a ; Carga en A el carácter lateral derecho
rst $10 ; Lo pinta
inc b ; Apunta B a la línea siguiente
ld a, b ; Carga el valor de B en A
cp $14 ; Comprueba si está en la línea veinte
jr nz, printFrame_loop ; Si no es así, sigue con el bucle
Como podemos observar, las seis lineas siguientes a printFrame_loop posicionan el cursor para pintar en el lateral izquierdo, más adelante vemos otras seis líneas que hacen lo mismo para el lateral derecho.
Yo estoy usando Visual Studio Code con la extensión Z80 Assembly meter, y por eso sé que esta rutina, desde printFrame_loop hasta JR NZ, printFrame_loop, consume ciento sesenta y cinco ciclos de reloj y veintiocho bytes.
Dado que ya hemos implementado una rutina que posiciona el cursor, acabamos de implementar At, vamos a sustituir estas líneas por llamadas a esa rutina.
Lo primero es, justo encima de printFrame_loop, modificar la línea LD B, $01, y dejarla como sigue:
ld b, COR_Y - $01
Recordad que con la rutina de la ROM las coordenadas están invertidas. Apuntamos B a la línea uno, LD B, COR_Y – $01.
Borramos la primeras seis líneas justo debajo de printFrame_loop y las sustituimos por las siguientes:
ld c, COR_X - $01
call At
Unas líneas más abajo, borramos desde LD A, $16 hasta el RST $10 que hay justo encima de LD A, $9a, y sustituimos estas líneas por las siguientes:
ld c, COR_X - MAX_X
call At
Ahora vamos a sustituir desde INC B hasta JR NZ, printFrame_loop.
dec b
ld a, COR_Y - MAX_Y + $01
sub b
jr nz, printFrame_loop
De esta manera, el aspecto final de la parte modificada es el siguiente:
ld b, COR_Y - $01 ; Apunta B a la línea 1
printFrame_loop:
ld c, COR_X - MIN_X ; Apunta C a la columna 0
call At ; Posiciona el cursor
ld a, $99 ; Carga en A el carácter lateral izquierdo
rst $10 ; Lo pinta
ld c, COR_X - MAX_X ; Apunta C a la columna 31
call At ; Posiciona el cursor
ld a, $9a ; Carga en A el carácter lateral derecho
rst $10 ; Lo pinta
dec b ; Decrementa B
ld a, COR_Y - MAX_Y + $01 ; Apunta A a la línea 20
sub b ; Resta la siguiente línea
jr nz, printFrame_loop ; Si el resultado no es cero, sigue con el bucle
ret
A simple vista, la rutina es más corta, consumiendo veintidós bytes y ciento once ciclos de reloj, pero cuidado, no es oro todo lo que reluce, a esos ciclos de reloj hay que sumarle los ciclos de reloj de la rutina At, que son sesenta y nueve (los bytes no los añadimos pues la rutina la vamos a usar desde más sitios).
Una vez sumados todos los valores, la nueva rutina ocupa veintidós bytes y cada iteración del bucle consume ciento ochenta ciclos de reloj, quince más que la implementación anterior, aunque hemos ahorrado seis bytes. Además, la rutina de la ROM tarda menos que posicionarnos usando el código de control de AT.
¿Qué hacemos? ¿Cómo lo dejamos?
Dado que la rutina que pinta el borde no es crítica ya que solo lo pintamos al inicio de cada nivel, y cuatro ciclos de reloj no se van a notar, optamos por el ahorro de seis bytes, nos quedamos con la nueva implementación.
Solo nos queda pintar la nave, vamos a Main.asm y justo debajo de CALL PrintInfoGame, añadimos la llamada a pintar la nave.
call PrintShip
Compilamos, cargamos en el emulador y vemos los resultados.
Movemos la nave
La nave se tiene que mover como respuesta a alguna acción del jugador, en nuestro caso a la pulsación de tres teclas: Z para mover la nave hacia la izquierda, X para mover la nave hacia la derecha y V para disparar.
Creamos el archivos Ctrl.asm e implementamos la rutina que lee el teclado y devuelve las teclas de control pulsadas.
La rutina que vamos a implementar lee el teclado y devuelve en el registro D las teclas de control pulsadas, parecido a como se hizo en PorompomPong, poniendo a uno el bit cero si se ha pulsado la tecla Z, el bit uno se ha pulsado la tecla X y el bit dos si se ha pulsado la tecla V.
CheckCtrl:
ld d, $00
ld a, $fe
in a, ($fe)
Primero ponemos D a cero, LD D, $00, cargamos en A la semifila Cs-V, LD A, $FE, y leemos el teclado, IN A, ($FE).
checkCtrl_fire:
bit $04, a
jr nz, checkCtrl_left
set $02, d
Comprobamos si se ha pulsado la tecla V, BIT $04, A. En el caso de que no se haya pulsado, saltamos a comprobar si se ha pulsado la tecla para mover hacia la izquierda, JR NZ, checkCtrl_left. Si se ha pulsado, pone a uno el bit dos del registro D, marcando que se ha pulsado el disparo, SET $02, D.
Cuando leemos del teclado, el estado de las teclas de la semifila leída está en el registro A, a uno las teclas que no se han pulsado y cero las que sí (el bit cero hace referencia a la tecla más alejada del centro del teclado y el cuatro a la más cercana).
La instrucción BIT evalúa el estado del bit especificado ($04), del registro especificado (A), y según esté a cero o uno, activa o desactiva el flag Z. La instrucción SET pone a uno el bit especificado ($02), del registro especificado (D). La instrucción RES es la contraria a SET, pone el bit a cero. RES y SET no afectan al registro F.
checkCtrl_left:
bit $01, a
jr nz, checkCtrl_right
set $00, d
Comprobamos si se ha pulsado la tecla Z, BIT $01, A. En el caso de que no se haya pulsado, saltamos a comprobar si se ha pulsado la tecla para mover hacia la derecha, JR NZ, checkCtrl_right. Si se ha pulsado, pone a uno el bit cero del registro D, marcando que se ha pulsado izquierda, SET $00, D.
checkCtrl_right:
bit $02, a
ret nz
set $01, d
Comprobamos si se ha pulsado la tecla X, BIT $02, A. En el caso de que no se haya pulsado, salimos. Si se ha pulsado, pone a uno el bit cero del registro D, marcando que se ha pulsado derecha, SET $00, D.
checkCtrl_testLR:
ld a, d
and $03
sub $03
ret nz
ld a, d
and $04
ld d, a
checkCtrl_end:
ret
Para finalizar, comprobamos si se ha pulsado al mismo tiempo izquierda y derecha, en cuyo caso desactivamos ambas.
Cargamos el A el valor de D, LD A, D, nos quedamos el valor de los bits cero y uno, AND $03, y le restamos tres, SUB $03. Si el resultado no es cero salimos, RET NZ, ya que no estaban los dos bits a uno y no tenemos que hacer nada.
Si no hemos salido, cargamos de nuevo el valor de D en A, LD A, D, nos quedamos solo con el valor del bit dos (disparo), AND $04, y cargamos el valor en D, LD D, A, desactivando de este modo la pulsación simultánea de izquierda y derecha. Finalmente, salimos, RET.
La última etiqueta, checkCtrl_end, no es necesaria, pero la ponemos para clarificar cual es el final de la rutina.
El aspecto final de la rutina es el siguiente:
; ----------------------------------------------------------------------------
; Evalúa si se ha pulsado alguna de la teclas de dirección.
; Las teclas de dirección son:
; Z -> Izquierda
; X -> Derecha
; V -> Disparo
;
; Retorna: D -> Teclas pulsadas.
; Bit 0 -> Izquierda
; Bit 1 -> Derecha
; Bit 2 -> Disparo
;
; Altera el valor de los registros A y D
; ----------------------------------------------------------------------------
CheckCtrl:
ld d, $00 ; Pone D a 0
ld a, $fe ; Carga la semifila Cs-V en A
in a, ($fe) ; Leee el teclado
checkCtrl_fire:
bit $04, a ; Evalúa si se ha pulsado la V
jr nz, checkCtrl_left ; Si no se ha pulsado, salta
set $02, d ; Activa el bit 2 de D
checkCtrl_left:
bit $01, a ; Evalúa si se ha pulsado la Z
jr nz, checkCtrl_right ; Si no se ha pulsado, salta
set $00, d ; Activa el bit 0 de D
checkCtrl_right:
bit $02, a ; Evalúa si se ha pulsado la X
ret nz ; Si no se ha pulsado, sale
set $01, d ; Activa el bit 1 de D
checkCtrl_testLR:
ld a, d ; Carga el valor de D en A
and $03 ; Se queda con el valor de los bits 0 y 1
sub $03 ; Comprueba si están activos los dos bits
ret nz ; Si el resultad no es cero, no están
; activos los dos bits y sale
ld a, d ; Carga el valor de D en A
and $04 ; Desactiva los bits 0 y 1
ld d, a ; Carga el valor de A en D
checkCtrl_end:
ret
Abrimos el archivo Main.asm y en cada iteración del bucle Main_loop vamos a llamar a la rutina que acabamos de implementar. Justo debajo de la etiqueta Main_loop añadimos la línea siguiente:
call CheckCtrl
Un poco más abajo, en la parte donde tenemos los includes, añadimos el include para el archivo Ctrl.asm.
include "Ctrl.asm"
Compilamos y comprobamos que compila bien; no hay errores.
Cuando se mueva la nave, primero hay que borrarla de la posición actual y volver a pintarla en la nueva posición.
En Const.asm, justo encima de las constantes que definimos para la nave, añadimos la siguiente constante:
; Código de carácter del carácter en blanco
WHITE_GRAPH:EQU $9e
Abrimos el archivo Print.asm y, al inicio del mismo, implementamos la rutina que borra la nave. Como esta rutina la vamos a usar también para borrar los enemigos y el disparo, recibe en BC las coordenadas del carácter que vamos a borrar.
DeleteChar:
call At
Como DeleteChar recibe en BC las coordenadas del carácter que vamos a borrar, el primer paso es posicionar el cursor, CALL At.
ld a, WHITE_GRAPH
rst $10
ret
Lo siguiente es cargar en A el carácter en banco, LD A, WHITE_GRAPH, y lo pintamos, RST $10, borrando así lo que hubiera pintado en esas coordenadas.
El aspecto final de la rutina es el siguiente:
; ----------------------------------------------------------------------------
; Borra un carácter de la pantalla
;
; Entrada: BC -> Coordenadas Y/X del carácter
; Altera el valor de los resgistros AF
; ----------------------------------------------------------------------------
DeleteChar:
call At ; Llama a posicionar el cursor
ld a, WHITE_GRAPH ; Carga en A el carácter de blanco
rst $10 ; Lo pinta y borra la nave
ret
Para probar que funciona, abrimos Main.asm y, justo debajo de CALL CheckCtrl, añadimos las líneas siguientes:
ld bc, (shipPos)
call DeleteChar
call PrintShip
Con estas líneas borramos y pintamos la nave en cada iteración de Main_loop. Si compilamos y cargamos en el emulador, veremos que la nave parpadea constantemente, señal de que el borrado de la nave funciona.
Ahora vamos a mover la nave. Creamos un nuevo archivo, Game.asm, dónde empezamos por implementar la rutina que cambia la posición de la nave y la pinta, esta rutina recibe en el registro D el estado de los controles (antes de continuar añadimos en la sección de includes de Main.asm el archivo Game.asm).
MoveShip:
ld bc, (shipPos)
bit $01, d
jr nz, moveShip_right
Cargamos la posición actual de la nave en BC, LD BC, (shipPos), comprobamos si se ha pulsado el control derecha, BIT $01, D, en cuyo caso saltamos a la parte que controla el movimiento hacia la derecha, JR NZ, moveShip_right.
bit $00, d
ret z
Comprobamos si se ha pulsado el control izquierda, BIT $00, D, y si no se ha pulsado salimos, RET Z.
moveShip_left:
ld a, SHIP_TOP_L + $01
sub c
ret z
call DeleteChar
inc c
ld (shipPos), bc
jr moveShip_print
Si se pulsado el control izquierda, comprobamos si podemos mover la nave. Cargamos en A el tope al que se puede mover la nave hacia la izquierda, LD A, SHIP_TOP_L + $01, y le restamos la columna actual de la posición de la nave, SUB C. Si el resultado es cero, la nave ya está en el tope y no se puede mover más hacia la derecha, así que salimos, RET Z.
Si no hemos salido, borramos la nave de la posición actual, CALL DeleteChar, incrementamos C para apuntar a la columna justo a la izquierda de la posición actual, INC C, actualizamos en memoria la nueva posición de la nave, LD (shipPos), BC, y saltamos al final de la rutina, jr moveShip_print.
La rutina que controla el movimiento de la nave hacia la derecha es prácticamente igual a la que controla el movimiento hacia la izquierda, por lo que solo vamos a marcar y explicar los cambios.
moveShip_right:
ld a, SHIP_TOP_R + $01 ; ¡CAMBIO!
sub c
ret z
call DeleteChar
dec c ; ¡CAMBIO!
ld (shipPos), bc
ret
Cargamos en A el tope al que se puede mover la nave hacia la derecha, LD A, SHIP_TOP_R. Decrementamos C para apuntar a la columna justo a la derecha de la posición actual, DEC C.
El aspecto final de la rutina es el siguiente:
; -----------------------------------------------------------------------------
; Mueve la nave
;
; Entrada: D -> Estado de los controles
; Altera el valor de los registros AF y BC
; -----------------------------------------------------------------------------
MoveShip:
ld bc, (shipPos) ; Carga la posición actual de la nave en BC
bit $01, d ; Comprueba si el control derecha viene activo
jr nz, moveShip_right ; En cuyo caso, sale
bit $00, d ; Comprueba si el control izquierda viene activo
ret z ; Si no es así, sale
moveShip_left:
ld a, SHIP_TOP_L + $01 ; Carga en A el tope para la nave por la izquierda
sub c ; Le resta la columna actual de la nave
ret z ; Si es la misma columna, sale
call DeleteChar ; Borra la nave de la posición actual
inc c ; Apunta C a la culumna a la izquierda a la actual
ld (shipPos), bc ; Actualiza la posición de la nave
jr moveShip_print ; Salta al final de la rutina
moveShip_right:
ld a, SHIP_TOP_R + $01 ; Carga en A el tope para la nave por la derecha
sub c ; Le resta la columna actual de la nave
ret z ; Si es la misma columna, sale
call DeleteChar ; Borra la nave de la posición actual
dec c ; Apunta C a la culumna a la derecha a la actual
ld (shipPos), bc ; Actualiza la posición de la nave
moveShip_print:
call PrintShip ; Pintamos la nave
ret
Antes de continuar, recordemos que anteriormente comentamos que At alteraba el valor de los resgistros BC y AF pero que no nos afectaba. Ahora que At se llama desde varios puntos, el cambio del registro BC si que nos afecta. La solución es tan sencilla como añadir a At PUSH BC y POP BC para preservar y recuperar el valor de BC, aunque vamos a hacer otra implementación y de paso vamos a ahorrar bytes y ciclos de reloj.
La nueva implementación de At queda de la siguiente manera:
; -----------------------------------------------------------------------------
; Posiciona el cursor en las coordenadas especificadas.
;
; Entrada: B = Coordenada Y (24 a 3).
; C = Coordenada X (32 a 1).
; Altera el valor de los registros AF
; -----------------------------------------------------------------------------
At:
push bc ; Preservamos el valor de BC
exx ; Preservamos el valor de BC, DE y HL
pop bc ; Recuperamos el valor de BC
call $0a23 ; Llama a la rutina de la ROM
exx ; Recuperamos el valor de BC, DE y HL
ret
Preservamos el valor de BC que es donde están las coordenadas, PUSH BC, preservamos el valor de los registros BC, DE y HL intercambiando su valor con los de los registros alternativos, EXX, recuperamos el valor de BC (coordenadas) de la pila, POP BC, y llamamos a la rutina de la ROM que posiciona el cursor, CALL $0A23.
Llegados a este punto, el valor de BC, DE y HL ha cambiado, lo recuperamo desde los registros alternativos, EXX, y salimos, RET.
La rutina ahora ocupa ocho bytes y tarda cincuenta y seis ciclos de reloj en ejecutarse, frente a los diez bytes y noventa ciclos de reloj que ocuparía usando la pila para los tres registros.
Es hora de comprobar si se mueve la nave, volvemos a Main.asm y sustituimos las líneas que hemos añadido antes:
ld bc, (shipPos)
call DeleteChar
call PrintShip
por:
call MoveShip
Compilamos, cargamos en el emulador y vemos los resultados.
La nave se mueve tanto a izquierda como a derecha, pero volvemos a tener el mismo problema que tuvimos en PorompomPong, se mueve extremadamente rápido, más rápido que las palas de Porompompong, ya que las palas se movían píxel a píxel y la nave se mueve carácter a carácter.
Podríamos solucionarlo igual que hicimos entonces, no moviendo la nave en cada iteración del bucle, pero dado que vamos a usar las interrupciones para más cosas, lo vamos a hacer a través de ellas y así vemos algo que no vimos en PorompomPong.
Ensamblador ZX Spectrum, conclusión
Ya tenemos la nave en el área de juego y la movemos, pero hemos observado un problema que ya tuvimos en PorompomPong, se mueve extremadamente rápido.
En el próximo capítulo solucionaremos esto con las interrupciones e implementaremos la parte del disparo.
Puedes descargar desde aquí el código que hemos generado.
Enlaces de interés
- Notepad++
- Visual Studio Code
- Sublime Text
- ZEsarUX
- PASMO
- Git
- Curso de ensamblador Z80 de Compiler Software
- Referencia Z80
- Ensamblador Z80 en Telegram
- Tutorial completo en formato PDF y EPUB
- Proyecto en itch.io
- Archivo .dsk con los juegos de los tutoriales
- Personalización y depuración con ZEsarUX
Ensamblador para ZX Spectrum Batalla espacial por Juan Antonio Rubio García.
Esta obra está bajo licencia de Creative Commons Reconocimiento-NoComercial-CompartirIgual 4.0 Internacional License.
También puedes visitar el resto de tutoriales:
Y recuerda, si lo usas no te limites a copiarlo, intenta entenderlo y adaptarlo a tus necesidades.