viernes, 18 de diciembre de 2015

Gestión de memoria en el Amstrad CPC

Introducción


Me he planteado escribir sobre este tema con el objetivo principal de recopilar información para el diseño del Megaflash Plus, y ya que recogía la información porqué no compartirla con vosotros. Si alguien se le ocurre leerla  :o  y encuentra cualquier error, le agradecería muchísimo que me lo hiciera saber.

Probablemente sea un tema insufrible para muchos de vosotros, pero igual le es útil a alguien, ya que la información que hay a este respecto esta dispersa y en inglés. Yo me conformo con que me sirva a mi mismo, pero si le sirve a alguien más, mejor que mejor.  :D 

El texto aborda las dos cosas necesarias para el diseño del Megaflash Plus:

- la gestión del paginado de la ROM (tanto la lowerROM como la UpperROM)
- la gestión del paginado de la RAM 

Comencemos pues con la descripción.

Gestión de la memoria en un CPC

La verdad es que el modelo de gestión de memoria del CPC está muy currado, me ha gustado mucho como está pensado.
Básicamente, en un CPC, como en cualquier otro microordenador de 8 bits, tenemos dos tipos de memoria; memoria ROM y memoria RAM.
Ya sabemos que el espacio de direcciones de un Z80 es de solo 64K, por lo que estamos acostumbrados a que si por ejemplo tenemos 16KB de ROM solo nos quede disponible para la memoria RAM 48KB. Este es el caso, por ejemplo, del Spectrum.
En un CPC, a diferencia de otros modelos de memoria, el espacio de direcciones de la ROM coexiste con el de la RAM, permitiendo que esta ocupe los 64KB completos del espacio de direcciones del Z80. Esto es posible gracias a un estudiado sistema de gestión de la memoria que permite activar y desactivar la ROM, además de otras muchas características adicionales que consiguen una gestión muy flexible.

UpperROM y LowerROM

En nuestros CPCs existen dos posibles tipos de ROM, la lowerROM y la UpperROM.
La lowerROM es la que podemos considerar la ROM tradicional con la que viene cualquier microordenador, donde residen las rutinas esenciales del sistema.
La lowerROM reside en los primeros 16K del espacio de direccionamiento ($0000-$3FFF).
Por otro lado, tenemos un segundo tipo de ROM llamado UpperROM. Esta ROM reside en la zona $C000-$FFFF, y tiene la particularidad de que no tiene porqué ser única, sino que puede tener multiples UperROMs que son activadas y desactivadas por el sistema y que se inicializan al encender el ordenador, asignandoles un número de UpperROM y guardando en una tabla una lista de comandos (RSX) con los que poder acceder a ellas.
En un CPC464 tal y como viene de fábrica existen una lowerROM (el firmware) y una UpperROM (el BASIC o upperROM 0). Ambas coexisten en un único chip de 32KB.
En el caso de un CPC6128 o un 664, además del BASIC tenemos una segunda upperROM en un chip aparte, con lo que denominamos el AMSDOS, que se inicializa en la posición número 7 de la tabla y contiene todos los comandos de acceso a disco.
Con el interface adecuado (por ejemplo el Megaflash) podemos tener muchas más upperROMs y completar el sistema con sistemas alternativos o añadirle juegos de comandos.
Por otro lado decíamos que podemos tener 64K de RAM ocupando todo el espacio de direcciones. Es el caso de un CPC464.
Por defecto tendremos 16KB de RAM situados en el espacio $0000-$3FFF que se solapan con la lowerROM y que solo podrían ser accesibles en lectura desactivando temporalmente dicha lowerROM, mientras que siempre estarán accesibles en escritura. Además tendremos otros 16KB de RAM que por defecto son los que corresponden a la pantalla, situados en la zona $C000-$FFFF y que por tanto se solapan con la upperROM. El caso es exactamente igual al anterior, por lo que para poder acceder a ellos en lectura deberemos desactivar la upperROM, pero nada nos impide escribir en ellos aunque la upperROM esté activa, por lo que escribir en pantalla no será ningún problema.
El resto de la RAM $4000-$BFFF está siempre accesible tanto en lectura como en escritura.
Los 64Kb de memoria RAM existentes en un 464 son lo que llamamos memoria base o principal.
En este modelo tanto la gestión del paginado de RAM como la de la ROM se realiza mediante un chip propietario llamado Gate-Array (40007 en los primeros modelos, y 40008 ó 40010 en los siguientes). Este Gate-Array es el único chip no estándar del CPC 464 y es a éste como la ULA al Spectrum. O sea, el típico chip que si está jodido ya puedes dar por muerto el CPC.

En el CPC6128 se añadió, además de la disquetera, una expansión de memoria RAM de 64Kb. A dicha memoria le llamaremos memoria extendida. Dicha memoria no puede ser manejada por el Gate-Array original, por lo que Amstrad decidió añadir un nuevo chip auxiliar al Gate-Array cuya función principal es implementar los modos de paginado de la RAM. Dicho chip es el que viene rotulado como 40030 más o menos a mitad del esquema del 6128, y no es ni mas ni menos que una PAL.

Si lo que fuéramos a implementar es una ampliación de memoria interna para el CPC6128, no tendríamos más que hacer uso de las funcionalidades implementadas en dicho chip, para conseguirlo, sin necesidad de añadir ninguna lógica adicional. El inconveniente de esto es que tenemos que meter mano dentro del CPC, además de que hemos de usar memoria dinámica. Otro inconveniente adicional es que esta ampliación no nos serviría para el CPC464 ya que éste no posee dicho chip.




