So you wanna run a CP/M network?

Introduction
I. Your First Server
    A. Telneting to the Server
    B. Configuration
II. Your First Client
    A. Running the Server
    B. Running the Client
III. Expanding Your Network
Epilogue
Scripts

CP/M does networks!? Who knew? Well, it does, it's called (perhaps not-too-creatively) CP/Net, and, like the rest of Digital Research's products, it has been freely available for many years. The problem has always been assembling, compiling and installing it.

Well, that used to be a problem. Not anymore. Udo Munk, developer of the best-in-class Z80Pack Z80 emulator, has done all the hard work, making it possible for even the most technically incompetent (speaking from experience) of us to become CP/M network admins in just a few minutes' time. And as an added bonus, through the magic of Z80Pack, you can connect your CP/Net network up to the Internet and access it from anywhere in the world. Not even Gary Kildall could have imagined that!

This tutorial assumes that you are running Linux as your host OS, that you already have Z80Pack installed to ~/z80pack/, and that you have basic familiarity with opening and using a command terminal. If you are running Windows, the differences should be trivial. And if you don't yet have Z80Pack installed, there are easy-to-follow instructions at the site, or you can use our quick installation script to get it done for you.

Ready? Here we go.

Your First Server

In this first part, you're going to install a single CP/Net server and connect to it via telnet. You've got Z80Pack installed, right? Now it's time to install the server. Ready ... set ...

You're done.

Yep, CP/Net is part of the base install of Z80Pack. All we need to do is make one configuration change and then launch our server. Open a terminal window and run the following commands:

$> cd ~/z80pack/cpmsim/conf/
$> ls

You should see two files: net_client.conf.example and net_server.conf.example. We're going to enable the server, so:

$> cp net_server.conf.example net_server.conf

That's it. We're now ready to start the server.

$> cd ~/z80pack/cpmsim
$> ./mpm

Z80Pack will load and launch a CP/M session. Look for messages like console 1 listening on port 4000, telnet = on as Z80Pack starts up.

#######  #####    ###            #####    ###   #     #
     #  #     #  #   #          #     #    #    ##   ##
    #   #     # #     #         #          #    # # # #
   #     #####  #     #  #####   #####     #    #  #  #
  #     #     # #     #               #    #    #     #
 #      #     #  #   #          #     #    #    #     #
#######  #####    ###            #####    ###   #     #

Release 1.35, Copyright (C) 1987-2017 by Udo Munk

CPU speed is unlimited
Server network configuration:
console 1 listening on port 4000, telnet = on
console 2 listening on port 4001, telnet = on
console 3 listening on port 4002, telnet = off
console 4 listening on port 4003, telnet = off

Booting...

64K CP/M Vers. 2.2 (Z80 CBIOS V1.2 for Z80SIM, Copyright 1988-2007 by Udo Munk)

A>

At the CP/M A> prompt, run:

A>MPMLDR

to load the multi-user version of CP/M. And that's it. You can work on the server directly, but the real fun is logging in remotely. Let's do that now.

Telneting to the Server

Open a new terminal window and run:

$> telnet localhost 4000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.


MP/M II V2.0
Copyright (C) 1981, Digital Research

1A>

You should have connected and found yourself at a CP/M prompt (that was easy!).

You can open a second terminal and telnet to port 4001. And for the piece de resistance, with a little port forwarding you can even telnet in to your server from the Internet. What fun!

WARNING! To exit your telnet connections, do not type exit or bye. This will shut down your server, which is probably not what you desired. If you're using the standard Linux telnet client, type ^] to escape to the telnet prompt, then close to close the telnet connection without affecting the server.

$> telnet localhost 4000 Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.


MP/M II V2.0
Copyright (C) 1981, Digital Research

1A>
telnet> close
Connection closed.

Configuration

Your CP/Net server supports up to four connections, but out of the box only two are enabled for telnet. Let's take a look now at the configuration file.

$> cd ~/z80pack/cpmsim/conf/
$> cat net_server.conf

