Section 5
CP/M 2 System Interface

Table of Contents

5.1 Introduction
5.2 Operating System Call Conventions
5.3 A Sample File-to-File Copy Program
5.4 A Sample File Dump Utility
5.5 A Sample Random Access Program
5.6 System Function Summary


5-1 CP/M Filetypes
5-2 File Control Block Fields
5-3 Edit Control Characters


5-1 CP/M Memory Organization
5-2 File Control Block Format

5.1 Introduction

This chapter describes CP/M (release 2) system organization including the structure of memory and system entry points. This section provides the information you need to write programs that operate under CP/M and that use the peripheral and disk I/O facilities of the system.

CP/M is logically divided into four parts, called the Basic Input/Output System (BIOS), the Basic Disk Operating System (BDOS), the Console Command Processor (CCP), and the Transient Program Area (TPA). The BIOS is a hardware-dependent module that defines the exact low level interface with a particular computer system that is necessary for peripheral device I/O. Although a standard BIOS is supplied by Digital Research, explicit instructions are provided for field reconfiguration of the BIOS to match nearly any hardware environment, see Section 6.

The BIOS and BDOS are logically combined into a single module with a common entry point and referred to as the FDOS. The CCP is a distinct program that uses the FDOS to provide a human-oriented interface with the information that is cataloged on the back-up storage device. The TPA is an area of memory, not used by the FDOS and CCP, where various nonresident operating system commands and user programs are executed. The lower portion of memory is reserved for system information and is detailed in later sections. Memory organization of the CP/M system is shown in Figure 5-1.

Figure 5-1. CP/M Memory Organization

The exact memory addresses corresponding to BOOT, TBASE, CBASE, and FBASE vary from version to version and are described fully in Section 6. All standard CP/M versions assume BOOT=0000H, which is the base of random access memory. The machine code found at location BOOT performs a system warm start, which loads and initializes the programs and variables necessary to return control to the CCP. Thus, transient programs need only jump to location BOOT to return control to CP/M at the command level. further, the standard versions assume TBASE=BOOT+0100H, which is normally location 0100H. The principal entry point to the FDOS is at location BOOT+0005H (normally 0005H) where a jump to BASE is found. The address field at BOOT+0006H (normally 006H) contains the value of FBASE and can be used to determine the size of available memory, assuming that the CCP is being overlaid by a transient program.

Transient programs are loaded into the TPA and executed as follows. The operator communicates with the CCP by typing command lines following each prompt. Each command line takes one of the following forms:

        command file1
        command file1 file2

where command is either a built-in function, such as DIR or TYPE, or the name of a transient command or program. If the command is a built-in function of CP/M, it is executed immediately. Otherwise, the CCP searches the currently addressed disk for a file by the name


If the file is found, it is assumed to be a memory image of a program that executes in the TPA and thus implicity originates at TBASE in memory. The CCP loads the COM file from the disk into memory starting at TBASE and can extend up to CBASE.

If the command is followed by one or two file specifications, the CCP prepares one or two File Control Block (FCB) names in the system parameter area. These optional FCBs are in the form necessary to access files through the FDOS and are described in Section 5.2.

The transient program receives control from the CCP and begins execution, using the I/O facilities of the FDOS. The transient program is called from the CCP. Thus, it can simply return to the CCP upon completion of its processing, or can Jump to BOOT to pass control back to CP/M. In the first case, the transient program must not use memory above CBASE, while in the latter case, memory up through FBASE-1 can be used.

The transient program can use the CP/M I/O facilities to communicate with the operator's console and peripheral devices, including the disk subsystem. The I/O system is accessed by passing a function number and an information address to CP/M through the FDOS entry point at BOOT+0005H. In the case of a disk read, for example, the transient program sends the number corresponding to a disk read, along with the address of an FCB to the CP/M FDOS. The FDOS, in turn, performs the operation and returns with either a disk read completion indication or an error number indicating that the disk read was unsuccessful.

5.2 Operating System Call Conventions

This section provides detailed information for performing direct operating system calls from user programs. Many of the functions listed below, however, are accessed more simply through the I/O macro library provided with the MAC macro assembler and listed in the Digital Research manual entitled Programmer's Utilities Guide for the CP/M Family of Operating Systems.

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

The following FDOS operations perform disk I/O:

As mentioned above, access to the FDOS 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.


0System Reset19Delete File
1Console Input20Read Sequential
2Console Output21Write Sequential
3Reader Input22Make File
4Punch Output23Rename File
5List Output24Return Login Vector
6Direct Console I/O25Return Current Disk
7Get I/O Byte26Set DMA Address
8Set I/O Byte27Get Addr(Alloc)
9Print String28Write Protect Disk
10Read Console Buffer29Get R/O Vector
11Get Console Status30Set File Attributes
12Return Version Number31Get Addr(Disk Parms)
13Reset Disk System32Set/Get User Code
14Select Disk33Read Random
15Open File34Write Random
16Close File35Compute File Size
17Search for First36Set Random Record
18Search for Next37Reset Drive
40Write Random with Zero Fill

Functions 28 and 32 should be avoided in application programs to maintain upward compatibility with CP/M.

Upon entry to a transient program, the CCP leaves the stack pointer set to an eight-level stack area with the CCP return address pushed onto the stack, leaving seven levels before overflow occurs. Although this stack is usually not used by a transient program (most transients return to the CCP through a jump to location 0000H) it is large enough to make CP/M system calls because the FDOS switches to a local stack at system entry. For example, the assembly-language program segment below reads characters continuously until an asterisk is encountered, at which time control returns to the CCP, assuming a standard CP/M system with BOOT = 0000H.

       ORG 0100H    ;BASE OF TPA
       CPI  '*'     ;END OF PROCESSING?
       RET          ;RETURN TO CCP

CP/M implements a named file structure on each disk, providing a logical organization that allows any particular file to contain any number of records from completely emptv to the full capacity of the drive. Each drive is logically distinct with a disk directory and file data area. The disk filenames are in three parts: the drive select code, the filename (consisting of one to eight nonblank characters), and the filetype (consisting of zero to three nonblank characters). The filetype names the generic category of a particular file, while the filename distinguishes individual files in each category. The filetypes listed in Table 5-1 name a few generic categories that have been established, although they are somewhat arbitrary.


Table 5-1. CP/M Filetypes
ASMAssembler Source
PRNPrinter Listing
HEXHex Machine Code
BASBasic Source File
INTIntermediate Code
COMCommand File
PLIPL/I Source File
RELRelocatable Module
TEXTEX Formatter Source
BAKED Source Backup
SYMSID Symbol File
$$$Temporary File

Source files are treated as a sequence of ASCII characters, where each line of the source file is followed by a carriage return, and line-feed sequence (0DH followed by 0AH). Thus, one 128-byte CP/M record can contain several lines of source text. The end of an ASCII file is denoted by a CTRL-Z character (1AH) or a real end-of-file returned by the CP/M read operation. CTRL-Z characters embedded within machine code files (for example, COM files) are ignored and the end-of-file condition returned by CP/M is used to terminate read operations.

Files in CP/M can be thought of as a sequence of up to 65536 records of 128 bytes each, numbered from 0 through 65535, thus allowing a maximum of 8 megabytes per file. Note, however, that although the records may be considered logically contiguous, they may not be physically contiguous in the disk data area. Internally, all files are divided into 16K byte segments called logical extents, so that counters are easily maintained as 8-bit values. The division into extents is discussed in the paragraphs that follow: however, they are not particularly significant for the programmer, because each extent is automatically accessed in both sequential and random access modes.

