CP/M Operating System Manual
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
Tables
5-1 CP/M Filetypes
5-2 File Control Block Fields
5-3 Edit Control Characters
Figures
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
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
command.COM
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
- read a console character
- write a console character
- read a sequential character
- write a sequential character
- get or set I/O status
- print console buffer
- interrogate console ready
The following FDOS operations perform disk I/O:
- disk system reset
- drive selection
- file creation
- file close
- directory search
- file delete
- file rename
- random or sequential read
- random or sequential write
- interrogate available disks
- interrogate selected disk
- set DMA address
- set/reset file indicators.
As mentioned above, access to the 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.
0 | System Reset | 19 | Delete File
|
1 | Console Input | 20 | Read Sequential
|
2 | Console Output | 21 | Write Sequential
|
3 | Reader Input | 22 | Make File
|
4 | Punch Output | 23 | Rename File
|
5 | List Output | 24 | Return Login Vector
|
6 | Direct Console I/O | 25 | Return Current Disk
|
7 | Get I/O Byte | 26 | Set DMA Address
|
8 | Set I/O Byte | 27 | Get Addr(Alloc)
|
9 | Print String | 28 | Write Protect Disk
|
10 | Read Console Buffer | 29 | Get R/O Vector
|
11 | Get Console Status | 30 | Set File Attributes
|
12 | Return Version Number | 31 | Get Addr(Disk Parms)
|
13 | Reset Disk System | 32 | Set/Get User Code
|
14 | Select Disk | 33 | Read Random
|
15 | Open File | 34 | Write Random
|
16 | Close File | 35 | Compute File Size
|
17 | Search for First | 36 | Set Random Record
|
18 | Search for Next | 37 | Reset Drive
|
| | 40 | Write Random with Zero Fill
|
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.
BDOS EQU 0005H ;STANDARD CP/M ENTRY
CONIN EQU 1 ;CONSOLE INPUT FUNCTION
;
ORG 0100H ;BASE OF TPA
NEXTC: MVI C,CONIN ;READ NEXT CHARACTER
CALL BDOS ;RETURN CHARACTER IN <A>
CPI '*' ;END OF PROCESSING?
JNZ NEXTC ;LOOP IF NOT
RET ;RETURN TO CCP
END
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
Filetype | Meaning
|
---|
ASM | Assembler Source
|
PRN | Printer Listing
|
HEX | Hex Machine Code
|
BAS | Basic Source File
|
INT | Intermediate Code
|
COM | Command File
|
PLI | PL/I Source File
|
REL | Relocatable Module
|
TEX | TEX Formatter Source
|
BAK | ED Source Backup
|
SYM | SID 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
DR | F1 - F8 | T1 - T3 | EX | S1 | S2 | RC
| D0 - DN | CR | R0 | R1 | R2
|
00 | 01 | 02 | ... | 08 | 09
| 10 | 11 | 12 | 13 | 14 | 15 | 16 | ...
| 31 | 32 | 33 | 34 | 35
|
The following table lists and describes each of the fields in the
File Control Block figure.
Table 5-2. File Control Block Fields
Field | Definition
|
---|
dr | 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.
|
f1...f8 | contain the filename in ASCII upper-case,
with high bit = 0
|
t1,t2,t3 | 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
|
ex | contains the current extent number, normally
set to 00 by the user, but in range 0-31
during file I/O
|
s1 | reserved for internal system use
|
s2 | reserved for internal system use, set to zero
on call to
OPEN, MAKE,
SEARCH
|
rc | record count for extent ex; takes on values
from 0-127
|
d0...dn | filled in by CP/M; reserved for system use
|
cr | current record to read or write in a
sequential file operation; normally set to
zero by user
|
r0,r1,r2 | 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
|
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:
PROGNAME B:X.ZOT Y.ZAP
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:
BOOT+0080H:
|
+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
|
| Register | Value
|
---|
Entry | C | 00H
|
---|
Return | (none)
|
---|
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
|
| Register | Value
|
---|
Entry | C | 01H
|
---|
Return | A | ASCII 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
|
| Register | Value
|
---|
Entry | C | 01H
|
---|
E | ASCII Character
|
Return | (none)
|
---|
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
|
| Register | Value
|
---|
Entry | C | 03H
|
---|
Return | A | ASCII 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
|
| Register | Value
|
---|
Entry | C | 04H
|
---|
E | ASCII Character
|
Return | (none)
|
---|
The Punch Output function sends the character from register E to
the logical punch device.
Function 5: List Output
|
| Register | Value
|
---|
Entry | C | 05H
|
---|
E | ASCII Character
|
Return | (none)
|
---|
The List Output function sends the ASCII character in register E
to the logical listing device.
Function 6: Direct Console I/O
|
| Register | Value
|
---|
Entry | C | 06H
|
---|
E | 0FFH (input) or char (output)
|
Return | A | char 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
|
| Register | Value
|
---|
Entry | C | 07H
|
---|
Return | A | I/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
|
| Register | Value
|
---|
Entry | C | 08H
|
---|
E | I/O Byte Value
|
Return | (none)
|
---|
The SET I/O Byte function changes the IOBYTE value to that given
in register E.
Function 9: Print String
|
| Register | Value
|
---|
Entry | C | 09H
|
---|
DE | String Address
|
Return | (none)
|
---|
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
|
| Register | Value
|
---|
Entry | C | 0AH
|
---|
DE | Buffer Address
|
Return | Console 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:
DE: | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | ... | +n
|
| mx | nc | cl | c2 | c3 | c4 | c5 | c6 | c7 | ... | ??
|
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
Character | Edit Control Function
|
---|
rub/del | removes and echoes the last character
|
CTRL-C | reboots when at the beginning of line
|
CTRL-E | causes physical end of line
|
CTRL-H | backspaces one character position
|
CTRL-J | (line-feed) terminates input line
|
CTRL-M | (return) terminates input line
|
CTRL-R | retypes the current line after new line
|
CTRL-U | removes current line
|
CTRL-X | same 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
|
| Register | Value
|
---|
Entry | C | 0BH
|
---|
Return | A | Console 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
|
| Register | Value
|
---|
Entry | C | 0CH
|
---|
Return | HL | Version 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
|
| Register | Value
|
---|
Entry | C | 0DH
|
---|
Return | (none)
|
---|
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
|
| Register | Value
|
---|
Entry | C | 0EH
|
---|
E | Selected Disk
|
Return | (none)
|
---|
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
|
| Register | Value
|
---|
Entry | C | 0FH
|
---|
DE | FCB Address
|
Return | A | Directory 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
|
| Register | Value
|
---|
Entry | C | 10H
|
---|
DE | FCB Address
|
Return | A | Directory 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
|
| Register | Value
|
---|
Entry | C | 11H
|
---|
DE | FCB Address
|
Return | A | Directory 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
|
| Register | Value
|
---|
Entry | C | 12H
|
---|
Return | A | Directory 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
|
| Register | Value
|
---|
Entry | C | 13H
|
---|
DE | FCB Address
|
Return | A | Directory 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
|
| Register | Value
|
---|
Entry | C | 14H
|
---|
DE | FCB Address
|
Return | A | Directory 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
|
| Register | Value
|
---|
Entry | C | 15H
|
---|
DE | FCB Address
|
Return | A | Directory 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
|
| Register | Value
|
---|
Entry | C | 16H
|
---|
DE | FCB Address
|
Return | A | Directory 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
|
| Register | Value
|
---|
Entry | C | 17H
|
---|
DE | FCB Address
|
Return | A | Directory 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
|
| Register | Value
|
---|
Entry | C | 18H
|
---|
Return | HL | Log-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
|
| Register | Value
|
---|
Entry | C | 19H
|
---|
Return | A | Current 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
|
| Register | Value
|
---|
Entry | C | 1AH
|
---|
DE | DMA Address
|
Return | (none)
|
---|
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)
|
| Register | Value
|
---|
Entry | C | 1BH
|
---|
Return | HL | ALLOC 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
|
| Register | Value
|
---|
Entry | C | 1CH
|
---|
Return | (none)
|
---|
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
|
| Register | Value
|
---|
Entry | C | 1DH
|
---|
Return | HL | R/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
|
| Register | Value
|
---|
Entry | C | 1EH
|
---|
DE | FCB Address>
|
Return | A | Directory 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)
|
| Register | Value
|
---|
Entry | C | 1FH
|
---|
Return | HL | DPB 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
|
| Register | Value
|
---|
Entry | C | 20H
|
---|
E | 0FFH (get) or User Code (set)
|
Return | A | Current 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
|
| Register | Value
|
---|
Entry | C | 21H
|
---|
DE | FCB Address
|
Return | A | Return 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.
01 | reading unwritten data
|
02 | (not returned in random mode)
|
03 | cannot close current extent
|
04 | seek to unwritten extent
|
05 | (not returned in read mode)
|
06 | seek 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
|
| Register | Value
|
---|
Entry | C | 22H
|
---|
DE | FCB Address
|
Return | A | Return 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
|
| Register | Value
|
---|
Entry | C | 23H
|
---|
DE | FCB Address
|
Return | Random 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
|
| Register | Value
|
---|
Entry | C | 24H
|
---|
DE | FCB Address
|
Return | Random 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
|
| Register | Value
|
---|
Entry | C | 25H
|
---|
DE | Drive Vector
|
Return | A | 00H
|
---|
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
|
| Register | Value
|
---|
Entry | C | 28H
|
---|
DE | FCB Address
|
Return | A | Return 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
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.
; FILE DUMP PROGRAM, READS AN INPUT FILE AND
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
;
; FILE CONTROL BLOCK DEFINITIONS
005C = FCBDN EQU FCB+0 ;DISK NAME
005D = FCBFN EQU FCB+1 ;FILE NAME
0065 = FCBFT EQU FCB+9 ;DISK FILE TYPE (3 CHARACTERS)
0068 = FCBRL EQU FCB+12 ;FILE'S CURRENT REEL NUMBER
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
; ENTRY STACK POINTER IN HL FROM THE CCP
0104 221502 SHLD OLDSP
; SET SP TO LOCAL STACK AREA (RESTORED AT FINIS)
0107 315702 LXI SP,STKTOP
; READ AND PRINT SUCCESSIVE BUFFERS
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
;
; FILE NOT THERE, GIVE ERROR MESSAGE AND RETURN
0112 11F301 LXI D,OPNMSG
0115 CD9C01 CALL ERR
0118 C35101 JMP FINIS ;TO RETURN
;
OPENOK: ;OPEN OPERATION OK, SET BUFFER INDEX TO END
011B 3E80 MVI A,80H
011D 321302 STA IBP ;SET BUFFER POINTER TO 80H
; HL CONTAINS NEXT ADDRESS TO PRINT
0120 210000 LXI H,0 ;START WITH 0000
;
GLOOP:
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
NONUM:
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
;
FINIS:
; END OF DUMP, RETURN TO CCP
; (NOTE THAT A JMP TO 0000H REBOOTS)
0151 CD7201 CALL CRLF
0154 2A1502 LHLD OLDSP
0157 F9 SPHL
; STACK POINTER CONTAINS CCP'S STACK LOCATION
0158 C9 RET ;TO THE CCP
;
;
; SUBROUTINES
;
BREAK: ;CHECK BREAK KEY (ACTUALLY ANY KEY WILL DO)
0159 E5D5C5 PUSH H! PUSH D! PUSH B; ENVIRONMENT SAVED
015C 0E0B MVI C,BRKF
015E CD0500 CALL BDOS
0161 C1D1E1 POP B! POP D! POP H; ENVIRONMENT RESTORED
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
;
CRLF:
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 "$"
019C 0E09 MVI C,PRINTF ;PRINT BUFFER FUNCTION
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
; ABSOLUTE CHARACTER ADDRESS IS IN HL
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$'
01F3 0D0A4E4F20OPNMSG: DB CR,LF,'NO INPUT FILE PRESENT ON DISK$'
; 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
STKTOP:
;
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
RANDOM X.DAT
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
nWnRQ
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
;
versok:
; 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
;
ready:
; 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
;
notq:
; 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?
erloop:
; 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
;
notw:
; 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
wloop:
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
;
error:
01B4 115402 lxi d,errmsg
01B7 CDD501 call print
01BA C33701 jmp ready
;
getchr:
; read next console character to a
01BD 0E01 mvi c,coninp
01BF CD0500 call bdos
01C2 C9 ret
;
putchr;
; 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
;
crlf:
; 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:
; 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
;
readcom:
; 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
endrd:
; 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
stack:
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
GETKEY NAMES.DAT LASTNAME 10 20
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
QUERY NAMES.DAT LASTNAME.KEY
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
Function Number
| Function Name | Input
| Output
|
---|
Dec | Hex
|
---|
0 | 0 | System Reset | none | none
|
1 | 1 | Console Input | none | A = ASCII char
|
2 | 2 | Console Output | E = char | none
|
3 | 3 | Reader Input | none | A = ASCII char
|
4 | 4 | Punch Output | E = char | none
|
5 | 5 | List Output | E = char | none
|
6 | 6 | Direct Console I/O
| E = 0FFH (input) | A = char
|
E = 0FEH (status) | A = status
|
E = char | none
|
7 | 7 | Get I/O Byte | none | A = I/O byte value
|
8 | 8 | Set I/O Byte | E = I/O byte | none
|
9 | 9 | Print String | DE = Buffer Address | none
|
10 | A | Read Console String | DE = Buffer | Console characters
in Buffer
|
11 | B | Get Console Status | none | A = 00/non zero
|
12 | C | Return Version # | none | HL = Version #
|
13 | D | Reset Disk System | none | none
|
14 | E | Select Disk | E = Disk # | none
|
15 | F | Open File | DE = FCB address | A = FF if not found
|
16 | 10 | Close File | DE = FCB address | A = FF if not found
|
17 | 11 | Search For First | DE = FCB address | A = Directory Code
|
18 | 12 | Search For Next | none | A = Directory Code
|
19 | 13 | Delete File | DE = FCB address | A = none
|
20 | 14 | Read Sequential | DE = FCB address | A = Error Code
|
21 | 15 | Write Sequential | DE = FCB Address | A = Error Code
|
22 | 16 | Make File | DE = FCB address | A = FF if no DIR Space
|
23 | 17 | Rename File | DE = FCB address | A = FF if not found
|
24 | 18 | Return Login Vector | none | HL = Login Vector*
|
25 | 19 | Return Current Disk | none | A = Current Disk Number
|
26 | 1A | Set DMA Address | DE = DMA address | none
|
27 | 1B | Get ADDR (ALLOC) | none | HL = ALLOC address*
|
28 | 1C | Write Protect Disk | none | none
|
29 | 1D | Get Read/only Vector | none | HL = ALLOC address*
|
30 | 1E | Set File Attributes | DE = FCB address | A = none
|
31 | 1F | Get ADDR (Disk Parms) | none | HL = DPB address
|
32 | 20 | Set/Get User Code
| E = 0FFH for Get | A = User Number
|
E = 00 to 0FH for Set | none
|
33 | 21 | Read Random | DE = FCB address | A = Error
|
34 | 22 | Write Random | DE = FCB address | A = Error Code
|
35 | 23 | Compute File Size | DE = FCB address | r0, r1, r2
|
36 | 24 | Set Random Record | DE = FCB address | r0, r1, r2
|
37 | 25 | Reset Drive | DE = Drive Vector | A = 0
|
38 | 26 | Access Drive | not supported
|
39 | 27 | Free Drive | not supported
|
40 | 28 | Write Random w/Fill | DE = FCB | A = error code
|
* Note that A=L, and B=H upon return.
Back to title page
     Next