;               DIRF.MAC (Z80 VERSION) ver 1.3
;                 by Keith Petersen, W8SDZ
;                    (revised 12/19/80)
;
;      DIRECTORY FUNCTION FOR CP/M 1.4 OR 2.x PROGRAMS
;
;--->NOTE: Assemble with Microsoft M80 assembler
;
;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.  L80 link command for testing:   L80 DIRF,DIRF,/N/E
;Execute program from CP/M, not L80.  It will return to the
;CCP after finishing.
;
       .Z80               ;Z80 SWITCH FOR ASSEMBLER
       ENTRY   DIRF       ;DEFINE DIRF AS PUBLIC
;
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:   LD      HL,0
       ADD     HL,SP      ;GET OLD STACK POINTER
       LD      (STACK),HL ;SAVE FOR LATER
       LD      SP,STACK   ;SET NEW STACK POINTER
;
;Check FCB for drive request
       LD      A,(FCB)    ;GET DRIVE NAME FROM FCB
       OR      A          ;ANY REQUESTED? (0=NO)
       JR      NZ,GOTDRV  ;NOT ZERO MEANS WE HAVE NAME
;
;Get drive name
       LD      C,CURDSK   ;GET CURRENT DRIVE NAME
       CALL    BDOS
       INC     A          ;MAKE 'A' RELATIVE TO 1 NOT 0
;
;Print signon message and drive name
GOTDRV: ADD     A,40H      ;MAKE IT ASCII
       LD      (DNAME),A  ;SAVE IT IN MESSAGE
       LD      DE,MSG     ;POINT TO MESSAGE
       LD      C,PRINT    ;PRINT IT
       CALL    BDOS
;
;Make FCB all '?' to match any file
       LD      HL,FCB+1
       LD      B,11       ;FN+FT COUNT
;
QLOOP:  LD      (HL),'?'   ;STORE '?' IN FCB
       INC     HL
       DEC     B
       JR      NZ,QLOOP
;
;Initialize number of names per line counter
       LD      A,NPL      ;NR. NAMES PER LINE
       LD      (NNAMS),A  ;INIT COUNTER
;
;Look up the FCB in the directory
       LD      C,FSRCHF   ;GET 'SEARCH FIRST' FNC
       LD      DE,FCB
       CALL    BDOS       ;READ FIRST
       INC     A          ;WERE THERE ANY?
       JR      NZ,SOME    ;GOT SOME
       CALL    ERXIT
       DEFB    '++NO FILE$'
;
;Read more directory entries
MORDIR: LD      C,FSRCHN   ;SEARCH NEXT
       LD      DE,FCB
       CALL    BDOS       ;READ DIR ENTRY
       INC     A          ;CHECK FOR END (0FFH)
       JP      Z,EXIT     ;NO MORE - EXIT
;
;Point to directory entry
SOME:   DEC     A          ;UNDO PREV 'INC A'
       AND     3          ;MAKE MODULUS 4
       ADD     A,A        ;MULTIPLY...
       ADD     A,A        ;..BY 32 BECAUSE
       ADD     A,A        ;..EACH DIRECTORY
       ADD     A,A        ;..ENTRY IS 32
       ADD     A,A        ;..BYTES LONG
       LD      HL,81H     ;POINT TO BUFFER (SKIP TO FN/FT)
       ADD     A,L        ;POINT TO ENTRY
       LD      L,A        ;SAVE - HL NOW = ENTRY ADRS
;
;Check for console break
       PUSH    HL         ;SAVE NAME POINTER
       LD      C,CONST    ;CK STATUS OF KBD
       CALL    BDOS
       POP     HL         ;RESTORE NAME POINTER
       OR      A          ;ANY KEY PRESSED?
       JP      NZ,ABORT   ;YES, ABORT
;
;Print an entry
       LD      B,8        ;FILE NAME LENGTH
       CALL    TYPEIT     ;TYPE FILENAME
       LD      A,'.'      ;PERIOD AFTER FN
       CALL    TYPE
       LD      B,3        ;GET THE FILETYPE
       CALL    TYPEIT
       LD      HL,NNAMS   ;POINT TO NAMES COUNTER
       DEC     (HL)       ;ONE LESS ON THIS LINE
       PUSH    AF
       CALL    NZ,FENCE   ;NO CR-LF NEEDED, DO FENCE
       POP     AF
       CALL    Z,CRLF     ;CR-LF NEEDED
       JP      MORDIR
;
;Print two spaces, fence character, then two more spaces
FENCE:  CALL    TWOSPC
       LD      A,':'      ;FENCE CHARACTER
       CALL    TYPE
;
;Print two spaces
TWOSPC: CALL    SPACE
;
;Print one space
SPACE:  LD      A,' '
;
;Type char in A register
TYPE:   PUSH    BC
       PUSH    DE
       PUSH    HL
       LD      E,A        ;CHAR TO E FOR CP/M
       LD      C,WRCHR    ;WRITE CHAR TO CONSOLE FUNC
       CALL    BDOS
       POP     HL
       POP     DE
       POP     BC
       RET
;
;Type (B) characters from memory (HL)
TYPEIT: LD      A,(HL)
       AND     7FH        ;REMOVE CP/M 2.x ATTRIBUTES
       CALL    TYPE
       INC     HL
       DEC     B
       JR      NZ,TYPEIT
       RET
;
;CR-LF routine. HL=NNAMS upon entry
CRLF:   LD      A,CR       ;CR
       CALL    TYPE
       LD      A,LF       ;LF
       CALL    TYPE
       LD      (HL),NPL   ;NUMBER OF NAMES PER LINE
       RET
;
;Error exit
ERXIT:  POP     DE         ;GET MSG
       LD      C,PRINT
       JR      CALLB      ;PRINT MSG, EXIT
;
;Abort - read char entered to clear it from CP/M buffer
ABORT:  LD      C,RDCHR    ;DELETE THE CHAR
;
;Fall into CALLB
;
CALLB:  CALL    BDOS
;
;Fall into EXIT
;
;Exit - All done, return to caller
EXIT:   LD      HL,(STACK) ;GET OLD STACK
       LD      SP,HL      ;MOVE TO STACK
       RET                ;...AND RETURN
;
MSG:    DEFB    TAB,TAB,'    Directory for drive '
DNAME:  DEFB    'X:',CR,LF,'$'
;
;Temporary storage area
;
NNAMS:  DEFS    1          ;NAMES PER LINE COUNTER
       DEFS    40         ;ROOM FOR STACK
STACK:  DEFS    2          ;OLD STACK STORED HERE
;
;END OF DIRECTORY MODULE
;
;Temporary end statement used only for testing DIRF module.
;Remove before including module in other programs.
       END     DIRF