;
;  PROGRAM:  LDIRZ
;  VERSION:  2.0
;  AUTHOR:  RICHARD CONN
;  DATE:  16 JAN 83
;  PREVIOUS VERSIONS:  1.0 (12 JAN 83)
;
vers    equ     20

;
;       LDIRZ prints a sorted directory of the default library file
; (installable via GENINS).  The printout gives file name and size.
; It is sorted alphabetically by file name and type.  Only one option
; is permitted, and that is a file spec (wild cards are OK).  Forms
; of the LDIRZ command are:
;
;               LDIRZ           <-- All Files
;               LDIRZ file.typ  <-- Selected Files
;               LDIRZ -library file.typ <-- Selected Files in Library
;               LDIRZ //        <-- Help Message
;

;
;  CP/M Constants
;
cpm     equ     0       ;base
bdose   equ     cpm+5
fcb     equ     cpm+5ch
tbuff   equ     cpm+80h
cr      equ     0dh
lf      equ     0ah

;
;  SYSLIB Routines
;
       ext     zgpins,print,zpfind,initfcb,f$open,f$read
       ext     sort,moveb,putud,logud,shftrh,phldc,codend
       ext     hmovb,crlf,cout,fname,cline

;
;  Branch to Start of Program
;
       jmp     start

;
;******************************************************************
;
;  SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format
;
;       This data block precisely defines the data format for
; initial features of a ZCPR2 system which are required for proper
; initialization of the ZCPR2-Specific Routines in SYSLIB.
;

;
;  EXTERNAL PATH DATA
;
EPAVAIL:
       DB      0FFH    ; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES)
EPADR:
       DW      40H     ; ADDRESS OF EXTERNAL PATH IF AVAILABLE

;
;  INTERNAL PATH DATA
;
INTPATH:
       DB      0,0     ; DISK, USER FOR FIRST PATH ELEMENT
                       ; DISK = 1 FOR A, '$' FOR CURRENT
                       ; USER = NUMBER, '$' FOR CURRENT
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0     ; DISK, USER FOR 8TH PATH ELEMENT
       DB      0       ; END OF PATH

;
;  MULTIPLE COMMAND LINE BUFFER DATA
;
MCAVAIL:
       DB      0FFH    ; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE?
MCADR:
       DW      0FF00H  ; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE

;
;  DISK/USER LIMITS
;
MDISK:
       DB      4       ; MAXIMUM NUMBER OF DISKS
MUSER:
       DB      31      ; MAXIMUM USER NUMBER

;
;  FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK
;
DOK:
       DB      0FFH    ; ALLOW DISK CHANGE? (0=NO, 0FFH=YES)
UOK:
       DB      0FFH    ; ALLOW USER CHANGE? (0=NO, 0FFH=YES)

;
;  PRIVILEGED USER DATA
;
PUSER:
       DB      10      ; BEGINNING OF PRIVILEGED USER AREAS
PPASS:
       DB      'chdir',0       ; PASSWORD FOR MOVING INTO PRIV USER AREAS
       DS      41-($-PPASS)    ; 40 CHARS MAX IN BUFFER + 1 for ending NULL

;
;  CURRENT USER/DISK INDICATOR
;
CINDIC:
       DB      '$'     ; USUAL VALUE (FOR PATH EXPRESSIONS)

;
;  DMA ADDRESS FOR DISK TRANSFERS
;
DMADR:
       DW      80H     ; TBUFF AREA

;
;  NAMED DIRECTORY INFORMATION
;
NDRADR:
       DW      00000H  ; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY
NDNAMES:
       DB      64      ; MAX NUMBER OF DIRECTORY NAMES
DNFILE:
       DB      'NAMES   '      ; NAME OF DISK NAME FILE
       DB      'DIR'           ; TYPE OF DISK NAME FILE

;
;  REQUIREMENTS FLAGS
;
EPREQD:
       DB      0FFH    ; EXTERNAL PATH?
MCREQD:
       DB      000H    ; MULTIPLE COMMAND LINE?
MXREQD:
       DB      000H    ; MAX USER/DISK?
UDREQD:
       DB      000H    ; ALLOW USER/DISK CHANGE?
PUREQD:
       DB      000H    ; PRIVILEGED USER?
