CP/M Internals

The reason most people are still drawn to CP/M is because it is so easy to fully understand the system, up from the tiniest detail. Yet, CP/M is the direct predecessor of MS-DOS (which was modeled very closely after CP/M) and has full functionality for normal use. If you know CP/M, you understand the low-level basics of any PC, and it gives you a level of understanding of the hardware that you'd never gain with, for instance, Linux. In short, understanding CP/M is relatively easy, and it gives you an insight in today's computers that is hard to obtain in any other way.

CP/M essentially provides programs with a set of function calls that allow them to communicate with the computer's I/O devices in a standardized manner. These system calls (the BDOS functions) ensure that user programs never have to bother with how the computer hardware stores a file or puts text on a screen - instead, the system calls can be relied upon to do the job. That concept is the basis for any operating system, and it enables computers that are very different in hardware to run the same programs. Here is a list with the CP/M BDOS calls - the operating system services.

The second service that CP/M provides is a command line interface and a number of small support programs that enable the user to maintain his computer and files. For CP/M, the list of support programs is minimal but complete: programs to edit files (ED, never to be used as it is truly bad. Instead, use VDE or WordStar) and copy (PIP) files, create assembler (ASM and LOAD) programs and debug (DDT) them.

Memory Map

​CP/M requires a minimum of 20K RAM, although realistically, 48K is the bare minimum. Most systems have a maximum of 64K. The chart below shows the memory map of a CP/M system: the first 256 bytes (100 hexadecimal) are used as a scratchpad by the operating system. It contains buffers, parameters and other transient data. This area is called Low Storage.


Moving up, the area where the user loads and runs his programs is called the TPA, Transient Program Area. The size of this area depends on the amount of RAM - its size is whatever is left after CP/M has taken what it needs. For a typical 64K system, the TPA stops at DC00h, but this number can vary.

​Running a program from the command line does nothing more that load the file into memory, starting at address 0100h, and start executing the code at 0100h after loading. ​

The top segment of memory is used by CP/M. First, the CCP area contains the command line user interface. 

Then, the BDOS contains all primitive functions (available to CP/M and user programs) to deal with disk drives, keyboard and screen, etc. BDOS and CCP are the same across CP/M machines, they do not need any customisation - other than the need for them to be relocated (shifted up or down in memory) if the size of total memory changes after a user forked out to buy another 16K of RAM, for instance.​

Lastly, the BIOS sits at the top end of memory. It normally is about 600h bytes in size, but can be larger if there is a lot of special hardware that needs to be managed. The BIOS is a function library that supports BDOS: anything that is hardware-specific needs to be done in the BIOS functions. BDOS, for instance, has a function for putting text on the screen, but will hand over the physical work of flipping bits in chips to the BIOS. BDOS has no idea whether the screen is a serial terminal or a graphics board.

​The Boot Process

To start up a CP/M computer, some bootup code needs to put the CP/M code into memory, and all hardware must be initialised properly. The Z80 is hardwired to start executing program code at location 0000h when it is switched on. There are various schemes to ensure that proper startup code is stored there; but the most common method is to have a ROM at location 0000h that does all it needs to do, and then switch that ROM out for RAM memory after the initial start-up has been completed.

Usually, the ROM will load the CP/M boot sector from disk, store it in memory from DC00h onwards (in a typical 64K system), and start executing the BIOS code. CP/M is then in charge and the Low Storage area is properly set up.

​The Low Storage Area

 The first 256 bytes of memory play a crucial role in CP/M. The area is initialised by the BIOS during the boot process, and then handed over to the CCP for maintenance. User programs will use specific parts of Low Storage when they need to access disk drives, or change I/O assignments (using the IO Byte).


Low Storage - The First Eight Bytes of Memory

Starting at the bottom of the memory map, the first three bytes of code contain the machine language instruction JMP F200h (or whatever address is the start of the BIOS). Because the Z80 processor starts itself up by beginning to execute whatever it finds at memory location 00, these three bytes are a logical place for the system reset routine. Executing the code causes the BIOS to reinitialise the Low Storage Area and forget about anything.

The following byte is the IO Byte. In good CP/M implementations (many computer vendors were sloppy in using this functionality), you can re-assign the real devices to CP/M virtual devices. For instance, you can assign the keyboard hardware to what CP/M sees as the serial port. This byte serves as a switch between real and virtual devices and is a GREAT idea.

Byte 04h logs the current default drive (in bits 0-3, 0 being A: and 1 being B:, etc) and the current user (in bits 4-7). The user code is normally 0, but through the USER x command, it can be changed.

