0x06 Ensamblador ZX Spectrum Marciano – Enemigos

En este capítulo de Ensamblador ZX Spectrum Marciano vamos a incluir los enemigos. Lo primero es crear la carpeta Paso06, copiamos desde la carpeta Paso05 los archivos Cargador.tap, Const.asm, Ctrl.asm, Game.asm, Graph.asm, Int.asm, Main.asm, Print.asm, Var.asm y make, o make.bat si estáis trabajando en Windows.

Definimos los enemigos

Los enemigos son elementos móviles, y como tales necesitamos saber la posición actual y la inicial. En total vamos a tener un máximo de veinte enemigos en pantalla, y vamos a usar dos bytes para especificar la posición actual del enemigo y alguna configuración más que nos va a hacer falta.

Abrimos el archivo Var.asm y tras la definición del marco de la pantalla, añadimos la configuración de los enemigos.

; -----------------------------------------------------------------------------
; Configuración de los enemigos
;
; 2 bytes por enemigo.
; -----------------------------------------------------------------------------
; Byte 1                        | Byte 2
; -----------------------------------------------------------------------------
; Bit 0-4:  Posición Y          | Bit 0-4:  Posición X
; Bit 5:    Libre               | Bit 5:    Libre
; Bit 6:    Libre               | Bit 6:    Dirección X 0 = Left 1 = Right
; Bit 7:	Activo 1/0          | Bit 7:    Dirección Y 0 = Up   1 = Down
; -----------------------------------------------------------------------------
enemiesConfig:
db $96, $dd, $96, $d7, $96, $d1, $96, $cb, $96, $c5
db $93, $9d, $93, $97, $93, $91, $93, $8b, $93, $85
db $90, $dd, $90, $d7, $90, $d1, $90, $cb, $90, $c5
db $8d, $9d, $8d, $97, $8d, $91, $8d, $8b, $8d, $85
enemiesConfigIni:
db $96, $dd, $96, $d7, $96, $d1, $96, $cb, $96, $c5
db $93, $9d, $93, $97, $93, $91, $93, $8b, $93, $85
db $90, $dd, $90, $d7, $90, $d1, $90, $cb, $90, $c5
db $8d, $9d, $8d, $97, $8d, $91, $8d, $8b, $8d, $85
enemiesConfigEnd:

En el primer byte vamos a tener la coordenada Y, bits del cero al cuatro, y si el enemigo está o no activo, bit siete. En el segundo byte vamos a tener la coordenada X, bits del cero al cuatro, la dirección horizontal, bit seis, y la dirección vertical, bit siete.

Según los bits seis y siete del segundo byte, la dirección del enemigo es:

  • 00b $00 Izquierda / Arriba
  • 01b $01 Izquierda / Abajo
  • 10b $02 Derecha / Arriba
  • 11b $03 Derecha / Abajo

Basándonos en esto, vamos a añadir la definición de los gráficos de los enemigos. Justo antes de enemiesConfig añadimos dicha definición.

; -----------------------------------------------------------------------------
; Gráficos de los enemigos
;
; 00 Up-Left
; 01 Up-Right
; 10 Down-Left
; 11 Down-Right
; -----------------------------------------------------------------------------
enemiesGraph:		
db $9f, $a0, $a1, $a2

Si buscamos los UDG de los enemigos, veremos que todo concuerda.

Volviendo a la definición de los enemigos, vamos a tener un total de veinte enemigos, repartidos en cuatro filas con cinco enemigos por fila.

Vamos a ver la definición de los enemigos de izquierda a derecha y de arriba a abajo; recordad que trabajamos con las coordenadas invertidas.

