REM Program: Hex Editor v7.7a, Module 6 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'

REM Replacement stub subroutines:
REM  LseekFile()
REM  ReadFile()
REM  Writefile()
REM  LockedFile()
REM  LocateCursor()
REM  LocateCursor2()
REM  LocateHilightCursor()
REM  BytePrint()
REM  DisplayPosition()
REM  ClearStatus()
REM  DisplayFilename()
REM  DisplayFileTitle()
REM  DisplayPageByte()
REM  ResetHilightBytes()
REM  ResetBytes()
REM  RestoreHilightBytes()
REM  ResetByte()
REM  ClearPageByte()
REM  ClearHilightByte()
REM  DisplayHilightByte()
REM  DisplayStatus1()
REM  DisplayStatus2()
REM  DisplayStatusLine()
REM  PromptKey()
REM  DisplayHexPage()
REM  RedrawRightWindow()
REM  RedrawWindow1()
REM  RedrawWindow2()
REM  PressKey()
REM  CheckASCIIValue(S$)
REM  CheckString(S$)
REM  CheckHexValue(S$)
REM  FormatPosition2(V%)
REM  AsciiToHex1(S$)
REM  AsciiToHex2(S$)
REM  CheckAsciiBytes(S$)
REM  CheckHexBytes(S$)
REM  GetShortFilename(S$)

REM position pointer in file.
SUB LseekFile
 ON LOCAL ERROR RESUME NEXT
 ' calculate high/low seek position.
 SeekPosition2 = SeekPosition - 1 ' offset is from zero.

 ' seek values are signed bit sensitive.
 High = INT(SeekPosition2 / 65536)
 Low = SeekPosition2 - High * &H10000

 ' seek position in file.
 InregsX.AX = &H4200
 InregsX.BX = Handle
 InregsX.CX = INT(VAL("&H" + HEX$(High))) ' hex conversion is signed.
 InregsX.DX = INT(VAL("&H" + HEX$(Low)))
 CALL InterruptX(&H21, InregsX, OutregsX)
END SUB

REM read 1 byte from file.
SUB ReadFile
 ON LOCAL ERROR RESUME NEXT
 InregsX.AX = &H3F00
 InregsX.BX = Handle
 InregsX.CX = 1
 InregsX.DS = VARSEG(Buffer)
 InregsX.DX = VARPTR(Buffer)
 CALL InterruptX(&H21, InregsX, OutregsX)
END SUB

REM write 1 byte to file.
SUB WriteFile
 ON LOCAL ERROR RESUME NEXT
 If FileLocked Then
    Write.Error = True
    Exit Sub
 Endif
 Write.Error = False
 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
    Write.Error = True
 Else
    If OutregsX.AX = False Then
       Write.Error = True
    Endif
 Endif
END SUB

REM display file status message.
SUB LockedFile
 ON LOCAL ERROR RESUME NEXT
 COLORf White
 If FileLocked Then
    If FileLength = False Then
       PRINTf "<locked file>"
    Else
       PRINTf "<read-only>"
    Endif
 Else
    PRINTf "<zero-byte>"
 Endif
 LOCATEf 1, 1, 0
END SUB

REM locate cursor on window.
SUB LocateCursor
 ON LOCAL ERROR RESUME NEXT
 IF FileLength = False THEN
    LOCATEf 1, 1, 0
    EXIT SUB
 END IF
 IF CurrentWindow = False THEN
    LOCATEf PageRow + 3, Column + 5, 1
 ELSE
    LOCATEf PageRow + 3, PageColumn + 54, 1
 END IF
END SUB

REM select cursor on window.
SUB LocateCursor2
 ON LOCAL ERROR RESUME NEXT
 IF CopyPositionStart = False THEN
    Call locatecursor
 ELSE
    Call locatehilightcursor
 END IF
END SUB

REM locate cursor on window for hilighted area.
SUB LocateHilightCursor
 ON LOCAL ERROR RESUME NEXT
 IF FileLength = False THEN
    LOCATEf 1, 1, 0
    EXIT SUB
 END IF
 IF CurrentWindow = False THEN
    LOCATEf PageRow + 3, Column + 7, 1
 ELSE
    LOCATEf PageRow + 3, PageColumn + 55, 1
 END IF
END SUB

REM print byte at fileposition in edit area.
SUB BytePrint
 ON LOCAL ERROR RESUME NEXT
 SeekPosition = FilePosition
 Call lseekfile
 Call readfile
 FileByte = Buffer
 ByteValue = ASC(FileByte)
 Column = CalculateColumn
 ' display hex byte.
 CALL HMouse
 LOCATEf PageRow + 3, Column + 5, 0
 PRINTf RIGHT$("00" + HEX$(ByteValue), 2)
 ' check right window toggled.
 IF CurrentWindow2 = False THEN
    ' display ascii byte.
    LOCATEf PageRow + 3, PageColumn + 54, 0
    ' skip unprintable characters.
    SELECT CASE ByteValue
    CASE 0, 7, 9 TO 13, 28 TO 32
       PRINTf "."
    CASE ELSE
       PRINTf FileByte
    END SELECT
 END IF
 CALL SMouse
END SUB

REM display info on file being edited.
SUB DisplayPosition
 ON LOCAL ERROR RESUME NEXT
 COLORf2 Yellow, 0
 StringLength = LEN("Editing file: " + RTRIM$(Filename) + " ") + 4
 LOCATEf 2, StringLength, 0
 PRINTf SPACE$(77 - StringLength)
 LOCATEf 2, StringLength, 0
 IF FileLength = False THEN
    Call lockedfile
    EXIT SUB
 END IF
 IF CurrentWindow2 = False THEN
    PRINTf "(Position:" + STR$(FilePosition - 1) + ") "
 ELSE
    PRINTf "(Position: " + RIGHT$("00000000" + HEX$(FilePosition - 1), 8) + "H) "
 END IF
 PRINTf "(Ascii:" + STR$(AsciiValue) + ") "
 PRINTf "(Hex: " + RIGHT$("00" + HEX$(AsciiValue), 2) + "H)"
 Call locatecursor2
END SUB

REM clear top status area
SUB ClearStatus
 ON LOCAL ERROR RESUME NEXT
 COLORf2 Yellow, 0
 LOCATEf 2, 4, 0
 PRINTf SPACE$(74)
 LOCATEf 2, 4, 0
END SUB

REM display name of file being edited.
SUB DisplayFilename
 ON LOCAL ERROR RESUME NEXT
 Call clearstatus
 PRINTf "Editing file: " + RTRIM$(Filename) + " "
 IF FileLength = False THEN
    Call lockedfile
    EXIT SUB
 END IF
 Call displayposition
END SUB

REM display 64-byte name of file being editied.
SUB DisplayFileTitle
 ON LOCAL ERROR RESUME NEXT
 ' redisplay line 3.
 LOCATEf 3, 4, 0
 COLORf Green
 PRINTf CHR$(ULcorner) + STRING$(46, Hline) + CHR$(URcorner) + " "
 PRINTf CHR$(ULcorner) + STRING$(22, Hline) + CHR$(URcorner)

 ' display filename.
 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)
 Z$ = MID$(STR$(CurrentFile), 2) + ": " + Z$
 Var = INT((80 - (LEN(RTRIM$(Z$))) + 2) / 2) - 1
 LOCATEf 3, Var, 0
 COLORf2 0, 7
 PRINTf " " + Z$ + " "
 COLORf2 7, 0
 Call locatecursor2
