# Using CP/M

## CP/M Quick Reference 

### Control key commands

<table border="1" cellspacing="0" id="bkmrk-ctrl-%2B-c-restarts-cp" style="width: 100%; border-collapse: collapse; border-width: 0px; border-spacing: 0px; height: 543.6px; border-style: solid;"><colgroup width="71"><col style="width: 39.8559%;"></col><col style="width: 60.1515%;"></col></colgroup> <colgroup width="886"></colgroup> <colgroup width="1196"></colgroup> <colgroup width="810"></colgroup><tbody><tr style="height: 37.7833px;"><td class="align-right" data-sheets-value="{ "1": 2, "2": "CTRL + C"}" height="17" style="padding: 10px; height: 37.7833px; border-width: 0px; width: 27.5%;">`CTRL + C`</td><td align="left" data-sheets-value="{ "1": 2, "2": "Restarts CP\/M by initiating a warm start"}" style="padding: 10px; height: 37.7833px; border-width: 0px; width: 72.5%;">Restarts CP/M by initiating a warm start</td></tr><tr style="height: 54.1833px;"><td class="align-right" data-sheets-value="{ "1": 2, "2": "CTRL + E"}" height="17" style="padding: 10px; height: 54.1833px; border-width: 0px; width: 27.5%;">`CTRL + E`</td><td align="left" data-sheets-value="{ "1": 2, "2": "Forces the cursor to the next line of the screen for continued command line input."}" style="padding: 10px; height: 54.1833px; border-width: 0px; width: 72.5%;">Forces the cursor to the next line of the screen for continued command line input.</td></tr><tr style="height: 37.7833px;"><td class="align-right" data-sheets-value="{ "1": 2, "2": "CTRL + H"}" height="17" style="padding: 10px; height: 37.7833px; border-width: 0px; width: 27.5%;">`CTRL + H`</td><td align="left" data-sheets-value="{ "1": 2, "2": "Moves the cursor one space to the left in the same manner as the BS key."}" style="padding: 10px; height: 37.7833px; border-width: 0px; width: 72.5%;">Moves the cursor one space to the left in the same manner as the BS key.</td></tr><tr style="height: 37.7833px;"><td class="align-right" data-sheets-value="{ "1": 2, "2": "CTRL + I"}" height="17" style="padding: 10px; height: 37.7833px; border-width: 0px; width: 27.5%;">`CTRL + I`</td><td align="left" data-sheets-value="{ "1": 2, "2": "Inputs a tab code (same as the TAB key)"}" style="padding: 10px; height: 37.7833px; border-width: 0px; width: 72.5%;">Inputs a tab code (same as the TAB key)</td></tr><tr style="height: 121.383px;"><td class="align-right" data-sheets-value="{ "1": 2, "2": "CTRL + P"}" height="17" style="padding: 10px; height: 121.383px; border-width: 0px; width: 27.5%;">`CTRL + P`</td><td align="left" data-sheets-value="{ "1": 2, "2": "Turns on or off the printer echo function. When the printer echo function is turned on"}" style="padding: 10px; height: 121.383px; border-width: 0px; width: 72.5%;">Turns on or off the printer echo function. When the printer echo function is turned on, data displayed on the LCD screen is output to the printer (or other device assigned to LST:) each time the RETURN key is pressed. The printer echo function is turned on by pressing CTRL + P once and is turned off when CTRL + P is pressed a second time. (Make sure the printer is connected properly before using this command.)  
</td></tr><tr style="height: 37.7833px;"><td class="align-right" data-sheets-value="{ "1": 2, "2": "CTRL + R"}" height="17" style="padding: 10px; height: 37.7833px; border-width: 0px; width: 27.5%;">`CTRL + R`</td><td align="left" data-sheets-value="{ "1": 2, "2": "Redisplays the contents of the current command line."}" style="padding: 10px; height: 37.7833px; border-width: 0px; width: 72.5%;">Redisplays the contents of the current command line.</td></tr><tr style="height: 54.2667px;"><td class="align-right" data-sheets-value="{ "1": 2, "2": "CTRL + S"}" height="17" style="padding: 10px; height: 54.2667px; border-width: 0px; width: 27.5%;">`CTRL + S`</td><td align="left" data-sheets-value="{ "1": 2, "2": "Momentarily stops processing currently being performed (same as the PAUSE key). Processing can be resumed by inputting this command again."}" style="padding: 10px; height: 54.2667px; border-width: 0px; width: 72.5%;">Momentarily stops processing currently being performed (same as the PAUSE key). Processing can be resumed by inputting this command again.</td></tr><tr style="height: 54.2667px;"><td class="align-right" data-sheets-value="{ "1": 2, "2": "CTRL + U"}" height="17" style="padding: 10px; height: 54.2667px; border-width: 0px; width: 27.5%;">`CTRL + U`</td><td align="left" data-sheets-value="{ "1": 2, "2": "Cancels the current command line and moves the cursor to the next line on the screen for input of a different command."}" style="padding: 10px; height: 54.2667px; border-width: 0px; width: 72.5%;">Cancels the current command line and moves the cursor to the next line on the screen for input of a different command.</td></tr><tr style="height: 54.1833px;"><td class="align-right" data-sheets-value="{ "1": 2, "2": "CTRL + X"}" height="17" style="padding: 10px; height: 54.1833px; border-width: 0px; width: 27.5%;">`CTRL + X`</td><td align="left" data-sheets-value="{ "1": 2, "2": "Erases the current command line and moves the cursor back to the beginning of the line."}" style="padding: 10px; height: 54.1833px; border-width: 0px; width: 72.5%;">Erases the current command line and moves the cursor back to the beginning of the line.</td></tr><tr style="height: 54.1833px;"><td class="align-right" data-sheets-value="{ "1": 2, "2": "CTRL + Z"}" height="17" style="padding: 10px; height: 54.1833px; border-width: 0px; width: 27.5%;">`CTRL + Z`</td><td align="left" data-sheets-value="{ "1": 2, "2": "Terminates input from the keyboard (used in combination with certain CP\/M transient commands)."}" style="padding: 10px; height: 54.1833px; border-width: 0px; width: 72.5%;">Terminates input from the keyboard (used in combination with certain CP/M transient commands).</td></tr></tbody></table>

