ProgramaciónTaller de Juan AntonioZX Spectrum

El taller de Juanan: 0x07 Manage Third

30/03/2025

Administración de tercios

En esta nueva entrega volvemos al ensamblador, en concreto con dos rutinas para manejar tercios de pantalla: la primera de ella para cambiar los atributos de color y la segunda para volcar parte de una pantalla cargada en memoria a un tercio específico; ambas rutinas son reubicable. Empezamos.

Cambio de atributos

Esta primera rutina se encarga de cambiar los atributos de un tercio (tinta, fondo, brillo y parpadeo) de manera inmediata. Para pasarle los datos utilizamos la dirección de memoria 23728 para pasarle los atributos del tercio y la 23729 para pasarle el tercio (de 1 a 3).

El código en ensamblador de la rutina es el siguiente:

INPUT: EQU $5CB0

;
; Asigna atributos al tercio de pantalla especificado.
;
; Altera el valor de los registros BC, DE y HL.
;
; Tamaño: 25 bytes
;
ClearAttr:
; Obtiene el tercio a limpiar y los atributos a aplicar
ld   bc, (INPUT)
; Calcula la dirección de los atributos del tercio a limpiar
ld   hl, $5800              ; Apunta HL a los atributos del tercio 1
dec  b
jr   z, clearThird_attr     ; Si el tercio era 1, salta a rellenar los atributos
inc  h                      ; Apunta HL a los atributos del tercio 2
dec  b
jr   z, clearThird_attr     ; Si el tercio era 2, salta a rellenar los atributos
inc  h                      ; Apunta HL a los atributos del tercio 3
clearThird_attr:
ld   d, h
ld   e, l
inc  e                      ; Apunta DE al segundo atributo del tercio
ld   (hl), c                ; Carga los atributos al primer byte
ld   bc, $ff                ; BC = longitud de atributos del tercio - 1
ldir                        ; Rellena los atributos del tercio
ret

Como puedes ver, la rutina toma los valores desde la dirección $5CB0 (23728) cargando en el registro B el tercio y en el registro C los atributos. Partiendo de la dirección de memoria dónde empiezan los atributos del primer tercio ($5800), calcula la dirección dónde empieza el tercio al que asignar los atributos, asigna el atributo especificado al primer byte del tercio y luego lo vuelca al resto de bytes. Lee los comentarios para entenderlo mejor.

Ahora hay que usar esta rutina desde BASIC. En este caso me he bajado la pantalla de carga de Sir Fred y la he pasado a .tap tal y como expliqué en otra entrega anterior. El programa va a cargar los DATA de la rutina, la pantalla de carga de Sir Fred y luego pulsando las teclas 1, 2 o 3 va a cambiar los atributos de color del correspondiente tercio. Vamos a ello.

  1 DATA 237,75,176,92,33,0,88,5
  2 DATA 40,5,36,5,40,1,36,84
  3 DATA 93,28,113,1,255,0,237,176,201
 10 FOR M=0 TO 24: READ A: POKE 32768+M,A: NEXT M
 20 LOAD""SCREEN$
 30 PAUSE 0
 40 POKE 23728,0*128+1*64+1*8+5:POKE 23729,1
 50 RANDOMIZE USR 32768
 60 PAUSE 0
 70 POKE 23728,0*128+1*64+2*8+3:POKE 23729,2
 80 RANDOMIZE USR 32768
 90 PAUSE 0
100 POKE 23728,0*128+1*64+4*8+6:POKE 23729,3
110 RANDOMIZE USR 32768
120 PAUSE 0

En las líneas de la 1 a la 3 están los bytes correspondientes a la rutina una vez ensamblada. En la línea 10 se cargan esos bytes en memoria a partir de la dirección 32768, en la línea 20 se carga la pantalla de carga y en la 30 se espera a que se pulse una tecla.

El resto de líneas como puedes ver son grupos de tres: en la primera línea se carga en la posición de memoria 23728 los atributos a aplicar, en la 23729 el tercio en el que se aplican, en la segunda línea se llama a la rutina y en la tercera se espera a que se pulse una tecla.

Los atributos se calculan de la siguiente manera: FLASH*128+BRIGHT*64+PAPER*8+INK.

Este código BASIC lo he grabado como LoaderClear.tap. Ahora tenemos que montar el .tap final; recuerda que este paso cambia si usas Windows o Linux.

Windows: copy /B LoaderClear.tap+SirFred.tap ClearThird.tap
Linux:   cat LoaderClear.tap SirFred.tap > ClearThird.tap

Ya sólo queda cargar el archivo final en un emulador o máquina real para ver los resultados. El aspecto tras ejecutar el programa y pulsar tres veces una tecla es el siguiente:

ManageThird

Carga de tercios

La siguiente rutina sirve para cargar en un tercio de la pantalla el contenido de un tercio de otra pantalla que tengamos en memoria. Para pasarle los datos utilizamos las direcciones de memoria 23728 y 23729 para indicar en qué dirección de memoria tenemos cargados los parámetros: dirección en la que se carga la pantalla, el tercio de la pantalla a vocal (de a 1 a 3) y el tercio de la pantalla sobre el que se vuelva (de 1 a 3).

