REM Program: Hex Editor for DOS v1.9a
REM Author: Erik Jon Oredson AS. Csci
REM Release: 08/10/2001.
REM Status: Public Domain.
REM Email: eoredson@yahoo.com

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

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

' declare error trap.
ON ERROR GOTO Error.Routine

' increase stack.
STACK 8192

' dimension all arrays.
REDIM UndoByte(1 TO 1024) AS INTEGER
REDIM UndoPosition(1 TO 1024) AS LONG

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

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

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

' read config file.
GOSUB ReadConfigFile

' check command line.
IF Command$="/?" THEN
   GOTO BootUsage
END IF

' read command line.
GOSUB ReadCommandLine

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

' store command line.
Filename=CommandLine$

' filename entry loop starts here.
StartFile:

' note: although the labels below are unreferenced by any GOTOs,
'  the error.routine function will jump to them using RESUME NEXT.

' check filename
IF Filename=Nul THEN
   IF Header=False THEN
      Header=True
      COLOR White
      PRINT "Hex Editor "+Version+" "+Release+"."
   END IF
   COLOR Yellow
   PRINT "Enter filename to edit";
   INPUT Filename
   IF Filename=Nul THEN
      GOTO BootUsage
   END IF
END IF
NextFile:

' init some functions
GOSUB StoreDTA
GOSUB CheckWindows
GOSUB SetDTA
NextFile2:

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

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

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

' conanicalize filename.
GOSUB ShortFilename
NextFile6:

' open/create file.
GOSUB OpenFile
NextFile7:

' check open error.
IF ValidFile=False THEN
   IF Header=False THEN
      Header=True
      COLOR White
      PRINT "Hex Editor "+Version+" "+Release+"."
   END IF
   COLOR Green
   PRINT "File not found."
   Filename=Nul
   GOTO StartFile
END IF
NextFile8:

' check length of file.
GOSUB GetFileLength
NextFile9:

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

' store startup variables.
CurrentUndo=False
MaxUndos=1024
NextFile11:

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

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

' main program loop starts here.
TopProgram:

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

' keyboard input loop.
DO
   ' get a keypress.
   CharInput$=Nul
   DO
      CharInput$=INKEY$
      IF LEN(CharInput$) THEN
         EXIT DO
      END IF
   LOOP

   ' parse the key.
   SELECT CASE LEN(CharInput$)
   CASE 1 ' single ascii key.
      SELECT CASE ASC(CharInput$)
      CASE 9 ' Tab
         IF FileLength>0 THEN
            GOSUB TabWindow
         END IF
      CASE 13 ' Enter
         IF FileLength>0 THEN
            GOSUB ChangeHexValue
         END IF
      CASE 63 ' ?
         GOSUB DisplayInfo
      CASE ELSE ' Unknown key.
         GOSUB HelpLine
      END SELECT
   CASE 2 ' extended ascii key.
      SELECT CASE ASC(RIGHT$(CharInput$,1))
      CASE 0 ' Control-Break
         BEEP
      CASE 30 ' Alt-A
         IF FileLength>0 THEN
            GOSUB AppendByte
         END IF
      CASE 46 ' Alt-C
         GOSUB ANSIChart
      CASE 32 ' Alt-D
         IF FileLength>0 THEN
            GOSUB DumpScreen
         END IF
      CASE 18 ' Alt-E
         IF FileLength>0 THEN
            GOSUB DumpHEXFile
         END IF
      CASE 35 ' Alt-H
         GOSUB HEXChart
      CASE 36 ' Alt-J
         IF FileLength>0 THEN
            GOSUB JumpByte
         END IF
      CASE 37 ' Alt-K
         IF FileLength>0 THEN
            GOSUB SearchASCII
         END IF
      CASE 50 ' Alt-M
         IF FileLength>0 THEN
            GOSUB AppendASCII
         END IF
      CASE 49 ' Alt-N
         GOSUB NewFile
         GOTO StartFile
      CASE 25 ' Alt-P
         IF FileLength>0 THEN
            GOSUB PrintScreen
         END IF
      CASE 16 ' Alt-Q
         GOSUB HelpScreen
      CASE 19 ' Alt-R
         GOSUB RedrawScreen
      CASE 31 ' Alt-S
         IF FileLength>0 THEN
            GOSUB SearchByte
         END IF
      CASE 20 ' Alt-T
         IF FileLength>0 THEN
            GOSUB PrintFile
         END IF
      CASE 22 ' Alt-U
         IF FileLength>0 THEN
            GOSUB UndoByte
         END IF
      CASE 45 ' Alt-X
         EXIT DO ' exit program.
      CASE 44 ' Alt-Z
         IF FileLength>0 THEN
            GOSUB UndoAll
         END IF
      CASE 82 ' Insert
         IF FileLength>0 THEN
            GOSUB ChangeASCIIValues
         END IF
      CASE 72 ' Up
         IF FileLength>0 THEN
            GOSUB CursorUp
         END IF
      CASE 80 ' Down
         IF FileLength>0 THEN
            GOSUB CursorDown
         END IF
      CASE 75 ' Left
         IF FileLength>0 THEN
            GOSUB CursorLeft
         END IF
      CASE 77 ' Right
         IF FileLength>0 THEN
            GOSUB CursorRight
         END IF
      CASE 73 ' PageUp
         IF FileLength>0 THEN
            GOSUB PageUp
         END IF
      CASE 81 ' PageDown
         IF FileLength>0 THEN
            GOSUB PageDown
         END IF
      CASE 79 ' End
         IF FileLength>0 THEN
            GOSUB EndKey
         END IF
      CASE 71 ' Home
         IF FileLength>0 THEN
            GOSUB HomeKey
         END IF
      CASE 115 ' Ctrl-Left
         IF FileLength>0 THEN
            GOSUB CtrlLeftKey
         END IF
      CASE 116 ' Ctrl-Right
         IF FileLength>0 THEN
            GOSUB CtrlRightKey
         END IF
      CASE 117 ' Ctrl-End
         IF FileLength>0 THEN
            GOSUB CtrlEndKey
         END IF
      CASE 119 ' Ctrl-Home
         IF FileLength>0 THEN
            GOSUB CtrlHomeKey
         END IF
      CASE 15 ' Shift-tab
         IF FileLength>0 THEN
            GOSUB TabWindow
         END IF
      CASE ELSE ' Unknown key.
         GOSUB HelpLine
      END SELECT
   CASE ELSE ' Unknown key.
      BEEP
   END SELECT
LOOP

' end of program.
GOSUB CloseFile
GOSUB ResetAttribute
IF ScreenRow THEN
   LOCATE 23,1,1
ELSE
   LOCATE 25,1,1
END IF
COLOR Plain
GOTO StopProgram

' load new file.
NewFile:
 GOSUB CloseFile
 GOSUB ResetAttribute
 GOSUB ResetDTA
 CLS
 ScreenDrawn=False
 LOCATE 24,1,1
 COLOR Plain

 ' reset variables.
 FilePage=1
 FilePosition=1
 PageRow=1
 PageColumn=1
 Buffer=Chr$(0)
 FileByte=Chr$(0)
 AsciiValue=False
 Filename=Nul
 FileLength=False
 FilePosition=False

 ' redimension all arrays.
 CurrentUndo=False
 MaxUndos=1024
 REDIM UndoByte(1 TO 1024) AS INTEGER
 REDIM UndoPosition(1 TO 1024) AS LONG
 RETURN

' display command line instructions.
BootUsage:
IF Header=False THEN
   COLOR White
   PRINT "Hex Editor "+Version+" "+Release+"."
END IF
COLOR Green
PRINT "Usage:"
COLOR Yellow
PRINT "   Hexedit <filename.ext>"
COLOR Green
PRINT "Where:"
COLOR Yellow
PRINT "   <filename.ext> is the file to edit."
PRINT "   may include drive/pathname.
COLOR Green
PRINT "Example:"
COLOR Yellow
PRINT "   Hexedit \dos\page.com"
COLOR Green
PRINT "Note:
COLOR Yellow
PRINT "   Hexedit supports long filenames in quotes."
COLOR Plain
GOTO StopProgram

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

' display ANSI charts.
ANSIChart:
 CLS
 COLOR White
 PRINT "ASCII Chart for characters 1 to 127:"
 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 PRINT
 NEXT
 PRINT
 COLOR White
 PRINT "Press a key:";
 WHILE INKEY$=Nul:WEND
 COLOR Plain
 PRINT
 CLS
 COLOR White
 PRINT "ASCII Chart for characters 128 to 255:"
 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 PRINT
 NEXT
 PRINT
 COLOR White
 PRINT "Press a key:";
 WHILE INKEY$=Nul:WEND
 COLOR Plain
 PRINT
 GOSUB RedrawScreen
 RETURN

' display HEX charts.
HEXChart:
 CLS
 COLOR White
 PRINT "HEX Chart for characters 1 to 127:"
 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 PRINT
 NEXT
 PRINT
 COLOR White
 PRINT "Press a key:";
 WHILE INKEY$=Nul:WEND
 COLOR Plain
 PRINT
 CLS
 COLOR White
 PRINT "HEX Chart for characters 128 to 255:"
 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 PRINT
 NEXT
 PRINT
 COLOR White
 PRINT "Press a key:";
 WHILE INKEY$=Nul:WEND
 COLOR Plain
 PRINT
 GOSUB RedrawScreen
 RETURN

' function for unknown key.
HelpLine:
 COLOR Yellow
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Editing file: ";Filename;
 COLOR White
 PRINT " Type Alt-Q for Help.";
 GOSUB LocateCursor
 RETURN

' display help screen.
HelpScreen:
 CLS
 COLOR White
 PRINT "Help screen: Hex Editor "+Version+" "+Release+" functions:"
 COLOR Red
 PRINT "Function keys:                          Editing keys:"
 COLOR Yellow
 PRINT "Alt-A  Append multiple bytes to file.   Tab    -- Switch windows."
 PRINT "Alt-C  ANSI Chart.                      Enter  -- Change byte value."
 PRINT "Alt-D  HEX Screen Dump.                 Insert -- Change string value."
 PRINT "Alt-E  HEX File Dump.                   Cursor Up/Down/Left/Right."
 PRINT "Alt-H  HEX Chart.                       Page Up/Page Down, Home/End."
 PRINT "Alt-J  Jump to byte position.           Ctrl-Home/Ctrl-End."
 PRINT "Alt-K  Search for ASCII string.         Ctrl-Right/Ctrl-Left."
 PRINT "Alt-M  Append ASCII string to file."
 PRINT "Alt-N  Load new file."
 PRINT "Alt-P  HEX Screen Print.";
 COLOR Red
 PRINT "                Other keys:"
 COLOR Yellow
 PRINT "Alt-Q  Help screen.                     (hex value numeral preceded)."
 PRINT "Alt-R  Redraw screen.                   (right window unprintable chars"
 PRINT "Alt-S  Search for multiple bytes.        are represented with a dot)."
 PRINT "Alt-T  HEX File Print.                  (? key displays file length,"
 PRINT "Alt-U  Undo last byte change.            and undos remaining.)"
 PRINT "Alt-X  Exit program.                    (<escape> key exits search.)"
 PRINT "Alt-Z  Undo All byte changes."
 COLOR White
 PRINT "Press a key:";
 WHILE INKEY$=Nul:WEND
 CLS
 PRINT "Hexedit information:"
 COLOR Yellow
 PRINT "Program: Hex Editor for DOS "+Version+" "+Release+"."
 PRINT "Author: Erik Jon Oredson AS. Csci"
 PRINT "Release: 08/10/2001."
 PRINT "Status: Public Domain."
 PRINT "Email: eoredson@yahoo.com"
 COLOR White
 PRINT "Press a key:";
 WHILE INKEY$=Nul:WEND
 COLOR Plain
 GOSUB RedrawScreen
 RETURN

' function for ? key.
DisplayInfo:
 COLOR Yellow
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Editing file: ";Filename;" ";
 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 FileLength=False THEN
    PRINT "<locked>";
 ELSE
    PRINT "(Length:";STR$(FileLength);") ";
    PRINT "(Undos:";STR$(CurrentUndo);") ";
 END IF
 GOSUB LocateCursor
 RETURN

' display name of file being edited.
DisplayFilename:
 COLOR Yellow
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Editing file: ";Filename;" ";
 GOSUB DisplayPosition
 RETURN

' display info on file being edited.
DisplayPosition:
 COLOR Yellow
 StringLength=LEN("Editing file: "+Filename+" ")+4
 LOCATE 2,StringLength,0
 PRINT SPACE$(74-StringLength);
 LOCATE 2,StringLength,0
 PRINT "(Position:";STR$(FilePosition);") ";
 PRINT "(Ascii:";STR$(AsciiValue);")";
 GOSUB LocateCursor
 RETURN

' redraw editing screen
RedrawScreen:
 ScreenDrawn=True
 GOSUB DisplayScreen
 GOSUB DisplayFilename
 GOSUB DisplayHexPage
 GOSUB DisplayPageByte
 RETURN

' display screen of current page of hex/ascii values of file being edited.
DisplayHexPage:
 IF FileLength=False THEN
    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
 Row=False
 Column=False
 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

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

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

 ' display filename.
 COLOR Yellow
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Screen dumped to: '"+Filename2$+"'. Press <esc>."
 GOSUB LocateCursor
 WHILE INKEY$<>CHR$(27):WEND
 GOSUB DisplayFilename
 RETURN

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

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

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

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

 ' display filename.
 COLOR Yellow
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 IF FileDumped THEN
    PRINT "File dumped to: '"+Filename2$+"'. Press <esc>."
 ELSE
    PRINT "Partial file dumped to: '"+Filename2$+"'. Press <esc>."
 END IF
 GOSUB LocateCursor
 WHILE INKEY$<>CHR$(27):WEND
 GOSUB DisplayFilename
 RETURN

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

' print file of current hex/ascii values to printer.
PrintFile:
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Enter printer port(1-3)";
 INPUT PortNumber
 PortNumber=INT(PortNumber)
 IF PortNumber>=1 AND PortNumber<=3 THEN
    CLOSE #1
    Filename2$="LPT"+MID$(STR$(PortNumber),2)+":"
    ErrorTrap=False
    OPEN Filename2$ FOR OUTPUT AS #1
    IF ErrorTrap THEN
       GOSUB RedrawScreen
       RETURN
    END IF
 ELSE
    COLOR Yellow
    LOCATE 2,4
    PRINT SPACE$(74);
    LOCATE 2,4
    PRINT "Invalid printer port number. Press <esc>."
    GOSUB LocateCursor
    WHILE INKEY$<>CHR$(27):WEND
    GOSUB DisplayFilename
    RETURN
 END IF
 Column=False
 ColumnSpace=False
 HexLine$=Nul
 HexLine2$=Nul

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

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

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

' reset hilight of current byte being edited.
DisplayPageByte:
 COLOR Yellow
 GOSUB DisplayByte

 ' store the current byte value being edited.
 AsciiValue=ASC(FileByte)
 GOSUB DisplayPosition
 RETURN

' display a byte on the screen.
DisplayByte:
 IF FileLength=False THEN
    RETURN
 END IF
 SeekPosition=FilePosition
 GOSUB LseekFile
 GOSUB ReadFile
 FileByte=Buffer
 ByteValue=ASC(FileByte)
 GOSUB CalculateColumn

 ' display hex byte.
 LOCATE PageRow+3,Column+5,0
 PRINT RIGHT$("00"+HEX$(ByteValue),2);

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

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

' add multiple bytes of an ASCII string to file and locate there.
AppendASCII:
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Enter ASCII string";
 INPUT ByteString$
 IF LEN(ByteString$)=False THEN
    LOCATE 2,4
    PRINT SPACE$(74);
    LOCATE 2,4
    PRINT "Invalid append string. Press <esc> to continue:";
    GOSUB LocateCursor
    WHILE INKEY$<>CHR$(27):WEND
    GOSUB DisplayFilename
    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:
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 IF CurrentWindow=False THEN
    PRINT "Enter hex byte(s)";
    INPUT ByteString$
    AllowWildcard=False
    GOSUB CheckHexBytes
 ELSE
    PRINT "Enter ascii byte(s)";
    INPUT ByteString$
    GOSUB CheckAsciiBytes
 END IF
 IF ValidByteString=False THEN
    LOCATE 2,4
    PRINT SPACE$(74);
    LOCATE 2,4
    PRINT "Invalid append string. Press <esc> to continue:";
    GOSUB LocateCursor
    WHILE INKEY$<>CHR$(27):WEND
    GOSUB DisplayFilename
    RETURN
 END IF
 ' append the byte string
StartAppend:
 LastPage=FilePage
 FOR Byte1=1 TO NumBytes
    GOSUB ClearPageByte
    Byte$=MID$(ByteString$,(Byte1-1)*2+1,2)
    Byte2=VAL("&H"+Byte$)
    FileByte=CHR$(Byte2)
    FileLength=FileLength+1
    SeekPosition=FileLength
    GOSUB LseekFile
    GOSUB WriteFile
    FilePosition=FileLength
    GOSUB CalculatePosition
    IF LastPage<>FilePage THEN
       LastPage=FilePage
       GOSUB DisplayHexPage
    END IF
    GOSUB DisplayPageByte
 NEXT
 GOSUB DisplayFilename
 RETURN

' search multiple bytes of an ASCII string in file and locate there.
SearchASCII:
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Enter ASCII string";
 INPUT ByteString$
 IF LEN(ByteString$)=False THEN
    LOCATE 2,4
    PRINT SPACE$(74);
    LOCATE 2,4
    PRINT "Invalid search string. Press <esc> to continue:";
    GOSUB LocateCursor
    WHILE INKEY$<>CHR$(27):WEND
    GOSUB DisplayFilename
    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:
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 IF CurrentWindow=False THEN
    PRINT "Enter hex byte(s)";
    INPUT ByteString$
    AllowWildcard=True
    GOSUB CheckHexBytes
 ELSE
    PRINT "Enter ascii byte(s)";
    INPUT ByteString$
    GOSUB CheckAsciiBytes
 END IF
 IF ValidByteString=False THEN
    LOCATE 2,4
    PRINT SPACE$(74);
    LOCATE 2,4
    PRINT "Invalid search string. Press <esc> to continue:";
    GOSUB LocateCursor
    WHILE INKEY$<>CHR$(27):WEND
    GOSUB DisplayFilename
    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 CalculatePosition
       IF LastPage<>FilePage THEN
          GOSUB DisplayHexPage
       END IF
       EXIT FOR
    END IF
 NEXT
 GOSUB DisplayPageByte
 IF FoundString=False THEN
    LOCATE 2,4
    PRINT SPACE$(74);
    LOCATE 2,4
    PRINT "String not found. Press <esc> to continue:"
    WHILE INKEY$<>CHR$(27):WEND
 END IF
 GOSUB DisplayFilename
 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:
 COLOR Yellow
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 DisplayLength=LEN(ByteString$)
 IF DisplayLength>32 THEN
    Display$="Searching for "+LEFT$(ByteString$,32)+"... (Press <esc> to quit):"
 ELSE
    Display$="Searching for "+ByteString$+": (Press <esc> to quit):"
 END IF
 PRINT Display$;
 RETURN

' display message about dumping.
DumpInfo:
 COLOR Yellow
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Writing file: (Press <esc> to quit):";
 RETURN

' jump to a byte position in file.
JumpByte:
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Enter file position";
 INPUT NewByte
 IF NewByte>=1 AND NewByte<=FileLength THEN
    IF NewByte<>FilePosition THEN
       GOSUB ClearPageByte
       LastPage=FilePage
       FilePosition=NewByte
       GOSUB CalculatePosition
       IF LastPage<>FilePage THEN
          GOSUB DisplayHexPage
       END IF
       GOSUB DisplayPageByte
    END IF
 END IF
 GOSUB DisplayFilename
 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 CalculatePosition
    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 CalculatePosition
       IF LastPage<>FilePage THEN
          LastPage=FilePage
          GOSUB DisplayHexPage
       END IF
       GOSUB DisplayPageByte
    NEXT
 END IF
 RETURN

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

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

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

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

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

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

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

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

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

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

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

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

' switch editing windows.
TabWindow:
 CurrentWindow=NOT CurrentWindow
 GOSUB LocateCursor
 RETURN

' change the byte value at the current position.
'   change by hex value in left window,
'   by ascii value in right window.
ChangeHexValue:
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 FileByte=Nul
 ValidByteString=False
 IF CurrentWindow=False THEN
    PRINT "Enter hex byte value";
    INPUT ByteString$
    IF LEN(ByteString$)=2 THEN
       AllowWildcard=False
       GOSUB CheckHexBytes
       IF ValidByteString THEN
          NewValue=VAL("&H"+ByteString$)
          FileByte=CHR$(NewValue)
       END IF
    END IF
 ELSE
    PRINT "Enter ascii byte value";
    INPUT NewValue
    IF NewValue>=False AND NewValue<=255 THEN
       ValidByteString=-1
       FileByte=CHR$(NewValue)
    END IF
 END IF
 IF ValidByteString=False THEN
    LOCATE 2,4
    PRINT SPACE$(74);
    LOCATE 2,4
    PRINT "Invalid byte. Press <esc> to continue:";
    GOSUB LocateCursor
    WHILE INKEY$<>CHR$(27):WEND
    GOSUB DisplayFilename
    RETURN
 END IF
 GOSUB StoreUndo
 SeekPosition=FilePosition
 GOSUB LseekFile
 GOSUB WriteFile
 GOSUB DisplayPageByte
 GOSUB DisplayFilename
 RETURN

' change the ascii values starting at the current position.
ChangeASCIIValues:
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Enter ascii value(s)? ";
 LINE INPUT ByteString$
 IF LEN(ByteString$)=False THEN
    LOCATE 2,4
    PRINT SPACE$(74);
    LOCATE 2,4
    PRINT "Invalid ascii string. Press <esc> to continue:";
    GOSUB LocateCursor
    WHILE INKEY$<>CHR$(27):WEND
    GOSUB DisplayFilename
    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 CalculatePosition
    IF LastPage<>FilePage THEN
       LastPage=FilePage
       GOSUB DisplayHexPage
    END IF
 NEXT
 GOSUB DisplayPageByte
 GOSUB DisplayFilename
 RETURN

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

' calculate current screen page number, row, and column given file position.
CalculatePosition:
 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

' critical error trap.
Error.Routine:
 IF ScreenDrawn THEN
    CLS
 END IF
 ScreenDrawn=False
 COLOR White
 PRINT "Hex Editor "+Version+" "+Release+" critical error trap:"
 COLOR Yellow
 ErrorTrap=Err
 SELECT CASE Err
 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 "Media error."
 CASE 58
    PRINT "File already exists."
 CASE 59
    PRINT "Bad record length."
 CASE 61
    PRINT "Disk full."
 CASE 62
    PRINT "Input past eof."
 CASE 63
    PRINT "Bad record length."
 CASE 64
    PRINT "Bad filename."
 CASE 67
    PRINT "Not enough file handles."
 CASE 68
    PRINT "Device unavailable."
 CASE 70
    PRINT "Permission denied."
 CASE 71
    PRINT "Disk not ready."
 CASE 72
    PRINT "Disk-media error."
 CASE 75
    PRINT "Path/File access error."
 CASE 76
    PRINT "Pathname not found."
 CASE 81
    PRINT "Invalid name."
 CASE ELSE
    PRINT "Untrapped error" + STR$(Err) + "."
 END SELECT

 ' display error prompt.
 COLOR Green
 PRINT "Press R(etry), C(ontinue), Q(uit), A(bort):";

 ' get keypress.
 DO
    ErrorRespond$=Nul
    DO
       ErrorRespond$=INKEY$
       IF LEN(ErrorRespond$) THEN
          EXIT DO
       END IF
    LOOP

    ' parse key.
    SELECT CASE LCASE$(ErrorRespond$)
    CASE "r"
       PRINT "r"
       RESUME
    CASE "c"
       PRINT "c"
       RESUME NEXT
    CASE "q"
       PRINT "q"
       RESUME TopProgram
    CASE "a"
       PRINT "a"
       EXIT DO
    END SELECT
 LOOP

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

' routine to read in custom config file variables.
' searchs current path, environment path, then path statement.
ReadConfigFile:
 ' search current directory.
 Config.Filename$=ConfigFile
 IF DIR$(Config.Filename$)<>Nul THEN
    GOTO ReadFile2
 END IF

 ' search environment data file.
 File$=ENVIRON$("HEXEDIT")
 IF LEN(File$) THEN
    File$=RTRIM$(File$)
    File$=LTRIM$(File$)
    IF RIGHT$(File$,1)<>"\" THEN
       File$=File$+"\"
    END IF
    Config.Filename$=File$+ConfigFile
    IF DIR$(Config.Filename$)<>Nul THEN
       GOTO ReadFile2
    END IF
 END IF

 ' search path statement.
 Path$=ENVIRON$("PATH")
 DO
    ' parse path.
    Parse=INSTR(Path$,";")
    IF Parse THEN
       File$=LEFT$(Path$,Parse-1)
       Path$=MID$(Path$,Parse+1)
    ELSE
       File$=Path$
       Path$=Nul
    END IF

    ' store filename.
    File$=RTRIM$(File$)
    File$=LTRIM$(File$)
    IF LEN(File$) THEN
       IF RIGHT$(File$,1)<>"\" THEN
          File$=File$+"\"
       END IF
       Config.Filename$=File$+ConfigFile
       IF DIR$(Config.Filename$)<>Nul THEN
          GOTO ReadFile2
       END IF
    ELSE
       EXIT DO
    END IF
 LOOP
 RETURN

' read in the data file
ReadFile2:
 ' open filename.
 ErrorTrap=False
 CLOSE #1
 OPEN Config.Filename$ FOR INPUT AS #1
 IF ErrorTrap THEN
    RETURN
 END IF

 ' read input lines.
 DO WHILE NOT EOF(1)
    LINE INPUT #1,FileLine$
    FileLine2$=FileLine$

    ' remove spaces.
    FileLine$=TrimSpaces$(FileLine$)

    ' check for comment.
    IF LEFT$(FileLine$,1)=";" THEN
       FileLine$=Nul
    END IF

    ' check assignment value.
    Parse=INSTR(FileLine$,"=")

    ' parse off left/right sides.
    IF Parse THEN

       ' get left/right side values.
       Char$=LEFT$(FileLine$,Parse-1)
       Setting$=MID$(FileLine$,Parse+1)

       ' get right side value for filenames
       ' enclosed in quotes containing spaces.
       Parse=INSTR(FileLine2$,"=")
       Setting2$=MID$(FileLine2$,Parse+1)
       Setting2$=RTRIM$(Setting2$)
       Setting2$=LTRIM$(Setting2$)

       ' check length of values.
       IF LEN(Char$)>0 AND LEN(Setting$)>0 THEN

          ' check config filenames.
          IF UCASE$(Char$)="DUMPFILE" THEN
             DumpFile=Setting$
          ELSE
             IF UCASE$(Char$)="EDITFILE" THEN
                Filename=Setting2$
             ELSE

                ' check ascii value.
                NewValue=INT(VAL(Setting$))
                IF NewValue>=False AND NewValue<=255 THEN
                   SELECT CASE UCASE$(Char$)
                   CASE "HLINE"
                      Hline=NewValue
                   CASE "VLINE"
                      Vline=NewValue
                   CASE "ULCORNER"
                      ULcorner=NewValue
                   CASE "URCORNER"
                      URcorner=NewValue
                   CASE "LLCORNER"
                      LLcorner=NewValue
                   CASE "LRCORNER"
                      LRcorner=NewValue
                   END SELECT
                END IF
             END IF
          END IF
       END IF
    END IF
 LOOP
 CLOSE #1
 RETURN

' remove spaces from variable.
FUNCTION TrimSpaces$(Var$)
 Var1$=Var$
 DO
    Parse=INSTR(Var1$," ")
    IF Parse THEN
       Var1$=LEFT$(Var1$,Parse-1)+MID$(Var1$,Parse+1)
    ELSE
       EXIT DO
    END IF
 LOOP
 TrimSpaces$=Var1$
END FUNCTION

REM All following procedures for long filenames.

' 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

' assign program dta.
SetDTA:
 InregsX.AX=&H1A00
 InregsX.DS=VARSEG(DTAfile)
 InregsX.DX=VARPTR(DTAfile)
 CALL InterruptX(&H21,InregsX,OutregsX)
 ' reset dta variable
 DTAactive=True
 RETURN

' restore basic dta.
ResetDTA:
 ' check dta variable
 IF DTAactive THEN
    InregsX.AX=&H1A00
    InregsX.DS=BASIC.DTA.SEG
    InregsX.DX=BASIC.DTA.OFF
    CALL InterruptX(&H21,InregsX,OutregsX)
 END IF
 RETURN

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

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

' close file.
CloseFile:
 InregsX.AX=&H3E00
 InregsX.BX=Handle
 CALL InterruptX(&H21,InregsX,OutregsX)
 IF (OutregsX.Flags AND &H1)=&H1 THEN
    DisplayError$="closing file"
    GOSUB LongFileError
 END IF
 RETURN

' open file based on windows or dos loaded.
'   create file if nonexistent,
'   append nul byte to new file,
'   reopen file.
' return ValidFile true if open successful.
OpenFile:
 ValidFile=True
 IF WindowsDetected THEN
    GOSUB OpenWinFile
    IF (OutregsX.Flags AND &H1)=&H1 THEN
       GOSUB CreateWinFile
       IF (OutregsX.Flags AND &H1)=&H1 THEN
          ValidFile=False
       ELSE
          GOSUB AppendFile
          IF (OutregsX.Flags AND &H1)=&H1 THEN
             ValidFile=False
          ELSE
             GOSUB CloseFile
             GOSUB OpenWinFile
          END IF
       END IF
    END IF
 ELSE
    GOSUB OpenDOSFile
    IF (OutregsX.Flags AND &H1)=&H1 THEN
       GOSUB CreateDOSFile
       IF (OutregsX.Flags AND &H1)=&H1 THEN
          ValidFile=False
       ELSE
          GOSUB AppendFile
          IF (OutregsX.Flags AND &H1)=&H1 THEN
             ValidFile=False
          ELSE
             GOSUB CloseFile
             GOSUB OpenDOSFile
          END IF
       END IF
    END IF
 END IF
 RETURN

' open file in windows.
'   store/reset read-only bit.
OpenWinFile:
 ' reset file attributes.
 FileAttribute=False
 ' get file attributes.
 InregsX.AX=&H7143
 InregsX.BX=&H0
 InregsX.DS=VARSEG(ASCIIZ)
 InregsX.DX=VARPTR(ASCIIZ)
 CALL InterruptX(&H21,InregsX,OutregsX)
 ' store file attributes.
 IF (OutregsX.Flags AND &H1)=&H0 THEN
    FileAttribute=OutregsX.CX
 END IF
 ' clear read-only bit for opening file for write.
 IF (FileAttribute AND ReadOnlyBit)=ReadOnlyBit THEN
    Attribute=(FileAttribute AND NOT ReadOnlyBit)
    ' reset file attribute
    InregsX.AX=&H7143
    InregsX.BX=&H1
    InregsX.CX=Attribute
    InregsX.DS=VARSEG(ASCIIZ)
    InregsX.DX=VARPTR(ASCIIZ)
    CALL InterruptX(&H21,InregsX,OutregsX)
 END IF
 ' open file for read/write.
 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

' open file in dos.
'   store/reset read-only bit.
OpenDOSFile:
 ' reset file attributes.
 FileAttribute=False
 ' get file attributes.
 InregsX.AX=&H4300
 InregsX.DS=VARSEG(ASCIIZ)
 InregsX.DX=VARPTR(ASCIIZ)
 CALL InterruptX(&H21,InregsX,OutregsX)
 ' store file attributes.
 IF (OutregsX.Flags AND &H1)=&H0 THEN
    FileAttribute=OutregsX.CX
 END IF
 ' clear read-only bit for opening file for write.
 IF (FileAttribute AND ReadOnlyBit)=ReadOnlyBit THEN
    Attribute=(FileAttribute AND NOT ReadOnlyBit)
    ' reset source attribute
    InregsX.AX=&H4301
    InregsX.CX=Attribute
    InregsX.DS=VARSEG(ASCIIZ)
    InregsX.DX=VARPTR(ASCIIZ)
    CALL InterruptX(&H21,InregsX,OutregsX)
 END IF
 ' open file for read/write.
 InregsX.AX=&H6C00
 InregsX.BX=&H2
 InregsX.CX=FileAttribute
 InregsX.DX=&H1
 InregsX.DS=VARSEG(ASCIIZ)
 InregsX.SI=VARPTR(ASCIIZ)
 CALL InterruptX(&H21,InregsX,OutregsX)
 Handle=OutregsX.AX
 RETURN

' create new file in windows.
CreateWinFile:
 InregsX.AX=&H716C
 InregsX.BX=&H1
 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 file in dos.
CreateDOSFile:
 InregsX.AX=&H6C00
 InregsX.BX=&H1
 InregsX.CX=&H0
 InregsX.DX=&H10
 InregsX.DS=VARSEG(ASCIIZ)
 InregsX.SI=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)
 IF (OutregsX.Flags AND &H1)=&H1 THEN
    DisplayError$="reading file"
    GOSUB LongFileError
 END IF
 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)
 IF (OutregsX.Flags AND &H1)=&H1 THEN
    DisplayError$="writing file"
    GOSUB LongFileError
 END IF
 RETURN

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

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

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

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

' conanicalizes filename.
FUNCTION Conanicalize$(Var$)
 Var1$=Var$
 ' strip drive letter.
 Var1$=UCASE$(Var1$)
 IF MID$(Var1$,2,1)=":" THEN
    Var1$=MID$(Var1$,3)
 END IF
 ' strip path.
 DO
    Path=INSTR(Var1$,"\")
    IF Path THEN
       Var1$=MID$(Var1$,Path+1)
    ELSE
       EXIT DO
    END IF
 LOOP
 Conanicalize$=Var1$
END FUNCTION

' restore read-only attribute.
ResetAttribute:
 IF (FileAttribute AND ReadOnlyBit)=ReadOnlyBit THEN
    IF WindowsDetected THEN
       InregsX.AX=&H7143
       InregsX.BX=&H1
       InregsX.CX=FileAttribute
       InregsX.DS=VARSEG(ASCIIZ)
       InregsX.DX=VARPTR(ASCIIZ)
       CALL InterruptX(&H21,InregsX,OutregsX)
    ELSE
       InregsX.AX=&H4301
       InregsX.CX=FileAttribute
       InregsX.DS=VARSEG(ASCIIZ)
       InregsX.DX=VARPTR(ASCIIZ)
       CALL InterruptX(&H21,InregsX,OutregsX)
    END IF
 END IF
 RETURN

' display message about long filename access error.
LongFileError:
 COLOR Yellow
 LOCATE 2,4
 PRINT SPACE$(74);
 LOCATE 2,4
 PRINT "Error ";DisplayError$;".";
 RETURN

 ' This program is worth a cookie and a cola!
