En una de las primeras entradas del blog describimos el mapa de memoria estándar del C64. Este mapa de memoria es así:

Es decir, consta de:
- La página 0, que contiene muchas variables o posiciones que se usan desde el intérprete de BASIC y desde el Kernal.
- La página 1, que se usa para la pila de llamadas a subrutinas.
- 40K de RAM, que incluyen la RAM de pantalla en su ubicación estándar ($0400-$07e7) y 38K de RAM para programas en BASIC ($0801-$9fff).
- El intérprete de BASIC en ROM.
- 4K de RAM donde, de forma muy habitual, se han ubicado los programas en código máquina, al menos, los programas pequeños.
- Los chips especiales para vídeo, sonido y entrada / salida. También la RAM de color.
- El Kernal en ROM.
Este es el mapa estándar, es decir, el que ofrece el C64 recién arrancado. Ahora bien, una de las características clave del C64 es que soporta diferentes mapas de memoria en función de las necesidades de la aplicación. De hecho, el microprocesador 6510 es esencialmente un 6502, con el mismo juego de instrucciones, pero con capacidad para gestionar diferentes mapas de memoria.
De esta gestión se encargan los registros:
- D6510 = $0000.
- R6510 = $0001.
El primero es un DDR o registro de dirección de datos. Es decir, sus bits sirven para configurar si los bits de un IOP o puerto de entrada / salida asociado, en este caso el puerto R6510 = $0001, son de entrada o salida. Su valor por defecto es 47 = %00101111, lo que significa que todos los bits del puerto R6510 son de salida (1), salvo el 4, que es de entrada (0); los bits 6 y 7 no se usan.
Por su lado, el registro R6510 = $0001 es el puerto de entrada / salida propiamente dicho. En este caso, en función de los valores 0 o 1 que se configuren en sus bits, se consigue que el microprocesador 6510 direccione unos u otros bancos de memoria, en particular, bancos de ROM (lo normal) o RAM (opcional).
En particular, los bits del puerto R6510 = $0001 son:
Bit |
Nombre bit |
Si vale 0 |
Si vale 1 |
0 |
LORAM |
Las direcciones $a000 – $bfff direccionan RAM |
Las direcciones $a000 – $bfff direccionan la ROM con el intérprete de BASIC |
1 |
HIRAM |
Las direcciones $e000 – $ffff direccionan RAM |
Las direcciones $e000 – $ffff direccionan la ROM con el Kernal |
2 |
CHAREN |
Las direcciones $d000 – $dfff direccionan la ROM con el mapa de caracteres |
Las direcciones $d000 – $dfff direccionan los chips especiales y la RAM de color |
El registro R6510 tiene más bits (3, 4 y 5), pero tienen que ver con el datasette, y no con los mapas de memoria. Los bits 6 y 7 no se utilizan.
Extensiones de RAM:
Total, configurando un 0 en el bit 0 de R6510 podemos eliminar el intérprete de BASIC y ganar 8K de RAM adicionales. Igualmente, configurando un 0 en el bit 1 de R6510 podemos eliminar el Kernal y ganar otros 8K de RAM adicionales.
Podemos eliminar sólo el BASIC o BASIC y Kernal. Lo que no podemos hacer es mantener el BASIC y quitar el Kernal, puesto que el BASIC se apoya en éste.
De este modo, si tenemos un programa en ensamblador / código máquina que es muy grande, podemos adaptar el mapa de memoria a nuestras necesidades:


Y si aun así sigue sin caber en RAM, tendremos que organizar el programa en varios “trozos” (por ejemplo, pantallas 1 – 10, pantallas 11 – 20, etc.) de modo que en cada momento tengamos cargada en RAM sólo la parte que estamos usando y, mediante cargas sucesivas, ir cambiando de parte.
Todos estos mapas de memoria, y otras posibilidades que hay si tenemos en cuenta los cartuchos de expansión, se describen en las páginas 260 y siguientes del “Programmer’s Reference Guide”.
Modelo de “RAM bajo ROM”:
Al modelo de memoria del C64 se le llama de “RAM bajo ROM”. Esto no sólo significa que haya bloques de RAM que están ocultos detrás de otros bloques de ROM y que, desactivando la ROM, se pueda usar la RAM.
Es que, además, y de forma sorprendente, se pueden usar los bloques de ROM y RAM a la vez. Veamos cómo:

Supongamos que tenemos los bloques de ROM activos (bits 0 y 1 de R6510 con valor 1). En esta situación, si leemos la posición $a000, puesto que la ROM está activa, estamos leyendo la ROM:

Pero si escribimos en la posición $a000, puesto que la ROM está activa, y la ROM es de sólo lectura, no tendría sentido escribir en la ROM. De hecho, es físicamente imposible escribir en la ROM. Por ello, lo que ocurre en realidad es que estamos escribiendo en la RAM que está “por debajo”:

De este modo, es perfectamente posible hacer cosas como:
- Copiar el intérprete de BASIC de ROM a RAM.
- Modificar el intérprete de BASIC ya copiado en RAM.
- Desactivar la ROM de BASIC, activando la RAM.
- Y usar el intérprete modificado en RAM.
Y los mismo con las rutinas del Kernal.
Por ejemplo, podemos modificar el “prompt” de BASIC de “READY.” a “C:/>” de este modo: (versión en ensamblador)
- Copiamos el intérprete de BASIC de ROM a RAM:

- Cambiamos el “prompt” en RAM (posiciones 41848 a 41853):

- Desactivamos la ROM de BASIC, dando paso a la RAM:

A partir de ese momento, el “prompt” deja de ser “READY.” y pasa a ser “C:/>”. Esto es así porque se está ejecutando la versión modificada de RAM. Igualmente, podríamos modificar comandos de BASIC o añadir otros nuevos.
Mapa de caracteres en ROM:
Sin actuar sobre el registro R6510, el mapa de caracteres en ROM no está accesible. Es decir, sí está accesible para el VIC para pintar los caracteres en pantalla, pero no está accesible para el 6510 o, lo que es lo mismo, no está accesible para los programas que se ejecutan en él.
Esto es fácil de comprobar si tenemos en cuenta que el primer carácter del juego de caracteres es la @:

Si el mapa de caracteres en ROM fuera direccionable, al leer la primera posición (la $d000 = 53.248) deberíamos leer el valor %00111100 = 60, ya que la fila superior de la @ tiene activados los bits 2 – 5. Sin embargo, leemos 0:

Esto es así porque, en realidad, tras la dirección $d000 lo que estamos leyendo es el registro SP0X del VIC, es decir, la coordenada X del sprite 0.
De hecho, podemos cambiar el valor de SP0X = $d000, lo que viene a confirmar nuevamente que no estamos direccionando ROM, porque la ROM no se puede modificar:

Total, el mapa de caracteres no está inicialmente disponible para el 6510 (ojo, sí para el VIC). Sin embargo, configurando un 0 en el bit 2 de R6510 podemos dejar de direccionar los chips especiales (VIC, SID y CIAs) y pasar a direccionar la ROM con el mapa de caracteres.
Esto puede ser útil, por ejemplo, para copiar ese mapa de caracteres de ROM a RAM, y luego adaptar los caracteres que sean de interés, a modo de juego de caracteres personalizados.
Sin embargo, este activar el mapa de caracteres no es inocuo, porque va acompañado de desactivar la entrada / salida. Por tanto, si se hace, hay que hacerlo con cuidado. Previamente habrá que desactivar las posibles interrupciones generadas por la entrada / salida (ver registro CIAICR) porque, caso de no desactivarlas, si se produjera una interrupción de este tipo durante el proceso, ésta no podría ser identificada ni tratada correctamente.
Total, casi merece más la pena diseñar un juego de caracteres personalizados completo, pero tomando como base de partida los caracteres estándar (como hace el editor de caracteres de CBM prg Studio). Te evitas andar copiando los caracteres estándar de ROM a RAM y, sobre todo, te evitas problemas con las interrupciones.
En todo caso, han pasado 30 años y no dejan de sorprenderme las capacidades del C64. Y eso que no hemos hablado de cartuchos de expansión…
Programa de ejemplo: Prog61