El código en ensamblador de la rutina en esta ocasión lo voy a mostrar por bloques.

INPUT: EQU $5CB0

;
; Mueve un tercio de una pantalla almacenada en memoria al tercio especificado.
;
; Entrada: HL -> Dirección de la pantalla almacenada en memoria.
;          B  -> Tercio de la pantalla almacenada en memoria (1, 2 o 3).
;          C  -> Tercio de la VideoRam sobre el que se vuelca (1, 2, 3).
;
; Altera el valor de los registros AF, BC, DE y HL.
;
; Tamaño: 86 bytes
;
MoveThird:
; Carga los valores
ld   HL, (INPUT)            ; HL = dirección dónde están los parámetros
ld   e, (HL)                ; E = parte baja dirección dónde está la pantalla
inc  hl
ld   d, (HL)                ; D = parte alta dirección dónde está la pantalla
inc  hl
ld   b, (hl)                ; B = tercio que se mueve de la pantalla almacenada
inc  hl
ld   c, (hl)                ; C = tercio de la VideoRAM sobre el que se vuelca
ex   de, hl                 ; HL = dirección dónde está la pantalla                           
; Calcula la dirección origen de los píxeles
ld   a, b                   ; A = tercio origen
ld   de, $0000              ; DE = desplazamiento tercio 1
dec  a                 
jr   z, moveThird_desPx     ; Si tercio = 1, salta
ld   d, $08                 ; DE = desplazamiento tercio 2
dec  a
jr   z, moveThird_desPx     ; Si tercio = 2, salta
ld   d, $10                 ; DE = desplazamiento tercio 3
moveThird_desPx:
or   a                      ; Pone acarreo a 0
adc  hl, de                 ; HL = dirección del tercio origen

En esta primera parte se carga la dirección dónde están los parámetros en HL, la dirección dónde está la pantalla en DE, en B el tercio que se vuelca y en C el tercio al que se vuelca. Finalmente se pasa la dirección dónde está la pantalla a HL.

Acto seguido se calcula el desplazamiento, según el tercio, para saber dónde están los píxeles a mover, cargando $0000 en DE y luego si es el tercio 2 cargando $08 en D y si es el tercio 3 cargando $10 en D. El valor que tenga DE es lo que se suma a HL para que HL apunte al primer byte del tercio que se va a cargar, no sin antes poner el flag de acarreo a cero.

; Calcula la dirección de destino de los píxeles
ld   a, c                   ; A = tercio destino
ld   de, $4000              ; DE = dirección tercio 1 VideoRAM
dec  a
jr   z, moveThird_px        ; Si tercio = 1, salta
ld   d, $48                 ; DE = dirección tercio 2 VideoRAM
dec  a
jr   z, moveThird_px        ; Si tercio = 2, salta
ld   d, $50                 ; DE = dirección tercio 3 VideoRAM
moveThird_px:
; Vuelca los píxeles del origen al destino
push bc
push hl                     ; Perserva registros
ld   bc, $800               ; BC = longitud píxeles tercio
ldir                        ; Vuelca origen en VideoRAM
pop  hl
pop  bc

En esta parte se hace algo parecido a lo que se hizo en la parte anterior, pero en esta ocasión para calcula la dirección en la que empieza el tercio de la VideoRAM dónde se va a cargar el tercio de origen. Los tercios de la VideoRAM empiezan en las posiciones $4000, $4800 y $5000, de ahí los valores que se van cargando en D. Una vez calculada la dirección, se preservan los registros BC y HL, se vuelcan los píxeles al tercio de la pantalla y se vuelve a recuperar el valor de los registros.

; Calcula la dirección de origen de los atributos
ld   a, b                   ; A = tercio origen
ld   de, $1800              ; DE = desplazamiento atributos tercio 1
dec  a
jr   z, moveThird_desAttr   ; Si tercio = 1, salta
ld   d, $11                 ; DE = desplazamiento atributos tercio 2
dec  a
jr   z, moveThird_desAttr   ; Si tercio = 2, salta
ld   d, $0a                 ; DE = desplazamiento atributos tercio 3
moveThird_desAttr:
or   a                      ; Acarreo = 0
adc  hl, de                 ; HL = dirección atributos origen

Ahora toca calcular la dirección de los atributos, lo cual se hace calculando el desplazamiento desde la parte de píxeles que se han movido. Si ha sido el tercio 1, está a $1800 bytes, a $1100 si ha sido el tercio 2 y a $0a00 si ha sido el tercio tres. Por último sumamos a HL el desplazamiento que se ha calculado en DE (en HL está la dirección del tercio).

; Calcula la dirección de destino de los atributos
ld   de, $5800              ; DE = dirección atributos tercio 1 VideoRAM
ld   a, c                   ; A = tercio destino
dec  a
jr   z, moveThird_attr      ; Si tercio = 1, salta
inc  d                      ; DE = dirección atributos tercio 2 VideoRAM
dec  a
jr   z, moveThird_attr      ; Si tercio = 2, salta
inc  d                      ; DE = dirección atributos tercio 3 VideoRAM
moveThird_attr:
; Vuelca los atributos del origen al destino
ld   bc, $100               ; BC = longitud atributos tercio
ldir                        ; Vuelca origen en VideoRAM
ret

