0x02 Ensamblador ZX Spectrum Marciano – Pintando UDG

En este capítulo de Ensamblador ZX Spectrum Marciano, vamos empezar a dibujar usando UDG. El mapa de caracteres del ZX Spectrum está compuesto por doscientos cincuenta y seis valores, de los cuales podemos redefinir veintiuno, en concreto los que se encuentran entre el $90 (144) y el $A4 (164), ambos inclusive.

Antes de empezar, creamos una carpeta que se llame Paso02 y copiamos el arhivo Var.asm (dónde definimos los gráficos que vamos a usar) desde la carpeta Paso01.

¿Dónde están los UDG?

El valor de la dirección de memoria $5C7B contiene la dirección de memoria donde están los gráficos definidos por el usuario, por lo que lo único que tenemos que hacer es cargar en esa dirección de memoria, la dirección dónde están definidos nuestros gráficos. Una vez que lo tengamos, al pintar con RST $10 cualquier carácter comprendido entre $90 y $A4, pintara los gráficos que hemos definido.

Vamos a crear un nuevo fichero llamado Const.asm y vamos a añadir la línea siguiente:

; Dirección de memoria donde se cargan los gráficos definidos por el usuario.
UDG:			EQU $5c7b

En esta constante guardamos la dirección de memoria dónde cargaremos la dirección dónde están nuestros gráficos.

Pintamos nuestros UDG

Es el momento de hacer nuestra primera prueba, vamos a pintar los UDG. Creamos un fichero llamado Main.asm y vamos a añadir las líneas siguientes:

org			$5dad

Main:
ld			a, $90
ld			b, $15

Loop:
push		af
rst			$10
pop			af
inc			a
djnz		Loop

ret

end			Main

Lo primero que hacemos es indicar dónde se va a cargar el programa, ORG $5DAD. El programa lo cargamos en la posición $5DAD (23981), ya que Batalla espacial va a ser un programa compatible con modelos 16K.

La siguiente línea es una etiqueta, Main, el punto de entrada del programa.

Lo siguiente que hacemos es cargar 144 en A, LD A, $90, y 21 en B, LD B, $15, para pintar desde el carácter 144 al 164, haciendo un total de veintiún caracteres.

El siguiente paso es hacer el bucle de veintiuna iteraciones, empezando con la etiqueta del mismo, Loop, y a continuación preservamos en la pila el valor del registro A, PUSH AF, lo cual es muy importante ya que la siguiente instrucción, RST $10, imprime en pantalla el carácter cuyo código esté cargado en el registro A, y luego modifica el valor de dicho registro. Acto seguido recuperamos de la pila el valor del registro A, POP AF.

A continuación, incrementamos A, INC A, para que apunte al siguiente carácter y decrementamos B y saltamos a Loop si no ha llegado a cero, DJNZ Loop. Por último volvemos al Basic, RET.

Con la última línea le indicamos a PASMO que debe incluir en el cargador Basic una llamada a la dirección de memoria donde se encuentra la etiqueta Main.

Es el momento de compilar y ver los resultados en el emulador.

pasmo --name Marciano --tapbas Main.asm Maciano.tap --public

Pero, ¿hemos pintado nuestros gráficos?

Ensamblador ZX Spectrum, pitando UDG
Ensamblador ZX Spectrum, pitando UDG

Como podemos ver, hemos pintado las letras mayúsculas de la A a la U, esto es debido a que en ningún momento hemos indicado dónde están nuestros gráficos.

Seguimos en el archivo Main.asm, justo debajo de la etiqueta Main añadimos las líneas siguientes:

ld			hl, udgsCommon
ld			(UDG), hl

Cargamos en HL la dirección de memoria dónde están nuestros gráficos, LD HL, udgsCommon, y luego cargamos ese valor en la dirección de memoria en la que hay que indicar dónde están nuestros gráficos, LD (UDG), HL.

