lunes, 15 de diciembre de 2008

Buenas:

Hace ya unos cuantos años compré una de las versiones del cartucho Flash que fabricó Padial. En este cartucho se pueden cargar ficheros en formato ROM para ser ejecutados en cualquier MSX, siempre y cuando sean de 8K, 16K, 32K o megaroms de hasta 512K con mappers ASCII8 ó ASCII16. Es decir, la flash sitúa rom en las páginas 1 y 2 del Z80 (de $4000 a $BFFF).

Sin embargo, no es posible cargar roms de 48K lineales como "Universe Unknown" ó "The Cure", ya que este tipo de roms se sitúan en las tres primeras páginas del Z80 (de $0000 a $BFFF), dejando la página 3 (de $C000 a $FFFF) con RAM...

¿Seguro que no es posible? Pues sí que lo es.

Primero veamos un poco de teoría sobre cómo funcionan los mappers de cartuchos. Una explicación más detallada y cubriendo más tipos de mappers se encuentra en la siguiente dirección:

http://bifi.msxnet.org/msxnet/tech/megaroms.html

Fijémonos en los mappers ASCII8 y ASCII16 (los que utiliza la flash de Padial). De acuerdo a la página anterior, las direcciones para cambiar los bancos en dichos mappers son:

Mapper$4000-$5FFF$6000-$7FFF$8000-$9FFF$A000-$BFFF
ASCII8$6000$6800$7000$7800
ASCII16$6000$7000

Es decir, en modo ASCII8 tenemos cuatro bancos de memoria en los que podemos seleccionar uno de los 64 (512/8) bloques de 8K disponibles, mientras que en modo ASCII16 tenemos dos bancos de memoria en los que podemos seleccionar uno de los 32 (512/16) bloques de 16K disponibles. La selección, en ambos modos, se realiza "pokeando" en las direcciones de memoria designadas el número de bloque a visualizar.

Ahora bien, para que funcionen de entrada las roms de 32K, la flash se inicializa "pokeando" en esas direcciones de memoria los valores 0, 1, 2 y 3 respectivamente, de forma que arrancando en modo ASCII8 tendríamos los cuatro primeros bloques visibles, es decir, los 32K de nuestra rom.

¿Y si arrancamos en modo ASCII16? Pues igual, la inicialización sería la misma, de forma que tendríamos en $4000-$7FFF el bloque 0 y en $8000-$C000 el bloque 2. Por eso no podemos ejecutar juegos de 32K en modo ASCII16, ya que no estarían situados los bloques correspondientes en su sitio (no, no vale eso de "que lo haga la rom", porque las roms de 32K no usan mapper).

Vale, ¿pero y las roms de 48K? Bien, el hecho es que hasta ahora sólo hemos visto el funcionamiento de los mappers y cómo se inicializa la flash. La cuestión clave (la cual conozco gracias a que BiFi se pegó el currazo de cacharrear con la flash) es que esta flash también mapea rom en la página 0 (en el rango $0000-$3FFF) del slot donde esté. ¿Qué hay ahí visible? Pues siempre, invariablemente, el bloque 0 del mapper, independientemente de si estamos en modo ASCII8 ó ASCII16.

Es decir, cuando estamos ejecutando una rom cargada en la flash de Padial, si estamos en modo ASCII8, de $0000 a $1FFF tendremos visibles sus primeros 8K o los primeros 16K (de $0000 a $3FFF) en modo ASCII16. Como las roms que usan mapper no tocan la página 0 de su slot, esto resulta totalmente transparente tanto para nosotros como para el MSX.

Pero ahora mirémoslo desde el punto de vista de los desarrolladores actuales. Si tenemos la posibilidad de visualizar los primeros 16K de nuestra rom en la página 0 del Z80, ¿cómo podríamos aprovechar esto para cargar juegos de 48K?

Obviamente de entrada ya descartamos utilizar el modo ASCII8, ya que con este modo no tendríamos nada en las direcciones de memoria de $2000 a $3FFF. Nos centramos en el modo ASCII16 y vemos que, según la inicialización, tenemos:

Memoria$0000-$3FFF$4000-$7FFF$8000-$BFFF
Bloque002