### Commands

<p class="callout info">`d:` - A drive letter  
`filematch` - an exact filename, or a wildcard match for files.  
Items such as `[item]` are optional, ones such as `<item>` are compulsory.</p>

`DIR [d:][filematch]`  
Display a list of files in the specified, or current drive.

`ERA [d:]<filematch>`  
Erase specified files. filematch \*.\* can be used to match all files in the current drive.

`REN [d:]newname.ext = oldname.ext`  
Change the name of a disk file from oldname.ext to newname.ext

`SAVE n [d:]filename.ext`  
Save the specified n number of pages of the transient program area to disk under the specified file name.

`TYPE [d:]filename.ext`  
Display the contents of the specified file on the display screen. Display is only meaningful if the file contents was ASCII text.

`USER n`  
Switches to the specified user area in a disk drive.  
A user area is a physical area on a disk which has its own directory and which is managed separately from the rest of the disk. Data cannot be read from or written to any user area other than that in which CP/M is currently operating. A disk can be divided up into a maximum of 16 user areas.

#### PIP

Copy `FOO.TXT` to console (print).

```
C:PIP CON:=A:FOO.TXT
```

Write to file from terminal (`CTRL-Z` to `EOF`, `CTRL-M+J` for new line):

```
C:PIP FOO.TXT=CON:
```

Copy file from current drive to `A`:

```
C:PIP A:UPLOAD.COM=UPLOAD.COM
```

#### ED

- http://www.gaby.de/cpm/manuals/archive/cpm22htm/ch2.htm

##### Commands mode  


