; ; SYSLIB Module Name: SDIR ; Author: Richard Conn ; SYSLIB Version Number: 2.7 ; Module Version Number: 1.2 ; Module Entry Points: ; DBUFFER DFREE DIRALPHA DIRF ; DIRFS DIRLOAD DIRNPACK DIRPACK ; DIRSEL DIRSLOAD DPARAMS FSIZE ; Module External References: ; PRINT ; ;* ;* EXTERNALS ;* EXT PRINT ; PRINT STRING PTED TO BY RETURN ADDRESS ;* ;* BASIC EQUATES ;* CPM EQU 0 ; CP/M WARM BOOT BIOS EQU CPM+1 ; BIOS BASE ADDRESS BDOS EQU CPM+5 ; BDOS ENTRY POINT ESIZE EQU 16 ; NUMBER OF BYTES/ENTRY IN MEMORY BUFFER BUFF EQU CPM+80H ; DEFAULT DMA BUFFER ;************************************************************************** ;* ;* GENERAL-PURPOSE DIRECTORY SELECT ROUTINE WITHOUT SIZING INFORMATION ;* THIS ROUTINE SCANS FOR THE FCB PTED TO BY DE AND LOADS ALL ENTRIES ;* WHICH MATCH IT INTO THE MEMORY BUFFER PTED TO BY HL. ON EXIT, ;* BC=NUMBER OF FILES IN BUFFER, AND HL PTS TO FIRST FILE IN BUFFER. ;* THE DIRECTORY BUFFER GENERATED BY DIRF CONTAINS ENTRIES WHICH MAY NOT ;* BE USED TO COMPUTE THE SIZE OF THE FILES USING THE FSIZE ROUTINE. THE ;* DIRFS ROUTINE IS DESIGNED FOR THIS PURPOSE. THE BASIC TRADEOFF BETWEEN ;* THE TWO ROUTINES IS THE DIRF RUNS FASTER THAN DIRFS, AND THIS IS NOTICABLE ;* IF THERE IS A SIGNIFICANT NUMBER OF FILES TO BE PROCESSED. ;* ;* INPUT PARAMETERS: ;* HL PTS TO BUFFER, DE PTS TO FCB, A IS SELECT FLAG: ;* Bit 7 - Select Non-Sys, Bit 6 - Select Sys ;* Bit 5 - Select All Users, Bits 4-0 - User Number ;* OUTPUT PARAMETERS: ;* HL PTS TO FIRST FILE IN BUFFER ;* BC = NUMBER OF FILES ;* PSW, DE UNCHANGED ;* DIRF:: PUSH PSW ; SAVE PSW PUSH D ; SAVE PTR TO FCB STA SELFLG ; SAVE SELECT FLAG FOR DIRSEL CALL DBUFFER ; GET PTRS CALL DIRLOAD ; LOAD DIRECTORY WITHOUT SIZING INFORMATION (FAST LOAD) POP D ; GET PTR TO FCB LDA SELFLG ; GET SELECT FLAG CALL DIRSEL ; SELECT FILES CALL DIRPACK ; PACK DIRECTORY MVI A,0 ; SORT FLAG CALL DIRALPHA ; ALPHABETIZE POP PSW ; GET PSW RET ;************************************************************************** ;* ;* BUFFERS ;* TFCB: DB 0,'???????????',0,0,0,0 ; TEMPORARY FCB (FN,FT WILD) DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0 BLKSHF: DS 1 ; BLOCK SHIFT FACTOR BLKMSK: DS 1 ; BLOCK MASK BLKMAX: DS 2 ; MAX NUMBER OF BLOCKS DIRMAX: DS 2 ; MAX NUMBER OF DIRECTORY ENTRIES SELFLG: DS 1 ; FILE ATTRIBUTE FLAG DFLAG: ; 0=SELECT FILES OF LARGER EX, ELSE OF SMALLER EX CMP$FLAG: ; 0=SORT BY FILE NAME/TYPE, ELSE BY TYPE/NAME DS 1 ORDER: DS 2 ; POINTER TO ORDER TABLE DIRBUF: DS 2 ; POINTER TO DIRECTORY DSTART: DS 2 ; POINTER TO FIRST DIRECTORY ENTRY EXTENT: DB 0 ; NUMBER OF EXTENT "0" (INITIALIZED TO 0) FCOUNT: DS 2 ; TOTAL NUMBER OF FILES/NUMBER OF SELECTED FILES HOLD: DS 35 ; EXCHANGE HOLD BUFFER FOR FCB'S PTPTR: DS 2 ; POINTER POINTER PTDIR: DS 2 ; DIRECTORY POINTER I: DS 2 ; INDEXES FOR SORT J: DS 2 JG: DS 2 N: DS 2 ; NUMBER OF ELEMENTS TO SORT GAP: DS 2 ; BINARY GAP SIZE ;************************************************************************** ;* ;* GENERAL-PURPOSE DIRECTORY SELECT ROUTINE WITH SIZING INFORMATION ;* THIS ROUTINE SCANS FOR THE FCB PTED TO BY DE AND LOADS ALL ENTRIES ;* WHICH MATCH IT INTO THE MEMORY BUFFER PTED TO BY HL. ON EXIT, ;* BC=NUMBER OF FILES IN BUFFER, AND HL PTS TO FIRST FILE IN BUFFER. ;* THE DIRECTORY BUFFER GENERATED BY DIRFS CONTAINS ENTRIES WHICH MAY ;* BE USED TO COMPUTE THE SIZE OF THE FILES USING THE FSIZE ROUTINE. THE ;* DIRFS ROUTINE IS DESIGNED FOR THIS PURPOSE. THE BASIC TRADEOFF BETWEEN ;* THE DIRF AND DIRFS ROUTINES IS THE DIRF RUNS FASTER THAN DIRFS, AND THIS ;* IS NOTICABLE IF THERE IS A SIGNIFICANT NUMBER OF FILES TO BE PROCESSED. ;* ;* INPUT PARAMETERS: ;* HL PTS TO BUFFER, DE PTS TO FCB, A IS SELECT FLAG: ;* Bit 7 - Select Non-Sys, Bit 6 - Select Sys ;* Bit 5 - Select All Users, Bits 4-0 - User Number ;* OUTPUT PARAMETERS: ;* HL PTS TO FIRST FILE IN BUFFER ;* BC = NUMBER OF FILES ;* PSW, DE UNCHANGED ;* DIRFS:: PUSH PSW ; SAVE PSW PUSH D ; SAVE PTR TO FCB STA SELFLG ; SAVE SELECT FLAG FOR DIRSEL CALL DBUFFER ; GET PTRS CALL DIRSLOAD ; LOAD DIRECTORY WITH SIZING INFORMATION POP D ; GET PTR TO FCB LDA SELFLG ; GET SELECT FLAG CALL DIRSEL ; SELECT FILES CALL DIRPACK ; PACK DIRECTORY MVI A,0 ; SORT FLAG CALL DIRALPHA ; ALPHABETIZE POP PSW ; GET PSW RET ;************************************************************************** ;* ;* THIS ROUTINE ACCEPTS A BASE ADDRESS FOR THE DYNAMIC BUFFERS ;* REQUIRED, DETERMINES HOW MUCH SPACE IS REQUIRED FOR THE BUFFERS, ;* AND SETS THE ORDER PTR TO PT TO THE FIRST AND DIRBUF TO PT TO ;* THE SECOND (ORDER SPACE = DIRMAX*2 AND DIRBUF = DIRMAX * ESIZE) ;* ON INPUT, HL PTS TO AVAILABLE BASE ;* ON OUTPUT, HL PTS TO DIRBUF ;* A=0 AND ZERO FLAG SET IF BDOS OVERRUN ;* DBUFFER:: PUSH D ; SAVE DE PUSH B ; SAVE BC SHLD ORDER ; PT TO ORDER TABLE CALL DPARAMS ; GET PARAMETERS LHLD DIRMAX ; NUMBER OF ENTRIES IN DIR XCHG ; ... IN DE LHLD ORDER ; ADD TO ORDER BASE DAD D ; *1 CALL MEMCHK ; CHECK FOR WITHIN RANGE DAD D ; HL PTS TO DIRBUF CALL MEMCHK ; CHECK FOR WITHIN RANGE SHLD DIRBUF ; SET PTR AND HL PTS TO DIRECTORY BUFFER POP B ; RESTORE BC POP D ; RESTORE DE MVI A,0FFH ; OK ORA A ; SET FLAGS RET MEMCHK: PUSH H ; SAVE REGS PUSH D XCHG ; NEXT ADDRESS IN DE LHLD BDOS+1 ; GET ADDRESS OF BDOS MOV A,D ; CHECK FOR PAGE OVERRUN CMP H JNC MEMORUN ; OVERRUN IF D>=H POP D POP H RET MEMORUN: POP D ; RESTORE POP H XRA A ; RETURN 0 POP B ; CLEAR STACK POP B ; RESTORE BC RET ;************************************************************************** ;* ;* THIS ROUTINE EXTRACTS DISK PARAMETER INFORMATON FROM THE DPB AND ;* STORES THIS INFORMATION IN: ;* BLKSHF <-- BLOCK SHIFT FACTOR (1 BYTE) ;* BLKMSK <-- BLOCK MASK (1 BYTE) ;* EXTENT <-- EXTENT MASK (1 BYTE) ;* BLKMAX <-- MAX NUMBER OF BLOCKS ON DISK (2 BYTES) ;* DIRMAX <-- MAX NUMBER OF DIRECTORY ENTRIES (2 BYTES) ;* DPARAMS:: PUSH B ; SAVE REGS PUSH D PUSH H PUSH PSW MVI C,12 ; GET VERSION NUMBER CALL BDOS MOV A,H ; CHECK FOR 1.4 ORA L JZ DPARM1 ; PRE-2.x...GET PARAMS THE 1.4 WAY ;* ;* VERSION 2.x OR MP/M ;* MVI C,31 ; 2.x OR MP/M...REQUEST DPB CALL BDOS INX H INX H MOV A,M ; GET BLOCK SHIFT STA BLKSHF ; BLOCK SHIFT FACTOR INX H ; GET BLOCK MASK MOV A,M STA BLKMSK ; BLOCK MASK INX H MOV A,M ; GET MAX EXTENT NUMBER STA EXTENT ; THIS IS CALLED THE EXTENT MASK INX H MOV E,M ; GET MAX BLOCK NUMBER INX H MOV D,M XCHG INX H ; ADD 1 FOR MAX NUMBER OF BLOCKS SHLD BLKMAX ; MAXIMUM NUMBER OF BLOCKS XCHG INX H MOV E,M ; GET DIRECTORY SIZE INX H MOV D,M XCHG INX H ; ADD 1 FOR NUMBER OF ENTRIES SHLD DIRMAX ; MAXIMUM NUMBER OF DIRECTORY ENTRIES JMP DPARM2 ;* ;* CP/M 1.4 ;* DPARM1: LHLD BDOS+1 ; GET PARAMS 1.4 STYLE MVI L,3BH ; POINT TO DIRECTORY SIZE ENTRY IN 1.4 BDOS MOV E,M ; GET IT MVI D,0 ; FORCE HI ORDER BYTE TO 0 XCHG ; SAVE SIZE OF DIRECTORY ENTRY INX H ; ADD 1 SHLD DIRMAX ; MAXIMUM NUMBER OF ENTRIES XCHG INX H ; POINT TO BLOCK SHIFT MOV A,M STA BLKSHF ; BLOCK SHIFT FACTOR INX H ; POINT TO BLOCK MASK MOV A,M STA BLKMSK ; BLOCK MASK INX H MOV E,M ; GET MAXIMUM BLOCK NUMBER MVI D,0 XCHG INX H ; ADD 1 SHLD BLKMAX ; MAXIMUM NUMBER OF BLOCKS XRA A ; A=0 STA EXTENT ; SET EXTENT MASK TO 0 FOR CP/M 1.4 EXTENT SIZE ;* ;* ALL PARAMETERS EXTRACTED ;* DPARM2: POP PSW ; RESTORE REGS POP H POP D POP B RET ;************************************************************************** ;* ;* COMPUTE AMOUNT OF FREE SPACE LEFT ON DISK ;* ON EXIT, DE=AMOUNT OF FREE SPACE ON DISK IN K ;* THE DPARAMS ROUTINE MUST BE CALLED BEFORE THIS ROUTINE IS USED ;* DFREE:: PUSH B ; SAVE REGS PUSH H PUSH PSW MVI C,27 ; GET ADDRESS OF ALLOCATION VECTOR CALL BDOS XCHG LHLD BLKMAX ; GET LENGTH OF ALLOCATION VECTOR LXI B,0 ; INIT BLOCK COUNT TO 0 ;* ;* BC IS ACCUMULATOR FOR SPACE ;* FREE1: PUSH D ; SAVE ALLOC ADDRESS LDAX D ; GET BIT PATTERN OF ALLOCATION BYTE MVI E,8 ; SET TO PROCESS 8 BLOCKS FREE2: RAL ; ROTATE ALLOCATED BLOCK BIT INTO CARRY FLAG JC FREE3 ; IF SET (BIT=1), BLOCK IS ALLOCATED INX B ; IF NOT SET, BLOCK IS NOT ALLOCATED, SO INCREMENT ; FREE BLOCK COUNT FREE3: MOV D,A ; SAVE REMAINING ALLOCATION BITS IN D DCX H ; COUNT DOWN NUMBER OF BLOCKS ON DISK MOV A,L ORA H JZ FREE4 ; DONE IF NO MORE BLOCKS LEFT MOV A,D ; A=CURRENT ALLOCATION BIT PATTERN DCR E ; HAVE ALL 8 BITS BEEN EXAMINED? JNZ FREE2 ; CONTINUE IF NOT POP D ; GET POINTER TO ALLOCATION VECTOR INX D ; POINT TO NEXT ALLOCATION BYTE JMP FREE1 ; CONTINUE BY PROCESSING NEXT ALLOCATION BYTE ;* ;* BC = TOTAL AMOUNT OF FREE SPACE IN TERMS OF BLOCKS ;* FREE4: POP D ; CLEAR DE FROM STACK MOV L,C ; HL=BC=NUMBER OF FREE BLOCKS MOV H,B LDA BLKSHF ; GET BLOCK SHIFT FACTOR SUI 3 ; CONVERT NUMBER OF BLOCKS TO K JZ FREE6 ; DONE IF SINGLE DENSITY (1K PER BLOCK) ;* ;* WE ARE AT A MORE ADVANCED DENSITY LEVEL; MULTIPLY THE NUMBER OF BLOCKS ;* BY THE SIZE OF A BLOCK IN K ;* FREE5: DAD H ; 2, 4, 8, 16, ETC K/BLK, SO BLOCK SHIFT FACTOR DCR A ; IS A POWER-OF-TWO MULTIPLE JNZ FREE5 ;* ;* AT THIS POINT, HL=AMOUNT OF FREE SPACE ON DISK IN K ;* FREE6: XCHG ; DE=ANSWER POP PSW ; RESTORE REGS POP H POP B RET ;************************************************************************** ;* ;* COMPUTE SIZE OF FILE WHOSE LAST EXTENT IS POINTED TO BY HL ;* FILE SIZE IS RETURNED IN DE IN K ;* NOTE THAT THE ROUTINE DPARAMS MUST HAVE BEEN CALLED BEFORE THIS ROUTINE ;* IS USED ;* FSIZE:: PUSH B ; SAVE REGS PUSH H PUSH PSW LXI D,12 ; POINT TO EXTENT DAD D MOV E,M ; GET EXTENT # MVI D,0 INX H ; SKIP S1 INX H ; SKIP S2 INX H ; HL PTS TO RECORD COUNT FIELD MOV A,M ; GET RECORD COUNT OF LAST EXTENT XCHG DAD H ; NUMBER OF EXTENTS TIMES 16K DAD H DAD H DAD H XCHG ; TOTAL SIZE OF PREVIOUS EXTENTS IN DE LXI H,BLKMSK ADD M ; ROUND LAST EXTENT TO BLOCK SIZE RRC RRC ; CONVERT FROM RECORDS TO K RRC ANI 1FH MOV L,A ; ADD SIZE OF LAST EXTENT TO TOTAL OF PREVIOUS EXTENTS MVI H,0 ; HL=SIZE OF LAST EXTENT, DE=TOTAL OF PREVIOUS EXTENTS DAD D ; HL=TOTAL FILE SIZE IN BLOCKS LDA BLKMSK ; GET RECORDS/BLK-1 RRC RRC ; CONVERT TO K/BLK RRC ANI 1FH CMA ; USE TO FINISH ROUNDING ANA L MOV L,A ; HL NOW EQUALS THE SIZE OF THE FILE IN K INCREMENTS XCHG ; DE=FILE SIZE IN K POP PSW ; RESTORE REGS POP H POP B RET ;************************************************************************** ;* ;* BUILD DIRECTORY TABLE AT DIRBUF ;* THIS IS THE OPTIMAL DIRECTORY LOAD ROUTINE; IT ONLY LOADS UNIQUE ;* FILE NAMES FROM DISK, BUT THE INFORMATION IS NOT SUFFICIENT ;* TO COMPUTE THE FILE SIZES ;* ON INPUT, HL PTS TO DIRECTORY BUFFER (16 x N MAX) ;* ON OUTPUT, BC IS NUM OF FILES ;* DE, HL, PSW UNAFFECTED ;* DIRLOAD:: PUSH H ; SAVE REGISTERS PUSH D MVI A,0FFH ; SELECT FILES OF SMALLER EX STA DFLAG ; SET FLAG DLCOMMON: MVI A,'?' ; SELECT FILES FROM ALL USER AREAS STA TFCB ; SET DR FIELD TO ZERO TO DO THIS SHLD DIRBUF ; SAVE PTR TO DIRECTORY BUFFER SHLD DSTART ; SET START OF BUFFER AREA ;* ;* THIS SECTION OF CODE INITIALIZES THE COUNTERS USED ;* LXI H,0 ; HL=0 SHLD FCOUNT ; TOTAL FILES ON DISK = 0 ;* ;* NOW WE BEGIN SCANNING FOR FILES TO PLACE INTO THE MEMORY BUFFER ;* MVI C,17 ; SEARCH FOR FILE LXI D,TFCB ; PT TO WILD NAME CALL BDOS CPI 255 ; NO MATCH? JZ DIRDN DIRLP: CALL PENTRY ; PLACE ENTRY IN DIR JZ DIROVFL ; MEMORY OVERFLOW ERROR MVI C,18 ; SEARCH FOR NEXT MATCH CALL BDOS CPI 255 ; DONE? JNZ DIRLP ;* ;* NOW WE ARE DONE WITH THE LOAD -- SET UP RETURN VALUES ;* DIRDN: MVI A,0FFH ; LOAD OK ORA A ; SET FLAGS DIRDNX: LHLD FCOUNT ; GET TOTAL NUMBER OF FILES MOV B,H ; ... IN BC MOV C,L ;* ;* RESTORE REGISTERS AND RETURN ;* POP D POP H RET ;* ;* MEMORY OVERFLOW ERROR ;* DIROVFL: XRA A ; LOAD ERROR JMP DIRDNX ;* ;* PENTRY -- ;* PLACE ENTRY IN DIRECTORY BUFFER IF NOT AN ERASED ENTRY ;* ;* ON INPUT, A=0-3 FOR ADR INDEX IN BUFF OF ENTRY FCB ;* FCOUNT=NUMBER OF FILES IN DIR SO FAR ;* ON OUTPUT, FCOUNT=NUMBER OF FILES IN DIR SO FAR ;* A=0 AND ZERO FLAG SET IF MEMORY OVERFLOW ERROR ;* PENTRY: PUSH B ; SAVE REGS PUSH D PUSH H RRC ; MULTIPLY BY 32 FOR OFFSET COMPUTATION RRC RRC ANI 60H ; A=BYTE OFFSET LXI D,BUFF ; PT TO BUFFER ENTRY MOV L,A ; LET HL=OFFSET MVI H,0 DAD D ; HL=PTR TO FCB MOV A,M ; GET USER NUMBER CPI 0E5H ; DELETED? JZ PEDONE ; SKIP IT IF DELETED ;* ;* HL=ADR OF FCB IN BUFF ;* ;* ;* SCAN DIRECTORY ENTRIES AS LOADED SO FAR FOR ANOTHER ENTRY BY THE SAME ;* NAME; IF FOUND, SET THAT ENTRY TO BE THE ENTRY WITH THE LARGER EX ;* AND RETURN WITH THE ZERO FLAG SET, INDICATING NO NEW FILE; IF NOT ;* FOUND, RETURN WITH ZERO FLAG RESET (NZ) ;* CALL DUPENTRY ; CHECK FOR DUPLICATE AND SELECT EX JZ PEDONE ; SKIP IF DUPLICATE ;* INCREMENT TOTAL NUMBER OF FILES PUSH H ; SAVE PTR TO FCB LHLD FCOUNT ; TOTAL FILES = TOTAL FILES + 1 INX H SHLD FCOUNT POP H ; GET PTR TO FCB ;* ;* COPY FCB PTED TO BY HL INTO DIRECTORY BUFFER ;* XCHG ; SAVE PTR IN DE LHLD DIRBUF ; PT TO NEXT ENTRY LOCATION XCHG ; HL PTS TO FCB, DE PTS TO NEXT ENTRY LOCATION MVI B,ESIZE ; NUMBER OF BYTES/ENTRY CALL MOVE ; COPY FCB INTO MEMORY BUFFER XCHG ; HL PTS TO NEXT ENTRY SHLD DIRBUF ; SET PTR XCHG ; PTR TO NEXT ENTRY IN DE LHLD BDOS+1 ; BASE ADDRESS OF BDOS IN HL MOV A,H ; GET BASE PAGE OF BDOS SUI 9 ; COMPUTE 1 PAGE IN FRONT OF BASE PAGE OF CCP CMP D ; IS PTR TO NEXT ENTRY BEYOND THIS? JNZ PEDONE ; OK IF NOT AT BUFFER OVERFLOW LEVEL ;* DONE WITH PENTRY WITH MEMORY OVERFLOW ERROR PEOVFL: XRA A ; ERROR POP H ; RESTORE REGS POP D POP B RET ;* DONE WITH PENTRY AND NO ERROR PEDONE: MVI A,0FFH ; NO ERROR ORA A ; SET FLAGS POP H ; RESTORE REGS POP D POP B RET ;* ;* COPY FROM HL TO DE FOR B BYTES ;* MOVE: MOV A,M ; GET BYTE STAX D ; PUT BYTE INX H ; PT TO NEXT INX D DCR B ; COUNT DOWN JNZ MOVE RET ;************************************************************************** ;* ;* BUILD DIRECTORY TABLE AT DIRBUF ;* THIS DIRECTORY LOAD ROUTINE IS MUST LESS EFFICIENT THAN DIRLOAD, ;* BUT IT DOES LOAD ENOUGH INFORMATION TO COMPUTE THE FILE ;* SIZES USING THE FSIZE ROUTINE ;* ON INPUT, HL PTS TO DIRECTORY BUFFER (16 x N MAX) ;* ON OUTPUT, BC IS NUM OF FILES ;* DE, HL, PSW UNAFFECTED ;* DIRSLOAD:: PUSH H ; SAVE REGISTERS PUSH D XRA A ; SELECT LARGER EX STA DFLAG ; SET FLAG JMP DLCOMMON ;* ;* SCAN DIRECTORY ENTRIES AS LOADED SO FAR FOR ANOTHER ENTRY BY THE SAME ;* NAME; IF FOUND, SET THAT ENTRY TO BE THE ENTRY WITH THE LARGER EX ;* AND RETURN WITH THE ZERO FLAG SET, INDICATING NO NEW FILE; IF NOT ;* FOUND, RETURN WITH ZERO FLAG RESET (NZ) ;* ON INPUT, HL PTS TO ENTRY TO SCAN FOR, FCOUNT = NUMBER OF ENTRIES SO FAR, ;* AND (DSTART) = STARTING ADDRESS OF DIRECTORY LOADED ;* ON OUTPUT, A=0 AND ZERO FLAG SET IF DUPLICATE ENTRY FOUND; A=0FFH AND NZ ;* IF NO DUP ENTRY FOUND ;* ONLY HL NOT AFFECTED ;* DUPENTRY: PUSH H ; SAVE PTR TO ENTRY TO SCAN FOR XCHG ; PTR IN DE LHLD FCOUNT ; CHECK COUNT MOV A,H ; NO ENTRIES? ORA L JZ NODUP ; NO DUPLICATE ENTRY RETURN MOV B,H ; BC=NUMBER OF ENTRIES MOV C,L LHLD DSTART ; HL PTS TO FIRST ENTRY DUPELOOP: PUSH B ; SAVE COUNT PUSH H ; SAVE PTRS PUSH D MVI B,12 ; COMPARE USER NUMBER, FN, AND FT CALL COMP POP D ; RESTORE PTRS POP H JNZ NODUPL ; CONTINUE LOOKING FOR ANOTHER ENTRY ; DUPLICATE ENTRIES HAVE BEEN IDENTIFIED AT THIS POINT PUSH H ; SAVE PTRS AGAIN PUSH D LXI B,12 ; PT TO EX FIELD DAD B XCHG DAD B ; DE PTS TO DIRECTORY ENTRY, HL PTS TO TARGET LDAX D ; GET EXTENT FIELD FROM DIRECTORY ENTRY CMP M ; COMPARE WITH THAT IN TARGET POP D ; GET PTRS POP H JNC DUPSMALL ; NEW TARGET IS LARGER THAN STORED ENTRY LDA DFLAG ; CHECK FLAG FOR LARGE OR SMALL EX ORA A ; 0=SELECT LARGER JNZ DUPFND DUPSEL: XCHG ; MAKE HL PT TO NEW TARGET, DE PT TO DEST MVI B,ESIZE ; NUMBER OF BYTES TO MOVE CALL MOVE ; MOVE IT JMP DUPFND ; NEW TARGET IS SMALLER THAN STORED ENTRY DUPSMALL: LDA DFLAG ; CHECK FLAG FOR LARGE OR SMALL EX ORA A ; 0FFH=SELECT SMALLER JNZ DUPSEL ; RETURN INDICATOR THAT DUPLICATE WAS FOUND DUPFND: POP B ; CLEAR COUNT FROM STACK XRA A ; INDICATE DUP FOUND POP H ; RESTORE PTR TO ENTRY TO SCAN FOR RET ; NO DUPLICATE FOUND; ADVANCE TO NEXT ENTRY NODUPL: LXI B,ESIZE ; HL PTS TO CURRENT ENTRY IN BUFFER, SO ADD ESIZE TO IT DAD B POP B ; GET COUNT DCX B ; COUNT DOWN MOV A,B ; CHECK FOR DONE ORA C JNZ DUPELOOP ; NO DUPLICATE FOUND, PERIOD NODUP: MVI A,0FFH ; INDICATE DUP NOT FOUND ORA A ; SET FLAGS POP H ; RESTORE PTR TO ENTRY TO SCAN FOR RET ;************************************************************************** ;* ;* DIRALPHA -- ALPHABETIZES DIRECTORY PTED TO BY HL; BC CONTAINS ;* THE NUMBER OF FILES IN THE DIRECTORY AND A = SORT FLAG ;* (0=SORT BY FILE NAME/TYPE, <>0 = SORT BY FILE TYPE/NAME) ;* DIRALPHA:: STA CMP$FLAG ; SET FLAG MOV A,B ; ANY FILES? ORA C RZ PUSH H ; SAVE REGS PUSH D PUSH B SHLD DIRBUF ; SAVE PTR TO DIRECTORY PUSH H ; SAVE HL MOV H,B ; HL=BC=FILE COUNT MOV L,C SHLD N ; SET "N" POP H ;* ;* 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 ;* SORT: XCHG ; POINTER TO DIRECTORY IN DE LHLD 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: MOV M,E ; STORE LOW-ORDER ADDRESS INX H ; PT TO NEXT ORDER BYTE MOV M,D ; STORE HIGH-ORDER ADDRESS INX H ; PT TO NEXT ORDER ENTRY PUSH H ; SAVE PTR LXI H,ESIZE ; HL=NUMBER OF BYTES/ENTRY DAD D ; PT TO NEXT DIR1 ENTRY XCHG ; DE PTS TO NEXT ENTRY POP H ; GET PTR TO ORDER TABLE DCX B ; COUNT DOWN MOV A,B ; DONE? ORA C JNZ 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 ;* LHLD N ; NUMBER OF ITEMS TO SORT SHLD GAP ; SET INITIAL GAP TO N FOR FIRST DIVISION BY 2 ;* FOR (GAP = N/2; GAP > 0; GAP = GAP/2) SRTL0: ORA A ; CLEAR CARRY LHLD GAP ; GET PREVIOUS GAP MOV A,H ; ROTATE RIGHT TO DIVIDE BY 2 RAR MOV H,A MOV A,L RAR MOV L,A ;* TEST FOR ZERO ORA H JZ SDONE ; DONE WITH SORT IF GAP = 0 SHLD GAP ; SET VALUE OF GAP SHLD I ; SET I=GAP FOR FOLLOWING LOOP ;* FOR (I = GAP + 1; I <= N; I = I + 1) SRTL1: LHLD I ; ADD 1 TO I INX H SHLD I ;* TEST FOR I <= N XCHG ; I IS IN DE LHLD N ; GET N MOV A,L ; COMPARE BY SUBTRACTION SUB E MOV A,H SBB D ; CARRY SET MEANS I > N JC SRTL0 ; DON'T DO FOR LOOP IF I > N LHLD I ; SET J = I INITIALLY FOR FIRST SUBTRACTION OF GAP SHLD J ;* FOR (J = I - GAP; J > 0; J = J - GAP) SRTL2: LHLD GAP ; GET GAP XCHG ; ... IN DE LHLD J ; GET J MOV A,L ; COMPUTE J - GAP SUB E MOV L,A MOV A,H SBB D MOV H,A SHLD J ; J = J - GAP JC SRTL1 ; IF CARRY FROM SUBTRACTIONS, J < 0 AND ABORT MOV A,H ; J=0? ORA L JZ SRTL1 ; IF ZERO, J=0 AND ABORT ;* SET JG = J + GAP XCHG ; J IN DE LHLD GAP ; GET GAP DAD D ; J + GAP SHLD JG ; JG = J + GAP ;* IF (V(J) <= V(JG)) CALL ICOMPARE ; J IN DE, JG IN HL ;* ... THEN BREAK JC SRTL1 ;* ... ELSE EXCHANGE LHLD J ; SWAP J, JG XCHG LHLD JG CALL ISWAP ; J IN DE, JG IN HL ;* END OF INNER-MOST FOR LOOP JMP SRTL2 ;* ;* SORT IS DONE -- RESTRUCTURE DIR1 IN SORTED ORDER IN PLACE ;* SDONE: LHLD N ; NUMBER OF ENTRIES MOV B,H ; ... IN BC MOV C,L LHLD ORDER ; PTR TO ORDERED POINTER TABLE SHLD PTPTR ; SET PTR PTR LHLD DIRBUF ; PTR TO UNORDERED DIRECTORY SHLD PTDIR ; SET PTR DIR BUFFER ;* FIND PTR TO NEXT DIR1 ENTRY SRTDN: LHLD PTPTR ; PT TO REMAINING POINTERS XCHG ; ... IN DE LHLD PTDIR ; HL PTS TO NEXT DIR ENTRY PUSH B ; SAVE COUNT OF REMAINING ENTRIES ;* FIND PTR TABLE ENTRY SRTDN1: LDAX D ; GET CURRENT POINTER TABLE ENTRY VALUE INX D ; PT TO HIGH-ORDER POINTER BYTE CMP L ; COMPARE AGAINST DIR1 ADDRESS LOW JNZ SRTDN2 ; NOT FOUND YET LDAX D ; LOW-ORDER BYTES MATCH -- GET HIGH-ORDER POINTER BYTE CMP H ; COMPARE AGAINST DIR1 ADDRESS HIGH JZ SRTDN3 ; MATCH FOUND SRTDN2: INX D ; PT TO NEXT PTR TABLE ENTRY DCX B ; COUNT DOWN MOV A,C ; END OF TABLE? ORA B JNZ SRTDN1 ; CONTINUE IF NOT ;* FATAL ERROR -- INTERNAL ERROR; POINTER TABLE NOT CONSISTENT FERR$PTR: CALL PRINT DB 0DH,0AH,'DIRALPHA -- Pointer Error',0 JMP CPM ;* 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: LHLD PTPTR ; GET PTR TO NEXT ORDERED ENTRY DCX D ; DE PTS TO LOW-ORDER POINTER ADDRESS MOV A,M ; MAKE PTR TO NEXT UNORDERED DIR1 PT TO BUFFER FOR STAX D ; DIR1 ENTRY TO BE MOVED TO NEXT UNORDERED DIR1 POS INX H ; PT TO NEXT PTR ADDRESS INX D MOV A,M ; MAKE HIGH POINT SIMILARLY STAX D ;* COPY NEXT UNORDERED DIR1 ENTRY TO HOLD BUFFER MVI B,ESIZE ; B=NUMBER OF BYTES/ENTRY LHLD PTDIR ; PT TO ENTRY LXI D,HOLD ; PT TO HOLD BUFFER PUSH B ; SAVE B=NUMBER OF BYTES/ENTRY CALL MOVE POP B ;* COPY TO-BE-ORDERED DIR1 ENTRY TO NEXT ORDERED DIR1 POSITION LHLD PTPTR ; POINT TO ITS POINTER MOV E,M ; GET LOW-ADDRESS POINTER INX H MOV D,M ; GET HIGH-ADDRESS POINTER LHLD PTDIR ; DESTINATION ADDRESS FOR NEXT ORDERED DIR1 ENTRY XCHG ; HL PTS TO ENTRY TO BE MOVED, DE PTS TO DEST PUSH B ; SAVE B=NUMBER OF BYTES/ENTRY CALL MOVE POP B XCHG ; HL PTS TO NEXT UNORDERED DIR1 ENTRY SHLD PTDIR ; SET POINTER FOR NEXT LOOP ;* COPY ENTRY IN HOLD BUFFER TO LOC PREVIOUSLY HELD BY LATEST ORDERED ENTRY LHLD PTPTR ; GET PTR TO PTR TO THE DESTINATION MOV E,M ; GET LOW-ADDRESS POINTER INX H MOV D,M ; HIGH-ADDRESS POINTER LXI H,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 LHLD PTPTR ; POINTER TO CURRENT ENTRY INX H ; SKIP OVER IT INX H SHLD PTPTR ;* COUNT DOWN POP B ; GET COUNTER DCX B ; COUNT DOWN MOV A,C ; DONE? ORA B JNZ SRTDN POP B ; RESTORE REGS POP D POP H RET ; DONE ;* ;* SWAP (Exchange) the pointers in the ORDER table whose indexes are in ;* HL and DE ;* ISWAP: PUSH H ; SAVE HL LHLD ORDER ; ADDRESS OF ORDER TABLE - 2 MOV B,H ; ... IN BC MOV C,L POP H DCX H ; ADJUST INDEX TO 0...N-1 FROM 1...N DAD H ; HL PTS TO OFFSET ADDRESS INDICATED BY INDEX ; OF ORIGINAL HL (1, 2, ...) DAD B ; HL NOW PTS TO POINTER INVOLVED XCHG ; DE NOW PTS TO POINTER INDEXED BY HL DCX H ; ADJUST INDEX TO 0...N-1 FROM 1...N DAD H ; HL PTS TO OFFSET ADDRESS INDICATED BY INDEX ; OF ORIGINAL DE (1, 2, ...) DAD B ; HL NOW PTS TO POINTER INVOLVED MOV C,M ; EXCHANGE POINTERS -- GET OLD (DE) LDAX D ; -- GET OLD (HL) XCHG ; SWITCH MOV M,C ; PUT NEW (HL) STAX D ; PUT NEW (DE) INX H ; PT TO NEXT BYTE OF POINTER INX D MOV C,M ; GET OLD (HL) LDAX D ; GET OLD (DE) XCHG ; SWITCH MOV M,C ; PUT NEW (DE) STAX D ; 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 H ; SAVE HL LHLD ORDER ; ADDRESS OF ORDER - 2 MOV B,H ; ... IN BC MOV C,L POP H DCX H ; ADJUST INDEX TO 0...N-1 FROM 1...N DAD H ; DOUBLE THE ELEMENT NUMBER TO POINT TO THE PTR DAD B ; ADD TO THIS THE BASE ADDRESS OF THE PTR TABLE XCHG ; RESULT IN DE DCX H ; ADJUST INDEX TO 0...N-1 FROM 1...N DAD H ; DO THE SAME WITH THE ORIGINAL DE DAD B XCHG ;* ;* 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 ;* MOV C,M ; BC IS MADE TO POINT TO THE OBJECT INDEXED TO INX H ; ... BY THE ORIGINAL HL MOV B,M XCHG MOV E,M ; DE IS MADE TO POINT TO THE OBJECT INDEXED TO INX H ; ... BY THE ORIGINAL DE MOV D,M MOV H,B ; SET HL = OBJECT PTED TO INDIRECTLY BY BC MOV 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 DE16K) ;* DIRNPACK:: PUSH H ; SAVE REGS PUSH D PUSH B PUSH PSW DNPACK: MOV A,B ; DONE? ORA C JZ DNPAK1 DCX B ; COUNT DOWN MOV A,M ; GET FIRST BYTE CMA ; FLIP BITS ANI 80H ; LOOK AT MOST SIG BIT MOV D,A ; SAVE IN D MOV A,M ; GET FIRST BYTE AGAIN ANI 7FH ; MASK OUT MS BIT ORA D ; MASK IN NEW MOST SIG BIT MOV M,A ; PUT BYTE BACK ANI 80H ; SELECTED NOW? JZ DNPAK0 ; SKIP IF NOT SELECTED PUSH B ; SAVE COUNTER CALL FLGCHK ; CHECK FOR FLAGS POP B ; GET COUNTER JZ DNPAK0 MOV A,M ; GET BYTE ANI 7FH ; DESELECT IT MOV M,A ; PUT BYTE BACK DNPAK0: LXI D,ESIZE ; POINT TO NEXT ENTRY DAD D JMP DNPACK DNPAK1: POP PSW ; RESTORE REGS POP B POP D POP H ; NOW FALL THRU TO DIRPACK ;************************************************************************** ;* ;* DIRECTORY PACK UTILITY -- RESTRUCTURE THE DIRECTORY TO INCLUDE ONLY ;* THOSE ENTRIES MARKED BY DIRSEL ;* ;* ON INPUT, HL PTS TO DIRECTORY BUFFER AND BC=NUMBER OF FILES ;* ON EXIT, BC=NUMBER OF SELECTED FILES ;* REQUIRED SIDE EFFECT IS THAT FLAG EXTENT (SET BY DIR::) BE CORRECT ;* (IN MOST CASES, DEFAULT OF 0 IS OK, EXCEPT WHEN EXTENT SIZE >16K) ;* DIRPACK:: PUSH H ; SAVE REGS PUSH D PUSH PSW PUSH H ; SAVE HL LXI H,0 SHLD FCOUNT ; INIT FILE COUNT POP H ; GET HL SHLD DIRBUF ; SAVE PTR PUSH B ; SAVE COUNTER DPLOOP: POP B ; GET COUNTER MOV A,B ; CHECK FOR DONE ORA C JZ DPDONE DCX B ; COUNT DOWN PUSH B ; SAVE COUNTER MOV A,M ; GET 1ST BYTE OF ENTRY ANI 80H ; SELECTED? JZ DPNEXT ;* FOUND SELECTED ENTRY MOV A,M ; CLEAR MSB OF SELECTED ENTRY ANI 7FH MOV M,A PUSH H ; SAVE PTR LHLD FCOUNT ; INCREMENT FILE COUNT INX H SHLD FCOUNT POP D ; PT TO CURRENT ENTRY IN DE LHLD DIRBUF ; PT TO NEXT ENTRY POSITION XCHG ; HL PTS TO CURRENT, DE PTS TO NEXT ENTRY MVI B,ESIZE ; COPY ENTRY CALL MOVE XCHG ; HL PTS TO NEXT ENTRY SHLD DIRBUF ; SAVE IT XCHG ; HL PTS TO NEXT ENTRY TO CHECK JMP DPLOOP ; CONTINUE ;* SKIP TO NEXT ENTRY DPNEXT: LXI B,ESIZE ; SKIP ENTRY DAD B JMP DPLOOP ; CONTINUE ;* COMPRESSION COMPLETE -- SET UP RETURNED VALUES DPDONE: LHLD FCOUNT ; PUT FILE COUNT MOV B,H ; ... IN BC MOV C,L POP PSW ; RESTORE REGS POP D POP H RET END