REM Program: Hex Editor v4.7a, Module 4 of 5, PD 2003.
REM Author: Erik Jon Oredson AS. Csci
REM Release: 07/21/2003.
REM Status: Public Domain.
REM Email: eoredson@yahoo.com
REM Urls: www.simtel.net www.filegate.net
REM  www.winsite.com

' get include file.
REM $INCLUDE: 'hexedit.inc'

REM Marker routines:

SUB Marker(Var)
' declare error trap.
ON LOCAL ERROR GOTO Error.Routine1

IF Var = 1 THEN GOTO ListMarkerRange
IF Var = 2 THEN GOTO AddMarker
IF Var = 3 THEN GOTO AddSpecificMarker
IF Var = 4 THEN GOTO DeleteMarker
IF Var = 5 THEN GOTO DeleteSpecificMarker
IF Var = 6 THEN GOTO JumpPreviousMarker
IF Var = 7 THEN GOTO JumpCurrentMarker
IF Var = 8 THEN GOTO JumpNextMarker
IF Var = 9 THEN GOTO JumpSpecificMarker
IF Var = 10 THEN GOTO ListAllMarkers
IF Var = 11 THEN GOTO ListSpecificMarker
IF Var = 12 THEN GOTO ListMarkersOfValue
IF Var = 13 THEN GOTO DeleteMarkerRange
IF Var = 14 THEN GOTO DeleteAllMarkers
IF Var = 15 THEN GOTO DeleteMarkersOfValue
EXIT SUB

TopProgram:
EXIT SUB

AddMarker:
 TempPosition3 = FilePosition
 GOSUB AddMarkerValue
 EXIT SUB

AddMarkerValue:
 IF CurrentUndo >= 32767 THEN
    StatusMessage = "Maximum markers are at 32,767."
    GOSUB DisplayStatus2
    RETURN
 END IF
 IF MarkerCount < 32767 THEN
    MarkerCount = MarkerCount + 1
    MarkerFile.Markers1(CurrentFile) = TempPosition3
    PUT #7, MarkerCount, MarkerFile
    CurrentMarker = MarkerCount
 END IF
 RETURN

AddSpecificMarker:
 GOSUB ClearStatus
 PRINT "Enter file position";
 INPUT ByteString$
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    GOSUB CheckHexValue
    IF ValidHexValue = False THEN
       EXIT SUB
    END IF
    TempPosition3 = HexValue
 ELSE
    GOSUB CheckASCIIValue
    IF ValidASCIIValue = False THEN
       EXIT SUB
    END IF
    TempPosition3 = ASCIIValue3
 END IF
 IF TempPosition3 >= 1 AND TempPosition3 <= FileLength THEN
    GOSUB AddMarkerValue
    GOSUB LocateCursor2
    GOSUB DisplayFilename
    EXIT SUB
 END IF
 StatusMessage = "Invalid file position."
 GOSUB DisplayStatus2
 EXIT SUB

DeleteMarker:
 IF CurrentMarker >= 1 AND CurrentMarker <= MarkerCount THEN
    MarkerFile.Markers1(CurrentFile) = False
    PUT #7, CurrentMarker, MarkerFile
 END IF
 EXIT SUB

DeleteSpecificMarker:
 GOSUB ClearStatus
 PRINT "Enter marker number";
 INPUT ByteString$
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    MarkerPosition = INT(VAL(ByteString$))
    IF MarkerPosition >= 1 AND MarkerPosition <= MarkerCount THEN
       MarkerFile.Markers1(CurrentFile) = False
       PUT #7, MarkerPosition, MarkerFile
       GOSUB LocateCursor2
       GOSUB DisplayFilename
       EXIT SUB
    END IF
 END IF
 StatusMessage = "Invalid marker number."
 GOSUB DisplayStatus2
 EXIT SUB

JumpPreviousMarker:
 NewMarker = CurrentMarker
 DO
    IF NewMarker <= 1 THEN
       EXIT SUB
    END IF
    NewMarker = NewMarker - 1
    IF Markers#(NewMarker) > 0# THEN
       CurrentMarker = NewMarker
       EXIT DO
    END IF
 LOOP
 GOTO JumpCurrentMarker

JumpNextMarker:
 NewMarker = CurrentMarker
 DO
    IF NewMarker >= MarkerCount THEN
       EXIT SUB
    END IF
    NewMarker = NewMarker + 1
    IF Markers#(NewMarker) > 0# THEN
       CurrentMarker = NewMarker
       EXIT DO
    END IF
 LOOP
 GOTO JumpCurrentMarker

JumpSpecificMarker:
 GOSUB ClearStatus
 PRINT "Enter marker number";
 INPUT ByteString$
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    MarkerPosition = INT(VAL(ByteString$))
    IF MarkerPosition >= 1 AND MarkerPosition <= MarkerCount THEN
       IF Markers#(MarkerPosition) > 0# THEN
          CurrentMarker = MarkerPosition
          GOTO JumpCurrentMarker
       END IF
    END IF
 END IF
 StatusMessage = "Invalid marker number."
 GOSUB DisplayStatus2
 EXIT SUB

JumpCurrentMarker:
 IF CurrentMarker >= 1 AND CurrentMarker <= MarkerCount THEN
    TempPosition = Markers#(CurrentMarker)
    IF TempPosition >= 1 AND TempPosition <= FileLength THEN
       IF TempPosition <> FilePosition THEN
          GOSUB ClearPageByte
          LastPage = FilePage
          FilePosition = TempPosition
          GOSUB CalculatePosition1
          IF LastPage <> FilePage THEN
             GOSUB DisplayHexPage
          END IF
          GOSUB DisplayPageByte
       END IF
    END IF
 END IF
 GOSUB DisplayFilename
 EXIT SUB

ListAllMarkers:
 CALL DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "Marker list:";
 PageCount = False
 CountFlag = False
 Var2 = 2
 FOR MarkerPosition = 1 TO MarkerCount
    IF Markers#(MarkerPosition) > 0# THEN
       PageCount = PageCount + 1
       CountFlag = True
       COLOR Yellow
       Var2 = Var2 + 1
       LOCATE Var2, 2
       GOSUB FormatPosition3
       IF PageCount >= 20 THEN
          PageCount = False
          LOCATE 23, 2
          GOSUB PressKey
          CALL DisplayScreen2
          COLOR White
          LOCATE 2, 2
          PRINT "Marker list:"
          Var2 = 2
       END IF
    END IF
 NEXT
 IF CountFlag = False THEN
    COLOR Yellow
    LOCATE 3, 2
    PRINT "No markers stored."
    LOCATE 23, 2
    GOSUB PressKey
 END IF
 IF PageCount > False THEN
    LOCATE 23, 2
    GOSUB PressKey
 END IF
 EXIT SUB

ListSpecificMarker:
 GOSUB ClearStatus
 PRINT "Enter marker number";
 INPUT ByteString$
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    MarkerPosition = INT(VAL(ByteString$))
    IF MarkerPosition >= 1 AND MarkerPosition <= MarkerCount THEN
       GOSUB ClearStatus
       GOSUB FormatPosition2
       GOSUB LocateCursor2
       WHILE INKEY$ <> CHR$(27): WEND
       GOSUB DisplayFilename
       EXIT SUB
    END IF
 END IF
 StatusMessage = "Invalid marker number."
 GOSUB DisplayStatus2
 EXIT SUB

ListMarkersOfValue:
 GOSUB ClearStatus
 PRINT "Enter marker value";
 INPUT ByteString$
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    GOSUB CheckHexValue
    IF ValidHexValue = False THEN
       EXIT SUB
    END IF
    TempPosition1 = HexValue
 ELSE
    GOSUB CheckASCIIValue
    IF ValidASCIIValue = False THEN
       EXIT SUB
    END IF
    TempPosition1 = ASCIIValue3
 END IF
 CALL DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "Marker list:"
 PageCount = False
 CountFlag = False
 Var2 = 2
 FOR MarkerPosition = 1 TO MarkerCount
    IF Markers#(MarkerPosition) = TempPosition1 THEN
       PageCount = PageCount + 1
       CountFlag = True
       COLOR Yellow
       Var2 = Var2 + 1
       LOCATE Var2, 2
       GOSUB FormatPosition3
       IF PageCount >= 20 THEN
          PageCount = False
          LOCATE 23, 2
          GOSUB PressKey
          CALL DisplayScreen2
          COLOR White
          LOCATE 2, 2
          PRINT "Marker list:"
          Var2 = 2
       END IF
    END IF
 NEXT
 IF CountFlag = False THEN
    COLOR Yellow
    LOCATE 3, 2
    PRINT "No markers of value stored."
    LOCATE 23, 2
    GOSUB PressKey
 END IF
 IF PageCount > False THEN
    LOCATE 23, 2
    GOSUB PressKey
 END IF
 EXIT SUB

ListMarkerRange:
 GOSUB ClearStatus
 PRINT "Enter beginning marker number";
 INPUT ByteString$
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    MarkerPosition1 = INT(VAL(ByteString$))
    IF MarkerPosition1 >= 1 AND MarkerPosition1 <= MarkerCount THEN
       GOSUB ClearStatus
       PRINT "Enter ending marker number";
       INPUT ByteString$
       IF LEN(ByteString$) THEN
          IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
             ByteString$ = "&" + ByteString$
          END IF
          MarkerPosition2 = INT(VAL(ByteString$))
          IF MarkerPosition2 >= 1 AND MarkerPosition2 <= MarkerCount THEN
             CALL DisplayScreen2
             COLOR White
             LOCATE 2, 2
             PRINT "Marker list:"
             PageCount = False
             CountFlag = False
             Var2 = 2
             FOR MarkerPosition = MarkerPosition1 TO MarkerPosition2
                IF Markers#(MarkerPosition) > 0# THEN
                   PageCount = PageCount + 1
                   CountFlag = True
                   COLOR Yellow
                   Var2 = Var2 + 1
                   LOCATE Var2, 2
                   GOSUB FormatPosition3
                   IF PageCount >= 20 THEN
                      PageCount = False
                      LOCATE 23, 2
                      GOSUB PressKey
                      CALL DisplayScreen2
                      COLOR White
                      LOCATE 2, 2
                      PRINT "Marker list:"
                      Var2 = 2
                   END IF
                END IF
             NEXT
             IF CountFlag = False THEN
                COLOR Yellow
                LOCATE 3, 2
                PRINT "No markers stored."
                LOCATE 23, 2
                GOSUB PressKey
             END IF
             IF PageCount > False THEN
                LOCATE 23, 2
                GOSUB PressKey
             END IF
             ValidFunction = -1
             EXIT SUB
          END IF
       END IF
    END IF
 END IF
 ValidFunction = 0
 StatusMessage = "Invalid marker range."
 GOSUB DisplayStatus2
 EXIT SUB

DeleteMarkerRange:
 GOSUB ClearStatus
 PRINT "Enter beginning marker number";
 INPUT ByteString$
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    MarkerPosition1 = INT(VAL(ByteString$))
    IF MarkerPosition1 >= 1 AND MarkerPosition1 <= MarkerCount THEN
       GOSUB ClearStatus
       PRINT "Enter ending marker number";
       INPUT ByteString$
       IF LEN(ByteString$) THEN
          IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
             ByteString$ = "&" + ByteString$
          END IF
          MarkerPosition2 = INT(VAL(ByteString$))
          IF MarkerPosition2 >= 1 AND MarkerPosition2 <= MarkerCount THEN
             FOR MarkerPosition = MarkerPosition1 TO MarkerPosition2
                MarkerFile.Markers1(CurrentFile) = False
                PUT #7, MarkerPosition, MarkerFile
             NEXT
             StatusMessage = "Range of markers deleted."
             GOSUB DisplayStatus2
             EXIT SUB
          END IF
       END IF
    END IF
 END IF
 StatusMessage = "Invalid marker range."
 GOSUB DisplayStatus2
 EXIT SUB

DeleteAllMarkers:
 CurrentMarker = 0
 MarkerCount = 0
 StatusMessage = "All markers deleted."
 GOSUB DisplayStatus2
 EXIT SUB

DeleteMarkersOfValue:
 GOSUB ClearStatus
 PRINT "Enter marker value";
 INPUT ByteString$
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       GOSUB CheckHexValue
       IF ValidHexValue = False THEN
          EXIT SUB
       END IF
       TempPosition1 = HexValue
    ELSE
       GOSUB CheckASCIIValue
       IF ValidASCIIValue = False THEN
          EXIT SUB
       END IF
       TempPosition1 = ASCIIValue3
    END IF
    FOR MarkerPosition = 1 TO MarkerCount
       IF Markers#(MarkerPosition) = TempPosition1 THEN
          MarkerFile.Markers1(CurrentFile) = False
          PUT #7, MarkerPosition, MarkerFile
       END IF
    NEXT
 END IF
 StatusMessage = "All markers of value deleted."
 GOSUB DisplayStatus2
 EXIT SUB

' read 1 byte from file.
ReadFile:
 InregsX.AX = &H3F00
 InregsX.BX = Handle
 InregsX.CX = 1
 InregsX.DS = VARSEG(Buffer)
 InregsX.DX = VARPTR(Buffer)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' write 1 byte to file.
WriteFile:
 Buffer = FileByte
 InregsX.AX = &H4000
 InregsX.BX = Handle
 InregsX.CX = 1
 InregsX.DS = VARSEG(Buffer)
 InregsX.DX = VARPTR(Buffer)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' position pointer in file.
LseekFile:
 ' calculate high/low seek position.
 SeekPosition2 = SeekPosition - 1
 High = INT(SeekPosition2 / 65536)
 Low = SeekPosition2 AND 65535
 ' seek position in file.
 InregsX.AX = &H4200
 InregsX.BX = Handle
 InregsX.CX = INT(VAL("&H" + HEX$(High)))
 InregsX.DX = INT(VAL("&H" + HEX$(Low)))
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' clear top status area
ClearStatus:
 COLOR Yellow
 LOCATE 2, 4
 PRINT SPACE$(74);
 LOCATE 2, 4
 RETURN

' display status line message and prompt type 2.
DisplayStatus2:
 StatusMessage = StatusMessage + " Press <esc> to continue:"

' display the status line message and prompt for key.
DisplayStatusLine:
 GOSUB ClearStatus
 PRINT StatusMessage;
 GOSUB LocateCursor2
 WHILE INKEY$ <> CHR$(27): WEND
 GOSUB DisplayFilename
 RETURN

' select cursor on window.
LocateCursor2:
 IF CopyPositionStart = 0 THEN
    GOSUB LocateCursor
 ELSE
    GOSUB LocateHilightCursor
 END IF
 RETURN

' locate cursor on window.
LocateCursor:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 IF CurrentWindow = False THEN
    LOCATE PageRow + 3, Column + 5, 1
 ELSE
    LOCATE PageRow + 3, PageColumn + 54, 1
 END IF
 RETURN

' locate cursor on window for hilighted area.
LocateHilightCursor:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 IF CurrentWindow = False THEN
    LOCATE PageRow + 3, Column + 7, 1
 ELSE
    LOCATE PageRow + 3, PageColumn + 55, 1
 END IF
 RETURN

' reset hilight bytes.
ResetHilightBytes:
 Temp# = FilePosition
 Temp2# = FilePage
 IF CopyPositionStart < (Temp2# - 1) * 320 + 1 THEN
    CopyPositionStart = (Temp2# - 1) * 320 + 1
 END IF
 IF CopyPositionEnd > (Temp2# - 1) * 320 + 320 THEN
    CopyPositionEnd = (Temp2# - 1) * 320 + 320
 END IF
 IF CopyPositionEnd > FileLength THEN
    CopyPositionEnd = FileLength
 END IF
 FOR FilePosition = CopyPositionStart TO CopyPositionEnd
    GOSUB CalculatePosition1
    COLOR White
    GOSUB BytePrint
 NEXT
 CopyPositionStart = 0
 CopyPositionEnd = 0
 FilePosition = Temp#
 GOSUB CalculatePosition1
 IF PageColumn + 1 <= 20 THEN
    IF FilePosition + 1 <= FileLength THEN
       GOSUB ClearPageByte
       PageColumn = PageColumn + 1
       FilePosition = FilePosition + 1
       GOSUB DisplayPageByte
    END IF
 END IF
 COLOR Yellow
 GOSUB BytePrint
 RETURN

' print the byte.
BytePrint:
 SeekPosition = FilePosition
 GOSUB LseekFile
 GOSUB ReadFile
 FileByte = Buffer
 ByteValue = ASC(FileByte)
 GOSUB CalculateColumn
 ' display hex byte.
 LOCATE PageRow + 3, Column + 5, 0
 PRINT RIGHT$("00" + HEX$(ByteValue), 2);
 ' check right window toggled.
 IF CurrentWindow2 = False THEN
    ' display ascii byte.
    LOCATE PageRow + 3, PageColumn + 54, 0
    ' skip unprintable characters.
    SELECT CASE ByteValue
    CASE 0, 7, 9 TO 13, 28 TO 32
       PRINT ".";
    CASE ELSE
       PRINT FileByte;
    END SELECT
 END IF
 RETURN

' display name of file being edited.
DisplayFilename:
 GOSUB ClearStatus
 PRINT "Editing file: "; RTRIM$(Filename); " ";
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 GOSUB DisplayPosition
 RETURN

' clear current byte on screen.
ClearPageByte:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 IF CopyPositionStart > 0# THEN
    GOSUB ResetHilightBytes
 END IF
 COLOR White
 GOSUB BytePrint
 RETURN

' clear current hilighted byte on screen.
ClearHilightByte:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 COLOR White
 GOSUB BytePrint
 RETURN

' calculate current screen page number, row, and column, given file position.
CalculatePosition1:
 FilePage = INT((FilePosition - 1) / 320) + 1
 PageRow = INT((FilePosition - (FilePage - 1) * 320 - 1) / 20) + 1
 PageColumn = INT((FilePosition - (FilePage - 1) * 320) - (PageRow - 1) * 20)
 RETURN

' display screen of current page of hex/ascii values of file being edited.
DisplayHexPage:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 COLOR White
 Row = False
 Column = False
 ColumnSpace = False
 FOR NextByte = (FilePage - 1) * 320 + 1 TO (FilePage - 1) * 320 + 320
    LOCATE Row + 4, Column + 6, 0
    IF NextByte <= FileLength THEN
       SeekPosition = NextByte
       GOSUB LseekFile
       GOSUB ReadFile
       FileByte = Buffer
       PRINT RIGHT$("00" + HEX$(ASC(FileByte)), 2);
    ELSE
       PRINT "  ";
    END IF
    ColumnSpace = ColumnSpace + 1
    IF ColumnSpace = 4 THEN
       PRINT " ";
       Column = Column + 1
       ColumnSpace = False
    END IF
    Column = Column + 2
    IF Column > 44 THEN
       Row = Row + 1
       Column = False
    END IF
 NEXT
 GOSUB RedrawRightWindow
 RETURN

' redraw right window.
RedrawRightWindow:
 IF CurrentWindow2 = False THEN
    GOSUB RedrawWindow1
 ELSE
    GOSUB RedrawWindow2
 END IF
 RETURN

' redraw right window of ascii values.
RedrawWindow1:
 COLOR White
 Row = False
 Column = False
 FOR NextLine = 0 TO 15
    LOCATE NextLine + 4, 54, 0
    PRINT " ";
 NEXT
 FOR NextByte = (FilePage - 1) * 320 + 1 TO (FilePage - 1) * 320 + 320
    LOCATE Row + 4, Column + 55, 0
    IF NextByte <= FileLength THEN
       SeekPosition = NextByte
       GOSUB LseekFile
       GOSUB ReadFile
       FileByte = Buffer
       ByteValue = ASC(FileByte)
       ' skip unprintable characters
       SELECT CASE ByteValue
       CASE 0, 7, 9 TO 13, 28 TO 32
          PRINT ".";
       CASE ELSE
          PRINT FileByte;
       END SELECT
    ELSE
       PRINT " ";
    END IF
    Column = Column + 1
    IF Column > 19 THEN
       Row = Row + 1
       Column = False
    END IF
 NEXT
 RETURN

' redraw right window of hex ranges.
RedrawWindow2:
 Row = False
 FOR NextLine = (FilePage - 1) * 320 + 1 TO (FilePage - 1) * 320 + 320 STEP 20
    LOCATE Row + 4, 54, 0
    Row = Row + 1
    IF NextLine <= FileLength THEN
       IF NextLine + 19 <= FileLength THEN
          COLOR White
          PRINT "x"; RIGHT$("00000000" + HEX$(NextLine - 1), 8);
          COLOR Yellow
          PRINT " - ";
          COLOR White
          PRINT "x"; RIGHT$("00000000" + HEX$(NextLine + 19 - 1), 8);
       ELSE
          COLOR White
          PRINT "x"; RIGHT$("00000000" + HEX$(NextLine - 1), 8);
          COLOR Yellow
          PRINT " - ";
          COLOR White
          PRINT "x"; RIGHT$("00000000" + HEX$(FileLength - 1), 8);
       END IF
    ELSE
       PRINT SPACE$(22);
    END IF
 NEXT
 RETURN

' display current byte being edited.
DisplayPageByte:
 ' check filename display.
 IF FileDisplay2 THEN
    FileDisplay2 = 0
    GOSUB ClearStatus
    PRINT "Editing file: "; RTRIM$(Filename); " ";
 END IF
 ' check filelength.
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 ' check hilighted bytes.
 IF CopyPositionStart > 0# THEN
    GOSUB ResetHilightBytes
 END IF
 ' display byte.
 COLOR Yellow
 GOSUB BytePrint
 ' store the current byte value being edited.
 AsciiValue = ASC(FileByte)
 GOSUB DisplayPosition
 RETURN

' display info on file being edited.
DisplayPosition:
 COLOR Yellow
 StringLength = LEN("Editing file: " + RTRIM$(Filename) + " ") + 4
 LOCATE 2, StringLength, 0
 PRINT SPACE$(77 - StringLength);
 LOCATE 2, StringLength, 0
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 IF CurrentWindow2 = False THEN
    PRINT "(Position:"; STR$(FilePosition - 1); ") ";
 ELSE
    PRINT "(Position: "; RIGHT$("00000000" + HEX$(FilePosition - 1), 8); "H) ";
 END IF
 PRINT "(Ascii:"; STR$(AsciiValue); ") ";
 PRINT "(Hex: "; RIGHT$("00" + HEX$(AsciiValue), 2); "H)";
 GOSUB LocateCursor2
 RETURN

' calculate the spaces between hex value groups.
CalculateColumn:
 SELECT CASE PageColumn
 CASE 1 TO 4
    Column = (PageColumn - 1) * 2 + 1
 CASE 5 TO 8
    Column = (PageColumn - 1) * 2 + 2
 CASE 9 TO 12
    Column = (PageColumn - 1) * 2 + 3
 CASE 13 TO 16
    Column = (PageColumn - 1) * 2 + 4
 CASE 17 TO 20
    Column = (PageColumn - 1) * 2 + 5
 END SELECT
 RETURN

' display message for locked file.
LockedFile:
 COLOR White
 PRINT "<locked file>";
 LOCATE 1, 1, 0
 RETURN

' key prompt.
PressKey:
 COLOR White
 PRINT "Press a key:";
 WHILE INKEY$ = Nul: WEND
 RETURN

' displays cell position in string format type 2.
FormatPosition2:
 PRINT "Marker#"; MID$(STR$(MarkerPosition), 2); ":";
 TempPosition3 = Markers#(MarkerPosition)
 IF CurrentWindow2 = False THEN
    PRINT MID$(STR$(TempPosition3 - 1), 2); " ";
 ELSE
    PRINT RIGHT$("00000000" + HEX$(TempPosition3 - 1), 8); "H ";
 END IF
 GOSUB CalculatePosition2
 IF CurrentWindow2 = False THEN
    PRINT "(page:"; MID$(STR$(FilePage2 - 1), 2); ",";
 ELSE
    PRINT "(page:"; RIGHT$("00000000" + HEX$(FilePage2 - 1), 8); "H,";
 END IF
 PRINT "row:"; MID$(STR$(PageRow2), 2); ",";
 PRINT "column:"; MID$(STR$(PageColumn2), 2); ")";
 PRINT " Press <esc>:";
 RETURN

' displays cell position in string format type 3.
FormatPosition3:
 PRINT "Marker#"; MID$(STR$(MarkerPosition), 2); " Position:";
 TempPosition3 = Markers#(MarkerPosition)
 IF CurrentWindow2 = False THEN
    PRINT MID$(STR$(TempPosition3 - 1), 2); " ";
 ELSE
    PRINT RIGHT$("00000000" + HEX$(TempPosition3 - 1), 8); "H ";
 END IF
 GOSUB CalculatePosition2
 IF CurrentWindow2 = False THEN
    PRINT "(page:"; MID$(STR$(FilePage2 - 1), 2); ",";
 ELSE
    PRINT "(page:"; RIGHT$("00000000" + HEX$(FilePage2 - 1), 8); "H,";
 END IF
 PRINT "row:"; MID$(STR$(PageRow2), 2); ",";
 PRINT "column:"; MID$(STR$(PageColumn2), 2); ")";
 RETURN

' check valid ascii value
CheckASCIIValue:
 ValidASCIIValue = False
 ASCIIValue3 = VAL(ByteString$)
 IF ASCIIValue3 <= 0# OR ASCIIValue3 > 2147483647# THEN
    StatusMessage = "Invalid ascii value."
    GOSUB DisplayStatus2
    RETURN
 END IF
 ValidASCIIValue = True
 RETURN

' check hex byte value.
'   returns ValidHexValue True for valid hex value.
' parameters:
'   Hex string must be 8 characters or less.
'   Hex value from 8000 to ffff return signed bit
'     and must be incremented by 65536.
'   Since 7fff ffff is max filesize,
'     hex values from 8000 0000 to ffff ffff
'     which are length 8 return negative value.
'   Since hex values start from an offset of 0
'     then the maximum value allowed is 7fff fffe.
CheckHexValue:
 ValidHexValue = False
 StringLength2 = LEN(MID$(ByteString$,2))
 IF StringLength2 > 8 THEN
    StatusMessage = "Invalid hex value."
    GOSUB DisplayStatus2
    RETURN
 END IF
 GOSUB CheckString
 IF ValidString=False THEN
    StatusMessage = "Invalid hex string."
    GOSUB DisplayStatus2
    RETURN
 END IF
 ByteString$ = "&" + ByteString$
 HexValue = VAL(ByteString$)
 ' check signed bit error.
 IF StringLength2 = 4 THEN
    IF HexValue < 0 THEN
       HexValue = HexValue + 65536#
    END IF
 END IF
 IF StringLength2 = 8 THEN
    IF HexValue < 0 THEN
       StatusMessage = "Hex underflow."
       GOSUB DisplayStatus2
       RETURN
    END IF
 END IF
 IF HexValue >= 2147483647# THEN
    StatusMessage = "Hex overflow."
    GOSUB DisplayStatus2
    RETURN
 END IF
 ValidHexValue = True
 ' adjust hex offset.
 HexValue = HexValue + 1#
 RETURN

' check a hex byte string.
CheckString:
 ValidString=True
 FOR HexPosition=2 TO LEN(ByteString$)
    SELECT CASE UCASE$(MID$(ByteString$,HexPosition,1))
    CASE "0" To "9", "A" TO "F"
       ' nul activity
    CASE ELSE
       ValidString=False
       RETURN
    END SELECT
 NEXT
 RETURN

' calculate screen page number, row, and column, given variable.
CalculatePosition2:
 IF TempPosition3 = 0 THEN
    FilePage2 = 0
    PageRow2 = 0
    PageColumn2 = 0
 ELSE
    FilePage2 = INT((TempPosition3 - 1) / 320) + 1
    PageRow2 = INT((TempPosition3 - (FilePage2 - 1) * 320 - 1) / 20) + 1
    PageColumn2 = INT((TempPosition3 - (FilePage2 - 1) * 320) - (PageRow2 - 1) * 20)
 END IF
 RETURN

' critical error trap.
Error.Routine1:
 IF ScreenDrawn THEN
    CLS
 END IF
 ScreenDrawn = False
 COLOR White
 PRINT "Hex Editor Clipboard " + Version + " " + Release + " critical error trap:"
 COLOR Yellow
 ErrorTrap = ERR
 SELECT CASE ERR
 CASE 5
    PRINT "Syntax error." ' should not happen.
 CASE 6
    PRINT "Overflow."
 CASE 9
    PRINT "Subscript out of range."
 CASE 14
    PRINT "Out of string space."
 CASE 24
    PRINT "Device timeout."
 CASE 25
    PRINT "Device fault."
 CASE 27
    PRINT "Out of paper."
 CASE 52
    PRINT "Bad file name or number."
 CASE 53
    PRINT "File not found."
 CASE 54
    PRINT "Bad file mode."
 CASE 55
    PRINT "File already open."
 CASE 57
    PRINT "Device I/O error."
 CASE 58
    PRINT "File already exists."
 CASE 59
    PRINT "Bad record length."
 CASE 61
    PRINT "Disk full."
 CASE 62
    PRINT "Input past eof."
 CASE 63
    PRINT "Bad record length."
 CASE 64
    PRINT "Bad filename."
 CASE 67
    PRINT "Not enough file handles."
 CASE 68
    PRINT "Device unavailable."
 CASE 70
    PRINT "Permission denied."
 CASE 71
    PRINT "Disk not ready."
 CASE 72
    PRINT "Disk-media error."
 CASE 75
    PRINT "Path/File access error."
 CASE 76
    PRINT "Pathname not found."
 CASE 81
    PRINT "Invalid name."
 CASE ELSE
    PRINT "Untrapped error" + STR$(ERR) + "."
 END SELECT
 ' display error prompt.
 COLOR Green
 PRINT "Press R(etry), C(ontinue), Q(uit):";
 ' get keypress.
 DO
    ErrorRespond$ = Nul
    DO
       ErrorRespond$ = INKEY$
       IF LEN(ErrorRespond$) THEN
          EXIT DO
       END IF
    LOOP
    ' parse key.
    SELECT CASE LCASE$(ErrorRespond$)
    CASE "r"
       PRINT "r"
       RESUME TopProgram
    CASE "c"
       PRINT "c"
       RESUME TopProgram
    CASE "q"
       PRINT "q"
       RESUME TopProgram
    END SELECT
 LOOP
END SUB

' process left mouse button drag. also includes shift-<key> hilighting.
SUB MouseButton1Drag(Var)
 ON LOCAL ERROR GOTO Error.Routine2
 IF Var = 0 THEN GOTO DragMouse
 IF Var = 1 THEN GOTO ShiftLeftKey
 IF Var = 2 THEN GOTO ShiftRightKey
 IF Var = 3 THEN GOTO ShiftUpKey
 IF Var = 4 THEN GOTO ShiftDownKey
 IF Var = 5 THEN GOTO ShiftPageUpKey
 IF Var = 6 THEN GOTO ShiftPageDownKey
 IF Var = 7 THEN GOTO ShiftEndKey
 IF Var = 8 THEN GOTO ShiftHomeKey
 IF Var = 9 THEN GOTO DragMousePageUp
 IF Var = 10 THEN GOTO DragMousePageDown
 EXIT SUB

REM Hilighting key routines:

' move hilight area left.
ShiftLeftKey:
 IF FilePosition = 1 THEN
    GOSUB StoreHilight
 ELSE
    IF PageColumn - 1 >= 1 THEN
       GOSUB StoreHilight
       IF CopyPositionEnd - 1 >= CopyPositionPivot THEN
          GOSUB ClearHilightBytex
          PageColumn = PageColumn - 1
          FilePosition = FilePosition - 1
          CopyPositionEnd = CopyPositionEnd - 1
       ELSE
          IF CopyPositionStart - 1 <= CopyPositionPivot THEN
             PageColumn = PageColumn - 1
             FilePosition = FilePosition - 1
             CopyPositionStart = CopyPositionStart - 1
          ELSE
             EXIT SUB
          END IF
       END IF
    END IF
 END IF
 GOSUB DisplayHilightBytex
 GOSUB LocateHilightCursorx
 EXIT SUB

' move hilight area right.
ShiftRightKey:
 IF FilePosition = FileLength THEN
    GOSUB StoreHilight
 ELSE
    IF PageColumn = 20 THEN
       GOSUB StoreHilight
    ELSE
       IF PageColumn + 1 <= 20 THEN
          IF FilePosition + 1 <= FileLength THEN
             IF CopyPositionStart = 0 THEN
                GOSUB StoreHilight
             ELSE
                IF CopyPositionStart + 1 <= CopyPositionPivot THEN
                   GOSUB ClearHilightBytex
                   PageColumn = PageColumn + 1
                   FilePosition = FilePosition + 1
                   CopyPositionStart = CopyPositionStart + 1
                ELSE
                   IF CopyPositionEnd + 1 >= CopyPositionPivot THEN
                      PageColumn = PageColumn + 1
                      FilePosition = FilePosition + 1
                      CopyPositionEnd = CopyPositionEnd + 1
                   ELSE
                      EXIT SUB
                   END IF
                END IF
             END IF
          END IF
       END IF
    END IF
 END IF
 GOSUB DisplayHilightBytex
 GOSUB LocateHilightCursorx
 EXIT SUB

' move hilight area up.
ShiftUpKey:
 IF PageRow - 1 >= 1 THEN
    GOSUB StoreHilight
    IF CopyPositionEnd > CopyPositionPivot THEN
       IF CopyPositionEnd - 20 >= CopyPositionPivot THEN
          Temp# = FilePosition
          Temp2# = PageRow
          FOR FilePosition = CopyPositionEnd TO CopyPositionEnd - 20 STEP -1
             GOSUB CalculatePosition1x
             GOSUB ClearHilightBytex
          NEXT
          PageRow = Temp2# - 1
          FilePosition = Temp# - 20
          CopyPositionEnd = CopyPositionEnd - 20
          GOSUB CalculatePosition1x
          GOSUB DisplayHilightBytex
          GOSUB LocateHilightCursorx
       ELSE
          IF CopyPositionEnd - 20 < CopyPositionPivot THEN
             Temp# = FilePosition
             Temp2# = PageRow
             FOR FilePosition = CopyPositionPivot TO CopyPositionEnd
                GOSUB CalculatePosition1x
                GOSUB ClearHilightBytex
             NEXT
             CopyPositionStart = CopyPositionEnd - 20
             CopyPositionEnd = CopyPositionPivot
             FOR FilePosition = CopyPositionStart TO CopyPositionPivot 
                GOSUB CalculatePosition1x
                GOSUB DisplayHilightBytex
             NEXT
             PageRow = Temp2# - 1
             FilePosition = Temp# - 20
             GOSUB CalculatePosition1x
             GOSUB DisplayHilightBytex
             GOSUB LocateHilightCursorx
          END IF
       END IF
    ELSE
       Temp# = FilePosition
       Temp2# = PageRow
       FOR FilePosition = CopyPositionStart - 20 TO CopyPositionStart
          GOSUB CalculatePosition1x
          GOSUB DisplayHilightBytex
       NEXT
       CopyPositionStart = CopyPositionStart - 20
       PageRow = Temp2# - 1
       FilePosition = Temp# - 20
       GOSUB CalculatePosition1x
       GOSUB DisplayHilightBytex
       GOSUB LocateHilightCursorx
    END IF
 END IF
 EXIT SUB

' move hilight area down.
ShiftDownKey:
 IF PageRow + 1 <= 16 THEN
    IF FilePosition + 20 <= FileLength THEN
       GOSUB StoreHilight
       IF CopyPositionStart < CopyPositionPivot THEN
          Temp# = FilePosition
          Temp2# = PageRow
          IF CopyPositionStart + 20 < CopyPositionPivot THEN
             FOR FilePosition = CopyPositionStart TO CopyPositionStart + 20
                GOSUB CalculatePosition1x
                GOSUB ClearHilightBytex
             NEXT
             PageRow = Temp2# + 1
             FilePosition = Temp# + 20
             CopyPositionStart = CopyPositionStart + 20
             GOSUB DisplayHilightBytex
             GOSUB LocateHilightCursorx
          ELSE
             FOR FilePosition = CopyPositionStart TO CopyPositionPivot
                GOSUB CalculatePosition1x
                GOSUB ClearHilightBytex
             NEXT
             CopyPositionStart = CopyPositionPivot
             CopyPositionEnd = Temp# + 20
             FOR FilePosition = CopyPositionPivot TO CopyPositionEnd
                GOSUB CalculatePosition1x
                GOSUB DisplayHilightBytex
             NEXT
             PageRow = Temp2# + 1
             FilePosition = CopyPositionEnd
             GOSUB DisplayHilightBytex
             GOSUB LocateHilightCursorx
          END IF
       ELSE
          IF CopyPositionEnd >= CopyPositionPivot THEN
             Temp# = FilePosition
             Temp2# = PageRow
             FOR FilePosition = CopyPositionEnd TO CopyPositionEnd + 20
                GOSUB CalculatePosition1x
                GOSUB DisplayHilightBytex
             NEXT
             PageRow = Temp2# + 1
             FilePosition = Temp# + 20
             CopyPositionEnd = CopyPositionEnd + 20
             GOSUB DisplayHilightBytex
             GOSUB LocateHilightCursorx
          END IF
       END IF
    END IF
 END IF
 EXIT SUB

' move hilight area up one page.
ShiftPageUpKey:
 ' check there is a page to move up.
 IF FilePosition - 320 >= 1 THEN
    GOSUB StoreHilight
    ' move copypositionstart up one page.
    IF CopyPositionEnd = CopyPositionPivot THEN
       FilePage = FilePage - 1
       FilePosition = FilePosition - 320
       CopyPositionStart = CopyPositionStart - 320
    ELSE
       IF CopyPositionEnd > CopyPositionPivot THEN
          ' move copypositionend up one page before pivot.
          IF CopyPositionEnd - 320 < CopyPositionPivot THEN
             FilePage = FilePage - 1
             FilePosition = FilePosition - 320
             CopyPositionStart = FilePosition
             CopyPositionEnd = CopyPositionPivot
          ELSE
             ' move copypositionend up one page.
             IF CopyPositionEnd - 320 >= CopyPositionPivot THEN
                FilePage = FilePage - 1
                FilePosition = FilePosition - 320
                CopyPositionEnd = CopyPositionEnd - 320
             END IF
          END IF
       END IF
    END IF
 ELSE
    ' check there is a first position on the first page to move up to.
    IF FilePosition > 1 THEN
       GOSUB StoreHilight
       PageRow = 1
       PageColumn = 1
       FilePosition = 1
       CopyPositionStart = 1
       CopyPositionEnd = CopyPositionPivot
    ELSE
       EXIT SUB
    END IF
 END IF
 GOSUB CalculatePosition1x
 GOSUB DisplayHexPagex
 Reset1 = 0
 GOSUB ResetBytex
 GOSUB DisplayHilightBytex
 GOSUB LocateHilightCursorx
 EXIT SUB

' move hilight area down one page.
ShiftPageDownKey:
 ' check there is a page to move down.
 IF FilePosition + 320 <= FileLength THEN
    GOSUB StoreHilight
    ' move copypositionend down one page.
    IF CopyPositionStart = CopyPositionPivot THEN
       FilePage = FilePage + 1
       FilePosition = FilePosition + 320
       CopyPositionEnd = CopyPositionEnd + 320
    ELSE
       IF CopyPositionStart < CopyPositionPivot THEN
          ' move copypositionstart down one page after pivot.
          IF CopyPositionStart + 320 <= CopyPositionPivot THEN
             FilePage = FilePage + 1
             FilePosition = FilePosition + 320
             CopyPositionStart = FilePosition
             CopyPositionEnd = CopyPositionPivot
          ELSE
             ' move copypositionstart down one page.
             IF CopyPositionStart + 320 > CopyPositionPivot THEN
                FilePage = FilePage + 1
                FilePosition = FilePosition + 320
                CopyPositionStart = CopyPositionPivot
                CopyPositionEnd = FilePosition
             END IF
          END IF
       END IF
    END IF
 ELSE
    ' check there is a last position on the last page to move down to.
    IF FilePosition < FileLength THEN
       GOSUB StoreHilight
       FilePosition = FileLength
       CopyPositionStart = CopyPositionPivot
       CopyPositionEnd = FileLength
    ELSE
       EXIT SUB
    END IF
 END IF
 GOSUB CalculatePosition1x
 GOSUB DisplayHexPagex
 Reset1 = 0
 GOSUB ResetBytex
 GOSUB DisplayHilightBytex
 GOSUB LocateHilightCursorx
 EXIT SUB

' move hilight area to end of line.
ShiftEndKey:
 ' set to column 20
 IF PageColumn < 20 THEN
    ' check column 20 is positioned before end of file
    GOSUB StoreHilight
    IF FilePosition - PageColumn + 20 <= FileLength THEN
       Temp# = FilePosition
       Temp2# = PageColumn
       TempPosition3 = CopyPositionPivot
       GOSUB CalculatePosition2x
       IF PageRow = PageRow2 THEN
          IF Temp# - Temp2# + 20 >= CopyPositionPivot THEN
             FOR FilePosition = Temp# - Temp2# + 1 TO CopyPositionPivot
                GOSUB CalculatePosition1x
                GOSUB ClearHilightBytex
             NEXT
             FOR FilePosition = CopyPositionPivot TO Temp# - Temp2# + 20
                GOSUB CalculatePosition1x
                GOSUB DisplayHilightBytex
             NEXT
          END IF
          CopyPositionEnd = Temp# - Temp2# + 20
          CopyPositionStart = CopyPositionPivot
       ELSE
          IF Temp# - Temp2# + 20 >= CopyPositionPivot THEN
             FOR FilePosition = Temp# TO Temp# - Temp2# + 20
                GOSUB CalculatePosition1x
                GOSUB DisplayHilightBytex
             NEXT
             CopyPositionEnd = Temp# - Temp2# + 20
             CopyPositionStart = CopyPositionPivot
          ELSE
             FOR FilePosition = Temp# TO Temp# - Temp2# + 20
                GOSUB CalculatePosition1x
                GOSUB ClearHilightBytex
             NEXT
             CopyPositionStart = Temp# - Temp2# + 1
             CopyPositionEnd = CopyPositionPivot
          END IF
       END IF
       FilePosition = Temp# - Temp2# + 20
       PageColumn = 20
       GOSUB DisplayHilightBytex
       GOSUB LocateHilightCursorx
    ELSE
       ' last position is end of file.
       Temp# = FilePosition
       Temp2# = PageColumn
       TempPosition3 = CopyPositionPivot
       GOSUB CalculatePosition2x
       IF PageRow = PageRow2 THEN
          IF FileLength >= CopyPositionPivot THEN
             FOR FilePosition = Temp# - Temp2# + 1 TO CopyPositionPivot
                GOSUB CalculatePosition1x
                GOSUB ClearHilightBytex
             NEXT
             FOR FilePosition = CopyPositionPivot TO FileLength
                GOSUB CalculatePosition1x
                GOSUB DisplayHilightBytex
             NEXT
          END IF
       END IF
       FilePosition = FileLength
       CopyPositionEnd = FileLength
       CopyPositionStart = CopyPositionPivot
       GOSUB CalculatePosition1x
       GOSUB DisplayHilightBytex
       GOSUB LocateHilightCursorx
    END IF
 END IF
 EXIT SUB

' move hilight area to start of line.
ShiftHomeKey:
 ' set to column 1
 IF PageColumn > 1 THEN
    GOSUB StoreHilight
    Temp# = FilePosition
    Temp2# = PageColumn
    TempPosition3 = CopyPositionPivot
    GOSUB CalculatePosition2x
    IF PageRow = PageRow2 THEN
       IF Temp# - Temp2# + 1 <= CopyPositionPivot THEN
          FOR FilePosition = CopyPositionPivot TO Temp#
             GOSUB CalculatePosition1x
             GOSUB ClearHilightBytex
          NEXT
          FOR FilePosition = Temp# - Temp2# + 1 TO CopyPositionPivot
             GOSUB CalculatePosition1x
             GOSUB DisplayHilightBytex
          NEXT
       END IF
       CopyPositionStart = Temp# - Temp2# + 1
       CopyPositionEnd = CopyPositionPivot
    ELSE
       IF Temp# - Temp2# + 1 <= CopyPositionPivot THEN
          FOR FilePosition = Temp# - Temp2# + 1 TO Temp#
             GOSUB CalculatePosition1x
             GOSUB DisplayHilightBytex
          NEXT
          CopyPositionStart = Temp# - Temp2# + 1
          CopyPositionEnd = CopyPositionPivot
       ELSE
          FOR FilePosition = Temp# - Temp2# + 1 TO Temp#
             GOSUB CalculatePosition1x
             GOSUB ClearHilightBytex
          NEXT
          CopyPositionEnd = Temp# - Temp2# + 1
          CopyPositionStart = CopyPositionPivot
       END IF
    END IF
    FilePosition = Temp# - Temp2# + 1
    PageColumn = 1
    GOSUB DisplayHilightBytex
    GOSUB LocateHilightCursorx
 END IF
 EXIT SUB

' display current hilight byte being edited.
DisplayHilightBytex:
 IF FileDisplay2 THEN
    FileDisplay2 = 0
    GOSUB ClearStatusx
    PRINT "Editing file: "; RTRIM$(Filename); " ";
 END IF
 ' display byte.
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 COLOR 0, 7
 GOSUB BytePrintx
 COLOR 7, 0
 ' store the current byte value being edited.
 AsciiValue = ASC(FileByte)
 GOSUB DisplayPositionx
 RETURN

' clear current hilighted byte on screen.
ClearHilightBytex:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 COLOR White
 GOSUB BytePrintx
 RETURN

' store inital hilight start
StoreHilight:
 IF CopyPositionStart = 0 THEN
    CopyPositionStart = FilePosition
    CopyPositionPivot = FilePosition
    CopyPositionEnd = FilePosition
 END IF
 RETURN

' display screen of current page of hex/ascii values of file being edited.
DisplayHexPagex:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 COLOR White
 Row = False
 Column = False
 ColumnSpace = False
 FOR NextByte = (FilePage - 1) * 320 + 1 TO (FilePage - 1) * 320 + 320
    LOCATE Row + 4, Column + 6, 0
    IF NextByte <= FileLength THEN
       SeekPosition = NextByte
       GOSUB LseekFilex
       GOSUB ReadFilex
       FileByte = Buffer
       PRINT RIGHT$("00" + HEX$(ASC(FileByte)), 2);
    ELSE
       PRINT "  ";
    END IF
    ColumnSpace = ColumnSpace + 1
    IF ColumnSpace = 4 THEN
       PRINT " ";
       Column = Column + 1
       ColumnSpace = False
    END IF
    Column = Column + 2
    IF Column > 44 THEN
       Row = Row + 1
       Column = False
    END IF
 NEXT
 GOSUB RedrawRightWindowx
 RETURN

' redraw right window.
RedrawRightWindowx:
 IF CurrentWindow2 = False THEN
    GOSUB RedrawWindow1x
 ELSE
    GOSUB RedrawWindow2x
 END IF
 RETURN

' redraw right window of ascii values.
RedrawWindow1x:
 COLOR White
 Row = False
 Column = False
 FOR NextLine = 0 TO 15
    LOCATE NextLine + 4, 54, 0
    PRINT " ";
 NEXT
 FOR NextByte = (FilePage - 1) * 320 + 1 TO (FilePage - 1) * 320 + 320
    LOCATE Row + 4, Column + 55, 0
    IF NextByte <= FileLength THEN
       SeekPosition = NextByte
       GOSUB LseekFilex
       GOSUB ReadFilex
       FileByte = Buffer
       ByteValue = ASC(FileByte)
       ' skip unprintable characters
       SELECT CASE ByteValue
       CASE 0, 7, 9 TO 13, 28 TO 32
          PRINT ".";
       CASE ELSE
          PRINT FileByte;
       END SELECT
    ELSE
       PRINT " ";
    END IF
    Column = Column + 1
    IF Column > 19 THEN
       Row = Row + 1
       Column = False
    END IF
 NEXT
 RETURN

' redraw right window of hex ranges.
RedrawWindow2x:
 Row = False
 FOR NextLine = (FilePage - 1) * 320 + 1 TO (FilePage - 1) * 320 + 320 STEP 20
    LOCATE Row + 4, 54, 0
    Row = Row + 1
    IF NextLine <= FileLength THEN
       IF NextLine + 19 <= FileLength THEN
          COLOR White
          PRINT "x"; RIGHT$("00000000" + HEX$(NextLine - 1), 8);
          COLOR Yellow
          PRINT " - ";
          COLOR White
          PRINT "x"; RIGHT$("00000000" + HEX$(NextLine + 19 - 1), 8);
       ELSE
          COLOR White
          PRINT "x"; RIGHT$("00000000" + HEX$(NextLine - 1), 8);
          COLOR Yellow
          PRINT " - ";
          COLOR White
          PRINT "x"; RIGHT$("00000000" + HEX$(FileLength - 1), 8);
       END IF
    ELSE
       PRINT SPACE$(22);
    END IF
 NEXT
 RETURN

' calculate screen page number, row, and column, given variable.
CalculatePosition2x:
 IF TempPosition3 = 0 THEN
    FilePage2 = 0
    PageRow2 = 0
    PageColumn2 = 0
 ELSE
    FilePage2 = INT((TempPosition3 - 1) / 320) + 1
    PageRow2 = INT((TempPosition3 - (FilePage2 - 1) * 320 - 1) / 20) + 1
    PageColumn2 = INT((TempPosition3 - (FilePage2 - 1) * 320) - (PageRow2 - 1) * 20)
 END IF
 RETURN

REM Mouse hilighting routines:

DragMouse:
 ' check mouse position in screen editing area.
 IF Mouse.Row > 3 AND Mouse.Row < 20 THEN
    IF Mouse.Column >= 6 AND Mouse.Column <= 49 THEN
       ' check initial click is inside editing area.
       IF CopyStart = 0 THEN
          EXIT SUB
       END IF
       ' check screen character.
       VarX = SCREEN(Mouse.Row, Mouse.Column)
       ' check space.
       IF VarX = 32 THEN
          EXIT SUB
       END IF
       ' check new position.
       IF VarX <> 32 THEN
          ' calculate new position.
          GOSUB CalculatePosition3x
          ' initialize copy area.
          IF CopyPositionStart = 0 THEN
             CopyPositionStart = CopyStart
             CopyPositionPivot = CopyStart
             CopyPositionEnd = CopyStart
          END IF
          CALL MouseFunction(HideMouse, 0)
          ' check initial click area.
          IF NewPosition = CopyPositionPivot THEN
             FOR TempPosition3 = CopyPositionStart TO CopyPositionEnd
                GOSUB CalculatePosition2x
                IF FilePage2 = FilePage THEN
                   FilePosition = TempPosition3
                   GOSUB CalculatePosition1x
                   COLOR White
                   GOSUB BytePrintx
                END IF
             NEXT
             CopyPositionStart = NewPosition
             CopyPositionEnd = NewPosition
             FilePosition = CopyPositionPivot
             GOSUB CalculatePosition1x
             COLOR 0, 7
             GOSUB BytePrintx
             COLOR 7, 0
          END IF
          ' check backwards wrap.
          IF NewPosition < CopyPositionPivot THEN
             IF CopyPositionEnd > CopyPositionPivot THEN
                FOR TempPosition3 = CopyPositionPivot TO CopyPositionEnd
                   GOSUB CalculatePosition2x
                   IF FilePage2 = FilePage THEN
                      FilePosition = TempPosition3
                      GOSUB CalculatePosition1x
                      COLOR White
                      GOSUB BytePrintx
                   END IF
                NEXT
             END IF
             IF NewPosition > CopyPositionStart THEN
                FOR TempPosition3 = CopyPositionStart TO NewPosition
                   GOSUB CalculatePosition2x
                   IF FilePage2 = FilePage THEN
                      FilePosition = TempPosition3
                      GOSUB CalculatePosition1x
                      COLOR White
                      GOSUB BytePrintx
                   END IF
                NEXT
             END IF
             CopyPositionStart = NewPosition
             CopyPositionEnd = CopyPositionPivot
             FOR TempPosition3 = CopyPositionStart TO CopyPositionPivot
                GOSUB CalculatePosition2x
                IF FilePage2 = FilePage THEN
                   FilePosition = TempPosition3
                   GOSUB CalculatePosition1x
                   COLOR 0, 7
                   GOSUB BytePrintx
                END IF
             NEXT
             COLOR 7, 0
          END IF
          ' check forwards wrap.
          IF NewPosition > CopyPositionPivot THEN
             IF CopyPositionStart < CopyPositionPivot THEN
                FOR TempPosition3 = CopyPositionStart TO CopyPositionPivot
                   GOSUB CalculatePosition2x
                   IF FilePage2 = FilePage THEN
                      FilePosition = TempPosition3
                      GOSUB CalculatePosition1x
                      COLOR White
                      GOSUB BytePrintx
                   END IF
                NEXT
             END IF
             IF NewPosition < CopyPositionEnd THEN
                FOR TempPosition3 = NewPosition TO CopyPositionEnd
                   GOSUB CalculatePosition2x
                   IF FilePage2 = FilePage THEN
                      FilePosition = TempPosition3
                      GOSUB CalculatePosition1x
                      COLOR White
                      GOSUB BytePrintx
                   END IF
                NEXT
             END IF
             CopyPositionStart = CopyPositionPivot
             CopyPositionEnd = NewPosition
             FOR TempPosition3 = CopyPositionPivot TO CopyPositionEnd
                GOSUB CalculatePosition2x
                IF FilePage2 = FilePage THEN
                   FilePosition = TempPosition3
                   GOSUB CalculatePosition1x
                   COLOR 0, 7
                   GOSUB BytePrintx
                END IF
             NEXT
             COLOR 7, 0
          END IF
          ' get new position and locate there.
          FilePosition = NewPosition
          GOSUB CalculatePosition1x
          COLOR 0, 7
          GOSUB BytePrintx
          COLOR 7, 0
          GOSUB DisplayPositionx
          GOSUB LocateCursorx
          CALL MouseFunction(ShowMouse, 0)
       END IF
    END IF
 END IF
TopProgram1:
 EXIT SUB

REM this variable must be set before a call to dragmouse functions:
'   store mouse timing.
' MouseTime = 1!

' move hilight area up one page.
DragMousePageUp:
 ' check initial click is inside editing area.
 IF CopyStart = 0 THEN
    EXIT SUB
 END IF
 ' check scrolling interval.
 IF Time1 = 0! THEN
    Time1 = TimeNow!
 END IF
 IF TimeElapsed(Time1, MouseTime) = 0 THEN
    EXIT SUB
 END IF
 Time1 = TimeNow!
 ' check drag variables.
 IF CopyPositionEnd = 0 THEN
    CopyPositionEnd = CopyStart
 END IF
 IF CopyPositionPivot = 0 THEN
    CopyPositionPivot = CopyPositionEnd
 END IF
 IF CopyPositionStart = 0 THEN
    CopyPositionStart = (Filepage - 1) * 320 + 1
 END IF
 ' check there is a page to move up.
 IF FilePage - 1 >= 1 THEN
    ' move copypositionstart up one page.
    FilePage = FilePage - 1
    FilePosition = (FilePage - 1) * 320 + 1
    CopyPositionStart = FilePosition
    GOSUB CalculatePosition1x
    GOSUB DisplayHexPagex
    Reset1 = 0
    GOSUB ResetBytex
    GOSUB DisplayHilightBytex
    GOSUB LocateHilightCursorx
 END IF
 EXIT SUB

' move hilight area down one page.
DragMousePageDown:
 ' check initial click is inside editing area.
 IF CopyStart = 0 THEN
    EXIT SUB
 END IF
 ' check scrolling interval.
 IF Time1 = 0! THEN
    Time1 = TimeNow!
 END IF
 IF TimeElapsed(Time1, MouseTime) = 0 THEN
    EXIT SUB
 END IF
 Time1 = TimeNow!
 ' check drag variables.
 IF CopyPositionStart = 0 THEN
    CopyPositionStart = CopyStart
 END IF
 IF CopyPositionPivot = 0 THEN
    CopyPositionPivot = CopyPositionStart
 END IF
 IF CopyPositionEnd = 0 THEN
    CopyPositionEnd = (Filepage - 1) * 320 + 320
 END IF
 ' check there is a page to move down.
 FilePage2 = INT((FileLength - 1) / 320) + 1
 IF FilePage + 1 <= FilePage2 THEN
    ' move copypositionend down one page.
    FilePage = FilePage + 1
    FilePosition = (FilePage - 1) * 320 + 320
    IF FilePosition > FileLength THEN
       FilePosition = FileLength
    END IF
    CopyPositionEnd = FilePosition
    GOSUB CalculatePosition1x
    GOSUB DisplayHexPagex
    Reset1 = 0
    GOSUB ResetBytex
    GOSUB DisplayHilightBytex
    GOSUB LocateHilightCursorx
 END IF
 EXIT SUB

' read 1 byte from file.
ReadFilex:
 InregsX.AX = &H3F00
 InregsX.BX = Handle
 InregsX.CX = 1
 InregsX.DS = VARSEG(Buffer)
 InregsX.DX = VARPTR(Buffer)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' position pointer in file.
LseekFilex:
 ' calculate high/low seek position.
 SeekPosition2 = SeekPosition - 1
 High = INT(SeekPosition2 / 65536)
 Low = SeekPosition2 AND 65535
 ' seek position in file.
 InregsX.AX = &H4200
 InregsX.BX = Handle
 InregsX.CX = INT(VAL("&H" + HEX$(High)))
 InregsX.DX = INT(VAL("&H" + HEX$(Low)))
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' calculate the spaces between hex value groups.
CalculateColumnx:
 SELECT CASE PageColumn
 CASE 1 TO 4
    Column = (PageColumn - 1) * 2 + 1
 CASE 5 TO 8
    Column = (PageColumn - 1) * 2 + 2
 CASE 9 TO 12
    Column = (PageColumn - 1) * 2 + 3
 CASE 13 TO 16
    Column = (PageColumn - 1) * 2 + 4
 CASE 17 TO 20
    Column = (PageColumn - 1) * 2 + 5
 END SELECT
 RETURN

' print the byte.
BytePrintx:
 SeekPosition = FilePosition
 GOSUB LseekFilex
 GOSUB ReadFilex
 FileByte = Buffer
 ByteValue = ASC(FileByte)
 GOSUB CalculateColumnx
 ' display hex byte.
 LOCATE PageRow + 3, Column + 5, 0
 PRINT RIGHT$("00" + HEX$(ByteValue), 2);
 ' check right window toggled.
 IF CurrentWindow2 = False THEN
    ' display ascii byte.
    LOCATE PageRow + 3, PageColumn + 54, 0
    ' skip unprintable characters.
    SELECT CASE ByteValue
    CASE 0, 7, 9 TO 13, 28 TO 32
       PRINT ".";
    CASE ELSE
       PRINT FileByte;
    END SELECT
 END IF
 RETURN

' draw byte area.
ResetBytex:
 Temp# = FilePosition
 Temp2# = FilePage
 Var1# = CopyPositionStart
 Var2# = CopyPositionEnd
 ' reset hilighted positions.
 IF Var1# < (Temp2# - 1) * 320 + 1 THEN
    Var1# = (Temp2# - 1) * 320 + 1
 END IF
 IF Var2# > (Temp2# - 1) * 320 + 320 THEN
    Var2# = (Temp2# - 1) * 320 + 320
 END IF
 IF Var2# > FileLength THEN
    Var2# = FileLength
 END IF
 ' draw the bytes.
 FOR FilePosition = Var1# TO Var2#
    GOSUB CalculatePosition1x
    IF Reset1 THEN
       COLOR White
    ELSE
       COLOR 0, 7
    END IF
    GOSUB BytePrintx
 NEXT
 COLOR 7, 0
 FilePosition = Temp#
 GOSUB CalculatePosition1x
 RETURN

' display info on file being edited.
DisplayPositionx:
 COLOR Yellow
 StringLength = LEN("Editing file: " + RTRIM$(Filename) + " ") + 4
 LOCATE 2, StringLength, 0
 PRINT SPACE$(77 - StringLength);
 LOCATE 2, StringLength, 0
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 IF CurrentWindow2 = False THEN
    PRINT "(Position:"; STR$(FilePosition - 1); ") ";
 ELSE
    PRINT "(Position: "; RIGHT$("00000000" + HEX$(FilePosition - 1), 8); "H) ";
 END IF
 PRINT "(Ascii:"; STR$(AsciiValue); ") ";
 PRINT "(Hex: "; RIGHT$("00" + HEX$(AsciiValue), 2); "H)";
 GOSUB LocateCursor2x
 RETURN

' clear top status area
ClearStatusx:
 COLOR Yellow
 LOCATE 2, 4
 PRINT SPACE$(74);
 LOCATE 2, 4
 RETURN

' select cursor on window.
LocateCursor2x:
 IF CopyPositionStart = 0 THEN
    GOSUB LocateCursorx
 ELSE
    GOSUB LocateHilightCursorx
 END IF
 RETURN

' locate cursor on window.
LocateCursorx:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 IF CurrentWindow = False THEN
    LOCATE PageRow + 3, Column + 5, 1
 ELSE
    LOCATE PageRow + 3, PageColumn + 54, 1
 END IF
 RETURN

' locate cursor on window for hilighted area.
LocateHilightCursorx:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 IF CurrentWindow = False THEN
    LOCATE PageRow + 3, Column + 7, 1
 ELSE
    LOCATE PageRow + 3, PageColumn + 55, 1
 END IF
 RETURN

' calculate current screen page number, row, and column, given file position.
CalculatePosition1x:
 FilePage = INT((FilePosition - 1) / 320) + 1
 PageRow = INT((FilePosition - (FilePage - 1) * 320 - 1) / 20) + 1
 PageColumn = INT((FilePosition - (FilePage - 1) * 320) - (PageRow - 1) * 20)
 RETURN

' calculate file position given mouse row, and column of current page.
CalculatePosition3x:
 NewPosition = (FilePage - 1) * 320 + (Mouse.Row - 4) * 20
 SELECT CASE Mouse.Column
 CASE 6, 7
    NewPosition = NewPosition + 1
 CASE 8, 9
    NewPosition = NewPosition + 2
 CASE 10, 11
    NewPosition = NewPosition + 3
 CASE 12, 13
    NewPosition = NewPosition + 4
 CASE 15, 16
    NewPosition = NewPosition + 5
 CASE 17, 18
    NewPosition = NewPosition + 6
 CASE 19, 20
    NewPosition = NewPosition + 7
 CASE 21, 22
    NewPosition = NewPosition + 8
 CASE 24, 25
    NewPosition = NewPosition + 9
 CASE 26, 27
    NewPosition = NewPosition + 10
 CASE 28, 29
    NewPosition = NewPosition + 11
 CASE 30, 31
    NewPosition = NewPosition + 12
 CASE 33, 34
    NewPosition = NewPosition + 13
 CASE 35, 36
    NewPosition = NewPosition + 14
 CASE 37, 38
    NewPosition = NewPosition + 15
 CASE 39, 40
    NewPosition = NewPosition + 16
 CASE 42, 43
    NewPosition = NewPosition + 17
 CASE 44, 45
    NewPosition = NewPosition + 18
 CASE 46, 47
    NewPosition = NewPosition + 19
 CASE 48, 49
    NewPosition = NewPosition + 20
 END SELECT
 RETURN

' critical error trap.
Error.Routine2:
 IF ScreenDrawn THEN
    CLS
 END IF
 ScreenDrawn = False
 COLOR White
 PRINT "Hex Editor Viewfile " + Version + " " + Release + " critical error trap:"
 COLOR Yellow
 ErrorTrap = ERR
 SELECT CASE ERR
 CASE 5
    PRINT "Syntax error." ' should not happen.
 CASE 6
    PRINT "Overflow."
 CASE 9
    PRINT "Subscript out of range."
 CASE 14
    PRINT "Out of string space."
 CASE 24
    PRINT "Device timeout."
 CASE 25
    PRINT "Device fault."
 CASE 27
    PRINT "Out of paper."
 CASE 52
    PRINT "Bad file name or number."
 CASE 53
    PRINT "File not found."
 CASE 54
    PRINT "Bad file mode."
 CASE 55
    PRINT "File already open."
 CASE 57
    PRINT "Device I/O error."
 CASE 58
    PRINT "File already exists."
 CASE 59
    PRINT "Bad record length."
 CASE 61
    PRINT "Disk full."
 CASE 62
    PRINT "Input past eof."
 CASE 63
    PRINT "Bad record length."
 CASE 64
    PRINT "Bad filename."
 CASE 67
    PRINT "Not enough file handles."
 CASE 68
    PRINT "Device unavailable."
 CASE 70
    PRINT "Permission denied."
 CASE 71
    PRINT "Disk not ready."
 CASE 72
    PRINT "Disk-media error."
 CASE 75
    PRINT "Path/File access error."
 CASE 76
    PRINT "Pathname not found."
 CASE 81
    PRINT "Invalid name."
 CASE ELSE
    PRINT "Untrapped error" + STR$(ERR) + "."
 END SELECT
 ' display error prompt.
 COLOR Green
 PRINT "Press R(etry), C(ontinue), Q(uit):";
 ' get keypress.
 DO
    ErrorRespond$ = Nul
    DO
       ErrorRespond$ = INKEY$
       IF LEN(ErrorRespond$) THEN
          EXIT DO
       END IF
    LOOP
    ' parse key.
    SELECT CASE LCASE$(ErrorRespond$)
    CASE "r"
       PRINT "r"
       RESUME TopProgram1
    CASE "c"
       PRINT "c"
       RESUME TopProgram1
    CASE "q"
       PRINT "q"
       RESUME TopProgram1
    END SELECT
 LOOP
END SUB