In the file operations starting with Function 15, DE usually addresses a FCB. Transient programs often use the default FCB area reserved by CP/M at location BOOT+005CH (normally 005CH) for simple file operations. The basic unit of file information is a 128-byte record used for all file operations. Thus, a default location for disk I/O is provided by CP/M at location BOOT+0080H (normally 0080H) which is the initial default DMA address. See Function 26.

All directory operations take place in a reserved area that does not affect write buffers as was the case in release 1, with the exception of Search First and Search Next, where compatibility is required.

The FCB data area consists of a sequence of 33 bytes for sequential access and a series of 36 bytes in the case when the file is accessed randomly. The default FCB, normally located at 005CH, can be used for random access files, because the three bytes starting at BOOT+007DH are available for this purpose. Figure 5-2 shows the FCB format with the following fields.


Figure 5-2. File Control Block Format
DRF1 - F8T1 - T3EXS1S2RC D0 - DNCRR0R1R2
000102...0809 10111213141516... 3132333435

The following table lists and describes each of the fields in the File Control Block figure.


Table 5-2. File Control Block Fields
drdrive 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.
f1...f8contain the filename in ASCII upper-case, with high bit = 0
t1,t2,t3contain 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
excontains the current extent number, normally set to 00 by the user, but in range 0-31 during file I/O
s1reserved for internal system use
s2reserved for internal system use, set to zero on call to OPEN, MAKE, SEARCH
rcrecord count for extent ex; takes on values from 0-127
d0...dnfilled in by CP/M; reserved for system use
crcurrent record to read or write in a sequential file operation; normally set to zero by user
r0,r1,r2optional 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

Each file being accessed through CP/M must have a corresponding FCB, which provides the name and allocation information for all subsequent file operations. When accessing files, it is the programmer's responsibility to fill the lower 16 bytes of the FCB and initialize the cr field. Normally, bytes 1 through 11 are set to the ASCII character values for the filename and filetype, while all other fields are zero.

FCBs are stored in a directory area of the disk, and are brought into central memory before the programmer proceeds with file operations (see the OPEN and MAKE functions). The memory copy of the FCB is updated as file operations take place and later recorded permanently on disk at the termination of the file operation, (see the CLOSE command).

The CCP constructs the first 16 bytes of two optional FCBs for a transient by scanning the remainder of the line following the transient name, denoted by file1 and file2 in the prototype command line described above, with unspecified fields set to ASCII blanks. The first FCB is constructed at location BOOT+005CH and can be used as is for subsequent file operations. The second FCB occupies the d0 ... dn portion of the first FCB and must be moved to another area of memory before use. If, for example, the following command line is typed:


the file PROGNAME.COM is loaded into the TPA, and the default FCB at BOOT+005CH is initialized to drive code 2, filename X, and filetype ZOT. The second drive code takes the default value 0, which is placed at BOOT+006CH, with the filename Y placed into location BOOT+006DH and filetype ZAP located 8 bytes later at BOOT+0075H. All remaining fields through cr are set to zero. Note again that it is the programmer's responsibility to move this second filename and filetype to another area, usually a separate file control block, before opening the file that begins at BOOT+005CH, because the open operation overwrites the second name and type.

If no filenames are specified in the original command, the fields beginning at BOOT+005DH and BOOT+006DH contain blanks. In all cases, the CCP translates lower-case alphabetics to upper-case to be consistent with the CP/M file naming conventions.

As an added convenience, the default buffer area at location BOOT+0080H is initialized to the command line tail typed by the operator following the program name. The first position contains the number of characters, with the characters themselves following the character count. Given the above command line, the area beginning at BOOT+0080H is initialized as follows:


+00+01+02+03+04+05+06+07+08+09 +0A+0B+0C+0D+0E
0EH' ''B'':''X''.''Z''O''T' ' ''Y''.''Z''A''P'

where the characters are translated to upper-case ASCII with uninitialized memory following the last valid character. Again, it is the responsibility of the programmer to extract the information from this buffer before any file operations are performed, unless the default DMA address is explicitly changed.

Individual functions are described in detail in the pages that follow.

Function 0: System Reset

The System Reset function returns control to the CP/M operating system at the CCP level. The CCP reinitializes the disk subsystem by selecting and logging-in disk drive A. This function has exactly the same effect as a jump to location BOOT.

Function 1: Console Input
ReturnAASCII Character

The Console Input function reads the next console character to register A. Graphic characters, along with carriage return, line- feed, and back space (CTRL-H) are echoed to the console. Tab characters, CTRL-I, move the cursor to the next tab stop. A check is made for start/stop scroll, CTRL-S, and start/stop printer echo, CTRL-P. The FDOS does not return to the calling program until a character has been typed, thus suspending execution if a character is not ready.

Function 2: Console Output
EASCII Character

The ASCII character from register E is sent to the console device. As in Function 1, tabs are expanded and checks are made for start/stop scroll and printer echo.

Function 3: Reader Input
ReturnAASCII Character

The Reader Input function reads the next character from the logical reader into register A. See the IOBYTE definition in Section 6. Control does not return until the character has been read.

Function 4: Punch Output
EASCII Character

The Punch Output function sends the character from register E to the logical punch device.

Function 5: List Output
EASCII Character

The List Output function sends the ASCII character in register E to the logical listing device.

Function 6: Direct Console I/O
E0FFH (input) or
char (output)
ReturnAchar or status

Direct Console I/O is supported under CP/M for those specialized applications where basic console input and output are required. Use of this function should, in general, be avoided since it bypasses all of the CP/M normal control character functions (for example, CTRL-S and CTRL-P). Programs that perform direct I/O through the BIOS under previous releases of CP/M, however, should be changed to use direct I/O under BDOS so that they can be fully supported under future releases of MP/M and CP/M.

Upon entry to Function 6, register E either contains hexadecimal FF, denoting a console input request, or an ASCII character. If the input value is FF, Function 6 returns A = 00 if no character is ready, otherwise A contains the next console input character.

If the input value in E is not FF, Function 6 assumes that E contains a valid ASCII character that is sent to the console.

Function 6 must not be used in conjunction with other console I/O functions.

Function 7: Get I/O Byte
ReturnAI/O Byte Value

The Get I/O Byte function returns the current value of IOBYTE in register A. See Section 6 for IOBYTE definition.

Function 8: Set I/O Byte
EI/O Byte Value

The SET I/O Byte function changes the IOBYTE value to that given in register E.

Function 9: Print String
DEString Address

The Print String function sends the character string stored in memory at the location given by DE to the console device, until a $ is encountered in the string. Tabs are expanded as in Function 2, and checks are made for start/stop scroll and printer echo.

Function 10: Read Console Buffer
DEBuffer Address
ReturnConsole Characters in Buffer

The Read Buffer function reads a line of edited console input into a buffer addressed by registers DE. Console input is terminated when either input buffer overflows or a carriage return or line-feed is typed. The Read Buffer takes the form:


where mx is the maximum number of characters that the buffer will hold, 1 to 255, and nc is the number of characters read (set by FDOS upon return) followed by the characters read from the console. If nc < mx, then uninitialized positions follow the last character, denoted by ?? in the above figure. A number of control functions, summarized in Table 5-3, are recognized during line editing.

Table 5-3. Edit Control Characters
CharacterEdit Control Function
rub/delremoves and echoes the last character
CTRL-Creboots when at the beginning of line
CTRL-Ecauses physical end of line
CTRL-Hbackspaces one character position
CTRL-J(line-feed) terminates input line
CTRL-M(return) terminates input line
CTRL-Rretypes the current line after new line
CTRL-Uremoves current line
CTRL-Xsame as CTRL-U

