REM Program: Hex Editor v7.7a, Module 1 of 6, PD 2007.
REM Author: Erik Jon Oredson AS. Csci
REM Release: 10/15/2007.
REM Status: Public Domain.
REM Email: eoredson@gmail.com
REM Urls: www.filegate.net www.simtel.net

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

' declare external procedures.
DECLARE SUB SetInt
DECLARE SUB RestInt

' increase stack for recursion.
STACK 4096

' install new interrupt service routine.
CALL SetTrap

' declare error trap.
ON ERROR GOTO Error.Routine

' initialize variables.
CALL InitVars

' initialize server name.
CALL StoreServerName

' read config file.
CALL ReadConfigFile(FileName)

' check windows override.
IF Load.Windows THEN
   Windows.Detected = True
END IF

' check DOS override.
IF Load.DOS THEN
   Windows.Detected = False
END IF

' get current drive/directory.
CALL GetDrive(Drive.Number, Directory$)

' init undo/marker file.
CALL OpenUndoFile(VarQ)

' check bad filename.
If VarQ Then
   Error.Trap = 64
   GOTO Error.Exit
END IF

' init paste file.
CALL InitializePaste(VarQ)

' check bad filename.
IF VarQ THEN
   Error.Trap = 64
   GOTO Error.Exit
END IF

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

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

' reset menu filename flag.
IsMenuFile = False

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

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

' read lowercase command line.
CALL ReadCommandLine(Var$)

' trap redirected stream.
Call ResetTrap
DO
   ' undocumented:
   '   exits program if redirected input detected.
   X$ = Inkey$
   IF X$ = Nul THEN
      EXIT DO
   END IF
LOOP
Call SetTrap

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

' filename entry starts here.
BeginFile:

' start mouse.
CALL SMouse

' read information from file menu box.
CALL Menu(NetPath2$, Drive$, Directory$, FileSpec1$, FileSpec2$)

' store current menu box netpath.
NetPath$ = NetPath2$

' reset menu filename flag.
IsMenuFile = True

' close menu directory/filename sort files.
CLOSE #2, #3

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

   ' exit program.
   GOTO BootUsage
END IF

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

' store filename from menu box.
CommandLine$ = Filename

' filename entry loop starts here.
StartFile:

' restart mouse.
CALL SMouse

' start filename entry loop.
DO
   ' check filenames
   IF CommandLine$ = Nul 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$ = Nul
   END IF

   ' parse command line
   IF IsMenuFile = False THEN
      IF LEFT$(CommandLine$, 1) = Quote THEN
         Var = INSTR(2, CommandLine$, Quote)
         IF Var = False 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 filenames.
   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$ = Nul
         ELSE
            EXIT DO
         END IF
      END IF

      ' check filelist specifier.
      IF LEFT$(Var$, 1) = "@" THEN
         CLOSE #1

         ' check filelist filename.
         Var4$ = UCASE$(MID$(Var$, 2))
         IF Windows.Detected THEN
            X$ = DIRz$(Var4$)
         ELSE
            X$ = DIRx$(Var4$)
         END IF
         IF Windows.Detected THEN
            ' close long filename search
            InregsX.AX = &H71A1
            InregsX.BX = Wfile.Handle
            CALL InterruptX(&H21, InregsX, OutregsX)
         END IF
         IF X$ <> Nul THEN

            ' ambiguate filename.
            ASCIIZ3 = ASCIIZ
            ASCIIZ = Var4$ + CHR$(0)
            CALL GetShortFilename(Short.Filename$)
            ASCIIZ = ASCIIZ3

            ' store ambiguated filename.
            Var4$ = Short.Filename$

            ' open filelist filename.
            IF LEN(Var4$) THEN
               ErrorTrap = False
               OPEN Var4$ FOR INPUT AS #1
               IF ErrorTrap = False THEN

                  ' read filelist files.
                  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

                        ' load file.
                        GOSUB LoadMultipleFile
                     END IF
                  LOOP
               END IF
            END IF
         END IF
      ELSE
         ' load file.
         GOSUB LoadMultipleFile
      END IF
   LOOP
LOOP

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

' main program loop starts here.
TopProgram:
CALL SMouse

' reset mouse activity.
IF Mouse.Present THEN
   CALL ClearMouse
END IF

' setup editing screen.
COLORf Plain
ScreenDrawn = True
CALL DisplayScreen
GOSUB RestoreCurrentFile
GOSUB RedrawScreen2
Call restorehilightbytes
CALL SMouse
GOSUB RestorePosition
Call locatecursor2

' keyboard/mouse input loop starts here.
StartLoop:

' keyboard/mouse input loop.
DO
   ' check for keypress or mouse activity.
   CharInput$ = Nul
   DO
      ' store keyboard buffer.
      CharInput$ = INKEY$
      IF LEN(CharInput$) THEN
         EXIT DO
      END IF

      ' call mouse subroutine.
      CALL MouseDriver2

      ' check left mouse button release.
      IF Mouse.ButtonX THEN
         ' store mouse position.
         Mouse.Row = Mouse.RowX
         Mouse.Column = Mouse.ColumnX
         ' process mouse row/column.
         IF Mouse.Drag = False THEN
            GOSUB MouseButton1
         END IF
         ' init mouse drag pivot byte.
         CopyStart = False
         Time1 = SFalse
         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 = SFalse
               Mouse.Drag = True
               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
                  Mouse.Drag = True
                  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
                     Mouse.Drag = True
                     CALL MouseButton1Drag(10)
                  END IF
               ELSE
                  ' check right/middle mouse button.
                  IF Mouse.Button2 OR Mouse.Button3 THEN
                     GOSUB MouseButton2
                  ELSE
                     ' check mouse position for mouseover.
                     IF Mouse.Row OR Mouse.Column THEN
                        Mouse.Drag = False
                        GOSUB MoveMouse
                     END IF
                  END IF
               END IF
            END IF
         END IF
      END IF

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

   ' reset mouse activity.
   CALL HMouse
   Call locatecursor2

   ' 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
      ' parse the key.
      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
               CALL MultiFileFunction(2)
               CALL Marker(1)
               IF ValidFunction THEN
                  COLORf Plain
                  ScreenDrawn = True
                  CALL DisplayScreen
                  GOSUB RestoreCurrentFile
                  GOSUB RedrawScreen2
                  Call restorehilightbytes
               END IF
            END IF
         ' 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
         CASE ELSE ' Unknown key.
            GOSUB HelpLine
         END SELECT
      CASE 2
         SELECT CASE ASC(RIGHT$(CharInput$, 1))
         CASE 15 ' Shift-tab = Switch windows.
            IF FileLength THEN
               GOSUB TabWindow
            END IF
         ' byte hilighting 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
         CASE ELSE ' Unknown key.
            GOSUB HelpLine
         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 FileLocked THEN
               Call clearstatus
               PRINTf "Editing file: " + RTRIM$(Filename) + " "
               Call lockedfile
            ELSE
               IF FileLength = 0 THEN
                  Byte2 = 0
                  Gosub AppendIt
               END IF
               IF FileLength THEN
                  GOSUB ChangeHexValue
               END IF
            END IF
         CASE 27 ' Escape = Start drop down menu.
            IF CurrentMenu <> 1 THEN
               ' remove hilight menu tab.
               COLORf2 White, 0
               CALL DisplayTab(CurrentMenu)
            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
               CALL MultiFileFunction(2)
               CALL Marker(10)
               COLORf Plain
               ScreenDrawn = True
               CALL DisplayScreen
               GOSUB RestoreCurrentFile
               GOSUB RedrawScreen2
               Call 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
               CALL MultiFileFunction(2)
               CALL Marker(12)
               COLORf Plain
               ScreenDrawn = True
               CALL DisplayScreen
               GOSUB RestoreCurrentFile
               GOSUB RedrawScreen2
               Call 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 Clipboard2(1)
            END IF
         CASE 22 ' Control-V = Paste area.
            IF FileLength THEN
               CALL Clipboard2(2)
            END IF
         CASE 24 ' Control-X = Undo previous paste.
            IF FileLength THEN
               CALL Clipboard2(3)
            END IF
         CASE 26 ' Control-Z = Undo all pastes.
            IF FileLength THEN
               CALL Clipboard2(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 80h at memory 0040h:0071h
            Call clearstatus
            PRINTf "Editing file: " + RTRIM$(Filename) + " "
            PRINTf "*break*"
            Call locatecursor2
         CASE 30 ' Alt-A = Append multiple bytes.
            IF FileLocked THEN
               Call clearstatus
               PRINTf "Editing file: " + RTRIM$(Filename) + " "
               Call lockedfile
            ELSE
               GOSUB AppendByte
            END IF
         CASE 48 ' Alt-B = Append multiple null bytes.
            IF FileLocked THEN
               Call clearstatus
               PRINTf "Editing file: " + RTRIM$(Filename) + " "
               Call lockedfile
            ELSE
               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.
               COLORf2 White, 0
               CALL DisplayTab(CurrentMenu)
            END IF
            CurrentMenu = 1
            CurrentMenuSelection = 1
            GOTO Menu2
         CASE 34 ' Alt-G = Append multiple specified bytes.
            IF FileLocked THEN
               Call clearstatus
               PRINTf "Editing file: " + RTRIM$(Filename) + " "
               Call lockedfile
            ELSE
               GOSUB AppendAnyByte
            END IF
         CASE 35 ' Alt-H = HEX chart.
            GOSUB HEXChart
         CASE 23 ' Alt-I = DOS command.
            GOSUB DOSCommand
         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 FileLocked THEN
               Call clearstatus
               PRINTf "Editing file: " + RTRIM$(Filename) + " "
               Call lockedfile
            ELSE
               GOSUB AppendASCII
            END IF
         CASE 49 ' Alt-N = Open new file.
            IF NumberFiles = 9 THEN
               StatusMessage = "Maximum of 9 files open."
               Call displaystatus2
            ELSE
               CALL MultiFileFunction(2)
               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.
            CALL MultiFileFunction(2)
            COLORf Plain
            ScreenDrawn = True
            CALL DisplayScreen
            GOSUB RestoreCurrentFile
            GOSUB RedrawScreen2
            Call restorehilightbytes
         CASE 31 ' Alt-S = Search for multiple bytes.
            IF FileLength THEN
               GOSUB SearchBytes
            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 > False THEN
               NextFile = Var
               GOSUB SelectFile
            END IF
            Call displayfilename
         CASE 17 ' Alt-W = Close file.
            GOSUB CloseCurrentFile
            IF NumberFiles = False 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 129 ' Alt-0 = Close all files.
            GOSUB CloseAllFiles
            GOSUB ResetAllAttributes
            GOSUB NewFile
            NumberFiles = False
            GOTO BeginFile
         CASE 59 ' F1 - toggle filename display.
            FileDisplay = NOT FileDisplay
            IF FileDisplay = False THEN
               Call DisplayFileTitle
            ELSE
               GOSUB DisplayScreen3
            END IF
         CASE 60 ' F2 - toggle bottom row help keys.
            ScreenRow = NOT ScreenRow
            CALL MultiFileFunction(2)
            COLORf Plain
            ScreenDrawn = True
            CALL DisplayScreen
            GOSUB RestoreCurrentFile
            GOSUB RedrawScreen2
            Call restorehilightbytes
         CASE 61 ' F3 - search ascii string.
            IF FileLength THEN
               GOSUB SearchASCII
            END IF
         CASE 62 ' F4 - search multiple bytes.
            IF FileLength THEN
               GOSUB SearchBytes
            END IF
         CASE 63 ' F5 - toggle right window.
            IF FileLength THEN
               GOSUB ToggleRight
            END IF
         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."
               Call displaystatus2
            ELSE
               CALL MultiFileFunction(2)
               GOSUB NewFile
               GOTO BeginFile
            END IF
         CASE 67 ' F9 - close current file.
            GOSUB CloseCurrentFile
            IF NumberFiles = False THEN
               GOSUB NewFile
               GOTO BeginFile
            END IF
         CASE 68 ' F10 - view files.
            CALL ViewFiles(Var)
            IF Var > False THEN
               NextFile = Var
               GOSUB SelectFile
            END IF
            Call displayfilename
         CASE 96 ' Ctrl-F3 = continue search.
            IF FileLength THEN
               GOSUB ContinueSearch
            END IF
         CASE 133 ' F11 - Drop to DOS.
            CALL MultiFileFunction(2)
            COLORf Plain
            CLS
            PRINTf "Type 'Exit' to return to editor.."
            Var$=Curdir$
            Call ResetTrap
            SHELL
            Chdrive Var$
            Chdir Var$
            Call SetTrap
            ScreenDrawn = True
            CALL DisplayScreen
            GOSUB RestoreCurrentFile
            GOSUB RedrawScreen2
            Call restorehilightbytes
         CASE 134 ' F12 - DOS Command.
            GOSUB DOSCommand
         CASE 76, 108 ' keypad '5' key/Alt-F5 - Hex/Dec calculator.
            GOSUB HEXCalculator
         CASE 120 TO 128 ' Alt-1 to Alt-9 - Select file.
            NextFile = ASC(RIGHT$(CharInput$, 1)) - 119
            GOSUB SelectFile
         CASE 82 ' Insert - Change string value.
            IF FileLocked THEN
               Call clearstatus
               PRINTf "Editing file: " + RTRIM$(Filename) + " "
               Call lockedfile
            ELSE
               GOSUB ChangeASCIIValues
            END IF
         CASE 83 ' Delete - Change HEX string.
            IF FileLocked THEN
               Call clearstatus
               PRINTf "Editing file: " + RTRIM$(Filename) + " "
               Call lockedfile
            ELSE
               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.
         ' impossible to get here.
      END SELECT
   END IF

   ' reset mouse activity.
   CALL SMouse
   GOSUB RestorePosition
   Call locatecursor2
LOOP

' stop program.
StopLabel:

' clear hilighted byte.
Call clearpagebyte
GOSUB MoveOldPosition

' clear status areas.
Call clearstatus
GOSUB DisplayScreen3

' display goodbye message.
COLORf2 Yellow, 0
LOCATEf 2, 4, 0
Z$ = "You have been using a public domain program written by "
Z$ = Z$ + "Erik Jon Oredson.." ' insert your name here.
Z$ = LEFT$(Z$, 74)
PRINTf Z$
LOCATEf 2, 4, 0

' clear file menu tab.
IF CurrentMenu THEN
   COLORf2 White, 0
   CALL DisplayTab(CurrentMenu)
END IF

' remove bottom lines of screen.
FOR Var = 21 TO 24
   LOCATEf Var, 1, 0
   PRINTf SPACE$(78)
NEXT
LOCATEf 21, 1, 0
COLORf Magenta
PRINTf " " + CHR$(LLcorner) + STRING$(75, Hline) + CHR$(LRcorner)

' goto end of program.
GOTO StopLoop
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$ = Nul
    C$ = Nul
    N$ = Nul
    X$ = Nul
    ' get netpath
    IF LEFT$(Filename, 2) = "\\" THEN
       Net1 = INSTR(3, Filename, "\")
       IF Net1 THEN
          Net2 = INSTR(Net1 + 1, Filename, "\")
          IF Net2 THEN
             N$ = LEFT$(Filename, Net2 - 1)
             Filename = MID$(Filename, Net2)
          END IF
       END IF
    END IF
    ' get directory
    FOR Var = LEN(Filename) TO 1 STEP -1
       IF MID$(Filename, Var, 1) = "\" THEN
          D$ = LEFT$(Filename, Var)
          X$ = MID$(Filename, Var + 1)
          EXIT FOR
       END IF
    NEXT
    ' restore filename
    IF N$ <> Nul THEN
       Filename = N$ + D$ + X$
    END IF
    ' get drive
    IF MID$(Filename, 2, 1) = ":" THEN
       C$ = LEFT$(Filename, 2)
       D$ = MID$(D$, 3)
    END IF
    ' clear filename array.
    FOR VarZ = 1 TO 9
       TempFiles(VarZ) = Nul
    NEXT
    ' store filename array.
    VarZ = False
    IF Windows.Detected THEN
       X$ = DIRz$(Filename)
    ELSE
       X$ = DIRx$(Filename)
    END IF
    DO
       IF LEN(X$) THEN
          IF VarZ < 9 THEN
             VarZ = VarZ + 1
             IF N$ = Nul THEN
                TempFiles(VarZ) = C$ + D$ + X$
             ELSE
                IF D$ = Nul THEN
                   TempFiles(VarZ) = N$ + "\" + X$
                ELSE
                   TempFiles(VarZ) = N$ + "\" + D$ + X$
                END IF
             END IF
          ELSE
             EXIT DO
          END IF
       ELSE
          EXIT DO
       END IF
       IF Windows.Detected THEN
          X$ = DIRz$(Nul)
       ELSE
          X$ = DIRx$(Nul)
       END IF
    LOOP
    IF Windows.Detected THEN
       ' close long filename search
       InregsX.AX = &H71A1
       InregsX.BX = Wfile.Handle
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' load filename array.
    FOR VarZ = 1 TO 9
       IF RTRIM$(TempFiles(VarZ)) <> "" THEN
          ' check max files.
          IF NumberFiles = 9 THEN
             RETURN
          END IF
          Filename = RTRIM$(TempFiles(VarZ))
          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

 ' check open error.
 IF ValidFile THEN

    ' get netpath
    GOSUB ParseNetPath

    ' conanicalize filename.
    GOSUB ShortFilename

    ' get length of file.
    CALL GetFileLength(Var)

    ' check filelength.
    IF Var THEN
       ' close handle.
       GOSUB CloseFile

       ' restore current file.
       GOSUB RestoreCurrentFile
       RETURN
    END IF

    ' get exclusion list.
    GOSUB CheckFile

    ' check excluded file.
    IF VarP THEN
       ' close handle.
       GOSUB CloseFile

       ' restore current file.
       GOSUB RestoreCurrentFile
       RETURN
    END IF

    ' initialize new file.
    GOSUB InitFile

    ' init paste files.
    CALL InitPasteFiles(VarQ)

    ' check bad paste filename.
    IF VarQ Then
       Error.Trap = 64
       GOTO Error.Exit
    END IF
 END IF
 RETURN

' initialize new file.
InitFile:
 NumberFiles = NumberFiles + 1
 CurrentFile = NumberFiles
 CALL MultiFileFunction(1)
 GOSUB RestoreCurrentFile
 RETURN

' check file exclusion list.
CheckFile:
 VarP = False
 Filename2$ = ExcludeFile
 Var = INSTR(Filename2$, "?")
 IF Var THEN
    MID$(Filename2$, Var, 1) = MID$(STR$(Process.Number), 2)
 END IF
 ASCIIZ3 = ASCIIZ
 ASCIIZ = Filename2$ + CHR$(0)
 CALL GetShortFilename(Short.Filename$)
 ASCIIZ = ASCIIZ3
 Filename2$ = Short.Filename$
 IF DIRx$(Filename2$) <> Nul THEN
    Check.Disk = True
    Disk.Ready = False
    CLOSE #2
    OPEN Filename2$ FOR INPUT AS #2
    Check.Disk = False
    IF Disk.Ready THEN
       RETURN
    END IF
    Y2$ = Filename
    Y2$ = UCASE$(Y2$)
    FOR Y = LEN(Y2$) TO 1 STEP -1
       IF MID$(Y2$, Y, 1) = "\" THEN
          Y2$ = MID$(Y2$, Y + 1)
          EXIT FOR
       END IF
    NEXT
    DO UNTIL EOF(2)
       LINE INPUT #2, Y1$
       Y1$ = LTRIM$(Y1$)
       Y1$ = RTRIM$(Y1$)
       Y1$ = UCASE$(Y1$)
       IF LEFT$(Y1$, 1) = Quote THEN
          Y1$ = MID$(Y1$, 2)
          IF RIGHT$(Y1$, 1) = Quote THEN
             Y1$ = LEFT$(Y1$, LEN(Y1$) - 1)
          END IF
       END IF
       Y1$ = LTRIM$(Y1$)
       Y1$ = RTRIM$(Y1$)
       IF LEN(Y1$) THEN
          IF INSTR(Y1$, " ") = False THEN
             ' check excluded file in filename.
             CALL CheckExcluded(Y1$, Y2$, Exclude.File%)
             IF Exclude.File% THEN
                VarP = True
                EXIT DO
             END IF
          END IF
       END IF
    LOOP
 END IF
 RETURN

' select specific file number.
SelectFile:
 IF NextFile <= NumberFiles THEN
    IF NextFile <> CurrentFile THEN
       CALL MultiFileFunction(2)
       CurrentFile = NextFile
       GOSUB RestoreCurrentFile
       GOSUB RedrawScreen2
       Call restorehilightbytes
    END IF
 END IF
 RETURN

' goto next file.
GotoNextFile:
 IF NumberFiles = 1 THEN
    RETURN
 END IF
 CALL MultiFileFunction(2)
 CurrentFile = CurrentFile + 1
 IF CurrentFile > NumberFiles THEN
    CurrentFile = 1
 END IF
 GOSUB RestoreCurrentFile
 GOSUB RedrawScreen2
 Call restorehilightbytes
 RETURN

' goto previous file.
GotoPreviousFile:
 IF NumberFiles = 1 THEN
    RETURN
 END IF
 CALL MultiFileFunction(2)
 CurrentFile = CurrentFile - 1
 IF CurrentFile = False THEN
    CurrentFile = NumberFiles
 END IF
 GOSUB RestoreCurrentFile
 GOSUB RedrawScreen2
 Call 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 > False THEN
    ' reset current file.
    IF CurrentFile > NumberFiles THEN
       CurrentFile = NumberFiles
    END IF

    ' restore current file.
    GOSUB RestoreCurrentFile
    GOSUB RedrawScreen2
    Call restorehilightbytes
 END IF
 RETURN

' prepare for new file.
NewFile:
 ' reset window.
 CLS
 LOCATEf 1, 1, 1
 PRINTf "Loading.."
 CurrentWindow = False
 CurrentWindow2 = False
 ScreenDrawn = False
 LOCATEf 24, 1, 1
 COLORf Plain
 RETURN

' restore current file variables.
RestoreCurrentFile:
 IF NumberFiles = 0 THEN
    RETURN
 END IF
 CALL MultiFileFunction(3)
 CALL OpenPasteFiles(VarQ)
 IF VarQ THEN
    Error.Trap = 64
    GOTO Error.Exit
 END IF
 RETURN

' display command line instructions.
BootUsage:
 CALL DisplayBootUsage
 COLORf Plain
 GOTO StopProgram

' display filename border area.
DisplayScreen3:
 CALL DisplayScreen3X
 Call locatecursor2
 RETURN

' display ANSI charts.
ANSIChart:
 CALL DisplayChart(1)
 Goto EndChart
' display HEX charts.
HEXChart:
 CALL DisplayChart(2)
 Goto EndChart
' display HEX calculator.
HEXCalculator:
 CALL HexCalc
EndChart:
 CALL MultiFileFunction(2)
 COLORf Plain
 ScreenDrawn = True
 CALL DisplayScreen
 GOSUB RestoreCurrentFile
 GOSUB RedrawScreen2
 Call restorehilightbytes
 RETURN

' function for unknown key.
HelpLine:
 Call clearstatus
 PRINTf "Editing file: " + RTRIM$(Filename)
 COLORf White
 PRINTf " Type Alt-Q for Help, Alt-O to print Help."
 Call locatecursor2
 RETURN

' displays help screens.
DisplayHelp:
 CALL HMouse
 CALL MultiFileFunction(2)
 CALL HelpScreen
 COLORf Plain
 ScreenDrawn = True
 CALL DisplayScreen
 GOSUB RestoreCurrentFile
 GOSUB RedrawScreen2
 Call restorehilightbytes
 CALL SMouse
 RETURN

' function for ? key.
DisplayInfo:
 Call clearstatus
 PRINTf "Editing file: " + RTRIM$(Filename) + " "
 IF FileLength = False THEN
    Call lockedfile
    RETURN
 END IF
 Attr$ = Nul
 IF (FileAttribute AND 1) = 1 THEN
    Attr$ = Attr$ + "o"
 END IF
 IF (FileAttribute AND 2) = 2 THEN
    Attr$ = Attr$ + "h"
 END IF
 IF (FileAttribute AND 4) = 4 THEN
    Attr$ = Attr$ + "s"
 END IF
 IF (FileAttribute AND 32) = 32 THEN
    Attr$ = Attr$ + "a"
 END IF
 IF LEN(Attr$) THEN
    PRINTf "(" + Attr$ + ") "
 END IF
 IF CurrentWindow2 = False THEN
    PRINTf "(Length:" + STR$(FileLength) + ") "
 ELSE
    PRINTf "(Length: " + RIGHT$("00000000" + HEX$(FileLength - 1), 8) + "H) "
 END IF
 PRINTf "(Undos:" + STR$(CurrentUndo) + ") "
 Call locatecursor2
 RETURN

' function for ! key.
DisplayData:
 Call clearstatus
 PRINTf "Editing file: " + RTRIM$(Filename) + " "
 IF FileLength = False THEN
    Call lockedfile
    RETURN
 END IF
 TempPosition3 = FilePosition
 CALL FormatPosition1
 Call locatecursor2
 RETURN

' function for ~ key.
DisplayLengths:
 Call clearstatus
 PRINTf "Editing file: " + RTRIM$(Filename) + " "
 IF FileLength = False THEN
    Call lockedfile
    RETURN
 END IF
 FilePage2 = INT((FileLength - 1) / 320) + 1
 IF CurrentWindow2 = False THEN
    PRINTf "(Last page:" + STR$(FilePage2 - 1) + ") "
 ELSE
    PRINTf "(Last page: " + RIGHT$("00000000" + HEX$(FilePage2 - 1), 8) + "H) "
 END IF
 ' count markers
 Var1! = SFalse
 FOR Var2! = 1! TO MarkerCount
    IF Markers#(Var2!) > 0# THEN
       Var1! = Var1! + 1!
    END IF
 NEXT
 PRINTf "(Markers: " + MID$(STR$(Var1!), 2) + ")"
 Call locatecursor2
 RETURN

' function for ; key.
DisplayCopy:
 Call clearstatus
 PRINTf "Editing file: " + RTRIM$(Filename) + " "
 IF FileLength = False THEN
    Call lockedfile
    RETURN
 END IF
 IF CurrentWindow2 = False THEN
    PRINTf "(Copy position:"
    PRINTf STR$(CopyPositionStart) + " -" + STR$(CopyPositionEnd) + ") "
 ELSE
    PRINTf "(CopyPosition: "
    IF CopyPositionStart = False AND CopyPositionEnd = False THEN
       PRINTf "00000000H - 00000000H) "
    ELSE
       PRINTf RIGHT$("00000000" + HEX$(CopyPositionStart - 1), 8) + "H - "
       PRINTf RIGHT$("00000000" + HEX$(CopyPositionEnd - 1), 8) + "H) "
    END IF
 END IF
 Call locatecursor2
 RETURN

' display paste undos.
DisplayPasteUndos:
 Call clearstatus
 PRINTf "Editing file: " + RTRIM$(Filename) + " "
 IF FileLength = False THEN
    Call lockedfile
    RETURN
 END IF
 IF LOF(5)=0 THEN
    PRINTf " : No paste undos."
 ELSE
    GET #5, 1, CopyByte
    ByteEntries = ASC(CopyByte) * 16 ^ 2
    GET #5, 2, CopyByte
    ByteEntries = ByteEntries + ASC(CopyByte)
    PRINTf " :" + STR$(ByteEntries) + "paste undos stored."
 END IF
 Call locatecursor2
 RETURN

' display path of file being edited.
DisplayPath:
 Call clearstatus
 Z$ = RTRIM$(ShortFilename)
 N$ = RTRIM$(CurrentNetPath)
 IF N$ <> Nul THEN
    IF MID$(Z$, 2, 1) = ":" THEN
       Z$ = MID$(Z$, 3)
    END IF
 END IF
 CALL Deconcatenate(N$, Z$, 64)
 Var = LEN(Z$)
 DO
    Var = Var - 1
    IF Var = False THEN
       Z$ = Nul
       EXIT DO
    END IF
    IF MID$(Z$, Var, 1) = "\" THEN
       Z$ = LEFT$(Z$, Var)
       EXIT DO
    END IF
 LOOP
 IF Z$ = Nul THEN
    Z$ = "\"
 END IF
 PRINTf "Editing path: " + Z$ + " "
 FileDisplay2 = True
 Call locatecursor2
 RETURN

' redraw editing screen
RedrawScreen:
 ScreenDrawn = True
 CALL DisplayScreen
RedrawScreen2:
 Call displayfilename
 Call displayhexpage
 Call displaypagebyte
 IF FileDisplay = False THEN
    Call DisplayFileTitle
 END IF
 RETURN

' search file for ascii string.
SearchASCII:
 CALL SearchFile(0, FoundString)
 GOTO FinishSearch
' search file for hex string.
SearchBytes:
 CALL SearchFile(1, FoundString)
 GOTO FinishSearch
' continue previous search.
ContinueSearch:
 IF RTRIM$(StoreSearchString) = Nul THEN
    RETURN
 END IF
 CALL SearchFile(2, FoundString)
' display search results.
FinishSearch:
 ' check byte comparison.
 IF FoundString = True THEN
    FilePosition = SearchBytePosition
    CALL CalculatePosition1
    IF LastPage <> FilePage THEN
       Call displayhexpage
    END IF
 END IF
 Call displaypagebyte
 IF FoundString = False THEN
    StatusMessage = "String not found."
    Call displaystatus2
    RETURN
 END IF
 Call displayfilename
 RETURN

' dump screen of current page of hex/ascii values to file.
DumpScreen:
 ' open dumpfile
 GOSUB OpenDumpFile
 IF ValidFile = False THEN
    RETURN
 END IF

 ' write screen.
 CALL DumpSub(1)

 ' redisplay page.
 Call displaypagebyte

 ' conanicalize filename.
 Filename2$ = Conanicalize$(DumpFile)
 Var = INSTR(Filename2$, "?")
 IF Var THEN
    MID$(Filename2$, Var, 1) = MID$(STR$(Process.Number), 2)
 END IF

 ' display filename.
 StatusMessage = "Screen dumped to: '" + Filename2$ + "'."
 Call displaystatus1
 RETURN

' dump file of current hex/ascii values to file.
DumpHEXFile:
 ' open dumpfile.
 GOSUB OpenDumpFile
 IF ValidFile = False THEN
    RETURN
 END IF

 ' write file.
 CALL DumpSub(2)

 ' redisplay page.
 Call displaypagebyte

 ' conanicalize filename.
 Filename2$ = Conanicalize$(DumpFile)
 Var = INSTR(Filename2$, "?")
 IF Var THEN
    MID$(Filename2$, Var, 1) = MID$(STR$(Process.Number), 2)
 END IF

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

' open dumpfile.
OpenDumpFile:
 Column = False
 ColumnSpace = False
 HexLine$ = Nul
 HexLine2$ = Nul

 CLOSE #1
 ErrorTrap = False
 ValidFile = True

 ' store filename.
 Filename2$ = DumpFile
 Var = INSTR(Filename2$, "?")
 IF Var THEN
    MID$(Filename2$, Var, 1) = MID$(STR$(Process.Number), 2)
 END IF

 ' test file exists or could exist.
 IF TestFile(Filename2$) = 0 THEN
    StatusMessage = "Error opening dump filename."
    Call displaystatus1
    ValidFile = False
    RETURN
 END IF

 ' check windows.
 IF Windows.Detected THEN
    ' store filenames.
    ASCIIZ3 = ASCIIZ
    ASCIIZ = Filename2$ + CHR$(0)

    ' check file exists.
    IF DIRz$(Filename2$) = Nul THEN

       ' create new file.
       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
    END IF
    ' close long filename search
    InregsX.AX = &H71A1
    InregsX.BX = Wfile.Handle
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' ambiguate filename.
    CALL GetShortFilename(Filename2$)
    ASCIIZ = ASCIIZ3
 END IF

 ' open filename for append.
 OPEN Filename2$ FOR APPEND AS #1
 IF ErrorTrap THEN
    CALL MultiFileFunction(2)
    COLORf Plain
    ScreenDrawn = True
    CALL DisplayScreen
    GOSUB RestoreCurrentFile
    GOSUB RedrawScreen2
    Call restorehilightbytes
    ValidFile = False
 END IF
 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.
 CALL PrintSub(1)
 Call displaypagebyte
 StatusMessage = "Screen dumped to printer port:" + STR$(PortNumber) + "."
 Call 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.
 CALL PrintSub(2)
 Call displaypagebyte
 IF FileDumped THEN
    StatusMessage = "File dumped to printer port:" + STR$(PortNumber) + "."
 ELSE
    StatusMessage = "Partial file dumped to printer port:" + STR$(PortNumber) + "."
 END IF
 Call displaystatus1
 RETURN

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

 ' start print.
 CALL PrintSub(3)
 Call displayfilename
 RETURN

' get printer port.
OpenPrinter:
 ValidPrint = False
 Call clearstatus
 Printf "Enter printer port(1-4)?"
 PortNumber$ = lineinput$(2, 29, 1)
 PortNumber = INT(VAL(PortNumber$))
 IF PortNumber >= 1 AND PortNumber <= 4 THEN
    Check.Disk=True
    Disk.Ready=False
    CLOSE #1
    Filename2$ = "LPT" + MID$(STR$(PortNumber), 2) + ":"
    OPEN Filename2$ FOR OUTPUT AS #1
    Check.Disk=False
    IF Disk.Ready THEN
       StatusMessage = "Invalid printer port number."
       Call displaystatus1
       RETURN
    END IF
 ELSE
    StatusMessage = "Invalid printer port number."
    Call displaystatus1
    RETURN
 END IF
 ValidPrint = True
 RETURN

' add multiple specified byte to file and locate there.
AppendAnyByte:
 Call clearstatus
 PRINTf "Enter number of bytes?"
 ByteString$ = lineinput$(2, 27, 10)
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid append bytes."
    Call displaystatus2
    RETURN
 END IF
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    CALL CheckHexValue(ByteString$)
    IF ValidHexValue = False THEN
       RETURN
    END IF
    NumBytes2# = HexValue - 1#
 ELSE
    CALL CheckASCIIValue(ByteString$)
    IF ValidASCIIValue = False THEN
       RETURN
    END IF
    NumBytes2# = ASCIIValue3
 END IF
 Call clearstatus
 ValidByteString = False
 PRINTf "Enter hex byte?"
 ByteString$ = lineinput$(2, 20, 2)
 IF LEN(ByteString$) = 2 THEN
    AllowWildcard = False
    CALL CheckHexBytes(ByteString$)
    IF ValidByteString THEN
       Byte2 = VAL("&H" + ByteString$)
    END IF
 END IF
 IF ValidByteString = False THEN
    StatusMessage = "Invalid append hex value."
    Call displaystatus2
    RETURN
 END IF
 Call clearstatus
 PRINTf "Editing file: " + RTRIM$(Filename)
 COLORf White
 PRINTf " Appending specified bytes to file:"
 CursorLocation = 53 + LEN(RTRIM$(Filename))
 GOTO AppendBytes2

' add multiple null bytes to file and locate there.
AppendNullBytes:
 Call clearstatus
 PRINTf "Enter number of bytes?"
 ByteString$ = lineinput$(2, 27, 10)
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid append bytes."
    Call displaystatus2
    RETURN
 END IF
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    CALL CheckHexValue(ByteString$)
    IF ValidHexValue = False THEN
       RETURN
    END IF
    NumBytes2# = HexValue - 1#
 ELSE
    CALL CheckASCIIValue(ByteString$)
    IF ValidASCIIValue = False THEN
       RETURN
    END IF
    NumBytes2# = ASCIIValue3
 END IF
 Byte2 = False
 Call clearstatus
 PRINTf "Editing file: " + RTRIM$(Filename)
 COLORf White
 PRINTf " Appending null bytes to file:"
 CursorLocation = 48 + LEN(RTRIM$(Filename))

' append bytes past end of file.
AppendBytes2:
 LastPage = FilePage
 Call clearpagebyte
 PercentDisplayed! = SFalse
 LOCATEf 2, CursorLocation, 0
 PRINTf " 0%"
 BlockBytes# = INT(NumBytes2# / 32767#)
 RemainingBytes# = NumBytes2# MOD 32767#
 ' write 32767 byte blocks.
 IF BlockBytes# > 0# THEN
    BlockByte = STRING$(32767, CHR$(Byte2))
    FOR Byte1# = 1# TO BlockBytes#
       IF INKEY$ = CHR$(27) THEN
          GOTO EndAppend
       END IF
       IF FileLength + 32767# <= 2147483647# THEN
          SeekPosition = FileLength + 1#
          FileLength = FileLength + 32767#
          Call lseekfile
          GOSUB WriteBlockFile
          IF Write.Error Then
             Call GetFileLength(Var)
             SeekPosition = FileLength
             Call lseekfile
             Goto EndAppend
          END IF
          FilePosition = FileLength
          CALL CalculatePosition1
          PercentCopied! = INT(CSNG(Byte1# / BlockBytes#) * 100!)
          IF PercentCopied! > PercentDisplayed! THEN
             LOCATEf 2, CursorLocation, 0
             PRINTf STR$(PercentCopied!) + "%"
             PercentDisplayed! = PercentCopied!
          END IF
       END IF
    NEXT
 END IF
 ' write remaining bytes.
 PercentDisplayed! = SFalse
 LOCATEf 2, CursorLocation, 0
 PRINTf " 0%  "
 IF RemainingBytes# > 0# THEN
    FileByte = CHR$(Byte2)
    FOR Byte1# = 1# TO RemainingBytes#
       IF FileLength + 1# <= 2147483647# THEN
          FileLength = FileLength + 1#
          SeekPosition = FileLength
          Call lseekfile
          Call writefile
          IF Write.Error Then
             Call GetFileLength(Var)
             SeekPosition = FileLength
             Call lseekfile
             Goto EndAppend
          END IF
          FilePosition = FileLength
          CALL CalculatePosition1
          IF LastPage = FilePage THEN
             Call clearpagebyte
          END IF
          PercentCopied! = INT(CSNG(Byte1# / RemainingBytes#) * 100!)
          IF PercentCopied! > PercentDisplayed! THEN
             LOCATEf 2, CursorLocation, 0
             PRINTf STR$(PercentCopied!) + "%"
             PercentDisplayed! = PercentCopied!
          END IF
       ELSE
          EXIT FOR
       END IF
    NEXT
 END IF
EndAppend:
 CALL CalculatePosition1
 IF LastPage <> FilePage THEN
    LastPage = FilePage
    Call displayhexpage
 END IF
 Call displaypagebyte
 Call displayfilename
 RETURN 

' add multiple bytes of an ASCII string to file and locate there.
AppendASCII:
 Call clearstatus
 PRINTf "Enter ASCII string?"
 ByteString$ = lineinput$(2, 24, 53)
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid append string."
    Call displaystatus2
    RETURN
 END IF
 NumBytes = LEN(ByteString$)
 CALL AsciiToHex2(ByteString$)
 GOTO StartAppend

' add multiple bytes to file and locate there.
'   left window specifies space-separated hex byte pairs,
'   right window specifies space-separated 3-byte ascii pairs.
AppendByte:
 Call clearstatus
 IF CurrentWindow = False THEN
    PRINTf "Enter hex byte(s)?"
    ByteString$ = lineinput$(2, 23, 55)
    IF LEFT$(ByteString$, 1) = "+" THEN
       ByteString$ = MID$(ByteString$, 2)
       CALL CheckAsciiBytes(ByteString$)
    ELSE
       AllowWildcard = False
       CALL CheckHexBytes(ByteString$)
    END IF
 ELSE
    PRINTf "Enter ascii byte(s)?"
    ByteString$ = lineinput$(2, 25, 53)
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = MID$(ByteString$, 2)
       AllowWildcard = False
       CALL CheckHexBytes(ByteString$)
    ELSE
       CALL CheckAsciiBytes(ByteString$)
    END IF
 END IF
 IF ValidByteString = False THEN
    StatusMessage = "Invalid append string."
    Call displaystatus2
    RETURN
 END IF

' append the byte string.
StartAppend:
 LastPage = FilePage
 FOR Byte1 = 1 TO NumBytes
    Call clearpagebyte
    Byte$ = MID$(ByteString$, (Byte1 - 1) * 2 + 1, 2)
    Byte2 = VAL("&H" + Byte$)
    GOSUB AppendIt
    IF ValidAppend = False THEN
       EXIT FOR
    END IF
 NEXT
 Call displayfilename
 RETURN

' appends a byte to file.
AppendIt:
 ValidAppend = False
 FileByte = CHR$(Byte2)
 IF FileLength + 1# <= 2147483647# THEN
    FileLength = FileLength + 1#
    SeekPosition = FileLength
    Call lseekfile
    Call writefile
    IF Write.Error Then
       Call GetFileLength(Var)
       SeekPosition = FileLength
       Call lseekfile
       Call displaypagebyte
       RETURN
    END IF
    ValidAppend = True
    FilePosition = FileLength
    CALL CalculatePosition1
    IF LastPage <> FilePage THEN
       LastPage = FilePage
       Call displayhexpage
    END IF
    Call displaypagebyte
 END IF
 RETURN

' jump to page position in file.
JumpPage:
 Call clearstatus
 PRINTf "Enter page position?"
 ByteString$ = lineinput$(2, 25, 10)
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    CALL CheckHexValue(ByteString$)
    IF ValidHexValue = False THEN
       RETURN
    END IF
    NextPage = HexValue
 ELSE
    CALL CheckASCIIValue(ByteString$)
    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
    Call 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
    CALL CalculatePosition1
    IF LastPage <> FilePage THEN
       Call displayhexpage
    END IF
    Call displaypagebyte
    Call displayfilename
    RETURN
 END IF
 StatusMessage = "Invalid page."
 Call displaystatus2
 RETURN

' jump to a byte position in file.
JumpByte:
 Call clearstatus
 PRINTf "Enter file position? "
 ByteString$ = lineinput$(2, 25, 10)
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    CALL CheckHexValue(ByteString$)
    IF ValidHexValue = False THEN
       RETURN
    END IF
    NewByte = HexValue
 ELSE
    CALL CheckASCIIValue(ByteString$)
    IF ValidASCIIValue = False THEN
       RETURN
    END IF
    NewByte = ASCIIValue3
 END IF
 IF NewByte >= 1 AND NewByte <= FileLength THEN
    IF NewByte <> FilePosition THEN
       Call clearpagebyte
       LastPage = FilePage
       FilePosition = NewByte
       CALL CalculatePosition1
       IF LastPage <> FilePage THEN
          Call displayhexpage
       END IF
       Call displaypagebyte
    END IF
    Call displayfilename
    RETURN
 END IF
 StatusMessage = "Invalid byte."
 Call displaystatus2
 RETURN

' undo a byte from stored arrays of previous position/byte value.
UndoByte:
 IF CurrentUndo > 0! THEN
    Call clearpagebyte
    LastPage = FilePage
    FileByte = CHR$(UndoByte%(CurrentUndo))
    FilePosition = UndoPosition#(CurrentUndo)
    CurrentUndo = CurrentUndo - 1!
    SeekPosition = FilePosition
    Call lseekfile
    Call writefile
    CALL CalculatePosition1
    IF LastPage <> FilePage THEN
       Call displayhexpage
    END IF
    Call 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
       Call clearpagebyte
       LastPage = FilePage
       FileByte = CHR$(UndoByte%(NextUndo!))
       FilePosition = UndoPosition#(NextUndo!)
       CurrentUndo = CurrentUndo - 1!
       SeekPosition = FilePosition
       Call lseekfile
       Call writefile
       CALL CalculatePosition1
       IF LastPage <> FilePage THEN
          LastPage = FilePage
          Call displayhexpage
       END IF
       Call displaypagebyte
    NEXT
 END IF
 RETURN

REM Cursor key routines:

' move cursor up.
CursorUp:
 IF PageRow - 1 >= 1 THEN
    Call clearpagebyte
    PageRow = PageRow - 1
    FilePosition = FilePosition - 20
    Call displaypagebyte
    RETURN
 END IF
 Call clearpagebyte
 Call displaypagebyte
 RETURN

' move cursor down.
CursorDown:
 IF PageRow + 1 <= 16 THEN
    IF FilePosition + 20 <= FileLength THEN
       Call clearpagebyte
       PageRow = PageRow + 1
       FilePosition = FilePosition + 20
       IF FilePosition > FileLength THEN
          PageColumn = PageColumn - 1
          FilePosition = FilePosition - 1
       END IF
       Call displaypagebyte
       RETURN
    END IF
 END IF
 Call clearpagebyte
 Call displaypagebyte
 RETURN

' move cursor left.
CursorLeft:
 IF PageColumn - 1 >= 1 THEN
    IF CopyPositionStart > False THEN
       IF PageColumn = 20 THEN
          Call clearpagebyte
          Call displaypagebyte
          RETURN
       END IF
       IF FilePosition = FileLength THEN
          Call clearpagebyte
          Call displaypagebyte
          RETURN
       END IF
    END IF
    Call clearpagebyte
    PageColumn = PageColumn - 1
    FilePosition = FilePosition - 1
    Call displaypagebyte
 ELSE
    Call clearpagebyte
    IF PageColumn > 1 THEN
       PageColumn = 1
       FilePosition = FilePosition - 1
    END IF
    Call displaypagebyte
 END IF
 RETURN

' move cursor right.
CursorRight:
 IF PageColumn + 1 <= 20 THEN
    IF FilePosition + 1 <= FileLength THEN
       Call clearpagebyte
       IF PageColumn < 20 THEN
          IF FilePosition + 1 <= FileLength THEN
             PageColumn = PageColumn + 1
             FilePosition = FilePosition + 1
          END IF
       END IF
       Call displaypagebyte
       RETURN
    END IF
 END IF
 Call clearpagebyte
 Call 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
       Call clearpagebyte
       FilePosition = FilePosition - PageColumn + 20
       PageColumn = 20
       Call displaypagebyte
    ELSE
       ' position end of file
       Call clearpagebyte
       FilePosition = FileLength
       CALL CalculatePosition1
       Call displaypagebyte
    END IF
 ELSE
    Call clearpagebyte
    Call displaypagebyte
    Call locatecursor
 END IF
 RETURN

' home key.
HomeKey:
 IF PageColumn > 1 THEN
    Call clearpagebyte
    FilePosition = FilePosition - PageColumn + 1
    PageColumn = 1
    Call displaypagebyte
 ELSE
    Call clearpagebyte
    IF PageColumn > 1 THEN
       PageColumn = 1
       FilePosition = FilePosition - 1
    END IF
    Call displaypagebyte
    Call locatecursor
 END IF
 RETURN

' control-left key.
CtrlLeftKey:
 NewColumn = False
 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 > False THEN
    Call clearpagebyte
    FilePosition = FilePosition - PageColumn + NewColumn
    PageColumn = NewColumn
    Call displaypagebyte
 END IF
 RETURN

' control-right key.
CtrlRightKey:
 NewColumn = False
 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
       Call clearpagebyte
       FilePosition = FilePosition - PageColumn + NewColumn
       PageColumn = NewColumn
       Call displaypagebyte
    ELSE
       ' position end of file
       Call clearpagebyte
       FilePosition = FileLength
       CALL CalculatePosition1
       Call displaypagebyte
    END IF
 END IF
 RETURN

' control-end key.
CtrlEndKey:
 IF FilePosition <> FileLength THEN
    LastPage = FilePage
    Call clearpagebyte
    FilePosition = FileLength
    CALL CalculatePosition1
    IF LastPage <> FilePage THEN
       Call displayhexpage
    END IF
    Call displaypagebyte
 ELSE
    Call clearpagebyte
    Call displaypagebyte
    Call locatecursor
 END IF
 RETURN

' control-home key.
CtrlHomeKey:
 IF FilePosition <> 1 THEN
    LastPage = FilePage
    Call clearpagebyte
    FilePosition = 1
    CALL CalculatePosition1
    IF LastPage <> FilePage THEN
       Call displayhexpage
    END IF
    Call 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
    Call displayhexpage
    Call displaypagebyte
 ELSE
    ' check there is a first position on the first page to move up to.
    IF FilePosition > 1 THEN
       Call clearpagebyte
       PageRow = 1
       PageColumn = 1
       FilePosition = 1
       Call displaypagebyte
    ELSE
       Call clearpagebyte
       Call displaypagebyte
       Call 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
    Call displayhexpage
    Call displaypagebyte
 ELSE
    ' check there is a last position on the last page to move down to.
    IF FilePosition < FileLength THEN
       Call clearpagebyte
       FilePosition = FileLength
       CALL CalculatePosition1
       Call displayhexpage
       Call displaypagebyte
    ELSE
       Call clearpagebyte
       Call displaypagebyte
       Call locatecursor
    END IF
 END IF
 RETURN

REM More routines:

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

' switch editing windows.
ToggleRight:
 CurrentWindow = False
 CurrentWindow2 = NOT CurrentWindow2
 Call redrawrightwindow
 Call 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:
 Call clearstatus
 FileByte = Nul
 ValidByteString = False
 IF CurrentWindow = False THEN
    PRINTf "Enter hex byte value?"
    ByteString$ = lineinput$(2, 26, 4)
    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
          CALL CheckHexBytes(BYteString$)
          IF ValidByteString THEN
             NewValue = VAL("&H" + ByteString$)
             FileByte = CHR$(NewValue)
          END IF
       END IF
    END IF
 ELSE
    PRINTf "Enter ascii byte value?"
    ByteString$ = lineinput$(2, 28, 4)
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = MID$(ByteString$, 2)
       IF LEN(ByteString$) = 2 THEN
          AllowWildcard = False
          CALL CheckHexBytes(ByteString$)
          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."
    Call displaystatus2
    RETURN
 END IF
 GOSUB StoreUndo
 SeekPosition = FilePosition
 Call lseekfile
 Call writefile
 Call displaypagebyte
 Call displayfilename
 RETURN

' change the ascii values with string starting at the current position.
ChangeASCIIValues:
 Call clearstatus
 PRINTf "Enter ascii value(s)?"
 ByteString$ = lineinput$(2, 26, 52)
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid ascii string."
    Call displaystatus2
    RETURN
 END IF
 ' init file.
 IF FileLength = 0 THEN
    Byte2 = 0
    Gosub AppendIt
    If ValidAppend = False Then
       RETURN
    Endif
 ELSE
    ' store all current values.
    StorePosition = FilePosition
    FOR NumberBytes = 1 TO LEN(ByteString$)
       SeekPosition = FilePosition
       Call lseekfile
       Call readfile
       FileByte = Buffer
       AsciiValue = ASC(FileByte)
       GOSUB StoreUndo
       IF FilePosition = FileLength THEN
          EXIT FOR
       END IF
       FilePosition = FilePosition + 1
    NEXT
    FilePosition = StorePosition
 END IF
 ' write ascii string.
 ValidAppend = True
 FOR NumberBytes = 1 TO LEN(ByteString$)
    IF FilePosition > FileLength THEN
       Byte2 = 0
       Gosub AppendIt
       If ValidAppend = False Then
          EXIT FOR
       Endif
    END IF
    FileByte = MID$(ByteString$, NumberBytes, 1)
    SeekPosition = FilePosition
    Call lseekfile
    Call writefile
    CALL CalculatePosition1
    IF LastPage <> FilePage THEN
       LastPage = FilePage
       Call displayhexpage
    END IF
    Call clearpagebyte
    FilePosition = FilePosition + 1
 NEXT
 If ValidAppend Then
    FilePosition = FilePosition - 1
 Endif
 Call displaypagebyte
 Call displayfilename
 RETURN

' change the hex values with string starting at the current position.
ChangeHEXValues:
 Call clearstatus
 PRINTf "Enter HEX value(s)?"
 ByteString$ = lineinput$(2, 24, 54)
 IF LEN(ByteString$) = False THEN
    StatusMessage = "Invalid hex string."
    Call displaystatus2
    RETURN
 END IF
 ' check hex string.
 AllowWildcard = False
 CALL CheckHexBytes(BYteString$)
 IF ValidByteString = False THEN
    StatusMessage = "Invalid hex string."
    Call displaystatus2
    RETURN
 END IF
 ' init file.
 IF FileLength = 0 THEN
    Byte2 = 0
    Gosub AppendIt
    If ValidAppend = False Then
       RETURN
    Endif
 ELSE
    ' store all current values.
    StorePosition = FilePosition
    FOR NumberBytes = 1 TO LEN(ByteString$)
       SeekPosition = FilePosition
       Call lseekfile
       Call readfile
       FileByte = Buffer
       AsciiValue = ASC(FileByte)
       GOSUB StoreUndo
       IF FilePosition = FileLength THEN
          EXIT FOR
       END IF
       FilePosition = FilePosition + 1
    NEXT
    FilePosition = StorePosition
 END IF
 ' write hex string.
 ValidAppend = True
 FOR NumberBytes = 1 TO LEN(ByteString$) STEP 2
    IF FilePosition > FileLength THEN
       Byte2 = 0
       Gosub AppendIt
       If ValidAppend = False Then
          EXIT FOR
       Endif
    END IF
    FileByte = CHR$(VAL("&H" + MID$(ByteString$, NumberBytes, 2)))
    SeekPosition = FilePosition
    Call lseekfile
    Call writefile
    CALL CalculatePosition1
    IF LastPage <> FilePage THEN
       LastPage = FilePage
       Call displayhexpage
    END IF
    Call clearpagebyte
    FilePosition = FilePosition + 1
 NEXT
 If ValidAppend Then
    FilePosition = FilePosition - 1
 Endif
 Call displaypagebyte
 Call displayfilename
 RETURN

' record the current byte position and value,
' increment the number of undos in record structure array.
StoreUndo:
 IF CurrentUndo < 16777216! THEN
    CurrentUndo = CurrentUndo + 1!
    GET #6, CurrentUndo!, UndoFile
    UndoFile.UndoByte1(CurrentFile) = AsciiValue
    UndoFile.UndoPosition1(CurrentFile) = FilePosition
    PUT #6, CurrentUndo!, UndoFile
 END IF
 RETURN

' drop down input menu.
Menu2:
 CALL SMouse
 CALL DropDownMenu
 GOSUB RestorePosition
 IF CurrentMenu = False THEN
    Call locatecursor
    GOTO StartLoop
 END IF
 ' reset mouse activity.
 CALL HMouse
 SELECT CASE CurrentMenu
 CASE 1
    SELECT CASE CurrentMenuSelection
    CASE 1 ' Alt-N  Open new file
       IF NumberFiles < 9 THEN
          CALL MultiFileFunction(2)
          GOSUB NewFile
          GOTO BeginFile
       END IF
    CASE 2 ' Alt-W  Close file
       GOSUB CloseCurrentFile
       IF NumberFiles = False THEN
          GOSUB NewFile
          GOTO BeginFile
       END IF
    CASE 3 ' Alt-0  Close All Files
       GOSUB CloseAllFiles
       GOSUB ResetAllAttributes
       GOSUB NewFile
       NumberFiles = False
       GOTO BeginFile
    CASE 4 ' Alt-V  View files
       CALL ViewFiles(Var)
       IF Var > False THEN
          NextFile = Var
          GOSUB SelectFile
       END IF
       Call displayfilename
    CASE 5 ' Alt-Y  Toggle window
       IF FileLength THEN
          GOSUB ToggleRight
       END IF
    CASE 6 ' 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
    IF FileLength Then
       SELECT CASE CurrentMenuSelection
       CASE 1 ' Alt-D  HEX Screen Dump
          GOSUB DumpScreen
       CASE 2 ' Alt-E  HEX File Dump
          GOSUB DumpHEXFile
       END SELECT
    END IF
 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
       CALL MultiFileFunction(2)
       COLORf Plain
       ScreenDrawn = True
       CALL DisplayScreen
       GOSUB RestoreCurrentFile
       GOSUB RedrawScreen2
       Call restorehilightbytes
    CASE 4 ' Alt-U  Undo last byte
       IF FileLength Then
          GOSUB UndoByte
       END IF
    CASE 5 ' Alt-Z  Undo All bytes
       IF FileLength Then
          GOSUB UndoAll
       END IF
    END SELECT
 CASE 5
    IF FileLength Then
       SELECT CASE CurrentMenuSelection
       CASE 1 ' Alt-J  Jump to byte
          GOSUB JumpByte
       CASE 2 ' Alt-L  Jump to page
          GOSUB JumpPage
       END SELECT
    END IF
 CASE 6
    IF FileLength Then
       SELECT CASE CurrentMenuSelection
       CASE 1 ' Alt-P  HEX Screen Print
          GOSUB PrintScreen
       CASE 2 ' Alt-T  HEX File Print
          GOSUB PrintFile
       END SELECT
    END IF
 CASE 7
    IF FileLength Then
       SELECT CASE CurrentMenuSelection
       CASE 1 ' Alt-K  Search for string
          GOSUB SearchASCII
       CASE 2 ' Alt-S  Search for bytes
          GOSUB SearchBytes
       END SELECT
    END IF
 END SELECT
 ' reset mouse activity.
 CALL SMouse
 GOSUB RestorePosition
 Call locatecursor
 GOTO StartLoop

REM Long filename routines follow:

REM open file based on Windows or DOS loaded.
REM   read file attribute,
REM   reset and store read-only bit,
REM   create file if nonexistent.
REM
REM returns: ValidFile=True if open successful.
REM          FileLocked=True if file locked.

OpenFile:
 IF Windows.Detected THEN
    GOSUB OpenFile1
 ELSE
    GOSUB OpenFile2
 END IF
 RETURN

REM open Windows file.
OpenFile1:
 ErrorTrap = False
 FileLocked = False
 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 1) = 1 THEN
    GOSUB SetWINAttribute
 END IF
 Check.Disk = True
 Disk.Ready = False
 GOSUB OpenWINFile
 Check.Disk = False
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    IF ErrorTrap = 70 Then
       Gosub CloseIt
       Check.Disk = True
       Disk.Ready = False
       Gosub OpenWINFile2
       Check.Disk = False
       FileLocked = True
    END IF
    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 CloseIt
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 GOSUB OpenWINFile
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 IF ErrorTrap = 70 Then
    FileLocked = True
 END IF
 FileAttribute = False
 ValidFile = True
 RETURN

REM open DOS file.
OpenFile2:
 ErrorTrap = False
 FileLocked = False
 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 1) = 1 THEN
    GOSUB SetDOSAttribute
 END IF
 Check.Disk = True
 Disk.Ready = False
 GOSUB OpenDOSFile
 Check.Disk = False
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    IF ErrorTrap = 70 Then
       Gosub CloseIt
       Check.Disk = True
       Disk.Ready = False
       Gosub OpenDOSFile2
       Check.Disk = False
       FileLocked = True
    END IF
    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 CloseIt
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 GOSUB OpenDOSFile
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    RETURN
 END IF
 IF ErrorTrap = 70 Then
    FileLocked = True
 END IF
 FileAttribute = False
 ValidFile = True
 RETURN

REM check command line and create filename.
GetFilename:
 IF LEFT$(Filename, 2) <> "\\" THEN
    IF LEFT$(Filename, 1) = "\" THEN
       ASCIIZ = LEFT$(CURDIR$, 2) + RTRIM$(Filename) + CHR$(0)
    ELSE
       IF MID$(Filename, 2, 1) <> ":" THEN
          IF RIGHT$(CURDIR$, 1) = "\" THEN
             ASCIIZ = CURDIR$ + RTRIM$(Filename) + CHR$(0)
          ELSE
             ASCIIZ = CURDIR$ + "\" + RTRIM$(Filename) + CHR$(0)
          END IF
       END IF
    END IF
 END IF
 RETURN

REM open file read/write in Windows.
OpenWINFile:
 InregsX.AX = &H716C
 InregsX.BX = &H0002
 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

REM open file read-only in Windows.
OpenWINFile2:
 InregsX.AX = &H716C
 InregsX.BX = &H0044
 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

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

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

REM open file read/write in DOS.
OpenDOSFile:
 InregsX.AX = &H6C00
 InregsX.BX = &H1002
 InregsX.CX = FileAttribute
 InregsX.DX = &H1
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.SI = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 Handle = OutregsX.AX
 RETURN

REM open file read-only in DOS.
OpenDOSFile2:
 InregsX.AX = &H6C00
 InregsX.BX = &H1000
 InregsX.CX = FileAttribute
 InregsX.DX = &H1
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.SI = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 Handle = OutregsX.AX
 RETURN

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

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

REM 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

REM create new file in DOS.
CreateDOSFile:
 InregsX.AX = &H6C00
 InregsX.BX = &H1000
 InregsX.CX = &H0
 InregsX.DX = &H10
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.SI = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 Handle = OutregsX.AX
 RETURN

REM close all files.
CloseAllFiles:
 FOR Var = 1 TO NumberFiles
    Handle = File(Var).Handle
    FileLocked = File(Var).FileLocked
    FileLength = File(Var).FileLength
    IF Handle Then
       GOSUB CloseFile
    END IF
 NEXT
 RETURN

REM check close file.
CloseFile:
 IF FileLocked = True Then
    RETURN
 END IF

REM close dos file.
CloseIt:
 InregsX.AX = &H3E00
 InregsX.BX = Handle
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

REM write byte block to file.
WriteBlockFile:
 IF FileLocked Then
    Write.Error = True
    RETURN
 END IF
 Write.Error = False
 InregsX.AX = &H4000
 InregsX.BX = Handle
 InregsX.CX = 32767
 InregsX.DS = VARSEG(BlockByte)
 InregsX.DX = VARPTR(BlockByte)
 CALL InterruptX(&H21, InregsX, OutregsX)
 IF (OutregsX.Flags AND &H1) = &H1 Then
    Write.Error = True
 Else
    IF OutregsX.AX = False Then
       Write.Error = True
    END IF
 END IF
 RETURN

REM get netpath from filename
ParseNetPath: ' \\serv1\share\filename.ext
 Netpath$ = Nul
 IF LEFT$(Filename, 2) = "\\" THEN
    Net1 = INSTR(3, Filename, "\")
    IF Net1 THEN
       Net2 = INSTR(Net1 + 1, Filename, "\")
       IF Net2 THEN
          Netpath$ = LEFT$(Filename, Net2 - 1)
       END IF
    END IF
 END IF
 RETURN

REM conanicalize filename for display purposes.
ShortFilename:
 ' read ambiguated 8.3 filename.
 ASCIIZ3 = ASCIIZ
 CALL GetShortFilename(Short.Filename$)
 ASCIIZ = ASCIIZ3

 ' store 64-byte filename.
 Filename = Short.Filename$
 ShortFilename = Filename
 CurrentNetPath = NetPath$

 ' read conanicalized 8.3 filename.
 Filename = Conanicalize$(Filename)
 RETURN

REM restore all read-only attributes.
ResetAllAttributes:
 FOR Var = 1 TO NumberFiles
    ' reset file attribute.
    ASCIIZ = File(Var).ASCIIZ
    FileAttribute = File(Var).FileAttribute
    Handle = File(Var).Handle
    If Handle Then
       GOSUB ResetAttribute
    Endif
 NEXT
 RETURN

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

REM 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

REM 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 run DOS command in shell.
DOSCommand:
 CALL MultiFileFunction(2)
 Call clearstatus
 PRINTf "Enter DOS command: "
 InputString$ = lineinput$(2, 23, 55)
 IF LEN(InputString$) THEN
    CALL MultiFileFunction(2)

    ' clear screen
    COLORf Plain
    CLS

    ' open filename.
    Close #1
    DOSfilename$ = "DOSEXIT"+MID$(STR$(Process.Number), 2)+".BAT"
    Open DOSfilename$ For Output Shared As #1

    ' write DOS commands.
    Print #1, InputString$
    Print #1,"PAUSE"
    Print #1,"EXIT"
    Close #1

    ' store current drive/path.
    Var$=Curdir$

    ' restore control-break.
    Call ResetTrap

    ' shell to batch file.
    Shell DOSfilename$

    ' restore drive/path.
    Chdrive Var$
    Chdir Var$

    ' reset control-break.
    Call SetTrap
 END IF
 ScreenDrawn = True
 CALL DisplayScreen
 GOSUB RestoreCurrentFile
 GOSUB RedrawScreen2
 Call restorehilightbytes
 RETURN

REM Mouse routines follow:

' move mouse cursor for mouseover.
MoveMouse:
 ' check mouse boundaries.
 IF Mouse.Row = 1 THEN
    NewMenuSelection = False
    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.
             CALL HMouse
             COLORf2 White, 0
             CALL DisplayTab(CurrentMenu)
             CALL SMouse
          END IF
       END IF
       CurrentMenu = NewMenuSelection
       CurrentMenuSelection = 1
    END IF
    ' hilight menu tab.
    IF CurrentMenu > False THEN
       CALL HMouse
       COLORf2 White, 1
       CALL DisplayTab(CurrentMenu)
       CALL SMouse
    END IF
    Call 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
 Call locatecursor2
 RETURN

' restores old mouse position.
MoveOldPosition:
 Store1 = Mouse.Row
 Store2 = Mouse.Column
 Mouse.Row = Old.Mouse.Row
 Mouse.Column = Old.Mouse.Column
 NewPosition = CalculatePosition3#
 Mouse.Row = Store1
 Mouse.Column = Store2
 TempPosition3 = NewPosition
 CALL CalculatePosition2
 Column2 = CalculateColumn2
 GOSUB CheckMousePosition
 IF MouseArea = False THEN
    CALL HMouse
    COLORf White
    LOCATEf PageRow2 + 3, Column2 + 5, 0
    VarX = SCREEN(PageRow2 + 3, Column2 + 5)
    PRINTf CHR$(VarX)
    LOCATEf PageRow2 + 3, Column2 + 6, 0
    VarX = SCREEN(PageRow2 + 3, Column2 + 6)
    PRINTf CHR$(VarX)
    CALL SMouse
 END IF
 COLORf2 Plain, 0
 RETURN

' restores original mouse position.
RestoreOldPosition:
 SeekPosition = FilePosition
 Call lseekfile
 Call readfile
 FileByte = Buffer
 ByteValue = ASC(FileByte)
 Column = CalculateColumn
 GOSUB CheckMousePosition2
 IF MouseArea = False THEN
    CALL HMouse
    COLORf Yellow
    LOCATEf PageRow + 3, Column + 5, 0
    PRINTf RIGHT$("00" + HEX$(ByteValue), 2)
    CALL SMouse
 END IF
 COLORf Plain
 RETURN

' moves to new mouse position.
MoveNewPosition:
 NewPosition = CalculatePosition3#
 FilePosition2 = NewPosition
 TempPosition3 = NewPosition
 CALL CalculatePosition2
 Column2 = CalculateColumn2
 GOSUB CheckMousePosition
 IF MouseArea = False THEN
    CALL HMouse
    COLORf2 White, 1
    LOCATEf PageRow2 + 3, Column2 + 5, 0
    VarX = SCREEN(PageRow2 + 3, Column2 + 5)
    PRINTf CHR$(VarX)
    LOCATEf PageRow2 + 3, Column2 + 6, 0
    VarY = SCREEN(PageRow2 + 3, Column2 + 6)
    PRINTf CHR$(VarY)
    CALL SMouse
 END IF
 COLORf2 Plain, 0
 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))

 ' store old values.
 StoreValue1# = FilePosition
 StoreValue2% = AsciiValue2
 ' reset to mouseover values.
 FilePosition = FilePosition2
 AsciiValue = AsciiValue2
 ' display mouseover position.
 Call displayfilename
 ' restore old values.
 FilePosition = StoreValue1#
 AsciiValue = StoreValue2%
 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
          Store1 = Mouse.Row
          Store2 = Mouse.Column
          Mouse.Row = Old.Mouse.Row
          Mouse.Column = Old.Mouse.Column
          NewPosition = CalculatePosition3#
          Mouse.Row = Store1
          Mouse.Column = Store2
          TempPosition3 = NewPosition
          CALL CalculatePosition2
          Column2 = CalculateColumn2
          IF CopyPositionStart = False THEN
             GOTO RestoreMouseByte
          END IF
          IF CopyPositionStart > False THEN
             IF NewPosition >= CopyPositionStart THEN
                IF NewPosition <= CopyPositionEnd THEN
                   RETURN
                END IF
             END IF
          END IF
          GOTO RestoreMouseByte
       END IF
    END IF
 END IF
 RETURN

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

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

' display hilighted mouse position byte.
RestoreMouseByte:
 CALL HMouse
 COLORf2 White, 1
 LOCATEf PageRow2 + 3, Column2 + 5, 0
 VarX = SCREEN(PageRow2 + 3, Column2 + 5)
 PRINTf CHR$(VarX)
 LOCATEf PageRow2 + 3, Column2 + 6, 0
 VarY = SCREEN(PageRow2 + 3, Column2 + 6)
 PRINTf CHR$(VarY)
 COLORf2 Plain, 0
 CALL SMouse
 RETURN

' process left mouse button click.
MouseButton1:
 ' stop program.
 IF Mouse.Row = 3 AND Mouse.Column = 77 THEN
    CALL 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
          CALL HMouse
          ' remove hilight menu tab.
          COLORf2 White, 0
          CALL DisplayTab(CurrentMenu)
          CALL SMouse
       END IF
    END IF
    CurrentMenu = NewMenuSelection
    CurrentMenuSelection = 1
    CALL HMouse
    ' hilight menu tab.
    COLORf2 White, 1
    CALL DisplayTab(CurrentMenu)
    CALL SMouse
    ' jump to file menu.
    CALL 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
          NewPosition = CalculatePosition3#
          Call clearpagebyte
          FilePosition = NewPosition
          CALL CalculatePosition1
          Call displaypagebyte
          Call 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
          NewPosition = CalculatePosition3#
          Call clearpagebyte
          FilePosition = NewPosition
          CALL CalculatePosition1
          Call displaypagebyte
          Call displayfilename
          GOSUB ChangeHexValue
          RETURN
       END IF
    END IF
 END IF
 RETURN

' reset hilight bytes for mouse.
ResetMouseBytes:
 IF CopyPositionStart = False THEN
    Call locatecursor2
    RETURN
 END IF
 Call resetbytes
 Call clearpagebyte
 Call displaypagebyte
 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.
 COLORf White
 LOCATEf Csrlin, 1, 0
 PRINTf "Hex Editor Main " + Version + " " + Release + " critical error trap:"
 ' display error.
 COLORf Yellow
 LOCATEf Csrlin+1, 1, 0
 CALL DisplayCriticalError(ErrorTrap)
 ' display error prompt.
 COLORf Green
 LOCATEf Csrlin+1, 1, 0
 PRINTf "Press R(etry), C(ontinue), Q(uit), A(bort):"
 LOCATEf Csrlin, 44, 1
 ' 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"
       PRINTf "r"
       LOCATEf Csrlin+1, 1, 0
       RESUME
    CASE "c"
       PRINTf "c"
       LOCATEf Csrlin+1, 1, 0
       RESUME NEXT
    CASE "q"
       PRINTf "q"
       LOCATEf Csrlin+1, 1, 0
       IF CurrentFile = False THEN
          GOSUB RedrawScreen
          RESUME NEXT
       END IF
       RESUME TopProgram
    CASE "a"
       PRINTf "a"
       LOCATEf Csrlin+1, 1, 0
       RESUME Stoploop
    END SELECT
    R = ReleaseTime
 LOOP
 END

' end of program.
Stoploop:
 ' close all files.
 GOSUB CloseAllFiles
 GOSUB ResetAllAttributes

' stop program.
StopProgram:
 On Error Resume Next
 COLORf2 7, 0

 ' restore current drive.
 InregsX.AX = &HE00
 InregsX.DX = Drive.Number ' 0=a, 1=b, ..
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' check windows dos.
 ASCIIZ3 = Directory.ASCIIZ
 IF Windows.Detected THEN
    ' restore current directory.
    InregsX.AX = &H713B
    InregsX.DS = VARSEG(ASCIIZ3)
    InregsX.DX = VARPTR(ASCIIZ3)
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    ' restore current directory.
    InregsX.AX = &H3B00
    InregsX.DS = VARSEG(ASCIIZ3)
    InregsX.DX = VARPTR(ASCIIZ3)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF
 CALL ResetTrap
 END

' terminal error exits here.
Error.Exit:
 ' display error.
 COLORf2 White, 0
 CLS
 PRINTf "Hex Editor " + Version + " " + Release + " critical error trap:"
 ' display error and exit.
 COLORf Yellow
 LOCATEf Csrlin+1, 1, 0
 CALL DisplayCriticalError(ErrorTrap)
 COLORf Green
 LOCATEf Csrlin+1, 1, 0
 PRINTf "Close Hexedit processes, Check TEMP variables, .CFG file,"
 LOCATEf Csrlin+1, 1, 0
 PRINTf "or DOS file handles, then restart."
 COLORf Plain
 CALL ResetTrap
 END

REM End-of-subprogram. I want 2 black siamese cats.
