;
;			 XDIR.ASM
;		     Revised 6/28/80
;
;	    EXTENDED (SORTED) DIRECTORY PROGRAM
;		by Keith Petersen, W8SDZ
;
;PRINTS A 3-WIDE DIRECTORY, SORTED ALPHABETICALLY,
;SHOWING EXTENT NUMBERS AND EXTENT SIZE.  BASED ON
;'FMAP' BY WARD CHRISTENSEN AND XDIR BY B. RATOFF.
;
;XDIR FILENAME.FILETYPE or just XDIR
;ALLOWS '*' OR '?' TYPE SPECIFICATIONS
;DRIVE NAME MAY ALSO BE SPECIFIED
;
;07/31/79 CORRECTED ERROR IN CONSTAT TEST
;	  FOR ABORTING PRINT.  (KBP)
;
;08/16/79 ADD CONDITIONAL ASSEMBLY FOR CP/M
;	  ON H8 OR TRS-80.  (KBP)
;
;05/21/80 ADD ANI 7FH TO REMOVE FILE ATTRIBUTES
;	  TO MAKE COMPATIBLE WITH CPM-2. (KBP)
;
;06/28/80 FIX ERROR IN FILE SIZE PRINTOUTS. (KBP)
;
FALSE	EQU	0
TRUE	EQU	NOT FALSE
;
BASE	SET	0
;
ALTCPM	EQU	FALSE	;PUT TRUE HERE FOR H8 OR TRS-80 CP/M
;
	IF	ALTCPM
BASE	SET	4200H
	ENDIF
;
FCB	EQU	BASE+5CH ;SYSTEM FCB
EXTENT	EQU	BASE+68H ;FCB EXTENT BYTE
NPL	EQU	3	;NUMBER OF NAMES PER LINE
DELIM	EQU	7CH	;FENCE (DELIMITER) CHARACTER
;
	ORG	BASE+100H
;
	JMP	START	;JMP AROUND I.D.
	DB	'XDIR.COM 6/27/80 '
;
;SAVE THE STACK
START	LXI	H,0
	DAD	SP	;H=STACK
	SHLD	STACK	;SAVE IT
	LXI	SP,STACK ;GET NEW STACK
;NO FCB SPECIFIED?
	LXI	H,FCB+1
	MOV	A,M
	CPI	' '
	JNZ	GOTFCB
;NO FCB - MAKE FCB ALL '?'
	MVI	B,11	;FN+FT COUNT
QLOOP	MVI	M,'?'	;STORE '?' IN FCB
	INX	H
	DCR	B
	JNZ	QLOOP
;LOOK UP THE FCB IN THE DIRECTORY
GOTFCB	MVI	A,'?'	;MATCH ALL EXTENTS
	STA	EXTENT
	MVI	C,FSRCHF ;GET 'SEARCH FIRST' FNC
	LXI	D,FCB
	CALL	BDOS	;READ FIRST
	INR	A	;WERE THERE ANY?
	JNZ	SOME	;GOT SOME
	CALL	ERXIT
	DB	'++NOT FOUND$'
;
;READ MORE DIRECTORY ENTRIES
MOREDIR	MVI	C,FSRCHN ;SEARCH NEXT
	LXI	D,FCB
	CALL	BDOS	;READ DIR ENTRY
	INR	A	;CHECK FOR END (0FFH)
	JZ	SPRINT	;NO MORE - SORT & PRINT
;POINT TO DIRECTORY ENTRY 
SOME	DCR	A	;UNDO PREV 'INR A'
	ANI	3	;MAKE MODULUS 4
	ADD	A	;MULTIPLY...
	ADD	A	;..BY 32 BECAUSE
	ADD	A	;..EACH DIRECTORY
	ADD	A	;..ENTRY IS 32
	ADD	A	;..BYTES LONG
	LXI	H,BASE+81H ;POINT TO BUFFER
			;(SKIP TO FN/FT)
	ADD	L	;POINT TO ENTRY
	MOV	L,A	;SAVE (CAN'T CARRY TO H)
;MOVE ENTRY TO TABLE
	XCHG		;ENTRY TO DE
	LHLD	NEXTT	;NEXT TABLE ENTRY TO HL
	MVI	B,11	;LENGTH OF NAME
;
TMOVE	LDAX	D	;GET ENTRY CHAR
	ANI	7FH	;REMOVE ATTRIBUTES
	MOV	M,A	;STORE IN TABLE
	INX	D
	INX	H
	DCR	B	;MORE?
	JNZ	TMOVE
	MVI	B,20	;LENGTH OF REST OF ENTRY
;
TMOVE2	LDAX	D	;GET ENTRY CHARACTER
	MOV	M,A	;STORE IN TABLE
	INX	D
	INX	H
	DCR	B	;MORE?
	JNZ	TMOVE2
	SHLD	NEXTT	;SAVE UPDATED TABLE ADDR
	LDA	COUNT	;GET PREV COUNT
	INR	A
	STA	COUNT
	JMP	MOREDIR
;
;SORT AND PRINT
SPRINT	LDA	COUNT	;INIT THE ORDER TABLE
	LXI	H,ORDER
	LXI	D,TABLE
	LXI	B,31	;ENTRY LENGTH
;
BLDORD	MOV	M,E	;SAVE LO ORD ADDR
	INX	H
	MOV	M,D	;SAVE HI ORD ADDR
	INX	H
	XCHG		;TABLE ADDR TO HL
	DAD	B	;POINT TO NEXT ENTRY
	XCHG
	DCR	A	;MORE?
	JNZ	BLDORD	;..YES
	LDA	COUNT	;GET COUNT
	STA	SCOUNT	;SAVE AS # TO SORT
	DCR	A	;ONLY 1 ENTRY?
	JZ	DONE	;..YES, SO SKIP SORT
;
SORT	XRA	A	;GET A ZERO
	STA	SWITCH	;SHOW NONE SWITCHED
	LDA	SCOUNT	;GET COUNT
	DCR	A	;USE 1 LESS
	STA	TEMP	;SAVE # TO COMPARE
	STA	SCOUNT	;SAVE HIGHEST ENTRY
	JZ	DONE	;EXIT IF NO MORE
	LXI	H,ORDER ;POINT TO ORDER TABLE
;
SORTLP	CALL	COMPR	;COMPARE 2 ENTRIES
	CM	SWAP	;SWAP IF NOT IN ORDER
	INX	H	;BUMP ORDER
	INX	H	;..TABLE POINTER
	LDA	TEMP	;GET COUNT
	DCR	A
	STA	TEMP
	JNZ	SORTLP	;CONTINUE
;ONE PASS OF SORT DONE
	LDA	SWITCH	;ANY SWAPS DONE?
	ORA	A
	JNZ	SORT
;SORT IS ALL DONE - PRINT ENTRIES
DONE	LXI	H,ORDER
	SHLD	NEXTT
;
;PRINT AN ENTRY
	MVI	C,NPL	;NR. OF NAMES PER LINE
;
ENTRY:	PUSH	B
	MVI	C,CONST	;CK STATUS OF KB
	CALL	BDOS	;ANY KEY PRESSED?
	POP	B
	ORA	A
	JNZ	ABORT	;YES, ABORT
	LHLD	NEXTT	;GET ORDER TABLE POINTER
	MOV	E,M	;GET LO ADDR
	INX	H
	MOV	D,M	;GET HI ADDR
	INX	H
	SHLD	NEXTT	;SAVE UPDATED TABLE POINTER
	XCHG		;TABLE ENTRY TO HL
	MVI	B,8	;FILE NAME LENGTH
	CALL	TYPEIT	;TYPE FILENAME
	CALL	PERIOD	;PERIOD AFTER FN
	MVI	B,3	;GET THE FILETYPE
	CALL	TYPEIT
	MOV	A,M	;GET EXTENT NUMBER
	ADI	'0'	;CONVERT TO ASCII
	CPI	':'	;ABOVE ASCII '9' ?
	JC	ENTRY2
	ADI	7
;
ENTRY2	CPI	'0'	;IS EXTENT NUMBER ZERO?
	JNZ	ENTRY3	;NO, PRINT '+' THEN NUMBER
	CALL	TWOSPCE	;IT'S ZERO - PRINT 2 SPACES
	JMP	ENTRY4	;THEN PRINT SIZE
;
ENTRY3	PUSH	PSW	;SAVE EXTENT NUMBER
	MVI	A,'+'
	CALL	TYPE	;PRINT '+'
	POP	PSW	;GET EXTENT NUMBER
	CALL	TYPE	;PRINT IT
;
ENTRY4	CALL	SPACE	;SPACE OVER ONE
	INX	H
	INX	H
	INX	H
	MOV	A,M	;GET EXTENT SIZE
	PUSH	B
	CALL	PDEC	;PRINT IT
	POP	B
	DCR	C	;ONE LESS ON THIS LINE
	PUSH	PSW
	CNZ	FENCE	;NO CR-LF NEEDED, DO FENCE
	POP	PSW
	CZ	CR	;CR-LF NEEDED
;SEE IF MORE ENTRIES
	LDA	COUNT
	DCR	A
	STA	COUNT
	JNZ	ENTRY	;YES, MORE
	JMP	EXIT
;
;PRINT VALUE IN A IN UNSIGNED DECIMAL FORMAT
;
PDEC:	STA	DTEMP	;SAVE THE NUMBER
	MVI	A,100
	STA	Q
	XRA	A	;CLEAR LEADING ZERO FLAG
	STA	LEAD0FL
; 
;DIVIDE A BY Q
;
PDEC2:	LDA	DTEMP
	MOV	C,A
	LDA	Q
	MOV	D,A
	CALL	BDIV
	MOV	A,B
	STA	DTEMP	;SAVE REMAINDER
	MOV	A,C
	ADI	0
	JNZ	PDEC3
	LDA	LEAD0FL
	ADI	0
	JZ	PDEC4
; 
PDEC3:	MOV	A,C
	ADI	'0'	;CONVERT TO ASCII
	CALL	TYPE	;PRINT QUOTIENT
	MVI	A,1	;STOP SUPRESSING ZEROS
	STA	LEAD0FL
	JMP	PDEC5
; 
PDEC4:	CALL	SPACE
; 
PDEC5:	LDA	Q
	MOV	C,A
	MVI	D,10
	CALL	BDIV	;DIVIDE Q BY 10
	MOV	A,C
	STA	Q
	LDA	Q
	CPI	1
	JNZ	PDEC2
	LDA	DTEMP
	ADI	'0'	;CONVERT TO ASCII
	JMP	TYPE	;PRINT IT THEN RETURN
; 
;BYTE DIVISION ROUTINE
;
BDIV:	MVI	B,0
	MVI	L,8
; 
BDIVLP:	STC
	CMC
	MOV	A,C
	RAL
	MOV	C,A
	MOV	A,B
	RAL
	MOV	B,A
	MOV	A,B
	SUB	D
	JM	BDIV2
	MOV	B,A
	MOV	A,C
	ORI	1
	MOV	C,A
; 
BDIV2:	DCR	L
	JNZ	BDIVLP
	RET
;
PERIOD	MVI	A,'.'
	JMP	TYPE
;
FENCE	CALL	SPACE
	MVI	A,DELIM	;FENCE CHARACTER
	CALL	TYPE
;
TWOSPCE	CALL	SPACE
;
SPACE	MVI	A,' '
;
;TYPE CHAR IN A
TYPE	PUSH	B
	PUSH	D
	PUSH	H
	ANI	7FH	;REMOVE ATTRIBUTES
	MOV	E,A
	MVI	C,WRCHR
	CALL	BDOS
	POP	H
	POP 	D
	POP	B
	RET
;
WRCON	MVI	C,PRINT
	JMP	BDOS
;
TYPEIT	MOV	A,M
	CALL	TYPE
	INX	H
	DCR	B
	JNZ	TYPEIT
	RET
;
CR	MVI	E,13	;PRINT
	MVI	C,2	;C/R
	CALL	BDOS
	MVI	E,10	;LF
	MVI	C,2
	CALL	BDOS
	MVI	C,NPL	;NUMBER OF NAMES PER LINE
	RET
;
;COMPARE ROUTINE FOR SORT
COMPR	PUSH	H	;SAVE TABLE ADDR
	MOV	E,M	;LOAD LO
	INX	H
	MOV	D,M	;LOAD HI
	INX	H
	MOV	C,M
	INX	H
	MOV	B,M
;BC, DE NOW POINT TO ENTRIES TO BE COMPARED
	XCHG
CMPLP	LDAX	B
	CMP	M
	INX	H
	INX	B
	JZ	CMPLP
	POP	H
	RET		;COND CODE TELLS ALL
;
;SWAP ENTRIES IN THE ORDER TABLE
SWAP	MVI	A,1
	STA	SWITCH	;SHOW A SWAP WAS MADE
	MOV	C,M
	INX	H
	PUSH	H	;SAVE TABLE ADDR+1
	MOV	B,M
	INX	H
	MOV	E,M
	MOV	M,C
	INX	H
	MOV	D,M
	MOV	M,B
	POP	H
	MOV	M,D
	DCX	H	;BACK POINTER TO CORRECT LOC'N
	MOV	M,E
	RET
;
;ERROR EXIT
ERXIT	POP	D	;GET MSG
	MVI	C,PRINT
	JMP	CALLB	;PRINT MSG, EXIT
;
;ABORT - READ CHAR ENTERED
ABORT	MVI	C,RDCHR
CALLB	CALL	BDOS	;DELETE THE CHAR
;
;FALL INTO EXIT
;EXIT - ALL DONE , RESTORE STACK
EXIT	LHLD	STACK	;GET OLD STACK
	SPHL		;MOVE TO STACK
	RET		;..AND RETURN
;
NEXTT	DW	TABLE	;NEXT TABLE ENTRY
COUNT	DB	0	;ENTRY COUNT
SCOUNT	DB	0	;# TO SORT
SWITCH	DB	0	;SWAP SWITCH FOR SORT
BUFAD	DW	BASE+80H ;OUTPUT ADDR
ORDER	DS	256	;ORDER TABLE (ROOM FOR 128 NAMES)
	DS	60	;STACK AREA
STACK	DS	2	;SAVE OLD STACK HERE
Q	DS	1	;FOR DIVIDER ROUTINE
DTEMP	DS	1	;FOR DIVIDER ROUTINE
LEAD0FL	DS	1	;LEADING ZERO FLAG
TEMP	DS	1	;SAVE DIR ENTRY
TABLE	EQU	$	;READ ENTRIES IN HERE
;
; BDOS EQUATES
;
RDCHR	EQU	1	;READ CHAR FROM CONSOLE
WRCHR	EQU	2	;WRITE CHR TO CONSOLE
PRINT	EQU	9	;PRINT CONSOLE BUFF
CONST	EQU	11	;CHECK CONS STAT
FOPEN	EQU	15	;0FFH=NOT FOUND
FCLOSE	EQU	16	;   "	"
FSRCHF	EQU	17	;   "	"
FSRCHN	EQU	18	;   "	"
ERASE	EQU	19	;NO RET CODE
FREAD	EQU	20	;0=OK, 1=EOF
FWRTE	EQU	21	;0=OK, 1=ERR, 2=?, 255=NO DIR SPC
FMAKE	EQU	22	;255=BAD
FREN	EQU	23	;255=BAD
FDMA	EQU	26
BDOS	EQU	BASE+5
REBOOT	EQU	BASE+0
;
	END