Bytes 05h-07h contain the machine language instruction JMP E400 (i.e., Jump to BDOS). Calling address 05h is the standard way in which programs use the services of CP/M. For instance, if a program wants to print the character 'A' to the screen:

it loads the value 65 (for A) in the Z80's accumulator,
loads the value 02h (for BDOS service #, print character) in the Z80's C register
and simply does a CALL 0005. CP/M will put the character on the screen, and then return execution to the program.

 Low Storage - the middle part​

Moving further up in the memory map, the restart vectors starting at byte 08h can be initialised by the BIOS or a user program. Each interrupt level of the Z80 has a vector here. If an interrupt occurs, the Z80 will stop with whatever it is doing, and jump execution to one of these vectors depending on the level of the interrupt (0 to 6). Interrupt 7 is reserved for a debugger. If a debugger like DDT is loaded, it'll put the instruction JMP XXXX in bytes 38h-3Ah, where XXXX is the address where the debugger resides. Many earlier computers had a debug button, which when pressed would simply create a hardware interrupt 7 signal to land the user in the debugger. CP/M doesn't use interrupts itself, so these vectors can be used for whatever interrupt-generating hardware is built into the system.

The area from 40h upwards is used as a variable scratchpad by the BIOS for normally undocumented purposes. The area above, starting at 50h, is unused by CP/M 2.2 (not true for MP/M). It is a great place to store small programs that can remain resident in the computer.

Low Storage - the upper part​

The File Control Block resides in bytes 60h-80h. This is a group of parameters that needs to be filled in before a program calls a BDOS function that has anything to do with disk drives. In the FCB, the program stores the drive it wants to look at, its file name, and any other relevant details. The FCB is explained further down in this text.

Above 80h is where the CCP puts the DMA buffer.  It also uses that memory for the arguments that are parsed for each command as it is executed.  This is called the command tail.  The first byte is the number of characters, without the final carriage return, followed by the upper-case representation of all the arguments

​BDOS Calls

All of the services of CP/M are accessed through the BDOS. For instance, if a program wants to print the character 'A' to the screen:

        MVI     A,65
        MVI     C,02H
        CALL    0005h
  • it loads the value 65 (for A) in the Z80's accumulator,
  • loads the value 02h (for BDOS service #, print character) in the Z80's C register
  • and simply does a CALL 0005 to execute the BDOS request
  • CP/M will put the character on the screen, and then return execution to the program.

CP/M facilities that are available for access by transient programs fall into two general categories: simple device I/O and disk file I/O. 

The simple device operations are:
  • read a console character
  • write a console character
  • read a sequential character
  • write a sequential character
  • get or set I/O status
  • print console buffer
  • interrogate console ready

The following FDOS operations perform disk I/O:
  • disk system reset
  • drive selection
  • file creation
  • file close
  • directory search
  • file delete
  • file rename
  • random or sequential read
  • random or sequential write
  • interrogate available disks
  • interrogate selected disk
  • set DMA address
  • set/reset file indicators.

As mentioned above, access to the BDOS functions is accomplished by passing a function number and information address through the primary point at location BOOT+0005H. 

In general, the function number is passed in register C with the information address in the double byte pair DE. Single byte values are returned in register A, with double byte values returned in HL, a zero value is returned when the function number is out of range. For reasons of compatibility, register A = L and register B = H upon return in all cases. Note that the register passing conventions of CP/M agree with those of the Intel PL/M systems programming language. 

CP/M functions and their numbers are listed below.

Number

Function

Number

Function

0

System Reset

19

Delete File

1

Console Input

20

Read Sequential

2

Console Output

21

Write Sequential

3

Reader Input

22

Make File

4

Punch Output

23

Rename File

5

List Output

24

Return Login Vector

6

Direct Console I/O

25

Return Current Disk

7

Get I/O Byte

26

Set DMA Address

8

Set I/O Byte

27

Get Addr(Alloc)

9

Print String

28

Write Protect Disk

10

Read Console Buffer

29

Get R/O Vector

11

Get Console Status

30

Set File Attributes

12

Return Version Number

31

Get Addr(Disk Parms)

13

Reset Disk System

32

Set/Get User Code

14

Select Disk

33

Read Random

15

Open File

34

Write Random

16

Close File

35

Compute File Size

17

Search for First

36

Set Random Record

18

Search for Next

37

Reset Drive



40

Write Random with Zero Fill


References:

This article uses material from the Wikipedia article CP/M which is released under the Creative Commons Attribution-Share-Alike License 4.0,

Obsolescence Guaranteed CP/M Internals

CP/M 2.2 Manual Chapter 5

Comments

Popular posts from this blog

Still Working on the New Home Page

A New Home Page is in the works!

Old Quizzes that have been taken down