;                       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