$96, $dd10010110, 11011101Activo Línea 22 Abajo/Derecha Columna 29
$96, $d710010110, 11010111Activo Línea 22 Abajo/Derecha Columna 23
$96, $d110010110, 11010001Activo Línea 22 Línea/Derecha Columna 17
$96, $cb10010110, 11001011Activo Línea 22 Abajo/Derecha Columna 11
$96, $c510010110, 11000101Activo Línea 22 Abajo/Derecha Columna 5
$93, $9d10010011, 10011101Activo Línea 19 Abajo/Izquierda Columna 29
$93, $9710010011, 10010111Activo Línea 19 Abajo/Izquierda Columna 23
$93, $9110010011, 10010001Activo Línea 19 Abajo/Izquierda Columna 17
$93, $8b10010011, 10001011Activo Línea 19 Abajo/Izquierda Columna 11
$93, $8510010011, 10000101Activo Línea 19 Abajo/Izquierda Columna 5
$90, $dd10010000, 11011101Activo Línea 16 Abajo/Derecha Columna 29
$90, $d710010000, 11010111Activo Línea 16 Abajo/Derecha Columna 23
$90, $d110010000, 11010001Activo Línea 16 Abajo/Derecha Columna 17
$90, $cb10010000, 11001011Activo Línea 16 Abajo/Derecha Columna 11
$90, $c510010000, 11000101Activo Línea 16 Abajo/Derecha Columna 5
$8d, $9d10001101, 10011101Activo Línea 13 Abajo/Izquierda Columna 29
$8d, $9710001101, 10010111Activo Línea 13 Abajo/Izquierda Columna 23
$8d, $9110001101, 10010001Activo Línea 13 Abajo/Izquierda Columna 17
$8d, $8b10001101, 10001011Activo Línea 13 Abajo/Izquierda Columna 11
$8d, $8510001101, 10000101Activo Línea 13 Abajo/Izquierda Columna 5
Ensamblador ZX Spectrum, definición de los enemigos

Una vez definidos los gráficos de los enemigos y su configuración, podemos proceder a pintarlos.

Pintamos los enemigos

La rutina que pinta los enemigos la vamos a implementar en Print.asm.

PrintEnemies:
ld      a, $06                  
call    Ink

ld      hl, enemiesConfig
ld      d, $14

Cargamos en A la tinta amarilla, LD A, $06, cambiamos la tinta, CALL Ink, cargamos la dirección de memoria de los enemigos en HL, LD HL, enemiesConfig, y el número total de enemigos en D, LD D, $14, veinte enemigos.

printEnemies_loop:
bit     $07, (hl)
jr      z, printEnemies_endLoop

Evaluamos si el enemigo está activo, BIT $07, (HL), y de no estarlo saltamos, JR Z, printEnemies_endLoop.

push    hl

ld      a, (hl)
and     $1f
ld      b, a

Preservamos el valor de HL, PUSH HL, cargamos el primer byte de la configuración del enemigo en A, LD A, (HL), nos quedamos con la coordenada Y, AND $1F, y la cargamos en B, LD B, A.

inc     hl
ld      a, (hl)
and     $1f
ld      c, a
call    At

Apuntamos HL al segundo byte de la configuración del enemigo, INC HL, cargamos el valor en A, LD A, (HL), nos quedamos con la coordenada X, AND $1F, cargamos el valor en C, LD C, A, y posicionamos el cursor, CALL At.

ld      a, (hl)
and     $c0
rlca
rlca
ld      c, a
ld      b, $00

Cargamos el segundo byte de la configuración del enemigo en A, LD A, (HL), nos quedamos con los bits de dirección, AND $c0, pasamos el valor a los bits cero y uno, RLCA RLCA, cargamos el valor en C, LD C, A, y ponemos B a cero, LD B, $00.

ld      hl, enemiesGraph
add     hl, bc
ld      a, (hl)
rst     $10

Cargamos en HL la dirección de memoria en la que definimos los caracteres para los enemigos, LD HL, enemiesGraph, le sumamos la dirección (izquierda, arriba …) del enemigo, ADD HL, BC, cargamos en A el carácter del enemigo que hay que pintar, LD A, (HL), y lo pintamos, RST $10.

pop      hl

printEnemies_endLoop:
inc     hl
inc     hl

dec     d
jr      nz, printEnemies_loop

ret

Recuperamos el valor de HL, POP HL, apuntamos HL al primer byte de la configuración del siguiente enemigo, INC HL INC HL, decrementamos D, DEC D, y seguimos hasta que D sea cero y hayamos recorrido todos los enemigos. Finalmente, salimos, RET.

El aspecto final de la rutina es el siguiente:

; -----------------------------------------------------------------------------
; Pinta los enemigos
;
; Altera el valor de los registros AF, BC, D y HL.
; -----------------------------------------------------------------------------
PrintEnemies:
ld      a, $06                      ; Carga en A la tinta amarilla        
call    Ink                         ; Cambia la tinta

ld      hl, enemiesConfig           ; Carga la dirección de la configuración 
                                    ; del enemigo en HL
ld      d, $14                      ; Carga en D 20 enemigos

printEnemies_loop:
bit     $07, (hl)                   ; Evalúa si el enemigo está activo
jr      z, printEnemies_endLoop     ; Si no lo está, salta

push    hl                          ; Preserva el valor de HL

