CoreMach nació como efecto secundario de BareMach. Al intentar llevar las máquinas de BluMach a una Raspberry Pi sin sistema operativo, aparecieron muchas dependencias entre sus componentes. Separar la emulación de todo lo demás daba tanto trabajo que acabó siendo un proyecto aparte: CoreMach, el motor portable.

Qué quería separar

Una cosa es el ordenador que intento reproducir: su procesador, memoria y dispositivos. Otra es la aplicación desde la que lo utilizo: las ventanas, la selección de archivos o la forma de mostrar la imagen y recibir las teclas.

La idea de CoreMach es tener el motor por un lado y poder usarlo desde distintos sitios: una versión portable de BluMach en el escritorio, con su catálogo y su configuración, y BareMach en la Raspberry Pi. Cada uno se encarga de mostrar la imagen, leer el teclado y acceder a los discos.

Que sea «portable» no quiere decir que funcione en cualquier sitio solo con compilarlo: cada destino hay que integrarlo y probarlo.

Las máquinas de prueba

Para probarlo de verdad necesitaba una máquina completa, y la primera fue el PCS 86, la misma que acabó arrancando en la Pi.

Más adelante también llegó el trabajo del PCS 286 en CoreMach. El 27 de septiembre, con su BIOS 1.42, arrancó DOS 3.30a desde disquete, y un archivo que copié desde DOS seguía ahí al cerrar y volver a abrir la imagen del disco. Pero el Customer Test todavía fallaba y el ATA estaba pendiente. Y es lento: en mis pruebas, el 286 de CoreMach gasta unas siete veces más CPU que el de BluMach y no llega a velocidad real ni en un x86 actual, así que en la Raspberry Pi, de momento, ni pensarlo. De «PCS 286 terminado», nada todavía.

Por qué lo rescato ahora

Rescato hoy esta historia porque, al final, este motor va a tener otra utilidad. BluMach arrastra una limitación heredada: es un emulador de x86 y, por la forma acoplada en la que está desarrollado, no admite otras plataformas. Eso no quiere decir que esté mal; resuelve muy bien el problema para el que se pensó.

El problema es que durante mucho tiempo el x86 se ha apropiado del término «ordenador personal» (Personal Computer), cuando en los setenta, ochenta y noventa hubo muchas máquinas que no eran x86. Y hoy el dominio del x86 se puede acabar.

BluMach y CoreMach, pieza a pieza

Con el PCS 86 se ve la diferencia. Los dos esquemas tienen la misma forma y las mismas piezas; lo que cambia es cómo se conectan. En BluMach, la interfaz y todos los dispositivos tiran del mismo estado global, y tocar una pieza obliga a arrastrar las demás:

BluMach: la interfaz Qt, la configuración y todos los dispositivos del PCS 86 dependen del mismo estado global; los dispositivos se registran en tablas globales de puertos, memoria e interrupcionesINTERFAZ (Qt, dentro de la misma aplicación)Ventanas y menús (Qt)ConfiguraciónVídeo y tecladoEstado global compartidocpu_state · io_sethandler · mem_mapping · picint() · timersgfxcard · fdc_current · hdc_current · keyboard_send · pcs86_activeTiempo: ligado a los contadores de la CPUMáquina: m_xt_olivetti_pcs86.cse registra en tablas globales y lee las opciones de la interfazCPU 808x / V30cpu_statePIC 8259picint()DMA 8237dma_channel_*INTRTablas globales de puertos y memoriaRAM + EMSmem_mappingROM (BIOS)bios_loadPIT 8253pit_devs[0]Placa/tecladokeyboard_sendSerie 16450device_addXTA (disco)hdc_currentFDC 765fdc_currentParalelodevice_addVídeo PVGA1Agfxcard[0]RTC MM58167device_addio_sethandler, mem_mapping, picint(): tablas y funciones globales.gfxcard, fdc_current, hdc_current: opciones que fija la interfaz.La BIOS la carga la propia máquina desde su ruta en roms/.
El PCS 86 en BluMach: interfaz, máquina y dispositivos comparten el mismo estado global.

En CoreMach, cada capa habla solo con la de al lado. La máquina es una composición de componentes, cada uno con su propio estado, y la interfaz se puede cambiar sin tocar el motor:

CoreMach: interfaces intercambiables sobre la API del motor y el runtime; dentro del PCS 86, la CPU y el DMA acceden al bus, del que cuelgan la memoria y los dispositivos, y las interrupciones llegan a la CPU a través del PICINTERFAZ (intercambiable)Qt (escritorio)Headless (pruebas)BareMach (Raspberry Pi)API del motorimagen · teclado · mediosRuntime: una línea de tiempo comúnrelojes: V30 10 MHz · PIT 1,193 MHz · RTC · XTASistema: Olivetti PCS 86monta la máquina: mapea el bus y conecta IRQ, DMA y relojesCPU NEC V3010 MHzPIC 8259puertos 20hDMA 8237canales 2 y 3INTRBus: memoria y puertos de E/SRAM + EMS640KROM (BIOS)2 × 32KPIT 8253IRQ 0Placa/tecladoIRQ 1Serie 16450IRQ 4XTA (disco)IRQ 5 · DMA 3FDC 765IRQ 6 · DMA 2ParaleloIRQ 7Vídeo PVGA1AframebufferRTC MM58167reloj propioIRQ n: el dispositivo avisa al PIC, y el PIC interrumpe a la CPU.DMA n: canal del 8237 que copia a la RAM sin pasar por la CPU.BIOS y discos no están en el motor: los entrega la interfaz.
El PCS 86 en CoreMach: la interfaz solo ve la API; dentro, los componentes se conectan por el bus, las interrupciones y el DMA.