En nuestro caso, Megaflash Plus será un interface externo, lo que hace que no podamos aprovecharnos de la lógica de dicho chip. Debemos entonces implementar toda la lógica en un chip ubicado en nuestro interface que haga las veces del mismo. La idea es utilizar para dicho fin una GAL, que no es ni más ni menos que una versión moderna de una PAL.
Bueno, realmente van a ser dos, una para la gestión de las ROMs (que en la primera versión del Megaflash estaba implementada con 5 chips) y la otra para sustituir la PAL que gestiona el paginado de la RAM.

Si lo vemos desde el punto de vista de un programa desarrollado para CPC, éste necesitará acceder a toda la memoria RAM y quizás también puede necesitar acceder a alguna rutina que tengamos en ROM. Todo esto, y algunas cosas más que no son objeto de este texto, se pueden hacer cambiando la configuración de Gate-Array, escribiendo en varios registros internos de éste. (A partir de ahora hablaremos de ambos chips como si fueran una unidad) 

El problema lo tendremos en el caso de las funcionalidades añadidas por nuestro interface, ya que entrarán en claro conflicto con las del Gate-Array. Para poder sincronizar este tipo de cosas necesitaremos utilizar una serie de señales presentes en el bus y que se verán al final del texto.
Por ejemplo, si queremos gestionar la RAM desde un interface externo deberemos utilizar la señal RAMDIS para deshabilitar los accesos a la RAM interna cuando nos interese.

¿Y como narices puedo yo cambiar un registro del Gate-Array si este es un chip que está en el interior del CPC y al que no tengo acceso? (o a las funcionalidades del Megaflash plus si está presente). Pues muy sencillo, escribiendo valores en un puerto destinado a este fin, el puerto $7Fxx.

Cuando escribimos en este puerto, el valor que escribimos nos indicará tanto el registro como el parámetro que queremos cambiar en ese registro.
Indicamos el registro al que queremos acceder con los tres bits superiores (7,6,5).
El resto de los bits (4,3,2,1,0) son el valor que asignamos al registro.

La siguiente tabla muestra los registros a los que podemos acceder por este método:



De entre todos los registros de la tabla, me voy a centrar en el RMR que gestiona, entre otras cosas, el mapeo de las ROM, y en el MMR que gestiona el paginado de la RAM.
El resto, aunque interesante, se sale del objeto de este texto.

- RMR (100) = Controla el contador de interrupción (reset) y selecciona el paginado de la LowerROM, la UpperROM y el modo de vídeo.

Con este registro habilitamos o no el paginado de la ROM y seleccionamos el modo de vídeo.
Si tenemos habilitado el paginado de la ROM, la selección de la UpperROM la haremos con el registro de selección de página de UpperROM, al que se accede en el puerto $DFxx. 
En lo que a mi me interesa, que es su implementación a nivel de hardware para el Megaflash, se hace una decodificación parcial de dicho puerto comprobando que el bit 13 es igual a 0. Si es así leo el contenido del bus de datos que debe contener la UpperROM seleccionada.

Se puede ver esto y el resto de la funcionalidad del registro RMR en la siguiente tabla.

RMRCommands
76543210
100IURLRVM
I - Si está a 1 se incializa el contador de interrupciones
UR - Habilitar (0), Deshabilitar (1) el paginado de la UpperROM (C000-FFFF)
LR - Habilitar (0), Deshabilitar (1) el paginado de la LowerROM (0000-4000) 
VM - Seleccionar el modo de video (modos 0, 1, 2, 3) Tomara efecto en el próximo HSync.

En el siguiente ejemplo se puede ver como haríamos uso de este registro para deshabilitar la lower y la upperROM.

LD BC,7F00 ;Gate array port
LD A,%10000000+001110 ;Mode and ROM selection (and Gate Array function)
OUT (C),A ;Send it

- MMR (11X) = Registro de selección de página de RAM. Gestiona el paginado de la RAM y nos permitirá gestionar la ampliación de memoria de 512Kb que se implementará en el Megaflash plus.


Como veis hay un montón de combinaciones y modos que se pueden seleccionar, mezclándose la memoria principal con la extendida. Este modo de paginar es algo más complejo que en un Spectrum, por lo que su implementación no es tan trivial como en este.

En el cuadro anterior hemos visto como el registro MMR gestiona en paginado de la RAM, pero para esto Hay dos tipos de RAM:

- Los 64Kb de memoria base. Que corresponden con los primeros 64Kb de la memoria interna. 
- La memoria extendida, ya sea la correspondiente a los 128Kb o las expansiones externas de memoria.
Mediante el registro MMR se permite el uso de hasta 512Kb. Las ampliaciones que aportan más RAM suelen usar direcciones de entrada/salida adicionales para el páginado como $7EXX, $7DXX, etc.

En un CPC 464 sólo tenemos memoria base, pero en un 6128 tenemos 64Kb de memoria base y 64Kb de memoria extendida. Añadiendo 512Kb con un interface externo como el Megaflash Plus, tendremos 64Kb de memoria base y 512Kb de memoria extendida, ya que los 64Kb que ya poseen los CPC6128 quedan reemplazados por la ampliación externa. Tendremos entonces un total de 576Kb.

Como podéis ver en la tabla, la memoria (tanto la memoria base como la extendida) se divide en bloques de 64Kb (Páginas) y estos a su vez en subbloques de 16Kb (Bancos). 

