        page    74,132
        title   --- ldir - list directory -

;    LDIR (c) Copyright Vernon D. Buerg 1985-1989
;    ALL RIGHTS RESERVED.

dtantry struc                           ; DTA entry returned by DOS
dtarsvd db      21 dup (0)              ;  reserved
dtaattr db      0                       ;  attribute
dtatime dw      0                       ;  update time
dtadate dw      0                       ;  update date
dtasize dw      0,0                     ;  size bytes (lo,hi)
dtaname db      12 dup (' ')            ;  name and ext
        db      0,13,10,255             ;  stopper and print
dtantry ends

argntry struc                           ; Search arguments
argdriv db      0,':'                   ;  drive
argpath db      '\'                     ;  path delimiter
argname db      64 dup (0),255          ;  path,file,ext
argntry ends

tblntry struc                           ; Table Entries
tblattr db      0                       ;  attribute
tblpath db      21 dup (0)              ;  path name(s)
tblname db      8 dup (' ')             ;  file name
tbldot  db      ' '                     ;  delimiter
tblext  db      3 dup (' ')             ;  extension
tblsize db      ' 0000000 '             ; File size
tbldate db      '80-01/01 '             ; Date
tbltime db      ' 0:00 '                ; Time
tblfatr db      3 dup (' ')             ; File attributes
tblctl  db      13,10,255               ; print control chars
tblntry ends

filntry struc                           ; List of files in table
filsub  db      0                       ; -index to subdirectory name
filattr db      0                       ; -file attributes
filtime dw      0                       ; -time stamp
fildate dw      0                       ; -date stamp
filsize dw      0,0                     ; -file size
filname db      12 dup ('?')            ; -asciiz file name
filstop db      ?                       ; -asciiz stopper
filntry ends


bios    segment at 40h          ; dos data area
        org     84h             ;
ega_row label   byte            ; rows on screen
bios    ends

save    macro   reglist
        irp     reg, <reglist>
        push    reg             ; save register
        endm
        endm

return  macro   reglist
        irp     reg, <reglist>
        pop     reg             ; restore register
        endm
        ret                     ; return to caller
        endm


cseg    segment public para 'CODE'
        assume  cs:cseg, ds:cseg, es:nothing
        org     100h

ldir    proc    far
        mov     sp,offset local_stack_end ; Use local stack
        mov     word ptr stackx,sp        ; Save stack ptr for exiting
        jmp     start

        page
;
;       Data Areas, Constants, Etc.

dta_len equ     size dtantry            ; DTA length
arg_len equ     size argntry            ; Argument length
tbl_len equ     size tblntry            ; Disk record entry
fil_len equ     size filntry            ; File description entry

tblpara equ     tbl_len/16              ; paragraphs in each table entry

depth   equ     3                       ; Maximum sub-DIR nest level
tab     equ     9                       ; A tabbie
lf      equ     10                      ; Line feed
cr      equ     13                      ; Carriage return
stopper equ     255                     ; Ends print strings

curdsk  equ     19h                     ;Get current disk
setdta  equ     1ah                     ;Set data transfer area
dskspc  equ     36h                     ;Get disk free space
chdir   equ     3bh                     ;Change directory
write   equ     40h                     ;Write to a file handle
getpath equ     47h                     ;Get current directory

stackx  dw      0                       ; Entry stack pointer
dirmask db      0                       ; Directory flags mask
errlvl  db      0                       ; DOS return code
_attr   db      7                       ; Clear attribute
_page   db      0                       ; Video page
_rows   db      0                       ; Rows on screen

flags   db      byname                  ; Command switches
 byattr  equ    1                       ; -attributes included
 byhide  equ    2                       ; -want hidden files
 byclear equ    4                       ; -clear screen
 bydate  equ    8                       ; -sort by date/time
 byext   equ    10h                     ; -sort by extension
 byname  equ    40h                     ; -sort by name
 bysize  equ    80h                     ; -sort by size

flags2  db      0                       ; More switches
 bymod   equ    01h                     ; -modified files only
 bywait  equ    02h                     ; -wait when screen full
 bybrief equ    4                       ; -four up
 bypath  equ    20h                     ; -all paths

date    record  yr:7,mo:4,dy:5          ; Packed date
time    record  hour:5,min:6,sec:5      ; Packed time

anyname db      '\????????.???',0,stopper ;Global filename.ext
subdir  db      '-Dir'

count   dw      0                       ; Number of files
numdir  dw      0                       ; Number of table entries
maxdir  dw      0                       ; Maximum table entries
nxtdir  dw      0                       ; Offset to next entry
segdir  dw      0                       ; Segment addr of of gotten table
numbyte dw      0,0                     ;Total bytes used
linecnt db      0                       ;Line counter for /W

ddptr   dw      0

        page
;
;       Headings and titles

titlea  db      'List DIRectory            Volume: '
volname db      11 dup (' '),11 dup (' ')
month   db      'mm/'
day     db      'dd/'
year    db      'yy '
hours   db      'hh:'
mins    db      'mm:'
secs    db      'ss',cr,lf,stopper

more    db      '... more',stopper
backsp  db      8 dup (8),stopper       ; backspace over 'more'

titleb  db      tab,tab,tab,tab,'  '    ;Command parameters
titles  db      64 dup (0),stopper      ;Current directory