En resumen:

AspectoBluMachCoreMach
OrganizaciónHeredada de PCem: CPU, dispositivos, configuración e interfaz comparten código.Por capas: componentes, máquinas completas, motor e interfaz.
EstadoMucho estado global: la CPU actual, el mapa de puertos, los temporizadores…Cada máquina tiene su propia CPU, sus dispositivos, sus buses y su tiempo. Así es fácil aislarla y probarla.
TiempoLigado a los contadores de la CPU heredados de PCem.Una línea de tiempo común; la CPU y cada dispositivo van a su propia frecuencia, guardada como fracción exacta (el PIT va a 14,31818 / 12 MHz).
ComponentesSe enganchan con las funciones y tablas globales de PCem.Cada componente declara lo que necesita (memoria, puertos, interrupciones, reloj) y la máquina se lo conecta.
Interfaz y plataformaUna sola aplicación, con todas las dependencias de 86Box: Qt, sonido, red…Motor C11 que puede compilarse sin Qt ni ventanas. Qt solo se usa si se quiere ventana.
CoberturaCasi todo el hardware de 86Box, más lo añadido en BluMach.Mucho menos: PCS 86, M15 y PCS 286, todavía en validación. El M10 está en camino.
CosteLa emulación corre en un hilo; el sonido y la red usan otros. Su V30 también simula el bus ciclo a ciclo.Todo el motor corre en un solo hilo. Con la V30 y el 8088 gasta lo mismo o menos que BluMach; con el 286, unas siete veces más.

Lo que he aprendido

Al separar el motor tienes que decidir cosas que antes estaban repartidas por todo el programa: quién hace avanzar el tiempo emulado, cómo hablan los dispositivos entre sí y qué pasa al reiniciar una máquina o cambiar un disco.

Probar cada componente por separado ayuda a encontrar fallos, pero que cada pieza funcione no quiere decir que funcione la máquina entera. Lo bueno es que ahora puedo probar la máquina completa fácilmente, sin arrastrar media aplicación.

CoreMach sigue en desarrollo. Mi idea era desecharlo, porque lo monté para la Raspberry Pi, pero ahora veo que va a ser muy útil para montar máquinas con arquitectura no x86, de Olivetti y de otras marcas. Solo en Olivetti hay unas cuantas con bastante documentación: el M10, un portátil con un 80C85; el M20, con un Zilog Z8000; o los Prodest PC128 y PC128S, que pese al nombre no tienen nada que ver: el primero es un Thomson MO6 con un 6809, y el segundo, un BBC Master Compact con un derivado del 6502. Da una idea de lo que se puede crear.

Lo próximo: el M10, y BluMach con dos motores

La siguiente máquina será el Olivetti M10, un portátil con un 80C85. Todavía no está terminado y tendrá su propia noticia.

Con el M10 llega además un cambio importante: BluMach empezará a usar CoreMach. A partir de ahí, BluMach tendrá dos motores de emulación: el heredado de 86Box para los PC x86 y CoreMach para las máquinas de otras arquitecturas. Ese es, al final, el sentido de haber desacoplado el motor.

El código está en GitHub: CoreMach y BluMach. BareMach todavía es privado; lo enlazaré cuando lo publique.

Fuentes y licencias

CoreMach no parte de cero. Este es el origen de cada parte, tal como lo anota el propio repositorio en provenance/components.json:

  • Base: CoreMach se extrajo de BluMach, que es una versión modificada de 86Box, que a su vez viene de PCem. Todo se distribuye bajo GPL-2.0 o posterior, y cada archivo conserva los avisos de copyright y los autores originales.
  • 8088 y V30: reescritura del intérprete heredado de 86Box, que a su vez es en gran parte un port de XTCE, el emulador de Andrew Jenner (reenigne), con trabajo de Miran Grča.
  • 80286: escrito en su mayor parte de nuevo a partir de la documentación de Intel (referencias 210760-002 y 210498-005, y el manual de programación del 80286/80287 de 1987). El arranque y el reinicio derivan del código de BluMach. Para comprobarlo se usan las pruebas de SingleStepTests/80286, de Daniel Balsom (licencia MIT).
  • Periféricos: el DMA 8237, el PIC 8259, el PIT 8253 y el vídeo PVGA1A derivan del código de BluMach y 86Box, contrastados con las hojas de datos de Intel y con el manual técnico del IBM PC AT. El controlador XTA deriva de un archivo de BluMach con licencia BSD-3-Clause (Fred N. van Kempen).
  • 8085 (para el M10, en desarrollo): sale del modo 8080 de la propia V30 de CoreMach, completado con el Microprocessor Data Book de OKI (1986).
  • IA: buena parte de este trabajo de reescritura lo hice con ayuda de IA.
  • Esquemas y medidas: los esquemas los he hecho a partir del código de los dos repositorios. Las comparaciones de consumo las medí ejecutando el mismo bucle sintético en los dos emuladores, en un x86 actual: dan una idea, no son un banco de pruebas completo.

No se distribuye ninguna BIOS, ROM ni imagen de disco. Olivetti y los nombres de sus máquinas son marcas de sus propietarios; este proyecto no está afiliado a Olivetti ni a 86Box.