Nuestro gozo en un pozo. Resulta que tenemos visible el bloque 0 en las páginas 0 y 1 del Z80 y el bloque 2 en la página 2. Pero el bloque 1, donde normalmente se sitúa la cabecera de la rom, no está visible en ningún sitio (y tenía que estar en la página 1). ¿Qué hacemos? Fácil, vamos a crear una segunda cabecera de rom en el bloque 0, de forma que sólo se ejecutará si el bloque 0 se sitúa en la página 1 del Z80.

La cabecera de los cartuchos siempre comienza con los bytes $41 $42, seguidos de la dirección de memoria donde se encuentra la rutina de inicialización del cartucho. Pero la cabecera contiene mucha más información que, en el 90% de los cartuchos, está a 0. Realmente la cabecera tiene 16 bytes de tamaño, aunque haya cartuchos que no respeten el formato al 100%.

La idea será que la cabecera "buena" (es decir, la situada en el bloque 1) deberá respetar el formato y dejar 12 bytes a 0 tras los bytes de identificación y la dirección de memoria de inicio. Por otro lado, la cabecera "falsa" (la que vamos a situar en el bloque 0 para aprovechar la flash de Padial) NO DEBE respetar este formato para no confundir a los emuladores ni a los cargadores, dejando sólo 7 bytes a 0 y usando los 5 bytes restantes en dos instrucciones adicionales. Así, los emuladores y los cargadores de roms (como el ODO) no situarán de forma incorrecta nuestro programa en la memoria.

Veamos, a continuación, el código que he utilizado para estas dos cabeceras en el QBIQS, que es una rom de 48K lineales que funciona en la flash de Padial sin problema alguno (así como en los emuladores y cargada con ODO).

.org $0000
.db $41,$42
.dw PADINIT+$4000
.ds 7
PADINIT:
ld a,1
ld [$6000],a

; AQUI VA EL RESTO DE CÓDIGO/DATOS SITUADO EN LA PÁGINA 0
[...]

.org $4000
.db $41,$42
.dw INIT
.ds 12
INIT:
; Y AQUÍ EL RESTO DEL CÓDIGO SITUADO EN LAS PÁGINAS 1 Y 2
[...]

Veamos cómo funciona el código. Si arrancamos la rom de forma normal (en un emulador, con el ODO o similares, o bien en un cartucho hecho ex-profeso para ello) la BIOS buscará la cabecera completa y la encontrará en $4000. Así que la ejecución llegará a la rutina INIT (situada en $4010 como habréis podido ver) y nuestra rom tomará el control del Z80.

Si ahora arrancamos en la flash de Padial, tendremos que en $4000 se sitúa la cabecera que hemos creado para la flash. La rutina especial de inicialización estará en $400B (correctamente señalada por la cabecera) y consiste, simplemente, en "pokear" un 1 en la dirección de memoria $6000, para así cambiar el bloque visible en el banco 0 y tener visible el bloque 1 en la página 1. Como (según podemos ver en la tabla anterior) ya teníamos el bloque 0 en la página 0 y el 2 en la 2, ya tenemos los 48K de nuestra rom visibles en las tres primeras páginas del Z80 como era nuestra idea.

Tras hacer ese cambio en el banco 0 del mapper, tenemos que el registro PC (el contador de programa del Z80) valdrá $4010 (justo la dirección de la rutina INIT) y que ahora el bloque 1 ocupa dicho banco, por lo que lo siguiente que se ejecutará es, precisamente, la rutina normal de inicialización tras haber situado correctamente los tres bloques de la rom.

Espero que este pequeño artículo os resulte de utilidad a la hora de crear nuevas roms de 48K para MSX, ya que ahora podrán ser cargadas desde la flash de Padial sin ningún problema.

Obviamente, también se puede coger una rom de 48K como Universe Unknown o The Cure (que mencionábamos al principio) y parchearlas de forma que se puedan cargar en este cartucho flash. Por si no os sentís con ánimos de hacerlo por vosotros mismos, BiFi ya se tomó la molestia y en http://ips.tni.nl/rom/flashrom/ podéis encontrar dichos parches en forma de ficheros IPS, así como otros parches expresamente creados para esta flash.