The user should also note that certain functions that return the carriage to the leftmost position (for example, CTRL-X) do so only to the column position where the prompt ended. In earlier releases, the carriage returned to the extreme left margin. This convention makes operator data input and line correction more legible.

Function 11: Get Console Status
ReturnAConsole Status

The Console Status function checks to see if a character has been typed at the console. If a character is ready, the value 0FFH is returned in register A. Otherwise a 00H value is returned.

Function 12: Return Version Number
ReturnHLVersion Number

Function 12 provides information that allows version independent programming. A two-byte value is returned, with H = 00 designating the CP/M release (H = 01 for MP/M) and L = 00 for all releases previous to 2.0. CP/M 2.0 returns a hexadecimal 20 in register L, with subsequent version 2 releases in the hexadecimal range 21, 22, through 2F. Using Function 12, for example, the user can write application programs that provide both sequential and random access functions.

Function 13: Reset Disk System

The Reset Disk function is used to programmatically restore the file system to a reset state where all disks are set to Read-Write. See functions 28 and 29, only disk drive A is selected, and the default DMA address is reset to BOOT+0080H. This function can be used, for example, by an application program that requires a disk change without a system reboot.

Function 14: Select Disk
ESelected Disk

The Select Disk function designates the disk drive named in register E as the default disk for subsequent file operations, with E = 0 for drive A, 1 for drive B, and so on through 15, corresponding to drive P in a full 16 drive system. The drive is placed in an on-line status, which activates its directory until the next cold start, warm start, or disk system reset operation. If the disk medium is changed while it is on-line, the drive automatically goes to a Read-Only status in a standard CP/M environment, see Function 28. FCBs that specify drive code zero (dr = 00H) automatically reference the currently selected default drive. Drive code values between 1 and 16 ignore the selected default drive and directly reference drives A through P.

Function 15: Open File
DEFCB Address
ReturnADirectory Code

The Open File operation is used to activate a file that currently exists in the disk directory for the currently active user number. The FDOS scans the referenced disk directory for a match in positions 1 through 14 of the FCB referenced by DE (byte s1 is automatically zeroed) where an ASCII question mark (3FH) matches any directory character in any of these positions. Normally, no question marks are included, and bytes ex and s2 of the FCB are zero.

If a directory element is matched, the relevant directory information is copied into bytes d0 through dn of FCB, thus allowing access to the files through subsequent read and write operations. The user should note that an existing file must not be accessed until a successful open operation is completed. Upon return, the open function returns a directory code with the value 0 through 3 if the open was successful or 0FFH (255 decimal) if the file cannot be found. If question marks occur in the FCB, the first matching FCB is activated. Note that the current record, (cr) must be zeroed by the program if the file is to be accessed sequentially from the first record.

Function 16: Close File
DEFCB Address
ReturnADirectory Code

The Close File function performs the inverse of the Open File function. Given that the FCB addressed by DE has been previously activated through an open or make function, the close function permanently records the new FCB in the reference disk directory (see functions 15 and 22). The FCB matching process for the close is identical to the open function. The directory code returned for a successful close operation is 0, 1, 2, or 3, while a 0FFH (255 decimal) is returned if the filename cannot be found in the directory. A file need not be closed if only read operations have taken place. If write operations have occurred, the close operation is necessary to record the new directory information permanently.

Function 17: Search for First
DEFCB Address
ReturnADirectory Code

Search First scans the directory for a match with the file given by the FCB addressed by DE. The value 255 (hexadecimal FF) is returned if the file is not found; otherwise, 0, 1, 2, or 3 is returned indicating the file is present. When the file is found, the current DMA address is filled with the record containing the directory entry, and the relative starting position is A * 32 (that is, rotate the A register left 5 bits, or ADD A five times). Although not normally required for application programs, the directory information can be extracted from the buffer at this position.

An ASCII question mark (63 decimal, 3F hexadecimal) in any position from f1 through ex matches the corresponding field of any directory entry on the default or auto-selected disk drive. If the dr field contains an ASCII question mark, the auto disk select function is disabled and the default disk is searched, with the search function returning any matched entry, allocated or free, belonging to any user number. This latter function is not normally used by application programs, but it allows complete flexibility to scan all current directory values. If the dr field is not a question mark, the s2 byte is automatically zeroed.

Function 18: Search for Next
ReturnADirectory Code

The Search Next function is similar to the Search First function, except that the directory scan continues from the last matched entry. Similar to Function 17, Function 18 returns the decimal value 255 in A when no more directory items match.

Function 19: Delete File
DEFCB Address
ReturnADirectory Code

The Delete File function removes files that match the FCB addressed by DE. The filename and type may contain ambiguous references (that is, question marks in various positions), but the drive select code cannot be ambiguous, as in the Search and Search Next functions.

Function 19 returns a decimal 255 if the referenced file or files cannot be found; otherwise, a value in the range 0 to 3 returned.

Function 20: Read Sequential
DEFCB Address
ReturnADirectory Code

Given that the FCB addressed by DE has been activated through an Open or Make function, the Read Sequential function reads the next 128-byte record from the file into memory at the current DMA address. The record is read from position cr of the extent, and the cr field is automatically incremented to the next record position. If the cr field overflows, the next logical extent is automatically opened and the cr field is reset to zero in preparation for the next read operation. The value 00H is returned in the A register if the read operation was successful, while a nonzero value is returned if no data exist at the next record position (for example, end-of-file occurs).

Function 21: Write Sequential
DEFCB Address
ReturnADirectory Code

Given that the FCB addressed by DE has been activated through an Open or Make function, the Write Sequential function writes the 128-byte data record at the current DMA address to the file named by the FCB. The record is placed at position cr of the file, and the cr field is automatically incremented to the next record position. If the cr field overflows, the next logical extent is automatically opened and the cr field is reset to zero in preparation for the next write operation. Write operations can take place into an existing file, in which case newly written records overlay those that already exist in the file. Register A = 00H upon return from a successful write operation, while a nonzero value indicates an unsuccessful write caused by a full disk.

Function 22: Make File
DEFCB Address
ReturnADirectory Code

The Make File operation is similar to the Open File operation except that the FCB must name a file that does not exist in the currently referenced disk directory (that is, the one named explicitly by a nonzero dr code or the default disk if dr is zero). The FDOS creates the file and initializes both the directory and main memory value to an empty file. The programmer must ensure that no duplicate filenames occur, and a preceding delete operation is sufficient if there is any possibility of duplication. Upon return, register A = 0, 1, 2, or 3 if the operation was successful and 0FFH (255 decimal) if no more directory space is available. The Make function has the side effect of activating the FCB and thus a subsequent open is not necessary.

Function 23: Rename File
DEFCB Address
ReturnADirectory Code

The Rename function uses the FCB addressed by DE to change all occurrences of the file named in the first 16 bytes to the file named in the second 16 bytes. The drive code dr at postion 0 is used to select the drive, while the drive code for the new filename at position 16 of the FCB is assumed to be zero. Upon return, register A is set to a value between 0 and 3 if the rename was successful and 0FFH (255 decimal) if the first filename could not be found in the directory scan.

Function 24: Return Log-in Vector
ReturnHLLog-in Vector

The log-in vector value returned by CP/M is a 16-bit value in HL, where the least significant bit of L corresponds to the first drive A and the high-order bit of H corresponds to the sixteenth drive, labeled P. A 0 bit indicates that the drive is not on- line, while a 1 bit marks a drive that is actively on-line as a result of an explicit disk drive selection or an implicit drive select caused by a file operation that specified a nonzero dr field. The user should note that compatibility is maintained with earlier releases, because registers A and L contain the same values upon return.

