CP/NET 1.1 server emulator for Unix systems

Here is a program you can use on your Linux machine to communicate with a single CP/M requester connected to a PC serial port. It uses the RS232 protocol described in Appendix E of the CP/NET documentation.

The program emulates either version 1.1 or 1.2 of CP/NET, and therefore must be used with a requester running the same CP/NET version. It should be noted that CP/NET versions 1.0, 1.1 and 1.2 are not compatible with each other (1.2 is not even backwards compatible with 1.1). Although they use the same physical-layer protocol over a serial line, they differ at the network-layer level.

I have included the package working SNIOS modules for the P112 CPU board, for both CP/NET 1.1 and 1.2 versions. The SNIOS uses the second serial port of the P112 at 38400 baud.


The package contains both a server (master) and a client (requester). The server runs under Unix, the requester under CP/M. Included is a Makefile that can be used to build the server using the GNU C compiler.

The requester files are in the cpmfiles directory, together with the CP/NET utilities from the standard Digital Research distribution. To compile the requester NIOS (SNIOS.ASM), use the supplied NIOS.SUB submit file on the P112 machine; you will also need DR's RMAC and LINK. You can build it under Unix as well, using a package like zxcc. But you can actually skip this step, since a ready-to-run SNIOS.SPR file for the P112 is provided.

Command line arguments

The server program accepts a number of command line options:

-ini filename Specify an alternate initialization file (see below). By default, the initialization file name is ${HOME}/.cpnet.ini
-level level The CP/NET version level to emulate, 'level' can be either 1.1 or 1.2 (defaults to 1.2)
-port device Sets the serial port to 'device', overriding the initialization file settings.
-speed value Sets the serial baud rate to 'value', overriding the initialization file settings. The argument must be an standard serial baud rate, 38400 is assumed if an invalid value is specified.
-debug value Sets the debug mask to 'value', overriding the initialization file settings. The debug mask can be either zero (no debug information is output), or a combination of the following bit values:
1 Show packet-level data transfer. This displays the data bytes sent in both directions in hexadecimal format and produces an awfully large amount of output. Useful for debugging the serial communication protocol.
2 Show network-level data transfer. This dumps CP/NET requests and responses in both hexadecimal and ASCII format, together with the NDOS function number and name. This proved to be very useful during the coding of the server program, especially since I could not find any CP/NET 1.1 documentation.
4 Show other miscellaneous debug information, like the disk-to-dir mapping table on startup, network logins and logoffs, etc.
For example, a value of 6 will output both network-level data and miscellaneous information, while a value of 7 will dump just everything.
-netid id Sets the server's network ID to 'id', overriding the initialization file settings.
-pwd passwd Sets the network password to 'passwd', overriding the initialization file settings. Only the first 8 characters are used, and the string is converted to UPPERCASE letters.
-version Shows the program version number, --version is a synonymous.
-help Shows a help screen, --help is a synonymous.

The initialization file

The CP/M-disk-to-Unix-directory mappings, communication device settings and a few other options are set through an initialization text file that is read by the server program at startup. By default, the program expects the file to be located in the user's home directory with the .cpnet.ini name. An alternate configuration file may be specified with the -ini command line option.

You can use the example cpnet.ini file supplied with the package when creating your own one.

The initialization file is structured similarly to MS-Windows .ini files, i.e. it is split in several sections, each one containing a number of settings, like this:

variable1 = value1
variable2 = value2


Below is a description of the recognized sections followed by the list of accepted options, and their default values in case no option is specified or the initialization file is missing:

Section [defaults]
level = 1.2 The CP/NET version level to emulate, either 1.1 or 1.2.
debug = 0 Sets the debug mask. See the command-line options description above for a list of possible values.
netid = 0 Sets the server's network ID. There is no reason to change this, unless you are running multiple servers on the same or different PCs, connected to the same CP/M machine.
password = PASSWORD Sets the network password. Only the first 8 letters will be used, and the string converted to UPPERCASE.
Section [comm]
device = /dev/ttyS0 Specifies the serial communication device.
speed = 38400 Specifies the serial communication baud rate.
Section [drives]
<drive letter> = <unix directory>
Specifies the disk-to-directory mappings, where <drive letter> is any valid CP/M drive name (A to P) in UPPERCASE, and <unix directory> is the full path specification to the directory to which the disk is mapped, for example:
A = /usr/local/cpm/cpmfiles
B = /usr/local/cpm/adventure
C = /usr/local/cpm/zsdos
By default, only drive A is defined as
A = <directory where the server was started>/cpmfiles
Section [printers]
lst<number> = <filename>
Specifies the printer-to-file mappings, where <number> can be from 0 to 7 (i.e. you can have up to eight virtual printers), and <filename> is the full path to the file or device where the output should be appended. Note that "lst" is in lowercase letters. The special name "-" (single dash) can be used to redirect the output of the corresponding virtual printer to the standard output, while "--" can be used to redirect the output to the standard error output. For example:
lst0 = /dev/lp
lst1 = /usr/local/cpm/lst/lst1.prn
lst2 = -
By default no printers are defined, and any printer output will be ignored.