ld      a, (hl)                     ; Carga el primer byte de configuración en A
and     $1f                         ; Se queda con la coordenda Y
ld      b, a                        ; La carga en B

inc     hl                          ; Apunta HL al segundo byte
ld      a, (hl)                     ; Carga el valor en A
and     $1f                         ; Se queda con la coordenada X
ld      c, a                        ; La carga en C
call    At                          ; Posiciona el cursor

ld      a, (hl)                     ; Vuelve a cargar el segudo byte en A
and     $c0                         ; Se queda con la dirección (izquierda ...)
rlca                                ; Pone el valor en los bits 0 y 1
rlca
ld      c, a                        ; Carga el valor en C
ld      b, $00                      ; Pone B a cero

ld      hl, enemiesGraph            ; Carga en HL el carácter del gráfico del enemigo
add     hl, bc                      ; Le suma la dirección de enemigo (izquierda ...)
ld      a, (hl)                     ; Carga en A el gráfico del enemigo
rst     $10                         ; Lo pinta

pop      hl                         ; Recupera el valor de HL

printEnemies_endLoop:
inc     hl                          ; Apunta HL al primer byte de la configuración
inc     hl                          ; del enemigo siguientes

dec     d                           ; Decrementa D
jr      nz, printEnemies_loop       ; hasta que D sea 0

ret

Para probar si funciona, vamos a ir a Main.asm y justo antes de Main_loop vamos a añadir las líneas siguientes:

ld      a, $01
call    LoadUdgsEnemies
call    PrintEnemies

Cargamos el nivel uno en A, LD A, $01, cargamos los gráficos de los enemigos del nivel uno en udgsExtension, CALL LoadUdgsEnemies, y pintamos los enemigos, CALL PrintEnemies.

Compilamos, cargamos en el emulador y vemos los resultados.

Ensamblador ZX Spectrum, enemigos
Ensamblador ZX Spectrum, enemigos

Movemos los enemigos

Los enemigos no los vamos a mover en cada iteración del bucle, como hacemos con el disparo, los vamos a mover cada N interrupciones, por lo qué lo primero que vamos a hacer es añadir otro comentario en la etiqueta flag, al inicio de Main.asm.

; Bit 2 -> se deben mover los enemigos      0 = No, 1 = Sí

Lo siguiente es establecer los límites de la pantalla hasta dónde los enemigos pueden llegar; estos límites los vamos a establecer en Const.asm.

; Topes de los enemigos
ENEMY_TOP_T:   EQU COR_Y - MIN_Y
ENEMY_TOP_B:   EQU COR_Y - MAX_Y + $01
ENEMY_TOP_L:   EQU COR_X - MIN_X
ENEMY_TOP_R:   EQU COR_X - MAX_X

Los límites que hemos establecido son arriba, abajo, izquierda y derecha.

Para que los enemigos se muevan cada N interrupciones, y como en flags vamos a usar un bit para indicar si se deben mover o no, vamos a ir al archivo Int.asm y vamos a activar dicho bit, para lo que añadimos la siguiente línea justo debajo de SET $00, (HL):

set     $02, (hl)

Ya tenemos todos listo para poder implementar en Game.asm la rutina que mueve los enemigos.

MoveEnemies:
ld      hl, flags
bit     $02, (hl)
ret     z
res     $02, (hl)

Cargamos en HL la dirección de memoria de flags, LD HL, flags, comprobamos si el bit de movimiento de los enemigos está activo, BIT $02, (HL), y si no está activo, salimos, RET Z. En el caso de estar activo, lo desactivamos para que no pase por aquí en la próxima iteración de Main_loop, RES $02, (HL).

ld      d, $14
ld      hl, enemiesConfig

moveEnemies_loop:
bit     $07, (hl)
jr      z, moveEnemies_endLoop

Cargamos en D el número total de enemigos (veinte), LD D, $14, cargamos en HL la dirección de memoria de la configuración de los enemigos, LD HL, enemiesConfig, comprobamos si el enemigo está activo, BIT $07, (HL), y si no está activo saltamos al final del bucle, JR Z, moveEnemies_endLoop.

push    hl

ld      a, (hl)
and     $1f
ld      b, a

inc     hl
ld      a, (hl)
and     $1f
ld      c, a

call    DeleteChar

pop     hl

Preservamos el valor de HL, PUSH HL, cargamos en A el primer byte de la configuración del enemigo, LD A, (HL), nos quedamos con la coordenada Y, AND $1F, y la cargamos en B, LD B, A.

