home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
pcmag
/
vol8n16.arc
/
DIRMATCH.ASM
< prev
next >
Wrap
Assembly Source File
|
1989-05-18
|
97KB
|
1,919 lines
;-----------------------------------------------------------------------------;
; DIRMATCH * PC Magazine * Michael J. Mefford ;
; Points out the similarity and differences of two directories and then ;
; lets you update the target directory with specific source directory files. ;
;-----------------------------------------------------------------------------;
_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 "DIRMATCH 1.0 (C) 1989 Ziff Communications Co. ",CR,LF
PROGRAMMER DB "PC Magazine ",BOX," Michael J. Mefford",CR,LF,LF
DB "Syntax: DIRMATCH source target [/D][/A]",CR,LF,LF
DB "/D = Different files",CR,LF
DB "/A = Alike files$",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
SPEC_LENGTH EQU 66 + 12
MENU LABEL BYTE
DB "F1 ",0, "All files ",0, "Different files ",0, "Alike files ",0
DB "F2 Mark all F3 Clear marks F4 CopyF5 Move F6 Print "
DB "Use: ",24,32,25," PgUp PgDn Home End +/- Mark/Unmark Esc to Exit",0
ALL EQU 0
UNIQUE EQU 2
ALIKE EQU 4
FILES_MODE DB ALL
MODES DW DO_ALL, DO_UNIQUE, DO_ALIKE
CRT_MODE EQU 49H
CRT_ROWS EQU 84H
STATUS_REG DW 3BAH
VIDEO_SEG DW 0B000H
ROWS DB 24
SYNTAX_FLAG DB 0
HEADING_ATTR DB 70H
ACTIVE_ATTR DB 0FH
BODY_ATTR DB 07H
HIGHLIGHT_ATTR DB 0FH
MENU_ATTR DB 07H
BAR_ATTR DB 70H
INTENSE EQU 7FH
INTENSE_BLUE EQU 1FH
BLUE EQU 17H
INVERSE_BLUE EQU 71H
CYAN EQU 31H
TOP_MEMORY DW ?
DEFAULT_DRIVE DB ?
SOURCE_SEG DW ?
TARGET_SEG DW ?
BUFFER_SEG DW ?
BUFFER_SIZE DW 0FFFFH
SOURCE_END DW ?
TARGET_END DW ?
LISTING_LINE DW 0
BAR_LINE DW 0
LAST_BAR DW ?
PAGE_LENGTH DW ?
SOURCE_COUNT DW ?
TARGET_COUNT DW ?
MARKED_COUNT DW ?
RESPONSE DW ?
OUT_OF_MEMORY DB "Not enough memory ",0,"$"
TOO_MANY DB "Too many files ",0,"$"
SOURCE_PROMPT DB CR,LF,LF,"Source$"
TARGET_PROMPT DB CR,LF,LF,"Target$"
NOT_FOUND DB " filespec not found",CR,LF
DB "Enter filespec: $"
PRESS_MSG DB "Error; Press any key",0
SOURCE_MSG DB " Source",0
DIRECTORY_MSG DB " Directory",0
FILES_MSG DB " files",0
GAP EQU 4
DIRECTORY_LEN EQU $ - SOURCE_MSG + GAP - 3
TO_MSG DB " to"
TARGET_MSG DB " Target",0
COPY_MSG DB "Copy ",0
MOVE_MSG DB "Move ",0
MARKED_MSG DB " marked files",0
QUERY_MSG DB "? Y/N ",0
STAR_DOT_STAR DB "*.*",0
RECENT DB ">", 6 DUP (?)
NOT_RECENT DB SPACE, 6 DUP (?)
DISPATCH_KEY DB 48H, 50H, 49H, 51H, 47H, 4FH
DB 3BH, 54H, 3CH, 3DH, 3EH, 3FH
DB 40H, 01H, 0DH, 4EH, 0CH, 4AH
DISPATCH_CNT EQU $ - DISPATCH_KEY
DISPATCH_TABLE DW UP, DOWN, PGUP, PGDN, HOME, END_KEY
DW F1, S_F1, F2, F3, F4, F5
DW F6, EXIT, PLUS, PLUS, MINUS, MINUS
DISPATCH_END EQU $ - 2
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
FILE_RECORD STRUC
LIST_NAME DB 12 DUP (?)
LIST_BYTES DB 9 DUP (?)
LIST_DATE DB 10 DUP (?)
LIST_TIME DB 8 DUP (?)
MARK DB ?
DOS_DATE DW ?
DOS_TIME DW ?
DISP_ORDER DW ?
FILE_RECORD ENDS
; CODE AREA
; ---------
MAIN PROC NEAR
CLD ;String instructions forward.
MOV BX,0FFFFH ;Memory allocation request will
MOV AH,4AH ; fail and BX will return with
INT 21H ; available memory in
; paragraphs.
CMP BX,64 * (1024 / 16) ;At least 64K paragraphs?
JAE ALLOCATE
MOV DX,OFFSET OUT_OF_MEMORY
JMP ERROR_EXIT ;If no, exit.
ALLOCATE: MOV AH,4AH ;Else, allocate all available.
INT 21H
MOV AX,DS ;Get our segment;
ADD AX,BX ; add to available and save
MOV TOP_MEMORY,AX ; as segment of memory top.
MOV SP,OFFSET STACK_POINTER ;Set up stack.
MOV DX,OFFSET DTA ;Set up Disk Transfer Address.
MOV AH,1AH
INT 21H
MOV AH,19H ;Get default drive so can
INT 21H ; restore after we change it.
MOV DEFAULT_DRIVE,AL
;----------------------------------------------;
CK_SWITCH: MOV SI,81H ;Point to command line.
NEXT_SWITCH: LODSB ;Get a byte.
CMP AL,CR ;Is it carriage return?
JZ PARSE_SOURCE ;If yes, done here.
CMP AL,"/" ;Is it switch delimiter?
JNZ NEXT_SWITCH ;If no, next byte.
LODSB ;Get the switch character.
CMP AL,CR ;Make sure it's not CR
JZ PARSE_SOURCE ; so we don't go past end.
AND AL,5FH ;Capitalize.
CK_UNIQUE: CMP AL,"D" ;If "D", then Different files.
JNZ CK_ALIKE
MOV FILES_MODE,UNIQUE
CK_ALIKE: CMP AL,"A" ;If "A", then Alike files.
JNZ NEXT_SWITCH ;If none of these, next byte.
MOV FILES_MODE,ALIKE
JMP NEXT_SWITCH
;---------------------------------------------------------------;
; Parse the command line for filespecs. If one or both missing ;
; or file can't be opened, prompt the user for filespec. ;
;---------------------------------------------------------------;
PARSE_SOURCE: MOV SI,81H ;Point to command line again.
CALL PARSE_SPEC ;Parse a filespec.
PUSH [SI] ;Save ending byte
PUSH SI ; and the pointer.
JNZ SOURCE_PATH ;If filespec found, check path.
PROMPT1: XOR DI,DI ;Else, ask user for first
CALL PROMPT_USER ; filespec.
CALL PARSE_SPEC ;Parse it.
JZ PROMPT1 ;If null string, prompt again.
SOURCE_PATH: MOV DI,OFFSET SOURCE_SPEC ;Storage for complete filespec.
CALL FIND_PATH ;Parse the path from filespec.
JC PROMPT1 ;If not found, prompt user.
MOV SOURCE_END,SI ;Else, store source path end.
MOV BP,ES ;Calculate the segment to save
MOV DI,OFFSET SOURCE_OFFSET ; first batch of filenames.
CALL CALC_SEG
MOV SOURCE_SEG,BP ;Save it.
CALL SOURCE_DIR
JC ERROR_EXIT ;If ran out of memory, exit.
POP SI ;Recover command line pointer
POP [SI] ; and restore string
; terminator.
PARSE_TARGET: CALL PARSE_SPEC ;Parse for second filespec.
JNZ TARGET_PATH ;If not null string, check
; path.
PROMPT2: MOV DI,1 ;Else, prompt user for second
CALL PROMPT_USER ; filespec and then parse it.
JMP PARSE_TARGET
TARGET_PATH: MOV DI,OFFSET TARGET_SPEC ;Storage for complete filespec.
CALL FIND_PATH ;Parse path from filespec.
JC PROMPT2 ;If not found, prompt user.
MOV TARGET_END,SI ;Else, save target path end.
CALL TARGET_DIR ;Get target files.
JC ERROR_EXIT
DO_SETUP: CALL VIDEO_SETUP ;Prepare for video environment.
CALL MATCH ;Match the filenames.
;----------------------------------------------------------;
; Loop here processing user commands and updating display. ;
;----------------------------------------------------------;
INPUT: CALL DISP_LISTINGS ;Update the display.
CALL GET_KEY
MOV DI,OFFSET DISPATCH_KEY ;Check dispatch table.
MOV CX,DISPATCH_CNT
REPNZ SCASB
JNZ INPUT ;If no match, ignore.
SHL CX,1 ;Else, look up subroutine
MOV DI,OFFSET DISPATCH_END
SUB DI,CX
MOV BP,BAR_LINE ;Paging commands need line
MOV BX,LISTING_LINE ; page data.
MOV CX,LAST_BAR
MOV DX,PAGE_LENGTH
CALL [DI] ;Process the command.
JMP INPUT ;Next input.
;----------------------------------------------;
ERROR_EXIT: CALL PRINT_STRING ;Print error message.
ERROR: 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,80 - 1
MOV BH,MENU_ATTR
MOV AX,600H ; by scrolling active page.
INT 10H
XOR BX,BX ;Border back to black.
MOV AH,0BH
INT 10H
MOV DH,CH ;Place cursor on next
DEC DH ; to last line.
XOR DL,DL
CALL SET_CURSOR
XOR AL,AL ;ERRORLEVEL zero.
TERMINATE: MOV AH,4CH ;Terminate.
INT 21H
MAIN ENDP
; ***************
; * SUBROUTINES *
; ***************
;-------------------------------------------------;
SOURCE_DIR: MOV BP,SOURCE_SEG
MOV DX,OFFSET SOURCE_SPEC
CALL FIND_FILES ;Find the filenames.
JC GET_END1
CALL CALC_SEG ;If successful, calculate
; segment of target DIR
MOV TARGET_SEG,BP ; filename storage.
CLC
GET_END1: RET
;-------------------------------------------------;
; OUTPUT: CY = 1 if failed; DX -> Error message. ;
;-------------------------------------------------;
TARGET_DIR: MOV BP,TARGET_SEG ;Retrieve segment for
; filenames.
MOV DX,OFFSET TARGET_SPEC
CALL FIND_FILES ;Find the filenames.
JC GET_END2 ;If ran out of memory, exit.
CALL CALC_SEG ;If successful, calculate
MOV BUFFER_SEG,BP ; segment of copy buffer.
MOV AX,TOP_MEMORY ;Retrieve top of memory
; segment.
SUB AX,BP ;Paragraphs available for
; buffer.
MOV DX,OFFSET OUT_OF_MEMORY
JBE GET_END2 ;If none, exit.
CMP AX,80H ;If less than practical minimum
JB GET_END2 ; of 2K bytes (128 para), exit.
CMP AX,1000H ;If more than 4K paragraphs
JAE GET_END2 ; (64K bytes), then use
; default.
MOV CL,4 ;Else, convert paragraphs to
SHL AX,CL ; bytes and save as buffer
MOV BUFFER_SIZE,AX ; size.
CLC
GET_END2: RET
;----------------------------------------------;
MATCH: MOV BAR_LINE,0
MOV LISTING_LINE,0 ;Move listing to top.
DO_MATCH: PUSH DS ;Preserve segments.
PUSH ES
MOV SOURCE_COUNT,0
MOV TARGET_COUNT,0 ;Initialize file counts to
; zero.
MOV DL,FILES_MODE ;Get offset of current
XOR DH,DH ; file display mode.
ADD DX,OFFSET MODES
MOV ES,TARGET_SEG ;Target listing segment.
MOV DS,SOURCE_SEG ;Source listing segment.
XOR BX,BX ;DS:BX -> source.
XOR BP,BP ;ES:BP -> target.
XOR AX,AX ;Counter.
NEXT_MATCH: CMP BYTE PTR ES:LIST_NAME[BP],-1 ;End of listing 2?
JNZ COMPARE ;If no, compare
; filenames.
CMP BYTE PTR LIST_NAME[BX],-1 ;Else, end of listing 1?
JZ MATCH_END ;If yes, done here.
COMPARE: MOV SI,BX ;Get pointers to filenames.
MOV DI,BP
MOV CX,SIZE LIST_NAME
REP CMPSB ;Compare the names.
MOV DI,DX
CALL CS:[DI] ;Process.
JMP NEXT_MATCH ;Do next match.
MATCH_END: POP ES ;Restore segments.
POP DS
DEC AX ;Adjust counter.
JGE STORE_BAR ;If non-zero, OK.
XOR AX,AX ;Else, use zero.
STORE_BAR: MOV LAST_BAR,AX ;Last bar location = count.
CALL DISPLAY_DIRS ;Update the directory file
; count.
RET
;----------------------------------------------;
DO_ALL: MOV SI,AX ;Current counter.
MOV DI,AX
JZ ALL_MATCH ;If name same, check date.
JB ALL_LEFT2 ;Else, update the one
CALL UPDATE_LIST2 ; earlier in the alphabet
JMP SHORT ALL_END ; with current display order.
ALL_MATCH: CALL DO_DATE
JB ALL_LEFT1
JA ALL_RIGHT1
OR DI,8000H ;Highlight flag.
ALL_RIGHT1: OR SI,8000H
JMP SHORT ALL_UPDATE
ALL_LEFT1: OR DI,8000H
ALL_UPDATE: CALL UPDATE_LIST2
ALL_LEFT2: CALL UPDATE_LIST1
ALL_END: INC AX ;Increment counter.
RET
;----------------------------------------------;
DO_UNIQUE: MOV SI,AX
MOV DI,AX
JZ UNIQUE_MATCH ;If match, check date.
JA UNIQUE_RIGHT ;Else, update the one
INC AX ; earlier in the alphabetic
JMP SHORT UNIQUE_LEFT ; with current display order.
UNIQUE_RIGHT: INC AX
CALL UPDATE_LIST2
JMP SHORT UNIQUE_END
UNIQUE_MATCH: CALL DO_DATE
JB UNIQUE_LEFT1
JA UNIQUE_RIGHT1
MOV SI,-1 ;Non-display flag.
MOV DI,-1
JMP SHORT UNIQUE_UPDATE
UNIQUE_RIGHT1: OR SI,8000H ;Highlight flag.
JMP SHORT UNIQUE_DATE
UNIQUE_LEFT1: OR DI,8000H
UNIQUE_DATE: INC AX
UNIQUE_UPDATE: CALL UPDATE_LIST2
UNIQUE_LEFT: CALL UPDATE_LIST1
UNIQUE_END: RET
;----------------------------------------------;
DO_ALIKE: MOV SI,AX
MOV DI,AX
JZ ALIKE_MATCH ;If match, then check date.
MOV SI,-1
JB ALIKE_LEFT
MOV DI,-1 ;Else, update the one earlier
CALL UPDATE_LIST2 ; in the alphabet with a -1
JMP SHORT ALIKE_END ; non-display flag.
ALIKE_MATCH: INC AX
CALL DO_DATE
JB ALIKE_LEFT1
JA ALIKE_RIGHT1
OR DI,8000H
ALIKE_RIGHT1: OR SI,8000H
JMP SHORT ALIKE_UPDATE
ALIKE_LEFT1: OR DI,8000H
ALIKE_UPDATE: CALL UPDATE_LIST2
ALIKE_LEFT: CALL UPDATE_LIST1
ALIKE_END: RET
;-----------------------------------------------------------------------------;
; INPUT: BX -> Source name; BP -> target name; OUTPUT: ZF = 1 if date matched. ;
;-----------------------------------------------------------------------------;
DO_DATE: PUSH SI
PUSH DI
MOV SI,BX ;Get filename pointers again.
MOV DI,BP
ADD SI,DOS_DATE ;Point to date/time in DOS
ADD DI,DOS_DATE ; format and compare.
MOV CX,(SIZE DOS_DATE + SIZE DOS_TIME) / 2
REP CMPSW
POP DI
POP SI
RET
;----------------------------------------------;
UPDATE_LIST1: MOV DISP_ORDER[BX],SI ;Store the display order value.
ADD BX,SIZE FILE_RECORD ;Point to next filename.
INC SI ;Is it -1, non-display marker?
JZ LIST1_END ;If yes, done here.
INC CS:SOURCE_COUNT ;Else, increment file count.
LIST1_END: RET
UPDATE_LIST2: MOV ES:DISP_ORDER[BP],DI ;Same for the second batch of
ADD BP,SIZE FILE_RECORD ; of filenames.
INC DI ;Is it -1, non-display marker?
JZ LIST2_END ;If yes, done here.
INC CS:TARGET_COUNT ;Else, increment file count.
LIST2_END: RET
;----------------------------------------------;
DISP_LISTINGS: PUSH DS ;Preserve data segment.
PUSH SI
MOV BP,LISTING_LINE ;1st line of listing to
; display.
MOV CX,PAGE_LENGTH ;No. of lines to display.
XOR SI,SI ;SI -> start of source.
XOR DX,DX ;DX -> start of target.
MOV DI,(3 * 160) ;Start display row 2; column 1.
NEXT_LIST: PUSH CX ;Preserve lines to display.
MOV DS,CS:SOURCE_SEG ;1st in left half of screen.
MOV CX,40 ;39 chars in filename plus
; mark.
PUSH DX ;Preserve DX.
CALL DO_LIST ;Display the left half of line.
POP DX ;Restore DX.
XCHG SI,DX ;Now point to target filename.
MOV DS,CS:TARGET_SEG ;Also need the segment.
INC DI ;Bump pointer to next line.
INC DI
MOV CX,39 ;It is 39 chars too.
PUSH DX ;Preserve DX.
CALL DO_LIST ;Display the right half of
; line.
POP DX ;Restore DX.
XCHG SI,DX ;Restore pointers.
INC BP ;Next order counter.
POP CX ;Retrieve line counter.
LOOP NEXT_LIST ;Do all lines.
POP SI
POP DS ;Restore data segment.
RET ;Done here.
;---------------------------------------------------------------;
; INPUT: DS:SI -> filename to display; Entry point at DO_LIST. ;
;---------------------------------------------------------------;
NEXT_REC1: ADD SI,SIZE FILE_RECORD ;Next filename.
DO_LIST: MOV BH,CS:BODY_ATTR ;Assume body attribute.
CMP LIST_NAME[SI],-1 ;Is this end of listing?
JZ PAD_LINE ;If yes, pad line with spaces.
MOV AX,DISP_ORDER[SI] ;Retrieve display order.
CMP AX,-1 ;Should it be displayed?
JZ NEXT_REC1 ;If no, next record.
TEST AX,8000H ;Highlight bit?
JZ COMPARE_ORDER ;If no, use body attribute.
MOV BH,CS:HIGHLIGHT_ATTR ;Else, highlight.
AND AX,NOT 8000H ;Strip highlight bit.
COMPARE_ORDER: CMP AX,BP ;Else, entry less than order?
JB NEXT_REC1 ;If yes, next record.
JA PAD_LINE ;If above, pad with spaces.
CMP CX,40 ;Is this the source directory?
JNZ DO_CHAR2 ;If no, skip mark field.
MOV AL,MARK[SI] ;Else, retrieve mark.
CALL WRITE_SCREEN ;Write it to display.
DEC CX ;Adjust count acting as flag.
CMP CS:BAR_LINE,BP ;Is this the bar line?
JNZ DO_CHAR2 ;If no, write the line.
PUSH BX ;Else, preserve attribute.
MOV BH,CS:BAR_ATTR ;Retrieve bar attribute.
MOV CX,SIZE LIST_NAME ;Write name with bar attribute.
DO_CHAR1: LODSB
CALL WRITE_SCREEN
LOOP DO_CHAR1
POP BX ;Retrieve display attribute.
MOV CX,39 - SIZE LIST_NAME ;Rest of name.
DO_CHAR2: LODSB ;Display the filename.
CALL WRITE_SCREEN
LOOP DO_CHAR2
ADD SI,7 ;Past date/time/order/mark
JMP SHORT DO_LIST_END ; fields.
PAD_LINE: CMP CX,40 ;Is this source directory?
JNZ DO_PAD2 ;If no, just write spaces.
MOV AL,SPACE ;Else, write one space as mark.
CALL WRITE_SCREEN
DEC CX ;Adjust counter flag.
CMP CS:BAR_LINE,BP ;Is this the bar line?
JNZ DO_PAD2 ;If no, write line.
PUSH BX ;Else, display bar and
MOV BH,CS:BAR_ATTR ; then rest of blank line.
MOV CX,SIZE LIST_NAME
DO_PAD1: MOV AL,SPACE
CALL WRITE_SCREEN
LOOP DO_PAD1
POP BX
MOV CX,39 - SIZE LIST_NAME
DO_PAD2: MOV AL,SPACE
CALL WRITE_SCREEN
LOOP DO_PAD2
DO_LIST_END: RET
;-------------------------------------------------------------------------;
; INPUT: SI -> string to parse. ;
; OUTPUT: BP -> filespec start; SI -> filespec end; BX -> filename start. ;
; ZF = 1 if null string. ;
;-------------------------------------------------------------------------;
PARSE_SPEC: LODSB ;Get a byte.
CMP AL,SPACE ;Is it a space char or below?
JA LEADING_END ;If no, found start.
CMP AL,CR ;Is it carriage return?
JNZ PARSE_SPEC ;If no, get next byte.
LEADING_END: DEC SI ;Adjust pointer to string
; start.
MOV BP,SI ;Save start of filespec.
MOV BX,SI ;Use BX as filename start
; pointer
FIND_END: LODSB ;Get a byte.
CMP AL,":" ;Is it a drive delimiter?
JNZ CK_SLASH ;If no, check path delimiter.
MOV DL,[SI-2] ;Else, retrieve drive
; specifier.
AND DL,5FH ;Capitalize.
SUB DL,"A" ;Convert to DOS format.
MOV AH,0EH ;Change drive.
INT 21H
MOV BX,SI ;Save as filename start.
MOV AH,19H ;Get current disk.
INT 21H
CMP AL,DL ;Was it a valid disk request?
JZ FIND_END ;If yes, continue parsing.
FIND_DELIMIT: LODSB ;Else, find delimiter
CMP AL,SPACE
JA FIND_DELIMIT
DEC SI
MOV BP,SI ; and emulate null string.
JMP SHORT PARSE_END
CK_SLASH: CMP AL,"\" ;Is it a path delimiter?
JNZ CK_DELIMITER ;If no, check switch character.
MOV BX,SI ;Else, save as filename start.
CK_DELIMITER: CMP AL,"/" ;Is it a switch delimiter?
JZ FOUND_END ;If yes, end of filespec.
CMP AL,SPACE ;Is it above space character?
JA FIND_END ;If yes, continue until find
; end.
FOUND_END: DEC SI ;Adjust.
PUSH SI
MOV SI,OFFSET CURRENT_DIR ;Get default directory.
CALL GET_DIR
POP SI
PARSE_END: CMP BP,SI ;Any filespec?
RET ;Return result to caller.
;-----------------------------------------------------------;
; INPUT: DI = 0 for first prompt, DI = 1 for second prompt ;
; OUTPUT: SI -> string start. ;
;-----------------------------------------------------------;
PROMPT_USER: CMP SYNTAX_FLAG,1 ;If first time through, display
JZ PROMPT ; syntax message.
MOV SYNTAX_FLAG,1
CALL CLS
XOR DX,DX
CALL SET_CURSOR
MOV DX,OFFSET SIGNATURE
CALL PRINT_STRING
PROMPT: MOV DX,OFFSET SOURCE_PROMPT ;Point to appropriate prompt
OR DI,DI
JZ DISP_PROMPT
MOV DX,OFFSET TARGET_PROMPT
DISP_PROMPT: CALL PRINT_STRING ; and display it.
MOV DX,OFFSET NOT_FOUND
CALL PRINT_STRING
MOV SI,OFFSET USER_INPUT ;User input storage.
MOV BYTE PTR [SI],65 ;Maximum of 65 characters.
MOV DX,SI
MOV AH,0AH
INT 21H ;Buffered Keyboard Input.
INC SI
MOV BYTE PTR [SI],SPACE ;Emulate command line entry.
INC SI ;SI -> string.
RET
;-------------------------------------------------------------------------;
; INPUT: BP -> filespec start; SI -> filespec end; BX -> filename start. ;
; DI -> filespec storage; ;
; OUTPUT: SI -> path end; CY = 0 if filespec exist; CY = 1 if not exist. ;
;-------------------------------------------------------------------------;
FIND_PATH: MOV BYTE PTR [SI],0 ;ASCIIZ the string.
CMP BYTE PTR [SI - 1],":" ;Drive-only?
JZ DO_GLOBAL ;If yes, use global.
CK_ROOT: MOV CX,1 ;CX=1:"\"not=root
;CX=0:"\"=root.
CMP BYTE PTR [BX - 1],"\" ;Filespec start path delimiter?
JNZ CK_PATH ;If no, not root.
CMP BYTE PTR [BX - 2],":" ;Else, prefaced with colon
JZ ROOT ;If yes, then root.
CMP BYTE PTR [BX - 2],SPACE ;Prefaced with white space?
JA CK_TRAILING ;If no, then trailing slash?
ROOT: DEC CX ;Else, root; CX=0 for root
; flag.
JMP SHORT CK_PATH ;Change default path.
CK_TRAILING: CMP BX,SI ;Filename start = filespec end?
JNZ CK_PATH ;If no, not trailing slash.
MOV BYTE PTR [BX - 1],0 ;Else, zero out trailing slash
MOV BX,OFFSET STAR_DOT_STAR ; and use global filespec.
CK_PATH: MOV DX,BP ;See if filespec is a path
CALL CHANGE_DIR ; by changing directory.
JC CK_FILESPEC ;If failed, remove filename.
DO_GLOBAL: MOV BX,OFFSET STAR_DOT_STAR ;Else, use global for filename.
JMP SHORT GOT_FILESPEC ;Done here.
CK_FILESPEC: JCXZ SAVE_DELIMIT ;Is path root?
; If yes leave "\".
DEC BX ;Else, point to slash.
SAVE_DELIMIT: PUSH [BX] ;Preserve filename start.
MOV BYTE PTR [BX],0 ;Temp ASCIIZ twixt path and
; name.
CALL CHANGE_DIR ;Change directory.
POP [BX] ;Restore first byte of
; filename.
PUSHF ;Save CHDIR status.
JCXZ CK_FILENAME ;If root, done here.
INC BX ;Else, re-adjust filename
; pointer.
CK_FILENAME: POPF ;Retrieve CHDIR status.
JNC GOT_FILESPEC ;If successful, got filespec.
CALL FIND_FIRST ;Else, check if filename.
JC RESTORE_DRIVE ;If not, invalid filespec.
GOT_FILESPEC: MOV AH,19H ;Get current drive.
INT 21H
ADD AL,"A" ;Convert to ASCII.
MOV [DI + SPEC_LENGTH],AL ;Make a copy.
STOSB ;Store it.
MOV AL,":" ;Add colon delimiter.
MOV [DI + SPEC_LENGTH],AL ;Make a copy.
STOSB
MOV SI,DI
ADD DI,SPEC_LENGTH
CALL GET_DIR ;Add complete path via DOS.
DEC SI ;Point to "\".
PATH_END: LODSB ;Make a copy and at the same
STOSB ; time find end of path.
OR AL,AL
JNZ PATH_END
DEC DI ;Point to end of path.
PUSH DI ;Add save it.
DEC SI ;Adjust spec path pointer also.
MOV DI,SI
MOV SI,BX
CALL ADD_NAME ;Add spec to end of path.
MOV DX,OFFSET CURRENT_DIR ;Restore default directory.
CALL CHANGE_DIR
POP SI ;Return path end pointer.
CLC ;Successful.
RESTORE_DRIVE: PUSHF
MOV DL,DEFAULT_DRIVE ;Restore default drive.
MOV AH,0EH
INT 21H
POPF
RET
;-----------------------------------------------------------------------------;
; INPUT: DX -> Complete filespec; BP -> Seg name storage. ;
; OUTPUT: BP:DI -> Storage end; CY = 0 if successful ;
; CY = 1 if segment full or ran out of memory and DX -> Error message. ;
;-----------------------------------------------------------------------------;
FIND_FILES: PUSH ES ;Preserve extra segment.
MOV ES,BP ;ES:DI -> filename storage.
XOR DI,DI
MOV WORD PTR ES:[DI],-1 ;Assume empty directory.
CALL FIND_FIRST ;Any files?
JC FILES_DONE ;If no, assumed right.
FIND_NEXT: CALL STORE_NAME ;Store a filename.
CALL CK_AVAIL ;Make sure we not out of
; memory.
JC FILES_END ;If not enough, exit.
MOV AH,4FH ;Find Next Matching.
INT 21H
JNC FIND_NEXT ;If successful store filename.
MOV AX,-1 ;Else, mark the end with -1.
STOSW
CALL SORT ;Sort 'em.
FILES_DONE: CLC ;No error so CY = 0.
FILES_END: POP ES ;Restore extra segment.
RET
;---------------------------------------------------------------------;
; INPUT: ES segment of filenames to sort; Selection sort algorithm. ;
;---------------------------------------------------------------------;
SORT: PUSH DS ;Preserve couple registers.
PUSH DI
PUSH ES
POP DS ;DS = ES.
XOR SI,SI ;SI = first record.
NEXT_SORT: MOV AX,SI ;Carry source pointer in AX.
MOV BX,SI ;Carry destination pointer in DX.
ADD BX,SIZE FILE_RECORD
CMP WORD PTR [BX],-1 ;Sort is done when last record.
JZ SORT_END
PUSH SI ;Save record pointer.
NEXT_RECORD: MOV SI,AX ;Restore inside loop pointers.
MOV DI,BX
MOV CX,SIZE LIST_NAME ;Filename length.
REPZ CMPSB ;Compare the name fields.
JB NO_SWITCH ;If below, no switch.
SWAP: MOV AX,BX ;Else, AX = selected record.
NO_SWITCH: ADD BX,SIZE FILE_RECORD ;Move to next record in list.
CMP WORD PTR [BX],-1 ;End of list?
JNZ NEXT_RECORD ;If no, continue.
POP SI ;Else, restore outside loop
; ptr.
CMP SI,BX ;Did we make a selection?
JZ SELECT_LOOP ;If no, go to next list.
PUSH SI ;Else, save pointer.
MOV CX,(SIZE FILE_RECORD - 2) / 2
MOV DI,AX
NEXT_SWAP: MOV AX,[DI] ;Swap the selection into place.
MOVSW
MOV [SI - 2],AX
LOOP NEXT_SWAP
POP SI
SELECT_LOOP: ADD SI,SIZE FILE_RECORD ;Next record.
JMP NEXT_SORT
SORT_END: POP DI ;Restore registers.
POP DS
RET
;----------------------------------------------------------------------------;
; INPUT: ES:DI pointer; OUTPUT: CY = 1 if segment full or ran out of memory ;
;----------------------------------------------------------------------------;
CK_AVAIL: CMP DI,65535 - (2 * SIZE FILE_RECORD) ;End of segment?
MOV DX,OFFSET TOO_MANY
JA AVAIL_END ;If yes, too many files.
MOV AX,DI ;Else, calculate the pointer
ADD AX,16 + 15 ; in paragraphs.
MOV CL,4
SHR AX,CL
MOV BX,ES
ADD AX,BX
CMP AX,TOP_MEMORY ;Top of memory?
MOV DX,OFFSET OUT_OF_MEMORY ;If yes, not enough memory.
JA AVAIL_END
STC
AVAIL_END: CMC ;Complement the carry status.
RET
;----------------------------------------------------------------------;
; INPUT: BP:DI = current seg:off; OUTPUT: BP = start of new segment. ;
;----------------------------------------------------------------------;
CALC_SEG: ADD DI,SIZE FILE_RECORD + 15
MOV CL,4
SHR DI,CL
ADD BP,DI
RET
;--------------------------------------------------------------------------;
; Keyboard subroutines. ;
; INPUT: BP = BAR_LINE; BX = LISTING_LINE; CX = LAST_BAR; DX = PAGE_LENGTH ;
;--------------------------------------------------------------------------;
PLUS: MOV AL,LITTLE_ARROW ;Use little right arrow for
; mark.
JMP SHORT PLUSMINUS
MINUS: MOV AL,SPACE ;Remove mark with space char.
PLUSMINUS: PUSH DS ;Preserve some registers.
PUSH BX
CALL FIND_BAR ;Find bar record.
JC PLUSMINUS_END ;If on blank line, skip.
MOV MARK[SI],AL ;Else, store the mark.
PLUSMINUS_END: POP BX ;Restore registers.
POP DS
MOV AH,2
INT 16H ;Shift key pressed?
TEST AL,SHIFT_KEYS ;If no, move down line.
JZ DOWN ;Else, move up a line.
UP: DEC BP ;Move bar up a line.
JL PAGEKEY_END ;If < 0, ignore.
CMP BP,BX ;If bar below top, OK.
JAE PAGE_UPDATE
DEC BX ;Else, move listing up a line.
JMP SHORT PAGE_UPDATE
DOWN: INC BP ;Move bar down a line.
CMP BP,CX ;If > last line, then ignore.
JA PAGEKEY_END
ADD DX,BX ;Listing top + page length =
CMP BP,DX ; listing bottom; If bar below
JB PAGE_UPDATE ; listing bottom, OK.
INC BX ;Else move listing down a line.
JMP SHORT PAGE_UPDATE
PGUP: SUB BP,DX ;Move bar up a page.
SUB BX,DX ;Move listing up a page.
JC HOME ;If listing < top, then home.
JMP SHORT PAGE_UPDATE ;Else, OK.
PGDN: ADD BX,DX ;Move listing down a page.
CMP BX,CX ;If <= last line, do bar.
JBE DO_BAR
SUB BX,DX ;Else, back to where we were
MOV BP,CX ; and move bar to last line
JMP SHORT PAGE_UPDATE ; and update.
DO_BAR: ADD BP,DX ;Move bar down a page.
CMP BP,CX ;If bar <= last line, OK.
JBE PAGE_UPDATE
MOV BP,CX ;Else, move bar to last line.
JMP SHORT PAGE_UPDATE
HOME: XOR BP,BP ;Move bar to top.
XOR BX,BX ;Move listing to top.
JMP SHORT PAGE_UPDATE
END_KEY: MOV BP,CX ;Move bar to last line.
INC CX ;Last line + 1 - page length =
SUB CX,DX ; top of last page.
CMP BX,CX ;If less than a full page,
JG PAGE_UPDATE ; then already at last page.
MOV BX,CX ;Else, move listing to last
; page.
JMP SHORT PAGE_UPDATE
PAGE_UPDATE: MOV BAR_LINE,BP ;Store the new bar
MOV LISTING_LINE,BX ; and listing line start.
PAGEKEY_END: RET
;----------------------------------------------;
F1: MOV AH,FILES_MODE ;Retrieve current file mode.
INC AH ;Go to next mode.
INC AH
CMP AH,ALIKE ;Is it above last mode, Alike?
JBE F1_STORE ;If no, OK.
XOR AH,AH ;Else, wrap to All file mode.
JMP SHORT F1_STORE ;Store and update.
S_F1: MOV AH,FILES_MODE ;Retrieve current file mode.
DEC AH ;Reverse direction.
DEC AH
CMP AH,ALL ;Is it below All file mode?
JGE F1_STORE ;If no, OK.
MOV AH,ALIKE ;Else, wrap to last mode,
; Alike?
F1_STORE: MOV FILES_MODE,AH ;Store new mode.
CALL DISPLAY_MENU ;Update the menu.
CALL MATCH ;Update the matches.
RET
;----------------------------------------------;
F2: MOV AL,LITTLE_ARROW ;Mark all records with little
JMP SHORT DO_MARKS ; right arrow.
;----------------------------------------------;
F3: MOV AL,SPACE ;Mark all records with space
; char
DO_MARKS: PUSH DS
MOV DS,SOURCE_SEG
XOR SI,SI ;DS:SI -> source records.
JMP SHORT DO_MARK
NEXT_ALL: ADD SI,SIZE FILE_RECORD ;Next record.
DO_MARK: CMP LIST_NAME[SI],-1 ;Is this last record?
JZ MARKS_END ;If yes, done.
MOV MARK[SI],AL ;Else, store mark.
JMP SHORT NEXT_ALL
MARKS_END: POP DS
RET
;----------------------------------------------;
F4: MOV RESPONSE,OFFSET COPY_MSG
JMP SHORT CK_COPY
F5: MOV RESPONSE,OFFSET MOVE_MSG
CMP FILES_MODE,ALIKE ;Can't move files in alike
; mode.
JNZ CK_COPY
CALL BEEP
RET
CK_COPY: PUSH DS ;Preserve data segment.
MOV DS,SOURCE_SEG ;DS:SI -> source records.
XOR SI,SI
XOR CX,CX ;Initialize counter to zero.
JMP SHORT COUNT_MARKS ;Count the marks.
FOUND_MARK: INC CX ;Increment count.
NEXT_COUNT: ADD SI,SIZE FILE_RECORD ;Next record.
COUNT_MARKS: CMP LIST_NAME[SI],-1 ;Last record?
JZ GOT_COUNT ;If yes, got count of marks.
CMP DISP_ORDER[SI],-1 ;Else, is record displayed?
JZ NEXT_COUNT ;If no, skip.
CMP MARK[SI],LITTLE_ARROW ;Else, is it marked?
JNZ NEXT_COUNT ;If no, skip.
JMP FOUND_MARK ;Else, count it.
GOT_COUNT: POP DS ;Restore data segment.
MOV MARKED_COUNT,CX ;Save count of marks.
JCXZ COPY_BAR ;If none marked, copy bar.
CALL GET_COUNT ;Else, convert count to ASCII.
MOV BX,OFFSET MARKED_MSG ;Will display marked message.
CALL GET_RESPONSE ;Ask user if should continue.
JNZ COPY_MARKED ;If confirmed, continue.
JMP F4_END ;Else, exit.
COPY_MARKED: PUSH LISTING_LINE ;Preserve current page
PUSH BAR_LINE ; and bar line.
CALL HOME ;Start searching for marked
XOR SI,SI ; files from home position.
JMP SHORT CK_ABORT ;Go check keypress for abort.
NEXT_REC3: ADD SI,SIZE FILE_RECORD ;Next record.
NEXT_MARKED: MOV BP,BAR_LINE ;Paging commands need line
MOV BX,LISTING_LINE ; page data.
MOV CX,LAST_BAR
MOV DX,PAGE_LENGTH
CALL DOWN ;Move bar down a line.
CK_ABORT: CALL CK_KEY ;Any keypress?
JNZ MARKED_END ;If yes, quite copying.
CALL DISP_LISTINGS ;Else, display current bar.
PUSH DS
MOV DS,SOURCE_SEG
MOV AX,DISP_ORDER[SI] ;Retrieve display order and
MOV BL,MARK[SI] ; mark of current record.
POP DS
CMP AX,-1 ;Is it displayed?
JNZ CK_BAR ;If yes, see if bar record.
ADD SI,SIZE FILE_RECORD ;Else, next record.
JMP CK_ABORT
CK_BAR: AND AX,NOT 8000H ;Else, strip highlight bit.
CMP AX,BAR_LINE ;Is it current line?
JA NEXT_MARKED ;If above, skip for now.
CMP BL,LITTLE_ARROW ;Is it marked?
JNZ NEXT_REC3 ;If no, next record.
MOV BP,SI ;Else, copy; First convert name
CALL GET_SOURCE ; back to dot delimited ASCIIZ.
CALL COPY ;Copy the file.
JC MARKED_END ;If failed, exit.
MOV SI,BP ;Else, retrieve record pointer.
DEC MARKED_COUNT ;Decrement marked count.
JNZ NEXT_REC3 ;Continue until zero.
CALL DISP_LISTINGS ;Display last asterisk.
MARKED_END: POP BAR_LINE ;Restore bar and page.
POP LISTING_LINE
JMP SHORT F4_REREAD ;Done.
;----------------------------------------------;
COPY_BAR: PUSH DS
CALL FIND_BAR ;Find bar record.
POP DS
JNC VALID_BAR ;Is bar on blank line?
CALL BEEP ;If yes, beep and exit.
JMP SHORT F4_END
VALID_BAR: MOV BP,SI ;Else, convert name to dot
CALL GET_SOURCE ; delimited ASCIIZ.
XOR BX,BX ;No "marked files" message.
CALL GET_RESPONSE ;Ask user for confirmation.
JZ F4_END ;If aborted, exit.
CALL COPY ;Else, copy the highlighted
; file.
F4_REREAD: CMP RESPONSE,OFFSET MOVE_MSG ;Did we move files?
JNZ READ_TARGET ;If no, just read target.
CALL SOURCE_DIR ;Else, get new source dir also.
READ_TARGET: CALL TARGET_DIR
JNC F4_UPDATE
CALL MENU_OFFSET ;Calculate menu offset.
ADD DI,80 * 2 ;Place error message on second
MOV BH,MENU_ATTR ; menu line with menu
; attribute.
MOV SI,DX ;Error message.
CALL WRITE_STRING ;Write the message.
MOV SI,OFFSET PRESS_MSG
CALL WRITE_STRING
CALL HIDE_CURSOR ;Rehide the cursor.
CALL BEEP ;Draw attention with beep.
CALL GET_KEY ;Pause for keystroke.
CALL CLS
XOR DX,DX
CALL SET_CURSOR
JMP ERROR ;Abort to DOS.
F4_UPDATE: CMP RESPONSE,OFFSET MOVE_MSG ;Did we do a move?
JZ KEEP_BAR ;If yes, preserve bar/page.
CMP FILES_MODE,UNIQUE ;Are different files
; displayed?
JNZ KEEP_BAR ;If no, then preserve
; bar/page.
CALL MATCH ;Else, home bar and page.
JMP SHORT F4_END
KEEP_BAR: CALL DO_MATCH
F4_END: CALL HIDE_CURSOR ;Rehide the cursor.
CALL DISPLAY_MENU ;Redisplay the menu.
CALL CLEAR_KEY ;Clear the keyboard.
RET ;Done.
;--------------------------------------------------;
; OUTPUT: CY = 1 if failed; CY = 0 if successful. ;
;--------------------------------------------------;
COPY: MOV SI,OFFSET SOURCE_NAME ;Add filename to both source
MOV DI,SOURCE_END ; and target path spec.
CALL ADD_NAME
MOV SI,OFFSET SOURCE_NAME
MOV DI,TARGET_END
CALL ADD_NAME
MOV DX,OFFSET SOURCE_FILE ;Get attribute, size and date
CALL FIND_FIRST ; of source file via
; Find First.
MOV SI,OFFSET DTA
MOV DI,OFFSET SOURCE_STATS ;Store the info.
MOV CX,SIZE MATCHING
REP MOVSB
MOV AX,3D00H ;Open source file for reading.
INT 21H
JNC SAVE_HANDLE
JMP COPY_END ;If failed, exit.
SAVE_HANDLE: MOV DI,AX ;Else, save source handle.
MOV DX,OFFSET TARGET_FILE ;Point to target name.
CALL FIND_FIRST ;Does it exist?
JNC CK_IF_MOVE ;If yes, see if move request.
CALL DISK_FREE ;Else, get disk free space.
JMP SHORT CK_FREE ;Go see if enough room.
CK_IF_MOVE: CMP RESPONSE,OFFSET MOVE_MSG ;Is it a move request?
JNZ CK_IF_SAME ;If no, see if same as source.
STC
JMP CLOSE_READ ;Else, exit with error flag.
CK_IF_SAME: MOV CL,DTA.ATTRIBUTE ;Get attribute of target
XOR CH,CH
MOV SI,CX ; and save.
XOR CX,1 ;Flip read-only attribute.
MOV AX,4301H ;Change file mode.
INT 21H
MOV DX,OFFSET SOURCE_FILE ;Now get source attribute.
MOV AX,4300H
INT 21H
JNC CK_CHANGED
JMP COPY_END ;Exit if error.
CK_CHANGED: CMP CL,SOURCE_STATS.ATTRIBUTE ;Else, see if it has
; changed.
PUSHF ;Save compare results.
MOV DX,OFFSET TARGET_FILE ;Restore target attribute.
MOV CX,SI
MOV AX,4301H ;Change file mode.
INT 21H
POPF ;Retrieve compare results.
STC ;Assume files are the same.
JNZ CLOSE_READ ;If attr changed, they are
; so exit
CALL DISK_FREE ;Else, get disk free space.
ADD AX,DTA.SIZE_LOW
ADC DX,DTA.SIZE_HIGH ;Target size + disk free.
CK_FREE: SUB AX,SOURCE_STATS.SIZE_LOW ;Total - source size.
SBB DX,SOURCE_STATS.SIZE_HIGH
JB CLOSE_READ ;If negative, not enough
; room.
CREATE: MOV DX,OFFSET TARGET_FILE ;Create and truncate target
XOR CX,CX ; file to zero
MOV AH,3CH
INT 21H
JC CLOSE_READ
MOV SI,AX ;Save handle.
MOV DS,BUFFER_SEG ;Read/write buffer.
XOR DX,DX ;Offset zero.
COPY_READ: MOV BX,DI ;Retrieve read handle.
MOV CX,CS:BUFFER_SIZE ;Buffer size.
MOV AH,3FH
INT 21H ;Read source file.
JC COPY_DONE ;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 COPY_DONE ;If failed, exit.
CMP CX,AX ;Write same number as read?
STC ;Assume no.
JNZ COPY_DONE ;If no, failed; exit.
CMP CX,CS:BUFFER_SIZE ;Was it a full read?
JZ COPY_READ ;If yes, there must be more.
CHANGE_DATE: MOV BX,SI ;Write handle.
MOV CX,CS:SOURCE_STATS.FILE_TIME ;Else, make time/date
MOV DX,CS:SOURCE_STATS.FILE_DATE ; same as source.
MOV AX,5701H
INT 21H
MOV DS,CS:SOURCE_SEG ;Mark success with asterisk.
MOV DS:MARK[BP],"*"
COPY_DONE: PUSH CS ;Restore data segment.
POP DS
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.
COPY_END: PUSHF ;Save status.
JNC CK_DELETE ;If no carry, successful.
CALL HIDE_CURSOR
CALL MENU_OFFSET ;Else, calculate menu offset.
ADD DI,80 * 2 ;Place error message on second
MOV BH,MENU_ATTR ; menu line with menu
MOV SI,RESPONSE ; attribute.
CALL WRITE_STRING ;Write the message.
MOV SI,OFFSET PRESS_MSG
CALL WRITE_STRING
CALL BEEP ;Draw attention with beep.
CALL GET_KEY ;Pause for keystroke.
JMP SHORT COPY_RETURN
CK_DELETE: CMP RESPONSE,OFFSET MOVE_MSG ;Move request?
JNZ COPY_RETURN ;If no, done here.
POPF
MOV DX,OFFSET SOURCE_FILE ;Else, delete the source
MOV AH,41H ; file.
INT 21H
PUSHF
COPY_RETURN: POPF ;Retrieve status.
RET
;----------------------------------------------;
; INPUT: SI -> Filename; DI -> End of path. ;
;----------------------------------------------;
ADD_NAME: MOV AL,"\" ;Backslash delimiter.
CMP [DI - 1],AL ;Is there a delimiter?
JZ ADD_IT ;If yes, continue.
STOSB ;Else, add delimiter.
ADD_IT: LODSB ;Add the filename.
CMP AL,"a"
JB GOT_NAME
CMP AL,"z"
JA GOT_NAME
AND AL,5FH ;Capitalize.
GOT_NAME: STOSB
OR AL,AL ;ASCIIZ.
JNZ ADD_IT
RET
;----------------------------------------------;
F6: PUSH DS ;Preserve segment registers.
PUSH ES ;Place cursor back on screen at
MOV DX,300H ; third row in case there is
CALL SET_CURSOR ; a DOS print error message.
MOV BX,SOURCE_SEG ;BX -> source segment.
XOR SI,SI ;Start of source.
XOR DI,DI ;Start of target.
XOR BP,BP ;Start of order.
JMP SHORT PRINT_HEADER ;Print directory header on
; page.
NEXT_PRINT: MOV DS,BX ;Source on left side of page.
CMP LIST_NAME[SI],-1 ;End of listing?
JNZ PRINT1 ;If no, continue.
MOV DS,CS:TARGET_SEG ;Target listing segment
CMP LIST_NAME[DI],-1 ;End of Target listing also?
JZ F6_END ;If yes, done.
PRINT1: MOV DS,BX ;Source segment.
MOV CX,40 ;40 characters per half line.
CALL DO_LINE ;Print a spec.
XCHG SI,DI ;Target pointer.
MOV DS,CS:TARGET_SEG ;Target segment.
MOV CX,40
CALL DO_LINE ;Print left half of line.
XCHG SI,DI ;Restore pointers.
CALL NEW_LINE ;CR LF to new line.
INC BP ;Increment order counter.
MOV AX,BP
XOR DX,DX
MOV CX,54
DIV CX
OR DX,DX ;If MOD 54 = 0 then formfeed.
JNZ NEXT_PRINT
CALL FORMFEED
PRINT_HEADER: PUSH CS ;Back to code segment.
POP DS
PUSH SI ;Save some pointers.
PUSH BP
MOV BP,OFFSET PRINT_CHAR ;Print directory heading at
CALL WRITE_PATH ; top of each page.
POP BP ;Restore the pointers.
POP SI
CALL NEW_LINE ;Double space before filenames.
CALL NEW_LINE
JMP NEXT_PRINT ;Next line.
F6_END: CALL FORMFEED ;Spit out last printed page.
CALL HIDE_CURSOR
POP ES ;Restore segments.
POP DS
RET
;----------------------------------------------------;
; INPUT: DS:SI -> filename. Entry point at DO_LINE ;
;----------------------------------------------------;
NEXT_REC2: ADD SI,SIZE FILE_RECORD ;Next filename.
DO_LINE: CMP LIST_NAME[SI],-1 ;Last record?
JZ BLANK_LINE ;If yes, blank line.
MOV DX,DISP_ORDER[SI] ;Retrieve display order.
CMP DX,-1 ;Is it displayed?
JZ NEXT_REC2 ;If no, next record.
MOV AX,OFFSET NOT_RECENT ;Assume not a recent file.
TEST DX,8000H ;Is it highlighted?
JZ COMPARE_PRINT ;If no, see if current order.
MOV AX,OFFSET RECENT ;Else, use recent codes.
AND DX,NOT 8000H ;Strip highlight bit.
COMPARE_PRINT: CMP DX,BP ;Entry less than order?
JB NEXT_REC2 ;If yes, next record.
JA BLANK_LINE ;If above, blank line.
PUSH BX
PUSH CX
MOV BX,AX ;Get offset.
MOV AL,CS:[BX] ;Get char or code count.
CMP AL,6 ;Is it above 6?
JA PRINT_MARK ;If yes, assumed right.
MOV CL,AL ;Else, it's a printer code
; count.
XOR CH,CH ;Zero in high half.
NEXT_CODE: INC BX ;Point to a code.
MOV AL,CS:[BX] ;Retrieve it.
CALL PRINT_CHAR ;Print it.
LOOP NEXT_CODE
MOV AL,SPACE ;Indent with a space.
PRINT_MARK: CALL PRINT_CHAR
POP CX
POP BX
DEC CX
PRINT_LINE: LODSB ;Else, print the line.
CALL PRINT_CHAR
LOOP PRINT_LINE
ADD SI,7 ;Pointer date/time/order
JMP SHORT DO_LINE_END ; fields.
BLANK_LINE: MOV AL,SPACE ;Blank line with spaces.
CALL PRINT_CHAR
LOOP BLANK_LINE
DO_LINE_END: RET
;----------------------------------------------;
NEW_LINE: MOV AL,CR ;Carriage return linefeed.
CALL PRINT_CHAR
MOV AL,LF
CALL PRINT_CHAR
RET
FORMFEED: MOV AL,FF ;Formfeed.
PRINT_CHAR: MOV DL,AL ;Print via DOS.
MOV AH,5
INT 21H
RET
;-----------------------------------------------------------------------;
; OUTPUT: DS:SI -> Highlighted bar record; CY = 1 if bar on blank line. ;
;-----------------------------------------------------------------------;
FIND_BAR: MOV DI,BAR_LINE ;Retrieve bar line.
XOR SI,SI ;DS:SI -> source records.
MOV DS,SOURCE_SEG
JMP SHORT GET_BAR
NEXT_BAR: ADD SI,SIZE FILE_RECORD ;Next record.
GET_BAR: CMP LIST_NAME[SI],-1 ;End of listing?
JZ NO_NAME ;If yes, no name.
MOV BX,DISP_ORDER[SI] ;Retrieve display order.
CMP BX,-1 ;Is it displayed?
JZ NEXT_BAR ;If no, next record.
AND BX,NOT 8000H ;Else, strip highlight bit.
CMP BX,DI ;Is display order same as bar?
JB NEXT_BAR ;If > then next record.
JA NO_NAME ;If above, then no name.
CLC ;If same, then valid name.
RET
NO_NAME: STC
RET
;-----------------------------------------------------------------;
; INPUT: CX = binary count; OUTPUT: SOURCE_NAME = ASCII of count. ;
;-----------------------------------------------------------------;
GET_COUNT: PUSH BX
PUSH DI
MOV AX,CX ;Retrieve number.
MOV BX,10 ;Divisor of ten.
XOR CX,CX ;Zero in counter.
NEXT_DIV: XOR DX,DX ;Zero in high half.
DIV BX ;Divide by ten.
ADD DL,"0" ;Convert to ASCII.
PUSH DX ;Save results.
INC CX ;Also increment count.
OR AX,AX ;Are we done?
JNZ NEXT_DIV ;Continue until zero.
MOV DI,OFFSET SOURCE_NAME
NEXT_NUMBER: POP AX ;Retrieve numbers.
STOSB ;And store.
LOOP NEXT_NUMBER
XOR AL,AL ;ASCIIZ string.
STOSB
POP DI
POP BX
RET
;--------------------------------------------------------------;
; INPUT: BP -> Source filename; OUTPUT: SOURCE_NAME = filename ;
;--------------------------------------------------------------;
GET_SOURCE: PUSH DS
MOV DS,SOURCE_SEG ;DS:SI -> Source name.
MOV SI,BP
MOV DI,OFFSET SOURCE_NAME ;Name storage.
MOV CX,8 ;8 characters of name.
NEXT_NAME: LODSB
CMP AL,SPACE ;End of name?
JZ EXTENSION ;If yes, do extension.
STOSB
LOOP NEXT_NAME
EXTENSION: MOV SI,BP ;Retrieve name start.
ADD SI,9 ;Move to extension field.
CMP BYTE PTR [SI],SPACE ;Is there any extension?
JZ NAME_DONE ;If no, done.
MOV AL,"." ;Else, add delimiting dot.
STOSB
MOV CX,3 ;3 characters for extension.
NEXT_EXT: LODSB
CMP AL,SPACE
JZ NAME_DONE
STOSB
LOOP NEXT_EXT
NAME_DONE: XOR AL,AL ;ASCIIZ.
STOSB
POP DS
RET
;-----------------------------------------------------------------------------;
; INPUT: BX -> MARKED_MSG if marked; OUTPUT: ZF=1 if cancel; ZF=0 if confirm. ;
;-----------------------------------------------------------------------------;
GET_RESPONSE: PUSH BX ;Save message pointer.
CALL MENU_OFFSET ;Calculate menu screen offset.
MOV BH,MENU_ATTR ;Menu attribute.
MOV CX,80 * 2 ;Blank out the two lines of
; menu.
NEXT_MENU: MOV AL,SPACE
CALL WRITE_SCREEN
LOOP NEXT_MENU
MOV DH,ROWS ;Retrieve the rows.
DEC DH ;Next to last line.
XOR DL,DL
CALL SET_CURSOR ;Set the cursor.
MOV SI,RESPONSE ;Write TTY, copy or move
CALL TTY_STRING ; message.
MOV SI,OFFSET SOURCE_NAME ;Write filename or mark count.
CALL TTY_STRING
POP BX ;Retrieve message pointer.
CMP BX,OFFSET MARKED_MSG ;Is it "marked files" message?
JNZ DO_TO ;If no, skip.
MOV SI,BX ;Else, display it.
CALL TTY_STRING
DO_TO: MOV SI,OFFSET TO_MSG ;Display rest of
CALL TTY_STRING ; "to Target Directory? Y/N"
MOV SI,OFFSET DIRECTORY_MSG ; message.
CALL TTY_STRING
MOV SI,OFFSET QUERY_MSG
CALL TTY_STRING
NEXT_RESPONSE: CALL GET_KEY ;Get a key.
CMP AL,ESC_SCAN ;If ESC, abort.
JZ RESPONSE_END
CMP AL,N_SCAN ;If "N", abort.
JZ RESPONSE_END
CMP AL,Y_SCAN ;If not "Y", ignore.
JNZ NEXT_RESPONSE ;Else, cursor to
MOV DX,300H ; third row in case there is
CALL SET_CURSOR ; a DOS critical error message.
OR AL,1 ;Return ZF = 0 to confirm.
RESPONSE_END: RET
;----------------------------------------------;
VIDEO_SETUP: MOV AX,500H ;Make sure active page is zero.
INT 10H
MOV AX,40H ;Point to the ROM BIOS data
MOV ES,AX ; area
MOV AL,ES:CRT_MODE ;Retrieve current video mode.
CMP AL,7 ;Is it mono mode?
JZ GET_ROWS ;If yes, continue.
ADD STATUS_REG,20H ;Else, adjust status register
ADD VIDEO_SEG,800H ; and video segment.
CMP AL,2 ;Is it BW80?
JZ GET_ROWS ;If yes, continue.
MOV HEADING_ATTR,INVERSE_BLUE
MOV ACTIVE_ATTR,INTENSE
MOV BODY_ATTR,BLUE ;Else, use color attributes.
MOV HIGHLIGHT_ATTR,INTENSE_BLUE
MOV MENU_ATTR,INVERSE_BLUE
MOV BAR_ATTR,CYAN
CMP AL,3 ;Is it mode CO80?
JZ GET_ROWS ;If yes, continue.
MOV AX,300H ;Else, change video mode to CO80.
INT 10H
GET_ROWS: MOV AL,ES:CRT_ROWS ;Retrieve rows - 1.
OR AL,AL ;BIOS supported?
JNZ STORE_ROWS ;If yes, store
MOV AL,24 ;Else, use 25 lines.
STORE_ROWS: MOV ROWS,AL ;Store rows.
XOR AH,AH
SUB AX,4
MOV PAGE_LENGTH,AX ;Page length = display rows-4.
;----------------------------------------------;
DISPLAY_SETUP: PUSH CS
POP ES
CALL CLS ;Clear screen.
CALL HIDE_CURSOR ;Hide the cursor off screen.
MOV BL,MENU_ATTR ;Turn on border.
MOV CL,4
SHR BL,CL
MOV AH,0BH
INT 10H
MOV SI,OFFSET COPYRIGHT ;Point to copyright message.
XOR DI,DI ;Point to top left of display.
MOV BH,HEADING_ATTR ;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.
CALL DISPLAY_DIRS ;Display directory heading.
MOV BP,OFFSET WRITE_SCREEN ;Display directories.
CALL WRITE_PATH
MOV BH,BODY_ATTR ;Body attribute.
MOV CX,PAGE_LENGTH ;Page length.
MOV DI,(3 * 160) + (2 * 40) ;Middle of third row.
NEXT_LINE: MOV AL,VERT_LINE ;Display delimiting
CALL WRITE_SCREEN ; vertical lines.
ADD DI,(2 * 79)
LOOP NEXT_LINE
;----------------------------------------------;
DISPLAY_MENU: MOV SI,OFFSET MENU ;Display menu on bottom two
CALL MENU_OFFSET ; lines.
MOV CL,MENU_ATTR ;Menu attribute.
MOV CH,ACTIVE_ATTR ;Active mode attribute.
MOV BH,CL
CALL WRITE_STRING ;"F1"
CMP FILES_MODE,ALL
JNZ DISPLAY3
MOV BH,CH
DISPLAY3: CALL WRITE_STRING ;"All files"
MOV BH,CL
CMP FILES_MODE,UNIQUE
JNZ DISPLAY4
MOV BH,CH
DISPLAY4: CALL WRITE_STRING ;"Different files"
MOV BH,CL
CMP FILES_MODE,ALIKE
JNZ DISPLAY5
MOV BH,CH
DISPLAY5: CALL WRITE_STRING ;"Alike files"
MOV BH,CL
CALL WRITE_STRING ;Print rest of menu
RET
;----------------------------------------------;
DISPLAY_DIRS: MOV BH,ACTIVE_ATTR ;Directory attribute.
MOV DI,80 * 2
MOV SI,OFFSET SOURCE_MSG ;Display "Source Directory"
CALL WRITE_STRING
CALL WRITE_STRING
MOV CX,SOURCE_COUNT
CALL WRITE_COUNT ;Write the source file count.
MOV SI,OFFSET TARGET_MSG ;Display "Target Directory".
CALL WRITE_STRING
MOV SI,OFFSET DIRECTORY_MSG
CALL WRITE_STRING
MOV CX,TARGET_COUNT ;And finally target files
; count.
WRITE_COUNT: CALL GET_COUNT
MOV CX,GAP
NEXT_GAP: MOV AL,SPACE ;Space over some.
CALL WRITE_SCREEN
LOOP NEXT_GAP
MOV SI,OFFSET SOURCE_NAME ;Storage for count in ASCIIZ.
MOV CX,40 - DIRECTORY_LEN
NEXT_NUM: LODSB ;Get a number.
OR AL,AL ;End of string?
JZ WRITE_FILES ;If yes, add "files" message.
CALL WRITE_SCREEN
LOOP NEXT_NUM
WRITE_FILES: MOV SI,OFFSET FILES_MSG
CALL WRITE_STRING
NEXT_SPACE: MOV AL,SPACE ;Pad balance with spaces.
CALL WRITE_SCREEN
LOOP NEXT_SPACE
RET
;----------------------------------------;
; OUTPUT: DI -> Screen offset for menu. ;
;----------------------------------------;
MENU_OFFSET: MOV AL,2 * 80 ;(Rows * chars) - chars
MUL ROWS ; = menu offset.
SUB AX,2 * 80
MOV DI,AX
RET
;-----------------------------------------------------------------------------;
; INPUT: SI->Filespec; DI->Video buffer; BP=OFFSET WRITE_SCREEN OR PRINT_CHAR;
;-----------------------------------------------------------------------------;
WRITE_PATH: MOV SI,OFFSET SOURCE_SPEC ;Write first string.
CALL DISP_FILESPEC
MOV SI,OFFSET TARGET_SPEC ;Write second string.
DISP_FILESPEC: MOV AL,SPACE ;Indent one space.
CALL BP
MOV CX,39
NEXT_FILESPEC: LODSB
OR AL,AL ;Null terminates string.
JZ PAD_SPACES ;Pad balance with spaces.
CALL BP
LOOP NEXT_FILESPEC
JMP SHORT FILESPEC_END
PAD_SPACES: MOV AL,SPACE
CALL BP
LOOP PAD_SPACES
FILESPEC_END: 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
STOSW ; OK 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
;----------------------------------------------;
STORE_NAME: MOV SI,OFFSET DTA.FILE_NAME ;Point to filename.
PUSH DI
MOV AX,SPACE SHL 8 + SPACE ;Initiate storage with spaces.
MOV CX,(SIZE FILE_RECORD - 6) / 2 ;Fill out with spaces.
REP STOSW
POP DI ;Point to start of storage
; again.
MOV CX,SIZE LIST_NAME ;Store 12 bytes of filename.
NEXT_STORE: LODSB ;Get a byte.
OR AL,AL ;End of filename?
JZ END_STORE ;If yes, finish with blanks.
CMP AL,"." ;Is it the period?
JNZ STORE_BYTE ;If no, store.
SUB CX,3 ;Else, store 3 spaces.
MOV AL,SPACE
REP STOSB
ADD CX,3
JMP SHORT NEXT_STORE ;Get next byte.
STORE_BYTE: STOSB ;Store byte.
LOOP NEXT_STORE ;Get next byte.
END_STORE: MOV AL,SPACE ;Pad balance with spaces.
REP STOSB
PUSH DI ;Save pointer.
ADD DI,8 ;Move to end of bytes field.
MOV DX,DTA.SIZE_LOW ;Retrieve high and low words
MOV AX,DTA.SIZE_HIGH ; of size in bytes.
MOV BX,10 ;Convert to decimal.
STD ;Reverse direction.
NEXT_SIZE: MOV CX,DX ;Low word in CX.
XOR DX,DX ;Zero in high half.
DIV BX ;Convert to decimal.
XCHG AX,CX ;Retrieve low word.
DIV BX
XCHG AX,DX ;Retrieve remainder.
ADD AL,"0" ;Convert to ASCII.
STOSB ;Store it.
MOV AX,CX ;Are we done?
OR CX,DX
JNZ NEXT_SIZE ;If no, divide again.
CLD ;Back to forward direction.
POP DI ;Retrieve pointer.
ADD DI,11 ;Move to date field.
DATE: MOV DX,DTA.FILE_DATE ;Retrieve date.
MOV AX,DX
MOV CL,5 ;Shift to lowest bits.
SHR AX,CL
AND AX,1111B ;Mask off all but month.
MOV CL,0FFH ;Flag as no leading zeros.
MOV CH,"-" ;Delimiting character.
CALL STORE_WORD ;Store it.
MOV AX,DX ;Retrieve date.
AND AX,11111B ;Mask off all but day.
XOR CL,CL ;Flag include leading zeros.
MOV CH,"-"
CALL STORE_WORD ;Store it.
MOV AX,DX ;Retrieve date for last time.
MOV CL,9
SHR AX,CL ;Mask off all but year.
ADD AX,80 ;Adjust to ASCII.
CMP AX,100 ;Past year 2000?
JB DISPLAY_DATE ;If no, display. Else, adjust
SUB AX,100 ; for next century. (Planning
; ahead!)
DISPLAY_DATE: XOR CL,CL ;Display leading zeros.
MOV CH,SPACE
CALL STORE_WORD ;Store it.
TIME: INC DI ;Move to time field.
MOV DX,DTA.FILE_TIME ;Retrieve time.
MOV AX,DX
MOV CL,11 ;Shift to hours bits.
SHR AX,CL
PUSH AX
CMP AX,12 ;Past noon?
JBE MERIDIAN
SUB AX,12 ;If yes, adjust.
MERIDIAN: CMP AX,0 ;Midnight?
JNZ NOT_MIDNIGHT
MOV AX,12 ;If yes, adjust.
NOT_MIDNIGHT: MOV CL,0FFH ;Suppress leading zeros.
MOV CH,":"
CALL STORE_WORD ;Store it.
MOV AX,DX ;Retrieve time.
MOV CL,5 ;Shift to minutes bits.
SHR AX,CL
AND AX,111111B ;Mask off all but minutes.
XOR CL,CL
POP DX ;Retrieve hours.
MOV CH,"p" ;Assume PM.
CMP DX,12 ;Is it PM?
JAE PM
MOV CH,"a" ;If no, AM.
PM: CALL STORE_WORD ;Store it.
INC DI ;Pointer past mark.
MOV AX,DTA.FILE_DATE
STOSW ;Store date in DOS format.
MOV AX,DTA.FILE_TIME
STOSW ;Store time in DOS format.
INC DI ;Bump pointer past DISP_ORDER.
INC DI
RET ;Done here.
;-----------------------------------------------------------------------;
; Converts a two byte hex number to decimal followed by delimiter. ;
; INPUT: AX = hex number; BL = 10; CH = delimiter character to store. ;
; CL = 0 if zeros are to be stored; CL = -1 if leading zeros ignored. ;
; ES:DI points to storage. ;
;-----------------------------------------------------------------------;
STORE_WORD: DIV BL ;Divide by ten.
ADD AX,"00" ;Convert to ASCII.
CMP CL,0 ;Display leading zero?
JZ STORE_IT ;If yes, store as is.
CMP AL,"0" ;Is it a leading zero?
JNZ STORE_IT ;If no, store it.
MOV AL,SPACE ;Else, store a space.
STORE_IT: STOSW
MOV AL,CH ;Also store delimiter character
STOSB
RET
;----------------------------------------------;
CLS: XOR CX,CX ;Top left corner.
MOV DH,ROWS
MOV DL,80 - 1
MOV BH,BODY_ATTR ;Normal attribute.
MOV AX,600H ;Scroll active page.
INT 10H
RET
;----------------------------------------------;
GET_DIR: MOV BYTE PTR [SI],"\" ;DOS doesn't preface directory
INC SI ; with slash so we must.
XOR DL,DL
MOV AH,47H ;Get current directory.
INT 21H
RET
CHANGE_DIR: MOV AH,3BH ;Change current directory.
INT 21H
RET
;----------------------------------------------;
FIND_FIRST: XOR CX,CX ;Normal files.
MOV AH,4EH ;Find first.
INT 21H
RET
;----------------------------------------------;
PRINT_STRING: MOV AH,9 ;Print string via DOS.
INT 21H
RET
;----------------------------------------------;
DISK_FREE: MOV DL,BYTE PTR TARGET_SPEC ;Target drive.
SUB DL,"A" - 1
MOV AH,36H ;Disk free space.
INT 21H
XOR DX,DX
MUL BX ;Sectors per cluster * clusters
MUL CX ;Result times bytes per cluster
RET ; = available bytes.
;----------------------------------------------;
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
;----------------------------------------------;
HIDE_CURSOR: MOV DH,ROWS ;Retrieve CRT rows.
INC DH ;Move one line below off
; screen.
XOR DL,DL ;Column zero.
SET_CURSOR: XOR BH,BH ;Page zero.
MOV AH,2 ;Set cursor position.
INT 10H
RET
;----------------------------------------------;
GET_KEY: MOV AH,0 ;Wait for next keyboard input.
INT 16H
XCHG AH,AL
RET
CK_KEY: MOV AH,1 ;Is 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
TTY: CALL WRITE_TTY
TTY_STRING: LODSB ;ASCIIZ marks end of string.
OR AL,AL
JNZ TTY
RET
;----------------------------------------------;
EVEN
DTA = $
SOURCE_STATS = DTA + SIZE MATCHING
CURRENT_DIR = SOURCE_STATS + SIZE MATCHING
SOURCE_SPEC = CURRENT_DIR + SPEC_LENGTH
SOURCE_FILE = SOURCE_SPEC + SPEC_LENGTH
TARGET_SPEC = SOURCE_FILE + SPEC_LENGTH
TARGET_FILE = TARGET_SPEC + SPEC_LENGTH
USER_INPUT = TARGET_FILE + SPEC_LENGTH
SOURCE_NAME = USER_INPUT + 14
STACK_POINTER = SOURCE_NAME + 68 + 256
SOURCE_OFFSET = STACK_POINTER
_TEXT ENDS
END START