; PROGRAM: FIND ; AUTHOR: RICHARD CONN ; VERSION: 1.1 ; DATE: 25 July 83 ; PREVIOUS VERSIONS: 1.0 (24 JULY 83) VERS EQU 11 ;version number ; ; FIND searches through all of the known disks for one or more ; files matching the passed file specification. AFNs (Ambiguous File Names) ; are permitted. FIND is invoked by the following command line: ; FIND afn,afn,afn,... o ; where "afn" refers to the file sought and "o" is none or more of: ; S - Include System Files ; FIND may be installed by GENINS to restrict the disks and user ; areas searched. ; ; ; 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 FCB2 EQU BOOT+6CH ;SECONDARY FILE CONTROL AREA CR EQU 'M'-'@' ;CTL-M FOR CARRIAGE RETURN LF EQU 'J'-'@' ;CTL-J FOR LINE FEED CTRLC EQU 'C'-'@' ;ABORT CTRLS EQU 'S'-'@' ;PAUSE ESIZE EQU 12 ;12 BYTES/DIR ENTRY ; ; This program is Copyright (c) 1983 by Richard Conn ; All Rights Reserved ; ; ZCPR2 and its utilities, including this one, are released ; to the public domain. Anyone who wishes to USE them may do so with ; no strings attached. The author assumes no responsibility or ; liability for the use of ZCPR2 and its utilities. ; ; The author, Richard Conn, has sole rights to this program. ; ZCPR2 and its utilities may not be sold without the express, ; written permission of the author. ; ; ; Branch to Start of Program ; ORG BOOT+100H JMP START ; ;****************************************************************** ; ; SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format ; ; This data block precisely defines the data format for ; initial features of a ZCPR2 system which are required for proper ; initialization of the ZCPR2-Specific Routines in SYSLIB. ; ; ; EXTERNAL PATH DATA ; EPAVAIL: DB 0FFH ; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES) EPADR: DW 40H ; ADDRESS OF EXTERNAL PATH IF AVAILABLE ; ; INTERNAL PATH DATA ; INTPATH: DB 0,0 ; DISK, USER FOR FIRST PATH ELEMENT ; DISK = 1 FOR A, '$' FOR CURRENT ; USER = NUMBER, '$' FOR CURRENT DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 ; DISK, USER FOR 8TH PATH ELEMENT DB 0 ; END OF PATH ; ; MULTIPLE COMMAND LINE BUFFER DATA ; MCAVAIL: DB 0FFH ; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE? MCADR: DW 0FF00H ; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE ; ; DISK/USER LIMITS ; MDISK: DB 4 ; MAXIMUM NUMBER OF DISKS MUSER: DB 31 ; MAXIMUM USER NUMBER ; ; FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK ; DOK: DB 0FFH ; ALLOW DISK CHANGE? (0=NO, 0FFH=YES) UOK: DB 0FFH ; ALLOW USER CHANGE? (0=NO, 0FFH=YES) ; ; PRIVILEGED USER DATA ; PUSER: DB 10 ; BEGINNING OF PRIVILEGED USER AREAS PPASS: DB 'chdir',0 ; PASSWORD FOR MOVING INTO PRIV USER AREAS DS 41-($-PPASS) ; 40 CHARS MAX IN BUFFER + 1 for ending NULL ; ; CURRENT USER/DISK INDICATOR ; CINDIC: DB '$' ; USUAL VALUE (FOR PATH EXPRESSIONS) ; ; DMA ADDRESS FOR DISK TRANSFERS ; DMADR: DW 80H ; TBUFF AREA ; ; NAMED DIRECTORY INFORMATION ; NDRADR: DW 00000H ; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY NDNAMES: DB 64 ; MAX NUMBER OF DIRECTORY NAMES DNFILE: DB 'NAMES ' ; NAME OF DISK NAME FILE DB 'DIR' ; TYPE OF DISK NAME FILE ; ; REQUIREMENTS FLAGS ; EPREQD: DB 000H ; EXTERNAL PATH? MCREQD: DB 000H ; MULTIPLE COMMAND LINE? MXREQD: DB 0FFH ; MAX USER/DISK? UDREQD: DB 000H ; ALLOW USER/DISK CHANGE? PUREQD: DB 000H ; PRIVILEGED USER? CDREQD: DB 000H ; CURRENT INDIC AND DMA? NDREQD: DB 000H ; NAMED DIRECTORIES? Z2CLASS: DB 0 ; CLASS 0 DB 'ZCPR2' DS 10 ; RESERVED ; ; END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA ; ;****************************************************************** ; ; ; Start of Program ; START: ; LXI H,0 ;SAVE STACK PTR DAD SP SHLD STACK LXI SP,STACK ;SET STACK POINTER CALL GTBIOS ;GET BIOS JUMP TABLE CALL HELLO ;SIGN ON MESSAGE CALL HELPCHK ;CHECK FOR AND PRINT HELP MESSAGE CALL OPTCHK ;BUILD FILE NAME TABLE AND PROCESS OPTIONS CALL CRLF ;NEW LINE CALL FIND ;DO THE SEARCHES CALL BYE ;SIGN OFF MESSAGE RETURN: LHLD STACK ;QUIET RETURN SPHL RET ; ; ** Main Routines ** ; ; ; SAY WHO WE ARE ; HELLO: CALL PRINT DB 'FIND, Version ' DB (VERS/10)+'0','.',(VERS MOD 10)+'0' DB 0 RET ; ; CHECK FOR HELP REQUEST ; HELPCHK: LDA FCB+1 ;GET 1ST BYTE OF FILENAME CPI '/' ;HELP? JZ HCK1 CPI ' ' ;MAKE SURE IT IS NON-BLANK RNZ ;OK - KEEP GOING ; ; IF NO FILE NAME IS SPECIFIED, ABORT WITH NOTICE ; HCK1: CALL PRINT DB CR,LF DB CR,LF,' FIND is used to search through all logged-in disks' DB CR,LF,'for any files matching an ambiguous file name (wild' DB CR,LF,'cards ar permitted). It is invoked as follows:' DB CR,LF,' FIND afn,afn,afn,... o' DB CR,LF,'where "afn" is the ambigous file name to search for' DB CR,LF,'(as many afns as desired may be specified), and' DB CR,LF,'"o" is none or more of the following options:' DB CR,LF,' S - Include System Files' DB CR,LF,'Drive prefixes (DU:) are ignored.' DB CR,LF DB 0 JMP RETURN ; ; CHECKS FOR S OPTION IN COMMAND LINE AND EXTRACTS FILE NAMES INTO TABLE ; OPTCHK: XRA A ;TURN OFF FLAGS STA SYSTEM ;NO SYSTEM FILES STA FFLAG ;NO FILES FOUND STA ECOUNT ;NO ENTRIES STA FNCOUNT ;NO FILE NAMES LXI H,TBUFF ;SCAN THRU TBUFF, BUILDING A FILE NAME TABLE MOV A,M ;GET CHAR COUNT INX H ;PT TO FIRST CHAR PUSH H ;SAVE PTR ADD L ;PT TO AFTER LAST CHAR MOV L,A MVI M,0 ;STORE ENDING ZERO POP H ;GET PTR TO FIRST CHAR CALL SBLANK ;SKIP BLANKS LXI D,FNTAB ;PT TO TABLE FNLOOP: PUSH D ;SAVE TABLE PTR CALL GETFN ;EXTRACT FILE NAME POP D PUSH H LXI H,11 ;PT TO NEXT TABLE ENTRY DAD D XCHG POP H LDA FNCOUNT ;INCREMENT COUNT INR A STA FNCOUNT MOV A,M ;GET TERMINATING CHAR INX H ;PT TO NEXT CPI ',' ;ANOTHER FOLLOWS? JZ FNLOOP DCX H ;POINT BACK TO DELIM CALL SBLANK ;SKIP TO NON-BLANK OPTCK1: MOV A,M ;GET OPTION CALL DELCHK ;DONE IF DELIM RZ CPI 'S' ;SYSTEM? JZ OPTCKS CALL PRINT DB CR,LF,'Invalid Option -- ',0 MOV A,M CALL TYPE JMP HCK1 OPTCKS: MVI A,0FFH ;SET FLAG STA SYSTEM INX H JMP OPTCK1 GETFN: PUSH D ;FILL TARGET FCB MVI B,11 ;11 BYTES MVI A,' ' ;SPACE FILL GETFN0: STAX D ;PUT SPACE INX D DCR B JNZ GETFN0 POP D ;PT TO ENTRY AGAIN CALL SCANCOL ;SCAN FOR COLON MVI B,8 ;8 CHARS MAX CALL GETFN1 ;GET AND FILL ENTRY MOV A,M ;GET CHAR CPI '.' ;DELIM? RNZ ;DONE INX H ;PT TO AFTER PERIOD MVI B,3 ;3 CHARS MAX AND DO IT AGAIN GETFN1: MOV A,M ;GET CHAR CPI '.' ;END OF FIELD? JZ GETFN3 CALL DELCHK ;CHECK DELIMITER RZ CPI '*' ;WILD? JZ GETFNQ STAX D ;STORE CHAR INX H ;PT TO NEXT INX D DCR B ;COUNT DOWN JNZ GETFN1 GETFN2: MOV A,M ;FLUSH CHARS TO DELIM CALL DELCHK ;CHECK FOR DELIMITER RZ INX H ;PT TO NEXT JMP GETFN2 GETFN3: INX D ;PT TO AFTER FIELD DCR B ;COUNT DOWN JNZ GETFN3 RET GETFNQ: MVI A,'?' ;FILL WITH QUESTION MARKS STAX D INX D DCR B JNZ GETFNQ JMP GETFN2 ;SKIP TO DELIM DELCHK: ORA A ;END OF LINE? RZ CPI '.' ;END OF FIELD? RZ CPI ',' ;END OF ENTRY? RZ CPI ' ' RET SBLANK: MOV A,M ;SKIP TO NON-BLANK CPI ' ' RNZ INX H JMP SBLANK SCANCOL: PUSH D ;SAVE TABLE PTR PUSH H ;SAVE PTR SCOL1: MOV A,M ;GET CHAR INX H ;PT TO NEXT CPI ':' ;COLON? JZ SCOLX CALL DELCHK ;CHECK FOR DELIMITER JNZ SCOL1 SCOL2: POP H ;RESTORE POP D RET SCOLX: XCHG ;DE PTS TO AFTER COLON POP H ;GET OLD PTR XCHG ;REPLACE IT POP D ;GET TABLE PTR RET ; ; LOOK THROUGH DIRECTORY ; FIND: XRA A ;SELECT FIRST DISK STA FCB CALL CPMCHK ;GET INFO THE FIRST TIME FIND1: RZ ;ABORT IF ERROR FIND2: CALL NXTSEC ;GET A DIRECTORY SECTOR JZ FIND3 ;RETURNS ZERO FLAG IF NO MORE CALL CHKENT ;CHECK IT OUT JMP FIND2 ;KEEP IT UP TILL DONE FIND3: CALL DIRALPHA ;SORT ENTRIES CALL PRFILES ;PRINT SORTED ENTRIES LDA FCB ;NEXT DISK INR A STA FCB CALL NXTDISK ;SELECT NEXT DISK JMP FIND1 ; ; SIGN OFF ; BYE: MVI C,13 ;RESET SYSTEM CALL BDOS LDA FFLAG ;GET FILE FOUND FLAG ORA A ;NO FILES FOUND? JNZ RETURN CALL PRINT DB CR,LF,'NO Files Found',0 JMP RETURN ; ; CHECKS THE CURRENT 4 DIRECTORY ENTRIES AGAINST ARGUMENT ; IF MATCH, REWRITES SECTOR WITH REACTIVATED 1ST BYTES ; CHKENT: MVI B,4 ;NUMBER OF ENTRIES PER SECTOR LXI H,TBUFF ;BEGINNING OF BUFFER CKLUP: PUSH B MOV A,M CPI 0E5H ;CHECK FOR UNUSED JZ CKINC XRA A ;A=0 STA CLPFLG ;SET FLAG FOR NO ENTRIES FOUND LDA FNCOUNT ;GET NUMBER OF FILE NAMES TO LOOK THRU MOV B,A ;... IN B LXI D,FNTAB ;PT TO TABLE CKLUP1: PUSH B ;SAVE COUNT PUSH H ;SAVE BEGINNING ADDRESS PUSH D CALL COMPAR ;COMPARE WITH ARGUMENT AND SAVE IF MATCH POP D LXI H,11 ;PT TO NEXT ENTRY DAD D XCHG POP H POP B DCR B ;COUNT DOWN JNZ CKLUP1 CKINC: POP B LXI D,32 ;LENGTH OF ENTRY DAD D DCR B JNZ CKLUP LHLD DIRMAX DCX H ;REDUCE SECTORS LEFT SHLD DIRMAX LHLD SECTOR ;POINT TO NEXT SECTOR INX H SHLD SECTOR XCHG LHLD MAXSEC ;REACHED LIMIT? INX H ;ONE MORE MOV A,H ;CHECK HIGH CMP D RNZ MOV A,L ;CHECK LOW CMP E RNZ LHLD TRACK ;NEXT TRACK INX H SHLD TRACK LXI H,1 ;FIRST SECTOR OF NEXT TRACK SHLD SECTOR RET ; ; COMPARE 11 BYTES OF DIRECTORY ENTRY AGAINST ARGUMENT; RNZ IF NOT MATCHED ; DE PTS TO TABLE ENTRY TO COMPARE TO ; COMPAR: LDA CLPFLG ;GET FOUND FLAG ORA A ;0=NO RNZ SHLD TEMP ;Hold pointer in case of match INX H XCHG MVI B,11 CMPR1: LDAX D ;GET DIRECTORY ENTRY CHARACTER ANI 7FH ;STRIP ANY FLAGS CMP M JZ CMPR2 MOV A,M CPI '?' RNZ CMPR2: INX D INX H ;BUMP TO NEXT CHARACTER DCR B JNZ CMPR1 ;LOOP FOR 11 CHARACTERS PUSH D ;SAVE ENTRY PTR LDAX D ;GET EXTENT IN B MOV B,A LDA EXTENT ;GET EXTENT MASK CMP B POP D ;GET ENTRY PTR JC CMPR4 ;NO MATCH LDA SYSTEM ;INCLUDE SYSTEM FILES? ORA A ;0=NO JNZ CMPR3 DCX D ;BACK UP 2 BYTES DCX D LDAX D ;GET T2 ANI 80H ;CHECK FOR SYS RNZ CMPR3: LHLD TEMP ;CHECK FOR USER LIMIT LDA MUSER ;GET MAX USER CMP M ;BEYOND MAX? JC CMPR4 LHLD FCOUNT ;INCREMENT COUNT INX H SHLD FCOUNT LHLD DSTART ;GET PTR TO NEXT ENTRY XCHG LHLD TEMP MVI B,ESIZE ;COPY ENTRY CALL MOVE XCHG SHLD DSTART ;PTR TO NEXT ENTRY XCHG LHLD BDOS+1 ;CHECK FOR MEMORY OVERFLOW MOV A,H SUI 10 ;BELOW CCP CMP D ;PT BEYOND LIMIT? JC MOVFL MVI A,0FFH ;SET FOUND FLAG STA FFLAG XRA A RET ;RETURNS 'ZERO' FLAG SET FOR MATCH CMPR4: MVI A,0FFH ;NO MATCH ORA A RET MOVFL: CALL PRINT DB CR,LF,'ABORT -- Not Enough Memory for Buffers',0 JMP RETURN ; ; CHECK FOR CP/M VERSION AND SET THINGS ; CPMCHK: MVI C,12 ;VERSION NUMBER REQUEST CALL BDOS CPI 20H ;EARLIER THAN 2.2? JC VERERR ; ; ADVANCE TO NEXT DISK ; NXTDISK: LXI B,TBUFF ;SET DMA ADDRESS CALL SETDMA LDA MDISK ;GET MAX DISK MOV B,A LDA FCB CMP B RZ ;ABORT IF BEYOND MAX MOV C,A MVI B,0 CALL SELDSK ;MAKE SURE DRIVE IS MOV A,H ; SELECTED ORA L RZ ;ERROR RETURN SHLD DPH ;SAVE THE ADDRESS LXI D,10 ;PT TO DPB DAD D MOV E,M ;GET DPB ADDRESS IN HL INX H MOV D,M XCHG MOV E,M ;NUMBER OF SECTORS/TRACK INX H ;AS 2-BYTE QUANTITY IN DE MOV D,M INX H XCHG SHLD MAXSEC ;SET MAX SECTORS/TRACK XCHG INX H INX H MOV A,M ;GET EXM STA EXTENT INX H ;PT TO DRM INX H INX H MOV E,M ;GET NUMBER OF INX H ; DIRECTORY ENTRIES MOV D,M XCHG INX H ;ACCOUNT FOR - 1 SHLD DSTART ;SAVE NUMBER OF DIRECTORY ENTRIES CALL SHFHL2 ;SHIFT 'HL' RIGHT 2 SHLD DIRMAX ;SAVE NUMBER DIRECTORY SECTORS LXI H,5 ;NOW POINT TO SYSTEM DAD D ; TRACK OFFSET MOV A,M ;PICK UP NUMBER OF INX H MOV H,M MOV L,A SHLD TRACK LXI H,1 ;SET SECTOR SHLD SECTOR LDA ECOUNT ;LAST NEW LINE? ANI 3 CNZ CRLF CALL PRINT DB 'Disk ',0 LDA FCB ADI 'A' CALL TYPE CALL PRINT DB ' --',CR,LF,0 LXI H,SCRATCH ;PT TO SCRATCH AREA SHLD ORDER ;ADDRESS OF ORDER TABLE XCHG LHLD DSTART ;GET NUMBER OF DIRECTORY ENTRIES DAD H ;DOUBLE FOR NUMBER OF BYTES IN ORDER TABLE DAD D ;PT TO FIRST BYTE OF DIRBUF SHLD DIRBUF ;SET PTR SHLD DSTART ;SET LOOP PTR LXI H,0 ;SET FILE COUNT SHLD FCOUNT XRA A ;SET COUNT STA ECOUNT CMA ;FLIP ORA A ;OK TO CONTINUE RET VERERR: CALL PRINT DB CR,LF,'ABORT -- NOT CP/M 2.x or Later',0 JMP RETURN ; ; GET BIOS JUMPS VECTORS FOR EASY REFERENCE ; GTBIOS: LHLD BOOT+1 ;POINTS TO BIOS JUMP TABLE+3 LXI D,WBOOT ;WHERE WE WILL KEEP A COPY MVI B,16*3 ;MOVE 48 BYTES AND FALL THRU TO MOVE ; ; GENERAL PURPOSE MOVE ROUTINE ; FROM 'HL' TO 'DE' FOR COUNT OF 8 ; MOVE: MOV A,M ;GET A BYTE STAX D ;PUT A BYTE INX D ;INCREMENT TO NEXT INX H DCR B ;COUNT DOWN JNZ MOVE RET ; ; READS NEXT SECTOR (GROUP OF FOUR DIRECTORY ENTRIES) ; RETURNS WITH ZERO FLAG SET IF NO MORE ; NXTSEC: LHLD DIRMAX ;SEE IF MORE SECTORS MOV A,H ORA L RZ ;RETURNS ZERO FLAG IF NO MORE LHLD TRACK ;SET TRACK MOV B,H MOV C,L CALL SETTRK LHLD SECTOR ;SET SECTOR MOV B,H MOV C,L CALL TRNSLT CALL SETSEC CALL READ ;READ A SECTOR ANI 1 ;REVERSE SENSE OF ERROR FLAG XRI 1 ;RETURNS WITH ZERO FLAG SET RET ;IF BAD READ ; ; PRINT FILES IN DIRBUF ; PRFILES: LHLD FCOUNT ;GET COUNT MOV A,H ;ANY? ORA L RZ MOV B,H ;COUNT IN BC MOV C,L LHLD DIRBUF ;PT TO FIRST ONE PRFLOOP: PUSH B ;SAVE COUNT PUSH H ;SAVE PTR CALL PRINTFCB ;PRINT FCB POP H ;GET REGS BACK POP B LXI D,ESIZE ;PT TO NEXT DAD D DCX B ;COUNT DOWN MOV A,B ORA C JNZ PRFLOOP MVI A,0FFH ;SET OK ORA A RET ; ; FCB PRINTING ROUTINE ; PRINTFCB: MVI A,' ' ;SPACE IN CALL TYPE CALL TYPE MOV A,M ;GET USER NUMBER CALL DECOUT ;PRINT IT MVI A,':' CALL TYPE INX H PR0: MVI B,8 CALL PR1 MVI A,'.' CALL TYPE MVI B,3 CALL PR1 LDA ECOUNT ;INCREMENT COUNT INR A STA ECOUNT ANI 3 ;EVERY 4 CZ CRLF ;NEW LINE RET PR1: MOV A,M ANI 7FH CALL TYPE INX H DCR B JNZ PR1 RET ; ; PRINT VALUE IN A AS DECIMAL ; DECOUT: PUSH B ;SAVE REG MVI C,'0' ;SET VALUE DEC1: SUI 10 ;DIVIDE BY 10 JC DEC2 INR C JMP DEC1 DEC2: ADI 10 ;ADD 10 BACK IN MOV B,A ;SAVE VALUE MOV A,C ;CHECK DIGITS CPI '0' JNZ DEC3 MVI A,' ' ;LEADING SPACE DEC3: CALL TYPE ;DON'T TYPE LEADING ZERO MOV A,B ;GET ONES ADI '0' ;CONVERT TO ASCII CALL TYPE POP B ;GET REG RET ; ; PRINT CHAR IN A ON TERMINAL; AFFECT NO REGS ; TYPE: PUSH H ;SAVE REGS PUSH D PUSH B PUSH PSW CALL CONST ;CHAR PENDING? ORA A ;0=NONE JZ TYPE1 CALL CONIN ;GET CHAR ANI 7FH ;MASK CPI CTRLC ;ABORT? JZ RETURN CPI CTRLS ;PAUSE? JNZ TYPE1 CALL CONIN ;GET ANOTHER CHAR CPI CTRLC JZ RETURN TYPE1: POP PSW ;GET CHAR TO SEND PUSH PSW MOV C,A ;CHAR IN C CALL CONOUT POP PSW ;RESTORE REGS POP B POP D POP H RET ; ; NEW LINE ; CRLF: MVI A,0DH ;NEW LINE CALL TYPE MVI A,0AH JMP TYPE ; ; PRINT MESSAGE AT RETURN ADDRESS ; PRINT: XTHL ;SAVE HL, GET PTR PRINTL: MOV A,M ;GET BYTE INX H ;PT TO NEXT ANI 7FH ;MASK JZ PRINTD ;DONE IF ZERO CALL TYPE JMP PRINTL PRINTD: XTHL RET ; ; SHIFT REGS 'HL' RIGHT 2 BITS LOGICAL ; SHFHL2: CALL SHFHL ;ROTATE RIGHT 1 BIT AND FALL THRU SHFHL: XRA A ;CLEAR CARRY MOV A,H RAR ;SHIFTED BIT IN CARRY MOV H,A MOV A,L RAR MOV L,A RET ; ; TRANSLATE REG 'BC' FROM LOGICAL TO PHYSICAL SECTOR NUMBER ; TRNSLT: LHLD DPH ;GET PTR TO DPH MOV E,M ;GET ADDRESS OF XLT INX H MOV D,M CALL SECTRAN ;USE BIOS ROUTINE MOV C,L ;RETURN VALUE IN BC MOV B,H RET ; ; DIRALPHA -- ALPHABETIZES DIRECTORY PTED TO BY HL; BC CONTAINS ; THE NUMBER OF FILES IN THE DIRECTORY ; DIRALPHA: LHLD FCOUNT ; GET FILE COUNT MOV A,H ; ANY FILES? ORA L RZ SHLD N ; SET "N" MOV B,H ; BC=COUNT MOV C,L LHLD 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: 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 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: 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 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 DE