titlec  db      cr,lf,'Filename Ext   Bytes  -Last Change-     '
titled  db      'Filename Ext   Bytes  -Last Change-'
newline db      lf,cr,stopper
nrmsg   db      cr,cr,2 dup (' ')       ;Ending message
nrbytes db      '       0 bytes in'
nrfiles db      '       0 File(s);  '
nrsize  db      '       0 bytes free.',stopper


extfcb  db      255,0,0,0,0,0           ; Extended FCB to get label
        db      8                       ; Attribute
drivenr db      0,11 dup('?')           ; Drive number
        db      2 dup(0)                ; Current block number
        db      3 dup(0)                ; Logical record size
        db      20 dup(0)               ; File size

temp    db      0                       ; Sort exchange area
origdr  db      'x:'                    ; Original drive
origdir db      '\',63 dup (0)          ; and path
origptr dw      origdir                 ; End of orig path name

rootdir db      'x:\',0                 ; To get vol label
olddate db      8 dup (0)               ;
drptr   dw      offset parmdr           ;

modelx  tblntry <>          ; Model print line
        page
;
;       Set default drive and path

start:
        mov     ah,8                    ; Get monitor stuff
        int     10h                     ;
        mov     _page,0                 ; -video page
        mov     _attr,ah                ; -current attribute

        push    es                      ; set dos data area
        mov     ax,bios                 ; segment address
        mov     es,ax                   ;
        mov     al,byte ptr es:ega_row  ; dos rows on screen
        mov     ah,24                   ; default max rows
        or      al,al                   ; is row value ok?
        jz      start1                  ; no, use default
        mov     ah,al                   ; yes,
start1: sub     ah,2                    ; compensate for heading
        mov     byte ptr _rows,ah       ; set max rows on screen
        pop     es                      ;

        call    switchs                 ; Get program options

        mov     ah,13                   ; Reset diskettes
        int     21h                     ;

        mov     ah,curdsk               ; Get current disk
        int     21h                     ;
        add     al,'A'                  ;
        mov     origdr,al               ; Save original drive letter
        mov     rootdir,al              ;  for reading vol label
        mov     parmdr,al               ;

        mov     dx,offset dta           ; Set Data Transfer Area
        mov     ah,setdta               ;
        int     21h                     ;

        call    getparm                 ; Get desired dr:path in 'parmdir'

        call    getvol                  ; Get vol name, current dir in 'origdir'

        call    setarg                  ; Set search argument

        call    alloc                   ; Allocate table for directory

        call    clock                   ; Date/time to heading

        call    getdir                  ; Read the directory

        test    flags,255               ; Any sort options?
        jz      nosort                  ; no, display fifo dir
        call    sort                    ; Sort directory table

nosort:
        call    print                   ; Display the entries

        mov     ax,count                ; Number of files
        sub     dx,dx                   ;
        mov     si,offset nrfiles       ;
        call    format                  ;

        mov     dx,numbyte              ; Total bytes used
        mov     ax,numbyte+2            ;
        mov     si,offset nrbytes       ;
        call    format                  ;

        mov     dx,offset nrmsg         ; Display summary stats

exit:   mov     sp,cs:stackx            ; Insure exiting stack
        call    prints                  ; Display final message

done:   mov     sp,cs:stackx            ; Insure exiting stack
        mov     al,errlvl               ; Return to system
        mov     ah,4ch                  ; via EXIT with error level
        int     21h                     ;

ldir    endp

        page
;
;       Set options from command line

switchs proc    near
        mov     si,82h                  ; Command tail
        mov     dh,flags                ; Default switches
        mov     dl,flags2
        sub     cx,cx
        or      cl,byte ptr -2[si]      ; test parm length
        jz      switch_exit             ; none, return as-is

sw1:    lodsb                           ; Scan for switch
        cmp     al,'/'
        loopne  sw1
        or      cx,cx                   ; found one?
        jz      switch_exit
        mov     byte ptr -1[si],cr      ; Stop string here
        jmp     short sw2a

switch_exit:
        mov     flags,dh                ; Store new switches
        mov     flags2,dl

        mov     al,dirmask              ; Current directory attribute mask
        test    flags,byhide            ;  include hidden files
        jz      swx1
        or      al,2
swx1:   test    flags,byattr            ;  include display of attributes?
        jz      swx2
        or      al,7
swx2:   test    flags2,bypath           ;  include sub-directories?
        jz      swx3
        or      al,16
swx3:   mov     byte ptr dirmask,al     ; Update dir search criteria
        ret

sw2:    lodsb
        cmp     al,'/'                  ; Another switch?
        loopne  sw2
        jcxz    switch_exit

sw2a:   lodsb                           ; yes, get letter following
        dec     cx
        jle     sw3                     ; missing switch

sw3:    cmp     al,'?'                  ; Help?
        jne     sw4
        mov     dx,offset help
        jmp     exit

sw4:    and     al,0dfh                 ; Make option uppercase
        cmp     al,'A'                  ; Attributes?
        jne     sw5
        or      dh,byattr
sw5:    cmp     al,'B'                  ; Brief?
        jne     sw6
        or      dl,bybrief
sw6:    cmp     al,'C'                  ; Clear?
        jne     sw7
        or      dh,byclear
        call    cls
sw7:    cmp     al,'D'                  ; Date?
        jne     sw8
        or      dh,bydate
sw8:    cmp     al,'X'                  ; Ext?
        jne     sw9
        or      dh,byext