All settings, except the disk-to-directory and printer-to-file mappings, can be overriden by command-line options.


Copy the supplied CP/NET requester files (you can use cpmtools, for example) from the cpmfiles directory to a P112-formatted and SYSGENed floppy. Among the files there is a compiled, ready-to-use SNIOS module.

Connect the second serial port of the P112 board to a free serial port on the PC. Start the server program on the PC side, making sure that the correct serial port device is specified in the initialization file. Alternatively you can use the -port and -speed command line options to override the .ini file settings. I would suggest using some debug output at this stage to make sure that the communication between the two machines works, -debug 6 should do the job.

Then, boot the P112 from the floppy and issue the CPNETLDR command. You should get an output like this:


CP/NET 1.2 Loader

BIOS         EE00H  1200H
BDOS         E000H  0E00H
SNIOS   SPR  DD00H  0300H
NDOS    SPR  D100H  0C00H
TPA          0000H  D100H

CP/NET 1.2 loading complete.


If the server is not yet running on the PC side, or if there are communication problems between the PC and the P112, you will get an additional "Network access failed" message. Most common causes include incorrect port device and/or speed settings, or the wrong type of cable is used to connect the two machines.

If everything went OK, then you can try to login into the server so you can start accessing files:


The login command does not produce any output on success. If you specified in the server configuration a password different than the default "PASSWORD", then you'll have to supply it in the login command line as well, otherwise the command will fail, e.g.:

A>login mypasswd

You can now map local drives to remote ones and start accessing the files:

A>network c:=a:

A>network d:=b:

A>dir c:
C: DDT      COM : DUMP     COM : ED       COM : T        BAS
C: TEST     BAS : PIP      COM : ASSM     COM : DDTZ     COM
C: ERRORS       : FORT     COM : FRUN     COM : GRAPH    FOR
C: LD       ASM : LOAD     FOR : RAND     FOR : READ     ME 
C: RAND     OBJ : LD       PRN : LOAD     OBJ : LD       OBJ

CP/NET 1.2 Status
Requester ID = 70H
Network Status Byte = 00H
Disk device status:
  Drive A: = LOCAL
  Drive B: = LOCAL
  Drive C: = Drive A: on Network Server ID = 00H
  Drive D: = Drive B: on Network Server ID = 00H
  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

If you need to connect to a more than a couple of drives on the remote PC, then it would be advantageous to create a submit file to do the job. For example, I use the following one:

A>type connect.sub
network c:=a:
network d:=b:
network e:=c:
network f:=d:
network g:=e:
network h:=f:
network i:=g:
network j:=h:
network lst:=0


Note that I did not include the CPNETLDR command at the top of the file, since it breaks the execution of the submit file.

When you are done, use CTRL/C on the PC side to end the server program and to return to the shell prompt.

Known issues

  • Only one requester at a time is supported. This is not really a problem, since under Unix several server processes can be run simultaneously to service several requesters.
  • The long unix file name conversion to CP/M 8.3 format algorithm is not smart enough, and there may be cases when two or more files beginning with the same letters appear under CP/M directory listings as duplicate files. Opening, deleting or renaming one of these files can cause unpredictable results. To avoid this problem, ensure that the directories to be accessed by the remote machine contain only files with 8.3-style names. Also, only files with lowercase names can be accessed.
  • For simplicity, we set the FCB fields r0,r1,r2 equal to ex,rc,cr respectively during read and write operations. That should not affect normal CP/M applications, and I haven't encountered any problems so far.
  • User numbers are ignored, so mapped drives will seem to contain the same files in every user area.
  • The CP/NET NDOS has a bug affecting the Search First and Search Next functions: upon return the L register is not set to the same value as the A register, like the standard CP/M BDOS does. Some programs may malfunction (e.g. PROLOGZ enters an endless loop or gives wrong results for the Files menu option). The bug is present in both CP/NET 1.1 and 1.2.
  • Mail exchange (CP/NET 1.1 only) is implemented as a test feature, and follows the very simple DR's protocol (see the source code for the SNIOS.ASM example and the SNDMAIL/RCVMAIL utilities). The server sends a single welcome mail message ("Welcome to <hostname>") to the requester immediately after login. The requester can read the message by using the RCVMAIL utility, or by doing a warm boot. A message can be sent to the server with the SNDMAIL utility, for example:
    A>sndmail (0) "hello, world!"
    The number in parenthesis is the destination ID, and is currently ignored by the server. Messages sent to the server are displayed on its standard output.

Last updated: 10-Sep-2007