You should see the following:

# Console       telnet flag     TCP/IP port
1               1               4000
2               1               4001
3               0               4002
4               0               4003

This tells us that the four supported consoles are connected to the four TCP/IP ports 4000-4003, and that ports 4000 and 4001 are enabled for telnet access. The second two have telnet disabled and can be used for login by CP/Net clients (more on this, later). Now run:

$> telnet localhost 4001

You should find yourself again connected to your server, only this time the command prompt says, 2A>, indicating you have been assigned user 2.

To connect more telnet sessions, you'll have to enable telnet on the other consoles. Shut down all your client sessions and your server by typing BYE at the server's console. Edit conf/net_server.conf, change the telnet flags to "1", save, exit and then relaunch your server. Now you will be able to telnet to any or all of the ports 4000 - 4003.

If all you wanted was a CP/M session you can remotely connect to, you're done. Enjoy! If you want to learn how to set up and connect a CP/M client, read on.

Your First Client

OK, I admit it. I lied. In the first part above we weren't really running a CP/Net server, just an MP/M machine with telnet-accessible consoles. But it pretty much acts like a server and everything, so why not call it a server?

In this part we're going to be installing a CP/Net client, but it will need a real server to connect to, so we will install one of those as well. But first, if you currently have a server running, shut it down by typing A:BYE.

To kick things off we'll download the client and server packages. So open a terminal and run these commands:

$> cd ~/z80pack/cpmsim/
$> wget https://www.autometer.de/unix4fun/z80pack/ftp/cpm2-net-1.2.tgz
$> wget https://www.autometer.de/unix4fun/z80pack/ftp/mpm2-net-1.2.tgz
$> tar -xvf cpm2-net-1.2.tgz
$> tar -xvf mpm2-net-1.2.tgz
$> ls

You should see two new session launch scripts in ~/z80pack/cpmsim/: cpm2-net2 and mpm-net2. These scripts will require updating before we can run them. We'll use the nano editor here, since it comes with most Linux distros.

$> nano cpm2-net2

Change all instances of, e.g., drivea.cpm to drivea.dsk, then save and exit. Then do the same for mpm-net2.

Running the Server

Now, we will need to enable networking and run the server:

$> cd ~/z80pack/cpmsim/conf/
$> rm *.conf
$> cp net_server.conf.example net_server.conf
$> cd ..
$> ./mpm-net2

You should see the start-up banner for the MP/M server:

console 1 listening on port 4000, telnet = on
console 2 listening on port 4001, telnet = on
console 3 listening on port 4002, telnet = off
console 4 listening on port 4003, telnet = off

Booting...

64K CP/M Vers. 2.2 (Z80 CBIOS V1.2 for Z80SIM, Copyright 1988-2007 by Udo Munk)

A>

If you see the message, bind server socket: Address already in use, check to make sure you didn't forget to shut down your other servers, then try again.

Finally, load MP/M:

A>MPMLDR

You should see lots of stuff, and then the sign-on banner:

MP/M II V2.0
Copyright (C) 1981, Digital Research

0A>

Running the Client

Your server is up and running. On to the client. Since we're going with default settings, we only need to swap network configuration files and start the client. Open a new terminal:

$> cd ~/z80pack/cpmsim/conf/
$> rm *.conf
$> cp net_client.conf.example net_client.conf
$> cd ..
$> ./cpm2-net2

Verify that you saw the message Connecting to localhost at port 4002 and the sign-on banner, 64K CP/M Vers. 2.2 (Z80 CBIOS V1.2 for Z80SIM, Copyright 1988-2007 by Udo Munk) then, at the A> prompt start the network, then log in and map a network resource:

A>CPNETLDR


CP/NET 1.2 Loader
=================

BIOS         FA00H  0600H
BDOS         EC00H  0E00H
SNIOS   SPR  E900H  0300H
NDOS    SPR  DD00H  0C00H
TPA          0000H  DD00H

CP/NET 1.2 loading complete.