Function 25: Return Current Disk
ReturnACurrent Disk

Function 25 returns the currently selected default disk number in register A. The disk numbers range from 0 through 15 corresponding to drives A through P.

Function 26: Set DMA Address
DEDMA Address

DMA is an acronym for Direct Memory Address, which is often used in connection with disk controllers that directly access the memory of the mainframe computer to transfer data to and from the disk subsystem. Although many computer systems use non-DMA access (that is, the data is transferred through programmed I/O operations), the DMA address has, in CP/M, come to mean the address at which the 128-byte data record resides before a disk write and after a disk read. Upon cold start, warm start, or disk system reset, the DMA address is automatically set to BOOT+0080H. The Set DMA function can be used to change this default value to address another area of memory where the data records reside. Thus, the DMA address becomes the value specified by DE until it is changed by a subsequent Set DMA function, cold start, warm start, or disk system reset.

Function 27: Get Addr (ALLOC)
ReturnHLALLOC Address

An allocation vector is maintained in main memory for each on-line disk drive. Various system programs use the information provided by the allocation vector to determine the amount of remaining storage (see the STAT program). Function 27 returns the base address of the allocation vector for the currently selected disk drive. However, the allocation information might be invalid if the selected disk has been marked Read-Only. Although this function is not normally used by application programs, additional details of the allocation vector are found in Section 6.

Function 28: Write Protect Disk

The Write Protect Disk function provides temporary write protection for the currently selected disk. Any attempt to write to the disk before the next cold or warm start operation produces the message:

        BDOS ERR on d: R/O

Function 29: Get Read-Only Vector
ReturnHLR/O Vector Value

Function 29 returns a bit vector in register pair HL, which indicates drives that have the temporary Read-Only bit set. As in Function 24, the least significant bit corresponds to drive A, while the most significant bit corresponds to drive P. The R/O bit is set either by an explicit call to Function 28 or by the automatic software mechanisms within CP/M that detect changed disks.

Function 30: Set File Attributes
DEFCB Address>
ReturnADirectory Code

The Set File Attributes function allows programmatic manipulation of permanent indicators attached to files. In particular, the R/O and System attributes (t1' and t2') can be set or reset. The DE pair addresses an unambiguous filename with the appropriate attributes set or reset. Function 30 searches for a match and changes the matched directory entry to contain the selected indicators. Indicators f1' through f4' are not currently used, but may be useful for applications programs, since they are not involved in the matching process during file open and close operations. Indicators f5' through f8' and t3' are reserved for future system expansion.

Function 31: Get Addr (DISKPARMS)
ReturnHLDPB Address

The address of the BIOS resident disk parameter block is returned in HL as a result of this function call. This address can be used for either of two purposes. First, the disk parameter values can be extracted for display and space computation purposes, or transient programs can dynamically change the values of current disk parameters when the disk environment changes, if required. Normally, application programs will not require this facility.

Function 32: Set/Get User Code
E0FFH (get) or
User Code (set)
ReturnACurrent Code (get) or
(no value)

An application program can change or interrogate the currently active user number by calling Function 32. If register E = 0FFH, the value of the current user number is returned in register A, where the value is in the range of 0 to 15. If register E is not 0FFH, the current user number is changed to the value of E, modulo 16.

Function 33: Read Random
DEFCB Address
ReturnAReturn Code

The Read Random function is similar to the sequential file read operation of previous releases, except that the read operation takes place at a particular record number, selected by the 24-bit value constructed from the 3-byte field following the FCB (byte positions r0 at 33, r1 at 34, and r2 at 35). The user should note that the sequence of 24 bits is stored with least significant byte first (r0), middle byte next (r1), and high byte last (r2). CP/M does not reference byte r2, except in computing the size of a file (see Function 35). Byte r2 must be zero, however, since a nonzero value indicates overflow past the end of file.

Thus, the r0, r1 byte pair is treated as a double-byte, or word value, that contains the record to read. This value ranges from 0 to 65535, providing access to any particular record of the 8-megabyte file. To process a file using random access, the base extent (extent 0) must first be opened. Although the base extent might or might not contain any allocated data, this ensures that the file is properly recorded in the directory and is visible in DIR requests. The selected record number is then stored in the random record field (r0, r1), and the BDOS is called to read the record.

Upon return from the call, register A either contains an error code, as listed below, or the value 00, indicating the operation was successful. In the latter case, the current DMA address contains the randomly accessed record. Note that contrary to the sequential read operation, the record number is not advanced. Thus, subsequent random read operations continue to read the same record.

Upon each random read operation, the logical extent and current record values are automatically set. Thus, the file can be sequentially read or written, starting from the current randomly accessed position. However, note that, in this case, the last randomly read record will be reread as one switches from random mode to sequential read and the last record will be rewritten as one switches to a sequential write operation. The user can simply advance the random record position following each random read or write to obtain the effect of sequential I/O operation.

Error codes returned in register A following a random read are listed below.

01reading unwritten data
02(not returned in random mode)
03cannot close current extent
04seek to unwritten extent
05(not returned in read mode)
06seek Past Physical end of disk

Error codes 01 and 04 occur when a random read operation accesses a data block that has not been previously written or an extent that has not been created, which are equivalent conditions. Error code 03 does not normally occur under proper system operation. If it does, it can be cleared by simply rereading or reopening extent zero as long as the disk is not physically write protected. Error code 06 occurs whenever byte r2 is nonzero under the current 2.0 release. Normally, nonzero return codes can be treated as missing data, with zero return codes indicating operation complete.

Function 34: Write Random
DEFCB Address
ReturnAReturn Code

The Write Random operation is initiated similarly to the Read Random call, except that data is written to the disk from the current DMA address. Further, if the disk extent or data block that is the target of the write has not yet been allocated, the allocation is performed before the write operation continues. As in the Read Random operation, the random record number is not changed as a result of the write. The logical extent number and current record positions of the FCB are set to correspond to the random record that is being written. Again, sequential read or write operations can begin following a random write, with the notation that the currently addressed record is either read or rewritten again as the sequential operation begins. You can also simply advance the random record position following each write to get the effect of a sequential write operation. Note that reading or writing the last record of an extent in random mode does not cause an automatic extent switch as it does in sequential mode.

The error codes returned by a random write are identical to the random read operation with the addition of error code 05, which indicates that a new extent cannot be created as a result of directory overflow.

Function 35: Compute File Size
DEFCB Address
ReturnRandom Record Field Set

When computing the size of a file, the DE register pair addresses an FCB in random mode format (bytes r0, r1, and r2 are present). The FCB contains an unambiguous filename that is used in the directory scan. Upon return, the random record bytes contain the virtual file size, which is, in effect, the record address of the record following the end of the file. Following a call to Function 35, if the high record byte r2 is 01, the file contains the maximum record count 65536. Otherwise, bytes r0 and r1 constitute a 16-bit value as before (r0 is the least significant byte), which is the file size.

Data can be appended to the end of an existing file by simply calling Function 35 to set the random record position to the end-of-file and then performing a sequence of random writes starting at the preset record address.

The virtual size of a file corresponds to the physical size when the file is written sequentially. If the file was created in random mode and holes exist in the allocation, the file might contain fewer records than the size indicates. For example, if only the last record of an 8-megabyte file is written in random mode (that is, record number 65535), the virtual size is 65536 records, although only one block of data is actually allocated.

Function 36: Set Random Record
DEFCB Address
ReturnRandom Record Field Set