De hecho hay combinaciones que permiten sustituir la RAM base en su totalidad por la memoria extendida, o modos (el 1 y el 2) en los que no está accesible la zona de memoria del display (banco 3 de la página 0).

Accederemos al registro MMR escribiendo en el puerto $7Fxx con el valor de selección deseado. Dicho valor deberá estar compuesto de los siguientes valores:

- Bits 7 y 6: deben valer "11" para indicar que estamos accediendo al registro MMR.

- En los bits del 5 al 3 indicaremos el número de la página de 64kb que deseamos utilizar.

- Por último los bits 2, 1 y 0 nos indicarán el modo en el que dispondremos la memoria en el espacio de direcciones del Z80.

Ejemplo 1:
Si dichos bits valen "011" tendremos la siguiente disposición de la memoria:
              - $0000-$3FFF - Banco 0 de la memoria base (o sea la memoria interna del CPC)
              - $4000-$7FFF - Banco 3 de la memoria base
              - $8000-$BFFF - Banco 2 de la memoria base
              - $C000-$FFFF - Banco 3 de la memoria de la página p de la memoria extendida, donde p corresponde a los bits 5, 4, y 3 de lo que hemos escrito en el puerto $7Fxx.
La selección de dicho modo suponiendo la selección de la página 4 (p=100) se podría hacer con la siguiente secuencia de instrucciones:

LD BC,7F00 ;Gate array port
LD A,%11000000 + %10000 + %011; MMR + p=100 + Mode=011
OUT (C),A ;Send it

Ejemplo 2:
Si el bit 2 vale 1 tendremos la siguiente disposición de la memoria:
              - $0000-$3FFF - Banco 0 de la memoria base (o sea la memoria interna del CPC)
              - $4000-$7FFF - Banco b de la página p de la memoria extendida, donde b es el número formado por los bits 1 y 0 de lo que hemos escrito en el puerto $7Fxx
              - $8000-$BFFF - Banco 2 de la memoria base.
              - $C000-$FFFF - Banco 3 de la memoria base.
Como veis es algo complejo, pero nada que no se pueda implementar con algo de lógica discreta.

SEÑALES DE COMUNICACIÓN PRESENTES EN EL BUS

Pero además del paginado propiamente dicho que gestionamos accediendo a los registros del Gate-Array, necesitamos algunas señales adicionales presentes en el bus de expansión para coordinarnos con éste.
Dichas señales son las siguientes:

Relacionadas exclusivamente con la gestión de la RAM
- RAMDIS (Internal RAM Disable; Entrada a la RAM interna)
Cuando RAMDIS="1" se fuerza a inactiva a la RAM interna del CPC. Se puede usar para sustituir la memoria interna con la de la RAM de expansión.
- /RAMRD (Ram Read; Salida desde el Gate-Array)
Cuando /RAMRD="0" está activa una operación de lectura. Esta señal la genera el Gate-Array. Esta señal será 0 cuando:
A15=A14="0" y el bit 2 del registro de configuración de ROM (RMR) del Gate-Array está a 1. (lower rom deshabilitada)
A15=A14="1" y el bit 3 del registro de configuración de ROM (RMR) del Gate-Array está a 1. (upper rom deshabilitada)
A15 es diferente de  A14.

Relacionadas exclusivamente con la gestión de la ROM

- ROMDIS (Internal ROM Disable; Entrada a la ROM interna). Cuando ROMDIS="1" la ROM interna del CPC es forzada a inactiva. Un interface con ROM incorporada como el Megaflash Plus, debería usar esta señal para sustituir la ROM seleccionada con la presente en el interface. La ROM interna será forzada a desactivarse y la ROM del interface se activará.

- /ROMEN (ROM Enable; salida desde el Gate-Array). Cuando /ROMEN="0" una operación de lectura está activa. Esta señal es generada por el Gate-Array. Esta señal será "0" cuando:
A15=A14="0" y el bit 2 del registro de configuración de ROM del Gate-Array es 0, (lower ROM habilitada)
A15=A14="1" y el bit 3 del registro de configuración de ROM del Gate-Array es 0, (upper ROM habilitada)
Un dispositivo de expansión como el Megaflash puede usar esta señal para activar la ROM seleccionada de las que incorpora en su chip de memoria flash.

Señales relacionadas con ambas.

- /MREQ (Memory Request; salida desde la CPU). Cuando /MREQ="0" la CPU está ejecutando una operación de acceso a memoria. A15-A0 contienen la dirección de memoria.
Si /RD es "0" entonces la opración es una lectura desde memnoria y D7-D0 contendrán el dato leido desde la memoria.
Sí /WR es "0" entonces la operación es una escritura a memoria y D7-D0 contendrá los datos a escribir.

- /IORQ (Input/Output Request; Salida desde la CPU). Cuando /IORQ="0" hay dos posibles funciones:
   -Reconocimiento de interrupción: /M1="0". La función de reconocimiento de interrupción se usa para indicar que el dispositivo que solicita la interrupción puede poner un vector de interrupción en D7-D0.
   -operación de entrada/salida (lectura o escritura): /M1="1", /WR="0" o /RD="0". La operación de entrada salida o I/O se usa para leer o escribir a un periférico. Cuando la CPU está ejecutando una operación de este tip A15-A0 contiene la dirección I/O.

Si /RD es "0" entonces la operación es de lectura y en D7-D0 podremos encontrar el dato leido desde el periférico.
Si /WR es "0" entonces la operación es de escritura y D7-D0 contendrá el dato a escribir.

