ProgramaciónTaller de Juan AntonioZX Spectrum

El taller de Juanan: 0x01 Load Screen

01/10/2024

Inauguro esta sección del Taller de Juanan, en ella iré mostrando rutinas en ensamblador que se puedan usar desde Basic y otras utilidades que nada tienen que ver con la programación.

En este primera entrega muestro una sencilla rutina que vuelca desde una posición de memoria 6912 bytes a la VideoRAM (pantalla del ZX Spectrum) de manera casi inmediata.

Antes de comenzar, el objetivo de esta sección no es que aprendas ensamblador (también puedes hacerlo), el objetivo es que puedas usar rutinas desarrolladas en ensamblador desde BASIC, pero que, en la medida de lo posible, entiendas qué es lo que hacen.

Load Screen

De todos es conocida la forma en la que se muestran, por defecto, las pantallas de carga en un Spectrum. Se van cargando línea a línea (1 píxel de alto) y una vez que está cargada se colorea, con ese sonido característico que todos tenemos grabados en la memoria.

Con esta rutina podremos cargar la pantalla en cualquier dirección de memoria y mostrarla en la VideoRAM de golpe. La rutina se puede usar en cualquier modelo de ZX Spectrum, ya sea de 16K, 48K o 128K, ocupa tan solo 16 bytes y el código es el siguiente:

ld   hl, 0x5cb0               ; HL = dirección carga pantalla
ld   d, (hl)                  ; D = byte alto
inc  hl                       ; HL += 1
ld   e, (hl)                  ; E = byte bajo
ex   de, hl                   ; HL = DE, DE = HL
ld   de, $4000                ; DE = VideoRAM
ld   bc, $1b00                ; BC = longitud VideoRAM
ldir                          ; Carga pantalla en VideoRAM
ret                           ; Vuelve a BASIC

Vamos a utilizar dos posiciones de memoria del área de variables de sistema que no se usan: la $5CB0 (23738) y la $5CB1 (23739). Desde BASIC tendremos que poner en la primera posición el byte alto de la dirección dónde vamos a cargar la pantalla y en la segunda el byte bajo.

Con LD HL, $5CB0 estamos cargando en el registro HL el valor $5CB0 (23728). LD D, (HL) carga en el registro D el valor que hay en la posición de memoria a la que apunta HL. INC HL le suma uno a HL, pasando su valor a $5CB1 (23729). LD E, (HL) hace lo mismo que hicimos con LD D, (HL), pero en esta ocasión carga el valor en el registro E. Llegados a este punto, el registro DE contiene la dirección en  la que se ha indicado que se ha cargado la pantalla.

Para cargar la pantalla en la VideoRAM vamos a usar la instrucción LDIR. LDIR vuelca datos desde la dirección de memoria indicada por HL, a la dirección de memoria indicada por DE. Vuelca los bytes que se indiquen en el registro BC, en este caso $1B00 (6912), el resultado de sumar 32 columnas por 192 scanlines del área gráfica de la pantalla, y 32 columnas por 24 filas del área de atributos.

        32*192  = 6144
      + 32* 24  = 0768
      --------------------
                = 6912

Como la dirección dónde está la pantalla la tenemos en DE, y LDIR necesita la dirección de origen en HL, intercambiamos el valor de ambos registros, EX DE, HL. LD DE, $4000 carga en DE la dirección de la primera posición de la VideoRAM, 16384. LD BC, $1B00 carga en BC la longitud del área gráfica y la de atributos (6912). LDIR vuelca la pantalla desde la posición en la que la hemos cargado a la VideoRAM. Por último, con RET volvemos al BASIC.

Para usar este código ensamblador desde BASIC necesitamos saber que bytes cargar en memoria, los DATA. Este código, si lo ensamblamos a mano sería:

21 B0 5C 56 23 5E EB 11 00 40 01 00 1B ED B0 C9

O en decimal:

33,176,92,86,35,94,235,17,0,64,1,0,27,237,176,201