The Set Random Record function causes the BDOS automatically to produce the random record position from a file that has been read or written sequentially to a particular point. The function can be useful in two ways.

First, it is often necessary initially to read and scan a sequential file to extract the positions of various key fields. As each key is encountered, Function 36 is called to compute the random record position for the data corresponding to this key. If the data unit size is 128 bytes, the resulting record position is placed into a table with the key for later retrieval. After scanning the entire file and tabulating the keys and their record numbers, the user can move instantly to a particular keyed record by performing a random read, using the corresponding random record number that was saved earlier. The scheme is easily generalized for variable record lengths, because the program need only store the buffer-relative byte position along with the key and record number to find the exact starting position of the keyed data at a later time.

A second use of Function 36 occurs when switching from a sequential read or write over to random read or write. A file is sequentially accessed to a particular point in the file, Function 36 is called, which sets the record number, and subsequent random read and write operations continue from the selected point in the file.

Function 37: Reset Drive
DEDrive Vector

The Reset Drive function allows resetting of specified drives. The passed parameter is a 16-bit vector of drives to be reset; the least significant bit is drive A:.

To maintain compatibility with MP/M, CP/M returns a zero value.

Function 40: Write Random with Zero Fill
DEFCB Address
ReturnAReturn Code

The Write With Zero Fill operation is similar to Function 34, with the exception that a previously unallocated block is filled with zeros before the data is written.

5.3 A Sample File-to-File Copy Program

The following program provides a relatively simple example of file operations. The program source file is created as COPY.ASM using the CP/M ED program and then assembled using ASM or MAC, resulting in a HEX file. The LOAD program is used to produce a COPY.COM file that executes directly under the CCP. The program begins by setting the stack pointer to a local area and proceeds to move the second name from the default area at 006CH to a 33-byte File Control Block called DFCB. The DFCB is then prepared for file operations by clearing the current record field. At this point, the source and destination FCBs are ready for processing, because the SFCB at 005CH is properly set up by the CCP upon entry to the COPY program. That is, the first name is placed into the default FCB, with the proper fields zeroed, including the current record field at 007CH. The program continues by opening the source file, deleting any existing destination file, and creating the destination file. If all this is successful, the program loops at the label COPY until each record is read from the source file and placed into the destination file. Upon completion of the data transfer, the destination file is closed and the program returns to the CCP command level by jumping to BOOT.

               ;    sample file-to-file copy program
               ;    at the ccp level, the command
               ;         copy a:x.y b:u.v
0000 =         boot    equ  0000h     ; system reboot
0005 =         bdos    equ  0005h     ; bdos entry point
005C =         fcb1    equ  005ch     ; first file name
005C =         sfcb    equ  fcb1      ; source fcb
006C =         fcb2    equ  006ch     ; second file name
0080 =         dbuff   equ  0080h     ; default buffer
0100 =         tpa     equ  0100h     ; beginning of tpa
0009 =         printf  equ  9         ; print buffer func#
000F =         openf   equ  15        ; open file func#
0010 =         closef  equ  16        ; close file func#
0013 =         deletef equ  19        ; delete file func#
0014 =         readf   equ  20        ; sequential read func#
0015 =         writef  equ  21        ; sequential write
0016 =         makef   equ  22        ; make file func#
0100                   org  tpa       ; beginning of tpa
0100 311902            lxi  sp,stack  ; set local stack
0103 0E10              mvi  c,16      ; half an fcb
0105 116C00            lxi  d,fcb2    ; source of move
0108 21D901            lxi  h,dfcb    ; destination fcb
010B 1A        mfcb:   ldax d         ; source fcb
010C 13                inx  d         ; ready next
010D 77                mov  m,a       ; dest fcb
010E 23                inx  h         ; ready next
010F 0D                dcr  c         ; count 16...0
0110 C20B01            jnz  mfcb      ; loop 16 times
               ;    name has been removed, zero cr
0113 AF                xra  a         ; a = 00h
0114 32F901            sta  dfcbcr    ; current rec = 0
               ;    source and destination fcb's ready
0117 115C00            lxi  d,sfcb    ; source file
011A CD6901            call open      ; error if 255
011D 118701            lxi  d,nofile  ; ready message
0120 3C                inr  a         ; 255 becomes 0
0121 CC6101            cz   finis     ; done if no file
               ;    source file open, prep destination
0124 11D901            lxi  d,dfcb    ; destination
0127 CD7301            call delete    ; remove if present
012A 11D901            lxi  d,dfcb    ; destination
012D CD8201            call make      ; create the file
0130 119601            lxi  d,nodir   ; ready message
0133 3C                inr  a         ; 255 becomes 0
0134 CC6101            cz   finis     ; done if no dir space
               ;    source file open, dest file open
               ;    copy until end of file on source
0137 115C00    copy:   lxi  d,sfcb    ; source
013A CD7801            call read      ; read next record
013D B7                ora  a         ; end of file?
013E C25101            jnz  eofile    ; skip write if so
               ;    not end of file, write the record
0141 11D901            lxi  d,dfcb    ; destination
0144 CD7D01            call write     ; write the record
0147 11A901            lxi  d,space   ; ready message
014A B7                ora  a         ; 00 if write ok
014B C46101            cnz  finis     ; end if so
014E C33701            jmp  copy      ; loop until eof
               eofile: ; end of file, close destination
0151 11D901            lxi  d,dfcb    ; destination
0154 CD6E01            call close     ; 255 if error
0157 21BA01            lxi  h,wrprot  ; ready message
015A 3C                inr  a         ; 255 becomes 00
015B CC6101            cz   finis     ; shouldn't happen
               ;    copy operation complete, end
015E 11CB01            lxi  d,normal  ; ready message
               finis:  ; write message given in de, reboot
0161 0E09              mvi  c,printf
0163 CD0500            call bdos      ; write message
0166 C30500            jmp  bdos      ; reboot system
               ;    system interface subroutines
               ;    (all return directly from bdos)
0169 0E0F      open:   mvi  c,openf
016B C30500            jmp  bdos
016E 0E10      close:  mvi  c,closef
0170 C30500            jmp  bdos
0173 0E13      delete: mvi  c,deletef
0175 C30500            jmp  bdos
0178 0E14      read:   mvi  c,readf
017A C30500            jmp  bdos
017D 0E15      write:  mvi  c,writef
017F C30500            jmp  bdos
0182 0E16      make:   mvi  c,makef
0184 C30500            jmp  bdos
               ;    console messages
0187 6E6F20736Fnofile: db 'no source file$'
0196 6E6F206469nodir:  db 'no directory space$'
01A9 6F7574206Fspace:  db 'out of dat space$'
01BA 7772697465wrprot: db 'write protected?$'
01CB 636F707920normal: db 'copy complete$'
               ;    data areas
01D9           dfcb:   ds   32        ; destination fcb
01F9 =         dfcbcr: equ  dfcb+32   ; current record
01F9                   ds   32        ; 16 level stack
0219                   end

Note that there are several simplifications in this particular program. First, there are no checks for invalid filenames that could contain ambiguous references. This situation could be detected by scanning the 32-byte default area starting at location 005CH for ASCII question marks. A check should also be made to ensure that the filenames have been included (check locations 005DH and 006DH for nonblank ASCII characters). Finally, a check should be made to ensure that the source and destination filenames are different. An improvement in speed could be obtained by buffering more data on each read operation. One could, for example, determine the size of memory by fetching FBASE from location 0006H and using the entire remaining portion of memory for a data buffer. In this case, the programmer simply resets the DMA address to the next successive 128-byte area before each read. Upon writing to the destination file, the DMA address is reset to the beginning of the buffer and incremented by 128 bytes to the end as each record is transferred to the destination file.