CDREQD:
       DB      0FFH    ; CURRENT INDIC AND DMA?
NDREQD:
       DB      000H    ; NAMED DIRECTORIES?
Z2CLASS:
       DB      5       ; CLASS 5
       DB      'ZCPR2'
       DS      10      ; RESERVED

;
;  END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA
;
;******************************************************************
;
library:
       DB      'COMMAND ' ; <---change this if you like---
libtyp:
       DB      'LBR'
;
;  START OF PROGRAM
;
start:
       call    zgpins  ; init ZCPR2 buffers
       call    putud   ; save current user/disk

;
;  Print Banner
;
       call    print
       db      'LDIRZ  Version '
       db      vers/10+'0','.',(vers mod 10)+'0',0

;
;  Set Default Library Name
;
       lxi     h,library
       lxi     d,fcb+1 ; copy into FCB
       mvi     b,11    ; 11 chars
       call    moveb

;
;  Extract Information
;
       lxi     h,tbuff ; get command line from buffer
       call    cline
       call    sblank  ; skip to non-blank
       ora     a       ; EOL?
       jz      help
       cpi     '/'     ; Help?
       jnz     start0

;
;  Print Help Message
;
help:
       call    print
       db      cr,lf,' LDIRZ prints a sorted directory of the current'
       db      cr,lf,'Library File.  It is invoked by one of the forms:'
       db      cr,lf,'         LDIRZ                   <-- All Files'
       db      cr,lf,'         LDIRZ filename.typ      <-- Selected Files'
       db      cr,lf,'         LDIRZ -lib filename.typ <-- Files from LIB'
       db      cr,lf,'         LDIRZ //                <-- Print Help'
       db      cr,lf,' Wild Cards (*,?) are permitted.'
       db      cr,lf,0
       ret

;
;  Check for Library Name and Set it if so
;
start0:
       cpi     '-'     ; library follows?
       jnz     start1
       inx     h       ; pt to library name
       lxi     d,fcb   ; pt to FCB
       call    fname   ; extract library name
       call    sblank  ; skip to non-blank
       lxi     d,fcb+9 ; check file type
       ldax    d
       cpi     ' '     ; none?
       jnz     start1
       push    h       ; save ptr
       lxi     h,libtyp        ; default type
       mvi     b,3     ; 3 chars
       call    moveb   ; copy it
       pop     h       ; get ptr
;
;  Check for File Spec and Set if so
;
start1:
       ora     a       ; any chars?
       jz      start2  ; fspec is already wild
       lxi     d,filesp        ; copy into file spec
       call    fname   ; extract file name

;
;  Find Library
;
start2:
       lxi     d,fcb           ; pt to FCB
       call    initfcb
       mvi     b,0ffh          ; search current
       call    zpfind          ; look for library file
       jnz     start3
       call    print
       db      cr,lf,'Library File ',0
       lxi     h,fcb+1 ; pt to name
       call    prfn    ; print file name
       call    print
       db      ' Not Found',0
       ret

;
;  Read in First Block of Library
;
start3:
       call    logud           ; log into directory
       call    f$open          ; open file
       call    f$read          ; read first block
       ora     a               ; ok?
       jz      readdir         ; extract directory info
       call    print
       db      cr,lf,'Library File ',0
       lxi     h,fcb+1
       call    prfn
       call    print
       db      ' is Empty',0
       ret
readdir:
       lxi     h,tbuff+14      ; pt to dir size
       mov     a,m
       inx     h
       mov     h,m             ; HL=size of directory
       mov     l,a
       mov     b,h             ; BC=number of blocks in dir
       mov     c,l
       lxi     h,0             ; set file count
       shld    filecnt
       call    codend          ; pt to spare buffer
       shld    bufptr          ; set buffer ptr
       shld    ssbstart        ; set starting address of first record
       lxi     h,tbuff+32      ; pt to first entry
       jmp     getentry        ; get next entry
;
;  Read Next Block
;
readblock:
       push    b               ; save count
       lxi     d,fcb           ; pt to FCB
       call    f$read          ; read block
       pop     b               ; restore regs
       lxi     h,tbuff         ; pt to next entry
       ora     a               ; error?
       jz      getentry        ; get next entry
       call    print
       db      cr,lf,'Premature End of Library File ',0
       lxi     h,fcb+1
       call    prfn
       call    print
       db      ' -- Aborting',0
       ret