Adjunto descripción del hardware en pseudocódigo para implementar con una GAL al final de la entrada.

Y de momento hasta aquí puedo leer. Si veo que hay algo que deba corregir o ampliar ya os lo dejaré caer por aquí.

Como decía al principio, no espero que os lo leáis, y de hecho, si habéis llegado hasta aquí tengo que deciros que olé vuestra paciencia, porque soy consciente del rollo que acabo de soltar.
Solo utilizo esto como medio de que quede escrito en algún sitio lo que voy descubriendo para tener un sitio fácil donde consultarlo, y si además le sirve a alguien, pues para que pedir más.

De momento tengo implementada con éxito la gestión del paginado de la ROM en la versión existente del Megaflash plus SX, mientras que la implementación del paginado de la RAM tendrá que esperar a la versión sin el "SX" que no tardaré en desarrollar.

DESCRIPCIÓN DEL HARDWARE EN PSEUDOCÓDIGO

Entradas
NO_ROMEN
A14
A15
D0..7
NO_IORQ        
NO_WR
NO_MEMREQ

Salidas
RAM_NOCE
RAM_NOOE     “Sacar fuera
RAM_NOWE    “” Cablear directamente a /WR
RAMA14
RAMA15
RAMA16
RAMA17
RAMA18
RAMDIS
CLKBUFF

Se habilita el acceso a RAM si NO_ROMEN=1.

Cambio de modo de paginado:

Puerto $7FXX
%01xxxxxx xxxxxxxx
Registro MMR = 11R
CLKBUFF = iA14 & !A15 & D6 & D7&!NO_IORQ; 

NO_RAMOE = !NO_WR  “Sacar fuera
Si !O2 & !O1 & !O0 entonces
“ Pagina = Memoria base
RAMA14=A14;
RAMA15=A15;
RAMA16=0;
RAMA17=0;
RAMA18=0;
NO_RAMCE = 1;
RAMDIS = 0;
endsi

Si !O2 & !O1 & O0 entonces
$C000 = O5O4O3

RAMA14=A14;
RAMA15=A15;
Si A14&A15 entonces
NO_RAMCE = !(NO_ROMEN & !NO_MEMREQ);
RAMDIS = 1;
RAMA16=O3;
RAMA17=O4;
RAMA18=O5;
else 
NO_RAMCE = 1;
RAMDIS = 0;

RAMA16=0;
RAMA17=0;
RAMA18=0;
endsi
endsi

Si !O2 & O1 & !O0 entonces
RAMA14=A14;
RAMA15=A15;
RAMA16=O3;
RAMA17=O4;
RAMA18=O5;

NO_RAMCE = !(NO_ROMEN & !NO_MEMREQ);
RAMDIS = 1;

$0000 = O5O4O3 (banco 0)
$4000 = O5O4O3 (banco 1)
$8000 = O5O4O3 (banco 2)
$C000 = O5O4O3 (banco 3)
endsi

Si !O2 & O1 & O0 entonces
Si A14&!A15 entonces
RAMA14=1;
sino
RAMA14=A14;
endsi;
RAMA15=A15;
Si A14&A15 entonces
RAMA16=O3;
RAMA17=O4;
RAMA18=O5;
NO_RAMCE = !(NO_ROMEN & !NO_MEMREQ);
RAMDIS = 1;
sino
RAMA16=0;
RAMA17=0;
RAMA18=0;
NO_RAMCE = 1;
RAMDIS = 0;
end si
$0000 = Memoria base (banco 0)
$4000 = Memoria base (banco 3)
$8000 = Memoria base (banco 2)
$C000 = O5O4O3 (banco 3)
endsi

Si O2 entonces
si A14&!A15 entonces
RAMA14=O0;
RAMA15=O1;
RAMA16=O3;
RAMA17=O4;
RAMA18=O5;
NO_RAMCE = !(NO_ROMEN & !NO_MEMREQ);
RAMDIS = 1;
sino
RAMA14=A14;
RAMA15=A15;
RAMA16=0;
RAMA17=0;
RAMA18=0;
NO_RAMCE = 1;
RAMDIS = 0;
end si


Referencias

The GateArray - Grimware.org
The Amstrad CPC Firmware Guide
The Amstrad CPC firmware guide
Amstrad CPC 16KBs ROM Game Development Competition!

viernes, 3 de julio de 2015

SUPERUPGRADE: USER MANUAL

SUPERUPGRADE: USER MANUAL



Interface’s features are the following ones:
  • Expansion 128/256/512kb of selectable RAM with Jumpers 
  • 2 connections of multistandard joysticks (Kempston, Sinclair, cursor)
  • Reset button
  • MNI button
  • 512kb flash memory wich contains a limit of 32 ROMS of different systems and games
  • AY sound chip
  • Audio input to mix the speaker’s sound with the AY’s sound
  • Composite video output
  • Bus expander
  • Compatible with DivIDE
  • Possibility of expanding with Add-on for ROM selection by software
  • Flash memory recording using Spectrum


As you can see, it's very close to convert it into a +3. The disk drive is the clearest lack, but it’ll come soon.

The main idea is making 128kb games which needn’t access to Floppy or which don’t use Shadow RAM work modes.

If we added an IDE interface or a divIDE, we would be able to execute the disk games too. With the expansion to 512kb some of the russian clone's games are likely to be executed.


BIOS ROM AND SELECTION OF ACTIVE ROM