5.4 A Sample File Dump Utility

The following file dump program is slightly more complex than the simple copy program given in the previous section. The dump program reads an input file, specified in the CCP command line, and displays the content of each record in hexadecimal format at the console. Note that the dump program saves the CCP's stack upon entry, resets the stack to a local area, and restores the CCP's stack before returning directly to the CCP. Thus, the dump program does not perform a warm start at the end of processing.

               PRINTS IN HEX
               ;   COPYRIGHT (C) 1975, 1976, 1977, 1978
               ;   DIGITAL RESEARCH
               ;   BOX 579, PACIFIC GROVE
               ;   CALIFORNIA, 93950
0100                   ORG  100H
0005 =         BDOS    EQU  0005H     ;DOS ENTRY POINT
0001 =         CONS    EQU  1         ;READ CONSOLE
0002 =         TYPEF   EQU  2         ;TYPE FUNCTION
0009 =         PRINTF  EQU  9         ;BUFFER PRINT ENTRY
000B =         BRKF    EQU  11        ;BREAK KEY FUNCTION
                                      ;(TRUE IF CHAR READY)
000F =         OPENF   EQU  15        ;FILE OPEN
0014 =         READF   EQU  20        ;READ FUNCTION
005C =         FCB     EQU  5CH       ;FILE CONTROL BLOCK ADDRESS
0080 =         BUFF    EQU  80H       ;INPUT DISK BUFFER ADDRESS
               ;   NON GRAPHIC CHARACTERS
000D =         CR      EQU  0DH       ;CARRIAGE RETURN
000A =         LF      EQU  0AH       ;LINE FEED
005C =         FCBDN   EQU  FCB+0     ;DISK NAME
005D =         FCBFN   EQU  FCB+1     ;FILE NAME
0065 =         FCBFT   EQU  FCB+9     ;DISK FILE TYPE (3 CHARACTERS)
006B =         FCBRC   EQU  FCB+15    ;FILE'S RECORD COUNT (0 TO 128)
007C =         FCBCR   EQU  FCB+32    ;CURRENT (NEXT) RECORD
                                      ;NUMBER (0 TO 127)
007D =         FCBLN   EQU  FCB+33    ;FCB LENGTH
               ;   SET UP STACK
0100 210000            LXI  H,0
0103 39                DAD  SP
0104 221502            SHLD OLDSP
0107 315702            LXI  SP,STKTOP
010A CDC101            CALL SETUP     ;SET UP INPUT FILE
010D FEFF              CPI  255       ;255 IF FILE NOT PRESENT
010F C21B01            JNZ  OPENOK    ;SKIP IF OPEN IS OK
0112 11F301            LXI  D,OPNMSG
0115 CD9C01            CALL ERR
0118 C35101            JMP  FINIS     ;TO RETURN
011B 3E80              MVI  A,80H
011D 321302            STA  IBP       ;SET BUFFER POINTER TO 80H
0120 210000            LXI  H,0       ;START WITH 0000
0123 E5                PUSH H         ;SAVE LINE POSITION
0124 CDA201            CALL GNB
0127 E1                POP  H         ;RECALL LINE POSITION
0128 DA5101            JC   FINIS     ;CARRY SET BY GNB IF END FILE
012B 47                MOV  B,A
               ;   PRINT HEX VALUES
               ;   CHECK FOR LINE FOLD
012C 7D                MOV  A,L
012D E60F              ANI  0FH       ;CHECK LOW 4 BITS
012F C24401            JNZ  NONUM
               ;   PRINT LINE NUMBER
0132 CD7201            CALL CRLF
               ;   CHECK FOR BREAK KEY
0135 CD5901            CALL BREAK
               ;   ACCUM LSB = 1 IF CHARACTER READY
0138 0F                RRC            ;INTO CARRY
0139 DA5101            JC   FINIS     ;DON'T PRINT ANY MORE
013C 7C                 MOV  A,H
013D CD8F01             CALL PHEX
0140 7D                 MOV  A,L
0141 CD8F01             CALL PHEX
0144 23                 INX  H        ;TO NEXT LINE NUMBER
0145 3E20               MVI  A,' '
0147 CD6501             CALL PCHAR
014A 78                 MOV  A,B
014B CD8F01             CALL PHEX
014E C32301             JMP  GLOOP
               ;   END OF DUMP, RETURN TO CCP
               ;   (NOTE THAT A JMP TO 0000H REBOOTS)
0151 CD7201             CALL CRLF
0154 2A1502             LHLD OLDSP
0157 F9                 SPHL
0158 C9                 RET       ;TO THE CCP
               ;   SUBROUTINES
015C 0E0B               MVI  C,BRKF
015E CD0500             CALL BDOS
0164 C9                 RET
               PCHAR:   ;PRINT A CHARACTER
0165 E5D5C5             PUSH H! PUSH D! PUSH B; SAVED
0168 0E02               MVI  C,TYPEF
016A 5F                 MOV  E,A
016B CD0500             CALL BDOS
016E C1D1E1             POP B! POP D! POP H; RESTORED
0171 C9                 RET
0172 3E0D               MVI  A,CR
0174 CD6501             CALL PCHAR
0177 3E0A               MVI  A,LF
0179 CD6501             CALL PCHAR
017C C9                 RET
               PNIB:    ;PRINT NIBBLE IN REG A
017D E60F               ANI  0FH      ;LOW 4 BITS
017F FE0A               CPI  10
0181 D28901             JNC  P10
               ;   LESS THAN OR EQUAL TO 9
0184 C630               ADI  '0'
0186 C38B01             JMP  PRN
               ;   GREATER OR EQUAL TO 10
0189 C637      P10:     ADI  'A' - 10
018B CD6501    PRN:     CALL PCHAR
018E C9                 RET
               PHEX:    ;PRINT HEX CHAR IN REG A
018F F5                 PUSH PSW
0190 0F                 RRC
0191 0F                 RRC
0192 0F                 RRC
0193 0F                 RRC
0194 CD7D01             CALL PNIB     ;PRINT NIBBLE
0197 F1                 POP  PSW
0198 CD7D01        CALL PNIB
019B C9                 RET
               ERR:     ;PRINT ERROR MESSAGE
               ;        D,E ADDRESSES MESSAGE ENDING WITH "$"
019E CD0500             CALL BDOS
01A1 C9                 RET
               GNB:     ;GET NEXT BYTE
01A2 3A1302             LDA  IBP
01A5 FE80               CPI  80H
01A7 C2B301             JNZ  G0
               ;        READ ANOTHER BUFFER
01AA CDCE01             CALL DISKR
01AD B7                 ORA  A         ;ZERO VALUE IF READ OK
01AE CAB301             JZ   G0        ;FOR ANOTHER BYTE
               ;        END OF DATA, RETURN WITH CARRY SET FOR EOF
01B1 37                 STC
01B2 C9                 RET
               G0:      ;READ THE BYTE AT BUFF+REG A
01B3 5F                 MOV  E,A       ;LS BYTE OF BUFFER INDEX
01B4 1600               MVI  D,0       ;DOUBLE PRECISION INDEX TO DE
01B6 3C                 INR  A         ;INDEX=INDEX+1
01B7 321302             STA  IBP       ;BACK TO MEMORY
               ;        POINTER IS INCREMENTED
               ;        SAVE THE CURRENT FILE ADDRESS
