Section 4Programming ExamplesThe programs presented in this section illustrate how to use the BDOS functions described in the previous section. The examples show how to copy a file, how to dump a file, how to create or access a random access file, and how to write an RSX program. | |
A Sample File-To-File Copy ProgramThe following program illustrates simple file operations. You can create the program source file, COPY.ASM, using ED or another editor, and then assemble COPY.ASM using MAC. MAC produces the file COPY.HEX. Use the utility HEXCOM to produce a COPY.COM file that can execute under CP/M 3. The COPY program first sets the stack pointer to a local area, then moves the second name from the default area at 006CH to a 33-byte file control block named DFCB. The DFCB is then prepared for file operations by clearing the current record field. Because the CCP sets up the source FCB at 005CH upon entry to the COPY program, the source and destination FCBs are now ready for processing. To prepare the source FCB, the CCP places the first name into the default FCB, with the proper fields zeroed, including the current record field at 007CH. COPY continues by opening the source file, deleting any existing destination file, and then creating the destination file. If each of these operations is successful, the COPY 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 ; copies the file named x.y from drive ; a to a file named u.v on drive b. ; 0000 = boot equ 0000h ; system reboot 0005 = bdos equ 0005h ; bdos entry point 005c = fcb1 equ 005ch ; first filename 005c = sfcb equ fcb1 ; source fcb 006c = fcb2 equ 006ch ; second filename 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 ; read file func* 0015 = writef equ 21 ; write file func* 0016 = makef equ 22 ; make file func* ; 0100 org tpa ; beginning of tpa 0100 311b02 lxi sp,stack ; local stack ; ; move second filename to dfcb ; 0103 0e10 mvi c,16 ; half an fcb 0105 116c00 lxi d,fcb2 ; source of move 0108 21da01 lxi h,dfcb ; destination 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 moved, zero cr ; 0113 af xra a ; a = 00h 0114 32fa01 sta dfcbcr ; current rec = 0 ; ; source and destination fcbs 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 11da01 lxi d,dfcb ; destination 0127 cd7301 call delete ; remove if present 012a 11da01 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 11da01 lxi d,dfcb ; destination 0144 cd7d01 call write ; write record 0147 11a901 lxi d,space ; ready message 014a b7 ora a ; 0 if write ok 014b c46101 cnz finis ; end if not so 014e c33701 jmp copy ; loop until eof ; ; end of file, close destination ; 0151 11da01 eofile: lxi d,dfcb ; destination 0154 cd6e01 call close ; 255 if error 0157 21bb01 lxi h,wrprot ; ready message 015a 3c inr a ; 255 becomes 0 015b cc6101 cz finis ; should not happen ; ; copy operation complete, end ; 015e 11cc01 lxi d,normal ; ready message ; ; write message given by de, reboot ; 0161 0e09 finis: mvi c,printf 0163 cd0500 call bdos ; write message 0166 c30000 jmp boot ; 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 data space$' 01bb 7772697465wrprot: db 'write protected?$' 01cc 636f707920normal: db 'copy complete$' ; ; data area ; 01da dfcb: ds 33 01fa = dfcbcr equ dfcb+32 01fb ds 32 ; 16 level stack stack: 021b end Note that this program makes several simplifications and could be enhanced. First, it does not check for invalid filenames that could, for example, contain ambiguous references. This situation could be detected by scanning the 32-byte default area starting at location 005CH for ASCII question marks. To check that the filenames have, in fact, been included, COPY could 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. Speed could be improved by buffering more data on each read operation. For example, you could determine the size of memory by fetching FBASE from location 0006H, and use the entire remaining portion of memory for a data buffer. You could also use CP/M 3's Multi-Sector I/O facility to read and write data in up to 16K units. | |
A Sample File Dump UtilityThe following dump program reads an input file specified in the CCP command line, and then displays the content of each record in hexadecimal format at the console. DUMP Program reads input file and displays hex data ; dump program reads input file ; and displays hex data ; 0100 org 100h 0005 = bdos equ 0005h ;bdos 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) 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 128) 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 ; ; open operation ok, set buffer index to end ; 011b 3e80 openok: 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 0123 e5 gloop: 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 ;do not print any more 013c 7c mov a,h 013d cd8f01 call phex 0140 7d mov a,l 0141 cd8f01 call phex 0144 23 nonum: inx h ;to next line number 0145 3e20 mvi a,' ' 0147 cd6501 call pchar 014a 78 mov a,b 014b cd8f01 call phex 014e c32301 jmp gloop ; ; end of dump ; 0151 cd7201 finis: call crlf 0154 2a1502 lhld oldsp 0157 f9 sphl ; ; stack pointer contains ccp's stack location ; 0158 c9 ret ;to the ccp ; ; subroutines ; ; check break key (actually any key will do) ; 0159 e5d5c5 break: 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 ; ; print a character ; 0165 e5d5c5 pchar: 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 ; ; move to start of new line ; 0172 3e0d crlf: mvi a,cr 0174 cd6501 call pchar 0177 3e0a mvi a,lf 0179 cd6501 call pchar 017c c9 ret ; ; print nibble in reg a ; 017d e60f pnib: 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 c657 p10: adi 'a' - 10 018b cd6501 prn: call pchar 018e c9 ret ; ; print hex char in reg a ; 018f f5 phex: 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 ; ; print error message ; d,e addresses message ending with "$" ; 019c 0e09 err: mvi c,printf ;print buffer function 019e cd0500 call bdos 01a1 c9 ret ; ; get next byte ; 01a2 3a1302 gnb: 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 ; ; read the byte at buff+reg a ; 01b3 5f g0: mov e,a ;is 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 ; ; set up file ; open the file for input ; 01c1 af setup: 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 ; ; read disk file record ; 01ce e5d5c5 diskr: 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 66696c6520signon: db 'file dump version 2.0$' 01f3 0d0a6e6f20opnmsg: 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 | |
A Sample Random Access ProgramThis example is an extensive but complete example of random access operation. The following program reads or writes random records upon command from the terminal. When the program has been created, assembled, and placed into a file labeled RANDOM.COM, the CCP level command A>RANDOM X.DAT can start the test program. In this case, the RANDOM program looks for a file X.DAT and, if it finds it, prompts the console for input. If X.DAT is not found, RANDOM creates the file before displaying the prompt. Each prompt takes the form: next command? and is followed by operator input, terminated by a carriage return. The input commands take the form: nW nR nF Q where n is an integer value in the range 0 to 262143, and W, R, F, and Q are simple command characters corresponding to random write, W, random read, R, random write with zero fill, F, and quit processing, Q. If you enter a W or F command, the RANDOM program issues the prompt: type data: You then respond 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 you enter an F command, the RANDOM program fills previously unallocated data blocks with zeros before writing record n. If you enter the R command, RANDOM reads record number n and displays the string value at the console. If you enter the Q command, the X.DAT file is closed, and the program returns to the console command processor. In the interest of brevity, the only error message is: err, 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 program uses the default file control block at 005CH and the default buffer at 0080H in all disk operations. The utility subroutines that follow 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 3 * ;* * ;******************************************* ; 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 0028 = wrtrzf equ 40 ; write random zero fill 0098 = parsef equ 152 ; parse function 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 313603 lxi sp,stack ; ; version 3.1? ; 0103 0e0c mvi c,version 0105 cd0500 call bdos 0108 fe31 cpi 31h ; version 3.1 or better? 010a d21601 jnc versok ; ; bad version, message and go back ; 010d 118102 lxi d,badver 0110 cd3102 call print 0113 c30000 jmp reboot ; ; correct version for random access ; 0116 0e0f versok: mvi c,openf ; open default fcb 0118 3a5d00 rdname: lda fcb+1 011b fe20 cpi ' ' 011d c22c01 jnz opfile 0120 11df02 lxi d,entmsg 0123 cd3102 call print 0126 cd2002 call parse 0129 c31601 jmp versok 012c 115c00 opfile: lxi d,fcb 012f cd0500 call bdos 0132 3c inr a ; err 255 becomes zero 0133 c24b01 jnz ready ; ; cannot open file, so create it ; 0136 0e16 mvi c,makef 0138 115c00 lxi d,fcb 013b cd0500 call bdos 013e 3c inr a ; err 255 becomes zero 013f c24b01 jnz ready ; ; cannot create file, directory full ; 0142 11a002 lxi d,nospace 0145 cd3102 call print 0148 c30000 jmp reboot ; back to ccp ; ;******************************************* ;* * ;* loop back to "ready" after each command * ;* * ;******************************************* ; ; file is ready for processing ; 014b cd3c02 ready: call readcom ; read next command 014e 227d00 shld ranrec ; store input record 0151 217f00 lxi h,ranovf 0154 71 mov m,c ; set ranrec high byte 0155 fe51 cpi 'Q' ; quit? 0157 c26901 jnz notq ; ; quit processing, close file ; 015a 0e10 mvi c,closef 015c 115c00 lxi d,fcb 015f cd0500 call bdos 0162 3c inr a ; err 255 becomes 0 0163 caff01 jz error ; error message 'retry' 0166 c30000 jmp reboot ; back to ccp ; ;************************************** ;* * ;* end of quit command, process write * ;* * ;************************************** ; ; not the quit command, random write? ; 0169 fe57 notq: cpi 'W' 016b c29c01 jnz notw ; ; this is a random write, ; fill buffer until cr ; 016e 11b302 lxi d,datmsg 0171 cd3102 call print ; data prompt 0174 0e7f mvi c,127 ; up to 127 characters 0176 218000 lxi h,buff ; destination ; ; read next character to buff ; 0179 c5 rloop: push b ; save counter 017a e5 push h ; next destination 017b cd0802 call getchr ; character to a 017e e1 pop h ; restore counter 017f c1 pop b ; restore next to fill 0180 fe0d cpi cr ; end of line? 0182 ca8b01 jz erloop ; ; not end; store character ; 0185 77 mov m,a 0186 23 inx h ; next to fill 0187 0d dcr c ; counter goes down 0188 c27901 jnz rloop ; end of buffer? ; ; end of read loop, store 00h ; 018b 3600 erloop: mvi m,0 ; ; write the record to selected record number ; 018d 0e22 mvi c,writer 018f 115c00 lxi d,fcb 0192 cd0500 call bdos 0195 b7 ora a ; error code zero? 0196 c2ff01 jnz error ; message if not 0199 c34b01 jmp ready ; for another record ; ;********************************** ;* * ;* end of write command, * ;* process write random zero fill * ;* * ;********************************** ; ; not the quit command; random write zero fill? ; 019c fe46 notw: cpi 'F' 019e c2cf01 jnz notf ; ; this is a random write; fill buffer until cr ; 01a1 11b302 lxi d,datmsg 01a4 cd3102 call print ; data prompt 01a7 0e7f mvi c,127 ; up to 127 characters 01a9 218000 lxi h,buff ; destination ; ; read next character to buff ; 01ac c5 rloop1: push b ; save counter 01ad e5 push h ; next destination 01ae cd0802 call getchr ; character to a 01b1 e1 pop h ; restore counter 01b2 c1 pop b ; restore next to fill 01b3 fe0d cpi cr ; end of line? 01b5 cabe01 jz erloop1 ; ; not end, store character ; 01b8 77 mov m,a 01b9 23 inx h ; next to fill 01ba 0d dcr c ; counter goes down 01bb c2ac01 jnz rloop1 ; end of buffer? ; ; end of read loop, store 00h ; 01be 3600 erloop1: mvi m,0 ; ; write the record to selected record number ; 01c0 0e28 mvi c,wrtrzf 01c2 115c00 lxi d,fcb 01c5 cd0500 call bdos 01c8 b7 ora a ; error code zero? 01c9 c2ff01 jnz error ; message if not 01cc c34b01 jmp ready ; for another record ; ;*************************************** ;* * ;* end of write commands; process read * ;* * ;*************************************** ; ; not a write command, read record? ; 01cf fe52 notf: cpi 'R' 01d1 c2ff01 jnz error ; skip if not ; ; read random record ; 01d4 0e21 mvi c,readr 01d6 115c00 lxi d,fcb 01d9 cd0500 call bdos 01dc b7 ora a ; return code 00h? 01dd c2ff01 jnz error ; ; read was successful, write to console ; 01e0 cd1502 call crlf ; new line 01e3 0e80 mvi c,128 ; max 128 characters 01e5 218000 lxi h,buff ; next to get 01e8 7e wloop: mov a,m ; next character 01e9 23 inx h ; next to get 01ea e67f ani 7fh ; mask parity 01ec ca4b01 jz ready ; for another command if 00h 01ef c5 push b ; save counter 01f0 e5 push h ; save next to get 01f1 fe20 cpi ' ' ; graphic? 01f3 d40e02 cnc putchr ; skip output if not 01f6 e1 pop h 01f7 c1 pop b 01f8 0d dcr c ; count=count-1 01f9 c2e801 jnz wloop 01fc c34b01 jmp ready ; ;*********************************************** ;* * ;* end of read command, all errors end-up here * ;* * ;*********************************************** ; 01ff 11bf02 error: lxi d,errmsg 0202 cd3102 call print 0205 c34b01 jmp ready ; ;*************************************** ;* * ;* utility subroutines for console i/o * ;* * ;*************************************** ; ; read next console character to a ; 0208 0e01 getchr: mvi c,coninp 020a cd0500 call bdos 020d c9 ret ; ;write character a to console ; 020e 0e02 putchr: mvi c,conout 0210 5f mov e,a ; character to send 0211 cd0500 call bdos ; send character 0214 c9 ret ; ; send carriage return line feed ; 0215 3e0d crlf: mvi a,cr ; carriage return 0217 cd0e02 call putchr 021a 3e0a mvi a,lf ; line feed 021c cd0e02 call putchr 021f c9 ret ; ; read and parse filespec ; 0220 11f002 parse: lxi d,conbuf 0223 0e0a mvi c,rstring 0225 cd0500 call bdos 0228 111203 lxi d,pfncb 022b 0e98 mvi c,parsef 022d cd0500 call bdos 0230 c9 ret ; ; print the buffer addressed by de until $ ; 0231 d5 print: push d 0232 cd1502 call crlf ; new line 0235 d1 pop d 0236 0e09 mvi c,pstring 0238 cd0500 call bdos ; print the string 023b c9 ret ; ; read the next command line to the conbuf ; 023c 11d002 readcom: lxi d,prompt 023f cd3102 call print ; command? 0242 0e0a mvi c,rstring 0244 11f002 lxi d,conbuf 0247 cd0500 call bdos ; read command line ; ; command line is present, scan it ; 024a 0e00 mvi c,0 ; start with 00h 024c 210000 lxi h,0 ; 0000h 024f 11f202 lxi d,conlin ; command line 0252 1a readc: ldax d ; next command character 0253 13 inx d ; to next command position 0254 b7 ora a ; cannot be end of command 0255 c8 rz ; ; not zero, numeric? ; 0256 d630 sui '0' 0258 fe0a cpi 10 ; carry if numeric 025a d27902 jnc endrd ; ; add-in next digit ; 025d f5 push psw 025e 79 mov a,c ; value in ahl 025f 29 dad h 0260 8f adc a ; i*2 0261 f5 push a ; save value * 2 0262 e5 push h 0263 29 dad h ; *4 0264 8f adc a 0265 29 dad h ; *8 0266 8f adc a 0267 c1 pop b ; i*2 + *8 = *10 0268 09 dad b 0269 c1 pop b 026a 88 adc b 026b c1 pop b ; +digit 026c 48 mov c,b 026d 0600 mvi b,0 026f 09 dad b 0270 ce00 aci 0 0272 4f mov c,a 0273 d25202 jnc readc 0276 c33c02 jmp readcom ; ; end of read, restore value in a ; 0279 c630 endrd: adi '0' ; command 027b fe61 cpi 'a' ; translate case? 027d d8 rc ; ; lower case, mask lower case bits ; 027e e6bf ani 10111111b 0280 c9 ret ; return with value in chl ; ;***************************************** ;* * ;* string data area for console messages * ;* * ;***************************************** ; 0281 736f727279badver: db 'sorry, you need cp/m version 3$' 02a0 6e6f206469nospace: db 'no directory space$' 02b3 7479706520datmsg: db 'type data: $' 02bf 6572726f72errmsg: db 'error, try again$' 02d0 6e65787420prompt: db 'next command? $' 02df 656e746572entmsg: db 'enter filename: $' ; ;******************************** ;* * ;* fixed and variable data area * ;* * ;******************************** ; 02f0 21 conbuf: db conlen ; length of console buffer 02f1 consiz: ds 1 ; resulting size after read 02f2 conlin: ds 32 ; length 32 buffer 0021 = conlen equ $-consiz 0312 f202 pfncb: dw conlin 0314 5c00 dw fcb 0316 ds 32 ; 16 level stack stack: 0336 end You could make the following major improvements to this program to enhance its operation. With some work, this program could evolve into a simple data base management system. You could, for example, assume a standard record size of 128 bytes, consisting of arbitrary fields within the record. You could develop a program called GETKEY 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 at 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. This list, sometimes called an inverted index, is an alphabetical list of LASTNAME fields with their corresponding record numbers. You could rename the program shown above to 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 which is a particular key to find in the NAMES.DAT data base. Because the LASTNAME.KEY list is sorted, you can find a particular entry quickly by performing a binary search, similar to looking up a name in the telephone directory. Start at both ends of the list and examine the entry halfway in between and, if not matched, split either the upper half or the lower half for the next search. You will quickly reach the item you are looking for, in log2(n) steps, where you will find the corresponding record number. Fetch and display this record at the console as the program illustrates. At this point, you are just getting started. With a little more work, you can allow a fixed grouping size, which differs from the 128-byte record shown above. You can accomplish this by keeping track of the record number as well as the byte offset within the record. Knowing the group size, you can randomly access the record containing the proper group, offset to the beginning of the group within the record, and read sequentially until the group size has been exhausted. Finally, you can improve QUERY considerably by allowing Boolean expressions that compute the set of records that satisfy several relationships, such as a LASTNAME between HARDY and LAUREL and an AGE less than 45. Display all the records that fit this description. Finally, if your lists are getting too big to fit into memory, randomly access your key files from the disk as well. | |
Construction of an RSX ProgramThis section describes the standard prefix of a Resident System Extension (RSX) and illustrates the construction of an RSX with an example. (See Section 1.6.4 for a discussion of how RSXs operate under CP/M 3.) RSX programs are usually written in assembler, but you can use other languages if the interface between the language and the calling conventions of the BDOS are set up properly. | |
The RSX PrefixThe first 27 bytes of an RSX program contain a standard data structure called the RSX prefix. The RSX prefix has the following format: serial: db 0,0,0,0,0,0 start: jmp ftest ; start of program next: db 0c3h ; jump instruction to dw 0 ; next module in line prev: dw 0 ; previous module remove: db 0ffh ; remove flag nonbank: db 0 ; nonbank flag name: db '123456713' ; any 8-character name loader: db 0 ; loader flag db 0,0 ; reserved area The only fields of the RSX prefix that you must initialize are the remove: flag, the nonbank: flag, and the name: of the RSX. For compatibility with previous releases of CP/M, the serial: field of the prefix is set to the serial number of the operating system by the LOADER module when the RSX is loaded into memory. Thus, the address in location 6 locates the byte following the serial number of the operating system with or without RSXs in memory. The start: field contains a jump instruction to the beginning of the RSX code where the RSX tests to see if this BDOS function call is to be intercepted or passed on to the next module in line. The next: field contains a jump instruction to the next module in the chain or the LOADER module if the RSX is the oldest one in memory. The RSX program must make its own BDOS function calls by calling the next: entry point. The prev: field contains the address of the preceding RSX in memory or location 5 if the RSX is the first RSX in the chain. The remove: field controls whether the RSX is removed from memory by the next call to the LOADER module via BDOS function 59. If the remove: flag is 0FFH, the LOADER removes the RSX from memory. Note that the CCP always calls the LOADER module during a warm start operation. An RSX that remains in memory past warm start because its remove: flag is zero, must set the flag at its termination to ensure its removal from memory at the following warm start. The nonbank: field controls when the RSX is loaded. If the field is 0FFH, the LOADER only loads the module into memory on nonbanked CP/M 3 systems. Otherwise, the RSX is loaded into memory under both banked and nonbanked versions of CP/M 3. The loader: flag identifies the LOADER RSX. When the LOADER module loads an RSX into memory, it sets this prefix flag of the loaded RSX to zero. However, the loader: flag in the LOADER's prefix contains 0FFH. Thus, this flag identifies the last RSX in the chain, which is always the LOADER. | |
Example of RSX UseThese two sample programs illustrate the use of an RSX program. The first program, CALLVERS, prints a message to the console and then makes a BDOS Function 12 call to obtain the CP/M 3 version number. CALLVERS repeats this sequence five times before terminating. The second program, ECHOVERS, is an RSX that intercepts the BDOS Function 12 call made by CALLVERS, prints a second message, and returns the version 0031H to CALLVERS. Although this example is simple, it illustrates BDOS function interception, stack swapping, and BDOS function calls within an RSX. ; callvers program 0005 = bdos equ 5 ; entry point for bdos 0009 = prtstr equ 9 ; print string function 000c = vers equ 12 ; get version function 000d = cr equ 0dh ; carriage return 000a = lf equ 0ah ; line feed 0100 org 100h 0100 1605 mvi d,5 ; perform 5 times 0102 d5 loop: push d ; save counter 0103 0e09 mvi c,prtstr 0105 111e01 lxi d,call$msg ; print call message 0108 cd0500 call bdos 010b 0e0c mvi c,vers 010d cd0500 call bdos ; try to get version ; callvers will intercept 0110 7d mov a,l 0111 323401 sta curvers 0114 d1 pop d 0115 15 dcr d ; decrement counter 0116 c20201 jnz loop 0119 0e00 mvi c,0 011b c30500 jmp bdos 011e 0d0a2a2a2acall$msg: db cr,lf,'**** callvers **** $' 0134 00 curvers: db 0 0135 end ; echovers rsx 0009 = pstring equ 9 ; string print function 000d = cr equ 0dh 000a = lf equ 0ah ; rsx prefix structure 0000 0000000000 db 0,0,0,0,0,0 ; room for serial number 0006 c31b00 jmp ftest ; begin of program 0009 c3 next: db 0c3h ; jump 000a 0000 dw 0 ; next module in line 000c 0000 prev: dw 0 ; previous module 000e ff remov: db 0ffh ; remove flag set 000f 00 nonbnk: db 0 0010 4543484f56 db 'echovers' 0018 000000 db 0,0,0 ; ; is this function 12? ; 001b 79 ftest: mov a,c 001c fe0c cpi 12 001e ca2400 jz begin ; yes - intercept 0021 c30900 jmp next ; some other function 0024 210000 begin: lxi h,0 0027 39 dad sp ; save stack 0028 225300 shld ret$stack 002b 317500 lxi sp,loc$stack 002e 0e09 mvi c,pstring 0030 113e00 lxi d,test$msg ; print message 0033 cd0900 call next ; call bdos 0036 2a5300 lhld ret$stack ; restore user stack 0039 f9 sphl 003a 213100 lxi h,0031h ; return version number 003d c9 ret 003e 0d0a2a2a2atest$msg: db cr,lf,'**** echovers ****$' 0053 0000 ret$stack: dw 0 0055 ds 32 ; 16 level stack loc$stack: 0075 end You can prepare the above programs for execution as follows: 1. Assemble the CALLVERS program using MAC as follows: MAC CALLVERS 2. Generate a COM file for CALLVERS with HEXCOM: HEXCOM CALLVERS 3. Assemble the RSX program ECHOVERS using RMAC: RMAC ECHOVERS 4. Generate a PRL file using the LINK command: LINK ECHOVERS [OP] 5. Rename the PRL file to an RSX file: RENAME ECHOVERS.RSX=ECHOVERS.PRL 6. Generate a COM file with an attached RSX using the GENCOM command: GENCOM CALLVERS ECHOVERS 7. Run the CALLVERS.COM module: CALLVERS The message **** CALLVERS followed by the message **** ECHOVERS **** appears on the screen five times if the RSX program works. | |
End of Section 4 Continue in... |
page URL: www.bigfoot.com/~c128page/cpm3-prg/cpm3prg4.htm
contact: c128page@bigfoot.com