sábado, 12 de septiembre de 2015

Modificar la ROM del Spectrum (Principiantes)

En este artículo voy a dar unas pocas instrucciones para personalizar un poco la ROM del Spectrum y que nos servirá a los que sabemos poco o nada para profundizar un poco más en la ROM.

Si no sabes cómo está estructurada la ROM de tu ZX Spectrum y no tienes ni idea de código máquina, este es tu hilo. Si ya tienes conocimientos, es mejor que consultes otros hilos, como por ejemplo este hilo

De este hilo he tomado ideas y las herramientas.


¿Qué necesitas?

  1. El fichero de la ROM original del Spectrum. Puedes bajártelo del hilo anterior. Este fichero está comentado y te da explicaciones en cada instrucción.
  2. Un programa ensamblador. Igualmente, recomiendo que te bajes el programa propuesto en el hilo anterior: sjASMPlus.
  3. Un editor de textos. El que más te guste. Yo uso Notepad++, muy completo y potente.
  4. Un emulador. Recomendable que tenga la opción de “Debugger” de manera que puedas incluso modificar la ROM una vez está corriendo en el emulador. Yo recomiendo Spectaculator y sobre todo, para este caso, ZX Spin.

Referencias:


La ROM del Spectrum en cuatro palabras

La ROM ocupa 16 kb, desde las direcciones 0000 a 16383 (3FFFh). Está estructura en 3 bloques principales y en este orden:
  1. Sistema Operativo:   7 kb
  2. Traductor BASIC:       8 kb
  3. Juego de caracteres: 1kb
En esos primeros 7 kb están almacenadas todas aquellas rutinas o grupos de órdenes que el micro utiliza para inicializar registros y datos, las rutinas de INPUT/OUTPUT (altavoz, casete, teclado, etc.), entre otros.

En la dirección 0000 comienza la rutina de arranque del ordenador “START” (al ponerlo en marcha o tras un reset).

Cambiar el BORDER

Al poco de iniciarse la rutina START, se salta a la rutina START/NEW en la dirección 11CBh. En la dirección 11CCh, se pueden identificar las siguientes instrucciones:

LD A, +07

Esta rutina carga en el registro del acumulador (A) el valor hexadecimal +07, o en binario 00000111. La siguiente instrucción es:

OUT (+FE), A

Esta instrucción envía el valor del acumulador A al puerto +FEh (puerto I/O de la ULA), que en decimal es 254. Los tres primeros bits almacenan el color del borde.


Pues si nos gusta tener el borde en color negro, no hay más que introducir su valor en hexadecimal al acumulador. El valor por defecto es +07h (00000111) que se corresponde con el color blanco. La codificación de colores que se ve en la tabla de la figura también nos servirá más adelante.

En binario: 00000000
En hexadecimal: 00h

Lo tendríamos cambiando la instrucción anterior por:

LD A, +00

Cambiar PAPER y el INK

Vamos a ver ahora cómo darle un aspecto retro total, poniendo el PAPER en color negro e INK en color blanco, justo a la inversa de cómo está la original.

En la dirección 1265h hay que buscar el siguiente grupo de instrucciones:

LD A, +38
LD (ATTR-P), A
LD (ATTR-T), A
LD (BORDCR), A

Estas instrucciones cargan el valor del acumulador (+38h, o en binario 00111000) en las variables del sistema ATTR-P, ATTR-T y BORDCR. La variable ATTR-P es para colores y atributos permanentes (PAPER, INK, FLASH y BRIGHT). ATTR-T es igual para uso temporal. Y BORDCR define los valores de atributos del borde en el área de edición, es decir, las dos últimas líneas.

Los atributos se fijan con un byte de la siguiente manera:

Bit 7: FLASH
Bit 6: BRIGHT
Bit 5-3: PAPER
Bit 2-0: INK

El valor por defecto es 00111000, lo cual significa que el FLASH y BRIGHT están desactivados (su bit con valor 0), el PAPER tiene el valor 111 que se corresponde con el color blanco, y finalmente el valor de INK es 000 que se corresponde con el color negro.

Poniendo el valor 00000111 (+7h) conseguimos el PAPER negro (000) e INK blanco (111). Por lo tanto no hay más que cambiar la siguiente instrucción de la rutina que estamos estudiando y ya lo tenemos:

LD A, +07 

Cambiar el Mensaje de Copyright

Los mensajes del sistema se encuentran a partir de la dirección 1391h, y en particular en la dirección 1539h se encuentra el famoso mensaje del copyright. El mensaje ocupa 28 bytes (28 caracteres), por lo tanto ese es el espacio de que disponéis para poner vuestra presentación. Si es más largo habrá problemas de direccionamiento y no funcionará la ROM. Si es menos, no pasará nada.

Antes del mensaje de copyright, tenéis todos los mensajes de error. Seguro que alguno tiene la tentación de traducirlos o poner otros. Siempre hay que tener presente la longitud del original.

Por supuesto que una modificación mayor se puede hacer, pero ya no serías un principiante.




Cambiar el Juego de Caracteres

En realidad no vamos a cambiar el juego de caracteres, algo que es más laborioso, pero sí vamos a modificar un carácter. ¿Alguien ha usado el símbolo de libra para algo?. Pues yo prefiero tener la letra ñ. Aunque en la ROM hay espacio disponible, vamos a modificar el caracter para ver como se hace. El juego de caracteres está ubicado entre las direcciones 3D00h (15616) y la 3FFF (16383), que en la tabla ASCII son los caracteres desde el 32 al 127, ambos inclusive.

En el fichero ROM que te puedes descargar, es muy fácil porque están “dibujados” los caracteres con 0 y 1, al final del fichero. El símbolo libra es el CHR$(96). Simplemente es modificar el patrón para obtener una “ñ”, cambiando los 0 y los 1 “diseñando” nuestra letra. Si abres el fichero ROM en ZX Spin por ejemplo, tendrás que ir a la dirección 3F00h e introducir los códigos en hexadecimal 78, 00, 78, 44, 44, 44, 44 y 00, en lugar de los que están (00 1C 22 78 20 20 7E 00). Y al ejecutar la ROM tendrás una “ñ” al pulsar SS+x.

Sin embargo, el Spectrum dispone de un espacio reservado para UDG al final de la memoria RAM física. En total son 168 bytes que se corresponden con 21 caracteres (21 x 8 = 168). La posición de memoria está apuntada por la variable del sistema UDG y que por defecto es a la dirección 65368. Es decir, después de la RAMTOP. Por lo que los últimos 168 bytes del spectrum están reservados para crear UDGs. Un CLEAR puede modificar este espacio. Estos 168 bytes no están vacíos, sino que se han volcado desde la ROM los caracteres de la A a la U en mayúsculas.

Cambiar los colores de las rutinas SAVE y LOAD

Siempre me he preguntado por qué los colores de las cabeceras (rojo y cian) y del bloque del programa (azul y amarillo) eran siempre los mismos….hasta que aparecieron esas maravillosas rutinas de carga particular, rápida y ultrarrápida.

Las rutinas relacionadas con SAVE y LOAD se encuentran entre las direcciones 04C2h (1218d) y 09F3h (2547d). Las sub-rutinas SAVE-BYTES (SA-BYTES) y LOAD-BYTES (LD-BYTES) son utilizadas tanto para la cabecera como para el bloque del programa. En ellas se encuentran los datos que definen los colores para ambas partes. Los registros que intervienen son DE (con la longitud del bloque en bytes), IX (localización del comienzo) y el registro A.

Vamos a fijarnos en cómo cambiar el color de la cabecera cuando hacemos un SAVE. Para ello vamos a la localización 04D5h en donde aparece:

LD A, +02  (que equivale en binario a 00000010, que es el color rojo)

A continuación se generan los pulsos de la cabecera en la rutina SA-LEADER (04D8h), en donde encontramos las siguientes instrucciones:

OUT (+FE), A  (que saca por el puerto 254 el valor del registro A)
XOR +0F  (que equivale en binario a 00001111)

La instrucción XOR es una operación lógica que actúa sobre el registro A y guarda el resultado de la operación en el mismo registro A. Por lo tanto:

+02: 00000010
+0F: 00001111
XOR: 00001101  (el bit 3 es el MIC, y los bits 0 a 2 son el borde en color cian)

La instrucción XOR es clave en esta operación porque el rojo (010) y el cian (101), son complementarios desde el punto de vista binario. Algo similar pasa con el azul (001) y el amarillo (110). Luego cambiando solo un color del par, al final cambiaremos indirectamente el otro. Luego cambiemos la siguientes instrucciones:

04D5h LD A, +04 (que equivale en binario a 00000100, que es el color verde)
04EEh LD A, +03 (que equivale en binario a 00000011, que es el color magenta)

La operación XOR nos dará el color que complementa al verde:

+04: 00000100
+0F: 00001111
XOR: 00001011 (el código binario de los bits 0-2 es 011, que se corresponde con el color magenta)

Todo esto es para la cabecera, para el código hay que hacer algo similar. Para ello nos fijamos en las siguientes instrucciones:

04F6h LD BC, +3B0E (en particular en los dos últimos -0E- que en binario es 00001110, y en particular los tres últimos – 110 - que se corresponden al color amarillo).
0508h LD A, +01 (que equivale en binario a 00000001, que es el color azul)
0524h INC A         (en este caso cambiaremos la instrucción)

En estas instrucciones se establecen los colores amarillo y azul de las franjas del borde cuando se graba un programa. Vamos a realizar el siguiente cambio para obtener franjas blancas y negras, y además muy curiosas:

04F6h LD BC, +3B0F
0508h LD A, +00
0524h DEC A

La última instrucción se ha cambiado para mantener un +00 en el registro A. Justamente en la dirección anterior (0523h) se encuentra la instrucción XOR A, que es una manera muy sencilla de poner un +00 en el registro A, con solo 1 byte. Por lo tanto con la instrucción INC A lo que hacemos es poner un +01 en el registro A, lo que equivale al color azul, de ahí el cambio.

Para la rutina de carga (LOAD), existen varias alternativas para cambiar los colores del borde, tanto en la parte de la cabecera como en el programa. Incluso obteniendo efectos curiosos.

En primer lugar, en la dirección 055Ah se define que el borde de inicio de la rutina LOAD es el blanco (LD A, +0F). Si ponemos LD A, +00, el borde inicial será de color negro. Un poco después encontramos lo siguiente:

0565h AND  +20
0567h OR  +02

El objetivo es fijar el color inicial del borde de la cabecera en rojo. Modificando la instrucción AND +17 obtendremos la cabecera en colores blanco y negro. El mismo efecto obtendremos poniendo el valor +00, en lugar del +02 en la instrucción OR.

Para modificar el color del bloque de programa de azul y amarillo a blanco y negro, tendremos que ir a la dirección 05A0h y buscar la instrucción XOR +03.

Con el cambio del color de la cabecera, llegado a este punto el acumulador A tiene un valor +0F, por lo que al hacerle una operación XOR +03, nos saldrán los bordes verde y magenta:

+0F: 00001111
+03: 00000011

XOR: 00001100 (el código binario de los bits 0-2 es 100, que se corresponde con el color verde)

Luego para tener en los bits 0-2 el valor 111 correspondiente al blanco, no hay más que poner un OR en lugar del XOR:

+0F: 00001111
+03: 00000011
OR: 00001111


Y como nota curiosa, haced los siguientes cambios:

0567h: OR +00 (en lugar de OR +02)
05A0h: OR +03 (en lugar de XOR +03)
05FFh: OR +0F (en lugar de OR +08)

Al ejecutar LOAD “” veremos que no tenemos colores aunque se oye el sonido de carga. Con esto lo que hemos forzado es a que la cabecera y el bloque de programa tengan el color blanco únicamente.