CoreMach was born as a side effect of BareMach. When I tried to take BluMach’s machines to a Raspberry Pi with no operating system, lots of dependencies between its components came to light. Separating the emulation from everything else was so much work that it ended up as a project of its own: CoreMach, the portable engine.

What I wanted to separate

One thing is the computer I’m trying to reproduce: its processor, memory and devices. Another is the application I use it from: the windows, choosing files, or how the image is shown and the keys are received.

The idea behind CoreMach is to have the engine on one side and be able to use it from different places: a portable version of BluMach on the desktop, with its catalogue and configuration, and BareMach on the Raspberry Pi. Each one takes care of showing the image, reading the keyboard and accessing the disks.

Being “portable” doesn’t mean it works anywhere just by compiling it: each target has to be integrated and tested.

The test machines

To test it properly I needed a complete machine, and the first one was the PCS 86, the same one that ended up booting on the Pi.

Later came the work on the PCS 286 in CoreMach too. On 27 September, with its BIOS 1.42, it booted DOS 3.30a from floppy, and a file I copied from DOS was still there after closing and reopening the disk image. But the Customer Test still failed and ATA was pending. And it’s slow: in my tests, CoreMach’s 286 uses about seven times as much CPU time as BluMach’s and doesn’t reach real speed even on a current x86, so on the Raspberry Pi, for now, forget it. “PCS 286 finished” it is not, not yet.

Why I’m bringing it back now

I’m bringing this story back today because, in the end, this engine is going to have another use. BluMach carries an inherited limitation: it’s an x86 emulator and, because of the coupled way it’s built, it doesn’t support other platforms. That doesn’t mean it’s bad; it solves the problem it was designed for very well.

The problem is that for a long time x86 has claimed the term “personal computer” for itself, when in the seventies, eighties and nineties there were many machines that weren’t x86. And today x86’s dominance may be coming to an end.

BluMach and CoreMach, piece by piece

The PCS 86 shows the difference. Both diagrams have the same shape and the same pieces; what changes is how they connect. In BluMach, the interface and all the devices draw on the same global state, and touching one piece means dragging the others along:

BluMach: the Qt interface, the configuration and all the PCS 86 devices depend on the same global state; the devices register in global tables of ports, memory and interruptsINTERFACE (Qt, inside the same application)Windows and menus (Qt)ConfigurationVideo and keyboardShared global statecpu_state · io_sethandler · mem_mapping · picint() · timersgfxcard · fdc_current · hdc_current · keyboard_send · pcs86_activeTime: tied to the CPU countersMachine: m_xt_olivetti_pcs86.cregisters in global tables and reads the interface optionsCPU 808x / V30cpu_statePIC 8259picint()DMA 8237dma_channel_*INTRGlobal port and memory tablesRAM + EMSmem_mappingROM (BIOS)bios_loadPIT 8253pit_devs[0]Board/keyb.keyboard_sendSerial 16450device_addXTA (disk)hdc_currentFDC 765fdc_currentParalleldevice_addVideo PVGA1Agfxcard[0]RTC MM58167device_addio_sethandler, mem_mapping, picint(): global tables and functions.gfxcard, fdc_current, hdc_current: options set by the interface.The machine loads its own BIOS from its path in roms/.
The PCS 86 in BluMach: interface, machine and devices share the same global state.

In CoreMach, each layer only talks to the one next to it. The machine is a composition of components, each with its own state, and the interface can be swapped without touching the engine:

CoreMach: interchangeable interfaces on top of the engine API and the runtime; inside the PCS 86, the CPU and the DMA access the bus, which the memory and devices hang off, and interrupts reach the CPU through the PICINTERFACE (interchangeable)Qt (desktop)Headless (tests)BareMach (Raspberry Pi)Engine APIimage · keyboard · mediaRuntime: one shared timelineclocks: V30 10 MHz · PIT 1.193 MHz · RTC · XTASystem: Olivetti PCS 86assembles the machine: maps the bus and wires up IRQ, DMA and clocksCPU NEC V3010 MHzPIC 8259ports 20hDMA 8237channels 2 and 3INTRBus: memory and I/O portsRAM + EMS640KROM (BIOS)2 × 32KPIT 8253IRQ 0Board/keyb.IRQ 1Serial 16450IRQ 4XTA (disk)IRQ 5 · DMA 3FDC 765IRQ 6 · DMA 2ParallelIRQ 7Video PVGA1AframebufferRTC MM58167own clockIRQ n: the device signals the PIC, and the PIC interrupts the CPU.DMA n: an 8237 channel that copies to RAM without going through the CPU.BIOS and disks aren't in the engine: the interface supplies them.
The PCS 86 in CoreMach: the interface only sees the API; inside, the components connect through the bus, interrupts and DMA.

In short:

AspectBluMachCoreMach
OrganisationInherited from PCem: CPU, devices, configuration and interface share code.In layers: components, complete machines, engine and interface.
StateLots of global state: the current CPU, the port map, the timers…Each machine has its own CPU, devices, buses and time. That makes it easy to isolate and test.
TimeTied to the CPU counters inherited from PCem.One shared timeline; the CPU and each device run at their own frequency, stored as an exact fraction (the PIT runs at 14.31818 / 12 MHz).
ComponentsThey hook in through PCem's global functions and tables.Each component declares what it needs (memory, ports, interrupts, clock) and the machine wires it up.
Interface and platformA single application, with all of 86Box's dependencies: Qt, sound, networking…A C11 engine that can be built without Qt or windows. Qt is only used if you want a window.
CoverageAlmost all of 86Box's hardware, plus what BluMach added.Much less: PCS 86, M15 and PCS 286, still being validated. The M10 is on its way.
CostEmulation runs on one thread; sound and networking use others. Its V30 also simulates the bus cycle by cycle.The whole engine runs on a single thread. With the V30 and the 8088 it uses the same or less than BluMach; with the 286, about seven times as much.

What I’ve learned

When you separate the engine, you have to decide things that used to be scattered all over the program: who advances emulated time, how devices talk to each other, and what happens when a machine is reset or a disk is changed.

Testing each component separately helps find bugs, but every piece working doesn’t mean the whole machine works. The good thing is that I can now test the complete machine easily, without dragging half the application along.

CoreMach is still in development. My plan was to throw it away, because I built it for the Raspberry Pi, but now I can see it’s going to be very useful for building machines with non-x86 architectures, from Olivetti and other brands. Olivetti alone has quite a few with good documentation: the M10, a portable with an 80C85; the M20, with a Zilog Z8000; or the Prodest PC128 and PC128S, which despite the name have nothing to do with each other: the first is a Thomson MO6 with a 6809, and the second a BBC Master Compact with a 6502 derivative. It gives an idea of what can be built.

Next: the M10, and BluMach with two engines

The next machine will be the Olivetti M10, a portable with an 80C85. It isn’t finished yet and will get its own post.

The M10 also brings an important change: BluMach will start using CoreMach. From then on, BluMach will have two emulation engines: the one inherited from 86Box for x86 PCs, and CoreMach for machines with other architectures. That, in the end, is the point of having decoupled the engine.

The code is on GitHub: CoreMach and BluMach. BareMach is still private; I’ll link it when I publish it.

Sources and licences

CoreMach doesn’t start from scratch. This is the origin of each part, as the repository itself records it in provenance/components.json:

  • Base: CoreMach was extracted from BluMach, which is a modified version of 86Box, which in turn comes from PCem. Everything is distributed under GPL-2.0 or later, and each file keeps its copyright notices and original authors.
  • 8088 and V30: a rewrite of the interpreter inherited from 86Box, which in turn is largely a port of XTCE, the emulator by Andrew Jenner (reenigne), with work by Miran Grča.
  • 80286: mostly written anew from Intel’s documentation (references 210760-002 and 210498-005, and the 1987 80286/80287 programmer’s reference manual). Boot and reset derive from BluMach’s code. It’s checked against the SingleStepTests/80286 tests by Daniel Balsom (MIT licence).
  • Peripherals: the 8237 DMA, 8259 PIC, 8253 PIT and PVGA1A video derive from BluMach and 86Box code, checked against Intel’s datasheets and the IBM PC AT technical reference manual. The XTA controller derives from a BluMach file under the BSD-3-Clause licence (Fred N. van Kempen).
  • 8085 (for the M10, in development): it comes from the 8080 mode of CoreMach’s own V30, completed with OKI’s Microprocessor Data Book (1986).
  • AI: much of this rewriting work I did with the help of AI.
  • Diagrams and measurements: I made the diagrams from the code in both repositories. I measured the CPU usage comparisons by running the same synthetic loop in both emulators, on a current x86: they give an idea, they aren’t a complete benchmark.

No BIOS, ROM or disk image is distributed. Olivetti and the names of its machines are trademarks of their owners; this project isn’t affiliated with Olivetti or 86Box.