          title DISKDIRL  -  DISKette DIRectory List    Ver 1.0
          page  85,132
;
code      segment
          assume   cs:code,ds:code
;
          org   100h
begin:    jmp   starther
;
version   db    0                  ;DOS Version flag
defdrv    db    0                  ;Default drive address at entry
numdrv    db    0                  ;Number of drives in the system
drivemsg  db    13,10,'Enter drive to be listed  (Esc to end): $'
errmsg1   db    13,10,'Invalid drive letter$'
setprt    db    27,'0',27,'C',44,15,0  ;Set 1/8" 132 ch 44 ln condensed
restprt   db    27,64,0            ;Restore printer to power up status
restr     db    12,0               ;Printer "restore" forms command
titlelen  equ   44                 ;Length of user inputed title line
titlemsg  db    13,10,'Enter 44 char title: $'
titlebuf  db    titlelen+1,0       ;Input buffer for user title
          db    titlelen+1 dup (?)
title1    db    6  dup (' ')       ;First title line
titlelne  db    49 dup (' ')       ;Where user supplied title input goes
          db    'Free: '
freespc   db    10 dup (' ')       ;Formatted free space in title line
month     db    '  /'              ;Date fields in title line
day       db    '  /'
year      db    '  ',0             ;End of title
f10000    dw    10000              ;Conversion constants
f1000     dw    1000,100,10
extfcb    db    0ffh,5 dup (0),16h ;Extended FCB for read all entries
          db    0,11 dup('?'),24 dup (?)
stacknum  dw    0                  ;Count of entries in stack
entryprt  db    0                  ;Number of entries printed
lnecnt    db    0                  ;Line count of current entry
work      db    7 dup (0),' ',0    ;Work buffer for file size
leftbdr   db    '| ',0             ;Left border
rightbdr  db    ' |'               ;*  Right border (includes CRLF)
crlf      db    13,10,0            ;*  Carriage return + line feed
          page
starther  proc  near
          mov   dx,offset setprt
          call  prtstrng           ;Set printer with required options
          mov   ah,30h
          int   21h                ;Check DOS Version
          or    al,al
          jz    notver2            ;Version 1.x
          dec   al                 ;Version 2.x
notver2:  mov   version,al         ;Save DOS version flag
          mov   ah,19h
          int   21h                ;Get default drive
          mov   defdrv,al          ;Save it
          mov   dl,al
          mov   ah,0eh
          int   21h                ;Get number of drives
          mov   numdrv,al          ;Save it
;
;         Start of main loop
;
mainloop: mov   dx,offset drivemsg
          mov   ah,9
          int   21h                ;Prompt for drive for directory read
          mov   ah,1
          int   21h                ;Get user response
          cmp   al,1bh             ;Check for exit (Esc char)
          je    finished           ;We're done, so end program
          or    al,' '             ;Force lower case
          sub   al,'a'-1           ;Compute drive number (A = 0)
          jnc   drvok              ;Check for valid drive
msgerror: mov   dx,offset errmsg1  ;Invalid drive message
          mov   ah,9
          int   21h                ;Put out error message
          jmp   mainloop
finished: mov   dl,12
          mov   ah,2
          int   21h                ;Force page restore
          mov   dl,defdrv          ;Load default drive at entry
          mov   ah,0eh
          int   21h                ;Restore default drive
          test  entryprt,1         ;Were any entries printed ?
          jz    norest             ;Skip if not
          mov   dx,offset crlf
          call  prtstrng           ;Restore page
norest:   mov   dx,offset restprt
          call  prtstrng           ;Reset printer
          int   20h                ;Exit program
drvok:    cmp   al,numdrv          ;Check for installed drive
          ja    msgerror           ;Drive not installed
          dec   al
          mov   dl,al
          mov   ah,0eh
          int   21h                ;Make selected drive default
;
;         Get Title routine
;
          mov   di,offset title1
          mov   cx,55              ;Length to clear
          mov   al,' '
          rep   stosb              ;Clear title line
          mov   dx,offset titlemsg
          mov   ah,9
          int   21h                ;Prompt for title
          mov   dx,offset titlebuf
          mov   ah,0ah
          int   21h                ;Get title from user
          mov   cl,titlebuf+1      ;Load length
          xor   ch,ch
          mov   si,offset titlebuf+2
          mov   di,offset titlelne
          rep   movsb              ;Move user title to title line
          mov   ah,2ah
          int   21h                ;Get today's date
          sub   cx,1900            ;Convert to two digit year
          mov   di,offset year
          mov   al,cl
          call  decimal            ;Fill in month/day/year on title line
          mov   di,offset month
          mov   al,dh
          call  decimal
          mov   di,offset day
          mov   al,dl
          call  decimal
;
;         Get Free Space routine
;
          test  version,1          ;DOS version 2.x will supply that
          jz    vers1x
          xor   dl,dl              ;Set for default drive
          mov   ah,36h
          int   21h                ;Ver 2.x - get free space from DOS
          mul   bx
          mul   cx                 ;AX,DX contains bytes free
          jmp   vers1x4            ;Enter common code
vers1x:   push  ds
          mov   ah,1bh
          int   21h                ;Ver 1.x - get FAT
          xor   ah,ah
          xchg  cx,dx              ;CX has number of units
          mul   dx                 ;Bytes/allocation unit
          push  ax                 ;Save
          xor   ax,ax
          mov   si,2               ;First FAT entry
vers1x1:  mov   di,si
          shr   di,1
          add   di,si              ;Compute 1 1/2 bytes
          mov   di,word ptr [bx+di]  ;Load FAT entry
          test  si,1               ;See if odd or even
          jz    vers1x2
          push  cx
          mov   cl,4
          shr   di,cl              ;Adjust for 12 bits
          pop   cx
vers1x2:  and   di,0fffh           ;Three nibbles
          jnz   vers1x3            ;In use, so don't count
          inc   ax
vers1x3:  inc   si                 ;Step to next entry
          loop  vers1x1            ;Loop through FAT
          pop   cx                 ;Restore bytes/allocation unit
          mul   cx                 ;Compute total free bytes
          pop   ds                 ;Restore program seg reg
vers1x4:  mov   di,offset freespc  ;Point to output area
          call  convert            ;Convert size to ASCII
;
;         Load Directory Entries routine
;
          mov   di,offset ptrtbl
          xor   ax,ax
          mov   cx,121
          rep   stosw              ;Clear pointer table
          mov   bx,offset ptrtbl   ;BX points to start of pointer list
          mov   di,offset entries  ;DI points to start of entry stack
          xor   cx,cx
          mov   dx,offset extfcb
          mov   ah,11h
nxdirent: int   21h                ;Get next entry
          or    al,al
          jnz   sortdir
          call  saventry           ;Stack entry
          inc   cx                 ;Count entry
          mov   dx,offset extfcb
          mov   ah,12h
          jmp   nxdirent
;
;         Sort Directory Entries routine
;
sortdir:  mov   stacknum,cx        ;Save entry count
          dec   cx
          mov   si,offset ptrtbl   ;Point to first stack entry ptr
sortdir1: mov   di,si
          add   di,2               ;Set to "next" pointer
          mov   dx,cx
          push  cx
sortdir2: push  si                 ;This compare forces short strings
          push  di                 ;     low since they end with nuls
          mov   cx,12              ;Max compare allowed
          mov   si,word ptr [si]   ;Point to entry
          mov   di,word ptr [di]   ;Point to other entry
          rep   cmpsb              ;Compare strings
          pop   di
          pop   si
          jbe   sortdir3           ;Ascending sequence, so no change
          mov   ax,word ptr [si]
          xchg  ax,word ptr [di]
          mov   word ptr [si],ax   ;Exchange pointers
sortdir3: add   di,2
          dec   dl
          jnz   sortdir2           ;Bubble through inner loop
          pop   cx
          add   si,2
          loop  sortdir1           ;Bubble through outer loop
;
;         Have Listing Produced routine
;
          inc   entryprt           ;Count numbers of prints
          mov   ax,stacknum        ;Load entry count
          add   ax,2               ;Round up before divide
          mov   dh,3               ;Divide by num of entrys per line
          div   dh
          cbw
          push  ax                 ;Entries per column count
          mov   cx,33              ;Set body default line count
          cmp   ax,cx              ;See if number of lines to print
          jl    prtent2            ;    is greater than the default
          mov   cx,ax
prtent2:  mov   lnecnt,cl          ;Set new body line count
          call  prtbordr           ;Do upper border
          call  blanklne           ;Do a blank line
          call  prtlbdr            ;Do left margin
          mov   dx,offset title1
          call  prtstrng           ;Output the title line
          mov   cx,6               ;Add 6 extra blanks after date
          call  prtblks
          call  prtrbdr            ;Do right margin
          call  blanklne           ;Another blank line
          pop   cx
          mov   bp,cx
          shl   bp,1               ;BP has offset/col in ptr list
          mov   si,offset ptrtbl   ;Point to start of ptr list
prtent3:  call  prtlbdr            ;Do a left margin
          mov   dl,3               ;Set inner loop count to columns
          xor   bx,bx              ;Clear column offset reg
prtent4:  call  prtentry           ;Print stack entry
          add   bx,bp              ;Step to next column entry
          dec   dl
          jnz   prtent4            ;End of inner loop
          call  prtrbdr            ;Do a right margin
          add   si,2               ;Step to next ptr
          dec   lnecnt             ;Decrement body line count
          loop  prtent3            ;End of outer loop
          mov   cl,lnecnt          ;Load remaining body lines
          xor   ch,ch
          jcxz  prtent6            ;All used
prtent5:  call  blanklne           ;Fill out body lines
          loop  prtent5
prtent6:  call  prtbordr           ;Do bottom border
          mov   dx,offset restr
          call  prtstrng           ;Restore page
          jmp   mainloop
starther  endp
          page
decimal   proc  near               ;Converts AL to two decimal digits
          aam                      ;Store it at SI
          or    ax,'00'
          xchg  al,ah
          stosw                    ;Save in image
          ret
decimal   endp
;
convert   proc  near               ;Convert 6 digits, zero surpressed
          push  di                 ;Save pointer for later use
          div   f10000             ;Result range 0-99
          aam
          or    ax,'00'            ;Make ASCII
          xchg  ah,al
          stosw                    ;Place in image
          mov   cx,3               ;Convert last four digits
          mov   si,offset f1000
divloop:  mov   ax,dx              ;Remainder becomes dividend
          xor   dx,dx
          div   word ptr [si]      ;Power of 10 divide
          or    al,'0'             ;Result range 0-9
          stosb
          add   si,2
          loop  divloop
          or    dl,'0'             ;Last digit in remainder
          mov   al,dl
          stosb
          mov   cx,5               ;Now zero surpress 5 digits
          pop   di
          mov   al,' '
padloop:  cmp   byte ptr [di],'0'
          jnz   cnvtret            ;Conversion complete
          stosb                    ;Replace leading zero with blank
          loop  padloop
cnvtret:  ret
convert   endp
;
saventry  proc  near
          push  cx
          mov   word ptr [bx],di   ;Save pointer to start of entry
          add   bx,2               ;Step pointer table reg
          push  di                 ;Save DI for now
          mov   di,81h+7+8         ;Point pass end of DTA - file name
          mov   cx,8
savcmpfn: dec   di
          cmp   byte ptr [di],' '  ;Look for last non-blank in name
          loope savcmpfn
          add   cx,1               ;Compensate for LOOPE
          mov   si,81h+7           ;Point to beginning DTA - file name
          pop   di
          rep   movsb              ;Copy DTA - file name to 'entries:'
          mov   si,89h+7           ;Point to DTA type field
          cmp   byte ptr [si],' '
          jz    savend             ;No file type
          mov   byte ptr [di],'.'
          inc   di
          mov   cx,3
          rep   movsb              ;Move type field to stack
savend:   mov   byte ptr [di],0    ;Mark end of string
          inc   di
          mov   si,9dh+7           ;Point to size of file
          mov   cx,4
          rep   movsb              ;And save in stack
          mov   si,99h+7           ;Point to last update date
          movsw                    ;Save it in the stack
          pop   cx
          ret
saventry  endp
;
prtstrng  proc  near               ;This sub prints the string pointed
          push  dx                 ;     to by the DX reg on entry.
          push  si                 ;     The string is terminated by
          mov   si,dx              ;     a nul byte.
          mov   ah,5
prtsloop: mov   dl,byte ptr [si]
          or    dl,dl
          jz    prtsend
          int   21h
          inc   si
          jmp   prtsloop
prtsend:  pop   si
          pop   dx
          ret
prtstrng  endp
;
prtentry  proc  near               ;Print one stack entry
          push  bx
          push  cx
          push  dx
          mov   cx,12
          mov   di,word ptr [si+bx]  ;DI points to stack entry
          or    di,di
          jz    prtenty4
          mov   ah,5
prtenty1: mov   dl,byte ptr [di]   ;Print to the end of the name/type
          or    dl,dl              ;    entry blanking remainder of 12
          jz    prtenty5           ;    character field
          int   21h
          inc   di
          loop  prtenty1
prtenty2: inc   di
          mov   ax,word ptr[di]    ;Load file size
          mov   dx,word ptr[di+2]
          mov   bx,word ptr[di+4]  ;Load file last update date
          push  si
          mov   di,offset work
          call  convert            ;Convert size to ASCII decimal
          pop   si
          mov   dx,offset work
          call  prtstrng           ;One blank between fields
          mov   ax,bx              ;Save last update date data
          mov   cl,1+8             ;Shift year in bottom part of reg
          shr   ax,cl
          add   ax,80              ;Format in year-1980, correct for it
          mov   di,offset year
          call  decimal            ;Make year printable
          mov   ax,bx              ;Now do month
          mov   cl,5
          shr   ax,cl
          and   ax,000fh           ;Reg now has month in it
          mov   di,offset month
          call  decimal
          mov   ax,bx              ;Finally do day
          and   ax,001fh
          mov   di,offset day
          call  decimal
          mov   dx,offset month-1  ;Use date starting with a blank
          call  prtstrng           ;Go print it
prtenty3: pop   dx
          push  dx                 ;Reload entry value
          dec   dl
          jz    prteend            ;If last column, don't space over
          mov   cx,2
          call  prtblks            ;Two blanks between columns
prteend:  pop   dx
          pop   cx
          pop   bx
          ret
prtenty4: mov   cx,27              ;No entry, so blank entire column
          call  prtblks
          jmp   prtenty3
prtenty5: call  prtblks            ;Blanks remainder of name/type field
          jmp   prtenty2
prtentry  endp
;
blanklne  proc  near
          push  cx
          call  prtlbdr            ;Output a bordered blank line
          mov   cx,85
          call  prtblks            ;Go clear line
          call  prtrbdr            ;Print right margin
          pop   cx
          ret
blanklne  endp
;
prtbordr  proc  near
          mov   cx,89
          mov   dl,'-'
          call  prtblk1            ;Output a top or bottom border
          mov   dx,offset crlf
          call  prtstrng
          ret
prtbordr  endp
;
prtlbdr   proc  near               ;Outputs "|  "
          push  dx
          mov   dx,offset leftbdr
          call  prtstrng
          pop   dx
          ret
prtlbdr   endp
;
prtrbdr   proc  near               ;Outputs "  |CRLF"
          push  dx
          mov   dx,offset rightbdr
          call  prtstrng
          pop   dx
          ret
prtrbdr   endp
;
prtblks   proc  near               ;Outputs CX blanks to the printer
          mov   dl,' '
prtblk1:  mov   ah,5               ;Outputs DL char CX times
prtbloop: int   21h
          loop  prtbloop
          ret
prtblks   endp
;
ptrtbl    dw    0                  ;Pointer list
entries   equ   ptrtbl+121*2       ;Start of entry stack
;
code      ends
;
          end   begin
