viernes, 04 de noviembre de 2016

Hace unos días os comentaba que estaba empezando a portar QBIQS a ColecoVision y que el port estaba bastante avanzado. Pues bien, ayer me confirmaron que (tras corregir un estúpido bug en la inicialización) el juego ya se ejecuta correctamente en una ColecoVision real. No es la versión definitiva porque faltan algunas cosas, pero es 100% jugable.

Aprovecho el post para comentar (por encima) el proceso que he seguido para portar QBIQS a Coleco.

Partamos de la versión MSX a la que le hemos quitado todo lo referente a MSX2 (sólo usa Screen 4, paletas, la VRAM extra y la capacidad de cambiar la frecuencia) y Franky. Se trata de una ROM de 48K que se sitúa en las páginas 0, 1 y 2. Es decir, de $0000 a $BFFF. Además usa 16K de RAM, localizada en la página 3 (de $C000 a $FFFF). Y necesita que toda esa memoria esté visible al mismo tiempo por el Z80.

Esto plantea un problema: una Coleco sólo tiene 1K de RAM. Es muy poca como para poder realizar un port directo del juego. Afortunadamente existe un periférico, llamado Super Game Module , que añade 32K de RAM y un PSG como el de MSX. Gracias a esto, es posible crear juegos que necesiten más de 1K de RAM.

Así pues, el primer paso fue reorganizar la memoria. Las ROMs en Coleco se sitúan en las páginas 2 y 3 (de $8000 a $FFFF), mientras que las páginas 0 y 1 contienen la RAM (hay que comprobar si el SGM está presente y activar la RAM extra desactivando, además, la BIOS). La idea es copiar un bloque de 16K de ROM a la RAM de la página 0, usar la página 1 como 16K de RAM y en las páginas 2 y 3 poner el resto de la ROM. De esta forma el Z80 puede ver los 48K originales de ROM y los 16K de RAM simultáneamente.

El siguiente paso es entender cómo funciona el mapper del MegaCart que se ha creado para que los juegos de ColecoVision puedan ser mayores de 32Kb. Esto es algo que me ha dado algún que otro dolor de cabeza...

El mapper del MegaCart admite ROMs de uno de estos tamaños 128K, 256K, 512K y 1024K. Estas ROMs se dividen en segmentos de 16K (lo que hace un total de 8, 16, 32 y 64 segmentos respectivamente, que son numerados empezando por el 0) y permite seleccionar uno cualquiera de ellos en la página 3. ¿Cómo se selecciona? Pues leyendo (sí, leyendo, no escribiendo como en los Mappers de MSX) de una determinada dirección de memoria.

Si tenemos una ROM de 128K como QBIQS (es el tamaño mínimo para un MegaCart), leer una de las direcciones de memoria de $FFF8 a $FFFF hará que el segmento correspondiente se sitúe en la página 3 del espacio de direcciones del Z80. Así, si leemos de $FFF8 seleccionaremos el segmento 0 de la ROM, si leemos de $FFF9, el segmento 1 y así sucesivamente. Esto significa que, en la práctica, no es posible situar código o datos más allá de la posición $FFC0 de memoria (ya que un mapper de 1024K usaría las posiciones $FFC0 a $FFFF para seleccionar los 64 segmentos), ya que si se quisiera ejecutar algo o acceder a los datos en esa zona, se estaría cambiando el mapper y, consecuentemente, alterando el contenido de dicha zona. ¡Una locura! (Sí, esto me pasó...)

Luego tenemos el hecho de que en la página 2 se sitúa invariablemente uno de los segmentos de la ROM. ¿El segmento 0? ¡No! ¡El último! Eso quiere decir que el código del programa que va a estar situado entre $8000 y $BFFF deberá situarse en el último segmento de 16K de la ROM.

Así pues, el bloque de código que es copiado a la página 0 lo he situado en el segmento 0. El bloque de código de la página 3 ocupa el segmento 1. Los segmentos 2 al 6 están vacíos (80K nada menos) y en el segmento 7 está el bloque de código que irá a la página 2. Al arrancar, el juego busca la RAM extra, la selecciona y comprueba que haya un PSG AY-3-8910 para tocar la música (ya que el Coleco ADAM tiene esa RAM, pero no tiene ese PSG). A continuación selecciona el segmento 0 en la página 3, copia su contenido (salvo los últimos 64 bytes, no cometáis el mismo error que yo) a la página 0 y selecciona el segmento 1. En este momento los 48K del juego más los 16K de RAM ya están visibles simultáneamente en memoria.

Si no hay RAM suficiente, el juego no funcionará, por lo que se limita a mostrar un aviso por pantalla.

El resto ha sido más o menos mecánico. Lo más pesado ha sido cambiar todos los DI/EI por RST a rutinas que desactivan/activan el bit 5 del registro R#1 del VDP, ya que las interrupciones generadas por el VDP son NMI, por lo que no se pueden desactivar con un simple DI. Por último, cambias los puertos del VDP y del PSG, modificas las rutinas de lectura de los joysticks... ¡Y listos! ¿A que era fácil?