<table border="1" id="bkmrk-%23a-load-all-lines-fr" style="border-collapse: collapse; width: 100%; height: 446.25px; border-width: 0px;"><colgroup><col style="width: 25.5142%;"></col><col style="width: 74.5362%;"></col></colgroup><tbody><tr style="height: 45.8667px;"><td style="height: 45.8667px; border-width: 0px;">`#a`  
</td><td style="height: 45.8667px; border-width: 0px;">load all lines from file (given as arg to the command) to memory</td></tr><tr style="height: 62.8px;"><td style="height: 62.8px; border-width: 0px;">`10a`  
</td><td style="height: 62.8px; border-width: 0px;">load 10 lines from file  
</td></tr><tr style="height: 62.8px;"><td style="height: 62.8px; border-width: 0px;">`b#t`  
</td><td style="height: 62.8px; border-width: 0px;">go to beginning, print all lines</td></tr><tr style="height: 29.4667px;"><td style="height: 29.4667px; border-width: 0px;">`b`  
</td><td style="height: 29.4667px; border-width: 0px;">go to beginning  
</td></tr><tr style="height: 96.4px;"><td style="height: 96.4px; border-width: 0px;">*`n`* or `-<em>n</em>`  
</td><td style="height: 96.4px; border-width: 0px;">move *n* lines ahead/behind and print</td></tr><tr style="height: 29.7833px;"><td style="height: 29.7833px; border-width: 0px;">`t`  
</td><td style="height: 29.7833px; border-width: 0px;">print current line</td></tr><tr style="height: 29.7833px;"><td style="height: 29.7833px; border-width: 0px;">`k`  
</td><td style="height: 29.7833px; border-width: 0px;">kill current line</td></tr><tr style="height: 29.7833px;"><td style="height: 29.7833px; border-width: 0px;">`i`  
</td><td style="height: 29.7833px; border-width: 0px;">insert before current line</td></tr><tr style="height: 29.7833px;"><td style="height: 29.7833px; border-width: 0px;">`e`  
</td><td style="height: 29.7833px; border-width: 0px;">save and exit</td></tr><tr style="height: 29.7833px;"><td style="height: 29.7833px; border-width: 0px;">`o`  
</td><td style="height: 29.7833px; border-width: 0px;">discard changes  
</td></tr></tbody></table>

##### In insert mode

<table border="1" id="bkmrk-ctrl-%2B-h-backspace-o" style="border-collapse: collapse; width: 100%; border-width: 0px;"><colgroup><col style="width: 34.5759%;"></col><col style="width: 65.4744%;"></col></colgroup><tbody><tr><td style="border-width: 0px;">`CTRL + H`  
</td><td style="border-width: 0px;">backspace one char</td></tr><tr><td style="border-width: 0px;">`CTRL + Z`</td><td style="border-width: 0px;">exit insert mode</td></tr></tbody></table>

#### ASM