Our interface’s flash memory has a capacity of 512kb. Taking into consideration that the smallest ROMS for Spectrum take up 16kb (as the rubber keys’ original one), that makes a total of 32 ROMs with the same size fit there.

Nevertheless, there are other versions such as the 128kb and the +2 grey which take  32kb and even +2a/+3 64kb, therefore, the quantity of ROMs we can have will change depending on the kind of ROMs we have in the chip.

The chip is divided in pieces of 16kb, each one of them can be recorded with the ROM we want, to keep  16kb memories, as well as the biggest ones of 32kb or 64kb.

Additionally, 16kb blocks come together in other bigger 64kb blocks, which we’ll call slots from now on, so we’ll have a total of 8 slots in the chip.

When it comes to keeping a 64kb ROM, we can’t start wherever we want, we have to take a slot, but with regard to 32kb ROMs, we should use either  the 0 and 1 ROMs of a concrete slot (lower semi-slot) , or the 2 and 3 ROMs of the slot (upper semi-slot)

The active slot is selectable with jumpers, JP3, JP4 and JP5, but we’ll need software aid if we want to select the 16kb block in a slot.

The system adds a ROM BIOS located in the first 16kb block. Although initially that ROM BIOS  was thought especially  for the rom’s selection by software with add-on’s  aid (which we’ll see later), some functions, which can be easily used in the interface’s  basic version, have been added. Among them we find the diagnostic tools and a really interesting option that will allow us to select the active ROM inside the slot we have selected with jumpers.

The space will be distributed as the one below:




BIOS USE



The Superupgrade Bios’ main screen shows us a list with the ROMs included in the flash memory.
To use one of them, we have to select the corresponding letter and press enter.

In  BIOS 0.2 version a tools submenu where we can access by pressing “V” and “ENTER”, from the main menu.



In this menu we have tools to test our interface:
-KEYBOARD TEST: it’ll show us visually the keys we’re pressing and  the three superior bits of each half row.
When it comes to the keyboard, we use bits 0 to 4, whereas bit 6 is used for the ear input. To finish the test we press BREAK.



-JOYSTICK’S TEST: it’ll allow us to make a complete joystick’s test, asking us to press sequentially each one of the joystick’s buttons. When we get to the Sinclair’s test we should press simultaneously both of the joysticks.

In case we press “BREAK” we’ll go on with the next button though we didn’t press the right button.



-RAM TEST: you’ll make the RAM paging test, pointing out how many memory pages are available.


It’s not about a full integrity RAM test, since it just checks the paging.


-AUDIO TEST: It’ll make a musical scale sound for each one of the three AY‘s channels.






-ROM’S PAGE SELECTION: this option is thought to be for those users that don’t insert the add-on.



Remember that the memory is divided in 64kb blocks which are selectable with jumpers, as well as each block of 64kb contents four 64kb pages.
If, after select the option, we change jumpers to the desired ROM, this option will allow us to select the 16kb page that we want to execute.
As soon as we select the desired page, the system will ask us to press a key and then spectrum will be restarted, executing the program contained in the selected page.
As an example to clarify it a little bit, we’ll include a table about jumper/rom’s position to be selected from the example set which has been sent prerecorded in the kit.
We’ll be able to change just around eight 64KB blocks (slots from now on), therefore, depending on jumper’s position, when we reset with the jumpers in a concrete posicion, the system will boot up with the ROM 0 of the selected slot.
To start up the other three slot’s  ROMs,  the ROMs’ selection option from the tools menu will be needed.

The jumpers aren’t in order so it may be a bit confusing its use. I’m adding a table down below in order to make it clearer:



As we can see there are some ROMs which take several 16kb blocks, so in this case it’ll only have sense to start up the first block of them. For instance, if we select the second ROM of the +3 the system will be probably hangs.

We see ,in the three first columns of the table,  The position jumpers must have to select a concrete slot.

For instance, the +2 grey  ROMs are in the Slot 2, so we’ll have to put jumpers in up, down, up positions, starting to count from the superior jumper of the three ones of the ROM’s selection.

For this ROM especially the selector won’t be needed, since it takes the 0 position of this slot. Putting the jumpers and pressing reset will be enough.

When it comes to a ROM which doesn’t take the 0 position of a Slot, we do need the selector. That’s the case of the Spectrum’s ROM, which is in the position 1 of the slot 0, so, with the jumpers in the down down up position, we’ll select the selection ROMs’ option and we’ll press the 1. As, in this case, we don’t need to MOVE jumpers, The 48Kb's ROM will appear pressing a button.

The Planetoids’s case is the same one, but in this case we’ll have to select the ROM2 in the ROMs’ change’s menu and we’ll have to press a button after moving jumpers to up up up position.

ADDING OUR OWN ROMS

The ROMs which are recorded in the chip are just an example.

We can replace them to the ones we want, but we should take into account the following instructions.

The ROMs should have precisely 16kb, 32kb, or 64kb. If we have a ROM of a game which takes up less SPACE, we have to complete it until 16kb with an hexadecimal editor.

The ROM BIOS must always take the first position.

From that BIOS we have a menu which will allow us start up the ROMs BY SOFT just if we have the add-on installed.

In the case we use the add-on, in the 0 ROM, we have certain parameters which we’ll have to modify for each one of the ROMs and they will show us how that programme will behave. 




These datum take a byte for ROM and they’re locared in $020E..$022D corresponding with the ROMs 0 to 31 ( BIOS is counted  too).