Apuntamos HL al segundo byte de la configuración del enemigo, INC HL, cargamos el valor en A, LD A, (HL), nos quedamos con la coordenada X, AND $1F, y la cargamos en C, LD C, A.

Borramos el enemigo, CALL DeleteChar, y recuperamos el valor de HL, POP HL.

ld      b, (hl)
inc     hl
ld      c, (hl)

Cargamos el primer byte de la configuración del enemigo en B, LD B, (HL), apuntamos HL al segundo byte de la configuración, INC HL, y cargamos el valor en C, LD C, (HL).

moveEnemies_X:
ld      a, c
and     $1f

bit     $06, c
jr      nz, moveEnemies_X_right

Cargamos el valor del segundo byte de la configuración del enemigo en A, LD A, C, y nos quedamos con la coordenada X, AND $1F.

Comprobamos el bit de dirección horizontal del enemigo, BIT $06, C, si está a uno, el enemigo se desplaza hacia la derecha y salta, JR Z, moveEnemies_X_right. Si no ha saltado, el enemigo se mueve hacia la izquierda.

moveEnemies_X_left:
inc     a
sub     ENEMY_TOP_L
jr      z, moveEnemies_X_leftChg

inc     c
jr      moveEnemies_Y

moveEnemies_X_leftChg:
set     $06, c
jr      moveEnemies_Y

Incrementamos A para que apunte a la columna a la izquierda de la actual, INC A, restamos el tope por la izquierda, SUB ENEMY_TOP_L, y si el resultado es cero, ha llegado al tope y salta para cambiar la dirección, JR Z, moveEnemies_X_leftChg.

Si no hay que cambiar la dirección, apunta C a la columna a la izquierda de la actual, INC C, y salta al movimiento vertical, JR moveEnemies_Y.

Si hay que cambiar la dirección, activa el bit seis de C para poner dirección horizontal hacia la derecha, SET $06, C, y salta al movimiento vertical, JR moveEnemies_Y.

Si el enemigo no se mueve hacia la izquierda, se mueve hacia la derecha.

moveEnemies_X_right:
dec     a
sub     ENEMY_TOP_R
jr      z, moveEnemies_X_rightChg

dec     c
jr      moveEnemies_Y

moveEnemies_X_rightChg:
res     $06, c

Decrementamos A para que apunte a la columna a la derecha de la actual, DEC A, restamos el tope por la derecha, SUB ENEMY_TOP_R, y si el resultado es cero, ha llegado al tope y salta para cambiar la dirección, JR Z, moveEnemies_X_rightChg.

Si no hay que cambiar la dirección, apunta C a la columna a la derecha de la actual, DEC C, y salta al movimiento vertical, JR moveEnemies_Y.

Si hay que cambiar la dirección, desactiva el bit seis de C para poner dirección horizontal hacia la izquierda, RES $06, C, y empezamos con el movimiento vertical.

moveEnemies_Y:
ld      a, b
and     $1f
bit     $07, c
jr      nz, moveEnemies_Y_down

Cargamos en A el valor del primer byte de la configuración del enemigo, LD A, B, y nos quedamos con la coordenada Y, AND $1F.

Comprobamos el bit siete de C para saber la dirección vertical del enemigo, BIT $07, C, y si está a uno el enemigo se mueve hacia abajo y salta, JR NZ, moveEnemies_Y_down.

Si el bit está a cero, el enemigo se mueve hacia arriba.

moveEnemies_Y_up:
inc     a
sub     ENEMY_TOP_T
jr      z, moveEnemies_Y_upChg

inc     b
jr      moveEnemies_endMove

moveEnemies_Y_upChg:
set     $07, c
jr      moveEnemies_endMove

Incrementamos A para que apunte a la línea superior a la actual, INC A, restamos el tope por arriba, SUB ENEMY_TOP_T, y si el resultado es cero, hemos llegado al tope y saltamos para cambiar la dirección, JR Z, moveEnemies_Y_upChg.

Si no hemos llegado al tope, incrementamos B para que apunte a la línea superior a la actual, INC B, y saltamos al final del bucle, JR moveEnemies_endMove.

Si hay que cambiar la dirección, activamos el bit siete de C para cambiar la dirección hacia abajo, SET $07, C, y saltamos al final del bucle, JR moveEnemies_endMove.

Si el enemigo no se mueve hacia arriba, se mueve hacia abajo.

moveEnemies_Y_down:
dec     a
sub     ENEMY_TOP_B
jr      z, moveEnemies_Y_downChg

