REM Program: Hex Editor v5.1a, Module 1 of 5, PD 2003.
REM Author: Erik Jon Oredson AS. Csci
REM Release: 10/03/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)

' store mouse timing.
MouseTime = 1!

' check mouse present.
Mouse.Present = TestMouse

' verify windows loaded.
Windows.Detected = TestWindows

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

' init undo file.
GOSUB OpenUndoFile

' init marker file.
GOSUB OpenMarkerFile

' init paste file.
GOSUB InitializePaste

' dimension multiple files array structure.
REDIM File(9) AS FileType
REDIM TempFiles(9) AS STRING

' dimension menu area arrays.
REDIM Area1(22, 42) AS INTEGER
REDIM Area2(22, 42) 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

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

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

' init 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

' define screen offsets for file menu box.
' allows menu reposotioning.
Xcoor = 5
Ycoor = 20

' read config file.
CALL ReadConfigFile(FileName)
IsMenuFile = 0

' check startup config edit file.
IF Filename <> "" THEN
   CommandLine$ = Filename
   GOTO StartFile
END IF

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

' read command line.
GOSUB ReadCommandLine

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

' filename entry starts here.
BeginFile:

' call file menu box.
CALL Menu(Drive$, Directory$, FileSpec1$, FileSpec2$)
IsMenuFile = -1
CLOSE #2, #3

' check filename.
IF FileSpec2$ = Nul THEN
   ' verify a file open already.
   IF NumberFiles >=1 THEN
      GOTO TopProgram
   END IF

   ' exit program.
   GOTO BootUsage
END IF

' get filename from menu box.
IF Directory$ = "" THEN
   Filename = Drive$ + ":\" + FileSpec2$
ELSE
   Filename = Drive$ + ":\" + Directory$ + "\" + FileSpec2$
END IF
CommandLine$ = Filename

' filename entry loop starts here.
StartFile:

' start filename entry loop.
DO
   ' check filenames
   IF CommandLine$ = "" THEN
      EXIT DO
   END IF

   ' get filename from command line.
   CommandLine$ = RTRIM$(CommandLine$)
   CommandLine$ = LTRIM$(CommandLine$)

   ' check menu filename returned.
   IF IsMenuFile THEN
      Filename = CommandLine$
      CommandLine$ = ""
   END IF
   IF IsMenuFile = 0 THEN
      IF LEFT$(CommandLine$, 1) = Quote THEN
         Var = INSTR(2, CommandLine$, Quote)
         IF Var = 0 THEN
            GOTO BeginFile
         END IF
         Filename = LEFT$(CommandLine$, Var - 1)
         Filename = MID$(Filename, 2)
         IF Filename = Nul THEN
            GOTO BeginFile
         END IF
         CommandLine$ = MID$(CommandLine$, Var + 1)
      ELSE
         Var = INSTR(CommandLine$, " ")
         IF Var THEN
            Filename = LEFT$(CommandLine$, Var - 1)
            CommandLine$ = MID$(CommandLine$, Var + 1)
         ELSE
            Filename = CommandLine$
            CommandLine$ = Nul
         END IF
      END IF
   END IF

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

   ' 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

   ' check max files.
   IF NumberFiles = 9 THEN
      EXIT DO
   END IF

   ' check filelist character.
   Var3$ = Filename
   DO
      ' check multiple filenames.
      Var3 = INSTR(Var3$, CHR$(13))
      IF Var3 THEN
         Var$ = LEFT$(Var3$, Var3 - 1)
         Var3$ = MID$(Var3$, Var3 + 1)
      ELSE
         IF LEN(Var3$) THEN
            Var$ = Var3$
            Var3$ = ""
         ELSE
            EXIT DO
         END IF
      END IF
      IF LEFT$(Var$, 1) = "@" THEN
         Var2$ = MID$(Var$, 2)
         CLOSE #1
         IF DIR$(Var2$)<>"" THEN
            ErrorTrap = False
            OPEN Var2$ FOR INPUT AS #1
            IF ErrorTrap = False THEN
               DO UNTIL EOF(1)
                  LINE INPUT #1, Var$
                  Var$ = RTRIM$(Var$)
                  Var$ = LTRIM$(Var$)
                  IF LEN(Var$) THEN
                     ' check max files.
                     IF NumberFiles = 9 THEN
                        EXIT DO
                     END IF
                     GOSUB LoadMultipleFile
                  END IF
               LOOP
            END IF
         END IF
      ELSE
         GOSUB LoadMultipleFile
      END IF
   LOOP
LOOP

' check files loaded.
IF NumberFiles = 0 THEN
   GOTO BeginFile
END IF

' initialize file buffer.
IF FileLength > 0 THEN
   SeekPosition = 1
   GOSUB LseekFile
   GOSUB ReadFile
   FileByte = Buffer
   AsciiValue = ASC(FileByte)
END IF

' main program loop starts here.
TopProgram:

' reset mouse activity.
IF Mouse.Present THEN
   ' clear mouse buttons.
   CALL MouseFunction(Button, 0)
   CALL MouseFunction(Button, 1)

   ' store mouse position.
   CALL MouseFunction2(1,1)
   CALL MouseFunction(Position, 0)

   ' reset mouse position.
   Mouse.X = OutregsX.DX / 8 + 1
   Mouse.Y = OutregsX.CX / 8 + 1
END IF

' setup editing screen.
Header = False
GOSUB RedrawScreen
GOSUB SMouse

' 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 MouseDriver2

      ' check left mouse button click.
      IF Mouse.ButtonX THEN
         ' store mouse left click release.
         Mouse.Row = Mouse.RowX
         Mouse.Column = Mouse.ColumnX
         ' process mouse row/column.
         GOSUB MouseButton1
         ' init mouse drag pivot byte.
         CopyStart = 0
         Time1 = 0!
         IF Mouse.Row > 3 AND Mouse.Row < 20 THEN
            IF Mouse.Column >= 6 AND Mouse.Column <= 49 THEN
               CopyStart = FilePosition
            END IF
         END IF
      ELSE
         ' check left mouse button drag.
         IF Mouse.Button1 = 2 THEN
            IF Mouse.Row OR Mouse.Column THEN
               Time1 = 0!
               CALL MouseButton1Drag(0)
            END IF
         ELSE
            ' check left mouse button drag/scroll up.
            IF Mouse.Button1 = 3 THEN
               IF Mouse.Row OR Mouse.Column THEN
                  CALL MouseButton1Drag(9)
               END IF
            ELSE
               ' check left mouse button drag/scroll down.
               IF Mouse.Button1 = 4 THEN
                  IF Mouse.Row OR Mouse.Column THEN
                     CALL MouseButton1Drag(10)
                  END IF
               ELSE
                  ' check right/middle mouse button.
                  IF Mouse.Button2 OR Mouse.Button3 THEN
                     GOSUB MouseButton2
                  ELSE
                     ' check mouse position.
                     IF Mouse.Row OR Mouse.Column THEN
                        GOSUB MoveMouse
                     END IF
                  END IF
               END IF
            END IF
         END IF
      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
      GOSUB HMouse
      GOSUB LocateCursor2
   END IF

   ' set to keyboard port segment.
   DEF SEG = &H40
   ' get shift state from keyboard address.
   ShiftFlag = PEEK(&H17)
   ' reset to basic segment.
   DEF SEG

   ' store shift state.
   LeftShift = ShiftFlag AND 1 ' left shift.
   RightShift = ShiftFlag AND 2 ' right shift.

   ' check shift flags.
   IF LeftShift OR RightShift THEN
      SELECT CASE LEN(CharInput$)
      CASE 1
         SELECT CASE ASC(CharInput$)
         CASE 33 ' ! = display page/row/column.
            GOSUB DisplayData
         CASE 34 ' " = display file path.
            GOSUB DisplayPath
         CASE 63 ' ? = display filelength\undos.
            GOSUB DisplayInfo
         CASE 123 ' { = display paste entries.
            GOSUB DisplayPasteUndos
         CASE 126 ' ~ = display last page\markers.
            GOSUB DisplayLengths
         CASE 43 ' + = list range of markers.
            IF FileLength THEN
               GOSUB StoreCurrentFile
               CALL Marker(1)
               IF ValidFunction THEN
                  COLOR Plain
                  ScreenDrawn = True
                  CALL DisplayScreen
                  GOSUB RestoreCurrentFile
                  GOSUB RedrawScreen2
                  GOSUB RestoreHilightBytes
               END IF
            END IF
         ' remaining byte hilighting functions.
         CASE 52 ' Shift-Left
            IF FileLength THEN
               CALL MouseButton1Drag(1)
            END IF
         CASE 54 ' Shift-Right
            IF FileLength THEN
               CALL MouseButton1Drag(2)
            END IF
         CASE 56 ' Shift-Up
            IF FileLength THEN
               CALL MouseButton1Drag(3)
            END IF
         CASE 50 ' Shift-Down
            IF FileLength THEN
               CALL MouseButton1Drag(4)
            END IF
         CASE 57 ' Shift-PageUp
            IF FileLength THEN
               CALL MouseButton1Drag(5)
            END IF
         CASE 51 ' Shift-PageDown
            IF FileLength THEN
               CALL MouseButton1Drag(6)
            END IF
         CASE 49 ' Shift-End
            IF FileLength THEN
               CALL MouseButton1Drag(7)
            END IF
         CASE 55 ' Shift-Home
            IF FileLength THEN
               CALL MouseButton1Drag(8)
            END IF
         END SELECT
      CASE 2
         SELECT CASE ASC(RIGHT$(CharInput$, 1))
         ' byte hlighting functions.
         CASE 75 ' Shift-Left
            IF FileLength THEN
               CALL MouseButton1Drag(1)
            END IF
         CASE 77 ' Shift-Right
            IF FileLength THEN
               CALL MouseButton1Drag(2)
            END IF
         CASE 72 ' Shift-Up
            IF FileLength THEN
               CALL MouseButton1Drag(3)
            END IF
         CASE 80 ' Shift-Down
            IF FileLength THEN
               CALL MouseButton1Drag(4)
            END IF
         CASE 73 ' Shift-PageUp
            IF FileLength THEN
               CALL MouseButton1Drag(5)
            END IF
         CASE 81 ' Shift-PageDown
            IF FileLength THEN
               CALL MouseButton1Drag(6)
            END IF
         CASE 79 ' Shift-End
            IF FileLength THEN
               CALL MouseButton1Drag(7)
            END IF
         CASE 71 ' Shift-Home
            IF FileLength THEN
               CALL MouseButton1Drag(8)
            END IF
         END SELECT
      END SELECT
   ELSE
      ' parse the key.
      SELECT CASE LEN(CharInput$)
      CASE 1 ' single ascii key.
         SELECT CASE ASC(CharInput$)
         CASE 9 ' Tab = Toggle window.
            IF FileLength THEN
               GOSUB TabWindow
            END IF
         CASE 13 ' Enter = Change byte value.
            IF FileLength THEN
               GOSUB ChangeHexValue
            END IF
         CASE 27 ' Escape = Start drop down menu.
            IF CurrentMenu <> 1 THEN
               ' remove hilight menu tab.
               COLOR White, Black
               GOSUB DisplayTab
            END IF
            CurrentMenu = 1
            CurrentMenuSelection = 1
            GOTO Menu2
         CASE 59 ' ; = display hilight range.
            GOSUB DisplayCopy
         CASE 49 ' 1 = Add marker.
            IF FileLength THEN
               CALL Marker(2)
            END IF
         CASE 50 ' 2 = Add marker value.
            IF FileLength THEN
               CALL Marker(3)
            END IF
         CASE 51 ' 3 = delete marker.
            IF FileLength THEN
               CALL Marker(4)
            END IF
         CASE 52 ' 4 = delete specified marker.
            IF FileLength THEN
               CALL Marker(5)
            END IF
         CASE 53 ' 5 = jump to previous marker.
            IF FileLength THEN
               CALL Marker(6)
            END IF
         CASE 54 ' 6 = jump to current marker.
            IF FileLength THEN
               CALL Marker(7)
            END IF
         CASE 55 ' 7 = Jump to next marker.
            IF FileLength THEN
               CALL Marker(8)
            END IF
         CASE 56 ' 8 = Jump to specified marker.
            IF FileLength THEN
               CALL Marker(9)
            END IF
         CASE 57 ' 9 = List all markers.
            IF FileLength THEN
               GOSUB StoreCurrentFile
               CALL Marker(10)
               COLOR Plain
               ScreenDrawn = True
               CALL DisplayScreen
               GOSUB RestoreCurrentFile
               GOSUB RedrawScreen2
               GOSUB RestoreHilightBytes
            END IF
         CASE 48 ' 0 = List specified markers.
            IF FileLength THEN
               CALL Marker(11)
            END IF
         CASE 45 ' - = List marker of value.
            IF FileLength THEN
               GOSUB StoreCurrentFile
               CALL Marker(12)
               COLOR Plain
               ScreenDrawn = True
               CALL DisplayScreen
               GOSUB RestoreCurrentFile
               GOSUB RedrawScreen2
               GOSUB RestoreHilightBytes
            END IF
         CASE 61 ' = = Delete range of markers.
            IF FileLength THEN
               CALL Marker(13)
            END IF
         CASE 91 ' [ = Delete all markers.
            IF FileLength THEN
               CALL Marker(14)
            END IF
         CASE 93 ' ] = Delete markers of specified values.
            IF FileLength THEN
               CALL Marker(15)
            END IF
         CASE 3 ' Control-C = Copy paste area.
            IF FileLength THEN
               CALL Clipboard(1)
            END IF
         CASE 22 ' Control-V = Paste area.
            IF FileLength THEN
               CALL Clipboard(2)
            END IF
         CASE 24 ' Control-X = Undo previous paste.
            IF FileLength THEN
               CALL Clipboard(3)
            END IF
         CASE 26 ' Control-Z = Undo all pastes.
            IF FileLength THEN
               CALL Clipboard(4)
            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 = Nul activity.
            ' 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 = Switch windows.
            IF FileLength THEN
               GOSUB TabWindow
            END IF
         CASE 30 ' Alt-A = Append multiple bytes.
            IF FileLength THEN
               GOSUB AppendByte
            END IF
         CASE 48 ' Alt-B = Append multiple null bytes.
            IF FileLength THEN
               GOSUB AppendNullBytes
            END IF
         CASE 46 ' Alt-C = ANSI Chart.
            GOSUB ANSIChart
         CASE 32 ' Alt-D = HEX screen dump.
            IF FileLength THEN
               GOSUB DumpScreen
            END IF
         CASE 18 ' Alt-E = HEX file dump.
            IF FileLength THEN
               GOSUB DumpHEXFile
            END IF
         CASE 33 ' Alt-F = Start drop down menu.
            IF CurrentMenu <> 1 THEN
               ' remove hilight menu tab.
               COLOR White, Black
               GOSUB DisplayTab
            END IF
            CurrentMenu = 1
            CurrentMenuSelection = 1
            GOTO Menu2
         CASE 34 ' Alt-G = Append multiple specified bytes.
            IF FileLength THEN
               GOSUB AppendAnyByte
            END IF
         CASE 35 ' Alt-H = HEX chart.
            GOSUB HEXChart
         CASE 36 ' Alt-J = Jump to byte.
            IF FileLength THEN
               GOSUB JumpByte
            END IF
         CASE 37 ' Alt-K = Search for ASCII string.
            IF FileLength THEN
               GOSUB SearchASCII
            END IF
         CASE 38 ' Alt-L = Jump to page.
            IF FileLength THEN
               GOSUB JumpPage
            END IF
         CASE 50 ' Alt-M = Append ASCII string.
            IF FileLength THEN
               GOSUB AppendASCII
            END IF
         CASE 49 ' Alt-N = Open new file.
            IF NumberFiles = 9 THEN
               StatusMessage = "Maximum of 9 files open."
               GOSUB DisplayStatus2
            ELSE
               GOSUB StoreCurrentFile
               GOSUB NewFile
               GOTO BeginFile
            END IF
         CASE 24 ' Alt-O = Print help screens.
            GOSUB PrintHelp
         CASE 25 ' Alt-P = HEX screen print.
            IF FileLength THEN
               GOSUB PrintScreen
            END IF
         CASE 16 ' Alt-Q = Display help screens.
            GOSUB DisplayHelp
         CASE 19 ' Alt-R = Redraw screen.
            GOSUB StoreCurrentFile
            COLOR Plain
            ScreenDrawn = True
            CALL DisplayScreen
            GOSUB RestoreCurrentFile
            GOSUB RedrawScreen2
            GOSUB RestoreHilightBytes
         CASE 31 ' Alt-S = Search for multiple bytes.
            IF FileLength THEN
               GOSUB SearchByte
            END IF
         CASE 20 ' Alt-T = HEX file print.
            IF FileLength THEN
               GOSUB PrintFile
            END IF
         CASE 22 ' Alt-U = Undo last byte change.
            IF FileLength THEN
               GOSUB UndoByte
            END IF
         CASE 47 ' Alt-V = View files.
            CALL ViewFiles(Var)
            IF Var > 0 THEN
               NextFile = Var
               GOSUB SelectFile
            END IF
            GOSUB DisplayFilename
         CASE 17 ' Alt-W = Close file.
            GOSUB CloseCurrentFile
            IF NumberFiles = 0 THEN
               GOSUB NewFile
               GOTO BeginFile
            END IF
         CASE 45 ' Alt-X = Exit program.
            EXIT DO ' exit program.
         CASE 21 ' Alt-Y = Toggle right window.
            IF FileLength THEN
               GOSUB ToggleRight
            END IF
         CASE 44 ' Alt-Z = Undo all byte changes.
            IF FileLength THEN
               GOSUB UndoAll
            END IF
         CASE 133 ' F11 = Drop to DOS.
            GOSUB StoreCurrentFile
            COLOR Plain
            CLS
            PRINT "Type 'Exit' to return to editor.."
            SHELL
            ScreenDrawn = True
            CALL DisplayScreen
            GOSUB RestoreCurrentFile
            GOSUB RedrawScreen2
            GOSUB RestoreHilightBytes
         CASE 120 TO 128 ' Alt-1 to Alt-9 = Select file.
            NextFile = ASC(RIGHT$(CharInput$, 1)) - 119
            GOSUB SelectFile
         CASE 59 ' F1 - toggle filename display.
            IF FileLength THEN
               FileDisplay = NOT FileDisplay
               IF FileDisplay = 0 THEN
                  GOSUB DisplayFilename3
               ELSE
                  GOSUB DisplayScreen3
               END IF
            END IF
         CASE 60 ' F2 - toggle bottom row help keys.
            ScreenRow = NOT ScreenRow
            GOSUB StoreCurrentFile
            COLOR Plain
            ScreenDrawn = True
            CALL DisplayScreen
            GOSUB RestoreCurrentFile
            GOSUB RedrawScreen2
            GOSUB RestoreHilightBytes
         CASE 64 ' F6 - goto next file.
            GOSUB GotoNextFile
         CASE 65 ' F7 - goto previous file.
            GOSUB GotoPreviousFile
         CASE 66 ' F8 - load new file.
            IF NumberFiles = 9 THEN
               StatusMessage = "Maximum of 9 files open."
               GOSUB DisplayStatus2
            ELSE
               GOSUB StoreCurrentFile
               GOSUB NewFile
               GOTO BeginFile
            END IF
         CASE 67 ' F9 - close current file.
            GOSUB CloseCurrentFile
            IF NumberFiles = 0 THEN
               GOSUB NewFile
               GOTO BeginFile
            END IF
         CASE 68 ' F10 - view files.
            CALL ViewFiles(Var)
            IF Var > 0 THEN
               NextFile = Var
               GOSUB SelectFile
            END IF
            GOSUB DisplayFilename
         CASE 82 ' Insert = Change string value.
            IF FileLength THEN
               GOSUB ChangeASCIIValues
            END IF
         CASE 83 ' Delete = Change HEX string.
            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 ' impossible to get here.
      END SELECT
   END IF

   ' reset mouse activity.
   IF Mouse.Present THEN
      GOSUB SMouse
      GOSUB RestorePosition
      GOSUB LocateCursor2
   END IF
LOOP

' stop program.
StopLabel:

' clear hilighted byte.
GOSUB ClearPageByte
GOSUB MoveOldPosition

' clear status areas.
GOSUB ClearStatus
GOSUB DisplayScreen3

' display goodbye message.
COLOR Yellow
LOCATE 2, 4
Z$ = "You have been using a public domain program written by "
Z$ = Z$ + LEFT$(Author, 16) + ".." ' insert your name here.
Z$ = LEFT$(Z$, 74)
PRINT Z$;
LOCATE 2, 4

' 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 CloseAllFiles
GOSUB ResetAllAttributes

' goto stop program.
COLOR Plain
GOTO StopProgram
END

' load filespec.
LoadMultipleFile:
 ' check max files.
 IF NumberFiles = 9 THEN
    RETURN
 END IF

 ' trim filename.
 Filename = Var$
 Filename = RTRIM$(Filename)
 Filename = LTRIM$(Filename)

 ' 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

 ' check wildcard characters.
 IF INSTR(Filename, "?") OR INSTR(Filename, "*") THEN
    ' store filename drive/directory
    D$ = ""
    C$ = ""
    FOR Var = LEN(Filename) TO 1 STEP -1
       IF MID$(Filename, Var, 1) = "\" THEN
          D$ = LEFT$(Filename, Var)
          EXIT FOR
       END IF
    NEXT
    IF MID$(Filename, 2, 1) = ":" THEN
       C$ = LEFT$(Filename, 2)
       D$ = MID$(D$, 3)
    END IF
    ' clear filename array.
    F = 0
    FOR Var = 1 TO 9
       TempFiles(Var) = ""
    NEXT
    ' store filename array.
    X$ = DIR$(Filename)
    DO
       IF LEN(X$) THEN
          IF F < 9 THEN
             F = F + 1
             TempFiles(F) = C$ + D$ + X$
          END IF
       ELSE
          EXIT DO
       END IF
       X$ = DIR$
    LOOP
    ' load filename array.
    FOR Var = 1 TO 9
       IF RTRIM$(TempFiles(Var)) <> "" THEN
          ' check max files.
          IF NumberFiles = 9 THEN
             RETURN
          END IF
          Filename = RTRIM$(TempFiles(Var))
          GOSUB LoadNewFile
       END IF
    NEXT
 ELSE
    ' load single file.
    GOSUB LoadNewFile
 END IF
 RETURN

' loads filename.
LoadNewFile:
 ' check max files.
 IF NumberFiles = 9 THEN
    RETURN
 END IF

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

 ' open/create file.
 GOSUB OpenFile

 ' conanicalize filename.
 GOSUB ShortFilename

 ' check open error.
 IF ValidFile THEN

    ' initialize multiple file variables.
    NumberFiles = NumberFiles + 1
    CurrentFile = NumberFiles
    GOSUB InitCurrentFile

    ' check length of file.
    GOSUB GetFileLength

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

    ' store filelength.
    File(CurrentFile).FileLength = FileLength

    ' restore current file variables.
    GOSUB RestoreCurrentFile

    ' init paste files.
    GOSUB InitPasteFiles
 END IF
 RETURN

' select specific file number.
SelectFile:
 IF NextFile <= NumberFiles THEN
    IF NextFile <> CurrentFile THEN
       GOSUB StoreCurrentFile
       CurrentFile = NextFile
       GOSUB RestoreCurrentFile
       GOSUB RedrawScreen2
       GOSUB RestoreHilightBytes
    END IF
 END IF
 RETURN

' goto next file.
GotoNextFile:
 IF NumberFiles = 1 THEN
    RETURN
 END IF
 GOSUB StoreCurrentFile
 CurrentFile = CurrentFile + 1
 IF CurrentFile > NumberFiles THEN
    CurrentFile = 1
 END IF
 GOSUB RestoreCurrentFile
 GOSUB RedrawScreen2
 GOSUB RestoreHilightBytes
 RETURN

' goto previous file.
GotoPreviousFile:
 IF NumberFiles = 1 THEN
    RETURN
 END IF
 GOSUB StoreCurrentFile
 CurrentFile = CurrentFile - 1
 IF CurrentFile = 0 THEN
    CurrentFile = NumberFiles
 END IF
 GOSUB RestoreCurrentFile
 GOSUB RedrawScreen2
 GOSUB RestoreHilightBytes
 RETURN

' close current file.
CloseCurrentFile:
 GOSUB CloseFile
 GOSUB ResetAttribute
 GOSUB PackFileArray
 RETURN

' pack file structure array.
PackFileArray:
 ' pack remaining structure.
 FOR Var = CurrentFile TO 8
    File(Var) = File(Var + 1)
 NEXT

 ' reduce files by one.
 NumberFiles = NumberFiles - 1

 ' check remaining files.
 IF NumberFiles > 0 THEN
    ' reset current file.
    IF CurrentFile > NumberFiles THEN
       CurrentFile = NumberFiles
    END IF

    ' restore current file.
    GOSUB RestoreCurrentFile
    GOSUB RedrawScreen2
    GOSUB RestoreHilightBytes
 END IF
 RETURN

' prepare for new file.
NewFile:
 ' reset window.
 CLS
 CurrentWindow = False
 CurrentWindow2 = False
 ScreenDrawn = False
 LOCATE 24, 1, 1
 COLOR Plain
 RETURN

' init multiple file variables.
InitCurrentFile:
 File(CurrentFile).ASCIIZ = ASCIIZ
 File(CurrentFile).FileAttribute = FileAttribute
 File(CurrentFile).Handle = Handle
 File(CurrentFile).ShortFilename = ShortFilename
 File(CurrentFile).Filename = Filename
 ' reset multiple file undo/marker variables.
 File(CurrentFile).CurrentUndo = 0
 File(CurrentFile).MarkerCount = 0
 File(CurrentFile).CurrentMarker = 0
 ' reset multiple file variables.
 File(CurrentFile).FilePage = 1
 File(CurrentFile).FilePosition = 1
 File(CurrentFile).PageRow = 1
 File(CurrentFile).PageColumn = 1
 File(CurrentFile).Buffer = CHR$(0)
 File(CurrentFile).FileByte = CHR$(0)
 File(CurrentFile).AsciiValue = False
 File(CurrentFile).FileLength = False
 File(CurrentFile).CopyPositionStart = 0
 File(CurrentFile).CopyPositionPivot = 0
 File(CurrentFile).CopyPositionEnd = 0
 RETURN

' store current file variables.
StoreCurrentFile:
 ' store multiple file variables.
 File(CurrentFile).ASCIIZ = ASCIIZ
 File(CurrentFile).FileAttribute = FileAttribute
 File(CurrentFile).Handle = Handle
 File(CurrentFile).ShortFilename = ShortFilename
 File(CurrentFile).Filename = Filename
 ' store multiple file undo/marker variables.
 File(CurrentFile).CurrentUndo = CurrentUndo
 File(CurrentFile).MarkerCount = MarkerCount
 File(CurrentFile).CurrentMarker = CurrentMarker
 ' store multiple file variables.
 File(CurrentFile).FilePage = FilePage
 File(CurrentFile).FilePosition = FilePosition
 File(CurrentFile).PageRow = PageRow
 File(CurrentFile).PageColumn = PageColumn
 File(CurrentFile).Buffer = Buffer
 File(CurrentFile).FileByte = FileByte
 File(CurrentFile).AsciiValue = AsciiValue
 File(CurrentFile).FileLength = FileLength
 File(CurrentFile).CopyPositionStart = CopyPositionStart
 File(CurrentFile).CopyPositionPivot = CopyPositionPivot
 File(CurrentFile).CopyPositionEnd = CopyPositionEnd
 RETURN

' restore current file variables.
RestoreCurrentFile:
 ' restore multiple file variables.
 ASCIIZ = File(CurrentFile).ASCIIZ
 FileAttribute = File(CurrentFile).FileAttribute
 Handle = File(CurrentFile).Handle
 ShortFilename = File(CurrentFile).ShortFilename
 Filename = File(CurrentFile).Filename
 ' restore multiple file undo/marker variables.
 CurrentUndo = File(CurrentFile).CurrentUndo
 MarkerCOunt = File(CurrentFile).MarkerCount
 CurrentMarker = File(CurrentFile).CurrentMarker
 ' restore multiple file variables.
 FilePage = File(CurrentFile).FilePage
 FilePosition = File(CurrentFile).FilePosition
 PageRow = File(CurrentFile).PageRow
 PageColumn = File(CurrentFile).PageColumn
 Buffer = File(CurrentFile).Buffer
 FileByte = File(CurrentFile).FileByte
 AsciiValue = File(CurrentFile).AsciiValue
 FileLength = File(CurrentFile).FileLength
 CopyPositionStart = 0
 CopyPositionPivot = 0
 CopyPositionEnd = 0
 GOSUB OpenPasteFiles
 RETURN

' init paste files for new file.
InitPasteFiles:
 ' make filename with process number.
 V1$ = "COPYFILE.DA" + MID$(STR$(Process.Number), 2)
 ' make filename with process number and file number.
 V2$ = "UNDOFIL" + MID$(STR$(Process.Number), 2) + ".DA" + MID$(STR$(CurrentFile), 2)
 ' get valid path.
 CALL OpenDataFiles(V1$, V2$)
 CLOSE #4, #5
 Check.Disk = True ' return from error routine.
 Disk.Ready = False
 ' attempt to open file.
 OPEN V1$ FOR RANDOM AS #4 LEN = 1
 Check.Disk = False ' reset error routine flag.
 IF Disk.Ready THEN
    GOTO Error.Exit ' exit editor.
 END IF
 Check.Disk = True ' return from error routine.
 Disk.Ready = False
 IF DIR$(V2$)<>"" THEN
    KILL V2$ ' remove temp file.
 END IF
 Check.Disk = False ' reset error routine flag.
 IF Disk.Ready THEN
    GOTO Error.Exit ' exit editor.
 END IF
 Check.Disk = True ' return from error routine.
 Disk.Ready = False
 ' attempt to open temp file.
 OPEN V2$ FOR RANDOM AS #5 LEN = 1
 Check.Disk = False ' reset error routine flag.
 IF Disk.Ready THEN
    GOTO Error.Exit ' exit editor.
 END IF
 RETURN

' open paste files for current file number.
OpenPasteFiles:
 ' make filename with process number.
 V1$ = "COPYFILE.DA" + MID$(STR$(Process.Number), 2)
 ' make filename with process number and file number.
 V2$ = "UNDOFIL" + MID$(STR$(Process.Number), 2) + ".DA" + MID$(STR$(CurrentFile), 2)
 ' get valid path.
 CALL OpenDataFiles(V1$, V2$)
 CLOSE #4, #5
 Check.Disk = True ' return from error routine.
 Disk.Ready = False
 ' attempt to open file.
 OPEN V1$ FOR RANDOM AS #4 LEN = 1
 Check.Disk = False ' reset error routine flag.
 IF Disk.Ready THEN
    GOTO Error.Exit ' exit editor.
 END IF
 Check.Disk = True ' return from error routine.
 Disk.Ready = False
 ' attemp to open file.
 OPEN V2$ FOR RANDOM AS #5 LEN = 1
 Check.Disk = False ' reset error routine flag.
 IF Disk.Ready THEN
    GOTO Error.Exit ' exit editor.
 END IF
 RETURN

' open undo file. also resets number of process running.
OpenUndoFile:
 ' reset process counter.
 Var = False
 ' start counter loop.
 DO
    ' make temp filename with counter.
    V1$ = "UNDOBYTE.DA" + MID$(STR$(Var), 2)
    V2$ = ""
    ' get valid path for file.
    CALL OpenDataFiles(V1$, V2$)
    ' check file open.
    CLOSE #6
    Check.Disk = True  ' return from error routine.
    Disk.Ready = False
    ' attempt to open file.
    OPEN V1$ FOR RANDOM AS #6 LEN = LEN(UndoFile)
    Check.Disk = False ' reset error routine flag.
    ' exit loop if valid file.
    IF Disk.Ready = False THEN
       EXIT DO
    END IF
    ' increment process counter.
    Var = Var + 1
    ' check maximum processes open.
    IF Var > 9 THEN
       Error.Trap = 90 ' to many processes error.
       GOTO Error.Exit ' exit editor.
    END IF
 LOOP
 ' store process number.
 Process.Number = Var
 RETURN

' open marker file.
OpenMarkerFile:
 ' make temp filename with process number.
 V1$ = "MARKERS1.DA" + MID$(STR$(Process.Number), 2)
 V2$ = ""
 ' get valid path.
 CALL OpenDataFiles(V1$, V2$)
 CLOSE #7
 Check.Disk = True ' return from error routine.
 Disk.Ready = False
 ' attempt to open file.
 OPEN V1$ FOR RANDOM AS #7 LEN = LEN(MarkerFile)
 Check.Disk = False ' reset error routine flag.
 IF Disk.Ready THEN
    GOTO Error.Exit ' exit editor.
 END IF
 RETURN

' init copy paste file.
InitializePaste:
 ' make temp filename with process number.
 V1$ = "COPYFILE.DA" + MID$(STR$(Process.Number), 2)
 V2$ = ""
 ' get valid path.
 CALL OpenDataFiles(V1$, V2$)
 CLOSE #4
 Check.Disk = True ' return from error routine.
 Disk.Ready = False
 ' check file exists.
 IF DIR$(V1$)<>"" THEN
    KILL V1$ ' delete file.
 END IF
 Check.Disk = False ' reset error routine flag.
 IF Disk.Ready THEN
    GOTO Error.Exit ' exit editor.
 END IF
 RETURN

' display command line instructions.
BootUsage:
IF Header = False THEN
   COLOR White
   PRINT "Hex Editor " + Version + " " + Release + "."
END IF
CALL DisplayBootUsage
COLOR Plain
GOTO StopProgram

' display filename border area
DisplayScreen3:
 LOCATE 3, 2
 COLOR Magenta
 PRINT CHR$(Vline);" ";
 COLOR Green
 PRINT CHR$(ULcorner); STRING$(46, Hline); CHR$(URcorner); " ";
 PRINT CHR$(ULcorner); STRING$(22, Hline); CHR$(URcorner);
 COLOR White
 PRINT "x";
 COLOR Magenta
 PRINT CHR$(Vline)
 GOSUB LocateCursor2
 RETURN

' display ANSI charts.
ANSIChart:
 CALL 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
 CALL 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 StoreCurrentFile
 COLOR Plain
 ScreenDrawn = True
 CALL DisplayScreen
 GOSUB RestoreCurrentFile
 GOSUB RedrawScreen2
 GOSUB RestoreHilightBytes
 RETURN

' display HEX charts.
HEXChart:
 CALL 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
 CALL 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 StoreCurrentFile
 COLOR Plain
 ScreenDrawn = True
 CALL DisplayScreen
 GOSUB RestoreCurrentFile
 GOSUB RedrawScreen2
 GOSUB RestoreHilightBytes
 RETURN

' function for unknown key.
HelpLine:
 GOSUB ClearStatus
 PRINT "Editing file: "; RTRIM$(Filename);
 COLOR White
 PRINT " Type Alt-Q for Help, Alt-O to print Help.";
 GOSUB LocateCursor2
 RETURN

' displays help screens.
DisplayHelp:
 GOSUB HMouse
 GOSUB StoreCurrentFile
 CALL HelpScreen
 COLOR Plain
 ScreenDrawn = True
 CALL DisplayScreen
 GOSUB RestoreCurrentFile
 GOSUB RedrawScreen2
 GOSUB RestoreHilightBytes
 GOSUB SMouse
 RETURN

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

' function for ? key.
DisplayInfo:
 GOSUB ClearStatus
 PRINT "Editing file: "; RTRIM$(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 LocateCursor2
 RETURN

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

' function for ~ key.
DisplayLengths:
 GOSUB ClearStatus
 PRINT "Editing file: "; RTRIM$(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
 Var1 = 0
 FOR Var2 = 1 TO MarkerCount
    IF Markers#(Var2) > 0# THEN
       Var1 = Var1 + 1
    END IF
 NEXT
 PRINT "(Markers: "; MID$(STR$(Var1), 2); ")";
 GOSUB LocateCursor2
 RETURN

' display copy start/end.
DisplayCopy:
 GOSUB ClearStatus
 PRINT "Editing file: "; RTRIM$(Filename); " ";
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 IF CurrentWindow2 = False THEN
    PRINT "(Copy position:";
    PRINT STR$(CopyPositionStart); " -"; STR$(CopyPositionEnd); ") ";
 ELSE
    PRINT "(CopyPosition: ";
    IF CopyPositionStart = 0 AND CopyPositionEnd = 0 THEN
       PRINT "00000000H - 00000000H) ";
    ELSE
       PRINT RIGHT$("00000000" + HEX$(CopyPositionStart - 1), 8); "H - ";
       PRINT RIGHT$("00000000" + HEX$(CopyPositionEnd - 1), 8); "H) ";
    END IF
 END IF
 GOSUB LocateCursor2
 RETURN

' display paste undos.
DisplayPasteUndos:
 GOSUB ClearStatus
 PRINT "Editing file: "; RTRIM$(Filename); " ";
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 IF LOF(5)=0 THEN
    PRINT " : No paste undos.";
 ELSE
    GET #5, 1, CopyByte
    ByteEntries = ASC(CopyByte) * 16 ^ 2
    GET #5, 2, CopyByte
    ByteEntries = ByteEntries + ASC(CopyByte)
    PRINT " :"; ByteEntries; "paste undos stored.";
 END IF
 GOSUB LocateCursor2
 RETURN

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

' display path of file being edited.
DisplayPath:
 IF FileLength = False THEN
    RETURN
 END IF
 GOSUB ClearStatus
 Z$ = RTRIM$(ShortFilename)
 CALL Deconcatenate(Z$, 64)
 Z$ = MID$(Z$, 3)
 Var = LEN(Z$)
 DO
    Var = Var - 1
    IF Var = 0 THEN
       Z$ = ""
       EXIT DO
    END IF
    IF MID$(Z$, Var, 1) = "\" THEN
       Z$ = LEFT$(Z$, Var)
       EXIT DO
    END IF
 LOOP
 IF Z$ = "" THEN
    Z$ = "\"
 END IF
 PRINT "Editing path: "; Z$; " ";
 FileDisplay2 = -1
 GOSUB LocateCursor2
 RETURN

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

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

' display 64-byte name of file being editied.
DisplayFilename3:
 ' redisplay line 3.
 LOCATE 3, 4
 COLOR Green
 PRINT CHR$(ULcorner); STRING$(46, Hline); CHR$(URcorner); " ";
 PRINT CHR$(ULcorner); STRING$(22, Hline); CHR$(URcorner);
 ' display filename.
 Z$ = RTRIM$(ShortFilename)
 CALL Deconcatenate(Z$, 64)
 Z$ = MID$(STR$(CurrentFile), 2) + ": " + Z$
 Var = INT((80 - (LEN(RTRIM$(Z$))) + 2) / 2) - 1
 LOCATE 3, Var
 COLOR 0, 7
 PRINT " ";Z$;" ";
 COLOR 7, 0
 GOSUB LocateCursor2
 RETURN

' display info on file being edited.
DisplayPosition2:
 COLOR Yellow
 StringLength = LEN("Editing file: " + RTRIM$(Filename) + " ") + 4
 LOCATE 2, StringLength, 0
 PRINT SPACE$(77 - StringLength);
 LOCATE 2, StringLength, 0
 IF FileLength = False THEN
    GOSUB LockedFile
    RETURN
 END IF
 IF CurrentWindow2 = False THEN
    PRINT "(Position:"; STR$(FilePosition2 - 1); ") ";
 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 LocateCursor2
 RETURN

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

' redraw editing screen
RedrawScreen:
 ScreenDrawn = True
 CALL DisplayScreen
RedrawScreen2:
 GOSUB DisplayFilename
 GOSUB DisplayHexPage
 GOSUB DisplayPageByte
 IF FileDisplay = 0 THEN
    GOSUB DisplayFilename3
 END IF
 RETURN

' display screen of current page of hex/ascii values of file being edited.
DisplayHexPage:
 IF FileLength = False THEN
    Row = False
    Column = False
    FOR NextByte = 1 TO 320
       LOCATE Row + 4, Column + 6, 0
       PRINT "  ";
       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
    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:
 IF FileLength = False THEN
    COLOR White
    Row = False
    Column = False
    FOR NextByte = 1 TO 320
       LOCATE Row + 4, Column + 55, 0
       PRINT " ";
       Column = Column + 1
       IF Column > 19 THEN
          Row = Row + 1
          Column = False
       END IF
    NEXT
    LOCATE 1, 1, 0
    RETURN
 END IF
 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 StoreCurrentFile
    COLOR Plain
    ScreenDrawn = True
    CALL DisplayScreen
    GOSUB RestoreCurrentFile
    GOSUB RedrawScreen2
    GOSUB RestoreHilightBytes
    RETURN
 END IF
 FirstByte = (FilePage - 1) * 320 + 1
 LastByte = (FilePage - 1) * 320 + 320
 GOSUB DumpLineRange
 PRINT #1, ""
 PRINT #1, DumpLine$
 Temp# = FirstByte
 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
          HexFile$ = HexFile$ + SPACE$(67 - LEN(HexFile$))
          HexFile$ = HexFile$ + "x" + RIGHT$("00000000" + HEX$(Temp# - 1#), 8)
          PRINT #1, HexFile$
       END IF
       HexLine$ = Nul
       HexLine2$ = Nul
       Temp# = NextByte + 1#
       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 StoreCurrentFile
    COLOR Plain
    ScreenDrawn = True
    CALL DisplayScreen
    GOSUB RestoreCurrentFile
    GOSUB RedrawScreen2
    GOSUB RestoreHilightBytes
    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
 GOSUB DumpLineRange
 PRINT #1, ""
 PRINT #1, DumpLine$
 Temp# = FirstByte
 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
          HexFile$ = HexFile$ + SPACE$(67 - LEN(HexFile$))
          HexFile$ = HexFile$ + "x" + RIGHT$("00000000" + HEX$(Temp# - 1#), 8)
          PRINT #1, HexFile$
       END IF
       HexLine$ = Nul
       HexLine2$ = Nul
       Temp# = NextByte + 1#
       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:
 ' get printer port.
 GOSUB OpenPrinter
 IF ValidPrint = False THEN
    RETURN
 END IF
 ' start print.
 Column = False
 ColumnSpace = False
 HexLine$ = Nul
 HexLine2$ = Nul
 FirstByte = (FilePage - 1) * 320 + 1
 LastByte = (FilePage - 1) * 320 + 320
 GOSUB DumpLineRange
 PRINT #1, ""
 PRINT #1, DumpLine$
 Temp# = FirstByte
 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
          HexFile$ = HexFile$ + SPACE$(67 - LEN(HexFile$))
          HexFile$ = HexFile$ + "x" + RIGHT$("00000000" + HEX$(Temp# - 1#), 8)
          PRINT #1, HexFile$
       END IF
       HexLine$ = Nul
       HexLine2$ = Nul
       Temp# = NextByte + 1#
       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:
 ' get printer port.
 GOSUB OpenPrinter
 IF ValidPrint = False THEN
    RETURN
 END IF
 ' start print.
 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
 GOSUB DumpLineRange
 PRINT #1, ""
 PRINT #1, DumpLine$
 Temp# = FirstByte
 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
          HexFile$ = HexFile$ + SPACE$(67 - LEN(HexFile$))
          HexFile$ = HexFile$ + "x" + RIGHT$("00000000" + HEX$(Temp# - 1#), 8)
          PRINT #1, HexFile$
       END IF
       HexLine$ = Nul
       HexLine2$ = Nul
       Temp# = NextByte + 1#
       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

' print help screens.
PrintHelp:
 ' get printer port.
 GOSUB OpenPrinter
 IF ValidPrint = False THEN
    RETURN
 END IF
 ' start print.
 CALL PrintHelpScreens
 GOSUB DisplayFilename
 RETURN

' get printer port.
OpenPrinter:
 ValidPrint = False
 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 StoreCurrentFile
       COLOR Plain
       ScreenDrawn = True
       CALL DisplayScreen
       GOSUB RestoreCurrentFile
       GOSUB RedrawScreen2
       GOSUB RestoreHilightBytes
       RETURN
    END IF
 ELSE
    StatusMessage = "Invalid printer port number."
    GOSUB DisplayStatus1
    RETURN
 END IF
 ValidPrint = True
 RETURN

' format range of lines to be dumped.
DumpLineRange:
 DumpLine$ = "File: " + RTRIM$(Filename) + ", position" + STR$(FirstByte) + " to"
 IF LastByte <= FileLength THEN
    DumpLine$ = DumpLine$ + STR$(LastByte)
 ELSE
    DumpLine$ = DumpLine$ + STR$(FileLength)
 END IF
 DumpLine$ = DumpLine$ + " (x" + RIGHT$("00000000" + HEX$(FirstByte - 1), 8) + " - "
 IF LastByte <= FileLength THEN
    DumpLine$ = DumpLine$ + "x" + RIGHT$("00000000" + HEX$(LastByte - 1), 8) +")"
 ELSE
    DumpLine$ = DumpLine$ + "x" + RIGHT$("00000000" + HEX$(FileLength - 1), 8) +")"
 END IF
 DumpLine$ = DumpLine$ + "."
 RETURN

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

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

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

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

' print the byte.
BytePrint:
 SeekPosition = FilePosition
 GOSUB LseekFile
 GOSUB ReadFile
 FileByte = Buffer
 ByteValue = ASC(FileByte)
 GOSUB CalculateColumn
 ' display hex byte.
 GOSUB HMouse
 LOCATE PageRow + 3, Column + 5, 0
 PRINT RIGHT$("00" + HEX$(ByteValue), 2);
 GOSUB SMouse
 ' 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

' reset hilight bytes.
ResetHilightBytes:
 GOSUB ResetBytes
 ' adjust file position.
 IF PageColumn + 1 <= 20 THEN
    IF FilePosition + 1 <= FileLength THEN
       GOSUB ClearPageByte
       PageColumn = PageColumn + 1
       FilePosition = FilePosition + 1
       GOSUB DisplayPageByte
    END IF
 END IF
 COLOR Yellow
 GOSUB BytePrint
 RETURN

' 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

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

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

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

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

' add multiple specified byte to file and locate there.
AppendAnyByte:
 GOSUB ClearStatus
 PRINT "Enter number of bytes";
 INPUT ByteString$
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid append bytes."
    GOSUB DisplayStatus2
    RETURN
 END IF
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    GOSUB CheckHexValue
    IF ValidHexValue = False THEN
       RETURN
    END IF
    NumBytes# = HexValue - 1#
 ELSE
    GOSUB CheckASCIIValue
    IF ValidASCIIValue = False THEN
       RETURN
    END IF
    NumBytes# = ASCIIValue3
 END IF
 GOSUB ClearStatus
 ValidByteString = False
 PRINT "Enter hex byte";
 INPUT ByteString$
 IF LEN(ByteString$) = 2 THEN
    AllowWildcard = False
    GOSUB CheckHexBytes
    IF ValidByteString THEN
       Byte2 = VAL("&H" + ByteString$)
    END IF
 END IF
 IF ValidByteString = False THEN
    StatusMessage = "Invalid append hex value."
    GOSUB DisplayStatus2
    RETURN
 END IF
 GOSUB ClearStatus
 PRINT "Editing file: "; RTRIM$(Filename);
 COLOR White
 PRINT " Appending specified bytes to file.";
 GOTO AppendBytes2

' add multiple null bytes to file and locate there.
AppendNullBytes:
 GOSUB ClearStatus
 PRINT "Enter number of bytes";
 INPUT ByteString$
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid append bytes."
    GOSUB DisplayStatus2
    RETURN
 END IF
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    GOSUB CheckHexValue
    IF ValidHexValue = False THEN
       RETURN
    END IF
    NumBytes# = HexValue - 1#
 ELSE
    GOSUB CheckASCIIValue
    IF ValidASCIIValue = False THEN
       RETURN
    END IF
    NumBytes# = ASCIIValue3
 END IF
 Byte2 = 0
 GOSUB ClearStatus
 PRINT "Editing file: "; RTRIM$(Filename);
 COLOR White
 PRINT " Appending null bytes to file.";

' append bytes past end of file.
AppendBytes2:
 LastPage = FilePage
 GOSUB ClearPageByte
 FOR Byte1# = 1# TO NumBytes#
    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
          GOSUB ClearPageByte
       END IF
    ELSE
       EXIT FOR
    END IF
 NEXT
 GOSUB CalculatePosition1
 IF LastPage <> FilePage THEN
    LastPage = FilePage
    GOSUB DisplayHexPage
 END IF
 GOSUB DisplayPageByte
 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$)
    GOSUB AppendIt
    IF ValidAppend = False THEN
       EXIT FOR
    END IF
 NEXT
 GOSUB DisplayFilename
 RETURN

' appends a byte to file.
AppendIt:
 ValidAppend = False
 FileByte = CHR$(Byte2)
 IF FileLength + 1# <= 2147483647# THEN
    ValidAppend = True
    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
 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.
' input:
'   ByteString$ is packed hex string.
' output:
'   returns ValidHexValue True for valid hex value.
'   returns HexValue contains value of hex string.
' 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

REM Cursor key routines:

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

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

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

' move cursor right.
CursorRight:
 IF PageColumn + 1 <= 20 THEN
    IF FilePosition + 1 <= FileLength THEN
       GOSUB ClearPageByte
       IF PageColumn < 20 THEN
          IF FilePosition + 1 <= FileLength THEN
             PageColumn = PageColumn + 1
             FilePosition = FilePosition + 1
          END IF
       END IF
       GOSUB DisplayPageByte
       RETURN
    END IF
 END IF
 GOSUB ClearPageByte
 GOSUB DisplayPageByte
 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
 ELSE
    GOSUB ClearPageByte
    GOSUB DisplayPageByte
    GOSUB LocateCursor
 END IF
 RETURN

' home key
HomeKey:
 IF PageColumn > 1 THEN
    GOSUB ClearPageByte
    FilePosition = FilePosition - PageColumn + 1
    PageColumn = 1
    GOSUB DisplayPageByte
 ELSE
    GOSUB ClearPageByte
    IF PageColumn > 1 THEN
       PageColumn = 1
       FilePosition = FilePosition - 1
    END IF
    GOSUB DisplayPageByte
    GOSUB LocateCursor
 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
 ELSE
    GOSUB ClearPageByte
    GOSUB DisplayPageByte
    GOSUB LocateCursor
 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 there is a first position on the first page to move up to.
    IF FilePosition > 1 THEN
       GOSUB ClearPageByte
       PageRow = 1
       PageColumn = 1
       FilePosition = 1
       GOSUB DisplayPageByte
    ELSE
       GOSUB ClearPageByte
       GOSUB DisplayPageByte
       GOSUB LocateCursor
    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 there is a last position on the last page to move down to.
    IF FilePosition < FileLength THEN
       GOSUB ClearPageByte
       FilePosition = FileLength
       GOSUB CalculatePosition1
       GOSUB DisplayHexPage
       GOSUB DisplayPageByte
    ELSE
       GOSUB ClearPageByte
       GOSUB DisplayPageByte
       GOSUB LocateCursor
    END IF
 END IF
 RETURN

REM More routines:

' switch editing windows.
TabWindow:
 IF CurrentWindow2 = False THEN
    CurrentWindow = NOT CurrentWindow
    GOSUB LocateCursor2
 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 record structure array.
StoreUndo:
 IF CurrentUndo >= 32767 THEN
    StatusMessage = "Maximum undos are at 32,767."
    GOSUB DisplayStatus2
    RETURN
 END IF
 IF CurrentUndo < 32767 THEN
    CurrentUndo = CurrentUndo + 1
    UndoFile.UndoByte1(CurrentFile) = AsciiValue
    UndoFile.UndoPosition1(CurrentFile) = FilePosition
    PUT #6, CurrentUndo, UndoFile
 END IF
 RETURN

' critical error trap.
Error.Routine:
 ' check error return type.
 ErrorTrap = ERR
 IF Check.Disk THEN
    Disk.Ready = ERR
    RESUME NEXT
 END IF
 ' check screen.
 IF ScreenDrawn THEN
    CLS
 END IF
 ScreenDrawn = False
 ' display error message.
 COLOR White
 PRINT "Hex Editor Main " + Version + " " + Release + " critical error trap:"
 COLOR Yellow
 ' display error.
 GOSUB DisplayCriticalError
 ' 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"
       IF CurrentFile = 0 THEN
          GOSUB RedrawScreen
          RESUME NEXT
       END IF
       GOSUB RedrawScreen
       RESUME TopProgram
    CASE "a"
       PRINT "a"
       EXIT DO
    END SELECT
 LOOP

' end of program.
GOSUB CloseAllFiles
GOSUB ResetAllAttributes

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

' terminal error goes here.
Error.Exit:
 ' display error.
 COLOR White, Black
 CLS
 PRINT "Hex Editor " + Version + " " + Release + " critical error trap:"
 ' display error and exit.
 COLOR Yellow
 GOSUB DisplayCriticalError
 COLOR Green
 PRINT "Close Hexedit processes, Check TEMP variables, .CFG file,"
 PRINT "or DOS file handles, then restart."
 COLOR Plain
 END

' display error.
DisplayCriticalError:
 SELECT CASE Error.Trap
 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 90
    PRINT "Too many processes."
 CASE ELSE
    PRINT "Untrapped error" + STR$(ERR) + "."
 END SELECT
 RETURN

' drop down input menu.
Menu2:
 GOSUB SMouse
 CALL DropDownMenu
 GOSUB RestorePosition
 IF CurrentMenu = False THEN
    GOSUB LocateCursor
    GOTO StartLoop
 END IF
 ' reset mouse activity.
 GOSUB HMouse
 SELECT CASE CurrentMenu
 CASE 1
    SELECT CASE CurrentMenuSelection
    CASE 1 ' Alt-N  Open new file
       IF NumberFiles < 9 THEN
          GOSUB StoreCurrentFile
          GOSUB NewFile
          GOTO BeginFile
       END IF
    CASE 2 ' Alt-W  Close file
       GOSUB CloseCurrentFile
       IF NumberFiles = 0 THEN
          GOSUB NewFile
          GOTO BeginFile
       END IF
    CASE 3 ' Alt-V  View files
       CALL ViewFiles(Var)
       IF Var > 0 THEN
          NextFile = Var
          GOSUB SelectFile
       END IF
       GOSUB DisplayFilename
    CASE 4 ' Alt-Y  Toggle window
       IF FileLength THEN
          GOSUB ToggleRight
       END IF
    CASE 5 ' 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 StoreCurrentFile
       COLOR Plain
       ScreenDrawn = True
       CALL DisplayScreen
       GOSUB RestoreCurrentFile
       GOSUB RedrawScreen2
       GOSUB RestoreHilightBytes
    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
    GOSUB SMouse
    GOSUB RestorePosition
 END IF
 GOSUB LocateCursor
 GOTO StartLoop

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

' 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

' 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 = CDBL(OutregsX.AX + 65536)
 ELSE
    Low = CDBL(OutregsX.AX)
 END IF
 IF OutregsX.DX < 0 THEN
    High = CDBL(OutregsX.DX + 65536)
 ELSE
    High = CDBL(OutregsX.DX)
 END IF
 FileLength = High * &H10000 + Low
 RETURN

' close all files.
CloseAllFiles:
 FOR Var = 1 TO NumberFiles
    Handle = File(Var).Handle
    GOSUB CloseFile
 NEXT
 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 GetFilename
 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
 GOSUB ShortFilename
 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 GetFilename
 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

' check command line filename.
GetFilename:
 IF LEFT$(Filename, 1) = "\" THEN
    ASCIIZ = LEFT$(CURDIR$, 2) + RTRIM$(Filename) + CHR$(0)
 ELSE
    IF MID$(Filename, 2, 1) <> ":" THEN
       ASCIIZ = CURDIR$ + "\" + RTRIM$(Filename) + CHR$(0)
    END IF
 END IF
 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 attribute.
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.
 SeekPosition2 = SeekPosition - 1 ' offset is from zero.
 ' seek values are signed bit sensitive.
 High = INT(SeekPosition2 / 65536)
 Low = SeekPosition2 AND 65535
 ' seek position in file.
 InregsX.AX = &H4200
 InregsX.BX = Handle
 InregsX.CX = INT(VAL("&H" + HEX$(High)))
 InregsX.DX = INT(VAL("&H" + HEX$(Low)))
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' 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 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

 ' store 64-byte filename.
 ShortFilename = Filename

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

' restore all read-only attributes.
ResetAllAttributes:
 FOR Var = 1 TO NumberFiles
    ' reset file attribute.
    ASCIIZ = File(Var).ASCIIZ
    FileAttribute = File(Var).FileAttribute
    GOSUB ResetAttribute
 NEXT
 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:

' 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.
             GOSUB HMouse
             COLOR White, Black
             GOSUB DisplayTab
             GOSUB SMouse
          END IF
       END IF
       CurrentMenu = NewMenuSelection
       CurrentMenuSelection = 1
    END IF
    ' hilight menu tab.
    IF CurrentMenu > False THEN
       GOSUB HMouse
       COLOR White, Blue
       GOSUB DisplayTab
       GOSUB SMouse
    END IF
    GOSUB LocateCursor2
    RETURN
 END IF
 ' check mouse position in screen editing area.
 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 ' at a space.
          GOSUB RestorePosition
          RETURN
       END IF
       ' get new position and reset there.
       IF VarX <> 32 THEN ' not a space.
          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
 ' restore position.
 GOSUB RestorePosition
 GOSUB LocateCursor2
 RETURN

' restores old mouse position.
MoveOldPosition:
 GOSUB CalculatePosition4
 TempPosition3 = NewPosition
 GOSUB CalculatePosition2
 GOSUB CalculateColumn2
 GOSUB CheckMousePosition
 IF MouseArea = 0 THEN
    GOSUB HMouse
    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);
    GOSUB SMouse
 END IF
 COLOR Plain, Black
 RETURN

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

' moves to new mouse position.
MoveNewPosition:
 GOSUB CalculatePosition3
 FilePosition2 = NewPosition
 TempPosition3 = NewPosition
 GOSUB CalculatePosition2
 GOSUB CalculateColumn2
 GOSUB CheckMousePosition
 IF MouseArea = 0 THEN
    GOSUB HMouse
    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);
    GOSUB SMouse
 END IF
 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

' restore screen position.
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
          IF CopyPositionStart = 0 THEN
             GOTO RestoreMouseByte
          END IF
          IF CopyPositionStart > 0 THEN
             IF NewPosition >= CopyPositionStart THEN
                IF NewPosition <= CopyPositionEnd THEN
                   RETURN
                ENDIF
             END IF
          END IF
          GOTO RestoreMouseByte
       END IF
    END IF
 END IF
 RETURN

' check mouse position in hilighted area.
CheckMousePosition:
 MouseArea = 0
 IF CopyPositionStart > 0 THEN
    IF TempPosition3 >= CopyPositionStart THEN
       IF TempPosition3 <= CopyPositionEnd THEN
          MouseArea = -1
       END IF
    END IF
 END IF
 RETURN

' check mouse position in hilighted area.
CheckMousePosition2:
 MouseArea = 0
 IF CopyPositionStart > 0 THEN
    IF FilePosition >= CopyPositionStart THEN
       IF TempPosition3 <= CopyPositionEnd THEN
          MouseArea = -1
       END IF
    END IF
 END IF
 RETURN

' display hilighted mouse position byte.
RestoreMouseByte:
 GOSUB HMouse
 COLOR White, Blue
 LOCATE PageRow2 + 3, Column2 + 5, 0
 VarX = SCREEN(PageRow2 + 3, Column2 + 5)
 PRINT CHR$(VarX); ' byte 1
 LOCATE PageRow2 + 3, Column2 + 6, 0
 VarY = SCREEN(PageRow2 + 3, Column2 + 6)
 PRINT CHR$(VarY); ' byte 2
 COLOR Plain, Black
 GOSUB SMouse
 RETURN

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

' hide mouse cursor.
'  calls to hide mouse are reentrant.
HMouse:
 IF Mouse.Present THEN
    CALL MouseFunction(HideMouse, 0)
 END IF
 RETURN

' show mouse cursor.
SMouse:
 IF Mouse.Present THEN
    CALL MouseFunction(ShowMouse, 0)
 END IF
 RETURN

' process left mouse button click.
MouseButton1:
 ' stop program.
 IF Mouse.Row = 3 AND Mouse.Column = 77 THEN
    GOSUB HMouse
    GOTO StopLabel
 END IF
 ' help screen.
 IF Mouse.Row = 4 AND Mouse.Column = 77 THEN
    GOSUB DisplayHelp
    RETURN
 END IF
 ' page up.
 IF Mouse.Row = 4 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 = 5 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
          GOSUB HMouse
          ' remove hilight menu tab.
          COLOR White, Black
          GOSUB DisplayTab
          GOSUB SMouse
       END IF
    END IF
    CurrentMenu = NewMenuSelection
    CurrentMenuSelection = 1
    GOSUB HMouse
    ' hilight menu tab.
    COLOR White, Blue
    GOSUB DisplayTab
    GOSUB SMouse
    ' check file.
    IF FileLength = 0 THEN
       IF CurrentMenu > 1 THEN
          RETURN
       END IF
    END IF
    ' jump to file menu.
    GOSUB HMouse
    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

' reset hilight bytes for mouse.
ResetMouseBytes:
 IF CopyPositionStart = 0 THEN
    GOSUB LocateCursor2
    RETURN
 END IF
 GOSUB ResetBytes
 GOSUB ClearPageByte
 GOSUB DisplayPageByte
 RETURN

' reset hilighted bytes.
ResetBytes:
 Reset1 = True
 GOSUB ResetByte
 CopyPositionStart = 0
 CopyPositionPivot = 0
 CopyPositionEnd = 0
 CopyStart = 0
 RETURN

' restore hilighted bytes.
RestoreHilightBytes:
 CopyPositionStart=File(CurrentFile).CopyPositionStart
 CopyPositionPivot=File(CurrentFile).CopyPositionPivot
 CopyPositionEnd=File(CurrentFile).CopyPositionEnd
 FilePosition=File(CurrentFile).FilePosition
 Reset1 = False
 GOSUB ResetByte
 RETURN

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

 REM End-of-program. Please deliver the cats now. I want 2 black siamese.
