Driving serial communication in CP/M on the P2000C using Z80 assembly#

Introduction#

The Philips P2000C, despite allowing for a 8088-CoPower Board, was in the first place a CP/M machine. CP/M (Control Program for Microcomputers) was an early operating system developed by Gary Kildall in 1974 for Intel 8080 microcomputers. It became one of the first widely used OS for personal computers, providing essential features like file management, device input/output control, and program execution. CP/M was particularly influential because it allowed software to run on different hardware platforms, establishing an early form of software portability. It laid the groundwork for future operating systems and influenced the development of MS-DOS, which became dominant in the 1980s. CP/M’s impact is significant in the history of personal computing.

System calls#

MS-DOS and CP/M share many architectural similarities, particularly in how they handle system calls and hardware interrupts, since MS-DOS was heavily influenced by CP/M. Both CP/M and MS-DOS provided system calls (or “function calls”) through a software interrupt mechanism, making it easy for applications to access system services like file management or input/output operations.

In CP/M, system calls were invoked using the CALL 5 instruction (using the memory location 0005h), which referred to the system’s entry point for its Basic Input/Output System (BIOS) or Command Processor (CCP). CP/M used a set of function codes passed via register C and associated parameters passed via register DE to specify operations like reading/writing files, manipulating disks, or performing serial communication. Similarly, MS-DOS used the INT 21h interrupt as the main mechanism for invoking system calls. A function number was passed through the AH register to specify the desired service, such as file operations, device I/O, or memory management.

Serial communication#

Whereas serial communication is handled in MS-DOS on the P2000C via INT 14, in CP/M it is assigned to BDOS functions 3 and 4, corresponding to raw console input and output, respectively. To test the serial communication, I hooked up an oscilloscope to te P2000C as shown in the image below.

../_images/p2000c-serial-cpm-01.jpg

Oscilloscope hooked up to the P2000C to investigate the RS-232 port.#

Similar to DEBUG.COM in MS-DOS, in CP/M there exists the program DDT, known as the Dynamic Debugging Tool. It uses nearly the same syntax as DEBUG.COM. To draft a small program, we start by typing a100, which starts the inline assembler at $100. Next, we enter the following program

mvi c,4
mvi e,aa
call 5
mvi c,4
mvi e,55
call 5
rst 7

The MVI command places a value in the register. CALL performs a jump to a routine in memory after which it returns. Finally RST 7 returns back to the DDT program via a so-called “reset” instruction. Once the program is placed into memory, we can run g100 to start running it. Upon execution, the oscilloscope shows the following as shown in the image below.

../_images/osc1_p2000c_cpm.png

Output on the oscilloscope after running the serial communication program.#

From the oscilloscope output, we can observe that two bytes have been sent, being 0xAA and 0x55. We can also readily see that by default, serial communication runs at 9600 BAUD, uses a single stop bit and has no parity checking.

Having figured out the communication protocol, writing a program that does the reverse, i.e. receive a character over the serial port is fairly trivial. The only missing ingredient is how to output a character to the screen. For this, we can use BDOS function 2. The complete program would look as follows:

mvi c,3
call 5
mvi c,2
mov e,a
call 5
rst 7

The only new instruction here is MOV which copies the value from one register to another. Upon execution of this code, once a character has been received over the serial port, it will be printed to the screen.

Further reading#

Below, I provide a number of useful web pages when you work with the Z80 assembly.