In the previous table we can see that the bits 0 to 4 of each one of the bytes show us the ROM’s order number (0…31), whereas the bits 5, 6 and 7 will allow us to define the ROM’s behavior.

The bits 5 and 6 allow us to deactivate the paging of the ROM totally or partly and they’re related to the ROM’s shape.

For instance, in the case of a speccy 48Kb ROM which takes a unique 16kb block we  would have to deactivate the paging completely. As we aren’t interested anymore in executing anything outside of that block of 16kb, so both bits have to be 1.

In the case of a ROM of 32kb we have to keep active the paging related to the $7FFD port which manages the change between block 0 and 1, but we’ll have to deactivate the change to the ROMs 2 and 3 which are managed with $1FFD port. Therefore, we’ll put bit 5 to 0 and bit 6 to 1.

Finally, in case of +3 ROMs, we’d better keep active full paging, so we’ll put both bits to 0.

Bit 7 will allow us to deactivate the divIDE for the cases when we want to start up a ROM , which is incompatible with divIDE, without needing to disconnect it.


Let’s use as an example the mentioned byte’s content for the example ROMSET’s ROMs:



The texts that will appear in the menu are located in $000F..$020E and they are made of a byte $0F and the text of the 15 characters long for each one of the options.

It’s important to be careful not to crush neither byte $0F nor the following option’s text.

The following table show us the beginning position of each one of the menu’s texts.



Once we have every file to include we have to gather them in the desired order, joining all of them with the command “COPY/B” or with a hexadecimal editor, checking that the final file has precisely 512kb (524.288 bytes)

I give you as an example the bat file I used to generate the file .bin to record:
del rom*.bin
copy /b BIOS.ROM+48.rom+inves.rom+tk90x.rom rom.bin
copy /b rom.bin+"plus3-spanish.rom" rom.bin
copy /b "rom.bin"+"plus2-spanish.rom"+"128-spanish.rom" "ROM.bin"
copy /b rom.bin+ "sm8es3eE.rom" rom.bin
Copy /b rom.bin+Ace_ROM.bin+tk95.ROM+gw03.rom+OPENSE.ROM rom.bin
Copy /b rom.bin+ZXTESTROM.BIN+backgammon.rom+testrom128.bin rom.bin
copy /b rom.bin+spaceraiders.rom+chess.rom+planetoids.rom+Popeye.rom rom.bin
copy /b rom.bin+qbert.rom+roj.rom+LocoMotion.rom+Gyruss.rom rom.bin
dir rom.bin

If you don’t want to make it complicated, an easy way is using as a base the file that I’ve included as an example and modifying it with a hexadecimal editor.

If we use an hexadecimal editor, we have to take into consideration that the ROMs have to be in the following positions:




INTERFACE’S PORTS’ MANAGEMENT


For selection of ROM and RAM’s paging in 128Kb Specrtums are used $7FFD  (32765) and $1FFD (8189) ports.
The change of the ROM or RAM’s page is done passing the right value to the corresponding port with an OUT, taking into consideration that each bit of that value has the following meaning:


$7FFD port



-Bits 0..2  form a number of 3bits which allow us to select a page among the 8 possible of the RAM of the 128kb.

-Bit 3 is related with the shadow RAM and it permits to select the screen which will be visualized. Having bit 3 to 0 will ask the ULA to show the conventional screen, whereas if it’s to 1 it’ll mean that Shadow RAM will be shown. This feature isn’t supported in the interface.

-Bit 4 will allow us to select among the two possible ROMs in Spectrum 128k and the +2 GREY.
Remaining to 1 ROM of the 48kb is selected and to 0, 128kb’s.

In the case of the +2A/+3 another bit that we‘ll see later will be taken into account, making with the two of them a number of 2bits that will permit us to select one of the 4 ROMs available.

-Bit 5 will allow us to disable the pages.

-Bits 6 and 7 are not used.

$1FFD port:



-D0 bit will permit us to select the memory model of the +3 or the CPM

-If we have selected the CPM mode D1 and D2 bits will permit us to modify the distribuition of the RAM according to the following FACTS (This feature is not supported in interface):



-If bit 0 is equal to 0, D2 bit will be the high part of the selection of the ROM, with the D4 of the $7FFD port.

We can see this bit (D2 $1FFD) as a selection  of A or B chip inside the +3 and the D4 of the $7FFD as the selector of the chip’s  low or high part.

-D3 is used to activate or deactivate the disk’s motor.
-D4 is the parallel port’s strobe’s signal.
-D5 and D7 are not used.


Pagging in Pentagon

In Pentagon two more bits are added for the RAM’s page’s selection corresponding to D6 and D7 of $7FFD, leaving that port in the following way.



In our interface this page’s mode is supported having 5 bits (32 pages) for the page’s selection.

Pages’ distribution in the address space.

The address space taken by the selected page is generally $C000-$FFFF, but there are some pages that have a special treatment.

The exact distribution is sum up down below:




-Bank number 2 will be always visible in the rank $8000-$C000, but it will be also accessible in $C000-$F000 if we have page 2 selected.

-Bank number 5 corresponds with the $4000-$8000 screen’s memory in a +2/+3. In our interface it is not supported and it is one more bank. This could be implemented in the future according to the mcleod_ideafix instructions.

-Bank number 7 corresponds with ShadowRAM and its content will be visualized if D3 is to 1 in $7FFD port. In our interface is not implemented and bank 7 is not different to another one. Moreover, it’s impossible to implement without cutting some traces and without modify internally the Spectrum. Fortunately, there aren’t so much programs that get advantage of this feature, which in the other hand is really interesting.