;
;  Get next entry from library directory
;    On entry, HL pts to entry, BC=count
;
getentry:
       push    h               ; save ptr
       push    b               ; save count
       mov     a,m             ; entry present?
       cpi     0ffh            ; none?
       jz      gete1
       cpi     0               ; deleted?
       jnz     gete1
       inx     h               ; pt to file name
       push    h               ; save ptr to entry
       lxi     d,fspec         ; pt to file spec
       mvi     b,11            ; check 11 bytes
getent1:
       ldax    d               ; check for match with wild cards
       mov     c,m             ; get target char
       inx     h               ; pt to next
       inx     d
       cpi     '?'             ; wild match?
       jz      getent2
       cmp     c               ; match?
       jnz     gete0           ; skip if not
getent2:
       dcr     b               ; count down
       jnz     getent1
       pop     h               ; file matches -- continue
       push    h               ; save ptr
       lhld    filecnt         ; increment file count
       inx     h
       shld    filecnt
       pop     h
       xchg                    ; ptr to file name in DE
       lhld    bufptr          ; get buffer ptr
       mvi     b,11            ; 11 bytes
       xchg                    ; HL pts to entry
       call    hmovb           ; copy and affect HL and DE
       inx     h               ; skip index
       inx     h
       mvi     b,2             ; copy size
       call    hmovb
       xchg                    ; get buffer ptr in HL
       shld    bufptr          ; save buffer ptr
       xchg                    ; put back in DE
       lhld    bdose+1         ; memory full?
       mov     a,h             ; check pages
       sui     10              ; below CCP
       cmp     d               ; in range?
       jnz     gete1
       pop     b
       pop     h               ; clear stack
       call    print
       db      cr,lf,'Memory Overflow',0
       ret
gete0:
       pop     h               ; clear stack
gete1:
       pop     b               ; get count
       pop     h               ; get ptr to block
       lxi     d,32            ; pt to next
       dad     d
       lxi     d,tbuff+80h     ; pt to end of buffer
       mov     a,e             ; new block if low-order is the same
       cmp     l
       jnz     getentry
       dcx     b               ; count down
       mov     a,b             ; done?
       ora     c
       jnz     readblock       ; continue with next block
;
;  Done with Buffer Load
;
       lhld    filecnt         ; set record count
       shld    ssbcnt
       lxi     d,ssb           ; pt to ssb
       call    sort            ; perform sort
       mov     a,l             ; get low (remainder)
       ani     3               ; select remainder
       sta     remain
       call    shftrh          ; shift right 2 bits
       call    shftrh
       shld    loopcnt         ; number of loops
       xchg                    ; count in DE
       lxi     h,0             ; compute sum
       lxi     b,13            ; bytes/entry
col0:
       mov     a,d             ; done?
       ora     e
       jz      col1
       dcx     d               ; count down
       dad     b               ; add in size
       jmp     col0
col1:
       xchg                    ; DE is size of list
       lhld    ssbstart        ; pt to first entry
       shld    entry1
       dad     d               ; pt to next entry
       lda     remain          ; add 1?
       ora     a               ; no if zero
       jz      col2
       dad     b               ; add in another entry
col2:
       shld    entry2          ; set ptr to 2nd entry
       dad     d               ; pt to next entry
       cpi     2               ; remainder >=2?
       jc      col3
       dad     b               ; add in another entry
col3:
       shld    entry3          ; set ptr to 3rd entry
       dad     d               ; pt to last entry
       cpi     3               ; add 1?
       jnz     col4
       dad     b               ; add in another entry
col4:
       shld    entry4
;
;  Directory Print -- ENTRYn pts to first element in each col, DE is
;    number of loops, and REMAIN is number of extra entries
;
       lxi     h,0     ; clear sum
       shld    sum
       call    print
       db      cr,lf
       db      'Filename.Typ  Size  '
       db      'Filename.Typ  Size  '
       db      'Filename.Typ  Size  '
       db      'Filename.Typ  Size'
       db      cr,lf
       db      '-------- --- -----  '
       db      '-------- --- -----  '
       db      '-------- --- -----  '
       db      '-------- --- -----',0

       lhld    loopcnt         ; get loop count
       xchg                    ; ... in DE