A>LOGIN

A>NETWORK B:=B:

A>

Both the LOGIN and NETWORK commands will return silently if successful. If you see the message Login failed, verify that you downloaded the same version of the client and server software, above.

So our server is running, our client is logged in, and we've mapped a network resource. Now it's time to play :) . On the client, run:

A>DIR B:
B: MPM      SYS : RESBDOS  SPR : NETWRKIF ASM : SYSTEM   DAT
B: BNKBDOS  SPR : TMP      SPR : GENSYS    OM : BNKXIOS  MAC
B: MPMSTAT  BRS : SPOOL    RSP : ABORT    RSP : SCHED    RSP
B: BNKXIOS  SPR : HWCLOCK  RSP : RESXIOS  SPR : BNKXDOS  SPR
B: SERVER   RSP : SPOOL    BRS : XDOS     SPR : SCHED    BRS
B: MAIL     COM : M80      COM : SYSGEN   SUB : MPMSTAT  RSP
B: NETWRKIF RSP
A>

Since we've mapped local drive B: to the server's B: drive, we are seeing the contents of B: on the server. Now let's check the network's status:

A>CPNETSTS

CP/NET 1.2 Status
=================
Requester ID = 11H
Network Status Byte = 10H
Disk device status:
  Drive A: = LOCAL
  Drive B: = Drive B: on Network Server ID = 00H
  Drive C: = LOCAL
  Drive D: = LOCAL
  Drive E: = LOCAL
  Drive F: = LOCAL
  Drive G: = LOCAL
  Drive H: = LOCAL
  Drive I: = LOCAL
  Drive J: = LOCAL
  Drive K: = LOCAL
  Drive L: = LOCAL
  Drive M: = LOCAL
  Drive N: = LOCAL
  Drive O: = LOCAL
  Drive P: = LOCAL
Console Device = LOCAL
List Device = LOCAL
A>

There is of course much more to running a CP/Net network. Udo Munk, the author of Z80Pack, has made full documentation available at the Z80Pack website. The two best places to begin are the CP/NET Network Operating System Reference Manual and the MP/M 2 manual.

Expanding Your Network

Yes! This is great and all, but you want more. More servers! More clients! More!

As mentioned above, it is possible to connect up to four clients to a server. But each client must be manually configured to connect to a specific server port.

$> cat conf/net_client.conf.example

# example for network client configuration
#
# Console       host                    TCP/IP port
#1              www.unix4fun.org        4052
1               localhost               4002

As you can see, the default setting for a client hardwires it to port 4002. To connect additional clients, you must manually change the port setting to 4001, etc., before launching the client.

