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
¡Muy bien escrito!
Una pregunta técnica: en el párrafo sobre el mapa de caracteres en la ROM, escribiste que, para evitar deshabilitar las interrupciones, es mejor NO copiar los caracteres de la ROM a la RAM y luego modificarlos.
Tu consejo es dibujar los 256 caracteres directamente usando el juego de caracteres estándar C64 como referencia. Básicamente, ¿a qué te refieres exactamente?
¿Puedes dar un ejemplo de codigo en BASIC o Ensamblador?
En BÁSIC se podría reservar un área de memoria en RAM de 2K para ser llenado con los DATOS relacionados con los datos de definición de caracteres (8 bytes por cada carácter) y luego, obviamente, la dirección en el Nibble inferior tendrá que cambiarse (bits del 1 al 3) del Registro 53272 para apuntar al nuevo mapa de caracteres en la RAM.
Un saludo, Attilio.
Fundador y Administrador de «RetroProgramming Italia – RP Italia»
Me gustaMe gusta
Hola, Attilio.
Muchas gracia por tu comentario.
Me refiero a lo siguiente:
Cuando quieres definir un juego de caracteres personalizados tienes que suministrar al VIC 8 filas x 1 byte x 256 caracteres, es decir, 2 KBytes de datos. Si además quieres definir mayúsculas y minúsculas, entonces tienes que suministrar 4 KBytes de datos.
Ahora hay dos situaciones que se pueden dar:
1) Quieres definir el juego de caracteres completo, o
2) Sólo quieres redefinir parte del juego de caracteres, dejando la otra parte igual que el juego estándar.
En el caso 1 (juego completo) lógicamente no hay más opción que aportar los 2 KBytes de datos. Esto lo puedes hacer diseñando los 256 caracteres con CBM prg Studio, exportando los datos, e importando estos desde ensamblador o BASIC.
En el caso 2 (juego parcial), sin embargo, se me ocurren dos formas de hacerlo:
2.1) Diseñar, exportar e importar sólo los caracteres que quieres personalizar y copiar el resto de ROM a RAM. Para copiar de ROM a RAM tendrías que hacer lo comentado, es decir, desactivar la zona de RAM de chips especiales y activar la zona de ROM con el mapa de caracteres estándar, y luego copiar parte de los caracteres (los no modificados) de ROM a RAM. Esto es lo que hay que hacer con cuidado, porque los chips especiales pueden generar interrupciones.
2.2) Puesto que ya tienes que definir una parte de los caracteres, y puesto que desactivar los chips especiales puede dar problemas con las interrupciones, por el mismo precio puedes diseñar, exportar e importar un juego de caracteres «completo», aunque una parte sea «copia» de los caracteres estándar.
Con 2.2) tienes que diseñar, exportar e importar caracteres igual que con 2.1), pero te evitas desactivar zonas de memoria y posibles problemas con las interrupciones.
Espero haberme explicado mejor ahora…
Un saludo, HVSW.
Me gustaMe gusta
Hola HVSW,
creo que todo está más claro ahora.
El caso es que excluí el uso de la CBM Prg Studio Tool e hice todo desde el Programa BÁSIC: Copia, parcial o completa, del juego de caracteres de la ROM a la RAM y posterior modificación (siempre desde Programa) de los caracteres a personalizar.
¡Todo hecho a través de líneas de código (BASIC o Ensamblador)!
Tal vez podría publicar un ejemplo práctico de cómo diseñar los caracteres con CBM Prg Studio, exportar datos de caracteres y finalmente importarlos al programa BASIC o Ensamblador.
Muchas gracias.
Attilio Capuozzo
Fundador y Administrator «RetroProgramming Italia – RP Italia»
Me gustaMe gusta
Hola, Attilio.
En esta entrada:
https://programacion-retro-c64.blog/2019/02/09/juegos-de-caracteres-personalizados/
tienes un ejemplo de cómo diseñar y exportar caracteres personalizados con CBM prg Studio, así como importarlos y activarlos en esamblador.
Si en vez de usar ensamblador usas BASIC las diferencias son pequeñas. A la hora de exportar desde CBM prg Studio tendrás que usar la opción Character Set > Export > To listing > BASIC data. No lo he probado, pero supongo que así te generará las típicas líneas DATA de BASIC con la definición de los caracteres.
Un saludo, HVSW.
Me gustaMe gusta
Muchas gracias HVSW.
Siempre eres preciso y completo en tus explicaciones.
Un saludo.
Attilio Capuozzo
Fundador y Administrador Grupo de Facebook «RetroProgramming Italia – RP Italia»
Me gustaMe gusta