Emulador de Commodore 16 con M5Stack Cardputer

En este artículo utilizaremos un Cardputer de M5Stack para emular un Commodore 16.

Sobre el Commodore 16

El Commodore 16 fue el intento del fabricante de competir con los ordenadores low-cost que empezaron a surgir en la primera mitad de los años 80. Prácticamente compartiendo estética con el Commodore 64, logró reducir precio a base de una simplificación de su diseño y una mayor fragilidad.

Así, a nivel hardware, sustituiría los chips de vídeo y sonido, VIC-II y SID, por el TED. Además, prescindía de la CIA, cuyas funciones eran mayoritariamente asumidas por TED. También cambió de procesador, MOS 7501 a 1,76 MHz (posteriormente MOS 8501) y se redujo la RAM a 16 KB.

Emulador

Utilizaremos como base el emulador para Commodore 64 sobre esta misma plataforma que ya utilizamos en otro artículo.

Cambio de arquitectura

En el emulador de C16 hubo que tener en cuenta la nueva arquitectura, por lo que se prescindió del código fuente referido a VIC, CIA y SID:

  • CIA (CIA.h/.cpp) reemplazado por timers/IRQ TED.
  • VIC (VIC.h/.cpp) reemplazado por TED.h/.cpp.
  • CPU C64 (CPUC64.h/.cpp) reemplazado por CPUC16.h/.cpp.
  • Orquestador C64 (C64.h/.cpp) reemplazado por C16.h/.cpp.
  • SID/reSID retirado en la iteración actual (audio C16 pendiente).

Habría que destacar las funciones siguientes:

  • C16::run(const std::string& path): orquesta memoria, CPU, TED, teclado y tareas FreeRTOS.
  • tedRefresh(void*): actualiza estado de TED.
  • Reemplazo de la capa de vídeo/IRQ del C64 por implementación de TED para C16.

Procesador y memoria

El archivo CPUC16.cpp incluye la gestión de memoria del C16, destacándose estas funciones:

  • uint8_t CPUC16::getMem(uint16_t addr): lectura con prioridad TED y ROM banking del C16.
  • void CPUC16::setMem(uint16_t addr, uint8_t val): escritura de RAM, registros TED y control $FF3E/$FF3F.
  • void CPUC16::tickTimers(uint8_t cycles): temporizadores del TED (sustituyen a los temporizadores de la CIA).
  • void IRAM_ATTR CPUC16::run(): bucle CPU por línea de barrido, IRQ raster/timer y NMI RESTORE.
  • void CPUC16::initMemAndRegs(): estado de reset C16, iflag=true, vector reset KERNAL.
  • void CPUC16::init(...): inyección de dependencias y estado inicial.
  • void CPUC16::setPC(uint16_t), cmd6502illegal(), getters getA/getX/getY/getSP/getSR/getPC.

Respecto al C64, se implementan estos cambios de comportamiento:

  • RAM física de 16 KB con protección para accesos altos.
  • ROM BASIC 3.5 en $8000-$BFFF y KERNAL en $C000-$FFFF.
  • Registros TED en $FF00-$FF3F siempre visibles.

Instalación

Vamos a descargar el código fuente del emulador M5Cardputer C16 Emulator desde GitHub en nuestro directorio de trabajo:

$ git clone https://github.com/RafaGS/M5Cardputer-C16-Emulator.git

Si no hemos instalado aún PlatformIO, procederemos a ello:

$ paru -Ss platformio platformio-udev-rules

Entramos al directorio donde acabamos de descargar los fuentes del emulador del Commodore 16.

$ cd M5Cardputer-C16-Emulator

Conectamos el Cardputer al PC y procedemos a generar y subir los binarios.

$ pio run --target upload

Emulación

Una vez realizada la subida, automáticamente se inicia el emulador, dándonos opción a ejecutar el intérprete BASIC o algún programa que proporcionemos vía microSD (aún por implementar).

Seleccionamos BASIC y, aunque tengamos que forzar un poco la vista, veremos cómo aparece la pantalla de presentación de este ordenador.

Para paliar el problema de visualización, que además empeora a la necesidad de escalado, se implementa una función de ampliación. Así, la pulsación de [fn][1] amplía el primer cuarto de la pantalla, [fn][2], [fn][3], [fn][4] amplían las demás partes de la pantalla, respectivamente. [fn][0] vuelve a la relación pantalla original.

Por fin podemos introducir códigos en BASIC que nos hagan recordar otros tiempos.

#1984 #Cardputer #Commodore #ESP32 #MOS7360TED #MOS7501