Curso: Dominando Ensamblador Z80, para ZX Spectrum - Obijuan/Learn-zx-spectrum-asm GitHub Wiki
El curso DEZ80, de Fran Gallego está realizado sobre un Amstrad CPC. Voy a re-hacer los retos planteados pero aplicados al ZX-Spectrum
Contenido
- Nivel-1
- Reto 1: Pinta un pixel en pantalla
- Reto 1-2: Pinta un pixel negro en (1-0)
- Reto 1-3: Pinta un pixel rojo en (7,0)
- Reto 1-4: Pinta cuatro pixeles rojos en (1,0), (3,0), (5,0) y (7,0)
- Reto 1-5: Linea horizontal de 16 pixeles
- Reto 1-6: Lineas rojas en izquierda, centro y derecha
- Reto 1-7: Lineas rojas debajo de B, H y o
- Reto 1-8: Lineas rojas encima de B, H y o (linea 1)
- Reto 1-9: Linea roja vertical de 4 pixeles en posicion (x=7, y=0)
- Reto 1-10: Bandera cyan-amarilla de 8x4
- Reto 1-11: sprite 8x8 en posicion (0,0)
- Reto 1-12: sprite 8x8: Dado, en posicion (x=10, y=15)
- Reto 1-13: sprite 16x16 en posicion (x=10, y=15)
- Nivel 2
- Reto 2.1: Animar un pixel
- Reto 2.2: Animar un pixel (8 posiciones)
- Reto 2.3: Animar una barra de progreso (8 posiciones)
- Reto 2.4: Animar un sprite de 8x8
- Reto 2.5: Disparo laser con avance de 8 píxeles
- Reto 2.6: Barra de progreso de 40 bits
- Experimentos
- [Reto 2.7: Trampa](#reto-27-trampa
- Reto 2.8: Pelota con gravedad
Nivel 1
En esta wiki están los Retos originales del Nivel 1
Reto 1: Pinta un pixel en pantalla
Los pixeles se pintan de 8 en 8 accediendo a la memoria de vídeo, que comienza en posición 0x4000. Si escribimos en esa dirección, los bits a 1 se visualizan y los bits a 0 no. Por eso, para mostrar el primer pixel, el de la posición 0,0 hay que escribir el valor 0x80 en 0x4000
El color del pixel es NEGRO porque son los atributos por defecto
- 18-reto1-pixel-negro.asm
;--------------------------------------------
;-- Reto 1: Pixel negro en posicion (0,0)
;--------------------------------------------
org $8000
;-- Poner borde amarillo para ver bien la posicion del pixel
;-- (y comprobar que está en 0,0)
ld a, 6
out ($FE), a
;-- Escribir valor 0x80 en direccion 0x4000
ld a,$80
ld ($4000),a
;-- Bucle infinito
inf:
jr inf
end $8000
Si aumentamos el area vemos que efectivamente está en la posición 0,0
Reto 1-2: Pinta un pixel negro en (1,0)
Si queremos pintar un pixel en la posición siguiente, la dirección de memoria es la misma: En la dirección 0x4000 se guarda un paquete con los 8 primeros píxeles. Para mostrar el pixel del a posicion x=1, hay que guardar el valor 0x40. En binario es: 01000000
. Es decir, sólo es segundo pixel, comenzando por la izquierda, estará encendido. El resto apagados
- 19-reto-1-2-pixel-negro.asm
;--------------------------------------------
;-- Reto 1: Pixel negro en posicion (1,0)
;--------------------------------------------
org $8000
;-- Poner borde amarillo para ver bien la posicion del pixel
;-- (y comprobar que está en 0,0)
ld a, 6
out ($FE), a
;-- Escribir valor 0x40 en direccion 0x4000
ld a,$40
ld ($4000),a
;-- Bucle infinito
inf:
jr inf
end $8000
En la imagen ampliada comprobamos que efectivamente el pixel está encendido en la posición siguiente a la 0 (la del anterior reto)
Reto 1-3: Pinta un pixel rojo en (7,0)
En este reto hay que resolver dos cosas... por un lado hay que pintar un pixel negro en la posición 7. Dentro de la posición de memoria 0x4000 hay 8 pixeles, uno por bit. El número 0x80 se corresponde con el pixel de la izquierda (posicion 0). El número 0x40 con el pixel de la posición 1... y así sucesivamente hasta llegar al valor 0x01 que se corresponde con el pixel de la posición 7
Por tanto, hay que escribir el valor 0x01 en la direción 0x4000. Esto nos dibuja un pixel negro
El siguiente paso es modificar sus atributos que se encuentra en una dirección diferente: 0x5800. Ahí tendremos que escribir el atributo que tiene el papel blanco y la tinta roja: 00111010: el número 0x3a
- 20-reto-1-3-pixel-rojo.asm
;--------------------------------------------
;-- Reto 1-3: Pixel rojo en posicion (7,0)
;--------------------------------------------
org $8000
;-- Poner borde amarillo para ver bien la posicion del pixel
;-- (y comprobar que está en 0,0)
ld a, 6
out ($FE), a
;-- Escribir valor 0x01 en direccion 0x4000
ld a,$01
ld ($4000),a
;-- Cambiar sus atributos al color rojo
;-- Papel: blanco (111)
;-- Tinta: roja (002)
;-- Valor atributo: 00111010 = $40
;-- Direccion memoria atributos: $5800
ld a, $3a
ld ($5800), a
;-- Bucle infinito
inf:
jr inf
end $8000
Reto 1-4: Pinta cuatro pixeles rojos en (1,0), (3,0), (5,0) y (7,0)
Como la posición x de todos los píxeles es menor a 8, están todos en el mismo bloque situado en la dirección 0x4000. Por tanto, los 4 píxeles se pintan todos a la vez escribiendo este valor binario: 01010101 (es decir, el valor 0x55)
Además hay que escribir el valor 0x3a para establecer el atributo de color rojo (sobre fondo blanco)
- 21-Reto-1-4-pixels-x1-3-5-7-rojo.asm
;---------------------------------------------------------------------
;-- Reto 1-4: Pintar 4 pixeles rojos en (1,0), (3,0), (5,0) y (7,0)
;---------------------------------------------------------------------
org $8000
;-- Poner borde amarillo para ver bien la posicion del pixel
;-- (y comprobar que está en 0,0)
ld a, 6
out ($FE), a
;-- Escribir valor 0x55 en direccion 0x4000
ld a,$55
ld ($4000),a
;-- Cambiar sus atributos al color rojo
;-- Papel: blanco (111)
;-- Tinta: roja (002)
;-- Valor atributo: 00111010 = $40
;-- Direccion memoria atributos: $5800
ld a, $3a
ld ($5800), a
;-- Bucle infinito
inf:
jr inf
end $8000
Reto 1-5: Linea horizontal de 16 pixeles
- Reto: Dibujar una línea de 16 pixeles de anchura, que vaya desde la posicion (0,0) hasta (15,0). La primera mitad de color rojo y la otra mitad amarilla (dejar el fondo azul)
Una linea de 8 pixeles consecuivos se corresponde con el valor binario: 11111111, es decir, $FF. Este valor hay que escribirlo en la dirección 0x4000 y en 0x5800 sus atributos correspondientes
Para pintar en los pixeles que están a la derecha hay que hacerlo en la posición de memoria siguiente: 0x4001 y sus atributos en 0x5801
- 22-Reto-1-5-linea-16.asm
;---------------------------------------------------------------------
;-- Reto 1-5: Pintar linea horizontal de 16 pixeles y dos colores
;-- (rojo y amarillo)
;---------------------------------------------------------------------
org $8000
;-- Poner borde azul
ld a, 1
out ($FE), a
;-- Dibujar primer mitad de la linea (8 bits)
ld a,$ff
ld ($4000),a
;-- Dibujar segunda mita de la linea (8 bits)
ld ($4001),a
;-- Primer segmento rojo (sobre fondo azul)
ld a, $0a
ld ($5800), a
;-- Segundo segmento amarillo (fondo azul)
ld a, $0E
ld ($5801), a
;-- Bucle infinito
inf:
jr inf
end $8000
Este código se puede hacer más compacto escribiendo los valores de dos en dos. Usamos el regitro hl para escribir de un solo golpe los dos bytes consecutivos
;---------------------------------------------------------------------
;-- Reto 1-5: Pintar linea horizontal de 16 pixeles y dos colores
;-- (rojo y amarillo)
;---------------------------------------------------------------------
org $8000
;-- Poner borde azul
ld a, 1
out ($FE), a
;-- Dibujar las dos mitadas. L=bits de 0x4000. H=bits de 0x4001
ld hl,$ffff
ld ($4000),hl
;-- Almacenar los dos atributos de un golpe
;-- L->Atributo de la primera mitad: Rojo sobre fondo azul
;-- H->Atributo de la segunda mitad: Amarillo sobre fondo azul
ld hl, $0e0a
ld ($5800), hl
;-- Bucle infinito
inf:
jr inf
end $8000
Reto 1-6: Lineas rojas en izquierda, centro y derecha
- Reto: Pintar 3 líneas de 8 bits en las posiciones de caracteres (0,0), (15,0) y (31,0)
El objetivo es comprener cómo se codifican las coordenadas x en las direcciones de la memoria de vídeo
- 24-reto-1-6-tres-lineas.asm
;---------------------------------------------------------------------------
;-- Reto 1-6: Pintar tres líneas de 8 bits en las posiciones (de caracteres)
;-- (0,0), (15, 0) y (31,0)
;---------------------------------------------------------------------------
org $8000
;-- Poner borde negro
ld a, 0
out ($FE), a
;-- Linea de la izquierda (0,0)
ld a,$FF
ld ($4000),a
;-- Linea central (15,0)
;-- La dirección es 0x4000 + F
ld ($400F),a
;-- Linea de la derecha (31,0)
;-- La direccion es 0x4000 + 0x1F
ld ($401F),a
;-- Atributos
ld a,$3a
ld ($5800), a ;-- Linea izquierda
ld ($580F),a ;-- Linea central
ld ($581F), a ;-- Linea derecha
;-- Bucle infinito
inf:
jr inf
end $8000
Reto 1-7: Lineas rojas debajo de B, H y o
- Reto: Pintar 3 líneas rojas de 8 bits en la línea 2, debajo de los caracteres B, H y o
Las líneas se encuentran en las posiciones de caracteres siguientes (x,y): (0,2), (7,2) y (15,2)
La codificacion de la línea está en el byte bajo de la dirección de memoria, en los 3 bits de mayor peso: LLLCCCCC
- 25-reto-1-7-tres-lineas-B-H_o.asm
;---------------------------------------------------------------------------
;-- Reto 1-7: Pintar 3 líneas rojas de 8 bits en la línea 2, debajo de los
;-- caracteres B, H y o
;---------------------------------------------------------------------------
org $8000
;-- Poner borde negro
ld a, 0
out ($FE), a
;-- Linea debajo de B: (0,2)
;-- LLLCCCCC = 010 00000 (Y=2, x=0)
ld a, $FF
ld ($4040),a
;-- Linea debajo de H: (7,2)
;-- LLLCCCCC = 010 00111 = 0100 0111 = 47
ld ($4047),a
;-- Linea debajo de o: (15,2)
;-- LLLCCCCC = 010 01111 = 0100 1111 = 4F
ld ($404F),a
;-- Atributos
ld a,$3a
ld ($5840), a ;-- Linea B
ld ($5847), a ;-- Linea H
ld ($584F), a ;-- Linea o
;-- Bucle infinito
inf:
jr inf
end $8000
Reto 1-8: Lineas rojas encima de B, H y o (linea 1)
Es igual que el reto anterior, pero posicionando las líneas en la fila 1 (en vez de la 2)
- 26-reto-1-8-tres-lineas-B-H-o.asm
;---------------------------------------------------------------------------
;-- Reto 1-8: Pintar 3 líneas rojas de 8 bits en la línea 2, encima de los
;-- caracteres B, H y o
;---------------------------------------------------------------------------
org $8000
;-- Poner borde negro
ld a, 0
out ($FE), a
;-- Linea encima de B: (0,1)
;-- LLLCCCCC = 001 00000 (Y=1, x=0)
ld a, $FF
ld ($4020),a
;-- Linea encima de H: (7,1)
;-- LLLCCCCC = 001 00111 = 0010 0111 = 27
ld ($4027),a
;-- Linea encima de o: (15,1)
;-- LLLCCCCC = 001 01111 = 0010 1111 = 2F
ld ($402F),a
;-- Atributos
ld a,$3a
ld ($5820), a ;-- Linea B
ld ($5827), a ;-- Linea H
ld ($582F), a ;-- Linea o
;-- Bucle infinito
inf:
jr inf
end $8000
Hay que observar cómo las letras B, H y o ahora se ponen de color rojo. Esto es debido a que los atributos son comunes para todos los píxeles de cada celda de 8x8
Reto 1-9: Linea roja vertical de 4 pixeles en posicion (x=7, y=0)
Cuando estamos dentro de una línea (hay 21 líneas...) la altura se controla con el scanline. El byte alto tiene el formato 010TTSSS. Como estamos en el primer tercio, TT=00. Los cuatro píxeles de altura se encuentran en los scanlines SSS=000, 001, 010 y 011. Eso nos da los valores para el byte alto de 40, 41, 42 y 43. El byte bajo es siempre 00
- 27-reto-1-9-linea-vertical-4.asm
;---------------------------------------------------------------------------
;-- Reto 1-9: Pintar una línea vertical de 4 pixeles
;---------------------------------------------------------------------------
org $8000
;-- Poner borde negro
ld a, 0
out ($FE), a
;-- La linea la ponemos en la posicion (7,0), para que se vea mejor
ld a,$01
ld ($4000),a ;-- Primer pixel
;--- Segundo pixel: (7,1)
;--- Byte bajo: LLLCCCCC = 00000000 = 00
;--- Byte alto: 010TTSSS = 01000001 = 41
ld ($4100), a
;--- Tercer pixel: (7,2)
;-- Mismo byte bajo: 00
;-- Byte alto: 01000010 = 42
ld ($4200), a
;-- Cuarto pixel: (7,3)
;-- Mismo byte bajo: 00
;-- Byte alto: 01000011 = 42
ld ($4300), a
;-- Atributos
ld a,$3a
ld ($5800), a
;-- Bucle infinito
inf:
jr inf
end $8000
Reto 1-10: Bandera cyan-amarilla de 8x4
En este reto se dibuja el primer sprite... muy simple
- 28-reto-1-10-bandera.asm
;---------------------------------------------------------------------------
;-- Reto 1-10: Bandera cyan-amarilla, de 8x4
;---------------------------------------------------------------------------
org $8000
;-- El sprite a dibujar ocupa 4bits de la primera celda, con atributos
;-- de tinta cyan y fondo blanco y 4 bits de la segunda celda, con
;-- atributos de tinta amarilla y fondo blanco
;-- Este es el sprite en blanco y negro:
;--
;-- Celda 0 Celda 1
;--
;-- 00001111 11110000 = 0F F0
;-- 00001111 11110000 = 0F F0
;-- 00001111 11110000 = 0F F0
;-- 00001111 11110000 = 0F F0
;-- El byte bajo de las direcciones (LLLCCCCC) es 00 ya que es la primera
;-- linea (0) y columnas 0 y 1
;-- El byte alto es: 01000SSS, con scanlines de 000,001,010,011
;-- Direcciones:
;-- $4000 $4001
;-- $4100 $4101
;-- $4200 $4200
;-- $4300 $4300
ld hl, $F00F
ld ($4000), hl
ld ($4100), hl
ld ($4200), hl
ld ($4300), hl
;-- Atributos. Parte cyan
;-- 00 111 101 = 0011 1101 = 3D
;-- Atributos. Parte amarilla
;-- 00 111 110 = 0011 1110 = 3E
ld hl,$3e3d
ld ($5800), hl
;-- Bucle infinito
inf:
jr inf
end $8000
Reto 1-11: sprite 8x8 en posicion (0,0)
Llega el momento de hacer el primer sprite. Comenzamos por uno pequeño de 8x8. Para dibujarlo podemos usar inkscape, activando el snap a las guías:
Los valores que definen este sprite son:
- 01111100 = 7C
- 10000010 = 82
- 10101010 = AA
- 10101010 = AA
- 10000010 = 82
- 10000010 = 82
- 01111100 = 7C
- 00000000 = 00
Esta es la otra versión, similar pero con relleno:
Valores:
-
0111 1100 = 7C
-
1111 1110 = FE
-
1101 0110 = D6
-
1101 0110 = D6
-
1111 1110 = FE
-
1111 1110 = FE
-
0111 1100 = 7C
-
0000 0000 = 00
-
29-Reto-1-11-sprite-cocoball-2.asm
;---------------------------------------------------------------------------
;-- Reto 1-10: Bandera cyan-amarilla, de 8x4
;---------------------------------------------------------------------------
;-- Sprite: Cocoball-1
;--
;-- 01111100 = 7C
;-- 10000010 = 82
;-- 10101010 = AA
;-- 10101010 = AA
;-- 10000010 = 82
;-- 10000010 = 82
;-- 01111100 = 7C
;-- 00000000 = 00
;-- Sprite: Cocoball-2
;--
;-- 0111 1100 = 7C
;-- 1111 1110 = FE
;-- 1101 0110 = D6
;-- 1101 0110 = D6
;-- 1111 1110 = FE
;-- 1111 1110 = FE
;-- 0111 1100 = 7C
;-- 0000 0000 = 00
org $8000
;--- Dibujar el Sprite en la posicion (0,0)
ld a, $7C
ld ($4000), a
ld a, $82
ld ($4100), a
ld a, $AA
ld ($4200), a
ld ($4300), a
ld a, $82
ld ($4400), a
ld ($4500), a
ld a,$7C
ld ($4600), a
ld a,$00
ld ($4700), a
;-- Dibujar otro Sprite (cocoball-2) en la posicion (2,0)
ld a, $7C
ld ($4002), a
ld a, $FE
ld ($4102), a
ld a, $D6
ld ($4202), a
ld ($4302), a
ld a, $FE
ld ($4402), a
ld ($4502), a
ld a,$7C
ld ($4602), a
ld a,$00
ld ($4702), a
;-- Atributos. Fondo blanco, color azul
;-- 00 111 001 = 0011 1001 = 39
ld a, $39
ld ($5800),a
ld ($5802),a
;-- Bucle infinito
inf:
jr inf
end $8000
Reto 1-12: sprite 8x8: Dado, en posicion (x=10, y=15)
En este reto se trata de hacer el mismo sprite del curso DEZ80 del profesor Fran Gallego, pero para el Spectrum. Esta es la imagen:
Se puede implementar de varias formas, pero voy a usar la tinta para el cuerpo del dado y el fondo para los puntos del número 5. Tinta amarilla y papel rojo
En realidad, est dado es de 8x7. Para poder usar 2 colores hay que rellenar todo el fondo, por lo que añadimos una línea más en la parte inferior
- 30-reto-1-12-dado.asm
;---------------------------------------------------------------------------
;-- Reto 1-12: Sprite de un dado, con el numero 5 (de 8x8), en posicion 10,15
;---------------------------------------------------------------------------
;-- Sprite: Dado, 5
;--
;-- 11111111 = FF
;-- 10011001 = 99
;-- 11111111 = FF
;-- 11100111 = E7
;-- 11111111 = FF
;-- 10011001 = 99
;-- 11111111 = FF
;-- 11111111 = FF
;--
;-- Atributos: fondo rojo (010). Tinta amarilla (110)
;-- 00 010 110 = 0001 0110 = 16
;--
;-- Dir atributos: 5800 + LLLLLCCCCC
;-- Linea 10: 01010 01111 -> 01 0100 1111 = 14F
;-- Col 15:
;-- Dir: 5800 + 14F = 594F
;-- Posicion 10,15:
;-- Linea: 10 --> 01010 (TTLLL) T=01, LLL=010
;-- Colu 15 ---> CCCCC = 01111
;-- Byte bajo: LLLCCCCC = 01001111 = 0100 1111 = 4F
;-- Byte alto: 010TTSSS = 01001000 = 0100 1000 = 48
;-- Direcciones: 4800, 4900, 4A00, 4B00, 4C00, 4D00, 4E00, 4F00
org $8000
;--- Dibujar el Sprite
ld a, $FF
ld ($484F), a
ld a, $99
ld ($494F), a
ld a, $FF
ld ($4A4F), a
ld a, $E7
ld ($4B4F), a
ld a, $FF
ld ($4C4F), a
ld a, $99
ld ($4D4F), a
ld a,$FF
ld ($4E4F), a
ld ($4F4F), a
;-- Atributos
ld a, $16
ld ($594F),a
;-- Bucle infinito
inf:
jr inf
end $8000
Reto 1-13: sprite 16x16 en posicion (x=10, y=15)
Como último reto vamos a hacer un sprite de 16x16 (4 celdas) situado en la posicion x=10, y=15. El sprite a hacer es el mismo que utilizamos en el curso DEZ80: Un bit '1' corriendo hacia la derecha, pero adaptado a los colores del spectrum
Este es el sprite 16x16:
- 31-retro-1-13-sprinte-16x16.asm
;---------------------------------------------------------------------------
;-- Reto 1-13: Sprite 16x16 en posicion 10,15
;---------------------------------------------------------------------------
;-- 00000000 = 00 ;-- 00000000 = 00
;-- 00000000 = 00 ;-- 01110000 = 70
;-- 00000000 = 00 ;-- 11110000 = F0
;-- 00000001 = 01 ;-- 11110000 = F0
;-- 00000011 = 03 ;-- 10110000 = B0
;-- 00000111 = 07 ;-- 00110000 = 30
;-- 00000010 = 02 ;-- 00110000 = 30
;-- 01100000 = 60 ;-- 00110000 = 30
;-- 00011000 = 18 00110000 = 30
;-- 00000000 = 00 00110000 = 30
;-- 01111000 = 78 00110000 = 30
;-- 00000000 = 00 01001000 = 48
;-- 00011000 = 18 01000100 = 44
;-- 01100001 = 61 10000100 = 84
;-- 00000001 = 01 00000110 = 06
;-- 00000000 = 00 00000000 = 00
;-- Atributos: fondo rojo (010). Tinta amarilla (110)
;-- 00 010 110 = 0001 0110 = 16
;--
;-- Dir atributos: 5800 + LLLLLCCCCC
;-- Linea 10: 01010 01111 -> 01 0100 1111 = 14F
;-- Col 15:
;-- Dir: 5800 + 14F = 594F
;-- Posicion 10,15:
;-- Linea: 10 --> 01010 (TTLLL) T=01, LLL=010
;-- Colu 15 ---> CCCCC = 01111
;-- Byte bajo: LLLCCCCC = 01001111 = 0100 1111 = 4F
;-- Byte alto: 010TTSSS = 01001000 = 0100 1000 = 48
;-- Direcciones: 484F, 494F, 4A4F, 4B4F, 4C4F, 4D4F, 4E4F, 4F4F
;-- Posicion: 11,15
;-- Linea: 11 --> 01011 (TTLLL) T=01, LLL=011
;-- Col 15 ---> CCCCC = 01111
;-- Byte bajo: LLLCCCCC = 01101111 = 0110 1111 = 6F
;-- Byte alto: 010TTSSS = 01001000 = 0100 1000 = 48
org $8000
;--- Dibujar el Sprite
;---
ld hl, $0000
ld ($484F), hl
ld hl, $7000
ld ($494F), hl
ld hl, $F000
ld ($4A4F), hl
ld hl, $F001
ld ($4B4F), hl
ld hl, $B003
ld ($4C4F), hl
ld hl, $3007
ld ($4D4F), hl
ld hl,$3002
ld ($4E4F), hl
ld hl,$3060
ld ($4F4F), hl
;---
ld hl, $3018
ld ($486F), hl
ld hl, $3000
ld ($496F), hl
ld hl, $3078
ld ($4A6F), hl
ld hl, $4800
ld ($4B6F), hl
ld hl, $4418
ld ($4C6F), hl
ld hl, $8461
ld ($4D6F), hl
ld hl,$0601
ld ($4E6F), hl
ld hl,$0000
ld ($4F6F), hl
;-- 00011000 = 18 00110000 = 30
;-- 00000000 = 00 00110000 = 30
;-- 01111000 = 78 00110000 = 30
;-- 00000000 = 00 01001000 = 48
;-- 00011000 = 18 01000100 = 44
;-- 01100001 = 61 10000100 = 84
;-- 00000001 = 01 00000110 = 06
;-- 00000000 = 00 00000000 = 00
;-- Atributos
ld hl, $3939
ld ($594F),hl
ld ($596F),hl
;-- Bucle infinito
inf:
jr inf
end $8000
Nivel 2
Ahora que ya tengo claro cómo calcular las direcciones de memoria (a pelo) asociadas a la posición de una celda, voy a empezar a hacer animaciones pixel a pixel...
Reto 2.1: Animar un pixel
El reto consiste en animar un pixel rojo que se debe mover 4 posiciones en la primera celda. Posiciones (0,0), (1,0), (2,0) y (3,0) y luego volver a comenzar
Para hacer las esperas utilizamos la instrucción halt
que hace que la cpu se pare hasta que llegue la siguiente instrucción. En el ZX spectrum se produce una interrupción periódica cada 20ms, cada vez que el raster llega al final de la pantalla (y tenemos un tiempo para refrescar la memoria de video antes de que se empiece a dibujar el frame)
La dirección de memoria de video a utilizar es siempre la 0x4000, y lo que tenemos que hacer es escribir los siguientes valores: 0x80, 0x40, 0x20 y 0x10, uno por frame. Esto se repite indefinidamente
- 32-Reto-2-1-pixel-x4.asm
;---------------------------------------------------------------------------
;-- Reto 2-1: Animacion de un pixel a lo largo de 4 posiciones
;---------------------------------------------------------------------------
org $8000
;-- Esperar a que el frame actual termine
halt
;-- Fijar los atributos de la primera celda
;-- Fondo: blanco, tinta: roja
ld a, $3a
ld ($5800),a
bucle:
;-- Pixel en posicion (0,0)
ld a, $80
ld ($4000), a
call wait_100ms
;-- Pixel en posicion (1,0)
ld a, $40
ld ($4000), a
call wait_100ms
;-- Pixel en posicion (2,0)
ld a, $20
ld ($4000), a
call wait_100ms
;-- Pixel en posicion (3,0)
ld a, $10
ld ($4000), a
call wait_100ms
;-- Repetir
jr bucle
;---------------------------------------------------------------------------------
;-- Wait 100ms
;--
;-- Se espera a que lleguen 5 frames. Cada frame llega a los 20ms, por lo que
;-- se hace una espera de unos 100ms
;---------------------------------------------------------------------------------
wait_100ms:
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
ret
end $8000
Si sólo se utiliza una instrucción halt
, el movimiento del pixel es muy rápido, y como sólo se mueve por 4 posiciones es mejor ralentizarlo llamando varias veces a halt. He creado una rutina que ejecuta 5 halts, generando una pausa de unos 100ms aproximadamente.
Esta es la animación del pixel
Reto 2.2: Animar un pixel (8 posiciones)
El reto es casi el mismo, pero moviendo el pixel por las 8 posiciones de la primera celda. En vez de escribir 8 valores diferentes, vamos a partir del valor 0x80 y lo rotamos hacia la derecha usando la instrucción rrca
El código es ahora mucho más compacto
- 33-Reto-2-2-pixel-x8.asm
;---------------------------------------------------------------------------
;-- Reto 2-2: Animacion de un pixel a lo largo de 8 posiciones
;---------------------------------------------------------------------------
org $8000
;-- Esperar a que el frame actual termine
halt
;-- Fijar los atributos de la primera celda
;-- Fondo: blanco, tinta: roja
ld a, $3a
ld ($5800),a
;-- Valor inicial: 0x80
ld a, $80
bucle:
;-- Mostrar pixel en posicion actual
ld ($4000), a
call wait_100ms
;-- Desplazar A un bit a la derecha
rrca
;-- Repetir
jr bucle
;---------------------------------------------------------------------------------
;-- Wait 100ms
;--
;-- Se espera a que lleguen 5 frames. Cada frame llega a los 20ms, por lo que
;-- se hace una espera de unos 100ms
;---------------------------------------------------------------------------------
wait_100ms:
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
ret
end $8000
En esta animación se muestra el resultado:
Reto 2.3: Animar una barra de progreso (8 posiciones)
Partimos de una barra de progreso nula (sin píxeles) y en cada iteración añadimos un pixel. Cuando la barra llega al final (8 píxeles) se queda un poco de tiempo en ese estado (para que se vea mejor) y luego vuelve a comenzar
La clave está en que ahora, en vez de una rotación a la derecha, usamos un desplazamento aritmético a la derecha. Es decir, un desplazamiento que conserva el bit de signo.
Así, si el valor del acumulador es 0x80 (10000000 en binario), como es un número negativo, al desplazarlo a la derecha un bit se conserva su signo, siendo el resultado del desplazamiento el valor 0xC0 (11000000)..y así sucesivamente hasta llegar a -1 (11111111). Si se hace un nuevo desplazamiento, el número no cambia -1, pero se activa el flag de C, que usamos para detectar queya se ha completado el progreso
- 34-Reto-2-3-barra-progreso.asm
;---------------------------------------------------------------------------
;-- Reto 2-3: Animacion Barra de progres de 8 posiciones
;---------------------------------------------------------------------------
org $8000
;-- Esperar a que el frame actual termine
halt
;-- Fijar los atributos de la primera celda
;-- Fondo: blanco, tinta: roja
ld a, $3a
ld ($5800),a
start:
;-- La animación comienza con una barra de progreso nula
ld a, 0
ld ($4000),a
call wait_100ms
;-- Para mover la bara usamos un bucle que comienza con el valor 0x80
;-- y aplicando desplazamientos aritmeticos a la derecha aparecen
;-- el resto de piexeles: 10000000 -> 11000000 -> 11100000 ->...
;-- -> 11111110 -> 11111111
;-- Si hacemos un desplazamiento más el Flag de C se pone a 1
;-- Valor inicial: 0x80
ld a, $80
bucle:
;-- Mostrar pixel en posicion actual
ld ($4000), a
call wait_100ms
;-- Desplazar A un bit a la derecha
;-- Desplazamiento aritmetico (el bit de signo se conserva)
sra a
;-- Si el desplazamiento se ha completado,
;-- lo inicializamos
jr nc,bucle
;-- Proceso completado.
;-- Repetimos desde la barra de progreso nula
;-- Esperamos un poco mas de tiempo para mostrar que ha finalizado
call wait_100ms
call wait_100ms
call wait_100ms
jr start
;---------------------------------------------------------------------------------
;-- Wait 100ms
;--
;-- Se espera a que lleguen 5 frames. Cada frame llega a los 20ms, por lo que
;-- se hace una espera de unos 100ms
;---------------------------------------------------------------------------------
wait_100ms:
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
ret
end $8000
Este es el resultado:
Reto 2.4: Animar un sprite de 8x8
El reto es animar un sprite de 8x8, con al menos 4 fotogramas. Vamos a utilizar el mismo ejemplo que para el curso DEZ80: una máquina que tiene un elemento que sube y baja. En total necesitamos 3 dibujos diferenes: La máquina en reposo, la máquina con el elemento a medio camino y la máquina con el elmeento totalmente bajado
Los sprites se han dibujado con Inkscape:
Hay 3 dibujos pero 4 Fotogramas (porque el dibujo 1 se repite en los fotogramas 1 y 3)
- 35-Reto-2-4-sprite-8x8.asm
El código NO OPTIMIZADO es este. Los sprites se dibuja "a pelo", y para el fotograma 3 he hecho un "copy&paste" (Son las mismas instrucciones que para el fotograma 1)
;---------------------------------------------------------------------------
;-- Reto 2-4: Animacion de un sprite 8x8
;---------------------------------------------------------------------------
org $8000
;-- Esperar a que el frame actual termine
halt
;-- Fijar los atributos de la primera celda
;-- Fondo: blanco, tinta: azul
ld a, $39
ld ($5800),a
bucle:
;-- Pintar fotograma 1
;-- 01111110 = 7E
;-- 00011000 = 18
;-- 00011000 = 18
;-- 00011000 = 18
;-- 00011000 = 18
;-- 01111110 = 7E
;-- 00111100 = 3C
;-- 00011000 = 18
ld a, $7E
ld ($4000), a
ld a, $18
ld ($4100), a
ld ($4200), a
ld ($4300), a
ld ($4400), a
ld a, $7E
ld ($4500), a
ld a, $3C
ld ($4600), a
ld a, $18
ld ($4700), a
call wait_100ms
;-- Pintar fotograma 2
;-- 0000 0000 = 00
;-- 0111 1110 = 7E
;-- 0001 1000 = 18
;-- 0001 1000 = 18
;-- 0001 1000 = 18
;-- 0111 1110 = 7E
;-- 0011 1100 = 3C
;-- 0001 1000 = 18
ld a, $00
ld ($4000), a
ld a, $7E
ld ($4100), a
ld a, $18
ld ($4200), a
ld ($4300), a
ld ($4400), a
ld a, $7E
ld ($4500), a
ld a, $3C
ld ($4600), a
ld a, $18
ld ($4700), a
call wait_100ms
;-- Pintar fotograma 3
;-- 0000 0000 = 00
;-- 0000 0000 = 00
;-- 0111 1110 = 7E
;-- 0001 1000 = 18
;-- 0001 1000 = 18
;-- 0111 1110 = 7E
;-- 0011 1100 = 3C
;-- 0001 1000 = 18
ld a, $00
ld ($4000), a
ld a, $00
ld ($4100), a
ld a, $7E
ld ($4200), a
ld a, $18
ld ($4300), a
ld ($4400), a
ld a, $7E
ld ($4500), a
ld a, $3C
ld ($4600), a
ld a, $18
ld ($4700), a
call wait_100ms
;-- Pintar de nuevo fotograma 2
ld a, $00
ld ($4000), a
ld a, $7E
ld ($4100), a
ld a, $18
ld ($4200), a
ld ($4300), a
ld ($4400), a
ld a, $7E
ld ($4500), a
ld a, $3C
ld ($4600), a
ld a, $18
ld ($4700), a
call wait_100ms
jp bucle
;---------------------------------------------------------------------------------
;-- Wait 100ms
;--
;-- Se espera a que lleguen 5 frames. Cada frame llega a los 20ms, por lo que
;-- se hace una espera de unos 100ms
;---------------------------------------------------------------------------------
wait_100ms:
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
ret
end $8000
Este es el resultado:
Ahora vamos a mejorar el código. Los sprites nos los llevamos como datos a memoria, y creamos la rutina draw_sprite
que dibuja el sprite indicado en la dirección de memoria de video (alineada) indicada
- 36-Reto-2-4-sprite-8x8.asm
;---------------------------------------------------------------------------
;-- Reto 2-4: Animacion de un sprite 8x8
;-- Vesión 2: más optmizada.....
;---------------------------------------------------------------------------
org $8000
;-- Esperar a que el frame actual termine
halt
;-- Fijar los atributos de la primera celda
;-- Fondo: blanco, tinta: azul
ld a, $39
ld ($5800),a
bucle:
;-- Fotograma 0
ld hl, $4000 ;-- Posicion 0,0
ld de, sprite_0
call draw_sprite
call wait_100ms
;-- Fotograma 1
ld hl, $4000
ld de, sprite_1
call draw_sprite
call wait_100ms
;-- Fotograma 2
ld hl, $4000
ld de, sprite_2
call draw_sprite
call wait_100ms
;-- Fotograma 3
ld hl, $4000
ld de, sprite_1
call draw_sprite
call wait_100ms
jr bucle
;----------------------------------------------------------------------
;-- Dibujar sprite
;--
;-- ENTRADAS:
;-- * HL: Direccion de la memoria de video (Alineada a celda)
;-- * DE: Direccion del sprite
;;---------------------------------------------------------------------
draw_sprite:
;-- Numero de bytes del sprite
ld b, 8
;-- Est bucle pinta el sprite
;-- byte a byte. Se repite 8 veces
loop:
;-- Leer byte
ld a, (de)
;-- Guardarlo en memoria de video
ld (hl), a
;-- Apuntar al siguient byte del sprite
inc de
;-- Apuntar a la siguiente scanline
inc h
;-- Repetir
djnz loop
ret
sprite_0:
defb $7E, $18, $18, $18, $18, $7E, $3C, $18
sprite_1:
defb $00, $7E, $18, $18, $18, $7E, $3C, $18
sprite_2:
defb $00, $00, $7E, $18, $18, $7E, $3C, $18
;---------------------------------------------------------------------------------
;-- Wait 100ms
;--
;-- Se espera a que lleguen 5 frames. Cada frame llega a los 20ms, por lo que
;-- se hace una espera de unos 100ms
;---------------------------------------------------------------------------------
wait_100ms:
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
halt ;-- 20ms
ret
end $8000
El resultado es el mismo que en la animación anterior
Reto 2.5: Disparo laser con avance de 8 píxeles
- 37-Reto-2-5-disparo-x8.asm
;---------------------------------------------------------------------------
;-- Reto 2-5: Disparo láser con avance de 8 píxeles
;---------------------------------------------------------------------------
org $8000
start:
;-- Posiciones a avanzar la bala
ld b,20
;-- Puntero a la posicion de la bala
ld hl, $4000
;-- Puntero a los atributos de la bala
ld de, $5800
;-- Dibujar la bala inicial
ld a, $FF
ld (hl), a
;-- Atributos
ld a, $3a
ld (de), a
call wait
bucle:
;-- Borrar la bala
ld a, 0
ld (hl), a
;-- Incrementar posicion y puntero de los atributos
inc hl
inc de
;-- Dibujar la bala
ld a, $FF
ld (hl), a
;-- Atributos
ld a, $3a
ld (de), a
call wait
;-- Repetir
djnz bucle
;-- Borrar la bala
ld a,0
ld (hl), a
;-- Espera mayor
call wait
call wait
call wait
call wait
call wait
call wait
jp start
;---------------------------------------------------------------------------------
;-- Wait
;--
;-- Se espera a que lleguen N frames. Cada frame llega a los 20ms, por lo que
;-- se hace una espera de unos 100ms
;---------------------------------------------------------------------------------
wait:
halt ;-- 20ms
halt ;-- 20ms
halt
halt
ret
end $8000
Reto 2.6: Barra de progreso de 40 bits
- 38-Reto-2-6-barra-x40.asm
;---------------------------------------------------------------------------
;-- Reto 2-6: Barra de progreso de 32 bits
;---------------------------------------------------------------------------
org $8000
;-- Sincronizarse con las interrupciones
halt
start:
;-- Puntero a la memoria de video
ld hl, $4000
;-- Puntero a la memoria de atributos
ld de, $5800
;-- Numero de celdas totales de la barra
ld b, 5
next:
;-- Meter atributo
ld a,$39
ld (de), a
;-- Barra de progreso en una celda
;-- Meter primer valor
ld a,$80
loop;
ld (hl), a
call wait
;-- Desplazar a la derecha
sra a
;-- Repetir hasta completar el progreso en la celda actual
jr nc,loop
;-- Pasar a la siguiente celda
inc hl
;-- Pasar al siguiente atributo
inc de
;-- Repetir hasta completar el tamaño de la celda
djnz next
;-- Esperar un tiempo hasta re-arrancar la barra de progreso
ld b,20
wait_loop:
call wait
djnz wait_loop
;-- Borrar la barra de progreso
ld b,5
ld hl, $4000
clear_next:
ld a,0
ld (hl), a
inc hl
djnz clear_next
jp start
;---------------------------------------------------------------------------------
;-- Wait
;--
;-- Se espera a que lleguen N frames. Cada frame llega a los 20ms, por lo que
;-- se hace una espera de unos 100ms
;---------------------------------------------------------------------------------
wait:
halt ;-- 20ms
;halt ;-- 20ms
ret
end $8000
Experimentos
Antes de continuar con los retos, voy a hacer algunos experimentos por mi cuenta. Quiero aprender a trabajar a nivel de pixeles...
Posicionamiento x de pixel en la celda (0,0)
Voy a empezar por resolver el problema de cómo posicionar un pixel en la primera celda, en cualquiera de sus posicones horizontales de la primera scanline. Esta rutina la voy a llamar plotx(p). Como argumento se le pasa la posición del pixel en esa celda: 0-7
b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
---|---|---|---|---|---|---|---|
pos 0 | pos 1 | pos 2 | pos 3 | pos 4 | pos 5 | pos 6 | pos 7 |
Así por ejemplo, si hacemos plox(0), se escribe un pixel en la primera posición (0,0):
- plotx(0): 1 0 0 0 0 0 0 0
- plotx(1): 0 1 0 0 0 0 0 0
- plotx(2): 0 0 1 0 0 0 0 0
- plotx(3): 0 0 0 1 0 0 0 0
- ...
- plotx(7): 0 0 0 0 0 0 0 1
En esta primera versión se borra lo que haya (se pone el pixel y el resto se dejan a cero). En las siguientes versiones se permitirá situar el pixel junto al resto de elementos
- 39-plotx-scan1-celda-0-0.asm
;---------------------------------------------------------------------------
;-- Experimento. Dibujo de un pixel en el primer scanline de la celda (0,0)
;-- Para probar la rutina plotx() se hace una animación del pixel, moviéndose
;-- entre las posiciones 0 y 7 de la celda (0,0) (Esquina superior izquierda)
;---------------------------------------------------------------------------
;-- Puerto para cambiar el color del borde
BORDER: EQU $FE
org $8000
;-- Poner borde amarillo para ver mejor la posicion
;-- de los píxeles
ld a, 6
out (BORDER), a
;-- Entrada: HL: Memoria de video
ld hl, $4000 ;-- Celda (0,0)
;-- Entrada B = Posicion del pixel (0-7)
ld b, 0
bucle
push bc
;-- Dibujar el pixel!
call plotx
;-- Esperar un tiempo
call wait
;-- Incrementar la posicion
pop bc
inc b
;-- La posicion queremos que sea siempre modulo 8
;-- (entre 0 y 7)
ld a,b
and 7
ld b,a
;-- Repetir
jr bucle
;-----------------------------------------------------------
;-- PLOTX(HL,pos)
;--
;-- ENTRADAS:
;-- * HL: Direccion de la memoria de video
;-- * B: Posicion del pixel (0-7) en el scanline 0 de la celda
;-----------------------------------------------------------------
plotx:
;-- Si es la posicion 0, se escribe el valor $80 y se termina
ld a,b
or a ;-- Actualizar flag z para comproar si A es 0
jr z,pos0 ;-- A=0, es la posicion 0
;-- NO es la posicion 0
;-- Partimos del valor $80 y lo desplazamos B posiciones
;-- a la derecha
ld a, $80
to_pos
rrca
djnz to_pos
;-- En A tenemos el valor a escribir en memoria de video
;-- Escribir!
ld (hl), a
jr fin
pos0:
;-- Valor a escribir en memoria video
ld a, $80
ld (hl), a
fin:
ret
;----------------------------------
;-- Esperar N frames...
;-- Cada frame son 20ms aprox...
;----------------------------------
wait:
halt ;-- 20 ms
halt
halt
halt
halt
ret
end $8000
Para comprobar que la función plotx() funciona correctamente se hace una animación, para mover el pixel desde la posición 0 a la 7 (y volver a empezar). Pero ahora se hace pasando la posición del pixel (0-7). En esta animación se muestra el resultado:
Posicionamiento x de pixel en cualquier posición 0-255 del primer scanline
Ahora ampliamos la rutina de posicionamiento plotx() para permitir dibujar un pixel en cualquiera de las 256 posiciones del primer scanline... Como ejemplo vamos a animar un pixel para que se mueva en esta primera línea...
En total, en cada scanline hay 256 píxeles, desde el pixel 0 al 255. Es una coordenada x de 8 bits, que se descompone en dos campos. Uno de 5 bits: la celda, y otro de 3 bits (el pixel dentro de la celda)
b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
---|---|---|---|---|---|---|---|
C | C | C | C | C | P | P | P |
Con el primer campo, CCCCC, se calcula la dirección base de la memoria de vídeo, y con el segundo PPP se pinta el pixel en esa celda
- 40-plotx-scan1.asm
;---------------------------------------------------------------------------
;-- Experimento. Dibujo de un pixel en el primer scanline. Posiciones 0-255
;-- Para probar la rutina plotx() se hace una animación del pixel, moviéndose
;-- entre las posiciones 0 y 255
;---------------------------------------------------------------------------
;-- Puerto para cambiar el color del borde
BORDER: EQU $FE
org $8000
;-- Poner borde amarillo para ver mejor la posicion
;-- de los píxeles
ld a, 6
out (BORDER), a
start:
;-- Entrada: HL: Memoria de video
ld hl, $4000
;-- Entrada B = Posicion del pixel (0-255)
ld b, 0
bucle
push bc
;-- Dibujar el pixel!
call plotx
;-- Esperar un tiempo
call wait
;-- Incrementar la posicion
pop bc
inc b
;-- Repetir
jr nz, bucle
;-- La linea ha llegado al final
;-- La borramos, para volver a comenzar
;-- Longitud: 32 celdas
ld b,32
ld hl, $4000
;-- Valor a guardar en la memoria
ld a,0
;-- Bucle de borrado
clear:
ld (hl),a
inc l
djnz clear
;-- Comenzar!
jp start
;--------------------------------------------------------------
;-- PLOTX(HL, POS) (Pos: 0-255)
;-- Dibujar un pixel en la posicion 0-255 de una scanline
;--
;-- ENTRADA:
;-- * HL: Direccion base de la memoria de video
;-- * B: Posicion x: 0 - 255
;--------------------------------------------------------------
plotx:
push hl
ld a,b
;-- Desplazamiento a la derecha 3 posiciones
;-- Para tener el campo CCCCC
sra a
sra a
sra a
and $1F ;-- Quedarse con los 5 bits de menor peso (000CCCCC)
;-- Sumar CCCCC a la dirección base
add a,l
ld l,a ;-- HL apunta a la celda CCCCC (0-31)
ld a,b ;-- Obtener el campo PPP
and 7
ld b,a
;-- Dibujar el pixel
call plotxp
pop hl
ret
;-----------------------------------------------------------
;-- PLOTXP(HL,pos) Meter pixel en una celda (pos: 0-7)
;--
;-- ENTRADAS:
;-- * HL: Direccion de la memoria de video
;-- * B: Posicion del pixel (0-7) en el scanline 0 de la celda
;-----------------------------------------------------------------
plotxp:
;-- Si es la posicion 0, se escribe el valor $80 y se termina
ld a,b
or a ;-- Actualizar flag z para comproar si A es 0
jr z,pos0 ;-- A=0, es la posicion 0
;-- NO es la posicion 0
;-- Partimos del valor $80 y lo desplazamos B posiciones
;-- a la derecha
ld a, $80
to_pos
rrca
djnz to_pos
;-- En A tenemos el valor a escribir en memoria de video
;-- Escribir!
jr plot
pos0:
;-- Valor a escribir en memoria video
ld a, $80
plot:
;-- Primer leemos lo que hay
ld b,(hl)
;-- Aplicamos un or para añdir a
or b
;-- Guardamos el nuevo valor
ld (hl), a
fin:
ret
;----------------------------------
;-- Esperar N frames...
;-- Cada frame son 20ms aprox...
;----------------------------------
wait:
halt ;-- 20 ms
ret
end $8000
Este es el resultado:
Posicionamiento Y de pixel en la primera celda (0,0)
Ahora hacemos la función ploty(x,y) que permite posicionar un pixel dentro de la primera celda, en cualquier posición x,y (con x,y en el rango 0-7). Como ejemplo se rellenan las líneas verticales de esta primera celda
- 41-ploty-celda0.asm
;---------------------------------------------------------------------------
;-- Experimento. Dibujo de lineas vertical en la primera celda (0,0)
;---------------------------------------------------------------------------
;-- Puerto para cambiar el color del borde
BORDER: EQU $FE
org $8000
;-- Poner borde amarillo para ver mejor la posicion
;-- de los píxeles
ld a, 6
out (BORDER), a
start:
;-- Memoria de video
ld hl, $4000
ld b,0 ;-- x
ld c,0 ;-- y
loop:
;-- Pintar pixel en posicion actual
push hl
push bc
call pixely
pop bc
pop hl
call wait
;-- Incrementar posicion
inc c
;-- Restringir la posicion a 0-7 (modulo 8)
ld a,c
and 7
;-- Si la linea llega al final... pasar a la siguiente linea vertical
jr z, next_col
ld c,a
jr loop
next_col:
;-- Pasar a la siguiente columna
inc b
;-- Incremento modulo 8
ld a,b
and 7
jr z,fin ;-- Si es la linea 7 hemos terminado
ld b,a
;-- Volver a y=0
ld c,0
jr loop
fin:
;-- Borrar la celda 0,0
ld b,8
ld a,0
ld hl,$4000
clear:
ld (hl),a
inc h
djnz clear
;-- Volver a empezar
call wait
call wait
jp start
;----------------------------------------------------------
;-- PIXEL_Y(y)
;--
;-- ENTRADAS:
;-- * HL: Direccion de la memoria de video
;-- * B: Posicion x del pixel (0-7)
;-- * C: Posicion y del pixel (0-7)
;-----------------------------------------------------------
pixely:
;-- Valor correspondiente al pixel de la posicion b
call pixel_mask
;--- h = h + c
push aF ;-- guardar a para no perderlo
ld a, h
add a,c
ld h,a ;-- h = h + c
pop af
;-- Dibujar pixel (over)
ld b,a
ld a,(hl) ;-- Leer valor actual
or b ;-- Añadir nuevo pixel
ld (hl), a
ret
;-----------------------------------------------------------
;-- PIXEL_MASK(x): Devolver la máscara de pixel a escribir en la
;-- memoria de video de la posición x (0-7)
;--
;-- ENTRADAS:
;-- * HL: Direccion de la memoria de video
;-- * B: Posicion x del pixel (0-7) en el scanline 0 de la celda
;--
;-- DEVUELVE:
;-- * A: Valor a escribir en la memoria de video
;-----------------------------------------------------------------
pixel_mask:
;-- Si es la posicion 0, se escribe el valor $80 y se termina
ld a,b
or a ;-- Actualizar flag z para comproar si A es 0
jr z,pos0 ;-- A=0, es la posicion 0
;-- NO es la posicion 0
;-- Partimos del valor $80 y lo desplazamos B posiciones
;-- a la derecha
ld a, $80
to_pos
rrca
djnz to_pos
;-- En A tenemos el valor a escribir en memoria de video
;-- Devolverlo
ret
pos0:
;-- Valor a escribir en memoria video
ld a, $80
ret
;----------------------------------
;-- Esperar N frames...
;-- Cada frame son 20ms aprox...
;----------------------------------
wait:
halt ;-- 20 ms
halt ;-- 20 ms
halt ;-- 20 ms
halt ;-- 20 ms
halt ;-- 20 ms
ret
end $8000
Este es el resultado:
Rutina de Plot(x,y)
Esta es la rutina completa de plot(x,y) para píxeles, con x en el rango (0,255) e y en el rango (0,191). Hay dos versiones: plot_set() y plot_clear(). La primer pone un pixle y la segunda lo elimina
- 42-pixel-xy-1.asm
En este programa de pruebas se recorren todos los píxeles de la pantalla, uno a uno, y se ponen negros. Luego se repite pero poniéndolos bancos, para comprobar ambas rutinas
;---------------------------------------------------------------------------
;-- Pruebas de plot(x,y) en diferentes puntos
;---------------------------------------------------------------------------
;-- Puerto para cambiar el color del borde
BORDER: EQU $FE
org $8000
;-- Poner borde amarillo para ver mejor la posicion
;-- de los píxeles
ld a, 6
out (BORDER), a
;-- Posicion inicial: (0,0)
ld b,0 ;-- x
ld c,0 ;-- y
;-- FASE 1: Poner todos los pixeles de la pantalla de negro, uno a uno
;-- Recorrer todos los pixeles de la pantalla
;-- B: 0-255
;-- C: 0-191
fase1:
;-- Pintar pixel
call plot_set
;-- Esperar un frame (20ms)
;-- Se deja comentado para ir mas rapido
;halt
;-- Incrementar la coordenada x (modulo 256)
inc b
;-- Repetir mientras b <=255
jr nz,fase1
;-- Pasar a la siguiente linea
inc c
;-- Si es la linea 192, hemos llegado al final: terminar
ld a,c
cp 192
;-- c < 192, repetir
jr nz,fase1
;-- Hemos terminado
;-- Nos quedamos en bucle infinito
;-- La pantalla debe estar en negro
;-- Posicion inicial: (0,0)
ld b,0 ;-- x
ld c,0 ;-- y
;-- FASE 2: Poner todos los pixeles de la pantalla de blanco, uno a uno
;-- Recorrer todos los pixeles de la pantalla
;-- B: 0-255
;-- C: 0-191
fase2:
;-- Borrar pixel
call plot_clear
;-- Esperar un frame (20ms)
;-- Se deja comentado para ir mas rapido
;halt
;-- Incrementar la coordenada x (modulo 256)
inc b
;-- Repetir mientras b <=255
jr nz,fase2
;-- Pasar a la siguiente linea
inc c
;-- Si es la linea 192, hemos llegado al final: terminar
ld a,c
cp 192
;-- c < 192, repetir
jr nz,fase2
;-- Hemos terminado
;-- Nos quedamos en bucle infinito
;-- La pantalla debe estar en negro
inf: jr inf
;-----------------------------------------------------------
;-- plot_clear (x,y): Borrar un pixel de la pantalla
;--
;-- ENTRADAS:
;-- * B: Coordenada x (0-255)
;-- * C: Coordenada y (0-192)
;------------------------------------------------------------
plot_clear:
;-- Guardar posicion del pixel
push bc
;-- Dejar en HL la direccion de la memoria de video
;-- asociada a la posicion B=x, C=y
call get_videoRam
;-- Recuperar posicion del pixel
pop bc
push bc ;-- Volver a guardarla, para luego
;-- Obtener la máscara del pixel
call pixel_mask
xor $FF
;-- Escribir el pixel!!
;-- Modo OVER
ld d,a
ld a, (hl) ;-- Leer el valor actual de la memoria
and d ;-- Restar el nuevo pixel
ld (hl),a ;-- Escribirlo!
;-- Recuperar coordenadas
pop bc
ret
;-----------------------------------------------------------
;-- plot_set(x,y): Pintar un pixel en pantalla (modo OVER)
;--
;-- ENTRADAS:
;-- * B: Coordenada x (0-255)
;-- * C: Coordenada y (0-192)
;------------------------------------------------------------
plot_set:
;-- Guardar posicion del pixel
push bc
;-- Dejar en HL la direccion de la memoria de video
;-- asociada a la posicion B=x, C=y
call get_videoRam
;-- Recuperar posicion del pixel
pop bc
push bc ;-- Volver a guardarla, para luego
;-- Obtener la máscara del pixel
call pixel_mask
;-- Escribir el pixel!!
;-- Modo OVER
ld d,a
ld a, (hl) ;-- Leer el valor actual de la memoria
or d ;-- Añdir el nuevo pixel
ld (hl),a ;-- Escribirlo!
;-- Recuperar coordenadas
pop bc
ret
;----------------------------------------------------------------------------
;-- GET_VIDEORAM: Obtener la memoria de video asociada a una posicion
;-- de pixel
;--
;-- ENTRADAS:
;-- * B: Coordenada x (0-255)
;-- * C: Coordenada y (0-192)
;-- DEVUELVE:
;-- * HL: Direccio de la memoria de video
;---------------------------------------------------------------------------
get_videoRam:
;-- HL = Direccion de video = 010TTSSS LLLCCCCC
;-- Convertir B y C a dirección en HL
;-- B = Coordenada x = CCCCCPPP
;-- C = Coordenada y = TTLLLSSS
;--------- Aislar campo CCCCC de B
ld a,b
;-- Desplazar tres bits a la derecha
sra a ;-- XCCCCCPP
sra a ;-- XXCCCCCP
sra a ;-- XXXCCCCC
and $1F ;-- 000CCCCC
ld l,a ;-- L = 000CCCCC
;-- Aislar LLL en los bits mas significativos
ld a,c ;-- TTLLLSSS
rlca ;-- TLLLSSS0
rlca ;-- LLLSSS00
and $E0 ;-- LLL00000
;-- Añadir campo CCCCC
or l ; A = LLLCCCCC
ld l,a ;-- L = LLLCCCCC (Completado!)
;-- Aislar y colocar campo TT
ld a,c ; TTLLLSSS
sra a ; xTTLLLSS
sra a ; xxTTLLLS
sra a ; xxxTTLLL
and $18 ; 000TT000
ld h,a ; H = 000TT000
;-- Aislar campo SSS
ld a,c ; A = TTLLLSSS
and $07 ; 00000SSS
or h ; 000TTSSS
add a, $40 ; 010TTSSS
ld h,a ; H = 010TTSSS (Completado!)
;-- HL CONTIENE LA DIRECCION
ret
;-----------------------------------------------------------
;-- PIXEL_MASK(x): Devolver la máscara de pixel a escribir en la
;-- memoria de video de la posición x (0-7)
;--
;-- ENTRADAS:
;-- * B: Posicion x del pixel (0-7) en el scanline 0 de la celda
;--
;-- DEVUELVE:
;-- * A: Valor a escribir en la memoria de video
;-----------------------------------------------------------------
pixel_mask:
;-- Si es la posicion 0, se escribe el valor $80 y se termina
ld a,b
or a ;-- Actualizar flag z para comproar si A es 0
jr z,pos0 ;-- A=0, es la posicion 0
;-- NO es la posicion 0
;-- Partimos del valor $80 y lo desplazamos B posiciones
;-- a la derecha
ld a, $80
to_pos
rrca
djnz to_pos
;-- En A tenemos el valor a escribir en memoria de video
;-- Devolverlo
ret
pos0:
;-- Valor a escribir en memoria video
ld a, $80
ret
end $8000
En esta animación se muestra el funcionamiento. La demo se ha restringido sólo a las primeras líneas, para que no tarde mucho:
Partícula de 1 pixel confinada en celda 0,0
- 43-particula-celda.asm
;---------------------------------------------------------------------------
;-- Pruebas de plot(x,y) en diferentes puntos
;---------------------------------------------------------------------------
;-- Puerto para cambiar el color del borde
BORDER: EQU $FE
org $8000
;-- Poner borde amarillo para ver mejor la posicion
;-- de los píxeles
ld a, 6
out (BORDER), a
ld b,0 ;-- x
ld c,4 ;-- y
call plot_set
bucle:
call wait
;-- Borrar pixel
call plot_clear
;----------------------------------------
;-- Actualizar posicion horizontal (x)
;----------------------------------------
;-- Calcular la nueva posicion del pixel segun la velocidad vx
ld a, (vx)
add a,b
;-- Comprobar colision a la derecha
cp 8
jr nz, continue
;-- Colision!
;-- Cambiar el signo de la velocidad
ld a, (vx)
neg
ld (vx), a ;-- Guardar nueva velocidad
add a,b ;-- Calcular nueva posicion
continue:
;-- Actualizar a la nueva posicion
ld b,a ;--- B = B + vx
;-- Comprobar colision a la izquirda
or a ; a=0?
jr nz, continue2
;-- Colision!
;-- Cambiar signo de la velocidad
ld a, (vx)
neg
ld (vx), a
add a,b
continue2:
;----------------------------------------
;-- Actualizar posicion vertical (y)
;----------------------------------------
;-- Calcular la nueva posicion del pixel segun la velocidad vy
ld a, (vy)
add a,c
;-- Comprobar colision inferior
cp 8
jr nz, continue3
;-- Colision!
;-- Cambiar el signo de la velocidad
ld a, (vy)
neg
ld (vy), a ;-- Guardar nueva velocidad
add a,c ;-- Calcular nueva posicion
continue3:
;-- Actualizar a la nueva posicion
ld c,a ;--- B = B + vy
;-- Comprobar colision a la izquirda
or a ; a=0?
jr nz, continue4
;-- Colision!
;-- Cambiar signo de la velocidad
ld a, (vy)
neg
ld (vy), a
add a,c
continue4:
;-- Dibujar el pixel
call plot_set
jr bucle
;-- Velocidad
vx: defb 1
vy: defb 1
;-----------------------------------------------------------
;-- plot_clear (x,y): Borrar un pixel de la pantalla
;--
;-- ENTRADAS:
;-- * B: Coordenada x (0-255)
;-- * C: Coordenada y (0-192)
;------------------------------------------------------------
plot_clear:
;-- Guardar posicion del pixel
push bc
;-- Dejar en HL la direccion de la memoria de video
;-- asociada a la posicion B=x, C=y
call get_videoRam
;-- Recuperar posicion del pixel
pop bc
push bc ;-- Volver a guardarla, para luego
;-- Obtener la máscara del pixel
call pixel_mask
xor $FF
;-- Escribir el pixel!!
;-- Modo OVER
ld d,a
ld a, (hl) ;-- Leer el valor actual de la memoria
and d ;-- Restar el nuevo pixel
ld (hl),a ;-- Escribirlo!
;-- Recuperar coordenadas
pop bc
ret
;-----------------------------------------------------------
;-- plot_set(x,y): Pintar un pixel en pantalla (modo OVER)
;--
;-- ENTRADAS:
;-- * B: Coordenada x (0-255)
;-- * C: Coordenada y (0-192)
;------------------------------------------------------------
plot_set:
;-- Guardar posicion del pixel
push bc
;-- Dejar en HL la direccion de la memoria de video
;-- asociada a la posicion B=x, C=y
call get_videoRam
;-- Recuperar posicion del pixel
pop bc
push bc ;-- Volver a guardarla, para luego
;-- Obtener la máscara del pixel
call pixel_mask
;-- Escribir el pixel!!
;-- Modo OVER
ld d,a
ld a, (hl) ;-- Leer el valor actual de la memoria
or d ;-- Añdir el nuevo pixel
ld (hl),a ;-- Escribirlo!
;-- Recuperar coordenadas
pop bc
ret
;----------------------------------------------------------------------------
;-- GET_VIDEORAM: Obtener la memoria de video asociada a una posicion
;-- de pixel
;--
;-- ENTRADAS:
;-- * B: Coordenada x (0-255)
;-- * C: Coordenada y (0-192)
;-- DEVUELVE:
;-- * HL: Direccio de la memoria de video
;---------------------------------------------------------------------------
get_videoRam:
;-- HL = Direccion de video = 010TTSSS LLLCCCCC
;-- Convertir B y C a dirección en HL
;-- B = Coordenada x = CCCCCPPP
;-- C = Coordenada y = TTLLLSSS
;--------- Aislar campo CCCCC de B
ld a,b
;-- Desplazar tres bits a la derecha
sra a ;-- XCCCCCPP
sra a ;-- XXCCCCCP
sra a ;-- XXXCCCCC
and $1F ;-- 000CCCCC
ld l,a ;-- L = 000CCCCC
;-- Aislar LLL en los bits mas significativos
ld a,c ;-- TTLLLSSS
rlca ;-- TLLLSSS0
rlca ;-- LLLSSS00
and $E0 ;-- LLL00000
;-- Añadir campo CCCCC
or l ; A = LLLCCCCC
ld l,a ;-- L = LLLCCCCC (Completado!)
;-- Aislar y colocar campo TT
ld a,c ; TTLLLSSS
sra a ; xTTLLLSS
sra a ; xxTTLLLS
sra a ; xxxTTLLL
and $18 ; 000TT000
ld h,a ; H = 000TT000
;-- Aislar campo SSS
ld a,c ; A = TTLLLSSS
and $07 ; 00000SSS
or h ; 000TTSSS
add a, $40 ; 010TTSSS
ld h,a ; H = 010TTSSS (Completado!)
;-- HL CONTIENE LA DIRECCION
ret
;-----------------------------------------------------------
;-- PIXEL_MASK(x): Devolver la máscara de pixel a escribir en la
;-- memoria de video de la posición x (0-7)
;--
;-- ENTRADAS:
;-- * B: Posicion x del pixel (0-7) en el scanline 0 de la celda
;--
;-- DEVUELVE:
;-- * A: Valor a escribir en la memoria de video
;-----------------------------------------------------------------
pixel_mask:
;-- Si es la posicion 0, se escribe el valor $80 y se termina
ld a,b
or a ;-- Actualizar flag z para comproar si A es 0
jr z,pos0 ;-- A=0, es la posicion 0
;-- NO es la posicion 0
;-- Partimos del valor $80 y lo desplazamos B posiciones
;-- a la derecha
ld a, $80
to_pos
rrca
djnz to_pos
;-- En A tenemos el valor a escribir en memoria de video
;-- Devolverlo
ret
pos0:
;-- Valor a escribir en memoria video
ld a, $80
ret
;----------------------------------
;-- Esperar N frames...
;-- Cada frame son 20ms aprox...
;----------------------------------
wait:
halt ;-- 20 ms
halt
halt
halt
halt
halt
ret
end $8000
Dos particulas confinadas
Ahora es el momento de extender el movimiento de la partícula a dos. La clave está en que ahora se definen los datos de la particula en una estructura y se hacen funciones para trabajar con esas estructuras
- 44-particulas-2.asm
;---------------------------------------------------------------------------
;-- Dos partículas confinadas en las celdas (0,0) y (1,0)
;---------------------------------------------------------------------------
;--- Constantes de acceso para las entidades
POSX EQU 0
POSY EQU 1
VX EQU 2
VY EQU 3
CXMIN EQU 4
CXMAX EQU 5
CYMIN EQU 6
CYMAX EQU 7
;-- Puerto para cambiar el color del borde
BORDER: EQU $FE
org $8000
;-- Poner borde amarillo para ver mejor la posicion
;-- de los píxeles
ld a, 6
out (BORDER), a
bucle:
;------ Particula 1
ld ix, particula1
call move_particle
;------ Particula 2
ld ix, particula2
call move_particle
;-- Esperar a que termine el frame
call wait
;-- Repetir
jr bucle
;------------------------------------------
;-- MOVE_PARTICLE: Mover la particula
;--
;-- ENTRADA:
;-- * IX: Puntero a la particula a mover
;-------------------------------------------
move_particle:
;-- Borrar pixel
ld b, (ix + POSX)
ld c, (ix + POSY)
call plot_clear
;-- Comprobar colision
call check_colision
;-- Actualizar posicion
call update_particle
;-- Dibujar la particula
ld b, (ix + POSX)
ld c, (ix + POSY)
call plot_set
ret
;---------------------------------------------------------------------
;-- UPDATE_PARTICLE: Actualizar la particula indicada por IX
;-- Se actualiza su posicion acorde a la velocidad y se comprueban
;-- las colisiones
;--
;-- ENTRADAS:
;-- * IX: Puntero a la entidad a comprobar
;---------------------------------------------------------------------
update_particle:
ld a, (ix + POSX)
ld b, (ix + VX)
add a,b ;-- x = x + vx
ld (ix + POSX), a
ld a, (ix + POSY)
ld b, (ix + VY)
add a,b ;-- y = y + vy
ld (ix + POSY), a
ret
;-----------------------------------------------------------------
;-- CHECK_COLISION: Comprobar la colision de la particula con los
;-- limites. Si hay colision se cambia el sentido de la velocidad
;--
;-- ENTRADAS:
;-- * IX: Puntero a la entidad a comprobar
;------------------------------------------------------------------
check_colision:
;---------- COMPROBAR COLISION EN EJE X -----------------------
;-- Comprobar colisión por la derecha
ld a, (ix + POSX) ;-- Leer x actual
ld b, (ix + CXMAX) ;-- Leer xmax
cp b ;-- x==xmax?
jr z, rebotex
;-- Comprobar colision por la izquierda
ld a, (ix + POSX)
ld b, (ix + CXMIN) ;-- Leer xmin
cp b ;-- x==xmin?
jr z, rebotex
;---------- COMPROBAR COLISION EN EJE Y -----------------------
;--- Comprobar colision por abajo
ld a, (ix + POSY) ;-- Leer y actual
ld b, (ix + CYMAX) ;-- Leer ymax
cp b ; y==ymax?
jr z, rebotey
;--- Comprobar colision por arriba
ld a, (ix + POSY)
ld b, (ix + CYMIN) ;-- Leer ymin
cp b ; y == ymin?
jr z, rebotey
ret
rebotex:
;-- Hay colision por la derecha
;-- Cambiar el sentido de la velocidad: rebote
ld a, (ix + VX)
neg
ld (ix + VX), a
ret
rebotey:
;-- Hay colision por abajo
;-- Cambiar el sentido de la velocidad: rebote
ld a, (ix + VY)
neg
ld (ix + VY), a
ret
;-- Informacion sobre las particulas
;----------------------------------------------
;-- PARTICULA 1
;----------------------------------------------
particula1:
defb 1 ; Posicion x
defb 4 ; Posicion y
defb 1 ; Velocidad en x
defb 1 ; Velocidad en y
;-- Datos de las coliciones
defb 0 ; xmin
defb 8 ; xmax
defb 0 ; ymin
defb 8 ; ymax
;----------------------------------------------
;-- PARTICULA 2
;----------------------------------------------
particula2:
defb 10 ; Posicion x
defb 5 ; Posicion y
defb -1 ; Velocidad en x
defb -1 ; Velocidad en y
;-- Datos de las coliciones
defb 8 ; xmin
defb 16 ; xmax
defb 0 ; ymin
defb 8 ; ymax
;-----------------------------------------------------------
;-- plot_clear (x,y): Borrar un pixel de la pantalla
;--
;-- ENTRADAS:
;-- * B: Coordenada x (0-255)
;-- * C: Coordenada y (0-192)
;------------------------------------------------------------
plot_clear:
;-- Guardar posicion del pixel
push bc
;-- Dejar en HL la direccion de la memoria de video
;-- asociada a la posicion B=x, C=y
call get_videoRam
;-- Recuperar posicion del pixel
pop bc
push bc ;-- Volver a guardarla, para luego
;-- Obtener la máscara del pixel
call pixel_mask
xor $FF
;-- Escribir el pixel!!
;-- Modo OVER
ld d,a
ld a, (hl) ;-- Leer el valor actual de la memoria
and d ;-- Restar el nuevo pixel
ld (hl),a ;-- Escribirlo!
;-- Recuperar coordenadas
pop bc
ret
;-----------------------------------------------------------
;-- plot_set(x,y): Pintar un pixel en pantalla (modo OVER)
;--
;-- ENTRADAS:
;-- * B: Coordenada x (0-255)
;-- * C: Coordenada y (0-192)
;------------------------------------------------------------
plot_set:
;-- Guardar posicion del pixel
push bc
;-- Dejar en HL la direccion de la memoria de video
;-- asociada a la posicion B=x, C=y
call get_videoRam
;-- Recuperar posicion del pixel
pop bc
push bc ;-- Volver a guardarla, para luego
;-- Obtener la máscara del pixel
call pixel_mask
;-- Escribir el pixel!!
;-- Modo OVER
ld d,a
ld a, (hl) ;-- Leer el valor actual de la memoria
or d ;-- Añdir el nuevo pixel
ld (hl),a ;-- Escribirlo!
;-- Recuperar coordenadas
pop bc
ret
;----------------------------------------------------------------------------
;-- GET_VIDEORAM: Obtener la memoria de video asociada a una posicion
;-- de pixel
;--
;-- ENTRADAS:
;-- * B: Coordenada x (0-255)
;-- * C: Coordenada y (0-192)
;-- DEVUELVE:
;-- * HL: Direccio de la memoria de video
;---------------------------------------------------------------------------
get_videoRam:
;-- HL = Direccion de video = 010TTSSS LLLCCCCC
;-- Convertir B y C a dirección en HL
;-- B = Coordenada x = CCCCCPPP
;-- C = Coordenada y = TTLLLSSS
;--------- Aislar campo CCCCC de B
ld a,b
;-- Desplazar tres bits a la derecha
sra a ;-- XCCCCCPP
sra a ;-- XXCCCCCP
sra a ;-- XXXCCCCC
and $1F ;-- 000CCCCC
ld l,a ;-- L = 000CCCCC
;-- Aislar LLL en los bits mas significativos
ld a,c ;-- TTLLLSSS
rlca ;-- TLLLSSS0
rlca ;-- LLLSSS00
and $E0 ;-- LLL00000
;-- Añadir campo CCCCC
or l ; A = LLLCCCCC
ld l,a ;-- L = LLLCCCCC (Completado!)
;-- Aislar y colocar campo TT
ld a,c ; TTLLLSSS
sra a ; xTTLLLSS
sra a ; xxTTLLLS
sra a ; xxxTTLLL
and $18 ; 000TT000
ld h,a ; H = 000TT000
;-- Aislar campo SSS
ld a,c ; A = TTLLLSSS
and $07 ; 00000SSS
or h ; 000TTSSS
add a, $40 ; 010TTSSS
ld h,a ; H = 010TTSSS (Completado!)
;-- HL CONTIENE LA DIRECCION
ret
;-----------------------------------------------------------
;-- PIXEL_MASK(x): Devolver la máscara de pixel a escribir en la
;-- memoria de video de la posición x (0-7)
;--
;-- ENTRADAS:
;-- * B: Posicion x del pixel (0-7) en el scanline 0 de la celda
;--
;-- DEVUELVE:
;-- * A: Valor a escribir en la memoria de video
;-----------------------------------------------------------------
pixel_mask:
;-- Si es la posicion 0, se escribe el valor $80 y se termina
ld a,b
or a ;-- Actualizar flag z para comproar si A es 0
jr z,pos0 ;-- A=0, es la posicion 0
;-- NO es la posicion 0
;-- Partimos del valor $80 y lo desplazamos B posiciones
;-- a la derecha
ld a, $80
to_pos
rrca
djnz to_pos
;-- En A tenemos el valor a escribir en memoria de video
;-- Devolverlo
ret
pos0:
;-- Valor a escribir en memoria video
ld a, $80
ret
;----------------------------------
;-- Esperar N frames...
;-- Cada frame son 20ms aprox...
;----------------------------------
wait:
halt ;-- 20 ms
halt
halt
halt
halt
halt
ret
end $8000
Este es el resultado:
Reto-2.7: Trampa
Estoy usndo las rutinas desarrolladas en mis experimetnos del Log
- Ejemplo:
01-reto-2-2.z80
include "zxspectrum.h"
org $8000
;------------- Pasar a Dark Mode ------------
BORDER BLACK
;-- Establecer los atributos permanentes
SET_ATTR_S BLACK, RED
;-- Borrar la pantalla
CALL ROM_CLS
;--------------------------------------------
;-- Trampa fija: Izquierda
ld hl, $4000
ld de, sprite_trampa_izq
call draw_sprite8
;----- Trampa derecha
ld hl, $4002
ld de, sprite_trama_der
call draw_sprite8
loop:
;------ Trama central: movil
;-- Sprite 0
ld hl, $4001
ld de, sprite_trampa0
call draw_sprite8
;-- Esperar
call wait_400ms
;-- Sprite 1
ld hl, $4001
ld de, sprite_trampa1
call draw_sprite8
;-- Esperar
call wait_100ms
;-- Sprite 2
ld hl, $4001
ld de, sprite_trampa2
call draw_sprite8
;-- Esperar
call wait_100ms
;-- Sprite 3
ld hl, $4001
ld de, sprite_trampa3
call draw_sprite8
;-- Esperar
call wait_100ms
;-- Sprite 4
ld hl, $4001
ld de, sprite_trampa4
call draw_sprite8
;-- Esperar
call wait_100ms
;-- Sprite 3
ld hl, $4001
ld de, sprite_trampa3
call draw_sprite8
;-- Esperar
call wait_100ms
;-- Sprite 2
ld hl, $4001
ld de, sprite_trampa2
call draw_sprite8
;-- Esperar
call wait_100ms
;-- Sprite 1
ld hl, $4001
ld de, sprite_trampa1
call draw_sprite8
;-- Esperar
call wait_100ms
jr loop
;-------------------
;-- SPRITES
;-------------------
;-- Parte iquierda de la trama: Fija
sprite_trampa_izq: DEFB $00, $00, $00, $FF, $AF, $DF, $AF, $FF
;-- Parte central: Movil
sprite_trampa0: DEFB $00, $00, $00, $FF, $35, $FA, $35, $FF
sprite_trampa1: DEFB $00, $00, $00, $7F, $1D, $7A, $1D, $7F
sprite_trampa2: DEFB $00, $00, $00, $3F, $0D, $3E, $0D, $3F
sprite_trampa3: DEFB $00, $00, $00, $1F, $07, $1F, $07, $1F
sprite_trampa4: DEFB $00, $00, $00, $0F, $03, $0F, $03, $0F
;-- Parte derecha: Fija
sprite_trama_der: DEFB $00, $00, $00, $FF, $55, $AB, $55, $FF
;-- Led medio-encendido
sprite_led_half: DEFB $00, $3C, $56, $4A, $52, $66, $3C, $00
;-- Led encendido
sprite_led_on: DEFB $00, $3C, $7E, $7E, $7A, $76, $3C, $00
include "Video.z80"
include "Keyboard.z80"
include "Delay.z80"
end $8000
Reto-2.8: Pelota con gravedad
TODO