Últimamente se ha comentado por aquí lo problemático que puede ser desarrollar juegos MSX en una plataforma u otra. Como de lo que se trata es de hacer programas que sean compatibles, esa es la gracia, con cualquier modelo, creo que sería interesante desarrollar una lista de normas de buena conducta para que a la hora de desarrollar software, se pueda garantizar su correcto funcionamiento en la mayor cantidad posible de modelos, idealmente todos.
Así a bote pronto se me ocurren los siguientes:
PARTE I
Versión de MSXEs lo primero que deberiamos comprobar. Si consultamos el valor de MSXVER ($002D) obtendremos el modelo sobre el que nos encontramos:
- 0 - MSX1
- 1 - MSX2
- 2 - MSX2+
- 3 - TR
- 4 - OCM
A continuación,
si no estamos sobre un MSX1 y nuestro programa lo requiere podemos afinar más la identificación consultando un par de posiciones de memoria en las que podremos encontrar información acerca de determinados usos locales para los que nuestro modelo de MSX ha sido acondicionado:
Estas posiciones son la $2B y la $2C. Su contenido se distribuye de la siguiente manera:
-------
| 2BH |
-------
b0 --+
| Generador de carácteres
b1 |
| 0: Japones 1: Internacional
b2 | 2: Ruso (¿Coreano?)
|
b3 --+
b4 --+
| Formato de fecha
b5 |
| 0:Y/M/D 1:M/D/Y 2:D/M/Y
b6 --+
b7 Periodo de sincronismo (VSYNC)
0:60Hz 1:50Hz
-------
| 2CH |
-------
b0 --+
| Tipo de teclado
b1 |
| 0:Japonés 1:Internacional
b2 | 2:Francés 3:Inglés
| 4:Alemán 5:Ruso 6:Español
b3 --+
b4 --+
|
b5 | Versión del BASIC (PRINT USING, etc.)
|
b6 | 0:Japonés 1:Internacional
|
b7 --+
Especialmente importantes son los contenidos del bit 7 de $2B (sincronismo) y de los bits 0-3 de $2C (tipo de teclado).
RAM mínima disponibleEl estandar determina que la RAM mínima disponible que puede tener un equipo de la norma es de 8Kb, ¿que significa esto?, pues que si hacemos los juegos pensando en usar 16Kb de RAM, no funcionarán en esas máquinas que sólo monten 8Kb. La decisión es nuestra, en caso de optar por la compatibilidad, deberemos ajustar nuestras necesidades a esa cantidad de memoria, que en realidad será bastante menor de 8Kb (no llegarán a 5Kb los disponibles).
Las areas de datos, en un caso o en otro deberán inicializarse como sigue:
8Kb RAM : Area de datos en RAM a partir de $E000
16Kb RAM : Area de datos en RAM a partir de $C000
Modelos MSX1 conocidos con 8Kb de RAM:RAM máxima disponible (MSX1)/ Slots primarios) y slots secundarios (Subslots)El Z80 o micro compatible que incorporan los MSX únicamente puede acceder a un rango de direcciones lineal comprendido entre 0 y 65535 ($FFFF), de ahí que la máxima memoria a la que puede acceder un MSX1 es de 64K (los MSX2 pueden acceder hasta a 4Mb mediante un mapper; pero eso ya es otra historia), no hay margen para más.
Estos 64Kb son lineales para la CPU; pero en realidad su disposición física puede diferir mucho de la lógica. En teoría nosotros vemos lo mismo que la CPU :
--- 0000 ---+----------+
| |
Página 0 | BIOS |
| |
--- 4000 ---+----------+
| |
Página 1 | BASIC |
| |
--- 8000 ---+----------+
| |
Página 2 | RAM>16KB |
| |
--- C000 ---+----------+
Página | RAM 16KB |
--- E000 ---+----------+
3 | RAM 8KB |
--- FFFF ---+----------+
Las páginas 0 y 1 siempre están ocupadas por la BIOS y el intérprete de BASIC respectivamente, a partir de ahí el contenido de las dos siguientes dependerá de la cantidad de RAM disponible por el MSX, los equipos con 32Kb o más (¿existen equipos con 24Kb de RAM?) tendrán RAM disponible en las páginas 2 y 3 ($8000 y $C000), los equipos con 16Kb sólo en la página 3 ($C000) y los de 8Kb, igualmente en la página 3; pero a partir de $E000. Respecto a esto, es importante hacer notar que lo único que podremos asumir en un MSX al inicializarse es que
en la página 3 siempre tendremos RAM.
Sin embargo, lo habitual es que un equipo MSX incorpore 64Kb, ¿donde están los otros 32Kb?. Están ahí, sólo que enmascarados por la BIOS y por el BASIC; despues de todo hemos dicho que un Z80 no puede ver más alla de 64Kb, así que si tuvieramos los 64Kb de RAM visibles, no podriamos tener al mismo tiempo ni BIOS, ni intérprete BASIC.
Esa RAM "paralela" se esconde en lo que a partir de ahora vamos a llamar
slot y que físicamente corresponden, cada uno de ellos, a una ranura de cartucho (aunque no tienen porque ser accesibles por nosotros
).
Cuando deciamos que la disposición física puede diferir mucho de la visible lógica, nos referiamos entonces a esto:
Número de slot
+--------+--------+--------+--------+--------+--------+--------+
| 0 | 1 | 2 | 3-0 | 3-1 | 3-2 | 3-3 |
| | | | | | | |
--- 0000 ---+========+========+========+========+========+========+========+
| | | | | | | |
Página 0 | BIOS | | | | | RAM |
| | | | | | | |
--- 4000 ---+--------+--------+--------+--------+--------+--------+--------+
| | | | | | | |
Página 1 | BASIC | | | | | RAM | |
| | | | | | | |
--- 8000 ---+--------+--------+--------+--------+--------+--------+--------+
| | | | | | | |
Página 2 | | | | | | RAM | |
| | | | | | | |
--- C000 ---+--------+--------+--------+--------+--------+--------+--------+
| | | | | | | |
Página 3 | | | | | | RAM | |
| | | | | | | |
--- FFFF ---+--------+--------+--------+--------+--------+--------+--------+
Esta sería la disposición de la RAM en un Philips VG-8020. Según el esquema podemos comprobar como los 64Kb se encuentran en serie en el slot 3, mientras que la BIOS y el BASIC están en el slot 0 (es lo habitual). Esto podemos comprobarlo desde BASIC ejecutando:
PRINT HEX$(INP(&HA8))
El resultado obtenido será $F0. Este valor tendremos que interpretarlo a partir de lo siguiente:
Puerto $A8bit 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
+---+---+---+---+---+---+---+---+
+---+ +---+ +---+ +---+
Slot Slot Slot Slot
Pág.3 Pág.2 Pág.1 Pág.0
Este resultado nos está indicando que en ese preciso instante (desde BASIC) en las páginas 2 y 3 estamos "viendo" el slot 3, que como consta en el anterior esquema contiene RAM y que en las páginas 0 y 1 vemos el slot 0, que contiene la BIOS y el BASIC,... todo en orden
Si queremos activar los otros 32Kb podriamos pensar que con un OUT &HA8,&HFF lo tendríamos todo arreglado; pero claro, más de uno ya habrá caido en la cuenta que desde BASIC no podemos desactivar ni la página de la BIOS, ni la del BASIC, el cuelgue sería inmediato. Para este menester será siempre necesario recurrir al código máquina; mediante una rutina como la que sigue conseguiriamos nuestro proposito de activar los 64K:
di
in a, ($ A8)
and $ F0
ld c, a
RRCA
RRCA
RRCA
RRCA
or c
ei
out ($A8),a
¿Y ya tendriamos los 64Kb dispuestos para ser usados?;.....pues no....
En el caso del Philips VG-8020 y de otros muchos modelos, si que es cierto que los 64Kb se encuentran en el mismo slot; pero la norma no especifica esto como algo de obligado cumplimiento; por lo que cada fabricante montará esas páginas de 16Kb donde más le convenga, podrán estar distribuidas al azar por cualquiera de los 4 slots. Está claro que para activar una página en concreto se hace necesaria una rutina que no se limite a aplicar una regla fija, sino que busque por
todos los slots esa RAM. Ahora, como si todavía no fuera lo suficientemente complicado, entrarán en juego los
slots secundarios 8o.
En el mapa de memoria del Philips podemos ver como la RAM está en el slot 3; pero hay algo más, esta aparece realmente en el 3-2, ¿que significa eso?; pues que cada uno de los slots primarios puede subdividirse en otros cuatro (0-3) llamados secundarios o subslots, estos nuevos slots no tienen representación física, se mapean únicamente a nivel lógico. Si un slot primario va a poder estar dividido o no en secundarios podemos consultarlo en las siguiente tabla de posiciones de memoria:
EXPTBL$FCC1 - Slot 0: $80 = expandido, 0 = no expandido.
$FCC2 - Slot 1: $80 = expandido, 0 = no expandido.
$FCC3 - Slot 2: $80 = expandido, 0 = no expandido.
$FCC4 - Slot 3: $80 = expandido, 0 = no expandido.
Por culpa de todo esto, un nuevo y rápido cálculo nos muestra que de las 16 ubicaciones originales que teniamos para las páginas de 16Kb, ¡ahora podremos tener hasta 64!.
La rutina de la que hemos hablado ya no deberá unicamente buscar la RAM por los slots primarios; sino que tendrá que tener en cuenta también la existencia de los slots secundarios.
Otro ejemplo típico de manipulación de slots es el caso de las ROMs de 32Kb ($4000-$BFFF). Si nos planteamos el hacer una, hemos de tener en cuenta que la BIOS únicamente inicializará por su cuenta la página 1 ($4000-$7FFF) que es donde se encuentra la cabecera de la ROM con la dirección de ejecución de la misma. Tendremos que ser nosotros los que inicialicemos la página 2 con los 16Kb superiores de nuestra ROM y para ello tendremos que averiguar en que slot primario y secundario se encuentra. El procedimiento sería como el que sigue:
Código por Ramones/ TMHB
ENASLT: equ 024h
slotvar: equ 0E000h ; My Rom slot
RSLREG: equ 0138h
EXPTBL: equ 0FCC1h ; Bios Slot / Expansion Slot
org 04000h
db 041h,042h
dw initmain
ds 12
initmain:
di
im 1
ld sp,0F380h
call search_slotset
; Slot posicionado
jr $
; -----------------------
; SEARCH_SLOTSET
; Posiciona en pagina 2
; Nuestro ROM.
; -----------------------
search_slotset:
call search_slot
jp ENASLT
; -----------------------
; SEARCH_SLOT
; Busca slot de nuestro rom
; -----------------------
search_slot:
call RSLREG
rrca
rrca
and 3
ld c,a
ld b,0
ld hl,0FCC1h
add hl,bc
ld a,(hl)
and 080h
or c
ld c,a
inc hl
inc hl
inc hl
inc hl
ld a,(hl)
and 0Ch
or c;
ld h,080h
ld (slotvar),a
ret
Con esta rutina, nuestra ROM ya estará completa en las páginas 1 y 2 y podrá ejecutarse correctamente.
ROMs de 48Kb (por Ramones)Aunque las ROMs más comunes en el MSX son las de 16Kb y 32Kb (1 y 2 páginas respectivamente) tambien podemos ensamblar ROMs de 48Kb con las que tendriamos el rango completo de direcciones (64Kb) bajo nuestro control.
Son algo más complicadas que las anteriores, el principal problema es que la única página que nos quedaba disponible sin usar es la página 0 y esta página contiene la BIOS. Esto complica algo la programación, ya no sólo porque dejaremos de tener disponible la BIOS para nuestro programa, sino porque con la BIOS perderemos el ISR (Interrupt Service Routine) asociado al modo de interrupciones estandar en los MSX, el IM1, instalado a partir de la dirección $38.
Para sobrellevar esto podremos emplear ciertas estrategias como:
- Usar la página 0 como almacen de datos: para ello, tendríamos que activar nuestra página de la ROM, usar los datos allí contenidos y finalmente restaurar la BIOS. El proceso se repetiría para cada acceso que quisieramos hacer.
Todo esto habría que hacerlo siempre con las interrupciones desactivadas, ya que como hemos dicho, en el modo IM1, el sistema trataría de ejecutar el código a partir de la dirección $38 al producirse una interrupción y en caso de suceder eso mientras que nuestra página estuviese activa, no existiría tal código y el programa se colgaría. - Programando un nuevo ISR: tendriamos que sustituir el ISR original de la BIOS con uno nuestro en la dirección $38, de esta manera ya podríamos tener permanentemente activa esta página, que ya podría contener lo que nosotros quisieramos, código, datos o ambos.
- Cambiando el modo de interrupciones a IM2: similar al anterior, mediante este método podríamos tener permanentemente activada la página ya que las interrupciones ya no tratarían de ejecutar el código en $38, nuestro ISR se instalaría en función de los requisitos de IM2 (ver ejemplo en la sección de snippets).
- Soluciones mixtas: ningún método es definitivo y en función de nuestras necesidades podremos en unos momentos tener la página permanentemente activada y en otros desactivarla para poder usar la BIOS.
En cualquier caso, la forma de acceder a esa página y de recuperar la bios, ya no puede ser realizada por las rutinas del sistema, es decir la BIOS. El MSX no cuenta con una manera de posicionar en la página de la BIOS otro slot y volver a recuperar la BIOS con otra rutina de sistema. Por eso hemos de hacerlo nosotros mismos, y para ello hay que ser conscientes y respetuosos con los slots y subslots. Lo que adjunto es un poco evolución de lo anteriormente expuesto, ya que necesitamos tener en la variable slotvar, el slot/subslot de nuestra ROM, por lo tanto estas rutinas no son funcionales si no hemos ejecutado antes la rutina
search_slot.
; ------------------------------
; SETROMPAGE0
; Posiciona nuestro cartucho en
; Pagina 0
; -----------------------------
setrompage0:
ld a,(slotvar)
jr setslotpage0
; ------------------------------
; RECBIOS
; Posiciona la bios ROM
; -------------------------------
recbios:
ld a,(EXPTBL)
; ---------------------------
; SETSLOTPAGE0
; Posiciona el slot pasado
; en pagina 0 del Z80
; A: Formato FxxxSSPP
; ----------------------------
setslotpage0:
di
ld b,a ; B = Slot param in FxxxSSPP format
in a,(0A8h)
and 011111100b
ld d,a ; D = Primary slot value
ld a,b
and 03
or d
ld d,a ; D = Final Value for primary slot
; Check if expanded
ld a,b
bit 7,a
jr z,recbiosprimary ; Not Expanded
and 03h
rrca
rrca
and 011000000b
ld c,a
ld a,d
and 00111111b
or c
ld c,a ; Primary slot value with main slot in page 3
ld a,b
and 00001100b
rrca
rrca
and 03h
ld b,a ; B = Expanded slot in page 3
ld a,c
out (0A8h),a ; Slot : Main Slot, xx, xx, Main slot
ld a,(0FFFFh)
cpl
and 011111100b
or b
ld (0FFFFh),a ; Expanded slot selected
recbiosprimary:
ld a,d ; A = Final value
out (0A8h),a
; Slot Final. Ram, rom c, rom c, Main
ret
FIN DE LA PARTE I