home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast.iso / pcmag / vol7n11.zip / COMPARE.ASM next >
Assembly Source File  |  1988-06-14  |  61KB  |  1,350 lines

  1.                TITLE   COMPARE.ASM
  2.                PAGE    60,132
  3.  
  4. ;-------------------------------------------;
  5. ;  COMPARE - Compares text and binary files ;
  6. ;  PC Magazine - Michael J. Mefford         ;
  7. ;-------------------------------------------;
  8.  
  9. _TEXT          SEGMENT PUBLIC 'CODE'          ;********************************;
  10.                ASSUME  CS:_TEXT,DS:_TEXT      ;*                              *;
  11.                ASSUME  ES:_TEXT,SS:_TEXT      ;*  Requires MASM 2.0 or later  *;
  12.                                               ;*     Remember to EXE2BIN      *;
  13.                ORG     100H                   ;*                              *;
  14. START:         JMP     MAIN                   ;********************************;
  15.  
  16.  
  17. ;              DATA AREA
  18. ;              ---------
  19. SYNTAX_MSG     DB      CR,SPACE,SPACE,SPACE,CR,LF
  20.  
  21. COPYRIGHT      DB      "COMPARE 1.0 (C) 1988 Ziff Communications Co.",CR,LF
  22. PROGRAMMER     DB      "PC Magazine ",BOX," Michael J. Mefford",CR,LF,CR,LF
  23.  
  24.                DB      "Syntax:  COMPARE filespec filespec[/B][/W]",CR,LF
  25.                DB      "/B = Binary   /W = WordStar$",CTRL_Z
  26.  
  27. TAB            EQU     9
  28. CR             EQU     13
  29. LF             EQU     10
  30. CTRL_Z         EQU     26
  31. SPACE          EQU     32
  32. BOX            EQU     254
  33.  
  34. CRT_MODE       EQU     49H
  35. CRT_COLS       EQU     4AH
  36. ADDR_6845      EQU     63H
  37. TEN_K          EQU     10 * 1024
  38. _64K_PARA      EQU     64 * (1024 / 16)
  39. SPEC_LENGTH    EQU     80
  40.  
  41. LINE_CHAR      EQU     0CDH
  42. LF_BAR_CHAR    EQU     0B5H
  43. RT_BAR_CHAR    EQU     0C6H
  44. BLINKING       EQU     10000000B
  45.  
  46. STATUS_REG     DW      ?
  47. VIDEO_SEG      DW      0B000H
  48. COLS           DW      ?
  49. ROWS           DB      24
  50. NORMAL_ATTRIB  DB      07H
  51. INVERSE_ATTRIB DB      70H
  52. STRIP_MASK     DB      0FFH
  53. SYNTAX_FLAG    DB      0
  54. DISPLAY_FLAG   DB      1
  55. MISMATCH_FLAG  DB      0
  56. WINDOW_FULL    DB      0
  57.  
  58. FILE_START     DW      FILE1_BUFFER2, FILE2_BUFFER2  ;CONSTANT
  59. FILE_POS       DW      2 DUP (?)
  60. FILE_END       DW      2 DUP (?)
  61. BUFFER_END     DW      FILE1_BUFFER2 + TEN_K, FILE2_BUFFER2 + TEN_K
  62. WINDOW_POS     DW      2 DUP (?)
  63. WINDOW_COL     DW      2 DUP (?)
  64. WINDOW_LINE    DW      2 DUP (?)
  65. WINDOW_SIZE    DB      ?
  66.  
  67. SAVE_COL       DW      80,80
  68. SAVE_POS       DW      FILE1_BUFFER2, FILE2_BUFFER2
  69. SYNC_POS       DW      2 DUP (?)
  70. LINE_ARRAY     DW      2 DUP (4 DUP (?))
  71. LINE_CAPACITY  DW      ?
  72. STATUS_LINE    DW      ?
  73. HEX_SEGMENT    DW      2 DUP (0)
  74. HEX_OFFSET     DW      2 DUP (0)
  75.  
  76. FILENAME       DW      FILESPEC + 2, FILESPEC + SPEC_LENGTH + 2
  77. FILE_HANDLE    DW      2 DUP (?)
  78.  
  79. NOT_ENOUGH     DB      "Requires 64K free RAM$"
  80. BAD_MODE_MSG   DB      "Unsupported video mode$"
  81. BINARY_NAME    DB      "COMEXE"
  82. PROMPT         DW      PROMPT1, PROMPT2
  83. PROMPT1        DB      CR,LF,LF,"Enter first file name",CR,LF,"$"
  84. PROMPT2        DB      CR,LF,LF,"Enter second file name",CR,LF,"$"
  85. NOT_FOUND_MSG  DB      CR,LF,"File not found$"
  86.  
  87. MENU           DB      "Press any key for next compare   Esc to Exit",0
  88. MENU_LENGTH    EQU     $ - MENU - 1
  89. WORKING_MSG    DB      "Working",0
  90. WORKING_LENGTH EQU     $ - WORKING_MSG - 1
  91. DIFF_MSG       DB      "Files significantly different",0
  92. DIFF_LENGTH    EQU     $ - DIFF_MSG - 1
  93. SAME_MSG       DB      "Files are effectively identical",0
  94. SAME_LENGTH    EQU     $ - SAME_MSG - 1
  95. DONE_MSG       DB      "Compare completed",0
  96. DONE_LENGTH    EQU     $ - DONE_MSG - 1
  97.  
  98.  
  99. ;              CODE AREA
  100. ;              ---------
  101. MAIN           PROC    NEAR
  102.  
  103.                CLD                             ;String instructions forward.
  104.  
  105.                MOV     BX,_64K_PARA            ;Make sure we have 64K to
  106.                MOV     AH,4AH                  ; to work in.
  107.                INT     21H
  108.                MOV     DX,OFFSET NOT_ENOUGH    ;Exit if cramped, else continue.
  109.                JNC     CK_SWITCH
  110.                JMP     ERROR_EXIT
  111.  
  112. ;--------------------------------------------------------;
  113. ; Check for /B Binary and /W WordStar switch characters. ;
  114. ;--------------------------------------------------------;
  115.  
  116. CK_SWITCH:     MOV     SI,81H                  ;Point to command line.
  117. NEXT_SWITCH:   LODSB                           ;Get a byte.
  118.                CMP     AL,CR                   ;Is it carriage return?
  119.                JZ      PARSE                   ;If yes, done here.
  120.                CMP     AL,"/"                  ;Is it switch delimiter?
  121.                JNZ     NEXT_SWITCH             ;If no, next byte.
  122.                MOV     BYTE PTR [SI-1],0       ;Else, ASCIIZ it out.
  123.                LODSB                           ;Get the switch character.
  124.                CMP     AL,CR                   ;Make sure it's not CR
  125.                JZ      PARSE                   ; so we don't go past end.
  126.                MOV     BYTE PTR [SI-1],0       ;ASCIIZ switch character also.
  127.                AND     AL,5FH                  ;Capitalize.
  128.                CMP     AL,"W"
  129.                JNZ     CK_BINARY               ;If it's not "W", then skip.
  130.                MOV     STRIP_MASK,7FH          ;Else, we will strip high bit.
  131.  
  132. CK_BINARY:     CMP     AL,"B"                  ;Is it "B"?
  133.                JNZ     NEXT_SWITCH             ;If no, next byte.
  134.                MOV     LINE_CAPACITY,16        ;Else, do hex compare.
  135.                JMP     SHORT NEXT_SWITCH       ;Next byte.
  136.  
  137. ;---------------------------------------------------------------;
  138. ; Parse the command line for filespecs.  If one or both missing ;
  139. ; or file can't be opened, prompt the user for filespec.        ;
  140. ;---------------------------------------------------------------;
  141.  
  142. PARSE:         MOV     SI,81H                  ;Point to command line again.
  143.                XOR     BP,BP                   ;Initialize filespec counter.
  144. NEXT_PARSE:    LODSB                           ;Get a byte.
  145.                CMP     AL,SPACE                ;Parse off leading delimiters
  146.                JA      LEADING_END
  147.                CMP     AL,CR                   ; as long as it's not ending
  148.                JNZ     NEXT_PARSE              ; carriage return.
  149. LEADING_END:   DEC     SI                      ;Adjust pointer.
  150.                MOV     DI,SI                   ;Save as filespec start position.
  151.                MOV     DX,SI
  152.  
  153. FIND_END:      LODSB                           ;Get a byte.
  154.                CMP     AL,SPACE                ;Are we at end of filespec?
  155.                JA      FIND_END
  156.                MOV     BYTE PTR [SI-1],0       ;If yes, make ASCIIZ.
  157.                PUSH    SI                      ;Save our place.
  158.                PUSH    AX                      ;And save the character.
  159.                CALL    OPEN_FILE               ;Try to open the file.
  160.                JNC     RESTORE_PARSE           ;If successful, get next filespec
  161.                CALL    PROMPT_USER             ; else, prompt user for filespec.
  162.  
  163. RESTORE_PARSE: POP     AX                      ;Restore last parsed character
  164.                POP     SI                      ; and position.
  165. STORE_START:   MOV     FILENAME[BP],DI         ;Store pointer to filename.
  166.                ADD     BP,2                    ;Next filespec.
  167.                CMP     BP,2
  168.                JA      CAP
  169.                CMP     AL,CR                   ;Was last character parsed CR?
  170.                JNZ     NEXT_PARSE              ;If no, get next.
  171.                CALL    PROMPT_USER             ;Else prompt user for filespec
  172.                JMP     SHORT STORE_START       ; and store pointer to it.
  173.  
  174. ;----------------------------------------------------------------------------;
  175. ; Capitalize filenames so parameter parsing can be done with one compare.    ;
  176. ;----------------------------------------------------------------------------;
  177.  
  178. CAP:           MOV     BP,2                    ;Two filenames to capitalize.
  179. CAPITALIZE:    MOV     SI,FILENAME[BP]         ;Point to filename.
  180. NEXT_CAP:      LODSB                           ;Get a byte.
  181.                CMP     AL,0                    ;Is it ASCIIZ?
  182.                JZ      LOOP_CAP                ;If yes, next name.
  183.                CMP     AL,"a"                  ;Is it a lower case a - z?
  184.                JB      NEXT_CAP
  185.                CMP     AL,"z"
  186.                JA      NEXT_CAP
  187.                AND     BYTE PTR [SI-1],5FH     ;If yes, capitalize.
  188.                JMP     SHORT NEXT_CAP
  189.  
  190. LOOP_CAP:      SUB     BP,2                    ;Capitalize both names.
  191.                JNC     CAPITALIZE
  192.  
  193. ;-----------------------------------------------------------------------------;
  194. ; Automatically do a binary compare if filespec has a .COM or .EXE extension. ;
  195. ;-----------------------------------------------------------------------------;
  196.  
  197.                MOV     BP,2
  198. BINARY:        MOV     SI,FILENAME[BP]         ;Point to filename.
  199. NEXT_BINARY:   LODSB                           ;Get a byte.
  200.                CMP     AL,0                    ;Is it ASCIIZ?
  201.                JZ      LOOP_BINARY             ;If yes, done here.
  202.                CMP     AL,"."                  ;Is it delimiting dot char?
  203.                JNZ     NEXT_BINARY             ;If no, next byte.
  204.                MOV     BX,2                    ;Two possible binary names --
  205.                MOV     DI,OFFSET BINARY_NAME   ; .COM and .EXE.
  206.  
  207. NEXT_EXEC:     PUSH    SI                      ;Save our place.
  208.                PUSH    DI
  209.                MOV     CX,3                    ;Do we have a match?
  210.                REP     CMPSB
  211.                POP     DI                      ;Restore our place.
  212.                POP     SI
  213.                JZ      BINARY_FILE             ;If match, mark a binary compare.
  214.                ADD     DI,3                    ;Else point to next extension
  215.                DEC     BX
  216.                JNZ     NEXT_EXEC               ; and check if it matches.
  217.                JMP     SHORT NEXT_BINARY
  218.  
  219. LOOP_BINARY:   SUB     BP,2                    ;Do both filenames.
  220.                JNC     BINARY
  221.                JMP     SHORT READY
  222.  
  223. BINARY_FILE:   MOV     LINE_CAPACITY,16        ;Else, do hex compare.
  224.  
  225. ;---------------------------------------------------------------------;
  226. ; Line array is used to keep track of the start of file line displays ;
  227. ; so we can display three lines preceeding mismatch for context.      ;
  228. ;---------------------------------------------------------------------;
  229.  
  230. READY:         MOV     AX,FILE_START[0]        ;Initalize current line-starts
  231.                MOV     LINE_ARRAY[6],AX        ; of array to start of file.
  232.                MOV     AX,FILE_START[2]
  233.                MOV     LINE_ARRAY[14],AX
  234.  
  235.                CALL    VIDEO_SETUP             ;Prepare for video environment.
  236.                CALL    DISPLAY_SETUP           ;Initialize display with heading
  237.                                                ; and window delimiting lines.
  238.  
  239. ;---------------------------------------------------------------------------;
  240. ; We will loop here filling the window with mismatches until all displayed. ;
  241. ;---------------------------------------------------------------------------;
  242.  
  243. DO_SEARCH:     CALL    SEARCH                  ;Search for mismatches.
  244.                JNC     GET_KEY                 ;If not carry, not done yet.
  245.                CALL    CLS_MENU                ;If done, clear menu.
  246.                MOV     DI,STATUS_LINE
  247.                ADD     DI,COLS
  248.                MOV     BH,NORMAL_ATTRIB
  249.                CMP     MISMATCH_FLAG,1         ;Display appropriate message
  250.                JZ      DO_DONE_MSG
  251.                MOV     SI,OFFSET SAME_MSG
  252.                SUB     DI,SAME_LENGTH AND NOT 1
  253.                JMP     SHORT MESSAGE_EXIT
  254. DO_DONE_MSG:   MOV     SI,OFFSET DONE_MSG
  255.                SUB     DI,DONE_LENGTH AND NOT 1
  256.                JMP     SHORT MESSAGE_EXIT
  257.  
  258. GET_KEY:       CALL    DISPLAY_MENU            ;If not done, display menu.
  259.                CALL    CLEAR_KEY               ;Clear any awaiting keystroke.
  260.                CALL    READ_KEY                ;Get a keystroke.
  261.                CMP     AH,1                    ;Is it Esc?
  262.                JNZ     DO_SEARCH               ;If no, search for next mismatch.
  263.                CALL    CLS_MENU                ;Else, clear menu
  264.                JMP     SHORT GOOD_EXIT         ; and exit.
  265.  
  266. ;--------------------------------------------------------------;
  267. ; Exit with appropriate message and put cursor back on screen. ;
  268. ;--------------------------------------------------------------;
  269.  
  270. ERROR_EXIT:    CALL    PRINT_STRING            ;Print error message.
  271.                MOV     AL,1                    ;Exit with error level one.
  272.                JMP     SHORT EXIT
  273.  
  274. MESSAGE_EXIT:  CALL    WRITE_STRING            ;Display exit message.
  275. GOOD_EXIT:     MOV     DH,ROWS                 ;Move cursor to next to last
  276.                DEC     DH                      ; line of screen.
  277.                XOR     DL,DL
  278.                XOR     BH,BH
  279.                MOV     AH,2
  280.                INT     10H
  281.                XOR     AL,AL                   ;Error level zero.
  282. EXIT:          MOV     AH,4CH                  ;Terminate.
  283.                INT     21H
  284.  
  285. MAIN           ENDP
  286.  
  287. ;              ***************
  288. ;              * SUBROUTINES *
  289. ;              ***************
  290.  
  291. ;-------------------------------------;
  292. ; INPUT                               ;
  293. ;   None                              ;
  294. ;                                     ;
  295. ; OUTPUT                              ;
  296. ;   Carry flag = 1 if search complete ;
  297. ;   Carry flag = 0 if more to search. ;
  298. ;                                     ;
  299. ;   All registers destroyed.          ;
  300. ;-------------------------------------;
  301.  
  302. SEARCH         PROC    NEAR
  303.  
  304. ;--------------------------------------------------------;
  305. ; Display "Working" message and initialization variables ;
  306. ;--------------------------------------------------------;
  307.  
  308.                CALL    CLS_MENU                ;Clear the menu.
  309.                MOV     SI,OFFSET WORKING_MSG   ;Display "Working"
  310.                MOV     DI,STATUS_LINE
  311.                ADD     DI,80 - WORKING_LENGTH AND NOT 1
  312.                MOV     BH,NORMAL_ATTRIB
  313.                OR      BH,BLINKING             ; with blinking attribute.
  314.                CALL    WRITE_STRING
  315.  
  316.                MOV     AX,COLS
  317.                SHL     AX,1                    ;Double for attribute.
  318.                MOV     DI,AX
  319.                SHL     DI,1                    ;Second line.
  320.                MOV     WINDOW_POS[0],DI        ;Initialize window pointers
  321.                MOV     DL,WINDOW_SIZE
  322.                INC     DL
  323.                MUL     DL                      ; to top left corner of
  324.                ADD     DI,AX                   ; each window.
  325.                MOV     WINDOW_POS[2],DI
  326.  
  327.                MOV     BP,2                    ;Restore the window columns,
  328. RESTORE_WIN:   MOV     AX,SAVE_COL[BP]         ; window lines and file
  329.                MOV     WINDOW_COL[BP],AX       ; positions to what they were
  330.                MOV     AX,SAVE_POS[BP]         ; when one of the windows
  331.                MOV     FILE_POS[BP],AX         ; was filled last search.
  332.                MOV     AL,WINDOW_SIZE
  333.                XOR     AH,AH
  334.                MOV     WINDOW_LINE[BP],AX
  335.                SUB     BP,2
  336.                JNC     RESTORE_WIN
  337.  
  338.                MOV     DI,OFFSET LINE_ARRAY    ;Initialize the array of
  339.                MOV     AX,LINE_ARRAY[6]        ; line starts to the last
  340.                MOV     CX,4                    ; line start from previous
  341.                REP     STOSW                   ; search.
  342.                MOV     AX,LINE_ARRAY[14]
  343.                MOV     CX,4
  344.                REP     STOSW
  345.  
  346.                MOV     WINDOW_FULL,0           ;Flag that windows are not full.
  347.                MOV     DISPLAY_FLAG,0          ;No display during search.
  348.  
  349. ;------------------------------------------------------------------------------;
  350. ; Check to see if End of File has been reached and/or windows have been filled ;
  351. ;------------------------------------------------------------------------------;
  352.  
  353. NEXT_SEARCH:   MOV     SI,FILE_POS[0]          ;Get current file position.
  354.                MOV     DI,FILE_POS[2]
  355.                CALL    STORE_WINDOW            ;See if current postion to be
  356.                CALL    CK_EOF1                 ; frozen for next search.
  357.                JNC     CK_WINDOW2              ;If both End of Files reached
  358.                CALL    CK_EOF2                 ; then compare complete.
  359.                JNC     CK_WIN_FULL
  360.                STC
  361.                RET
  362.  
  363. CK_WINDOW2:    CALL    CK_EOF2                 ;If neither EOF then continue.
  364.                JNC     BOTH_WINDOWS
  365. CK_WIN_FULL:   TEST    WINDOW_FULL,111B        ;If one EOF and windows full
  366.                JZ      COMPARE                 ; then return, else continue.
  367.                JMP     SHORT SEARCH_END
  368.  
  369. BOTH_WINDOWS:  CMP     WINDOW_FULL,111B        ;If both windows full, return.
  370.                JZ      SEARCH_END
  371.  
  372. ;------------------------------------------------------------------------;
  373. ; Files are compared a byte at a time until mismatch is found.           ;
  374. ; In comparing text files, if one byte is a carriage return and it is    ;
  375. ; mismatch with a space then both are ignored (line wrap); else just     ;
  376. ; the CR is ignored (word wrap).  No special treatment for binary files. ;
  377. ;------------------------------------------------------------------------;
  378.  
  379. COMPARE:       MOV     BH,NORMAL_ATTRIB        ;Use normal attribute.
  380.                CALL    CK_EOF1                 ;If either EOF then match
  381.                JC      DO_MISMATCH             ; not possible.
  382.                CALL    CK_EOF2
  383.                JC      DO_MISMATCH
  384.                MOV     AL,[SI]                 ;Get a byte from each file.
  385.                MOV     AH,[DI]
  386.                CMPSB                           ;Are they the same?
  387.                JZ      FORMAT_BOTH             ;If yes, format display.
  388.                CMP     LINE_CAPACITY,16        ;If no, is it a binary compare?
  389.                JZ      DO_MISMATCH             ;If yes, mismatch.
  390.                CMP     AL,LF                   ;Else, is it linefeed?
  391.                JZ      ADJUST_2A               ;If yes, ignore.
  392.                CMP     AL,CR                   ;If CR, check if matched
  393.                JZ      CK_ADJUST_2A            ; with a space character.
  394.                CMP     AH,LF                   ;Do the same for both files.
  395.                JZ      FORMAT_1A
  396.                CMP     AH,CR
  397.                JNZ     DO_MISMATCH
  398.  
  399. CK_ADJUST_1A:  CMP     AL,SPACE                ;If CR matched with space,
  400.                JZ      FORMAT_BOTH             ; format both characters.
  401. ADJUST_1A:     JMP     SHORT FORMAT_1A         ;Else, just format the CR.
  402.  
  403. CK_ADJUST_2A:  CMP     AH,SPACE                ;Do the same for other file.
  404.                JZ      FORMAT_BOTH
  405. ADJUST_2A:     XOR     BP,BP                   ;File one index.
  406.                JMP     SHORT FORMAT_2A
  407.  
  408. FORMAT_BOTH:   XOR     BP,BP
  409.                CALL    FORMAT
  410. FORMAT_1A:     MOV     BP,2                    ;File two index.
  411.                MOV     AL,AH                   ;Call format with character
  412. FORMAT_2A:     CALL    FORMAT                  ; in AL.
  413.                JMP     NEXT_SEARCH             ;Check next bytes.
  414.  
  415. DO_MISMATCH:   MOV     MISMATCH_FLAG,1         ;Flag that mismatch found.
  416.                CALL    MISMATCH                ;Go highlight the mismatch.
  417.                JMP     NEXT_SEARCH             ;Check next bytes.
  418.  
  419. SEARCH_END:    CLC
  420.                RET
  421.  
  422. SEARCH         ENDP
  423.  
  424. ;------------------------------------;
  425. ; INPUT                              ;
  426. ;   AL = Character to format.        ;
  427. ;   BP = Index to file.              ;
  428. ;                                    ;
  429. ; OUTPUT                             ;
  430. ;   WINDOW_COL[BP]  WINDOW_LINE[BP]  ;
  431. ;   WINDOW_FULL     FILE_POS[BP]     ;
  432. ;           are updated.             ;
  433. ;                                    ;
  434. ;   AX, BX, CX, SI and DI preserved. ;
  435. ;------------------------------------;
  436.  
  437. FORMAT         PROC    NEAR
  438.  
  439.                PUSH    AX                      ;Preserve registers.
  440.                PUSH    CX
  441.                PUSH    SI
  442.                PUSH    DI
  443.                MOV     CX,WINDOW_COL[BP]       ;Retrieve current column.
  444.                CMP     LINE_CAPACITY,16        ;Are we working on a binary file?
  445.                JZ      DO_HEX                  ;If yes, do a hex display.
  446.                CALL    LINES                   ;Else, display text lines.
  447.                JMP     SHORT CK_LINE_START
  448.  
  449. DO_HEX:        CALL    HEX
  450.  
  451. CK_LINE_START: INC     FILE_POS[BP]            ;Increment file position.
  452.                JCXZ    LINE_START              ;End of a display line?
  453.                MOV     WINDOW_COL[BP],CX       ;If no, store column postion
  454.                JMP     SHORT FORMAT_END        ; and return.
  455.  
  456. LINE_START:    MOV     AX,LINE_CAPACITY        ;Else, go to next column.
  457.                MOV     WINDOW_COL[BP],AX
  458.                CMP     AX,16                   ;Are we doing a binary compare?
  459.                JZ      CK_ARRAY                ;If yes, skip CR check.
  460.                OR      BP,BP                   ;Else, check if EOF.
  461.                JNZ     CK_CR2
  462.                CALL    CK_EOF1
  463.                JMP     SHORT CK_CR
  464. CK_CR2:        CALL    CK_EOF2
  465. CK_CR:         JC      CK_ARRAY                ;If EOF, skip CR check.
  466.                MOV     DI,FILE_POS[BP]         ;Else, if byte at end of an 80
  467.                CMP     BYTE PTR [DI],CR        ; column display is CR, bump
  468.                JNZ     CK_ARRAY                ; file postion past it so
  469.                INC     FILE_POS[BP]            ; don't double space display.
  470.  
  471. CK_ARRAY:      CMP     DISPLAY_FLAG,1          ;Are we displaying?
  472.                JNZ     ADJUST_ARRAY            ;If no, skip line check.
  473.                CMP     WINDOW_LINE[BP],0       ;Else, is window full?
  474.                JZ      FORMAT_END              ;If yes, done here.
  475.                DEC     WINDOW_LINE[BP]         ;Else, decrement line display.
  476.                JNZ     ADJUST_ARRAY            ;If window not full, skip next.
  477.                MOV     CX,BP                   ;Else, mark appropriate
  478.                ADD     CL,2                    ; WINDOW_FULL bit as full.
  479.                OR      WINDOW_FULL,CL
  480.                JMP     SHORT FORMAT_END
  481.  
  482. ADJUST_ARRAY:  TEST    WINDOW_FULL,001B        ;Has window been stored?
  483.                JNZ     FORMAT_END              ;If yes, skip array indexing.
  484.                CALL    ARRAY_INDEX             ;Else, index into array.
  485.                MOV     SI,DI
  486.                ADD     SI,2
  487.                MOV     CX,3
  488.                REP     MOVSW                   ;Move all line starts up one
  489.                MOV     AX,FILE_POS[BP]
  490.                MOV     [DI],AX                 ; and store new line start.
  491. FORMAT_END:    POP     DI                      ;Restore registers.
  492.                POP     SI
  493.                POP     CX
  494.                POP     AX
  495.                RET
  496.  
  497. FORMAT         ENDP
  498.  
  499. ;------------------------------;
  500. ; INPUT                        ;
  501. ;   AL = Character to display. ;
  502. ;   CX = current column.       ;
  503. ;   BP = Index to file.        ;
  504. ;                              ;
  505. ; OUTPUT                       ;
  506. ;   CX = new column.           ;
  507. ;   WINDOW_POS[BP] is updated. ;
  508. ;                              ;
  509. ;   BX preserved.              ;
  510. ;------------------------------;
  511.  
  512. LINES          PROC    NEAR
  513.  
  514.                CMP     AL,CR                   ;Carriage return?
  515.                JZ      PAD_SPACES              ;If yes, pad balance of line.
  516.                CMP     AL,TAB                  ;Is it tab character?
  517.                JZ      EXPAND_TAB              ;If yes, expand to spaces.
  518.                CMP     AL,LF                   ;Is it linefeed?
  519.                JZ      LINES_END               ;If yes, skip.
  520.  
  521.                PUSH    CX                      ;Save column.
  522.                MOV     CX,1                    ;Display one character.
  523.                CALL    CK_DISPLAY
  524.                POP     CX                      ;Restore column and decrement.
  525.                DEC     CX
  526.                RET
  527.  
  528. EXPAND_TAB:    PUSH    CX                      ;Save column.
  529.                DEC     CX                      ;Adjust column counter.
  530.                AND     CX,7                    ;Get bottom three bits.
  531.                INC     CX                      ;Adjust.
  532.                PUSH    CX                      ;Save.
  533.                CALL    PAD_SPACES              ;Move to next tab position.
  534.                POP     AX
  535.                POP     CX
  536.                SUB     CX,AX                   ;Adjust column counter.
  537.                RET
  538.  
  539. PAD_SPACES:    MOV     AL,SPACE                ;If CR display spaces.
  540. CK_DISPLAY:    CMP     DISPLAY_FLAG,1          ;Are we to write it to screen?
  541.                JNZ     DISPLAY_END             ;If no, return.
  542.                CMP     WINDOW_LINE[BP],0       ;Window full?
  543.                JZ      DISPLAY_END             ;If yes, return.
  544.                MOV     DI,WINDOW_POS[BP]       ;Else, retrieve display position.
  545. WRITE_VIEW:    CALL    WRITE_SCREEN            ;Write character CX times.
  546.                LOOP    WRITE_VIEW
  547.                MOV     WINDOW_POS[BP],DI       ;Store new display position.
  548.  
  549. DISPLAY_END:   XOR     CX,CX
  550. LINES_END:     RET
  551.  
  552. LINES          ENDP
  553.  
  554. ;------------------------------;
  555. ; INPUT                        ;
  556. ;   AL = Character to display. ;
  557. ;   CX = current column.       ;
  558. ;   BP = Index to file.        ;
  559. ;                              ;
  560. ; OUTPUT                       ;
  561. ;   CX = new column.           ;
  562. ;   WINDOW_POS[BP] is updated. ;
  563. ;                              ;
  564. ;   BX preserved.              ;
  565. ;------------------------------;
  566.  
  567. HEX            PROC    NEAR
  568.  
  569.                CMP     DISPLAY_FLAG,1          ;Are we to write to screen?
  570.                JNZ     DEC_COLUMN              ;If no, just update column.
  571.                CMP     WINDOW_LINE[BP],0       ;Else, is window full?
  572.                JNZ     GO_HEX                  ;If yes, continue.
  573. DEC_COLUMN:    DEC     CX                      ;Else, update column and return.
  574.                RET
  575.  
  576. GO_HEX:        PUSH    CX                      ;Save some registers.
  577.                PUSH    AX
  578.                PUSH    CX
  579.  
  580.                MOV     DI,WINDOW_POS[BP]       ;Retrieve window position.
  581.                CMP     CX,16                   ;Is it first column?
  582.                JNZ     DISP_NUMBERS            ;If no, just display the byte.
  583.                PUSH    AX                      ;Else, save character and
  584.                PUSH    BX                      ; attribute.
  585.  
  586.                MOV     AX,FILE_POS[BP]         ;Retrieve file position
  587.                SUB     AX,FILE_START[BP]       ;Subtract the starting position.
  588.                ADD     AX,HEX_OFFSET[BP]       ;Add the 64K offset.
  589.                PUSH    AX                      ;Save the offset.
  590.                MOV     AX,HEX_SEGMENT[BP]      ;Retrieve segment.
  591.                JNC     DISP_SEGMENT            ;Did offset carry?
  592.                ADD     AX,1000H                ;If yes, add 1000h to segment.
  593.  
  594. DISP_SEGMENT:  MOV     BH,NORMAL_ATTRIB        ;Yes normal attribute.
  595.                MOV     CX,1                    ;Segment and offset counter.
  596. NEXT_ADDRESS:  XCHG    AH,AL                   ;AL = byte to display.
  597.                CALL    DISPLAY_HEX
  598.                XCHG    AH,AL                   ;Get second byte to display.
  599.                CALL    DISPLAY_HEX
  600.                OR      CX,CX                   ;Segment and offset displayed?
  601.                JZ      ADDRESS_END             ;If yes, done here.
  602.                MOV     AL,":"                  ;Else, display delimiting colon.
  603.                CALL    WRITE_SCREEN
  604.                POP     AX                      ;Retrieve offset.
  605.                DEC     CX                      ;Decrement counter.
  606.                JMP     SHORT NEXT_ADDRESS      ;Display segment.
  607.  
  608. ADDRESS_END:   ADD     DI,4                    ;Move right two spaces.
  609.                POP     BX                      ;Retrieve attribute.
  610.                POP     AX                      ;Retrieve character.
  611.  
  612. DISP_NUMBERS:  CALL    DISPLAY_HEX             ;Display the hex number.
  613.                MOV     AL,SPACE                ;Delimit with a space.
  614.                POP     CX                      ;Retrieve column counter.
  615.                CMP     CX,9                    ;Is it half way?
  616.                JNZ     DELIMITER
  617.                MOV     AL,"-"                  ;If yes, display delimiting dash
  618. DELIMITER:     PUSH    BX                      ; with normal attribute.
  619.                MOV     BH,NORMAL_ATTRIB
  620.                CALL    WRITE_SCREEN
  621.                POP     BX                      ;Restore attribute.
  622.                MOV     WINDOW_POS[BP],DI       ;Store window position.
  623.                SHL     CX,1                    ;Multiply column by 8.
  624.                SHL     CX,1
  625.                ADD     DI,CX                   ;Add to current window postion.
  626.                ADD     DI,30                   ;Tab over additional 15.
  627.                POP     AX                      ;Retrieve character and display.
  628.                CALL    WRITE_SCREEN
  629.                POP     CX                      ;Restore column counter
  630.                DEC     CX                      ; and update.
  631.                JNZ     HEX_END                 ;If last column, bump window
  632.                MOV     AX,COLS                 ; to next line.
  633.                SUB     AX,59
  634.                SHL     AX,1
  635.                ADD     WINDOW_POS[BP],AX
  636. HEX_END:       RET
  637.  
  638. HEX            ENDP
  639.  
  640. ;-------------------------------;
  641. ; INPUT                         ;
  642. ;   AL = Hex number to display. ;
  643. ;                               ;
  644. ; OUTPUT                        ;
  645. ;   none                        ;
  646. ;                               ;
  647. ;   BX and CX preserved.        ;
  648. ;-------------------------------;
  649.  
  650. DISPLAY_HEX    PROC    NEAR
  651.  
  652.                PUSH    CX                      ;Preserve CX.
  653.                MOV     CX,0204H                ;2 char/byte; 4 bits/byte.
  654. ROTATE_HEX:    ROL     AL,CL                   ;Get next four bits.
  655.                PUSH    AX                      ;Save number.
  656.                AND     AL,00001111B            ;Mask off high bits.
  657.                ADD     AL,30H                  ;Convert to ASCII.
  658.                CMP     AL,"9"                  ;Is it above 9?
  659.                JLE     PRINT_HEX
  660.                ADD     AL,7                    ;If yes, convert to hex alpha.
  661. PRINT_HEX:     CALL    WRITE_SCREEN
  662.                POP     AX                      ;Restore number.
  663.                DEC     CH                      ;Do both hex characters.
  664.                JNZ     ROTATE_HEX
  665.                POP     CX
  666.                RET
  667.  
  668. DISPLAY_HEX    ENDP
  669.  
  670. ;----------------------------;
  671. ; INPUT                      ;
  672. ;   BH = Display attribute.  ;
  673. ;                            ;
  674. ; OUTPUT                     ;
  675. ;   None                     ;
  676. ;                            ;
  677. ;   All registers destroyed. ;
  678. ;----------------------------;
  679.  
  680. MISMATCH       PROC    NEAR
  681.  
  682.                CMP     DISPLAY_FLAG,1          ;Is this first time here?
  683.                JZ      SYNCHRONIZE             ;If yes no context to display.
  684.                MOV     DISPLAY_FLAG,1          ;Else, flag to display.
  685.                CALL    CLS_WINDOWS             ;Clear the windows.
  686.  
  687.                MOV     BP,2                    ;Two windows.
  688. NEXT_WINDOW:   MOV     AX,LINE_CAPACITY        ;Restore line to first column.
  689.                MOV     WINDOW_COL[BP],AX
  690.                MOV     CX,FILE_POS[BP]         ;Save current position.
  691.                CALL    ARRAY_INDEX
  692.                MOV     AX,[DI]                 ;Retrieve third line back.
  693.                MOV     FILE_POS[BP],AX
  694. NEXT_CONTEXT:  MOV     SI,FILE_POS[BP]         ;Display context until current
  695.                CMP     SI,CX                   ; mismatch position.
  696.                JAE     LOOP_WINDOW
  697.                LODSB
  698.                CALL    FORMAT
  699.                JMP     SHORT NEXT_CONTEXT
  700.  
  701. LOOP_WINDOW:   SUB     BP,2                    ;Do both windows.
  702.                JNC     NEXT_WINDOW
  703.  
  704. ;-----------------------------------------------------------------------------;
  705. ; Try to synchronize by matching 10 bytes up to 200 postions away from        ;
  706. ; current position.  In text files, ignore spaces so won't get false matches. ;
  707. ;-----------------------------------------------------------------------------;
  708.  
  709. SYNCHRONIZE:   MOV     BX,FILE_POS[0]          ;File 1 postion in BX.
  710.                MOV     CX,400                  ;400 byte reach.
  711.  
  712. NEXT_SYNC:     PUSH    CX                      ;Save counter.
  713.                MOV     DX,FILE_POS[2]          ;Second file position in DX.
  714.                MOV     CX,400                  ;Also a 400 byte reach.
  715.  
  716. NEXT_TRY:      PUSH    CX                      ;Save counter.
  717.                MOV     CX,10                   ;Require 10 bytes for match.
  718.  
  719.                MOV     SI,BX                   ;Initialize pointers.
  720.                MOV     DI,DX
  721. NEXT_CHAR:     CALL    CK_EOF1                 ;EOF of file one?
  722.                JC      NEXT_TRY_END            ;If yes, increment file 2.
  723.                CALL    CK_EOF2                 ;EOF of file two?
  724.                JC      FILE2_END               ;If yes, increment file 1.
  725.                MOV     AL,[SI]                 ;Retrieve characters.
  726.                MOV     AH,[DI]
  727.                CMPSB                           ;Do they match?
  728.                JZ      POSSIBLE_SYNC           ;If yes, poss. synchronization.
  729.                CMP     LINE_CAPACITY,16        ;Else, is it binary compare?
  730.                JZ      NEXT_TRY_END            ;If yes, no match.
  731.                CMP     AL,LF                   ;Else, if linefeed, ignore.
  732.                JZ      ADJUST_2B
  733.                CMP     AL,CR                   ;If carriage return, ignore
  734.                JZ      CK_ADJUST_2B            ; and ignore any match space.
  735.                CMP     AH,LF                   ;Do same check for both files.
  736.                JZ      ADJUST_1B
  737.                CMP     AH,CR
  738.                JNZ     NEXT_TRY_END
  739.  
  740. CK_ADJUST_1B:  CMP     AL,SPACE                ;If CR with space, ignore both.
  741.                JZ      NEXT_CHAR
  742. ADJUST_1B:     DEC     SI                      ;Else, ignore CR only.
  743.                JMP     SHORT NEXT_CHAR
  744.  
  745. CK_ADJUST_2B:  CMP     AH,SPACE                ;Do same for both files.
  746.                JZ      NEXT_CHAR
  747. ADJUST_2B:     DEC     DI
  748.                JMP     SHORT NEXT_CHAR
  749.  
  750. POSSIBLE_SYNC: CMP     AL,SPACE                ;If sync char is space, ignore.
  751.                JZ      NEXT_CHAR
  752.                LOOP    NEXT_CHAR               ;Need ten matches for sync.
  753.                JMP     SHORT EVALUATE
  754.  
  755. FILE2_END:     POP     CX                      ;If file 2 EOF, skip to
  756.                JMP     SHORT NEXT_SYNC_END     ; next file 1 postion.
  757.  
  758. NEXT_TRY_END:  INC     DX                      ;Increment file 2 postion.
  759.                POP     CX                      ;Decrement reach counter.
  760.                LOOP    NEXT_TRY                ;Keep trying up to 400 positions.
  761.  
  762. NEXT_SYNC_END: INC     BX                      ;Increment file 1 position.
  763.                POP     CX                      ;Decrement reach counter.
  764.                LOOP    NEXT_SYNC               ;Keep trying up to 400 positions.
  765.  
  766.                PUSH    SI                      ;Save pointers.
  767.                PUSH    DI
  768.                MOV     SI,OFFSET DIFF_MSG
  769.                MOV     DI,STATUS_LINE
  770.                MOV     AX,COLS
  771.                SHL     AX,1
  772.                SUB     AX,DIFF_LENGTH * 2
  773.                ADD     DI,AX
  774.                PUSH    BX
  775.                PUSH    DX                      ;Display "Files significantly
  776.                MOV     BH,NORMAL_ATTRIB        ; different" if didn't find
  777.                CALL    WRITE_STRING            ; a match.
  778.                POP     DX
  779.                POP     BX
  780.                POP     DI
  781.                POP     SI
  782.                JMP     SHORT EVALUATE_EOF
  783.  
  784. ;--------------------------------------------;
  785. ; Display mismatch until EOF or window full. ;
  786. ;--------------------------------------------;
  787.  
  788. EVALUATE:      POP     CX                      ;Fix stack.
  789.                POP     CX
  790. EVALUATE_EOF:  CALL    CK_EOF1                 ;EOF of file 1?
  791.                JNC     EOF2
  792.                MOV     BX,FILE_END[0]          ;If yes, use file end pointer.
  793. EOF2:          CALL    CK_EOF2                 ;EOF of file 2?
  794.                JNC     INVERSE
  795.                MOV     DX,FILE_END[2]          ;If yes, use file end pointer.
  796. INVERSE:       MOV     SYNC_POS[0],BX          ;Save synchronization position.
  797.                MOV     SYNC_POS[2],DX
  798.                MOV     BH,INVERSE_ATTRIB       ;Display in inverse attribute.
  799.  
  800.                MOV     BP,2                    ;Two windows to display.
  801. HIGHLIGHT:     MOV     SI,FILE_POS[BP]         ;Is current postion = sync pos?
  802.                CMP     SI,SYNC_POS[BP]
  803.                JZ      LOOP_INVERSE            ;If yes, done.
  804.                CMP     WINDOW_LINE[BP],0       ;Is window full?
  805.                JZ      LOOP_INVERSE            ;If yes, done.
  806.                LODSB                           ;Else, display mismatch.
  807.                CALL    FORMAT
  808.                JMP     SHORT HIGHLIGHT
  809.  
  810. LOOP_INVERSE:  SUB     BP,2                    ;Do both windows.
  811.                JNC     HIGHLIGHT
  812. EVALUATE_END:  CALL    STORE_WINDOW            ;Freeze postion if window full.
  813.                MOV     BP,2                    ;Restore current position.
  814. RESTORE_POS:   MOV     AX,SYNC_POS[BP]
  815.                MOV     FILE_POS[BP],AX
  816.                SUB     BP,2
  817.                JNC     RESTORE_POS
  818.                RET
  819.  
  820. MISMATCH       ENDP
  821.  
  822. ;----------------------------;
  823. ; INPUT                      ;
  824. ;   None                     ;
  825. ;                            ;
  826. ; OUTPUT                     ;
  827. ;   None                     ;
  828. ;                            ;
  829. ;   AX and BP destroyed.     ;
  830. ;----------------------------;
  831.  
  832. STORE_WINDOW   PROC    NEAR
  833.  
  834. ;------------------------------------------------------------------------;
  835. ; As soon as a window becomes full, freeze the position for next search. ;
  836. ;------------------------------------------------------------------------;
  837.  
  838.                TEST    WINDOW_FULL,110B        ;Either window full?
  839.                JZ      STORE_END               ;If no, done.
  840.                TEST    WINDOW_FULL,001B        ;Else, have we already stored?
  841.                JNZ     STORE_END               ;If yes, done.
  842.                OR      WINDOW_FULL,001B        ;Else, flag stored.
  843.                MOV     BP,2
  844. FREEZE_POS:    MOV     AX,FILE_POS[BP]         ;And store current position
  845.                MOV     SAVE_POS[BP],AX
  846.                MOV     AX,WINDOW_COL[BP]       ; and column.
  847.                MOV     SAVE_COL[BP],AX
  848.                SUB     BP,2
  849.                JNC     FREEZE_POS
  850. STORE_END:     RET
  851.  
  852. STORE_WINDOW   ENDP
  853.  
  854. ;---------------------------------;
  855. ; INPUT                           ;
  856. ;   SI and DI = current position. ;
  857. ;   Two entry points:             ;
  858. ;      CK_EOF1 and CK_EOF2        ;
  859. ;                                 ;
  860. ; OUTPUT                          ;
  861. ;   Carry flag = 1 if EOF.        ;
  862. ;   Carry flag = 0 if not EOF.    ;
  863. ;                                 ;
  864. ;   BP destroyed.                 ;
  865. ;---------------------------------;
  866.  
  867. CK_EOF         PROC    NEAR
  868.  
  869. ;-----------------------------------------------------------------;
  870. ; These two subroutines read the next 10K bytes if end of buffer  ;
  871. ; reached and EOF not reached.  The second buffer is moved to the ;
  872. ; first buffer and all appropriate pointers are adjusted.         ;
  873. ;-----------------------------------------------------------------;
  874.  
  875. CK_EOF1:       XOR     BP,BP                   ;File index pointer.
  876.                CMP     SI,FILE_END[BP]         ;Is position = EOF?
  877.                JB      NOT_EOF                 ;If no, not EOF.
  878.                CMP     SI,BUFFER_END[BP]       ;Else, is it at end of buffer?
  879.                JB      EOF                     ;If no, EOF.
  880.                JMP     SHORT MOVE_BUFFER       ;Else, more to file; read it.
  881.  
  882. CK_EOF2:       MOV     BP,2                    ;Do same for file 2.
  883.                CMP     DI,FILE_END[BP]
  884.                JB      NOT_EOF
  885.                CMP     DI,BUFFER_END[BP]
  886.                JB      EOF
  887.  
  888. MOVE_BUFFER:   PUSH    AX                      ;Save registers.
  889.                PUSH    BX
  890.                PUSH    CX
  891.                PUSH    DX
  892.                PUSH    SI
  893.                PUSH    DI
  894.                MOV     SI,FILE_START[BP]       ;Retrieve buffer start address.
  895.                MOV     DI,SI
  896.                SUB     DI,TEN_K
  897.                MOV     CX,TEN_K / 2            ;And move data to buffer
  898.                REP     MOVSW                   ; 10K below.
  899.  
  900.                CALL    READ_FILE               ;Read the next 10K.
  901.                SUB     SAVE_POS[BP],TEN_K      ;Adjust all the pointers by 10K.
  902.                SUB     SYNC_POS[BP],TEN_K
  903.                SUB     FILE_POS[BP],TEN_K
  904.                ADD     HEX_OFFSET[BP],TEN_K    ;Add 10K to offset.
  905.                JNC     DO_ARRAY                ;If carry, add 1000h to segment.
  906.                ADD     HEX_SEGMENT[BP],1000H
  907. DO_ARRAY:      CALL    ARRAY_INDEX             ;Adjust line start array.
  908.                MOV     CX,4
  909. SUB_ARRAY:     SUB     [DI],TEN_K
  910.                INC     DI
  911.                INC     DI
  912.                LOOP    SUB_ARRAY
  913.                POP     DI
  914.                POP     SI
  915.                POP     DX
  916.                POP     CX
  917.                POP     BX
  918.                POP     AX
  919.                OR      BP,BP                   ;If file 1, adjust DI and DX.
  920.                JZ      ADJUST_FILE1
  921.                SUB     DI,TEN_K
  922.                SUB     DX,TEN_K
  923.                JMP     SHORT NOT_EOF
  924. ADJUST_FILE1:  SUB     SI,TEN_K                ;Else, adjust SI and BX.
  925.                SUB     BX,TEN_K
  926.  
  927. NOT_EOF:       CLC
  928.                RET
  929.  
  930. EOF:           STC
  931.                RET
  932.  
  933. CK_EOF         ENDP
  934.  
  935. ;-----------------------------------;
  936. ; INPUT                             ;
  937. ;   BP = file index.                ;
  938. ;                                   ;
  939. ; OUTPUT                            ;
  940. ;   DI = start of line-start array. ;
  941. ;                                   ;
  942. ;   All registers preserved.        ;
  943. ;-----------------------------------;
  944.  
  945. ARRAY_INDEX    PROC    NEAR
  946.  
  947.                MOV     DI,BP
  948.                SHL     DI,1                    ;Multiply file index by 4
  949.                SHL     DI,1                    ; to index into index.
  950.                ADD     DI,OFFSET LINE_ARRAY
  951.                RET
  952.  
  953. ARRAY_INDEX    ENDP
  954.  
  955. ;-----------------------------------;
  956. ; INPUT                             ;
  957. ;   BP = file index                 ;
  958. ;   DX = points to ASCIIZ filename. ;
  959. ;                                   ;
  960. ; OUTPUT                            ;
  961. ;   Carry flag = 1 if failed.       ;
  962. ;   Carry flag = 0 if successful.   ;
  963. ;                                   ;
  964. ;   BP preserved.                   ;
  965. ;-----------------------------------;
  966.  
  967. OPEN_FILE      PROC    NEAR
  968.  
  969.                MOV     AX,3D00H                ;Open file for reading.
  970.                INT     21H
  971.                JC      OPEN_END
  972.                MOV     FILE_HANDLE[BP],AX      ;Save file handle if successful
  973.                CALL    READ_FILE               ; and read 10K.
  974. OPEN_END:      RET
  975.  
  976. OPEN_FILE      ENDP
  977.  
  978. ;-----------------------------------;
  979. ; INPUT                             ;
  980. ;   BP = file index                 ;
  981. ;   DX = points to ASCIIZ filename. ;
  982. ;                                   ;
  983. ; OUTPUT                            ;
  984. ;   Carry flag = 1 if failed.       ;
  985. ;   Carry flag = 0 if successful.   ;
  986. ;                                   ;
  987. ;   BP preserved.                   ;
  988. ;-----------------------------------;
  989.  
  990. READ_FILE      PROC    NEAR
  991.  
  992.                MOV     BX,FILE_HANDLE[BP]      ;Retrieve filehandle.
  993.                MOV     DX,FILE_START[BP]       ;Point to storage buffer.
  994.                MOV     SI,DX                   ;Save it.
  995.                MOV     CX,TEN_K                ;Read maximum of 10K.
  996.                MOV     AH,3FH                  ;Read file.
  997.                INT     21H
  998.                JC      READ_FILE_END           ;If failed, exit.
  999.                ADD     DX,AX                   ;Else, add bytes read to buffer
  1000.                MOV     FILE_END[BP],DX         ; address and store as file end.
  1001.  
  1002.                MOV     CX,AX                   ;Bytes read in counter.
  1003.                JCXZ    READ_END                ;Skip if zero bytes read.
  1004.                MOV     AL,STRIP_MASK           ;Else, retrieve strip mask.
  1005.                CMP     AL,0FFH                 ;If not WordStar, skip.
  1006.                JZ      READ_END
  1007. WORDSTAR:      AND     DS:[SI],AL              ;Else, strip the high bit
  1008.                INC     SI                      ; of all bytes read.
  1009.                LOOP    WORDSTAR
  1010.  
  1011. READ_END:      CLC
  1012. READ_FILE_END: RET
  1013.  
  1014. READ_FILE      ENDP
  1015.  
  1016. ;-----------------------------------;
  1017. ; INPUT                             ;
  1018. ;   None                            ;
  1019. ;                                   ;
  1020. ; OUTPUT                            ;
  1021. ;   None                            ;
  1022. ;                                   ;
  1023. ;   ES preserved.                   ;
  1024. ;-----------------------------------;
  1025.  
  1026. VIDEO_SETUP    PROC    NEAR
  1027.  
  1028.                PUSH    ES                      ;Preserve ES.
  1029.                MOV     AX,500H                 ;Make sure active page is zero.
  1030.                INT     10H
  1031.                MOV     AX,40H                  ;Point to the ROM BIOS data area
  1032.                MOV     ES,AX
  1033.  
  1034.                MOV     AL,ES:CRT_MODE          ;Retrieve current video mode.
  1035.                CMP     AL,7                    ;Is it mono mode?
  1036.                JZ      SUPPORTED               ;If yes, continue.
  1037.                CMP     AL,3                    ;Is it text?
  1038.                JBE     SUPPORTED               ;If yes, continue.
  1039. UNSUPPORTED:   MOV     DX,OFFSET BAD_MODE_MSG  ;Else, do not pass GO.
  1040.                JMP     ERROR_EXIT              ;Go directly to jail.
  1041.  
  1042. SUPPORTED:     MOV     AH,12H
  1043.                MOV     BL,10H
  1044.                INT     10H
  1045.                CMP     BL,10H                  ;Is there an EGA?
  1046.                JZ      CK_CGA                  ;If no, check if CGA.
  1047.  
  1048.                TEST    ES:BYTE PTR [87H],8     ;Else, EGA_info; Is it active?
  1049.                JNZ     CK_CGA                  ;If no, check CGA.
  1050.                XOR     BH,BH                   ;Else, retrieve CRT rows.
  1051.                MOV     AX,1130H
  1052.                PUSH    ES
  1053.                INT     10H
  1054.                POP     ES
  1055.                MOV     ROWS,DL                 ;Save CRT rows.
  1056.  
  1057. CK_CGA:        MOV     AL,ES:CRT_COLS          ;Retrieve CRT cols.
  1058.                XOR     AH,AH                   ;Zero in high half.
  1059.                MOV     DX,AX                   ;Save it.
  1060.                MOV     BX,LINE_CAPACITY        ;Retrieve line capacity.
  1061.                CMP     BX,16                   ;Are we displaying hex?
  1062.                JNZ     CAPACITY                ;If no, store.
  1063.                MOV     DX,BX                   ;Else, use 16 as capacity.
  1064.                CMP     AL,40                   ;Are we in 40 column mode?
  1065.                JZ      UNSUPPORTED             ;If yes, useless display.
  1066.  
  1067. CAPACITY:      MOV     LINE_CAPACITY,DX        ;Store capacity and initialize
  1068.                MOV     SAVE_COL[0],DX          ; columns.
  1069.                MOV     SAVE_COL[2],DX
  1070.                MOV     COLS,AX                 ;Store columns.
  1071.                SHL     AX,1                    ;Times two for attribute.
  1072.                MOV     DL,ROWS                 ;Retrieve rows
  1073.                MUL     DL                      ; and multiply.
  1074.                MOV     STATUS_LINE,AX          ;Save as address of status line.
  1075.                SUB     DL,4                    ;Subtract 4 from CRT rows
  1076.                SHR     DL,1                    ; and divide by two
  1077.                MOV     WINDOW_SIZE,DL          ; and save as window size.
  1078.  
  1079.                MOV     AX,ES:ADDR_6845         ;Retrieve display card.
  1080.                ADD     AX,6                    ;Add six to get status register
  1081.                MOV     STATUS_REG,AX           ;Store as status register.
  1082.                CMP     AX,3BAH                 ;Is it monochrome?
  1083.                JZ      VIDEO_END               ;If yes, done here.
  1084.                ADD     VIDEO_SEG,800H          ;Else, adjust video segment.
  1085.                MOV     AH,8                    ;Get attribute at cursor postion.
  1086.                INT     10H
  1087.                MOV     NORMAL_ATTRIB,AH        ;And save as forground.
  1088.                XOR     AH,01110111B            ;Flip color bits.
  1089.                MOV     INVERSE_ATTRIB,AH       ;And save as highlight attribute.
  1090.  
  1091. VIDEO_END:     POP    ES                       ;Restore ES.
  1092.                RET
  1093.  
  1094. VIDEO_SETUP    ENDP
  1095.  
  1096. ;-----------------------------------;
  1097. ; INPUT                             ;
  1098. ;   AL = character to write.        ;
  1099. ;   BH = attribute.                 ;
  1100. ;                                   ;
  1101. ; OUTPUT                            ;
  1102. ;   None                            ;
  1103. ;                                   ;
  1104. ;   AL, BH, CX preserved.           ;
  1105. ;-----------------------------------;
  1106.  
  1107. WRITE_SCREEN   PROC    NEAR
  1108.  
  1109.                PUSH    ES
  1110.                MOV     DX,VIDEO_SEG            ;Point to screen segment.
  1111.                MOV     ES,DX
  1112.                MOV     DX,STATUS_REG           ;Retrieve status register.
  1113.                MOV     BL,AL                   ;Store character in BL.
  1114.  
  1115. HORZ_RET:      IN      AL,DX                   ;Get status.
  1116.                RCR     AL,1                    ;Is it low?
  1117.                JC      HORZ_RET                ;If not, wait until it is.
  1118.                CLI                             ;No more interrupts.
  1119.  
  1120. HWAIT:         IN      AL,DX                   ;Get status.
  1121.                RCR     AL,1                    ;Is it high?
  1122.                JNC     HWAIT                   ;If no, wait until it is.
  1123.  
  1124.                MOV     AX,BX                   ;Retrieve character; now it's OK
  1125.                STOSW                           ; to write to screen buffer.
  1126.                STI                             ;Interrupts back on.
  1127.                POP     ES
  1128.                RET                             ;Return
  1129.  
  1130. WRITE_SCREEN   ENDP
  1131.  
  1132. ;-----------------------------------;
  1133. ; INPUT                             ;
  1134. ;   None                            ;
  1135. ;                                   ;
  1136. ; OUTPUT                            ;
  1137. ;   None                            ;
  1138. ;                                   ;
  1139. ;   All registers destroyed.        ;
  1140. ;-----------------------------------;
  1141.  
  1142. DISPLAY_SETUP  PROC    NEAR
  1143.  
  1144.                CALL    CLS                     ;Clear screen.
  1145.                MOV     DH,ROWS                 ;Retrieve CRT rows.
  1146.                INC     DH                      ;Move one line below off screen.
  1147.                XOR     DL,DL                   ;Column zero.
  1148.                XOR     BH,BH                   ;Page zero.
  1149.                MOV     AH,2                    ;Set cursor position.
  1150.                INT     10H
  1151.  
  1152.                XOR     DI,DI                   ;Point to top left of display.
  1153.                MOV     SI,OFFSET COPYRIGHT     ;Point to copyright message.
  1154.                MOV     BH,NORMAL_ATTRIB        ;And display it.
  1155.                CALL    WRITE_STRING
  1156.                MOV     DI,COLS                 ;Retrieve columns.
  1157.                SHL     DI,1                    ;Double for attribute.
  1158.                SUB     DI,64                   ;Right justify my name.
  1159.                INC     SI                      ;Bump pointer past linefeed.
  1160.                CALL    WRITE_STRING
  1161.  
  1162.                MOV     BP,0                    ;Initialize counter.
  1163. NEXT_LINE:     MOV     CX,COLS                 ;Write line characters
  1164.                PUSH    DI                      ;Save position.
  1165.                MOV     AL,LINE_CHAR            ; to screen.
  1166. NEXT_WRITE:    CALL    WRITE_SCREEN
  1167.                LOOP    NEXT_WRITE
  1168.  
  1169.                POP     DI                      ;Retrieve position.
  1170.                CMP     BP,2                    ;Do two windows.
  1171.                JA      SETUP_END
  1172.                PUSH    DI                      ;Save screen pointer.
  1173.                ADD     DI,10                   ;Tab in five spaces
  1174.                MOV     AL,LF_BAR_CHAR          ; and print a left bar char.
  1175.                CALL    WRITE_SCREEN
  1176.                MOV     SI,FILENAME[BP]         ;Point to filename and print it.
  1177.                CALL    WRITE_STRING
  1178.                MOV     AL,RT_BAR_CHAR          ;Finish frame with right bar char
  1179.                CALL    WRITE_SCREEN
  1180.                POP     DI                      ;Restore display pointer.
  1181.                MOV     AX,COLS
  1182.                SHL     AX,1                    
  1183.                MOV     DL,WINDOW_SIZE
  1184.                INC     DL                      ;Multiply line length by window
  1185.                MUL     DL                      ; size plus one and add to
  1186.                ADD     DI,AX                   ; current position to get to
  1187.                ADD     BP,2                    ; next position to display line.
  1188.                JMP     SHORT NEXT_LINE
  1189. SETUP_END:     RET
  1190.  
  1191. DISPLAY_SETUP  ENDP
  1192.  
  1193. ;-----------------------------------;
  1194. ; INPUT                             ;
  1195. ;   None                            ;
  1196. ;                                   ;
  1197. ; OUTPUT                            ;
  1198. ;   None                            ;
  1199. ;                                   ;
  1200. ;   All registers destroyed.        ;
  1201. ;-----------------------------------;
  1202.  
  1203. DISPLAY_MENU   PROC    NEAR
  1204.  
  1205.                MOV     SI,OFFSET MENU          ;Point to menu.
  1206.                MOV     DI,STATUS_LINE          ;Point to status line.
  1207.                MOV     BH,NORMAL_ATTRIB        ;Use normal attribute
  1208.                CALL    WRITE_STRING            ; and display menu.
  1209.                RET
  1210.  
  1211. DISPLAY_MENU   ENDP
  1212.  
  1213. ;-----------------------------------;
  1214. ; INPUT                             ;
  1215. ;   BP = file index                 ;
  1216. ;                                   ;
  1217. ; OUTPUT                            ;
  1218. ;   None                            ;
  1219. ;                                   ;
  1220. ;   All registers destroyed.        ;
  1221. ;-----------------------------------;
  1222.  
  1223. PROMPT_USER    PROC    NEAR
  1224.  
  1225.                CMP     SYNTAX_FLAG,1           ;If first time through, display
  1226.                JZ      NEXT_PROMPT             ; syntax message.
  1227.                MOV     SYNTAX_FLAG,1
  1228.                MOV     DX,OFFSET SYNTAX_MSG
  1229.                CALL    PRINT_STRING
  1230.  
  1231. NEXT_PROMPT:   MOV     DX,PROMPT[BP]           ;Retrieve appropriate prompt
  1232.                CALL    PRINT_STRING            ; and display.
  1233.                MOV     DI,FILENAME[BP]         ;Retrieve address of filename
  1234.                MOV     DX,DI                   ; storage and save.
  1235.                SUB     DX,2                    ;Put in first byte of filename
  1236.                MOV     BX,DX                   ; buffer, the buffer length (80).
  1237.                MOV     BYTE PTR [BX],SPEC_LENGTH
  1238.                MOV     AH,0AH
  1239.                INT     21H                     ;Buffered Keyboard Input.
  1240.                PUSH    DI                      ;Save filename pointer.
  1241.                MOV     AL,CR                   ;Carriage return points
  1242. ASCIIZ:        SCASB                           ; to last byte of input.
  1243.                JNZ     ASCIIZ
  1244.                MOV     BYTE PTR [DI-1],0       ;Replace it with zero (ASCIIZ).
  1245.                POP     DI                      ;Retrieve filename pointer.
  1246.                MOV     DX,DI
  1247.                CALL    OPEN_FILE               ;Attempt to open the file.
  1248.                JNC     PROMPT_END              ;If successful, done here.
  1249.                MOV     DX,OFFSET NOT_FOUND_MSG ;Else, display "Not found"
  1250.                CALL    PRINT_STRING            ; message
  1251.                JMP     SHORT NEXT_PROMPT       ; and prompt user again.
  1252. PROMPT_END:    RET
  1253.  
  1254. PROMPT_USER    ENDP
  1255.  
  1256. ;-------------------------------------;
  1257. ; INPUT                               ;
  1258. ;   SI = points to string to display. ;
  1259. ;   Entry point is WRITE_STRING.      ;
  1260. ;                                     ;
  1261. ; OUTPUT                              ;
  1262. ;   None                              ;
  1263. ;                                     ;
  1264. ;   All registers destroyed.          ;
  1265. ;-------------------------------------;
  1266.  
  1267. WRITE_IT:      CALL    WRITE_SCREEN            ;Write a character.
  1268. WRITE_STRING:  LODSB                           ;Retrieve a character.
  1269.                CMP     AL,SPACE                ;Keep writing until a carriage
  1270.                JAE     WRITE_IT                ; return or space encountered.
  1271.                RET
  1272.  
  1273. ;-------------------;
  1274. ; BIOS Keyboard I/O ;
  1275. ;-------------------;
  1276.  
  1277. READ_KEY:      MOV     AH,0                    ;Wait for next keyboard
  1278.                INT     16H                     ; input.
  1279.                RET
  1280.  
  1281. CK_KEY:        MOV     AH,1                    ;See if character ready.
  1282.                INT     16H
  1283.                RET
  1284.  
  1285. CLEAR_IT:      CALL    READ_KEY                ;If characters are ready
  1286. CLEAR_KEY:     CALL    CK_KEY                  ; read them to clear keyboard
  1287.                JNZ     CLEAR_IT                ; buffer.
  1288.                RET
  1289.  
  1290. ;---------------------------; 
  1291. ; Screen clearing routines. ;
  1292. ;---------------------------;
  1293.  
  1294. CLS:           XOR     CX,CX                   ;Top left corner.
  1295.                MOV     DX,COLS                 ;Right corner
  1296.                DEC     DL
  1297.                MOV     DH,ROWS                 ;Bottom row.
  1298.                JMP     SHORT CLEAR_WINDOW      ;Clear the screen.
  1299.  
  1300. CLS_WINDOWS:   MOV     CX,0200H                ;Row 3; column zero.
  1301.                MOV     DX,COLS                 ;Right corner.
  1302.                DEC     DL
  1303.                MOV     DH,WINDOW_SIZE          ;Bottom of window one.
  1304.                INC     DH
  1305.                CALL    CLEAR_WINDOW            ;Clear the window.
  1306.                ADD     CH,WINDOW_SIZE          ;Increment a window size
  1307.                INC     CH                      ; and a delimiting line.
  1308.                ADD     DH,WINDOW_SIZE
  1309.                INC     DH
  1310.                JMP     SHORT CLEAR_WINDOW      ;Clear second window.
  1311.  
  1312. CLS_MENU:      MOV     CH,ROWS                 ;Bottom row.
  1313.                XOR     CL,CL                   ;Column zero.
  1314.                MOV     DH,CH
  1315.                MOV     DL,2CH                  ;To column 44.
  1316.  
  1317. CLEAR_WINDOW:  PUSH   BX                       ;Preserve BX.
  1318.                MOV    BH,NORMAL_ATTRIB         ;Normal attribute.
  1319.                MOV    AX,600H                  ;Scroll active page.
  1320.                INT    10H
  1321.                POP    BX                       ;Restore BX.
  1322.                RET
  1323.  
  1324. ;-------------------; 
  1325. ; DOS print string. ;
  1326. ;-------------------;
  1327.  
  1328. PRINT_STRING   PROC    NEAR
  1329.  
  1330.                MOV     AH,9
  1331.                INT     21H
  1332.                RET
  1333.  
  1334. PRINT_STRING   ENDP
  1335.  
  1336. ;----------------------------------------------------------; 
  1337. ; Buffered keyboard input and file buffers at end of code. ;
  1338. ;----------------------------------------------------------;
  1339.  
  1340. EVEN
  1341.  
  1342. FILESPEC       EQU     $
  1343. FILE1_BUFFER1  EQU     FILESPEC + 2 * (SPEC_LENGTH + 2)
  1344. FILE1_BUFFER2  EQU     FILE1_BUFFER1 + TEN_K
  1345. FILE2_BUFFER1  EQU     FILE1_BUFFER2 + TEN_K
  1346. FILE2_BUFFER2  EQU     FILE2_BUFFER1 + TEN_K
  1347.  
  1348. _TEXT          ENDS
  1349.                END     START
  1350.