So now we have a running CP/M system, great. Except that it has no content, and no means to get any new content onto the virtual disk. That makes it pretty much useless.
Intel HEX
We need one more step in the emulator to resolve this final bootstrapping issue. And the simplest approach I have found is as follows:
- create a little CP/M tool, called “hexsave”, which takes a filename as argument
- when run, it waits for Intel HEX data sent to it over the serial console
- all the incoming data is checked and saved in memory
- when the closing Intel HEX command arrives, save all data to a new file
If we somehow had this utility on the disk, we could use it to bring in other files, including more advanced file-transfer tools (such as the popular XMODEM utility from those days). In other words, once we can transfer new files into this virtual CP/M system, everything else becomes possible.
The HEXSAVE utility, written in Z80 assembler, can be found here: source and binary.
Emulator support
To avoid having to understand CP/M disk formats, we use a little trick: instead
of actually storing the HEXSAVE.COM
file on the virtual disk, the emulator
will just leave it in memory. Then we can manually save it to file, by typing
this command, right after CP/M starts up:
save 2 hexsave.com
The steps for this are the same as for the system track. A copy of the binary code is saved as data inside the emulator, and then copied to RAM, just before starting the emulation:
xxd -i <hexsave.com >../common-z80/hexsave.h
And in the emulator, we add:
const uint8_t ram [] = {
#include "hexsave.h"
};
… and this one line, just before the emulation loop:
memcpy(mapMem(&context, 0x0100), ram, sizeof ram);
Now we have a way to get a copy of HEXSAVE.COM to disk, and from there all
other file transfers become possible. One of the first tools to get might be a
version of XMODEM.COM
or equivalent, and perhaps LS.COM
for better
(sorted!) directory listings. Also of course, all the standard CP/M utilities,
e.g. DUMP.COM
, PIP.COM
, STAT.COM
, etc.
Summary
This concludes this series on getting CP/M working on an F407 µC.
But this is of course only the beginning, as far as retrocomputing goes. There are masses of CP/M software packages floating around on the internet to explore, e.g. programming languages, word processors, spreadsheets, business applications, games, and more.
Let me just close off with summaries of the entire process described in these articles.
First the steps I went through to get everything installed and working:
- port the original
z80emu
Z80 emulator to F407 - verify proper operation with the
ZEXALL
instruction exerciser - implement a virtual disk in flash, using wear leveling
- write a simple CP/M BIOS in Z80 assembly code
- implement the matching system calls in the emulator
- write a tiny boot loader, for storage in the first disk block
- write a small
HEXSAVE
utility in Z80 assembly code - process all the Z80 source code with a cross-assembler
- convert the resulting binaries to C header files using
xxd -i
- embed the boot loader, ccp+bdos, bios, and hexsave in the emulator
- add logic to initialise flash when starting for the first time
- add logic to place the hexsave binary in RAM before starting emulation
The actual bootstrap process in the virtual Z80 environment proceeds as follows:
- the emulator prepares flash memory, if this is the very first time
- the emulator loads disk block zero to RAM, starting at 0x0000
- the emulator copies
HEXSAVE
to RAM, for use once CP/M is running - start the emulation, i.e. execute code, starting with PC = 0x0000
- the system boot loader starts running inside the Z80 emulation
- it prints out the “
[JeeLabs Retro Z80]
” greeting - it loads 48 sectors, from track 0, sector 1 until track 1, sector 22
- these sectors are loaded to RAM, addresses 0xE800 .. 0xFFFF
- jump to the BIOS cold start location, which is at 0xFE00
- the BIOS prints out the “
64K CP/M Version 2.2
” message - after some setup, the BIOS passes control to the CP/M core
- the CP/M’s CCP (command processor) prints out the “
>A
” prompt - CP/M is now waiting for a command, to be entered on the serial console
That’s a lot of steps, but it should be obvious what they all do and why they are needed.
Now if you’ll excuse me, I need to go do some CP/M’ing in these cold winter days …