CP/M from the 1970’s was an operating system for 8080 and Z80 8-bit micrcomputers. It was very popular among hobbyists, because it came at the right time and offered a way to manage data file storage on the upcoming 8” and 5.25” floppy disks. Later still came the 3.5” floppy, and 8086 16-bit micrcomputers with MS-DOS, Windows 3.11, etc.
In short: it all started with CP/M (the original version is also often called CP/M-80).
Here is an Altair 8800 on top of an 8” floppy drive, as (very low-end) CP/M system:
Although there are many retrocomputing projects “out there” already, in both hardware and software variants, I’d like to recreate this world in my own way: emulated on a more modern microcontroller, such as the STM32F407. I find this mix particularly interesting because it lets me reconstruct a computing experience from the past with all its clunkiness and deficencies, while also exploring and expanding drivers and code for the JeeH library.
In addition, the combination of an emulated world (a raw Z80 setup) with the modern world (C++11 with templates) provides a unique opportunity to dive into a lot of different technologies. My passion has always been to understand and to explain how things work on the inside - to counteract a prevalent trend of collecting and assembling technological pieces without understanding how they are made. And to peel away the obscuring layers.
There’s a lot of self-inflicted complexity out there, and IMO much of it is redundant fluff.
But I digress. Let’s just get on with the task ahead: implementing CP/M - with a little bit of hardware, in a totally virtualised context, and with lots of exploration and fun up ahead …
This is no place to explain how CP/M works, there are numerous sites, articles, and books about this subject. Let me just list the main pieces needed to get going:
- an 8080 or Z80 microcomputer: check, we have a Z80 with plenty of emulated RAM
- (virtual) disk storage: I’ll explore internal flash, SPI flash, and SD cards for this
- floppy disks: superseded by the above options, which are much larger and faster
- a console: this can be either a UART or the serial USB device on the F407
- keyboard, video screen, printer, modem: I’ll pass on that for now (maybe later?)
- a front panel (“blinkenlights”): could be added as gimmick, not needed for actual use
I’m not ruling out connecting a front panel, video or LCD screen, or floppy drive, but it’s definitely not a priority. The first aim is to create a stable environment for exploring the (software) world of CP/M, with its wide range of tools, programming languages, and applications from that era.
There have been a range of extensions and replacements for CP/M over the years. It would be interesting to try out not just the “classical” CP/M 2.2 (and add-ons such as ZCPR), but also CP/M 3.0 (“Plus”) with banked memory, the multi-tasking variant called MP/M, and a modern Unix-like alternative called FUZIX. There are definitely enough resources on the Black F407 board to explore each of these alternatives.
But first, let’s aim for “plain” CP/M 2.2 - a Z80 with 64K RAM, a serial console, and some sort of emulated floppy disk. Oh, and a bootstrap mechanism of some sort.
The challenge that comes up with every new computer system, is how to get it going. Even if there is a lot of software available for similar systems, it won’t be of any use until this system is up and running. After that, it’s common for CP/M to be entirely self-hosted, i.e. to be able to build an updated version of its own system on its own setup.
CP/M is written in assembly language (well, actually PL/M, but that’s another story). And since there are a range of “assemblers” and “linkers” available (using either 8080 or Z80 conventions) and running on CP/M, this can be used to make changes to CP/M itself.
Operating systems are tricky beasts, even simple ones such as CP/M. They need to interface to the exact hardware environment on which they run, which is almost always going to differ from one system to the next. That’s the main task of an operating system in fact: to present an abstraction for the hardware so that the rest of the software running in the OS can work with a standardised set of conventions: the OS “system calls”.
With CP/M, the Hardware Abstraction Layer is called the BIOS. We will need to create a custom BIOS. Fortunately, there are many examples which can be used as starting point, and – even more fortunately – since the Z80 runs in a virtual environment, we can also define the “hardware” it sees in the emulator in any way we like, and entirely in software!
Apart from the BIOS (which is coded in Z80 assembly language) and an interface to the virtual console in the emulator (which is coded in C++), we’ll also need disk storage.
Lastly, we need a mechanism to boot the (virtual) Z80 from its (virtual) disk drive, and a way to transfer CP/M itself as well as actual CP/M programs onto that (virtual) disk.
That’s a lot of steps. All of them must work properly before we can do anything with CP/M.
Up next …
In this 6-part set of articles, I’m going to go over each of these aspects in greater detail. Not only to show that there really is no magic, but also to highlight how extremely logical (and simple) the startup process of an operating system is. Every operating system goes through very similar motions when powering up, it’s merely more or less elaborate.
The fact that this old OS (CP/M) runs on an ancient 8-bit CPU (Z80) is almost irrelevant.