dec     b
jr      moveEnemies_endMove

moveEnemies_Y_downChg:
res     $07, c

Decrementamos A para que apunte a la línea inferior a la actual, DEC A, restamos el tope por abajo, SUB ENEMY_TOP_B, y si el resultado es cero, hemos llegado al tope y saltamos para cambiar la dirección, JR Z, moveEnemies_Y_downChg.

Si no hemos llegado al tope, decrementamos B para que apunte a la línea inferior a la actual, DEC B, y saltamos al final del bucle, JR moveEnemies_endMove.

Si hay que cambiar la dirección, desactivamos el bit siete de C para cambiar la dirección hacia arriba, RES $07, C.

moveEnemies_endMove:
ld      (hl), c
dec     hl
ld      (hl), b

moveEnemies_endLoop:
inc     hl
inc     hl
dec     d
jr      nz, moveEnemies_loop

Actualizamos en memoria el segundo byte de la configuración del enemigo, LD (HL), C, apuntamos HL al primer byte, DEC HL, y actualizamos en memoria, LD (HL), B.

Apuntamos HL al primer byte de la configuración del siguiente enemigo, INC HL, INC HL, decrementamos D, DEC D, y seguimos en el bucle hasta que D sea cero y hayamos recorrido los veinte enemigos, JR Z, moveEnemies_loop.

moveEnemies_end:
call    PrintEnemies

ret

Pintamos los enemigos en las nuevas posiciones, CALL PrintEnemies, y salimos, RET.

Con esto, hemos implementado la rutina que mueve los enemigos, cuyo aspecto final es el siguiente:

; -----------------------------------------------------------------------------
; Mueve los enemigos.
;
; Altera el valor de lo registros AF, BC, D y HL.
; -----------------------------------------------------------------------------
MoveEnemies:
ld      hl, flags           ; Cargamos la dirección de memoria de flags en HL
bit     $02, (hl)           ; Comprueba si el bit 2 está activo
ret     z                   ; Si no es así, sale                
res     $02, (hl)           ; Desactiva el bit 2 de flags

ld      d, $14              ; Carga en D el número total de enemigos (20)
ld      hl, enemiesConfig   ; Cara en HL la dirección de la configuración 
                            ; de los enemigos
moveEnemies_loop:
bit     $07, (hl)           ; Comprueba si el enemigo está activo
jr      z, moveEnemies_endLoop  ; Si no lo está, salta a final del bucle

push    hl                  ; Preserva el valor de HL

ld      a, (hl)             ; Carga en A el primer byte de la cofiguración
and     $1f                 ; Se queda con la coordenada Y
ld      b, a                ; Carga el valor en B

inc     hl                  ; Apunta HL al segundo byte de la configuración
ld      a, (hl)             ; Carga el valor en A
and     $1f                 ; Se queda con la coordeanda X
ld      c, a                ; Carga el valor en C

call    DeleteChar          ; Borra el enemigo

pop     hl                  ; Recupera el valor de HL

ld      b, (hl)             ; Carga en B el primer byte de la configuración
inc     hl                  ; Apunta HL al segundo byte de la configuración
ld      c, (hl)             ; Carga en C el segundo byte de la configuración

moveEnemies_X:
ld      a, c                ; Carga en A el segundo byte de la configuración
and     $1f                 ; Se queda con la coordeada X

bit     $06, c              ; Evalúa la dirección horizontal del enemigo
jr      nz, moveEnemies_X_right ; Si está a uno, hacia la derecha, salta

moveEnemies_X_left:
inc     a                   ; Apunta A a la columna anterior 
sub     ENEMY_TOP_L         ; Resta el tope por la izquierda
jr      z, moveEnemies_X_leftChg    ; Si es cero, ha llegado al tope, salta

inc     c                   ; Apunta C a la columna anterior
jr      moveEnemies_Y       ; Salta al movimiento vertical 

moveEnemies_X_leftChg:
set     $06, c              ; Pone la dirección horizontal hacia la derecha
jr      moveEnemies_Y       ; Salta al movimiento vertical

moveEnemies_X_right:
dec     a                   ; Apunta A a la columna posterior
sub     ENEMY_TOP_R         ; Resta el tope por la derecha
jr      z, moveEnemies_X_rightChg   ; Si es cero, ha llegado al tope, salta

dec     c                   ; Apunta C a la columna posterior
jr      moveEnemies_Y       ; Salta al movimiento vertical

