;                   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