Libro: Ensamblador para ZX‐Spectrum - Obijuan/Learn-zx-spectrum-asm GitHub Wiki
Experimentos y log de aprendizaje del libro "Ensamblador para ZX Spectrum ¿Hacemos un juego?", de Juan Antonio Rubio García
CONTENIDO
Introducción
Empezamos por el Hola mundo de la página 31. Lo primero es instalar las herramientas
El ensamblador pasmo se puede instalar directamente desde ubuntu (la versión 0.5.3)
sudo apt install pasmo
Ahora lo probamos:
obijuan@Hoth:~/Develop/Learn-zx-spectrum-asm
$ pasmo
Pasmo v. 0.5.3 (C) 2004-2005 Julian Albo
Usage:
pasmo [options] source object [symbol]
See the README file for details.
obijuan@Hoth:~/Develop/Learn-zx-spectrum-asm
$
Para instalarlo me he bajado el paquete ZEsarUX_linux-10.3-ubuntu22_x86_64.tar.gz
Lo he decomprimido y ejecutado el install.sh
obijuan@Hoth:~/Bin/ZEsarUX-10.3
$ sudo ./install.sh
Installing ZEsarUX under /usr ...
Install done
obijuan@Hoth:~/Bin/ZEsarUX-10.3
$
Ahora ya se puede arrancar zesaurx desde la línea de comandos
HOLA MUNDO
Este es el hola mundo:
org $8000
ret
end $8000
Lo compilamos con esta línea
pasmo --name HolaMundo --tapbas holamundo.asm holamundo.tap --log
Se genera el ejecutable holanumdo.tap y lo abrimos con el emulador
obijuan@Hoth:~/Develop/Learn-zx-spectrum-asm/01-Hello-world
$ zesarux holamundo.tap
ZEsarUX - ZX Second-Emulator And Released for UniX
https://github.com/chernandezba/zesarux
Copyright (C) 2013 Cesar Hernandez Bano
ZEsarUX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Please read the other licenses used in ZEsarUX, from the menu Help->Licenses or just open files from folder licenses/
ZEsarUX v.10.3 - La Abadia del Crimen edition. 10 May 2023
Uncompressing Linux... done, booting the kernel ... Just kidding ;)
Y nos aparece esta pantalla
En realidad me aparece la pantalla de inicio (que sólo debe aparecer la primera vez) pero no consigo que se quita. Al volver a arrancar siempre aparece... es molesto
Con esto ha mejorado un poco...
zesarux --disable-all-first-aid holamundo.tap
Pero todavía sale el mensaje bienvenida (que se quita a los pocos segundos)
OK!!! Ya he dado con la tecla:
zesarux --disable-all-first-aid --nowelcomemessage holamundo.tap
He añadido los scripts build.sh
y run.sh
para hacerlo más sencillo. Estamos listos para seguir con los ejemplos!!!
Escritura en la memoria de pantalla
Este es el programa holamundo2.asm
que escribe el valor 0xFF
en la dirección 0x4000
y por tanto se encienden 8 pixeles con los atributos actuales (color negro sobre fondo blanco)
;-- Programa hola mundo
;--- Comienzo de la RAM
org $8000
ld hl, $4000 ;--- Direccion de comienzo de la memoria de video
ld (hl), $FF ;--- Activar 8 pixeles
ret
end $8000
Este es el resultado:
Impresión de caracteres
Para imprimir caracteres en la pantalla usamos una rutina almacenada en la memoria ROM, a la que accedemos usando la instrucción rst $10
. El caracter almacenado en el acumulador se imprime en la posición actual de la pantalla
- holamundo3.asm:
org $8000
;----- Escribir el caracter 'A' en la posicion actual
;----- de la pantalla
ld a, 'A'
rst $10
ret
end $8000
- holamundo4.asm:
;--------------------------------------
;-- Ejemplo para imprimir el mensaje HI
;-- en la pantalla, caracter a caracter
;-- Cada caracter se lee de memoria y se imprime
org $8000
;-- Direccion de la cadena en HL
ld hl, msg
;-- Imprimir el primer caracter
ld a, (hl) ;-- Leer caracter
rst $10 ;-- Imprimirlo!
;-- Apuntar al siguiente caracter
inc hl
;-- Imprimir el siguiente carcter
ld a, (hl)
rst $10
;-- Terminar
ret
msg: defm 'HI'
end $8000
- holamundo5.asm
;------------------------------------------------------------------------------
;-- Impresión de una cadena en la pantalla
;------------------------------------------------------------------------------
org $8000
;-- Cargar la direccion de la cadena
ld hl, msg
bucle:
;-- Leer caracter
ld a, (hl)
;-- Si es 0, terminar
or a ;-- Evaluar z
jr z, fin
;-- Imprimir caracter
rst $10
;-- Apuntar al siguiente caracter
inc hl
;-- Repetir
jr bucle
fin:
ret
;--- Cadena terminada en 0
msg: defm 'Holi...',$00
end $8000
Subrutinas en ROM
LOCATE
Cuando se trabaja en Basic, la esquina superior izuierda tiene las coordenadas 0,0. Usando el print AT podemos situar caracteres en las diferentes posiciones. El primer parametro es Y (Filas) y el segundo X las columnas. Las filas van de 0 a 21 y las columnas de 0 a 31. Este programa en basic sirve para imprimir una "A" en cada esquina. Se cambia el borde a otro color para visualizar los límites
5 BORDER 1
10 PRINT AT 0,0;"A"
20 PRINT AT 21,0;"A"
30 PRINT AT 0,31;"A"
40 PRINT AT 21,31;"A"
Para introducir el programa es necesario tener a mano un teclado del spectrum:
La tecla Symbol-shift es el "Control". Para introducir PRINT
hay que dar a la P
. Para el comando AT
hay que pulsar Ctrl-I. Para el punto y coma ;
Ctrl-O y para las comillas "
Ctrl-P
Esto es lo que sale al ejecutarlo:
Vamos a hacerlo en ensamblador. Lo primero es aprender a cambiar el borde. Eso se hace escribiendo el color en el puerto 0xFE
- holamundo6.asm
;-----------------------------------------------------------------------------
;-- Ejemplo para cambiar el borde a otro color
;-----------------------------------------------------------------------------
;--- Puerto a escribir para cambiar el color
;--- del borde
BORDER: EQU $FE
org $8000
ld a, 1 ;-- Color azul
;-- Escribir en el puerto
out (BORDER), a
;-- Terminar
ret
end $8000
Ahora ya podemos usar la rutina de locate para la ROM. Esta rutina usa un sistema de coordenadas diferentes. La esquina superior izquierda se encuentra en la posición (24, 33), la que nosotros llamamos (0,0) en Basic con Print-AT
Este es el programa que escribe 4 Aes en las esquinas. Vemos que las As inferiores no están abajo del todo: esa es la parte que usa el spectrum para comunicarse con el usuario. Si imprimimos ahí se produce un scroll de la pantalla
;-----------------------------------------------------------------------------
;-- Impresion del caracter 'A' en cada esquina de la pantalla, usando
;-- la rutina ROM de locate
;-----------------------------------------------------------------------------
;--- Puerto a escribir para cambiar el color
;--- del borde
BORDER: EQU $FE
;-------------------------------------------------------------
;-- RUTINAS DE LA MEMORIA ROM
;-------------------------------------------------------------
;-------------------------------------------------------
;-- LOCATE: Posicionar el cursor (x,y)
;--
;-- ENTRADA:
;-- B : Coordenada y
;- C : Coordenada X
;--
;-- Esquina superior izquierda: (y=24, x=33)
;-- Esquina superior derecha: (y=24, x=2)
;-- Esquina inferior izquierda (y=3, x=33)
;-- Esquina inferior derecha: (y=3, x=2)
;-------------------------------------------------------
LOCATE: EQU $0DD9
;-----------------------------------
;-- MAIN
;-----------------------------------
org $8000
;-- Poner borde Azul
ld a, 1
out (BORDER), a
;-- Caracter en la esquina superior izquierda (24, 33)
ld b, 24
ld c, 33
call LOCATE
ld a, 'A'
rst $10
;-- Esquina superior derecha (24, 2)
ld b, 24
ld c, 2
call LOCATE
ld a, 'A'
rst $10
;-- Esquina inferior izquierda (3, 33)
ld b, 3
ld c, 33
call LOCATE
ld a, 'A'
rst $10
;-- Esquina inferior derecha (3, 2)
ld b, 3
ld c, 2
call LOCATE
ld a, 'A'
rst $10
;-- Terminar
;-- (bucle infinito)
inf: jr inf
end $8000
Para podemos escribir en las mismas coordenadas que con el PRINT AT
, hay que hacer una pequeña transformación. Este es el nuevo programa:
;-----------------------------------------------------------------------------
;-- Impresion del caracter 'A' en cada esquina de la pantalla, usando
;-- la rutina ROM de locate
;-----------------------------------------------------------------------------
;--- Puerto a escribir para cambiar el color
;--- del borde
BORDER: EQU $FE
;-------------------------------------------------------------
;-- RUTINAS DE LA MEMORIA ROM
;-------------------------------------------------------------
;-------------------------------------------------------
;-- LOCATE: Posicionar el cursor (x,y)
;--
;-- ENTRADA:
;-- B : Coordenada y
;- C : Coordenada X
;--
;-- Esquina superior izquierda: (y=24, x=33)
;-- Esquina superior derecha: (y=24, x=2)
;-- Esquina inferior izquierda (y=3, x=33)
;-- Esquina inferior derecha: (y=3, x=2)
;-------------------------------------------------------
LOCATE: EQU $0DD9
;-----------------------------------
;-- MAIN
;-----------------------------------
org $8000
;-- Poner borde Azul
ld a, 1
out (BORDER), a
;-- Caracter en la esquina superior izquierda (0, 0)
ld b, 24 - 0
ld c, 33 - 0
call LOCATE
ld a, 'A'
rst $10
;-- Esquina superior derecha (0, 31)
ld b, 24 - 0
ld c, 33 - 31
call LOCATE
ld a, 'A'
rst $10
;-- Esquina inferior izquierda (21, 0)
ld b, 24 - 21
ld c, 33 - 0
call LOCATE
ld a, 'A'
rst $10
;-- Esquina inferior derecha (21, 31)
ld b, 24 - 21
ld c, 33 - 31
call LOCATE
ld a, 'A'
rst $10
;-- Terminar
;-- (bucle infinito)
inf: jr inf
end $8000
El resultado es el mismo que antes
CLS
Vamos a probar la rutina de CLS de la ROM. Lo que hace es eliminar todos los caracteres que haya y poner los atributos que hay almacenados en la variable del sistema ATTR_S
;-----------------------------------------------------------------------------
;-- Ejemmplo de borrado de la pantalla llamando a la rutina ROM CLS
;-----------------------------------------------------------------------------
;------------------------------------
;--- VARIABLES DEL SISTEMA
;------------------------------------
;-- Atributos permanentes del sistema. Es lo que se usa cuando se llama
;-- a CLS
;-- Formato: Flash, Bright, paper (3-bits), ink (3-bits)
ATTR_S: EQU $5C8D
;-------------------------------------------------
;-- Rutina de CLS
;-- Altera el valor de los registros AF, BC, DE Y HL
;----------------------------------------------------
CLS: EQU $0DAF
org $8000
;-- Establecer los atributos permanentes
ld a, $0e ;-- Paper=1 (azul) ink = 6 (amarillo?)
ld hl, ATTR_S
ld (hl), a
;-- Borrar la pantalla
CALL CLS
;-- Terminar
ret
end $8000
Hola mundo final
;-----------------------------------------------------------------------------
;-- Hola mundo final: Se cambia el color de la pantalla y se imprime
;-- un mensaje en la parte central
;-----------------------------------------------------------------------------
;--- Puerto a escribir para cambiar el color
;--- del borde
BORDER: EQU $FE
;------------------------------------
;--- VARIABLES DEL SISTEMA
;------------------------------------
;-- Atributos permanentes del sistema. Es lo que se usa cuando se llama
;-- a CLS
;-- Formato: Flash, Bright, paper (3-bits), ink (3-bits)
ATTR_S: EQU $5C8D
;-- Atributos de las impresiones actuales
ATTR_T: EQU $5C8F
;-------------------------------------------------------------
;-- RUTINAS DE LA MEMORIA ROM
;-------------------------------------------------------------
;-------------------------------------------------
;-- Rutina de CLS
;-- Altera el valor de los registros AF, BC, DE Y HL
;----------------------------------------------------
CLS: EQU $0DAF
;-------------------------------------------------------
;-- LOCATE: Posicionar el cursor (x,y)
;--
;-- ENTRADA:
;-- B : Coordenada y
;- C : Coordenada X
;--
;-- Esquina superior izquierda: (y=24, x=33)
;-- Esquina superior derecha: (y=24, x=2)
;-- Esquina inferior izquierda (y=3, x=33)
;-- Esquina inferior derecha: (y=3, x=2)
;-------------------------------------------------------
LOCATE: EQU $0DD9
org $8000
;--- Cambiar el borde a color azul
ld a, 1
out (BORDER), a
;-- Establecer atributos del sistema y actuales
ld a, $0e
ld hl, ATTR_T
ld (hl), a
ld hl, ATTR_S
ld (hl), a
;-- Limpiar la pantalla
CALL CLS
;-- Situarse en (10, 2)
ld b, 24 - 10
ld c, 33 - 2
call LOCATE
;-- Imprimir el mensaje en la pantalla
ld hl, msg
bucle:
ld a, (hl) ;-- Leer caracter
or a ;-- Comprobar si a = 0
jr z, fin ;-- Si es 0, terminar
;--Imprimir caracter
rst $10
;-- Apuntar al siguiente
inc hl
;-- Repetir
jr bucle
fin:
;-- Terminar
inf: jr inf
msg: defm "Hola ensamblador zx Spectrum", $00
end $8000
Experimentos
Antes de seguir con los siguientes capítulos del libo voy a hacer algunos experimentos por mi cuenta... El primero va a ser rellenar la memoria de vídeo... para ver las diferentes secciones
- 01-fill.asm
;-----------------------------------------------------------------------------
;-- Rellenar toda la memoria de video con unos para encender todos
;-- los pixeles
;-----------------------------------------------------------------------------
;-- Puerto para el borde
BORDER: EQU $FE
org $8000
;--- Poner el borde amarillo
ld a, 6
out (BORDER), a
ld hl, $4000 ;-- Apuntar a la memoria de video
bucle:
;-- Activar los 8 pixeles
ld a, $ff
ld (hl), a
;-- Condicion de terminacion
ld a, h
cp $57
jr nz, next
;-- Comprobar si l=0xFF
ld a, l
cp $FF
jr z, fin
next:
;-- Incrementar la direccion
inc hl
;-- Hacer una pausa para ver cómo se rellena
;-- la pantalla
call wait
;-- Repetir
jr bucle
fin:
jr fin
;-------------------------------
;-- Realizar un pausa
;--------------------------------
wait:
ld a,$ff
loop:
dec a
nop
jr nz, loop
ret
end $8000
Se ha añadido una rutina de pausa para ver el relleno
- 02-two-lines.asm
En este ejemplo se dibujan dos lineas horizontales,uno justo debajo de la otra. Para ello hay que sumar 0x100 a la dirección de HL
;----------------------------------------------------------------------------
;-- Programa para dibujar dos líneas horizontales, una debajo de la otra
;----------------------------------------------------------------------------
;-- Puerto para el borde
BORDER: EQU $FE
org $8000
;--- Cambiar color del fondo
ld a, 6
out (BORDER), a
;-- Puntero a la memoria de video
ld hl, $4000
;-- Numero de lineas horizontales a dibujar
;-- Entre 1 y 8
ld b,2
loop:
call lineah
;-- Incrementar en 0x100 (256 bytes)
;-- Ahí empieza la linea justo debajo
inc h
;-- Una linea menos
dec b
jr nz, loop
ret
;------------------------------------------------------------
;-- LINEAH: Pintar una linea horizontal
;--
;-- ENTRADAS:
;-- * HL: Puntero a la memoria de video
;-- MODIFICA:
;-- * Registros AF, B
;------------------------------------------------------------
lineah:
;-- Guardar direccion inicial
push hl
;-- Guardar bc
push bc
;-- Numero de bytes para completar una linea horizontal
ld b, $20
lineah_loop:
ld a, $ff
ld (hl), a
;-- Pausa para ver la linea dibujarse
halt
;-- Apuntar a la siguiente posicion
inc hl
;-- Decrementar contador de bytes
dec b
ld a, b
or a
jr nz, lineah_loop
;-- Recuperar bc
pop bc
;-- Recuperar direccion inicial
pop hl
;-- Retornar
ret
end $8000
ZX PONG
Paso 1: Dibujando por pantalla
- Memoria de vídeo: 0x4000 - 0x57FF
- Pixeles: 256 * 192
- Celdas: 32 x 24
El formato de las direcciones es:
- 010T-TSSS-LLLC-CCCC
- TT: Tercio (0-2)
- SSS: Scanline (0-7)
- LLL: Línea (0-7)
- CCCCC: Columna (0-31)
Rutina NextScan
Esta rutina parte de una dirección de la memoria de video y nos devuelve la dirección de la siguiente scanline. La rutina está guardada en el fichero Video.asm
:
;--------------------------------------------------------------------
;-- NextScan: Obtener la direccion del siguiente scanline
;--
;-- ENTRADA:
;-- * HL: Scanline actual
;-- SALIDA:
;-- * HL: Scanline siguiente
;--
;-- Formato direccion: 010TTSSS LLLCCCCC
;---------------------------------------------------------------------
NextScan:
;-- Incrementar scanline (Módulo 7)
inc h
ld a,h
and $7
ret nz ;-- Si no es cero hemos terminado
;-- El scanline es 8 (mayor de 3 bits)
;-- Hay que pasar a la siguiente linea (incrementar LLL)
ld a,l ;-- A = LLLC CCCC
add a,$20 ;-- + 0010 0000
ld l,a
ret c ;-- Si hay acarreo hemos terminado
;-- Decrementar el tercio (se había incrementado en el paso 1)
ld a, h ;-- A = 010T TSSS
sub $08 ;-- 0000 1000
ld h, a
ret
Desde el fichero Main.asm
hacemos un bucle para dibujar una línea vertical de 192 scanlines (192 píxeles de altura).
org $8000
;-- Dibujar Linea vertical izquierda
;-- Apuntar a la celda (0,0)
ld hl, $4000
ld b, 192 ;-- Recorrer las 192 scanlines...
loop:
ld (hl), $3c ;-- Escribir en memoria de video
call NextScan ;-- Siguiente scanline
;-- DEBUG: Hacer una pausa para ver como se
;-- dibuja la linea de arriba hacia abajo
;halt
djnz loop
include "Video.asm"
end $8000
Este es el resultado:
Rutina PreviousScan
Esta rutina es similar a NextScan pero nos devuelve el scanline anterior, en vez del siguiente:
;--------------------------------------------------------------------
;-- PreviousScan: Obtener la direccion scanline anterior
;--
;-- ENTRADA:
;-- * HL: Scanline actual
;-- SALIDA:
;-- * HL: Scanline anterior
;--
;-- Formato direccion: 010TTSSS LLLCCCCC
;---------------------------------------------------------------------
PreviousScan:
;-- Decrementar el scanline
ld a,h
dec h
and $7
ret nz ;-- Nos quedamos en la fila actual
;-- El scanline está en la fila anterior. Hay que decrementar
;-- la fila
ld a, l
sub $20
ld l,a
ret c ;-- Hemos cambiado de fila, y de tercio
;-- Hay que incrementar el tercio para que se quede como está
ld a,h
add a, $08
ld h,a
ret
Ampliamos el programa Main.asm
para dibujar una línea vertical por la derecha, de abajo hacia arriba para probar PreviousScan
;--------------- Dibujar linea vertical derecha
;-- La dibujamos de abajo hacia arriba, para probar la
;-- rutina PreviousScan
ld hl, $57FF ;-- Posicion: (255,192)
ld b,192 ;-- Recorrer las 192 scanlines
loop2:
ld (hl), $3c
call PreviousScan
;-- DEBUG: Hacer una pausa para ver el dibujo
;-- de la linea
;-- halt
djnz loop2
ret
Este es el resultado:
Paso 2: Teclas de control
Hay 8 semifilas. Para el escaneo primero se envía el número de semifila (1-8) pero en lógica negativa: la semifila activa se pone a 0. El resultado se lee por el puerto del teclado ($FE). Si la tecla está pulsada se lee un bit a 0, ó un 1 si NO está pulsada
Rutina ScanKeys
El código de Controls.asm
es:
;--- Puerto del teclado
TECLADO EQU $FE
;-- Semifila 1. Teclas: CAPS Z X C V
;-- b4 b3 b2 b1 b0
;-- V C X Z CAPS
SFILA_CAPS_V EQU $FE
KEY_Z EQU $01
;-- Semifila 2: Teclas: A S D F G
;-- b4 b3 b2 b1 b0
;-- G F D S A
SFILA_AG EQU $FD
KEY_A EQU $00 ;-- Mascara para tecla A
;-- Semifila 5: Teclas: 6 7 8 9 0
;-- b4 b3 b2 b1 b0
;-- 6 7 8 9 0
SFILA_6_0 EQU $EF
KEY_0 EQU $00
;-- Semifila 6: Teclas P O I U Y
;-- b4 b3 b2 b1 b0
;-- Y U I O P
SFILA_P_Y EQU $DF
KEY_O EQU $01
;-- Mascara de los bits en el registro D
;-- Para la lectura de las teclas
BIT_A EQU $00 ;-- Bit para la tecla A
BIT_Z EQU $01 ;-- Bit para la tecla Z
BIT_0 EQU $02 ;-- Bit para la tecla 0
BIT_O EQU $03 ;-- Bit para la tecla O
;----------------------------------------------------------------------------
;-- ScanKeys:
;--
;-- Registro D:
;-- b4 b3 b2 b1 b0
;-- Z A
;--- Bit a 1: Tecla pulsada
;-- Bit a 0: NO pulsada
;----------------------------------------------------------------------------
ScanKeys:
ld d, 0 ;-- Registro D a 0
ScanKeys_A:
ld a, SFILA_AG ;-- Semifila donde esta la tecla A
in a, (TECLADO) ;-- Leer teclado!
bit KEY_A, a ;-- Comprobar si Bit tecla A esta a 0
jr nz, ScanKeys_Z ;-- Tecla A NO pulsada... pasar a la siguiente
;-- Tecla A pulsada
;-- Activar el flag de D correspondiente
set BIT_A, d
;-- Continuar el scaneo...
ScanKeys_Z:
ld a, SFILA_CAPS_V
in a, (TECLADO)
bit KEY_Z, a
jr nz, ScanKeys_0
;-- Tecla Z pulsada
;-- Activar el flag de D correspondiente
set BIT_Z, d
;-- Comprobacion de las dos teclas A-Z a la vez
ld a, d
sub $03
jr nz, ScanKeys_O ;-- NO se ha pulsado a la vez
ld d, a ;-- Poner D a 0
ScanKeys_0:
ld a, SFILA_6_0
in a, (TECLADO)
bit KEY_0, a
jr nz, ScanKeys_O
;-- Tecla 0 pulsada
;-- Activar el flag de D correspondiente
set BIT_0, d
ScanKeys_O:
ld a, SFILA_P_Y
in a, (TECLADO)
bit KEY_O, a
ret nz
;-- Tecla O pulsada
;-- Activar el flag de D correspondiente
set BIT_O,d
;-- Comprobar las dos teclas 0-O
ld a, d
and $0c ;-- Aislar los bits de 0 y O
cp $0c ;-- Comprobar si las dos teclas se ha pulsado a la vez
ret nz
ld a, d ;-- Se han pulsado...
and $03 ;-- Quedarse con los bits de A y Z
ld d, a
ret
Este es el Main.asm
:
org $8000
;-- HL direccion esquina superior izquierda del video
ld hl, $4000
bucle:
call ScanKeys
ld (hl), d
jr bucle
include "Controls.asm"
end $8000
Al apretar las teclas A,Z,O,0 se activan los píxeles correspondientes. En esta animación se muestra el funcionamiento cuando se aprietan las diferentes teclas:
Paso 3: Palas y línea central
Los atributos de la pantalla empiezan en la dirección $5800
. Hay 32 columnas y 24 líneas. El atributo es el mismo para cada celda (grupo de 8x8 píxeles)
Rutina CLS
Primero se ponen todos los bytes de la memoria de vídeo a 0 (para borrar todos los píxeles) y luego se establecen los atributos a fondo negro y tinta blanca
;----------------------------------------------------------------------------
;-- Cls: Borrar la pantalla con tinta blanca y fondo negro
;----------------------------------------------------------------------------
Cls:
;-- Rellenar toda la memoria de video con ceros para limpiar...
ld hl, $4000 ;-- HL: Puntero a la memoria de Video
ld (hl), $00 ;-- Borrar posicion actual
ld de, $4001 ;-- Siguiente posicion
ld bc, $17ff ;-- Tamaño de la memoria de video
ldir
;-- Establecer los atributos de la memoria de video
ld hl, $5800
ld (hl), $07 ;-- Fondo negro, tinta blanca
ld de, $5801
ld bc, $2ff ;-- Tamaño de la memoria de atributo
ldir
ret
En el programa principal se pone el borde rojo y se llama a CLS para poner el fondo negro
;--- Puerto del borde
BORDE EQU $FE
org $8000
;-- Borde rojo
ld a, $02
out (BORDE), a
;-- Borrar pantalla: Tinta blanca, Fondo negro
call Cls
;-- Terminar
ret
include "Video.asm"
end $8000
Rutina PrintLine
La red está formada por 24 caracteres. El Sprite de la red (8x8) tiene la línea superior en blanco, la inferior en blanco y la central es una línea vertical de 4 píxeles de ancho. Los sprintes se define en el fichero Sprites.asm
;-- Red
ZERO: EQU $00
LINE: EQU $80
Esta es la rutina:
;-----------------------------------------------------------------------------
;-- PrintLine: Imprimir la linea central de la red
;-----------------------------------------------------------------------------
PrintLine:
ld b, 24 ;-- Tamaño: 24 líneas
ld hl, $4010 ;-- Columna central, Primera línea (16,0)
PrintLine_loop:
;-- No hay parte superior de la linea
ld (hl), ZERO
inc h
push bc
ld b, 6 ;-- Pintar 6 lineas verticales
PrintLine_loop2:
ld (hl), LINE ;-- Pintar linea vertical
inc h ;-- Siguiente scanline
djnz PrintLine_loop2
pop bc
;-- Pintar ultimo tramo: sin linea
ld (hl), ZERO
call NextScan
;-- Repetir para las 24 líneas
djnz PrintLine_loop
ret
Este es el resultado:
Rutina PrintPaddle
Las raquetas son de 24 scanlines de alto y un byte de ancho. El sprite de la raqueta tiene 4 bits de ancho. Utilizamos dos variables para almacenar las posiciones de las raquetas de los jugadores. Este es el fichero Sprite.asm
;-- Red
ZERO: EQU $00
LINE: EQU $80
;--- Raquetas
PADDLE: EQU $3C
PADDLE_TOP: EQU $00 ;-- Posicion superior de la raqueta en el campo
PADDLE_BOTTOM: EQU 168 ;-- Posicion inferior de la raqueta en el campo
;-- 010TTSSS LLLCCCCC
paddle1pos: DW $4861 ;-- 01001000 01100001 (1, 11)
paddle2pos: DW $487e ;-- 01001000 01111110 (30, 11)
Esta es la rutina para pintar las raquetas:
;----------------------------------------------------------------------------
;-- PrintPaddle: Imprimir la raqueta
;--
;-- ENTRADA:
;-- * HL: Posicion de la raqueta (direccion)
;----------------------------------------------------------------------------
PrintPaddle:
;-- Parte superior de la raqueta: blanco
ld (hl), ZERO
call NextScan
;-- Pintar la parte visible de la pala
;-- Longitud: 22 scanlines
ld b, 22
printPaddle_loop:
ld (hl), PADDLE
call NextScan
djnz printPaddle_loop
;-- La última línea de la pala en blanco
ld (hl), ZERO
ret
Este es el resultado:
Movimiento de las palas
Con las teclas 'A' y 'Z' se mueve la raqueta izquierda y con '0' y 'O' la raqueta derecha
;--- Puerto del borde
BORDE EQU $FE
org $8000
;-- Borde Rojo
ld a, $02
out (BORDE), a
;-- Borrar pantalla: Tinta blanca, Fondo negro
call Cls
;-- Dibujar linea
call PrintLine
loop:
call ScanKeys
MovePaddle1Up:
bit 0, d ;-- Comprobar si tecla A pulsada
jr z, MovePaddle1Down ;-- Si no pulsada, salta
;-- Tecla A pulsada: Movimiento hacia arriba
;-- Comprobar si hemos alcanzado el límite superior
ld hl, (paddle1pos)
ld a, PADDLE_TOP ;-- Margen superior
call CheckTop ;-- Evaluar si limite alcanzado
jr z, MovePaddle2Up ;-- Limite alcanzado. No mover. Pasar
;-- a la raqueta 2
;-- Limite no alcanzado
;-- Obtener la direccion
call PreviousScan ;-- Obtener scanline anterior a la pala
ld (paddle1pos), hl ;-- Es la nueva posicion
jr MovePaddle2Up ;-- Saltar
MovePaddle1Down:
bit 1, d ;-- Comprobar si tecla Z pulsada
jr z, MovePaddle2Up
;-- Tecla Z pulsada: Movimiento hacia abajo
;-- Comprobar si limite inferior alcanzado
ld hl, (paddle1pos)
ld a, PADDLE_BOTTOM
CALL CheckBottom
jr z, MovePaddle2Up ;-- Limite alcanzado. No mover. Pasar a
;-- la raqueta 2
;-- Limite no alcanzado
;-- Obtener la direccion de la siguiente posicion
call NextScan
ld (paddle1pos), hl
MovePaddle2Up:
bit 2, d ;-- Comprobar si tecla 0 pulsada
jr z, MovePaddle2Down
ld hl, (paddle2pos)
ld a, PADDLE_TOP
call CheckTop
jr Z, MovePaddleEnd
call PreviousScan
ld (paddle2pos), hl
jr MovePaddleEnd
MovePaddle2Down:
bit 3, d ;-- Comprobar si tecla O pulsada
jr z, MovePaddleEnd
ld hl, (paddle2pos)
ld a, PADDLE_BOTTOM
call CheckBottom
jr z, MovePaddleEnd
call NextScan
ld (paddle2pos),hl
MovePaddleEnd:
;-- Imprimir raqueta 1
ld hl, (paddle1pos)
call PrintPaddle
;-- Imprimir raqueta 2
ld hl, (paddle2pos)
call PrintPaddle
halt
jr loop
;-- Terminar
ret
include "Controls.asm"
include "Sprite.asm"
include "Video.asm"
end $8000
En esta animación se muestra el resultado: