.Z80 TITLE WHERFIND Version 3.01 - 07/02/86 - 20:07 PAGE ;************************************************************************ ;* * ;* Written by G.B. Shaffstall, SYSOP Lakewood RCP/M * ;* * ;* Modified for use with ZCPR3 by R.T. Hilgert SYSOP XEROX RCP/M * ;* * ;* Modified for two column output, pagination, combined WHEREIS * ;* with FIND and added extensive annotation 7/2/86 by * ;* Gil Shultz Sysop the REALM - (313) 349-3408 Novi Michigan. * ;* * ;* This program allows full wildcard searches of the directories * ;* and ALL LIBRARY files on a CP/M system for a requested file, * ;* starting on drive A, user 0, working its way up and across all * ;* drive/user areas until a trapped I/O error causes exit or CTL-C * ;* or CTL-X is hit at the keyboard. Optionally, the user can * ;* specify a single drive to be searched by including the drive * ;* name as a prefix to the search file. This is based on SD-81, so * ;* credit is given to the MANY people that have worked on it in the * ;* past. * ;* * ;* Entering WHEREIS will display a brief help message. * ;* * ;* The USER AREA PATCH TABLE is the same as in SD with two * ;* exceptions: [1] if 0FFH is placed in a user location that drive * ;* will be skipped, and [2] If using with ZCPR all entries should * ;* be set to 0FFH in the source file, as the ZCPR conditional code * ;* will re-load the table on entry based on ZCPR's MAXDRIVE and * ;* MAXUSER values. * ;* * ;* Users of this program with hard disks will want to set the QUIET * ;* equate to 0 to provide the message "Searching : User " * ;* so the user will know that the program is still running. With * ;* any other value, the message "+++ NO FILE ON :" will be * ;* printed as the program switches drives and no files are found. * ;* * ;* There is no page pause capability, but unless something like * ;* WHEREIS *.* is used this should be no problem. * ;* * ;************************************************************************ SUBTTL EQUATES PAGE USRMSK EQU 00001111B ; Mask for maximum user number CR EQU 0DH ; carriage return character LF EQU 0AH ; line feed character ESC EQU 1BH ; escape character YES EQU 0FFH ; true NO EQU 0 ; NOT YES ;************************************************************************ ;* * ;* If running with ZCPR3 with NDRS ste ZCPR3 true to display * ;* named directory information. * ;* * ZCPR3 EQU NO ; YES * ;* * ;* Set the following equate true if using ZCPR to limit Drive * ;* user number searching. * ;* * ZCPR EQU YES ; YES * ;* * ;* * ;* Set one and only one of the following equates true for the * ;* program you want. The programs generated will be * ;* * ;* WHEREIS or FIND * ;* * WHERE EQU NO ; set to yes for WHEREIS * FIND EQU YES ; set to yes for FIND * ;* * ;* Set the following equate true for two column output. * ;* * TWOCOL EQU YES ; set to yes for two columns. * ;* * ;* Set the following equate true for pagination. * ;* * PAGNAT EQU YES ; set true for pagination. * ;* * ;************************************************************************ QFLAG EQU 0 ; set to <> 0 for silent mode MDRIV EQU 03DH ; ZCPR Max drive storage location MUSR EQU 03FH ; ZCPR Max user storage location RDCHR EQU 1 ; read char from console WRCHR EQU 2 ; write char to console PRINTS EQU 9 ; print string CONST EQU 11 ; check cons stat RESET EQU 13 ; reset disk system SELDSK EQU 14 ; select disk OPEN EQU 15 ; 0ffh=not found CLOSE EQU 16 ; " " SEARCH EQU 17 ; " " NEXT EQU 18 ; " " READ EQU 20 ; not 0 = eof WRITE EQU 21 ; not 0 = disk full MAKE EQU 22 ; 0ffh = directory full CURDSK EQU 25 ; get currently logged disk name SETDMA EQU 26 ; set current dma GALLOC EQU 27 ; get address of allocation vector CURDPB EQU 31 ; get current disk parameters CURUSR EQU 32 ; get currently logged user number (2.x only) BASE EQU 0 ; default to 0 FCB EQU BASE+5CH ; force relocatable code to 5CH BDOS EQU BASE+5 ; force relocatable code to 05H COND ZCPR3 PAGE .8080 INCLUDE Z3BASE.LIB .Z80 PAGE ENDC ; ZCPR3 ;************************************************************************ ;* * ;* BEGIN EXECUTABLE PROGRAM CODE * ;* * ;************************************************************************ JP START ; goto entry point SUBTTL DRIVE / USER TABLE PAGE ;************************************************************************ ;* * ;* Drive code/user area lookup table * ;* * ;* Note that the lodrv-hidrv table is included here fully * ;* configured. For your own use, you should change the maximum * ;* user areas as appropriate for each drive on your system. * ;* Note that there are only 16 user areas available under cp/m * ;* 2, so the highest legal user area you can specify is 15 * ;* (range 0-15 = 16 areas). The program will convert anything * ;* over 15 into mod 15. Enter an 0ffh to cause a drive to be * ;* skipped on search. NOTE: set to 0FFH for ZCPR use. * ;* * ;************************************************************************ LODRV: ; mark beginning of drive/user table DEFB 0FFH ; maximum user area for drive A DEFB 0FFH ; " " " " " B DEFB 0FFH ; " " " " " C DEFB 0FFH ;---"-----"----"----"----"----D DEFB 0FFH ; " " " " " E DEFB 0FFH ; " " " " " F DEFB 0FFH ; " " " " " G DEFB 0FFH ;---"-----"----"----"----"----H DEFB 0FFH ; " " " " " I DEFB 0FFH ; " " " " " J DEFB 0FFH ; " " " " " K DEFB 0FFH ;---"-----"----"----"----"----L DEFB 0FFH ; " " " " " M DEFB 0FFH ; " " " " " N DEFB 0FFH ; " " " " " O DEFB 0FFH ;---"-----"----"----"----"----P HIDRV: ; mark end of drive/user table SUBTTL LOCAL STORAGE, MESSAGES ETC. PAGE QUIET: DEFB QFLAG ; patch to non zero for quiet mode BDOLOC: DEFW 0 COND WHERE VERNAME: DEFB CR,LF,'WHEREIS v 3.01 for the REALM - 07/02/86 - 20:07',CR,LF DEFB CR,LF,'A Total Wild-Card FILE including Libraries.',CR,LF DEFB CR,LF,'Syntax to search all drives and user areas' DEFB CR,LF,'is WHEREIS ',CR,LF DEFB CR,LF,'Examples:' DEFB CR,LF,'A0>WHEREIS *.AQ6 or A0>WHEREIS MBOOT*.*',CR,LF DEFB CR,LF,'To search a single drive and all user areas' DEFB CR,LF,'Use WHEREIS ',CR,LF DEFB CR,LF,'Examples:' DEFB CR,LF,'A0>WHEREIS B:MDM???.* or A0>WHEREIS D:KP*.*',CR,LF DEFB CR,LF,'Pagination is ' COND PAGNAT DEFB 'on' ENDC COND (NOT PAGNAT AND 0FFH) DEFB 'off' ENDC DEFB CR,LF,'Two column output is ' COND TWOCOL DEFB 'on' ENDC COND (NOT TWOCOL AND 0FFH) DEFB 'off' ENDC DEFB CR,LF,CR,LF+80H PROCES: DEFB CR,LF,' Searching ' PROC1: DEFB ' : User',' '+80H SIGNON: DEFB CR,LF,'WHEREIS v 3.01 for the REALM - 07/02/86 - 20:07',CR,LF DEFB ' ^X or ^C to abort',CR,LF,CR,LF+80H ENDC COND FIND VERNAME: DEFB CR,LF,'FIND v 3.01 for the REALM - 07/03/86 - 20:07',CR,LF DEFB CR,LF,'A Total Wild-Card FILE Search Utility.',CR,LF DEFB CR,LF,'Syntax to search all drives and user areas' DEFB CR,LF,'is FIND ',CR,LF DEFB CR,LF,'Examples:' DEFB CR,LF,'A0>FIND *.AQ6 or A0>FIND MBOOT*.*',CR,LF DEFB CR,LF,'To search a single drive and all user areas' DEFB CR,LF,'Use FIND ',CR,LF DEFB CR,LF,'Examples:' DEFB CR,LF,'A0>FIND B:MDM???.* or A0>FIND D:KP*.*',CR,LF DEFB CR,LF,'Pagination is ' COND PAGNAT DEFB 'on' ENDC COND (NOT PAGNAT AND 0FFH) DEFB 'off' ENDC DEFB CR,LF,'Two column output is ' COND TWOCOL DEFB 'on' ENDC COND (NOT TWOCOL AND 0FFH) DEFB 'off' ENDC DEFB CR,LF,CR,LF+80H PROCES: DEFB CR,LF,' Searching ' PROC1: DEFB ' : User',' '+80H SIGNON: DEFB CR,LF,'FIND v 3.01 for the REALM - 07/03/86 - 20:07',CR,LF DEFB ' ^X or ^C to abort',CR,LF,CR,LF,+80H ENDC ;******************************** ;* * ;* Initialized data area * ;* * ;******************************** COND WHERE LBRTYP: DEFB 'LBR' ENDC VERBAD: DEFB '+++ Needs CP/M 2.0 or Newer to RU','N'+80H DREMSG: DEFB '+++ Driv','e' OR 80H ERRMS1: DEFB ' ' ERRMS2: DEFB 'Erro','r' OR 80H USRMSG: DEFB 'User ','#' OR 80H NOFMS1: DEFB '+++ NO FILE ON ' NOFMS2: DEFB ' ',':' OR 80H VECTBL: DEFW DSKERR ; bdos sector error intercept vector DEFW DSKERR ; bdos select error intercept vector DOPFLG: DEFB 0 SUBTTL UNITIALIZED STORAGE AREA PAGE BASUSR: DEFB 0 ; dupe of original dir. user # to search BLKMAX: DEFW 0 ; highest block # on drive BLKMSK: DEFB 0 ; sec/blk - 1 BLKSHF: DEFB 0 ; # shifts to mult by sec/blk COUNT: DEFW 0 ; entry count DIRMAX: DEFW 0 ; highest file # in directory GAP: DEFW 0 ; sort routine storage HITRAP: DEFB 0 ; highlight trap (previously typed char) I: DEFW 0 ; sort routine storage J: DEFW 0 ; sort routine storage JG: DEFW 0 ; sort routine storage LZFLG: DEFB 0 ; 0 when printing leading zeros MAXUSR: DEFB 0 ; max user # for drive from lookup table NEWUSR: DEFB 0 ; contains user # selected by "$u" option NEXTT: DEFW 0 ; next table entry OLDDSK: DEFB 0 ; holder for currently logged-in drive OLDUSR: DEFB 0 ; contains user number upon invocation SCOUNT: DEFW 0 ; # to sort SUPSPC: DEFB 0 ; leading space flag for decimal routine TBLOC: DEFW 0 ; pointer to start of name table TEMP: DEFW 0 ; save dir entry VERFLG: DEFB 0 ; cp/m version number (0=pre-cp/m 2) FNDFLG: DEFB 0 ; file found flag LLENLOC: DEFW 0 ; running total of .lbr length LCOUNT: DEFW 0 NEXTL: DEFW 0 SLFILE: DEFW 0 SEARN: DEFS 11 ; holding area for search name COND WHERE LBRFCB: DEFS 36 ; library FCB LBBUF: DEFS 80H ; library buffer ENDC SUBTTL MAIN PROGRAM CODE PAGE ;**************************************** ;* * ;* Main program starts here. * ;* * ;**************************************** START: LD HL,0 ; clear HL ADD HL,SP ; hl=old stack LD (STACK),HL ; save it LD SP,STACK ; get new stack COND ZCPR LD HL,LODRV ; point at drive user table PUSH HL ; and get it to DE POP DE ; got it INC DE ; up bye one (setup for LDIR) LD A,(MUSR) ; get maximum user ; DEC A ; adjust it LD (LODRV),A ; stuff maximum user in table LD B,0 ; clear hi byte LD A,(MDRIV) ; get maximum drive OR A ; test for drive A JP Z,NOMOVE ; yep, only drive so move nothing DEC A ; adjust drive number (gfs) JP Z,NOMOVE ; nope, exit LD C,A ; set up counter DEFB 0EDH,0B0H ; LDIR NOMOVE: ENDC XOR A ; get a zero LD (FNDFLG),A ; clear file found flag LD (NEWUSR),A ; make new user = 0 LD (BASUSR),A ; duplicate it if multi-disk mode LD C,12 ; get and save the cp/m version # CALL BDOS ; get version from system LD A,L ; get it LD (VERFLG),A ; and save it LD (DOPFLG),A ; do not allow multi-drive yet CP 20H ; set carry if cp/m 1.4 JP C,VERERR ; exit on earlier than 2.0 LD HL,FCB+1 ; point to name LD A,(HL) ; any specified? CP ' ' ; test JP Z,NONERR ; so print help info PUSH HL ; save fcb address LD DE,SEARN ; point to search name holding area LD B,11 ; size of file name, type CALL MOVE ; move it POP HL ; restore fcb address LD E,0FFH ; get current user number LD C,CURUSR ; set it up CALL CPM ; and do it LD (OLDUSR),A ; initialize startup user number CLNON: LD C,CURDSK ; set it up CALL CPM ; get current disk nr LD (OLDDSK),A ; save for reset if needed LD HL,FCB ; set up pointer LD A,(HL) ; get drive name for directory search OR A ; any specified? JP NZ,START2 ; yes skip next routine XOR A LD (DOPFLG),A ; ok let multi-drive in LD A,1 ; otherwise, get disk "a" START2: LD (HL),A ; put the absolute drive code in ; ..directory fcb CKREST: LD DE,SIGNON ; point to hello message CALL PRINT ; and say hello LD A,(DOPFLG) ; get flag OR A ; and test it CALL Z,SWAPEM ; swap bdos error vector tables ;******************************************************** ;* * ;* Validate drive code and user area numbers * ;* from the drive table. * ;* * ;******************************************************** NOOPT: LD DE,DREMSG ; get the drive/user error message PUSH DE ; save on stack LD A,(FCB) ; get directory drive code DEC A ; normalize to range of 0-15 CP HIDRV-LODRV ; compare with maximum drives on-line JP NC,ERXIT ; take drive error exit if out of range LD HL,USRMSG ; switch to user # error message EX (SP),HL LD E,A ; use drive code as index into table LD D,0 ; clear hi byte so offset is ok USRCK: LD HL,LODRV ; point to base of drive/user table ADD HL,DE ; add offset into table LD A,(HL) ; get the maximum user # for this drive CP 0FFH ; check for skip drive JP Z,ERXIT ; exit if not wanted USRCK2: AND USRMSK ; make sure its in range 0 - 15 (gfs) LD (MAXUSR),A ; save it for later LD HL,NEWUSR ; point to the directory user area CP (HL) ; compare it with the maximum JP C,ERXIT ; take error exit if user number illegal POP DE ; destroy error message pointer LD HL,FCB+1 ; point to name ;******************************************************** ;* * ;* Make fcb all '?' to search for every file * ;* * ;******************************************************** WCD: LD B,11 ; fn+ft count QLOOP: LD (HL),'?' ; store '?' in fcb INC HL ; bump FCB pointer DEC B ; count off JP NZ,QLOOP ; and loop till finished GOTFCB: LD A,'?' ; force wild extent LD (FCB+12),A CALL SETSRC ; set dma for bdos media change check LD HL,FCB ; point to fcb drive code for directory LD E,(HL) ; get the drive code out of the fcb DEC E ; normalize drive code for select LD C,SELDSK ; select the directory drive to retrieve CALL CPM ; ..the proper allocation vector LD C,CURDPB ; it is 2.x or mp/m...request dpb CALL BDOS ; put the system to work INC HL ; bump pointer to INC HL ; block shift location LD A,(HL) ; get block shift LD (BLKSHF),A ; save block shift factor INC HL ; bump to block mask LD A,(HL) ; get block mask LD (BLKMSK),A ; get it saved INC HL ; bump pointer to max block INC HL ; there LD E,(HL) ; get max block # INC HL ; bump pointer LD D,(HL) ; get mask EX DE,HL ; swap pointers LD (BLKMAX),HL ; save it EX DE,HL ; and back, we saved it INC HL ; bump pointer LD E,(HL) ; get directory size INC HL ; and bump again LD D,(HL) ; get size EX DE,HL ; swap pointers LD (DIRMAX),HL ; save max # of entries in directory ;************************************************************************ ;* * ;* Reenter here on subsequent passes while in the all-users mode * ;* * ;************************************************************************ SETTBL: LD A,(QUIET) ; check if in quiet mode OR A ; test JP NZ,SETTB1 ; yes so skip printout LD A,(FCB) ; get drive ADD A,'A'-1 ; ASCIFY it LD (PROC1),A ; and stuff it away LD DE,PROCES ; show the user what area is being CALL PRINT ; worked on. CALL TYPUSR ; now display user number COND ZCPR3 LD A,' ' ; get a space CALL TYPE ; and send it CALL SHONDR ; now show drive ENDC ; ZCPR3 SETTB1: LD HL,(DIRMAX) ; get directory maximum again INC HL ; directory size is dirmax+1 ADD HL,HL ; double directory size LD DE,ORDER ; to get size of order table ADD HL,DE ; allocate order table LD (TBLOC),HL ; name tbl begins where order tbl ends LD (NEXTT),HL ; save in both places EX DE,HL ; swap pointers LD HL,(BDOS+1) ; make sure we have room to continue LD A,E ; get low SUB L ; and subtract LD A,D ; now get hi SBC A,H ; and subtract, carry = ok JP NC,OUTMEM ; tell not enough memory LD A,(NEWUSR) ; get user area for directory LD E,A ; save it LD C,CURUSR ; get the user function CALL CPM ; ..and set new user number ;************************************************ ;* * ;* Look up the fcb in the directory * ;* * ;************************************************ LD A,'?' ; get a question mark LD HL,FCB+12 ; set pointer LD (HL),A ; match all extents INC HL ; up pointer LD (HL),A ; match all s1 bytes INC HL ; up pointer LD (HL),A ; match all s2 bytes LD HL,0 ; now clear match counter LD (COUNT),HL ; initialize match counter CALL SETSRC ; set dma for directory search LD C,SEARCH ; get 'SEARCH FIRST' function JP LOOK ; ..and go search for 1st match ;**************************************** ;* * ;* Read more directory entries * ;* * ;**************************************** MORDIR: LD C,NEXT ; search next LOOK: LD DE,FCB ; point to FCB CALL CPM ; read directory entry INC A ; check for end (0ffh) JP Z,SPRINT ; if no more, sort & print what we have ;**************************************** ;* * ;* Point to directory entry * ;* * ;**************************************** SOME: DEC A ; undo previous 'INR A' AND 3 ; make modulus 4 ADD A,A ; multiply... ADD A,A ; ..by 32 because ADD A,A ; ..each directory ADD A,A ; ..entry is 32 ADD A,A ; ..bytes long LD HL,BASE+81H ; point to buffer (skip to fn/ft) ADD A,L ; point to entry ADD A,9 ; point to system byte LD L,A ; save (can't carry to h) LD A,(HL) ; get system byte OR A ; check bit 7 JP M,MORDIR ; skip that file SYSFOK: LD A,L ; go back now SUB 10 ; back to user number (alloc flag) LD L,A ; hl points to entry now LD A,(NEWUSR) ; get current user CP (HL) ; test it JP NZ,MORDIR ; ignore if different INC HL ;******************************** ;* * ;* Move entry to table * ;* * ;******************************** EX DE,HL ; entry to de LD HL,(NEXTT) ; next table entry to hl LD B,11 ; entry length (name, type, extent) TMOVE: LD A,(DE) ; get entry char AND 7FH ; remove attributes LD (HL),A ; store in table INC DE ; bump pointers INC HL ; both of them DEC B ; more? JP NZ,TMOVE ; yep, move it INC DE ; de->> s1 INC DE ; de->> s2 LD A,(DE) ; get s2 byte, overflow=int(extents/32) PUSH HL ; save hl LD L,A ; set up 16-bit multiply LD H,0 ; clear hi LD B,5 ; set up shift factor CALL SHLL ; hl is now # of overflow extents DEC DE ; de->> s1 DEC DE ; de->> extent LD A,(DE) ; get extent ADD A,L ; add it in LD L,A ; save in L LD A,H ; get HI ADC A,0 ; add in carry LD H,A ; hl now has total extents LD B,7 ; get shift factor CALL SHLL ; hl now has total sectors less last ext INC DE ; de->> s1 INC DE ; de->> s2 INC DE ; point to sector count LD A,(DE) ; get it ADD A,L ; add it LD L,A ; save in L LD A,H ; get HI ADC A,0 ; and add in carry LD H,A ; hl now has total sectors EX (SP),HL ; do some fancy shuffling EX DE,HL ; swap pointers EX (SP),HL ; and swap with top of stack EX DE,HL ; swap them back LD (HL),D ; get value INC HL ; and bump pointer LD (HL),E ; now stuff it POP DE ; all back to normal... INC HL ; up pointer LD (NEXTT),HL ; save updated table address EX DE,HL ; swap it LD HL,(COUNT) ; bump the # of matches made INC HL ; up counter LD (COUNT),HL ; and save count LD HL,13 ; size of next entry ADD HL,DE ; add offset EX DE,HL ; future nextt is in de LD HL,(BDOS+1) ; pick up tpa end LD A,E ; get low SUB L ; compare nextt-tpa end LD A,D ; now get hi SBC A,H ; now set flag JP C,MORDIR ; if tpa end > nextt, loop back for more OUTMEM: CALL ERXIT ; exit if directory too large DEFB 'Memor','y' OR 80H ;************************************************ ;* * ;* Shift HL left by n bits (in B) * ;* * ;************************************************ SHLL: ADD HL,HL ; shift left by one DEC B ; count down shift count RET Z ; return if finished JP SHLL ; else shift again ;**************** ;* * ;* Sort * ;* * ;**************** SPRINT: CALL SETFOP ; return to file output dma & user # LD HL,(COUNT) ; get file name count LD A,L ; do 16 bit test OR H ; any found? JP Z,PRTOTL ; exit if no files found PUSH HL ; save file count LD (SUPSPC),A ; enable leading zero suppression ;**************************************** ;* * ;* Initialize the order table * ;* * ;**************************************** LD HL,(TBLOC) ; get start of name table EX DE,HL ; into de LD HL,ORDER ; point to order table LD BC,13 ; entry length BLDORD: LD (HL),E ; save low order address INC HL ; bump pointer LD (HL),D ; save high order address INC HL ; bump pointer EX DE,HL ; table address to hl ADD HL,BC ; point to next entry EX DE,HL ; swap EX (SP),HL ; save tbl address, fetch loop counter DEC HL ; count down loop LD A,L ; do 16 bit test OR H ; more? EX (SP),HL ; (restore tbl address, save counter) JP NZ,BLDORD ; yes, go do another one POP HL ; clean loop counter off stack LD HL,(COUNT) ; get count LD (SCOUNT),HL ; save as # to sort DEC HL ; only 1 entry? LD A,L ; do test OR H ; for all 16 bits JP Z,NOOUT ; yes, so skip sort ;******************************************************** ;* * ;* This sort routine is adapted from Software * ;* Tools by Kernigan and Plaugher. * ;* * ;******************************************************** SORT: LD HL,(SCOUNT) ; number of entries L0: OR A ; clear carry LD A,H ; gap=gap/2 RRA LD H,A LD A,L RRA LD L,A OR H ; is it zero? JP Z,NOOUT ; then none left LD A,L ; make gap odd OR 1 LD L,A LD (GAP),HL INC HL ; i=gap+1 L2: LD (I),HL EX DE,HL LD HL,(GAP) LD A,E ; j=i-gap SUB L LD L,A LD A,D SBC A,H LD H,A L3: LD (J),HL EX DE,HL LD HL,(GAP) ; jg=j+gap ADD HL,DE LD (JG),HL LD A,13 ; compare 13 characters {sfk} CALL COMPARE ; compare (j) and (jg) JP P,L5 ; if a(j)<=a(jg) LD HL,(J) EX DE,HL LD HL,(JG) CALL SWAP ; exchange a(j) and a(jg) LD HL,(J) ; j=j-gap EX DE,HL LD HL,(GAP) LD A,E SUB L LD L,A LD A,D SBC A,H LD H,A JP M,L5 ; if j>0 goto l3 OR L ; check for zero JP Z,L5 JP L3 L5: LD HL,(SCOUNT) ; for later EX DE,HL LD HL,(I) ; i=i+1 INC HL LD A,E ; if i<=n goto l2 SUB L LD A,D SBC A,H JP P,L2 LD HL,(GAP) JP L0 ;******************************************************** ;* * ;* Sort is all done - print entries that compare * ;* * ;******************************************************** NOOUT: LD HL,(COUNT) ; get count LD (LCOUNT),HL ; and save it LD HL,ORDER ; initialize order table pointer LD (NEXTL),HL ; get pointer LD (NEXTT),HL ; and save it ;******************************************************** ;* * ;* Output the directory files we've matched. * ;* * ;******************************************************** ENTRY: LD HL,(COUNT) ; get count DEC HL ; dock file count LD (COUNT),HL ; and save it LD A,H ; is this the last file? OR L ; test all 16 bits JP Z,OKPRNT ; if count=0, last file so skip compare ;**************************************************************** ;* * ;* Compare each entry to make sure that it isn't part * ;* of a multiple extent file. Go only when we have the * ;* last extent of the file. * ;* * ;**************************************************************** CALL CKABRT ; check for abort code from keyboard LD HL,(NEXTT) ; get pointer LD A,11 ; set up length of filename.ext CALL COMPR ; does this entry match next one? JP NZ,OKPRNT ; no, print it INC HL ; bump pointer INC HL ; skip since highest extent last in list LD (NEXTT),HL ; save it JP ENTRY ; loop back for next lowest extent OKPRNT: LD HL,(NEXTT) ; get order table pointer LD E,(HL) ; get low order address INC HL ; bump pointer LD D,(HL) ; get high order address INC HL ; bump pointer LD (NEXTT),HL ; save updated table pointer EX DE,HL ; table entry to hl ;************************************************ ;* * ;* Put in user & drive printout here. * ;* * ;************************************************ PUSH HL ; save pointer on stack LD HL,(TFILES) ; get count INC HL ; bump it up by one LD (TFILES),HL ; and update count POP HL ; restore original pointer CALL COMPS ; match what we are looking for ? JP NZ,OKEXIT ; no, so don't print it PUSH HL ; save pointer on stack LD HL,(TMATCH) ; get match count INC HL ; add one to it LD (TMATCH),HL ; and save match count POP HL ; restore pointer from stack CALL GCRLF ; space it out LD A,(FCB) ; ..precede new line with drive name ADD A,'A'-1 ; ascify it CALL TYPE ; and display drive CALL TYPUSR ; now display user number LD A,':' ; tag header with a colon and a space CALL TYPE ; ..and exit back to entry COND ZCPR3 CALL SHONDR ENDC ; ZCPR3 LD A,' ' ; get a space CALL TYPE ; and display it LD B,8 ; file name length CALL TYPEIT ; type filename LD A,'.' ; period after fn CALL TYPE ; display dot LD B,3 ; display 3 characters of filetype CALL TYPEIT ; now display file extension LD D,(HL) ; now calculate size of file INC HL ; bump pointer LD E,(HL) ; size in de (sectors) LD A,(BLKMSK) ; get block mask PUSH AF ; save on stack ADD A,E ; add to sectors LD E,A ; and put back in E LD A,D ; now get hi ADC A,0 ; add in carry bit LD D,A ; and put it back POP AF ; restore block mask CPL ; and test it AND E ; mask it LD E,A ; size in de (sectors rounded to blocksize) LD B,3 ; set up shift factor SHRR: LD A,D ; get value OR A ; clear carry RRA ; rotate (shift) i LD D,A ; put it back LD A,E ; get HI RRA ; shift it with carry LD E,A ; and put it back DEC B ; test to see if shift is finished JP NZ,SHRR ; no, do it again EX DE,HL ; get file size ;******************************************************** ;* * ;* Output the size of the individual file. * ;* * ;******************************************************** CALL DECPRT ; ..go print it LD A,'K' ; ..and follow with k size CALL TYPE ; terminate file size with a 'K' LD A,0FFH ; get flag value LD (FNDFLG),A ; set file found flag ;******************************************************** ;* * ;* One file output - test to see if we have to * ;* output another one. * ;* * ;******************************************************** OKEXIT: LD HL,(COUNT) ; get current file counter and test it LD A,H ; take hi byte OR L ; and test with low JP Z,PRTOTL ; if no more files exit to summary output JP ENTRY ; else do next ;******************************************************** ;* * ;* Compute the size of the file/library and * ;* update our summary datum. This has been changed * ;* into a subroutine so that both the file size * ;* computation and a library size (when printing * ;* out library members) can be computed in K. * ;* * ;******************************************************** DOIT: LD E,(HL) ; get extent # LD D,0 ; clear hi INC HL ; up pointer LD A,(HL) ; get sector count of last extent EX DE,HL ADD HL,HL ; # of extents times 16k ADD HL,HL ADD HL,HL ADD HL,HL EX DE,HL ; save in de LD HL,BLKMSK ; get block mask ADD A,(HL) ; round last extent to block size RRCA ; adjust it RRCA ; convert from sectors to k RRCA ; adjusted AND 1FH ; now mask it LD L,A ; add to total k LD H,0 ; clear hi ADD HL,DE ; add it in LD A,(BLKMSK) ; get sectors/blk-1 RRCA ; adjust it RRCA ; convert to k/blk RRCA ; adjusted AND 1FH ; now mask it CPL ; use to finish rounding AND L ; mask it LD L,A ; and all finished RET ; return, were finished ;**************************************************************** ;* * ;* Print HL in decimal with leading zero suppression. * ;* * ;**************************************************************** DECPRT: XOR A ; clear leading zero flag LD (LZFLG),A ; flag cleared LD DE,-1000 ; print 1000'S DIGIT CALL DIGIT ; print digit LD DE,-100 ; etc. CALL DIGIT ; print digit LD DE,-10 ; print 100's digit CALL DIGIT ; do it LD A,'0' ; get 1'S DIGIT ADD A,L ; add just it JP TYPE ; and print it DIGIT: LD B,'0' ; start off with ascii 0 DIGLP: PUSH HL ; save current remainder ADD HL,DE ; subtract JP NC,DIGEX ; quit on overflow POP AF ; throw away remainder INC B ; bump digit JP DIGLP ; loop back DIGEX: POP HL ; restore pointer LD A,B ; get data CP '0' ; zero digit? JP NZ,DIGNZ ; no, type it LD A,(LZFLG) ; leading zero? OR A ; test it LD A,'0' ; get ascii zero JP NZ,TYPE ; print digit LD A,(SUPSPC) ; get space suppression flag OR A ; see if printing file totals RET Z ; yes, don't give leading spaces LD A,' ' ; get a space JP TYPE ; leading zero...print space DIGNZ: LD (LZFLG),A ; leading zero flag so next zero prints JP TYPE ; and print digit ;**************************************** ;* * ;* Now check for libraries * ;* * ;**************************************** COND WHERE PRTOTL: LD HL,(LCOUNT) ; how many files did we see? LD A,H ; get hi byte OR L ; and do 16 bit test with low CALL NZ,PRTLMEM ; skip the .lbr check if none found XOR A ; get a zero to... LD (SUPSPC),A ; suppress leading spaces in totals ENDC COND FIND PRTOTL: XOR A ;get a zero to... LD (SUPSPC),A ;suppress leading spaces in totals ENDC ;**************************************************************** ;* * ;* Directory for one user area completed. If 'ALL USERS' * ;* option is selected, then go do another directory on * ;* the next user number until we exceed the maximum user * ;* # for the selected drive. * ;* * ;**************************************************************** NXTUSR: CALL CKABRT ; check for user abort first LD A,(MAXUSR) ; no abort - get maximum user number LD HL,NEWUSR ; bump directory user number INC (HL) CP (HL) ; does next user # exceed maximum? JP NC,SETTBL ; continue if more user areas to go LD A,(BASUSR) ; reset base user number for the LD (HL),A ; ..next directory search ;**************************************************************** ;* * ;* Directory for all user areas completed. If the * ;* multi-disk option is enabled and selected, reset to * ;* the base user area and repeat the directory for next * ;* drive on-line until we either exceed the drives in our * ;* lodrv-hidrv table, or the BDOS shuts us down with a * ;* select or bad sector error, which will be intercepted * ;* back to the exit module. * ;* * ;**************************************************************** NXTDSK: LD HL,FNDFLG ; get file found flag LD A,(HL) ; get flag LD (HL),0 ; clear file found flag for next drive OR A ; test old flag JP NZ,NDSK ; continue if at least 1 file found LD A,(QUIET) ; check output mode OR A ; test it JP Z,NDSK ; if zero, next CALL CRLF ; clean up lint LD A,(FCB) ; stash ascii dir. drive in no file msg ADD A,'A'-1 ; ascify it LD (NOFMS2),A ; and stuff it LD DE,NOFMS1 ; print "no file on ? - " CALL PRINT ; display it NDSK: LD A,(DOPFLG) ; get flag OR A ; and test it JP NZ,EXIT ; ok, exit CALL CKABRT ; check for user abort first LD A,HIDRV-LODRV ; get maximum drive code to search LD HL,FCB ; bump directory fcb drive code INC (HL) ; up pointer CP (HL) ; does next disk exceed maximum? JP C,EXIT ; if so, exit LD E,(HL) ; get value LD D,0 ; clear hi DEC E ; count down LD HL,LODRV ; get it ADD HL,DE ; add offset LD A,(HL) ; pick up value from table CP 0FFH ; and test it JP Z,NDSK ; search next disk if maxdr not true JP NOOPT ; onward ;**************************************************************** ;* * ;* Print the user number of the directory in decimal. * ;* * ;**************************************************************** TYPUSR: LD A,(NEWUSR) ; get user number CP 10 ; if user no. > 9 print leading 1 JP C,DUX ; ok, print LD A,'1' ; else set leading user number CALL TYPE ; display it LD A,(NEWUSR) ; print low digit of user # SUB 10 ; adjust it DUX: ADD A,'0' ; ascify it JP TYPE ; and display it COND TWOCOL GCRLF: LD A,(HCOUNT) ; get count CP 5 ; see if at start RET C ; yep, exit LD A,' ' ; get a space CALL TYPE ; send a space LD A,(HCOUNT) ; get count CP 5 ; see if at start RET C ; yep, exit CP 30 ; see if library displayed JP C,SPACXX ; space it out CP 50 ; test line length JP NC,CRLF ; else space line out SEPCRL: LD A,(NEWUSR) ; get user number CP 10 ; test it JP NC,SEPLNG ; space for < 10 LD A,' ' ; get a space CALL TYPE ; and send space SEPLNG: LD A,'|' ; get fence character CALL TYPE ; and display it LD A,' ' ; get a space CALL TYPE ; now display it RET ; return were finished SPACXX: PUSH HL ; save the world PUSH DE PUSH BC PUSH AF LD B,15 ; number of columns to move LD HL,XXFIL ; space filled string CALL TYPEIT ; send spaces POP AF ; and restore the world POP BC POP DE POP HL JP SEPCRL ; send separator XXFIL: DEFB ' ' ENDC COND (NOT TWOCOL AND 0FFH) GCRLF: ENDC ;******************************************************** ;* * ;* Send a carriage return line feed to console. * ;* * ;******************************************************** CRLF: LD A,CR ; send cr CALL TYPE ; display it LD A,LF ; send lf JP TYPE ; exit to caller from type ;**************************************************************** ;* * ;* Output character in a to console, and optionally * ;* to printer and/or the output file. * ;* * ;**************************************************************** TYPE: PUSH BC ; save the world PUSH DE PUSH HL PUSH AF ; save the character to output CALL TYPE1 ; send it to console COND PAGNAT LD A,(HCOUNT) ; get hor count INC A ; up count LD (HCOUNT),A ; and save updated count POP AF ; get character PUSH AF ; and save on stack AND 01111111B ; kill parity bit CP LF ; test if at end JP NZ,TYPRET ; do exit LD A,0 ; get a zero LD (HCOUNT),A ; and update counter LD A,(VCOUNT) ; get vertical count INC A ; up count LD (VCOUNT),A ; save new count CP 60 ; 60 lines per page JP C,TYPRET ; not at end, onward LD B,6 ; 6 blank lines FRMFED: PUSH BC ; save it LD A,CR ; we go around the normal CALL TYPE1 ; output routine so we LD A,LF ; don't get trapped in CALL TYPE1 ; a loop ; CALL CRLF ; clean line POP BC ; get count DJNZ FRMFED ; count it down LD A,0 ; get a zero LD (VCOUNT),A ; up count ENDC ; pagnat TYPRET: POP AF ; restore the output character POP HL ; exit from type POP DE POP BC RET ; back, were finished ;******************************** ;* * ;* Output character * ;* * ;******************************** TYPE1: LD E,A ; get character into bdos entry register LD C,WRCHR ; set up function JP BDOS ; call conout via the bdos ;************************************************ ;* * ;* Print a string at HL of length B * ;* * ;************************************************ TYPEIT: LD A,(HL) ; pick up character AND 07FH ; mask parity bit off CALL TYPE ; go display it INC HL ; point to next character DEC B ; and count down characters JP NZ,TYPEIT ; to display RET ; return if none left ;************************************************ ;* * ;* Print string terminated with last * ;* byte high on console * ;* * ;************************************************ PRINT: LD A,(DE) ; pick up character PUSH AF ; save on stack AND 7FH ; mask it (no 8th bit) CALL TYPE ; send to console POP AF ; get original back OR A ; test it RET M ; back if MSB set INC DE ; else bump pointer JP PRINT ; and do next ;******************************************************** ;* * ;* Fetch character from console (without echo) * ;* * ;******************************************************** CINPUT: LD HL,(BASE+1) ; get warm boot vector LD L,9 ; get input offset CALL GOHL ; go to conin routine in bios AND 7FH ; mask any parity bit RET ; and back to caller, we have it ;**************************************************************** ;* * ;* Check for a CTL-C or CTL-S entered from the keyboard. * ;* Jump to exit if CTL-C, pause on CTL-S. * ;* * ;**************************************************************** CKABRT: LD HL,(BASE+1) ; get warm boot vector LD L,6 ; check status of keyboard CALL GOHL ; any key pressed? OR A ; test RET Z ; no, return to caller CALL CINPUT ; get character CP 'C'-40H ; ctl-c? JP Z,EX0 ; if ctl-c then quit CP 'X'-40H ; ctl-x? JP Z,EX0 ; if ctl-x then quit CP 'S'-40H ; ctl-s? RET NZ ; no, return to caller CALL CINPUT ; yes, wait for another char. CP 'C'-40H ; might be ctl-c JP Z,EX0 ; if ctl-c then quit CP 'X'-40H ; might be ctl-x JP Z,EX0 ; if ctl-x, else fall thru and continue RET ; all finished, exit ;************************************************ ;* * ;* Kludge to allow call to address in HL * ;* * ;************************************************ GOHL: JP (HL) ;******************************************************** ;* * ;* Entry to BDOS saving all extended registers * ;* * ;******************************************************** CPM: PUSH BC ; save them registers PUSH DE PUSH HL CALL BDOS ; LD B,A ; save return code LD A,(VERFLG) ; is this 3.0? CP 30H ; see if a zero LD A,B ; get value JP C,CPM20 ; no, exit normally CP 0FFH ; it is 3.0 - was return code 0FFH? JP NZ,CPM20 ; no, exit normally LD A,H ; 3.0 and a=0FFH - check for error code OR A ; test it JP NZ,DSKERR ; trap out if we got a physical error LD A,B ; else continue normally CPM20: POP HL ; restore the world POP DE POP BC RET ; and back to caller, were finished ; ;**************************************************************** ;* * ;* For file output mode, return to old user area and * ;* set dma for the file output buffer. * ;* * ;**************************************************************** SETFOP: LD A,(OLDUSR) ; get user number at startup LD E,A ; set it up LD C,CURUSR ; set up function CALL CPM ; reset the old user number RET ; and back, were finished ;**************************************************************** ;* * ;* Move disk buffer dma to default buffer for directory * ;* search operations and bdos media change routines * ;* (necessary for pre-cp/m 2 systems while in file output * ;* mode with an active buffer). * ;* * ;**************************************************************** SETSRC: LD DE,BASE+80H ; point to default DMA address SET2: LD C,SETDMA ; set up function call JP CPM ; and set dma address ;**************************************** ;* * ;* Compare routine for sort. * ;* * ;**************************************** COMPR: PUSH HL ; save table address LD E,(HL) ; load low order INC HL ; bump pointer LD D,(HL) ; load high order INC HL ; bump it again LD C,(HL) ; pick up data INC HL ; bump pointer LD B,(HL) ; and last piece of information ;******************************************************** ;* * ;* BC, DE now point to entries to be compared. * ;* * ;******************************************************** EX DE,HL ; swap pointers LD E,A ; get count CMPLP: LD A,(HL) ; get character AND 7FH ; mask parity bit LD D,A ; save it in D LD A,(BC) ; get next character AND 7FH ; mask parity bit CP D ; test for match INC HL ; up source pointer INC BC ; up compare pointer JP NZ,NOTEQL ; quit on mismatch DEC E ; or end of count JP NZ,CMPLP ; loop till end of string NOTEQL: POP HL ; restore pointer RET ; cond code tells all ;************************************************ ;* * ;* Swap entries in the order table. * ;* * ;************************************************ SWAP: LD BC,ORDER-2 ; table base ADD HL,HL ; *2 ADD HL,BC ; + base EX DE,HL ADD HL,HL ; *2 ADD HL,BC ; + base LD C,(HL) ; get low address of pointer LD A,(DE) ; and do it for other pointer EX DE,HL ; swap pointers LD (HL),C ; swap low pointer value LD (DE),A ; swaped INC HL ; bump pointer INC DE ; bump pointers, both of them LD C,(HL) ; get hi address LD A,(DE) ; and the other EX DE,HL ; swap pointers LD (HL),C ; swap hi pointer value LD (DE),A ; swaped RET ; and back, were finished ;******************************** ;* * ;* New compare routine. * ;* * ;******************************** COMPARE: LD BC,ORDER-2 ; table base ADD HL,HL ; *2 ADD HL,BC ; + base EX DE,HL ADD HL,HL ; *2 ADD HL,BC ; + base EX DE,HL ; swap pointers LD C,(HL) ; get low value INC HL ; bump pointer LD B,(HL) ; get low test value EX DE,HL ; bump pointer LD E,(HL) ; get hi value INC HL ; bump pointer LD D,(HL) ; get it EX DE,HL ; swap pointers LD E,A ; count CMPLPE: LD A,(HL) ; get character AND 7FH ; mask out 8th bit LD D,A ; save it in D LD A,(BC) ; get test character AND 7FH ; mask out 8th bit CP D ; see if match (z flag) INC BC ; up pointers INC HL ; both of them RET NZ ; no match, exit DEC E ; else count down string length JP NZ,CMPLPE ; and loop till all match RET ; if we get here, they matched ;******************************** ;* * ;* --- Error exit --- * ;* * ;******************************** ERXIT: CALL CRLF ; space down POP DE ; get pointer to message string CALL PRINT ; print it LD DE,ERRMS1 ; print " error" CALL PRINT ; send message CALL CRLF ; space down ;************************************************ ;* * ;* --- Exit - all done restore stack --- * ;* * ;************************************************ EXIT: LD A,(DOPFLG) ; check multi disk mode OR A ; set cpu flag JP NZ,EX0 ; onward CALL CKABRT ; check for user abort first LD A,HIDRV-LODRV ; get maximum drive code to search LD HL,FCB ; bump directory fcb drive code INC (HL) ; up pointer CP (HL) ; does next disk exceed maximum? JP C,EX0 ; onward JP NOOPT ; search next disk if maxdr not true TMMSG: DEFB CR,LF,LF,'Files Matched -',' '+80H TCMSG: DEFB CR,LF,'Files Checked -',' '+80H TLMSG: DEFB CR,LF,'Libraries Searched -',' '+80H TFILES: DEFW 0 TLIBRA: DEFW 0 TMATCH: DEFW 0 EX0: LD DE,TMMSG ; point to matched message CALL PRINT ; display it LD HL,(TMATCH) ; get count CALL DECPRT ; and tell how many matched LD DE,TCMSG ; point to checked message CALL PRINT ; display it LD HL,(TFILES) ; get checked count CALL DECPRT ; and tell him how many LD DE,TLMSG ; point to library message CALL PRINT ; display it LD HL,(TLIBRA) ; get count CALL DECPRT ; and tell how many libraries checked LD C,CONST ; check console status CALL CPM ; get status OR A ; char waiting? LD C,RDCHR ; assume one waiting CALL NZ,CPM ; gobble up char LD A,(VERFLG) ; or error mode, depending on version CP 30H ; test for match JP C,EXIT0 ; nope, cpm 2.xx LD C,45 ; get value LD E,0 ; set error mode back to default CALL CPM ; go do it JP EXIT1 ; onward EXIT0: LD A,(DOPFLG) ; ..if they were swapped OR A ; test flag CALL Z,SWAPEM ; reverse them if so EXIT1: LD A,'L'-'@' ; form feed character CALL TYPE1 ; go send to top of page LD HL,(STACK) ; get old stack pointer LD SP,HL ; move back to old stack RET ; ..and return to ccp VERERR: LD DE,VERBAD ; abort cp/m is version 1.? VERER1: CALL PRINT ; display message JP EXIT1 ; and abort NONERR: LD DE,VERNAME ; print help info JP VERER1 ; tell him ;******************************************************** ;* * ;* Trap BDOS select and sector error vectors to * ;* our own intercept routine so we can catch a * ;* reference to an illegal drive. * ;* * ;******************************************************** SWAPEM: LD A,(VERFLG) ; check version CP 30H ; see if error mode call is available JP C,SWAP20 ; if not, use bdos error vectors LD C,45 LD E,0FFH ; use set error mode call CALL CPM ; set "return code only" mode RET ; and back, were done SWAP20: LD HL,(BDOLOC) ; get pointer to base of bios LD A,L ; get low value OR H ; test with hi JP NZ,SWAPO0 ; yep, onward LD HL,BASE+6 ; get bdos address SWAPO0: LD L,9 ; set pointer to error vectors SWAPOK: LD DE,VECTBL ; exchanging with our own vector table LD A,4 ; 4 bytes to swap SWAPLP: LD B,(HL) ; get byte from hl EX DE,HL LD C,(HL) ; get byte from de LD (HL),B ; put byte from hl EX DE,HL LD (HL),C ; put byte from de INC HL ; bump exchange pointers INC DE DEC A ; dock counter JP NZ,SWAPLP ; continue swapping till done RET ;************************************************ ;* * ;* Recovery point from intercepted BDOS * ;* select and bad sector errors. * ;* * ;************************************************ DSKERR: LD SP,STACK ; get out of bdos' STACK JP EXIT ; ..and exit back to ccp COND WHERE ;================================================================ ;= = ;= SUBROUTINES TO READ LIBRARY FILE DIRECTORY = ;= = ;================================================================ PRTLMEM: LD HL,SEARN+8 ; get value CALL CKLBR ; see if library entry RET Z ; nope, exit LD HL,ORDER ; initialize order table pointer LD (NEXTL),HL ; save pointer ENTRYL: LD HL,(LCOUNT) ; get fcb count DEC HL ; decrement it LD (LCOUNT),HL ; and update counter LD A,H ; is this the last file? OR L ; see if count = 0 JP Z,LBRTST ; if count=0, last file skip compare PUSH BC ; save it CALL CKABRT ; check for abort code from keyboard LD HL,(NEXTL) ; get value LD A,11 ; set up length of filename CALL COMPR ; does this entry match next one? POP BC ; restore it JP NZ,LBRTST ; no, print it INC HL ; up pointer INC HL ; skip, highest extent comes last in list LD (NEXTL),HL ; and save new pointer JP ENTRYL ; loop back for next lowest extent ;**************************************** ;* * ;* Exit library member printing * ;* * ;**************************************** LBEXIT: XOR A ; get a zero to... LD (SUPSPC),A ; suppress leading spaces in totals RET ; back, were finished ;************************************************ ;* * ;* Valid entry obtained - spit it out. * ;* * ;************************************************ LBRTST: LD HL,(NEXTL) ; get order table pointer LD E,(HL) ; get low order address INC HL ; up pointer LD D,(HL) ; get high order address INC HL ; and up it again LD (NEXTL),HL ; save updated table pointer LD HL,8 ; set up offset ADD HL,DE ; add offset CALL CKLBR ; go test it JP NZ,LBRNEX ; onward PUSH DE ; now put DE into HL POP HL ; done ;************************************************ ;* * ;* Saves the library file name into lbrfcb * ;* * ;************************************************ LD A,(FCB) LD DE,LBRFCB ; to LD (DE),A INC DE LD B,11 ; length CALL MOVE ; do the move EX DE,HL LD B,25 CLMFCB: LD (HL),0 ; clear memory <- HL INC HL ; up it by 1 DEC B ; count down JP NZ,CLMFCB ; and clear rest of FCB CALL SETLDMA ; set up xfer address LD DE,LBRFCB ; point to file LD C,OPEN ; get function CALL CPM ; open it LD C,READ ; set up read function LD DE,LBRFCB ; point to FCB CALL CPM ; read a record CALL SETFOP ; set it up LD HL,LBBUF ; set up pointer LD A,(HL) ; get byte OR A ; and see if zero JP Z,CKLDIR ; check directory present? LMLEXI: CALL LBCLOSE ;******************************** ;* * ;* Do next library file * ;* * ;******************************** LBRNEX: LD HL,(LCOUNT) ; check count LD A,H ; do a 16 bit test OR L ; tested JP Z,LBEXIT ; no more, all done JP ENTRYL ; else, get next .lbr file ;******************************** ;* * ;* Close the library file. * ;* * ;******************************** LBCLOSE: LD DE,LBRFCB ; point to FCB LD C,CLOSE ; set up function CALL CPM ; have the system close it RET ; and back to caller, were finished ;************************************************ ;* * ;* Set the library file dma address. * ;* * ;************************************************ SETLDMA: LD A,(NEWUSR) ; get user area for directory LD E,A ; set user up LD C,CURUSR ; get the user function CALL CPM ; ..and set new user number LD DE,LBBUF ; point to buffer LD C,SETDMA ; set up xfer address function CALL CPM ; set xfer address RET ; and back to caller, were finished ;******************************************************** ;* * ;* Check to see if there indeed is a lbr file * ;* directory and barf if not! * ;* * ;******************************************************** CKLDIR: LD B,11 ; length of file name LD A,' ' ; space INC HL ; up pointer CKDLP: CP (HL) ; see if we match JP NZ,LMLEXI ; nope, onward DEC B ; else count down INC HL ; up pointer JP NZ,CKDLP ; and loop back ;******************************************************** ;* * ;* The first entry in the LBR directory is indeed * ;* blank. Now see if the directory size is >0. * ;* * ;******************************************************** LD E,(HL) ; file starting location low INC HL ; must be zero here LD A,(HL) ; file starting location high OR E ; must be zero here also JP NZ,LMLEXI ; nope, onward INC HL ; yep, bump pointer LD E,(HL) ; get library size low INC HL ; point to library size high LD D,(HL) ; get library size high LD A,D ; set it up OR E ; library must have some size JP Z,LMLEXI ; yep, onward DEC DE ; back it down EX DE,HL ; swap pointers LD (SLFILE),HL ; save pointer LD B,3 ; LD HL,17 ; set up offset ADD HL,DE ; add offset PUSH HL ; save on stack LD HL,(TLIBRA) ; get count INC HL ; increment it LD (TLIBRA),HL ; and save it POP HL ; restore value JP LMTEST ; onward LFMLOP: LD HL,(SLFILE) ; get count LD A,L ; get low OR H ; and do 16 bit test JP Z,LMLEXI ; zero, exit DEC HL ; count down LD (SLFILE),HL ; and save new count CALL SETLDMA ; set up the xfer address LD C,READ ; read a block of data function LD DE,LBRFCB ; point to library FCB CALL CPM ; read the block in CALL SETFOP LD B,4 ; get file count per sector LD HL,LBBUF ; get buffer starting address LMTEST: LD A,(HL) ; get member open flag OR A ; test for open JP Z,PRMNAM ; if zero, onward LMTESA: LD DE,32 ; member not open get offset ADD HL,DE ; to next and add it in. DEC B ; is buffer empty ? JP NZ,LMTEST ; no so test next entry JP LFMLOP ; yes get next buffer... PRMNAM: PUSH HL ; print member name and size PUSH BC CALL CKABRT ; check for abort code from keyboard PRMNA1: POP BC ; now we swap HL POP HL ; and BC PUSH HL ; save on the stack PUSH BC ; for later INC HL ; bump pointer PUSH HL ; save as we update count LD HL,(TFILES) ; get count INC HL ; bump it by one LD (TFILES),HL ; and save count POP HL ; now restore the pointer CALL COMPS ; match what we are looking for ? JP NZ,LBGNXT ; nope, onward PUSH HL ; save it on the stack LD HL,(TMATCH) ; get match count INC HL ; up it by one LD (TMATCH),HL ; and save it for later POP HL ; get pointer back from stack CALL GCRLF ; changed for two column output LD A,(FCB) ; ..precede new line with drive name ADD A,'A'-1 ; ascify it CALL TYPE ; and display drive CALL TYPUSR ; now display user LD A,':' ; tag header with a colon and a space CALL TYPE ; ..and exit back to entry COND ZCPR3 CALL SHONDR ENDC ; ZCPR3 LD A,' ' ; get a space CALL TYPE ; and display it after drive-user-: LD B,8 ; file name length CALL TYPEIT ; display file name LD A,'.' ; period after fn CALL TYPE ; display the dot LD B,3 ; display 3 characters of filetype CALL TYPEIT ; display the extension INC HL ; bump the pointer by 2 INC HL LD E,(HL) ; get value INC HL ; bump pointer LD D,(HL) ; we have the 16 bit value EX DE,HL ;******************************************************** ;* * ;* Output the size of the individual file. * ;* * ;******************************************************** PUSH DE ; save on stack PUSH HL PUSH HL LD HL,(LLENLOC) ; get pointer PUSH HL ; switch around on stack POP DE ; swapping POP HL ; swapped ADD HL,DE ; add offset and form pointer LD (LLENLOC),HL ; save new pointer POP HL ; and restore old pointer ;******************************************************** ;* * ;* New code added to convert lib members from * ;* sectors to 'K'. * ;* * ;* Upon entry member's size in sectors is in HL. * ;* * ;******************************************************** EX DE,HL ; put it in de LD HL,0 ; zero out hl LD A,E ; put low byte of sector count in a ADD A,7 ; add seven to always round up 1k RRCA ; convert it to k RRCA RRCA AND 1FH ; mask it LD E,A ; and put it back LD L,D ; get the high byte if any LD D,0 ; clean out the old resting place ADD HL,HL ; multiply it by 32 to convert to ADD HL,HL ; number ADD HL,HL ; of ADD HL,HL ; k ADD HL,HL ; bytes ADD HL,DE ; and add in the low byte POP DE CALL DECPRT ; ..go print it LD A,'K' ; ..and follow with size CALL TYPE ; display the 'K' LD HL,INLBF ; set pointer LD B,3 ; gfs spacing CALL TYPEIT ; and display it LD HL,LBRFCB+1 ; point to filename LD B,8 ; file name length CALL TYPEIT ; display file name LD A,'.' ; period after fn CALL TYPE ; display dot LD B,3 ; display 3 characters of filetype CALL TYPEIT ; display extension LD A,0FFH ; get flag value LD (FNDFLG),A ; set file found flag ;******************************************************** ;* * ;* At least one more file to output - can we * ;* put it on the current line? * ;* * ;******************************************************** LBGNXT: POP BC ; restore the pointers POP HL ; from the stack JP LMTESA ; .. and go output another file ENDC ; whereis COMPS: PUSH HL ; save the world PUSH DE PUSH BC LD BC,SEARN ; set pointer LD E,11 ; set count to filename.ext length COMPS1: LD A,(HL) ; get character AND 7FH ; mask parity bit LD D,A ; and save it in D LD A,(BC) ; get test character INC BC ; up pointers INC HL ; both of them AND 7FH ; mask parity from test character CP '?' ; see if ambiguous character JP Z,COMPS2 ; yep, onward CP D ; else see if match JP NZ,COMPS3 ; nope, onward COMPS2: DEC E ; count down filename.ext length JP NZ,COMPS1 ; loop back till finished COMPS3: POP BC ; now we restore the world POP DE POP HL RET ; and back to caller, were finished INLBF: DEFB ' > ' ;******************************************************** ;* * ;* Move characters from 'HL' to 'DE' length in 'B' * ;* * ;******************************************************** MOVE: LD A,(HL) ; get a character LD (DE),A ; store it INC HL ; to next 'FROM' INC DE ; to next 'TO' DEC B ; more? JP NZ,MOVE ; yes, loop RET ; no, return COND ZCPR3 SHONDR: PUSH AF PUSH BC PUSH DE PUSH HL PUSH IX LD IX,Z3NDIR-18 LD BC,18 LD A,(FCB) LD D,A LD A,(NEWUSR) LD E,A SHONDR1: ADD IX,BC LD A,(IX+0) OR A JR Z,SHONDR6 LD H,(IX+0) LD L,(IX+1) XOR A SBC HL,DE JR NZ,SHONDR1 LD B,8 INC IX LD A,'<' CALL TYPE SHONDR2: INC IX LD A,(IX+0) CP ' ' JR Z,SHONDR3 CALL TYPE DJNZ SHONDR2 SHONDR3: LD A,'>' CALL TYPE LD A,B OR A JR Z,SHONDR5 SHONDR4: LD A,' ' CALL TYPE DJNZ SHONDR4 SHONDR5: POP IX POP HL POP DE POP BC POP AF RET SHONDR6: LD B,10 JR SHONDR4 ENDC ; ZCPR3 COND WHERE ;**************************************** ;* * ;* Test file extent for LBR. * ;* * ;**************************************** CKLBR: PUSH HL ; save the world PUSH DE PUSH BC ; EX DE,HL ; swap pointers LD HL,LBRTYP ; set up pointer for test LD C,3 ; set up extension length CKLBL: LD A,(DE) ; get character AND 7FH ; mask MSB CP (HL) ; see if a match JP NZ,CKLBX ; nope, not .LBR so exit INC HL ; yep, up pointers INC DE ; both source and test DEC C ; count down length JP NZ,CKLBL ; and loop to all matched CKLBX: POP BC ; restore the world POP DE POP HL RET ; and back to caller, were finished ENDC HCOUNT: DEFB 0 ; horizontal count VCOUNT: DEFB 0 ; vertical count PROGEND: STACK EQU PROGEND+82H ORDER EQU PROGEND+84H SUBTTL SYMBOL TABLE END