Uso desde Basic

Pasamos a ver cómo utilizar esta rutina desde un programa BASIC para que la pantalla de carga aparezca de golpe.

10 DATA 33,176,92,86,35,94,235,17,0,64,1,0,27,237,176,201
20 POKE 23728,INT(24576/256):POKE 23729,24576-INT(24576/256)*256
30 FOR F=1 TO 16:READ A:POKE 32000+F,A:NEXT F
40 LOAD ""CODE 24576
50 RANDOMIZE USR 32000
60 PAUSE 0
70 CLS
80 FOR F=1 TO 6912:POKE 16383+F,PEEK (24575+F):NEXT F
90 PAUSE 0

En la línea 10 tenemos los datas de la rutina en ensamblador. En la línea 20 cargamos en la dirección 23728 el byte alto de la dirección dónde vamos a cargar la pantalla y en la 23729 el byte bajo. En estas direcciones es dónde la rutina lee para saber la localización de la pantalla de carga. En el ejemplo la pantalla la vamos a cargar en la dirección 24576 ($6000) para que sea compatible también con los modelos de 16K.

En la línea 30 cargamos la rutina a partir de la dirección 32000. Podríamos cargarla en la dirección 23296; a partir de esta dirección, y hasta la 23551, tenemos el buffer de la impresora, esto son 255 bytes que podemos usar siempre y cuando no tengamos conectada una impresora al Spectrum, pero en el caso de los modelos 128K +2, +2A, +2B y +3, existen ciertas incompatibilidades que producirán el cuelgue del Spectrum.

En la línea 40 cargamos la pantalla a partir de la dirección 24576 (0x6000) y en la 50 llamamos a la rutina en ensamblador que hemos cargado en la línea 30, que vuelca la pantalla que hemos cargado a la VideoRAM.

En la línea 60 esperamos hasta que se pulse una tecla, en la 70 limpiamos la pantalla y en la 80 volvemos a volcar la pantalla, pero esta vez usando BASIC.

En la noventa volvemos a esperar a que se pulse una tecla.

Para ver todo esto en funcionamiento:

  • Escribid el código BASIC en un emulador y grabadlo en un archivo .tap: SAVE”LoadScreen” LINE 10
  • Descargad desde Spectrum Computing cualquier pantalla de carga, la que más os guste.
  • Descargad, si no lo tenéis, el programa ZX-Paintbrush, abrid la pantalla de carga con este programa y desde File\Export file/selection as, grabadla como .tap. Cuándo os pida el nombre de la cabecera, poned por ejemplo Screen y dadle a OK.

Para montar el archivo .tap final en Windows, desde el símbolo de sistema ejecutad:

copy /b /y Cargador.tap+Screen.tap LoadScreen.tap

Si usáis Linux el comando a ejecutar sería:

cat Cargador.tap Screen.tap > LoadScreen.tap

Podréis observar la diferencia de tiempo abismal que hay entre usar la rutina para volcar la pantalla y usar BASIC. Posiblemente la línea 80 se pueda optimizar, el BASIC no es mi fuerte, pero aun así no podrá igualar el tiempo que tarda la rutina en ensamblador.

Incluso podríamos hacer que la rutina fuera más rápida, pero solo en modelos 48K y superiores. Al cargar la rutina en los segundos 16KB, la estamos cargando en lo que se denomina memoria contenida, memoria a la que acceden tanto la ULA como el Z80, y la ULA tiene preferencia. Esto hace que el código alojado en la memoria contenida sea un 25% más lento que el alojado a partir de la dirección 32768 ($8000).

Una vez cargado el programa, para volver a ejecutar sólo tendréis que poner GO TO 50 y pulsar Enter, si queréis que se ejecute la rutina en ensamblador, o GO TO 80 para volcar la pantalla solo con BASIC.

Todo el código fuente y los programas resultantes los podéis descargar desde aquí.

BASICEnsambladorZX Spectrum

Deja una respuesta