Setting up an Emulated CP/M Development System

Introduction

CP/M was a very popular operating system for 8-bit microcomputers in the 70’s and into the 80’s.  At first it only supported the Intel 8080 processor and then later it also supported a few other processes, most notably the Zilog Z80.  Eventually there were versions for 16-bit processors as well, but these weren’t as popular as the original 8-bit implementation.  I still enjoy using CP/M nowadays just for the fun of it; one of the attractions for me is that it’s possible to understand a system completely from bottom to top, which is not practical with modern systems.

I use a real CP/M system occasionally, but if I’m developing CP/M software it’s easier to first use an emulated system because that way I can take advantage of the host system; e.g. network access, easy backups, quieter and cheaper to run, etc.

This article describes setting up an emulated CP/M 3.0 system using z80pack on either Linux or macOS.

Alternatively, you can instead use YAZE-AG; I’ve written some brief notes about setting up YAZE-AG on macOS.

Approach

We’ll be installing z80pack into ~/cpmsim and related tools into ~/local.  The emulated system will have the usual standard development tools plus some third-party tools and applications.

Installation

First, download z80pack via http://www.autometer.de/unix4fun/z80pack/index.html#download  The file we want is z80pack-1.36.tgz.  Once it is downloaded, build it and install it like this (these instructions are based on those on the z80pack website):

cd
tar -xf Downloads/z80pack-1.36.tgz
cd z80pack-1.36/cpmsim/srcsim
make -f Makefile.osx
make -f Makefile.osx clean
cd ../srctools

One small issue here is that the Makefile for srctools copies the executables to ~/bin which is not what we want. So edit Makefile and change INSTALLDIR to have a value of ..

Once that is done, continue:

make
make install
make clean
cd ../disks
cp library/* backups/
cd ../..
mv cpmsim ~
cd
rm -rf z80pack-1.36

At this point we could now run the as-packaged CP/M 2.2 or CP/M 3.0 system, but we first want to tailor it more to suit our purposes.  First, we want to add cpmtools.  cpmtools is a nice collection of command-line utilities which understand CP/M file formats and allow you to copy files into or out of a CP/M image file, etc. This makes life much easier when dealing with emulated CP/M systems.

Download cpmtools from http://www.moria.de/~michael/cpmtools/ and build and install it like this:

cd
tar -xzf Downloads/cpmtools-2.20.tar.gz
cd cpmtools-2.20
./configure --prefix=${HOME}/local
make
make install
cd
rm -rf cpmtools-2.20

Setting up the emulated media

At this point we populate our emulated system with 4 floppies and 3 hard disks:

cd ~/cpmsim
cp disks/library/cpm3-1.dsk disks/library/zob-a.dsk
cp disks/library/cpm3-2.dsk disks/library/zob-b.dsk
./mkdskimg c
./mkdskimg d
mv disks/drivec.dsk disks/library/zob-c.dsk
mv disks/drived.dsk disks/library/zob-d.dsk
cp disks/library/hd-tools.dsk disks/library/zob-i.dsk
./mkdskimg j
./mkdskimg p
mv disks/drivej.dsk disks/library/zob-j.dsk
mv disks/drivep.dsk disks/library/zob-p.dsk

This gives us A: through D: which are 8” floppy disks in ibm-3740 format, I: and J: which are 4MB hard disks in z80pack-hd format, and P: which is a 512MB hard disk in z80pack-hdb format.  A:, B:, I: are a copy of the original content but the others are all empty.

Create a script in the current directory called zob.sh like this:

#!/bin/sh
mkdir -p /tmp/.z80pack
mkfifo /tmp/.z80pack/auxin
mkfifo /tmp/.z80pack/auxout
cd ~/cpmsim
rm -f disks/drive[abcdijp].cpm
for i in a b c d i j p
do
    ln -sf library/zob-$i.dsk disks/drive$i.dsk
done
./cpmsim -f4

Don’t forget to make the script executable:

chmod +x zob.sh

Running zob.sh will start an emulated CP/M 3 system at 4MHz with 7 disks.  You can exit the emulated environment by using the bye command.

I recommend setting up a terminal environment to be a more realistic CP/M experience by setting up a shortcut or script to start a terminal with suitable font, font size, colours, and with dimension of 80 columns and 24 rows.  If possible, configure it to have ANSI terminal emulation.

At this point, drive I: has

  • 0: standard CP/M dev tools
  • 1: DRI PL/I 1.3
  • 2: Aztec C compiler 1.06D
  • 3: DRI Pascal MT+ 5.6.1
  • 8: games

Now we start to add some interesting third-party software.

Add Turbo Pascal into user 4 on I:

From http://www.autometer.de/unix4fun/z80pack/index.html, download the disk image which contains Turbo Pascal.  From it, extract file tp301a.dsk and place it in the ‘library’ used above.  Manually mount it as D: (by temporarily copying tp301a.dsk into ~/cpmsim/disks/drived.dsk) and then copy it to drive I: user 4: like so:

A>pip i:[g4]=d:*.*
A>I4:
4I>tinst

Select ANSI terminal emulation and a 4MHz clock.  Once finished, exit the emulated system by using the ‘bye’ command and then remove the ~/cpmsim/disks/drived.dsk image, it will be replaced on the next run anyway.

Once that’s done, you’ll be able to run Turbo Pascal in your emulated CP/M system by doing:

A>I4:
4I>turbo

Add SLR ASM into user 0 on I:

Make sure that the cpmtools executables are in your PATH if not already done so:

export PATH=$PATH:$HOME/local/bin

Ensure that you can properly read the HDD image, like this:

cd ~/cpmsim
cpmls -F -f z80pack-hd disks/library/zob-i.dsk

This test is important; if you don’t get a meaningful directory listing, you’ve probably got the disk formats wrong in cpmtools diskdefs, fix that before proceeding otherwise you’ll corrupt your virtual media.

The supplied disk images which we’re using as a basis already include Z80ASM by SLR Systems; this is the assembler that we’ll use in these examples.  But we don’t yet have the corresponding disassembler so we’ll add it now into disk I: in user 0:.

Download http://www.retroarchive.org/cpm/lang/slrdis.zip

Unzip it, and then do:

cpmcp -f z80pack-hd ~/cpmsim/disks/library/zob-i.dsk z80dis.com 0:
cpmcp -f z80pack-hd -t ~/cpmsim/disks/library/zob-i.dsk z80dis.doc 0:

ZDE

We also want to have a good editor for programming.  Based on the recommendations at TechTinkering we’ll go with ZDE.

Download http://cpmarchives.classiccmp.org/cpm/Software/WalnutCD/cpm/editor/zde16.lbr

Decompress it with a tool that understands LBR (such as The UnArchiver on macOS, or using unar on Ubuntu via sudo apt install unar).  Then do:

cpmcp -f z80pack-hd ~/cpmsim/disks/library/zob-i.dsk ZDE16.COM ZDENST16.COM 0:

The software is now on our I: drive but we still need to configure it.  Start the emulated system and do:

A>i:
I>setdef i:
I>zdenst16 zde16.com

Select terminal type ANSI and then save and quit, and then do:

I>ren zde.com=zde16.com
I>set zde.com [sys]

It should now be usable.  To do a  save-and-exit, use ESC x.  For more keys see TechTinkering.

Usage Example – Assembly Language

First, we want to create a simple “Hello World” in this environment.

Just to be arbitrary, we will develop assembly code on J: in user 7 and Pascal code on J: in user 8.

Start the CP/M simulator using the script shown earlier, with ANSI terminal emulation.

Edit

We use the ZDE editor to create a file called hello.z80. Of course you can use whatever editor you prefer, bonus points if you can cope with the “ed” editor supplied with CP/M!

A>j7:
7J>zde hello.z80

Enter the following source code and save it using ESC x

        org     0100h

; do the print call
        ld      hl,msg
        call    vprint

; exit back to the operating system
        ld      c,0
        call    5
        halt

; Print a null-terminated string to CONOUT
; HL = Start address of string
vprint: ld      a,(hl)
        cp      0       ; terminator?
        ret     z       ; return if so
        inc     hl      ; bump string pointer
        push    hl      ; and save it for later
        ld      e,a     ; E = character to print
        ld      c,2     ; C = BDOS conout
        call    5
        pop     hl
        jr      vprint

msg:    db      'Hello',0

Build

We compile hello.z80 using Z80ASM by SLR System. By default, this assembler creates an executable .COM file in one step.

7J>z80asm hello

Test

Now it is just a simple matter to run the produced binary code in hello.com.

7J>hello

Disassemble

It can sometimes be handy to disassemble a binary file back to Z80 source. This isn’t quite so useful for your own code but can be very useful when investigating a third-party binary. But in this example we disassemble our own binary file, using Z80DIS by SLR Systems.

The usage of Z80DIS is a little unconventional; in the following example we specify .ccc after the file name; this is not a file extension but instead is three flags specifying the drive on which the binary is found, where the resultant output should be sent, etc. (Note that we do this example on the C: drive to avoid accidentally overwriting our original source code.)

7J>pip c:=hello.com
7>c:
7C>z80dis hello.ccc

Debug

And of course this web site needs an example of a symbolic debugger! On CP/M 3 I normally use ZSID by Digital Research. This does all the basic things that one needs from a symbolic debugger. For its era, it was a good debugger.

7C>7j:
7J>zsid hello.com

Quick guide to driving ZSID:

  • use ‘l’ to list, ‘ls’ to list from address ‘s’ (a hex start address)
  • use ‘ds’ to display RAM from address ‘s’ (a hex start address) in both hex and ASCII
  • use ‘x’ to examine CPU state including registers
  • use ‘t’ to single step; this shows the CPU state and THEN does the step
  • use control-c to quit ZSID

Refer to the ZSID manual for more details.

Usage Example – Turbo Pascal

A>j4:
4J>turbo

Edit the source code using keys described at TechTinkering.

To compile and run from RAM just use ‘r’. To compile to a .com file change the compiler options first.

Future additions

Add WordStar, VisiCalc, and dBase just for the memories.