Category : Files from Magazines
Archive   : N7V11.ZIP
Filename : COMPARE.ASM

 
Output of file : COMPARE.ASM contained in archive : N7V11.ZIP
TITLE COMPARE.ASM
PAGE 60,132

;-------------------------------------------;
; COMPARE - Compares text and binary files ;
; PC Magazine - Michael J. Mefford ;
;-------------------------------------------;

_TEXT SEGMENT PUBLIC 'CODE' ;********************************;
ASSUME CS:_TEXT,DS:_TEXT ;* *;
ASSUME ES:_TEXT,SS:_TEXT ;* Requires MASM 2.0 or later *;
;* Remember to EXE2BIN *;
ORG 100H ;* *;
START: JMP MAIN ;********************************;


; DATA AREA
; ---------
SYNTAX_MSG DB CR,SPACE,SPACE,SPACE,CR,LF

COPYRIGHT DB "COMPARE 1.0 (C) 1988 Ziff Communications Co.",CR,LF
PROGRAMMER DB "PC Magazine ",BOX," Michael J. Mefford",CR,LF,CR,LF

DB "Syntax: COMPARE filespec filespec[/B][/W]",CR,LF
DB "/B = Binary /W = WordStar$",CTRL_Z

TAB EQU 9
CR EQU 13
LF EQU 10
CTRL_Z EQU 26
SPACE EQU 32
BOX EQU 254

CRT_MODE EQU 49H
CRT_COLS EQU 4AH
ADDR_6845 EQU 63H
TEN_K EQU 10 * 1024
_64K_PARA EQU 64 * (1024 / 16)
SPEC_LENGTH EQU 80

LINE_CHAR EQU 0CDH
LF_BAR_CHAR EQU 0B5H
RT_BAR_CHAR EQU 0C6H
BLINKING EQU 10000000B

STATUS_REG DW ?
VIDEO_SEG DW 0B000H
COLS DW ?
ROWS DB 24
NORMAL_ATTRIB DB 07H
INVERSE_ATTRIB DB 70H
STRIP_MASK DB 0FFH
SYNTAX_FLAG DB 0
DISPLAY_FLAG DB 1
MISMATCH_FLAG DB 0
WINDOW_FULL DB 0

FILE_START DW FILE1_BUFFER2, FILE2_BUFFER2 ;CONSTANT
FILE_POS DW 2 DUP (?)
FILE_END DW 2 DUP (?)
BUFFER_END DW FILE1_BUFFER2 + TEN_K, FILE2_BUFFER2 + TEN_K
WINDOW_POS DW 2 DUP (?)
WINDOW_COL DW 2 DUP (?)
WINDOW_LINE DW 2 DUP (?)
WINDOW_SIZE DB ?

SAVE_COL DW 80,80
SAVE_POS DW FILE1_BUFFER2, FILE2_BUFFER2
SYNC_POS DW 2 DUP (?)
LINE_ARRAY DW 2 DUP (4 DUP (?))
LINE_CAPACITY DW ?
STATUS_LINE DW ?
HEX_SEGMENT DW 2 DUP (0)
HEX_OFFSET DW 2 DUP (0)

FILENAME DW FILESPEC + 2, FILESPEC + SPEC_LENGTH + 2
FILE_HANDLE DW 2 DUP (?)

NOT_ENOUGH DB "Requires 64K free RAM$"
BAD_MODE_MSG DB "Unsupported video mode$"
BINARY_NAME DB "COMEXE"
PROMPT DW PROMPT1, PROMPT2
PROMPT1 DB CR,LF,LF,"Enter first file name",CR,LF,"$"
PROMPT2 DB CR,LF,LF,"Enter second file name",CR,LF,"$"
NOT_FOUND_MSG DB CR,LF,"File not found$"

MENU DB "Press any key for next compare Esc to Exit",0
MENU_LENGTH EQU $ - MENU - 1
WORKING_MSG DB "Working",0
WORKING_LENGTH EQU $ - WORKING_MSG - 1
DIFF_MSG DB "Files significantly different",0
DIFF_LENGTH EQU $ - DIFF_MSG - 1
SAME_MSG DB "Files are effectively identical",0
SAME_LENGTH EQU $ - SAME_MSG - 1
DONE_MSG DB "Compare completed",0
DONE_LENGTH EQU $ - DONE_MSG - 1


; CODE AREA
; ---------
MAIN PROC NEAR

CLD ;String instructions forward.

MOV BX,_64K_PARA ;Make sure we have 64K to
MOV AH,4AH ; to work in.
INT 21H
MOV DX,OFFSET NOT_ENOUGH ;Exit if cramped, else continue.
JNC CK_SWITCH
JMP ERROR_EXIT

;--------------------------------------------------------;
; Check for /B Binary and /W WordStar switch characters. ;
;--------------------------------------------------------;

CK_SWITCH: MOV SI,81H ;Point to command line.
NEXT_SWITCH: LODSB ;Get a byte.
CMP AL,CR ;Is it carriage return?
JZ PARSE ;If yes, done here.
CMP AL,"/" ;Is it switch delimiter?
JNZ NEXT_SWITCH ;If no, next byte.
MOV BYTE PTR [SI-1],0 ;Else, ASCIIZ it out.
LODSB ;Get the switch character.
CMP AL,CR ;Make sure it's not CR
JZ PARSE ; so we don't go past end.
MOV BYTE PTR [SI-1],0 ;ASCIIZ switch character also.
AND AL,5FH ;Capitalize.
CMP AL,"W"
JNZ CK_BINARY ;If it's not "W", then skip.
MOV STRIP_MASK,7FH ;Else, we will strip high bit.

CK_BINARY: CMP AL,"B" ;Is it "B"?
JNZ NEXT_SWITCH ;If no, next byte.
MOV LINE_CAPACITY,16 ;Else, do hex compare.
JMP SHORT NEXT_SWITCH ;Next byte.

;---------------------------------------------------------------;
; Parse the command line for filespecs. If one or both missing ;
; or file can't be opened, prompt the user for filespec. ;
;---------------------------------------------------------------;

PARSE: MOV SI,81H ;Point to command line again.
XOR BP,BP ;Initialize filespec counter.
NEXT_PARSE: LODSB ;Get a byte.
CMP AL,SPACE ;Parse off leading delimiters
JA LEADING_END
CMP AL,CR ; as long as it's not ending
JNZ NEXT_PARSE ; carriage return.
LEADING_END: DEC SI ;Adjust pointer.
MOV DI,SI ;Save as filespec start position.
MOV DX,SI

FIND_END: LODSB ;Get a byte.
CMP AL,SPACE ;Are we at end of filespec?
JA FIND_END
MOV BYTE PTR [SI-1],0 ;If yes, make ASCIIZ.
PUSH SI ;Save our place.
PUSH AX ;And save the character.
CALL OPEN_FILE ;Try to open the file.
JNC RESTORE_PARSE ;If successful, get next filespec
CALL PROMPT_USER ; else, prompt user for filespec.

RESTORE_PARSE: POP AX ;Restore last parsed character
POP SI ; and position.
STORE_START: MOV FILENAME[BP],DI ;Store pointer to filename.
ADD BP,2 ;Next filespec.
CMP BP,2
JA CAP
CMP AL,CR ;Was last character parsed CR?
JNZ NEXT_PARSE ;If no, get next.
CALL PROMPT_USER ;Else prompt user for filespec
JMP SHORT STORE_START ; and store pointer to it.

;----------------------------------------------------------------------------;
; Capitalize filenames so parameter parsing can be done with one compare. ;
;----------------------------------------------------------------------------;

CAP: MOV BP,2 ;Two filenames to capitalize.
CAPITALIZE: MOV SI,FILENAME[BP] ;Point to filename.
NEXT_CAP: LODSB ;Get a byte.
CMP AL,0 ;Is it ASCIIZ?
JZ LOOP_CAP ;If yes, next name.
CMP AL,"a" ;Is it a lower case a - z?
JB NEXT_CAP
CMP AL,"z"
JA NEXT_CAP
AND BYTE PTR [SI-1],5FH ;If yes, capitalize.
JMP SHORT NEXT_CAP

LOOP_CAP: SUB BP,2 ;Capitalize both names.
JNC CAPITALIZE

;-----------------------------------------------------------------------------;
; Automatically do a binary compare if filespec has a .COM or .EXE extension. ;
;-----------------------------------------------------------------------------;

MOV BP,2
BINARY: MOV SI,FILENAME[BP] ;Point to filename.
NEXT_BINARY: LODSB ;Get a byte.
CMP AL,0 ;Is it ASCIIZ?
JZ LOOP_BINARY ;If yes, done here.
CMP AL,"." ;Is it delimiting dot char?
JNZ NEXT_BINARY ;If no, next byte.
MOV BX,2 ;Two possible binary names --
MOV DI,OFFSET BINARY_NAME ; .COM and .EXE.

NEXT_EXEC: PUSH SI ;Save our place.
PUSH DI
MOV CX,3 ;Do we have a match?
REP CMPSB
POP DI ;Restore our place.
POP SI
JZ BINARY_FILE ;If match, mark a binary compare.
ADD DI,3 ;Else point to next extension
DEC BX
JNZ NEXT_EXEC ; and check if it matches.
JMP SHORT NEXT_BINARY

LOOP_BINARY: SUB BP,2 ;Do both filenames.
JNC BINARY
JMP SHORT READY

BINARY_FILE: MOV LINE_CAPACITY,16 ;Else, do hex compare.

;---------------------------------------------------------------------;
; Line array is used to keep track of the start of file line displays ;
; so we can display three lines preceeding mismatch for context. ;
;---------------------------------------------------------------------;

READY: MOV AX,FILE_START[0] ;Initalize current line-starts
MOV LINE_ARRAY[6],AX ; of array to start of file.
MOV AX,FILE_START[2]
MOV LINE_ARRAY[14],AX

CALL VIDEO_SETUP ;Prepare for video environment.
CALL DISPLAY_SETUP ;Initialize display with heading
; and window delimiting lines.

;---------------------------------------------------------------------------;
; We will loop here filling the window with mismatches until all displayed. ;
;---------------------------------------------------------------------------;

DO_SEARCH: CALL SEARCH ;Search for mismatches.
JNC GET_KEY ;If not carry, not done yet.
CALL CLS_MENU ;If done, clear menu.
MOV DI,STATUS_LINE
ADD DI,COLS
MOV BH,NORMAL_ATTRIB
CMP MISMATCH_FLAG,1 ;Display appropriate message
JZ DO_DONE_MSG
MOV SI,OFFSET SAME_MSG
SUB DI,SAME_LENGTH AND NOT 1
JMP SHORT MESSAGE_EXIT
DO_DONE_MSG: MOV SI,OFFSET DONE_MSG
SUB DI,DONE_LENGTH AND NOT 1
JMP SHORT MESSAGE_EXIT

GET_KEY: CALL DISPLAY_MENU ;If not done, display menu.
CALL CLEAR_KEY ;Clear any awaiting keystroke.
CALL READ_KEY ;Get a keystroke.
CMP AH,1 ;Is it Esc?
JNZ DO_SEARCH ;If no, search for next mismatch.
CALL CLS_MENU ;Else, clear menu
JMP SHORT GOOD_EXIT ; and exit.

;--------------------------------------------------------------;
; Exit with appropriate message and put cursor back on screen. ;
;--------------------------------------------------------------;

ERROR_EXIT: CALL PRINT_STRING ;Print error message.
MOV AL,1 ;Exit with error level one.
JMP SHORT EXIT

MESSAGE_EXIT: CALL WRITE_STRING ;Display exit message.
GOOD_EXIT: MOV DH,ROWS ;Move cursor to next to last
DEC DH ; line of screen.
XOR DL,DL
XOR BH,BH
MOV AH,2
INT 10H
XOR AL,AL ;Error level zero.
EXIT: MOV AH,4CH ;Terminate.
INT 21H

MAIN ENDP

; ***************
; * SUBROUTINES *
; ***************

;-------------------------------------;
; INPUT ;
; None ;
; ;
; OUTPUT ;
; Carry flag = 1 if search complete ;
; Carry flag = 0 if more to search. ;
; ;
; All registers destroyed. ;
;-------------------------------------;

SEARCH PROC NEAR

;--------------------------------------------------------;
; Display "Working" message and initialization variables ;
;--------------------------------------------------------;

CALL CLS_MENU ;Clear the menu.
MOV SI,OFFSET WORKING_MSG ;Display "Working"
MOV DI,STATUS_LINE
ADD DI,80 - WORKING_LENGTH AND NOT 1
MOV BH,NORMAL_ATTRIB
OR BH,BLINKING ; with blinking attribute.
CALL WRITE_STRING

MOV AX,COLS
SHL AX,1 ;Double for attribute.
MOV DI,AX
SHL DI,1 ;Second line.
MOV WINDOW_POS[0],DI ;Initialize window pointers
MOV DL,WINDOW_SIZE
INC DL
MUL DL ; to top left corner of
ADD DI,AX ; each window.
MOV WINDOW_POS[2],DI

MOV BP,2 ;Restore the window columns,
RESTORE_WIN: MOV AX,SAVE_COL[BP] ; window lines and file
MOV WINDOW_COL[BP],AX ; positions to what they were
MOV AX,SAVE_POS[BP] ; when one of the windows
MOV FILE_POS[BP],AX ; was filled last search.
MOV AL,WINDOW_SIZE
XOR AH,AH
MOV WINDOW_LINE[BP],AX
SUB BP,2
JNC RESTORE_WIN

MOV DI,OFFSET LINE_ARRAY ;Initialize the array of
MOV AX,LINE_ARRAY[6] ; line starts to the last
MOV CX,4 ; line start from previous
REP STOSW ; search.
MOV AX,LINE_ARRAY[14]
MOV CX,4
REP STOSW

MOV WINDOW_FULL,0 ;Flag that windows are not full.
MOV DISPLAY_FLAG,0 ;No display during search.

;------------------------------------------------------------------------------;
; Check to see if End of File has been reached and/or windows have been filled ;
;------------------------------------------------------------------------------;

NEXT_SEARCH: MOV SI,FILE_POS[0] ;Get current file position.
MOV DI,FILE_POS[2]
CALL STORE_WINDOW ;See if current postion to be
CALL CK_EOF1 ; frozen for next search.
JNC CK_WINDOW2 ;If both End of Files reached
CALL CK_EOF2 ; then compare complete.
JNC CK_WIN_FULL
STC
RET

CK_WINDOW2: CALL CK_EOF2 ;If neither EOF then continue.
JNC BOTH_WINDOWS
CK_WIN_FULL: TEST WINDOW_FULL,111B ;If one EOF and windows full
JZ COMPARE ; then return, else continue.
JMP SHORT SEARCH_END

BOTH_WINDOWS: CMP WINDOW_FULL,111B ;If both windows full, return.
JZ SEARCH_END

;------------------------------------------------------------------------;
; Files are compared a byte at a time until mismatch is found. ;
; In comparing text files, if one byte is a carriage return and it is ;
; mismatch with a space then both are ignored (line wrap); else just ;
; the CR is ignored (word wrap). No special treatment for binary files. ;
;------------------------------------------------------------------------;

COMPARE: MOV BH,NORMAL_ATTRIB ;Use normal attribute.
CALL CK_EOF1 ;If either EOF then match
JC DO_MISMATCH ; not possible.
CALL CK_EOF2
JC DO_MISMATCH
MOV AL,[SI] ;Get a byte from each file.
MOV AH,[DI]
CMPSB ;Are they the same?
JZ FORMAT_BOTH ;If yes, format display.
CMP LINE_CAPACITY,16 ;If no, is it a binary compare?
JZ DO_MISMATCH ;If yes, mismatch.
CMP AL,LF ;Else, is it linefeed?
JZ ADJUST_2A ;If yes, ignore.
CMP AL,CR ;If CR, check if matched
JZ CK_ADJUST_2A ; with a space character.
CMP AH,LF ;Do the same for both files.
JZ FORMAT_1A
CMP AH,CR
JNZ DO_MISMATCH

CK_ADJUST_1A: CMP AL,SPACE ;If CR matched with space,
JZ FORMAT_BOTH ; format both characters.
ADJUST_1A: JMP SHORT FORMAT_1A ;Else, just format the CR.

CK_ADJUST_2A: CMP AH,SPACE ;Do the same for other file.
JZ FORMAT_BOTH
ADJUST_2A: XOR BP,BP ;File one index.
JMP SHORT FORMAT_2A

FORMAT_BOTH: XOR BP,BP
CALL FORMAT
FORMAT_1A: MOV BP,2 ;File two index.
MOV AL,AH ;Call format with character
FORMAT_2A: CALL FORMAT ; in AL.
JMP NEXT_SEARCH ;Check next bytes.

DO_MISMATCH: MOV MISMATCH_FLAG,1 ;Flag that mismatch found.
CALL MISMATCH ;Go highlight the mismatch.
JMP NEXT_SEARCH ;Check next bytes.

SEARCH_END: CLC
RET

SEARCH ENDP

;------------------------------------;
; INPUT ;
; AL = Character to format. ;
; BP = Index to file. ;
; ;
; OUTPUT ;
; WINDOW_COL[BP] WINDOW_LINE[BP] ;
; WINDOW_FULL FILE_POS[BP] ;
; are updated. ;
; ;
; AX, BX, CX, SI and DI preserved. ;
;------------------------------------;

FORMAT PROC NEAR

PUSH AX ;Preserve registers.
PUSH CX

PUSH SI
PUSH DI
MOV CX,WINDOW_COL[BP] ;Retrieve current column.
CMP LINE_CAPACITY,16 ;Are we working on a binary file?
JZ DO_HEX ;If yes, do a hex display.
CALL LINES ;Else, display text lines.
JMP SHORT CK_LINE_START

DO_HEX: CALL HEX

CK_LINE_START: INC FILE_POS[BP] ;Increment file position.
JCXZ LINE_START ;End of a display line?
MOV WINDOW_COL[BP],CX ;If no, store column postion
JMP SHORT FORMAT_END ; and return.

LINE_START: MOV AX,LINE_CAPACITY ;Else, go to next column.
MOV WINDOW_COL[BP],AX
CMP AX,16 ;Are we doing a binary compare?
JZ CK_ARRAY ;If yes, skip CR check.
OR BP,BP ;Else, check if EOF.
JNZ CK_CR2
CALL CK_EOF1
JMP SHORT CK_CR
CK_CR2: CALL CK_EOF2
CK_CR: JC CK_ARRAY ;If EOF, skip CR check.
MOV DI,FILE_POS[BP] ;Else, if byte at end of an 80
CMP BYTE PTR [DI],CR ; column display is CR, bump
JNZ CK_ARRAY ; file postion past it so
INC FILE_POS[BP] ; don't double space display.

CK_ARRAY: CMP DISPLAY_FLAG,1 ;Are we displaying?
JNZ ADJUST_ARRAY ;If no, skip line check.
CMP WINDOW_LINE[BP],0 ;Else, is window full?
JZ FORMAT_END ;If yes, done here.
DEC WINDOW_LINE[BP] ;Else, decrement line display.
JNZ ADJUST_ARRAY ;If window not full, skip next.
MOV CX,BP ;Else, mark appropriate
ADD CL,2 ; WINDOW_FULL bit as full.
OR WINDOW_FULL,CL
JMP SHORT FORMAT_END

ADJUST_ARRAY: TEST WINDOW_FULL,001B ;Has window been stored?
JNZ FORMAT_END ;If yes, skip array indexing.
CALL ARRAY_INDEX ;Else, index into array.
MOV SI,DI
ADD SI,2
MOV CX,3
REP MOVSW ;Move all line starts up one
MOV AX,FILE_POS[BP]
MOV [DI],AX ; and store new line start.
FORMAT_END: POP DI ;Restore registers.
POP SI
POP CX
POP AX
RET

FORMAT ENDP

;------------------------------;
; INPUT ;
; AL = Character to display. ;
; CX = current column. ;
; BP = Index to file. ;
; ;
; OUTPUT ;
; CX = new column. ;
; WINDOW_POS[BP] is updated. ;
; ;
; BX preserved. ;
;------------------------------;

LINES PROC NEAR

CMP AL,CR ;Carriage return?
JZ PAD_SPACES ;If yes, pad balance of line.
CMP AL,TAB ;Is it tab character?
JZ EXPAND_TAB ;If yes, expand to spaces.
CMP AL,LF ;Is it linefeed?
JZ LINES_END ;If yes, skip.

PUSH CX ;Save column.
MOV CX,1 ;Display one character.
CALL CK_DISPLAY
POP CX ;Restore column and decrement.
DEC CX
RET


EXPAND_TAB: PUSH CX ;Save column.
DEC CX ;Adjust column counter.
AND CX,7 ;Get bottom three bits.
INC CX ;Adjust.
PUSH CX ;Save.
CALL PAD_SPACES ;Move to next tab position.
POP AX
POP CX
SUB CX,AX ;Adjust column counter.
RET

PAD_SPACES: MOV AL,SPACE ;If CR display spaces.
CK_DISPLAY: CMP DISPLAY_FLAG,1 ;Are we to write it to screen?
JNZ DISPLAY_END ;If no, return.
CMP WINDOW_LINE[BP],0 ;Window full?
JZ DISPLAY_END ;If yes, return.
MOV DI,WINDOW_POS[BP] ;Else, retrieve display position.
WRITE_VIEW: CALL WRITE_SCREEN ;Write character CX times.
LOOP WRITE_VIEW
MOV WINDOW_POS[BP],DI ;Store new display position.

DISPLAY_END: XOR CX,CX
LINES_END: RET

LINES ENDP

;------------------------------;
; INPUT ;
; AL = Character to display. ;
; CX = current column. ;
; BP = Index to file. ;
; ;
; OUTPUT ;
; CX = new column. ;
; WINDOW_POS[BP] is updated. ;
; ;
; BX preserved. ;
;------------------------------;

HEX PROC NEAR

CMP DISPLAY_FLAG,1 ;Are we to write to screen?
JNZ DEC_COLUMN ;If no, just update column.
CMP WINDOW_LINE[BP],0 ;Else, is window full?
JNZ GO_HEX ;If yes, continue.
DEC_COLUMN: DEC CX ;Else, update column and return.
RET

GO_HEX: PUSH CX ;Save some registers.
PUSH AX
PUSH CX

MOV DI,WINDOW_POS[BP] ;Retrieve window position.
CMP CX,16 ;Is it first column?
JNZ DISP_NUMBERS ;If no, just display the byte.
PUSH AX ;Else, save character and
PUSH BX ; attribute.

MOV AX,FILE_POS[BP] ;Retrieve file position
SUB AX,FILE_START[BP] ;Subtract the starting position.
ADD AX,HEX_OFFSET[BP] ;Add the 64K offset.
PUSH AX ;Save the offset.
MOV AX,HEX_SEGMENT[BP] ;Retrieve segment.
JNC DISP_SEGMENT ;Did offset carry?
ADD AX,1000H ;If yes, add 1000h to segment.

DISP_SEGMENT: MOV BH,NORMAL_ATTRIB ;Yes normal attribute.
MOV CX,1 ;Segment and offset counter.
NEXT_ADDRESS: XCHG AH,AL ;AL = byte to display.
CALL DISPLAY_HEX
XCHG AH,AL ;Get second byte to display.
CALL DISPLAY_HEX
OR CX,CX ;Segment and offset displayed?
JZ ADDRESS_END ;If yes, done here.
MOV AL,":" ;Else, display delimiting colon.
CALL WRITE_SCREEN
POP AX ;Retrieve offset.
DEC CX ;Decrement counter.
JMP SHORT NEXT_ADDRESS ;Display segment.

ADDRESS_END: ADD DI,4 ;Move right two spaces.
POP BX ;Retrieve attribute.
POP AX ;Retrieve character.

DISP_NUMBERS: CALL DISPLAY_HEX ;Display the hex number.
MOV AL,SPACE ;Delimit with a space.
POP CX ;Retrieve column counter.
CMP CX,9 ;Is it half way?
JNZ DELIMITER
MOV AL,"-" ;If yes, display delimiting dash
DELIMITER: PUSH BX ; with normal attribute.
MOV BH,NORMAL_ATTRIB
CALL WRITE_SCREEN
POP BX ;Restore attribute.
MOV WINDOW_POS[BP],DI ;Store window position.
SHL CX,1 ;Multiply column by 8.
SHL CX,1
ADD DI,CX ;Add to current window postion.
ADD DI,30 ;Tab over additional 15.
POP AX ;Retrieve character and display.
CALL WRITE_SCREEN
POP CX ;Restore column counter
DEC CX ; and update.
JNZ HEX_END ;If last column, bump window
MOV AX,COLS ; to next line.
SUB AX,59
SHL AX,1
ADD WINDOW_POS[BP],AX
HEX_END: RET

HEX ENDP

;-------------------------------;
; INPUT ;
; AL = Hex number to display. ;
; ;
; OUTPUT ;
; none ;
; ;
; BX and CX preserved. ;
;-------------------------------;

DISPLAY_HEX PROC NEAR

PUSH CX ;Preserve CX.
MOV CX,0204H ;2 char/byte; 4 bits/byte.
ROTATE_HEX: ROL AL,CL ;Get next four bits.
PUSH AX ;Save number.
AND AL,00001111B ;Mask off high bits.
ADD AL,30H ;Convert to ASCII.
CMP AL,"9" ;Is it above 9?
JLE PRINT_HEX
ADD AL,7 ;If yes, convert to hex alpha.
PRINT_HEX: CALL WRITE_SCREEN
POP AX ;Restore number.
DEC CH ;Do both hex characters.
JNZ ROTATE_HEX
POP CX
RET

DISPLAY_HEX ENDP

;----------------------------;
; INPUT ;
; BH = Display attribute. ;
; ;
; OUTPUT ;
; None ;
; ;
; All registers destroyed. ;
;----------------------------;

MISMATCH PROC NEAR

CMP DISPLAY_FLAG,1 ;Is this first time here?
JZ SYNCHRONIZE ;If yes no context to display.
MOV DISPLAY_FLAG,1 ;Else, flag to display.
CALL CLS_WINDOWS ;Clear the windows.

MOV BP,2 ;Two windows.
NEXT_WINDOW: MOV AX,LINE_CAPACITY ;Restore line to first column.
MOV WINDOW_COL[BP],AX
MOV CX,FILE_POS[BP] ;Save current position.
CALL ARRAY_INDEX
MOV AX,[DI] ;Retrieve third line back.
MOV FILE_POS[BP],AX
NEXT_CONTEXT: MOV SI,FILE_POS[BP] ;Display context until current
CMP SI,CX ; mismatch position.
JAE LOOP_WINDOW
LODSB
CALL FORMAT
JMP SHORT NEXT_CONTEXT

LOOP_WINDOW: SUB BP,2 ;Do both windows.
JNC NEXT_WINDOW

;-----------------------------------------------------------------------------;
; Try to synchronize by matching 10 bytes up to 200 postions away from ;
; current position. In text files, ignore spaces so won't get false matches. ;
;-----------------------------------------------------------------------------;

SYNCHRONIZE: MOV BX,FILE_POS[0] ;File 1 postion in BX.
MOV CX,400 ;400 byte reach.

NEXT_SYNC: PUSH CX ;Save counter.
MOV DX,FILE_POS[2] ;Second file position in DX.
MOV CX,400 ;Also a 400 byte reach.

NEXT_TRY: PUSH CX ;Save counter.
MOV CX,10 ;Require 10 bytes for match.

MOV SI,BX ;Initialize pointers.
MOV DI,DX
NEXT_CHAR: CALL CK_EOF1 ;EOF of file one?
JC NEXT_TRY_END ;If yes, increment file 2.
CALL CK_EOF2 ;EOF of file two?
JC FILE2_END ;If yes, increment file 1.
MOV AL,[SI] ;Retrieve characters.
MOV AH,[DI]
CMPSB ;Do they match?
JZ POSSIBLE_SYNC ;If yes, poss. synchronization.
CMP LINE_CAPACITY,16 ;Else, is it binary compare?
JZ NEXT_TRY_END ;If yes, no match.
CMP AL,LF ;Else, if linefeed, ignore.
JZ ADJUST_2B
CMP AL,CR ;If carriage return, ignore
JZ CK_ADJUST_2B ; and ignore any match space.
CMP AH,LF ;Do same check for both files.
JZ ADJUST_1B
CMP AH,CR
JNZ NEXT_TRY_END


CK_ADJUST_1B: CMP AL,SPACE ;If CR with space, ignore both.
JZ NEXT_CHAR
ADJUST_1B: DEC SI ;Else, ignore CR only.
JMP SHORT NEXT_CHAR

CK_ADJUST_2B: CMP AH,SPACE ;Do same for both files.
JZ NEXT_CHAR
ADJUST_2B: DEC DI
JMP SHORT NEXT_CHAR

POSSIBLE_SYNC: CMP AL,SPACE ;If sync char is space, ignore.
JZ NEXT_CHAR
LOOP NEXT_CHAR ;Need ten matches for sync.
JMP SHORT EVALUATE

FILE2_END: POP CX ;If file 2 EOF, skip to
JMP SHORT NEXT_SYNC_END ; next file 1 postion.

NEXT_TRY_END: INC DX ;Increment file 2 postion.
POP CX ;Decrement reach counter.
LOOP NEXT_TRY ;Keep trying up to 400 positions.

NEXT_SYNC_END: INC BX ;Increment file 1 position.
POP CX ;Decrement reach counter.
LOOP NEXT_SYNC ;Keep trying up to 400 positions.

PUSH SI ;Save pointers.
PUSH DI
MOV SI,OFFSET DIFF_MSG
MOV DI,STATUS_LINE
MOV AX,COLS
SHL AX,1
SUB AX,DIFF_LENGTH * 2
ADD DI,AX
PUSH BX
PUSH DX ;Display "Files significantly
MOV BH,NORMAL_ATTRIB ; different" if didn't find
CALL WRITE_STRING ; a match.
POP DX
POP BX
POP DI
POP SI
JMP SHORT EVALUATE_EOF

;--------------------------------------------;
; Display mismatch until EOF or window full. ;
;--------------------------------------------;

EVALUATE: POP CX ;Fix stack.
POP CX
EVALUATE_EOF: CALL CK_EOF1 ;EOF of file 1?
JNC EOF2
MOV BX,FILE_END[0] ;If yes, use file end pointer.
EOF2: CALL CK_EOF2 ;EOF of file 2?
JNC INVERSE
MOV DX,FILE_END[2] ;If yes, use file end pointer.
INVERSE: MOV SYNC_POS[0],BX ;Save synchronization position.
MOV SYNC_POS[2],DX
MOV BH,INVERSE_ATTRIB ;Display in inverse attribute.

MOV BP,2 ;Two windows to display.
HIGHLIGHT: MOV SI,FILE_POS[BP] ;Is current postion = sync pos?
CMP SI,SYNC_POS[BP]
JZ LOOP_INVERSE ;If yes, done.
CMP WINDOW_LINE[BP],0 ;Is window full?
JZ LOOP_INVERSE ;If yes, done.
LODSB ;Else, display mismatch.
CALL FORMAT
JMP SHORT HIGHLIGHT

LOOP_INVERSE: SUB BP,2 ;Do both windows.
JNC HIGHLIGHT
EVALUATE_END: CALL STORE_WINDOW ;Freeze postion if window full.
MOV BP,2 ;Restore current position.
RESTORE_POS: MOV AX,SYNC_POS[BP]
MOV FILE_POS[BP],AX
SUB BP,2
JNC RESTORE_POS
RET

MISMATCH ENDP

;----------------------------;
; INPUT ;
; None ;
; ;
; OUTPUT ;
; None ;
; ;
; AX and BP destroyed. ;
;----------------------------;

STORE_WINDOW PROC NEAR

;------------------------------------------------------------------------;
; As soon as a window becomes full, freeze the position for next search. ;
;------------------------------------------------------------------------;

TEST WINDOW_FULL,110B ;Either window full?
JZ STORE_END ;If no, done.
TEST WINDOW_FULL,001B ;Else, have we already stored?
JNZ STORE_END ;If yes, done.
OR WINDOW_FULL,001B ;Else, flag stored.
MOV BP,2
FREEZE_POS: MOV AX,FILE_POS[BP] ;And store current position
MOV SAVE_POS[BP],AX
MOV AX,WINDOW_COL[BP] ; and column.
MOV SAVE_COL[BP],AX
SUB BP,2
JNC FREEZE_POS
STORE_END: RET

STORE_WINDOW ENDP

;---------------------------------;
; INPUT ;
; SI and DI = current position. ;
; Two entry points: ;
; CK_EOF1 and CK_EOF2 ;
; ;
; OUTPUT ;
; Carry flag = 1 if EOF. ;
; Carry flag = 0 if not EOF. ;
; ;
; BP destroyed. ;
;---------------------------------;

CK_EOF PROC NEAR

;-----------------------------------------------------------------;
; These two subroutines read the next 10K bytes if end of buffer ;
; reached and EOF not reached. The second buffer is moved to the ;
; first buffer and all appropriate pointers are adjusted. ;
;-----------------------------------------------------------------;

CK_EOF1: XOR BP,BP ;File index pointer.
CMP SI,FILE_END[BP] ;Is position = EOF?
JB NOT_EOF ;If no, not EOF.
CMP SI,BUFFER_END[BP] ;Else, is it at end of buffer?
JB EOF ;If no, EOF.
JMP SHORT MOVE_BUFFER ;Else, more to file; read it.

CK_EOF2: MOV BP,2 ;Do same for file 2.
CMP DI,FILE_END[BP]
JB NOT_EOF
CMP DI,BUFFER_END[BP]
JB EOF

MOVE_BUFFER: PUSH AX ;Save registers.
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
MOV SI,FILE_START[BP] ;Retrieve buffer start address.
MOV DI,SI
SUB DI,TEN_K
MOV CX,TEN_K / 2 ;And move data to buffer
REP MOVSW ; 10K below.

CALL READ_FILE ;Read the next 10K.
SUB SAVE_POS[BP],TEN_K ;Adjust all the pointers by 10K.
SUB SYNC_POS[BP],TEN_K
SUB FILE_POS[BP],TEN_K
ADD HEX_OFFSET[BP],TEN_K ;Add 10K to offset.
JNC DO_ARRAY ;If carry, add 1000h to segment.
ADD HEX_SEGMENT[BP],1000H
DO_ARRAY: CALL ARRAY_INDEX ;Adjust line start array.
MOV CX,4
SUB_ARRAY: SUB [DI],TEN_K
INC DI
INC DI
LOOP SUB_ARRAY
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
OR BP,BP ;If file 1, adjust DI and DX.
JZ ADJUST_FILE1
SUB DI,TEN_K
SUB DX,TEN_K
JMP SHORT NOT_EOF
ADJUST_FILE1: SUB SI,TEN_K ;Else, adjust SI and BX.
SUB BX,TEN_K

NOT_EOF: CLC
RET

EOF: STC
RET

CK_EOF ENDP

;-----------------------------------;
; INPUT ;
; BP = file index. ;
; ;
; OUTPUT ;
; DI = start of line-start array. ;
; ;
; All registers preserved. ;
;-----------------------------------;

ARRAY_INDEX PROC NEAR

MOV DI,BP
SHL DI,1 ;Multiply file index by 4
SHL DI,1 ; to index into index.
ADD DI,OFFSET LINE_ARRAY
RET

ARRAY_INDEX ENDP

;-----------------------------------;
; INPUT ;
; BP = file index ;
; DX = points to ASCIIZ filename. ;
; ;
; OUTPUT ;
; Carry flag = 1 if failed. ;
; Carry flag = 0 if successful. ;
; ;
; BP preserved. ;
;-----------------------------------;

OPEN_FILE PROC NEAR

MOV AX,3D00H ;Open file for reading.
INT 21H
JC OPEN_END
MOV FILE_HANDLE[BP],AX ;Save file handle if successful
CALL READ_FILE ; and read 10K.
OPEN_END: RET

OPEN_FILE ENDP

;-----------------------------------;
; INPUT ;
; BP = file index ;
; DX = points to ASCIIZ filename. ;
; ;
; OUTPUT ;
; Carry flag = 1 if failed. ;
; Carry flag = 0 if successful. ;
; ;
; BP preserved. ;
;-----------------------------------;

READ_FILE PROC NEAR

MOV BX,FILE_HANDLE[BP] ;Retrieve filehandle.
MOV DX,FILE_START[BP] ;Point to storage buffer.
MOV SI,DX ;Save it.
MOV CX,TEN_K ;Read maximum of 10K.
MOV AH,3FH ;Read file.
INT 21H
JC READ_FILE_END ;If failed, exit.
ADD DX,AX ;Else, add bytes read to buffer
MOV FILE_END[BP],DX ; address and store as file end.

MOV CX,AX ;Bytes read in counter.
JCXZ READ_END ;Skip if zero bytes read.
MOV AL,STRIP_MASK ;Else, retrieve strip mask.
CMP AL,0FFH ;If not WordStar, skip.
JZ READ_END
WORDSTAR: AND DS:[SI],AL ;Else, strip the high bit
INC SI ; of all bytes read.
LOOP WORDSTAR

READ_END: CLC
READ_FILE_END: RET

READ_FILE ENDP

;-----------------------------------;
; INPUT ;
; None ;
; ;
; OUTPUT ;
; None ;
; ;
; ES preserved. ;
;-----------------------------------;

VIDEO_SETUP PROC NEAR

PUSH ES ;Preserve 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 AL,ES:CRT_MODE ;Retrieve current video mode.
CMP AL,7 ;Is it mono mode?
JZ SUPPORTED ;If yes, continue.
CMP AL,3 ;Is it text?
JBE SUPPORTED ;If yes, continue.
UNSUPPORTED: MOV DX,OFFSET BAD_MODE_MSG ;Else, do not pass GO.
JMP ERROR_EXIT ;Go directly to jail.

SUPPORTED: MOV AH,12H
MOV BL,10H
INT 10H
CMP BL,10H ;Is there an EGA?
JZ CK_CGA ;If no, check if CGA.

TEST ES:BYTE PTR [87H],8 ;Else, EGA_info; Is it active?
JNZ CK_CGA ;If no, check CGA.
XOR BH,BH ;Else, retrieve CRT rows.
MOV AX,1130H
PUSH ES
INT 10H
POP ES
MOV ROWS,DL ;Save CRT rows.

CK_CGA: MOV AL,ES:CRT_COLS ;Retrieve CRT cols.
XOR AH,AH ;Zero in high half.
MOV DX,AX ;Save it.
MOV BX,LINE_CAPACITY ;Retrieve line capacity.
CMP BX,16 ;Are we displaying hex?
JNZ CAPACITY ;If no, store.
MOV DX,BX ;Else, use 16 as capacity.
CMP AL,40 ;Are we in 40 column mode?
JZ UNSUPPORTED ;If yes, useless display.

CAPACITY: MOV LINE_CAPACITY,DX ;Store capacity and initialize
MOV SAVE_COL[0],DX ; columns.
MOV SAVE_COL[2],DX
MOV COLS,AX ;Store columns.
SHL AX,1 ;Times two for attribute.
MOV DL,ROWS ;Retrieve rows
MUL DL ; and multiply.
MOV STATUS_LINE,AX ;Save as address of status line.
SUB DL,4 ;Subtract 4 from CRT rows
SHR DL,1 ; and divide by two
MOV WINDOW_SIZE,DL ; and save as window size.

MOV AX,ES:ADDR_6845 ;Retrieve display card.
ADD AX,6 ;Add six to get status register
MOV STATUS_REG,AX ;Store as status register.
CMP AX,3BAH ;Is it monochrome?
JZ VIDEO_END ;If yes, done here.
ADD VIDEO_SEG,800H ;Else, adjust video segment.
MOV AH,8 ;Get attribute at cursor postion.
INT 10H
MOV NORMAL_ATTRIB,AH ;And save as forground.
XOR AH,01110111B ;Flip color bits.
MOV INVERSE_ATTRIB,AH ;And save as highlight attribute.

VIDEO_END: POP ES ;Restore ES.
RET

VIDEO_SETUP ENDP

;-----------------------------------;
; INPUT ;
; AL = character to write. ;
; BH = attribute. ;
; ;
; OUTPUT ;
; None ;
; ;
; AL, BH, CX preserved. ;
;-----------------------------------;

WRITE_SCREEN PROC NEAR

PUSH ES
MOV DX,VIDEO_SEG ;Point to screen segment.
MOV ES,DX
MOV DX,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

WRITE_SCREEN ENDP

;-----------------------------------;
; INPUT ;
; None ;
; ;
; OUTPUT ;
; None ;
; ;
; All registers destroyed. ;
;-----------------------------------;

DISPLAY_SETUP PROC NEAR

CALL CLS ;Clear screen.
MOV DH,ROWS ;Retrieve CRT rows.
INC DH ;Move one line below off screen.
XOR DL,DL ;Column zero.
XOR BH,BH ;Page zero.
MOV AH,2 ;Set cursor position.
INT 10H

XOR DI,DI ;Point to top left of display.
MOV SI,OFFSET COPYRIGHT ;Point to copyright message.
MOV BH,NORMAL_ATTRIB ;And display it.
CALL WRITE_STRING
MOV DI,COLS ;Retrieve columns.
SHL DI,1 ;Double for attribute.
SUB DI,64 ;Right justify my name.
INC SI ;Bump pointer past linefeed.
CALL WRITE_STRING

MOV BP,0 ;Initialize counter.
NEXT_LINE: MOV CX,COLS ;Write line characters
PUSH DI ;Save position.
MOV AL,LINE_CHAR ; to screen.
NEXT_WRITE: CALL WRITE_SCREEN
LOOP NEXT_WRITE

POP DI ;Retrieve position.
CMP BP,2 ;Do two windows.
JA SETUP_END
PUSH DI ;Save screen pointer.
ADD DI,10 ;Tab in five spaces
MOV AL,LF_BAR_CHAR ; and print a left bar char.
CALL WRITE_SCREEN
MOV SI,FILENAME[BP] ;Point to filename and print it.
CALL WRITE_STRING
MOV AL,RT_BAR_CHAR ;Finish frame with right bar char
CALL WRITE_SCREEN
POP DI ;Restore display pointer.
MOV AX,COLS
SHL AX,1
MOV DL,WINDOW_SIZE
INC DL ;Multiply line length by window
MUL DL ; size plus one and add to
ADD DI,AX ; current position to get to
ADD BP,2 ; next position to display line.
JMP SHORT NEXT_LINE
SETUP_END: RET

DISPLAY_SETUP ENDP

;-----------------------------------;
; INPUT ;
; None ;
; ;
; OUTPUT ;
; None ;
; ;
; All registers destroyed. ;
;-----------------------------------;

DISPLAY_MENU PROC NEAR

MOV SI,OFFSET MENU ;Point to menu.
MOV DI,STATUS_LINE ;Point to status line.
MOV BH,NORMAL_ATTRIB ;Use normal attribute
CALL WRITE_STRING ; and display menu.
RET

DISPLAY_MENU ENDP

;-----------------------------------;
; INPUT ;
; BP = file index ;
; ;
; OUTPUT ;
; None ;
; ;
; All registers destroyed. ;
;-----------------------------------;

PROMPT_USER PROC NEAR

CMP SYNTAX_FLAG,1 ;If first time through, display
JZ NEXT_PROMPT ; syntax message.
MOV SYNTAX_FLAG,1
MOV DX,OFFSET SYNTAX_MSG
CALL PRINT_STRING

NEXT_PROMPT: MOV DX,PROMPT[BP] ;Retrieve appropriate prompt
CALL PRINT_STRING ; and display.
MOV DI,FILENAME[BP] ;Retrieve address of filename
MOV DX,DI ; storage and save.
SUB DX,2 ;Put in first byte of filename
MOV BX,DX ; buffer, the buffer length (80).
MOV BYTE PTR [BX],SPEC_LENGTH
MOV AH,0AH
INT 21H ;Buffered Keyboard Input.
PUSH DI ;Save filename pointer.
MOV AL,CR ;Carriage return points
ASCIIZ: SCASB ; to last byte of input.
JNZ ASCIIZ
MOV BYTE PTR [DI-1],0 ;Replace it with zero (ASCIIZ).
POP DI ;Retrieve filename pointer.
MOV DX,DI
CALL OPEN_FILE ;Attempt to open the file.
JNC PROMPT_END ;If successful, done here.
MOV DX,OFFSET NOT_FOUND_MSG ;Else, display "Not found"
CALL PRINT_STRING ; message
JMP SHORT NEXT_PROMPT ; and prompt user again.
PROMPT_END: RET

PROMPT_USER ENDP

;-------------------------------------;
; INPUT ;
; SI = points to string to display. ;
; Entry point is WRITE_STRING. ;
; ;
; OUTPUT ;
; None ;
; ;
; All registers destroyed. ;
;-------------------------------------;

WRITE_IT: CALL WRITE_SCREEN ;Write a character.
WRITE_STRING: LODSB ;Retrieve a character.
CMP AL,SPACE ;Keep writing until a carriage
JAE WRITE_IT ; return or space encountered.
RET

;-------------------;
; BIOS Keyboard I/O ;
;-------------------;

READ_KEY: MOV AH,0 ;Wait for next keyboard
INT 16H ; input.
RET

CK_KEY: MOV AH,1 ;See if character ready.
INT 16H
RET

CLEAR_IT: CALL READ_KEY ;If characters are ready
CLEAR_KEY: CALL CK_KEY ; read them to clear keyboard
JNZ CLEAR_IT ; buffer.
RET

;---------------------------;
; Screen clearing routines. ;
;---------------------------;

CLS: XOR CX,CX ;Top left corner.
MOV DX,COLS ;Right corner
DEC DL
MOV DH,ROWS ;Bottom row.
JMP SHORT CLEAR_WINDOW ;Clear the screen.

CLS_WINDOWS: MOV CX,0200H ;Row 3; column zero.
MOV DX,COLS ;Right corner.
DEC DL
MOV DH,WINDOW_SIZE ;Bottom of window one.
INC DH
CALL CLEAR_WINDOW ;Clear the window.
ADD CH,WINDOW_SIZE ;Increment a window size
INC CH ; and a delimiting line.
ADD DH,WINDOW_SIZE
INC DH
JMP SHORT CLEAR_WINDOW ;Clear second window.

CLS_MENU: MOV CH,ROWS ;Bottom row.
XOR CL,CL ;Column zero.
MOV DH,CH
MOV DL,2CH ;To column 44.

CLEAR_WINDOW: PUSH BX ;Preserve BX.
MOV BH,NORMAL_ATTRIB ;Normal attribute.
MOV AX,600H ;Scroll active page.
INT 10H
POP BX ;Restore BX.
RET

;-------------------;
; DOS print string. ;
;-------------------;

PRINT_STRING PROC NEAR

MOV AH,9
INT 21H
RET

PRINT_STRING ENDP

;----------------------------------------------------------;
; Buffered keyboard input and file buffers at end of code. ;
;----------------------------------------------------------;

EVEN

FILESPEC EQU $
FILE1_BUFFER1 EQU FILESPEC + 2 * (SPEC_LENGTH + 2)
FILE1_BUFFER2 EQU FILE1_BUFFER1 + TEN_K
FILE2_BUFFER1 EQU FILE1_BUFFER2 + TEN_K
FILE2_BUFFER2 EQU FILE2_BUFFER1 + TEN_K

_TEXT ENDS
END START


  3 Responses to “Category : Files from Magazines
Archive   : N7V11.ZIP
Filename : COMPARE.ASM

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

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

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