And just by the way, as the configuration file shows us, we could connect our client to a server on the internet, or even to a local network address, allowing CP/Net to piggyback over TCP/IP networks! Cool, no? (Just don't try the example shown; Udo Munk no longer runs that server.)

It is also possible to install multiple servers. But in order to prevent servers from stepping on each other's ports, you must manually configure each server's port assignments in net_server.conf, and make sure each port you wish to connect a client to has telnet disabled.

As you can imagine, all this manual downloading and installation, and constant reconfiguring of port settings and swapping in and out of configuration files can become tedious (not to mention error prone) like real quick. But, hey, I got your back. Nathanael's Super-Duper Client Installation Script (or "NaSuDuClInSc", for short :) ) and Nathanael's Super-Duper Server Installation Script ("NaSuDuSerInSc") are here for you.

Click on each of the links above to see the scripts. Save each script to ~/z80pack/cpmsim/; we'll call ours client_install and server_install. Then chmod +x each one.

First, shut down all clients and servers currently running by type A:BYE in each one. Then since we will no longer be needing the client and server we installed above, let's adios them.

$> cd ~/z80pack/cpmsim
$> rm cpm2-net2
$> rm mpm-net2

Now run the server installation script:

$> ./server_install

The script will download and install the server software, place a network configuration file for it in conf/library/ and then create a session launch script called mpm-server1.

Now run the client installation script twice to create two clients.

$> ./client_install
$> ./client_install

and you will have two session launch scripts called cpm2-client1 and cpm2-client2.

You can run the server and client install scripts repeatedly to install additional servers and clients and they will be assigned sequential names such as mpm-server2, mpm-server3, cpm2-client3, etc. The install and launch session scripts will automatically handle all the configuration stuff; all you need to do is to run the session script when you wish to run a particular server or client.

Note that each client is configured to connect to a specific server, so that if you wish to run a specific client, you should make sure its assigned server is already running. It might be good to look briefly at how these install scripts set things up.

Those familiar with Z80Pack know that it stores virtual disk images in disks/library/ and that a session install script will create links to those images in disks/. Network configuration is handled by the server and client install scripts in the same way: a config file is placed in conf/library/ for each client and server, and that client's or server's session launch script will place a link to it in conf/.

On installation, each server is assigned a range of ports, with the telnet flags enabled for Consoles 1 and 2, disabled for Consoles 3 and 4.

ServerConsole 1 (T)Console 2 (T)Console 3 (-T)Console 4 (-T)
mpm-server14000400140024003
mpm-server24004400540064007
mpm-server34008400940104011
mpm-server44012401340144015

On the client side, each pair of clients is configured to connect as follows:

ClientPortServer
cpm2-client14002mpm-server1
cpm2-client24003mpm-server1
cpm2-client34006mpm-server2
cpm2-client44007mpm-server2
cpm2-client54010mpm-server3
cpm2-client64011mpm-server3
cpm2-client74014mpm-server4
cpm2-client84015mpm-server4

Of course these are just default settings which you are free to change should you want to. Just edit the .conf files in conf/library/.

Now let's run our server and clients:

$> ./mpm-server1

At the A> prompt, load MP/M:

A>mpmldr

Server's up and waiting! Open a second terminal.

$> ./cpm2-client1

Look for a message saying, Connecting to localhost at port 4002. Load CP/Net and login in.

A>cpnetldr


CP/NET 1.2 Loader
=================

BIOS         FA00H  0600H
BDOS         EC00H  0E00H
SNIOS   SPR  E900H  0300H
NDOS    SPR  DD00H  0C00H
TPA          0000H  DD00H

CP/NET 1.2 loading complete.

A>login

A>

Now the second client. In a third terminal do:

$> ./cpm2-client2

This time you should see, Connecting to localhost at port 4003. Again, load CP/Net and login.

Epilogue

Th-th-th-that's all, folks! Through the wonders of Z80Pack, you can set up as simple or complex a CP/Net network as you'd like, and all you have to do is run a couple of scripts and edit a few configuration files.

So what's next? The fun, of course, of learning how to administer a CP/Net network. For that, the Z80Pack website has made full documentation available. Start by reading through the CP/Net Network Operating System manual, the CP/M 2.2 Operating System Manual and the MP/M 2 User's Guide, then explore the wealth of other resources available at the site. To discuss with other CP/M afficianados, head on over to the very active comp.os.cpm. And to download CP/M software, try my own archive, the *HUMONGOUS* CP/M Software Archives.

Scripts

These scripts have only been tested with Bash. For Windows users, they shouldn't be too difficult to convert.

Z80Pack Installation

Save this script to whatever directory you want Z80Pack installed inside of. E.g., if you want Z80Pack to be installed to ~/z80pack-1.36/ then save this script in ~. Then just run it. If there is a more recent version of Z80Pack available, or if for some reason you want to install an older version, then change the zver variable accordingly. Note that currently this script generates an "unexpected end of file" message at the end. This error is harmless and can be ignored.

#!/bin/bash

# Variables

  zver=1.36           # set to the version you wish to install
  zroot=~/cpm         # set to the location where you want z80pack-1.36/
  zloc=$zroot/z80pack-$zver
  osenv=linux         # Your operating environment; one of linux, cygwin, osx, solaris or bsd 

function main {
  echo " ------------------------------ "
  echo "   Installing Z80Pack           "
  echo " ------------------------------ "

# Obtain and unpack Z80Pack
  mkdir -p $zroot
  cd $zroot
  wget https://www.autometer.de/unix4fun/z80pack/ftp/z80pack-$zver.tgz
  tar xvf z80pack-$zver.tgz
  rm z80pack-$zver.tgz
}

function buildit     {
  echo " ------------------------------ "
  echo "   Building $component          "
  echo " ------------------------------ "
  # build it
  cd $zloc/$component/ # two steps to accomodate frontpanel
  cd srcsim/           # which doesn't have a srcsim subdir
  make -f Makefile.$osenv
  make -f Makefile.$osenv clean

  # Backup disk images
  if [ -d ../disks ]
   then
    cd ../disks
    mkdir -p backups
    cp -p library/* backups/
  fi
}

function cpmsim      {
  component=cpmsim ; buildit

  # Compile support programs
  mkdir -p ~/bin
  cd $zloc/cpmsim/srctools
  make
  make install
  make clean
  # Make sure ~/bin is in your search path: export PATH=PATH:~/bin
}

function frontpanel {
# --- FRONTPANEL ---
  # front panel requires the following:
  # sudo apt-get install g++
  # sudo apt-get install libjpeg9-dev
  # sudo apt-get install libglu1-mesa-dev
  # sudo apt-get install libxmu-dev   

  component=frontpanel ; buildit

  echo " ------------------------------ "
  echo "   Enter your admin password:   "
  echo " ------------------------------ "
  sudo cp libfrontpanel.so /usr/lib/ # or a shared library path on your system.
}

function altairsim   {
  component=altairsim ; buildit
  
  # Install additional
  cd $zloc/altairsim/
  for mits in dbl.hex dbl.asm dbl.prn; do
    wget https://www.autometer.de/unix4fun/z80pack/ftp/altair/$mits
   done
  for mits in mits-cpm22.tgz mits-cpm14.tgz mits-basic40.tgz mits-basic50.tgz mits-cpm-tools.tgz mits-cpm-ws30.tgz mits-dos.tgz
   do
    wget https://www.autometer.de/unix4fun/z80pack/ftp/altair/$mits
    tar -xvf $mits
    rm $mits
   done
   
  # Make scripts
  cat > mits-cpm22 <<EOL
#!/bin/sh
#
rm -f disks/drive[abcd].dsk
ln disks/library/mits-cpm22-56k.dsk disks/drivea.dsk
ln disks/library/mits-cpm-tools.dsk disks/drivec.dsk
#
./altairsim -x dbl.hex $*
EOL

  chmod +x mits-cpm22

  cat > mits-cpm14 <<EOL
#!/bin/sh
#
rm -f disks/drive[abcd].dsk
ln disks/library/mits-cpm14-24k.dsk disks/drivea.dsk
ln disks/library/mits-cpm-tools.dsk disks/drivec.dsk
#
./altairsim -x dbl.hex $*
EOL

  chmod +x mits-cpm14


  cat > mits-dos <<EOL
#!/bin/sh
#
rm -f disks/drive[a].dsk
ln disks/library/mits-dos.dsk disks/drivea.dsk
#
./altairsim -x dbl.hex $*
EOL

  chmod +x mits-dos

}

function cromemcosim {
  component=cromemcosim ; buildit
}

function webfrontend {
  cd $zloc/webfrontend/civetweb/
  make -f Makefile.$osenv
  make -f Makefile.$osenv clean
}

function imsaisim    {
  component=imsaisim ; buildit
}

# --- MAIN ---

main
cpmsim
frontpanel
altairsim
cromemcosim
# webfrontend is not in 1.36 or earlier
if [ -d $zloc/webfrontend/ ] && webfrontend 
imsaisim

exit 0

Client Installation

#!/bin/bash

# Variables
  zver=1.36 # The latest version of Z80Pack
  zloc=~/cpm/z80pack-$zver
  confdir=conf
  conflib=$confdir/library

# Create dir
  cd $zloc
  mkdir -p $conflib

# skip existing clients
  clientnum=1
  while [ -f cpm2-client$clientnum ]; do clientnum=$(( $clientnum + 1 )) ;
  done

# Fetch and install
  wget https://www.autometer.de/unix4fun/z80pack/ftp/cpm2-net-1.2.tgz

  # --transform replaces the string 'net2' with 'clientx' in all extracted
  # filenames. Unfortunately, 'client$clientnum' is interpreted literally,
  # so the files first need to be extracted to 'clientx' then renamed.

  tar --transform='flags=r;s|net2|clientx|' -xvf cpm2-net-1.2.tgz
  mv disks/library/cpm2-clientx.dsk disks/library/cpm2-client$clientnum.dsk
  rm cpm2-clientx # We will rebuild this later

# Build configuration files

  # Create network configuration file
  confile=$conflib/cpm2-client$clientnum.conf
  listenport=$((4000 + clientnum*2 - ((clientnum-1) % 2)))

cat > $confile <<EOL
# Network client configuration for Client $clientnum
#
# Console       host                    TCP/IP port
1               localhost               $listenport
EOL

# Build launch script
script=cpm2-client$clientnum

cat > $script <<EOL
#!/bin/bash
#
rm -f disks/drive[a].dsk
ln disks/library/cpm2-client$clientnum.dsk disks/drivea.dsk
#
rm -f $confdir/*.conf
ln $conflib/cpm2-client$clientnum.conf $confdir/net_client.conf
#
./cpmsim $*
EOL

chmod +x $script

#cleanup
  rm cpm2-net-1.2.tgz

Server Installation

#!/bin/bash

# Variables
  zver=1.36 # The latest version of Z80Pack
  #basedir=~/cpm/z80pack/cpmsim
  confdir=conf
  conflib=$confdir/library

# Create dir
# cd $basedir
  mkdir -p $conflib

# skip existing servers
  servernum=1
  while [ -f mpm-server$servernum ]; do servernum=$(( $servernum + 1 )) ; done

# Fetch and install; rename extracted files
  wget https://www.autometer.de/unix4fun/z80pack/ftp/mpm-net-1.2.tgz

  # --transform replaces the string 'net2' with 'serverx' in all extracted
  # filenames. Unfortunately, 'server$servernum' is interpreted literally,
  # so the files first need to be extracted to 'serverx' then renamed.

  tar --transform='flags=r;s|net2|serverx|' -xvf mpm-net-1.2.tgz
  mv disks/library/mpm-serverx-1.dsk disks/library/mpm-server$servernum-1.dsk
  mv disks/library/mpm-serverx-2.dsk disks/library/mpm-server$servernum-2.dsk
  rm mpm-serverx # We will rebuild this later
  
# Build configuration files

  # Create network configuration file
  confile=$conflib/mpm-server$servernum.conf
  baseport=$(( 4000 + (servernum-1)*4))

cat > $confile <<EOL
# Network server configuration for MP/M Server $servernum
#
# console:      # of the console port, 1-4
# telnet flag:  1 = telnet option negotiation on, 0 = off
# TCP/IP port:  every console needs a different port; every server a unique range
#
# Console       telnet flag     TCP/IP port
1               1               $((baseport+0))
2               1               $((baseport+1))
3               0               $((baseport+2))
4               0               $((baseport+3))
EOL

# Build launch script
  script=mpm-server$servernum

cat > $script <<EOL
#!/bin/sh
#
rm -f disks/drive[ab].dsk
ln disks/library/mpm-server$servernum-1.dsk disks/drivea.dsk
ln disks/library/mpm-server$servernum-2.dsk disks/driveb.dsk
#
rm -f $confdir/*.conf
ln $conflib/mpm-server$servernum.conf $confdir/net_server.conf
#
./cpmsim $*
EOL

chmod +x $script

#cleanup
  rm mpm-net-1.2.tgz