Dado que tanto udgsCommon, como UDG no están definidas en el fichero Main.asm, justo detrás de la instrucción RET hay que añadir los includes para los ficheros Const.asm y Var.asm.

include	"Const.asm"
include	"Var.asm"

Ahora sí, podemos volver a compilar el programa, cargarlo en el emulador y ver nuestros gráficos en pantalla.

Ensamblador ZX Spectrum, pitando UDG
Ensamblador ZX Spectrum, pitando UDG

Mucho mejor, ¿verdad? Pero, hemos pintado veintiún gráficos: la nave, el disparo, la explosión, el marco, el carácter vacío, los cuatro gráficos del enemigo uno, y dos gráficos del enemigo dos. ¿Cómo vamos a pintar los otros dos gráficos del enemigo dos y los gráficos de los veintiocho enemigos restantes?

Cargamos los UDG de los enemigos

Si observáis la definición de los gráficos, la primera etiqueta se llama udgsCommon, y esto debería darnos una pista de como lo vamos a hacer. Como UDG comunes tenemos definidos quince gráficos (nave, disparo, explosión, marco y blanco), por lo que vamos a definir treinta y dos bytes para poder ir volcando en ellos los gráficos de los enemigos; lo vamos a hacer así porque los enemigos son uno por nivel, y la operación de volcado solo la tenemos que hacer una vez, justo con el cambio de nivel.

En el archivo Var.asm, justo por encima de la etiqueta udgsEnemiesLeve1 añadimos las siguientes líneas:

udgsExtension:
db $00, $00, $00, $00, $00, $00, $00, $00	; $9f Left/Up
db $00, $00, $00, $00, $00, $00, $00, $00	; $a0 Rigth/Up
db $00, $00, $00, $00, $00, $00, $00, $00	; $a1 Left/Down
db $00, $00, $00, $00, $00, $00, $00, $00	; $a2 Rigth/Down

En este bloque de memoria es dónde vamos a ir volcando los gráficos de los enemigos, dependiendo del nivel en el que nos encontremos.

Probad a compilar ahora y observad que pinta. ¿Faltan los gráficos del enemigo uno verdad? Está pintando udgsExtension.

Creamos un nuevo archivo, Graph.asm, y vamos a implementar en él la rutina que carga en udgsExtension los gráficos de los enemigos de cada nivel, dato que recibe en A.

Para calcular la dirección de memoria donde se encuentran los gráficos, vamos a multiplicar el nivel por treinta y dos (bytes que ocupan los gráficos) y el resultado se lo vamos a sumar a la dirección de memoria donde se encuentran los gráficos del primer enemigo.

LoadUdgsEnemies:
dec			a
ld			h, $00
ld			l, a

Dado que los niveles van de uno a treinta, decrementamos A, DEC A, para que no sume un nivel de más (si el nivel es cero, tiene que sumar cero veces a udgsEnemies, si es dos una vez, etc.).

Lo siguiente es cargar el nivel en HL, para lo cual cargamos cero en H, LD H, $00, y el nivel en L, LD L, A.

add			hl, hl
add			hl, hl
add			hl, hl
add			hl, hl
add			hl, hl

Multiplicamos el nivel por treinta y dos, sumando HL a si mismo cinco veces, ADD HL, HL. La primera suma es igual a multiplicar por dos, la segunda por cuatro y las siguientes por ocho, por dieciséis, y por treinta y dos.

ld			de, udgsEnemiesLevel1
add			hl, de
ld			de, udgsExtension
ld			bc, $20
ldir
ret

Por último, cargamos la dirección de los gráficos del enemigo uno en DE, LD DE, udgsEnemiesLevel1, y se lo sumamos a HL, ADD HL, DE, cargamos la dirección de la extensión de udgs en DE, LD DE, udgsExtension, cargamos en BC en número de bytes que vamos a cargar en udgsExtension, LD BC, $20, y cargamos los treinta y dos bytes de los gráficos del enemigo del nivel a udgsExtension, LDIR. Finalmente salimos, RET.

