Category : File Managers
Archive   : SAP300.ZIP
Filename : SAP300.ASM

 
Output of file : SAP300.ASM contained in archive : SAP300.ZIP
TITLE Sort and Pack Directory

; ********************* N O T I C E *********************
; * Contrary to the current trend in MS-DOS software, *
; * this program, for whatever it is worth, is NOT *
; * copyrighted! The program, in whole or in part, *
; * may be used freely in any fashion or environment *
; * desired. If you find this program to be useful *
; * to you, do NOT send any contribution to the *
; * author; in the words of Rick Conn, 'Enjoy!' *
; * However, if you make any improvements, I would *
; * enjoy receiving a copy of the modified source. I *
; * can be reached, usually within 24 hours, by *
; * messages on any of the Phoenix FIDO systems, *
; * particularly: *
; * *
; * FIDO 114/8 Bob's Answering Machine *
; * (602) 242-3158 300/1200 bps *
; * FIDO 114/1 Phoenix Node *
; * (602) 242-5230 300/1200/2400 bps *
; * FIDO 114/446 XTRA #1 *
; * (602) 979-6352 300/1200/2400 bps *
; * Technoids Anonymous [PCBOARD] *
; * (602) 899-4876 300/1200/2400 bps *
; * *
; * *
; * Every effort has been made to avoid error and *
; * moderately extensive testing has been performed *
; * on this program, however, the author does not *
; * warrant it to be fit for any purpose or to be *
; * free from error and disclaims any liability for *
; * actual or any other damage arising from the use *
; * of this program. *
; *******************************************************


LF EQU 0AH ; ASCII Line Feed
CR EQU 0DH ; ASCII Carriage Return

FCB1 EQU 5CH ; 1st 'Default' FCB

Code SEGMENT
ASSUME CS:Code, DS:Code, ES:Code, SS:Code

ORG 0100H


Start: jmp Begin ; Jump over constants and variables

;=========================================================================
; Messages - All of the messages used in the program are here

LogOn db 'SAP: Version 3.00: 04-30-86',CR,LF,'$'

SrtMs1 db 'Sorting: $'

SrtMs2 db ' .... Sorted.',CR,LF,'$'

Query db ' Press RETURN ... any other key to abort $'

CanMsg db 'Sort cancelled',CR,LF,'$'

MajEr1 db 'Major Program Error. Cannot restore original Current'
db 'Directory.',CR,LF,'$'

MajEr2 db 'Major Program Error. Disk that was valid earlier'
db ' is no longer valid',CR,LF,'$'

WErMsg db 'Error writing to disk - check write protect',CR,LF,'$'

VerMsg db 'SAP requires DOS 2.0 or higher.',CR,LF,'$'

BadPat db CR,LF,'Invalid path: $'

FatErr db 'Error reading disk FAT',CR,LF,'$'

InvDsk db 'Invalid disk: $'

CrLfMs db CR,LF,'$'

InvSub db 'Invalid Sub-Directory name',CR,LF,'$'

RdErr1 db 'Disk read error',CR,LF,'$'

RdErr2 db 'Read error on Root Directory',CR,LF,'$'

SecMsg db ' Sectors: $'

ActMsg db ' Active entries = $'

EraMs1 db ' Erased entries removed = $'
EraMs2 db ' Erased entries = $'

; End of messages
;-------------------------------------------------------------------------

;=========================================================================
; Disk Parameters - Disk Parameter Block [DPB]
; The Disk Parameter Block is an undocumented feature of MS-DOS that is
; analogous to the documented Disk Parameter Block of CP/M. It provides
; the factors that define the physical characteristics of the disk.
; The Disk Parameter Block is returned by INT 21H, SubFunction 32H
;

Para db 0 ; Assigned disk
db 0 ; Unit within device [0 for RAM disk]
BPS dw 0 ; Bytes per Sector
AluSec db 0 ; Sectors per ALU [cluster] - 1
db 0 ; Number of heads - 1
Fat1st dw 0 ; Reserved sctors [Adr 1st FAT sector]
db 0 ; Copies of FAT
DirNum dw 0 ; Number of Directory Entries
dw 0 ; First usable [data] sector
MaxAlu dw 0 ; Max ALUs [clusters] on disk + 1
FatSec db 0 ; Number of sectors per FAT
Dir1st dw 0 ; First sector of directory
dw 0,0 ; Device driver adr [Ofs:Seg]
Media db 0 ; 'Media' code
db 0 ; Unknown

; End of actual Disk Parameter Block
;
; The following values are computed by the program from values in the
; Disk Parameter Block

AluSiz dw 0 ; Bytes per ALU [cluster]
DirSec dw 0 ; Number of Directory Sectors
Dat1st dw 0 ; First sector of data area

; End of Disk Parameter Information
;-------------------------------------------------------------------------

Retry db 0 ; Retry Count for I/O operations

FatBeg dw 0 ; Start adr of FAT buffer

DirBeg dw 0 ; Start adr of Directory Buffer
DirEnd dw 0 ; End adr of Directory Buffer

Sector dw 0

Alu1st dw 0 ; 1st ALU [cluster] of sub directory

USec dw 0 ; Directory Sectors actually used
UAlus dw 0 ; Directory ALUs " "

Disk db 0 ; Disk number of Working Disk

DirLen db 0 ; Length of Working Directory string

Drive db 0,':\'
DirStr db 62 DUP(?) ; Full Path name for input Para
EndStr dw 0 ; Adr of end of path NULL

Parent db '\..',0

Any db '*.*',0

DTASeg dw 0
DTAOfs dw 0

SavDsk db 0,':\'
SavDir db 62 DUP(?) ; Saved Current Directory

UnUsed db 0 ; Fill for 'UnUsed' entry
db 31 DUP (0F6H)

ExtFcb db 0FFH ; Extended FCB Flag
db 5 DUP (0) ; Reserved
EAttr db 10H ; Attribute - Directory
Fcb db 0 ; Drive
db 8 DUP (0) ; Name
db 3 DUP (0) ; Extension
dw 0 ; Current Block Number
BlkSiz dw 0 ; Logical Record Size
dw 0,0 ; File Size
dw 0 ; Date
db 10 DUP(0) ; Reserved

VerSta db 0 ; Original Verify State

Active dw 0 ; Computed 'Active' entry count
Erased dw 0 ; Computed 'Erased' entry count

Thou10 dw 10000 ; Ten thousand for decimal convert
Thou1 dw 1000 ; One thousand " " "
Hund dw 100 ; One Hundred " " "
Ten dw 10 ; Ten for decimal convert

Supres db 0 ; Zero suppresion switch
NQSwt db 0 ; No Query Switch 0 = OFF
DetSwt db 0 ; Detail Statistics 0 = OFF
RecSwt db 0 ; Recursion Switch 0 = OFF
EraSwt db 0 ; Remove erased swt 0 = OFF

; End of Data
;-------------------------------------------------------------------------


;=========================================================================
; Beginning of Program

Begin: mov AH,0DH ; Flush buffers to ensure good
int 21H ; ... Begin
call Initialize ; Print SignOn and Initialize
call SortDirectory ; Do actual sort
mov AH,0DH ; Flush buffers
int 21H ; ... MS-DOS Entry Interrupt

; Restore original current directory of working disk

EndProgram:
lea DX,SavDsk ; Get ptr to Directory name
mov AH,3BH ; ... MS-DOS 'Change Directory'
int 21H ; ... MS-DOS Entry Interrupt
cmp AL,0 ; Check for error
je Terminate ; ... Xfr - no errors
lea DX,MajEr1 ; Display Error message
ErrorExit:
call PrintMessage ; ...
Terminate:
int 20H ; Program Terminate Interrupt

ErrorEnd:
call PrintMessage ; Display error message
jmp short EndProgram ; Go end program


;=========================================================================
; SortDirectory - The main routine that reads a directory into memory,
; sorts it, and writes out the sorted directory

SortDirectory:
push BP
lea DX,SrtMs1 ; Display 'Sorting' message
call PrintMessage ; ...
lea SI,Drive ; Get ptr to directory name
call PrintString ; ... and display it
cmp NQSwt,0 ; Check NoQeury switch
jne Proceed ; ... Xfr - NoQuery ON
lea DX,Query ; Display 'Continue' message
call PrintMessage ; ...
mov AH,0CH ; MS-DOS 'Char Input/Buffer Flush'
mov AL,8 ; ... 'Console Input/No Echo'
int 21H ; ... MS-DOS Entry Interrupt
cmp AL,CR ; Check input for CR
jz Proceed ; ... Xfr - got CR
lea DX,CanMsg ; Display 'cancelled' message
jmp ErrorEnd ; ... terminate with message

Proceed:
call GetDirectory ; Read entire Directory into memory
call GetUsed ; Find end of used directory entries
mov SI,word ptr DirBeg
test byte ptr DirLen,0FFH ; Check for Root directory
jnz DoSort ; ... Xfr not Root
call CheckSystem ; Check for system disk
DoSort: push BP
mov BP,SI ; Set array base for Sort
mov DX,word ptr DirEnd ; Compute number of entries
sub DX,SI ; ...
mov CL,5 ; ...
shr DX,CL ; ...
mov CX,32 ; Entry size to CX for Sort
call Sort ; Sort directory array
pop BP
call Pack ; Pack the Directory
call WriteDirectory ; Write out Sorted Directory
call PrintEoj ; Print End of Job message(s)
test RecSwt,0FFH ; Check for recursion specified
jnz WalkTree ; ... Xfr - Recursion specified
pop BP
ret

WalkTree:
sub SP,51
mov BP,SP
add BP,6
push ES
mov DX,BP ; Set DTA to stack frame
call ChangeDTA ; ...
mov [BP-4],BX ; Save old DTA adr in stack frame
mov [BP-2],ES ; ...
pop ES
mov DI,EndStr
mov [BP-6],DI
lea DX,Any
mov CX,10H
mov AH,4EH
int 21H
jb PopLevel
ProcessSub:
test byte ptr [BP+21],10H
jz GetNextSub
cmp byte ptr [BP+30],'.'
jz GetNextSub
mov DI,EndStr
cmp DI,offset DirStr ; Check for Root
jne PS1 ; ... Xfr - not Root
lea DI,DirStr ; Get subdirectory ptr for Root
jmp PS2
PS1: mov AL,'\'
stosb
PS2: mov SI,BP
add SI,30
call MoveNullString
push BP
call SortDirectory
pop BP
GetNextSub:
mov AH,4FH
mov CX,10H
int 21H
jc PopLevel
jmp short ProcessSub

PopLevel:
push ES
mov ES,[BP-2]
mov DX,[BP-4]
call ChangeDTA
pop ES
add SP,51
pop BP
mov DX,[BP-6]
mov EndStr,DX
ret


; End of SortDirectory
;-------------------------------------------------------------------------

;=========================================================================
; WriteDirectory Write out Sorted directory

WriteDirectory:
push BP
mov AH,54H ; MS-DOS 'Get Verify State'
int 21H ; ... MS-DOS Entry Interrupt
mov VerSta,AL ; Save current Verify State

mov AH,2EH ; MS-DOS 'Set/Rest Verify'
mov DL,0 ; ... Zero required by DOS
mov AL,1 ; ... Vefify ON flag
int 21H ; ... MS-DOS Entry Interrupt

cld ; Clear Direction Flag [forward]
mov byte ptr Retry,0 ; Initialize error retry count
test byte ptr DirLen,0FFH ; Check for Root
jz WriteRoot ; ... Xfr - Root - not SubDirectory

WriteSub:
mov BX,word ptr DirBeg ; Initiialize ouput ptr
lea SI,DTABUF+1 ; ... and cluster list ptr
mov AL,byte ptr DTABUF ; Get cluster count

XWrF1: push AX ; Save current cluster count
lodsw ; Get cluster
call AluToSector ; Convert Cluster number to Sector
push SI ; Save Cluster index over Write
mov DX,AX ; Move sector numbe to DX for BIOS
mov CL,AluSec ; ... get sectors per ALU as number
mov CH,0 ; ... of sectors to write
mov AL,Disk ; Get Disk Number and
dec AL ; ... convert to code
int 26H ; ... BIOS Absolute Write
jb ErrWrt ; ... Xfr - Write Error
popf ; Fix stack
pop SI ; Restore Cluster Index
pop AX ; Restore current cluster count
dec AL ; ... and decr
jz WriteDirExit ; ... Xfr - no more clusters
add BX,AluSiz
jmp XWrF1

ErrWrt: popf ; Fix stack
pop AX ; Restore current cluster count
inc byte ptr Retry ; Incr Retry count
cmp byte ptr Retry,3 ; Check for max retries
jbe XWrF1 ; ... Xfr - not max - Retry
jmp WriteError ; Xfr - solid write error

WriteRoot:
mov AL,Disk ; ... get disk number
dec AL ; ... and convert to code
mov BX,DirBeg ; Get Buffer Address
mov CX,USec ; ... get number of Sectors
mov DX,Dir1st ; ... get beginning Sector number
int 26H ; ... BIOS Absolute Disk Write
jb WrrtEr ; ... Xfr - Write Error
popf ; Fix Stack

WriteDirExit:
mov AH,2EH ; MS-DOS 'Set/Rest Verify'
mov DL,0 ; ... Zero required by DOS
mov AL,VerSta ; ... restore original Verify State
mov AL,1 ; ... Vefify ON flag
int 21H ; ... MS-DOS Entry Interrupt
pop BP
ret

WrrtEr: popf ; Fix Stack
inc byte ptr Retry ; Incr Retry count
cmp byte ptr Retry,3 ; Check for max retries
jbe WriteRoot ; ... Xfr - not max - go Retry
WriteError:
lea DX,WErMsg ; Display 'write error' message
jmp ErrorEnd ; End program

; End of WriteDirectory
;-------------------------------------------------------------------------

;=========================================================================
; INITIALIZE Initialize the program and check with the operator before
; continuing the Sort.

Initialize:
lea DX,LogOn ; Display logon message
call PrintMessage ; ...
mov AH,30H ; ... MS-DOS 'Get DOS Version'
int 21H ; ... MS-DOS Entry Interrupt
cmp AL,2 ; Check for 2.xx or greater
jnb VersionOk ; ... Xfr - Version 2.xx or greater
lea DX,VerMsg ; Display 'bad version' message
jmp ErrorExit ; ... terminate with message

VersionOk:
mov AH,19H ; MS-DOS 'Get Current Disk'
int 21H ; ... DOS Entry Interrupt
add AL,'A' ; Convert disk Number to alpha
mov Drive,AL ; ... and store in beginning path
lea SI,DirStr ; Get ptr to path
mov DL,0 ; ... on current disk
mov AH,47H ; ... MS-DOS 'Get Current Path'
int 21H ; ... DOS Entry Interrupt

call ProcessArgs ; Process Command Line arguments
mov AL,Drive ; Get disk from working path
sub AL,'@' ; ... and convert to numeric
mov Disk,AL ; Save as working disk
mov DL,AL ; ... and move to DL for DOS
call GetFat ; Read in FAT
lea SI,DirStr ; Get length of working directory
call Leng ; ...
mov DirLen,CL ; Store length - used only as a test
; ... for Root directory
mov AL,Disk ; Get Disk number
mov Fcb,AL ; ... and store in FCB
cld ; Clear Direction Flag [forward]
mov AL,Drive
mov SavDsk,AL
mov DL,Disk ; Get disk code
lea SI,SavDir ; Get ptr to return area
mov AH,47H ; ... MS-DOS 'Get Current Directory'
int 21H ; ... MS-DOS Entry Interrupt
cmp AL,0FFH ; Check for error
jne XFileA ; ... Xfr - no error
lea DX,MajEr2 ; Display error message
jmp ErrorExit ; ... terminate with message

; Now change to the Directory that we want to Sort

XFileA: lea DX,Drive
mov AH,3BH
int 21H
cmp AL,0 ; Check for error return
je XF2 ; ... Xfr - no error
lea DX,BadPat ; Display 'invalid path' message
call PrintMessage ; ...
lea SI,Drive ; ... with bad path
call PrintString ; ...
jmp Terminate ; ... and Terminate program

XF2: ret

; End of Initialize
;-------------------------------------------------------------------------

;=========================================================================
; ProcessArgs - Used by Initialize to get and process Commann Line
; parameters

ProcessArgs:
call GetArgs ; Parse Command Line into arguments
lea SI,ArgV ; Get ptr to 1st Argument pointer
mov CX,ArgC ; Get argument count to CX
jcxz EndArguments ; ... Xfr - there were no parameters
ArgLoop:
push SI ; Save ArgV ptr
mov SI,[SI] ; Get argument ptr
lodsb ; Get 1st char of argument
cmp AL,'/' ; Check for switch
je SwitchArg ; ... Xfr - it is a switch
jmp short DirectoryArg ; Go get directory specification

NextArg:
pop SI ; Restore argument ptr
add SI,2 ; Incr to next argument
loop ArgLoop
EndArguments:
ret

; Process a switch argument as a string

SwitchArg:
lodsb ; Get next char from parameter
or AL,AL ; Check for NUL terminator
jz NextArg ; ... Xfr - NULL terminator
cmp AL,'/' ; Check for another switch char
je SwitchArg ; ... Xfr - by-pass switch char
and AL,0DFH ; Force upper case
cmp AL,'N' ; Check for No Query
je SetNoQuery ; ... Xfr - No Query
cmp AL,'D' ; Check for Statistics
je SetStats ; ... Xfr - Stats
cmp AL,'S' ; Check for recursion
je SetRecur ; ... Xfr - Recursion
cmp AL,'E' ; Check for Pack
je SetPack ; ... Xfr - Pack
jmp short SwitchArg ; By-pass unknown switches

SetNoQuery:
mov NQSwt,0FFH ; Turn ON No Query switch
jmp short SwitchArg ; Go get next char from Argument

SetStats:
mov DetSwt,0FFH ; Turn ON Statistics switch

jmp short SwitchArg ; Go get next char from Argument

SetRecur:
mov RecSwt,0FFH ; Turn ON Recursion switch
jmp short SwitchArg ; Go get next char from Argument

SetPack:
mov EraSwt,0FFH ; Turn ON Pack switch
jmp short SwitchArg


; Process a string argument as a directory specification

DirectoryArg:
push CX ; Save current argument count
dec SI ; Back up argument ptr to 1st char
call UpperCaseString ; Convert to upper case
cmp byte ptr [SI+1],':' ; Check for disk specified
jne NoDisk ; ... Xfr - no disk specified
mov AL,byte ptr [SI] ; Get specified disk
mov Drive,AL ; ... and store in beginning path
push SI ; Save parameter pointer
sub AL,'@' ; Convert alpha disk to Disk Number
lea SI,DirStr ; Set to get current directory
mov AH,19H ; ... MS-DOS 'Get Current Directory'
int 21H ; ... DOS Entry Interrupt
pop SI ; Restore parameter pointer
add SI,2 ; Skip over disk specification
NoDisk: cmp byte ptr [SI],'\' ; Check for begin at root
jne NotRoot ; ... Xfr - path name is relative
add SI,1 ; Skip root desgination
jmp MovePath ; ... Go move path name
NotRoot:
xchg SI,DI ; Save argument ptr in DI
lea SI,DirStr ; Find end of DirStr
call Leng ; ...
mov AL,'\' ; Store element delimitor
stosb ; ... at end of DirStr
xchg SI,DI ; Append argument to DirStr
MovePath:
lea DI,DirStr ; Move path name
call MoveNullString ; ...
pop CX ; Restore current argument count
jmp NextArg ; Go process next argument

; End of ProcessArgs
;-------------------------------------------------------------------------

;=========================================================================
; GETFAT Read the FAT from the drive specified in DL [1=A, 2=B, etc.]
; into the area specified by BX.

GetFat: lea DI,Para ; Get DPB address for GetDpb
call GetDpb ; ... and get the Disk Parameters

; Compute beginning of FAT buffer

mov AX,BPS ; Get bytes per Sector
mov CL,AluSec ; ... and Sectors per cluster
mov CH,0 ; ...
mul CX ; ... compute bytes per cluster
add AX,offset DTABUF ; ... add to adr of DTA buffer
mov FatBeg,AX ; ... store as beginning of FAT buf

; Compute beginning of Directory Buffer

mov AX,BPS ; Get bytes per Sector
mov CL,FatSec ; ... get Sectors per FAT as 16-bit
mov CH,0 ; ...
mul CX ; ... compute bytes in FAT
add AX,FatBeg ; ... add to beginning of FAT buf
mov DirBeg,AX ; ... store adr of Directory buf

mov BX,FatBeg
mov DX,Fat1st ; ... FAT beginning Sector
mov CL,FatSec ; ... number of FAT Sectors to CX
mov CH,0 ; ...
mov AL,Disk ; ... drive to AL for Read
dec AL ; ... and convert to code
int 25H ; ... Read in entire FAT
jnb XGF1 ; ... Xfr - no Read Error on FAT
jmp FatError
XGF1: popf ; Fix stack
ret

FatError:
popf ; Fix Stack
lea DX,FatErr ; Display 'bad FAT' message
jmp ErrorExit ; ... terminate with message

; End of GetFat
;-------------------------------------------------------------------------

;=========================================================================
; GetDpb Get the Disk Parameter Block [DPB] for the Disk specified
; in DL into the area specified in DI.

GetDpb: push DS ; Save Data Segment over DOS Function
mov ah,32H ; Request the DPB
int 21H ; ... MS-DOS Entry Interrupt
cmp AL,0 ; Check for error [bad disk]
je GDpb1 ; ... Xfr - no error
lea DX,InvDsk ; Display 'invalid disk' message
call PrintMessage ; ...
add DL,'@' ; Convert disk number to alpha
mov AH,2 ; ... and display
int 21H
jmp EndProgram ; ... and Terminate program

GDpb1: cld ; Clear Direction Flag [forward]
mov SI,BX ;
mov CX,12 ; ... move 24 bytes [12 words]
rep movsw ; ... move disk parameters
pop DS ; Restore Data Segment
inc AluSec ; Fix Sectors Per Cluster
mov AL,AluSec ; Compute bytes per cluster
cbw ; ...
mul BPS ; ...
stosw

; Compute Maximum number of Root Directory Sectors

mov DX,0 ; Zero-set DX
mov AX,DirNum ; Compute Max number of Directory
mov CL,5 ; ... Sectors [(DirNum * 32) / BPS]
shl AX,CL ; ...
div BPS ; ...
stosw

add AX,Dir1st ; Compute 1st Sector of data area
stosw
ret

; End of GetDPB
;-------------------------------------------------------------------------

;=========================================================================
; CheckSystem Checks for possible System Files. In the Root directory of
; an MS-DOS System Disk, the 1st two Files are the System
; itself and must remain as the 1st two Files. These Files
; usually, but not always, have the System and Hidden
; attributes and, although the names vary, they have an
; Extension of 'SYS'. CheckSystem checks the 1st two Files in a
; directory for either of these conditions and, if either of
; the 1st two Files meets either of the criteria, excludes
; both Files from the Sort.

CheckSystem:
push CX ; Save the user's CX
push DX ; ... and DX
push SI ; ... and the adr of the directory
mov byte ptr SysSwt,0 ; Set 1st time switch

TestSystem:
mov CH,[SI+11] ; Get File's attribute
and CH,6 ; ... and check for System & Hidden
cmp CH,6 ; ...
jne CS1 ; ... Xfr - not System & Hidden
jmp short GotSystem ; ... Xfr - Assume System Disk
CS1: add SI,8 ; Incr ptr to Extension field
lea DI,SysStr ; Get ptr to test string
mov CX,3 ; Set length for Extension
repz cmpsb ; Compare strings
jcxz GotSystem ; ... Xfr - no match
NotEqual:
cmp byte ptr SysSwt,0 ; Check for 1st File
jne NotSystem ; ... Xfr - not 1st - Assume not Sys
mov byte ptr SysSwt,1 ; Turn OFF 1st File switch
pop SI ; Restore adr of directory
push SI ; ... and save again
add SI,32 ; Incr to 2nd File
jmp short TestSystem ; ... and go test it

NotSystem:
pop SI ; Restore directory adr
jmp EndCheck ; ... Xfr to return

GotSystem:
pop SI ; Restore directory adr
add SI,64 ; Exclude 1st 2 Files from Sort
EndCheck:
pop DX ; Restore user's DX
pop CX ; ... and CX
ret

SysSwt: db 0 ; 1st time switch
SysStr: db 'SYS' ; Test string for 'SYS' Extension

; End of CheckSystem
;-------------------------------------------------------------------------

;=========================================================================
; ALUTOSECTOR Convert the ALU number in AX to a logical Sector
; number. Uses Global, Dat1st, which must contain the
; first data Sector.

AluToSector:
push CX ; Save user's CX
push DX ; ... and DX [destroyed by MUL]
sub AX,2 ; Compute logical Sector number from
mov CL,AluSec ; ... cluster number
mov CH,0 ; ... [((cluster - 2) * secs per
mul CX ; ... cluster) + 1st data Sector]
add AX,Dat1st ; ...
pop DX ; Restore user's DX
pop CX ; ... and CX
ret
;
;-------------------------------------------------------------------------

;=========================================================================
; NEXTALU Uses the ALU [cluster] number in AX to find the next ALU in
; the chain. Returns ABOVE OR EQUAL if there are no more
; ALUs in the chain, BELOW if there are more. The
; next ALU is returned in AX

NextAlu:
push CX ; Save users's registers
push SI ; ...
cmp word ptr MaxAlu,0FF8H ; Check for 16-bit FAT entries
ja Bit16 ; ... Xfr - 16-bit entries
mov CX,3 ; Multiply by 3
mul CX ; ...
rcr AX,1 ; ... divide by 2 [ODD -> CARRY]
pushf ; Save CARRY
mov SI,FatBeg ; Get ptr to beginning of FAT buffer
add SI,AX ; ... and add in offset
lodsw ; Fetch next cluster from FAT
popf ; Restore save Odd/Even indication
jc XNxCl1 ; ... Xfr - prior cluster was ODD
and AX,0FFFH ; Keep low-order 12 bits for EVEN
jmp short XNxCl2
XNxCl1: mov CL,4 ; Keep high-order 12 bits for ODD
shr AX,CL ; ...

XNxCl2: cmp AX,0FF8H ; Check for last cluster
pop SI ; Restore user's registers
pop CX ; ...
ret

; Get next Cluster from 16-bit FAT

Bit16: shl AX,1 ; Multiply by 2
mov SI,FatBeg ; Compute address of next cluster
add SI,AX ; ...
lodsw ; Fetch next cluster from FAT
cmp AX,0FFF8H ; Check for last cluster
pop SI ; Restore user's registers
pop CX ; ...
ret

;
;-------------------------------------------------------------------------

;=========================================================================
; NEWLINE Advances the display to a new line [sends CR/LF to display

NewLine:
lea DX,CrLfMs ; Get ptr to Cr/Lf pair
call PrintMessage ; ... and send it
ret
;
;-------------------------------------------------------------------------

;=========================================================================
; PrintString Displays the NULL Terminated string pointed to by SI on the
; Console. Uses DOS Function 2. All registers preserved.

PrintString:
push AX ; Save user's AX
push DX ; ... and DX
cld ; Clear Direction Flag [forward]

XPs1: lodsb ; Get char from message
cmp AL,0 ; Check for Terminator [NULL]
je XPs2 ; ... Xfr - Terminator - through
mov DL,AL ; Move char to DL for BDOS
mov AH,2 ; ... MS-DOS 'Console Output'
int 21H ; ... MS-DOS Entry Interrupt
jmp short XPs1 ; Go get next char

XPs2: pop DX ; Restore user's DX
pop AX ; ... and AX
ret
;
;--------------------------------------------------------------------------

;=========================================================================
; ChangeDTA - A utility subroutine to change the DTA to a specified adr
; and return to adr of the old DTA. On input, ES:DX points
; to the new DTA. On output, ES:BX points to the old DTA.
; Potentially modifies ES.

ChangeDTA:
push DS ; Save Data Segment
push DS ; Save input ES in DS
push ES ; ...
pop DS ; ...
pop ES ; ...
mov AH,2FH ; MS-DOS 'Get DTA'
int 21H ; ... DOS Entry Interrupt
mov AH,1AH ; MS-DOS 'Set DTA
int 21H ; ... DOS Entry Interrupt
pop DS ; Restore input Data Segment
ret

; End of ChangeDTA
;-------------------------------------------------------------------------

;=========================================================================
; Leng - A utility subroutine to find the length of a NULL-Terminated
; [ASCIZ] string. On input, SI points to the string
; On output, CX contains the length and SI points to the terminal
; NULL.

Leng: push AX ; Save AX
mov CX,-1 ; Set CX to maximum
mov AL,0 ; Set to search for NULL
xchg SI,DI ; Move string ptr to DI for search
repnz scasb ; ... and search for NULL
dec DI ; Back up string ptr to NULL
xchg SI,DI ; ... and move back to SI
neg CX ; Convert CX to actual length in
sub CX,2 ; ... chars
pop AX ; Restore AX
ret

; End of Leng
;--------------------------------------------------------------------------

;==========================================================================
; HexInteger Display the conTents of AX as 4 HEX ASCII chars

HexInteger:
push CX ; Save user's CX
push AX ; Save input value
mov CX,8 ; Isolate high byte in low position
shr AX,CL ; ...
call HexOut ; Display in HEX ASCII
pop AX ; Restore input value
push AX ; ... and save again
and AX,0FFH ; Now isolate low byte
call HexOut ; ... and display in HEX ASCII
pop AX ; Restore input value
pop CX ; ... and user's CX
ret
;
;-------------------------------------------------------------------------

;=========================================================================
; HEXOUT Display the byte in AL as 2 HEX ASCII chars

HexOut: push CX ; Save user's CX
push AX ; Save input value
mov CX,4 ; Move high nibble to low position
shr AX,CL ; ....
call HexNibble ; Display nibble in HEX ASCII
pop AX ; Restore input value
push AX ; ... and save again
call HexNibble ; Display low nibble in HEX ASCII
pop AX ; Restore input value again
pop CX ; Restore user's CX
ret
;
;-------------------------------------------------------------------------

;=========================================================================
; HexNibble Display the low order nibble of AL as HEX ASCII char

HexNibble:
push DX ; Save user's DX
push AX ; Save input value
and AX,0FH ; Isolate low nibble
add AL,30H ; Convert to ASCII
cmp AL,39H ; Check for above '9'
jbe XHN1 ; ... Xfr - not above '9'
add AL,7 ; Convert to 'A'-'F'
XHN1: mov DL,AL ; Move it to DL for output
mov AH,2 ; ... MS-DOS 'Console Output'
int 21H ; ... DOS Entry Interrupt
pop AX ; Restore input value
pop DX ; ... and user's DX
ret
;
;-------------------------------------------------------------------------

;=========================================================================
; DecimalOut Display the 16-bit binary value in AX as 5 decimal digits
; with leading zero suppresion

DecimalOut:
push DX ; Save user's DX
mov Supres,0 ; Turn ON zero suppression
cwd
div Thou10 ; Get 1st decimal digit
call DecimalPrint ; ... and print it
mov AX,DX ; Remainder to AX
cwd
div Thou1 ; Get 2nd decimal digit
call DecimalPrint ; ... and print it
mov AX,DX ; Remainder to AX
cwd
div Hund ; Get 3rd deciaml digit
call DecimalPrint ; ... and print it
mov AX,DX ; Remainder to AX
cwd
div Ten ; Get 4th decimal digit
call DecimalPrint ; ... and print it
mov AX,DX ; Remainder is 5th digit
mov Supres,1 ; Always print last digit
call DecimalPrint ; ... print it
pop DX ; Restore user's DX
ret
;
;-------------------------------------------------------------------------

;=========================================================================
; DecimalPrint Print the 4-bit value in AL as a decimal digit with
; leading zero suppression

DecimalPrint:
push CX ; Save user's CX
push DX ; ... and DX
cmp AL,0 ; Check for zero value
jne XDp1 ; ... Xfr - not zero
test Supres,0FFH ; Check for suppression
jnz XDp2 ; ... Xfr - suppression OFF
mov DL,' ' ; Print a BLANK
jmp short XDp3 ; ...
XDp1: mov Supres,AL ; Turn OFF suppression
XDp2: add AL,'0' ; Convert binary to ASCII
mov DL,AL ; ... and move to DL for DOS
XDp3: mov AH,2 ; MS-DOS 'Console Output'
int 21H ; ... MS-DOS Entry Interrupt
pop DX ; Restore user's DX
pop CX ; ... and CX
ret
;
;-------------------------------------------------------------------------

;=========================================================================
; SORT Shell Sort algorithm
; Entry: BP contains Array base
; CX contains entry size
; DX contains number of entries

Sort: push AX ; Save all registers
push BX ; ...
push CX ; ...
push DX ; ...
push SI ; ...
push DI ; ...

mov SI,DX ; Move Count to SI for safekeeping
; Initial Gap is Count

GLoop: shr SI,1 ; Gap = Gap / 2
cmp SI,0 ; Check for zero
jz SrtEnd ; ... Xfr - Gap = ZERO - end

mov BX,SI ; Gap to BX as Initial I

ILoop: cmp BX,DX ; Compare Count to I
jnc GLoop ; ... Xfr - i >= Count
push BX ; Save I
push DX ; ... and count

; Initial J is I

JLoop: sub BX,SI ; J = J - Gap
jc EndI ; ... Xfr - J < 0

call SortCompare ; Compare a[J] to a[J + Gap]
jnc EndI ; ... Xfr - a[J] <= a[J + Gap]
call Swap ; Exchange a[J] and a[J + Gap]
jmp short JLoop

EndI: pop DX ; Restore Count
pop BX ; Restore I
inc BX ; ... and incr
jmp short ILoop

SrtEnd: pop DI ; Restore registers
pop SI ; ...
pop DX ; ...
pop CX ; ...
pop BX ; ...
pop AX ; ...
ret
;
;-------------------------------------------------------------------------

;=========================================================================
; GetPointer Convert index in BX into ^a[J] in DI and ^a[J + Gap] in SI

GetPointer:
mov AX,BX ; Move J to AX for computation of ^a[J + Gap]
add AX,SI ; ... compute J + Gap,
mul CX ; ... multiply by entry size, and
add AX,BP ; ... add in Array base
mov SI,AX ; Move ^a[J + Gap] offset to SI
mov AX,BX ; Now compute ^a[J]
mul CX ; ... multilpy by entry size,
add AX,BP ; ... add in array base,
mov DI,AX ; ... and move ^a[J] to DI
ret
;
;-------------------------------------------------------------------------

;=========================================================================
; SortCompare Compare a[J] to a[J + Gap]
; Entry: CX = entry length
; BX = J
; SI = GAP;
; BP = Array
; Returns: CARRY if a[J] > a[J + Gap]
; ZERO if a[j] = a[J + Gap]
; NC if a[J] <= a[J + Gap]

SortCompare:
push BX ; Save registers used
push DX ; ...
push CX ; ...
push SI ; ...
push DI ; ...
call GetPointer ; ^a[J] in DI; ^a[J + Gap] in SI

; Ensure that Erased entries Sort higher than Active entries

cmp byte ptr [DI],0E5H
jne Era1
cmp byte ptr [SI],0E5H
je ExCmp
jmp short DIHi
Era1: cmp byte ptr [SI],0E5H
jne CompareAttribute
jmp short DILo

; Ensure that directories Sort lower than Files

CompareAttribute:
mov AL,byte ptr [DI+11]
mov AH,byte ptr [SI+11]
and AX,1010H
jz CompareName
cmp AX,1010H
je CompareName
cmp AL,10H
je DILo
DIHi: stc
jmp short ExCmp
DILo: clc
jmp short ExCmp

CompareName:
mov CX,11
repz cmpsb ; Compare strings [CX contains entry length]

ExCmp: pop DI ; Restore registers
pop SI ; ...
pop CX ; ...
pop DX ; ...
pop BX ; ...
ret
;
;------------------------------------------------------------------------

;========================================================================
; SWAP Swap a[J] and a[J + Gap]
; Entry: BX = J
; CX = Entry length
; BP = Array base
; Exit: ConTents of a[J] and a[J + Gap] exchanged

Swap: push BX ; Save registers used
push DX ; ...
push CX ; ...
push DI ; ...
push SI ; ...
call GetPointer ; Get ^a[J] in DI; ^a[J + Gap] in SI

Swl: mov AL,[SI] ; Byte from a[J + Gap] to AL
mov AH,[DI] ; Byte from a[J] to AH
mov [DI],AL ; Interchange
mov [SI],AH ; ...
inc SI ; Incr both pointers
inc DI ; ...
loop Swl ; Loop for entry length

pop SI ; Restore registers
pop DI ; ...
pop CX ; ...
pop DX ; ...
pop BX ; ...
ret
;
;-------------------------------------------------------------------------

;=========================================================================
; MoveNullString A utility subroutine to move a NULL-terminated string
; pointed to by SI to the destination pointed to by DI

MoveNullString:
lodsb ; Get char from source
stosb ; ... and store at destination
or AL,AL ; Was char NULL terminator?
jne MoveNullString ; ... Xfr - NO - go move another
ret

; End of MoveNullString
;-------------------------------------------------------------------------

;=========================================================================
; GetArgs is a subroutine to parse a Command Line into ArgC and ArgV
; similar to those variables in a C program. It requires the definition
; of ARGC as a word and ARGV as the 1st of a Pointer Array of sufficient
; length to hold pointers to all Command Line parameters. GetArg does
; NOT preserve any registers.

GetArgs:
mov word ptr ARGC,0 ; Initialize Arg Count to 0
cld ; Clear Direction Flag [forward]
mov SI,80H ; Get ptr to Command Tail length
mov CL,[SI] ; Get length to CX as 16-bit value
mov CH,0 ; ...
jcxz GAexit ; ... Xfr - no arguments
push CX ; Save Command Tail length
inc SI ; Incr ptr to 1st data byte
lea DI,COMMND ; Get ptr to internal Command Tail
repz movsb ; ... and move it
mov byte ptr [DI],0 ; Store NULL terminator
pop CX ; Restore Command Tail length
lea SI,COMMND ; Get ptr to internal Command Tail
lea BX,ARGV ; Initialize ARGV to Path

DoNext:
mov AL,' ' ; By pass leading blanks
xchg SI,DI ; ...
repz scasb ; ...
dec DI ; Back up ptr
inc CX ; ... and count
xchg SI,DI ; ... and put back in Source Index
jcxz GAExit ; Xfr - nothing but blanks
mov [BX],SI ; Save ptr to file Path Name
add BX,2 ; Incr Arg ptr offset to next
inc word ptr ARGC ; Incr Arg Count
mov AL,' ' ; Now scan for blank following path
xchg SI,DI ; ...
repnz scasb ; ...
dec DI ; Back up ptr
xchg SI,DI
jcxz GAExit ; Xfr - end of Tail
mov byte ptr [SI],0 ; Store NULL terminator at end of Path
inc SI ; Incr ptr past NULL
jmp short DoNext ; Go get next arg

GAExit: ret

; End of GetArg
;-------------------------------------------------------------------------

;=========================================================================
; UpperCaseString A utility subroutine to convert the entire NULL-terminated
; string pointed to by SI to upper case. Only lower case alphas are
; converted. Preseves all registers.

UpperCaseString:
push SI
push AX

UCS1: lodsb ; Get byte from string
or AL,AL ; Check for NULL terminator
je UCSExit ; ... Xfr - NULL end of string
cmp AL,'a' ; Check lower bound of lower case
jb UCS1 ; ... Xfr - not lower - too low
cmp AL,'z' ; Check upper bound of lower case
ja UCS1 ; ... Xfr - not lower - too high
and AL,0DFH ; Mask off lower case bit
mov [SI-1],AL ; ... and store back
jmp short UCS1 ; Go process next char

UCSExit:
pop AX
pop SI
ret

; End of UpperCaseString
;-------------------------------------------------------------------------

;=========================================================================
; GetDirectory - Reads entire Directory into memory using BIOS Read
; Absolute.

GetDirectory:
lea SI,DirStr ; Get ptr to path name
call Leng ; ... and end length
mov DirLen,CL ; Save length for Root check
mov EndStr,SI ; Save ptr to end of path NULL
jcxz Root ; ... Xfr - length 0 = Root
jmp SubDirectory ; ... Xfr - not Root - SubDirectory

;=========================================================================
; Do Root Directory

Root: mov AL,Disk ; Get disk number and convert to
dec AL ; ... code for BIOS
mov BX,DirBeg ; ... Get Directory Buffer adr
mov CX,DirSec ; ... Get number of Sectors to read
mov DX,Dir1st ; ... Get 1st Sector of Directory
int 25H ; ... Read entire directory
jb RdEr1 ; ... Xfr - Read Error
popf ; Fix Stack
ret

RdEr1: popf ; Fix stack
lea DX,RdErr2 ; Display error message
jmp ErrorEnd ; ... and end program

;=========================================================================
; Do Sub-Directory

SubDirectory:
std ; Set Direction Flag [reverse]
dec SI ; Back up ptr to last char

SD1: lodsb ; Search, in reverse, for end of
cmp AL,'\' ; ... parent
je SD2 ; ...
cmp AL,'/' ; ...
je SD2 ; ...
loop SD1 ; ...
dec SI

SD2: cld ; Clear Direction Flag [forward]
add SI,2 ; Incr ptr to 1st of element name
lea DI,Fcb ; Use DOS 'Parse File Name' to set
mov AX,2900H ; ... up FCB for DOS 'Find 1st'
int 21H ; ...
or AL,AL ; Check for error
jz SD3 ; ... Xfr - no error
jmp Terminate ; Deep trouble - quit

SD3: mov SI,EndStr ; Restore ptr to end of path NULL
xchg SI,DI ; ... and append '\..' to set
lea SI,Parent ; ... Current Directory to parent
call MoveNullString ; ...
lea DX,Drive ; Change to parent directory
mov AH,3BH ; ...
int 21H ; ...

push ES
lea DX,DTABUF ; Set DTA to DTABUF
call ChangeDTA ; ...
mov DTASeg,ES ; Save old DTA Segment
mov DTAOfs,BX ; ... and offset
pop ES

lea DX,ExtFcb ; Must get directory entry to find
mov AH,11H ; ... FAT ptr
int 21H ; ... MS-DOS Entry Interrupt
cmp AL,0FFH ; Check for not found
jnz FatA ; ... Xfr - found
lea DX,InvSub ; Display 'invalid sub' message
jmp ErrorEnd ; End program

FatA: mov AX,0 ; Use the DTA buffer to keep the
mov word ptr DTABUF,AX ; ... count of clusters and
lea DI,DTABUF+1 ; ... the list of clusters
mov AX,word ptr DTABUF+27+7 ; Get starting cluster number
mov Alu1st,AX ; ... and save
mov BX,DirBeg ; Initialize input buffer adr

Fat0: push AX ; Save current Cluster
stosw ; ... and put it in list
inc word ptr DTABUF ; Incr Cluster count
call AluToSector ; Convert Cluster to Sector
push DI ; Save Cluster table index over Read
mov DX,AX ; Move to DX for BIOS Read
mov CL,AluSec ; Get number of Sectors to read
mov CH,0 ; ...
mov AL,Disk ; Get disk number for BIOS Read
dec AL ; ... and convert to code
int 25H ; ... BIOS Absolute Read'
jb ErrA ; ... Xfr - Read Error
popf ; Fix stack
pop DI ; Restore Cluster Index
pop AX ; Restore Current Cluster

; Now get next cluster from FAT

call NextAlu
jae Fat3 ; ... Xfr - was last cluster
add BX,AluSiz ; Incr input buffer by cluster size
jmp Fat0 ; Go read next cluster

Fat3: mov DI,EndStr ; Restore input Current Directory
mov AL,0 ; Restore original path
stosb ; ...
lea DX,Drive ; Now change directory back
mov AH,3BH
int 21H
push ES ; Restore DTA also
mov DX,DTAOfs
mov ES,DTASeg
call ChangeDTA
pop ES
ret

ErrA: lea DX,RdErr1 ; Display 'read error' message
jmp ErrorEnd ; ... and end program

; End of GetDirectory
;-------------------------------------------------------------------------

;=========================================================================
; GetUsed

GetUsed:
mov BX,DirBeg ; Initialize search index and count
mov CX,0

; Find address of 1st 'UnUsed' entry in directory

XRoot1: cmp byte ptr [BX],0 ; Check for 'UnUsed' code
je xRoot2 ; ... Xfr - found 1st 'UnUsed
add BX,32 ; Incr index by entry size
inc CX ; Incr entry count
cmp CX,DirNum ; ... and check for max
jb XRoot1

; Save address of 1st 'UnUsed' entry as end of sortable directory

XRoot2: mov DirEnd,BX ; ... and store

XRt2A: mov AX,DirEnd ; Compute Sectors actually used
sub AX,DirBeg ; ...[(bytes used + BPS - 1) / BPS]
add AX,BPS ; ...
dec AX ; ...
mov DX,0
div BPS ; ...
mov USec,AX ; ... store for Root Write
mov CL,AluSec ; Make Sectors per cluster 16-bit
mov CH,0 ; ...
add AX,CX ; Compute Clusters actually used
dec AX ; ... [(USec + AluSec - 1) / AluSec]
mov DX,0
div AluSec ; ...
mov UAlus,AX ; ... store for sub directory write
ret

; End of GetUsed
;-------------------------------------------------------------------------

;=========================================================================
; Eliminate Erased Files

Pack:
mov BX,DirBeg ; Initialize search index

XRoot3: cmp byte ptr [BX],0E5H ; Check for 'Erased' code
je xRoot4 ; ... Xfr - found 1st 'Erased'
add BX,32 ; Incr ptr by entry size
cmp BX,DirEnd ; ... and check for end
jb XRoot3 ; ... Xfr - not at end yet

; Compute number of 'Active' entries

XRoot4: mov AX,BX ; Adr of 1st 'Erased' to AX
sub AX,DirBeg ; ... compute byte length of Active
mov CL,5 ; Divide by 32 [entry length] to get
shr AX,CL ; ... number of 'Active' entries
mov word ptr Active,AX ; Save 'Active' entry count

; Now compute number of 'Erased' entries

mov AX,DirEnd ; Adr of end of directory to AX
sub AX,BX ; ... compute byte len of Erased
shr AX,CL ; Divide by entry length
mov word ptr Erased,AX ; Save 'Erased' entry count

; Now eliminate 'Erased' entries [change them to 'UnUsed']

test EraSwt,0FFH ; Pack directory?
jz NoPack ; ... Xfr - no
mov DI,BX ; Set up to eliminate 'Erased'
XRoot5: lea SI,UnUsed ; ... entries
mov CX,16 ; ...
rep movsw
cmp DI,DirEnd ; Check for end
jb XRoot5 ; ... Xfr - not end

NoPack: ret

; End of Pack
;-------------------------------------------------------------------------

;=========================================================================
; PrintEoj - Print out End of Job messages, including statistics, if
; requested

PrintEoj:
cmp DetSwt,0 ; Check for Statistics requested
jne PrintStat ; ... Xfr - Statistics were requested
lea DX,SrtMs2 ; Display 'sorted' message
call PrintMessage ; ...
ret

PrintStat:
lea DX,SrtMs2 ; Display 'Sorted' message
call PrintMessage ; ...
lea DX,SecMsg ; Display Sector data
call PrintMessage ; ...
test DirLen,0FFH ; Check for Root
jnz EndSub ; ... Xfr - not Root
mov AX,Dir1st ; Get first Sector of Root
mov CX,DirSec ; Get number of Sectors in Root
call PrintSector ; Print out Sector data
jmp End1

EndSub: mov AL,byte ptr DTABUF ; Get cluster count
lea SI,DTABUF+1 ; ... and ptr to list
mov CL,AluSec ; Get Sectors per cluster to CX
mov CH,0 ; ... as 16-bit value

EndLop: push AX ; Save count
lodsw ; Get cluster from list
call AluToSector ; ... and convert it to Sector
call PrintSector ; ... and print Sector data
pop AX ; Restore cluster count
dec AL ; ... and decr
jz End1
push AX ; Save count over DOS call
mov DL,' ' ; Display BLANK separator
mov AH,2
int 21H
pop AX ; Restore cluster count
jmp short EndLop ; Loop


End1: call NewLine ; Advance display
lea DX,ActMsg ; Display active entries data
call PrintMessage ; ...
mov AX,Active ; Get count
call DecimalOut ; ... and display in decimal
call NewLine ; Advance display
lea DX,EraMs2
test EraSwt,0FFH ; Packing directories?
jz End2 ; ... Xfr - no
lea DX,EraMs1 ; Display erased entry data
End2: call PrintMessage ; ...
mov AX,Erased ; Get count
call DecimalOut ; ... and display in decimal
call NewLine ; Advance display
ret

; End of PrintEoj
;-------------------------------------------------------------------------

;=========================================================================
; Print Message Subroutine - A utility subroutine used to print out '$'
; terminated messages.

PrintMessage:
mov AH,9 ; MS-DOS 'Print String'
int 21H ; ... MS-DOS Entry Interrupt
ret

; End of PrintMessage
;-------------------------------------------------------------------------

;=========================================================================
; PrintSector - Display the first and last sectors of an ALU in HEX

PrintSector:
call HexInteger ; ... and display in HEX
add AX,CX ; Compute last Sector in cluster
dec AX ; ...
push AX ; Save over DOS call
mov DL,'-' ; Print '-' spearator
mov AH,2 ; ... MS-DOS 'Display Char'
int 21H ; ... MS-DOS Entry Interrupt
pop AX ; Restore Sector
call HexInteger ; ... and display it in HEX
ret

; End of PrintSector
;-------------------------------------------------------------------------


ArgC dw 0 ; Count of Command Line arguments
ArgV dw 10 DUP (0) ; Pointer Array of Command Arguments
COMMND EQU $

DTABUF EQU COMMND+256

Code ENDS
end Start



  3 Responses to “Category : File Managers
Archive   : SAP300.ZIP
Filename : SAP300.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/