;		    DIRF.ASM ver 1.3
;		by Keith Petersen, W8SDZ
;		   (revised 12/19/80)
;
;	DIRECTORY FUNCTION FOR CP/M 1.4 OR 2.x PROGRAMS
;
;This file contains routines which can be included in any
;CP/M program to allow listing the directory.  No sorting
;is done because that would require use of more memory above
;the program.  These routines use the 80h default buffer for
;all operations so if you have data there be sure to move it
;before running this directory function.  Assume all registers
;destroyed when calling 'DIRF'.
;
;This module may be assembled as a stand-alone program for
;testing.  Execute program from CP/M, not DDT or L80.  It
;will return to the CCP after finishing.
;
;Remove the following ORG statement and the END statement
;when including this module in other programs
	ORG	100H	 ;TEMPORARY ORG FOR TESTING
;
;--->DIRF.LIB STARTS HERE
;
NPL	EQU	4	 ;NUMBER OF NAMES PER LINE
TAB	EQU	9	 ;HORIZONTAL TAB
CR	EQU	0DH	 ;CARRIAGE RETURN
LF	EQU	0AH	 ;LINE FEED
;
;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
FSRCHF	EQU	17	 ;0FFH=NOT FOUND
FSRCHN	EQU	18	 ;   "	     "
CURDSK	EQU	25	 ;GET CURRENT DISK NAME
BDOS	EQU	5
FCB	EQU	5CH
;
;First, we preserve old stack pointer and set a new one
;because some functions take more stack space than may
;be available in the calling program.
DIRF:	LXI	H,0
	DAD	SP	 ;GET OLD STACK POINTER
	SHLD	STACK	 ;SAVE FOR LATER
	LXI	SP,STACK ;SET NEW STACK POINTER
;
;Check FCB for drive request
	LDA	FCB	 ;GET DRIVE NAME FROM FCB
	ORA	A	 ;ANY REQUESTED? (0=NO)
	JNZ	GOTDRV	 ;NOT ZERO MEANS WE HAVE NAME
;
;Get drive name
	MVI	C,CURDSK ;GET CURRENT DRIVE NAME
	CALL	BDOS
	INR	A	 ;MAKE 'A' RELATIVE TO 1 NOT 0
;
;Print signon message and drive name
GOTDRV:	ADI	40H	 ;MAKE IT ASCII
	STA	DNAME	 ;SAVE IT IN MESSAGE
	LXI	D,MSG	 ;POINT TO MESSAGE
	MVI	C,PRINT	 ;PRINT IT
	CALL	BDOS
;
;Make FCB all '?' to match any file
	LXI	H,FCB+1
	MVI	B,11	 ;FN+FT COUNT
;
QLOOP:	MVI	M,'?'	 ;STORE '?' IN FCB
	INX	H
	DCR	B
	JNZ	QLOOP
;
;Initialize number of names per line counter
	MVI	A,NPL	 ;NR. NAMES PER LINE
	STA	NNAMS	 ;INIT COUNTER
;
;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?
	JNZ	SOME	 ;GOT SOME
	CALL	ERXIT
	DB	'++NOT FOUND$'
;
;Read more directory entries
MORDIR:	MVI	C,FSRCHN ;SEARCH NEXT
	LXI	D,FCB
	CALL	BDOS	 ;READ DIR ENTRY
	INR	A	 ;CHECK FOR END (0FFH)
	JZ	EXIT	 ;NO MORE - EXIT
;
;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)
;
;Check for console break
	PUSH	H	 ;SAVE NAME POINTER
	MVI	C,CONST	 ;CK STATUS OF KBD
	CALL	BDOS
	POP	H	 ;RESTORE NAME POINTER
	ORA	A	 ;ANY KEY PRESSED?
	JNZ	ABORT	 ;YES, ABORT
;
;Print an entry
	MVI	B,8	 ;FILE NAME LENGTH
	CALL	TYPEIT	 ;TYPE FILENAME
	MVI	A,'.'	 ;PERIOD AFTER FN
	CALL	TYPE
	MVI	B,3	 ;GET THE FILETYPE
	CALL	TYPEIT
	LXI	H,NNAMS	 ;POINT TO NAMES COUNTER
	DCR	M	 ;ONE LESS ON THIS LINE
	PUSH	PSW
	CNZ	FENCE	 ;NO CR-LF NEEDED, DO FENCE
	POP	PSW
	CZ	CRLF	 ;CR-LF NEEDED
	JMP	MORDIR
;
;Print two spaces, fence character, then two more spaces
FENCE:	CALL	TWOSPC
	MVI	A,':'	 ;FENCE CHARACTER
	CALL	TYPE
;
;Print two spaces
TWOSPC:	CALL	SPACE
;
;Print one space
SPACE:	MVI	A,' '
;
;Type char in A register
TYPE:	PUSH	B
	PUSH	D
	PUSH	H
	MOV	E,A	 ;CHAR TO E FOR CP/M
	MVI	C,WRCHR	 ;WRITE CHAR TO CONSOLE FUNC
	CALL	BDOS
	POP	H
	POP 	D
	POP	B
	RET
;
;Type (B) characters from memory (HL)
TYPEIT:	MOV	A,M
	ANI	7FH	 ;REMOVE CP/M 2.x ATTRIBUTES
	CALL	TYPE
	INX	H
	DCR	B
	JNZ	TYPEIT
	RET
;
;CR-LF routine. HL=NNAMS upon entry
CRLF:	MVI	A,CR	 ;CR
	CALL	TYPE
	MVI	A,LF	 ;LF
	CALL	TYPE
	MVI	M,NPL	 ;NUMBER OF NAMES PER LINE
	RET
;
;Error exit
ERXIT:	POP	D	 ;GET MSG
	MVI	C,PRINT
	JMP	CALLB	 ;PRINT MSG, EXIT
;
;Abort - read char entered to clear it from CP/M buffer
ABORT:	MVI	C,RDCHR	 ;DELETE THE CHAR
;
;Fall into CALLB

CALLB:	CALL	BDOS
;
;Fall into EXIT
;
;Exit - All done, return to caller
EXIT:	LHLD	STACK	 ;GET OLD STACK
	SPHL		 ;MOVE TO STACK
	RET		 ;...AND RETURN
;
MSG:	DB	TAB,TAB,'    Directory for drive '
DNAME:	DB	'X:',CR,LF,'$'
;
;Temporary storage area
NNAMS:	DS	1	 ;NAMES PER LINE COUNTER
	DS	40	 ;ROOM FOR STACK
STACK:	DS	2	 ;OLD STACK STORED HERE
;
;Temporary end statement used only for testing DIRF module.
;Remove before including module in other programs.
	END	DIRF
