home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Hall of Fame
/
HallofFameCDROM.cdr
/
pcmag
/
v9n12.lzh
/
PRUNE.ZIP
/
PRUNE.ASM
next >
Wrap
Assembly Source File
|
1990-05-19
|
118KB
|
3,300 lines
;--------------------------------------------------;
; PRUNE * PC Magazine * Michael J. Mefford ;
; ;
; Directory tree pruning and grafting utility. ;
;--------------------------------------------------;
_TEXT SEGMENT PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:_TEXT,ES:_TEXT,SS:_TEXT
ORG 100H
START: JMP MAIN
; DATA AREA
; ---------
SIGNATURE DB CR,SPACE,SPACE,SPACE,CR,LF
COPYRIGHT DB "PRUNE 1.0 (c) 1990 Ziff Communications Co. ",CR,LF
PROGRAMMER DB "PC Magazine ",BOX," Michael J. Mefford",LF
CRLF DB CR,LF
SYNTAX DB "Syntax: PRUNE [d:] [d:]",CR,LF,LF
DB "$",CTRL_Z
TAB EQU 9
CR EQU 13
LF EQU 10
CTRL_Z EQU 26
SPACE EQU 32
BOX EQU 254
VERT_LINE EQU 179
FF EQU 12
SHIFT_KEYS EQU 3
LITTLE_ARROW EQU 26
ESC_SCAN EQU 1
Y_SCAN EQU 15H
N_SCAN EQU 31H
NOTE EQU 1046 ; C
COLOR_ATTRIBS STRUC
B DB 71H ;Blue on lt. gray
W DB 17H ;Lt. gray on blue
C DB 31H ;Blue on cyan
I DB 1FH ;White on blue
D DB 17H ;Lt. gray on blue
COLOR_ATTRIBS ENDS
COLOR COLOR_ATTRIBS <>
COLOR_ATTR COLOR_ATTRIBS <>
MONO_ATTR COLOR_ATTRIBS <70H, 07H, 70H, 0FH, 0FH>
BORDER_FLAG DB 0 ; =1 to disable.
COMSPEC DB "COMSPEC="
PARAMETER1 DB 30, "/C DR", 25 DUP (SPACE), CR
PARAMETER2 DB 100, "/C DIRMATCH "
ARGUMENTS DB 112 DUP (SPACE), CR
ENVIRONMENT DW ?
COM_LINE_PTR DW ?, ?
STACK_SEG DW ?
STACK_PTR DW ?
BREAK DB ?
DEFAULT_DRIVE DB ?,":",CR
CRT_MODE EQU 49H
CRT_COLS EQU 4AH
CRT_ROWS EQU 84H
COLUMNS DW ?
CRT_WIDTH DW ?
CRT_START DW ?
STATUS_REG DW 3BAH
VIDEO_SEG DW 0B000H
ROWS DB 24
LISTING_LEN DW ?
ROOT DB "\ (Root)"
ROOT_LEN EQU $ - ROOT
ROOT_DIR DB "\",0
DOT_DOT DB "..",0
VOL_DRIVE DB ?,":\"
STAR_DOT_STAR DB "*.*",0
TEMP DB "$$$$$$$$",0
E5 DB 0E5H
UNNAMED DB "UnNamed "
VOLUME_LEN EQU $ - UNNAMED
DRIVE_SPEC DB " Drive "
DRIVE_LETTER DB ?,": Volume ",0
VOLUME_NAME DW VOLUME1,VOLUME2
VOLUME1 DB 11 DUP (?),0
VOLUME2 DB 11 DUP (?),0
SECTOR_RECORDS DW ?
ROOT_SECTOR DW ?
ROOT_SECTORS DW ?
CLUST_RECORDS DW ?
LAST_CLUSTER DW ?
LAST_ENTRY DW ?
TREE_LEVEL DW ?
DIRS DW ?
DIR_COUNT DW ?,?
LISTING_TOP DW 0,0
BAR_LINE DW 0,0
ACTIVE_TREE DW 0
TREE_COLOR DB ?
MAX_ENTRIES EQU 1000
BUFFER_SEG LABEL WORD
FAT_SEG DW ?
TREE_DATA_SEG DW ?,?
TREE_SEG DW ?,?
CLUSTER_SEG DW ?
OLD_PARENT DW ?
NEW_PARENT DW ?
BIG_DISK_FLAG DB ?
DATA_SECTOR DW ?
SOURCE_INDEX DW ?
SOURCE_POINTER LABEL DWORD
SOURCE_LINE DW ?
MARK_SEG DW ?
SOURCE_BAR DW ?
DEST_POINTER LABEL DWORD
DEST_LINE DW ?
DEST_SEG DW ?
PACKET LABEL BYTE
STARTING_SECTOR DW ?,?
NUMBER_OF_SECTORS DW ?
TRANSFER_ADDRESS DW ?,?
BPB LABEL BYTE
BPB_BytesPerSector DW ?
BPB_SectorsPerCluster DB ?
BPB_ReservedSectors DW ?
BPB_NumberOfFats DB ?
BPB_RootEntries DW ?
BPB_TotalSectors DW ?
BPB_MediaDescriptor DB ?
BPB_SectorsPerFat DW ?
BPB_LENGTH EQU $ - BPB
FAT_TYPE DB ?
FAT_16_BIT EQU 0
FAT_12_BIT EQU 1
TREE_DRIVE DB ?,?,?
DEFAULT_PATH DW OFFSET DEFAULT_DIR1, OFFSET DEFAULT_DIR2
FREE_CLUSTERS DW ?,?
TOTAL_CLUSTERS DW ?,?
BYTES_CLUSTERS DW ?,?
DIRECTORIES DB " directories",0
BYTES DB " bytes ",0
TOTAL DB "total "
PERCENT_100 DB "100%",0
USED DB "used ",0
FREE DB "free ",0
CLUSTER_CNT DW ?
TOTAL_FILES DW ?
ENTRY_CNT DW ?
BRANCH_FLAG DB ?
REREAD_FLAG DB 0
BRANCH_NUM DW ?
DTA EQU 80H
MATCHING STRUC
RESERVED DB 21 DUP (?)
ATTRIBUTE DB ?
FILE_TIME DW ?
FILE_DATE DW ?
SIZE_LOW DW ?
SIZE_HIGH DW ?
FILE_NAME DB 13 DUP (?)
MATCHING ENDS
DIR_ENTRY STRUC
DIR_NAME DB 8 DUP (?)
DIR_EXT DB 3 DUP (?)
DIR_ATTR DB ?
DIR_RESERVED DB 10 DUP (?)
DIR_TIME DW ?
DIR_DATE DW ?
DIR_CLUSTER DW ?
DIR_SIZE DD ?
DIR_ENTRY ENDS
SOURCE_RECORD DB SIZE DIR_ENTRY DUP (?)
DIR_LEN DW ?
TREE_DATA STRUC
LEVEL DB ?
ENTRY DW ?
PARENT DW ?
DIRECTORY DB 11 DUP (?)
CLUSTER DW ?
ATTRIB DW ?
TREE_DATA ENDS
TREE STRUC
MARK DB ?
BRANCH DB 38 DUP (?)
TREE_CLUSTER DW ?
BRANCH_NO DW ?
TREE ENDS
M_DISPATCH DB 48H, 50H, 49H, 51H, 47H, 4FH
DB 0FH, 4BH, 4DH
DB 3BH, 3CH, 3DH, 3EH
DB 3FH, 40H, 41H, 42H, 43H
M_LEN EQU $ - M_DISPATCH
DW UP, DOWN, PGUP, PGDN, HOME, END_KEY
DW TAB_KEY, LEFT, RIGHT
DW COPY_BRANCH, REMOVE_BRANCH, RENAME_BRANCH, MOVE_BRANCH
DW SIZE_BRANCH, SIZE_DIR, NEW_DRIVE, DR, DIRMATCH
D_DISPATCH DB 48H, 50H, 49H, 51H, 47H, 4FH
DB 0FH, 4BH, 4DH
D_LEN EQU $ - D_DISPATCH
DW UP, DOWN, PGUP, PGDN, HOME, END_KEY
DW TAB_KEY, LEFT, RIGHT
NOT_ENOUGH DB "Requires 276K free memory$"
INVALID_DRIVE DB "Invalid Drive$"
READING DB CR,LF,"Reading Directories",CR,LF,"$"
CANT_ROOT DB "Can't prune, copy or rename root directory.",0
ILLOGICAL_MSG DB "Illogical to graft to this directory.",0
DUPLICATE_MSG DB "Directory name already exits.",0
SUBDIR_MSG DB "Subdirectory creation error; Can't prune.",0
LOGICAL_MSG DB "Logical error; Can't prune.",0
ANYKEY_MSG DB " Press any key to continue.",0
DEST_MSG DB "Highlight destination directory for branch and press Enter",0
WORKING_MSG DB "Pruning in progress...",0
REMOVE_MSG LABEL BYTE
DB "WARNING: All files in marked dir AND it's subdirs will be lost. Continue"
YES_NO DB "? Y/N ",0
REMOVE_MSG2 LABEL BYTE
DB "Press Enter to confirm and start directory branch removal. Esc to cancel."
DB 0
TO_MSG DB "and any subdirs to ",0
DELETE_MSG DB "Removing subdirectories and files...",0
FILES_MSG DB " files ",0
CLUSTER_MSG DB " cluster bytes",0
SIZE_MSG DB "Root size is bytes used.",0
DRIVE_MSG DB "Enter new drive letter ",0
DRIVE_MSG_LEN EQU $ - DRIVE_MSG
DRIVE_ERR DB "Drive does not exist.",0
COPY_MSG DB "Copying subdirectories and files...",0
ROOM_MSG DB "Not enough room to move or copy.",0
RENAME_MSG DB "Enter new name for ",0
DOS3_MSG DB "Requires DOS 3.x or later",0
MSG DW ?
COPYA DB "Copy ",0
MOVEA DB "Move ",0
MENU LABEL BYTE
DB "F1 Copy F2 Remove F3 Rename F4 Move F5 Branch Size "
DB "F6 Dir Size ",0
DB "F7 New Drive F8 DR F9 DIRMATCH Tab "
DB 27,26," Select Tree ",24,25," Branch Esc to Exit",0
; CODE AREA
; ---------
MAIN PROC NEAR
CLD ;String instructions forward.
MOV AX,3300H ;Get Ctrl-Break state.
INT 21H
MOV BREAK,DL
MOV AH,19H ;Get default drive so can
INT 21H ; restore after we change it.
MOV DEFAULT_DRIVE,AL
MOV BX,(12+128+64+64+8) * (1024/16)
MOV AH,4AH ;Allocate memory.
INT 21H
JNC SETUP_STACK
MOV DX,OFFSET NOT_ENOUGH
JMP ERROR_EXIT ;If not enough, exit.
SETUP_STACK: MOV AX,OFFSET STACK_POINTER
MOV SP,AX ;Set up stack.
ADD AX,15
MOV CL,4
SHR AX,CL
MOV CX,DS
ADD AX,CX
MOV FAT_SEG,AX ;And data segments.
ADD AX,128 * (1024/16)
MOV TREE_DATA_SEG[0],AX
ADD AX,MAX_ENTRIES * SIZE TREE_DATA / 16 + 1
MOV TREE_DATA_SEG[2],AX
ADD AX,MAX_ENTRIES * SIZE TREE_DATA / 16 + 1
MOV TREE_SEG[0],AX
ADD AX,MAX_ENTRIES * SIZE TREE / 16 + 1
MOV TREE_SEG[2],AX
ADD AX,MAX_ENTRIES * SIZE TREE / 16 + 1
MOV CLUSTER_SEG,AX
PARSE: MOV SI,81H
XOR BP,BP ;Tree index.
NEXT_PARSE: LODSB
CMP AL,CR
JNZ CK_WHITE
MOV SI,OFFSET DEFAULT_DRIVE ;Use default drive if no para.
PUSH [SI]
ADD BYTE PTR [SI],"A"
LODSB
CALL DRIVEEXIST
POP WORD PTR DEFAULT_DRIVE
JMP SHORT FIRST_DRIVE
CK_WHITE: CMP AL,SPACE
JBE NEXT_PARSE
CALL DRIVEEXIST
FIRST_DRIVE: JNZ PARSE_ERROR
STORE_DRIVE: MOV TREE_DRIVE[0],DL
CALL SELECT_DISK
PUSH SI
MOV SI,DEFAULT_PATH[BP] ;Get default path so can
CALL GET_DIR ; restore on exit.
POP SI
INC BP ;Next tree.
INC BP
NEXT_PARSE2: LODSB
CMP AL,CR
JZ PARSE_END
CMP AL,SPACE
JBE NEXT_PARSE2
CALL DRIVEEXIST
JNZ PARSE_ERROR
PARSE_END: MOV TREE_DRIVE[2],DL
CALL SELECT_DISK
MOV SI,DEFAULT_PATH[BP]
CALL GET_DIR
BREAK_OFF: XOR DL,DL ;Turn break off.
MOV AX,3301H
INT 21H
CALL SETUP
;************* Main Loop *************;
NEXT_KEY: CALL HIDE_CURSOR
CALL CLEAR_KEY
MOV DI,OFFSET M_DISPATCH
MOV CX,M_LEN
CALL DISPATCH
JNC NEXT_KEY
JMP SHORT EXIT
;----------------------------------------------;
PARSE_ERROR: MOV DX,OFFSET INVALID_DRIVE
ERROR_EXIT: PUSH DX
MOV DX,OFFSET SIGNATURE
CALL PRINT_STRING
POP DX
ERROR: CALL PRINT_STRING ;Print error message.
MOV AL,1 ;Exit with ERRORLEVEL one.
JMP SHORT TERMINATE
EXIT: MOV DH,ROWS ;Clear last two lines.
MOV CH,DH
DEC CH
XOR CL,CL
MOV DL,BYTE PTR COLUMNS
DEC DL
MOV BH,COLOR.B
MOV AX,600H ; by scrolling active page.
INT 10H
CMP BORDER_FLAG,1
JZ PLACE_CURSOR
XOR BX,BX ;Border back to black.
MOV AH,0BH
INT 10H
PLACE_CURSOR: MOV DH,CH ;Place cursor on next
DEC DH ; to last line.
XOR DL,DL
CALL SET_CURSOR
XOR BP,BP ;Restore paths.
CALL RESTORE_DIR
INC BP
INC BP
CALL RESTORE_DIR
XOR AL,AL ;ERRORLEVEL zero.
TERMINATE: PUSH AX
MOV DL,DEFAULT_DRIVE ;Restore default drive.
MOV AH,0EH
INT 21H
MOV DL,BREAK ;Restore Break state.
MOV AX,3301H
INT 21H
MOV AH,0DH ;Disk reset to flush buffers.
INT 21H
POP AX
MOV AH,4CH ;Terminate.
INT 21H
MAIN ENDP
; ***************
; * SUBROUTINES *
; ***************
RENAME_BRANCH: MOV AH,30H
INT 21H
CMP AL,3
JAE CAN_RENAME
MOV SI,OFFSET DOS3_MSG
CALL DISPLAY_ERROR
JMP RENAME_END
CAN_RENAME: MOV AX,SIZE TREE
MUL BAR_LINE[BP]
MOV SOURCE_LINE,AX
OR AX,AX
JNZ DISPLAY_OLD
MOV SI,OFFSET CANT_ROOT
CALL DISPLAY_ERROR
JMP RENAME_END
DISPLAY_OLD: CALL CLEAR_MENU
MOV SI,OFFSET RENAME_MSG
CALL WRITE_STRING
MOV SI,SOURCE_LINE
PUSH SI
MOV DS,TREE_SEG[BP]
CALL GET_NAME
MOV CS:SOURCE_LINE,SI
CALL WRITE_STRING
CALL DOS_CURSOR
POP SI
CALL SELECT_DIR
PUSH CS
POP DS
MOV DX,OFFSET DOT_DOT
CALL CHANGE_DIR
MOV DH,ROWS
DEC DH
MOV DL,34
CALL SET_CURSOR
MOV BX,DX
MOV DI,OFFSET ARGUMENTS
NEXT_RENAME: CALL GET_KEY
CMP AL,ESC_SCAN
JZ RENAME_END
CMP AH,CR
JZ DO_RENAME
XCHG AL,AH
CMP AL,8
JZ BACKSPACE
CMP AL,SPACE
JBE NEXT_RENAME
CMP DI,OFFSET ARGUMENTS + 12
JZ NEXT_RENAME
STOSB
CALL WRITE_TTY
JMP NEXT_RENAME
BACKSPACE: CMP DI,OFFSET ARGUMENTS
JZ NEXT_RENAME
CALL WRITE_TTY
MOV AL,SPACE
CALL WRITE_TTY
MOV AL,8
CALL WRITE_TTY
DEC DI
JMP NEXT_RENAME
DO_RENAME: XOR AL,AL
STOSB
CALL DOS_CURSOR
MOV DI,OFFSET ARGUMENTS
MOV DX,SOURCE_LINE
MOV DS,TREE_SEG[BP]
MOV AH,56H
INT 21H
PUSH CS
POP DS
JNC RENAME_END2
CALL BEEP
JMP SHORT RENAME_END
RENAME_END2: MOV REREAD_FLAG,1
JMP SELECT_UPDATE
RENAME_END: CALL UPDATE_TREE
CALL DISPLAY_MENU
RET
;----------------------------------------------;
MOVE_BRANCH: MOV MSG,OFFSET MOVEA
CALL GET_DEST ;Get the destination.
JC MOVE_END
CMP AL,AH ;Same drive?
JNZ DO_COPY
CALL FAST_MOVE ;If yes, only change dot dot
JMP SELECT_UPDATE ; pointers.
DO_COPY: CALL COPY ;Else, do copy
JC READ_DISKS
CALL DELETE ; then delete.
READ_DISKS: MOV AX,CS
MOV DS,AX
MOV ES,AX
CMP REREAD_FLAG,1
JNZ MOVE_END
MOV REREAD_FLAG,0
CALL READ_DRIVE ;Reread both trees.
XOR BP,2
CALL READ_DRIVE
MOVE_END: MOV AX,CS
MOV DS,AX
MOV ES,AX
CALL UPDATE_TREE
CALL DISPLAY_MENU
RET
;----------------------------------------------;
COPY_BRANCH: MOV MSG, OFFSET COPYA
CALL GET_DEST ;Get destination.
JC COPY_END3
CALL COPY ;Copy the branch.
JMP SHORT SELECT_UPDATE ;Update the screen.
COPY_END3: CALL UPDATE_TREE
CALL DISPLAY_MENU
RET
;----------------------------------------------;
REMOVE_BRANCH: MOV AX,BAR_LINE[BP] ;Root directory?
OR AX,AX
JNZ CAUTION
MOV SI,OFFSET CANT_ROOT
CALL DISPLAY_ERROR
JMP SHORT REMOVE_END
CAUTION: CALL DISPLAY_MARK
CALL ERASE_MARK
CALL CLEAR_MENU
MOV SI,OFFSET REMOVE_MSG ;Warning message.
CALL WRITE_STRING
CALL BEEP
CALL CLEAR_KEY
CALL GET_KEY ;Get response.
CMP AL,Y_SCAN
JNZ REMOVE_END
CALL CLEAR_MENU
MOV SI,OFFSET REMOVE_MSG2
CALL WRITE_STRING
CALL HIDE_CURSOR
CALL CLEAR_KEY
CALL GET_KEY
CMP AH,CR
JNZ REMOVE_END
MOV DL,TREE_DRIVE[BP] ;If yes, log on drive.
CALL SELECT_DISK
CALL DOS_CURSOR
CALL DELETE ;Delete the branch.
SELECT_UPDATE: MOV AX,CS
MOV DS,AX
MOV ES,AX
CMP REREAD_FLAG,1
JNZ REMOVE_END
MOV REREAD_FLAG,0
MOV BX,BP
XOR BX,2
MOV DL,TREE_DRIVE[BX] ;If tree drives different,
CMP DL,TREE_DRIVE[BP]
JZ DUP_DELETE
CALL READ_DRIVE ; just update the one.
JMP SHORT REMOVE_END
DUP_DELETE: XOR BP,BP ;Else, if both tree drives same
CALL READ_DRIVE ; then just update one and
INC BP
INC BP
CALL DUP_TREE ; copy for other.
REMOVE_END: CALL UPDATE_TREE
CALL DISPLAY_MENU
RET
;----------------------------------------------;
SIZE_DIR: MOV BRANCH_FLAG,0 ;Just the one directory.
JMP SHORT CALC_CLUST
SIZE_BRANCH: MOV BRANCH_FLAG,1 ;Directory and it's subdirs.
CALC_CLUST: PUSH DS
MOV BX,BYTES_CLUSTERS[BP] ;Use current tree's cluster size.
MOV AX,SIZE TREE
MUL BAR_LINE[BP]
MOV SI,AX
OR SI,SI ;Root directory?
JZ BRANCH_ERR1
PUSH SI
PUSH BX
CALL DISPLAY_MARK
CALL ERASE_MARK
POP BX
POP SI
MOV DS,TREE_SEG[BP]
CALL CALC_BRANCH ;Calculate size.
MOV SI,OFFSET LOGICAL_MSG
JC BRANCH_ERR
POP DS
PUSH BX
CALL CLEAR_MENU
ADD DI,2 * 17
MOV AL,COLOR.C
MOV TREE_COLOR,AL
MOV AX,CLUSTER_CNT ;Display size stats.
POP SI
MUL SI
CALL DISP_STATS
MOV SI,OFFSET CLUSTER_MSG
CALL WRITE_STRING
ADD DI,2*8
MOV AX,TOTAL_FILES ;Total file count.
XOR DX,DX
CALL DISP_STATS
MOV SI,OFFSET FILES_MSG
CALL WRITE_STRING
MOV SI,OFFSET ANYKEY_MSG
CALL WRITE_STRING
CALL HIDE_CURSOR
CALL CLEAR_KEY
CALL GET_KEY ;Pause.
CALL UPDATE_TREE
CALL DISPLAY_MENU
RET
BRANCH_ERR1: MOV SI,OFFSET SIZE_MSG
BRANCH_ERR: CALL DISPLAY_ERROR
POP DS
RET
;----------------------------------------------;
NEW_DRIVE: CALL DOS_CURSOR
CALL RESTORE_DIR ;Restore current path.
NEW_DRIVE2: CALL CLEAR_MENU
MOV SI,OFFSET DRIVE_MSG
CALL WRITE_STRING
MOV DH,ROWS
DEC DH
MOV DL,DRIVE_MSG_LEN - 1
CALL SET_CURSOR
JMP SHORT GET_DRIVE
BAD_DRIVE: CALL BEEP
GET_DRIVE: CALL GET_KEY ;Get new drive letter.
CMP AL,ESC_SCAN
JZ DRIVE_RET
AND AH,5FH ;Capitalize.
CMP AH,"A"
JB BAD_DRIVE
CMP AH,"Z"
JA BAD_DRIVE
PUSH AX
MOV SI,OFFSET DEFAULT_DRIVE
PUSH [SI]
MOV [SI],AH
MOV AL,AH
CALL WRITE_SCREEN
LODSB
CALL DRIVEEXIST ;Does drive exist?
POP WORD PTR DEFAULT_DRIVE
POP AX
JZ GET_DRIVE2
MOV SI,OFFSET DRIVE_ERR
CALL DISPLAY_ERROR
JMP NEW_DRIVE2
GET_DRIVE2: SUB AH,"A"
PUSH WORD PTR TREE_DRIVE[BP]
MOV TREE_DRIVE[BP],AH ;Save new drive.
CALL READ_DRIVE ;Read it.
POP AX
JNC DRIVE_DONE
MOV TREE_DRIVE[BP],AL
CALL BEEP
JMP NEW_DRIVE2 ;If error, prompt again.
DRIVE_DONE: MOV BAR_LINE[BP],0
MOV LISTING_TOP[BP],0
CALL CLEAR_DRIVE ;Display new tree.
CALL DISPLAY_DRIVE
DRIVE_RET: CALL DISPLAY_MENU
RET
;----------------------------------------------;
FIRST_MSG DB "Highlight 1st directory to match and press Enter",0
SECOND_MSG DB "Now highlight 2nd directory and press Enter",0
DIRMATCH: CALL CLEAR_MENU
MOV SI,OFFSET FIRST_MSG
CALL WRITE_STRING
NEXT_1ST: MOV DI,OFFSET D_DISPATCH
MOV CX,D_LEN
CALL DISPATCH ;Get destination.
JC DIRMATCH_END ;Abort if Esc.
CMP AH,CR ;Carriage return if selected.
JNZ NEXT_1ST
CALL DISPLAY_MARK
CALL CLEAR_MENU
MOV SI,OFFSET SECOND_MSG
CALL WRITE_STRING
MOV DI,OFFSET ARGUMENTS
MOV DX,BAR_LINE[BP]
CALL MAKE_DIR
NEXT_2ND: PUSH DI
MOV DI,OFFSET D_DISPATCH
MOV CX,D_LEN
CALL DISPATCH ;Get destination.
POP DI
JC DIRMATCH_END2 ;Abort if Esc.
CMP AH,CR ;Carriage return if selected.
JNZ NEXT_2ND
MOV DX,BAR_LINE[BP]
CALL MAKE_DIR
MOV AX,OFFSET PARAMETER2
JMP SHORT EXEC
DIRMATCH_END2: CALL ERASE_MARK
DIRMATCH_END: CALL UPDATE_TREE
CALL DISPLAY_MENU
RET
;----------------------------------------------;
; INPUT: DX = Line number
MAKE_DIR: PUSH DS
MOV AL,TREE_DRIVE[BP]
ADD AL,"A"
STOSB
MOV AL,":"
STOSB
MOV AX,SIZE TREE
MUL DX
MOV SI,AX
MOV DS,TREE_SEG[BP]
CALL GET_NAME
XOR CX,CX
NEXT_PATH: CMP SI,1
JBE NEXT_MAKE4
PUSH SI
CALL FIND_PARENT
INC CX
JMP NEXT_PATH
NEXT_MAKE4: MOV AL,"\"
STOSB
JCXZ PATH_END
POP SI
NEXT_MAKE5: LODSB
OR AL,AL
JZ LOOP_MAKE
STOSB
JMP NEXT_MAKE5
LOOP_MAKE: LOOP NEXT_MAKE4
PATH_END: MOV AL,SPACE
STOSB
MOV AL,CR
STOSB
DEC DI
POP DS
RET
;----------------------------------------------;
DR: MOV AX,OFFSET PARAMETER1
;----------------------------------------------;
EXEC: MOV COM_LINE_PTR[0],AX
MOV COM_LINE_PTR[2],DS
PUSH DS
PUSH ES
MOV DL,TREE_DRIVE[BP] ;Log on to current tree drive.
CALL SELECT_DISK
MOV AX,SIZE TREE
MUL BAR_LINE[BP]
MOV SI,AX
MOV DS,TREE_SEG[BP]
CALL SELECT_DIR ;And highlighted directory.
PUSH CS
POP DS
MOV BX,OFFSET STACK_POINTER
ADD BX,15
MOV CL,4
SHR BX,CL
MOV AX,CS
ADD BX,AX
MOV AH,4AH ;Deallocate memory.
INT 21H
CALL DOS_CURSOR
MOV BH,COLOR.D ;White on blue.
CALL CLS2 ;Clear screen.
CLI
MOV STACK_SEG,SS ;Save stack segment and pointer.
MOV STACK_PTR,SP
STI
MOV AX,DS:[2CH] ;Retrieve environment segment.
MOV ENVIRONMENT,AX ;And put in paramter block.
MOV DS,AX
XOR AX,AX ;Zero out pointer.
FIND_COMSPEC: MOV SI,AX ;Point to environment offset.
INC AX ;Next offset.
MOV DI,OFFSET COMSPEC ;Find "Comspec=".
MOV CX,8
REP CMPSB
JNZ FIND_COMSPEC
MOV DX,SI ;What follows is Command.com path.
MOV BX,OFFSET ENVIRONMENT ;Point to parameter block.
MOV AX,4B00H ;Execute.
INT 21H
CLI
MOV SP,CS:STACK_PTR ;Restore stack segment and pointer.
MOV SS,CS:STACK_SEG
STI
POP ES ;Restore segment registers.
POP DS
MOV DX,80H ;Restore Disk Transfer Address.
MOV AH,1AH
INT 21H
MOV BX,(12+128+64+64+8) * (1024/16)
MOV AH,4AH ;Reallocate memory.
INT 21H
JNC RESTORE_DISP
MOV DX,OFFSET NOT_ENOUGH ;Exit if not enough.
JMP ERROR_EXIT
RESTORE_DISP: CALL SETUP
RET
;**********************************************;
;----------------------------------------------;
; INPUT: DS:SI = SOURCE_POINTER -> Source; ES:DI = DEST_POINTER -> Destination
; AL and AH = drive; BX = SOURCE_INDEX; BP = Destination index.
; OUTPUT: CF=1 if failed.
COPY: CALL CK_DUPLICATE ;Does branch name already exist?
JNC CK_IF_ROOT
JMP COPY_END2
CK_IF_ROOT: MOV AX,CS
MOV DS,AX
MOV ES,AX
CMP DEST_LINE,0 ;Destination root directory?
JNZ SETUP_DEST
CALL CREATE_TEMP ;If yes, create and delete
JNC SETUP_DEST ; temporary directory in root
CALL FAST_ERROR2 ; to make sure there is room.
JMP SHORT COPY_END2
SETUP_DEST: MOV DL,TREE_DRIVE[BP] ;Log on destination drive.
MOV AL,DL
ADD AL,"A"
MOV DI,OFFSET DEST_SPEC ;Create destination spec.
STOSB
MOV AL,":"
STOSB
MOV SI,DI
CALL GET_DIR
FIND_FILES: MOV BRANCH_FLAG,1 ;Calculate source requirement
MOV AX,BYTES_CLUSTERS[BP] ; using destination cluster
PUSH BP ; size.
PUSH BX
MOV BP,BX
MOV BX,AX
LDS SI,SOURCE_POINTER
CALL CALC_BRANCH
POP BX
POP BP
JC COPY_END2
PUSH CS
POP DS
MOV AX,FREE_CLUSTERS[BP] ;Is there enough room on
CMP AX,CLUSTER_CNT ; target?
JAE ROOM
MOV SI,OFFSET ROOM_MSG
CALL DISPLAY_ERROR
JMP SHORT COPY_END2
ROOM: MOV DL,TREE_DRIVE[BX] ;Log onto source drive.
CALL SELECT_DISK
CALL CONFIRM
JC COPY_END2
CALL CLEAR_MENU
MOV SI,OFFSET COPY_MSG ;Copy message.
CALL WRITE_STRING
MOV REREAD_FLAG,1
PUSH BP
MOV SI,OFFSET DEST_SPEC ;Find end of destination
NEXT_SPEC: LODSB ; spec.
OR AL,AL
JNZ NEXT_SPEC
DEC SI
MOV BP,SI
LDS SI,SOURCE_POINTER ;Log onto source directory.
CALL SELECT_DIR
CALL GET_NAME
MOV DX,SI
CALL GET_DIRS2 ;Copy branch.
POP BP
COPY_END2: RET
;----------------------------------------------;
; OUTPUT: CF=1 if unsuccessful.
MAKE_COPY: MOV DX,SI
CALL CHANGE_DIR ;Next source subdir.
JC MAKE_COPY_END
GET_DIRS2: PUSH BP
PUSH DS
CALL MAKE_SPEC ;Make destination spec.
MOV BP,DI ;Save end of spec pointer
DEC BP ; as start of next sub
MOV AX,CS ; dir appendage.
MOV DS,AX
MOV DX,OFFSET DEST_SPEC
MOV AH,39H ;Create a directory.
INT 21H
JC MAKE_DONE
OR CL,CL ;Is it a hidden directory?
JZ GET_FILE
MOV CX,2 ;If yes, make dest hidden also.
MOV AX,4301H
INT 21H
GET_FILE: MOV DX,OFFSET STAR_DOT_STAR
MOV CX,3 ;Normal, hidden and read-only.
MOV AH,4EH ;Find first matching file.
INT 21H
JC NEXT_SUB3
JMP SHORT MAKE_FILE
NEXT_FILE2: MOV AH,4FH ;Find next matching file.
INT 21H
JC NEXT_SUB3
MAKE_FILE: CALL CREATE ;Copy the file to destination
JNC NEXT_FILE2 ; directory.
JMP SHORT MAKE_DONE
NEXT_SUB3: POP DS
ADD SI,SIZE TREE ;Next record.
NEXT_SUB4: CMP BYTE PTR [SI + 1],"─" ;Is there another branch?
JNZ DIR_DONE2
INC SI
INC SI
CALL MAKE_COPY ;If yes, copy it.
JC MAKE_DONE2
DEC SI
DEC SI
JMP NEXT_SUB4
DIR_DONE2: PUSH DS
PUSH CS
POP DS
MOV DX,OFFSET DOT_DOT ;Return to parent dir.
CALL CHANGE_DIR
MAKE_DONE: POP DS
MAKE_DONE2: POP BP
MAKE_COPY_END: RET
;----------------------------------------------;
; OUTPUT: CF=1 if failed.
CREATE: PUSH SI
MOV DX,DTA.FILE_NAME ;Add file name to destination
CALL MAKE_SPEC ; spec.
MOV AX,3D00H ;Open for reading.
INT 21H
JC CREATE_END
MOV DI,AX ;Read handle.
MOV CL,DS:[DTA.ATTRIBUTE] ;File attribute.
XOR CH,CH
MOV DX,OFFSET DEST_SPEC
MOV AH,3CH ;Create file.
INT 21H
JC CLOSE_READ
MOV SI,AX ;Write handle.
MOV DS,BUFFER_SEG ;Read/write buffer.
XOR DX,DX ;Offset zero.
COPY_READ: MOV BX,DI ;Retrieve read handle.
MOV CX,0FFFFH ;Full read.
MOV AH,3FH
INT 21H ;Read source file.
JC CLOSE_WRITE ;If carry, failed; exit.
OR AX,AX ;If zero bytes read, done.
JZ CHANGE_DATE ;Change target date to source.
MOV CX,AX ;Else, bytes read into counter.
MOV BX,SI ;Retrieve write handle.
MOV AH,40H
INT 21H ;Write the buffer to disk.
JC CLOSE_WRITE ;If failed, exit.
CMP CX,AX ;Write same number as read?
STC ;Assume no.
JNZ CLOSE_WRITE ;If no, failed; exit.
INC CX ;Was it a full read?
JZ COPY_READ ;If yes, there must be more.
CHANGE_DATE: PUSH CS
POP DS
MOV BX,SI ;Write handle.
MOV CX,DS:[DTA.FILE_TIME] ;Else, make time/date same
MOV DX,DS:[DTA.FILE_DATE] ; as source.
MOV AX,5701H
INT 21H
CLOSE_WRITE: PUSHF ;Save error if any.
MOV BX,SI ;Close write file.
MOV AH,3EH
INT 21H
POP AX ;Retrieve flags.
JC CLOSE_READ ;Close successful?
XCHG AH,AL ;If no, exit with error, else
SAHF ; retrieve write state.
CLOSE_READ: PUSHF ;Save flags.
MOV BX,DI ;Close read file.
MOV AH,3EH
INT 21H
POPF ;Write file status.
CREATE_END: POP SI
RET
;----------------------------------------------;
; OUTPUT: CL=directory attribute "H" if hidden.
MAKE_SPEC: PUSH SI
MOV SI,DX
MOV DI,BP ;Current spec end.
MOV AL,"\"
CMP BYTE PTR ES:[DI-1],AL ;Add backslash delimiter
JZ NEXT_MAKE2 ; if necessary.
STOSB
NEXT_MAKE2: LODSB ;Add sub dir or file name.
STOSB
OR AL,AL
JNZ NEXT_MAKE2
LODSB
MOV CL,AL ;Return directory attribute.
POP SI
RET
;----------------------------------------------;
; OUTPUT: DS:SI = SOURCE_POINTER -> Source; ES:DI = DEST_POINTER -> Destination
; AL and AH = drive; BX = SOURCE_INDEX; BP = Destination index.
; CF=1 if abort.
GET_DEST: MOV AX,BAR_LINE[BP]
OR AX,AX ;Root directory?
JNZ GET_DEST2
MOV SI,OFFSET CANT_ROOT
CALL DISPLAY_ERROR
STC
JMP SHORT DEST_END
GET_DEST2: CALL DISPLAY_MARK
CALL CLEAR_MENU
MOV SI,OFFSET DEST_MSG ;Destination message.
CALL WRITE_STRING
GET_DEST3: PUSH ACTIVE_TREE
NEXT_DEST2: MOV DI,OFFSET D_DISPATCH
MOV CX,D_LEN
CALL DISPATCH ;Get destination.
JC ABORT_DEST ;Abort if Esc.
CMP AH,CR ;Carriage return if selected.
JNZ NEXT_DEST2
CALL ERASE_MARK
POP BX ;BX=Source Tree
MOV SOURCE_INDEX,BX
MOV AX,SIZE TREE
MUL BAR_LINE[BP]
MOV DI,AX ;Destination address.
MOV DEST_LINE,AX
MOV AX,TREE_SEG[BP] ;Destination segment.
MOV ES,AX
MOV DEST_SEG,AX
MOV AL,TREE_DRIVE[BX] ;Source drive.
MOV AH,TREE_DRIVE[BP] ;Destination drive.
MOV SI,SOURCE_LINE ;Source address.
MOV DS,TREE_SEG[BX] ;Source seg.
CLC
DEST_END: RET
ABORT_DEST: CALL ERASE_MARK
ADD SP,2
STC
RET
;----------------------------------------------;
CONFIRM: PUSH DS
CALL HIDE_CURSOR
CALL CLEAR_MENU
MOV SI,MSG
CALL WRITE_STRING
PUSH BP
MOV BP,SOURCE_INDEX
MOV DX,SOURCE_BAR
CALL DISPLAY_PATH
POP BP
MOV SI,OFFSET TO_MSG
CALL WRITE_STRING
MOV DX,BAR_LINE[BP]
CALL DISPLAY_PATH
MOV SI,OFFSET YES_NO
CALL WRITE_STRING
CALL CLEAR_KEY
CALL GET_KEY
CMP AL,Y_SCAN
CLC
JZ CONFIRM_END
STC
CONFIRM_END: PUSHF
CALL DOS_CURSOR
POPF
POP DS
RET
;----------------------------------------------;
; INPUT: DX = Bar line.
DISPLAY_PATH: PUSH DI
MOV DI,OFFSET ARGUMENTS
CALL MAKE_DIR
MOV DX,DI
POP DI
MOV SI,OFFSET ARGUMENTS
CMP DX,OFFSET ARGUMENTS + 26
JBE DISPLAY_PATH2
PUSH DX
MOV BYTE PTR ARGUMENTS + 2,0
CALL WRITE_STRING
MOV SI,OFFSET DOT_DOT
CALL WRITE_STRING
POP SI
SUB SI,22
DISPLAY_PATH2: CALL WRITE_STRING
RET
;----------------------------------------------;
; INPUT: DS:SI -> Directory; BP=Index tree; BX=Bytes/cluster; Recursive Proc.
; OUTPUT: CLUSTER_CNT=Total clusters of files; CF=1 if unsucessful.
CALC_BRANCH: PUSH BX
CALL DOS_CURSOR
MOV CS:TOTAL_FILES,0 ;Initialize variables.
MOV CS:CLUSTER_CNT,0
MOV DL,TREE_DRIVE[BP] ;Log onto target drive
CALL SELECT_DISK ; and directory.
CALL SELECT_DIR
JC CALC_END
CALL GET_NAME
CALL GET_DIRS ;Get size of branch.
PUSHF
CALL HIDE_CURSOR
POPF
POP BX
CALC_END: RET
;----------------------------------------------;
CALC_DIR: MOV DX,SI
CALL CHANGE_DIR ;Log on to next subdir.
JC DIR_END
GET_DIRS: PUSH DS
MOV AX,CS
MOV DS,AX
MOV ENTRY_CNT,2 ;Dot and dot dot entries.
MOV DX,OFFSET STAR_DOT_STAR
MOV CX,3 ;Normal, hidden and read-only.
CMP BRANCH_FLAG,1
JNZ FIND_FIRST
OR CX,10H ;Find sub dirs if branch count.
FIND_FIRST: MOV AH,4EH
INT 21H
JC DIR_SPACE
JMP SHORT CK_FILE
NEXT_FILE: MOV AH,4FH
INT 21H
JC DIR_SPACE
CK_FILE: CMP BYTE PTR DS:[DTA.FILE_NAME],"." ;Skip dot and dot dot.
JZ NEXT_FILE
INC ENTRY_CNT
TEST DS:[DTA.ATTRIBUTE],10H
JNZ NEXT_FILE
INC TOTAL_FILES
MOV AX,DS:[DTA.SIZE_LOW] ;Calculate cluster bytes
MOV DX,DS:[DTA.SIZE_HIGH] ; for file.
CALL CALC_TOTAL
JMP NEXT_FILE
DIR_SPACE: MOV AX,ENTRY_CNT ;Calculate cluster bytes
MOV CX,32 ; for directory.
MUL CX
CALL CALC_TOTAL
ADD SI,SIZE TREE ;Next subdir.
CMP BRANCH_FLAG,1
POP DS
CLC
JNZ DIR_END
NEXT_SUB2: CMP BYTE PTR [SI + 1],"─"
JNZ DIR_DONE
INC SI
INC SI
CALL CALC_DIR ;Next branch.
JC DIR_END
DEC SI
DEC SI
JMP NEXT_SUB2
DIR_DONE: PUSH DS
PUSH CS
POP DS
MOV DX,OFFSET DOT_DOT ;Parent directory.
CALL CHANGE_DIR
POP DS
DIR_END: RET
;----------------------------------------------;
; INPUT: DX:AX = file or subdir size; BX = Destination bytes/cluster.
CALC_TOTAL: DIV BX
OR DX,DX
JZ SUB_TOTAL
INC AX ;Round up.
SUB_TOTAL: ADD CLUSTER_CNT,AX
RET
;----------------------------------------------;
; INPUT: DS:SI -> Source; ES:DI -> Destination; AL and AH = drive.
; Tree's contents of DS:SI and ES:DI are identical.
FAST_MOVE: OR DI,DI ;Is destination, root?
JNZ CK_IF_SAME
CMP BRANCH[SI],"├" ;Is source root directory?
JNZ DO_FAST
JMP FAST_ERROR
CK_IF_SAME: MOV CX,BRANCH_NO[SI]
CMP CX,BRANCH_NO[DI]
JNZ DO_FAST
CMP SI,DI ;Source line, destination line.
JNZ SAVE_POINTER
JMP FAST_ERROR ;Can't graft to same.
SAVE_POINTER: PUSH SI
JB CK_ANCESTOR ;If below, see if in same branch.
CK_PARENT: MOV DX,CS:SOURCE_BAR ;Else, see if destination is
CALL GET_NAME ; parent dir.
CALL FIND_PARENT
POP SI
CMP DX,CS:BAR_LINE[BP]
JNZ DO_FAST
JMP FAST_ERROR
CK_ANCESTOR: MOV SI,DI ;Can't graft farther out
CALL GET_NAME ; in same branch.
MOV DX,CS:BAR_LINE[BP]
NEXT_ANCESTOR: CALL FIND_PARENT
CMP DX,CS:SOURCE_BAR
JNZ CK_ABOVE
JMP ANCESTOR_ERR
CK_ABOVE: JA NEXT_ANCESTOR
POP SI
DO_FAST: CALL CK_DUPLICATE ;Can't graft if name already
JNC FAST_READY ; exists.
JMP FAST_END
FAST_READY: MOV AX,CS
MOV DS,AX
MOV ES,AX
CALL CONFIRM
JNC WORKING
JMP FAST_END
WORKING: CALL CLEAR_MENU
MOV SI,OFFSET WORKING_MSG
CALL WRITE_STRING
CALL CREATE_TEMP ;Create/delete to make sure
JNC FAST_BOOT ; there is a free space.
JMP FAST_ERROR2
FAST_BOOT: CALL GET_BOOT ;Get boot and fat again to
JC LILLY_ERR ; make sure not media switch.
CALL GET_FAT
LILLY_ERR: JC FAST_ERROR
MOV REREAD_FLAG,1
LES DI,SOURCE_POINTER ;Find source dot dot entry.
MOV DX,ES:TREE_CLUSTER[DI]
MOV BX,OFFSET DOT_DOT
MOV DIR_LEN,2
CALL FIND_DIR
JC LOGICAL_ERR
MOV AX,ES:DIR_CLUSTER[DI] ;Save old starting cluster.
MOV OLD_PARENT,AX
LDS SI,DEST_POINTER
MOV AX,TREE_CLUSTER[SI]
PUSH CS
POP DS
MOV NEW_PARENT,AX
MOV ES:DIR_CLUSTER[DI],AX ;Change to new parent.
CALL WRITE_SECTOR
CALL MAKE_SOURCE ;Make source dir name into a
MOV DX,OLD_PARENT ; directory entry record type
MOV BX,OFFSET SOURCE_RECORD ;Find it.
MOV DIR_LEN,11
CALL FIND_DIR
JC FAST_ERROR
CALL SAVE_SOURCE ;Save the directory entry.
MOV ES:DIR_NAME[DI],0E5H ;Delete existing.
CALL WRITE_SECTOR
MOV DX,NEW_PARENT ;Find an erased entry (our
MOV BX,OFFSET E5 ; temporary we created above.)
MOV DIR_LEN,1
CALL FIND_DIR
JC FAST_ERROR
MOV SI,OFFSET SOURCE_RECORD ;Place directory entry in it's
MOV CX,SIZE DIR_ENTRY ; place.
REP MOVSB
CALL WRITE_SECTOR
CLC
FAST_END: RET
ANCESTOR_ERR: POP SI
FAST_ERROR: MOV SI,OFFSET ILLOGICAL_MSG
FAST_ERROR2: PUSH CS
POP DS
CALL DISPLAY_ERROR
RET
LOGICAL_ERR: MOV SI,OFFSET LOGICAL_MSG
JMP FAST_ERROR2
;----------------------------------------------;
DELETE: PUSH CS
POP DS
CALL CLEAR_MENU
MOV SI,OFFSET DELETE_MSG
CALL WRITE_STRING
MOV REREAD_FLAG,1
LDS SI,CS:SOURCE_POINTER
CALL SELECT_DIR
JC DELETE_END
CALL GET_NAME
JMP SHORT FIND_TOP
DO_DELETE: MOV DX,SI ;Change to next subdir.
CALL CHANGE_DIR
FIND_TOP: PUSH DS
PUSH SI
ADD SI,SIZE TREE
NEXT_TOP: CMP BYTE PTR [SI + 1],"─" ;Is there a branch?
JNZ DO_FILES
INC SI
INC SI
CALL DO_DELETE ;If yes, delete branch also.
JC DELETE_DONE
DEC SI
DEC SI
JMP NEXT_TOP
DO_FILES: MOV AX,CS
MOV DS,AX
MOV DX,OFFSET STAR_DOT_STAR ;Find all files.
MOV CX,1 OR 2 ;Hidden, read-only and normal.
MOV AH,4EH
INT 21H
JC REMOVE_SUBDIR
JMP SHORT CK_ATTR
NEXT_DELETE: MOV AH,4FH
INT 21H
JC REMOVE_SUBDIR
CK_ATTR: MOV DX,DTA.FILE_NAME
TEST DS:[DTA.ATTRIBUTE],1 OR 2
JZ DELETE_FILE
XOR CX,CX
MOV AX,4301H ;Make normal attribute if
INT 21H ; not already so can be deleted.
JC DELETE_DONE
DELETE_FILE: MOV AH,41H ;Delete file.
INT 21H
JC DELETE_DONE
JMP NEXT_DELETE
REMOVE_SUBDIR: MOV DX,OFFSET DOT_DOT ;Return to parent directory.
CALL CHANGE_DIR ;(Can't remove a directory
POP DX ; while logged onto the
POP DS ; directory.)
MOV CX,10H OR 2 ;Is directory hidden?
MOV AH,4EH
INT 21H
JC DELETE_END
TEST CS:[DTA.ATTRIBUTE],2
JZ REMOVE_IT
XOR CX,CX
MOV AX,4301H ;If yes, make normal.
INT 21H
REMOVE_IT: MOV AH,3AH ;Remove subdirectory.
INT 21H
JMP SHORT DELETE_END
DELETE_DONE: POP DX
POP DS
DELETE_END: RET
;----------------------------------------------;
; OUTPUT: CF=1 if can't create subdirectory.
CREATE_TEMP: MOV DX,OFFSET TEMP
MOV AH,39H ;Create subdirectory.
INT 21H
JC TEMP_END
REMOVE_DIR: MOV AH,3AH ;Remove subdirectory.
INT 21H
CALL DISK_FREE ;Update disk stats.
TEMP_END: MOV SI,OFFSET SUBDIR_MSG
RET
;----------------------------------------------;
; INPUT: DS:SI -> Source; DX = Sibling bar.
; OUTPUT: DS:SI -> Parent; DX = Parent bar.
FIND_PARENT: DEC SI
DEC SI
NEXT_PARENT: SUB SI,SIZE TREE
DEC DX
CMP SI,1
JZ PARENT_END
CMP BYTE PTR [SI],"│"
JZ NEXT_PARENT
CMP BYTE PTR [SI],"├"
JZ NEXT_PARENT
PARENT_END: RET
;----------------------------------------------;
; OUTPUT: CF=1 if dest dir has a subdir or file with name same as source dir.
CK_DUPLICATE: PUSH DS
CALL DOS_CURSOR
MOV DL,CS:TREE_DRIVE[BP]
CALL SELECT_DISK
LDS SI,CS:DEST_POINTER ;Log on to destination.
CALL SELECT_DIR
MOV SI,OFFSET LOGICAL_MSG
JC DUP_ERR
LDS SI,CS:SOURCE_POINTER ;Source name.
CALL GET_NAME
MOV DX,SI
MOV CX,10H OR 3 ;Subdir, hidden, read-only
MOV AH,4EH ; or normal.
INT 21H ;Does it already exist?
CMC
JNC DUP_END
MOV SI,OFFSET DUPLICATE_MSG
DUP_ERR: CALL FAST_ERROR2
STC
DUP_END: POP DS
RET
;----------------------------------------------;
; INPUT: DS:SI -> Record start. OUTPUT: DS:SI -> Directory name.
GET_NAME: OR SI,SI ;Root?
JZ GET_NAME_END
NEXT_NAME: LODSB
CMP AL,"─"
JNZ NEXT_NAME
GET_NAME_END: RET
;----------------------------------------------;
DISPLAY_MARK: PUSH DS
MOV AX,BAR_LINE[BP]
MOV SOURCE_BAR,AX
MOV CX,SIZE TREE
MUL CX
MOV DI,AX
MOV SOURCE_LINE,DI
MOV AX,TREE_SEG[BP]
MOV MARK_SEG,AX
MOV DS,AX
MOV MARK[DI],LITTLE_ARROW ;Mark selected line with
POP DS ; a little arrow.
CALL DISPLAY_TREE
RET
ERASE_MARK: PUSH DS
LDS DI,SOURCE_POINTER
XOR AL,AL
OR DI,DI
JNZ REMOVE_ARROW
MOV AL,"\"
REMOVE_ARROW: MOV MARK[DI],AL ;Remove little arrow.
POP DS
RET
;----------------------------------------------;
; INPUT: DX = Starting cluster; DS:BX -> Dirname to find; DIR_LEN = length.
; OUTPUT: ES:DI -> matching record; DX = Sector; CF=1 if failed.
FIND_DIR: MOV AX,CLUSTER_SEG
MOV ES,AX
OR DX,DX ;Special case if root.
JZ FIND_ROOT
NEXT_SOURCE: MOV LAST_CLUSTER,DX
MOV AX,DX
DEC AX
DEC AX
MOV CL,BPB_SectorsPerCluster
XOR CH,CH
MUL CX
ADD AX,DATA_SECTOR
ADC DX,0
MOV STARTING_SECTOR[2],DX
MOV DX,AX
NEXT_SECTOR2: PUSH CX
CALL FIND ;Find the record.
POP CX
JNC FIND_DIR_END
ADD DX,1 ;Next sector.
ADC STARTING_SECTOR[2],0
LOOP NEXT_SECTOR2
CALL CK_FAT
JNC NEXT_SOURCE
FIND_DIR_END: RET
;----------------------------------------------;
; Special case for root dirs.
FIND_ROOT: MOV DX,ROOT_SECTOR ;Root sector is below
MOV CX,ROOT_SECTORS ; data sectors.
MOV STARTING_SECTOR[2],0
NEXT_ROOT2: PUSH CX
CALL FIND
POP CX
JNC ROOT_END
INC DX
LOOP NEXT_ROOT2
STC
ROOT_END: RET
;----------------------------------------------;
; INPUT: ES -> Cluster seg; DS:BX -> Dirname to find;
; DX = Sector; DIR_LEN = length.
; OUTPUT: CF=0 If found; Else, DX = Next sector.
FIND: PUSH BX
XOR BX,BX
MOV CX,1
MOV AX,CLUSTER_SEG
MOV TRANSFER_ADDRESS[2],AX
MOV DS,AX
CALL READ_DISK ;Read the sector.
PUSH CS
POP DS
POP BX
JC FIND_DONE
XOR DI,DI
MOV CX,SECTOR_RECORDS ;Records per sector.
JMP SHORT FIRST_TEMP
NEXT_TEMP: ADD DI,SIZE DIR_ENTRY ;Next entry.
FIRST_TEMP: PUSH CX
PUSH DI
MOV SI,BX
MOV CX,DIR_LEN
REPZ CMPSB ;Is it a match?
POP DI
POP CX
JNZ FIND_TEMP
TEST ES:DIR_ATTR[DI],10H ;Is it a subdir?
CLC
JNZ FIND_END
FIND_TEMP: LOOP NEXT_TEMP
FIND_DONE: STC
FIND_END: RET
;----------------------------------------------;
MAKE_SOURCE: PUSH DS
LDS SI,SOURCE_POINTER
CALL GET_NAME
PUSH CS
POP ES
MOV DI,OFFSET SOURCE_RECORD ;Store name in directory
MOV CX,8 ;entry format that removes
NEXT_MAKE: LODSB ; the delimiting dot
CMP AL,"." ; and pads with spaces.
JZ EXT2
OR AL,AL
JZ EXT1
STOSB
LOOP NEXT_MAKE
CMP BYTE PTR [SI],"."
JNZ EXT2
INC SI
JMP SHORT EXT2
EXT1: DEC SI
EXT2: MOV AL,SPACE
REP STOSB
MOV CX,3
NEXT_EXT: LODSB
OR AL,AL
JZ EXT_DONE
STOSB
LOOP NEXT_EXT
EXT_DONE: MOV AL,SPACE
REP STOSB
POP DS
RET
;----------------------------------------------;
; INPUT: ES:DI -> Source directory entry.
SAVE_SOURCE: PUSH DS ;Save directory record.
PUSH ES
PUSH DI
MOV AX,ES
MOV DS,AX
MOV SI,DI
MOV DI,OFFSET SOURCE_RECORD
PUSH CS
POP ES
MOV CX,SIZE DIR_ENTRY
REP MOVSB
POP DI
POP ES
POP DS
RET
;----------------------------------------------;
; INPUT: SI -> Message.
DISPLAY_ERROR: CALL CLEAR_MENU
CALL WRITE_STRING
MOV SI,OFFSET ANYKEY_MSG
CALL WRITE_STRING
CALL BEEP
CALL HIDE_CURSOR
CALL CLEAR_KEY
CALL GET_KEY ;Pause.
CALL DISPLAY_MENU
STC
RET
;----------------------------------------------;
; OUTPUT: DI -> Start of window; BH=Menu color.
CLEAR_MENU: MOV AL,ROWS
DEC AL ;Menu row.
MOV DH,AL
MOV CH,AL
XOR CL,CL
MOV DL,79 ;Menu width.
PUSH AX
MOV BH,COLOR.C ;Error color.
CALL DO_CLEAR
POP AX
XOR AH,AH
CALL CALC_ADDR
RET
;----------------------------------------------;
; INPUT: BP=Tree Index; AX=DIR_COUNT; CX=BAR_LINE; DX=LISTING_TOP
; DI=Listing Bottom; SI=Listing Length
UP: MOV BX,-1 ;Up a line.
JMP SHORT DO_ARROW
DOWN: MOV BX,1 ;Down a line.
DO_ARROW: ADD CX,BX
CK_ARROW1: OR CX,CX
JNS CK_ARROW2
XOR CX,CX
CK_ARROW2: CMP CX,AX
JBE CK_PAGE
MOV CX,AX
CK_PAGE: CMP CX,DX
JB ADJUST_PAGE
CMP CX,DI
JBE NAVIGATE_DONE
ADJUST_PAGE: ADD DX,BX
JMP SHORT NAVIGATE_DONE
PGUP: NEG SI ;Up a page
PGDN: ADD CX,SI ;Down a page.
ADD DX,SI
JNS CK_PGDN
XOR DX,DX
CK_PGDN: XOR BX,BX
CMP DX,AX
JBE CK_ARROW1
SUB DX,SI
JMP CK_ARROW1
HOME: XOR CX,CX ;Home
XOR DX,DX
JMP SHORT NAVIGATE_DONE
END_KEY: MOV CX,AX ;End.
DEC SI
SUB DI,SI
CMP DI,DX
JB NAVIGATE_DONE
MOV DX,AX
SUB DX,SI
JNS NAVIGATE_DONE
XOR DX,DX
NAVIGATE_DONE: MOV BAR_LINE[BP],CX
MOV LISTING_TOP[BP],DX
CALL DISPLAY_TREE
NAVIGATE_END: RET
;----------------------------------------------;
TAB_KEY: XOR ACTIVE_TREE,2
JMP SHORT CHANGE_TREE
LEFT: MOV ACTIVE_TREE,0
JMP SHORT CHANGE_TREE
RIGHT: MOV ACTIVE_TREE,2
CHANGE_TREE: MOV BP,ACTIVE_TREE
XOR BP,2
CALL DISPLAY_DRIVE
XOR BP,2
CALL DISPLAY_DRIVE
RET
;----------------------------------------------;
; INPUT: BP = Tree Index. OUTPUT: CF = 0 if successful.
READ_DRIVE: CALL DOS_CURSOR
MOV DL,TREE_DRIVE[BP] ;Retrieve drive.
INC DL ;Adjust.
MOV AH,32H ;Retrieve Disk Block.
INT 21H
MOV DH,[BX] ;BIOS disk block drive no.
PUSH CS
POP DS
OR AL,AL ;Does disk request exist?
JZ EXISTS
READ_ERROR: PUSH CS
POP ES
STC
RET
EXISTS: DEC DL
CMP DL,DH ;Substitued?
JNZ READ_ERROR
CALL GET_BOOT
JC READ_ERROR
MOV SI,OFFSET UNNAMED
MOV DI,VOLUME_NAME[BP]
MOV CX,VOLUME_LEN
REP MOVSB
CALL GET_ROOT
JZ READ_END ;If none, done here.
CALL GET_FAT
JNC GET_SUBS
JMP READ_ERROR
;----------------------------------------------;
; INPUT: BX = Root directory entries.
GET_SUBS: XOR SI,SI
UP_LEVEL: MOV CX,BX ;Retrieve record count.
ADD TREE_LEVEL,2 ;Bump point to next level.
MOV BX,TREE_LEVEL
MOV [WORD PTR LEVEL_ADDRESS + BX],DI ;Save level address.
XOR BX,BX ;Zero out entry counter.
NEXT_SECTOR: MOV DX,ES:CLUSTER[SI] ;Retrieve starting cluster.
NEXT_CLUSTER: MOV LAST_CLUSTER,DX ;And save.
PUSH CX ;Save some registers.
PUSH BX
CALL READ_CLUSTER ;Retrieve directory sector.
POP BX
MOV DX,ES:ENTRY[SI] ;Retrieve parent entry number.
PUSH SI
MOV CX,CLUST_RECORDS ;Retrieve records per cluster.
CALL STORE_RECORD ;Store subdirectories.
POP SI
POP CX
JC END_LEVEL ;If carry, last entry found.
CALL CK_FAT ;Else, get next cluster.
JNC NEXT_CLUSTER ;If carry, last cluster found.
END_LEVEL: ADD SI,SIZE TREE_DATA ;Else, point to next root entry.
LOOP NEXT_SECTOR ;Find next subdirectory.
CMP SI,DI ;Did we find any at this level?
JNZ UP_LEVEL ;If yes, continue.
MOV AX,0FFFFH ;Else, done; mark with signature.
STOSB
MOV BX,TREE_LEVEL
MOV [WORD PTR LEVEL_ADDRESS+BX+2],AX ;Mark level address.
CALL SORT ;Alphabetize by level.
READ_END: CALL DISK_FREE ;Get disk stats.
JC READ_DONE
CALL FORMAT ;Format into tree.
CALL CK_VARS
PUSH CS
POP ES
CLC
READ_DONE: RET
;----------------------------------------------;
; INPUT: BP = Tree Index.
; OUTPUT: BIG_DISK_FLAG=1 if >32 meg; CF=1 if failed.
GET_BOOT: MOV BIG_DISK_FLAG,0 ;Assume small disk (<32Meg.)
NEXT_BOOT: PUSH DS
MOV CX,1 ;Retrieve one boot record.
XOR DX,DX ;Sector zero.
MOV STARTING_SECTOR[2],0 ;High half.
XOR BX,BX ;Offset zero transfer address.
MOV AX,CLUSTER_SEG
MOV TRANSFER_ADDRESS[2],AX
MOV DS,AX
MOV BYTE PTR [BX],0 ;Zero to detect bad read.
CALL READ_DISK
POP DS
JNC STORE_BOOT
CMP BIG_DISK_FLAG,1
MOV BIG_DISK_FLAG,1
JNZ NEXT_BOOT
STC
JMP SHORT BOOT_END
STORE_BOOT: MOV DS,CLUSTER_SEG
CMP BYTE PTR DS:[0],0
STC
JZ BOOT_END
MOV SI,11 ;Start of BPB in boot record.
MOV DI,OFFSET BPB
MOV CX,BPB_LENGTH
REP MOVSB
PUSH CS
POP DS
MOV FAT_TYPE,FAT_16_BIT ;12 or 16 bit FAT?
MOV CL,BPB_SectorsPerCluster
XOR CH,CH
MOV AX,BPB_TotalSectors
OR AX,AX
JZ GET_RECORDS
XOR DX,DX
DIV CX
CMP AX,4086
JAE GET_RECORDS
MOV FAT_TYPE,FAT_12_BIT
GET_RECORDS: MOV AX,BPB_BytesPerSector ;Retrieve bytes per sector.
XOR DX,DX
MOV BX,SIZE DIR_ENTRY
DIV BX
MOV SECTOR_RECORDS,AX
MUL CX ;Times sector per cluster.
MOV CLUST_RECORDS,AX
MOV AX,BPB_RootEntries ;Retrieve number of root entries.
MUL BX
DIV BPB_BytesPerSector ;Divide by bytes per sector.
MOV ROOT_SECTORS,AX ;That's root length in sectors.
MOV AX,BPB_ReservedSectors
MOV CL,BPB_NumberOfFats
XOR CH,CH
ADD_FATS: ADD AX,BPB_SectorsPerFat
LOOP ADD_FATS
MOV ROOT_SECTOR,AX
ADD AX,ROOT_SECTORS ;AX = directory sectors.
MOV DATA_SECTOR,AX
CLC
BOOT_END: PUSH CS
POP DS
RET
;----------------------------------------------;
; OUTPUT: BX=Root directory entires; ZF=1 if none.
GET_ROOT: MOV ES,TREE_DATA_SEG[BP]
XOR DI,DI ;Point to database storage.
MOV BYTE PTR ES:[DI],0FFH ;Initialize with end signature.
MOV WORD PTR LEVEL_ADDRESS,DI ;Store starting level address.
XOR BX,BX ;Entry number.
MOV TREE_LEVEL,BX ;Tree level zero also.
MOV DX,ROOT_SECTOR
MOV CX,ROOT_SECTORS
NEXT_ROOT: PUSH CX
PUSH BX
MOV CX,1
XOR BX,BX ;Transfer address offset.
MOV STARTING_SECTOR[2],BX ;High half is zero.
MOV AX,CLUSTER_SEG
MOV TRANSFER_ADDRESS[2],AX
MOV DS,AX
CALL READ_DISK
PUSH CS
POP DS
POP BX
MOV CX,SECTOR_RECORDS
CALL STORE_RECORD ;Store them in database.
POP CX
JC GET_ROOT_END ;Done when first 0 entry
INC DX ; encountered.
LOOP NEXT_ROOT
GET_ROOT_END: OR BX,BX ;BX returns with no. entries.
RET
;----------------------------------------------;
; OUTPUT: CF = 1 if failed. BX preserved.
GET_FAT: PUSH BX ;Save entry count.
MOV DX,BPB_ReservedSectors
MOV CX,BPB_SectorsPerFat
MOV BX,FAT_SEG
NEXT_FAT: PUSH BX
PUSH CX
PUSH DX
MOV STARTING_SECTOR[2],0 ;High half.
MOV TRANSFER_ADDRESS[2],BX
MOV DS,BX
XOR BX,BX
MOV CX,1
CALL READ_DISK
PUSH CS
POP DS
JNC FAT_OK
ADD SP,6
STC
JMP SHORT FAT_END
FAT_OK: POP DX
INC DX
MOV AX,BPB_BytesPerSector
MOV CL,4
SHR AX,CL ;Paragraphs per sector.
POP CX
POP BX
ADD BX,AX
LOOP NEXT_FAT
CLC
FAT_END: POP BX
RET
;----------------------------------------------;
; INPUT: CX = No. of Sectors; DX = Logical Sector; DS:BX -> Transfer Address.
; OUTPUT: CF = 0 if successful; CF = 1 if failed.
READ_DISK: mov ah,0dh
int 21h
PUSH BP ;Call destroys all registers
PUSH DX
PUSH SI ; except segment registers
PUSH DI ; so we have to preserve.
MOV AL,CS:TREE_DRIVE[BP] ;Retrieve drive.
CMP CS:BIG_DISK_FLAG,0
JZ DIRECT_READ
PUSH CS
POP DS
MOV TRANSFER_ADDRESS[0],BX
MOV BX,OFFSET PACKET
MOV STARTING_SECTOR[0],DX
MOV NUMBER_OF_SECTORS,CX
MOV CX,-1
DIRECT_READ: INT 25H ;Read the sectors.
POP AX ;Get rid off flags left on stack.
POP DI
POP SI
POP DX
POP BP
mov ah,0dh
int 21h
RET
;----------------------------------------------;
; INPUT: DX = Logical Sector.
; OUTPUT: CF = 0 if successful; CF = 1 if failed.
WRITE_SECTOR: mov ah,0dh
int 21h
PUSH BP ;Call destroys all registers
PUSH DS
XOR BX,BX
MOV CX,1
MOV AL,TREE_DRIVE[BP] ;Retrieve drive.
CMP BIG_DISK_FLAG,0
MOV DS,CLUSTER_SEG
JZ DIRECT_WRITE
PUSH CS
POP DS
MOV BX,OFFSET PACKET
MOV CX,-1
DIRECT_WRITE: INT 26H ;Write the sector.
POP AX ;Get rid off flags left on stack.
POP DS
POP BP
mov ah,0dh
int 21h
RET
;----------------------------------------------;
READ_CLUSTER: PUSH BX
MOV AX,LAST_CLUSTER ;Retrieve cluster number.
DEC AX ;Subtract to reflect cluster
DEC AX ; 2 as first logical data sector.
MOV CL,BPB_SectorsPerCluster ;Retrieve sectors per cluster.
XOR CH,CH
MUL CX ;Multiply to get sector start.
ADD AX,DATA_SECTOR ;Add start of first data cluster.
ADC DX,0
MOV STARTING_SECTOR[2],DX ;High half.
MOV DX,AX
XOR BX,BX
MOV AX,CLUSTER_SEG
MOV TRANSFER_ADDRESS[2],AX
MOV DS,AX
CALL READ_DISK ;And get data.
PUSH CS
POP DS
POP BX
RET
;------------------------------------------------------------------;
; This subroutine stores the level number, entry number, parent ;
; entry number, the directory name and attribute byte in database. ;
;------------------------------------------------------------------;
; INPUT: CX=Directory records; BX=Level; DX=Parent; ES:DI -> Tree_data storage.
STORE_RECORD: PUSH DS
XOR SI,SI
MOV DS,CLUSTER_SEG
NEXT_RECORD: CMP BYTE PTR [SI],0 ;No more entries?
JZ STORE_RETURN ;If yes, done here.
TEST DIR_ATTR[SI],8 ;Is it a volume label?
JNZ STORE_VOLUME
TEST DIR_ATTR[SI],10H ;Is it a directory?
JZ LOOP_STORE ;If no, next entry.
CMP DIR_NAME[SI],0E5H ;Is it a removed directory?
JZ LOOP_STORE ;If yes, skip.
CMP DIR_NAME[SI],"." ;Is it a dot or dot-dot entry?
JZ LOOP_STORE ;If yes, skip.
PUSH SI
MOV AX,CS:TREE_LEVEL ;Store level number.
STOSB
MOV AX,BX ;Store entry number.
STOSW
MOV AX,DX ;Store parent entry number.
STOSW
PUSH CX
MOV CX,11 ;Store 11 bytes of name.
REP MOVSB
POP CX
ADD SI,15 ;Store starting cluster.
MOVSW
INC BX ;Increment entry count.
POP SI
XOR AX,AX ;Assume not a hidden directory.
TEST DIR_ATTR[SI],2 ;Is it hidden?
JZ STORE_ATTRIB ;If no, store null.
MOV AX,"H" ;Else, store "H".
STORE_ATTRIB: STOSW
LOOP_STORE: ADD SI,SIZE DIR_ENTRY ;Point to next entry.
LOOP NEXT_RECORD
POP DS
CLC ;Indicate full sector.
RET
STORE_RETURN: STC
POP DS
RET
STORE_VOLUME: CMP DIR_NAME [SI],0E5H ;Is it a removed volume?
JZ LOOP_STORE ;If yes, skip.
PUSH SI
PUSH DI
PUSH ES
PUSH CX
PUSH CS
POP ES
MOV DI,CS:VOLUME_NAME[BP]
MOV CX,VOLUME_LEN
REP MOVSB
POP CX
POP ES
POP DI
POP SI
JMP LOOP_STORE
;-------------------------------------------------------------------;
; This subroutine finds the next cluster in the chain from the FAT. ;
;-------------------------------------------------------------------;
; OUTPUT: DX = Cluster; CF = 0 if not last cluster; CF = 1 if no more clusters.
CK_FAT: PUSH ES ;Save some registers.
PUSH BX
PUSH CX
MOV AX,LAST_CLUSTER ;Retrieve last cluster.
PUSH AX ;Save.
CMP FAT_TYPE,FAT_16_BIT
JZ SIXTEEN_BIT
TWELVE_BIT: MOV BX,15 ;Else, 12 bit FAT.
MUL BX ;Multiply by 1.5 (or 15/10).
MOV BX,10
DIV BX
MOV BX,AX
MOV ES,FAT_SEG
MOV DX,ES:[BX] ;Retrieve cluster pointer.
POP AX ;Retrieve last cluster.
TEST AX,1 ;Was last cluster even number?
JZ LOW_ORDER ;If yes, use low order 12 bits.
MOV CL,4 ;Else, high order 12 bits.
SHR DX,CL ;Shift right 4 bits.
JMP SHORT CK_12BIT
LOW_ORDER: AND DX,0000111111111111B ;Mask off top 4 bits.
CK_12BIT: CMP DX,0FF8H ;Is it end of chain?
JAE CLUSTER_END ;If yes, indicate so.
JMP SHORT RETURN_CLUST ;Else, return with cluster in DX.
SIXTEEN_BIT: MOV CL,3
SHR AX,CL
MOV BX,FAT_SEG
ADD BX,AX
MOV ES,BX
POP BX ;Retrieve last cluster.
SHL BX,1 ;Multiply by 2.
AND BX,15
MOV DX,ES:[BX] ;Retrieve next cluster number.
CMP DX,0FFF8H ;Is it end of chain?
JAE CLUSTER_END ;If yes, indicate so.
RETURN_CLUST: POP CX ;Restore registers.
POP BX
POP ES
CLC ;Indicate not end of chain.
RET
CLUSTER_END: POP CX
POP BX
POP ES
STC ;Indicate end of cluster chain.
RET
;--------------------------------------------------------;
; This subroutine alphabetizes the directories by level. ;
;--------------------------------------------------------;
SORT: PUSH DS
PUSH BP
PUSH ES
POP DS
XOR BP,BP ;Zero in level pointer.
LEVEL_SORT: MOV DX,CS:[WORD PTR LEVEL_ADDRESS+BP+2] ;Level offset.
CMP DX,0FFFFH ;End signature?
JZ SORT_END ;If yes, done here.
SUB DX,SIZE TREE_DATA ;Point to end of lower level.
PUSH BP ;Save level pointer.
NEXT_PASS: POP BP
PUSH BP
MOV BX,CS:[WORD PTR LEVEL_ADDRESS+BP] ;Start of level.
XOR BP,BP ;Zero in exchange flag.
CMP BX,DX ;End of level?
JZ SORT_SET ;If yes, next level.
NEXT_SORT: MOV SI,BX ;Source and destination.
ADD SI,DIRECTORY ;Point to name
MOV DI,BX ; in each record.
ADD DI,SIZE TREE_DATA + DIRECTORY
MOV CX,SIZE DIRECTORY
REPZ CMPSB ;Compare names.
JBE END_SORT ;If already in order, skip.
MOV SI,BX ;Else, recover pointers.
MOV DI,BX
ADD DI,SIZE TREE_DATA
MOV CX,SIZE TREE_DATA ;Exchange the records.
NEXT_SWAP: MOV AL,[DI]
MOVSB
MOV [SI-1],AL
LOOP NEXT_SWAP
MOV BP,1 ;Flag that exchange was made.
END_SORT: ADD BX,SIZE TREE_DATA ;Point to next record.
CMP BX,DX ;End of top?
JB NEXT_SORT ;If no, bubble sort next.
SUB DX,SIZE TREE_DATA ;Else, move top down one record.
OR BP,BP ;Was there exchange made?
JNZ NEXT_PASS
SORT_SET: POP BP
INC BP ;Next level.
INC BP
JMP LEVEL_SORT ;Sort it.
SORT_END: POP BP
POP DS ;Restore data segment.
RET
;-------------------------------------------------------------;
; This subroutine formats the database into a directory tree. ;
;-------------------------------------------------------------;
FORMAT: PUSH BP
PUSH DS
MOV ES,TREE_SEG[BP]
STORE_ROOT: XOR AX,AX
XOR DI,DI
MOV CX,SIZE TREE * MAX_ENTRIES
SHR CX,1
REP STOSW
ADC CX,CX
REP STOSB
XOR DI,DI
MOV SI,OFFSET ROOT
MOV CX,ROOT_LEN
REP MOVSB
MOV DIRS,1
MOV DI,SIZE TREE ;Point to storage.
MOV LAST_ENTRY,DI ;Save current entry.
MOV DS,TREE_DATA_SEG[BP]
XOR BP,BP ;Zero in level pointer.
XOR SI,SI
CMP BYTE PTR [SI],0FFH ;No subs signature?
JZ SKIP_FORMAT ;If yes, skip format.
MOV CS:BRANCH_NUM,0
NEXT_FORMAT: INC CS:BRANCH_NUM
CALL STORE_NAME ;Format the directory name.
ADD SI,SIZE TREE_DATA ;Point to next record.
CMP BYTE PTR [SI],0 ;End of root directory?
JZ NEXT_FORMAT ;If no, get next entry.
SKIP_FORMAT: MOV AX,0FFH
STOSW
POP DS
POP BP
MOV AX,DIRS ;Retrieve directory count.
MOV DIR_COUNT[BP],AX
FORMAT_END: RET
;-----------------------------------------------------------;
; This subroutine recursively builds the subdirectory tree. ;
;-----------------------------------------------------------;
; INPUT: DS:SI -> Tree_Data; BP = Tree level; ES:DI -> Tree storage.
STORE_NAME: PUSH SI ;Save a couple registers.
PUSH BP
PUSH DI ;Save pointers.
PUSH SI
PUSH SI
MOV SI,CS:LAST_ENTRY ;Retrieve last entry.
MOV DX,DI ;Retrieve current position.
CMP SI,DX ;Have stored any subdirectories?
JZ STORE_DIR ;If no, skip tree structuring.
MOV BYTE PTR ES:BRANCH[SI+BP],"├"
PLACE_BAR: ADD SI,SIZE TREE ;Move to next record.
CMP SI,DX ;Are we at the current record?
JZ STORE_DIR ;If yes, done here.
MOV BYTE PTR ES:BRANCH[SI+BP],"│"
JMP SHORT PLACE_BAR ;Repeat until up to date.
STORE_DIR: MOV CS:LAST_ENTRY,SI ;Store current position.
POP SI ;Restore current record pointer.
INC CS:DIRS ;Increment directory counter.
ADD DI,BP ;Add level pointer.
INC DI ;Pointer past mark.
MOV AL,"└" ;Store L line character.
STOSB
MOV AL,"─" ;Store horizontal line character.
STOSB
ADD SI,DIRECTORY ;Point to name.
MOV CX,8
CALL STORE_BYTES ;And store in tree.
CMP BYTE PTR [SI],SPACE
JZ END_NAME
MOV AL,"." ;If exists, append extension.
STOSB
MOV CX,3
CALL STORE_BYTES
END_NAME: POP SI ;Restore current record pointer.
INC DI ;Bump pointer for space.
MOV AX,ATTRIB[SI] ;Retrieve "hidden" field.
STOSB ;And store it.
NEXT_DEST: POP DI ;Restore destination pointer.
MOV AX,CLUSTER[SI] ;Dir starting cluster.
MOV ES:TREE_CLUSTER[DI],AX
MOV AX,CS:BRANCH_NUM
MOV ES:BRANCH_NO[DI],AX
ADD DI,SIZE TREE ;Point to next tree record.
PUSH CS:LAST_ENTRY ;Save current position pointer.
MOV CS:LAST_ENTRY,DI ;Save our current position.
INC BP ;Bump pointer to next level.
INC BP
MOV BX,CS:[WORD PTR LEVEL_ADDRESS+BP] ;Get level offset.
NEXT_SUB: MOV AL,LEVEL[BX] ;Retrieve level pointer.
XOR AH,AH
CMP AX,BP ;Is it our level?
JA STORE_END ;If next level, done here.
MOV AX,PARENT[BX] ;Retrieve parent entry number.
CMP AX,ENTRY[SI] ;Is it our entry number?
JNZ SKIP_SUB ;If no, check next record.
GET_SUB: PUSH BX ;Else, save pointers.
PUSH SI
MOV SI,BX ;Point to our offset.
CALL STORE_NAME ;Store subdirectory.
POP SI ;Restore pointers.
POP BX
SKIP_SUB: ADD BX,SIZE TREE_DATA ;Next record.
JMP SHORT NEXT_SUB
STORE_END: POP CS:LAST_ENTRY ;Restore last entry pointer.
POP BP
POP SI
RET
;----------------------------------------------;
; Add logic to store spaces after first non-space char.
STORE_BYTES: LODSB ;Store all non space name
CMP AL,SPACE ; characters.
JZ SKIP_STORE
STOSB
SKIP_STORE: LOOP STORE_BYTES
RET
;----------------------------------------------;
; INPUT: BP = Tree Index
DISPLAY_TREE: CALL SETUP_COLOR
PUSH DS
PUSH BP
MOV AX,LISTING_TOP[BP]
MOV CX,SIZE TREE
MUL CX
MOV SI,AX
MOV AX,2
CALL CALC_COL
MOV DX,LISTING_TOP[BP]
MOV BH,TREE_COLOR
MOV CX,LISTING_LEN
CMP BP,ACTIVE_TREE
MOV DS,TREE_SEG[BP]
MOV BP,CS:BAR_LINE[BP]
JZ NEXT_DISPLAY
MOV BP,-1 ;Turn off highlight bar.
NEXT_DISPLAY: PUSH CX
PUSH DX
PUSH SI
PUSH DI
MOV CX,SIZE MARK + SIZE BRANCH
CMP DX,BP
JNZ NEXT_DISP1
OR DX,DX ;Is it the root?
JZ HIGHLIGHT
BAR: LODSB
DEC CX
CALL WRITE_SCREEN
CMP BL,"─"
JNZ BAR
HIGHLIGHT: MOV BH,CS:COLOR.C
NEXT_HIGH: LODSB
OR AL,AL
JZ BAR_END
CALL WRITE_SCREEN
LOOP NEXT_HIGH
JMP SHORT LOOP_DISPLAY
BAR_END: MOV BH,CS:TREE_COLOR
DEC SI
NEXT_BAR_END: LODSB
CALL WRITE_SCREEN
LOOP NEXT_BAR_END
JMP SHORT LOOP_DISPLAY
NEXT_DISP1: LODSB
CALL WRITE_SCREEN
LOOP NEXT_DISP1
LOOP_DISPLAY: POP DI
ADD DI,CS:CRT_WIDTH
POP SI
ADD SI,SIZE TREE
POP DX
INC DX
POP CX
LOOP NEXT_DISPLAY
POP BP
POP DS
RET
;----------------------------------------------;
; INPUT: BP = Tree Index
DISPLAY_DRIVE: CALL SETUP_COLOR
MOV AL,TREE_DRIVE[BP]
ADD AL,"A"
MOV DRIVE_LETTER,AL
MOV AX,1
CALL CALC_COL
MOV BH,TREE_COLOR
MOV SI,OFFSET DRIVE_SPEC
CALL WRITE_STRING
MOV SI,VOLUME_NAME[BP]
CALL WRITE_STRING
;----------------------------------------------;
STATS: MOV AL,ROWS
SUB AL,5
XOR AH,AH
CALL CALC_COL
ADD DI,34
PUSH DI
MOV AX,DIR_COUNT[BP]
CALL DISP_STATS
MOV SI,OFFSET DIRECTORIES
CALL WRITE_STRING
POP DI
ADD DI,CRT_WIDTH
PUSH DI
MOV AX,TOTAL_CLUSTERS[BP]
MUL BYTES_CLUSTERS[BP]
CALL DISP_STATS
MOV SI,OFFSET BYTES
CALL WRITE_STRING
MOV SI,OFFSET TOTAL
CALL WRITE_STRING
POP DI
ADD DI,CRT_WIDTH
PUSH DI
MOV AX,TOTAL_CLUSTERS[BP]
SUB AX,FREE_CLUSTERS[BP]
PUSH AX
MUL BYTES_CLUSTERS[BP]
CALL DISP_STATS
MOV SI,OFFSET BYTES
CALL WRITE_STRING
MOV SI,OFFSET USED
CALL WRITE_STRING
POP AX
CALL DISP_PERCENT
POP DI
ADD DI,CRT_WIDTH
MOV AX,BYTES_CLUSTERS[BP]
MUL FREE_CLUSTERS[BP]
CALL DISP_STATS
MOV SI,OFFSET BYTES
CALL WRITE_STRING
MOV SI,OFFSET FREE
CALL WRITE_STRING
MOV AX,100
MOV AX,FREE_CLUSTERS[BP]
CALL DISP_PERCENT
CALL DISPLAY_TREE
DRIVE_END: RET
;----------------------------------------------;
; INPUT: AX = CLUSTERS
DISP_PERCENT: PUSH BP
MOV SI,100
MUL SI
MOV BP,TOTAL_CLUSTERS[BP]
DIV BP
SHL DX,1
INC DX
SUB BP,DX
ADC AX,0
CMP AX,100
JNZ DO_PERCENT
MOV SI,OFFSET PERCENT_100
CALL WRITE_STRING
JMP SHORT PERCENT_END
DO_PERCENT: PUSH AX
MOV AL,SPACE
CALL WRITE_SCREEN
POP AX
CALL DECIMAL_ASCII
DO_PERCENT2: MOV AL,"%"
CALL WRITE_SCREEN
PERCENT_END: POP BP
RET
;----------------------------------------------;
; INPUT: DX:AX = number; DI -> Display
DISP_STATS: PUSH BP
PUSH BX
MOV BP,DX
MOV SI,AX
XOR CX,CX
MOV BX,10
JMP SHORT DO_DIV
NEXT_DIV: CMP CH,3
JNZ DO_DIV
MOV AL,","
PUSH AX
XOR CH,CH
INC CL
DO_DIV: MOV AX,BP
XOR DX,DX
DIV BX
MOV BP,AX
MOV AX,SI
DIV BX
MOV SI,AX
MOV AX,DX
ADD AL,"0"
PUSH AX
ADD CX,0101H
OR BP,BP
JNZ NEXT_DIV
OR SI,SI
JNZ NEXT_DIV
XOR CH,CH
SUB DI,CX
SUB DI,CX
MOV BH,TREE_COLOR
NEXT_WRITE: POP AX
CALL WRITE_SCREEN
LOOP NEXT_WRITE
POP BX
POP BP
RET
;----------------------------------------------;
; INPUT: AX = Number.
DECIMAL_ASCII: MOV DL,10
DIV DL
ADD AX,"0" SHL 8 + "0"
PUSH AX
CMP AL,"0"
JNZ DO_DECIMAL
MOV AL,SPACE
DO_DECIMAL: CALL WRITE_SCREEN
POP AX
MOV AL,AH
CALL WRITE_SCREEN
RET
;----------------------------------------------;
SETUP_COLOR: MOV AL,COLOR.I
CMP BP,ACTIVE_TREE
JZ STORE_COLOR
MOV AL,COLOR.W
STORE_COLOR: MOV TREE_COLOR,AL
RET
;----------------------------------------------;
; INPUT: BP = Tree Index; CH = Starting row; AL = Size.
CLEAR_WINDOW: MOV DH,CH
ADD DH,AL
XOR CL,CL
OR BP,BP
JZ CLEAR
MOV CL,41
CLEAR: MOV DL,CL
ADD DL,38
MOV BH,TREE_COLOR
DO_CLEAR: MOV AX,600H
push bp
INT 10H
pop bp
RET
;----------------------------------------------;
SETUP: MOV DX,OFFSET READING
CALL PRINT_STRING
XOR BP,BP
CALL READ_DRIVE
JC INVALID
MOV AL,TREE_DRIVE[BP]
INC BP
INC BP
CMP AL,TREE_DRIVE[BP]
JNZ READ_SECOND
CALL DUP_TREE
JMP SHORT DO_VIDEO
READ_SECOND: CALL READ_DRIVE
JNC DO_VIDEO
INVALID: JMP PARSE_ERROR
DO_VIDEO: CALL VIDEO_SETUP
;----------------------------------------------;
UPDATE_TREE: XOR BP,BP
NEXT_TREE: CALL CLEAR_DRIVE
CALL DISPLAY_DRIVE
INC BP
INC BP
CMP BP,2
JNA NEXT_TREE
MOV BP,ACTIVE_TREE
RET
;----------------------------------------------;
DUP_TREE: PUSH DS
PUSH ES
MOV SI,OFFSET VOLUME1
MOV DI,OFFSET VOLUME2
MOV CX,VOLUME_LEN
REP MOVSB
MOV AX,DIR_COUNT[0]
MOV DIR_COUNT[2],AX
MOV AX,FREE_CLUSTERS[0]
MOV FREE_CLUSTERS[2],AX
MOV AX,TOTAL_CLUSTERS[0]
MOV TOTAL_CLUSTERS[2],AX
MOV AX,BYTES_CLUSTERS[0]
MOV BYTES_CLUSTERS[2],AX
MOV ES,TREE_SEG[2]
MOV DS,TREE_SEG[0]
XOR SI,SI
XOR DI,DI
MOV CX,MAX_ENTRIES * SIZE TREE
SHR CX,1
REP MOVSW
ADC CX,CX
REP MOVSB
POP ES
POP DS
CALL CK_VARS
RET
;----------------------------------------------;
CK_VARS: PUSH ES
MOV ES,TREE_SEG[BP]
NEXT_VARS: MOV AX,SIZE TREE
MUL BAR_LINE[BP]
MOV DI,AX
INC DI
XOR AL,AL
MOV CX,SIZE TREE - 1
REP SCASB
JNZ VARS_END
DEC BAR_LINE[BP]
MOV AX,BAR_LINE[BP]
CMP AX,LISTING_TOP[BP]
JAE NEXT_VARS
DEC LISTING_TOP[BP]
JMP NEXT_VARS
VARS_END: POP ES
RET
;----------------------------------------------;
CLEAR_DRIVE: CALL SETUP_COLOR
MOV CH,1 ;Starting row 1
XOR AL,AL ;Window size.
CALL CLEAR_WINDOW
MOV CH,ROWS
SUB CH,5
MOV AL,3
CALL CLEAR_WINDOW
RET
;----------------------------------------------;
; INPUT: DS:SI -> Directory record. OUTPUT: CF=1 if failed.
SELECT_DIR: PUSH SI
PUSH DS
MOV AX,CS
MOV DS,AX
MOV DX,OFFSET ROOT_DIR
CALL CHANGE_DIR
POP DS
OR SI,SI
JZ SELECT_END
CALL GET_NAME
XOR CX,CX ;Zero in level counter.
NEXT_SUBDIR: PUSH SI
INC CX
CALL FIND_PARENT
CMP SI,1
JA NEXT_SUBDIR
CHANGE_THEM: POP DX ;Retrieve directory name pointer.
CALL CHANGE_DIR ;Change directories.
JC RESTORE_STACK ;If nonexistant, restore stack
LOOP CHANGE_THEM ;Else, continue up the tree.
SELECT_END: CLC
POP SI
RET
NEXT_STACK: POP DX ;If error, restore stack
RESTORE_STACK: LOOP NEXT_STACK ; before returning.
STC
POP SI
RET
;----------------------------------------------;
; INPUT: AL=Drive Letter; BP = Tree Index; SI -> ":"
; OUTPUT: DL=Drive Number; ZF=0 if exists
DRIVEEXIST: MOV DL,AL
AND DL,5FH
SUB DL,"A"
CMP BYTE PTR [SI],":"
JNZ DRIVEEXIST_END
CMP BYTE PTR [SI+1],SPACE
JA DRIVEEXIST_END
DEC SI
MOV DI,DEFAULT_PATH[BP]
MOV AX,2900H ;Parse filename.
INT 21H
DRIVEEXIST_END:OR AL,AL
RET
;-------------------------------------------------;
; INPUT: BP = Tree Index.
RESTORE_DIR: MOV DL,TREE_DRIVE[BP]
MOV AH,0EH
INT 21H
MOV DX,DEFAULT_PATH[BP]
CALL CHANGE_DIR
RET
;----------------------------------------------;
VIDEO_SETUP: PUSH ES
MOV AX,500H ;Make sure active page is zero.
INT 10H
MOV AX,40H ;Point to the ROM BIOS data area
MOV ES,AX
MOV AX,ES:CRT_COLS
MOV COLUMNS,AX
SHL AX,1
MOV CRT_WIDTH,AX
MOV AX,ES:[4EH]
MOV CRT_START,AX
MOV AX,ES:[63H]
ADD AX,6
MOV CX,0B000H
CMP AX,3BAH
JZ STORE_SEG
ADD CX,800H
STORE_SEG: MOV STATUS_REG,AX
MOV VIDEO_SEG,CX
MOV SI,OFFSET MONO_ATTR
MOV AL,ES:CRT_MODE ;Retrieve current video mode.
CMP AL,7 ;Is it mono mode?
JZ GET_ROWS ;If yes, continue.
CMP AL,2 ;Is it BW80?
JZ GET_ROWS ;If yes, continue.
MOV SI,OFFSET COLOR_ATTR
CMP AL,3 ;Is it mode CO80?
JZ GET_ROWS ;If yes, continue.
MOV AX,3 ;Else, change video mode to CO80.
INT 10H
GET_ROWS: XOR BH,BH
MOV DL,24
MOV AX,1130H
INT 10H
MOV ROWS,DL ;Store rows.
SUB DL,7
XOR DH,DH
MOV LISTING_LEN,DX
POP ES
MOV DI,OFFSET COLOR
MOV CX,SIZE COLOR_ATTRIBS
REP MOVSB
;----------------------------------------------;
DISPLAY_SETUP: CALL CLS ;Clear screen.
CMP BORDER_FLAG,1
JZ DO_COPYRIGHT
MOV BL,COLOR.W ;Turn on border.
AND BL,7
XOR BH,BH
MOV AH,0BH
INT 10H
DO_COPYRIGHT: XOR AX,AX
CALL CALC_ADDR
INC DI
INC DI
MOV SI,OFFSET COPYRIGHT ;Point to copyright message.
MOV BH,COLOR.B ;Use header attribute.
CALL WRITE_STRING ;And display it.
MOV AL,BOX
CALL WRITE_SCREEN
MOV AL,SPACE
CALL WRITE_SCREEN
INC SI ;Bump pointer past LF
CALL WRITE_STRING ; and display rest of header.
;----------------------------------------------;
DISPLAY_MENU: MOV AL,ROWS
DEC AL
XOR AH,AH
CALL CALC_ADDR
MOV SI,OFFSET MENU
PUSH DI
MOV BH,COLOR.B
CALL WRITE_STRING
POP DI
ADD DI,CRT_WIDTH
CALL WRITE_STRING
RET
;----------------------------------------------;
; INPUT: AX = Starting line; BP = Tree Index; OUTPUT: DI = Video address.
CALC_COL: CALL CALC_ADDR
OR BP,BP
JZ COL_END
ADD DI,COLUMNS
INC DI
INC DI
COL_END: RET
;----------------------------------------------;
; INPUT: AX = Starting line; OUTPUT: DI = Video address.
CALC_ADDR: MUL CRT_WIDTH
ADD AX,CRT_START
MOV DI,AX
RET
;----------------------------------------------;
; INPUT: AL = character to write; BH = attribute.
WRITE_SCREEN: PUSH ES
MOV ES,CS:VIDEO_SEG ;Point to screen segment.
MOV DX,CS:STATUS_REG ;Retrieve status register.
MOV BL,AL ;Store character in BL.
HORZ_RET: IN AL,DX ;Get status.
RCR AL,1 ;Is it low?
JC HORZ_RET ;If not, wait until it is.
CLI ;No more interrupts.
HWAIT: IN AL,DX ;Get status.
RCR AL,1 ;Is it high?
JNC HWAIT ;If no, wait until it is.
MOV AX,BX ;Retrieve character; now it's OK
STOSW ; to write to screen buffer.
STI ;Interrupts back on.
POP ES
RET ;Return
;----------------------------------------------;
; INPUT: SI -> to string to display; DI -> where to display it.
; Entry point is WRITE_STRING.
WRITE_IT: CALL WRITE_SCREEN ;Write a character.
WRITE_STRING: LODSB ;Retrieve a character.
CMP AL,CR ;Keep writing until a carriage
JA WRITE_IT ; return or zero encountered.
RET
;----------------------------------------------;
; INPUT: DL = Drive Number.
SELECT_DISK: MOV AH,0EH
INT 21H
RET
;----------------------------------------------;
; INPUT: SI -> Path Storage; DL=Drive Number
GET_DIR: MOV BYTE PTR [SI],"\" ;DOS doesn't preface directory
INC SI ; with slash so we must.
CHDIR: PUSH DX
INC DL
MOV AH,47H ;Get current directory.
INT 21H
POP DX
RET
CHANGE_DIR: MOV AH,3BH ;Change current directory.
INT 21H
RET
;----------------------------------------------;
PRINT_STRING: MOV AH,9 ;Print string via DOS.
INT 21H
RET
;----------------------------------------------;
; OUTPUT: CF=1 if failed.
DISK_FREE: PUSH DS
PUSH CS
POP DS
PUSH BX
CALL DOS_CURSOR
MOV DL,TREE_DRIVE[BP]
INC DL
MOV AH,36H ;Disk free space.
INT 21H
CMP AX,-1
STC
JZ FREE_END
MOV FREE_CLUSTERS[BP],BX
MOV TOTAL_CLUSTERS[BP],DX
MUL CX
MOV BYTES_CLUSTERS[BP],AX
CLC
FREE_END: POP BX
POP DS
RET
;----------------------------------------------;
BEEP: MOV BX,NOTE ;Tone frequency divisor.
MOV DX,12H
XOR AX,AX
DIV BX
MOV BX,AX ;8253 countdown.
CALL DELAY ;Wait till clock rolls over.
MOV AL,0B6H ;Channel 2 speaker functions.
OUT 43H,AL ;8253 Mode Control.
JMP $+2 ;IO delay.
MOV AX,BX ;Retrieve countdown.
OUT 42H,AL ;Channel 2 LSB.
JMP $+2
MOV AL,AH ;Channel 2 MSB.
OUT 42H,AL
IN AL,61H ;Port B.
OR AL,3 ;Turn on speaker.
JMP $+2
OUT 61H,AL
CALL DELAY ;Delay one second.
IN AL,61H ;Get Port B again.
AND AL,NOT 3 ;Turn speaker off.
JMP $+2
OUT 61H,AL
RET ;Done.
;----------------------------------------------;
DELAY: PUSH DS ;Preserve data segment.
MOV AX,40H ;Point to BIOS data segment.
MOV DS,AX
MOV AX,DS:[6CH] ;Retrieve timer low.
NEXT_BEEP: MOV DX,DS:[6CH] ;Retrieve timer low.
CMP DX,AX ;Have we timed out?
JZ NEXT_BEEP ;If not, wait until second up.
POP DS ;Restore data segment.
RET
;----------------------------------------------;
CLS: MOV BH,COLOR.B ;Normal attribute.
CLS2: XOR CX,CX ;Top left corner.
MOV DL,BYTE PTR COLUMNS
DEC DL
MOV DH,ROWS
MOV AX,600H ;Scroll active page.
PUSH BP
INT 10H
POP BP
RET
;----------------------------------------------;
DOS_CURSOR: MOV DX,200H ;Put cursor on screen in case
JMP SHORT SET_CURSOR ; of critical error like drive
; drive door open.
HIDE_CURSOR: MOV DH,ROWS ;Retrieve CRT rows.
INC DH ;Move one line below off screen.
XOR DL,DL ;Column zero.
SET_CURSOR: PUSH BX
XOR BH,BH ;Page zero.
MOV AH,2 ;Set cursor position.
INT 10H
POP BX
RET
;----------------------------------------------;
; INPUT: DI -> Valid scan codes table; CX = Length of table
; OUTPUT: AL=Scan code; AH=Char; CF=1 if Esc pressed.
DISPATCH: CALL GET_KEY
PUSH AX
CMP AL,ESC_SCAN
STC
JZ DISPATCH_END
CMP AH,CR
JZ DISPATCH_END
MOV BX,CX
MOV DX,DI
ADD DX,CX
REPNZ SCASB
JZ GOT_DISPATCH
CALL SEARCH
JMP SHORT NO_DISPATCH
GOT_DISPATCH: SUB BX,CX
DEC BX
SHL BX,1
ADD BX,DX
MOV AX,DIR_COUNT[BP]
DEC AX
MOV CX,BAR_LINE[BP]
MOV DX,LISTING_TOP[BP]
MOV DI,DX
MOV SI,LISTING_LEN
ADD DI,SI
DEC DI
CMP DI,AX
JBE DO_DISPATCH
MOV DI,AX
DO_DISPATCH: CALL [BX] ;Process the command.
NO_DISPATCH: CLC
DISPATCH_END: POP AX
RET
;----------------------------------------------;
SEARCH: PUSH DS
OR AL,AL
JZ NO_MATCH
CMP AH,"a"
JB DO_SEARCH
CMP AH,"z"
JA DO_SEARCH
AND AH,5FH
DO_SEARCH: MOV BL,AH
MOV AX,SIZE TREE
MOV CX,BAR_LINE[BP]
MUL CX
MOV SI,AX
MOV DS,TREE_SEG[BP]
PUSH SI
CALL GET_NAME
CMP BL,BYTE PTR [SI]
POP SI
JZ NEXT_SEARCH
XOR SI,SI
XOR CX,CX
NEXT_SEARCH: INC CX
CMP CX,CS:DIR_COUNT[BP]
JAE NO_MATCH
ADD SI,SIZE TREE
PUSH SI
CALL GET_NAME
CMP BL,BYTE PTR [SI]
POP SI
JNZ NEXT_SEARCH
POP DS
MOV BAR_LINE[BP],CX
MOV AX,LISTING_TOP[BP]
MOV BX,LISTING_LEN
CMP CX,AX
JB CENTER_BAR
ADD AX,BX
DEC AX
CMP CX,AX
JBE SEARCH_END
CENTER_BAR: SHR BX,1
SUB CX,BX
JNC NEW_TOP
XOR CX,CX
NEW_TOP: MOV LISTING_TOP[BP],CX
SEARCH_END: CALL DISPLAY_TREE
RET
NO_MATCH: POP DS
CALL BEEP
RET
;----------------------------------------------;
GET_KEY: MOV AH,0 ;Wait for next keyboard input.
INT 16H
XCHG AH,AL
RET
CK_KEY: MOV AH,1 ;Is there a keystroke available.
INT 16H
RET
CLEAR_IT: CALL GET_KEY ;Read keystrokes until buffer
CLEAR_KEY: CALL CK_KEY ; empty.
JNZ CLEAR_IT
RET
;----------------------------------------------;
WRITE_TTY: MOV AH,0EH
INT 10H
RET
;----------------------------------------------;
DEFAULT_DIR1 = $
DEFAULT_DIR2 = DEFAULT_DIR1 + 65
DEST_SPEC = DEFAULT_DIR2 + 65
LEVEL_ADDRESS = DEST_SPEC + 65 + 13
EVEN
STACK_POINTER = $ + 100 + 500
_TEXT ENDS
END START