Category : Assembly Language Source Code
Archive   : SDIR24.ZIP
Filename : SDIR24.ASM

 
Output of file : SDIR24.ASM contained in archive : SDIR24.ZIP
TITLE SDIR - SORTED DIRECTORY COMMAND, Version 2.4
PAGE 64,132 ; JAN 1983
COMMENT |
SDIR [d:][path][filename[.ext]] [options] 2.4
[filespec] same as for DIR command

[options] * /A - List hidden files.
* /E - Without screen erase.
* /P - 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 with screen erase.
* - 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.
|

SUBTTL EQUATES & STRUCTURES
PAGE
IF1
DOSCALL MACRO FUNC,PARM1
.xcref
F_C = FUNC
IFNB
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
P_DTE RECORD P_YR:7,P_MO:4,P_DY:5
; PC-DOS packed time
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.4 '; 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 32 DUP(' ') ; 2.3
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 ' $' ;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
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' ;Hidden & system & directory files?
JNZ B3 ;Nope, try next one.
MOV HOLDATR,2+4+16 ;Hidden & system & directory
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
JNZ G1 ;If not erase screen
SUB CX,CX
MOV DX,24*256+79 ;row=24 col=79
MOV BH,7 ;Video mode
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 pause option
JZ PRTDRVR ;Nope, continue
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 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


  3 Responses to “Category : Assembly Language Source Code
Archive   : SDIR24.ZIP
Filename : SDIR24.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/