sw9:    cmp     al,'P'                  ; Paths?
        jne     sw10
        or      dl,bypath
sw10:   cmp     al,'S'                  ; Size?
        jne     sw11
        or      dh,bysize
sw11:   cmp     al,'H'                  ; Hidden?
        jne     sw12
        or      dh,byhide
sw12:   cmp     al,'M'                  ; Modified only
        jne     sw12b
        or      dl,bymod
sw12b:  cmp     al,'W'                  ; Wait when screen fills?
        jne     sw12c
        or      dl,bywait
sw12c:  cmp     al,'F'                  ; Sort by filename?
        jne     sw12d
        or      dh,byname
sw12d:  cmp     al,'N'                  ; No sorting?
        jne     sw13
        and     dh,0ffh-byname-byext-bysize-bydate

sw13:   jmp     sw2                     ; Try for another option

switchs endp
        page
;
;       Copy command parameters

getparm proc    near
        mov     si,82h                  ; Command tail
        mov     di,offset parmdir       ; goes after d:\
        sub     cx,cx                   ;
        or      cl,byte ptr -2[si]      ; any parmeters?
        jz      parm9                   ; no, use defaults

parm0:  cmp     byte ptr 1[si],':'      ; Drive specified?
        jne     parm1                   ;  no
        mov     di,offset parmdr        ;  yes, replace drive letter

parm1:  lodsb                           ; Skip leading blanks
        cmp     al,' '                  ;
        jne     parm3                   ;
        loope   parm1                   ;
        jcxz    parm9                   ;

parm2:  lodsb                           ; Copy d:\path\fname.ext
parm3:  cmp     al,cr                   ; end of string?
        je      parm9                   ;
        cmp     al,' '                  ; end of operand?
        jbe     parm9                   ;
        cmp     al,','                  ;
        je      parm9                   ;
        stosb                           ;
        loop    parm2                   ;

parm9:  and     parmdr,0dfh             ; Upper case drive parm
        mov     drptr,di                ; Save ptr to parm end
        mov     al,0                    ; Insure asciiz
        stosb                           ;
        mov     al,parmdr               ;
        mov     origdr,al               ;
        mov     rootdir,al              ;
        ret                             ;
getparm endp
        page
;
;       Get volume label and disk free space

getvol  proc    near
        mov     dl,parmdr               ; Get drive letter
        sub     dl,64                   ; and make it a number
        mov     drivenr,dl              ;
        mov     si,offset origdir+1     ; Save current path name
        mov     ah,getpath              ;
        int     21h                     ;

        mov     al,0                    ; Get end of path name
        mov     di,offset origdir       ;
        mov     cx,67                   ;
        repne   scasb                   ;
        sub     di,2                    ;
        mov     origptr,di              ;

        cmp     origdir+1,0             ; Already in root?
        je      getvl3                  ; yes, no chdir needed
        inc     origptr                 ;
        mov     dx,offset rootdir       ; no, point to root directory
        mov     ah,chdir                ;
        int     21h                     ;
        jc      getvl9                  ;

getvl3: mov     dx,offset extfcb        ; Search for volume entry
        mov     ah,11h                  ;
        int     21h                     ;

        or      al,al                   ; Any found?
        jnz     getvl4                  ; no, tough
        mov     cx,11                   ; yes, copy it to heading
        mov     si,offset dta+8         ;
        mov     di,offset volname       ;
        repz    movsb                   ;

getvl4: cmp     origdir+1,0             ; Need to restore curdir?
        je      getvl9                  ;
        mov     dx,offset origdr        ; Back to current dir
        mov     ah,chdir                ;
        int     21h                     ;

getvl9:                                 ; Get disk free space
        mov     dl,parmdr               ; Current drive letter
        sub     dl,64                   ;  as a number
        mov     ah,dskspc               ; Get free space
        int     21h                     ;
        cmp     ax,0ffffh               ; Valid?
        je      getvl10                 ; no, skip it
        mul     cx                      ;  Bytes per cluster
        mul     bx                      ;  Total free
        mov     si,offset nrsize        ; Point to msg
        call    format                  ;
getvl10:
        ret                             ;
getvol  endp                            ;
        page
;
;       Set search criteria

setarg  proc    near
set1:   mov     bp,offset dta           ; First DTA
        mov     bx,offset search        ; Search arument entries

        mov     ax,word ptr parmdr      ; Set search drive
        lea     di,[bx].argdriv         ;
        stosw                           ;

;       parameter is possibly a filespec

set2:   cmp     byte ptr parmdir,'\'    ; path requested?
        je      set8                    ; yes, more than a filespec
        mov     cx,origptr              ; no, set search path
        mov     si,offset origdr+2      ;  from original path
        sub     cx,si                   ;
        jcxz    set2a                   ; In a subdir?
        rep     movsb                   ;
        mov     al,'\'                  ; Add filespec delimiter
        stosb                           ;

set2a:  mov     si,offset parmdir       ; Copy filespec
        mov     cx,drptr                ;  to search criteria
        sub     cx,si                   ;
        jcxz    set5                    ; None, append global filespec
        rep     movsb                   ;
        jmp     set7                    ; Done

;       parameter is possibly a path\filespec

set8:   mov     dx,offset parmdr        ; Requested d:\path\filespec
        mov     ah,chdir                ;
        int     21h                     ; Is parm a valid path name?
        jc      set4                    ;  no, have d:\path\filespec

set8a:  mov     dx,offset origdr        ; Restore path
        mov     ah,chdir                ;
        int     21h                     ;

        mov     si,offset parmdir       ; Set search d:\path
        mov     cx,drptr                ;  from command parameter
        sub     cx,si                   ;
        jcxz    set5                    ;
        rep     movsb                   ;
        jmp     set5                    ; Append global filespec

set4:   mov     si,offset parmdir       ; Set search d:\path
        mov     cx,drptr                ;  from command parameter
        sub     cx,si                   ;
set4a:  jcxz    set5                    ;
        rep     movsb                   ;
        jmp     set7                    ; Done

;       append global filespec to search criteria

set5:   mov     si,offset anyname       ; Add global search arg
        cmp     byte ptr -1[di],'\'     ; Already have delimiter?
        jne     set6                    ;
        dec     di                      ; yes, overlay it

set6:   mov     cx,15                   ; Length of global filespec
        push    di                      ; Save current end pointer
        rep     movsb                   ;
        pop     di                      ; Restore work reg
        jmp     set9                    ;

set7:   mov     al,0                    ; Append ASCIIZ stopper
        stosb                           ;

set9:   lea     si,[bx].argdriv         ; Copy d:\path\filespec
        mov     cx,di                   ;
        sub     cx,si                   ;
        mov     di,offset titles        ;  to titles
        rep     movsb                   ;
        mov     byte ptr [di],stopper   ; Add print stopper

set10:  ret                             ; Back to caller

setarg  endp
        page
;
;       Obtain directory table

alloc   proc    near
        push    bx                      ; save registers
        mov     bx,pgmsize              ; size of program module
        mov     ah,4ah                  ; modify memory
        int     21h                     ;
        mov     bx,-1                   ; ask for all remaining memory
alloc1:
        mov     ah,48h                  ; allocate memory
        int     21h                     ;
        jc      alloc1                  ; get what there is

        mov     segdir,ax               ; segment addr of table
        mov     nxtdir,0                ; offset to first entry

        shr     bx,1                    ; change number of paragraphs
        shr     bx,1                    ;  to 64-byte entries
        mov     maxdir,bx               ; maximum table count
        pop     bx                      ; restore registers
        ret                             ;
alloc   endp

        page
;
;       Build table of directory entries

getdir  proc    near
        push    bp                      ; Ptr to DTA
        push    bx                      ; Ptr to search are

; Set DTA for current nesting level
        mov     dx,bp                   ; Data transfer area
        mov     ah,setdta               ; Set local DTA
        int     21h

; Set search criteria for this level
        mov     dx,bx                   ; Search criteria
        sub     cx,cx                   ; Directory options
        mov     cl,dirmask
        mov     ah,4eh                  ; Find first matching entry
        int     21h

; Examine directory entry just returned
get1:   or      al,al
        jnz     gotdir                  ; Not found, quit looking.
        cmp     byte ptr [bp].dtaattr,10h
        jne     get3                    ; Is it a sub-dir?
        cmp     byte ptr [bp].dtaname,'.'
        je      get4                    ; May be <DIR> entry

        call    addfile                 ; Add directory entry to table

; Build parms for sub-dir search
        lea     dx,[bp].dtaname         ; Save ptr to found name
        lea     si,[bx].argdriv         ; Point to current arg
        add     bp,dta_len              ; Next DTA
        add     bx,arg_len              ; Next search arg

; Copy previous arg as next search arg
        mov     cx,64                   ; Maximum length
        lea     di,[bx].argdriv         ; Point to new search arg
get6:   lodsb                           ;
        cmp     al,'?'                  ; Used global name?
        je      get9                    ;  yes, single nest
        cmp     al,0                    ; End of dir name?
        je      get5                    ;  yes, append wild cards
        stosb                           ;
        loop    get6                    ; Continue copying

; Add sub-dir name to search arg
get9:   mov     si,dx                   ; Saved ptr to found name
get8:   lodsb                           ;
        cmp     al,0                    ; End of DIR name?
        je      get5                    ;  yes, append wild cards
        stosb                           ;  no, add to arg
        loop    get8                    ; Continue copying fname

get5:   mov     si,offset anyname       ; Append wild cards
        rep     movsb                   ;

        call    getdir                  ; Search the sub-dir
        sub     bx,arg_len              ; restore arg
        sub     bp,dta_len              ; and DTA

; Restore DTA to find next matching entry
        mov     dx,bp                   ; Data transfer area
        mov     ah,setdta               ; Set DTA
        int     21h                     ;
        jmp     short get4              ;

get3:   call    addfile                 ; Add the entry

get4:   mov     cx,12                   ; Clear found name
        mov     al,' '                  ;
        lea     di,[bp].dtaname         ; Point to file name area
        rep     stosb                   ;

        mov     ah,4fh                  ; Search for next file
        mov     dx,bp                   ;
        int     21h                     ;
        jmp     get1                    ; Loop for next one

gotdir: pop     bx                      ; Restore arg
        pop     bp                      ;  and DTA
        ret                             ; Return to caller
getdir  endp                            ;
        page
;
;       Add a directory entry to table

