CP/M Emulator

January 2020

In 1981 Xerox released a computer called the Xerox 820, based on the Z80 CPU and the CP/M operating system. It failed spectacularly, beaten a few months later by the IBM PC, and by 1983 you could buy surplus motherboards for $50. My dad and his friends got them in bulk, built a new computer around it, dubbed the "Xerloot", and for a while we had a CP/M machine at home. It didn't last long; we got an IBM PC soon after and the Xerloot never booted again. Still, I got to briefly experience this classic operating system, with its weird user command, and appreciate the brilliantly-designed WordStar word processor.

Thirty years later, after having written a few Z80-oriented libraries in TypeScript, including a full emulator for the TRS-80 machine, I wanted to find a way to run CP/M programs. The question was, which machine to emulate? CP/M ran on hundreds of machines (including our own Alice 3!), but I didn't want to go down the road of researching and emulating any of them, especially the relatively unknown and maligned Xerox 820.

It occurred to me that CP/M is simple enough that I could make my own “machine” that proxies to my local Mac for file system calls and other I/O. I could then run CP/M programs right from the Mac terminal.

CP/M was designed to be easy to port. There's a low-level interface to the hardware, called the CBIOS (CP/M Basic Input/Output System), which handles keyboard input, screen output, and various disk access at the track and sector level. I didn't want to lay out an actual CP/M disk, though, I wanted to access files in macOS directly, so I had to intercept calls at a higher level. The BDOS (Basic Disk Operating System) also has a handful of calls, including console I/O, and its disk operations are at the file and directory level. My code intercepts these (by checking the program counter), does the right thing (reads the file, updates memory and registers), and returns to the caller immediately.

Here's a demo of compiling the emulator, configuring Turbo Pascal, running it, writing a quick “Hello world!” program, compiling that program, writing a .COM (executable command) program back to the macOS directory, then running that directly.

The source code is available on GitHub.

~ See all projects ~