Distribution of ROMs in the +3


In the +2/+3  4 ROMs come distributed in two chips (A and B), accumulating two ROMs each one of them.



In our interface, we accumulate the four ROMs in one and only slot, distributing its space in the following way:



It’s important to take into consideration that de table’s addressing is from the chip’s point of view, but the selected ROM will always access with the $0000-$3FFF standard directions’ rank.

Finally, a list about each ROM’s function:




Add-on $43B’s new port’s functionality

To be able to realize its function, add-on uses a new port which has been demanded to the ZXI specification group.
The port given to realize those tasks is $043B port.
This port is an only-wrtiting port and it has as a main functionality being able to select active the ROM inside the 512kb chip, as well as being able to deactivate divIDE and the standard pages with $1FFD and $7FFD ports.
Functionality is sum up in the following table:



Bits 0…1 would correspond with the ROM’s page and bits 2…4 with the slot’s number.

The ROM page’s change with this port will work even though pages were deactivated with $1FFD and $7FFD ports.

Bit 5 will allow us to deactivate pages with $7FFD port.

Bit 6 will permit us to deactivate pages with $1FFD port.

Bit 7 will allow us to deactivate divIDE. Actually what this port makes is screening /MREG and A15 signals avoiding THEM GOING TOWARDS THE EXPANSION BUS (PUTTING +5V), so its effect is that divIDE never detects that we are executing ROM’s directions. Therefore, it will never be activated though we have it connected.


SPECCY’S  SUPERGRADE’S BUS


Supergrade’s bus doesn’t coincide one by one on the front and back. The reason why it is like that is because there are some signals that are transformed to give them to the rest of the peripherals in a more convenient way for interface’s functioning.
I give you some descriptive tables of spectrum and supergrade’s bus down below, where we can see these differences.
Spectrum’s bus images have been borrowed from "El trastero del spectrum". :)








FLASH MEMORY RECORDING FROM SPECTRUM


We’re now in the middle of  the developing of a flash memory’s recording’s tool  from Spectrum.

The next program has been developed by antoniovillena  and it consists of a command's line software that, since a ROM’s file from up to 64kb, will generate a tap file ready to be executed in our Spectrum.


That tap file can be made into wav and charged by audio, or it can also be charged directly from divIDE.

If we decide to go for the charge by audio, a great idea would be update spectrum’s ROM (that it’s installed in 0 block, rom 1, in the example) with the cargandoleches ROM as a first step, and from that moment use it always to charge roms’ taps that we’re going to BURN in our flash.

In the folder we should have “SuperUpgrade.exe” file and “SuperUpgrade.bin” file.

Program’s syntax is in the following way:
SuperUpgrade <outputfile> <roms> <inputfile1> <inputfile2> .. <inputfilen>
<outputfile>  Output TAP file
<roms>        4 digit number, X to preserve. 0123 full 64K, XXX3 only ROM3
<inputfileX>  Input binary file/s
That is, to generate a tap with +3 ROMS we would do the following:
SuperUpgrade hola.tap 0123 plus3en41.rom

Once the tap is generated, I can pass it to my divIDE’s CF or charge it by audio, and once the  spectrum is charged, a screen like the one below will appear:



In the case we have “Add-on”, we just have to press a key between 0 and 7 coinciding with the slot where we want to record ROM/s.

If, on the opposite, we don’t have Add-on , we have to change jumpers to the desired position and press a key.

Currently, our mate pachuquín is developing a frontend that firstly was thought to make easy the ROMSETs’ generation to record from a EPROMs’ recorder, but it will soon add antoniuovillena’s utility and it will permit us generate taps from a more friendly graphic interface.

viernes, 5 de junio de 2015

Reparando la ULA de un ZX-Interface 2

Hace algún tiempo que el amigo Fermars disponía de un IF2 con la ULA averiada, y comentándo en Va-De-Retro nos propusimos crear una ULA casera para poder sustituir este chip que es prácticamente imposible de conseguir hoy en día.

Dejamos el tema en espera durante algún tiempo, y el otro día retomamos el tema. Después de algunas pruebas a distancia, Fermars me envió el IF2 y este es el resultado.   :D



Y esta la caja cerrada con el conjunto dentro:


Lo primero que se necesita para poder abordar el tema es el esquema del interface:




Como se puede ver en el esquema anterior, el circuito es muy simple. Consta de una ULA que controla la gestión de los joysticks y el simple cableado para enrutar el slot de los cartuchos y la expansión del bus para la conexión de una zx-printer.

La gestión del joystick consiste en leer el estado de pulsación de los botones y direcciones del joystick y convertirlos en pulsaciones de teclas. Como decía antes, de esto se encarga la ULA, no interviniendo ningún otro circuito en el tema, por lo que utilizando una GAL, quizás podamos emular su funcionalidad..
La idea inicial era utilizar una GAL20v8 para sustituir a la ULA, pero al final me han hecho falta también algunas resistencias y diodos.

El esquema final es este (abrir en una pestaña nueva para ver en detalle):


Estos los archivos Eagle: Descargar

Y el archivo JED este otro: Descargar

Aquí tenéis los fuentes en ABEL HDL: Descargar

Lo primero que hice fue probar en una placa de entrenamiento casera.



Y después de algunos cambios las pruebas fueron satisfactorias.
La idea inicial era utilizar solo la GAL sin ningún componente adicional, pero enseguida me encontré con algunas dificultades.