END SUB

REM display current byte being edited.
SUB DisplayPageByte
 ON LOCAL ERROR RESUME NEXT
 ' check filename display.
 IF FileDisplay2 THEN
    FileDisplay2 = False
    Call clearstatus
    PRINTf "Editing file: " + RTRIM$(Filename) + " "
 END IF

 ' check filelength.
 IF FileLength = False THEN
    LOCATEf 1, 1, 0
    EXIT SUB
 END IF

 ' check hilighted bytes.
 IF CopyPositionStart > 0# THEN
    CALL ResetHilightBytes
 END IF

 ' display byte.
 COLORf Yellow
 Call byteprint

 ' store the current byte value being edited.
 AsciiValue = ASC(FileByte)
 Call displayposition
END SUB

REM reset hilight bytes.
SUB ResetHilightBytes
 ON LOCAL ERROR RESUME NEXT
 Call ResetBytes

 ' adjust file position.
 IF PageColumn + 1 <= 20 THEN
    IF FilePosition + 1 <= FileLength THEN
       CALL ClearPageByte
       PageColumn = PageColumn + 1
       FilePosition = FilePosition + 1
       Call displaypagebyte
    END IF
 END IF
 COLORf Yellow
 Call byteprint
END SUB

REM reset hilighted byte.
SUB ResetBytes
 ON LOCAL ERROR RESUME NEXT
 Reset1 = True
 Call ResetByte
 CopyPositionStart = False
 CopyPositionPivot = False
 CopyPositionEnd = False
 CopyStart = False
END SUB

REM restore hilighted bytes.
SUB RestoreHilightBytes
 ON LOCAL ERROR RESUME NEXT
 CopyPositionStart=File(CurrentFile).CopyPositionStart
 CopyPositionPivot=File(CurrentFile).CopyPositionPivot
 CopyPositionEnd=File(CurrentFile).CopyPositionEnd
 FilePosition=File(CurrentFile).FilePosition
 Reset1 = False
 Call ResetByte
END SUB

REM draw byte area.
SUB ResetByte
 ON LOCAL ERROR RESUME NEXT
 Temp# = FilePosition
 Temp2# = FilePage
 Var1# = CopyPositionStart
 Var2# = CopyPositionEnd

 ' reset hilighted positions.
 IF Var1# < (Temp2# - 1) * 320 + 1 THEN
    Var1# = (Temp2# - 1) * 320 + 1
 END IF
 IF Var2# > (Temp2# - 1) * 320 + 320 THEN
    Var2# = (Temp2# - 1) * 320 + 320
 END IF
 IF Var2# > FileLength THEN
    Var2# = FileLength
 END IF

 ' draw the bytes.
 FOR FilePosition = Var1# TO Var2#
    CALL CalculatePosition1
    IF Reset1 THEN
       COLORf White
    ELSE
       COLORf2 0, 7
    END IF
    Call byteprint
 NEXT
 COLORf2 7, 0
 FilePosition = Temp#
 CALL CalculatePosition1
END SUB

REM clear current byte on screen.
SUB ClearPageByte
 ON LOCAL ERROR RESUME NEXT
 IF FileLength = False THEN
    LOCATEf 1, 1, 0
    EXIT SUB
 END IF
 IF CopyPositionStart > 0# THEN
    Call resethilightbytes
 END IF
 COLORf White
 Call byteprint
END SUB

REM clear current hilighted byte on screen.
SUB ClearHilightByte
 ON LOCAL ERROR RESUME NEXT
 IF FileLength = False THEN
    LOCATEf 1, 1, 0
    EXIT SUB
 END IF
 COLORf White
 CALL BytePrint
END SUB

REM display current hilight byte being edited.
SUB DisplayHilightByte
 ON LOCAL ERROR RESUME NEXT
 IF FileDisplay2 THEN
    FileDisplay2 = False
    Call ClearStatus
    PRINTf "Editing file: " + RTRIM$(Filename) + " "
 END IF

 ' display byte.
 IF FileLength = False THEN
    LOCATEf 1, 1, 0
    EXIT SUB
 END IF
 COLORf2 0, 7
 CALL BytePrint
 COLORf2 7, 0

 ' store the current byte value being edited.
 AsciiValue = ASC(FileByte)
 CALL DisplayPosition
END SUB

REM display status line message and prompt type 1.
SUB DisplayStatus1
 ON LOCAL ERROR RESUME NEXT
 StatusMessage = StatusMessage + " Press <esc>:"
 CALL DisplayStatusLine
END SUB

REM display status line message and prompt type 2.
SUB DisplayStatus2
 ON LOCAL ERROR RESUME NEXT
 StatusMessage = StatusMessage + " Press <esc> to continue:"
 CALL DisplayStatusLine
END SUB

REM display the status line message and prompt for key or mouse click.
SUB DisplayStatusLine
 ON LOCAL ERROR RESUME NEXT
 Call clearstatus
 PRINTf StatusMessage
 Call locatecursor2
 Call PromptEscKey
 Call displayfilename
END SUB

REM prompts for escape key or left mouse click.
SUB PromptEscKey
 ON LOCAL ERROR RESUME NEXT
 DO
    ' check escape key pressed.
    IF INKEY$ = CHR$(27) THEN
       EXIT DO
    END IF

    ' call mouse subroutine.
    CALL MouseDriver

    ' check left mouse button release.
    IF Mouse.ButtonX THEN
       EXIT DO
    END IF

    ' release windows time slice.
    R = ReleaseTime
 LOOP
END SUB

REM prompts for any key or left mouse click.
SUB PromptKey
 ON LOCAL ERROR RESUME NEXT
 DO
    ' check any key pressed.
    IF INKEY$ <> Nul THEN
       EXIT DO
    END IF

    ' call mouse subroutine.
    CALL MouseDriver

    ' check left mouse button release.
    IF Mouse.ButtonX THEN
       EXIT DO
    END IF

    ' release windows time slice.
    R = ReleaseTime
 LOOP
END SUB

REM display screen of current page of hex/ascii values of file being edited.
SUB DisplayHexPage
 ON LOCAL ERROR RESUME NEXT
 IF FileLength = False THEN
    Row = False
    Column = False
    CALL HMouse
    FOR NextByte = 1 TO 320
       LOCATEf Row + 4, Column + 6, 0
       PRINTf "  "
       ColumnSpace = ColumnSpace + 1
       IF ColumnSpace = 4 THEN
          PRINTf " "
          Column = Column + 1
          ColumnSpace = False
       END IF
       Column = Column + 2
       IF Column > 44 THEN
          Row = Row + 1
          Column = False
       END IF
    NEXT
    CALL SMouse
    CALL RedrawRightWindow
    LOCATEf 1, 1, 0
    EXIT SUB
 END IF
 COLORf White
 Row = False
 Column = False
 ColumnSpace = False
 CALL HMouse
 FOR NextByte = (FilePage - 1) * 320 + 1 TO (FilePage - 1) * 320 + 320
    LOCATEf Row + 4, Column + 6, 0
    IF NextByte <= FileLength THEN
       SeekPosition = NextByte
       Call lseekfile
       Call readfile
       FileByte = Buffer
       PRINTf RIGHT$("00" + HEX$(ASC(FileByte)), 2)
    ELSE
       PRINTf "  "
    END IF
    ColumnSpace = ColumnSpace + 1
    IF ColumnSpace = 4 THEN
       PRINTf " "
       Column = Column + 1
       ColumnSpace = False
    END IF
    Column = Column + 2
    IF Column > 44 THEN
       Row = Row + 1
       Column = False
    END IF
 NEXT
 CALL SMouse
 CALL RedrawRightWindow
