;
; SYSLIB Module Name:  SDIR
; Author:  Richard Conn
; SYSLIB Version Number:  2.0
; Module Version Number:  1.0
; 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

	ORI	20H	; SET ALL USERS DUE TO THE WAY DIRLOAD WORKS
	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
CMP$FLAG:
	DS	1	; 0=SORT BY FILE NAME/TYPE, ELSE BY TYPE/NAME
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
	XRA	A	; JUST SELECT FILES FROM CURRENT DISK
	STA	TFCB	; SET DR FIELD TO ZERO TO DO THIS
	SHLD	DIRBUF	; SAVE PTR TO DIRECTORY BUFFER
;*
;*  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
;*

;*  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
	MVI	A,'?'	; SELECT ALL ENTRIES FROM CURRENT DISK
	STA	TFCB	; SET DR FIELD TO '?' TO DO THIS
	SHLD	DIRBUF	; SAVE PTR TO DIRECTORY BUFFER
	SHLD	DSTART	; SET PTR TO FIRST DIRECTORY ELEMENT
;*
;*  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
DIRSLP:
	CALL	PSENTRY	; PLACE ENTRY IN DIR
	JZ	DIROVFL	; MEMORY OVERFLOW ERROR
	MVI	C,18	; SEARCH FOR NEXT MATCH
	CALL	BDOS
	CPI	255	; DONE?
	JNZ	DIRSLP
	JMP	DIRDN	; DONE

;*
;*  PSENTRY --
;*  PLACE ENTRY IN DIRECTORY BUFFER IF NOT AN ERASED ENTRY
;*	IF ANOTHER ENTRY BY THE SAME NAME IS ALREADY THERE, DETERMINE WHICH
;*	HAS THE LARGER EXTENT AND PLACE THAT ONE
;*
;*  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
;*
PSENTRY:
	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	; SCAN FOR DUPLICATE ENTRIES (BY NAME ONLY)
	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
	JMP	PEOVFL

;*
;*  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	DUPFND
;  NEW TARGET IS LARGER THAN STORED ENTRY
	XCHG		; MAKE HL PT TO NEW TARGET, DE PT TO DEST
	MVI	B,ESIZE	; NUMBER OF BYTES TO MOVE
	CALL	MOVE	; MOVE IT
;  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 DE<HL
;*	RET W/ZERO SET MEANS DE=HL
;*
CMP$ENTRY:
	LDA	CMP$FLAG	; GROUP BY FILE TYPE?
	ORA	A
	JZ	CMP$FN$FT
;*
;*  COMPARE BY FILE TYPE, FILE NAME, EXTENSION, AND USER (IN THAT ORDER)
;*
CMP$FCB1:	
	PUSH	H
	PUSH	D
	LXI	B,9	; PT TO FT (8 BYTES + 1 BYTE FOR USER NUMBER)
	DAD	B
	XCHG
	DAD	B
	XCHG		; DE, HL NOW PT TO THEIR FT'S
	MVI	B,3	; 3 BYTES
	CALL	COMP	; COMPARE FT'S
	POP	D
	POP	H
	RNZ		; CONTINUE IF COMPLETE MATCH
	PUSH	H
	PUSH	D
	INX	H	; PT TO FILE NAME
	INX	D
	MVI	B,8	; 8 BYTES
	CALL	COMP	; COMPARE FN'S
	POP	D
	POP	H
	RNZ		; CONTINUE IF COMPLETE MATCH
	PUSH	H
	PUSH	D
	LXI	B,12	; PT TO EXT (11 BYTES FOR FN/FT AND 1 BYTE FOR USER)
	DAD	B
	XCHG
	DAD	B
	XCHG		; DE, HL NOW PT TO THEIR EXT'S
	LDAX	D	; COMPARE
	CMP	M
	POP	D
	POP	H
	RNZ
	LDAX	D	; COMPARE USER NUMBERS
	CMP	M
	RET
;*
;*  COMPARE BY FILE NAME, FILE TYPE, EXTENSION, AND USER NUM (IN THAT ORDER)
;*
CMP$FN$FT:
	PUSH	H
	PUSH	D
	INX	H	; PT TO FN
	INX	D
	MVI	B,12	; COMPARE FN, FT, EX
	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

;*
;*  DIRSEL -- SELECT DIRECTORY ENTRIES WHICH MATCH USER NUMBER, FILE
;*    NAME, AND FILE TYPE PTED TO BY DE
;*
;*  ON INPUT, HL PTS TO DIRECTORY, DE PTS TO FCB, BC=NUMBER OF FILES, A=FLAG:
;*			Bit 7 - Select Non-Sys, Bit 6 - Select Sys
;*			Bit 5 - Select All Users, Bits 4-0 - User Number
;*  ON OUTPUT, THE MS BIT OF THE USER OF THE SELECTED FILES IS SET
;*
DIRSEL::
	PUSH	H	; SAVE REGS
	PUSH	D
	PUSH	B
	PUSH	PSW
	STA	SELFLG	; SAVE SELECT FLAG
	SHLD	DIRBUF	; SAVE PTR
	PUSH	B	; SAVE COUNT

;*  CHECK FOR MATCH
DSMAT:
	POP	B	; GET COUNT
	MOV	A,B	; CHECK FOR NO ENTRIES
	ORA	C
	JZ	DSDONE	; DONE IF NONE
	DCX	B	; COUNT DOWN
	PUSH	B	; SAVE COUNT
	PUSH	H	; SAVE PTRS
	PUSH	D
	INX	H	; PT TO FN
	INX	D
	MVI	B,8	; CHECK 8 CHARS
	XCHG		; LET HL PT TO FCB
	CALL	COMP2	; COMPARE WITH '?' MATCH
	POP	D	; GET PTRS
	POP	H
	JNZ	DSMATNX	; ADVANCE TO NEXT ENTRY IF NO MATCH
	PUSH	H	; SAVE PTRS
	PUSH	D
	LXI	B,9	; CHECK R/O FLAG, SYS FLAG, LAST BYTE
	DAD	B
	XCHG
	DAD	B	; DON'T EXCHANGE -- HL PTS TO FCB, DE PTS TO DIR
	MVI	B,3	; CHECK 3 BYTES
	CALL	COMP2	; COMPARE WITH '?' MATCH
	POP	D	; RESTORE PTRS
	POP	H
	JNZ	DSMATNX	; NO MATCH?
	CALL	FLGCHK	; CHECK FLAGS
	JNZ	DSMATNX	; NO MATCH?

;*
;*  WE HAVE A MATCH -- MARK ENTRY
;*
DSMARK:
	MOV	A,M	; GET BYTE
	ORI	80H	; SET MSB
	MOV	M,A	; PUT BYTE

;*
;*  ADVANCE TO NEXT ENTRY
;*
DSMATNX:
	LXI	B,ESIZE	; NUMBER OF BYTES/ENTRY
	DAD	B	; PT TO NEXT ENTRY
	JMP	DSMAT	; CONTINUE

;*
;*  DONE WITH SCAN
;*
DSDONE:
	POP	PSW		; RESTORE REGS
	POP	B
	POP	D
	POP	H
	RET

;*
;*  WE HAVE A NAME MATCH -- NOW CHECK FLAGS
;*	RETURN Z IF MATCH, NZ IF NO MATCH
;*
FLGCHK:
	PUSH	D	; SAVE FCB PTR
	PUSH	H	; SAVE ENTRY PTR
	LXI	D,10	; CHECK SYSTEM BIT
	DAD	D	; HL PTS TO SYSTEM BIT
	MOV	A,M	; GET BYTE
	POP	H	; RESTORE PTRS
	POP	D
	ANI	80H	; MASK FOR SYSTEM BIT
	LDA	SELFLG	; GET FLAG BYTE
	JZ	DSNSBIT
; IT IS A SYSTEM FILE, SO LOOK AT BIT 6 OF FLAG
	ANI	40H	; LOOK AT BIT 6
	JNZ	DSUSER	; OK, SO LOOK AT USER
	JMP	DSNOMAT	; CONTINUE PROCESSING
; IT IS A NON-SYSTEM FILE, SO LOOK AT BIT 7 OF FLAG
DSNSBIT:
	ANI	80H	; LOOK AT BIT 7
	JZ	DSNOMAT	; NOT SET, SO SKIP ENTRY AS FAILING TEST
; NOW CHECK FOR PROPER USER AREA
DSUSER:
	LDA	SELFLG	; GET FLAG
	ANI	20H	; CHECK FOR ALL USERS
	JNZ	DSYESMAT	; MATCH IF SET
	LDA	SELFLG	; GET FLAG
	ANI	1FH	; GET USER NUMBER (LOW 5 BITS)
	MOV	B,A	; SAVE IN B
	MOV	A,M	; COMPARE USER NUMBER TO DIR ENTRY
	ANI	1FH	; LOOK AT USER NUMBER
	CMP	B	; COMPARE TO PASSED USER NUMBER
	JNZ	DSNOMAT	; SKIP IF NOT SAME USER NUMBER
;  MATCH, SO RETURN Z
DSYESMAT:
	XRA	A	; SET ZERO
	RET
;  NOT A MATCH, SO RETURN NZ
DSNOMAT:
	MVI	A,0FFH	; SET NO ZERO
	ORA	A
	RET

;**************************************************************************

;*
;*  NEGATIVE DIRECTORY PACK UTILITY -- RESTRUCTURE THE DIRECTORY TO INCLUDE
;*    ONLY THOSE ENTRIES NOT 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)
;*
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