; PROGRAM: FF (Find File) ; AUTHOR: Jay Sage ; VERSION: 1.0 ; DATE: March 14, 1987 ; .xlist ; VERS equ 22 ; 01/12/90 - Howard Goldstein ; EXPERIMENTAL equ 0 XSUBVERS equ 2 ;experimental subversion # ; This program is copyrighted 1987 by NAOG/ZSIG. It may be copied and ; modified freely for personal use but may not be sold or distributed for a ; fee. Modified versions must be submitted to and approved by NAOG/ZSIG ; before they may be distributed. See the file ZSIGPOL1.DOC on Z-Nodes for ; the ZSIG policy on signing out and modifying programs. ; For revision history see accompanying documentation file. ;============================================================================= ; ; D E F I N I T I O N S S E C T I O N ; ;============================================================================= Z3ENV DEFL 0FE00H ; ZCPR3 Environment address, default ; System equates: BOOT EQU 0000H ; CP/M warm boot jump vector BDOS EQU BOOT+05H ; CP/M bdos call jump vector TBUFF EQU BOOT+80H ; Disk I/O buffer FCB EQU BOOT+5CH ; Default file control block CR EQU 'M'-'@' ; Carriage return LF EQU 'J'-'@' ; Line feed TAB EQU 'I'-'@' BELL EQU 'G'-'@' ; Bell character CTRLC EQU 'C'-'@' ; Abort CTRLX equ 'X'-'@' CTRLK equ 'K'-'@' CTRLS EQU 'S'-'@' ; Pause ESIZE EQU 12 ; 12 bytes/dir entry ; offsets in external environment ; ETYPOFF equ 08h ; env type byte VDRVOFF equ 34h ; valid drives vector ; MDOFF EQU 2CH ; Max drive offset in Z3ENV MUOFF EQU 2DH ; Max user offset in Z3ENV RIGHTCH EQU '>' ; Character to right of directory name MSDOSID EQU 0FDh ; id byte for DosDisk fcb/directory entry ; SYSLIB and Z3LIB Routines EXT WHRENV ; locate Z3 external environment EXT Z3INIT,GETWHL,CODEND,GETCRT EXT CST,CIN,COUT,CRLF,PRINT,PAFDC,DUNDR EXT PUTER2,PUTREG ;============================================================================= ; ; M A I N C O D E S E C T I O N ; ;============================================================================= JP START DB 'Z3ENV' ; ZCPR3 ID DB 1 ; External environment descriptor Z3EADR: DW Z3ENV ; Configuration area DEFB 'DRVTBL' DRVTBL0: ; ABCDEFGH ; Drives A..H DB 11111111B ; IJKLMNOP ; Drives I..P DB 11111111B DEFB 'REG#' Z3REG: ; User register in which to return the number DB 0 ; ..of files found (if > 9, disable) DEFB 'SYSF' SYSFLG: DB 0 ; Non-zero: include system files as default START: SUB A ; Test for z80 cpu LD DE,Z80MSG JP PE,DOSMSG LD HL,(Z3EADR) ; Point to default ZCPR3 environment CALL WHRENV ; address, and verify it. LD A,H OR L JP Z,NOENV CALL Z3INIT ; Initialize the ZCPR3 ENV ld (Z3EADR),HL CALL INIT ; Initialize program LD (STACK),SP ; Save stack (so cleanup not needed at end) CALL CODEND ; Determine free space LD (FNTAB),HL ; File name table INC H ; 1/2 K space INC H LD (SCRATCH),HL ; Beginning of scratch area LD SP,HL ; And top of stack LD C,25 ; Save current drive CALL BDOS LD (OLDDRV),A CALL DOSVERS ; get bdos version CALL HELLO ; Sign on message CALL HELPCHK ; Check for and print help message CALL OPTCHK ; Build file name table and process options call mask ; Mask against Z34 system drive vector CALL FIND ; Do the searches CALL BYE ; Sign off message RETURN: LD SP,(STACK) ; Quiet return RET ;----------------------------------------------------------------------------- ; NOENV: LD DE,NOENVMSG ; exit when no z3env DOSMSG: LD C,9 JP BDOS NOENVMSG: DB CR,LF DB 'Requires ZCPR3$' Z80MSG: DB CR,LF DB 'Requires Z80$' ; DOSVERS:LD C,12 ; get version of bdos CALL BDOS CP 30h LD A,0 JR C,DOSV1 DEC A DOSV1: LD (CPM3FLG),A RET ; SAY WHO WE ARE HELLO: CALL PRINT DB CR,LF DB 'Find File (FF) v. ' DB [VERS/10]+'0','.',[VERS MOD 10]+'0' IF EXPERIMENTAL DB 'X' DB XSUBVERS +'0' ENDIF DB ' (compatible with CP/M 3 & DosDisk)' DB ' [ZSIG]' DB CR,LF,0 RET ;----------------------------------------------------------------------------- ; CHECK FOR HELP REQUEST HELPCHK: LD HL,TBUFF+1 ; Look at command tail CALL SBLANK ; Skip blanks LD A,(HL) ; Get first byte or a jr z,hck1 ; Help if no args. CP '/' ; Help? ret nz ; no inc hl ld a,(hl) cp '/' ; Only exactly two slashes allowed ret nz inc hl inc (hl) ; No trailing spaces allowed dec (hl) ret nz ; Only "FF" or "FF//" (exactly) get ; ..help, otherwise process as filename ; IF NO FILE NAME IS SPECIFIED, ABORT WITH NOTICE HCK1: CALL PRINT DB CR,LF,LF DB 'Find all files matching a list of file ' DB 'specs on all drives, a specific ' DB CR,LF DB 'drive, or a list of drives. All file ' DB 'specs are automatically made' DB CR,LF DB 'wild ("A.B" -> "A*.B*").' DB CR,LF,LF DB ' Syntax: FF [D: or DIR:]afn[,afn].. [d..][/o..]' DB CR,LF,LF DB 'Options (d) before slash:' DB CR,LF DB TAB,'List of drives to scan' DB CR,LF,LF DB 'Options (o) after slash:' DB CR,LF DB TAB,'E - Exact (no auto wildcarding)',cr,lf DB TAB,'P - No Paging',0 CALL GETWHL JR Z,HCK2 CALL PRINT DB CR,LF DB TAB,'S - ',0 LD A,(SYSFLG) ; Check system files flag OR A JR Z,INCSYS CALL PRINT DB 'Ex',0 JR HCK1A INCSYS: CALL PRINT DB 'In',0 HCK1A: CALL PRINT DB 'clude SYS Files',0 HCK2: CALL PRINT DB CR,LF,LF DB 'Error flag set if no files found' DB 0 LD A,(Z3REG) ; See if register used to store file number CP 10 JR NC,HCK3 LD B,A ; Save register number in B CALL PRINT DB '. Number of files put in REG ' DB 0 LD A,B ADD '0' CALL COUT HCK3: CALL PRINT DB '.' DB CR,LF DB 'Auto-scanned drives are: ' DB 0 LD HL,(DRVTBL) ; Get drive map LD A,H ; Swap H and L LD H,L LD L,A LD A,(ZMAXDRV) ; Scan up to max drive only LD B,A LD A,'A' ; Initial drive letter HCK4: ADD HL,HL ; High bit of HL into carry flag CALL C,COUT ; Display drive letter if bit was set INC A ; Advance to next drive letter DJNZ HCK4 ; Loop through drives CALL PRINT DB '.',CR,LF,0 JP RETURN ;----------------------------------------------------------------------------- INVRTTBL: EX DE,HL ; put right-to-left vector into de LD B,16 LD HL,0 ;initialize the left-to-right vector INVRTT0:ADD HL,HL ; shift left SRL D ;16-bit shift right RR E JR NC,INVRTT1 ; if entering vector's bit is set SET 0,L ; ..set the new one's INVRTT1:DJNZ INVRTT0 LD A,H LD H,L LD L,A RET ; INITIALIZATION INIT: LD HL,DATABEG ; Zero out data area LD DE,DATABEG+1 LD BC,DATAEND-DATABEG-1 LD (HL),0 ; Seed value LDIR ; Fill 'em up LD HL,(DRVTBL0) ; Copy master drive table map LD (DRVTBL),HL ; ..into working copy call mask ; Mask against Z34 system drive vector CALL GETCRT ; Get data on current console INC HL ; Point to full number of lines on screen LD A,(HL) DEC A ; Reduce by one LD (LPS),A ; Save in lines-per-screen location LD A,0FFH LD (NOFILFLAG),A ; Set no-files-found flag LD (PAGEOPT),A ; Paging option on LD A,1 LD (LINECNT),A ; Set initial line count CALL PUTER2 ; Set Z3 program error flag CALL GETWHL ; Check wheel JR Z,INIT0 ; If Z (wheel false), no system files LD A,(SYSFLG) ; Otherwise, set option from flag byte LD (SYSTEM),A INIT0: LD A,20H LD (WILDCHR),A ; Initially treat spaces as wildcards ; LD HL,(Z3EADR) LD DE,MDOFF ; Get maxdrv and maxusr from env. ADD HL,DE LD DE,ZMAXDRV LDI LDI ; if ccp is in protected memory ; don't need to subtract its length LD A,(0002) ; BIOS page - #ccp pages SUB 8 LD HL,(0006) CP H LD A,10 JR Z,INIT01 LD A,2 INIT01: LD (ADJPAGE),A RET ; ; MASK - Mask (DRVTBL) with system valid drives vector if we have an ; extended environment type. MASK: ld de,(Z3EAdr) ; Get env address LD HL,ETYPOFF ADD HL,DE ; if extended external environment type BIT 7,(HL) ret z LD HL,VDRVOFF ; ..get valid-drive bits from environment ADD HL,DE LD A,(HL) INC HL LD H,(HL) LD L,A CALL INVRTTBL ; convert from right-to-left to the reverse LD DE,DRVTBL ; Point to program's list of valid drives LD A,(DE) AND L ; and with system table LD (DE),A ; Update INC DE ; And again for second byte LD A,(DE) AND H LD (DE),A ret ;----------------------------------------------------------------------------- ; CHECKS FOR OPTIONS IN COMMAND LINE AND EXTRACTS FILE NAMES INTO ; TABLE OPTCHK:: ; Process list of file specifications LD HL,TBUFF+1 ; Look at command tail CALL SBLANK ; Skip blanks LD DE,(FNTAB) ; Pointer to file name table in DE FNLOOP: ; Scan thru tbuff, building a file name table PUSH DE ; Save table ptr CALL GETFN ; Extract file name POP DE PUSH HL LD HL,11 ; Pt to next table entry ADD HL,DE EX DE,HL POP HL LD A,(FNCOUNT) ; Increment count INC A LD (FNCOUNT),A LD A,(HL) ; Get terminating char INC HL ; Pt to next CP ',' ; Another follows? JR NZ,OPTCK1 ; If not, on to second command-line token LD A,(HL) ; Make sure list did not end with comma CP ' '+1 JR NC,FNLOOP ; If not, back for another file spec ; Process second command-line token OPTCK1:: DEC HL ; Point back to delim CALL SBLANK ; Skip to non-blank LD A,(HL) ; Get option CALL DELCHK ; Done if delim RET Z CP '/' ; See if non-drive option leadin '/' JR Z,OPTCK3 ; If so, branch now ; Process drive options XOR A ; Initialize for drive list LD DE,DRVTBL ; Point to drive map LD (DE),A ; Zero it out INC DE LD (DE),A LD (FCB),A ; Nullify any drive from file spec list OPTCK2:: LD A,(HL) ; Get drive letter SUB 'A' ; Convert to number in range 0..15 JR C,OPTER ; If less than 'A', it is an error CP 16 ; If not less than 16, it is an error JR NC,OPTER PUSH HL ; Save pointer to option list CALL SETDRV ; Set bit in HL corresponding to drive LD DE,(DRVTBL) ; Get the map so far LD A,D ; Merge with new drives OR L ; Drives 8..15 LD D,A LD A,E OR H ; Drives 0..7 LD E,A LD (DRVTBL),DE ; Store new drive map POP HL ; Get pointer back INC HL ; Get next character LD A,(HL) CP '/' ; Non-drive option leadin? JR Z,OPTCK3 ; If so, take care of non-drive options CALL DELCHK ; End of list? RET Z ; Return if so JR OPTCK2 ; Else back for next drive ; Process non-drive options OPTCK3:: INC HL ; Point to next option character LD A,(HL) ; ..and get it CALL DELCHK ; Delimiter? RET Z ; If so, we are done CP 'P' ; Paging Toggle? JR Z,POPT CP 'E' ; Exact mode toggle? JR Z,EOPT CP 'S' ; SYS files option? JR NZ,OPTER ; If not, it's an option error ; Else fall through to SOPT SOPT: CALL GETWHL JR Z,OPTER ; If Z (wheel false), not allowed LD A,(SYSTEM) ; Get default status DEC A CP 0FFH JR Z,SOPT1 ; Flag was zero; now flipped to 0ffh XOR A ; Else, set to zero SOPT1: LD (SYSTEM),A JR OPTCK3 EOPT: LD A,'?' LD (WILDCHR),A ; Set "?" as only wildcard char JR OPTCK3 POPT: XOR A LD (PAGEOPT),A JR OPTCK3 OPTER: CALL PRINT DB BELL DB CR,LF DB 'Invalid Option -- ',0 LD A,(HL) CALL COUT JP HCK1 ; Set bit in HL corresponding to drive number in A SETDRV:: LD HL,1 ; Seed value (drive 15) CPL ; Invert drive number AND 0FH ; ..so 0->15, 15->0 RET Z ; If zero, seed value is what we want LD B,A ; Get count into B SETDR1:: ADD HL,HL ; Shift bit left DJNZ SETDR1 ; ..number of times in B RET ; Extract file specification GETFN: PUSH DE ; Fill target fcb LD B,11 ; 11 bytes LD A,' ' ; Space fill GETFN0: LD (DE),A ; Put space INC DE DJNZ GETFN0 POP DE ; Pt to entry again CALL SCANCOL ; Scan for colon LD B,8 ; 8 chars max CALL GETFN1 ; Get and fill entry LD A,(HL) ; Get char CP '.' ; Delim? RET NZ ; Done INC HL ; Pt to after period LD B,3 ; 3 chars max and do it again GETFN1: LD A,(HL) ; Get char CP '.' ; End of field? JR Z,GETFN3 CALL DELCHK ; Check delimiter RET Z CP '*' ; Wild? JR Z,GETFNQ LD (DE),A ; Store char INC HL ; Pt to next INC DE DJNZ GETFN1 ; Count down GETFN2: LD A,(HL) ; Flush chars to delim CALL DELCHK ; Check for delimiter RET Z INC HL ; Pt to next JR GETFN2 GETFN3: INC DE ; Pt to after field DJNZ GETFN3 ; Count down RET GETFNQ: LD A,'?' ; Fill with question marks LD (DE),A INC DE DJNZ GETFNQ JR GETFN2 ; Skip to delim DELCHK: OR A ; End of line? RET Z CP '.' ; End of field? RET Z CP ',' ; End of entry? RET Z CP ' ' RET SBLANK: LD A,(HL) ; Skip to non-blank CP ' ' RET NZ INC HL JR SBLANK SCANCOL: PUSH DE ; Save table ptr PUSH HL ; Save ptr SCOL1: LD A,(HL) ; Get char INC HL ; Pt to next CP ':' ; Colon? JR Z,SCOLX CALL DELCHK ; Check for delimiter JR NZ,SCOL1 SCOL2: POP HL ; Restore POP DE RET SCOLX: EX DE,HL ; De pts to after colon POP HL ; Get old ptr EX DE,HL ; Replace it POP DE ; Get table ptr RET ;----------------------------------------------------------------------------- ; ; v 1.1 Search with bdos searchfirst/search next. ; ; LOOK THROUGH DIRECTORY FIND: LD A,(FCB) ; Disk selection, Zero if all disks LD (DISKNO),A LD (DISK),A ; Remember it OR A JR Z,FIND0 DEC A ; Offset so A=0 ; FIND0: LD (DISKNO),A CALL NXTDISK ; Get info the first time FIND1: RET C ; CY if no more disks JR Z,FIND4 ; Z if disk not available in bios LD A,17 ; Set search-first function # LD (FSTNXT),A FIND2: CALL NXTENT ; Get a directory entry JR Z,FIND3 ; Returns zero flag if no more ; Allow abort between pages call cst jr nz,find2a ; No char. ready call cin call ckabort ; Check and abort if needed find2a: CALL CHKENT ; Check it out JR FIND2 ; Keep it up till done ; FIND3: CALL DIRALPHA ; Sort entries CALL PRFILES ; Print sorted entries LD A,(ECOUNT) ; Check count of files listed AND 1 ; If odd, then CALL NZ,NEWLINE ; ..terminate last line FIND4: LD A,(DISK) ; Just a specific disk? OR A RET NZ ; ..yes, quit LD A,(DISKNO) ; Bump to next disk INC A JR FIND0 ;----------------------------------------------------------------------------- ; SIGN OFF BYE: LD A,(NOFILFLAG) ; Get no-file-found flag CALL PUTER2 ; Store in Z3 program error flag OR A ; If flag reset, files were found JR Z,BYE1 ; ..so we return without message CALL PRINT ; ..else we report to user DB CR,LF,' NO Files Found',0 BYE1: LD A,(Z3REG) ; Get register to use for file count CP 10 ; If not in range 0..9, skip JR NC,BYE3 LD B,A ; Save register number in B LD HL,(TCOUNT) ; Get total file count LD A,H ; See if count is > 255 OR A LD A,255 ; Preset for maximum possible value JR NZ,BYE2 LD A,L ; If < 256, use value in L BYE2: CALL PUTREG ; Save value in A in register in B BYE3: LD A,(OLDDRV) ; reselect original drive ld (DISKNO),a ; by doing bios, then bdos select call seldsk JP RETURN ;----------------------------------------------------------------------------- ; ; v 1.1 Check a single entry, because bdos emulators needn't ; return a full directory sector with 4 entries to the dma. ; ; Checks the current directory entry against argument ; stores matching names in table ; Enter hl-> entry in dma buffer ; CHKENT: LD A,(HL) CP 0E5H ; Check for unused RET Z LD A,(ZMAXUSR) ; Check for > maxuser from ENV LD E,A INC E ; To make cp easy LD A,(HL) ; entry user # CP A,E ; > maxuser? RET NC ; Yes, if nc XOR A ; A=0 LD (CLPFLG),A ; Set flag for no entries found LD A,(FNCOUNT) ; Get number of file names to look thru LD B,A ; In b LD DE,(FNTAB) ; Pt to table CKLUP1: PUSH BC ; Save count PUSH HL ; Save beginning address PUSH DE CALL COMPAR ; Compare with argument and save if match POP DE LD HL,11 ; Pt to next entry ADD HL,DE EX DE,HL POP HL POP BC DJNZ CKLUP1 ; Count down RET ;----------------------------------------------------------------------------- ; COMPARE 11 BYTES OF DIRECTORY ENTRY AGAINST ARGUMENT; RNZ IF NOT MATCHED ; DE PTS TO TABLE ENTRY TO COMPARE TO COMPAR: LD A,(CLPFLG) ; Get found flag OR A ; 0=no RET NZ LD (TEMP),HL ; Hold pointer in case of match INC HL EX DE,HL LD B,11 CMPR1: LD A,(DE) ; Get directory entry character AND 7FH ; Strip any flags CP (HL) JR Z,CMPR2 LD A,(WILDCHR) ; Get alternate wildcard char CP (HL) JR Z,CMPR2 LD A,(HL) CP '?' RET NZ ; exit, no match CMPR2: INC DE INC HL ; Bump to next character DJNZ CMPR1 ; Loop for 11 characters ; PUSH DE ; Save entry ptr (EXT) INC DE ; S1 INC DE ; S2 INC DE ; RC INC DE ; fcb[16] INC DE ; fcb[17] LD A,(DE) ; Check for DosDisk entry CP MSDOSID JR NZ,CMPREXT DEC DE LD A,(DE) CP MSDOSID JR Z,CMPRSYS ; ..DosDisk entry, skip extent check CMPREXT: POP DE ; check for 1st extent LD A,(DE) ; Get extent in b LD B,A LD A,(EXTENT) ; Get extent mask CP B JR C,CMPR4 ; Not first extent PUSH DE INC DE ; S1 INC DE ; S2 LD A,(DE) OR A ; Check S2 POP DE JR Z,CMPRSY1 RET ; Not first extent ; CMPRSYS:POP DE CMPRSY1:LD A,(SYSTEM) ; Include system files? OR A ; 0=no JR NZ,CMPR3 DEC DE ; Back up 2 bytes DEC DE LD A,(DE) ; Get t2 AND 80H ; Check for sys RET NZ CMPR3: LD HL,(TEMP) ; Check for user limit LD A,31 ; Max user CP (HL) ; Beyond max? JR C,CMPR4 LD HL,(FCOUNT) ; Increment count INC HL LD (FCOUNT),HL LD HL,(DSTART) ; Get ptr to next entry EX DE,HL LD HL,(TEMP) LD B,ESIZE ; Copy entry CALL MOVE LD (DSTART),DE ; Ptr to next entry LD HL,(BDOS+1) ; Check for memory overflow LD A,H ADJPAGE equ $+1 SUB 10 ; Below ccp/rsx? CP D ; Pt beyond limit? JR C,MOVFL XOR A LD (NOFILFLAG),A ; Reset no-files-found flag RET ; Returns 'zero' flag set for match CMPR4: LD A,0FFH ; No match OR A RET MOVFL: CALL PRINT DB CR,LF,'ABORT -- Not Enough Memory for Buffers',0 JP RETURN ;----------------------------------------------------------------------------- ; ; LOG in (NEXT) DISK ; v 1.1 exit: Z if bios error, CY if no more disks ; DISKNO has already been set/advanced by FIND. NXTDISK: LD DE,TBUFF ; Set dma address LD C,26 CALL BDOS NXTDISK0: LD A,(DISKNO) LD C,A ; Save drive in C LD A,(ZMAXDRV) ; Check against maxdrive in ENV dec a ; Set to A=0 CP C JR NC,NXTDISK1 ; Jump if OK SCF ; Else set CY flag and return RET NXTDISK1: LD A,(DISK) ; See if specific drive requested OR A JR NZ,NXTDISK3 ; If so, skip table check LD HL,(DRVTBL) ; Check against table of available drives LD A,H ; Swap H and L to get bit order right LD H,L LD L,A LD B,C ; Use drive as count in B INC B ; Shift count range from 0-15 to 1-16 NXTDISK2: ; Shift appropriate bit into carry ADD HL,HL DJNZ NXTDISK2 JR C,NXTDISK3 ; If drive enabled, proceed LD A,C ; Else get drive back INC A ; Increment it LD (DISKNO),A ; JR NXTDISK0 ; Go back to process it NXTDISK3: CALL SELDSK ; Get (next) disk's parameters RET Z ; Z = invalid bios disk CALL NEWLINE ; Skip one extra line between disks CALL PRINT DB 'Disk ',0 LD A,(DISKNO) ADD 'A' CALL COUT CALL PRINT DB ' --',CR,LF,0 CALL CHKPAGE ; Check for paging LD HL,(SCRATCH) ; Pt to scratch area LD (ORDER),HL ; Address of order table EX DE,HL LD HL,(DSTART) ; Get number of directory entries ADD HL,HL ; Double for number of bytes in order table ADD HL,DE ; Pt to first byte of dirbuf LD (DIRBUF),HL ; Set ptr LD (DSTART),HL ; Set loop ptr LD HL,0 ; Set file count LD (FCOUNT),HL XOR A ; Set count LD (ECOUNT),A CPL ; Flip OR A ; Ok to continue, NZ, CY clear RET ;----------------------------------------------------------------------------- ; ; v 1.1 Use bdos bios-function call for cp/m3. ; ; Select (DISKNO) and get dpb parameters ; Exit Z if select error. ; ; SELDSK: LD A,(CPM3FLG) ; CP/M 3 requires a dos OR A ; call to access bios functions LD A,(DISKNO) JR NZ,SELDSK3 ; SELDSK2:LD C,A ; CP/M 2 bios SELDSK call LD HL,(0001H) LD DE,1BH-3 ADD HL,DE LD E,0 ; set new mount flag (bit 0=0) CALL GOHL JR SELDCK ; SELDSK3:LD (BCREG),A ; CP/M 3 bios SELDSK call LD A,9 LD (SCBFN),A XOR A ; set new mount flag LD (DEREG),A LD C,50 ; make bios call thru bdos LD DE,SCBPB CALL BDOS ; ; SELDCK: LD A,H ; good drive select? OR L RET Z ; ..no ; LD A,(DISKNO) LD E,A ; save drive PUSH DE LD C,14 ; do BDOS drive select CALL BDOS ; to stay in sync with bios POP DE LD C,31 ; get dpb CALL BDOS INC HL INC HL INC HL INC HL LD A,(HL) ; Get exm LD (EXTENT),A INC HL ; Pt to drm INC HL INC HL LD E,(HL) ; Get number of INC HL ; Directory entries LD D,(HL) INC DE ; Account for - 1 in dpb LD (DSTART),DE ; Save number of directory entries INC A ; set NZ RET ; GOHL: JP (HL) ; ; ----------------------------------------------------------- ; ; GENERAL PURPOSE MOVE ROUTINE FROM 'HL' TO 'DE' FOR COUNT ; GIVEN IN B REGISTER MOVE: LD A,(HL) ; Get a byte LD (DE),A ; Put a byte INC DE ; Increment to next INC HL DJNZ MOVE ; Count down RET ;----------------------------------------------------------------------------- ; ; v 1.1 Use bdos searchfirst/next function. ; ; Get next entry in directory, return hl-> entry ; Zero flag set if no more NXTENT: LD DE,FCB LD A,'?' ;get every entry in directory LD (DE),A FSTNXT EQU $+1 LD C,17 ;search first/search next function LD A,18 ; set it to search next LD (FSTNXT),A ; for subsequent calls CALL BDOS CP 0FFh RET Z ; no more entries LD HL,TBUFF AND 00000011B ; index to directory entry RRA ; within directory sector RRA RRA RRA ADD L LD L,A RET ; NZ, found ;----------------------------------------------------------------------------- ; PRINT FILES IN DIRBUF PRFILES: LD HL,(FCOUNT) ; Get count LD A,H ; Any? OR L RET Z LD B,H ; Count in bc LD C,L LD HL,(DIRBUF) ; Pt to first one PRFLOOP: PUSH BC ; Save count PUSH HL ; Save ptr CALL PRINTFCB ; Print fcb CALL CST ; Check for abort character JR NZ,PRFL1 ; If not, go on CALL CIN ; See if character is control-c call CkAbort PRFL1: POP HL ; Get regs back POP BC LD DE,ESIZE ; Pt to next ADD HL,DE DEC BC ; Count down LD A,B OR C JR NZ,PRFLOOP RET ; ; CKABORT - Test character in A for ^c, c, C, ^x, x, X, ^k, k, K, and ; abort if true. All registers preserved. ; CkAbort: push af and 00011111b ; Mask c, C --> ^c, etc. cp ctrlc ; (side effect: allows '#', '+', and '8') jr z,ckexit ; Abort cp ctrlx jr z,ckexit cp ctrlk jr z,ckexit pop af ret CkExit: CALL PRINT ; Say abort & quit DB CR,LF DB ' +++ aborted +++',CR,LF,0 JP BYE1 ; Set stack pointer to entry value & return ;------------------------------------------------------------------------------ ; FCB PRINTING ROUTINE PRINTFCB: CALL PRINT ; 4 spaces DB ' ',0 LD A,(DISKNO) ; Get drive LD B,A ; Save in B INC B ; ..offset to A = 1 ADD A,'A' ; Convert to character CALL COUT LD A,(HL) ; Get user number PUSH HL ; Save pointer LD C,A ; Save in C CALL PAFDC ; Print it CALL DUNDR ; Convert DU to NDR LD B,9 ; Spaces to skip if no NDR JR Z,PRFCB2 ; Jump if no named directory ; Named directory display LD A,':' ; Print the colon separator CALL COUT INC HL ; Skip over disk in NDR INC HL ; Skip over user in NDR LD B,8 ; Length of name PRFCB1: LD A,(HL) ; Get character CP ' ' ; Look for space JR Z,PRFCB2 ; Don't print spaces CALL COUT INC HL DJNZ PRFCB1 PRFCB2: LD A,RIGHTCH ; Print ending '>' CALL COUT POP HL ; Restore pointed to file buffer LD A,(HL) ; Get user number again CP 10 ; Set carry of only one digit LD A,0 ; Increment B if carry set ADC A,B INC A ; One extra space in any case LD B,A ; Save count in B LD A,' ' ; Pad with spaces for alignment PRFCB3: CALL COUT DJNZ PRFCB3 INC HL ; Point to name of file PR0: LD B,8 CALL PR1 LD A,'.' CALL COUT LD B,3 CALL PR1 LD HL,(TCOUNT) ; Get total file count INC HL ; Increment it LD (TCOUNT),HL ; Save new value LD A,(ECOUNT) ; Increment count of files INC A ; ..found on this drive LD (ECOUNT),A AND 1 ; If evenEvery 2 JR Z,NEWLINE ; ..make new line CALL PRINT ; ..else space to second column DB ' ',0 RET ;----------------------------------------------------------------------------- ; PRINT A CRLF AND KEEP COUNT OF LINES ON PAGE, STOPPING WHEN PAGE IS FULL NEWLINE: CALL CRLF ; Check for end of page of display CHKPAGE: LD A,(PAGEOPT) ; See if paging in effect OR A RET Z ; Return if option not set LD A,(LPS) ; Get lines-per-screen value LD B,A ; ..into B LD A,(LINECNT) ; Get count of lines on page INC A LD (LINECNT),A CP B RET C ; Return if less than full page ; Pause for page CALL PRINT BEG1: DB ' [SP=line ^C=abort RETURN=page] ' END1: DB 0 CALL CIN LD C,A ; Save user character LD A,CR ; Back to beginning of line CALL COUT LD A,' ' ; Overwrite message with spaces LD B,END1-BEG1 CHKP1: CALL COUT DJNZ CHKP1 LD A,C ; Retrieve user response call CkAbort CP ' ' ; See if space JR NZ,CHKP2 LD A,(LINECNT) ; ..decrement line count DEC A JR CHKP3 CHKP2: XOR A ; Reset line count CHKP3: LD (LINECNT),A LD A,CR JP COUT PR1: LD A,(HL) AND 7FH CALL COUT INC HL DJNZ PR1 RET ;----------------------------------------------------------------------------- ; SHIFT REGS 'HL' RIGHT 2 BITS LOGICAL SHFHL2: CALL SHFHL ; Rotate right 1 bit and fall thru SHFHL: SRL H RR L RET ;----------------------------------------------------------------------------- ; DIRALPHA -- ALPHABETIZES DIRECTORY PTED TO BY HL; BC CONTAINS ; THE NUMBER OF FILES IN THE DIRECTORY DIRALPHA: LD HL,(FCOUNT) ; Get file count LD A,H ; Any files? OR L RET Z LD (N),HL ; Set "N" LD B,H ; Bc=count LD C,L LD HL,(DIRBUF) ; Pt to directory ; SHELL SORT -- ; THIS SORT ROUTINE IS ADAPTED FROM "SOFTWARE TOOLS" ; BY KERNIGAN AND PLAUGHER, PAGE 106. COPYRIGHT, 1976, ADDISON-WESLEY. ; ON ENTRY, BC=NUMBER OF ENTRIES AND HL=ADDRESS OF FIRST ENTRY SORT: EX DE,HL ; Pointer to directory in de LD HL,(ORDER) ; Pt to order table ; SET UP ORDER TABLE; HL PTS TO NEXT ENTRY IN ORDER TABLE, DE PTS TO NEXT ; ENTRY IN DIRECTORY, BC = NUMBER OF ELEMENTS REMAINING SORT1: LD (HL),E ; Store low-order address INC HL ; Pt to next order byte LD (HL),D ; Store high-order address INC HL ; Pt to next order entry PUSH HL ; Save ptr LD HL,ESIZE ; Hl=number of bytes/entry ADD HL,DE ; Pt to next dir1 entry EX DE,HL ; De pts to next entry POP HL ; Get ptr to order table DEC BC ; Count down LD A,B ; Done? OR C JR NZ,SORT1 ; THIS IS THE MAIN SORT LOOP FOR THE SHELL SORT IN "SOFTWARE TOOLS" BY K&P ; SHELL SORT FROM "SOFTWARE TOOLS" BY KERNINGHAN AND PLAUGER LD HL,(N) ; Number of items to sort LD (GAP),HL ; Set initial gap to n for first division by 2 ; FOR (GAP = N/2; GAP > 0; GAP = GAP/2) SRTL0: OR A ; Clear carry LD HL,(GAP) ; Get previous gap LD A,H ; Rotate right to divide by 2 RRA LD H,A LD A,L RRA LD L,A ; TEST FOR ZERO OR H JR Z,SDONE ; Done with sort if gap = 0 LD (GAP),HL ; Set value of gap LD (K),HL ; Set k=gap for following loop ; FOR (K = GAP + 1; K <= N; K = K + 1) SRTL1: LD HL,(K) ; Add 1 to k INC HL LD (K),HL ; TEST FOR K <= N EX DE,HL ; K is in de LD HL,(N) ; Get n LD A,L ; Compare by subtraction SUB E LD A,H SBC A,D ; Carry set means k > n JR C,SRTL0 ; Don't do for loop if k > n LD HL,(K) ; Set j = k initially for first subtraction of gap LD (J),HL ; FOR (J = K - GAP; J > 0; J = J - GAP) SRTL2: LD HL,(GAP) ; Get gap EX DE,HL ; In de LD HL,(J) ; Get j LD A,L ; Compute j - gap SUB E LD L,A LD A,H SBC A,D LD H,A LD (J),HL ; J = j - gap JR C,SRTL1 ; If carry from subtractions, j < 0 and abort LD A,H ; J=0? OR L JR Z,SRTL1 ; If zero, j=0 and abort ; SET JG = J + GAP EX DE,HL ; J in de LD HL,(GAP) ; Get gap ADD HL,DE ; J + gap LD (JG),HL ; Jg = j + gap ; IF (V(J) <= V(JG)) CALL ICOMPARE ; J in de, jg in hl ; ... THEN BREAK JR C,SRTL1 ; ... ELSE EXCHANGE LD HL,(J) ; Swap j, jg EX DE,HL LD HL,(JG) CALL ISWAP ; J in de, jg in hl ; END OF INNER-MOST FOR LOOP JR SRTL2 ; SORT IS DONE -- RESTRUCTURE DIR1 IN SORTED ORDER IN PLACE SDONE: LD HL,(N) ; Number of entries LD B,H ; In bc LD C,L LD HL,(ORDER) ; Ptr to ordered pointer table LD (PTPTR),HL ; Set ptr ptr LD HL,(DIRBUF) ; Ptr to unordered directory LD (PTDIR),HL ; Set ptr dir buffer ; FIND PTR TO NEXT DIR1 ENTRY SRTDN: LD HL,(PTPTR) ; Pt to remaining pointers EX DE,HL ; In de LD HL,(PTDIR) ; Hl pts to next dir entry PUSH BC ; Save count of remaining entries ; FIND PTR TABLE ENTRY SRTDN1: LD A,(DE) ; Get current pointer table entry value INC DE ; Pt to high-order pointer byte CP L ; Compare against dir1 address low JR NZ,SRTDN2 ; Not found yet LD A,(DE) ; Low-order bytes match -- get high-order pointer byte CP H ; Compare against dir1 address high JR Z,SRTDN3 ; Match found SRTDN2: INC DE ; Pt to next ptr table entry DEC BC ; Count down LD A,C ; End of table? OR B JR NZ,SRTDN1 ; Continue if not ; FATAL ERROR -- INTERNAL ERROR; POINTER TABLE NOT CONSISTENT FERR$PTR: CALL PRINT DB 0DH,0AH,'DIRALPHA -- Pointer Error',0 JP RETURN ; FOUND THE POINTER TABLE ENTRY WHICH POINTS TO THE NEXT UNORDERED DIR1 ENTRY ; MAKE BOTH POINTERS (PTR TO NEXT, PTR TO CURRENT UNORDERED DIR1 ENTRY) ; POINT TO SAME LOCATION (PTR TO NEXT DIR1 ENTRY TO BE ORDERED) SRTDN3: LD HL,(PTPTR) ; Get ptr to next ordered entry DEC DE ; De pts to low-order pointer address LD A,(HL) ; Make ptr to next unordered dir1 pt to buffer for LD (DE),A ; Dir1 entry to be moved to next unordered dir1 pos INC HL ; Pt to next ptr address INC DE LD A,(HL) ; Make high point similarly LD (DE),A ; COPY NEXT UNORDERED DIR1 ENTRY TO HOLD BUFFER LD B,ESIZE ; B=number of bytes/entry LD HL,(PTDIR) ; Pt to entry LD DE,HOLD ; Pt to hold buffer PUSH BC ; Save b=number of bytes/entry CALL MOVE POP BC ; COPY TO-BE-ORDERED DIR1 ENTRY TO NEXT ORDERED DIR1 POSITION LD HL,(PTPTR) ; Point to its pointer LD E,(HL) ; Get low-address pointer INC HL LD D,(HL) ; Get high-address pointer LD HL,(PTDIR) ; Destination address for next ordered dir1 entry EX DE,HL ; Hl pts to entry to be moved, de pts to dest PUSH BC ; Save b=number of bytes/entry CALL MOVE POP BC EX DE,HL ; Hl pts to next unordered dir1 entry LD (PTDIR),HL ; Set pointer for next loop ; COPY ENTRY IN HOLD BUFFER TO LOC PREVIOUSLY HELD BY LATEST ORDERED ENTRY LD HL,(PTPTR) ; Get ptr to ptr to the destination LD E,(HL) ; Get low-address pointer INC HL LD D,(HL) ; High-address pointer LD HL,HOLD ; Hl pts to hold buffer, de pts to entry dest CALL MOVE ; B=number of bytes/entry ; POINT TO NEXT ENTRY IN POINTER TABLE LD HL,(PTPTR) ; Pointer to current entry INC HL ; Skip over it INC HL LD (PTPTR),HL ; COUNT DOWN POP BC ; Get counter DEC BC ; Count down LD A,C ; Done? OR B JR NZ,SRTDN RET ; Done ; SWAP (Exchange) the pointers in the ORDER table whose indexes are in ; HL and DE ISWAP: PUSH HL ; Save hl LD HL,(ORDER) ; Address of order table - 2 LD B,H ; In bc LD C,L POP HL DEC HL ; Adjust index to 0...n-1 from 1...n ADD HL,HL ; Hl pts to offset address indicated by index ; Of original hl (1, 2, ...) ADD HL,BC ; Hl now pts to pointer involved EX DE,HL ; De now pts to pointer indexed by hl DEC HL ; Adjust index to 0...n-1 from 1...n ADD HL,HL ; Hl pts to offset address indicated by index ; Of original de (1, 2, ...) ADD HL,BC ; Hl now pts to pointer involved LD C,(HL) ; Exchange pointers -- get old (de) LD A,(DE) ; -- get old (hl) EX DE,HL ; Switch LD (HL),C ; Put new (hl) LD (DE),A ; Put new (de) INC HL ; Pt to next byte of pointer INC DE LD C,(HL) ; Get old (hl) LD A,(DE) ; Get old (de) EX DE,HL ; Switch LD (HL),C ; Put new (de) LD (DE),A ; Put new (hl) RET ;----------------------------------------------------------------------------- ; ICOMPARE compares the entry pointed to by the pointer pointed to by HL ; with that pointed to by DE (1st level indirect addressing); on entry, ; HL and DE contain the numbers of the elements to compare (1, 2, ...); ; on exit, Carry Set means ((DE)) < ((HL)), Zero Set means ((HL)) = ((DE)), ; and Non-Zero and No-Carry means ((DE)) > ((HL)) ICOMPARE: PUSH HL ; Save hl LD HL,(ORDER) ; Address of order - 2 LD B,H ; In bc LD C,L POP HL DEC HL ; Adjust index to 0...n-1 from 1...n ADD HL,HL ; Double the element number to point to the ptr ADD HL,BC ; Add to this the base address of the ptr table EX DE,HL ; Result in de DEC HL ; Adjust index to 0...n-1 from 1...n ADD HL,HL ; Do the same with the original de ADD HL,BC EX DE,HL ; HL NOW POINTS TO THE POINTER WHOSE INDEX WAS IN HL TO BEGIN WITH ; DE NOW POINTS TO THE POINTER WHOSE INDEX WAS IN DE TO BEGIN WITH ; FOR EXAMPLE, IF DE=5 AND HL=4, DE NOW POINTS TO THE 5TH PTR AND HL ; TO THE 4TH POINTER LD C,(HL) ; Bc is made to point to the object indexed to INC HL ; By the original hl LD B,(HL) EX DE,HL LD E,(HL) ; De is made to point to the object indexed to INC HL ; By the original de LD D,(HL) LD H,B ; Set hl = object pted to indirectly by bc LD L,C ; COMPARE DIR ENTRY PTED TO BY HL WITH THAT PTED TO BY DE; ; NO NET EFFECT ON HL, DE; RET W/CARRY SET MEANS DE or "?") DRVTBL: DS 2 ; Working copy of drive map OLDDRV: DS 1 ; Drive at entry ; ZMAXDRV:DS 1 ; } maxdrv, copy from env. ZMAXUSR:DS 1 ; } maxusr ; DISKNO: DS 1 CPM3FLG:DS 1 ; NZ if CP/M 3 ; SCBPB: ;CP/M 3 system control block ; parameter block SCBFN: DS 1 ; bios function # AREG: DS 1 ; A register BCREG: DS 2 ; BC register DEREG: DS 2 ; DE register HLREG: DS 2 ; HL register ; DATAEND: ; Marker for end of data END