;			MDIR.ASM
;		     revised 8/17/80
;
;	   CP/M-2 MASTER DISK DIRECTORY PROGRAM
;		    by Jeff Hammersley, MD.
;
; PRINTS A COMPLETE DISK DIRECTORY INCLUDING ALL USERS,
; SORTED ALPHABETICALLY AND PRINTED 4 WIDE.
;
; Based on 'FMAP' as originally written by Ward Christensen
; and its subsequent evolution to 'SDIR' by Keith Petersen.
;
;---------------------------------------------------------------;
;								;
; Commands:							;
;								;
; MDIR	  returns all files on the default disk under any user.	;
;								;
; Note: optional drive name may be specified.			;
;								;
;								;
; MDIR FILENAME.FILETYPE    searches for all files that match	;
;		in all user areas.				;
;								;
; MDIR S???.*  returns all 4 character files with any extention	;
;		in any user areas.				;
;								;
;_______________________________________________________________;
; 
;07/02/80 FIXED TO ALLOW MORE THAN 256 NAMES IN DIRECTORY,
;	  PRINT DRIVE NAME, AND ALLOW SPECIFYING DRIVE.
;	  BY KEITH PETERSEN, W8SDZ.
;
;07/30/80 FIXED TO ELIMINATE CRASH WHEN NO NAMES ARE IN
;	  DIRECTORY. REPLACED DECIMAL OUTPUT ROUTINE WITH
;	  SIMPLER ONE WHICH IS SMALLER AND ALLOWS USE OF
;	  ONE LESS LEADING SPACE SO DISPLAY CAN BE PRINTED
;	  ON A 72-CHARACTER PRINTER. (KBP)
;
;08/17/80 CORRECTED MISSING INR A IN SEARCH FIRST ROUTINE,
;	  WHICH CAUSED DUPLICATE LISTING OF THIRD DIRECTORY
;	  ENTRY. (KBP)
;
BASE	SET	0
;
ALTCPM	EQU	0	;PUT 1 HERE FOR H8 OR TRS-80 CP/M
;
	IF	ALTCPM
BASE	SET	4200H
	ENDIF
;
NAMES	EQU	256	;MAX NUMBER OF NAMES IN DIRECTORY
;
FCB	EQU	BASE+5CH ;SYSTEM FCB
NPL	EQU	4	;NUMBER OF NAMES PER LINE
CR	EQU	0DH	;CARRIAGE RETURN
LF	EQU	0AH	;LINE FEED
DELIM	EQU	':'	;FENCE (DELIMITER) CHARACTER
;
	ORG	BASE+100H
;
	JMP	START	;JUMP AROUND I.D.
	DB	'MDIR.COM 8/17/80 '
;
;SAVE THE STACK
START	LXI	H,0
	DAD	SP	;H=STACK
	SHLD	STACK	;SAVE IT
	LXI	SP,STACK ;GET NEW STACK
	LDA	FCB
	ORA	A	;ANY DRIVE SPECIFIED?
	JZ	FSPEC2
	DCR	A	;CORRECT DISK NUMBER FOR CP/M
	PUSH	PSW	;SAVE REQUESTED DISK NUMBER
	MVI	C,CURDSK ;FIND OUT WHERE WE'RE LOGGED
	CALL	BDOS
	STA	CDSK	;SAVE IT FOR LATER
	POP	PSW	;GET REQUESTED DISK NR.
	MOV	E,A	;TO E FOR CP/M
	MVI	C,SELDSK ;SELECT DISK REQUESTED
	CALL	BDOS
;
FSPEC2	MVI	C,CURDSK ;FIND OUT WHERE WE'RE LOGGED
	CALL	BDOS
	ADI	'A'	;MAKE PRINTABLE
	STA	DRNAME
;PRINT HEADER
	CALL	FSPEC3
	DB	CR,LF
	DB	'.. MASTER DIRECTORY - DRIVE '
DRNAME	DB	'X:'
	DB	CR,LF,CR,LF,'$'
;
FSPEC3	POP	D
	MVI	C,PRINT
	CALL	BDOS
	LXI	H,FCB+1
	MOV	A,M	;GET 1ST CHAR. OF NAME REQUEST
	STA	SAVFCB	;SAVE FOR LATER
;
;MAKE DR FIELD OF FCB '?' TO FORCE RETURN OF ALL USER ENTRIES
	DCX	H	;POINT TO DR FIELD
	MVI	M,'?'	;STORE '?' IN FCB
;
;RETURN ALL DIRECTORY ENTRIES
	MVI	C,FSRCHF ;GET 'SEARCH FIRST' FNC
	LXI	D,FCB
	CALL	BDOS	;READ FIRST
	INR	A	;COMPENSATE FOR LATER DCR
	JMP	SOME	;GOT SOME (BECAUSE OF ? IN DR FIELD)
;
;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+80H ;POINT TO BUFFER
	ADD	L	;POINT TO ENTRY
	MOV	L,A	;SAVE (CAN'T CARRY TO H)
;MOVE ENTRY TO TABLE
	MOV	A,M	;GET DR FIELD
	CPI	0E5H	;IS IT AN ERASED FILE ?
	JZ	MOREDIR	;YES, IGNORE AND GET ANOTHER
	MOV	D,H	;ENTRY TO DE
	MOV	E,L
	MVI	A,12	;GET OFFSET TO EXTENT BYTE
	ADD	L	;ADD TO GET CORRECT POINTER
	MOV	L,A
	MOV	A,M	;GET EXTENT BYTE
	ORA	A	;IS EXTENT OTHER THAN ZERO ?
	JNZ	MOREDIR	;IGNORE ALL EXTENTS BEYOND THE FIRST
	LHLD	NEXTT	;NEXT TABLE ENTRY TO HL
	MVI	B,12	;ENTRY LENGTH
;
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
	SHLD	NEXTT	;SAVE UPDATED TABLE ADDR
	LHLD	COUNT	;GET PREV COUNT
	INX	H	;ADD ONE
	SHLD	COUNT	;RESAVE COUNT
	JMP	MOREDIR
;
;SORT AND PRINT
SPRINT	LHLD	COUNT	;GET COUNT
	MOV	A,H
	ORA	L	;CHECK FOR ZERO COUNT
	JZ	NFEXIT	;NOTHING IN DIRECTORY, EXIT
	SHLD	OCOUNT	;SAVE FOR ORDER TABLE COUNT
	SHLD	SCOUNT	;SAVE FOR SORT COUNT
	LXI	H,ORDER
	LXI	D,TABLE
	LXI	B,12	;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		;NEXT ENTRY TO DE
	PUSH	H
	LHLD	OCOUNT	;GET COUNT
	DCX	H	;ONE LESS
	SHLD	OCOUNT	;SAVE NEW COUNT
	MOV	A,L
	ORA	H	;MORE?
	POP	H
	JNZ	BLDORD	;..YES
	LHLD	COUNT	;GET COUNT
	DCX	H
	MOV	A,L
	ORA	H	;ONLY 1 ENTRY?
	JZ	DONE	;..YES, SO SKIP SORT
;
SORT	XRA	A	;GET A ZERO
	STA	SWITCH	;SHOW NONE SWITCHED
	LHLD	SCOUNT	;GET COUNT
	DCX	H	;USE 1 LESS
	SHLD	TEMP	;SAVE # TO COMPARE
	SHLD	SCOUNT	;SAVE HIGHEST ENTRY
	MOV	A,L
	ORA	H
	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
	PUSH	H
	LHLD	TEMP	;GET COUNT
	DCX	H	;ONE LESS
	SHLD	TEMP	;SAVE COUNT
	MOV	A,L
	ORA	H	;DONE?
	POP	H
	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 KBD
	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
	LDA	SAVFCB	;GET SAVED 1ST CHAR. OF FCB
	CPI	' '	;WAS NAME SPECIFIED?
	JNZ	MATCH	;YES, GO SEE IF PRESENT
;PRINT USER #
PTONE	MOV	A,M	;GET USER NUMBER
	CPI	32	;IS IT VALID ? (IE.< 32)
	JNC	NULL	; NO, IGNORE IT
	ORA	A	;IS IT USER 0 ?
	JNZ	PTONE2	;NO, SKIP NEXT ROUTINE
	CALL	SPACE2	;USER 0, FILL WITH SPACES
	JMP	PTONE3	;SKIP DECOUT PRINT
;
PTONE2	PUSH	H
	MVI	H,0
	MOV	L,A	;NUMBER TO L FOR DECOUT
	CPI	10	;LESS THAN 10?
	CC	SPACE	;YES, ADD ONE SPACE
	CALL	DECOUT	;PRINT USER NUMBER
	POP	H
;
PTONE3	CALL	SPACE	;MAKE IT LOOK NICE
	INX	H	;POINT TO NAME
	MVI	B,8	;FILE NAME LENGTH
	CALL	TYPEIT	;TYPE FILENAME
	CALL	PERIOD	;PERIOD AFTER FN
	MVI	B,3	;GET THE FILETYPE
	CALL	TYPEIT
	DCR	C	;ONE LESS ON THIS LINE
	PUSH	PSW
	CNZ	FENCE	;NO CR-LF NEEDED, DO FENCE
	POP	PSW
	CZ	CRLF	;CR-LF NEEDED
;SEE IF MORE ENTRIES
NULL	PUSH	H
	LHLD	COUNT
	DCX	H
	SHLD	COUNT
	MOV	A,L
	ORA	H
	POP	H
	JNZ	ENTRY	;YES, MORE
	JMP	EXIT
;
MATCH	PUSH	D	;SAVE DE
	PUSH	H	;SAVE HL
	LXI	D,FCB	;GET FCB REQUEST
	INX	H	;MOVE TO FN.FT
	INX	D	;
	MVI	B,11	;GET NUMBER OF CHARACTERS TO COMPARE
;
MAT1	LDAX	D	;GET CHARACTER
	CPI	'*'	;IS FILENAME OR FILETYPE AMBIGUOUS ?
	JZ	WHCH	; YES, DETERMINE WHICH ONE
	CPI	'?'	;IS IT A AMBIGUOUS CHARACTER ?
	JZ	AMBC	; YES
	CMP	M	;COMPARE TO MEMORY
	JNZ	NOMATCH	;MATCH FAILURE
;
AMBC	INX	D
	INX	H	;GET NEW CHARACTERS
	DCR	B	;ARE WE DONE ?
	JNZ	MAT1	;NO, COMPARE NEXT
;
FTYP	POP	H
	POP	D	;RESTORE REGISTERS
	JMP	PTONE	;GO PRINT ONE
;
WHCH	MOV	A,B	;GET B
	CPI	3	;IS IT STILL THE FILENAME ?
	JC	FTYP	; NO, IT IS ANY TYPE SO WE ARE DONE.
	MVI	B,3	;YES, LOAD UP TO FILETYPE
	JMP	MAT1	;RETURN FOR MORE
;
NOMATCH	POP	H
	POP	D	;RESTORE REGISTERS
	JMP	NULL	;GET NEW FILENAME TO TRY
;
PERIOD	MVI	A,'.'
	JMP	TYPE
;
FENCE	CALL	SPACE2
	MVI	A,DELIM	;FENCE CHARACTER
	JMP	TYPE
;
SPACE3	CALL	SPACE
;
SPACE2	CALL	SPACE
;
SPACE	MVI	A,' '
;
;TYPE CHAR IN A
TYPE	PUSH	B
	PUSH	D
	PUSH	H
	MOV	E,A
	MVI	C,WRCHR
	CALL	BDOS
	POP	H
	POP 	D
	POP	B
	RET
;
TYPEIT	MOV	A,M
	CALL	TYPE
	INX	H
	DCR	B
	JNZ	TYPEIT
	RET
;
CRLF	MVI	A,CR	;CARRIAGE RETURN
	CALL	TYPE
	MVI	A,LF	;LINE FEED
	CALL	TYPE
	MVI	C,NPL	;NUMBER OF NAMES PER LINE
	RET
;
;DECIMAL OUTPUT ROUTINE
;
DECOUT:	PUSH	B
	PUSH	D
	PUSH	H
	LXI	B,-10
	LXI	D,-1
;
DECOU2:	DAD	B
	INX	D
	JC	DECOU2
	LXI	B,10
	DAD	B
	XCHG
	MOV	A,H
	ORA	L
	CNZ	DECOUT
	MOV	A,E
	ADI	'0'
	CALL	TYPE
	POP	H
	POP	D
	POP	B
	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
;
NFEXIT	CALL	ERXIT
	DB	'++NO FILES ON THIS DISK',CR,LF,'$'
;
;ERROR EXIT
ERXIT	POP	D	;GET MSG
	MVI	C,PRINT
	JMP	CALLB	;PRINT MSG, EXIT
;
;ABORT - READ CHAR ENTERED
ABORT	MVI	C,RDCHR	;DELETE THE CHAR
CALLB	CALL	BDOS
;
;FALL INTO EXIT
;EXIT - ALL DONE, RESTORE DISK LOG IN AND STACK
EXIT	LDA	CDSK	;GET ORIGINAL DISK NR.
	MOV	E,A	;TO E FOR CP/M
	MVI	C,SELDSK ;RESTORE LOG-IN TO IT
	CALL	BDOS
	LHLD	STACK	;GET OLD STACK
	SPHL		;MOVE TO STACK
	RET		;..AND RETURN
;
NEXTT	DW	TABLE	;NEXT TABLE ENTRY
COUNT	DW	0	;ENTRY COUNT
SWITCH	DB	0	;SWAP SWITCH FOR SORT
SAVFCB	DS	1	;SAVED 1ST CHAR. OF FCB
CDSK	DS	1	;CURRENTLY LOGGED DISK NUMBER
OCOUNT	DS	2	;# NAMES TO PUT IN ORDER TABLE
SCOUNT	DS	2	;# TO SORT
TEMP	DS	2	;SAVE DIR ENTRY
ORDER	DS	NAMES*2	;ORDER TABLE (TWO BYTES PER NAME)
	DS	60	;STACK AREA
STACK	DS	2	;SAVE OLD STACK HERE
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
SELDSK	EQU	14	;SELECT DISK DRIVE
FSRCHF	EQU	17	;   "	"
FSRCHN	EQU	18	;   "	"
CURDSK	EQU	25	;CHECK CURRENT DISK 0=A: 1=B:, ETC.
BDOS	EQU	BASE+5
;
	END
