.cs 5
.mt 5
.mb 6
.pl 66
.ll 65
.po 10
.hm 2
.fm 2
.sp 3
.he CP/M Operating System Manual   5.5   Sample Random Access Program
.sh
5.5  A Sample Random Access Program
.qs
.tc    5.5  A Sample Random Access Program
.pp
This chapter 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
.sp
.ti 8
RANDOM X.DAT
.sp
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
.sp
.ti 8
next command?
.sp
and is followed by operator input, followed by a carriage 
return.  The input commands take the form
.sp
.ti 8
nW  nR  Q
.sp
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
.sp
.ti 8
type data:
.sp
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
.sp
.ti 8
error, try again.
.pp
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.
.ll 75
.sp 3
.nf
.sh
                            Sample Random Access Program for CP/M 2.0
.qs

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 until '$'
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
                         ;


.sh
                          Load SP, Set-Up File for Random Access
.qs

0100 31bc00                       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 d21600                       jnc    versok
                         ;        bad version, message and go back
010d 111b00                       lxi    d,badver
0110 cdda00                       call   print
0113 c30000                       jmp    reboot
                         ;
                         versok:
                         ;        correct versionm for random access
0116 0e0f                         mvi    c,openf   ;open default fcb
0118 115c00                       lxi    d,fcb
011b cd 0500                      call   bdos
011e 3c                           inr    a         ;err 255 becomes zero
011f c23700                       jnz    ready
                         ;
                         ;        connot 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 c23700                       jnz    ready
                         ;
                         ;        cannot create file, directory full
012e 113a00                       lxi    d,nospace
0131 cdda00                       call   print
0134 c30000                       jmp    reboot    ;back to ccp
.sp 2
.sh
                          Loop Back to Ready After Each Command
.qs
.sp
                         ;
                         ready:
                         ;        file is ready for processing
                         ;
0137 cde500                       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 c25600                       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 cab900                       jz     error     ;error message, retry
0153 c30000                       jmp    reboot    ;back to ccp
                         ;
.sp 2
.sh
                          End of Quit Command, Process Write
.qs
.sp
                         notq:
                         ;        not the quit command, random write?
0156 fe57                         cpi    'W'
0158 c28900                       jnz    notw
                         ;
                         ;        this is a random write, fill buffer untill cr
015b 114d00                       lxi    d,datmsg
015e cdda00                       call   print     ;data prompt
0161 0e7f                         mvi    c,127     ;up to 127 characters
0163 218000                       lxi    h,buff    ;destination
                         rloop:   ;read next character to buff
0166 c5                           push   b         ;save counter
0167 e5                           push   h         ;next destination
0168 cdc200                       call   getchr    ;character to a
016b e1                           pop    h         ;restore counter
016c c1                           pop    b         ;restore next to fill
016d fe0d                         cpi    cr        ;end of line?
016f ca7800                       jz     erloop
                         ;        not end, store character
0172 77                           mov    m,a
0173 23                           inx    h         ;next to fill
0174 0d                           dcr    c         ;counter goes down
0175 c26600                       jnz    rloop     ;end of buffer?
                         erloop:
                         ;        end of read loop, store 00
0178 3600                         mvi    m,0
                         ;
                         ;        write the record to selected record number
017a 0e22                         mvi    c,writer
017c 115c00                       lxi    d,fcb
017c cd0500                       call   bdos
0182 b7                           ora    a         ;erro code zero?
0183 c2b900                       jnz    error     ;message if not
0186 c33700                       jmp    ready     ;for another record
                         ;
.sp 2
.sh
                          End of Write Command, Process Read
.qs
.sp
                         notw:
                         ;        not a write command, read record?
0189 fe52                         cpi    'R'
018b c2b900                       jnz    error     ;skip if not
                         ;
                         ;        read random record
018e 0e21                         mvi    c,readr
0190 115c00                       lxi    d,fcb
0193 cd0500                       call   bdos
0196 b7                           ora    a         ;return code 00?
0197 c2b900                       jnz    error
                         ;
                         ;        read was successful, write to console
019a cdcf00                       call   crlf      ;new line
019d 0e80                         mvi    c,128     ;max 128 characters
019f 218000                       lxi    h,buff    ;next to get
                         wloop:
01a2 7e                           mov    a,m       ;next character
01a3 23                           inx    h         ;next to get
01a4 e67f                         ani    7fh       ;mask parity
01a6 ca3700                       jz     ready     ;for another command
                                                   ;if 00
01a9 c5                           push   b         ;save counter
01aa e5                           push   h         ;save next to get
01ab fe20                         cpi    ''        ;graphic?
01ad d4c800                       cnc    putchr    ;skip output if not
01b0 e1                           pop    h
01b1 c1                           pop    b
01b2 0d                           dcr    c         ;count=count-1
01b3 c2a200                       jnz    wloop
01b6 c33700                       jmp    ready
.bp
.sh
                          End of Read Command, All Errors End Up Here
.qs
.sp
                         ;
                         error:
01b9 115900                       lxi    d,errmsg
01bc cdda00                       call   print
01bf c33700                       jmp    ready
                         ;
.sp 2
.sh
                          Utility Subroutines for Console I/O
.qs
.sp
                         getchr:
                                  ;read next console character to a
01c2 0e01                         mvi    c,coninp
01c4 cd0500                       call   bdos
01c7 c9                           ret
                         ;
                         putchr:
                                  ;write character from a to console