- [http://www.gaby.de/cpm/manuals/archive/cpm22htm/ch3.htm#Section\_3.6](http://www.gaby.de/cpm/manuals/archive/cpm22htm/ch3.htm#Section_3.6)

Compile and run `example` program:

```
c:asm example
c:load example
example
```

Show compiled program listing:

```
type example.prn
```

##### Compiler output line markers

<table border="1" id="bkmrk-s-syntax-error-l-bad" style="border-collapse: collapse; width: 100%; height: 145.383px; border-width: 0px;"><colgroup><col style="width: 17.1162%;"></col><col style="width: 82.9398%;"></col></colgroup><tbody><tr style="height: 29.1167px;"><td style="height: 29.1167px; border-width: 0px;">`S`  
</td><td style="height: 29.1167px; border-width: 0px;">syntax error</td></tr><tr style="height: 29.4667px;"><td style="height: 29.4667px; border-width: 0px;">`L`  
</td><td style="height: 29.4667px; border-width: 0px;">bad label

</td></tr><tr style="height: 28.9333px;"><td style="border-width: 0px; height: 28.9333px;">`E`  
</td><td style="border-width: 0px; height: 28.9333px;">bad expression

</td></tr><tr style="height: 28.9333px;"><td style="border-width: 0px; height: 28.9333px;">`R`  
</td><td style="border-width: 0px; height: 28.9333px;">bad register

</td></tr><tr style="height: 28.9333px;"><td style="border-width: 0px; height: 28.9333px;">`Y`  
</td><td style="border-width: 0px; height: 28.9333px;">bad value

</td></tr></tbody></table>

## API

- [https://www.seasip.info/Cpm/bdos.html](https://www.seasip.info/Cpm/bdos.html)
- [https://obsolescence.wixsite.com/obsolescence/cpm-internals](https://obsolescence.wixsite.com/obsolescence/cpm-internals)
- [http://www.gaby.de/cpm/manuals/archive/cpm22htm/ch5.htm](http://www.gaby.de/cpm/manuals/archive/cpm22htm/ch5.htm)

Entry point: `05h` (`CALL 05h`).

### Memory map

[![cpm-memmap.gif](https://wiki.hexadust.net/uploads/images/gallery/2025-10/9ccwKynSuvVOpJLT-cpm-memmap.gif)](https://wiki.hexadust.net/uploads/images/gallery/2025-10/9ccwKynSuvVOpJLT-cpm-memmap.gif)

[![cpm-lowstorage.gif](https://wiki.hexadust.net/uploads/images/gallery/2025-10/3OVsmkkZ8bd2zPpm-cpm-lowstorage.gif)](https://wiki.hexadust.net/uploads/images/gallery/2025-10/3OVsmkkZ8bd2zPpm-cpm-lowstorage.gif)

#### File Control Block

[![cpm-file_control_block.gif](https://wiki.hexadust.net/uploads/images/gallery/2025-10/vA4ZzXkaFhsHv6ps-cpm-file-control-block.gif)](https://wiki.hexadust.net/uploads/images/gallery/2025-10/vA4ZzXkaFhsHv6ps-cpm-file-control-block.gif)

FCB start: `5Ch` (not `60h` as in the picture).

<table border="1" id="bkmrk-field-definition-dr-" style="width: 100%;"><tbody><tr><th style="width: 9.17358%;">**Address**</th><th style="width: 9.05445%;">**Field**</th><th style="width: 81.7283%;">**Definition**</th></tr><tr><td style="width: 9.17358%;">`FCB+00h`</td><td style="width: 9.05445%;">dr</td><td style="width: 81.7283%;">drive code (0-16)  
0 = use default drive for file  
1 = auto disk select drive A,  
2 = auto disk select drive B,  
 ...  
16 = auto disk select drive P.</td></tr><tr><td style="width: 9.17358%;">`FCB+01h`</td><td style="width: 9.05445%;">f1...f8</td><td style="width: 81.7283%;">contain the filename in ASCII upper-case, with high bit = 0</td></tr><tr><td style="width: 9.17358%;">`FCB+09h`</td><td style="width: 9.05445%;">t1,t2,t3</td><td style="width: 81.7283%;">contain the filetype in ASCII upper-case, with high bit = 0. t1', t2', and t3' denote the bit of these positions,  
t1' = 1 = Read-Only file,  
t2' = 1 = SYS file, no DIR list</td></tr><tr><td style="width: 9.17358%;">`FCB+0Ch`</td><td style="width: 9.05445%;">ex</td><td style="width: 81.7283%;">contains the current extent number, normally set to 00 by the user, but in range 0-31 during file I/O</td></tr><tr><td style="width: 9.17358%;">`FCB+0Dh`</td><td style="width: 9.05445%;">s1</td><td style="width: 81.7283%;">reserved for internal system use</td></tr><tr><td style="width: 9.17358%;">`FCB+0Eh`</td><td style="width: 9.05445%;">s2</td><td style="width: 81.7283%;">reserved for internal system use, set to zero on call to [OPEN](http://www.gaby.de/cpm/manuals/archive/cpm22htm/ch5.htm#Function_15), [MAKE](http://www.gaby.de/cpm/manuals/archive/cpm22htm/ch5.htm#Function_22), [SEARCH](http://www.gaby.de/cpm/manuals/archive/cpm22htm/ch5.htm#Function_18)</td></tr><tr><td style="width: 9.17358%;">`FCB+0Fh`</td><td style="width: 9.05445%;">rc</td><td style="width: 81.7283%;">record count for extent ex; takes on values from 0-127; Set this to 0 when opening a file</td></tr><tr><td style="width: 9.17358%;">`FCB+10h`</td><td style="width: 9.05445%;">d0...dn</td><td style="width: 81.7283%;">filled in by CP/M; reserved for system use</td></tr><tr><td style="width: 9.17358%;">`FCB+20h`</td><td style="width: 9.05445%;">cr</td><td style="width: 81.7283%;">current record to read or write in a sequential file operation; normally set to zero by user after opening file</td></tr><tr><td style="width: 9.17358%;">`FCB+21h`</td><td style="width: 9.05445%;">r0,r1,r2</td><td style="width: 81.7283%;">optional random record number in the range 0- 65535, with overflow to r2, r0, r1 constitute a 16-bit value with low byte r0, and high byte r1</td></tr></tbody></table>

File Buffer start: `80h` (128 bytes long).

#### Notes

- When running command in CP/M with file path as first argument the drive number and file name are set in default FCB automatically before program runs.
- When two arguments are given another drive number and file name are set starting from absolute `6Ch`. It will be overwritten by file operations.
- If no arguments are given the FCB file name is all blank/space characters (` `)
- Command line arguments are also available from `80h` (File Buffer).

#### Useful constants:

```
FCB       EQU  5Ch     ; File Control Block
FCBFNAME  EQU  FCB+01h ; FCB File Name
FCBEX     EQU  FCB+0Ch ; FCB Excent Number
FCBRC     EQU  FCB+0Fh ; FCB Record Count
FCBCR     EQU  FCB+20h ; FCB Current Record
FB        EQU  80h     ; File Buffer
```

### CP/M procedures

<table border="1" id="bkmrk-functionnumber-funct"><tbody><tr><th colspan="2">**Function**  
**Number**</th><th rowspan="2">**Function**  
**Name**</th><th rowspan="2">**Input**</th><th rowspan="2">**Output**</th></tr><tr><th>**Dec**</th><th>**Hex**</th></tr><tr><td>0</td><td>0</td><td>System Reset</td><td>none</td><td>none</td></tr><tr><td>1</td><td>1</td><td>Console Input</td><td>none</td><td>A = ASCII char</td></tr><tr><td>2</td><td>2</td><td>Console Output</td><td>E = char</td><td>none</td></tr><tr><td>3</td><td>3</td><td>Reader Input</td><td>none</td><td>A = ASCII char</td></tr><tr><td>4</td><td>4</td><td>Punch Output</td><td>E = char</td><td>none</td></tr><tr><td>5</td><td>5</td><td>List Output</td><td>E = char</td><td>none</td></tr><tr><td rowspan="3">6</td><td rowspan="3">6</td><td rowspan="3">Direct Console I/O</td><td>E = 0FFH (input)</td><td>A = char</td></tr><tr><td>E = 0FEH (status)</td><td>A = status</td></tr><tr><td>E = char</td><td>none</td></tr><tr><td>7</td><td>7</td><td>Get I/O Byte</td><td>none</td><td>A = I/O byte value</td></tr><tr><td>8</td><td>8</td><td>Set I/O Byte</td><td>E = I/O byte</td><td>none</td></tr><tr><td>9</td><td>9</td><td>Print String</td><td>DE = Buffer Address</td><td>none</td></tr><tr><td>10</td><td>A</td><td>Read Console String</td><td>DE = Buffer</td><td>Console characters in Buffer</td></tr><tr><td>11</td><td>B</td><td>Get Console Status</td><td>none</td><td>A = 00/non zero</td></tr><tr><td>12</td><td>C</td><td>Return Version #</td><td>none</td><td>HL = Version #</td></tr><tr><td>13</td><td>D</td><td>Reset Disk System</td><td>none</td><td>none</td></tr><tr><td>14</td><td>E</td><td>Select Disk</td><td>E = Disk #</td><td>none</td></tr><tr><td>15</td><td>F</td><td>Open File</td><td>DE = FCB address</td><td>A = FF if not found</td></tr><tr><td>16</td><td>10</td><td>Close File</td><td>DE = FCB address</td><td>A = FF if not found</td></tr><tr><td>17</td><td>11</td><td>Search For First</td><td>DE = FCB address</td><td>A = Directory Code</td></tr><tr><td>18</td><td>12</td><td>Search For Next</td><td>none</td><td>A = Directory Code</td></tr><tr><td>19</td><td>13</td><td>Delete File</td><td>DE = FCB address</td><td>A = none</td></tr><tr><td>20</td><td>14</td><td>Read Sequential</td><td>DE = FCB address</td><td>A = Error Code</td></tr><tr><td>21</td><td>15</td><td>Write Sequential</td><td>DE = FCB Address</td><td>A = Error Code</td></tr><tr><td>22</td><td>16</td><td>Make File</td><td>DE = FCB address</td><td>A = FF if no DIR Space</td></tr><tr><td>23</td><td>17</td><td>Rename File</td><td>DE = FCB address</td><td>A = FF if not found</td></tr><tr><td>24</td><td>18</td><td>Return Login Vector</td><td>none</td><td>HL = Login Vector\*</td></tr><tr><td>25</td><td>19</td><td>Return Current Disk</td><td>none</td><td>A = Current Disk Number</td></tr><tr><td>26</td><td>1A</td><td>Set DMA Address</td><td>DE = DMA address</td><td>none</td></tr><tr><td>27</td><td>1B</td><td>Get ADDR (ALLOC)</td><td>none</td><td>HL = ALLOC address\*</td></tr><tr><td>28</td><td>1C</td><td>Write Protect Disk</td><td>none</td><td>none</td></tr><tr><td>29</td><td>1D</td><td>Get Read/only Vector</td><td>none</td><td>HL = ALLOC address\*</td></tr><tr><td>30</td><td>1E</td><td>Set File Attributes</td><td>DE = FCB address</td><td>A = none</td></tr><tr><td>31</td><td>1F</td><td>Get ADDR (Disk Parms)</td><td>none</td><td>HL = DPB address</td></tr><tr><td rowspan="2">32</td><td rowspan="2">20</td><td rowspan="2">Set/Get User Code</td><td>E = 0FFH for Get</td><td>A = User Number</td></tr><tr><td>E = 00 to 0FH for Set</td><td>none</td></tr><tr><td>33</td><td>21</td><td>Read Random</td><td>DE = FCB address</td><td>A = Error</td></tr><tr><td>34</td><td>22</td><td>Write Random</td><td>DE = FCB address</td><td>A = Error Code</td></tr><tr><td>35</td><td>23</td><td>Compute File Size</td><td>DE = FCB address</td><td>r0, r1, r2</td></tr><tr><td>36</td><td>24</td><td>Set Random Record</td><td>DE = FCB address</td><td>r0, r1, r2</td></tr><tr><td>37</td><td>25</td><td>Reset Drive</td><td>DE = Drive Vector</td><td>A = 0</td></tr><tr><td>38</td><td>26</td><td>Access Drive</td><td align="center" colspan="2">not supported</td></tr><tr><td>39</td><td>27</td><td>Free Drive</td><td align="center" colspan="2">not supported</td></tr><tr><td>40</td><td>28</td><td>Write Random w/Fill</td><td>DE = FCB</td><td>A = error code</td></tr></tbody></table>

#### Reading file

Given file name in the arguments one can first check if argument was given by checking for space character in `FCBFNAME` (`FBC + 01h`).

If file name is filled we can open the file:

```
     XOR  A
     LD   (FCBEX),A   ; select 0'th extent
     LD   (FCBRC),A   ; clear
     LD   (FCBCR),A   ; clear
     LD   C,FOPE      ; open file
     LD   DE,FCB      ; use default FCB
     CALL BDOS
```

Set *EX*, *RC* and *CR* to `0` and call File Open (`0Fh`). *A* will contain `FFh` if file was not found.

Next file can be read:

```
     XOR  A
     LD   (FCBCR),A ; start from beginning
     LD   C,FRED    ; read file
     LD   DE,FCB    ; use default FCB
     CALL BDOS
```

Set *CR* to `0` to start reading from beginning of the file and call Read Sequential (`14h`). *A* is set to `0` if read was successful.

128 bytes was read to *FB* (starting at `80h`). *CR* (and *EX*) will be automatically incremented, so calling Read Sequential again will read next 128 byte of the file to *FB*.