; PROGRAM:  FINDF
; AUTHOR:  RICHARD CONN
; VERSION:  2.0
; DATE:  18 May 84
; PREVIOUS VERSIONS: 1.1 (25 July 83), 1.0 (24 JULY 83)
;
VERS    EQU     20              ;version number
z3env   SET     0f400h

;
;       FINDF searches through all of the known disks for one or more
; files matching the passed file specification.  AFNs (Ambiguous File Names)
; are permitted.  FINDF is invoked by the following command line:
;               FINDF afn,afn,afn,... o
; where "afn" refers to the file sought and "o" is none or more of:
;               S - Include System Files
;

;
; 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'-'@'         ;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

;
; SYSLIB and Z3LIB Routines
;
       ext     z3init,codend
       ext     cout,crlf,print,padc

;
; Environment Definition
;
       if      z3env ne 0
;
; External ZCPR3 Environment Descriptor
;
       jmp     start
       db      'Z3ENV' ;This is a ZCPR3 Utility
       db      1       ;External Environment Descriptor
z3eadr:
       dw      z3env
start:
       lhld    z3eadr  ;pt to ZCPR3 environment
;
       else
;
; Internal ZCPR3 Environment Descriptor
;
       MACLIB  Z3BASE.LIB
       MACLIB  SYSENV.LIB
z3eadr:
       jmp     start
       SYSENV
start:
       lxi     h,z3eadr        ;pt to ZCPR3 environment
       endif

;
; Start of Program -- Initialize ZCPR3 Environment
;
       call    z3init  ;initialize the ZCPR3 Env and the VLIB Env
       LXI     H,0             ;SAVE STACK PTR
       DAD     SP
       SHLD    STACK
       CALL    CODEND          ;DETERMINE FREE SPACE
       SHLD    FNTAB           ;FILE NAME TABLE
       LXI     D,512           ;1/2 K SPACE
       DAD     D
       SHLD    SCRATCH         ;BEGINNING OF SCRATCH AREA
       SPHL                    ;AND TOP OF STACK
       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      'FINDF, 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,'Syntax:'
       DB      CR,LF,'   FINDF afn,afn,afn,... o'
       db      cr,lf,'Options:'
       DB      CR,LF,'   S - Include System Files'
       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
       LHLD    FNTAB   ;PT TO TABLE
       XCHG            ;... IN DE
       LXI     H,TBUFF+1       ;SCAN THRU TBUFF, BUILDING A FILE NAME TABLE
       CALL    SBLANK  ;SKIP BLANKS
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    COUT
       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    NXTDISK         ;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
       PUSH    H
       LHLD    FNTAB           ;PT TO TABLE
       XCHG                    ;... IN DE
       POP     H
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
       MVI     A,31            ;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
;
;  ADVANCE TO NEXT DISK
;
NXTDISK:
       LXI     B,TBUFF         ;SET DMA ADDRESS
       CALL    SETDMA
       LDA     FCB
       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    COUT
       CALL    PRINT
       DB      ' --',CR,LF,0
       LHLD    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
;
; 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:
       CALL    PRINT           ;2 SPACES
       DB      '  ',0
       MOV     A,M             ;GET USER NUMBER
       CALL    PADC            ;PRINT IT
       MVI     A,':'
       CALL    COUT
       INX     H
PR0:
       MVI     B,8
       CALL    PR1
       MVI     A,'.'
       CALL    COUT
       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    COUT
       INX     H
       DCR     B
       JNZ     PR1
       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<HL
;       RET W/ZERO SET MEANS DE=HL
;
CMP$ENTRY:
;
;  COMPARE BY FILE NAME, FILE TYPE, EXTENSION, AND USER NUM (IN THAT ORDER)
;
       PUSH    H
       PUSH    D
       INX     H       ; PT TO FN
       INX     D
       MVI     B,11    ; COMPARE FN, FT
       CALL    COMP
       POP     D
       POP     H
       RNZ
       LDAX    D       ; COMPARE USER NUMBER
       CMP     M
       RET
;
;  COMP COMPARES DE W/HL FOR B BYTES; RET W/CARRY IF DE<HL
;       MSB IS DISREGARDED
;
COMP:
       MOV     A,M     ; GET (HL)
       ANI     7FH     ; MASK MSB
       MOV     C,A     ; ... IN C
       LDAX    D       ; COMPARE
       ANI     7FH     ; MASK MSB
       CMP     C
       RNZ
       INX     H       ; PT TO NEXT
       INX     D
       DCR     B       ; COUNT DOWN
       JNZ     COMP
       RET
;
;  AS COMP, BUT MATCH ON '?' PTED TO BY HL
;
COMP2:
       MOV     A,M     ; GET (HL)
       ANI     7FH     ; MASK MSB
       CPI     '?'     ; MATCH '?'
       JZ      COMP2A
       MOV     C,A     ; ... IN C
       LDAX    D       ; COMPARE
       ANI     7FH     ; MASK MSB
       CMP     C
       RNZ
COMP2A:
       INX     H       ; PT TO NEXT
       INX     D
       DCR     B       ; COUNT DOWN
       JNZ     COMP2
       RET

;
;  SORT BUFFERS
;
ORDER:
       DS      2       ; PTR TO ORDER TABLE
DIRBUF:
       DS      2       ; POINTER TO DIRECTORY
DSTART:
       DS      2       ; POINTER TO FIRST DIRECTORY ENTRY
FCOUNT:
       DS      2       ; TOTAL NUMBER OF FILES/NUMBER OF SELECTED FILES
HOLD:
       DS      ESIZE   ; 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
;
; THIS IS THE WORKING COPY OF THE BIOS JUMP TABLE
;
WBOOT:   DS     3
CONST:   DS     3
CONIN:   DS     3
CONOUT:  DS     3
LIST:    DS     3
PUNCH:   DS     3
READER:  DS     3
HOME:    DS     3
SELDSK:  DS     3
SETTRK:  DS     3
SETSEC:  DS     3
SETDMA:  DS     3
READ:    DS     3
WRITE:   DS     3
LISTST:  DS     3
SECTRAN: DS     3
;
STACK:
       DS      2               ;LOCATION OF STACK
;
; DATA AREAS
;
FNCOUNT:
       DS      1               ;NUMBER OF FILE NAMES FOUND
CLPFLG: DS      1               ;0 FOR NO MATCH LOCALLY
SYSTEM: DS      1               ;0 IF NO SYSTEM FILES
ECOUNT: DS      1               ;COUNT OF ENTRIES PRINTED - 1
FFLAG:  DS      1               ;FILE FOUND FLAG (0=NO)
TEMP:   DS      2               ;TEMP STORAGE FOR FCB PRINT
;
;  DISK PARAMETER DATA
;
DPH:    DS      2               ;ADDRESS OF DPH
DIRMAX: DS      2               ;NUMBER OF SECTORS IN DIRECTORY =
;                               ;   MAXIMUM NUMBER OF DIRECTORY ENTRIES
;                               ;   DIVIDED BY 4 (ENTRIES PER SECTOR)
EXTENT: DS      1               ;EXTENT MASK
MAXSEC: DS      2               ;MAXIMUM NUMBER OF SECTORS/TRACK
SECTOR: DS      2               ;CURRENT SECTOR NUMBER
TRACK:  DS      2               ;TRACK NUMBER OF DIRECTORY

FNTAB:  DS      2               ;FILE NAME TABLE

SCRATCH:
       DS      2               ;SCRATCH AREA

       END