END SUB

REM redraw right window.
SUB RedrawRightWindow:
 ON LOCAL ERROR RESUME NEXT
 IF CurrentWindow2 = False THEN
    CALL RedrawWindow1
 ELSE
    CALL RedrawWindow2
 END IF
END SUB

REM redraw right window of ascii values.
SUB RedrawWindow1
 ON LOCAL ERROR RESUME NEXT
 IF FileLength = False THEN
    CALL HMouse
    COLORf White
    Row = False
    Column = False
    FOR NextByte = 1 TO 320
       LOCATEf Row + 4, Column + 55, 0
       PRINTf " "
       Column = Column + 1
       IF Column > 19 THEN
          Row = Row + 1
          Column = False
       END IF
    NEXT
    LOCATEf 1, 1, 0
    CALL SMouse
    EXIT SUB
 END IF
 Row = False
 Column = False
 COLORf White
 CALL HMouse
 FOR NextLine = 0 TO 15
    LOCATEf NextLine + 4, 54, 0
    PRINTf " "
 NEXT
 FOR NextByte = (FilePage - 1) * 320 + 1 TO (FilePage - 1) * 320 + 320
    LOCATEf Row + 4, Column + 55, 0
    IF NextByte <= FileLength THEN
       SeekPosition = NextByte
       Call lseekfile
       Call readfile
       FileByte = Buffer
       ByteValue = ASC(FileByte)
       ' skip unprintable characters
       SELECT CASE ByteValue
       CASE 0, 7, 9 TO 13, 28 TO 32
          PRINTf "."
       CASE ELSE
          PRINTf FileByte
       END SELECT
    ELSE
       PRINTf " "
    END IF
    Column = Column + 1
    IF Column > 19 THEN
       Row = Row + 1
       Column = False
    END IF
 NEXT
 CALL SMouse
END SUB

REM redraw right window of hex ranges.
SUB RedrawWindow2:
 ON LOCAL ERROR RESUME NEXT
 CALL HMouse
 Row = False
 FOR NextLine = (FilePage - 1) * 320 + 1 TO (FilePage - 1) * 320 + 320 STEP 20
    LOCATEf Row + 4, 54, 0
    Row = Row + 1
    IF NextLine <= FileLength THEN
       IF NextLine + 19 <= FileLength THEN
          COLORf White
          PRINTf "x" + RIGHT$("00000000" + HEX$(NextLine - 1), 8)
          COLORf Yellow
          PRINTf " - "
          COLORf White
          PRINTf "x" + RIGHT$("00000000" + HEX$(NextLine + 19 - 1), 8)
       ELSE
          COLORf White
          PRINTf "x" + RIGHT$("00000000" + HEX$(NextLine - 1), 8)
          COLORf Yellow
          PRINTf " - "
          COLORf White
          PRINTf "x" + RIGHT$("00000000" + HEX$(FileLength - 1), 8)
       END IF
    ELSE
       PRINTf SPACE$(22)
    END IF
 NEXT
 CALL SMouse
END SUB

REM key prompt.
SUB PressKey
 ON LOCAL ERROR RESUME NEXT
 COLORf White
 PRINTf "Press a key:"
 WHILE INKEY$ = Nul
    R = ReleaseTime
 WEND
END SUB

REM check valid ascii value
SUB CheckASCIIValue(ByteString$)
 ON LOCAL ERROR RESUME NEXT
 ValidASCIIValue = False
 ASCIIValue3 = VAL(ByteString$)
 IF ASCIIValue3 <= 0# OR ASCIIValue3 > 2147483647# THEN
    StatusMessage = "Invalid ascii value."
    Call displaystatus2
    EXIT SUB
 END IF
 ValidASCIIValue = True
END SUB

REM check hex byte string.
SUB CheckString(ByteString$)
 ON LOCAL ERROR RESUME NEXT
 ValidString=True
 FOR HexPosition=2 TO LEN(ByteString$)
    SELECT CASE UCASE$(MID$(ByteString$,HexPosition,1))
    CASE "0" To "9", "A" TO "F"
       ' nul activity
    CASE ELSE
       ValidString=False
       EXIT SUB
    END SELECT
 NEXT
END SUB

REM check hex byte value.
REM input:
REM   ByteString$ is packed hex string.
REM output:
REM   returns ValidHexValue True for valid hex value.
REM   returns HexValue contains value of hex string.
REM parameters:
REM   Hex string must be 8 characters or less.
REM   Hex value from 8000 to ffff return signed bit
REM     and must be incremented by 65536.
REM   Since 7fff ffff is max filesize,
REM     hex values from 8000 0000 to ffff ffff
REM     which are length 8 return negative value.
REM   Since hex values start from an offset of 0
REM     then the maximum value allowed is 7fff fffe.
SUB CheckHexValue(ByteString$)
 ON LOCAL ERROR RESUME NEXT
 ValidHexValue = False
 StringLength2 = LEN(MID$(ByteString$,2))
 IF StringLength2 > 8 THEN
    StatusMessage = "Invalid hex value."
    Call displaystatus2
    EXIT SUB
 END IF
 CALL CheckString(ByteString$)
 IF ValidString=False THEN
    StatusMessage = "Invalid hex string."
    Call displaystatus2
    EXIT SUB
 END IF
 ByteString$ = MID$(ByteString$, 2)
 DO
    IF LEFT$(ByteString$, 1) = "0" THEN
       ByteString$ = MID$(ByteString$, 2)
    ELSE
       EXIT DO
    END IF
 LOOP
 StringLength2 = LEN(ByteString$)
 ByteString$ = "&H" + ByteString$
 HexValue = VAL(ByteString$)
 ' check signed bit error.
 IF StringLength2 = 4 THEN
    IF HexValue < 0 THEN
       HexValue = HexValue + 65536#
    END IF
 END IF
 IF StringLength2 = 8 THEN
    IF HexValue < 0 THEN
       StatusMessage = "Hex underflow."
       Call displaystatus2
       EXIT SUB
    END IF
 END IF
 IF HexValue >= 2147483647# THEN
    StatusMessage = "Hex overflow."
    Call displaystatus2
    EXIT SUB
 END IF
 ValidHexValue = True
 ' adjust hex offset.
 HexValue = HexValue + 1#
END SUB

REM check ascii byte string.
REM   input: ByteString$ is space-separated 3-byte ascii pair string.
REM   returns: ValidByteString equals true if string is valid,
REM     NumBytes is number of ascii byte pairs,
REM     ByteString$ is concatenated hex byte string.
SUB CheckAsciiBytes(ByteString$)
 ON LOCAL ERROR RESUME NEXT
 AsciiByte$ = Nul
 ByteString$ = LTRIM$(ByteString$)
 ByteString$ = RTRIM$(ByteString$)
 IF LEN(ByteString$) = False THEN
    ValidByteString = False
    EXIT SUB
 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
          EXIT SUB
       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
                EXIT SUB
             END IF
          END IF
       CASE ELSE
          ValidByteString = False
          EXIT SUB
       END SELECT
    END IF
 NEXT
 Var1$ = ByteString$
 DO
    Parse = INSTR(Var1$, " ")
    IF Parse THEN
       Var1$ = LEFT$(Var1$, Parse - 1) + MID$(Var1$, Parse + 1)
    ELSE
       EXIT DO
    END IF
 LOOP
 ByteString$ = Var1$
 CALL AsciiToHex1(ByteString$)