01c8 0e02                         mvi    c,conout
01ca 5f                           mov    e,a       ;character to send
01cb cd0500                       call   bdos      ;send character
01ce c9                           ret
                         ;
                         crlf:
                                  ;send carriage return line feed
01cf 3e0d                         mvi    a,cr      ;carriage return
01d1 cdc800                       call   putchr
01d4 3e0a                         mvi    a,lf      ;line feed
01d6 cdc800                       call   putchr
01d9 c9                           ret
                         ;
                         print:
                                  ;print the buffer addressed by de untill $
01da d5                           push   d
01db cdcf00                       call   crlf
01de d1                           pop    d         ;new line
01df 0e09                         mvi    c,pstring
01e0 cd0500                       call   bdos      ;print the string
01e4 c9                           ret
                         ;
                         readcom:
                                  ;read the next command line to the conbuf
01e5 116b00                       lxi    d,prompt
01e8 cdda00                       call   print     ;command?
01eb 0e0a                         mvi    c,rstring
01ed 117a00                       lxi    d,conbuf
01f0 cd0500                       call   bdos      ;read command line
                         ;        command line is present, scan it
01f3 210000                       lxi    h,0       ;start with 0000
01f6 117c00                       lxi    d,conlin  ;command line
01f9 1a                  readc:   ldax   d         ;next command
                                                   ;character
01fa 13                           inx    d         ;to next command
                                                   ;position
01fb b7                           ora    a         ;cannot be end of
                                                   ;command
01fc c8                           rz
                         ;        not zero, numeric?
01fd d630                         sui    '0'
01ff fe0a                         cpi    10        ;carry if numeric
0201 d21300                       jnc    endrd
                         ;        add-in next digit
0204 29                           dad    h         ;*2
0205 4d                           mov    c,l
0206 44                           mov    b,h       ;bc = value * 2
0207 29                           dad    h         ;*4
0208 29                           dad    h         ;*8
0209 09                           dad    b         ;*2 + *8 = *10
020a 85                           add    l         ;*digit
020b 6f                           mov    l,a
020c d2f900                       jnc    readc     ;for another char
020f24                            inr    h         ;overflow
0210 c3f900                       jmp    readc     ;for another char
                         endrd:
                         ;        end of read, restore value in a
0213 c630                         adi    '0'       ;command
0215 fe61                         cpi    'a'       ;translate case?
0217 d8                           rc
                         ;        lower case, mask lower case bits
0218 e65f                         ani    101$1111b
021a c9                           ret
                         ;
.sp 2
.sh
                          String Data Area for Console Messages
.qs
.sp
                         badver:
021b 536f79                       db     'sorry, you need cp/m version 2$'
                         nospace:
023a 4e6f29                       db     'no directory space$'
                         datmsg:
024d 547970                       db     'type data: $'
                         errmsg:
0259 457272                       db     'error, try again.$'
                         prompt:
026b 4e6570                       db     'next command? $'
                         ;
.sp 2
.mb 5
.fm 1
.sh
                          Fixed and Variable Data Area
.qs
.sp
027a 21                  conbuf:  db     conlen     ;length of console buffer
027b                     consiz:  ds     1          ;resulting size after read
027c                     conlin:  ds     32         ;length 32 buffer
0021 =                   conlen   equ    $-consiz
                         ;
029c                              ds     32         ;16 level stack
                         stack:
02bc                              end
.ll 65
.fi
.pp
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
.mb 6
.fm 2
.sp
.ti 8
GETKEY NAMES.DAT LASTNAME 10 20
.sp
would cause GETKEY to read the data base file NAMES.DAT and 
extract the LAST-NAME 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.
.pp
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
.sp
.ti 8
QUERY NAMES.DAT LASTNAME.KEY
.sp
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.
.pp
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.
.pp
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.
.bp
.tc    5.6  System Function Summary
.he CP/M Operating System Manual         5.6  System Function Summary
.sh
5.6  System Function Summary
.qs
.sp
.nf
Function        Function                Input                Output
Number          Name
.sp
Decimal     Hex
.sp
 0           0   System Reset            C = 00H              none
 1           1   Console Input           C = 01H              A = ASCII char
 2           2   Console Output          E = char             none
 3           3   Reader Input                                 A = ASCII char
 4           4   Punch Output            E = char             none
 5           5   List Output             E = char             none
 6           6   Direct Console I/O      C = 06H              A = char or status

                                         E = 0FFH (input) or  (no value)
                                             0FEH (status) or
                                             char (output)
 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 Buffer     DE = Buffer          Console
                                                               Characters
                                                               in Buffer
11           B   Get Console Status      none                 A = 00/non zero
12           C   Return Version Number   none                 HL: Version
                                                                 Number
13           D   Reset Disk System       none                   none
14           E   Select Disk             E = Disk Number        none
15           F   Open File               DE = FCB Address       FF if not found
16          10   Close File              DE = FCB Address       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 in 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 = R/O
                                                                 Vector Value*
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       User Number
                                         E = 00 to 0FH for Set
33          21   Read Random             DE = FCB Address       A = Error Code
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 with Fill  DE = FCB               A = Error Code
.fi
.sp 4
*Note that A = L, and B = H upon return.
.sp 2
.ce
End of Section 5
.nx sixa
