;			MDIR.ASM 2.1
;		     revised 02/25/82
;
;	   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.				;
;								;
;_______________________________________________________________;
; 
; 02/25/82 FIXED PROBLEM WITH TOP-OF-MEMORY CHECK...PROGRAM NOW
;	   KEEPS TRACK OF WHETHER OR NOT IT OVERWRITES CCP, AND
;	   DOES A WARM BOOT IF NECESSARY.  ALSO ADDED TOP-OF-MEM
;	   CHECK IN BLDORD (MISSED THAT IN VERSION 2.0).  THANKS
;	   TO KEITH PETERSEN (W8SDZ) FOR POINTING OUT THESE DEF-
;	   ICIENCIES.		   RON FOWLER
;
; 01/11/81 FIXED PROBLEM WITH SYSTEMS USING NON-ZERO EXTENT MASKS
;	   BY MODIFYING EXTENT CHECK WITHIN 80H BUFFER TO USE EXM
;	   FIELD OF THE DISK PARAMETER BLOCK.
;	   ALSO REMOVED "NAMES" COUNT, ADDED DYNAMIC MEMORY SIZING,
;	   AND OUT-OF-MEMORY CHECK.
;	   (you may wonder: "with the power of SD-4x, why bother
;	   with MDIR?"...answer: "I like its simplicity.")
;	   
;			-- RON FOWLER
;
;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
;
GETVER	EQU	12	;GET VERSION BDOS FUNCT #
GETDPB	EQU	31	;GET DPB ADRS BDOS FUNCT #
;
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 02/25/82 '
;
;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	GETEXM	;GET EXTENT MASK
	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
	XCHG		;ENTRY TO DE
	LXI	H,12	;OFFSET TO EXTENT BYTE
	DAD	D
	LDA	EXTMSK	;GET DPB EXTENT MASK
	CMA		;GET NOT(EXTMSK)
	ANA	M	;MASK OUT ZERO-EXT BITS
	JNZ	MOREDIR ;IGNORE NON-ZERO PHYSICAL EXTENTS
	LHLD	NEXTT	;NEXT TABLE ENTRY TO HL
	MVI	B,12	;ENTRY LENGTH
	LDA	BASE+7	;GET TOP OF SYS PAGE #
	MOV	C,A	;INTO C REG
;
TMOVE	LDAX	D	;GET ENTRY CHAR
	ANI	7FH	;REMOVE ATTRIBUTES
	MOV	M,A	;STORE IN TABLE
	INX	D
	INX	H
	MOV	A,H	;SUBTRACT PTR FROM TOP
	SUB	C
	JNZ	OKMEM	;JUMP, NOT OUT OF MEM
;
MEMFUL: CALL	ERXIT	;OOPS, END REACHED
	DB	CR,LF,'OUT OF MEMORY'
	DB	CR,LF,'$'
;
OKMEM:	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	NEXTT	;DEFINE ORDER TABLE LOC
	SHLD	ORDER
	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
	LHLD	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
;
; TEST FOR MEMORY LIMIT EXCEEDED
;
	LDA	BASE+7	;GET TOP PAGE #
	SUB	H	;SUBTRACT CURRENT PAGE NUMBER
	JZ	MEMFUL	;ERROR OUT WHEN WE GET TOO HIGH
	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
	SHLD	HIMEM	;SAVE FOR CCP REBOOT FLAG
	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
	LHLD	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	LHLD	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
;
; SUBROUTINE TO RETRIEVE EXTENT MASK FROM DISK PARM BLK
;
GETEXM: MVI	C,GETVER ;GET CP/M VERSION NUMBER
	CALL	BDOS
	MOV	A,H
	ORA	L	;TEST FOR VERS 1.4
	JZ	PUTEXM	;MOVE THE ZERO EXTMSK IF 1.4
	MVI	C,GETDPB ;IS CPM >1.4, GET DPB ADRS
	CALL	BDOS
	LXI	D,4	;INDEX TO EXTMSK
	DAD	D
	MOV	A,M	;FETCH EXTENT MASK
;
PUTEXM: STA	EXTMSK	;PUT IT AWAY
	RET
;
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	BASE+6	;GET TOP-OF-MEMORY PTR
	MVI	L,0	;ROUND TO PAGE ADRS
	LXI	D,-800H ;SUBTRACT SIZE OF CCP
	DAD	D
	XCHG		;DE=@CCP
	LHLD	HIMEM	;FETCH OUR HIGHEST USED
	MOV	A,L	;SUBTRACT: HIMEM-CCPLOC
	SUB	E
	MOV	A,H
	SBB	D
	JC	BASE	;MUST WARM-BOOT, CCP NOT THERE
	LHLD	STACK	;GET OLD STACK
	SPHL		;MOVE TO STACK
	RET		;..AND RETURN
;
HIMEM:	DW	0	;HIGHEST MEMORY USED
EXTMSK: DB	0	;EXTENT MASK
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	2	;LOC OF 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