01BA 218000             LXI  H,BUFF
01BD 19                 DAD  D
01BE 7E                 MOV  A,M
               ;        BYTE IS IN THE ACCUMULATOR
01BF B7                 ORA  A         ;RESET CARRY BIT
01C0 C9                 RET
               SETUP:   ;SET UP FILE
               ;        OPEN THE FILE FOR INPUT
01C1 AF                 XRA  A         ;ZERO TO ACCUM
01C2 327C00             STA  FCBCR     ;CLEAR CURRENT RECORD
01C5 115C00             LXI  D,FCB
01C8 0E0F               MVI  C,OPENF
01CA CD0500             CALL BDOS
               ;        255 IN ACCUM IF OPEN ERROR
01CD C9                 RET
               DISKR:   ;READ DISK FILE RECORD
01CE E5D5C5             PUSH H! PUSH D! PUSH B
01D1 115C00             LXI  D,FCB
01D4 0E14               MVI  C,READF
01D6 CD0500             CALL BDOS
01D9 C1D1E1             POP B! POP D! POP H
01DC C9                 RET
               ;        FIXED MESSAGE AREA
01DD 46494C4520SIGNON:  DB   'FILE DUMP VERSION 1.4$'
               ;        VARIABLE AREA
0213           IBP:     DS   2        ;INPUT BUFFER POINTER
0215           OLDSP:   DS   2        ;ENTRY SP VALUE FROM CCP
               ;        STACK AREA
0217                    DS   64       ;RESERVE 32 LEVEL STACK
0257                    END

5.5 A Sample Random Access Program

This section concludes with an extensive example of random access operation. The program listed below performs the simple function of reading or writing random records upon command from the terminal. When a program has been created, assembled, and placed into a file labeled RANDOM.COM, the CCP level command


starts the test program. The program looks for a file by the name X.DAT and, if found, proceeds to prompt the console for input. If not found, the file is created before the prompt is given. Each prompt takes the form

     next command?

and is followed by operator input, followed by a carriage return. The input commands take the form


where n is an integer value in the range 0 to 65535, and W, R, and Q are simple command characters corresponding to random write, random read, and quit processing, respectively. If the W command is issued, the RANDOM program issues the prompt

     type data:

The operator then responds by typing up to 127 characters, followed by a carriage return. RANDOM then writes the character string into the X.DAT file at record n. If the R command is issued, RANDOM reads record number n and displays the string value at the console, If the Q command is issued, the X.DAT file is closed, and the program returns to the CCP. In the interest of brevity, the only error message is

     error, try again.

The program begins with an initialization section where the input file is opened or created, followed by a continuous loop at the label ready where the individual commands are interpreted. The DFBC at 005CH and the default buffer at 0080H are used in all disk operations. The utility subroutines then follow, which contain the principal input line processor, called readc. This particular program shows the elements of random access processing, and can be used as the basis for further program development.

Sample Random Access Program for CP/M 2.0

0100                    org  100h      ; base of tpa
0000 =         reboot   equ  0000h     ; system reboot
0005 =         bdos     equ  0005h     ; bdos entry point
0001 =         coninp   equ  1         ; console input function
0002 =         conout   equ  2         ; console output function
0009 =         pstring  equ  9         ; print string function
000A =         rstring  equ  10        ; read console buffer
000C =         version  equ  12        ; return version number
000F =         openf    equ  15        ; file open function
0010 =         closef   equ  16        ; close function
0016 =         makef    equ  22        ; make file function
0021 =         readr    equ  33        ; read random
0022 =         writer   equ  34        ; write random
005C =         fcb      equ  005ch     ; default file control block
007D =         ranrec   equ  fcb+33    ; random record position
007F =         ranovf   equ  fcb+35    ; high order (overflow)
                                       ; byte
0080 =         buff     equ  0080h     ; buffer address
000D =         cr       equ  0dh       ; carriage return
000A =         lf       equ  0ah       ; line feed
               ;        load sp, set-up file for random access
0100 31B702             lxi  sp,stack
               ;        version 2.0
0103 0E0C               mvi  c,version
0105 CD0500             call bdos
0108 FE20               cpi  20h       ; version 2.0 or better?
010A D21601             jnc  versok
               ;        bad version, message and go back
010D 111502             lxi  d,badver
0110 CDD501             call print
0113 C30000             jmp  reboot
               ;        correct version for random access
0116 0E0F               mvi  c,openf   ; open default fcb
0118 115C00             lxi  d,fcb
011B CD0500             call bdos
011E 3C                 inr  a         ; err 255 becomes zero
011F C23701             jnz  ready
               ;        cannot open file, so create it
0122 0E16               mvi  c,makef
0124 115C00             lxi  d,fcb
0127 CD0500             call bdos
012A 3C                 inr  a         ; err 255 becomes zero
012B C23701             jnz  ready
               ;        cannot create file, directory full
012E 113402             lxi  d,nospace
0131 CDD501             call print
0134 C30000             jmp  reboot    ; back tp CCP
               ;        loop back to ready after each read command
               ;        file is ready for processing
0137 CDE001             call readcom   ; read next command
013A 227D00             shld ranrec    ; store input record #
013D 217F00             lxi  h,ranovf
0140 3600               mvi  m,0       ; clear high byte if set
0142 FE51               cpi  'Q'       ; Quit?
0144 C25601             jnz  notq
               ;        quit processing, close file
0147 0E10               mvi  c,closef
0149 115C00             lxi  d,fcb
014C CD0500             call bdos
014F 3C                 inr  a         ; err 255 becomes 0
0150 CAB401             jz   error     ; error message, retry
0153 C30000             jmp  reboot    ; back to ccp
               ;        end of command, process write
               ;        not the quit command, random write?
0156 114702             lxi  d,datmsg
0159 CDD501             call print     ; data prompt
015C 0E7F               mvi  c,127     ; up to 127 characters
015E 218000             lxi  h,buff    ; destination
               rloop:   ;read next character to buff
0161 C5                 push b         ; save counter
0162 E5                 push h         ; next destination
0163 CDBD01             call getchr    ; character to a
0166 E1                 pop  h         ; restore counter
0167 C1                 pop  b         ; resore next to fill
0168 FE0D               cpi  cr        ; end of line?
016A CA7301             jz   erloop
               ;       not end, store character
016D 77                 mov  m,a
016E 23                 inx  h         ; next to fill
016F 0D                 dcr  c         ; counter goes down
0170 C26101             jnz  rloop     ; end of buffer?
               ;        end of read loop, store 00
0173 3600               mvi  m,0
               ;        write the record to selected record number
0175 0E22               mvi  c,writer
0177 115C00             lxi  d,fcb
017A CD0500             call bdos
017D B7                 ora  a         ; error code zero?
017E C2B401             jnz  error     ; message if not
0181 C33701             jmp  ready     ; for another record
               ;        end of write command, process read
               ;        not a write command, read record?
0184 FE52               cpi  'R'
0186 C2B401             jnz  error     ; skip if not
               ;        read random record
0189 0E21               mvi  c,readr
018B 115C00             lxi  d,fcb
018E CD0500             call bdos
0191 B7                 ora  a         ; return code 00?
0192 C2B401             jnz  error
               ;        read was successful, write to console
0195 CDCA01             call crlf      ; new line
0198 0E80               mvi  c,128     ; max 128 characters
019A 218000             lxi  h,buff    ; next to get
019D 7E                 mov  a,m       ; next character
019E 23                 inx  h         ; next to get
019F E67F               ani  7fh       ; mask parity
01A1 CA3701             jz   ready     ; for another command if 00
01A4 C5                 push b         ; save counter
01A5 E5                 push h         ; save next to get
01A6 FE20               cpi  ' '       ; graphic?
01A8 D4C301             cnc  putchr    ; skip output if not
01AB E1                 pop  h
01AC C1                 pop  b
01AD 0D                 dcr  c         ; count=count-1
01AE C29D01             jnz  wloop
01B1 C33701             jmp  ready
               ;        end of read command, all errors end up here
01B4 115402             lxi  d,errmsg
01B7 CDD501             call print
01BA C33701             jmp  ready
                        ; read next console character to a
01BD 0E01               mvi  c,coninp
01BF CD0500             call bdos
01C2 C9                 ret
                        ; write character from a to console
01C3 0E02               mvi  c,conout
01C5 5F                 mov  e,a       ; char to send
01C6 CD0500             call bdos      ; send char
01C9 C9                 ret
                        ; send carriage return, line feed
01CA 3E0D               mvi  a,cr      ; carriage return
01CC CDC301             call putchr
01CF 3E0A               mvi  a,lf      ; line feed
01D1 CDC301             call putchr
01D4 C9                 ret
                        ; print the buffer addressed by de until $
01D5 D5                 push d
01D6 CDCA01             call crlf
01D9 D1                 pop  d         ; new line
01DA 0E09               mvi  c,pstring
01DC CD0500             call bdos      ; print the string
01DF C9                 ret
                        ; read the next command line to the conbuf
01E0 116602             lxi  d,prompt
01E3 CDD501             call print     ; command?
01E6 0E0A               mvi  c,rstring
01E8 117502             lxi  d,conbuf
01EB CD0500             call bdos
               ;        command line is present, scan it
01EE 210000             lxi  h,0       ; start with 0000
01F1 117702             lxi  d,conlin  ; command line
01F4 1A        readc:   dax  d         ; next command character
01F5 13                 inx  d         ; to next command position
01F6 B7                 ora  a         ; cannot be end of command
01F7 C8                 rz
               ;        not zero, numeric?
01F8 D630               sui  '0'
01FA FE0A               cpi  10        ; carry if numeric
01FC D20D02             jnc  endrd
               ;        add-in next digit
01FF 29                 dad  h         ; *2
0200 49                 mov  c,1
0201 44                 mov  b,h       ; bc - value * 2
0202 29                 dad  h         ; *4
0203 09                 dad  b         ; *2 + *8 = *10
0204 85                 add  l
0205 6F                 mov  l,a
0206 D2F401             jnc  readc     ; for another char
0209 24                 inr  h         ; overflow
020A C3F401             jmp  readc     ; for another char
               ;        end of read, restore value in a
020D C630               adi  '0'       ; command
020F FE61               cpi  'a'       ; translate case?
0211 D8                 rc
               ;        lower case, mask lower case bits
0212 E65F               ani 101$1111b
0214 C9                 ret
               ;       string data area
0215 736F727279badver:  db  'sorry, you need cp/m version 2$'
0234 6E6F206469nospace: db  'no directory space$'
0247 7479706520datmsg:  db  'type datas: $'
0254 6572726F72errmsg:  db  'error, try again.$'
0266 6E65787420prompt:  db  'next command? $'
               ;       fixed and variable data area
0275 21        conbuf:  db  conlen     ; length of console buffer
0276           consiz:  ds  1          ; resulting size after read
0277           conlin:  ds  32         ; length 32 buffer
0021 =         conlen   equ $-consiz
0297                    ds  32
02B7                    end

Major improvements could be made to this particular program to enhance its operation. In fact, with some work, this program could evolve into a simple data base management system. One could, for example, assume a standard record size of 128 bytes, consisting to arbitrary fields within the record. A program, called GETKEY, could be developed that first reads a sequential file and extracts a specific field defined by the operator. For example, the command


would cause GETKEY to read the data base file NAMES.DAT and extract the LASTNAME field from each record, starting in position 10 and ending at character 20. GETKEY builds a table in memory consisting of each particular LASTNAME field, along with its 16- bit record number location within the file. The GETKEY program then sorts this list and writes a new file, called LASTNAME.KEY, which is an alphabetical list of LASTNAME fields with their corresponding record numbers. This list is called an inverted index in information retrieval parlance.

If the programmer were to rename the program shown above as QUERY and modify it so that it reads a sorted key file into memory, the command line might appear as


Instead of reading a number, the QUERY program reads an alphanumeric string that is a particular key to find in the NAMES.DAT data base. Because the LASTNAME.KEY list is sorted, one can find a particular entry rapidly by performing a binary search, similar to looking up a name in the telephone book. Starting at both ends of the list, one examines the entry halfway in between and, if not matched, splits either the upper half or the lower half for the next search. You will quickly reach the item you are looking for and find the corresponding record number. You should fetch and display this record at the console, just as was done in the program shown above.

With some more work, you can allow a fixed grouping size that differs from the 128-byte record shown above. This is accomplished by keeping track of the record number and the byte offset within the record. Knowing the group size, you randomly access the record containing the proper group, offset to the beginning of the group within the record read sequentially until the group size has been exhausted.

Finally, you can improve QUERY considerably by allowing boolean expressions, which compute the set of records that satisfy several relationships, such as a LASTNAME between HARDY and LAUREL and an AGE lower than 45. Display all the records that fit this description. Finally, if your lists are getting too big to fit into memory, randomly access key files from the disk as well.

5.6 System Function Summary

Input Output
00System Resetnonenone
11Console InputnoneA = ASCII char
22Console OutputE = charnone
33Reader InputnoneA = ASCII char
44Punch OutputE = charnone
55List OutputE = charnone
66Direct Console I/O E = 0FFH (input)A = char
E = 0FEH (status)A = status
E = charnone
77Get I/O BytenoneA = I/O byte value
88Set I/O ByteE = I/O bytenone
99Print StringDE = Buffer Addressnone
10ARead Console StringDE = BufferConsole characters in Buffer
11BGet Console StatusnoneA = 00/non zero
12CReturn Version #noneHL = Version #
13DReset Disk Systemnonenone
14ESelect DiskE = Disk #none
15FOpen FileDE = FCB addressA = FF if not found
1610Close FileDE = FCB addressA = FF if not found
1711Search For FirstDE = FCB addressA = Directory Code
1812Search For NextnoneA = Directory Code
1913Delete FileDE = FCB addressA = none
2014Read SequentialDE = FCB addressA = Error Code
2115Write SequentialDE = FCB AddressA = Error Code
2216Make FileDE = FCB addressA = FF if no DIR Space
2317Rename FileDE = FCB addressA = FF if not found
2418Return Login VectornoneHL = Login Vector*
2519Return Current DisknoneA = Current Disk Number
261ASet DMA AddressDE = DMA addressnone
271BGet ADDR (ALLOC)noneHL = ALLOC address*
281CWrite Protect Disknonenone
291DGet Read/only VectornoneHL = ALLOC address*
301ESet File AttributesDE = FCB addressA = none
311FGet ADDR (Disk Parms)noneHL = DPB address
3220Set/Get User Code E = 0FFH for GetA = User Number
E = 00 to 0FH for Setnone
3321Read RandomDE = FCB addressA = Error
3422Write RandomDE = FCB addressA = Error Code
3523Compute File SizeDE = FCB addressr0, r1, r2
3624Set Random RecordDE = FCB addressr0, r1, r2
3725Reset DriveDE = Drive VectorA = 0
3826Access Drivenot supported
3927Free Drivenot supported
4028Write Random w/FillDE = FCBA = error code

* Note that A=L, and B=H upon return.

Back to title page       Next