dirloop:
       mov     a,d             ; Done?
       ora     e
       jz      dirdone
       dcx     d               ; count down
       call    crlf            ; new line
       lhld    entry1          ; print entry
       call    prentry
       shld    entry1
       lhld    entry2
       call    prentry
       shld    entry2
       lhld    entry3
       call    prentry
       shld    entry3
       lhld    entry4
       call    prentry
       shld    entry4
       jmp     dirloop
dirdone:
       lda     remain          ; print last line
       ora     a               ; none?
       jz      done
       mov     d,a             ; count in D
       call    crlf            ; new line
       lhld    entry1          ; print first col
       call    prentry
       dcr     d               ; count down
       jz      done
       lhld    entry2          ; print 2nd col
       call    prentry
       dcr     d               ; count down
       jz      done
       lhld    entry3          ; print 3rd col
       call    prentry
done:
       call    crlf            ; new line
       lhld    filecnt         ; print number of entries
       call    phldc
       call    print
       db      ' Files Selected occupying a Total of ',0
       lhld    sum             ; print sum of sizes
       call    phldc
       call    print
       db      'K Bytes',0
       ret
;
;  Print File Name Entry
;
prentry:
       push    d
       push    h
       push    b
       call    prfn            ; print file name
       lxi     d,11            ; pt to size
       dad     d
       mov     c,m             ; get size in BC
       inx     h
       mov     b,m
       mov     a,c             ; part of a K?
       ani     7
       sta     kpart
       mov     h,b             ; file size in HL
       mov     l,c
       call    shftrh          ; move right 3 bits for size in K
       call    shftrh
       call    shftrh
       lda     kpart           ; add 1 for overflow?
       ora     a               ; 0=no
       jz      prent1
       inx     h               ; add 1 K
prent1:
       call    phldc           ; print as decimal
       mvi     a,'K'           ; print K
       call    cout
       mvi     a,' '           ; print 2 spaces
       call    cout
       call    cout
       xchg                    ; size in DE
       lhld    sum             ; add to sum
       dad     d
       shld    sum
       pop     b               ; get regs
       pop     h
       lxi     d,13            ; pt to next entry
       dad     d
       pop     d
       ret
;
;  Print file name pted to by HL
;
prfn:
       push    h               ; save regs
       push    b
       mvi     b,8             ; 8 chars
       call    prfn1
       mvi     a,'.'
       call    cout
       mvi     b,3             ; 3 chars
       call    prfn1
       pop     b               ; get regs
       pop     h
       ret
prfn1:
       mov     a,m             ; print chars
       inx     h               ; pt to next
       call    cout
       dcr     b               ; count down
       jnz     prfn1
       ret
;
;  Compare Routine
;       Compare (DE) to (HL) and Return with Carry if (DE) < (HL)
;
compare:
       push    h               ; save regs
       push    d
       push    b
       mvi     b,11            ; 11 bytes to compare
comp:
       ldax    d               ; get byte
       cmp     m               ; compare
       jnz     compd           ; done if not same
       inx     h               ; pt to next
       inx     d
       dcr     b               ; count down
       jnz     comp
compd:
       pop     b               ; restore regs
       pop     d
       pop     h
       ret

;
;  Skip to Non-Blank
;
sblank:
       mov     a,m     ; skip to non-blank
       inx     h
       cpi     ' '
       jz      sblank
       dcx     h
       ret

;
;  Buffers
;
ssb:                            ; sort specification block
ssbstart:
       ds      2               ; ptr to first byte of first record
ssbcnt:
       ds      2               ; number of records
       dw      13              ; 13 bytes/record
       dw      compare         ; address of compare routine
       dw      0               ; no ptr table
       db      0,0             ; don't use ptrs
filesp:
       db      0
fspec:
       db      '????????'      ; file spec
       db      '???'
       ds      24
remain:
       ds      1               ; column remainder
kpart:
       ds      1               ; K remainder
filecnt:
       ds      2               ; number of files
bufptr:
       ds      2               ; scratch buffer ptr
sum:
       ds      2               ; sum of file sizes
loopcnt:
       ds      2               ; number of lines to print
entry1:
       ds      2
entry2:
       ds      2
entry3:
       ds      2
entry4:
       ds      2

       end