Intro
I got in a nostalgic mood lately, thinking about the long gone days when I made a Z80 slave CPU for my BBC model B computer and ported CP/M 2.2 (and later ZCPR3) to it as well.
RetroComputing seems to be on the rise as well and quite some kits are offered to bring back the computer experience of those long gone days. For the Z80, perhaps the most successful kit is the RC2014 project which can be found on Tindie.
If you don’t want to jump directly into old hardware, there are plenty of software options that bring back the old days on your modern PC. The one I’m currently looking at is RunCPM that runs on a desktop PC (Linux, Windows or macOS) but also on a number of (modern) embedded development boards like the Arduino Due.
In this blog post I’ll into what it takes to get RunCPM
running on a Linux
PC and the Arduino Due.
Linux
To get started we need to clone the RunCPM
repo [at your preferred location
of your hard disk]:
git clone https://github.com/MockbaTheBorg/RunCPM.git -v
results in this output (I cloned the RunCPM
repo in my Documents folder):
[koen@manjaro-3900X Documents]$ git clone https://github.com/MockbaTheBorg/RunCPM.git -v
Cloning into 'RunCPM'...
POST git-upload-pack (165 bytes)
remote: Enumerating objects: 22, done.
remote: Counting objects: 100% (22/22), done.
remote: Compressing objects: 100% (18/18), done.
remote: Total 2650 (delta 8), reused 12 (delta 4), pack-reused 2628
Receiving objects: 100% (2650/2650), 3.85 MiB | 5.16 MiB/s, done.
Resolving deltas: 100% (1832/1832), done.
[koen@manjaro-3900X Documents]$
Next we need to navigate to the folder where the make files are stored:
cd RunCPM/RunCPM
To build RunCPM
for Linux, we need to issue this command:
make posix build
This will build RunCPM
on your PC. If the build fails, this might be
because development tools like the GCC C compiler, linker, ncurses, etc.
are not installed. Installing the missing packages should solve any build
problems you encounter.
After a successful build, you should find a new file in the directory
you’re currently in, named RunCPM
(without any extension and around 93
KByte in size).
We can already try to run RunCPM
by issuing the following command:
./RunCPM
You will be greeted with this screen:
CP/M 2.2 Emulator v4.4 by Marcelo Dantas
Build Aug 21 2020 - 16:21:11
-----------------------------------------
CCP : CCP-DR.60K CCP Address: 0xe400
RunCPM Version 4.4 (CP/M 2.2 60K)
Unable to load CP/M CCP.
CPU halted
We see that RunCPM
indeed starts up but can’t find the necessary code of the
CP/M CCP (in this case it wants the CCP-DR.60K
CCP).
To avoid messing up the RunCPM git repo, I prefer to move the RunCPM
executable
we just build to another location and add the missing pieces there. With the
following 2 commands I create a RunCPM_testArea
directory in the Documents
directory (next to the already existing RunCPM
directory) and copy the
RunCPM
executable to the RunCPM_testArea
directory:
mkdir ~/Documents/RunCPM_testArea
cp RunCPM ~/Documents/RunCPM_testArea/
Now we need to copy the missing CCP-DR.60K
code. Luckily this is part of the git
repo we cloned earlier and we can copy it with this command (assuming you’re still
in the directory where you build RunCPM
):
cp ../CCP/CCP-DR.60K ~/Documents/RunCPM_testArea/
If we would now start RunCPM
we would see that the CCP
is indeed loaded but could
not find any disk drive:
CP/M 2.2 Emulator v4.4 by Marcelo Dantas
Build Aug 21 2020 - 16:21:11
-----------------------------------------
CCP : CCP-DR.60K CCP Address: 0xe400
RunCPM Version 4.4 (CP/M 2.2 60K)
Bdos Error on A : Select
Adding a disk drive to RunCPM
is very simple (and one of its strong points IMHO). We
simply create a new folder with the letter of the disk drive we want to use next to the
RunCPM
executable. However, since RunCPM version 3.7
, user areas are mandatory.
In practise this means that we need to create a sub directory in each “disk drive” directory
as well which we simply name it as 0
:
mkdir ~/Documents/RunCPM_testArea/A
mkdir ~/Documents/RunCPM_testArea/A/0
Now we need to add some files to our disk drive. The git repo already contains a zipped sample A drive. We can unzip and put the unzipped files in the correct place with this command:
unzip ../DISK/A.ZIP -d ~/Documents/RunCPM_testArea/A/0/
Now we navigate to the directory containing the RunCPM
executable:
cd ~/Documents/RunCPM_testArea/
And start up RunCPM
. With the dir
command we can list the files of drive A
to verify if everything works:
CP/M 2.2 Emulator v4.4 by Marcelo Dantas
Build Aug 21 2020 - 16:21:11
-----------------------------------------
CCP : CCP-DR.60K CCP Address: 0xe400
RunCPM Version 4.4 (CP/M 2.2 60K)
A>dir
A: 1STREAD ME : ASM COM : BDOS ASM : BDOS LUA
A: BDOS SUB : BDOSEQU LIB : CAL COM : CCP-ZCP3 BIN
A: CCP ASM : CCP SUB : CCPZ SUB : CCPZ Z80
A: CLEAN SUB : CONSOLE7 COM : CONSOLE7 Z80 : CONSOLE8 COM
A: CONSOLE8 Z80 : DDT COM : DISKDEF LIB : DISPLAY LIB
A: DUMP ASM : DUMP COM : ED COM : EXIT COM
A: EXIT SUB : EXIT Z80 : FORMAT COM : FORMAT SUB
A: FORMAT Z80 : HELLO LUA : INFO COM : INFO SUB
A: INFO Z80 : LOAD COM : LU COM : LUA COM
A: LUA SUB : LUA Z80 : LUAINFO LUA : MAC COM
A: MAKEFCB LIB : MBASIC COM : MLOAD ASM : MLOAD COM
A: MLOAD DOC : MOVCPM COM : OPCODES DOC : PIP COM
A: RSTAT COM : RSTAT SUB : RSTAT Z80 : STAT COM
A: SUBMIT COM : SUBMITD COM : SYSGEN COM : TE COM
A: UNARC COM : UNCR COM : USQ COM : XMODEM COM
A: XSUB COM : Z2HDR LIB : Z3BASE LIB : Z3HDR LIB
A: Z80ASM COM : Z80ASM PDF : Z80CCP ASM : Z80CCP SUB
A: ZCPR2 ASM : ZCPR2 SUB : ZCPR3 ASM : ZCPR3 SUB
A: ZEXALL COM : ZEXDOC COM : ZSID COM : ZTRAN COM
A>
Let’s try out Microsoft Basic
which we can find on the drive as the file
MBASIC.COM
:
A>mbasic
BASIC-85 Rev. 5.29
[CP/M Version]
Copyright 1985-1986 $ by Microsoft
Created: 28-Jul-85
34872 Bytes free
Ok
10 for i=1 to 10
20 print "Hello World"
30 next i
list
10 FOR I=1 TO 10
20 PRINT "Hello World"
30 NEXT I
Ok
run
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Ok
SYSTEM
RunCPM Version 4.4 (CP/M 2.2 60K)
A>
ZCPR3
ZCPR is a replacement for the CP/M CCP which offers quite some new, interesting features:
- shells
- aliases
- I/O redirection
- flow control
- named directories
- search paths
- custom menus
- passwords
- on line help
ZCPR3.3 also includes a full complement of utilities with considerably extended capabilities.
Very convenient, the ZCPR3 CCP is also part of the git repo (as the file CCP-ZCP3.60K
).
However, we do need to recompile the RunCPM
executable to select the ZCPR3 CCP instead of
the default CCP-DR.60K
(which is the CP/M 2.2 CCP
).
For this we need to modify the globals.h
file. When you open globals.h
you will find
these lines:
/* Definition of which CCP to use (must define only one) */
//#define CCP_INTERNAL // If this is defined, an internal CCP will emulated
#define CCP_DR
//#define CCP_CCPZ
//#define CCP_ZCPR2
//#define CCP_ZCPR3
//#define CCP_Z80
To select the ZCPR3 CCP, we make the following modifications:
/* Definition of which CCP to use (must define only one) */
//#define CCP_INTERNAL // If this is defined, an internal CCP will emulated
//#define CCP_DR
//#define CCP_CCPZ
//#define CCP_ZCPR2
#define CCP_ZCPR3
//#define CCP_Z80
Now we recompile the RunCPM
executable again in the same way as we did earlier and copy
the new executable to our test directory. Executing RunCPM
now shows this greeting:
CP/M 2.2 Emulator v4.4 by Marcelo Dantas
Build Aug 21 2020 - 17:16:53
-----------------------------------------
CCP : CCP-ZCP3.60K CCP Address: 0xdc00
RunCPM Version 4.4 (CP/M 2.2 60K)
A0>dir
1STREAD .ME | ASM .COM | BDOS .ASM | BDOS .LUA
BDOS .SUB | BDOSEQU .LIB | CAL .COM | CCP-ZCP3.BIN
CCP .ASM | CCP .SUB | CCPZ .SUB | CCPZ .Z80
CLEAN .SUB | CONSOLE7.COM | CONSOLE7.Z80 | CONSOLE8.COM
CONSOLE8.Z80 | DDT .COM | DISKDEF .LIB | DISPLAY .LIB
DUMP .ASM | DUMP .COM | ED .COM | EXIT .COM
EXIT .SUB | EXIT .Z80 | FORMAT .COM | FORMAT .SUB
FORMAT .Z80 | HELLO .LUA | INFO .COM | INFO .SUB
INFO .Z80 | LOAD .COM | LU .COM | LUA .COM
LUA .SUB | LUA .Z80 | LUAINFO .LUA | MAC .COM
MAKEFCB .LIB | MBASIC .COM | MLOAD .ASM | MLOAD .COM
MLOAD .DOC | MOVCPM .COM | OPCODES .DOC | PIP .COM
RSTAT .COM | RSTAT .SUB | RSTAT .Z80 | STAT .COM
SUBMIT .COM | SUBMITD .COM | SYSGEN .COM | TE .COM
UNARC .COM | UNCR .COM | USQ .COM | XMODEM .COM
XSUB .COM | Z2HDR .LIB | Z3BASE .LIB | Z3HDR .LIB
Z80ASM .COM | Z80ASM .PDF | Z80CCP .ASM | Z80CCP .SUB
ZCPR2 .ASM | ZCPR2 .SUB | ZCPR3 .ASM | ZCPR3 .SUB
ZEXALL .COM | ZEXDOC .COM | ZSID .COM | ZTRAN .COM
A0>
RunCPM on an Arduino Due
Interestingly, RunCPM
has been ported to run on a few embedded development
platforms as well like the Arduino Due I discuss here. This gives you a more
realistic experience on how it was to use a CP/M system back in the days. E.g.
slower terminals, small disks, primitive editing, no copy/paste, etc.
If you start from a virgin Arduino IDE, you might need to install the Arduino
Due Board package first (use the Board manager...
menu option for this).
Compiling RunCPM
for the Arduino Due is rather easy and can be done with
the free Arduino Due IDE. Simply open the RunCPM.ino
file in the Arduino IDE:
We do need to make a small modification in the RunCPM.ino
file. By default,
the esp32
platform is included while we need the Arduino Due
platform.
Simply modify line 13 from
#include "hardware/esp32.h"
to
#include "hardware/due.h"
as shown here as well:
Needed libraries
Since SD cards are used to emulate the disk drives, we need the SdFat
library to
be present in the Arduino IDE.
Compile error…
Now we can compile the code but I encountered this error:
/tmp/arduino_build_320087/sketch/hardware/due.h:4:1: error: 'SdFatEX' does not name a type
SdFatEX SD;
^
This was easily fixed in the hardware/due.h
file as follows. Change this line:
SdFatEX SD;
to
SdFat SD;
And now we get a successful compile which can be uploaded to the Arduino Due.
Preparing the SD card
The next step is connecting the SD Card reader to your Arduino Due. The easiest for me is to refer to the “Arduino Due CP/M Personal Computer” article explaining how to wire the SD Card shield to the Arduino Due
The SD Card needs to be formatted as FAT32 (obviously since we needed to use the
SdFat library). If you already tried out RunCPM
on Linux (like above) then you
already have one or more folders prepared as disk drives. You can simply copy those
over to the FAT32 formatted SD card.
Don’t forget to copy the CCP of your choice (e.g. CCP-DR.60K
, CCP-ZCP3.60K
, …)
at the root of the SD Card as well.
Trying it out.
Now we’re ready to try everything out. Make sure you have installed everything that
is needed on your SD Card. I.e. at the root, your preferred CCP like CCP-ZCP3.60K
and the A
and 0
sub directories filled with the files you want.
Upload the binary created by the Arduino IDE to the Arduino Due.
Start up a terminal emulator, e.g. Minicom
. On Linux, you would use this command
(assuming /dev/ttyACM0
is the USB port that is in use for the serial monitor):
minicom -b 9600 -o -D /dev/ttyACM0
And you get greeted by:
CP/M 2.2 Emulator v4.4 by Marcelo Dantas
Arduino read/write support by Krzysztof Klis
Build Aug 21 2020 - 18:50:20
--------------------------------------------
CCP: CCP-ZCP3.60K CCP Address: 0xdc00
BOARD: ARDUINO DUE
Initializing SD card.
RunCPM Version 4.4 (CP/M 2.2 60K)
A0>dir
1STREAD .ME | ASM .COM | BDOS .ASM | BDOS .LUA
BDOS .SUB | BDOSEQU .LIB | CAL .COM | CCP-ZCP3.BIN
CCP .ASM | CCP .SUB | CCPZ .SUB | CCPZ .Z80
CLEAN .SUB | CONSOLE7.COM | CONSOLE7.Z80 | CONSOLE8.COM
CONSOLE8.Z80 | DDT .COM | DISKDEF .LIB | DISPLAY .LIB
DUMP .ASM | DUMP .COM | ED .COM | EXIT .COM
EXIT .SUB | EXIT .Z80 | FORMAT .COM | FORMAT .SUB
FORMAT .Z80 | HELLO .LUA | INFO .COM | INFO .SUB
INFO .Z80 | LOAD .COM | LU .COM | LUA .COM
LUA .SUB | LUA .Z80 | LUAINFO .LUA | MAC .COM
MAKEFCB .LIB | MBASIC .COM | MLOAD .ASM | MLOAD .COM
MLOAD .DOC | MOVCPM .COM | OPCODES .DOC | PIP .COM
RSTAT .COM | RSTAT .SUB | RSTAT .Z80 | STAT .COM
SUBMIT .COM | SUBMITD .COM | SYSGEN .COM | TE .COM
UNARC .COM | UNCR .COM | USQ .COM | XMODEM .COM
XSUB .COM | Z2HDR .LIB | Z3BASE .LIB | Z3HDR .LIB
Z80ASM .COM | Z80ASM .PDF | Z80CCP .ASM | Z80CCP .SUB
ZCPR2 .ASM | ZCPR2 .SUB | ZCPR3 .ASM | ZCPR3 .SUB
ZEXALL .COM | ZEXDOC .COM | ZSID .COM | ZTRAN .COM
A0>
As you can see, I’m using ZCPR3 here and instead of the A>
we see here a
A0>
indicating we’re on the A drive, user area 0.
Faster terminal output.
By default, the terminal is set to 9600 baud. You might find this too slow
(although this was a fairly typical baud rate at that time, for years I had
to use a mere 300 baud…). You can increase this to e.g. 115200 baud by
again modifying the RunCPM.ino
file on line 22 like this:
// Serial port speed
#define SERIALSPD 9600
to:
// Serial port speed
#define SERIALSPD 115200
recompile and upload to the Arduino Due and use this command line for
minicom
:
minicom -b 115200 -o -D /dev/ttyACM0
Have fun exploring the CP/M world of yesteryear!