Section V USER'S GUIDE ZCPR3: THE LIBRARIES A Reference Manual and User's Guide for SYSLIB, Z3LIB, and VLIB Written by Richard Conn Copyright 1986 Richard Conn 6s This Page Left Blank ZCPR3: The Libraries USER Introduction 20. INTRODDCTION This is a User's Guide which illustrates techniques for applying the libraries. It is designed to be used as an instructional section of ZCPR3: The Libraries. The earlier sections of this book present the "hard facts" on the library routines, and this User's Guide presents the philosophy behind the design of the library routines and an explanation of how they are intended to interact with each other and work together to provide a flexible, powerful tool set for the assembly language programmer. The earlier sections constitute a Reference Manual. The User's Guide is a tutorial. This User's Guide is not exhaustive. All routines in the libraries are not covered by it, and the guide is intended to give the reader a start in understanding how to use the various routines. From the information presented in this guide, the reader should be able to study the reference sections and effectively make use of all routines in the libraries. 20-1 ZCPR3: The Libraries USER Introduction NOTES: 20-2 ZCPH3: The Libraries USER Input/Output 21. INPUT AND OUTPUT There is frequently a need to send the pair of characters to the console or printer. SYSLIB provides CRLF and LCRLF, which perform this function with the added advantage that they have no effect on any registers, so the programmer does not have to worry about side effects: o CRLF — Output CR LF to the Console o LCRLF — Output CR LF to the LST: Device The pair of SYSLIB Subroutines just discussed is probably the simplest of the entire library. Next to them in complexity are the single character input/output routines: o CIN — Input Character from Console in A o RIN — Input Character from Reader in A o COUT — Output Character in A to Console o LOUT — Output Character in A to LST: Device o POUT — Output Character in A to PUN: Device o CCOUT — Output Character in A to Console with Control Character Processing o CLOUT — Output Character in A to LST: Device with Control Character Processing o CPOUT — Output Character in A to PUN: Device with Control Character Processing Note that these routines perform exactly the function as documented and nothing else. CIN, for example, only inputs a character, and does not echo this character. To input and then echo a character, a sequence like CALL CIN ; input ch CALL COUT ; echo ch would be used. Only the A register is affected by the input routines, and no register is affected by the output routines. The output routines can output characters with or without control character processing. The routines which process control characters will output characters like control-C (binary 3) as "C and the routines which do not do such processing would output the exact binary code. The Console Status routine, CST, returns the status of the console in register A, saying if a key has been pressed or not. If data is available, A=0. It would be used as follows: CALL CST ; get status ORA A ; see if A=0 JNZ NOIN ; routine to process if no input CALL CIN ; get character and process The Conditional Input routine, CONDIN, provides the 21-1 ZCPR3: The Libraries USER Input/Output functions just described in a somewhat simpler and more useful form. CONDIN will sample the console input and return the character if one is available with a flag saying that a character is returned. If a character is not available, CONDIN will simply return with the appropriate flag. The same routine implemented using CONDIN looks like this; CALL CONDIN ; get conditional input JZ NOIN ; routine to process if no input ... ; process char in A from CONDIN String printing is the next step up from simple character input/output. This is done by the print routines; o PRINT — Print String Pointed to by Return Address on Console o LPRINT — Print String Pointed to by Return Address on LST: Device o PSTR — Print String Pointed to by HL on Console o LPSTR — Print String Pointed to by HL on LST: Device A string is a sequence of characters terminated by a binary 0. The programmer could define a string in the following ways: DB 'This is a string',0 DB Odh,0ah,'This is another string',Odh,Oah,0 PRINT and LPRINT print strings pointed to by their return addresses, and they return to the byte following the string they just printed. They would typically be used as follows: CALL PRINT ; Print the following string DB 'This is a test',0 ... ; Continue processing PSTR and LPSTR require that HL points to a valid string before they are called. An example of the use of PSTR is: LXI H.MYNAME ; point to string CALL PSTR ; print it ... ; continue processing MYNAME: DB 'Rick',0 ; string to be printed PSTR and LPSTR affect the HL register pair. Upon exit, HL points to the byte following the indicated strings. These routines were designed in this way to easily allow successive calls to them to print one string after another: 21-2 ZCPR3: The Libraries USER Input/Output LOOP; LXI H,LIST MVI B,0 MOV A,M ORA A JZ DONE INR B MOV A,B CALL PADC MVI A, ' ' CALL COUT CALL PSTR CALL CRLF JMP LOOP DONE; point to first string init counter get first character check to see if list end continue processing increment count get count print number print space (next ch=0) print string print new line continue until end of list continue processing LIST: DB 'Rick',0 DB 'Olivia',0 DB 'Carolyn',0 DB 0 ; first name in list ; second name in list ; third name in list end of list The output from this code segment will be: 1 Rick 2 Olivia 3 Carolyn This example is intended to drive several key points home to the reader: 1. SYSLIB routines are designed to perform a function and have minimal side effects. Note that the B register is used as a counter in this routine, and it isn't saved anywhere. None of these SYSLIB routines have any effect on B. 2. SYSLIB routines are designed to be cohesive. They "bond together" to form a functional module and may be called one after the other to collectively provide a function for the user. 3. SYSLIB has been designed to eliminate a lot of overhead in assembly language programming. Imagine how much more of a programming task it would be if you had to write the PADC (Print A as Decimal Characters with Leading Spaces) routine. A number of SYSLIB routines are designed to output numbers to the user. Both 8-bit numbers and 16-bit numbers may be output, and in all cases, the A register contains the 8-bit number to output and the HL register pair contains the 16-bit number to output. These routines are: 21-3 ZCPR3: The Libraries USER Input/Output o PADC, IADC PADC = Print A as Decimal Characters IADC = List A as D.C. o PA3DC, LA3DC PA3DC = Print A as 3 Decimal Characters o PA2HC, LA2HC Print the number in the A register as up to 3 digits in a 3-character field. If 3 significant digits are not required (there are leading zeroes), print spaces in their places. PADC prints to the Console, LADC prints to the LST: Device Like PADC and LADC, but print 3 digits, including the leading zeroes PA2HC Print A as 2 Hexadecimal Characters Like PA3DC and LA3DC, but print 2 hexadecimal digits, including the leading zeroes o PHLDC, LHLDC PHLDC Print H and L as Decimal Characters o PHL5DC, LHL5DC PHL5DC Print HL as 5 D.C. Print the number in the HL register pair as up to 5 digits in a 5-character field. If 5 significant digits are not required (there are leading zeroes), print spaces in their places. PHLDC prints to the Console, LHLDC prints to the LST: Device Like PHLDC and LHLDC, but print 5 digits, including the leading zeroes o PHL4HC, LHL5HC PHL4HC Print HL as 4 Hex Chars Like PHL5DC and LHL5DC, but print 4 hexadecimal digits, including the leading zeroes None of these print routines have any effects on any registers. They may be used freely to print out values. The following tables provide examples of what outputs would look like from these routines: 21-4 ZCPR3: The Libraries USER Input/Output 8-Bit. Numeric Outputs A Reqister PADC, 0 16 100 255 LADC PA3DC, LA3DC PA2HC, 00 10 64 FF LA2HC 0 16 100 255 000 016 100 255 16-Bit Numeric Outputs HL Register 0 16 100 256 4096 16536 PHLDC, LHLDC PHL5DC, LHL5DC PHL4HC, LHL4HC 0 00000 00016 00100 00256 04096 16536 0000 0010 0064 0100 1000 4000 16 100 256 4096 16536 (Note: _ denotes a space) This sample code segment: LXI H,100 ; set value CALL PRINT ; print text DB 'HL = ',0 CALL PHLDC ; print HL as decimal chars CALL PRINT ; print more text DB ' Decimal or ',0 CALL PHL4HC ; print HL as hexadecimal chars CALL PRINT ; print ending text DB ' Hexadecimal',0 prints this text; HL_=__100JDecimal_or_0064_Hexadecimal (Note: _ denotes a space) HL is NOT AFFECTED by the numeric print routines (PHLDC, PHL4HC) or by the string print routine (PRINT). For that matter, NO register is affected by the calls to these routines. 21-5 ZCPR3: The Libraries USER Input/Output This example, like the previous example, is intended to drive several key points home to the reader: 1. SYSLIB routines are designed to perform a function and have minimal side effects. 2. SYSLIB routines are designed to be cohesive. They "bond together" to form a functional module and may be called one after the other to collectively provide a function for the user. 3. SYSLIB has been designed to eliminate a lot of overhead in assembly language programming. 4. SYSLIB routines are frequently named to serve as mnemonic aids in order to help the programmer remember what the routine does. Flexibility is a key word for SYSLIB, and one good question to ask is what capability does SYSLIB give the programmer to output my numbers in any way I desire (like HL as up to 4 decimal characters instead of 5)? The next set of routines addresses this problem: o MADC ~- Place up to 3 ASCII digit characters into memory which represent the number MADC = Memory in the A register and use leading (store) spaces. The first byte of a three- A as byte memory buffer is pointed to by Dec the DE register pair. Chars o MA3DC — Like MADC, but including leading zeroes o MA2HC — Like MA3DC, but hexadecimal digits o MHLDC — 5 ASCII digits characters stored in memory to represent the value in HL o MHL5DC — Like MHLDC, but including leading zeroes o MHL4HC — Like MHL5DC, but hexadecimal digits DE is affected by these routines. On entry, DE points to the first byte of the buffer used to store the desired characters, and, on exit, DE points to the first byte after the last character stored. These routines are set up this way so that whole strings of text can be sequentially stored in memory with some ease. For example: 21-6 ZCPR3: The Libraries USER Input/Output LXI D,NUM3 ; 3-byte buffer MVI A,10 ; Number 10 CALL MADC ; Store in Buffer LXI H,400 ; Number 400 CALL MHLDC ; Store in 5-byte buffer NUM3: DS 3 NUM5: - DS 5 ; reserve 3 bytes ; reserve 5 bytes Results: Address ASCII Char Address ASCII Char NUM3 (space) NUM5 (space) NUM3+1 1 NUM5+1 (space) NUM3+2 0 NUM5+2 4 NUM5+3 0 NUM5+4 0 21-7 ZCPR3; The Libraries USER Input/Output NOTES: 21-8 The two BDOS-Based in __ and BBLINE. They differ mainly in the way they handle "buSters, and, in both cases, they return with HL pointing to the first character of the string input by the user and the A register containing a count of the number of characters typed (not counting the ending zero). Note that HL is returned as a string pointer, and the input line is stored as a NULL-terminated string of characters in the standard SYSLIB sense. BBLINE contains a 200-byte buffer internally in which the line to be input is stored. This is probably the roost frequently used of the SYSLIB input line editors since it is so simple to use. Its only input is a flag in the A register — if A is 0, the input line is not capitalized, and if A is not 0, the input ZCPR3: The Libraries USER Input Line Editors EXT BBLINE EXT PRINT EXT PSTR Reference BBLINE PRINT STRING ROUTINE PRINT STRING PTED TO BY HL CALL PRINT DB 'What Is Your Name? ',0 XRA A CALL BBLINE A=0 SO DON'T CAPITALIZE LINE GET LINE FROM USER ON RETURN, HL PTS TO FIRST CHAR AND A IS NUMBER OF CHARS TYPED CALL PRINT DB ODH,OAH ; NEW LINE DB 'Your Name is: ',0 CALL PSTR ; PRINT STRING PTED TO BY HL ; ... HL WAS SET BY BBLINE The BLINE routine is similar to BBLINE, with the one difference that the user provides an external buffer. The only advantage of BLINE over BBLINE is that it is smaller (does not contain that 200-byte buffer), but with BLINE the programmer has to worry about allocating buffer space. The same program using BLINE: EXT BLINE EXT PRINT EXT PSTR ; Reference BLINE ; PRINT STRING ROUTINE ; PRINT STRING PTED TO BY HL CALL PRINT DB 'What Is Your Name? ',0 INLINE: BUFF: XRA LXI A=0 SO DON'T CAPITALIZE LINE PT TO BUFFER GET LINE FROM USER ON RETURN, HL PTS TO BUFF AND A IS NUMBER OF CHARS TYPED A H,INLINE CALL BLINE CALL PRINT DB ODH,OAH DB 'Youl CALL PSTR NEW LINE 'Your Name is: ',0 PRINT STRING PTED TO BY HL ... HL WAS SET BY BBLINE SIZE OF BUFFER DB 40 DB 0 DS 41 RETURNED SIZE OF LINE BUFFER SPACE + 1 FOR ENDING 0 22.3. The INLINE Input Line Editor INLINE is intended to be used for secure applications. It has two key features which come into play for such applications; 1. INLINE cannot be aborted by "C. The "C is stored in the input line buffer if it is typed. 2. INLINE can be made to echo its input or not echo its input. If something like a password is being entered, then it may be desirable to not echo it. INLINE does not perform optional capitalization like the other two, and, if capitalization is required, the CAPSTR routine can 22-2 ZCPR3: The Libraries USER Input Line Editors be used immediately after the call to INLINE to correct this problem. As a tradeoff, INLINE takes up more space than either BLINE or BBLINE, and the user still has to provide an external buffer. INLINE will not permit more than 256 characters to be stored, so the buffer size should generally be 256+1 for the ending 0. For inputs, INLINE requires HL to point to the first byte of the buffer and A to contain a 0 if no echo is desired or not 0 if echo is desired. Note that INLINE requires HL to point to the first byte of the buffer itself, and NOT a buffer size value. The example above implemented with INLINE looks like this: DB XRA LXI DB DB Reference INLINE PRINT STRING ROUTINE PRINT STRING PTED TO BY HL EXT INLINE EXT PRINT EXT PSTR CALL PRINT •What Is Your Password? ',0 A=0 SO DON'T ECHO USER INPUT A H.BUFF PT TO BUFFER GET LINE FROM USER CALL INLINE CALL CAPSTR CALL PRINT ODH.OAH ON RETURN, HL PTS TO BUFF CAPITALIZE INPUT STRING NEW LINE * Your Password is; ',0 CALL PSTR BUFF: DS 257 PRINT STRING PTED TO BY HL ... HL WAS SET BY BBLINE BUFFER SPACE + 1 FOR ENDING 0 22-3 ZCPR3: The Libraries USER Input Line Editors NOTES: 22-4 ZCPR3: The Libraries USER Numeric String Evaluation 23. NUMERIC STRING EVALUATION It will be occasionally necessary for the programmer to be able to accept a number as typed in by the user and convert it to a binary form that can be used by the code of the program. SYSLIB contains five routines which perform such conversions. These routines are; o EVAL2 — convert binary digit strings to binary o EVAL8 — convert octal digit strings to binary o EVAL10 — convert decimal digit strings to binary o EVAL16 — convert hexadecimal digit strings to binary o EVAL — determine base employed by user based on an optional suffix character and convert In all cases, the address of the first character of the string is passed to the routine in the HL register pair, and the routine returns a 16-bit value in DE and an 8-bit value in A (A=E). Evaluation of the string progresses from the first character until an invalid digit (for the appropriate base) is encountered, and the routine returns HL pointing to the character which terminated the scan. EVAL2 evaluates binary character strings < only ' 0' and '1' are the valid digits). Strings like "1010" are evaluated and converted to binary. Given a string like "1010B", the routine will stop when it encounters the "B" and return with HL pointing to this character. EVAL8 evaluates octal character strings (only '0' to '7' are the valid digits). EVAL10 evaluates decimal character strings (only '0' to '9' are the digits), and EVAL16 evaluates hexadecimal character strings (only '0' to '9' and 'A' to 'F' are the digits, and case is not Significant with the letters). EVAL accepts strings of the following forms: bbbbbbbbbbbbbbbbB — b='0' or '1'; Binary string oooooooO or oooooooQ — '0' <= o <= '7'; Octal string ttttt or tttttD — '0' <= t <= '9'; Decimal string hhhhH or hhhhX — '0' <= h <= 'F'; Hexadecimal string EVAL determines the type of string used and calls the appropriate routine to perform the conversion. EVAL returns one other flag than the others; Carry Flag Set means that an invalid string format was given, and HL points to the offending character on exit. The following is an example of a code segment which accepts two numbers input by the user and adds them together: 23-1 ZCPR3: The Libraries USER Numeric String Evaluation EXT PRINT EXT BBLINE EXT PHLDC EXT CRLF EXT EVAL ; USE SYSLIB ; USE SYSLIB USE SYSLIB USE SYSLIB USE SYSLIB PRINT STRING ROUTINE INPUT LINE EDITOR PRINT HL AS DEC ROUTINE NEW LINE ROUTINE EVAL ROUTINE CALL PRINT DB 'Input Your CALL BBLINE CALL EVAL JC NUMERR PUSH D MOV A,M CPI ' , ' JNZ CERR INX H CALL EVAL JC NUMERR POP H CALL CRLF DAD D CALL PRINT DB 'The sum of CALL PHLDC ; PROMT USER Numbers, Separated by a Comma: ',0 GET LINE FROM USER HL POINTS TO FIRST ARGUMENT EXTRACT FIRST ARGUMENT ERROR IF CARRY SET SAVE FIRST NUMBER ON STACK GET OFFENDING CHARACTER ERROR IF IT IS NOT A COMMA SKIP OVER THE COMMA GET THE 2ND NUMBER IN DE PROCESS ERROR GET FIRST NUMBER FROM STACK NEW LINE HL=HL+DE these numbers is ',0 PRINT AS DECIMAL •. 23-2 ZCPR3: The Libraries USER Disk Input/Output 24. DISK INPOT/ODTPDT SYSLIB provides a number of conveniences to the programmer when it comes to manipulating the disk under ZCPR3. The library provides the following groups of utilities for disk input/output: 1. Directory Manipulation Routines — Allocate Buffer Space — Extract Disk Parameter Information — Determine Amount of Free Space on Disk — Determine the Size of a Disk File — Load a Disk Directory into Memory — Alphabetize a Loaded Disk Directory — Select Files from a Loaded Disk Directory — Pack (Compress) a Loaded Disk Directory — Combination Utility (Load, Select, Alphabetize, and Pack a Disk Directory) 2. Block-Oriented File Input/Output — Open, Create, and Close a File — Delete a File — Rename a Pile — See if a File Exists — Read and Write Blocks from and to a File 3. Byte-Oriented File Input/Output — Open and Close a File — Read and Write a Byte from and to a File 4. File Control Block (FCB) Manipulation — Set Up an FCB from a String — Initialize an FCB 24.1. Directory Manipulation The directory manipulation routines of SYSLIB are designed with a maximum of generality and flexibility in mind. Acting to relieve the programmer of the task of writing routines which access the directory information on disk, these routines access the disk directory for him and load its contents into a memory buffer in a form which is relatively easy to use. The image of a disk directory as placed in memory by SYSLIB is a series of 16-byte entries organized as follows: ; 1 Byte ; 8 Bytes ; 3 Bytes : 1 Byte i 2 Bytes ! 1 Byte ; !User NuroiFile Name;File Type! Extent 1 Unused ! Rec Cnt! Note that this is identical to the first 16 bytes of the 24-1 ZCPR3: The Libraries USER Disk Input/Output CP/M directory entry as it exists on disk and is specified in the File Control Block (FCB). Note also that the first byte is used to indicate the User Number associated with the file, and NOT the disk number (as this byte is used for in the FCB). Before the disk directory is accessed, however, it is a good idea to use the SYSLIB DBUFFER routine to allocate memory space for the loaded directory entries, especially if the alphabetization routine is to be used later. The DBUFFER routine is passed a pointer to a scratch memory area which extends to the top of the TPA, and it accesses the disk parameter information, allocates pointer-space for the alphabetization routine, checks for Transient Program Area (TPA) overflow, and returns a pointer to the next available byte after the pointer space into which the directory is to be later loaded. The memory address required by DBUFFER can be frequently provided by the CODEND routine (unless the scratch area is already being used by the program, in which case the programmer has to provide this value from within his code). DBUFFER and CODEND fit nicely together for this purpose. The typical calling sequence looks like this: CALL CODEND ; HL now points to the bottom of the scratch area CALL DBUFFER ; HL now points to the bottom of the directory load area ; and A = 0 and Zero Flag is Set (Z) if TPA Overflow JZ TPAOVFL ; Handle Error and Abort The memory map for directory access looks like this: Top of TPA/Bottom of BDOS DIRLOAD or DIRSLOAD Load This ———> DBUFFER Computes This —————> Beginning of Scratch Area End of Program 100H CP/M BDOS Unused Space Directory Entries (16 Bytes) are loaded here Pointer Space for Alphabetization Dead Space CODEND Routine Other SYSLIB Routines Programmer's Code It is assumed that the programmer has already logged in the required disk before DBUFFER is called. If not, the disk parameter information may be in error, and insufficient pointer space may be allocated. DBUFFER calls the routine DPARAMS to determine the specifics on the CP/M disk structure and the information it requires to 24-2 ZCPR3: The Libraries USER Disk Input/Output allocate the pointer space. In particular, DPABAMS returns the maximum number of disk directory entries, and DBUFFER allocates enough pointer space to load all directory entries if necessary. DPARAMS extracts much necessary information for use by SYSLIB in dealing with disk directories. The programmer need not be concerned with exactly what this information is, since SYSLIB uses it internally and provides the programmer with what he needs to know through its subroutines. For the interest of the reader, however, the following information is extracted by DPARAMS for internal SYSLIB use: o Block Shift Factor o Block Mask o Extent Mask o Maximum Number of Blocks on Disk o Maximum Number of Directory Entries Those readers familiar with CP/M internals may be interested to know that this is all of the information required to perform these disk functions. DPARAMS also determines what version of CP/M is being used (1.4 or 2.2 or MP/M) , and extracts this information from the BDOS internals if CP/M 1.4 is being used or from the BDOS Function Calls if CP/M 2.2 or MP/M is being used. Those really interested in pursuing more detail are invited to read the SYSLIB source code. Now that the preliminaries are overwith and the buffers have been properly initialized, the next logical step is to load the disk directory. Two SYSLIB routines are provided to do this: DIRLOAD and DIRSLOAD. Both DIRLOAD and DIRSLOAD load the directory buffer pointed to by HL with the entries of all unerased files on the disk in all user areas of the disk. Both DIRLOAD and DIRSLOAD return a flag in the A register if TPA Overflow occurs (not enough room to store all the directory entries), and, if a TPA overflow has not occurred, then the BC register pair contains the number of directory entries loaded into memory. The tradeoff between DIRLOAD and DIRSLOAD lies in what information is required by the programmer. DIRLOAD executes faster than DIRSLOAD, and it loads only the first entry for each file. DIRSLOAD loads the LAST entry for each file, and if the programmer wishes to later compute the size of.his selected files, DIRSLOAD must be used instead of DIRLOAD. To recap, DIRLOAD and DIRSLOAD provide the "process box" which loads a directory from disk into memory. Our developing program now looks like this: 24-3 ZCPR3: The Libraries USER Disk Input/Output CALL CODEND ; HL now points to the bottom of the scratch area CALL DBUPFER ; HL now points to the bottom of the directory load area ; and A = 0 and Zero Flag is Set (Z) if TPA Overflow JZ TPAOVFL ; Handle Error and Abort CALL DIRLOAD ; Load Disk Directory DIRLOAD/DIRSLOAD only affect BC, so, at this point: HL = address of first directory entry loaded BC = number of directory entries loaded if no error A = error flag (A=0 and Zero Flag Set if TPA Overflow) JZ TPAOVFL ; Handle Error and Abort Now that the disk directory has been loaded into memory, what types of things would one want to do with it? Under SYSLIB, routines are provided to do the following: o Select Entries (Match Target FCB) o Pack Directory Image (Get Rid of Unselected Entries) o Alphabetize the Directory Image o Compute Sizes of Files DIRSEL is the routine used to select entries from a loaded disk directory. DIRSEL requires the following information: HL = address of first entry DE = address of FCB containing of desired files - only the file name and file type fields are used, so a full FCB need not be allocated - only the first 12 bytes of an PCB are required by DIRSEL, and the first byte is ignored BC = number of files in the directory A = selection flag, organized as follows: - Bit 7 = Select Non-System Piles if Set - Bit 6 = Select System Files if Set - Bit 5 = Select Piles in All User Areas if Set - Bits 4-0 = If Bit 5 is 0, user number (5 bits) of user area to select files from Our evolving program now contains a DIRSEL call as the next logical step: 24-4 ZCPR3: The Libraries USER Disk Input/Output CALL CODEND HL now points to the bottom of the scratch area CALL DBUFFER HL now points to the bottom of the directory load area and A = 0 and Zero Flag is Set (Z) if TPA Overflow JZ TPAOVFL ; Handle Error and Abort CALL DIRLOAD ; Load Disk Directory DIRLOAD/DIRSLOAD only affect BC, so, at this point: HL = address of first directory entry loaded BC = number of directory entries loaded if no error A = error flag (A=0 and Zero Flag Set if TPA Overflow) JZ TPAOVFL ; Handle Error and Abort Prepare for DIRSEL Call HL and BC already contained values required by DIRSEL LXI D,FCB ; Point to FCB MVI A.lllOOOOOB ; Select Non-System and System ; Files in All User Areas CALL DIRSEL No Error Code is Returned, and All Registers are Unchanged; Hence, the following important values are retained: ; HL = address of first directory entry loaded BC = number of directory entries loaded Data Buffer Area FCB: DB 0 ; Just Filler DB 'ANYFIL? TXT' ; File Spec, may include wild card of ? DIRSEL selects its files by setting the Most Significant Bit of the User Number field of the selected directory entries to a 1. Hence, since this is all that is changed, DIRSEL may be used repeatedly to select several groups of files, and these groups may include the same files (MYF?? and MXF? may match some of the same files). Since DIRSEL changes no registers, one call after another may be made: LXI D,FCB1 ; Match first set MVI A,11100000B ; Non-Sys, Sys, and All Users CALL DIRSEL LXI D,FCB2 ; Match 2nd set CALL DIRSEL Once all the desired files have been selected, DIRPACK and DIRNPACK are used to pack the directory buffer, leaving in only those entries desired. If DIRPACK is used, entries not marked by DIRSEL are discarded, and the buffer is reorganized to contain only those entries marked by DIRSEL. If DIRNPACK is used, en- tries marked by DIRSEL are discarded (negative selection), and the buffer is reorganized to contain only those entries not marked by DIRSEL. 24-5 ZCPR3: The Libraries USER Disk Input/Output DIRPACK and DIRNPACK require the following inputs: HL = address of first entry in directory buffer BC = number of entries in directory buffer DIRPACK and DIRNPACK return the following values: HL = address of first entry in directory buffer BC = number of desired entries left in directory buffer Only the BC register pair may be changed, and all other registers are not affected. Again, our program now evolves as indicated: CALL CODEND ; HL now points to the bottom of the scratch area CALL DBUFFER ; HL now points to the bottom of the directory load area ; and A = 0 and Zero Flag is Set (Z) if TPA Overflow JZ TPAOVPL ; Handle Error and Abort CALL DIRLOAD ; Load Disk Directory ; DIRLOAD/DIRSLOAD only affect BC, so, at this point: ; HL = address of first directory entry loaded ; BC = number of directory entries loaded if no error ; A = error flag (A=0 and Zero Flag Set if TPA Overflow) JZ TPAOVPL ; Handle Error and Abort ; Prepare for DIRSEL Call ; HL and BC already contained values required by DIRSEL LXI D.FCB ; Point to FCB MVI A,11100000B ; Select Non-System and System ; Files in All User Areas CALL DIRSEL ; No Error Code is Returned, and ; All Registers are Unchanged; Hence, the following important ; values are retained: ; HL = address of first directory entry loaded ; BC = number of directory entries loaded CALL DIRPACK ; Pack Directory ; Only BC is changed, and now: ; BC = number of remaining (desired) directory entries ; This is typically followed by a test to see if BC=0 (no ; entries selected MOV A,B ; See if BC=0 ORA C JZ EMPTY ; Process No Selected File Routine * • • • I ; Data Buffer Area • , FCB: DB 0 ; Just Filler DB 'ANYFIL? TXT' ; File Spec, may include wild card of ? Now that we have loaded the directory from disk, selected the files, and finally packed the loaded directory, we may wish to alphabetize this loaded directory for ease of access and user- friendliness in our program (list the files to the user alphabetically). DIRALPHA is the SYSLIB routine which does this. DIRALPHA requires the following inputs and has no effect on 24-6 ZCPR3: The Libraries USER Disk Input/Output any registers; HL = address of first directory entry BC = number of directory entries to sort A = sort flag; if A=0, sort first by file name and then by file type (HISFILE.TXT comes before MYFILE.MAC), else sort first by file type and then by file name (MYFILE.MAC comes before HISFILE.TXT) Our program has finally evolved into the following: CALL CODEND ; HL now points to the bottom of the scratch area CALL DBDFFER ; HL now points to the bottom of the directory load area ; and A = 0 and Zero Flag is Set (Z) if TPA Overflow JZ TPAOVFL ; Handle Error and Abort CALL DIRLOAD ; Load Disk Directory ; DIRLOAD/DIRSLOAD only affect BC, so, at this point: HL = address of first directory entry loaded BC = number of directory entries loaded if no error A = error flag (A=0 and Zero Flag Set if TPA Overflow) JZ TPAOVFL ; Handle Error and Abort Prepare for DIRSEL Call HL and BC already contained values required by DIRSEL LXI D.FCB ; Point to FCB MVI A,11100000B ; Select Non-System and System ; Piles in All User Areas CALL DIRSEL No Error Code is Returned, and All Registers are Unchanged; Hence, the following important values are retained: HL = address of first directory entry loaded BC = number of directory entries loaded CALL DIRPACK ; Pack Directory Only BC is changed, and now: BC = number of remaining (desired) directory entries This is typically followed by a test to see if BC=0 (no entries selected MOV A,B ; See if BC=0 ORA C JZ EMPTY ; Process No Selected File Routine CALL DIRALPHA ; Alphabetize Directory No error codes are returned, and the following registers are significant: HL = address of first directory entry loaded BC = number of directory entries loaded The programmer may now continue with his specific application Data Buffer Area FCB: DB 0,'ANYFIL? TXT' ; File Spec, may include wild card of ? As the reader can see, the SYSLIB routines are certainly cohesively designed and work together with a minimum of overhead. Since the above sequence of instructions are so frequently needed in exactly the same order, the DIRF and DIRFS routines are 24-7 2CPR3: The Libraries USER Disk Input/Output provided. DIRF and DIRFS perform the following operations in the order indicated: 1. Initialize Buffer Area (DBUFFER call) 2. Load the Disk Directory into the Buffer (DIRF calls DIRLOAD, DIRFS calls DIRSLOAD) 3. Select a Set of Files (DIRSEL call) 4. Pack the Files in the Memory Buffer (DIRPACK call) 5. Alphabetize the Files in the Memory Buffer (DIRALPHA call) by file name and then file type DIRF and DIRFS require the following input parameters: HL = base of dynamic buffer area (as returned by CODEND) DE = address of FCB containing file spec to match (only 1st 12 bytes required) A = selection flag, as for DIRSEL DIRF and DIRFS return the following output parameters: HL = address of first file entry BC = number of file entries A = error flag; A=0 and Zero Flag Set < Read or Write Bytes using GET and PUT > < Close Files for Input or Output > The above example showed how this was done for writing to a file. The $OPEN routines require a 36-byte FCB which is pointed to by DE as an input argument, and they return with the Zero Flag Set if an error occurred. The $CLOSE routines require no input argument (they know what files they are dealing with), and they return with Zero Flag Set if an error occurred. The $GET and $PUT routines only require A to contain the character to output (for the $PUT routines) or input (for the $GET routines), and they return an error flag (Zero Flag Set if Error) and error code (in A) if an error occurs. If an error occurs, A returns the error code; if no error occurs, A is unchanged. 24.4. File Control Block (FCB) Manipulation SYSLIB provides two simple routines for FCB manipulation. The key fields we are interested in an FCB are the file name and file type fields. The FCB used by SYSLIB is always 36 bytes long, and it typically declared with the following code: FCB; DB 0 ; Zero Disk Number DB 'FILENAMETYP' ; File Name and Type DS 4 ; Rest of first 16 bytes DS 16 ; Second 16 bytes DS 4 ; Last required by SYSLIB The first routine is INITFCB. This routine simply clears all of the fields (except the file name and file type fields) of the 36-byte FCB pointed to by DE to zero. It is a nice, quick way to initialize an FCB fully. INITFCB clears the disk number field (the first field) as well, thereby selecting the current disk. It is intended that the user manually select the disk himself rather than allowing the BDOS to auto-select a disk. The second SYSLIB routine discussed here is FNAME. This is a file name string parser. FNAME builds an FCB and extracts user and disk information from a group of characters terminated by a delimiter. On input, HL points to the first byte of the group of characters and DE points to the FCB. On output, the following register values are returned: B = Number of Disk Specified (A=l to P=16 or OFFH if no disk specified) C = User Number Specified (0 to 31, '?' if all users, or OPFH if no user specified) HL = address of delimiter which ended the scan A = error flag; A=0 and Zero Flag Set if invalid disk or user number specified Scanning via FNAME is stopped when a delimiter is found. The following are classified by SYSLIB as delimiters: 24-11 ZCPR3: The Libraries USER Disk Input/Output The group of characters is intended to be a file specifica- tion, similar to the CP/M standard. It is of the general form: du:filename.typ where d=disk letter, u=user number, and the rest is standard. Valid disk letters are A-P, and valid user numbers are 0-31 and the '?' character. The FCB is initialized by this routine, and only the file name and file type fields are set (to the filename.typ parts of the string). All lower case characters are converted to upper case. Examples of valid character sequences are: Sequence test.txt a:t 5:.t cl0:x*.t?t Returned Values File Name = TEST B Reg = OFFH File Name = T B Reg = 1 File Type = TXT C Reg = OFFH File Type = C Reg = OFFH File Name = Pile Type = T B Reg = OFFH C Reg = 5 File Name = X??????? File Type = T?T B Reg =3 C Reg = 10 PNAME is frequently used in processing user input: CALL PRINT DB 'Name of CALL BBLINE LXI D.FCBCALL FNAME JZ FNERR MOV A,B STA DISK MOV A,C STA USER• • •FCB: DS 36 DISK: DS 1 USER: DS 1 File? ',0 ; GET NAME OF FILE FROM USER ; PT TO FCB WITH DE, HL ALREADY PTS TO ; FIRST CHAR OF FILE NAME ; EXTRACT INFORMATION ; ERROR HANDLER ; SAVE DISK NUMBER ; SAVE USER NUMBER NO INITIALIZATION NECESSARY FOR FCB USER-SPECIFIED DISK NUMBER USER-SPECIFIED USER NUMBER 24-12 ZCPR3: The Libraries USER Math 25. MATH SYSLIB contains a module of 16-bit math functions which work with just the HL register pair if only one argument is required or the HL and DE register pairs if two arguments are necessary. In all cases, the HL register pair contains the final result. These math functions affect ONLY the HL register pair and have no effect on any other registers, including the A register and the condition code flags, unless so noted. The math functions provided in SYSLIB are; 0 ADDHD HL = HL + DE 0 SUBHD HL = HL - DE 0 MULHD HL = HL * DE 0 DIVHD HL = HL / DE 0 NEGH HL = 2's Corn] 0 CMPH HL = 1's Corn] 0 ANDHD HL s HL ( Log: 0 ORHD HL = HL ( Log: 0 XORHD HL = HL < Log: 0 ROTLH HL is Ro t ate< The old 0 ROTRH HL is Ro t atei The old 0 SHFTLH HL is Sh i fte< The LSB 0 SHFTRH HL is Sh i fte< MSB and LSB refer to Significant Bit, respectively, logical manner. Refer to the of the error return codes. Most Significant Bit and Least Error Conditions are handled in a reference section for a description 25-1 ZCPR3: The Libraries USER Math NOTES: 25-2 ZCPR3: The Libraries USER Cyclic Redundancy Checks 26. CYCLIC REDUNDANCY CHECKS The SYSLIB CRC routines may be used to check the validity of an incoming serial byte stream of arbitrary length. They are particularly good in making sure that a given file contains valid data or that data transmitted via the phone lines (as per a modem program) was correctly received. These routines compute and check a true 16-bit Cyclic Redundancy Code (CRC), and their use will guarantee the detection of: o all single- and double-bit errors o all errors with an odd number of error bits o all burst errors of length 16 or less Also, the CRC routines will detect the following: o 99.9969% of all 17-bit error bursts o 99.9984% of all possible longer error bursts Three sets of CRC routines are provided. They are: CRC CRC1 CRC2 Function CRCCLR CRC1CLR CRC2CLR Clear CRC Accumulator CRCUPD CRC1UPD CRC2UPD Update CRC Accumulator CRCDONE CRC1DONE CRC2DONE Done and Return CRC Value The polynomials used by these routines are: CRC = X~16 + X~12 + X^5 + 1 CRC1 = X"16 + X~15 + X"2 + 1 CRC2 = X"16 + X-15 + X"13 + X"7 + X-4 + X"2 + X + 1 The SYSLIB programmer need not be concerned with how this polynomial is applied internally ... just that it does its job. The interested reader is invited to examine the source code. As a side note, CRC2 is used by the public domain CRCK program of Keith Petersen. CRC2 is not a TRUE CRC algorithm, but more of a hash coding that has been accepted as a standard. The CRCCLR routine requires no arguments and returns none. It has no effect on any register. The CRCUPD routine updates the CRC Accumulator with the value of the next byte, which is passed to it in the A register. No registers are affected by this routine. The CRCDONE routine returns the calculated 16-bit CRC value in the HL register pair. It simply returns a value and may be called as many times as desired. The following code segment illustrates a typical way to use these routines: 26-1 ZCPR3: The Libraries USER Cyclic Redundancy Checks EXT CRCCLR ; CLEAR CRC EXT CRCUPD ; UPDATE CRC EXT CRCDONE ; RETURN CRC EXT COMPHD ; COMPARE HL TO DE AND RET W/Z IF EQUAL ; ROUTINE TO COMPUTE TRANSMITTED CRC • CRC$MAKE: CALL CRCCLR ; CLEAR ACCUMULATOR < Loop CALLing CRCUPD with Byte Values in A > CALL CRCDONE ; GET VALUE PUSH H ; SAVE IT ; ROUTINE TO EVALUATE INCOMING DATA FOR VALID CRC • CRC$CHECK: CALL CRCCLR ; CLEAR ACCUMULATOR < Loop CALLing CRCUPD with Byte Values in A > CALL CRCDONE ; GET CRC VALUE IN HL POP D ; GET FIRST CRC VALUE IN DE CALL COMPHD ; COMPARE HL TO DE JZ OK ; PROCESS IF OK ; THE ERROR ROUTINE GOES HERE 26-2 ZCPR3: The Libraries USER Sort 27. SORT There are two SYSLIB routines which give the SYSLIB programmer access to a very flexible sorting system. The main routine is called SORT, and it provides a utility which does an in-memory sort of a set of fixed-length records. The sorting technique used is a Shell Sort, adapted from the book Software Tools by Kernigan and Plaugher, published by Addison-Wesly, 1976, page 106. This sort is much faster than the simple bubble sort. The SORT routine can be instructed to perform the in-memory sort in one of two ways -- with or without using pointers. Sorting without using pointers is slower than sorting with pointers, mainly because when using pointers, a swap is done by exchanging pointers (2 byte values), while a sort without pointers requires a swap to completely exchange the n-byte records being sorted. The only advantage to not using pointers is the savings of the space taken up by the pointer table. This table requires 2*(number of entries to be sorted) in bytes. The SORT is controlled by passing a pointer to the Sort Specification Block (SSB) in DE to the SORT routine.. This Sort Specification Block is a series of six 2-byte words which contain the following information: Bytes O&l — Starting Address of 1st Record Bytes 2&3 — Number of Records to Sort Bytes 4&5 — Size of Each Record (in Bytes) Bytes 6&7 — Address of Compare Routine Provided by the Programmer Bytes 8&9 — Address of a Pointer Table (if needed) Byte 10 — Flag; 0 means to use pointers, OFFH not Byte 11 — Unused Two routines are provided in the SSORT module of SYSLIB. They are SSBINIT, which is used to initialize buffers, and SORT, which performs the actual sort. SSBINIT will not be covered here; see the reference section. SORT accepts as input a pointer to an SSB in DE and performs the sort. No errors are generated, and no registers are affected by SORT. An example of a code sequence using SORT follows: 27-1 ZCPR3; The Libraries USER Sort EXT SORT • • • CALL CODEND SHLD SSBPTR LXI D,SSB CALL SORT ; SORT ROUTINE IN SYSLIB ; GET ADDRESS OF SCRATCH AREA FOR PTRS ; SET ADDRESS IN SSB ; POINT TO SSB ; DO THE SORT ; THIS IS THE COMPARE ROUTINE • COMPARE: PUSH H PUSH D PUSH B MVI B,RECSIZ ; SIZE OF RECORDS ; ONLY PSW MAY BE AFFECTED CLOOP: CDONE: LDAX D CMP M JNZ CDONE INX H INX D DCR B JNZ CLOOP POP B POP D POP H RET ; COMPARE (DE) TO (HL) ; RETURN WITH CARRY SET IF (DEX(HL) ; PT TO NEXT BYTE ; COUNT DOWN REQUIRED NUMBER OF BYTES ; RESTORE REGISTERS ; RETURN WITH CARRY SET IF (DE)«HL) ; OR ZERO SET IF (DE)=(HL) 9 THE SORT SI ?ECIFICAT: * 9 SSB: DW REC1 DW NREC DW RECSIZ DW COMPARE ; ADDRESS OF FIRST RECORD ; NUMBER OF RECORDS TO SORT ; SIZE OF EACH RECORD ; ADDRESS OF COMPARE ROUTINE SSBPTR; ; ADDRESS OF POINTER TABLE, FILLED IN DS ABOVE DB 0,0 ; USE POINTERS • REC1: DB • THIS' DB i IS ' DB > A ' DB i TEST' RECSIZ EQU 4 NREC EQU ( $-REC 4-BYTE RECORDS ; SIZE OF EACH RECORD ; NUMBER OF RECORDS 27-2 ZCPR3: The Libraries USER CODEND 28. CODEND There are many times when it is necessary to program and data areas end and the beginning of which extends from the end of your program/data of the Transient Program Area (TPA) is located. a memory map of the situation described: know where your the scratch area areas to the end The following is Top of Memory CP/M BIOS and Buffers CP/M BDOS CP/M CCP Unused Memory Space above your program and its data areas Your Program CP/M Buffers and JMPs 100H Bottom of Memory The routine CODEND in SYSLIB provides the end address of your program/data areas and the beginning of the Unused Memory Space diagrammed above. CODEND is a routine located in the SCODEN module of SYSLIB, and this module is ALWAYS the last module in the library. During the linking process, SYSLIB is to be the last library specified to the linker, and, since SCODEN is the last module in SYSLIB, if the CODEND routine is referenced by the program, then it will be loaded, and it will ALWAYS be the last subroutine in your program. CODEND returns the address of the next page following the last byte of code. The situation described looks like this; End of BDOS Start of BDOS/ End of Scratch •> Next Page After Pgm End of Your Program CODEND Returns This Address 100H BDOS Scratch Space for your work area, as desired Dead Space (Not Used) CODEND Routine Other SYSLIB Routines Your Program Code 28-1 ZCPR3: The Libraries USER CODEND As in the normal ZCPR3 environment, the base of the BDOS can be determined by loading the address of the BDOS call at location 5 into HL: LHLD 6 ; load address of BDOS call At location 5 is a CALL instruction (CALL Address), so the desired Address is at locations 6 and 7, low-order byte at location 6. CODEND can be used to determine the start of the scratch area, so the following code segment illustrates a technique to determine the size of the scratch area. ; SYSLIB EXTERNAL REFERENCES — NOTE THAT ORDER OF THESE ; EXT PSEUDO-OPS IS NOT IMPORTANT EXT CODEND ; CODEND ROUTINE EXT SUBHD ; HL=HL-DE EXT PRINT ; PRINT STRING EXT PHLDC ; PRINT HL AS UP TO 5 DEC CHARS • • • ENTRY EQU 5 ; BDOS ENTRY CALL • • • CALL CODEND ; GET ADDRESS IN HL XCHG ; ... ADDRESS IN DE LHLD ENTRY+1 ; GET ADDRESS OF BDOS BASE MVI L,0 ; SET ON PAGE BOUNDARY CALL SUBHD ; HL=HL-DE (SIZE OF SCRATCH AREA) CALL PRINT DB 'The size of the scratch area is ',0 CALL PHLDC ; PRINT IN DECIMAL CALL PRINT DB ' bytes',0 28-2