END SUB

REM check hex byte string.
REM   input: AllowWildcard true to allow ? character.
REM     ByteString$ is space-separated hex byte pair string.
REM   returns: ValidByteString equals true if string is valid,
REM     NumBytes is number of hex byte pairs,
REM     ByteString$ is concatenated hex byte string.
SUB CheckHexBytes(ByteString$)
 ON LOCAL ERROR RESUME NEXT
 ByteString$ = LTRIM$(ByteString$)
 ByteString$ = RTRIM$(ByteString$)
 IF LEN(ByteString$) = False THEN
    ValidByteString = False
    EXIT SUB
 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
          EXIT SUB
       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
                EXIT SUB
             END IF
          END IF
          MID$(ByteString$, StringPosition, 1) = UCASE$(Byte$)
       CASE ELSE
          ValidByteString = False
          EXIT SUB
       END SELECT
    END IF
 NEXT
 Var1$ = ByteString$
 DO
    Parse = INSTR(Var1$, " ")
    IF Parse THEN
       Var1$ = LEFT$(Var1$, Parse - 1) + MID$(Var1$, Parse + 1)
    ELSE
       EXIT DO
    END IF
 LOOP
 ByteString$ = Var1$
END SUB

REM convert a 3-digit packed ascii string to a hex string.
SUB AsciiToHex1(ByteString$)
 ON LOCAL ERROR RESUME NEXT
 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$
END SUB

REM convert a 1-digit packed ascii string to a hex string.
SUB AsciiToHex2(ByteString$)
 ON LOCAL ERROR RESUME NEXT
 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$
END SUB

REM display cell position in string format.
SUB FormatPosition2(Var)
 ON LOCAL ERROR RESUME NEXT
 IF Var = 1 THEN
    PRINTf "Marker#" + MID$(STR$(MarkerPosition), 2) + ":"
 ELSE
    PRINTf "Marker#" + MID$(STR$(MarkerPosition), 2) + " Position:"
 END IF
 TempPosition3 = Markers#(MarkerPosition)
 IF CurrentWindow2 = False THEN
    PRINTf MID$(STR$(TempPosition3 - 1), 2) + " "
 ELSE
    PRINTf RIGHT$("00000000" + HEX$(TempPosition3 - 1), 8) + "H "
 END IF
 CALL CalculatePosition2
 IF CurrentWindow2 = False THEN
    PRINTf "(page:" + MID$(STR$(FilePage2 - 1), 2) + ","
 ELSE
    PRINTf "(page:" + RIGHT$("00000000" + HEX$(FilePage2 - 1), 8) + "H,"
 END IF
 PRINTf "row:" + MID$(STR$(PageRow2), 2) + ","
 PRINTf "column:" + MID$(STR$(PageColumn2), 2) + ")"
 IF Var = 1 THEN
    PRINTf " Press <esc>:"
 END IF
END SUB

REM returns ambiguated filename.
SUB GetShortFilename(Short.Filename$)
 ON LOCAL ERROR GOTO Error.Routine11
 Short.Filename$ = Nul
 ' read 8.3 filename.
 IF Windows.Detected THEN
    InregsX.AX = &H7160
    InregsX.CX = &H8001
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.SI = VARPTR(ASCIIZ)
    InregsX.ES = VARSEG(ASCIIZ2)
    InregsX.DI = VARPTR(ASCIIZ2)
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    InregsX.AX = &H6000
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.SI = VARPTR(ASCIIZ)
    InregsX.ES = VARSEG(ASCIIZ2)
    InregsX.DI = VARPTR(ASCIIZ2)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' check carry flag error.
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    Short.Filename$ = ASCIIZ2
    Imbedded = INSTR(Short.Filename$, CHR$(0))
    IF Imbedded THEN
       Short.Filename$ = LEFT$(Short.Filename$, Imbedded - 1)
    END IF
 END IF
Error.Resume11:
 EXIT SUB
Error.Routine11:
 RESUME Error.Resume11
END SUB

REM Mouse routines:
REM   MouseDriver       --  processes mouse activity.
REM   MouseDriver1      --  processes mouse activity.
REM   MouseDriver2      --  processes mouse drag activity.
REM   MouseFunction     --  accesses mouse.
REM   MouseFunction2    --  positions mouse.
REM   HMouse            --  hides mouse cursor.
REM   SMouse            --  shows mouse cursor.

REM subroutine to check mouse activity.
SUB MouseDriver
 ON LOCAL ERROR RESUME NEXT
 STATIC Pressed1 AS INTEGER
 Mouse.Button1 = False
 Mouse.Button2 = False
 Mouse.Button3 = False
 Mouse.Row = False
 Mouse.Column = False
 Mouse.ButtonX = False

 ' check mouse present
 IF Mouse.Present = False THEN
    EXIT SUB
 END IF

 ' read middle mouse button.
 CALL MouseFunction(Button, 2)
 IF (OutregsX.AX AND 4) = 4 THEN
    IF OutregsX.BX > 0 THEN
       Mouse.Button3 = True
       Mouse.Row = OutregsX.DX / 8 + 1
       Mouse.Column = OutregsX.CX / 8 + 1
       EXIT SUB
    END IF
 END IF

 ' read right mouse button.
 CALL MouseFunction(Button, 1)
 IF (OutregsX.AX AND 2) = 2 THEN
    IF OutregsX.BX > 0 THEN
       Mouse.Button2 = True
       Mouse.Row = OutregsX.DX / 8 + 1
       Mouse.Column = OutregsX.CX / 8 + 1
       EXIT SUB
    END IF
 END IF

 ' read left mouse button.
 CALL MouseFunction(Button, 0)
 Var2 = OutregsX.CX / 8 + 1
 Var3 = OutregsX.DX / 8 + 1
 IF (OutregsX.AX AND 1) = 0 THEN
    ' check button released.
    IF Pressed1 THEN
       Pressed1 = 0
       ' get button press data.
       Mouse.ButtonX = True
       Mouse.RowX = Var3
       Mouse.ColumnX = Var2
       EXIT SUB
    END IF
 END IF

 ' check mouse button pressed.
 IF (OutregsX.AX AND 1) = 1 THEN
    Pressed1 = True
    EXIT SUB
 END IF

 ' read mouse position.
 CALL MouseFunction(Position, 0)
 Var2 = OutregsX.CX / 8 + 1
 Var3 = OutregsX.DX / 8 + 1
 IF Var3 <> Mouse.X OR Var2 <> Mouse.Y THEN
    Mouse.X = Var3
    Mouse.Y = Var2
    Mouse.Row = Mouse.X
    Mouse.Column = Mouse.Y
 END IF
END SUB

REM subroutine to check mouse activity.
SUB MouseDriver1
 ON LOCAL ERROR RESUME NEXT
 Mouse.Row = False
 Mouse.Column = False
 Mouse.ButtonX = False

 ' check mouse present
 IF Mouse.Present = False THEN
    EXIT SUB
 END IF

 ' read left mouse button.
 CALL MouseFunction(Button, 0)
 IF (OutregsX.AX AND 1) = 1 THEN
    IF OutregsX.BX > 0 THEN
       Mouse.ButtonX = True
       Mouse.RowX = OutregsX.DX / 8 + 1
       Mouse.ColumnX = OutregsX.CX / 8 + 1
       EXIT SUB
    END IF
 END IF

 ' read mouse position.
 CALL MouseFunction(Position, 0)
 Var2 = OutregsX.CX / 8 + 1
 Var3 = OutregsX.DX / 8 + 1
 IF Var3 <> Mouse.X OR Var2 <> Mouse.Y THEN
    Mouse.X = Var3
    Mouse.Y = Var2
    Mouse.Row = Mouse.X
    Mouse.Column = Mouse.Y
 END IF
END SUB

REM subroutine to check mouse activity.
SUB MouseDriver2
 ON LOCAL ERROR RESUME NEXT
 STATIC Pressed2 AS INTEGER
 Mouse.Button1 = False
 Mouse.Button2 = False
 Mouse.Button3 = False
 Mouse.Row = False
 Mouse.Column = False
 Mouse.ButtonX = False

 ' check mouse present
 IF Mouse.Present = False THEN
    EXIT SUB
 END IF

 ' read middle mouse button.
 CALL MouseFunction(Button, 2)
 IF (OutregsX.AX AND 4) = 4 THEN
    IF OutregsX.BX > 0 THEN
       Mouse.Button3 = True
       Mouse.Row = OutregsX.DX / 8 + 1
       Mouse.Column = OutregsX.CX / 8 + 1
       EXIT SUB
    END IF
 END IF

 ' read right mouse button.
 CALL MouseFunction(Button, 1)
 IF (OutregsX.AX AND 2) = 2 THEN
    IF OutregsX.BX > 0 THEN
       Mouse.Button2 = True
       Mouse.Row = OutregsX.DX / 8 + 1
       Mouse.Column = OutregsX.CX / 8 + 1
       EXIT SUB
    END IF
 END IF

 ' read left mouse button.
 CALL MouseFunction(Button, 0)
 Var2 = OutregsX.CX / 8 + 1
 Var3 = OutregsX.DX / 8 + 1
 IF (OutregsX.AX AND 1) = 0 THEN
    ' check button released.
    IF Pressed2 THEN
       Pressed2 = 0
       ' get button press data.
       Mouse.ButtonX = True
       Mouse.RowX = Var3
       Mouse.ColumnX = Var2
       EXIT SUB
    END IF
 END IF

 ' check mouse button pressed.
 IF (OutregsX.AX AND 1) = 1 THEN
    ' check button pressed w/ mouse position.
    CALL MouseFunction(Position, 0)
    Var2 = OutregsX.CX / 8 + 1
    Var3 = OutregsX.DX / 8 + 1
    IF Var3 <> Mouse.X OR Var2 <> Mouse.Y THEN
       Mouse.Button1 = 2
       Mouse.X = Var3
       Mouse.Y = Var2
       Mouse.Row = Mouse.X
       Mouse.Column = Mouse.Y
       EXIT SUB
    END IF

    ' check button pressed w/ mouse position.
    CALL MouseFunction(Position, 0)
    Var2 = OutregsX.CX / 8 + 1
    Var3 = OutregsX.DX / 8 + 1
    IF Var3 = 2 OR Var3 = 3 THEN
       IF Var2 >= 6 AND Var2 <= 49 THEN
          Mouse.Button1 = 3
          Mouse.X = Var3
          Mouse.Y = Var2
          Mouse.Row = Mouse.X
          Mouse.Column = Mouse.Y
          EXIT SUB
       END IF
    END IF

    ' check button pressed w/ mouse position.
    CALL MouseFunction(Position, 0)
    Var2 = OutregsX.CX / 8 + 1
    Var3 = OutregsX.DX / 8 + 1
    IF Var3 = 20 OR Var3 = 21 THEN
       IF Var2 >= 6 AND Var2 <= 49 THEN
          Mouse.Button1 = 4
          Mouse.X = Var3
          Mouse.Y = Var2
          Mouse.Row = Mouse.X
          Mouse.Column = Mouse.Y
          EXIT SUB
       END IF
    END IF
    Pressed2 = True

    ' init mouse drag pivot byte.
    CALL MouseFunction(Button, 0)
    Var2 = OutregsX.CX / 8 + 1
    Var3 = OutregsX.DX / 8 + 1
    CopyStart = 0
    Time1 = SFalse
    IF Var3 > 3 AND Var3 < 20 THEN
       IF Var2 >= 6 AND Var2 <= 49 THEN
          CopyStart = FilePosition
       END IF
    END IF
    EXIT SUB
 END IF

 ' read mouse position.
 CALL MouseFunction(Position, 0)
 Var2 = OutregsX.CX / 8 + 1
 Var3 = OutregsX.DX / 8 + 1
 IF Var3 <> Mouse.X OR Var2 <> Mouse.Y THEN
    Mouse.X = Var3
    Mouse.Y = Var2
    Mouse.Row = Mouse.X
    Mouse.Column = Mouse.Y
 END IF
END SUB

REM subroutine calls mouse bios function.
REM INT 33H InregsX.AX=
REM   00 - initialize mouse.
REM   01 - show mouse cursor.
REM   02 - hide mouse cursor. (re-entrant).
REM   03 - return position and button status.
REM   04 - position mouse.
REM   05 - return button press data.
SUB MouseFunction (Var1, Var2)
 ON LOCAL ERROR RESUME NEXT
 InregsX.AX = Var1
 InregsX.BX = Var2
 CALL InterruptX(&H33, InregsX, OutregsX)
END SUB

REM subroutine positions mouse.
REM INT 33H InregsX.AX = 04 - position mouse.
SUB MouseFunction2 (Var1, Var2)
 ON LOCAL ERROR RESUME NEXT
 InregsX.AX = MovePosition
 InregsX.CX = Var1 ' column
 InregsX.DX = Var2 ' row
 CALL InterruptX(&H33, InregsX, OutregsX)
END SUB

REM hide mouse.
REM   Hide/Show should be in pairs with the text update inbetween.
REM   Multiple calls to HideMouse should be released with ShowMouse
REM   the same number of times.
SUB HMouse
 On Local Error Resume Next
 IF Mouse.Present THEN
    CALL MouseFunction(HideMouse, 0)
    Show.Mouse = Show.Mouse - 1
 END IF
END SUB

REM show mouse.
REM   Show.Mouse=1 indicates the cursor has been initialized.
REM   more than 1 show mouse may confuse system and hang.
SUB SMouse
 On Local Error Resume Next
 IF Mouse.Present THEN
    IF Show.Mouse < 1 THEN
       CALL MouseFunction(ShowMouse, 0)
       Show.Mouse = Show.Mouse + 1
    END IF
 END IF
END SUB

REM Functions:
REM   ReleaseTime       --  releases time slice to windows.
REM   Conanicalize      --  trims a filename.
REM   TestFile          --  test if a file could or does exist.
REM   DIRx$             --  specialized DIR$ function for DOS.
REM   DIRz$             --  specialized DIR$ function for Windows.
REM   CalculateColumn   --  calculate column groups.
REM   CalculateColumn2  --  calculate column groups.
REM   CalculatePosition3#  --  calculate mouse position.
REM   DumpLineRange$    --  format line range.
REM   Directories       --  get a directory from temp data file.
REM   Filenames         --  get a filename from temp data file.
REM   UndoByte          --  get an undo byte from temp data file.
REM   UndoPosition      --  get an undo position from temp data file.
REM   Markers           --  get a marker value from temp data file.

REM function to release time slice in windows.
FUNCTION ReleaseTime
 ON LOCAL ERROR RESUME NEXT
 IF SupportedCall = False THEN
    InregsX.AX = &H1680
    InregsX.BX = &H0
    CALL InterruptX(&H2F, InregsX, OutregsX)
    IF (OutregsX.AX AND &HFF) = &H80 THEN
       SupportedCall = True
    END IF
 END IF
 ReleaseTime = True
END FUNCTION

REM conanicalizes filename.
FUNCTION Conanicalize$ (Var$)
 ON LOCAL ERROR RESUME NEXT
 Var1$ = Var$

 ' strip drive letter.
 Var1$ = UCASE$(Var1$)
 IF MID$(Var1$, 2, 1) = ":" THEN
    Var1$ = MID$(Var1$, 3)
 END IF

 ' strip dos path.
 DO
    Path = INSTR(Var1$, "\")
    IF Path THEN
       Var1$ = MID$(Var1$, Path + 1)
    ELSE
       EXIT DO
    END IF
 LOOP
 Conanicalize$ = Var1$
END FUNCTION

REM Test data file exists.
REM Returns: TestFile=-1 if file exists,
REM   or non-existent and can be created.
FUNCTION TestFile(Var$)
 On Local Error Resume Next

 ' store asciiz filename.
 DIM TestASCIIZ AS STRING * 260
 TestASCIIZ = Var$ + CHR$(0)

 IF Windows.Detected THEN
    ' get file attribute.
    InregsX.AX = &H7143
    InregsX.BX = &H0 ' get attr
    InregsX.DS = VARSEG(TestASCIIZ)
    InregsX.DX = VARPTR(TestASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    ' get file attribute.
    InregsX.AX = &H4300
    InregsX.DS = VARSEG(TestASCIIZ)
    InregsX.DX = VARPTR(TestASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' check carry flag error.
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    TestFile = True ' file exists
    EXIT FUNCTION
 END IF

 ' check file nonexistent.
 IF (OutregsX.AX) = 2 THEN
    TestFile = True ' file not found
    EXIT FUNCTION
 END IF
 TestFile = False
END FUNCTION

REM specialized DIR$ function for DOS.
FUNCTION DIRx$ (Filespec$) STATIC ' keep data between calls.
 ON LOCAL ERROR GOTO Error.Routine10
 DIM InregsX4 AS RegTypeX
 DIM OutregsX4 AS RegTypeX
 DIM DTAfilex AS DTAtype
 DIM ASCIIZx AS STRING * 260
 DIM ASCIIZx2 AS STRING * 260
 DIM Current.DTA.SEG AS INTEGER
 DIM Current.DTA.OFF AS INTEGER

 ' check call type.
 IF Filespec$ = Nul THEN
    GOSUB FindNext
 ELSE
    GOSUB CheckDrive
    ASCIIZx = UCASE$(Filespec$) + CHR$(0)
    GOSUB FindFirst
 END IF
 DIRx$ = Filenamex$
 EXIT FUNCTION

' initate findfile.
FindFirst:
 ' store current dta
 InregsX4.AX = &H2F00
 CALL InterruptX(&H21, InregsX4, OutregsX4)
 Current.DTA.SEG = OutregsX4.ES
 Current.DTA.OFF = OutregsX4.BX

 ' store function dta
 InregsX4.AX = &H1A00
 InregsX4.DS = VARSEG(DTAfilex)
 InregsX4.DX = VARPTR(DTAfilex)
 CALL InterruptX(&H21, InregsX4, OutregsX4)

 ' findfirst
 InregsX4.AX = &H4E00
 InregsX4.CX = &H37
 InregsX4.DS = VARSEG(ASCIIZx)
 InregsX4.DX = VARPTR(ASCIIZx)
 CALL InterruptX(&H21, InregsX4, OutregsX4)

 ' check carry flag error
 IF (OutregsX4.flags AND &H1) = &H1 THEN
    Filenamex$ = Nul
 END IF
 IF (OutregsX4.flags AND &H1) = &H0 THEN
    GOSUB Store.Fileinfo
 END IF

 ' restore current dta
 InregsX4.AX = &H1A00
 InregsX4.DS = Current.DTA.SEG
 InregsX4.DX = Current.DTA.OFF
 CALL InterruptX(&H21, InregsX4, OutregsX4)
 RETURN

' continue filespec match.
FindNext:
 ' store current dta
 InregsX4.AX = &H2F00
 CALL InterruptX(&H21, InregsX4, OutregsX4)
 Current.DTA.SEG = OutregsX4.ES
 Current.DTA.OFF = OutregsX4.BX

 ' store function dta
 InregsX4.AX = &H1A00
 InregsX4.DS = VARSEG(DTAfilex)
 InregsX4.DX = VARPTR(DTAfilex)
 CALL InterruptX(&H21, InregsX4, OutregsX4)

 ' find next filename
 InregsX4.AX = &H4F00
 CALL InterruptX(&H21, InregsX4, OutregsX4)

 ' check carry flag error
 IF (OutregsX4.flags AND &H1) = &H1 THEN
    Filenamex$ = Nul
 END IF
 IF (OutregsX4.flags AND &H1) = &H0 THEN
    GOSUB Store.Fileinfo
 END IF

 ' restore current dta
 InregsX4.AX = &H1A00
 InregsX4.DS = Current.DTA.SEG
 InregsX4.DX = Current.DTA.OFF
 CALL InterruptX(&H21, InregsX4, OutregsX4)
 RETURN

' check drive.
CheckDrive:
 Disk.Ready = 0
 IF MID$(Filespec$, 2, 1) = ":" THEN
    GOSUB StoreDrive
    Z$ = LEFT$(Filespec$, 1) + ":\"
    CHDIR Z$
    IF Disk.Ready THEN
       DIRx$ = Nul
       EXIT FUNCTION
    END IF
    CHDIR DefaultDir$
 END IF
 RETURN

' store current drive/directory.
StoreDrive:
 InregsX4.AX = &H1900
 CALL InterruptX(&H21, InregsX4, OutregsX4)
 Drive.Number = OutregsX4.AX AND &HFF
 InregsX4.AX = &H4700
 InregsX4.DX = Drive.Number + 1
 InregsX4.DS = VARSEG(ASCIIZx2)
 InregsX4.SI = VARPTR(ASCIIZx2)
 CALL InterruptX(&H21, InregsX4, OutregsX4)
 DefaultDir$ = LEFT$(ASCIIZx2, INSTR(ASCIIZx2, CHR$(0)) - 1)
 IF LEFT$(DefaultDir$, 1) <> "\" THEN
    DefaultDir$ = "\" + DefaultDir$
 END IF
 RETURN

' store file information
Store.Fileinfo:
 ' strip filename from ASCIIZ
 Filenamex$ = DTAfilex.ASCIIZfilename
 VX = INSTR(Filenamex$, CHR$(0))
 IF VX THEN
    Filenamex$ = LEFT$(Filenamex$, VX - 1)
 END IF
 RETURN

REM error routine
Error.Routine10:
 ErrorValue = ERR
 Select Case ErrorValue
 Case 57, 71, 76
    Disk.Ready = True
    RESUME NEXT
 END Select
 RESUME NEXT
END FUNCTION

REM specialized DIR$ function for Windows.
FUNCTION DIRz$ (Filespec$)
 ON LOCAL ERROR GOTO Error.Routine10x
 DIM InregsX4 AS RegTypeX
 DIM OutregsX4 AS RegTypeX
 DIM ASCIIZx AS STRING * 260
 DIM ASCIIZx2 AS STRING * 260

 ' check call type.
 IF Filespec$ = Nul THEN
    GOSUB FindNext2
 ELSE
    GOSUB CheckDrive2
    ASCIIZx = Filespec$ + CHR$(0)
    GOSUB FindFirst2
 END IF
 DIRz$ = Filenamex$
 EXIT FUNCTION

' initate findfile.
FindFirst2:
 ' findfirst
 InregsX4.AX = &H714E
 InregsX4.CX = &H27
 InregsX4.SI = &H1
 InregsX4.DS = VARSEG(ASCIIZx)
 InregsX4.DX = VARPTR(ASCIIZx)
 InregsX4.ES = VARSEG(WDTAfile)
 InregsX4.DI = VARPTR(WDTAfile)
 CALL InterruptX(&H21, InregsX4, OutregsX4)
 Wfile.Handle = OutregsX4.AX

 ' check carry flag error
 IF (OutregsX4.flags AND &H1) = &H1 THEN
    Filenamex$ = Nul
 END IF
 IF (OutregsX4.flags AND &H1) = &H0 THEN
    GOSUB Store.Fileinfo2
 END IF
 RETURN

' continue filespec match.
FindNext2:
 ' find next filename
 InregsX4.AX = &H714F
 InregsX4.BX = Wfile.Handle
 InregsX4.SI = &H1
 InregsX4.ES = VARSEG(WDTAfile)
 InregsX4.DI = VARPTR(WDTAfile)
 CALL InterruptX(&H21, InregsX4, OutregsX4)

 ' check carry flag error
 IF (OutregsX4.flags AND &H1) = &H1 THEN
    Filenamex$ = Nul
 END IF
 IF (OutregsX4.flags AND &H1) = &H0 THEN
    GOSUB Store.Fileinfo2
 END IF
 RETURN

' check drive.
CheckDrive2:
 Disk.Ready = 0
 IF MID$(Filespec$, 2, 1) = ":" THEN
    GOSUB StoreDrive2
    Z$ = LEFT$(Filespec$, 1) + ":\"
    CHDIR Z$
    IF Disk.Ready THEN
       DIRz$ = Nul
       EXIT FUNCTION
    END IF
    CHDIR DefaultDir$
 END IF
 RETURN

' store current drive/directory.
StoreDrive2:
 InregsX4.AX = &H1900
 CALL InterruptX(&H21, InregsX4, OutregsX4)
 Drive.Number = OutregsX4.AX AND &HFF
 InregsX4.AX = &H4700
 InregsX4.DX = Drive.Number + 1
 InregsX4.DS = VARSEG(ASCIIZx2)
 InregsX4.SI = VARPTR(ASCIIZx2)
 CALL InterruptX(&H21, InregsX4, OutregsX4)
 DefaultDir$ = LEFT$(ASCIIZx2, INSTR(ASCIIZx2, CHR$(0)) - 1)
 IF LEFT$(DefaultDir$, 1) <> "\" THEN
    DefaultDir$ = "\" + DefaultDir$
 END IF
 RETURN

' store file information
Store.Fileinfo2:
 ' strip filename from ASCIIZ
 Filenamex$ = WDTAfile.ASCIIZfull
 VX = INSTR(Filenamex$, CHR$(0))
 IF VX THEN
    Filenamex$ = LEFT$(Filenamex$, VX - 1)
 END IF
 RETURN

REM error routine
Error.Routine10x:
 ErrorValue = ERR
 Select Case ErrorValue
 Case 57, 71, 76
    Disk.Ready = True
    RESUME NEXT
 END Select
 RESUME NEXT
END FUNCTION

REM function to calculate column groups.
FUNCTION CalculateColumn
 On Local Error Resume Next
 SELECT CASE PageColumn
 CASE 1 TO 4
    CalculateColumn = (PageColumn - 1) * 2 + 1
 CASE 5 TO 8
    CalculateColumn = (PageColumn - 1) * 2 + 2
 CASE 9 TO 12
    CalculateColumn = (PageColumn - 1) * 2 + 3
 CASE 13 TO 16
    CalculateColumn = (PageColumn - 1) * 2 + 4
 CASE 17 TO 20
    CalculateColumn = (PageColumn - 1) * 2 + 5
 END SELECT
END FUNCTION

REM function to calculate column groups.
FUNCTION CalculateColumn2
 On Local Error Resume Next
 SELECT CASE PageColumn2
 CASE 1 TO 4
    CalculateColumn2 = (PageColumn2 - 1) * 2 + 1
 CASE 5 TO 8
    CalculateColumn2 = (PageColumn2 - 1) * 2 + 2
 CASE 9 TO 12
    CalculateColumn2 = (PageColumn2 - 1) * 2 + 3
 CASE 13 TO 16
    CalculateColumn2 = (PageColumn2 - 1) * 2 + 4
 CASE 17 TO 20
    CalculateColumn2 = (PageColumn2 - 1) * 2 + 5
 END SELECT
END FUNCTION

REM function to calculate mouse position.
FUNCTION CalculatePosition3#
 On Local Error Resume Next
 Var1# = (FilePage - 1) * 320 + (Mouse.Row - 4) * 20
 SELECT CASE Mouse.Column
 CASE 6, 7
    Var1# = Var1# + 1#
 CASE 8, 9
    Var1# = Var1# + 2#
 CASE 10, 11
    Var1# = Var1# + 3#
 CASE 12, 13
    Var1# = Var1# + 4#
 CASE 15, 16
    Var1# = Var1# + 5#
 CASE 17, 18
    Var1# = Var1# + 6#
 CASE 19, 20
    Var1# = Var1# + 7#
 CASE 21, 22
    Var1# = Var1# + 8#
 CASE 24, 25
    Var1# = Var1# + 9#
 CASE 26, 27
    Var1# = Var1# + 10#
 CASE 28, 29
    Var1# = Var1# + 11#
 CASE 30, 31
    Var1# = Var1# + 12#
 CASE 33, 34
    Var1# = Var1# + 13#
 CASE 35, 36
    Var1# = Var1# + 14#
 CASE 37, 38
    Var1# = Var1# + 15#
 CASE 39, 40
    Var1# = Var1# + 16#
 CASE 42, 43
    Var1# = Var1# + 17#
 CASE 44, 45
    Var1# = Var1# + 18#
 CASE 46, 47
    Var1# = Var1# + 19#
 CASE 48, 49
    Var1# = Var1# + 20#
 END SELECT
 CalculatePosition3# = Var1#
END FUNCTION

REM function to format line range.
FUNCTION DumpLineRange$
 On Local Error Resume Next
 Var$ = "File: " + RTRIM$(Filename) + ", position" + STR$(FirstByte) + " to"
 IF LastByte <= FileLength THEN
    Var$ = Var$ + STR$(LastByte)
 ELSE
    Var$ = Var$ + STR$(FileLength)
 END IF
 Var$ = Var$ + " (x" + RIGHT$("00000000" + HEX$(FirstByte - 1), 8) + " - "
 IF LastByte <= FileLength THEN
    Var$ = Var$ + "x" + RIGHT$("00000000" + HEX$(LastByte - 1), 8) +")"
 ELSE
    Var$ = Var$ + "x" + RIGHT$("00000000" + HEX$(FileLength - 1), 8) +")"
 END IF
 Var$ = Var$ + "."
 DumpLineRange$ = Var$
END FUNCTION

REM directory structure function.
FUNCTION Directories$ (Var1!)
 ON LOCAL ERROR RESUME NEXT
 If Windows.Detected Then
    GET 3, Var1!, WinFileStruc
    Directories$ = WinFileStruc.Name
 Else
    GET 3, Var1!, DosFileStruc
    Directories$ = DosFileStruc.Name
 Endif
END FUNCTION

REM filename structure function.
FUNCTION Filenames$ (Var1!)
 ON LOCAL ERROR RESUME NEXT
 If Windows.Detected Then
    GET 2, Var1!, WinFileStruc
    Filenames$ = WinFileStruc.Name
 Else
    GET 2, Var1!, DosFileStruc
    Filenames$ = DosFileStruc.Name
 Endif
END FUNCTION

REM undo structure function.
FUNCTION UndoByte%(Var1!)
 ON LOCAL ERROR RESUME NEXT
 GET 6, Var1!, UndoFile
 UndoByte = UndoFile.UndoByte1(CurrentFile)
END FUNCTION

REM undo structure function.
FUNCTION UndoPosition#(Var1!)
 ON LOCAL ERROR RESUME NEXT
 GET 6, Var1!, UndoFile
 UndoPosition = UndoFile.UndoPosition1(CurrentFile)
END FUNCTION

REM marker structure function.
FUNCTION Markers#(Var1!)
 ON LOCAL ERROR RESUME NEXT
 GET 7, Var1!, MarkerFile
 Markers = MarkerFile.Markers1(CurrentFile)
END FUNCTION

REM Subroutines:
REM   GetFileLength     --  get filelength from asciiz.
REM   SetTrap           --  reset control-break trapping.
REM   ResetTrap         --  restore control-break trapping.

REM Functions:
REM   MakeFilename      --  constructs filename for file menu box.

REM read filelength from asciiz filename.
SUB GetFileLength(Var)
 On Local Error Resume Next
 ' reset filesize.
 FileLength = DFalse
 Var = False

 ' store dta.
 IF Windows.Detected = False THEN
    InregsX.AX = &H1A00
    InregsX.DS = VARSEG(DTAfile)
    InregsX.DX = VARPTR(DTAfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' check windows/get file info.
 IF Windows.Detected THEN
    InregsX.AX = &H714E
    InregsX.CX = &H27
    InregsX.SI = &H1
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    InregsX.ES = VARSEG(WDTAfile)
    InregsX.DI = VARPTR(WDTAfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Wfile.Handle = OutregsX.AX
 ELSE
    InregsX.AX = &H4E00
    InregsX.CX = &H27
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' check findfirst error.
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' store filesize.
    IF Windows.Detected THEN
       Temp# = ASC(MID$(WDTAfile.FileSizeHigh, 4, 1))
       Temp# = Temp# * &H100 + ASC(MID$(WDTAfile.FileSizeHigh, 3, 1))
       Temp# = Temp# * &H100 + ASC(MID$(WDTAfile.FileSizeHigh, 2, 1))
       Temp# = Temp# * &H100 + ASC(MID$(WDTAfile.FileSizeHigh, 1, 1))
       Temp# = Temp# * &H100 + ASC(MID$(WDTAfile.FileSizeLow, 4, 1))
       Temp# = Temp# * &H100 + ASC(MID$(WDTAfile.FileSizeLow, 3, 1))
       Temp# = Temp# * &H100 + ASC(MID$(WDTAfile.FileSizeLow, 2, 1))
       Temp# = Temp# * &H100 + ASC(MID$(WDTAfile.FileSizeLow, 1, 1))
    ELSE
       Temp# = ASC(MID$(DTAfile.FileSize, 4, 1))
       Temp# = Temp# * &H100 + ASC(MID$(DTAfile.FileSize, 3, 1))
       Temp# = Temp# * &H100 + ASC(MID$(DTAfile.FileSize, 2, 1))
       Temp# = Temp# * &H100 + ASC(MID$(DTAfile.FileSize, 1, 1))
    END IF
    FileLength = Temp#
 END IF

 ' FAT32 extended overflow.
 IF FileLength > 2147483647# THEN
    Var = True
    EXIT SUB
 END IF

 ' check windows dos.
 IF Windows.Detected THEN
    ' close long filename search
    InregsX.AX = &H71A1
    InregsX.BX = Wfile.Handle
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    ' restore basic dta
    InregsX.AX = &H1A00
    InregsX.DS = BASIC.DTA.SEG
    InregsX.DX = BASIC.DTA.OFF
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF
END SUB

REM reset control-break trapping.
SUB SetTrap
 On Local Error Resume Next
 IF TrapInit = 0 THEN ' make sure trap sets correctly.
    TrapInit = -1
    CALL SetInt
 END IF
END SUB

REM restore control-break trapping.
SUB ResetTrap
 On Local Error Resume Next
 IF TrapInit = -1 THEN ' make sure trap resets correctly.
    TrapInit = 0
    CALL RestInt
 END IF
END SUB

FUNCTION MakeFilename$(N$, C$, D$, X$)
 On Local Error Resume Next
 IF N$ = Nul THEN
    IF D$ = Nul THEN
       MakeFilename$ = C$ + ":\" + X$
    ELSE
       MakeFilename$ = C$ + ":\" + D$ + "\" + X$
    END IF
 ELSE
    IF D$ = Nul THEN
       MakeFilename$ = N$ + "\" + X$
    ELSE
       MakeFilename$ = N$ + "\" + D$ + "\" + X$
    END IF
 END IF
END FUNCTION

FUNCTION MakeFilename2$(N$, C$, D$)
 On Local Error Resume Next
 IF N$ = Nul THEN
    IF D$ = Nul THEN
       MakeFilename2$ = C$ + ":\"
    ELSE
       IF D$ = "\" THEN
          MakeFilename2$ = C$ + ":\"
       ELSE
          MakeFilename2$ = C$ + ":\" + D$
       END IF
    END IF
 ELSE
    IF D$ = Nul THEN
       MakeFilename2$ = N$ + "\"
    ELSE
       IF D$ = "\" THEN
          MakeFilename2$ = N$ + "\"
       ELSE
          MakeFilename2$ = N$ + "\" + D$
       END IF
    END IF
 END IF
END FUNCTION

FUNCTION MakeFilename3$(N$, C$, D$)
 On Local Error Resume Next
 IF N$ = Nul THEN
    IF D$ = Nul THEN
       MakeFilename3$ = C$ + ":\*.*"
    ELSE
       IF D$ = "\" THEN
          MakeFilename3$ = C$ + ":\*.*"
       ELSE
          MakeFilename3$ = C$ + ":\" + D$ + "\*.*"
       END IF
    END IF
 ELSE
    IF D$ = Nul THEN
       MakeFilename3$ = N$ + "\*.*"
    ELSE
       IF D$ = "\" THEN
          MakeFilename3$ = N$ + "\*.*"
       ELSE
          MakeFilename3$ = N$ + "\" + D$ + "\*.*"
       END IF
    END IF
 END IF
END FUNCTION

FUNCTION MakeFilename4$(N$, C$, D$)
 On Local Error Resume Next
 IF N$ = Nul THEN
    MakeFilename4$ = C$ + ":\" + D$
 ELSE
    IF D$ = "\" THEN
       MakeFilename4$ = N$ + "\"
    ELSE
       MakeFilename4$ = N$ + "\" + D$
    END IF
 END IF
END FUNCTION

REM End-of-subprogram. Surprising it still works.
