REM Program: Hex Editor for DOS v3.2a Module 1 of 2.
REM Author: Erik Jon Oredson AS. Csci
REM Release: 03/31/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'

' declare error trap.
ON ERROR GOTO Error.Routine

' increase stack.
STACK 8192

' store quote.
Quote = CHR$(34)

' declare filename variables.
ConfigFile = "hexedit.cfg"
DumpFile = "hexedit.dmp"

' dimension undo arrays.
REDIM UndoByte(1 TO 1024) AS INTEGER
REDIM UndoPosition(1 TO 1024) AS DOUBLE

' dimension marker array.
REDIM Markers(1 TO 1024) AS DOUBLE

' dimension menu area arrays.
REDIM Area1(22, 22) AS INTEGER
REDIM Area2(22, 22) AS INTEGER

' check for screen row variable.
IF ENVIRON$("HEXSCREEN") <> Nul THEN
   ScreenRow = True
END IF

' check for heap sort variable.
IF ENVIRON$("HEXSORT") <> Nul THEN
   HeapSortOff = True
END IF

' store ascii characters.
Hline = 205
Vline = 186
ULcorner = 201
URcorner = 187
LLcorner = 200
LRcorner = 188

' declare alternate characters.
IF ENVIRON$("HEXCHARS") <> Nul THEN
   Hline = 45
   Vline = 124
   ULcorner = 43
   URcorner = 43
   LLcorner = 43
   LRcorner = 43
END IF

' store BASIC DTA.
GOSUB StoreDTA

' read config file.
CALL ReadConfigFile (FileName)
IF Filename <> "" THEN
   GOTO StartFile
END IF

' check command line.
SELECT CASE COMMAND$
CASE "/?", "-?"
   GOTO BootUsage
END SELECT

' read command line.
GOSUB ReadCommandLine

' check config filename.
IF CommandLine$ <> Nul THEN
   ' store command line.
   Filename = CommandLine$
   GOTO StartFile
END IF

' filename entry starts here.
BeginFile:

' call file menu box.
CALL Menu(Drive$, Directory$, FileSpec1$, FileSpec2$)

' check filename.
IF FileSpec2$ = Nul THEN
   GOTO BootUsage
END IF
IF Directory$ = "" THEN
   Filename = Drive$ + ":\" + FileSpec2$
ELSE
   Filename = Drive$ + ":\" + Directory$ + "\" + FileSpec2$
END IF

' filename entry loop starts here.
StartFile:

' init some functions
GOSUB CheckWindows
NextFile2:

' trim filename.
Filename = RTRIM$(Filename)
Filename = LTRIM$(Filename)
NextFile3:

' check long filename in quotes.
IF LEFT$(Filename, 1) = Quote THEN
   IF RIGHT$(Filename, 1) = Quote THEN
      Filename = MID$(Filename, 2)
      Filename = LEFT$(Filename, LEN(Filename) - 1)
   END IF
END IF
NextFile4:

' store filename.
ASCIIZ = Filename + CHR$(0)
NextFile5:

' conanicalize filename.
GOSUB ShortFilename
NextFile6:

' open/create file.
GOSUB OpenFile
NextFile7:

' check open error.
IF ValidFile = False THEN
   Filename = Nul
   GOTO BeginFile
END IF
NextFile8:

' check length of file.
GOSUB GetFileLength
NextFile9:

' create 1 byte file.
IF FileLength = False THEN
   GOSUB AppendFile
   GOSUB GetFileLength
END IF
NextFile10:

' store startup undo variables.
CurrentUndo = 0
MaxUndos = 1024

' store startup marker variables.
CurrentMarker = 0
MarkerCount = 0
MaxMarkers = 1024
NextFile11:

' store hex editing screen values.
FilePage = 1
FilePosition = 1
PageRow = 1
PageColumn = 1
NextFile12:

' initialize file buffer.
SeekPosition = 1
GOSUB LseekFile
GOSUB ReadFile
FileByte = Buffer
AsciiValue = ASC(FileByte)
NextFile13:

' main program loop starts here.
TopProgram:

' check mouse present.
GOSUB CheckMouse

' reset mouse activity.
IF Mouse.Present THEN
   ' clear mouse buttons.
   CALL MouseFunction(Button, 0)
   CALL MouseFunction(Button, 1)
   ' store mouse position.
   CALL MouseFunction(Position, 0)
   Mouse.X = OutregsX.DX / 8 + 1
   Mouse.Y = OutregsX.CX / 8 + 1
END IF

' setup editing screen.
Header = False
GOSUB RedrawScreen
NextFile14:

' reset mouse activity.
IF Mouse.Present THEN
   ' show mouse cursor.
   CALL MouseFunction(ShowMouse, 0)
END IF

' keyboard/mouse input loop starts here.
StartLoop:

' keyboard/mouse input loop.
DO
   ' check for keypress or mouse activity.
   CharInput$ = Nul
   DO
      ' call mouse subroutine.
      CALL MouseDriver

      ' check left mouse button.
      IF Mouse.Button1 THEN
         GOSUB MouseButton1
      END IF
      ' check right/middle mouse button.
      IF Mouse.Button2 OR Mouse.Button3 THEN
         GOSUB MouseButton2
      END IF
      ' check mouse position.
      IF Mouse.Row OR Mouse.Column THEN
         GOSUB MoveMouse
      END IF

      ' store keyboard buffer.
      CharInput$ = INKEY$
      IF LEN(CharInput$) THEN
         EXIT DO
      END IF

      ' release time slice.
      ' (speeds up mouse in windows).
      Var = ReleaseTime
   LOOP

   ' reset mouse activity.
   IF Mouse.Present THEN
      ' hide mouse cursor.
      CALL MouseFunction(HideMouse, 0)
   END IF

   ' parse the key.
   SELECT CASE LEN(CharInput$)
   CASE 1 ' single ascii key.
      SELECT CASE ASC(CharInput$)
      CASE 9 ' Tab
         IF FileLength THEN
            GOSUB TabWindow
         END IF
      CASE 13 ' Enter
         IF FileLength THEN
            GOSUB ChangeHexValue
         END IF
      CASE 27 ' Escape
         IF CurrentMenu <> 1 THEN
            ' remove hilight menu tab.
            COLOR White, Black
            GOSUB DisplayTab
         END IF
         CurrentMenu = 1
         CurrentMenuSelection = 1
         GOTO Menu2
      CASE 63 ' ?
         GOSUB DisplayInfo
      CASE 33 ' !
         GOSUB DisplayData
      CASE 126 ' ~
         GOSUB DisplayLengths
      CASE 49 ' 1
         IF FileLength THEN
            GOSUB AddMarker
         END IF
      CASE 50 ' 2
         IF FileLength THEN
            GOSUB AddSpecificMarker
         END IF
      CASE 51 ' 3
         IF FileLength THEN
            GOSUB DeleteMarker
         END IF
      CASE 52 ' 4
         IF FileLength THEN
            GOSUB DeleteSpecificMarker
         END IF
      CASE 53 ' 5
         IF FileLength THEN
            GOSUB JumpPreviousMarker
         END IF
      CASE 54 ' 6
         IF FileLength THEN
            GOSUB JumpCurrentMarker
         END IF
      CASE 55 ' 7
         IF FileLength THEN
            GOSUB JumpNextMarker
         END IF
      CASE 56 ' 8
         IF FileLength THEN
            GOSUB JumpSpecificMarker
         END IF
      CASE 57 ' 9
         IF FileLength THEN
            GOSUB ListAllMarkers
         END IF
      CASE 48 ' 0
         IF FileLength THEN
            GOSUB ListSpecificMarker
         END IF
      CASE 45 ' -
         IF FileLength THEN
            GOSUB ListMarkersOfValue
         END IF
      CASE 43 ' +
         IF FileLength THEN
            GOSUB ListMarkerRange
         END IF
      CASE 61 ' =
         IF FileLength THEN
            GOSUB DeleteMarkerRange
         END IF
      CASE 91 ' [
         IF FileLength THEN
            GOSUB DeleteAllMarkers
         END IF
      CASE 93 ' ]
         IF FileLength THEN
            GOSUB DeleteMarkersOfValue
         END IF
      CASE ELSE ' Unknown key.
         GOSUB HelpLine
      END SELECT
   CASE 2 ' extended ascii key.
      SELECT CASE ASC(RIGHT$(CharInput$, 1))
      CASE 0 ' Control-Break
         ' Note: Ctrl-Break clears keyboard buffer,
         '   places word 0000h in buffer, invokes INT 1B,
         '   and sets flag 80 hex at 0040h:0071h
         BEEP
      CASE 15 ' Shift-tab
         IF FileLength THEN
            GOSUB TabWindow
         END IF
      CASE 30 ' Alt-A
         IF FileLength THEN
            GOSUB AppendByte
         END IF
      CASE 46 ' Alt-C
         GOSUB ANSIChart
      CASE 32 ' Alt-D
         IF FileLength THEN
            GOSUB DumpScreen
         END IF
      CASE 18 ' Alt-E
         IF FileLength THEN
            GOSUB DumpHEXFile
         END IF
      CASE 33 ' Alt-F
         IF CurrentMenu <> 1 THEN
            ' remove hilight menu tab.
            COLOR White, Black
            GOSUB DisplayTab
         END IF
         CurrentMenu = 1
         CurrentMenuSelection = 1
         GOTO Menu2
      CASE 35 ' Alt-H
         GOSUB HEXChart
      CASE 36 ' Alt-J
         IF FileLength THEN
            GOSUB JumpByte
         END IF
      CASE 37 ' Alt-K
         IF FileLength THEN
            GOSUB SearchASCII
         END IF
      CASE 38 ' Alt-L
         IF FileLength THEN
            GOSUB JumpPage
         END IF
      CASE 50 ' Alt-M
         IF FileLength THEN
            GOSUB AppendASCII
         END IF
      CASE 49 ' Alt-N
         GOSUB NewFile
         GOTO BeginFile
      CASE 25 ' Alt-P
         IF FileLength THEN
            GOSUB PrintScreen
         END IF
      CASE 16 ' Alt-Q
         GOSUB HelpScreen
      CASE 19 ' Alt-R
         GOSUB RedrawScreen
      CASE 31 ' Alt-S
         IF FileLength THEN
            GOSUB SearchByte
         END IF
      CASE 20 ' Alt-T
         IF FileLength THEN
            GOSUB PrintFile
         END IF
      CASE 22 ' Alt-U
         IF FileLength THEN
            GOSUB UndoByte
         END IF
      CASE 45 ' Alt-X
         EXIT DO ' exit program.
      CASE 21 ' Alt-Y
         IF FileLength THEN
            GOSUB ToggleRight
         END IF
      CASE 44 ' Alt-Z
         IF FileLength THEN
            GOSUB UndoAll
         END IF
      CASE 82 ' Insert
         IF FileLength THEN
            GOSUB ChangeASCIIValues
         END IF
      CASE 83 ' Delete
         IF FileLength THEN
            GOSUB ChangeHEXValues
         END IF
      CASE 72 ' Up
         IF FileLength THEN
            GOSUB CursorUp
         END IF
      CASE 80 ' Down
         IF FileLength THEN
            GOSUB CursorDown
         END IF
      CASE 75 ' Left
         IF FileLength THEN
            GOSUB CursorLeft
         END IF
      CASE 77 ' Right
         IF FileLength THEN
            GOSUB CursorRight
         END IF
      CASE 73 ' PageUp
         IF FileLength THEN
            GOSUB PageUp
         END IF
      CASE 81 ' PageDown
         IF FileLength THEN
            GOSUB PageDown
         END IF
      CASE 79 ' End
         IF FileLength THEN
            GOSUB EndKey
         END IF
      CASE 71 ' Home
         IF FileLength THEN
            GOSUB HomeKey
         END IF
      CASE 115 ' Ctrl-Left
         IF FileLength THEN
            GOSUB CtrlLeftKey
         END IF
      CASE 116 ' Ctrl-Right
         IF FileLength THEN
            GOSUB CtrlRightKey
         END IF
      CASE 117 ' Ctrl-End
         IF FileLength THEN
            GOSUB CtrlEndKey
         END IF
      CASE 119 ' Ctrl-Home
         IF FileLength THEN
            GOSUB CtrlHomeKey
         END IF
      CASE ELSE ' Unknown key.
         GOSUB HelpLine
      END SELECT
   CASE ELSE ' Unknown key.
      BEEP
   END SELECT
   ' reset mouse activity.
   IF Mouse.Present THEN
      ' show mouse cursor.
      CALL MouseFunction(ShowMouse, 0)
      GOSUB RestorePosition
   END IF
LOOP
StopLabel:

' clear hilighted byte.
GOSUB ClearPageByte
GOSUB MoveOldPosition

' clear file menu tab.
IF CurrentMenu THEN
   COLOR White, Black
   GOSUB DisplayTab
END IF

' clear lower screen lines.
FOR Var = 21 TO 24
   LOCATE Var, 1, 1
   PRINT SPACE$(78);
NEXT
LOCATE 21, 1
COLOR Magenta
PRINT " "; CHR$(LLcorner); STRING$(75, Hline); CHR$(LRcorner);
COLOR Plain

' end of program.
GOSUB CloseFile
GOSUB ResetAttribute
COLOR Plain
GOTO StopProgram

' load new file.
NewFile:
 ' close file handle.
 GOSUB CloseFile
 ' reset file attribute.
 GOSUB ResetAttribute
 ' reset window.
 CLS
 CurrentWindow = False
 CurrentWindow2 = False
 ScreenDrawn = False
 LOCATE 24, 1, 1
 COLOR Plain
 ' reset variables.
 FilePage = 1
 FilePosition = 1
 PageRow = 1
 PageColumn = 1
 Buffer = CHR$(0)
 FileByte = CHR$(0)
 AsciiValue = False
 Filename = Nul
 FileLength = False
 FilePosition = False
 ' redimension all arrays.
 REDIM UndoByte(1 TO 1024) AS INTEGER
 REDIM UndoPosition(1 TO 1024) AS DOUBLE
 REDIM Markers(1 TO 1024) AS DOUBLE
 ' reset all array variables.
 CurrentUndo = 0
 MaxUndos = 1024
 MaxMarkers = 1024
 MarkerCount = 0
 CurrentMarker = 0
 RETURN

' display command line instructions.
BootUsage:
IF Header = False THEN
   COLOR White
   PRINT "Hex Editor " + Version + " " + Release + "."
END IF
COLOR Green
PRINT "Usage:"
COLOR Yellow
PRINT "   Hexedit <filename.ext>"
COLOR Green
PRINT "Where:"
COLOR Yellow
PRINT "   <filename.ext> is the file to edit. May include drive/pathname, and"
PRINT "   supports long filenames in quotes. Filename may be omitted."
COLOR Green
PRINT "Example:"
COLOR Yellow
PRINT "   Hexedit " + CHR$(34) + "\dos\page.com" + CHR$(34)
COLOR Green
PRINT "Menu Usage:"
COLOR Yellow
PRINT "   Navigate using cursors, selecting with <enter>, and switch windows"
PRINT "   with <tab>. F1 selects drive letter. F2 toggles sorting."
PRINT "   Exits with <escape>."
COLOR Green
PRINT "Editor Usage:"
COLOR Yellow
PRINT "   Enter editing functions or cursor movement through the hex values,"
PRINT "   while changing them with <enter>, <delete> or <insert> as you edit."
COLOR Plain
GOTO StopProgram

' main program editing screen.
DisplayScreen:
CLS
COLOR Magenta
PRINT " "; CHR$(ULcorner);
PRINT STRING$(3,HLine);
COLOR White
PRINT "File";
COLOR Magenta
PRINT STRING$(6,HLine);
COLOR White
PRINT "Charts";
COLOR Magenta
PRINT STRING$(4,HLine);
COLOR White
PRINT "Dump";
COLOR Magenta
PRINT STRING$(6,HLine);
COLOR White
PRINT "Edit";
COLOR Magenta
PRINT STRING$(6,HLine);
COLOR White
PRINT "Jump";
COLOR Magenta
PRINT STRING$(6,HLine);
COLOR White
PRINT "Print";
COLOR Magenta
PRINT STRING$(3,HLine);
COLOR White
PRINT "Search";
COLOR Magenta
PRINT STRING$(8,HLine);
PRINT CHR$(URcorner)
PRINT " "; CHR$(Vline); STRING$(75, 32); CHR$(Vline)
PRINT " "; CHR$(Vline); " ";
COLOR Green
PRINT CHR$(ULcorner); STRING$(46, Hline); CHR$(URcorner);
COLOR White
PRINT "<";
COLOR Green
PRINT CHR$(ULcorner); STRING$(22, Hline); CHR$(URcorner);
COLOR White
PRINT "x";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "0";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline);
COLOR White
PRINT "|";
COLOR Green
PRINT CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "1";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "2";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "3";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "4";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "5";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "6";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "7";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "8";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "9";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "A";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "B";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "C";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "D";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "E";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline); " "; CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR White
PRINT "F";
COLOR Green
PRINT CHR$(Vline); STRING$(46, 32); CHR$(Vline);
COLOR White
PRINT "|";
COLOR Green
PRINT CHR$(Vline); STRING$(22, 32); CHR$(Vline); " ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline); " ";
COLOR Green
PRINT CHR$(LLcorner); STRING$(46, Hline); CHR$(LRcorner);
COLOR White
PRINT ">";
COLOR Green
PRINT CHR$(LLcorner); STRING$(22, Hline); CHR$(LRcorner); " ";
' check to skip info line area.
IF ScreenRow THEN
   GOTO NextRow
END IF
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR Yellow
PRINT " Alt-A Append bytes, Alt-M Append string, Alt-C ANSI Chart, Alt-N New File ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR Yellow
PRINT " Alt-H HEX Chart, Alt-R Redraw, Alt-S Search bytes, Alt-U Undo, Alt-X Exit ";
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(Vline);
COLOR Yellow
PRINT " Alt-J Jump, Alt-K Search string, Alt-D Dump, Alt-P Print, Alt-Z Undo All. ";
' display last row.
NextRow:
COLOR Magenta
PRINT CHR$(Vline)
PRINT " "; CHR$(LLcorner); STRING$(75, Hline); CHR$(LRcorner);
COLOR Plain
RETURN

' display screen border.
DisplayScreen2:
CLS
COLOR Magenta
PRINT CHR$(ULcorner); STRING$(76, Hline); CHR$(URcorner)
FOR Var2 = 2 TO 23
   LOCATE Var2, 1
   PRINT CHR$(Vline);
   LOCATE Var2, 78
   PRINT CHR$(Vline);
NEXT
LOCATE 24, 1
PRINT CHR$(LLcorner); STRING$(76, Hline); CHR$(LRcorner);
COLOR Plain
RETURN

' display ANSI charts.
ANSIChart:
 GOSUB DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "ASCII Chart for characters 1 to 127:"
 Var2 = 3
 LOCATE 3, 2
 FOR Char = 1 TO 127
    SELECT CASE Char
    ' skip unprintable characters
    CASE 0, 7, 9 TO 13, 28 TO 32
       DisplayChar$ = " "
    ' store character
    CASE ELSE
       DisplayChar$ = CHR$(Char)
    END SELECT
    Var$ = MID$(STR$(Char), 2)
    Var1$ = RIGHT$("00" + Var$, 3) + " "
    COLOR Green
    PRINT Var1$;
    COLOR Yellow
    PRINT DisplayChar$; " ";
    ' check full screen line
    IF (Char MOD 10) = False THEN
       Var2 = Var2 + 1
       LOCATE Var2, 2
    END IF
 NEXT
 LOCATE 16, 2
 GOSUB PressKey
 GOSUB DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "ASCII Chart for characters 128 to 255:"
 Var2 = 3
 LOCATE 3, 2
 FOR Char = 128 TO 255
    DisplayChar$ = CHR$(Char)
    Var$ = MID$(STR$(Char), 2)
    Var1$ = RIGHT$("00" + Var$, 3) + " "
    COLOR Green
    PRINT Var1$;
    COLOR Yellow
    PRINT DisplayChar$; " ";
    ' check full screen line
    IF ((Char - 7) MOD 10) = False THEN
       Var2 = Var2 + 1
       LOCATE Var2, 2
    END IF
 NEXT
 LOCATE 16, 2
 GOSUB PressKey
 GOSUB RedrawScreen
 RETURN

' display HEX charts.
HEXChart:
 GOSUB DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "HEX Chart for characters 1 to 127:"
 Var2 = 3
 LOCATE 3, 2
 FOR Char = 1 TO 127
    SELECT CASE Char
    ' skip unprintable characters
    CASE 0, 7, 9 TO 13, 28 TO 32
       DisplayChar$ = " "
    ' store character
    CASE ELSE
       DisplayChar$ = CHR$(Char)
    END SELECT
    Var$ = HEX$(Char)
    Var1$ = RIGHT$("0" + Var$, 2) + " "
    COLOR Green
    PRINT Var1$;
    COLOR Yellow
    PRINT DisplayChar$; " ";
    ' check full screen line
    IF (Char MOD 10) = False THEN
       Var2 = Var2 + 1
       LOCATE Var2, 2
    END IF
 NEXT
 LOCATE 16, 2
 GOSUB PressKey
 COLOR Plain
 GOSUB DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "HEX Chart for characters 128 to 255:"
 Var2 = 3
 LOCATE 3, 2
 FOR Char = 128 TO 255
    DisplayChar$ = CHR$(Char)
    Var$ = HEX$(Char)
    Var1$ = RIGHT$("0" + Var$, 2) + " "
    COLOR Green
    PRINT Var1$;
    COLOR Yellow
    PRINT DisplayChar$; " ";
    ' check full screen line
    IF ((Char - 7) MOD 10) = False THEN
       Var2 = Var2 + 1
       LOCATE Var2, 2
    END IF
 NEXT
 LOCATE 16, 2
 GOSUB PressKey
 GOSUB RedrawScreen
 RETURN

' function for unknown key.
HelpLine:
 GOSUB ClearStatus
 PRINT "Editing file: "; Filename;
 COLOR White
 PRINT " Type Alt-Q for Help.";
 GOSUB LocateCursor
 RETURN

' display help screens.
HelpScreen:
 GOSUB DisplayHelpScreen1
 GOSUB DisplayHelpScreen2
 GOSUB DisplayHelpScreen3
 GOSUB DisplayHelpScreen4
 COLOR Plain
 GOSUB RedrawScreen
 RETURN

' help screen header.
HelpHeader:
 GOSUB DisplayScreen2
 LOCATE 2, 2
 COLOR White
 PRINT "Help screen: Hex Editor " + Version + " " + Release + " functions:"
 RETURN

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

' help screen 1.
DisplayHelpScreen1:
 GOSUB HelpHeader
 COLOR Red
 LOCATE 3, 2
 PRINT "Function keys:                          Editing keys:"
 COLOR Yellow
 LOCATE 4, 2
 PRINT "Alt-A  Append multiple bytes to file.   Tab    -- Switch windows."
 LOCATE 5, 2
 PRINT "Alt-C  ANSI Chart.                      Enter  -- Change byte value."
 LOCATE 6, 2
 PRINT "Alt-D  HEX Screen Dump.                 Insert -- Change string value."
 LOCATE 7, 2
 PRINT "Alt-E  HEX File Dump.                   Delete -- Change hex string."
 LOCATE 8, 2
 PRINT "Alt-H  HEX Chart.                       Cursor Up/Down/Left/Right."
 LOCATE 9, 2
 PRINT "Alt-J  Jump to byte position.           Page Up/Page Down, Home/End."
 LOCATE 10, 2
 PRINT "Alt-K  Search for ASCII string.         Ctrl-Home/Ctrl-End."
 LOCATE 11, 2
 PRINT "Alt-L  Jump to page position.           Ctrl-Right/Ctrl-Left."
 LOCATE 12, 2
 PRINT "Alt-M  Append ASCII string to file.     Escape/Alt-F = Input Menu."
 COLOR Yellow
 LOCATE 13, 2
 PRINT "Alt-N  Load new file.                   ";
 COLOR Red
 PRINT "Other keys:"
 COLOR Yellow
 LOCATE 14, 2
 PRINT "Alt-P  HEX Screen Print.                (hex value numeral preceded)."
 LOCATE 15, 2
 PRINT "Alt-Q  Help screen.                     (right window unprintable chars"
 LOCATE 16, 2
 PRINT "Alt-R  Redraw screen.                   are represented with a dot)."
 LOCATE 17, 2
 PRINT "Alt-S  Search for multiple bytes.       (ascii values precede with H"
 LOCATE 18, 2
 PRINT "Alt-T  HEX File Print.                  treated as hexcidecimal)."
 LOCATE 19, 2
 PRINT "Alt-U  Undo last byte change.           (<escape> key exits search)."
 LOCATE 20, 2
 PRINT "Alt-X  Exit program."
 LOCATE 21, 2
 PRINT "Alt-Y  Toggle right window."
 LOCATE 22, 2
 PRINT "Alt-Z  Undo All byte changes."
 LOCATE 23, 2
 GOSUB PressKey
 RETURN

' help screen 2.
DisplayHelpScreen2:
 GOSUB HelpHeader
 COLOR Red
 LOCATE 3, 2
 PRINT "Marker keys:"
 COLOR Yellow
 LOCATE 4, 2
 PRINT "1   add marker"
 LOCATE 5, 2
 PRINT "2   add specific marker value"
 LOCATE 6, 2
 PRINT "3   delete marker"
 LOCATE 7, 2
 PRINT "4   delete specific marker"
 LOCATE 8, 2
 PRINT "5   jump previous marker"
 LOCATE 9, 2
 PRINT "6   jump current marker"
 LOCATE 10, 2
 PRINT "7   jump next marker"
 LOCATE 11, 2
 PRINT "8   jump specific marker"
 LOCATE 12, 2
 PRINT "9   list all markers"
 LOCATE 13, 2
 PRINT "0   list specific marker"
 LOCATE 14, 2
 PRINT "-   list markers of value"
 LOCATE 15, 2
 PRINT "+   list range of markers"
 LOCATE 16, 2
 PRINT "=   delete a range"
 LOCATE 17, 2
 PRINT "[   delete all markers"
 LOCATE 18, 2
 PRINT "]   delete markers of value"
 LOCATE 19, 2
 GOSUB PressKey
 RETURN

' help screen 3.
DisplayHelpScreen3:
 GOSUB HelpHeader
 COLOR Red
 LOCATE 3, 2
 PRINT "Information keys:"
 COLOR Yellow
 LOCATE 4, 2
 PRINT "? key displays file length, and undos remaining."
 LOCATE 5, 2
 PRINT "! key displays current page, row, and column."
 LOCATE 6, 2
 PRINT "~ key displays last page and markers used."
 LOCATE 7, 2
 GOSUB PressKey
 RETURN

' help screen 4.
DisplayHelpScreen4:
 GOSUB DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "Hexedit information: ";
 IF Windows.Detected THEN
    PRINT "(Windows loaded);";
 ELSE
    PRINT "(DOS loaded);";
 END IF
 COLOR Yellow
 LOCATE 3, 2
 PRINT "Program: " + Program + " " + Version + " " + Release + "."
 LOCATE 4, 2
 PRINT "Author: " + Author
 LOCATE 5, 2
 PRINT "Release: " + Publish
 LOCATE 6, 2
 PRINT "Status: " + Status
 LOCATE 7, 2
 PRINT "Email: " + Email
 LOCATE 8, 2
 PRINT "Urls: " + Urls
 LOCATE 9, 2
 GOSUB PressKey
 RETURN

' function for ? key.
DisplayInfo:
 GOSUB ClearStatus
 PRINT "Editing file: "; Filename; " ";
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 Attr$ = Nul
 IF (FileAttribute AND ReadOnlyBit) = ReadOnlyBit THEN
    Attr$ = Attr$ + "o"
 END IF
 IF (FileAttribute AND HiddenBit) = HiddenBit THEN
    Attr$ = Attr$ + "h"
 END IF
 IF (FileAttribute AND SystemBit) = SystemBit THEN
    Attr$ = Attr$ + "s"
 END IF
 IF (FileAttribute AND ArchiveBit) = ArchiveBit THEN
    Attr$ = Attr$ + "a"
 END IF
 IF LEN(Attr$) THEN
    PRINT "("; Attr$; ") ";
 END IF
 IF CurrentWindow2 = False THEN
    PRINT "(Length:"; STR$(FileLength); ") ";
 ELSE
    PRINT "(Length: "; RIGHT$("00000000" + HEX$(FileLength - 1), 8); "H) ";
 END IF
 PRINT "(Undos:"; STR$(CurrentUndo); ") ";
 GOSUB LocateCursor
 RETURN

' function for ! key.
DisplayData:
 GOSUB ClearStatus
 PRINT "Editing file: "; Filename; " ";
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 TempPosition3 = FilePosition
 GOSUB FormatPosition1
 GOSUB LocateCursor
 RETURN

' function for ~ key.
DisplayLengths:
 GOSUB ClearStatus
 PRINT "Editing file: "; Filename; " ";
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 FilePage2 = INT((FileLength - 1) / 320) + 1
 IF CurrentWindow2 = False THEN
    PRINT "(Last page:"; STR$(FilePage2 - 1); ") ";
 ELSE
    PRINT "(Last page: "; RIGHT$("00000000" + HEX$(FilePage2 - 1), 8); "H) ";
 END IF
 ' count markers
 TempPosition3 = 0&
 FOR TempPosition = 1 TO MarkerCount
    IF Markers(TempPosition) > 0# THEN
       TempPosition3 = TempPosition3 + 1&
    END IF
 NEXT
 PRINT "(Markers: "; MID$(STR$(TempPosition3), 2); ")";
 GOSUB LocateCursor
 RETURN

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

' display info on file being edited.
DisplayPosition:
 COLOR Yellow
 StringLength = LEN("Editing file: " + 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 LocateCursor
 RETURN

' display name of file being edited.
DisplayFilename2:
 GOSUB ClearStatus
 PRINT "Editing file: "; Filename; " ";
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 GOSUB DisplayPosition2
 RETURN

' display info on file being edited.
DisplayPosition2:
 COLOR Yellow
 StringLength = LEN("Editing file: " + 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$(FilePosition2); ") ";
 ELSE
    PRINT "(Position: "; RIGHT$("00000000" + HEX$(FilePosition2 - 1), 8); "H) ";
 END IF
 PRINT "(Ascii:"; STR$(AsciiValue2); ") ";
 PRINT "(Hex: "; RIGHT$("00" + HEX$(AsciiValue2), 2); "H)";
 GOSUB LocateCursor
 RETURN

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

' redraw editing screen
RedrawScreen:
 ScreenDrawn = True
 GOSUB DisplayScreen
 GOSUB DisplayFilename
 GOSUB DisplayHexPage
 GOSUB DisplayPageByte
 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

' dump screen of current page of hex/ascii values to file.
DumpScreen:
 Column = False
 ColumnSpace = False
 HexLine$ = Nul
 HexLine2$ = Nul
 CLOSE #1
 ErrorTrap = False
 IF TestFile(DumpFile) = False THEN
    StatusMessage = "Error opening dump filename."
    GOSUB DisplayStatus1
    RETURN
 END IF
 OPEN DumpFile FOR APPEND AS #1
 IF ErrorTrap THEN
    GOSUB RedrawScreen
    RETURN
 END IF
 FirstByte = (FilePage - 1) * 320 + 1
 LastByte = (FilePage - 1) * 320 + 320
 DumpLine$ = "File: " + Filename + ", position" + STR$(FirstByte) + " to"
 IF LastByte <= FileLength THEN
    DumpLine$ = DumpLine$ + STR$(LastByte)
 ELSE
    DumpLine$ = DumpLine$ + STR$(FileLength)
 END IF
 DumpLine$ = DumpLine$ + "."
 PRINT #1, DumpLine$
 FOR NextByte = FirstByte TO LastByte
    IF NextByte <= FileLength THEN
       SeekPosition = NextByte
       GOSUB LseekFile
       GOSUB ReadFile
       FileByte = Buffer
       ByteValue = ASC(FileByte)
       ' skip unprintable characters
       IF ByteValue < 32 THEN
          HexLine2$ = HexLine2$ + "."
       ELSE
          HexLine2$ = HexLine2$ + FileByte
       END IF
       HexLine$ = HexLine$ + RIGHT$("00" + HEX$(ASC(FileByte)), 2)
    ELSE
       HexLine$ = HexLine$ + "  "
       HexLine2$ = HexLine2$ + " "
    END IF
    ColumnSpace = ColumnSpace + 1
    IF ColumnSpace = 4 THEN
       HexLine$ = HexLine$ + " "
       Column = Column + 1
       ColumnSpace = False
    END IF
    Column = Column + 2
    IF Column > 44 THEN
       HexFile$ = HexLine$ + " " + HexLine2$
       HexFile$ = RTRIM$(HexFile$)
       IF LEN(HexFile$) THEN
          PRINT #1, HexFile$
       END IF
       HexLine$ = Nul
       HexLine2$ = Nul
       Column = False
    END IF
 NEXT
 GOSUB DisplayPageByte

 ' conanicalize filename.
 Filename2$ = Conanicalize$(DumpFile)

 ' display filename.
 StatusMessage = "Screen dumped to: '" + Filename2$ + "'."
 GOSUB DisplayStatus1
 RETURN

' dump file of current hex/ascii values to file.
DumpHEXFile:
 Column = False
 ColumnSpace = False
 HexLine$ = Nul
 HexLine2$ = Nul
 CLOSE #1
 ErrorTrap = False
 IF TestFile(DumpFile) = False THEN
    StatusMessage = "Error opening dump filename."
    GOSUB DisplayStatus1
    RETURN
 END IF
 OPEN DumpFile FOR APPEND AS #1
 IF ErrorTrap THEN
    GOSUB RedrawScreen
    RETURN
 END IF

 ' calculate last page
 FirstByte = 1
 LastPage = INT((FileLength - 1) / 320) + 1
 LastByte = (LastPage - 1) * 320 + 320

 ' write to file
 FileDumped = True
 GOSUB DumpInfo
 DumpLine$ = "File: " + Filename + ", position" + STR$(FirstByte) + " to"
 IF LastByte <= FileLength THEN
    DumpLine$ = DumpLine$ + STR$(LastByte)
 ELSE
    DumpLine$ = DumpLine$ + STR$(FileLength)
 END IF
 DumpLine$ = DumpLine$ + "."
 PRINT #1, DumpLine$
 FOR NextByte = FirstByte TO LastByte
    IF NextByte <= FileLength THEN
       SeekPosition = NextByte
       GOSUB LseekFile
       GOSUB ReadFile
       FileByte = Buffer
       ByteValue = ASC(FileByte)
       ' skip unprintable characters
       IF ByteValue < 32 THEN
          HexLine2$ = HexLine2$ + "."
       ELSE
          HexLine2$ = HexLine2$ + FileByte
       END IF
       HexLine$ = HexLine$ + RIGHT$("00" + HEX$(ASC(FileByte)), 2)
    ELSE
       HexLine$ = HexLine$ + "  "
       HexLine2$ = HexLine2$ + " "
    END IF
    ColumnSpace = ColumnSpace + 1
    IF ColumnSpace = 4 THEN
       HexLine$ = HexLine$ + " "
       Column = Column + 1
       ColumnSpace = False
    END IF
    Column = Column + 2
    IF Column > 44 THEN
       IF INKEY$ = CHR$(27) THEN
          FileDumped = False
          EXIT FOR
       END IF
       HexFile$ = HexLine$ + " " + HexLine2$
       HexFile$ = RTRIM$(HexFile$)
       IF LEN(HexFile$) THEN
          PRINT #1, HexFile$
       END IF
       HexLine$ = Nul
       HexLine2$ = Nul
       Column = False
    END IF
 NEXT
 GOSUB DisplayPageByte

 ' conanicalize filename.
 Filename2$ = Conanicalize$(DumpFile)

 ' display filename.
 IF FileDumped THEN
    StatusMessage = "File dumped to: '" + Filename2$ + "'."
 ELSE
    StatusMessage = "Partial file dumped to: '" + Filename2$ + "'."
 END IF
 GOSUB DisplayStatus1
 RETURN

' print screen of current page of hex/ascii values to printer.
PrintScreen:
 GOSUB ClearStatus
 PRINT "Enter printer port(1-3)";
 INPUT PortNumber
 PortNumber = INT(PortNumber)
 IF PortNumber >= 1 AND PortNumber <= 3 THEN
    CLOSE #1
    Filename2$ = "LPT" + MID$(STR$(PortNumber), 2) + ":"
    ErrorTrap = False
    OPEN Filename2$ FOR OUTPUT AS #1
    IF ErrorTrap THEN
       GOSUB RedrawScreen
       RETURN
    END IF
 ELSE
    StatusMessage = "Invalid printer port number."
    GOSUB DisplayStatus1
    RETURN
 END IF
 Column = False
 ColumnSpace = False
 HexLine$ = Nul
 HexLine2$ = Nul
 FirstByte = (FilePage - 1) * 320 + 1
 LastByte = (FilePage - 1) * 320 + 320
 DumpLine$ = "File: " + Filename + ", position" + STR$(FirstByte) + " to"
 IF LastByte <= FileLength THEN
    DumpLine$ = DumpLine$ + STR$(LastByte)
 ELSE
    DumpLine$ = DumpLine$ + STR$(FileLength)
 END IF
 DumpLine$ = DumpLine$ + "."
 PRINT #1, DumpLine$
 FOR NextByte = FirstByte TO LastByte
    IF NextByte <= FileLength THEN
       SeekPosition = NextByte
       GOSUB LseekFile
       GOSUB ReadFile
       FileByte = Buffer
       ByteValue = ASC(FileByte)
       IF ByteValue < 32 THEN
          HexLine2$ = HexLine2$ + "."
       ELSE
          HexLine2$ = HexLine2$ + FileByte
       END IF
       HexLine$ = HexLine$ + RIGHT$("00" + HEX$(ASC(FileByte)), 2)
    ELSE
       HexLine$ = HexLine$ + "  "
       HexLine2$ = HexLine2$ + " "
    END IF
    ColumnSpace = ColumnSpace + 1
    IF ColumnSpace = 4 THEN
       HexLine$ = HexLine$ + " "
       Column = Column + 1
       ColumnSpace = False
    END IF
    Column = Column + 2
    IF Column > 44 THEN
       HexFile$ = HexLine$ + " " + HexLine2$
       HexFile$ = RTRIM$(HexFile$)
       IF LEN(HexFile$) THEN
          PRINT #1, HexFile$
       END IF
       HexLine$ = Nul
       HexLine2$ = Nul
       Column = False
    END IF
 NEXT
 PRINT #1, CHR$(12);
 GOSUB DisplayPageByte
 StatusMessage = "Screen dumped to printer port:" + STR$(PortNumber) + "."
 GOSUB DisplayStatus1
 RETURN

' print file of current hex/ascii values to printer.
PrintFile:
 GOSUB ClearStatus
 PRINT "Enter printer port(1-3)";
 INPUT PortNumber
 PortNumber = INT(PortNumber)
 IF PortNumber >= 1 AND PortNumber <= 3 THEN
    CLOSE #1
    Filename2$ = "LPT" + MID$(STR$(PortNumber), 2) + ":"
    ErrorTrap = False
    OPEN Filename2$ FOR OUTPUT AS #1
    IF ErrorTrap THEN
       GOSUB RedrawScreen
       RETURN
    END IF
 ELSE
    StatusMessage = "Invalid printer port number."
    GOSUB DisplayStatus1
    RETURN
 END IF
 Column = False
 ColumnSpace = False
 HexLine$ = Nul
 HexLine2$ = Nul

 ' calculate last page
 FirstByte = 1
 LastPage = INT((FileLength - 1) / 320) + 1
 LastByte = (LastPage - 1) * 320 + 320

 ' write to printer
 FileDumped = True
 GOSUB DumpInfo
 DumpLine$ = "File: " + Filename + ", position" + STR$(FirstByte) + " to"
 IF LastByte <= FileLength THEN
    DumpLine$ = DumpLine$ + STR$(LastByte)
 ELSE
    DumpLine$ = DumpLine$ + STR$(FileLength)
 END IF
 DumpLine$ = DumpLine$ + "."
 PRINT #1, DumpLine$
 FOR NextByte = FirstByte TO LastByte
    IF NextByte <= FileLength THEN
       SeekPosition = NextByte
       GOSUB LseekFile
       GOSUB ReadFile
       FileByte = Buffer
       ByteValue = ASC(FileByte)
       IF ByteValue < 32 THEN
          HexLine2$ = HexLine2$ + "."
       ELSE
          HexLine2$ = HexLine2$ + FileByte
       END IF
       HexLine$ = HexLine$ + RIGHT$("00" + HEX$(ASC(FileByte)), 2)
    ELSE
       HexLine$ = HexLine$ + "  "
       HexLine2$ = HexLine2$ + " "
    END IF
    ColumnSpace = ColumnSpace + 1
    IF ColumnSpace = 4 THEN
       HexLine$ = HexLine$ + " "
       Column = Column + 1
       ColumnSpace = False
    END IF
    Column = Column + 2
    IF Column > 44 THEN
       IF INKEY$ = CHR$(27) THEN
          FileDumped = False
          EXIT FOR
       END IF
       HexFile$ = HexLine$ + " " + HexLine2$
       HexFile$ = RTRIM$(HexFile$)
       IF LEN(HexFile$) THEN
          PRINT #1, HexFile$
       END IF
       HexLine$ = Nul
       HexLine2$ = Nul
       Column = False
    END IF
 NEXT
 PRINT #1, CHR$(12);
 GOSUB DisplayPageByte
 IF FileDumped THEN
    StatusMessage = "File dumped to printer port:" + STR$(PortNumber) + "."
 ELSE
    StatusMessage = "Partial file dumped to printer port:" + STR$(PortNumber) + "."
 END IF
 GOSUB DisplayStatus1
 RETURN

' clear hilight of current byte on screen.
ClearPageByte:
 COLOR White
 GOSUB DisplayByte
 RETURN

' reset hilight of current byte being edited.
DisplayPageByte:
 COLOR Yellow
 GOSUB DisplayByte
 ' store the current byte value being edited.
 AsciiValue = ASC(FileByte)
 GOSUB DisplayPosition
 RETURN

' display a byte on the screen.
DisplayByte:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 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

' 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

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

' determine which window the cursor is on.
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

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

' display status line message and prompt type 1.
DisplayStatus1:
 StatusMessage = StatusMessage + " Press <esc>:"
 GOTO DisplayStatusLine

' 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 LocateCursor
 WHILE INKEY$ <> CHR$(27): WEND
 GOSUB DisplayFilename
 RETURN

' add multiple bytes of an ASCII string to file and locate there.
AppendASCII:
 GOSUB ClearStatus
 PRINT "Enter ASCII string";
 INPUT ByteString$
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid append string."
    GOSUB DisplayStatus2
    RETURN
 END IF
 NumBytes = LEN(ByteString$)
 GOSUB AsciiToHex2
 GOTO StartAppend
' add multiple bytes to file and locate there.
' left window adds space-separated hex byte pairs,
' right window adds space-separated 3-byte ascii pairs.
AppendByte:
 GOSUB ClearStatus
 IF CurrentWindow = False THEN
    PRINT "Enter hex byte(s)";
    INPUT ByteString$
    IF LEFT$(ByteString$, 1) = "+" THEN
       ByteString$ = MID$(ByteString$, 2)
       GOSUB CheckAsciiBytes
    ELSE
       AllowWildcard = False
       GOSUB CheckHexBytes
    END IF
 ELSE
    PRINT "Enter ascii byte(s)";
    INPUT ByteString$
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = MID$(ByteString$, 2)
       AllowWildcard = False
       GOSUB CheckHexBytes
    ELSE
       GOSUB CheckAsciiBytes
    END IF
 END IF
 IF ValidByteString = False THEN
    StatusMessage = "Invalid append string."
    GOSUB DisplayStatus2
    RETURN
 END IF
 ' append the byte string
StartAppend:
 LastPage = FilePage
 FOR Byte1 = 1 TO NumBytes
    GOSUB ClearPageByte
    Byte$ = MID$(ByteString$, (Byte1 - 1) * 2 + 1, 2)
    Byte2 = VAL("&H" + Byte$)
    FileByte = CHR$(Byte2)
    IF FileLength + 1& <= 2147483647& THEN
       FileLength = FileLength + 1&
       SeekPosition = FileLength
       GOSUB LseekFile
       GOSUB WriteFile
       FilePosition = FileLength
       GOSUB CalculatePosition1
       IF LastPage <> FilePage THEN
          LastPage = FilePage
          GOSUB DisplayHexPage
       END IF
       GOSUB DisplayPageByte
    END IF
 NEXT
 GOSUB DisplayFilename
 RETURN

' search multiple bytes of an ASCII string in file and locate there.
SearchASCII:
 GOSUB ClearStatus
 PRINT "Enter ASCII string";
 INPUT ByteString$
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid search string."
    GOSUB DisplayStatus2
    RETURN
 END IF
 NumBytes = LEN(ByteString$)
 GOSUB AsciiToHex2
 GOTO StartSearch
' search multiple bytes in file and locate there.
' left window adds space-separated hex byte pairs,
' right window adds space-separated 3-byte ascii pairs.
SearchByte:
 GOSUB ClearStatus
 IF CurrentWindow = False THEN
    PRINT "Enter hex byte(s)";
    INPUT ByteString$
    IF LEFT$(ByteString$, 1) = "+" THEN
       ByteString$ = MID$(ByteString$, 2)
       GOSUB CheckAsciiBytes
    ELSE
       AllowWildcard = True
       GOSUB CheckHexBytes
    END IF
 ELSE
    PRINT "Enter ascii byte(s)";
    INPUT ByteString$
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = MID$(ByteString$, 2)
       AllowWildcard = True
       GOSUB CheckHexBytes
    ELSE
       GOSUB CheckAsciiBytes
    END IF
 END IF
 IF ValidByteString = False THEN
    StatusMessage = "Invalid search string."
    GOSUB DisplayStatus2
    RETURN
 END IF
StartSearch:
 ' search for the byte string
 FoundString = False
 GOSUB ClearPageByte
 LastPage = FilePage
 GOSUB SearchInfo
 FOR FileBytePosition = FilePosition TO FileLength
    ' check to break search
    IF INKEY$ = CHR$(27) THEN
       EXIT FOR
    END IF
    ' store next byte string
    TotalByte$ = Nul
    FOR TotalBytes = 1 TO NumBytes
       NextByte = FileBytePosition + TotalBytes
       IF NextByte > FileLength THEN
          TotalByte$ = Nul
          EXIT FOR
       END IF
       SeekPosition = NextByte
       GOSUB LseekFile
       GOSUB ReadFile
       FileByte = Buffer
       Byte$ = HEX$(ASC(FileByte))
       Byte$ = RIGHT$("00" + Byte$, 2)
       TotalByte$ = TotalByte$ + Byte$
    NEXT
    ' compare byte strings
    IF LEN(TotalByte$) THEN
       FoundString = True
       FOR TotalBytes = 1 TO NumBytes * 2
          Byte1$ = MID$(ByteString$, TotalBytes, 1)
          Byte2$ = MID$(TotalByte$, TotalBytes, 1)
          IF Byte1$ <> "?" THEN
             IF Byte1$ <> Byte2$ THEN
                FoundString = False
                EXIT FOR
             END IF
          END IF
       NEXT
    END IF
    ' check byte comparison
    IF FoundString THEN
       FilePosition = FileBytePosition + 1
       GOSUB CalculatePosition1
       IF LastPage <> FilePage THEN
          GOSUB DisplayHexPage
       END IF
       EXIT FOR
    END IF
 NEXT
 GOSUB DisplayPageByte
 IF FoundString = False THEN
    StatusMessage = "String not found."
    GOSUB DisplayStatus2
    RETURN
 END IF
 GOSUB DisplayFilename
 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

' checks hex byte string.
'   input: AllowWildcard true to allow ? character.
'     ByteString$ is space-separated hex byte pair string.
'   returns: ValidByteString equals true if string is valid,
'     NumBytes is number of hex byte pairs,
'     ByteString$ is concatenated hex byte string.
CheckHexBytes:
 ByteString$ = LTRIM$(ByteString$)
 ByteString$ = RTRIM$(ByteString$)
 IF LEN(ByteString$) = False THEN
    ValidByteString = False
    RETURN
 END IF
 NumBytes = 1
 ValidByteString = True
 FOR StringPosition = 1 TO LEN(ByteString$)
    Byte$ = MID$(ByteString$, StringPosition, 1)
    IF (StringPosition MOD 3) = False THEN
       IF Byte$ <> " " THEN
          ValidByteString = False
          RETURN
       END IF
       NumBytes = NumBytes + 1
    ELSE
       SELECT CASE Byte$
       CASE "0" TO "9", "A" TO "F", "a" TO "f", "?"
          IF Byte$ = "?" THEN
             IF AllowWildcard = False THEN
                ValidByteString = False
                RETURN
             END IF
          END IF
          MID$(ByteString$, StringPosition, 1) = UCASE$(Byte$)
       CASE ELSE
          ValidByteString = False
          RETURN
       END SELECT
    END IF
 NEXT
 ByteString$ = TrimSpaces$(ByteString$)
 RETURN

' checks ascii byte string.
'   input: ByteString$ is space-separated 3-byte ascii pair string.
'   returns: ValidByteString equals true if string is valid,
'     NumBytes is number of ascii byte pairs,
'     ByteString$ is concatenated hex byte string.
CheckAsciiBytes:
 AsciiByte$ = Nul
 ByteString$ = LTRIM$(ByteString$)
 ByteString$ = RTRIM$(ByteString$)
 IF LEN(ByteString$) = False THEN
    ValidByteString = False
    RETURN
 END IF
 NumBytes = 1
 ValidByteString = True
 FOR StringPosition = 1 TO LEN(ByteString$)
    Byte$ = MID$(ByteString$, StringPosition, 1)
    IF (StringPosition MOD 4) = False THEN
       IF Byte$ <> " " THEN
          ValidByteString = False
          RETURN
       END IF
       NumBytes = NumBytes + 1
    ELSE
       SELECT CASE Byte$
       CASE "0" TO "9"
          AsciiByte$ = AsciiByte$ + Byte$
          IF LEN(AsciiByte$) = 3 THEN
             ByteValue = VAL(AsciiByte$)
             IF ByteValue >= False AND ByteValue <= 255 THEN
                AsciiByte$ = Nul
             ELSE
                ValidByteString = False
                RETURN
             END IF
          END IF
       CASE ELSE
          ValidByteString = False
          RETURN
       END SELECT
    END IF
 NEXT
 ByteString$ = TrimSpaces$(ByteString$)
 GOSUB AsciiToHex1
 RETURN

' converts a 3-digit packed ascii string to a hex string.
AsciiToHex1:
 Byte$ = Nul
 NewString$ = Nul
 FOR StringPosition = 1 TO LEN(ByteString$)
    Byte$ = Byte$ + MID$(ByteString$, StringPosition, 1)
    IF LEN(Byte$) = 3 THEN
       NewString$ = NewString$ + RIGHT$("00" + HEX$(VAL(Byte$)), 2)
       Byte$ = Nul
    END IF
 NEXT
 ByteString$ = NewString$
 RETURN

' converts a 1-digit packed ascii string to a hex string.
AsciiToHex2:
 Byte$ = Nul
 NewString$ = Nul
 FOR StringPosition = 1 TO LEN(ByteString$)
    Byte$ = MID$(ByteString$, StringPosition, 1)
    NewString$ = NewString$ + RIGHT$("00" + HEX$(ASC(Byte$)), 2)
 NEXT
 ByteString$ = NewString$
 RETURN

' display message about searching.
SearchInfo:
 GOSUB ClearStatus
 DisplayLength = LEN(ByteString$)
 IF DisplayLength > 32 THEN
    PRINT "Searching for " + LEFT$(ByteString$, 32) + "...";
 ELSE
    PRINT "Searching for " + ByteString$ + ":";
 END IF
 PRINT " (Press <esc> to quit)";
 RETURN

' display message about dumping.
DumpInfo:
 GOSUB ClearStatus
 PRINT "Writing file: (Press <esc> to quit):";
 RETURN

' jump to page position in file.
JumpPage:
 GOSUB ClearStatus
 PRINT "Enter page position";
 INPUT ByteString$
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    GOSUB CheckHexValue
    IF ValidHexValue = False THEN
       RETURN
    END IF
    NextPage = HexValue
 ELSE
    GOSUB CheckASCIIValue
    IF ValidASCIIValue = False THEN
       RETURN
    END IF
    NextPage = ASCIIValue3
 END IF
 ' check the new page is there to jump to.
 TempPosition3 = (NextPage - 1) * 320 + 1
 IF TempPosition3 >= 1 AND TempPosition3 <= FileLength THEN
    GOSUB ClearPageByte
    LastPage = FilePage
    ' preserve current row, column.
    FilePosition = TempPosition3
    TempPosition2 = FilePosition + (PageRow - 1) * 20 + PageColumn - 1
    IF TempPosition2 <= FileLength THEN
       FilePosition = TempPosition2
    ELSE
       FilePosition = FileLength
    END IF
    GOSUB CalculatePosition1
    IF LastPage <> FilePage THEN
       GOSUB DisplayHexPage
    END IF
    GOSUB DisplayPageByte
    GOSUB DisplayFilename
    RETURN
 END IF
 StatusMessage = "Invalid page."
 GOSUB DisplayStatus2
 RETURN

' jump to a byte position in file.
JumpByte:
 GOSUB ClearStatus
 PRINT "Enter file position";
 INPUT ByteString$
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    GOSUB CheckHexValue
    IF ValidHexValue = False THEN
       RETURN
    END IF
    NewByte = HexValue
 ELSE
    GOSUB CheckASCIIValue
    IF ValidASCIIValue = False THEN
       RETURN
    END IF
    NewByte = ASCIIValue3
 END IF
 IF NewByte >= 1 AND NewByte <= FileLength THEN
    IF NewByte <> FilePosition THEN
       GOSUB ClearPageByte
       LastPage = FilePage
       FilePosition = NewByte
       GOSUB CalculatePosition1
       IF LastPage <> FilePage THEN
          GOSUB DisplayHexPage
       END IF
       GOSUB DisplayPageByte
    END IF
    GOSUB DisplayFilename
    RETURN
 END IF
 StatusMessage = "Invalid byte."
 GOSUB DisplayStatus2
 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

' undo a byte from stored arrays of previous position/byte value.
UndoByte:
 IF CurrentUndo > 0 THEN
    GOSUB ClearPageByte
    LastPage = FilePage
    FileByte = CHR$(UndoByte(CurrentUndo))
    FilePosition = UndoPosition(CurrentUndo)
    CurrentUndo = CurrentUndo - 1
    SeekPosition = FilePosition
    GOSUB LseekFile
    GOSUB WriteFile
    GOSUB CalculatePosition1
    IF LastPage <> FilePage THEN
       GOSUB DisplayHexPage
    END IF
    GOSUB DisplayPageByte
 END IF
 RETURN

' undo all bytes from stored arrays of previous position/byte value.
UndoAll:
 IF CurrentUndo > 0 THEN
    TotalUndos = CurrentUndo
    FOR NextUndo = TotalUndos TO 1 STEP -1
       GOSUB ClearPageByte
       LastPage = FilePage
       FileByte = CHR$(UndoByte(NextUndo))
       FilePosition = UndoPosition(NextUndo)
       CurrentUndo = CurrentUndo - 1
       SeekPosition = FilePosition
       GOSUB LseekFile
       GOSUB WriteFile
       GOSUB CalculatePosition1
       IF LastPage <> FilePage THEN
          LastPage = FilePage
          GOSUB DisplayHexPage
       END IF
       GOSUB DisplayPageByte
    NEXT
 END IF
 RETURN

' move cursor up.
CursorUp:
 IF PageRow - 1 >= 1 THEN
    GOSUB ClearPageByte
    PageRow = PageRow - 1
    FilePosition = FilePosition - 20
    GOSUB DisplayPageByte
 END IF
 RETURN

' move cursor down.
CursorDown:
 IF PageRow + 1 <= 16 THEN
    IF FilePosition + 20 <= FileLength THEN
       GOSUB ClearPageByte
       PageRow = PageRow + 1
       FilePosition = FilePosition + 20
       GOSUB DisplayPageByte
    END IF
 END IF
 RETURN

' move cursor left.
CursorLeft:
 IF PageColumn - 1 >= 1 THEN
    GOSUB ClearPageByte
    PageColumn = PageColumn - 1
    FilePosition = FilePosition - 1
    GOSUB DisplayPageByte
 END IF
 RETURN

' move cursor right.
CursorRight:
 IF PageColumn + 1 <= 20 THEN
    IF FilePosition + 1 <= FileLength THEN
       GOSUB ClearPageByte
       PageColumn = PageColumn + 1
       FilePosition = FilePosition + 1
       GOSUB DisplayPageByte
    END IF
 END IF
 RETURN

' end key
EndKey:
 ' set to column 20
 IF PageColumn < 20 THEN
    ' check column 20 is positioned before end of file
    IF FilePosition - PageColumn + 20 <= FileLength THEN
       GOSUB ClearPageByte
       FilePosition = FilePosition - PageColumn + 20
       PageColumn = 20
       GOSUB DisplayPageByte
    ELSE
       ' position end of file
       GOSUB ClearPageByte
       FilePosition = FileLength
       GOSUB CalculatePosition1
       GOSUB DisplayPageByte
    END IF
 END IF
 RETURN

' home key
HomeKey:
 IF PageColumn > 1 THEN
    GOSUB ClearPageByte
    FilePosition = FilePosition - PageColumn + 1
    PageColumn = 1
    GOSUB DisplayPageByte
 END IF
 RETURN

' control-left key
CtrlLeftKey:
 NewColumn = 0
 SELECT CASE PageColumn
 CASE 2, 3, 4
    NewColumn = 1
 CASE 5
    NewColumn = 4
 CASE 6, 7, 8
    NewColumn = 5
 CASE 9
    NewColumn = 8
 CASE 10, 11, 12
    NewColumn = 9
 CASE 13
    NewColumn = 12
 CASE 14, 15, 16
    NewColumn = 13
 CASE 17
    NewColumn = 16
 CASE 18, 19, 20
    NewColumn = 17
 END SELECT
 IF NewColumn > 0 THEN
    GOSUB ClearPageByte
    FilePosition = FilePosition - PageColumn + NewColumn
    PageColumn = NewColumn
    GOSUB DisplayPageByte
 END IF
 RETURN

' control-right key
CtrlRightKey:
 NewColumn = 0
 SELECT CASE PageColumn
 CASE 1, 2, 3
    NewColumn = 4
 CASE 4
    NewColumn = 5
 CASE 5, 6, 7
    NewColumn = 8
 CASE 8
    NewColumn = 9
 CASE 9, 10, 11
    NewColumn = 12
 CASE 12
    NewColumn = 13
 CASE 13, 14, 15
    NewColumn = 16
 CASE 16
    NewColumn = 17
 CASE 17, 18, 19
    NewColumn = 20
 END SELECT
 IF NewColumn THEN
    ' check new column is positioned before end of file
    IF FilePosition - PageColumn + NewColumn <= FileLength THEN
       GOSUB ClearPageByte
       FilePosition = FilePosition - PageColumn + NewColumn
       PageColumn = NewColumn
       GOSUB DisplayPageByte
    ELSE
       ' position end of file
       GOSUB ClearPageByte
       FilePosition = FileLength
       GOSUB CalculatePosition1
       GOSUB DisplayPageByte
    END IF
 END IF
 RETURN

' control-end key
CtrlEndKey:
 IF FilePosition <> FileLength THEN
    LastPage = FilePage
    GOSUB ClearPageByte
    FilePosition = FileLength
    GOSUB CalculatePosition1
    IF LastPage <> FilePage THEN
       GOSUB DisplayHexPage
    END IF
    GOSUB DisplayPageByte
 END IF
 RETURN

' control-home key
CtrlHomeKey:
 IF FilePosition <> 1 THEN
    LastPage = FilePage
    GOSUB ClearPageByte
    FilePosition = 1
    GOSUB CalculatePosition1
    IF LastPage <> FilePage THEN
       GOSUB DisplayHexPage
    END IF
    GOSUB DisplayPageByte
 END IF
 RETURN

' move one page up.
PageUp:
 ' check there is a page to move up.
 IF FilePosition - 320 >= 1 THEN
    FilePage = FilePage - 1
    FilePosition = FilePosition - 320
    GOSUB DisplayHexPage
    GOSUB DisplayPageByte
 ELSE
    ' check to move to 1,1 of current top page.
    IF FilePosition > 1 THEN
       GOSUB ClearPageByte
       PageRow = 1
       PageColumn = 1
       FilePosition = 1
       GOSUB DisplayPageByte
    END IF
 END IF
 RETURN

' move one page down.
PageDown:
 ' check there is a page to move down.
 IF FilePosition + 320 <= FileLength THEN
    FilePage = FilePage + 1
    FilePosition = FilePosition + 320
    GOSUB DisplayHexPage
    GOSUB DisplayPageByte
 ELSE
    ' check the last page is there to move down to.
    IF FilePage * 320 <= FileLength THEN
       PageRow = 1
       PageColumn = 1
       FilePage = FilePage + 1
       FilePosition = (FilePage - 1) * 320 + 1
       GOSUB DisplayHexPage
       GOSUB DisplayPageByte
    ELSE
       ' check there is a last position on the last page to move down to.
       IF FilePosition < FileLength THEN
          GOSUB ClearPageByte
          FilePosition = FileLength
          GOSUB CalculatePosition1
          GOSUB DisplayPageByte
       END IF
    END IF
 END IF
 RETURN

' switch editing windows.
TabWindow:
 IF CurrentWindow2 = False THEN
    CurrentWindow = NOT CurrentWindow
    GOSUB LocateCursor
 END IF
 RETURN

' switch editing windows.
ToggleRight:
 CurrentWindow = False
 CurrentWindow2 = NOT CurrentWindow2
 GOSUB RedrawRightWindow
 GOSUB DisplayPageByte
 RETURN

' change the byte value at the current position.
'   change by hex value in left window,
'   by ascii value in right window.
'   overrides with +255 in left window,
'   and with hff in right window.
ChangeHexValue:
 GOSUB ClearStatus
 FileByte = Nul
 ValidByteString = False
 IF CurrentWindow = False THEN
    PRINT "Enter hex byte value";
    INPUT ByteString$
    IF LEFT$(ByteString$, 1) = "+" THEN
       ByteString$ = MID$(ByteString$, 2)
       IF LEN(ByteString$) = 3 THEN
          NewValue = INT(VAL(ByteString$))
          IF NewValue >= False AND NewValue <= 255 THEN
             ValidByteString = True
             FileByte = CHR$(NewValue)
          END IF
       END IF
    ELSE
       IF LEN(ByteString$) = 2 THEN
          AllowWildcard = False
          GOSUB CheckHexBytes
          IF ValidByteString THEN
             NewValue = VAL("&H" + ByteString$)
             FileByte = CHR$(NewValue)
          END IF
       END IF
    END IF
 ELSE
    PRINT "Enter ascii byte value";
    INPUT ByteString$
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = MID$(ByteString$, 2)
       IF LEN(ByteString$) = 2 THEN
          AllowWildcard = False
          GOSUB CheckHexBytes
          IF ValidByteString THEN
             NewValue = VAL("&H" + ByteString$)
             FileByte = CHR$(NewValue)
          END IF
       END IF
    ELSE
       IF LEN(ByteString$) = 3 THEN
          NewValue = INT(VAL(ByteString$))
          IF NewValue >= False AND NewValue <= 255 THEN
             ValidByteString = True
             FileByte = CHR$(NewValue)
          END IF
       END IF
    END IF
 END IF
 IF ValidByteString = False THEN
    StatusMessage = "Invalid byte."
    GOSUB DisplayStatus2
    RETURN
 END IF
 GOSUB StoreUndo
 SeekPosition = FilePosition
 GOSUB LseekFile
 GOSUB WriteFile
 GOSUB DisplayPageByte
 GOSUB DisplayFilename
 RETURN

' change the ascii values with string starting at the current position.
ChangeASCIIValues:
 GOSUB ClearStatus
 PRINT "Enter ascii value(s)? ";
 LINE INPUT ByteString$
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid ascii string."
    GOSUB DisplayStatus2
    RETURN
 END IF
 ' store all current ascii values.
 GOSUB ClearPageByte
 StorePosition = FilePosition
 FOR NumberBytes = 1 TO LEN(ByteString$)
    SeekPosition = FilePosition
    GOSUB LseekFile
    GOSUB ReadFile
    FileByte = Buffer
    AsciiValue = ASC(FileByte)
    GOSUB StoreUndo
    IF FilePosition = FileLength THEN
       EXIT FOR
    END IF
    FilePosition = FilePosition + 1
 NEXT
 ' write ascii string.
 FilePosition = StorePosition
 FOR NumberBytes = 1 TO LEN(ByteString$)
    FileByte = MID$(ByteString$, NumberBytes, 1)
    SeekPosition = FilePosition
    GOSUB LseekFile
    GOSUB WriteFile
    GOSUB DisplayPageByte
    IF FilePosition = FileLength THEN
       EXIT FOR
    END IF
    GOSUB ClearPageByte
    FilePosition = FilePosition + 1
    GOSUB CalculatePosition1
    IF LastPage <> FilePage THEN
       LastPage = FilePage
       GOSUB DisplayHexPage
    END IF
 NEXT
 GOSUB DisplayPageByte
 GOSUB DisplayFilename
 RETURN

' change the hex values with string starting at the current position.
ChangeHEXValues:
 GOSUB ClearStatus
 PRINT "Enter HEX value(s)? ";
 LINE INPUT ByteString$
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid hex string."
    GOSUB DisplayStatus2
    RETURN
 END IF
 ' check hex string.
 AllowWildcard = False
 GOSUB CheckHexBytes
 IF ValidByteString = False THEN
    StatusMessage = "Invalid hex string."
    GOSUB DisplayStatus2
    RETURN
 END IF
 ' store all current hex values.
 GOSUB ClearPageByte
 StorePosition = FilePosition
 FOR NumberBytes = 1 TO LEN(ByteString$)
    SeekPosition = FilePosition
    GOSUB LseekFile
    GOSUB ReadFile
    FileByte = Buffer
    AsciiValue = ASC(FileByte)
    GOSUB StoreUndo
    IF FilePosition = FileLength THEN
       EXIT FOR
    END IF
    FilePosition = FilePosition + 1
 NEXT
 ' write hex string.
 FilePosition = StorePosition
 FOR NumberBytes = 1 TO LEN(ByteString$) STEP 2
    FileByte = CHR$(VAL("&H" + MID$(ByteString$, NumberBytes, 2)))
    SeekPosition = FilePosition
    GOSUB LseekFile
    GOSUB WriteFile
    GOSUB DisplayPageByte
    IF FilePosition = FileLength THEN
       EXIT FOR
    END IF
    GOSUB ClearPageByte
    FilePosition = FilePosition + 1
    GOSUB CalculatePosition1
    IF LastPage <> FilePage THEN
       LastPage = FilePage
       GOSUB DisplayHexPage
    END IF
 NEXT
 GOSUB DisplayPageByte
 GOSUB DisplayFilename
 RETURN

' record the current byte position and value.
' increment the number of undos in array.
StoreUndo:
 IF CurrentUndo < 16384 THEN
    IF CurrentUndo + 1 > MaxUndos THEN
       MaxUndos = MaxUndos + 1024
       REDIM PRESERVE UndoByte(1 TO MaxUndos) AS INTEGER
       REDIM PRESERVE UndoPosition(1 TO MaxUndos) AS DOUBLE
    END IF
    CurrentUndo = CurrentUndo + 1
    UndoByte(CurrentUndo) = AsciiValue
    UndoPosition(CurrentUndo) = FilePosition
 END IF
 RETURN

' critical error trap.
Error.Routine:
 IF ScreenDrawn THEN
    CLS
 END IF
 ScreenDrawn = False
 COLOR White
 PRINT "Hex Editor Main " + 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), A(bort):";
 ' 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"
       GOSUB RedrawScreen
       RESUME NEXT
    CASE "q"
       PRINT "q"
       GOSUB RedrawScreen
       RESUME TopProgram
    CASE "a"
       PRINT "a"
       EXIT DO
    END SELECT
 LOOP

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

' drop down input menu.
Menu2:
 ' reset mouse activity.
 IF Mouse.Present THEN
    ' show mouse cursor.
    CALL MouseFunction(ShowMouse, 0)
 END IF
 CALL DropDownMenu
 IF CurrentMenu = False THEN
    GOSUB LocateCursor
    GOTO StartLoop
 END IF
 ' reset mouse activity.
 IF Mouse.Present THEN
    ' hide mouse cursor.
    CALL MouseFunction(HideMouse, 0)
 END IF
 SELECT CASE CurrentMenu
 CASE 1
    SELECT CASE CurrentMenuSelection
    CASE 1 ' Alt-N  Load new file
       GOSUB NewFile
       GOTO BeginFile
    CASE 2 ' Alt-Y  Toggle window
       IF FileLength THEN
          GOSUB ToggleRight
       END IF
    CASE 3 ' Alt-X  Exit program
       GOTO StopLabel
    END SELECT
 CASE 2
    SELECT CASE CurrentMenuSelection
    CASE 1 ' Alt-C  ANSI Chart
       GOSUB ANSIChart
    CASE 2 ' Alt-H  HEX Chart
       GOSUB HEXChart
    END SELECT
 CASE 3
    SELECT CASE CurrentMenuSelection
    CASE 1 ' Alt-D  HEX Screen Dump
       GOSUB DumpScreen
    CASE 2 ' Alt-E  HEX File Dump
       GOSUB DumpHEXFile
    END SELECT
 CASE 4
    SELECT CASE CurrentMenuSelection
    CASE 1 ' Alt-A  Append bytes
       GOSUB AppendByte
    CASE 2 ' Alt-M  Append ASCII string
       GOSUB AppendASCII
    CASE 3 ' Alt-R  Redraw screen
       GOSUB RedrawScreen
    CASE 4 ' Alt-U  Undo last byte
       GOSUB UndoByte
    CASE 5 ' Alt-Z  Undo All bytes
       GOSUB UndoAll
    END SELECT
 CASE 5
    SELECT CASE CurrentMenuSelection
    CASE 1 ' Alt-J  Jump to byte
       GOSUB JumpByte
    CASE 2 ' Alt-L  Jump to page
       GOSUB JumpPage
    END SELECT
 CASE 6
    SELECT CASE CurrentMenuSelection
    CASE 1 ' Alt-P  HEX Screen Print
       GOSUB PrintScreen
    CASE 2 ' Alt-T  HEX File Print
       GOSUB PrintFile
    END SELECT
 CASE 7
    SELECT CASE CurrentMenuSelection
    CASE 1 ' Alt-K  Search for string
       GOSUB SearchASCII
    CASE 2 ' Alt-S  Search for bytes
       GOSUB SearchByte
    END SELECT
 END SELECT
 ' reset mouse activity.
 IF Mouse.Present THEN
    ' show mouse cursor.
    CALL MouseFunction(ShowMouse, 0)
    GOSUB RestorePosition
 END IF
 GOSUB LocateCursor
 GOTO StartLoop

REM Marker routines follow:

AddMarker:
 TempPosition3 = FilePosition

AddMarkerValue:
 IF MarkerCount < 16384 THEN
    IF MarkerCount + 1 > MaxMarkers THEN
       MaxMarkers = MaxMarkers + 1024
       REDIM PRESERVE Markers(1 TO MaxMarkers) AS DOUBLE
    END IF
    MarkerCount = MarkerCount + 1
    Markers(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 LocateCursor
       GOSUB DisplayFilename
       RETURN
    END IF
 END IF
 StatusMessage = "Invalid file position."
 GOSUB DisplayStatus2
 RETURN

DeleteMarker:
 IF CurrentMarker >= 1 AND CurrentMarker <= MarkerCount THEN
    Markers(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(TempPosition) = False
       GOSUB LocateCursor
       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(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(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(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(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:
 GOSUB DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "Marker list:";
 PageCount = False
 CountFlag = False
 Var2 = 2
 FOR TempPosition = 1 TO MarkerCount
    IF Markers(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
          GOSUB 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 LocateCursor
       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
 GOSUB DisplayScreen2
 COLOR White
 LOCATE 2, 2
 PRINT "Marker list:"
 PageCount = False
 CountFlag = False
 Var2 = 2
 FOR TempPosition = 1 TO MarkerCount
    IF Markers(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
          GOSUB 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
             GOSUB DisplayScreen2
             COLOR White
             LOCATE 2, 2
             PRINT "Marker list:"
             PageCount = False
             CountFlag = False
             Var2 = 2
             FOR TempPosition = TempPosition1 TO TempPosition2
                IF Markers(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
                      GOSUB 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(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
 MaxMarkers = 1024
 REDIM Markers(1 TO 1024) AS DOUBLE
 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(TempPosition) = TempPosition1 THEN
          Markers(TempPosition) = False
       END IF
    NEXT
 END IF
 StatusMessage = "All markers of value deleted."
 GOSUB DisplayStatus2
 RETURN

REM Position calculation routines follow:

' displays cell position in string format type 1.
FormatPosition1:
 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

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

' displays cell position in string format type 3.
FormatPosition3:
 PRINT "Marker#"; MID$(STR$(TempPosition), 2); " Position:";
 TempPosition3 = Markers(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

' 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

' 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

' calculate file position given mouse row, and column of current page.
CalculatePosition3:
 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

' calculate file position given old mouse row, and column of current page.
CalculatePosition4:
 NewPosition = (FilePage - 1) * 320 + (Old.Mouse.Row - 4) * 20
 SELECT CASE Old.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

REM Long filename routines follow:

' read lowercase command line.
ReadCommandLine:
 CommandLine$ = Nul
 InregsX.AX = &H6200
 CALL InterruptX(&H21, InregsX, OutregsX)
 PSPsegment = OutregsX.BX
 PSPoffset = 128
 DEF SEG = PSPsegment
 FOR Count = 1 TO 127
    CommandChar = PEEK(PSPoffset + Count)
    SELECT CASE CommandChar
    CASE 0, 10, 13
       EXIT FOR
    CASE ELSE
       CommandLine$ = CommandLine$ + CHR$(CommandChar)
    END SELECT
 NEXT
 DEF SEG
 RETURN

' store basic dta.
StoreDTA:
 InregsX.AX = &H2F00
 CALL InterruptX(&H21, InregsX, OutregsX)
 BASIC.DTA.SEG = OutregsX.ES
 BASIC.DTA.OFF = OutregsX.BX
 RETURN

' verify windows loaded.
CheckWindows:
 InregsX.AX = &H160A
 CALL InterruptX(&H2F, InregsX, OutregsX)
 IF OutregsX.AX = False THEN
    Temp = (OutregsX.BX AND &HFF00) / 256
    IF Temp >= 4 THEN
       Windows.Detected = True
    END IF
 END IF
 RETURN

' determine length of file.
GetFileLength:
 FileLength = False
 InregsX.AX = &H4202
 InregsX.BX = Handle
 InregsX.CX = &H0
 InregsX.DX = &H0
 CALL InterruptX(&H21, InregsX, OutregsX)
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 IF OutregsX.AX < 0 THEN
    Low = CLNG(OutregsX.AX + 65536)
 ELSE
    Low = CLNG(OutregsX.AX)
 END IF
 IF OutregsX.DX < 0 THEN
    High = CLNG(OutregsX.DX + 65536)
 ELSE
    High = CLNG(OutregsX.DX)
 END IF
 FileLength = High * &H10000 + Low
 RETURN

' close file.
CloseFile:
 InregsX.AX = &H3E00
 InregsX.BX = Handle
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' open file based on windows or dos loaded.
'   read file attribute,
'      reset read-only bit,
'   create file if nonexistent,
'   append nul byte to new file,
'   reopen file.
' return ValidFile true if open successful.
OpenFile:
 IF Windows.Detected THEN
    GOSUB OpenFile1
 ELSE
    GOSUB OpenFile2
 END IF
 RETURN

' open Windows file.
OpenFile1:
 ValidFile = False
 FileAttribute = False
 GOSUB GetWINAttribute
 ' can't read attribute of devices.
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    ' check file nonexistent.
    IF (OutregsX.AX) <> 2 THEN
       RETURN
    END IF
 END IF
 FileAttribute = OutregsX.CX
 ' reset read-only bit before opening file for read-write.
 IF (FileAttribute AND ReadOnlyBit) = ReadOnlyBit THEN
    GOSUB SetWInAttribute
 END IF
 GOSUB OpenWinFile
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ValidFile = True
    RETURN
 END IF
 ' check file nonexistent.
 IF (OutregsX.AX) <> 2 THEN
    RETURN
 END IF
 GOSUB CreateWinFile
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 GOSUB AppendFile
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 GOSUB CloseFile
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 GOSUB OpenWinFile
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 FileAttribute = False
 ValidFile = True
 RETURN

' open DOS file.
OpenFile2:
 ValidFile = False
 FileAttribute = False
 GOSUB GetDOSAttribute
 ' can't read attribute of devices.
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    ' check file nonexistent.
    IF (OutregsX.AX) <> 2 THEN
       RETURN
    END IF
 END IF
 FileAttribute = OutregsX.CX
 ' reset read-only bit before opening file for read-write.
 IF (FileAttribute AND ReadOnlyBit) = ReadOnlyBit THEN
    GOSUB SetDOSAttribute
 END IF
 GOSUB OpenDOSFile
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ValidFile = True
    RETURN
 END IF
 ' check file nonexistent.
 IF (OutregsX.AX) <> 2 THEN
    RETURN
 END IF
 GOSUB CreateDOSFile
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 GOSUB AppendFile
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 GOSUB CloseFile
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 GOSUB OpenDOSFile
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 FileAttribute = False
 ValidFile = True
 RETURN

' open file in windows.
OpenWinFile:
 InregsX.AX = &H716C
 InregsX.BX = &H2
 InregsX.CX = FileAttribute
 InregsX.DX = &H1
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.SI = VARPTR(ASCIIZ)
 InregsX.DI = &H1
 CALL InterruptX(&H21, InregsX, OutregsX)
 Handle = OutregsX.AX
 RETURN

' get file attributes.
GetWINAttribute:
 InregsX.AX = &H7143
 InregsX.BX = &H0
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' reset read-only bit.
SetWInAttribute:
 Attribute = (FileAttribute AND NOT ReadOnlyBit)
 InregsX.AX = &H7143
 InregsX.BX = &H1
 InregsX.CX = Attribute
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' open dos file.
OpenDOSFile:
 InregsX.AX = &H3D42
 InregsX.CX = &H27
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 Handle = OutregsX.AX
 RETURN

' get file attribute.
GetDOSAttribute:
 InregsX.AX = &H4300
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' reset read-only bit.
SetDOSAttribute:
 Attribute = (FileAttribute AND NOT ReadOnlyBit)
 InregsX.AX = &H4301
 InregsX.CX = Attribute
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' create new file in windows.
CreateWinFile:
 InregsX.AX = &H716C
 InregsX.BX = &H2
 InregsX.CX = &H0
 InregsX.DX = &H10
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.SI = VARPTR(ASCIIZ)
 InregsX.DI = &H1
 CALL InterruptX(&H21, InregsX, OutregsX)
 Handle = OutregsX.AX
 RETURN

' create new dos file.
CreateDOSFile:
 InregsX.AX = &H3C00
 InregsX.CX = &H0
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 Handle = OutregsX.AX
 RETURN

' write a nul byte to file.
AppendFile:
 FileByte = CHR$(0)
 GOSUB WriteFile
 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.
 High = INT(SeekPosition / 65536)
 Low = (SeekPosition AND 65535) - 1
 ' 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

' conanicalize filename for display purposes.
ShortFilename:
 ' read 8.3 filename.
 IF Windows.Detected THEN
    InregsX.AX = &H7160
    InregsX.CX = &H8001
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.SI = VARPTR(ASCIIZ)
    InregsX.ES = VARSEG(ASCIIZ2)
    InregsX.DI = VARPTR(ASCIIZ2)
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    InregsX.AX = &H6000
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.SI = VARPTR(ASCIIZ)
    InregsX.ES = VARSEG(ASCIIZ2)
    InregsX.DI = VARPTR(ASCIIZ2)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' store 8.3 filename.
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    Filename = ASCIIZ2
    Imbedded = INSTR(Filename, CHR$(0))
    IF Imbedded THEN
       Filename = LEFT$(Filename, Imbedded - 1)
    END IF
 END IF

 ' conanicalize filename.
 Filename = Conanicalize$(Filename)
 RETURN

' restore read-only attribute.
ResetAttribute:
 IF (FileAttribute AND ReadOnlyBit) = ReadOnlyBit THEN
    IF Windows.Detected THEN
       GOSUB ResetWINAttribute
    ELSE
       GOSUB ResetDOSAttribute
    END IF
 END IF
 RETURN

' restore windows read-only attribute.
ResetWINAttribute:
 InregsX.AX = &H7143
 InregsX.BX = &H1
 InregsX.CX = FileAttribute
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' restore DOS read-only attribute.
ResetDOSAttribute:
 InregsX.AX = &H4301
 InregsX.CX = FileAttribute
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

REM Mouse routines follow:

' check for mouse.
CheckMouse:
 Mouse.Present = False
 CALL MouseFunction(CheckMouse, 0)
 IF OutregsX.AX = &HFFFF THEN
    Mouse.Present = True
    ' set pixels per row/column.
    InregsX.AX = 7
    InregsX.BX = 0
    InregsX.CX = 0
    InregsX.DX = 632
    CALL InterruptX(&H33, InregsX, OutregsX)
    InregsX.AX = 8
    InregsX.BX = 0
    InregsX.CX = 0
    InregsX.DX = 192
    CALL InterruptX(&H33, InregsX, OutregsX)
 END IF
 RETURN

' move mouse cursor.
MoveMouse:
 ' check mouse boundaries.
 IF Mouse.Row = 1 THEN
    NewMenuSelection = 0
    SELECT CASE Mouse.Column
    CASE 6 TO 9 ' File
       NewMenuSelection = 1
    CASE 16 TO 21 ' Charts
       NewMenuSelection = 2
    CASE 26 TO 29 ' Dump
       NewMenuSelection = 3
    CASE 36 TO 39 ' Edit
       NewMenuSelection = 4
    CASE 46 TO 49 ' Jump
       NewMenuSelection = 5
    CASE 56 TO 60 ' Print
       NewMenuSelection = 6
    CASE 64 TO 69 ' Search
       NewMenuSelection = 7
    END SELECT
    IF NewMenuSelection > False THEN
       IF NewMenuSelection <> CurrentMenu THEN
          IF CurrentMenu > False THEN
             ' remove hilight menu tab.
             IF Mouse.Present THEN
                CALL MouseFunction(HideMouse, 0)
             END IF
             COLOR White, Black
             GOSUB DisplayTab
             IF Mouse.Present THEN
                CALL MouseFunction(ShowMouse, 0)
             END IF
          END IF
       END IF
       CurrentMenu = NewMenuSelection
       CurrentMenuSelection = 1
    END IF
    ' hilight menu tab.
    IF CurrentMenu > False THEN
       IF Mouse.Present THEN
          CALL MouseFunction(HideMouse, 0)
       END IF
       COLOR White, Blue
       GOSUB DisplayTab
       IF Mouse.Present THEN
          CALL MouseFunction(ShowMouse, 0)
       END IF
    END IF
    RETURN
 END IF
 IF Mouse.Row > 3 AND Mouse.Row < 20 THEN
    IF Mouse.Column > 5 AND Mouse.Column < 50 THEN
       ' check screen character.
       VarX = SCREEN(Mouse.Row, Mouse.Column)
       ' reset position and return.
       IF VarX = 32 THEN
          GOSUB RestorePosition
          RETURN
       END IF
       ' get new position and reset there.
       IF VarX <> 32 THEN
          IF Old.Mouse.Row > False AND Old.Mouse.Column > False THEN
             Old.Value = False
             IF Old.Mouse.Row = PageRow + 3 THEN
                IF Old.Mouse.Column = Column + 5 OR Old.Mouse.Column = Column + 6 THEN
                   Old.Value = True
                END IF
             END IF
             IF Old.Value = False THEN
                GOSUB MoveOldPosition
             ELSE
                GOSUB RestoreOldPosition
             END IF
          END IF
          GOSUB MoveNewPosition
          RETURN
       END IF
    END IF
    ' check boundary edges.
    IF Mouse.Column = 5 OR Mouse.Column = 50 THEN
       GOSUB RestorePosition
       RETURN
    END IF
 END IF
 ' reset position anyway.
 GOSUB RestorePosition
 RETURN

' restores old mouse position.
MoveOldPosition:
 GOSUB CalculatePosition4
 TempPosition3 = NewPosition
 GOSUB CalculatePosition2
 GOSUB CalculateColumn2
 COLOR White
 LOCATE PageRow2 + 3, Column2 + 5, 0
 VarX = SCREEN(PageRow2 + 3, Column2 + 5)
 PRINT CHR$(VarX);
 LOCATE PageRow2 + 3, Column2 + 6, 0
 VarX = SCREEN(PageRow2 + 3, Column2 + 6)
 PRINT CHR$(VarX);
 COLOR Plain
 RETURN

' restores original mouse position.
RestoreOldPosition:
 SeekPosition = FilePosition
 GOSUB LseekFile
 GOSUB ReadFile
 FileByte = Buffer
 ByteValue = ASC(FileByte)
 GOSUB CalculateColumn
 COLOR Yellow
 LOCATE PageRow + 3, Column + 5, 0
 PRINT RIGHT$("00" + HEX$(ByteValue), 2);
 COLOR Plain
 RETURN

' moves to new mouse position.
MoveNewPosition:
 GOSUB CalculatePosition3
 FilePosition2 = NewPosition
 TempPosition3 = NewPosition
 GOSUB CalculatePosition2
 GOSUB CalculateColumn2
 COLOR White, Blue
 LOCATE PageRow2 + 3, Column2 + 5, 0
 VarX = SCREEN(PageRow2 + 3, Column2 + 5)
 PRINT CHR$(VarX);
 LOCATE PageRow2 + 3, Column2 + 6, 0
 VarY = SCREEN(PageRow2 + 3, Column2 + 6)
 PRINT CHR$(VarY);
 COLOR Plain, Black
 IF Mouse.Row > 3 AND Mouse.Row < 20 THEN
    IF Mouse.Column > 5 AND Mouse.Column < 50 THEN
       Old.Mouse.Row = Mouse.Row
       Old.Mouse.Column = Mouse.Column
    END IF
 END IF
 AsciiValue2 = VAL("&H" + CHR$(VarX) + CHR$(VarY))
 GOSUB DisplayFilename2
 RETURN

RestorePosition:
 IF Old.Mouse.Row > 3 AND Old.Mouse.Row < 20 THEN
    IF Old.Mouse.Column > 5 AND Old.Mouse.Column < 50 THEN
       VarX = SCREEN(Old.Mouse.Row, Old.Mouse.Column)
       IF VarX <> 32 THEN
          GOSUB CalculatePosition4
          TempPosition3 = NewPosition
          GOSUB CalculatePosition2
          GOSUB CalculateColumn2
          COLOR White, Blue
          LOCATE PageRow2 + 3, Column2 + 5, 0
          VarX = SCREEN(PageRow2 + 3, Column2 + 5)
          PRINT CHR$(VarX);
          LOCATE PageRow2 + 3, Column2 + 6, 0
          VarY = SCREEN(PageRow2 + 3, Column2 + 6)
          PRINT CHR$(VarY);
          COLOR Plain, Black
       END IF
    END IF
 END IF
 RETURN

' redraws menu tab.
DisplayTab:
 SELECT CASE CurrentMenu
 CASE 1 ' File
    LOCATE 1, 6, 1
    PRINT "File";
 CASE 2 ' Charts
    LOCATE 1, 16, 1
    PRINT "Charts";
 CASE 3 ' Dump
    LOCATE 1, 26, 1
    PRINT "Dump";
 CASE 4 ' Edit
    LOCATE 1, 36, 1
    PRINT "Edit";
 CASE 5 ' Jump
    LOCATE 1, 46, 1
    PRINT "Jump";
 CASE 6 ' Print
    LOCATE 1, 56, 1
    PRINT "Print";
 CASE 7 ' Search
    LOCATE 1, 64, 1
    PRINT "Search";
 END SELECT
 COLOR White, Black
 RETURN

' process left mouse button.
MouseButton1:
 ' stop program.
 IF Mouse.Row = 3 AND Mouse.Column = 77 THEN
    ' hide mouse cursor.
    CALL MouseFunction(HideMouse, 0)
    GOTO StopLabel
 END IF
 ' page up.
 IF Mouse.Row = 3 AND Mouse.Column = 52 THEN
    IF FileLength THEN
       GOSUB PageUp
    END IF
    RETURN
 END IF
 ' page down.
 IF Mouse.Row = 20 AND Mouse.Column = 52 THEN
    IF FileLength THEN
       GOSUB PageDown
    END IF
    RETURN
 END IF
 ' line up.
 IF Mouse.Row = 4 AND Mouse.Column = 52 THEN
    IF FileLength THEN
       GOSUB CursorUp
    END IF
    RETURN
 END IF
 ' line down.
 IF Mouse.Row = 19 AND Mouse.Column = 52 THEN
    IF FileLength THEN
       GOSUB CursorDown
    END IF
    RETURN
 END IF
 ' select drop down menu.
 IF Mouse.Row = 1 THEN
    SELECT CASE Mouse.Column
    CASE 6 TO 9 ' File
       NewMenuSelection = 1
    CASE 16 TO 21 ' Charts
       NewMenuSelection = 2
    CASE 26 TO 29 ' Dump
       NewMenuSelection = 3
    CASE 36 TO 39 ' Edit
       NewMenuSelection = 4
    CASE 46 TO 49 ' Jump
       NewMenuSelection = 5
    CASE 56 TO 60 ' Print
       NewMenuSelection = 6
    CASE 64 TO 69 ' Search
       NewMenuSelection = 7
    CASE ELSE
       RETURN
    END SELECT
    IF NewMenuSelection <> CurrentMenu THEN
       IF CurrentMenu > False THEN
          IF Mouse.Present THEN
             CALL MouseFunction(HideMouse, 0)
          END IF
          ' remove hilight menu tab.
          COLOR White, Black
          GOSUB DisplayTab
          IF Mouse.Present THEN
             CALL MouseFunction(ShowMouse, 0)
          END IF
       END IF
    END IF
    CurrentMenu = NewMenuSelection
    CurrentMenuSelection = 1
    IF Mouse.Present THEN
       CALL MouseFunction(HideMouse, 0)
    END IF
    ' hilight menu tab.
    COLOR White, Blue
    GOSUB DisplayTab
    IF Mouse.Present THEN
       CALL MouseFunction(ShowMouse, 0)
    END IF
    ' check file.
    IF FileLength = 0 THEN
       IF CurrentMenu > 1 THEN
          RETURN
       END IF
    END IF
    ' jump to file menu.
    IF Mouse.Present THEN
       CALL MouseFunction(HideMouse, 0)
    END IF
    GOTO Menu2
 END IF
 ' move to new byte.
 IF Mouse.Row > 3 AND Mouse.Row < 20 THEN
    IF Mouse.Column > 5 AND Mouse.Column < 50 THEN
       VarX = SCREEN(Mouse.Row, Mouse.Column)
       IF VarX = 32 THEN
          GOSUB RestorePosition
          RETURN
       END IF
       IF VarX <> 32 THEN
          GOSUB CalculatePosition3
          GOSUB ClearPageByte
          FilePosition = NewPosition
          GOSUB CalculatePosition1
          GOSUB DisplayPageByte
          GOSUB DisplayFilename
          RETURN
       END IF
    END IF
 END IF
 RETURN

' process right mouse button.
MouseButton2:
 ' move to new byte and edit.
 IF Mouse.Row > 3 AND Mouse.Row < 20 THEN
    IF Mouse.Column > 5 AND Mouse.Column < 50 THEN
       VarX = SCREEN(Mouse.Row, Mouse.Column)
       IF VarX = 32 THEN
          GOSUB RestorePosition
          RETURN
       END IF
       IF VarX <> 32 THEN
          GOSUB CalculatePosition3
          GOSUB ClearPageByte
          FilePosition = NewPosition
          GOSUB CalculatePosition1
          GOSUB DisplayPageByte
          GOSUB DisplayFilename
          GOSUB ChangeHexValue
          RETURN
       END IF
    END IF
 END IF
 RETURN

 REM End-of-program. Please deliver the cats now.