Por último se calcula la dirección de destino de los atributos. Los atributos están en la dirección $5800 para el tercio 1, en la $5900 para el tercio 2 y en la $5A00 en el 3, de ahí que se vaya incrementando D. Una vez calculada la dirección del tercio de destino, se vuelcan los atributos desde el tercio de origen.

Llega el turno del BASIC. Al igual que en la rutina anterior, voy a usar la pantalla de carga de Sir Fred para hacer las pruebas.

Una vez cargada la rutina y la pantalla de carga, el programa funciona de distintas maneras dependiendo de la tecla que se pulse.

  • 1: mueve el tercio 1 al tercio 1.
  • 2: mueve el tercio 2 al tercio 2.
  • 3: mueve el tercio 3 al tercio 3.
  • 4: mueve el tercio 1 al tercio 2.
  • 5: mueve el tercio 2 al tercio 3.
  • 6: mueve el tercio 3 al tercio 1.
  • 7: mueve el tercio 1 al tercio 3.
  • 8: mueve el tercio 2 al tercio 1.
  • 9: mueve el tercio 3 al tercio 2.
  • 0: limpia la pantalla.
  1 DATA 42,176,92,94,35,86,35,70
  2 DATA 35,78,235,120,17,0,0,61
  3 DATA 40,7,22,8,61,40,2,22
  4 DATA 16,183,237,90,121,17,0,64
  5 DATA 61,40,7,22,72,61,40,2
  6 DATA 22,80,197,229,1,0,8,237
  7 DATA 176,225,193,120,17,0,24,61
  8 DATA 40,7,22,17,61,40,2,22
  9 DATA 10,183,237,90,17,0,88,121
 10 DATA 61,40,5,20,61,40,1,20
 11 DATA 1,0,1,237,176,201
 50 FOR M=0 TO 85: READ A: POKE 32768+M,A: NEXT M
 60 LOAD""CODE 40000
 70 POKE 23728,50000-INT(50000/256)*256:POKE 23729,INT(50000/256):
    POKE 50000,40000-INT(40000/256)*256:POKE 50001, INT(40000/256)
 80 LET ORG=0: LET DES=0
 90 PAPER 0:INK 0:BORDER 0:CLS
100 LET K$ = INKEY$
110 IF K$="1" THEN LET ORG=1:LET DES=1
120 IF K$="2" THEN LET ORG=2:LET DES=2
130 IF K$="3" THEN LET ORG=3:LET DES=3
140 IF K$="4" THEN LET ORG=1:LET DES=2
150 IF K$="5" THEN LET ORG=2:LET DES=3
160 IF K$="6" THEN LET ORG=3:LET DES=1
170 IF K$="7" THEN LET ORG=1:LET DES=3
180 IF K$="8" THEN LET ORG=2:LET DES=1
190 IF K$="9" THEN LET ORG=3:LET DES=2
200 IF K$="0" THEN CLS: GO TO 100
210 IF K$>="1" AND K$<="9" THEN POKE 50002,ORG:POKE 50003,DES:
    RANDOMIZE USR 32768
220 GO TO 100

De la línea 1 a la 11 están los DATA con los bytes de la rutina, la línea 50 carga la rutina a partir de la dirección 32768 y la línea 60 carga la pantalla de carga de Sir Fred a partir de la dirección 40000.

En la línea 70 pone en las direcciones 23728 y 23729 la dirección dónde están los parámetros (50000), y en la dirección 50000 la dirección en la que se carga la pantalla (40000), en la línea 80 se declaran las variables ORG y DES que van a cambiar de valor dependiendo de la tecla pulsada, en la 90 se asignan atributos a la pantalla y se limpia.

De la línea 100 a la 200 se espera la pulsación de una tecla y dependiendo de la tecla pulsada pueden cambiar los valores de ORG y DES, limpiar la pantalla o no hacer nada.

Por último, en la línea 210 se cargan en las posiciones 50002 y 50003 los tercios de origen y destino y se llama a la rutina. En programa se sigue ejecutando en bucle para que se puedan probar todas las combinaciones.

Ya sólo queda montar el .tap final y probarlo. Este código BASIC lo he grabado como LoaderManage.tap. Para montar el .tap recuerda que este paso cambia si usas Windows o Linux.

Windows: copy /B LoaderManage.tap+SirFred.tap ManageThird.tap
Linux:   cat LoaderManage.tap SirFred.tap > ManageThird.tap

Si cargas el programa y pulsas las tecas 4, 5 y 6, el resultado debe ser éste:

Manage Third

Podéis ver el resultado final en este vídeo.

Puedes descargar todos los archivos que he utilizado desde aquí. No vemos en la entrega siguiente.

BASICEnsambladorZX Spectrum

Deja una respuesta