El aspecto final de la rutina es el siguiente:

; ----------------------------------------------------------------------------
; Carga los gráficos definidos por el usuario relativos a los enemigos
;
; Entrada: A -> Nivel de 1 a 30
;
; Altera el valor de los registros A, BC, DE y HL
; ----------------------------------------------------------------------------
LoadUdgsEnemies:
dec			a 			; Decrementa A para que no sume un nivel de más

ld			h, $00 
ld			l, a		; Carga en HL el nivel
add 		hl, hl		; Multiplica por 2
add			hl, hl 		; por 4
add			hl, hl 		; por 8
add			hl, hl 		; por 16
add			hl, hl 		; por 32
ld			de, udgsEnemiesLevel1	; Carga la dirección de los gráficos del
						; enemigo 1 en DE
add			hl, de 		; Lo suma a HL
ld 			de, udgsExtension 	; Carga en DE la dirección de la extensión
ld 			bc, $20 	; Carga en BC el número de bytes a copiar, 32
ldir 					; Copia los bytes del enemigo en los de extensión

ret

Y ahora vamos a probar la nueva rutina, para lo cual vamos a editar el archivo Main.asm, empezando por cambiar la instrucción LD B, $15, justo encima de la etiqueta Loop, y la dejamos como sigue, para imprimir los quince primeros UDG, los comunes:

ld			b, $0f

El resto lo vamos a implementar entre la instrucción DJNZ Loop y la instrucción RET.

ld			a, $01
ld			b, $1e

Cargamos en A el nivel uno, LD A, $01, y en B el número de niveles totales (treinta), LD B, $1E. Implementamos un bucle para pintar los enemigos de los treinta niveles.

Loop2:
push		af
push		bc
call		LoadUdgsEnemies

Preservamos los valores de AF, PUSH AF, y de BC, PUSH BC, ya que usamos A y B para controlar que enemigos pintamos y las iteraciones del bucle. A continuación, llamamos a la rutina que carga los gráficos del enemigo del nivel en udgsExtension, CALL LoadUdgsEnemies.

ld			a, $9f
rst			$10
ld			a, $a0
rst			$10
ld			a, $a1
rst			$10
ld			a, $a2
rst			$10

Los caracteres correspondientes a los gráficos de los enemigos son $9F, $A0, $A1 y $A2; los vamos cargando en A, LD A, $9F, y pintando, RST $10. Repetimos la operación con $A0, $A1 y $A2.

pop			bc
pop			af
inc			a
djnz		Loop2

Recuperamos el valor de BC, POP BC, de AF, POP AF, incrementamos A para pasar al siguiente nivel, INC A, y repetimos hasta que B sea 0, DJNZ Loop2.

Por último, a final del fichero y antes de END Main, incluimos el fichero Graph.asm.

include	"Graph.asm"

El código final de Main.asm es el siguiente:

org		$5dad

Main:
ld		hl, udgsCommon
ld		(UDG), hl

ld		a, $90
ld		b, $0f
Loop:
push	af
rst		$10
pop		af
inc		a
djnz	Loop

ld		a, $01
ld		b, $1e
Loop2:
push	af
push	bc
call	LoadUdgsEnemies
ld		a, $9f
rst		$10
ld		a, $a0
rst		$10
ld		a, $a1
rst		$10
ld		a, $a2
rst		$10
pop		bc
pop		af
inc 	a
djnz	Loop2

ret

include	"Const.asm"
include	"Var.asm"
include	"Graph.asm"

end		Main

Compilamos y cargamos en el emulador; ya pintamos todos nuestros gráficos.

Ensamblador ZX Spectrum, pintando UDG
Ensamblador ZX Spectrum, pintando UDG

Ensamblador ZX Spectrum, conclusión

Llegados a este punto, ya tenemos definidos todos los gráficos y hemos aprendido como pintarlos.

En el próximo capítulo pintaremos el área de juego.

El código generado lo podéis descargar 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: