REM Program: Hex Editor v3.6a, Module 4 of 4, PD 2003.
REM Author: Erik Jon Oredson AS. Csci
REM Release: 04/01/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.Routine

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

AddMarker:
 TempPosition3 = FilePosition

AddMarkerValue:
 IF MarkerCount < 16384 THEN
    IF MarkerCount + 1 > MaxMarkers THEN
       MaxMarkers = MaxMarkers + 1024
       REDIM PRESERVE Markers(9, 1 TO MaxMarkers) AS DOUBLE
    END IF
    MarkerCount = MarkerCount + 1
    Markers(CurrentFile, MarkerCount) = TempPosition3
    CurrentMarker = MarkerCount
 END IF
 RETURN

AddSpecificMarker:
 GOSUB ClearStatus
 PRINT "Enter file position";
 INPUT ByteString$
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    TempPosition3 = INT(VAL(ByteString$))
    IF TempPosition3 >= 1 AND TempPosition3 <= FileLength THEN
       GOSUB AddMarkerValue
       GOSUB LocateCursor2
       GOSUB DisplayFilename
       RETURN
    END IF
 END IF
 StatusMessage = "Invalid file position."
 GOSUB DisplayStatus2
 RETURN

DeleteMarker:
 IF CurrentMarker >= 1 AND CurrentMarker <= MarkerCount THEN
    Markers(CurrentFile, CurrentMarker) = False
 END IF
 RETURN

DeleteSpecificMarker:
 GOSUB ClearStatus
 PRINT "Enter marker number";
 INPUT ByteString$
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    TempPosition = INT(VAL(ByteString$))
    IF TempPosition >= 1 AND TempPosition <= MarkerCount THEN
       Markers(CurrentFile, TempPosition) = False
       GOSUB LocateCursor2
       GOSUB DisplayFilename
       RETURN
    END IF
 END IF
 StatusMessage = "Invalid marker number."
 GOSUB DisplayStatus2
 RETURN

JumpPreviousMarker:
 NewMarker = CurrentMarker
 DO
    IF NewMarker <= 1& THEN
       RETURN
    END IF
    NewMarker = NewMarker - 1&
    IF Markers(CurrentFile, NewMarker) > 0# THEN
       CurrentMarker = NewMarker
       EXIT DO
    END IF
 LOOP
 GOSUB JumpCurrentMarker
 RETURN

JumpNextMarker:
 NewMarker = CurrentMarker
 DO
    IF NewMarker >= MarkerCount THEN
       RETURN
    END IF
    NewMarker = NewMarker + 1&
    IF Markers(CurrentFile, NewMarker) > 0# THEN
       CurrentMarker = NewMarker
       EXIT DO
    END IF
 LOOP
 GOSUB JumpCurrentMarker
 RETURN

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

JumpCurrentMarker:
 IF CurrentMarker >= 1 AND CurrentMarker <= MarkerCount THEN
    TempPosition = Markers(CurrentFile, 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
 RETURN

ListAllMarkers:
 CALL DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "Marker list:";
 PageCount = False
 CountFlag = False
 Var2 = 2
 FOR TempPosition = 1 TO MarkerCount
    IF Markers(CurrentFile, TempPosition) > 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
 GOSUB RedrawScreen
 RETURN

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

ListMarkersOfValue:
 GOSUB ClearStatus
 PRINT "Enter marker value";
 INPUT ByteString$
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    GOSUB CheckHexValue
    IF ValidHexValue = False THEN
       RETURN
    END IF
    TempPosition1 = HexValue
 ELSE
    GOSUB CheckASCIIValue
    IF ValidASCIIValue = False THEN
       RETURN
    END IF
    TempPosition1 = ASCIIValue3
 END IF
 CALL DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "Marker list:"
 PageCount = False
 CountFlag = False
 Var2 = 2
 FOR TempPosition = 1 TO MarkerCount
    IF Markers(CurrentFile, TempPosition) = 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
 GOSUB RedrawScreen
 RETURN

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
    TempPosition1 = INT(VAL(ByteString$))
    IF TempPosition1 >= 1 AND TempPosition1 <= 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
          TempPosition2 = INT(VAL(ByteString$))
          IF TempPosition2 >= 1 AND TempPosition2 <= MarkerCount THEN
             CALL DisplayScreen2
             COLOR White
             LOCATE 2, 2
             PRINT "Marker list:"
             PageCount = False
             CountFlag = False
             Var2 = 2
             FOR TempPosition = TempPosition1 TO TempPosition2
                IF Markers(CurrentFile, TempPosition) > 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
             GOSUB RedrawScreen
             RETURN
          END IF
       END IF
    END IF
 END IF
 StatusMessage = "Invalid marker range."
 GOSUB DisplayStatus2
 RETURN

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
    TempPosition1 = INT(VAL(ByteString$))
    IF TempPosition1 >= 1 AND TempPosition1 <= 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
          TempPosition2 = INT(VAL(ByteString$))
          IF TempPosition2 >= 1 AND TempPosition2 <= MarkerCount THEN
             FOR TempPosition = TempPosition1 TO TempPosition2
                Markers(CurrentFile, TempPosition) = False
             NEXT
             StatusMessage = "Range of markers deleted."
             GOSUB DisplayStatus2
             RETURN
          END IF
       END IF
    END IF
 END IF
 StatusMessage = "Invalid marker range."
 GOSUB DisplayStatus2
 RETURN

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

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
          RETURN
       END IF
       TempPosition1 = HexValue
    ELSE
       GOSUB CheckASCIIValue
       IF ValidASCIIValue = False THEN
          RETURN
       END IF
       TempPosition1 = ASCIIValue3
    END IF
    FOR TempPosition = 1 TO MarkerCount
       IF Markers(CurrentFile, TempPosition) = TempPosition1 THEN
          Markers(CurrentFile, TempPosition) = False
       END IF
    NEXT
 END IF
 StatusMessage = "All markers of value deleted."
 GOSUB DisplayStatus2
 RETURN


' 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

' redraw editing screen
RedrawScreen:
 ScreenDrawn = True
 CALL DisplayScreen
 GOSUB DisplayFilename
 GOSUB DisplayHexPage
 GOSUB DisplayPageByte
 IF FileDisplay = 0 THEN
    GOSUB DisplayFilename3
 END IF
 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.
ClearHilightPageByte:
 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:
 IF FileDisplay2 THEN
    FileDisplay2 = 0
    GOSUB ClearStatus
    PRINT "Editing file: "; RTRIM$(Filename); " ";
 END IF
 ' display byte.
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 IF CopyPositionStart > 0# THEN
    GOSUB ResetHilightBytes
 END IF
 COLOR Yellow
 GOSUB BytePrint
 ' store the current byte value being edited.
 AsciiValue = ASC(FileByte)
 GOSUB DisplayPosition
 RETURN

' display 64-byte name of file being editied.
DisplayFilename3:
 Z$ = ShortFilename
 GOSUB Deconcatenate
 Var = INT((80 - (LEN(RTRIM$(Z$))) + 2) / 2) - 1
 LOCATE 3, Var
 COLOR 0, 7
 PRINT " ";Z$;" ";
 COLOR 7, 0
 GOSUB LocateCursor2
 RETURN

' deconcatenate long path with \..\ characters
Deconcatenate:
 DO
    IF LEN(Z$) > 64 THEN
       V1 = INSTR(Z$, "\")
       IF V1 > 0 THEN
          DO
             IF MID$(Z$, V1, 4) = "\..\" THEN
                V1 = INSTR(V1 + 1, Z$, "\")
             ELSE
                EXIT DO
             END IF
          LOOP
          V2 = INSTR(V1 + 1, Z$, "\")
          IF V2 > 0 THEN
             Z$ = LEFT$(Z$, V1) + ".." + MID$(Z$, V2)
          ELSE
             EXIT DO
          END IF
       END IF
    ELSE
       EXIT DO
    END IF
 LOOP
 DO
    V = INSTR(Z$, "\..\..\")
    IF V > 0 THEN
       Z$ = LEFT$(Z$, V - 1) + "\..\" + MID$(Z$, V + 7)
    ELSE
       EXIT DO
    END IF
 LOOP
 IF LEN(Z$) > 64 THEN
    Z$ = LEFT$(Z$, 64)
 END IF
 RETURN

' display info on file being edited.
DisplayPosition:
 COLOR Yellow
 StringLength = LEN("Editing file: " + RTRIM$(Filename) + " ") + 4
 LOCATE 2, StringLength, 0
 PRINT SPACE$(76 - StringLength);
 LOCATE 2, StringLength, 0
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 IF CurrentWindow2 = False THEN
    PRINT "(Position:"; STR$(FilePosition); ") ";
 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 3.
FormatPosition3:
 PRINT "Marker#"; MID$(STR$(TempPosition), 2); " Position:";
 TempPosition3 = Markers(CurrentFile, TempPosition)
 IF CurrentWindow2 = False THEN
    PRINT MID$(STR$(TempPosition3), 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

' displays cell position in string format type 2.
FormatPosition2:
 PRINT "Marker#"; MID$(STR$(TempPosition), 2); ":";
 TempPosition3 = Markers(CurrentFile, TempPosition)
 IF CurrentWindow2 = False THEN
    PRINT MID$(STR$(TempPosition3), 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

' 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.Routine:
 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
    CASE "c"
       PRINT "c"
       RESUME NEXT
    CASE "q"
       PRINT "q"
       RESUME TopProgram
    END SELECT
 LOOP

' anything goes here stops program.
StopProgram:
 COLOR Plain
 END
END SUB