La primera de ellas fue que la distribución lógica de la GAL no permitía dejar cada una de las salidas por separado en estado de alta impedancia sin consumir una patilla adicional. Esto era necesario, para no bloquear las semifilas superiores del teclado.
El tema es que hay que forzar solo los bits que son cero del bus de datos, que correponden con una tecla pulsada, ya que si forzamos los unos el teclado no podrá poner ceros en el bus y no nos funcionarán las dos semifilas superiores.

Este hecho me obligó a añadir diodos para dejar pasar únicamente los ceros. El resultado debería ser un OR entre las pulsaciones del bus y el teclado.

Otra conclusión es que no te puedes fiar de las resistencias internas de pull-up, porque si dejas desconectada una patilla se comporta como si fuera un registro, es decir conserva su último estado. Pienso que esto debe ser configurable por algún sitio pero no he encontrado la opción. Son por tanto necesarias las resistencias de pull-up de entrada correspondientes a las direcciones de los dos joystick.

Voy a exponer el comportamiento teórico que debe tener:

- Para que se habiliten las salidas debemos tener 0v en las siguientes señales: /RD, /IORQ, A0 y al menos una entre A11 y A12.

- La semifila de la izquierda "12345" es el primer joystick y utiliza el puerto $F7FE (1111 0111 1111 1110). Se identifica con la decodificación parcial A11=0 y A0=0. Se corresponden las siguientes teclas y botones de joystick:

- D0  = "Tecla 1" = LEFT1
- D1  = "Tecla 2" = RIGHT1
- D3  = "Tecla 3" = DOWN1
- D4  = "Tecla 4" = UP1
- D5  = "Tecla 5" = FIRE1

- La semifila de la derecha "67890" es el segundo joystick y utiliza el puerto $EFFE (1110 1111 1111 1110). Se identifica con la decodificación parcial A12=0 y A0=0. Se corresponden las siguientes teclas y botones de joystick:
- D0  = "Tecla 0" = FIRE2
- D1  = "Tecla 9" = UP2
- D3  = "Tecla 8" = DOWN2
- D4  = "Tecla 7" = RIGHT2
- D5  = "Tecla 6" = LEFT2

Si tenemos las resistencias colocadas en las entradas entre +5v y cada patilla, las entradas se mantienen a +5v en estado de reposo. Estos +5v son débiles gracias a la resistencia que hemos intercalado, por lo que cualquier pulsación de una de las direcciones o del botón de fuego conecta el pin con masa y lo lleva a 0v.
Mientras el ordenador no está consultando el teclado, la GAL mantiene en alta impedancia las salidas ya que no se cumple /RD=/IORQ=A0=0. En el momento en el que consulta, por ejemplo, la semifila izquierda /RD=/IORQ=A0=A11=0, momento en el cual las salidas copian el estado de las entradas, es decir, si teníamos pulsado LEFT1, tendremos su patilla a 0V y D0 tendrá 0V mientras que el resto de las entradas estarán a +5v gracias a las resistencias de pull-up, y por tanto las salidas D1,D2,D2 y D4 tendrán 5v (o algo parecido mayor que 2,5V).

Para la otra semifila será lo mismo pero se deberá cumplir /RD=/IORQ=A0=A12=0.

Al final, la lógica de la GAL se resume en lo siguiente:

DATAOE = (A0
     # IORQ
     # RD
     # A11 & A12);
D4 = (!RD & !IORQ & !A0 & A11 & !A12 & LEFT2
     # !RD & !IORQ & !A0 & !A11 & FIRE1);
D4.OE = (!DATAOE);
D3 = (!RD & !IORQ & !A0 & A11 & !A12 & RIGHT2
     # !RD & !IORQ & !A0 & !A11 & UP1);
D3.OE = (!DATAOE);
D2 = (!RD & !IORQ & !A0 & A11 & !A12 & DOWN2
     # !RD & !IORQ & !A0 & !A11 & DOWN1);
D2.OE = (!DATAOE);
D1 = (!RD & !IORQ & !A0 & A11 & !A12 & UP2
     # !RD & !IORQ & !A0 & !A11 & RIGHT1);
D1.OE = (!DATAOE);
D0 = (!RD & !IORQ & !A0 & A11 & !A12 & FIRE2
     # !RD & !IORQ & !A0 & !A11 & LEFT1);
D0.OE = (!DATAOE);
Lo anterior, en un lenguaje de descripción un poco más inteligible quedaría como sigue:
!DATAOE = !(RD#IORQ) & (!A0&!A11 # !A0&!A12);
Data.oe = !DATAOE;
when !(RD # IORQ) then {
"Sinclair Left
when !A11 & !A0 then {
Data = [FIRE1,UP1,DOWN1,RIGHT1,LEFT1];
}
"Sinclair Right
else when !A12 & !A0 then {
Data = [LEFT2,RIGHT2,DOWN2,UP2,FIRE2];
}
}

Como veis, la solución es bastante sencilla, pero no por ello menos interesante.

Espero que hayáis disfrutado con esta entrega, yo la verdad es que he aprendido bastante, y sobre todo he disfrutado con el desarrollo.

Ahora, por fin mi amigo Fermars puede volver a disfrutar de su Interface 2. 
Con lo anterior ya me doy por satisfecho, pero me encantaría saber que la publicación de estas líneas pueda ayudar a devolver a la vida a algún Interface 2 más que ya se había dado por muerto.

Un saludo y hasta la próxima.

Alejandro Valero (wilco2009)