addfile proc    near
        save    <cx,bp,di,si>           ; Save registers

        mov     cx,tbl_len              ; Initialize table entry
    mov si,offset modelx    ;
        mov     di,offset pline         ;
        rep     movsb                   ;

        mov     al,[bp].dtaattr         ; Copy file attributes
        mov     pline.tblattr,al        ;
        test    flags2,bymod            ; Just modified files?
        jz      addfile1                ;  no, add all
        test    al,10h                  ;  yes, pass subdirs
        jnz     addfile1                ;
        test    al,20h                  ; is it modified tho?
        jnz     addfile1                ; yes, add the entry
        jmp     addfile11               ; no, exit

addfile1:
        call    getdate                 ; Format date
        call    gettime                 ;  time
        call    getsize                 ;  bytes

        lea     si,[bp].dtaname         ; Copy file name
        lea     di,pline.tblname        ;
        mov     cx,12                   ;
addfile2:
        lodsb                           ;
        cmp     al,0                    ;
        je      addfile5                ;
        cmp     al,'.'                  ; separate extension
        je      addfile3                ;
        stosb                           ;
        loop    addfile2                ;
addfile3:
        jcxz    addfile5                ;
        lea     di,pline.tblname+8      ;
addfile4:
        stosb                           ;
        lodsb                           ;
        cmp     al,0                    ;
        je      addfile5                ;
        loop    addfile4                ;

addfile5:
        lea     di,pline.tblpath        ; Copy path name
        lea     si,[bx].argname         ;
        mov     cx,size tblpath         ;
addfile6:
        cmp     byte ptr 1[si],'?'      ; Wildcard part?
        je      addfile7                ; yes, have name
        lodsb                           ;
        stosb                           ;
        loop    addfile6                ;

addfile7:
        lea     si,pline.tblname        ;
        test    pline.tblattr,10h       ; Subdirectory?
        jnz     addfile8                ;  yes, copy path name
        jcxz    addfile10               ;  no, pad with blanks
        mov     al,' '                  ;
        rep     stosb                   ;
        jmp     addfile10               ;

addfile8:
        cmp     cl,12                   ; Max subdir name length
        jbe     addfile9                ; to be copied
        mov     cl,12                   ;
addfile9:
        rep     movsb                   ;

addfile10:
        mov     cx,numdir               ; Number of table entries
        cmp     cx,maxdir               ; is table full?
        jae     addfile11               ;  yes, skip it
        push    es                      ;  no, add to table
        mov     ax,word ptr segdir      ; Segment addr of table
        add     ax,nxtdir               ; plus next entry offset
        sub     di,di                   ;
        mov     si,offset pline         ; copy table stuff
        mov     cx,tbl_len              ;
        mov     es,ax                   ; Set target segment
        rep     movsb                   ;
        pop     es                      ;

        inc     numdir                  ; Incr entry count
        add     nxtdir,tblpara          ; Bump offset for next table entry

addfile11:
        return  <si,di,bp,cx>           ; restore registers
addfile endp
        page
;
;       Print file information

print   proc    near
        cmp     numdir,1                ; Just one file?
        ja      printt                  ;  no, two up heading
        mov     word ptr titled,0a0dh   ;
        mov     titled+2,stopper        ;

printt:
        mov     dx,offset titlea        ; Top titles
        call    prints                  ;
        mov     dx,offset titleb        ; Subdir name
        call    prints                  ;
        mov     dx,offset titlec        ; Headings
        call    prints                  ;

        mov     ax,cs                   ; Set extra seg
        mov     es,ax                   ;  locally

        sub     ax,ax                   ; Offset to first table entry
        mov     nxtdir,ax               ;  as current entry
        mov     ax,numdir               ; Compute offset
        inc     ax                      ;  to second half
        shr     ax,1                    ;
        mov     cl,2                    ; Times 64 (4 paras each)
        shl     ax,cl                   ;
        mov     cx,numdir               ; Number of entries
        or      cx,cx                   ; Even number of entries?
        jz      print7                  ; yes, all set
        inc     cx                      ; no, just one
        shr     cx,1                    ; Set half of count

print_next:
        call    print0                  ; Left side
        push    [nxtdir]                ;
        add     nxtdir,ax               ;
        call    print0                  ; Right side
        pop     [nxtdir]                ;

        add     nxtdir,tblpara          ; Point to next entry

        test    flags2,bywait           ; Wait for screen full?
        jz      printw1                 ;  no, continue
        inc     linecnt                 ;  yes, bump line count
        mov     dl,_rows                ; rows available on screen
        dec     dl                      ; leave room for headings
        cmp     linecnt,dl              ; Full now?
        jb      printw1                 ;  no, continue
        call    waitkey                 ;  yes, get a key

printw1:
        loop    print_next              ; continue for all entries
print7:
        ret                             ; done, return
print   endp                            ;


print0  proc    near
        save    <ax,cx,di>              ; Save registers

        push    ds                      ; copy table entry to print line
        mov     cx,tbl_len              ; length of table entry
        sub     si,si                   ; offset to next entry
        mov     di,offset pline         ; offset to print line
        mov     ax,word ptr segdir      ; Segment addr for table
        add     ax,nxtdir               ;  plus paras offset
        mov     ds,ax                   ;   as source segment
        rep     movsb                   ;    for copy
        pop     ds                      ;

        inc     count                   ; Number of files
        mov     ax,count                ;
        cmp     ax,numdir               ; Done em all?
        jbe     print1                  ; no, display this one
        dec     count                   ; yes,
        jmp     print9                  ;  done, return

print1:
        mov     cl,byte ptr pline.tblattr
        lea     di,byte ptr pline.tblfatr

        test    cl,32                   ; Archived?
        jnz     print2                  ;
        mov     al,'A'                  ;
        stosb                           ;

print2: test    cl,4                    ; System?
        jz      print3                  ;
        mov     al,'S'                  ;
        stosb                           ;

print3: test    cl,2                    ; Hidden?
        jz      print4                  ;
        mov     al,'H'                  ;
        stosb                           ;

print4: test    cl,1                    ; Read only?
        jz      print5                  ;
        mov     al,'R'                  ;
        stosb                           ;

print5: test    cl,10h                  ; Sub directory?
        jz      print5a                 ; no
        lea     di,pline.tblfatr-1      ; yes, special display
        mov     si,offset subdir        ;
        mov     cx,4                    ;
        rep     movsb                   ;   for copying

print5a:
        test    flags2,bypath           ; Doing all paths?
        jz      print6                  ;
        cmp     pline.tblpath+1,'?'     ; Nested entry?
        je      print6                  ;  no, as-is
        mov     pline.tblfatr-1,'-'     ;  yes, flag it in display

print6: lea     si,pline.tbldate        ; Reformat date
        mov     di,offset olddate       ;   for copying
        mov     cx,8                    ;
        rep     movsb                   ;

        lea     di,pline.tbldate        ; Format date
        mov     ax,word ptr olddate+3   ;
        stosw                           ;
        mov     al,"/"                  ;
        stosb                           ;
        mov     ax,word ptr olddate+6   ;
        stosw                           ;
        mov     al,"/"                  ;
        stosb                           ;
        mov     ax,word ptr olddate     ;
        stosw                           ;

        cmp     pline.tblext,' '        ; Any extension?
        jne     print8                  ;  yes, leave the dot
        mov     pline.tblext-1,' '      ;  no, rid it

print8: lea     dx,pline.tblname        ;
        mov     byte ptr pline.tblctl,' '
        mov     byte ptr pline.tblctl+1,stopper
        mov     nrmsg+1,lf              ;
        test    count,1                 ; Right side?
        jnz     print8a                 ;
        mov     nrmsg+1,cr              ;
        mov     word ptr pline.tblctl,0a0dh
print8a:
        call    prints                  ; Display an entry

print9:
        return  <di,cx,ax>              ; Restore registers
print0  endp

        page
;
;       Pause at end of screen for any key

waitkey proc    near
        save    <ax,bx,cx,dx>           ; save work regs

        mov     linecnt,0               ; reset counter
        mov     ah,2                    ; Set cursor position
        mov     dl,0                    ;  col 72
        mov     dh,_rows                ;  last row
        add     dh,2                    ; adjust for heading
        sub     bx,bx
        int     10h

        mov     dx,offset more          ; ask for more
        call    prints                  ;

        mov     ah,0                    ; wait for a key
        int     16h                     ;

        mov     dx,offset backsp        ; backspace over 'more...'
        call    prints                  ;

        test    flags,byclear           ; Clear screen each time?
        jz      waitkeyc                ;  no
        call    cls                     ; Clear screen
waitkeyc:
        mov     dx,offset titlea        ; Top titles
        call    prints                  ;
        mov     dx,offset titleb        ; Subdir name
        call    prints                  ;
        mov     dx,offset titlec        ; Headings
        call    prints                  ;

waitkeyx:
        return  <dx,cx,bx,ax>           ; restore registers
waitkey endp
        page
;
;       Format the date

getdate proc    near                    ;Format the date
        mov     ax,word ptr [bp].dtadate
        mov     di,offset pline.tbldate
        or      ax,ax                   ;Is it zero?
        jz      gotdate
        push    ax                      ;Save date
        and     ax,mask yr              ;Get year part
        mov     cl,yr                   ;Bits to shift
        call    cnvrt
        or      al,'8'                  ;Adjust for base year
        stosw
        mov     al,'-'
        stosb

        pop     ax                      ;Get the date back
        push    ax                      ;Save it
        and     ax,mask mo              ;Get month part
        mov     cl,mo                   ;Bits to shift
        call    cnvrt2
        stosw
        mov     al,'/'
        stosb

        pop     ax                      ;Get the date back
        and     ax,mask dy              ;Get day part
        mov     cl,dy                   ;Bits to shift
        call    cnvrt
        stosw
gotdate:ret
getdate endp

        page
;
;       Format the time

gettime proc    near                    ; Format the date
        mov     ax,word ptr [bp].dtatime;
        lea     di,pline.tbltime        ;
        or      ax,ax                   ; It is zero?
        jz      gottime                 ;
        push    ax                      ; Save date
        and     ax,mask hour            ; Get hour part
        mov     cl,hour                 ; Bits to shift
        shr     ax,cl                   ;
        call    cnvrt1                  ;
        stosw                           ;
        mov     al,':'                  ;
        stosb                           ;

gt3:    pop     ax                      ; Get the time back
        and     ax,mask min             ; Get min part
        mov     cl,min                  ; Bits to shift
        call    cnvrt                   ;
        stosw                           ;
gottime:
        ret                             ;
gettime endp

cnvrt2  proc    near                    ; Convert to ASCII
        call    cnvrt                   ;
        cmp     al,'0'                  ; Suppress leading zero
        jne     cnvrtd                  ;
        mov     al,' '                  ;
        ret                             ;

cnvrt:  shr     ax,cl                   ;
cnvrt1: aam                             ; Make AL into BCD
        or      ax,'00'                 ; and to ASCII
        xchg    al,ah                   ;
cnvrtd: ret                             ;
cnvrt2  endp                            ;
        page
;
;       Format the size

getsize proc    near
        save    <bp,bx>                 ; Ptr to Dta entry
        mov     ax,word ptr [bp].dtasize;
        add     numbyte+2,ax            ;
        mov     dx,word ptr [bp].dtasize+2
        adc     numbyte,dx              ;
        lea     si,pline.tblsize        ; Target offset
        call    format                  ; Format double word
        return  <bx,bp>                 ;
getsize endp

        page
;
;       Format ASCII numbers from binary

format  proc    near                    ; Formats a 32 bit integer in DX:AX
        save    <bp,bx,di,si>           ; to DS:SI
        mov     ddptr,si                ; addr of target field
        mov     di,dx                   ; subroutine uses di:si
        mov     si,ax
        call    printdd
        return  <si,di,bx,bp>

printdd:
        xor     ax,ax           ;zero out the
        mov     bx,ax           ; working
        mov     bp,ax           ; registers.
        mov     cx,32           ;# bits of precision
j1:     shl     si,1
        rcl     di,1
        xchg    bp,ax
        call    j6
        xchg    bp,ax
        xchg    bx,ax
        call    j6
        xchg    bx,ax
        adc     al,0
        loop    j1
        mov     cx,1710h
        mov     ax,bx
        call    j2
        mov     ax,bp
j2:     push    ax
        mov     dl,ah
        call    j3
        pop     dx
j3:     mov     dh,dl
        shr     dl,1            ;move high
        shr     dl,1            ; nibble to
        shr     dl,1            ; the low
        shr     dl,1            ; position.
        call    j4
        mov     dl,dh
j4:     and     dl,0fh          ;mask low nibble
        jz      j5              ;if not zero
        sub     cl,cl
j5:     dec     ch
        and     cl,ch
        or      dl,'0'          ;fold in ascii zero
        sub     dl,cl
        mov     bx,ddptr
        mov     [bx],dl         ;ptr to next target field
        inc     ddptr
        ret

j6:     adc     al,al
        daa
        xchg    al,ah
        adc     al,al
        daa
        xchg    al,ah
        ret


format  endp
        page
;
;       Print String like INT 21H function 9

prints  proc    near                    ; DX has offset to string
        save    <si,bx,cx>              ;  ending in char x'FF'
        mov     si,dx                   ; Ptr to string text
        sub     cx,cx                   ; Overall text length
ps1:    lodsb
        cmp     al,stopper              ; Ending hex FF?
        je      ps9
        inc     cx
        jmp     short ps1

ps9:
        mov     bx,1                    ; Standard output device
        mov     ah,40h                  ;  to write to
        int     21h

        return  <cx,bx,si>              ; Recover registers
prints  endp


cls     proc    near                    ; Clear screen
        save    <ax,bx,cx,dx,si>

        mov     ax,600h                 ; Scroll all lines
        sub     cx,cx                   ;  upper left
        mov     dx,184fh                ;  lower right
        mov     bl,_page                ;  video page
        mov     bh,_attr                ;  attribute
        int     10h                     ; Video I/O

        mov     bl,_attr                ; Set cursor position
        mov     bh,_page
        sub     dx,dx                   ;  to 1,1
        mov     ah,2
        int     10h

        return  <si,dx,cx,bx,ax>        ; return to caller
cls     endp

        page
;
;       Format current date and time

clock   proc    near
        save    <ax,bx,cx,dx>           ; save work regs

        mov     ah,2ah                  ;Get date
        int     21h
        sub     cx,1900                 ; last two digits
        mov     ax,cx                   ;Make readable
        call    cnvrt1                  ;Convert to ASCII
        mov     word ptr year,ax
        xchg    al,dh                   ;Get month
        call    cnvrt1                  ;Convert to ASCII
        mov     word ptr month,ax
        xchg    al,dl                   ;Get day
        call    cnvrt1                  ;Convert to ASCII
        mov     word ptr day,ax

        mov     ah,2ch                  ;Get time
        int     21h
        xchg    al,ch                   ;Get hours
        call    cnvrt1                  ;Convert to ASCII
        mov     word ptr hours,ax
        xchg    al,cl                   ;Get minutes
        call    cnvrt1                  ;Convert to ASCII
        mov     word ptr mins,ax
        xchg    al,dh                   ;Get seconds
        call    cnvrt1                  ;Convert to ASCII
        mov     word ptr secs,ax

        return  <dx,cx,bx,ax>           ; return to caller
clock   endp

        page
;
;       Shell-Metzger Sort of 64-byte table records

;       Variables used for sort subroutine

numrec  dw      0                       ; Number of entries
loc     dw      0
index1  dw      0
incr    dw      0
limit   dw      0
index2  dw      0
ptr1    dw      0                       ;Offset to record Index1
ptr2    dw      0                       ;Offset to record Index2

keyptr  dw      0                       ;Offset to key
keylen  dw      0                       ;Length of key
base    dw      0                       ;Seg addr of array

sort    proc    near
        push    ds                      ; Save seg regs
        push    es

;       Set sort options

        sub     bx,bx                   ; Offset to key
        mov     bl,offset tblpath
        test    flags,bysize
        jz      sort1
        mov     bl,offset tblsize       ; Sort by size
sort1:  test    flags,bydate
        jz      sort2
        mov     bl,offset tbldate       ; Sort by date/time
sort2:  test    flags,byext
        jz      sort3
        mov     bl,offset tblext

;       Initialize sort parameters

sort3:  mov     cx,numdir               ; Number of entries
        mov     numrec,cx
        mov     keyptr,bx

        mov     dx,64                   ; Length of key
        sub     dx,bx
        mov     keylen,dx
        mov     es,segdir               ; Seg addr of table
        mov     ax,es                   ; Save array addr
        sub     ax,4                    ;  adjust for indexing
        mov     base,ax

;       Sort 64-byte entries

        mov     loc,cx                  ; Loc=NumRecs

check:  cmp     loc,1                   ; IF Loc<=1 THEN
        jg      check1                  ; GOTO SORTED
        jmp     sorted

check1: mov     ax,loc
        sar     ax,1                    ; Loc=2*(Loc/4)+1
        or      ax,1
        mov     loc,ax

        mov     ax,numrec               ; Limit=NumRec-Loc
        sub     ax,loc
        mov     limit,ax

        mov     incr,0                  ; Incr=0

again:  inc     incr                    ; Incr=Incr+1

        mov     ax,incr                 ; IF Incr>Limit THEN GOTO CHECK
        cmp     ax,limit
        jg      check

        shl     ax,1
        shl     ax,1
        mov     index1,ax               ; Index1=Incr

        mov     index2,ax               ; Index2=Index1+Loc
        mov     ax,loc
        shl     ax,1                    ; times 2
        shl     ax,1                    ; times 4
        add     index2,ax

comp:   mov     ax,index1               ; IF array(Index1)<=array(Index2)
        add     ax,base
        mov     es,ax
        mov     ptr1,ax
        mov     di,keyptr

        mov     ax,index2               ; THEN GOTO AGAIN
        add     ax,base
        mov     ptr2,ax
        mov     si,keyptr
        mov     cx,cs:keylen

        push    ds
        mov     ds,ax
        repe    cmpsb
        pop     ds
        jae     again

swap:   mov     bx,ptr1                 ; ELSE
        mov     dx,ptr2                 ; TEMP=array(Index1)

        mov     ax,cs
        mov     es,ax
        mov     di,offset temp
        mov     cx,tbl_len

        mov     ds,bx
        sub     si,si
        rep     movsb

        mov     es,bx                   ; array(Index1)=array(Index2)
        mov     ds,dx
        sub     di,di
        sub     si,si
        mov     cx,tbl_len
        rep     movsb

        mov     ax,cs                   ; array(Index2)=TEMP
        mov     ds,ax
        mov     si,offset temp
        mov     es,dx
        sub     di,di
        mov     cx,tbl_len
        rep     movsb

        mov     ax,index1               ; Index2=Index1
        mov     index2,ax

        mov     ax,loc                  ; Index1=Index1-Loc
        shl     ax,1
        shl     ax,1
        sub     index1,ax

        jg      comp                    ; IF Index1>0 then GOTO COMP
        jmp     again                   ; ELSE GOTO AGAIN

sorted: pop     es                      ;Recover register
        pop     ds
        ret
sort    endp

        page
;
;       Usage information, display panel

help    db      cr,lf,tab,tab,' -- LDIR Version 4.1 --- 25 Jan 89 --'
        db      cr,lf,tab,tab,'  (c) Copyright Vernon D. Buerg 1986-89'
        db      cr,lf
        db      cr,lf,tab,tab,tab,' Command syntax:'
        db      cr,lf
        db      cr,lf,tab,tab,'LDIR [d:][\path[fname[.ext]]] /?.../?'
        db      cr,lf,tab,tab
        db      cr,lf,tab,tab,'    /A   include Attributes'
        db      cr,lf,tab,tab,'    /C   Clear screen first'
        db      cr,lf,tab,tab,'    /D   sort by Date'
;       Db      CR,LF,TAB,TAB,'    /F   sort by Filename (default)'
        db      cr,lf,tab,tab,'    /H   include Hidden files'
        db      cr,lf,tab,tab,'    /M   only Modified files'
        db      cr,lf,tab,tab,'    /N   No sorting'
        db      cr,lf,tab,tab,'    /P   include all Paths'
        db      cr,lf,tab,tab,'    /S   sort by Size'
        db      cr,lf,tab,tab,'    /W   Wait after screen full'
        db      cr,lf,tab,tab,'    /X   sort by eXtension'
        db      stopper
;       Db      CR,LF,TAB,TAB,'    /?   display usage syntax',Stopper


;
;       Data structures and work areas


parmdr          db      ' :'                            ; Drive letter
parmdir         equ     $                               ; for specific path

pline           equ     parmdir+64                      ; Print line

dta             equ     pline+tbl_len                   ; Disk transfer areas

search          equ     dta + (depth+1)*dta_len         ; Search arguments

local_stack     equ     search + (depth+1)*arg_len      ; Local stack
local_stack_end equ     local_stack+512

workend         equ     local_stack_end + 2             ; End of work areas

pgmsize         equ     (workend - cseg + 512) /16      ; Size of program

cseg            ends
                end     ldir
