; Ward Christenson's FMAP from CP/M UG Vol 8
; Modified by Trevor Marshall
;		Uni W A
;		Dept Elec Eng
; to reflect his modifications on the 
;    CACHE T7 disk
; and to allow compatibility with CDOS
;    (up to 512 entries)
;
	ORG	100H
;	JMP	START	;SKIP PROGRAM ID
	CALL	START
ID:	DB	'CDOS 2.35 FMAP Sept 80',0DH,0AH,'$' 
;SORTED DIRECTORY MAP PROGRAM
;WITH OPTION OF WRITING FILE OF NAMES
;
;FMAP FN.FT OR JUST MAP
;FMAP FN.FT F   TO WRITE A FILE
;ALLOWS '*' OR '?' TYPE SPECIFICATIONS
FCB	EQU	5CH	;SYSTEM FCB
;SAVE THE STACK
START	POP	D
	MVI	C,9
	CALL	BDOS
	LXI	H,0
	DAD	SP	;H=STACK
	SHLD	STACK	;SAVE IT
	LXI	SP,STACK ;GET NEW STACK
;SAVE FILE WRITE REQUEST CHAR
	LDA	FCB+17
	STA	FILESW
;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
;Mod #2
GOTFCB	MVI	A,'?'
	STA	68H
;LOOK UP THE FCB IN THE DIRECTORY
	MVI	C,FSRCHF ;GET 'SEARCH FIRST' FNC
	LXI	D,FCB
	CALL	BDOS	;READ FIRST
	INR	A	;WERE THERE ANY?
	STA	TEMP	;SAVE
	JNZ	PRTTL1	;GOT SOME - PRT TITLE, CONT
	LXI	D,NONMSG
	CALL	WRCON
	JMP	EXIT
NONMSG	DB	'*** No Entries Found ***$'
; Mod #3
PRTTL1	LDA	FILESW
	CPI	'Q'
	JZ	JMP1
	CPI	'D'
	JZ	JMP1
	CPI	'M'
	JZ	JMP1
;PRINT TITLE
PRTTL	LXI	D,TTL
	CALL	WRCON
	CALL	CR
JMP1:	LDA	TEMP	;RELOAD EXTENT
	JMP	SOME
TTL	DB	'FILENAME TYP EX RC -----EXTENT-----$'
;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,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,31	;ENTRY LENGTH
TMOVE	LDAX	D	;GET ENTRY CHAR
	MOV	M,A	;STORE IN TABLE
	INX	D
	INX	H
	DCR	B	;MORE?
	JNZ	TMOVE
	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
; Mod #4
	LDA	FILESW
	CPI	' '
	JZ	ENTRY
;IF WRITING A FILE, OPEN THE FILE
;	LDA	FILESW
;	CPI	'F'
;	JNZ	ENTRY
	LXI	D,MYFCB
	MVI	C,ERASE
	CALL	BDOS
	LXI	D,MYFCB
	MVI	C,FMAKE	;MAKE THE FILE
	CALL	BDOS
	INR	A
	JNZ	ENTRY
;MAKE ERROR
	CALL	ERXIT
	DB	'++FILE MAKE ERROR$'
;PRINT AN ENTRY
ENTRY	MVI	C,CONST	;CK STATUS OF KB
	CALL	BDOS	;ANY KEY PRESSED?
	DCR	A
	JZ	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
; Mod #5
	LXI	H,0BH
	DAD	D
	MOV	A,M
	STA	NEWONE
	XCHG		;TABLE ENTRY TO HL
	LDA	FILESW
	CPI	'D'
	CZ	ADDSUB
	LDA	FILESW
	CPI	'M'
	PUSH	PSW
	CZ	ADDSUB
	POP	PSW
	CZ	ADDSUB2
	MVI	B,8	;FILE NAME LENGTH
	CALL	TYPEIT	;TYPE FILENAME
	CALL	PERIOD	;SPACE AFTER FN
	MVI	B,3	;GET THE FILETYPE
	CALL	TYPEIT
;Mod #6
	LDA	FILESW
	CPI	'D'
	CZ	ADDSUB2
	CALL	FILECR
	MOV	A,M
	CALL	XOB
	INX	H	;SKIP EXTENT
	INX	H	;SKIP
	INX	H	;UNUSED
	MOV	A,M	;GET REC COUNT
	DCR	A	;FUDGE
	RAR		;DIVIDE
	RAR		;..BY 8
	RAR
	ANI	1FH	;DELETE GARBAGE
	INR	A	;MAKE RELATIVE TO 1, NOT 0
	MOV	B,A	;SAVE AS # EXTENTS
	MOV	A,M	;RELOAD RECORD COUNT
	CALL	XOB	;PRINT RECORD COUNT
	INX	H	;SKIP RECORD COUNT
	MVI	C,0	;FOR EXTENT SKIP CTL
EXTLP	MOV	A,M	;GET EXTENT BYTE
	ORA	A	;EMPTY?
	JZ	ENDEXT	;..YES
	CALL	XO	;..NO, PRINT IT
	INR	C	;INCR COUNT
	MOV	A,C	;TIME TO SPACE?
	ANI	3
	CZ	SPACE
	INX	H	;POINT TO NEXT CHR
	DCR	B	;MORE IN EXTENT?
	JNZ	EXTLP	;YES
;BUMP TOTAL FILE COUNT
ENDEXT	LDA	NFILE	;GET # FILES
	INR	A	;BUMP
	DAA		;MAKE DECIMAL
	STA	NFILE	;SAVE IT BACK
	CALL	CR	;END, TYPE C/R
;SEE IF MORE ENTRIES
	LDA	COUNT
	DCR	A
	STA	COUNT
	JNZ	ENTRY	;YES, MORE
; Mod #7
	LDA	FILESW
	CPI	' '
	JZ	NXT1
	MVI	A,'F'
	STA	FILESW
;ALL DONE - PRINT # FILES
NXT1:	LDA	NFILE
	CALL	XOB
	LXI	D,NMSG
	CALL	WRCON
;CLOSE FILE IF NECESSARY
	LDA	FILESW
; Mod #8
;	CPI	'F'
;	JNZ	EXIT
	CPI	' '
	JZ	EXIT
	XRA	A
	STA	NEWONE
;
	MVI	A,'Z'-40H ;EOF CHAR
	CALL	FILCHR	;WRITE IT
	CALL	WRSEC	;WRITE FINAL SECTOR
	LXI	D,MYFCB
	MVI	C,FCLOSE
	CALL	BDOS
	JMP	EXIT
NMSG	DB	'FILES$'
;HEX OUTPUT W/BLANK
XOB	CALL	XO
	JMP	SPACE
;HEX OUTPUT 
XO	PUSH	PSW	;SAVE CHAR
	RAR
	RAR
	RAR
	RAR
	CALL	NIBBL	;PRINT LEFT NIBBLE
	POP	PSW	;GET VALUE BACK
NIBBL	ANI	0FH	;ISOLATE NIBBLE
	CPI	10	;NUMBER?
	JC	XNUM	;YES
	ADI	7	;FUDGE ALPHA HEX
XNUM	ADI	'0'	;MAKE PRINTABLE
;TYPE CHAR IN A
TYPE	PUSH	B
	PUSH	D
	PUSH	H
	MOV	E,A
;Mod #9
	MVI	C,WRCHR
	LDA	FILESW
	CPI	'F'
	JZ	NXT2
	CPI	' '
NXT2	CZ	BDOS
	POP	H
	POP 	D
	POP	B
	RET
WRCON	MVI	C,PRINT
	JMP	BDOS
; Mod #10
ADDSUB	MVI	A,'$'
	CALL	L03B7
	MVI	A,'1'
	CALL	L03B7
	MVI	A,' '
	CALL	L03BA
	MVI	A,'$'
	CALL	L03B7
	MVI	A,'2'
	JMP	L03B7
ADDSUB2 MVI	A,' '
	CALL	L03BA
	MVI	A,'$'
	CALL	L03B7
	MVI	A,'3'
	JMP	L03B7
;
TYPEIT	MOV	A,M
	CALL	FILCHR	;TO DISK IF REQ'D
	CALL	TYPE
	INX	H
	DCR	B
	JNZ	TYPEIT
	RET
SPACE	MVI	A,' '
	JMP	TYPE
CR	MVI	A,0DH
	CALL	TYPE
	MVI	A,0AH
	CALL	TYPE
	RET
;CR	MVI	E,13	;PRINT
;	MVI	C,2	;C/R
;	CALL	BDOS
;	MVI	E,10	;LF
;	MVI	C,2
;	JMP	BDOS
TEMP	DS	1	;SAVE DIR ENTRY
;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
;ROUTINES FOR CREATING FILE
;
; Mod #11
;WRITE CHAR IN A TO FILE
;(SAVES ALL REGS INCLUDING A)
L03B7:		;
FILCHR	CPI	' '
	RZ		;DON'T WRITE BLANKS
L03BA:	PUSH	PSW
	LDA	FILESW	;WRITING A FILE?
;	CPI	'F'
;	JNZ	NOFILE
	CPI	' '
	JZ	NOFILE
	LDA	NEWONE
	ORA	A
	JNZ	NOFILE
;
	POP	PSW	;GET CHAR
	PUSH	PSW	;SAVE IT BACK
	PUSH	H
	LHLD	BUFAD	;CURRENT BUFFER ADDR
	MOV	M,A
	INX	H
	SHLD	BUFAD
	MOV	A,H	;SEE IF FULL BUFF
	DCR	A
	CZ	WRSEC	;YES, WRITE SECTOR
	POP	H
NOFILE	POP	PSW	;RESTORE CHAR
	RET
;WRITE A SECTOR
WRSEC	PUSH	B
	PUSH	D
	LXI	D,MYFCB
	MVI	C,FWRTE
	CALL	BDOS
	ORA	A
	JZ	WROK
	CALL	ERXIT
	DB	'++WRITE ERROR$'
WROK	LXI	H,80H	;START OF BUFF
	SHLD	BUFAD
	POP	D
	POP	B
	RET
;TYPE A PERIOD INTO THE FILE
PERIOD	MVI	A,'.'	;GET PERIOD
	CALL	FILCHR	;WRITE TO FILE
	JMP	SPACE
;WRITE CR/LF INTO FILE
FILECR	MVI	A,13
	CALL	FILCHR
	MVI	A,10
	CALL	FILCHR
	JMP	SPACE
;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
	DS	30	;STACK AREA
STACK	DS	2	;SAVE OLD STACK HERE
NFILE	DB	0	;NUMBER OF FILES PRINTED
;
; 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	5
REBOOT	EQU	0
NEXTT	DW	TABLE	;NEXT TABLE ENTRY
NEWONE	DS	1	;******I think
COUNT	DB	0	;ENTRY COUNT
SCOUNT	DB	0	;# TO SORT
SWITCH	DB	0	;SWAP SWITCH FOR SORT
FILESW	DS	1	;'F' IF WRITING FILE
BUFAD	DW	80H	;OUTPUT ADDR
MYFCB	DB	0,'NAMES   SUB',0
	DS	19
	DB	0
;Modified table size for 512 Directory Entries
ORDER	DS	1024	;ORDER TABLE
TABLE	EQU	$	;READ ENTRIES IN HERE
	END	100H