moveEnemies_X_rightChg:
res     $06, c              ; Pone la dirección horizontal hacia la izquierda

moveEnemies_Y:
ld      a, b                ; Carga en A el primer byte de la configuración
and     $1f                 ; Se qued con la coordenda Y
bit     $07, c              ; Evalúa la dirección vertical del enemigo
jr      nz, moveEnemies_Y_down  ; Si está a uno, hacia abajo, salta

moveEnemies_Y_up:
inc     a                   ; Apunta A a la línea anterior
sub     ENEMY_TOP_T         ; Resta el tope por arriba
jr      z, moveEnemies_Y_upChg  ; Si es cero, ha llegado al tope, salta

inc     b                   ; Apunta B a la línea posterior
jr      moveEnemies_endMove ; Salta al final del bucle

moveEnemies_Y_upChg:
set     $07, c              ; Pone la dirección vertical hacia abajo
jr      moveEnemies_endMove ; Salta al final del bucle

moveEnemies_Y_down:
dec     a                   ; Apunta A a la línea posterior
sub    ENEMY_TOP_B         ; Resta el tope por abajo
jr      z, moveEnemies_Y_downChg    ; Si es cero, ha llegado al tope, salta

dec     b                   ; Apunta B a la línea posterior
jr      moveEnemies_endMove ; Salta al final del bucle

moveEnemies_Y_downChg:
res     $07, c              ; Pone la dirección vertical hacia arriba

moveEnemies_endMove:
ld      (hl), c             ; Actualiza el segundo byte de la configuración
dec     hl                  ; Apunta HL al primer byte de la configuración
ld      (hl), b             ; Actualiza el primer byte de la configuración

moveEnemies_endLoop
inc     hl
inc     hl                  ; Aputa HL al primer byte de la configuración
                            ; del siguiente enemigo
dec     d                   ; Decrementa D
jr      nz, moveEnemies_loop    ; Hasta que D sea cero (20 enemigos)

moveEnemies_end:
call    PrintEnemies        ; Pinta los enemigos

ret

Ha llegado el momento de ver como se mueven los enemigos. Abrimos el archivo Main.asm y en la etiqueta Main_loop, justo debajo de CALL MoveShip, añadimos la línea siguiente:

call    MoveEnemies

Compilamos, cargamos en el emulador y vemos los resultados.

Ensamblador ZX Spectrum, enemigos
Ensamblador ZX Spectrum, enemigos

¿Qué tal? ¿Se mueven los enemigos? Sí, se mueven, pero van demasiado deprisa y el disparo se ha vuelto más lento. Vamos a ralentizar el movimiento de los enemigos, y lo vamos hacer desde la rutina de la interrupciones, así que vamos al archivo Int.asm.

Vamos a hacer algo parecido a lo que hicimos en PorompomPong, vamos a añadir un contador al final del archivo para controlar cuando activamos el movimiento de los enemigos.

countEnemy: db $00

Ahora entre las líneas SET $00, (HL) y SET $02, (HL) vamos a implementar el uso de este contador.

ld      a, (countEnemy)
inc     a
ld      (countEnemy), a
sub     $03
jr      nz, Isr_end
ld      (countEnemy), a

Cargamos en A el valor del contador, LD A, (countEnemy), incrementamos A, INC A, y actualizamos el contador en memoria, LD (countEnemy), A. Restamos a A el valor que tiene que alcanzar el contador para activar el movimiento, SUB $03, y si no ha llegado salta al final de la rutina, JR NZ, Isr_end.

Si ya ha alcanzado el valor, pone a cero el contador, LD (countEnemy), A, y activa el bit para mover los enemigos, SET $02, (HL).

Compilamos, cargamos en el emulador y vemos que hemos recuperado la velocidad del disparo y que los enemigos se siguen moviendo rápido.

Ensamblador ZX Spectrum, conclusión

Ya tenemos en movimiento todos los elementos de nuestro juego.

En el siguiente capítulo implementaremos la detección de colisiones, para poder matar a los enemigos y que ellos nos maten a nosotros, y poder ir cambiando de nivel, hasta un total de treinta.

Podéis descargar todo el código generado desde aquí.

Enlaces de interés

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.

Aquí puedes ver más cosas que he desarrollado para .Net, y aquí las desarrolladas en ensamblador para Z80.

Y recuerda, si lo usas no te limites a copiarlo, intenta entenderlo y adaptarlo a tus necesidades.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

ACEPTAR
Aviso de cookies
A %d blogueros les gusta esto: