    TITLE   SDIR - SORTED DIRECTORY COMMAND, Version 2.41
    PAGE    64,132                            ; DEC 1983
COMMENT |
    SDIR [d:][path][filename[.ext]] [options]                           2.41
     [filespec] same as for DIR command
 
     [options] * /A - List without hidden files
               * /E - With screen erase.
               * /P - No pause when screen full.
                 /X - Sort by extension.
                 /S - Sort by size.
                 /D - Sort by date/time.
                 /N - Do not sort, original order.
 
       Default = *.* sorted by name.ext without screen erase and
                     with pause when screen is full
       * - Option may be combined with other options.
 
   This source file was created from an object file obtained
 from Gene Plantz's BBS in Chicago. The original file name
 was SD.HEX.  I then used DEBUG and CAPTURE to get the first
 dis-assembly which  was then edited with WORDSTAR to create
 a source that when assembled using MASM would duplicate the
 original object file.
   Comments have been added and I do hope they are helpful.
 I have made several modifications to the first version and
 am continuing to add comments.  This source file is an
 excellent example for anyone wishing to learn 8086/8088
 assembly language.  Use at your own risk and feel free to
 share this file with your friends.
   I certainly wish that John Chapman would publish his
 source file.  His comments are sure to be more meaningful
 than mine could ever be.  Some of the conversion routines
 are very elegant, but difficult to understand.  As far as
 I'm concerned, PRINTDD is magic.
   Several modifications have been made.  They are:
 
        1. Filespecs are processed like DIR does.
        2. No sort option was added. /N
        3. Pause when screen full option added. /P
    4. Number of files found is printed.
 
                                    Ted Reuss
                                    Houston, TX
 
    SDIR Version 2.2  The GETFREE Subroutine was updated for DOS 2.0
    April 1, 1983   by   Jack Y. Fong
    Changes are denoted by "JYF" at the end of changed lines.


        SDIR Version 2.3:
        Added display of current directory name and volume name
                in header lines for DOS Release 2.0.
        Added total of file sizes for the displayed files.
                This is helpful in DOS 2.0 since it allows you to determine
                the number of bytes used by all the files in a root or 
                subdirectory (or used by a specified subset of 
                the files in a root or subdirectory).

        John F. Ratti   29 June, 1983
        Changes are denoted by "2.3" at the end of the changed lines.

        SDIR Version 2.4:
        Added display of attribute byte. The column 'ATR' will display
                up to 4 attribute codes. The codes are as follows:
                A - Archive bit is off (file has been archived)
                H - Hidden bit is on (file is hidden)
                R - Read-only bit is on (file is read-only)
                S - System bit is on (file is a system file)
        Corrected display of hidden, system, read-only and directory
                entries when /a option is specified.
        Added pathname capability. Any pathname that DIR will accept
                will work. The code should have been rewritten, rather than
                modified. The resulting code is somewhat obtuse, and I 
                apologize for it. I plan to write a proper pathname
                parser. When I do, I'll incorporate it into SDIR.
        This version MUST be used under MS-DOS release 2.0.
        John F. Ratti   03 July, 1983
        Changes are denoted by "2.4" at the end of the changed lines.
 
	SDIR Version 2.41:
	Changed the default options so that /A,/P and /E are on.
	That is, all files (system, hidden, directories, etc.) are
	shown, when the screen is full it pauses automatically, and
	when SDIR is called the screen does not automatically clear
	first.
	For those with color monitors, if /E is called the screen
	attribute is preserved so that the color is not reset to
	white.
	Also added to the header of the directory listing that
	SDIR returns is a list of the available switches.
	R. Frazier	12 Dec., 1983
	Changes are denoted by "2.41" at the end of the changed lines.
|
 
    SUBTTL  EQUATES & STRUCTURES
    PAGE
IF1
DOSCALL MACRO       FUNC,PARM1
.xcref
F_C =       FUNC
IFNB <PARM1>
IF F_C EQ 2 OR (F_C GE 4 AND F_C LE 6) OR F_C EQ 14 OR F_C EQ 46
    MOV     DL,PARM1
ELSE
    MOV     DX,OFFSET PARM1
ENDIF
ENDIF
        MOV     AH,FUNC
        INT     21H
.cref
        ENDM
ENDIF
.SALL   ;supress all macro expansions
;       PC-DOS INTERRUPT 21H FUNCTION CODES
;
@CHROUT EQU     2       ;display char in DL
@KEYIN  EQU     8       ;kybd input w/o echo
@STROUT EQU     9       ;print string terminated with $
@CKEYIN EQU     12      ;clr kybd bufr & do inp.func in AL
@OPEN   EQU     15      ;open XFCB                                      2.4
@SRCH1  EQU     17      ;search for first dir entry
@SRCH2  EQU     18      ;search for next dir entry
@GETDSK EQU     25      ;get default disk drive
@SETDTA EQU     26      ;set disk transfer addr
@FATAD2 EQU     28      ;get FAT of drive # in DL
@PARSEF EQU     41      ;parse filename
@GETDTE EQU     42      ;get system date
@GETTME EQU     44      ;get system time
@GETVER EQU     30H     ;get version number                             JYF
@CTLBRK EQU     33H     ;get/set ctrl/break checking                    2.4
@DSKFSP EQU     36H     ;get disk free space                            JYF
@CHDIR  EQU     3BH     ;change directory                               2.4
@CHMOD  EQU     43H     ;change/get file mode                           2.4
@GETCD  EQU     47H     ;get current directory                          2.3
@FIND1  EQU     4EH     ;find first dir. entry (DOS 2.0)                2.4
@FIND2  EQU     4FH     ;find next dir entry (DOS 2.0)                  2.4
 
CR  EQU     0DH     ;carriage return
LF  EQU     0AH     ;line feed
FCB_1       EQU     5CH     ;fcb for parameter 1
PARAM_L EQU 80H     ;# characters in PARAM_B
PARAM_B EQU 81H     ;DOS cmd parameter buffer.
 
; PC-DOS packed date   <yyyyyyym mmmddddd>
P_DTE       RECORD  P_YR:7,P_MO:4,P_DY:5
; PC-DOS packed time   <hhhhhmmm mmmsssss>
P_TME       RECORD  P_HR:5,P_MI:6,P_2S:5
 
DIR      RECORD  P_YR:7,P_MO:4,P_DY:5
; PC-DOS packed time   <hhhhhmmm mmmsssss>
P_TME       RECORD  P_HR:5,P_MI:6,P_2S:5
 
DIRNTRY STRUC               ;directory entry structure
LNK DW      0       ;ptr to next entry
NAM DB      8 DUP(0),'.' ;filename
EXT DB      3 DUP(0) ;extension
TME DW      0       ;time
DTE     DW      0       ;date
SZL     DW      0       ;low word of size
SZH     DW      0       ;high word of size
ATR     DB      0       ;attribute byte                                 2.4
DIRNTRY ENDS
 
        SUBTTL  DATA AREA & INITIALIZATION
        PAGE
SDIR    SEGMENT PUBLIC 'CODE'
        ASSUME  CS:SDIR,DS:SDIR,ES:SDIR
        ORG     100H
MAIN    PROC    FAR
        JMP     STARTS
 
DIRLNK  DW      DIRBUF  ;ptr to next opening in DIRBUF
C1LNK   DW      0       ;ptr to row 1, column 1
C2LNK   DW      0       ;ptr to row 1, column 2
NBRFILS DW      0       ;# of files or detail lines
SRTFLG  DB      0       ;if = 0 then sort else no sort
CLSFLG      DB      0       ;if = 0 then clear screen
EXTFLG      DB      0       ;if <> 0 then sort by ext
SIZFLG      DB      0       ;if <> 0 then sort by size
DTEFLG      DB      0       ;if <> 0 then sort by date/time
PSEFLG      DB      0       ;if <> 0 then pause if screen full
LPERSCR EQU 25      ;Lines per screen
LINCNT      DB      LPERSCR-5 ;Number of lines left
PSEMSG      DB      'Strike a key when ready . . . $'
 
HDNG1       DB      'SDIR: Sorted DIRectory listing, Version 2.41 ';   2.3
            DB      'Volume '            ;                             2.3
VOLNAME     DB      '             ' ;                                  2.3
D_MM        DW      '00'            ;Month
    DB      '/'
D_DD        DW      '00'            ;Day
    DB      '/'
D_YY        DW      '00'            ;Year
    DB      ' '
T_HH        DW      '00'            ;Hours
    DB      ':'
T_MM        DW      '00'            ;Minutes
            DB      CR,LF,'$'       ;                                  2.3
HDNG2       DB      7 DUP(' ')      ;                                  2.41
            DB      'Switches: /AEXPSDN' ;                             2.41
            DB      8 DUP(' ')      ;                                  2.41
            DB      'Directory of ' ;                                  2.3
HDRVE       DB      '@:'            ;                                  2.3
DIRNAME     DB      '\',69 DUP(0)       ; directory name                2.4
            DB      LF,LF,CR,'$'    ;                                  2.3
CRLF    DB      CR,LF,'$'
LFLFCR  DB      LF,LF,CR,'$'            ;literal for a blank line       2.3
HDNG3   DB      'FILESPEC.EXT  BYTES ATR -LAST  CHANGE-$'
        DB      8 DUP(' ')
SPACES  DB      '$'
HDNG4   DB      ' Bytes in $'                   ;                       2.3
HDNG5   DB      ' File(s); ','$'                ;                       2.4
HDNG6   DB      ' bytes free.',CR,LF,'$'        ;                       2.4
 
        SUBTTL  DISK TRANSFER AREA & FREE SPACE ENTRY DEFS
        PAGE

        DB      'PATH NAME='
PATHD   DB      '@:'
PATH    DB      64 DUP(0)       ;hold area for pathname                 2.4
 
XFCB    DB      -1,7 DUP(0),11 DUP('?'),25 DUP(0)
ATTRIB  EQU     XFCB+6          ;file attribute
DRVNUM  EQU     ATTRIB+1        ;drive # (1=A, 2=B, etc.)
 
DTA     DB      40 DUP(0)       ;Disk Transfer Area used
XNAME   EQU     DTA+8           ;volume name                            2.4
FILNAME EQU     DTA+30          ;by FIND for the                        2.4
FILTIME EQU     DTA+22          ;directory search.                      2.4
FILSIZE EQU     DTA+26          ;                                       2.4
FILATTR EQU     DTA+21          ;(file attribute byte)                  2.4
 
FREESPC DW      0               ;Free space entry.
        DB      '*FREE SPACE*',4 DUP(0)
LOSIZE  DW      0               ;of free space
HISIZE  DW      0               ;of free space

LOTOTAL DW      0               ;total size of all files: low word      2.3
HITOTAL DW      0               ;total size of all files: high word     2.3

PATHLEN DW      0               ;length of subdirectories in pathname   2.4
HOLDATR DB      0               ;hold area for XFCB attribute byte      2.3
HOLDLEN DW      0               ;hold area for length of directory name 2.3

DBLCNT  DW      0               ;current offset into DOUBLE             2.4
DOUBLE  DB      8 DUP(' '),'$'  ;destination for PRINTDX                2.4

PRTATR  DB      5 DUP(' '),'$'  ;area to build ATR for printing         2.4
INITATR DB      5 DUP(' ')      ;used to re-init ATR                    2.4

PRTDIR  DB      '  <DIR>$'      ;literal used to denote directories     2.4
ALLF    DB      '*.*'           ;literal for displaying all files       2.4
PATHALL DB      '\*.*',0        ;same as above for subdirectories       2.4

DRVNBR  DB      0               ;place to hold working drive number     2.4 

CURDSK  DB      '@:\'           ;hold area for current dirname so we can2.4
CURDIR  DB      64 DUP(0)       ;restore it later                       2.4

PATHNF  DB      'Path not found$' ;error message literal                2.4

        SUBTTL  MAIN PROGRAM SECTION
        PAGE
STARTS:
        PUSH    DS              ;Set up the
        XOR     AX,AX           ; stack for a
        PUSH    AX              ; return to DOS.
        CALL    GETARGS         ;Process arguments
        CALL    SRCHDIR         ;Search directory
        CMP     SRTFLG,0        ;Check if any sort
        JZ      A1              ; option selected.
        CALL    LNKDIRB         ;Leave in original
        JMP     SHORT A2        ; directory order.
A1:     CALL    SRTDIRB         ;Sort by major key
A2:     CALL    GETFREE         ;Get free space
        CALL    SPLTLST         ;Set up for 2 columns
        CALL    PRTHDNG         ;Print headings
        CALL    PRTDRVR         ;Print detail lines
        CALL    PRTNFLS         ;Print byte total and # of files        2.3
        RET                     ;Return to DOS
MAIN    ENDP
 
        SUBTTL  GETARGS - PROCESS ARGUMENTS
        PAGE
GETARGS PROC    NEAR
        MOV     DI,PARAM_L      ;point to command buffer length         2.4
        XOR     CH,CH           ;clear high-order byte                  2.4
        MOV     CL,BYTE PTR [DI] ;get length                            2.4
        JCXZ    G24F            ;jump if no param provided              2.4
        INC     DI              ;point to command buffer                2.4
        MOV     AL,' '          ;we'll search for the first non-blank   2.4
        CLD                     ;search forward                         2.4
        REPE    SCASB           ;find first non-blank                   2.4
        JE      G24F            ;all blanks: treat like no parm provided2.4
        INC     CX              ;and adjust length                      2.4
        DEC     DI              ;adjust offset                          2.4
        MOV     SI,DI           ;save offset of first non-blank         2.4
        MOV     DX,CX           ;save cx                                2.4
        MOV     AL,'/'          ;search for a slash                     2.4
        CLD                     ;clear direction flag                   2.4
        REPNE   SCASB           ;search for a slash                     2.4
        JCXZ    G24A            ;jump if slash not found                2.4
        INC     CX              ;adjust cx if slash found               2.4
G24A:   NEG     CX              ;subtract count from original length    2.4
        ADD     CX,DX           ;to get length of drive+path            2.4
        JCXZ    G24F            ;if 0, only options specified           2.4
        MOV     DI,OFFSET PATH  ;point to path name hold area           2.4
        CMP     BYTE PTR [SI+1],':' ;was drive number specified?        2.4
        JNE     G24B            ;No, go move the path                   2.4
        INC     SI              ;point past drive number                2.4
        INC     SI              ;point past :                           2.4
        DEC     CX              ;adjust...                              2.4
        DEC     CX              ;...length                              2.4
        JNZ     G24B            ;if any characters remain process them  2.4
G24F:   MOV     SI,OFFSET ALLF  ;point to the all-files literal         2.4
        MOV     DI,OFFSET PATH  ;the path needs it                      2.4
        MOV     CX,3            ;length of literal                      2.4
        CLD                     ;to assure forward direction            2.4
        REP     MOVSB           ;move the literal                       2.4
        JMP     G24C            ;now process normally                   2.4
G24B:   CLD                     ;clear direction flag                   2.4
        MOV     DX,CX           ;save the length                        2.4
        REP     MOVSB           ;and move the pathname to path          2.4
        CMP     DX,1            ;is length of pathname 1?               2.4
        JNE     G24C            ;no                                     2.4
        CMP     PATH,'\'        ;is the only character a backslash?     2.4
        JNE     G24C            ;no                                     2.4
        MOV     SI,OFFSET ALLF  ;point to the all-files literal         2.4
        MOV     DI,OFFSET PATH+1 ;the path needs it                     2.4
        MOV     CX,3            ;length of literal                      2.4
        CLD                     ;to assure forward direction            2.4
        REP     MOVSB           ;move the literal                       2.4
G24C:   MOV     SI,PARAM_B      ;point to cmd buffer                    2.4
        MOV     DI,OFFSET DRVNUM ;point to FCB                          2.4
        MOV     AL, 1111B       ;Select parse options
        DOSCALL @PARSEF         ;Parse filename
        CMP     BYTE PTR [DI],0 ;If <> 0 then
        JNZ     B1              ; not default drive
        DOSCALL @GETDSK         ;AL <- default disk
        INC     AL              ;Increment drive #
        STOSB                   ;Save drive #
B1:     MOV     AL,DRVNUM       ;get drive number                       2.4
        MOV     DRVNBR,AL       ;save as working drive number           2.4
        ADD     PATHD,AL        ;convert drive number to ascii          2.4
        XOR     CX,CX           ;clear cx just in case...               2.4
        MOV     AX,0            ;load "get attribute" subfunction code  2.4
        DOSCALL @CHMOD,PATHD    ;get attribute of the current path in cx2.4
        OR      AL,AL           ;check for error                        2.4
        JNZ     G24X            ;if so, it's not a directory            2.4
        TEST    CX,10H          ;is it a directory?                     2.4
        JZ      G24X            ;no                                     2.4
        MOV     DI,OFFSET PATH  ;we need to find the end of PATH        2.4
        MOV     CX,64           ;which can be up to 64 bytes long...    2.4
        MOV     AL,0            ;...so we'll search for a 0             2.4
        CLD                     ;search forward                         2.4
        REPNE   SCASB           ;find the end of PATH                   2.4
        DEC     DI              ;point di at the zero                   2.4
        MOV     SI,OFFSET PATHALL ;point to literal                     2.4
        MOV     CX,5            ;literal is 5 bytes long                2.4
        CLD                     ;i'm a coward                           2.4
        REP     MOVSB           ;now move the literal to the path       2.4
G24X:   MOV     CX,64           ;up to 64 characters in a pathname      2.4
        MOV     AL,'\'          ;we'll search for the last backslash    2.4
        MOV     DI,OFFSET PATH+63 ;point to the end of the pathname     2.4
        STD                     ;search backwards                       2.4
        REPNE   SCASB           ;for the last backslash                 2.4
        MOV     PATHLEN,CX      ;and save path length                   2.4
        CMP     PATH,'\'        ;is first path character a backslash?   2.4
        JNE     G24D            ;no, full path not specified            2.4
        MOV     CX,PATHLEN      ;get the length of the subdirectories   2.4
        MOV     SI,OFFSET PATH  ;point to path                          2.4
        MOV     DI,OFFSET DIRNAME ;and to dest                          2.4
        CLD                     ;just in case                           2.4
        REP     MOVSB           ;move the pathname                      2.4
        JMP     G24E            ;bypass the rest of this                2.4
G24D:   MOV     SI,OFFSET DIRNAME+1 ;point to directory name field      2.4
        MOV     DL,BYTE PTR DRVNBR ;pick up drive number                2.4
        DOSCALL @GETCD          ;get the current directory              2.4
        MOV     CX,PATHLEN      ;if pathlen is zero, no subdirectories  2.4
        JCXZ    G24E            ;so don't concatenate path              2.4
        MOV     CX,64           ;we're going to find the end of...      2.4
        MOV     AL,0            ;...DIRNAME so we can concatenate...    2.4
        MOV     DI,OFFSET DIRNAME ;...the path subdirectories to it     2.4
        CLD                     ;search forwards                        2.4
        REPNE   SCASB           ;search for a 0                         2.4
        DEC     DI              ;point back to the zero                 2.4
        DEC     DI              ;point to last character of current dir 2.4
        CMP     BYTE PTR [DI],'\' ;is the last character a backslash?   2.4
        JNE     G24Z            ;no, it's not a root directory          2.4
        INC     DI              ;adjust pointer                         2.4
        JMP     G24Y            ;and skip extra-separator stuff         2.4
G24Z:   INC     DI              ;adjust pointer                         2.4
        MOV     AL,'\'          ;get a literal                          2.4
        STOSB                   ;to separate the 2 concatenated parts   2.4
G24Y:   MOV     CX,PATHLEN      ;get length of subdirectories           2.4
        MOV     SI,OFFSET PATH  ;move from path to dirname              2.4
        CLD                     ;forward move                           2.4
        REP     MOVSB           ;now concatenate the dir + pathnames    2.4
G24E:   MOV     AL,0            ;read ctrl/break state into dl          2.4
        DOSCALL @CTLBRK         ;read status                            2.4
        PUSH    DX              ;save status                            2.4
        MOV     AL,1            ;prepare to set ctl/break state...      2.4
        MOV     DL,0            ;...to OFF...                           2.4
        DOSCALL @CTLBRK         ;...so we won't mess up the current dir 2.4
        MOV     DL,BYTE PTR DRVNBR ;get drive number                    2.4
        ADD     CURDSK,DL       ;convert to ascii                       2.4
        ADD     HDRVE,DL        ;convert to ascii                       2.4
        MOV     SI,OFFSET CURDIR ;point to current directory save area  2.4
        DOSCALL @GETCD          ;get and save current directory         2.4
        DOSCALL @CHDIR,HDRVE    ;temporarily set current directory      2.4
        CMP     AL,3            ;check for error return                 2.4
        JNE     G24Q            ;jump if no error                       2.4
        DOSCALL @STROUT,PATHNF  ;issue 'path not found message'         2.4
        INT     20H             ;and get lost                           2.4
G24Q:   MOV     DL,BYTE PTR DRVNBR ;get drive number                    2.4
        MOV     SI,OFFSET DIRNAME+1 ;point to dirname                   2.4
        DOSCALL @GETCD          ;now get the dirname without '\..'s     2.4
        DOSCALL @CHDIR,CURDSK   ;now reset the current directory        2.4
        MOV     AL,1            ;set ctrl/break state...                2.4
        POP     DX              ;...to previous value                   2.4
        DOSCALL @CTLBRK         ;and do it                              2.4
        MOV     DI,OFFSET DIRNAME ;point to directory name field again  2.4
        MOV     CX,64           ;name can be up to 64 bytes long        2.4
        MOV     AL,0            ;name is terminated by a zero           2.4
        CLD                     ;just in case                           2.4
        REPNE   SCASB           ;search for the end                     2.4
        MOV     AX,64           ;get constant                           2.4
        SUB     AX,CX           ;subtract remaining count               2.4
        SHR     AX,1            ;divide result by 2                     2.4
        MOV     HOLDLEN,AX      ;and save result                        2.4
        DEC     DI              ;adjust pointer                         2.4
        MOV     CX,4            ;load length of literal                 2.4
        MOV     SI,OFFSET LFLFCR ;point to literal                      2.4
        CLD                     ;just in case                           2.4
        REP     MOVSB           ;and terminate the dirname with literal 2.4
        MOV     SI,PARAM_L      ;SI <- ptr cmd length
        MOV     CH,0
        MOV     CL,[SI]         ;CL <- # chars in cmd
        JCXZ    B10
        MOV	HOLDATR,2+4+16  ;Hidden & system & directory           2.41
B2:     INC     SI              ;Point to next char
        CMP     BYTE PTR [SI],'/'
        JNZ     B8              ;If not a slash
        MOV     AL,[SI+1]       ;AL <- option letter
        AND     AL,0DFH         ;Force to upper-case
        CMP     AL,'A'          ;No hidden & system & directory files? 2.41
        JNZ     B3              ;Nope, try next one.                   2.4
	MOV	AL,0      	;Get ready to blank HOLDATR            2.41
	MOV	HOLDATR,AL   	;Do it                                 2.41
B3:     CMP     AL,'E'          ;Without screen erase?
        JNZ     B4              ;Nope, try next one.
        MOV     CLSFLG,AL
B4:     CMP     AL,'S'          ;Sort by size?
        JNZ     B5              ;Nope, try next one.
        MOV     SIZFLG,AL
B5:     CMP     AL,'D'          ;Sort by date/time?
        JNZ     B6              ;Nope, try next one.
        MOV     DTEFLG,AL
B6:     CMP     AL,'X'          ;Sort by extension?
        JNZ     B7              ;Nope, try next one.
        MOV     EXTFLG,AL
B7:     CMP     AL,'N'          ;Original order?
        JNZ     B8              ;Nope, try next one.
        MOV     SRTFLG,AL
B8:     CMP     AL,'P'          ;Pause when screen full?
        JNZ     B9              ;Nope, try next one.
        MOV     PSEFLG,AL
B9:     LOOP    B2              ;Test for another param.
B10:    RET
GETARGS ENDP
 
        SUBTTL  SRCHDIR - SEARCH DIRECTORY
        PAGE
SRCHDIR PROC    NEAR

        DOSCALL @SETDTA,DTA     ;Set DTA for dir. search                2.3
        MOV     CX,11           ;we'll move 11...                       2.4
        MOV     AL,'?'          ;...question marks...                   2.4
        MOV     DI,OFFSET XFCB+8 ;...to the XFCB...                     2.4
        CLD                     ;...in case the filename is crashed...  2.4
        REP     STOSB           ;...so that we can search for the volid 2.4
        MOV     BYTE PTR ATTRIB,8 ;set attribute byte and...            2.3
        DOSCALL @SRCH1,XFCB     ;get the volume name                    2.3
        MOV     SI,OFFSET XNAME ;point to filename within xfcb          2.4
        MOV     CX,11           ;load length of volume name             2.3
        MOV     DI,OFFSET VOLNAME ;point to destination field in HDNG1  2.3
        CLD                     ;forward, march                         2.3
        REP     MOVSB           ;move the volume name                   2.3
        MOV     DI,OFFSET DIRNAME ;point to the directory name again    2.3
        MOV     CL,HOLDATR      ;get the real attribute byte            2.4
        XOR     CH,CH           ;clear the high-order byte              2.4
        DOSCALL @FIND1,PATHD    ;First call to search dir.              2.4
C1:     OR      AL,AL
        JNZ     C2              ;Not found, quit looking.
        MOV     BX,DIRLNK       ;BX <- base of DIRBUF
        LEA     DI,[BX].NAM     ;point to beginning of dirntry          2.4
        MOV     SI,OFFSET FILNAME
        MOV     CX,12           ;length of filename                     2.4
        CMP     BYTE PTR [SI],'.' ;is first character a period?         2.4
        JNE     C24A            ;no, it's not a special case            2.4
        MOVSB                   ;move it to the buffer                  2.4
        DEC     CX              ;adjust count                           2.4
        CMP     BYTE PTR [SI],'.' ;is next character a period?          2.4
        JNE     C24A            ;no, let normal routine pad the rest    2.4
        MOVSB                   ;move it to the buffer                  2.4
        DEC     CX              ;adjust count                           2.4
C24A:   CMP     BYTE PTR [SI],0 ;is it a zero?                          2.4
        JNE     C24B            ;no                                     2.4
        MOV     AL,' '          ;load a space into al                   2.4
        REP     STOSB           ;and blank out the rest of the name     2.4
        JMP     C24X            ;and go get other fields                2.4
C24B:   CMP     BYTE PTR [SI],'.' ;is it a period?                      2.4
        JNE     C24C            ;no, process it                         2.4
        MOV     DX,CX           ;save CX                                2.4
        SUB     CX,4            ;determine number of spaces needed      2.4
        SUB     DX,CX           ;adjust saved CX                        2.4
        MOV     AL,' '          ;get a space                            2.4
        REP     STOSB           ;and pad out the name                   2.4
        MOV     CX,DX           ;restore CX                             2.4
C24C:   MOVSB                   ;move the character to the buffer       2.4
        DEC     CX              ;adjust count                           2.4
        JNZ     C24A            ;and loop if anything's left to process 2.4
C24X:   MOV     SI,OFFSET FILTIME ;exit point for filename routine      2.4
        MOVSW                   ;Move time to DIRBUF
        MOVSW                   ;Move date to DIRBUF
        MOV     SI,OFFSET FILSIZE
        MOVSW                   ;Move size to DIRBUF
        MOVSW
        MOV     SI,OFFSET FILATTR ;point to file attribute byte         2.4
        MOVSB                   ;and move it to dirntry                 2.4
        ADD     BX,SIZE DIRNTRY ;Point to next entry
        MOV     DIRLNK,BX       ;Save ptr
        INC     NBRFILS         ;Increment file count
        MOV     CL,HOLDATR      ;get the real attribute byte            2.4
        XOR     CH,CH           ;clear the high-order byte              2.4
        DOSCALL @FIND2,PATHD    ;Search for next file                   2.4
        JMP     C1              ;Loop for next one
C2:     RET
SRCHDIR ENDP
 
        SUBTTL  SRTDIRB - SORTS ENTRIES IN DIRBUF
        PAGE
SRTDIRB PROC    NEAR    ;Sorts directory entries in DIRBUF
        MOV     DI,OFFSET DIRBUF ;Point to DIRBUF
D1:     CMP     DI,DIRLNK       ;Are there anymore?
        JNC     D8              ;NO, exit
        MOV     SI,OFFSET C1LNK ;Start with column 1 ptr
D2:     MOV     BX,SI
        MOV     SI,[BX]         ;SI<-ptr to next entry
        OR      SI,SI
        JZ      D7              ;if link=0
        MOV     AX,SI
        MOV     DX,DI
        XOR     CL,CL           ;CL <- 0
        CMP     CL,SIZFLG
        JNZ     D5              ;If sort by size
        CMP     CL,DTEFLG
        JNZ     D4              ;If sort by date/time
        CMP     CL,EXTFLG
        JNZ     D3              ;If sort by ext
        LEA     SI,[SI].NAM
        LEA     DI,[DI].NAM
        MOV     CX,1+SIZE NAM+SIZE EXT  ;# of bytes
        JMP     SHORT D6
D3:     LEA     SI,[SI].EXT     ;Sort by extension
        LEA     DI,[DI].EXT
        MOV     CX,SIZE EXT     ;# of bytes
        JMP     SHORT D6
D4:     LEA     SI,[SI].DTE     ;Sort by date/time
        LEA     DI,[DI].DTE
        MOV     CX,2            ;# of words
        STD
        REPZ    CMPSW
        MOV     DI,DX
        MOV     SI,AX
        JBE     D2
        JMP     SHORT D7
D5:     LEA     SI,[SI].SZH     ;Sort by size
        LEA     DI,[DI].SZH
        MOV     CX,2            ;# of words
        STD
        REPZ    CMPSW
        MOV     DI,DX
        MOV     SI,AX
        JBE     D2
        JMP     SHORT D7
D6:     CLD                     ;Sort by name.ext
        REPZ    CMPSB
        MOV     DI,DX
        MOV     SI,AX
        JBE     D2
D7:     MOV     [DI],SI
        MOV     [BX],DI
        ADD     DI,SIZE DIRNTRY ;Point to next entry
        JMP     D1
D8:     RET
SRTDIRB ENDP
 
        SUBTTL
        PAGE
; LNKDIRB - LINKS ENTRIES IN DIRBUF
 
LNKDIRB PROC    NEAR            ;LINK ENTRIES IN DIRBUF
        MOV     DI,OFFSET DIRBUF
        MOV     C1LNK,DI       ;Point to 1st entry
        MOV     CX,NBRFILS      ;Set loop counter
        DEC     CX
LNK1:   MOV     BX,DI
        ADD     DI,SIZE DIRNTRY ;Offset to next entry
        MOV     [BX],DI         ;Store ptr
        LOOP    LNK1            ;Link next entry
        MOV     [DI],CX         ;Last ptr <- null
        RET
LNKDIRB ENDP
 
; SPLTLST - SPLITS LINKED LIST IN HALF
 
SPLTLST PROC    NEAR
        MOV     CX,NBRFILS      ;Get # of entries
        SAR     CX,1            ; and divide by 2
        JZ      F2              ;if NBRFILS < 2
        ADC     CL,0            ;Account for odd #
        MOV     BX,OFFSET C1LNK
F1:     MOV     BX,[BX]         ;Chain thru list to
        LOOP    F1              ; last row of column 1.
        MOV     AX,[BX]         ;Get ptr to 1st row of col 2
        MOV     C2LNK,AX        ; C2LNK <- R1,C2 ptr
        MOV     [BX],CX         ;Last row of col 1 <- null
F2:     RET
SPLTLST ENDP
 
        SUBTTL  GETFREE - GET DISK FREE SPACE
    PAGE
GETFREE PROC        NEAR            ;cluster = allocation unit
    MOV     DL,DRVNBR       ;Get drive #
    PUSH    DS              ;Save DS
    DOSCALL @GETVER         ;get DOS version number                    JYF
    CMP     AL,2            ;is this version 2.0 or higher?            JYF
    JGE     E4              ;yes                                       JYF
                            ;no                                        JYF
    DOSCALL @FATAD2         ;Get FAT info from DOS
    MOV     AH,0            ;AL = sector size
    XCHG    CX,DX           ;Sector size times the
    MUL     DX              ; # sectors/cluster
    PUSH    AX              ;Save cluster size
    XOR     AX,AX           ;Unused clusters = 0
    MOV     SI,2            ;Skip first 3 clusters
E1: MOV     DI,SI           ;DI <- cluster #
    SHR     DI,1            ;Divide cluster number
    ADD     DI,SI           ; by 1.5
    MOV     DI,[BX+DI]      ;Fetch from FAT
    TEST    SI,1            ;Test if even or odd
    JZ      E2              ;If even then skip
    SHR     DI,1            ; else if odd
    SHR     DI,1            ;  right justify the
    SHR     DI,1            ;  cluster number.
    SHR     DI,1
E2: AND     DI,0FFFH        ;Mask the low 12 bits
    JNZ     E3              ;If not 0 then skip, else
    INC     AX              ; increment counter.
E3: INC     SI              ;Point to next cluster
    LOOP    E1              ; and go check it.
    POP     CX              ;Get cluster size, times
    MUL     CX              ;  # of free clusters
    JMP     E5              ;skip processing for DOS 2.0                JYF
E4:                         ;processing for DOS 2.00                    JYF
    DOSCALL @DSKFSP         ;get disk free space                        JYF
    MUL     BX              ;AX (sectors/clustor) * BX (free clustors)  JYF
    MOV     DX,AX           ;                                           JYF
    MUL     CX              ;AX * CX (bytes/clustor)                    JYF
E5:                         ;                                           JYF
    POP     DS              ;Restore DS
    MOV     LOSIZE,AX       ;Save the 32 bit
    MOV     HISIZE,DX       ; binary free space
    RET
GETFREE ENDP
 
    SUBTTL  PRTHDNG - PRINT HEADINGS
    PAGE
PRTHDNG PROC        NEAR
        MOV     AL,CLSFLG
        OR      AL,AL
        JZ      G1              ;If erase screen                       2.41
	MOV	AH,3		;Read cursor position                  2.41
	MOV	BH,0            ;Page no.                              2.41
	INT	10H		;BIOS video call                       2.41
	MOV	DL,0		;Column 0 			       2.41
	MOV	AH,2		;Set cursor position		       2.41
	INT	10H		;BIOS video call	               2.41
	MOV	AH,8		;Read attribute byte  		       2.41
	INT	10H		;BIOS video call  		       2.41
	MOV	BH,AH		;Put attribute in video mode           2.41
        SUB     CX,CX
        MOV     DX,24*256+79    ;row=24 col=79
	MOV	AX,0600H
        INT     10H             ;BIOS video call
        SUB     DX,DX
        MOV     AH,2            ;Clear screen
        MOV     BH,0
        INT     10H             ;BIOS video call
G1:     MOV     AL,DRVNBR       ;Get drive #
        DOSCALL @GETDTE ; CX<-year, DH<-month, DL<-day
        MOV     AL,DH
        AAM
        XCHG    AL,AH
        OR      D_MM,AX         ;Fold into month
        MOV     AL,DL
        AAM
        XCHG    AL,AH
        OR      D_DD,AX         ;Fold into day
        MOV     AX,CX
        SUB     AX,1900
        AAM
        XCHG    AL,AH
        OR      D_YY,AX         ;Fold into year
        DOSCALL @GETTME ; CH<-hours, CL<-minutes
        MOV     AL,CH           ;AL<-binary hours
        AAM                     ;Convert AL to two
        XCHG    AL,AH           ; BCD digits in AX.
        OR      T_HH,AX         ;Fold into hours
        MOV     AL,CL           ;AL<-binary minutes
        AAM                     ;Convert AL to two
        XCHG    AL,AH           ; BCD digits in AX.
        OR      T_MM,AX         ;Fold into minutes
        DOSCALL @STROUT,HDNG1   ;Print main heading
        MOV     DX,OFFSET HDNG2 ;point to 2nd heading                   2.3
        ADD     DX,HOLDLEN      ;add offset length to center DIRNAME    2.3
        DOSCALL @STROUT         ;Print subheading                       2.3
        DOSCALL @STROUT,HDNG3   ;Print column 1 heading
        CMP     WORD PTR C2LNK,0
        JZ      G2              ;If not 2 columns
        DOSCALL @STROUT,SPACES-3 ;Print 3 spaces                        2.4
        DOSCALL @STROUT,HDNG3   ;Print column 2 heading
G2:     DOSCALL @STROUT,CRLF    ;Start a new line
        RET
PRTHDNG ENDP
 
        SUBTTL  PRINT DETAIL LINES
        PAGE
PRTDRVR PROC    NEAR            ;Driver routine
        MOV     BX,C1LNK
        OR      BX,BX           ;more to print?
        JZ      H2              ; no, return
        MOV     AX,[BX]
        MOV     C1LNK,AX
        CALL    PRTDTL          ;print column one
        MOV     BX,C2LNK
        OR      BX,BX
        JZ      H1              ;If no column 2 entry
        DOSCALL @STROUT,SPACES-3 ;print 3 spaces                        2.4
        MOV     AX,[BX]
        MOV     C2LNK,AX
        CALL    PRTDTL          ;print column two
H1:     DOSCALL @STROUT,CRLF
        CMP     PSEFLG,0        ;Check for no pause option             2.41
        JNZ     PRTDRVR         ;Nope, continue                        2.41
        DEC     LINCNT          ;Decrement line counter
        JNZ     PRTDRVR         ;If page not full?
        MOV     LINCNT,LPERSCR-2 ;Reset to # lines/screen
        DOSCALL @STROUT,PSEMSG  ;Display pause message.
        MOV     AL,@KEYIN       ;Specify input function
        DOSCALL @CKEYIN         ;Wait for key press
        DOSCALL @STROUT,CRLF    ;Set to new line
        JMP     PRTDRVR         ;Go do the next line
H2:     RET
PRTDRVR ENDP
 
PRTDTL  PROC    NEAR            ;Prints file.ext, size, date & time
        MOV     CX,1+SIZE NAM+SIZE EXT  ;                               2.4
        SUB     DI,DI           ;DI <- 0
I1:     DOSCALL @CHROUT,[BX+DI].NAM
        INC     DI              ;point to next char.
        LOOP    I1              ;go do next char.
        TEST    BYTE PTR [BX].ATR,10H ;is it a directory entry?         2.4
        JZ      I2              ;no, print size                         2.4
        DOSCALL @STROUT,PRTDIR  ;yes, print <DIR> instead of size       2.4
        JMP     I3              ;and bypass size print                  2.4
I2:     PUSH    BX              ;save entry base
        MOV     SI,[BX].SZL     ;SI <- low size
        MOV     DI,[BX].SZH     ;DI <- high size
        ADD     LOTOTAL,SI      ;add low-order word to total            2.3
        ADC     HITOTAL,DI      ;and add high-order word to total       2.3
        CALL    PRINTDX         ;convert size                           2.4
        MOV     DX,OFFSET DOUBLE+1 ;point past first byte of decimal #  2.4
        DOSCALL @STROUT         ;Print size                             2.4
        POP     BX              ;restore entry base
I3:     MOV     CX,5            ;we need to move 5 bytes                2.4
        MOV     SI,OFFSET INITATR ;to initialize PRTATR                 2.4
        MOV     DI,OFFSET PRTATR  ;with spaces                          2.4
        CLD                     ;just in case                           2.4
        REP     MOVSB           ;now move the spaces                    2.4
        TEST    BYTE PTR [BX].ATR,10H ;is it a directory file?          2.4
        JNZ     I24X            ;yes, bypass the other tests            2.4
        MOV     DI,OFFSET PRTATR+3 ;point to right side of field        2.4
        STD                     ;and work backwards                     2.4
        TEST    BYTE PTR [BX].ATR,20H ;is it an archived file?          2.4
;note that the archive bit is set OFF when the file is archived...      2.4
        JNZ     I24H            ;no, test for hidden file               2.4
        MOV     AL,'A'          ;get flag character                     2.4
        STOSB                   ;and put it into PRTATR                 2.4
I24H:   TEST    BYTE PTR [BX].ATR,02H ;is it a hidden file?             2.4
        JZ      I24R            ;no, test for r/o file                  2.4
        MOV     AL,'H'          ;get flag character                     2.4
        STOSB                   ;and put it into PRTATR                 2.4
I24R:   TEST    BYTE PTR [BX].ATR,01H ;is it a r/o file?                2.4
        JZ      I24S            ;no, test for system file               2.4
        MOV     AL,'R'          ;get flag character                     2.4
        STOSB                   ;and put it into PRTATR                 2.4
I24S:   TEST    BYTE PTR [BX].ATR,04H ;is it a system file?             2.4
        JZ      I24X            ;no, exit tests                         2.4
        MOV     AL,'S'          ;get flag character                     2.4
        STOSB                   ;and put it into PRTATR                 2.4
I24X:   DOSCALL @STROUT,PRTATR  ;Print the attribute characters         2.4
        MOV     AX,[BX].DTE     ;AX <- packed date
        CALL    PRTDTE
        DOSCALL @STROUT,SPACES-1 ;print 1 space                         2.4
        MOV     AX,[BX].TME     ;AX <- packed time
        CALL    PRTTME
        RET
PRTDTL  ENDP
 
        SUBTTL  PRINTDD - PRINT A DOUBLE WORD IN DI:SI
        PAGE
PRINTDD PROC    NEAR            ;Prints a 32 bit integer in DI:SI
        CALL    PRINTDX         ;format the double word into DOUBLE     2.4
        DOSCALL @STROUT,DOUBLE  ;print the digits                       2.4
        RET                     ;and return                             2.4
PRINTDD ENDP                    ;                                       2.4

PRINTDX PROC    NEAR            ;converts integer in DI:SI to decimal   2.4
        MOV     DBLCNT,0        ;reset the offset for DOUBLE            2.4
        XOR     AX,AX           ;Zero out the                           2.4
        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        ;5904 ?
        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
        MOV     CL,0
J5:     DEC     CH
        AND     CL,CH
        OR      DL,'0'          ;Fold in ASCII zero
        SUB     DL,CL
        PUSH    DI              ;save DI                                2.4
        MOV     DI,OFFSET DOUBLE ;point to DOUBLE                       2.4
        ADD     DI,DBLCNT       ;add current offset                     2.4
        INC     DBLCNT          ;bump offset for next time              2.4
        MOV     BYTE PTR [DI],DL ;move next digit to DOUBLE             2.4
        POP     DI              ;restore DI                             2.4
        RET                     ;Exit to caller
PRINTDX ENDP                    ;                                       2.4

J6      PROC    NEAR
        ADC     AL,AL
        DAA
        XCHG    AL,AH
        ADC     AL,AL
        DAA
        XCHG    AL,AH
        RET
J6      ENDP
 
        SUBTTL  PRINT DATE, TIME & # FILES ROUTINES
        PAGE
PRTDTE  PROC    NEAR    ;Print packed date in AX as MM/DD/YY
        OR      AX,AX
        JNZ     K1              ;If date <> 0
        DOSCALL @STROUT,SPACES-8 ;Print 8 spaces
        RET
K1:     PUSH    AX
        AND     AX,MASK P_MO    ;Mask the month,
        MOV     CL,P_MO         ; set shift count,
        SHR     AX,CL           ; right justify, &
        CALL    PRTBCD          ; print it.
        DOSCALL @CHROUT,'/'
        POP     AX
        PUSH    AX
        AND     AX,MASK P_DY    ;Mask the day &
        CALL    PRTBCD          ; print it.
        DOSCALL @CHROUT,'/'
        POP     AX
        AND     AX,MASK P_YR    ;Mask the year,
        MOV     CL,P_YR         ; set shift count,
        SHR     AX,CL           ; right justify,
        ADD     AX,80           ; add in year bias, &
                                ; print it.
PRTBCD: AAM                     ;Convert AL to BCD
        OR      AX,'00'         ;Convert to ASCII
        PUSH    AX
        DOSCALL @CHROUT,AH      ;High order digit
        POP     AX
        DOSCALL @CHROUT,AL      ;Low order digit
        RET
PRTDTE  ENDP
 
PRTTME  PROC    NEAR    ;Print packed time in AX as HH:MM
        OR      AX,AX
        JNZ     L1
        DOSCALL @STROUT,SPACES-5 ;Print 5 spaces
        RET
L1:     PUSH    AX
        AND     AX,MASK P_HR    ;Mask the hours,
        MOV     CL,P_HR         ; set shift count,
        SHR     AX,CL           ; right justify, &
        CALL    PRTBCD          ; print it.
        DOSCALL @CHROUT,':'
        POP     AX
        AND     AX,MASK P_MI    ;Mask the minutes,
        MOV     CL,P_MI         ; set shift count,
        SHR     AX,CL           ; right justify, &
        CALL    PRTBCD          ; print it.
        RET
PRTTME  ENDP
 
PRTNFLS PROC    NEAR            ;print byte total and number of files   2.3
        MOV     SI,LOTOTAL      ;get low-order word of total            2.3
        MOV     DI,HITOTAL      ;get high-order word of total           2.3
        CALL    PRINTDD         ;and print the total                    2.3
        DOSCALL @STROUT,HDNG4   ;follow the total with a message        2.3
        MOV     SI,NBRFILS      ;get # of files
        XOR     DI,DI           ;zero high order
        CALL    PRINTDD         ;Print # of files
        DOSCALL @STROUT,HDNG5   ;follow it with a heading
        MOV     SI,LOSIZE       ;get low-order word of freespace        2.4
        MOV     DI,HISIZE       ;get high-order word of freespace       2.4
        CALL    PRINTDD         ;and print freespace                    2.4
        DOSCALL @STROUT,HDNG6   ;follow the freespace with explanation  2.4
        RET
PRTNFLS ENDP
        EVEN
DIRBUF  DIRNTRY <>      ;Buffer for directory entries
SDIR    ENDS
        END     MAIN
	ëâ6?:Œ0âŽyE>¼‰Ø—²Òœ<2§¨Õ#öI9<§ôDý¥2š—ŒÏº[%@o¬d(È
W˜™™™™™¹?$@$@`CONCåååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååååå