REM file: Whatis - Public Domain DOS Utility. Module 1 of 2. v2.1a.

' get standard include declarations
REM $INCLUDE: 'WHATIS.INC'

' declare external procedures
DECLARE SUB SetInt
DECLARE SUB RestInt

' backwards compatible for bc 7.1
REM $INCLUDE: 'BC7.INC'

' install new interrupt service routine
CALL SetInt

' program variables
REDIM Arrays(0 TO 26, 0 TO Max.Arrays) AS DOUBLE
REDIM Definitions(0 TO Max.FNs) AS STRING
REDIM Strngs(0 TO 26) AS STRING
REDIM Variables(0 TO 26) AS DOUBLE

' increase stack for recursion
STACK STACK

' declare global error routine
ON ERROR GOTO Error.Routine

' initialize some variables
Token.List = " -+*/\^()[]{}<>=|&!%~?:#@`;," + CHR$(34) ' cannot be changed
White.Space = CHR$(32) + CHR$(9) ' can be changed

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

' read command line from PSP
Command.Line = False
Command.Line$ = ""
InregsX.AX = &H6200
CALL InterruptX(&H21, InregsX, OutregsX)
PSPsegment = OutregsX.BX
PSPoffset = 128
DEF SEG = PSPsegment
FOR Count = 1 TO 127
   Command.Char = PEEK(PSPoffset + Count)
   SELECT CASE Command.Char
   CASE 0, 10, 13
      EXIT FOR
   CASE ELSE
      Command.Line$ = Command.Line$ + CHR$(Command.Char)
   END SELECT
NEXT
DEF SEG
Command.Line$ = LTRIM$(Command.Line$)
Command.Line$ = RTRIM$(Command.Line$)
IF LEN(Command.Line$) THEN
   Command.Line = True
END IF

' check break flag override
IF Command.Line THEN
   Var1 = INSTR(Command.Line$, "/~")
   IF Var1 THEN
      Var2 = ClearBreak
      Command.Line$ = LEFT$(Command.Line$, Var1 - 1) + MID$(Command.Line$, Var1 + 2)
   END IF
END IF
Command.Line$ = LTRIM$(Command.Line$)
Command.Line$ = RTRIM$(Command.Line$)
IF Command.Line$ = Nul THEN
   Command.Line = False
END IF

' check control break
IF BreakIS THEN
   GOTO End.Whatis
END IF

' get standard input
Standard.Input = False
Standard.Input$ = NUL
InregsX.AX = &HB00
CALL InterruptX(&H21, InregsX, OutregsX)
DO WHILE (OutregsX.AX AND &HFF) = &HFF
   Standard.Input = True
   InregsX.AX = &H800
   CALL InterruptX(&H21, InregsX, OutregsX)
   Char$ = CHR$(OutregsX.AX AND &HFF)
   SELECT CASE ASC(Char$)
   CASE 10, 13, 26
      ' nul
   CASE ELSE
      Standard.Input$ = Standard.Input$ + Char$
   END SELECT
   InregsX.AX = &HB00
   CALL InterruptX(&H21, InregsX, OutregsX)
LOOP

' store redirected input
IF Standard.Input THEN
   Quiet.Mode = True
   Out2 = Standard.Input$
END IF

' clear break flag
IF Standard.Input = False THEN
   Var = ClearBreak
END IF

' check control break
IF BreakIS THEN
   GOTO End.Whatis
END IF

' check nul filename input
IF Standard.Input$ = NUL THEN
   CALL RestInt ' restore Control-Break
   X$ = Inkey$ ' quits here
   CALL SetInt ' reset Control-Break
   IF X$ = CHR$(0) + CHR$(0) THEN
      GOTO End.Whatis
   END IF
END IF

' read the environment
IF ENVIRON$("WHATIS") <> Nul THEN
   Quiet.Mode = True
END IF

' display banner
IF Quiet.Mode = False THEN
   COLOR White, Black
   PRINT "Whatis v2.1a: Expression parser;"
END IF

' parse command line
IF Command.Line THEN
   Data.Error = False
   Command.Line$ = LTRIM$(Command.Line$)
   IF UCASE$(LEFT$(Command.Line$, 2)) = "/X" THEN
      Add.Lines = True
      Symbol$="+"
      Command.Line$ = MID$(Command.Line$, 3)
      IF LEFT$(Command.Line$, 1) = CHR$(34) THEN
         Symbol$ = MID$(Command.Line$, 2, 1)
         Command.Line$ = MID$(Command.Line$, 4)
      END IF
   END IF
   IF UCASE$(LEFT$(Command.Line$, 3)) = "/F:" THEN
      Filename$ = MID$(Command.Line$, 4)
      OPEN Filename$ FOR INPUT AS #1
      Out2 = ""
      WHILE NOT EOF(1)
         LINE INPUT #1, Var$
         IF LEN(Var$) THEN
            Out2 = Out2 + Var$
            IF Add.Lines THEN
               Out2 = Out2 + Symbol$
            END IF
         END IF
      WEND
      Out2 = RTRIM$(Out2)
      IF Add.Lines THEN
         Out2 = LEFT$(Out2, LEN(Out2) - 1)
      END IF
   ELSE
      Out2 = Command.Line$
   END IF
   CALL Remove.Spaces
END IF

' determine input type
IF Command.Line OR Standard.Input THEN
   IF Quiet.Mode = False THEN
      PRINT Out2; " Equals:";
   END IF
   ' clear recurse flags
   Recurse = 0
   RecurseNest = 0
   CALL Enter.Equate(True)
   Error.Resume1:
   CALL RestInt
   COLOR Plain, Black
   END
END IF

' reset error flow
Data.Error = True

' display header
IF Quiet.Mode = False THEN
   PRINT "Enter 'quit' to return to system."
END IF

Error.Resume2:

' keyboard input loop
DO
   COLOR Yellow, Black
   LOCATE , 1, 1
   PRINT ">";
   LOCATE , , 1
   ' get input line
   Out2 = KeyboardLine$
   PRINT
   ' check for break
   IF BreakIS THEN
      COLOR Plain, Black
      PRINT "*break*"
      EXIT DO
   END IF
   ' parse input line
   SELECT CASE UCASE$(Out2)
   CASE "QUIT"
      EXIT DO
   CASE ELSE
      ' calculate line
      CALL Remove.Spaces
      ' clear recurse flags
      Recurse = 0
      RecurseNest = 0
      CALL Enter.Equate(False)
   END SELECT
LOOP

' end program
IF Quiet.Mode = False THEN
   IF BreakIS THEN
      GOTO End.Whatis
   END IF
   COLOR White, Black
   PRINT "Press <enter> to return to DOS:";
   DO
      IF KeyIS THEN
         IF BreakIS THEN
            EXIT DO
         END IF
         Input.Char$ = KeyboardChar$
         IF Input.Char$ = CHR$(13) THEN
            EXIT DO
         END IF
      END IF
   LOOP
   PRINT
END IF

' end program label
End.Whatis:

' restore key trapping
CALL RestInt

' stop program
COLOR Plain, Black
END

' main error routine
Error.Routine:
 IF Data.Error = False THEN
    PRINT
 END IF
 IF Quiet.Mode THEN
    GOTO Error.Exit1
 END IF
 Error.Mode = ERR
 SELECT CASE Error.Mode
 CASE 53
    Print "File not found."
 CASE 57
    Print "Media error."
 CASE 61
    Print "Disk full."
 CASE 70
    Print "Permission denied."
 CASE 71
    Print "Disk not ready."
 CASE 5
    PRINT "Error #001: Illegal function call."
 CASE 6
    PRINT "Error #002: Overflow."
 CASE 9
    PRINT "Error #003: Subscript out of range."
 CASE 11
    PRINT "Error #004: Division by zero."
 CASE 92
    PRINT "Error #005: Whatis: "; Strng
 CASE 130
    PRINT "Error #006: Bad numeric specifier."
 CASE 140
    PRINT "Error #007: Bad exponent specifier."
 CASE 145
    PRINT "Error #008: Bad DEFFN declaration."
 CASE ELSE
    PRINT "Error #000: Error"; Error.Mode
 END SELECT
Error.Exit1:
 IF Data.Error = False THEN
    RESUME Error.Resume1
 ELSE
    RESUME Error.Resume2
 END IF
 END

' routine accepts an operation and performs on two values, string or numeric
SUB Arith (Token.Parsed$, Temp#, Temp2#)
 IF Last.Token THEN
    SELECT CASE Token.Parsed$
    CASE "-"
       Temp# = Temp# - Temp2#
    CASE "+"
       Temp# = Temp# + Temp2#
    CASE "/"
       Temp# = Temp# / Temp2#
    CASE "\"
       Temp# = Temp# \ Temp2#
    CASE "*"
       Temp# = Temp# * Temp2#
    CASE "^"
       Temp# = Temp# ^ Temp2#
    CASE "<"
       Temp# = (Temp# < Temp2#)
    CASE ">"
       Temp# = (Temp# > Temp2#)
    CASE "="
       Temp# = (Temp# = Temp2#)
    CASE "<="
       Temp# = (Temp# <= Temp2#)
    CASE ">="
       Temp# = (Temp# >= Temp2#)
    CASE "<>"
       Temp# = (Temp# <> Temp2#)
    CASE "|"
       Temp# = Temp# OR Temp2#
    CASE "&"
       Temp# = Temp# AND Temp2#
    CASE "%"
       Temp# = Temp# MOD Temp2#
    CASE "~"
       Temp# = Temp# XOR Temp2#
    CASE "?"
       Temp# = Temp# IMP Temp2#
    CASE ":"
       Temp# = Temp# EQV Temp2#
    CASE "#"
       Temp# = NOT (Temp# OR Temp2#)
    CASE "@"
       Temp# = NOT (Temp# IMP Temp2#)
    CASE "`"
       Temp# = NOT (Temp# AND Temp2#)
    END SELECT
    EXIT SUB
 END IF
 SELECT CASE Token.Parsed$
 CASE "-"
    IF RIGHT$(Out3, LEN(Out4)) = Out4 THEN
       Out3 = LEFT$(Out3, LEN(Out3) - LEN(Out4))
    END IF
 CASE "+"
    Out3 = Out3 + Out4
 CASE "/", "\"
    IF LEN(Out3) > False AND LEN(Out4) > False THEN
       Imbedded = INSTR(Out3, Out4)
       WHILE Imbedded
	  Out3 = LEFT$(Out3, Imbedded - 1) + MID$(Out3, Imbedded + LEN(Out4))
	  Imbedded = INSTR(Out3, Out4)
       WEND
    END IF
 CASE "<"
    Last.Token = True
    Temp# = Out3 < Out4
 CASE ">"
    Last.Token = True
    Temp# = Out3 > Out4
 CASE "="
    Last.Token = True
    Temp# = Out3 = Out4
 CASE "<="
    Last.Token = True
    Temp# = Out3 <= Out4
 CASE ">="
    Last.Token = True
    Temp# = Out3 >= Out4
 CASE "<>"
    Last.Token = True
    Temp# = Out3 <> Out4
 END SELECT
END SUB

SUB Assignment1 (S$, S%)
 IF LEFT$(S$, 2) = "--" THEN
    Assign = True
    Variables(S%) = Variables(S%) - 1#
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "++" THEN
    Assign = True
    Variables(S%) = Variables(S%) + 1#
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "**" THEN
    Assign = True
    Variables(S%) = Variables(S%) ^ 2#
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "//" THEN
    Assign = True
    Variables(S%) = SQR(Variables(S%))
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "-=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) - T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "+=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) + T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "/=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) / T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "\=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) \ T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "*=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) * T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "^=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) ^ T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 3) = "^-=" THEN
    Out2 = MID$(S$, 4)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) ^ (-T#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "%=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) MOD T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "|=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) OR T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "&=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) AND T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "~=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) XOR T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "?=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) IMP T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = ":=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = Variables(S%) EQV T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "#=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = NOT (Variables(S%) OR T#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "@=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = NOT (Variables(S%) IMP T#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "`=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = NOT (Variables(S%) AND T#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 1) = "=" THEN
    Out2 = MID$(S$, 2)
    Out2 = LTRIM$(Out2)
    CALL Equate(T#)
    IF Last.Token = True THEN
       Assign = True
       Variables(S%) = T#
    END IF
    EXIT SUB
 END IF
END SUB

SUB Assignment2 (S$, S1%, S2%)
 IF LEFT$(S$, 2) = "--" THEN
    Assign = True
    Arrays(S1%, S2%) = Arrays(S1%, S2%) - 1#
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "++" THEN
    Assign = True
    Arrays(S1%, S2%) = Arrays(S1%, S2%) + 1#
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "**" THEN
    Assign = True
    Arrays(S1%, S2%) = Arrays(S1%, S2%) ^ 2#
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "//" THEN
    Assign = True
    Arrays(S1%, S2%) = SQR(Arrays(S1%, S2%))
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "-=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) - T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "+=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) + T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "/=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) / T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "\=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) \ T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "*=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) * T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "^=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) ^ T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 3) = "^-=" THEN
    Out2 = MID$(S$, 4)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) ^ (-T#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "%=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) MOD T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "|=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) OR T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "&=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) AND T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "~=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) XOR T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "?=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) IMP T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = ":=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = Arrays(S1%, S2%) EQV T#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "#=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = NOT (Arrays(S1%, S2%) OR T#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "@=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = NOT (Arrays(S1%, S2%) IMP T#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 2) = "`=" THEN
    Out2 = MID$(S$, 3)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = NOT (Arrays(S1%, S2%) AND T#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(S$, 1) = "=" THEN
    Out2 = MID$(S$, 2)
    Out2 = LTRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(T#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(S1%, S2%) = T#
    END IF
 END IF
END SUB

' parses single letter tokens
SUB Parse.Alphabetic1 (Temp#)
 SELECT CASE UCASE$(Strng) ' test variable symbol
 CASE "A" TO "Z"
    Element = ASC(UCASE$(Strng)) - 64
    Element.Type$ = MID$(Out2, Token.Index, 1)
    SELECT CASE Element.Type$
    CASE "("
       CALL Get.Token
       CALL Get.Token
       CALL Parse1(Temp#)
       IF Strng = ")" THEN
          Temp# = Arrays(Element, CINT(Temp#))
          Last.Token = True
       ELSE
          Strng = "<missing closing token>"
          ERROR 92
       END IF
    CASE "["
       CALL Get.Token
       CALL Get.Token
       CALL Parse1(Temp#)
       IF Strng = "]" THEN
          Temp# = Arrays(Element, CINT(Temp#))
          Last.Token = True
       ELSE
          Strng = "<missing closing token>"
          ERROR 92
       END IF
    CASE "{"
       CALL Get.Token
       CALL Get.Token
       CALL Parse1(Temp#)
       IF Strng = "}" THEN
          Temp# = Arrays(Element, CINT(Temp#))
          Last.Token = True
       ELSE
          Strng = "<missing closing token>"
          ERROR 92
       END IF
    CASE ELSE
       Temp# = Variables(Element)
       Last.Token = True
    END SELECT
 CASE ELSE
    ERROR 92
 END SELECT
END SUB

' parses two-letter tokens
SUB Parse.Alphabetic2 (Temp#)
 SELECT CASE UCASE$(Strng)
 ' FN(1) to FN(26)
 CASE "FN"
    Var$ = LEFT$(Out2, Token.Index - 3)
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp4#)
    CALL Get.Token4
    Func% = CINT(Temp4#)
    IF Func% >= 1 AND Func% <= Max.FNs THEN
       Var2$ = MID$(Out2, Token.Index)
       Out2 = Definitions(Func%)
       Out2 = LTRIM$(Out2)
       ' test for nul token
       IF Out2 = Nul THEN
          Strng = "<bad FN token>"
          ERROR 92
       END IF
       ' test first call recurses
       If Recurse = Func% Then
          Strng = "<bad FN recurse>"
          ERROR 92
       Endif
       ' store first recursing call
       If Recurse = 0 Then
          Recurse = Func%
       Endif
       ' count recursing calls
       RecurseNest = RecurseNest + 1
       IF RecurseNest > 8 THEN
          Strng = "<bad max FN recurse>"
          ERROR 92
       END IF
       ' recursively call functions
       Token.Index = 1
       CALL Get.Token
       CALL Parse1(Temp#)
       ' reset recursive count
       RecurseNest = 0
       ' test huge recursed FN
       IF LEN(Var$) + LEN(Var2$) >= 4096 THEN
          Var$ = Nul
          Var2$ = Nul
          Strng = "<bad FN string>
          ERROR 92
       END IF
       Out2 = Var$ + Var2$
       Token.Index = LEN(Var$) + 1
       EXIT SUB
    END IF
    Strng = "<bad FN range>"
    ERROR 92
 ' functions w/o parameters
 CASE "PI" ' calculate PI
    Temp# = ATN(1#) * 4#
    Last.Token = True
 CASE "EX" ' calculate E
    Temp# = EXP(1#)
    Last.Token = True
 ' special case functions
 CASE "OR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Number# OR Temp#
    CALL Get.Token4
    Last.Token = True
 CASE ELSE
    IF MID$(Strng, 2, 1) = "$" THEN
       SELECT CASE UCASE$(LEFT$(Strng, 1))
       CASE "A" TO "Z"
	  Element = ASC(UCASE$(LEFT$(Strng, 1))) - 64
	  Out3 = Strngs(Element)
	  Last.Token = False
       END SELECT
    ELSE
       ERROR 92
    END IF
 END SELECT
END SUB

' parses three and greater letter tokens
SUB Parse.Alphabetic3 (Temp#)
 DIM DTAfile AS DTAtype
 SELECT CASE UCASE$(Strng)
 ' constant functions
 CASE "AUTHOR$"
    Out3 = "Erik Jon Oredson AS. CSci"
    Last.Token = False
 CASE "EMAIL$"
    Out3 = "eoredson@yahoo.com"
    Last.Token = False
 CASE "URL$"
    Out3 = "www.simtel.net www.filegate.net"
    Last.Token = False
 CASE "VERSION$"
    Out3 = "2.1a"
    Last.Token = False
 CASE "RELEASE$"
    Out3 = "1.0"
    Last.Token = False
 CASE "PROGRAM$"
    Out3 = "whatis"
    Last.Token = False
 CASE "PUBLISH$"
    Out3 = "07/13/2002"
    Last.Token = False
 CASE "COOKIE$"
    Out3 = "Thank you for all the cookies!"
    Last.Token = False
 ' functions w/o parameters
 CASE "FALSE"
    Temp# = FalseD
    Last.Token = True
 CASE "TRUE"
    Temp# = TrueD
    Last.Token = True
 CASE "TIMER"
    Temp# = TIMER
    Last.Token = True
 CASE "RND"
    Temp# = RND
    Last.Token = True
 ' boolean functions
 CASE "AND"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Number# AND Temp#
    CALL Get.Token4
    Last.Token = True
 CASE "MOD"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Number# MOD Temp#
    CALL Get.Token4
    Last.Token = True
 CASE "NOR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = NOT (Number# OR Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "NON"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = NOT (Number# IMP Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "XAN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = NOT (Number# AND Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "XOR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Number# XOR Temp#
    CALL Get.Token4
    Last.Token = True
 CASE "IMP"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Number# IMP Temp#
    CALL Get.Token4
    Last.Token = True
 CASE "EQV"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Number# EQV Temp#
    CALL Get.Token4
    Last.Token = True
 CASE "NOT"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = NOT Temp#
    CALL Get.Token4
    Last.Token = True
 ' date\time functions
 CASE "CLOCK"
    Temp# = Timer
    Last.Token = True
 CASE "DATE$"
    Out3 = DATE$
    Last.Token = False
 CASE "TIME$"
    Out3 = TIME$
    Last.Token = False
 ' conversion/calculation functions
 CASE "BIN$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = Nul
    IF Temp# >= FalseD THEN
       Digits = False
       DO
	  IF 2 ^ (Digits + 1) > Temp# THEN
	     EXIT DO
	  END IF
	  Digits = Digits + 1
       LOOP
       FOR Power = Digits TO 0 STEP -1
	  IF Temp# - 2 ^ Power >= 0 THEN
	     Temp# = Temp# - 2 ^ Power
	     Out3 = Out3 + "1"
	  ELSE
	     Out3 = Out3 + "0"
	  END IF
       NEXT
    END IF
    CALL Get.Token4
    Last.Token = False
 CASE "CHR$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = CHR$(CINT(Temp#))
    CALL Get.Token4
    Last.Token = False
 CASE "HEX$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = HEX$(Temp#)
    CALL Get.Token4
    Last.Token = False
 CASE "OCT$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = OCT$(Temp#)
    CALL Get.Token4
    Last.Token = False
 CASE "STR$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = STR$(Temp#)
    CALL Get.Token4
    Last.Token = False
 CASE "LCASE$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LCASE$(Out3)
    CALL Get.Token4
    Last.Token = False
 CASE "UCASE$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = UCASE$(Out3)
    CALL Get.Token4
    Last.Token = False
 CASE "RTRIM$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = RTRIM$(Out3)
    CALL Get.Token4
    Last.Token = False
 CASE "LTRIM$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LTRIM$(Out3)
    CALL Get.Token4
    Last.Token = False
 CASE "TRIM$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LTRIM$(RTRIM$(Out3))
    CALL Get.Token4
    Last.Token = False
 CASE "SPACE$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = STRING$(Temp#,32)
    CALL Get.Token4
    Last.Token = False
 CASE "MID$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp4# = Temp#
    IF Strng = "," THEN
       CALL Get.Token
       CALL Parse1(Temp#)
       Length# = Temp#
       Out3 = MID$(Out3, Temp4#, Length#)
    ELSE
       Out3 = MID$(Out3, Temp4#)
    END IF
    CALL Get.Token4
    Last.Token = False
 CASE "REMID$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    Start# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Length# = Temp#
    Out3 = LEFT$(Out3, Start# - 1) + MID$(Out3, Start# + Length#)
    CALL Get.Token4
    Last.Token = False
 CASE "DEMID$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    String1$ = Out3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF LEN(String1$) AND LEN(Out3) THEN
       Imbedded = INSTR(String1$, Out3)
       WHILE Imbedded
          String1$ = LEFT$(String1$, Imbedded - 1) + MID$(String1$, Imbedded + LEN(Out3))
	  Imbedded = INSTR(String1$, Out3)
       WEND
    END IF
    Out3 = String1$
    CALL Get.Token4
    Last.Token = False
 CASE "REPLACE$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    String1$ = Out3
    CALL Get.Token
    CALL Parse1(Temp#)
    String2$ = Out3
    CALL Get.Token
    CALL Parse1(Temp#)
    String3$ = Out3
    IF String1$ = Nul OR String2$ = Nul OR String2$ = String3$ THEN
       Out3 = String1$
       EXIT SUB
    END IF
    Temp1 = 1
    DO
       IF String1$ = Nul THEN
	  EXIT DO
       END IF
       Var1 = INSTR(Temp1, String1$, String2$)
       IF Var1 = False THEN
	  EXIT DO
       END IF
       String1$ = LEFT$(String1$, Var1 - 1) + String3$ + MID$(String1$, Var1 + LEN(String2$))
       Temp1 = Temp1 + LEN(String3$)
    LOOP
    Out3 = String1$
    CALL Get.Token4
    Last.Token = False
 CASE "LEFT$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LEFT$(Out3, Temp#)
    CALL Get.Token4
    Last.Token = False
 CASE "RIGHT$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = RIGHT$(Out3, Temp#)
    CALL Get.Token4
    Last.Token = False
 CASE "STRING$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Out3 = STRING$(Number#, Temp#)
    ELSE
       Out3 = STRING$(Number#, Out3)
    END IF
    CALL Get.Token4
    Last.Token = False
 CASE "ABS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = ABS(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "ASC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = ASC(Out3)
    CALL Get.Token4
    Last.Token = True
 CASE "ATN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = ATN(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "COS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = COS(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "COT"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = COS(Temp#) / SIN(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "CSC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = 1# / SIN(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "EXP"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = EXP(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "FBN" ' calculate xth fibonachi value
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Fibo.A# = 1
    Fibo.B# = 1
    IF Temp# >= 1 THEN
       FOR Fibo.Number# = 1 TO Temp# - 1
	   Fibo.Temp# = Fibo.B#
	   Fibo.B# = Fibo.A# + Fibo.B#
	   Fibo.A# = Fibo.Temp#
       NEXT
    END IF
    Temp# = Fibo.B#
    CALL Get.Token4
    Last.Token = True
 CASE "FCT"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Factorial# = 1
    Factorial.Count# = INT(Temp#)
    IF Factorial.Count# >= 1 THEN
       FOR Factorial.Count# = 1 TO Temp#
	  Factorial# = Factorial# * Factorial.Count#
       NEXT
    END IF
    Temp# = Factorial#
    CALL Get.Token4
    Last.Token = True
 CASE "FIX"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = FIX(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "INSTR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Start# = Temp#
       CALL Get.Token
       CALL Parse1(Temp#)
       Stored.String$ = Out3
       CALL Get.Token
       CALL Parse1(Temp#)
    ELSE
       Start# = 1#
       Stored.String$ = Out3
       CALL Get.Token
       CALL Parse1(Temp#)
    END IF
    Temp# = INSTR(Start#, Stored.String$, Out3)
    CALL Get.Token4
    Last.Token = True
 CASE "FRE"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Value# = Temp#
       Temp# = FRE(Value#)
    ELSE
       Temp# = FRE(Out3)
    END IF
    CALL Get.Token4
    Last.Token = True
 CASE "INT"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = INT(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "LEN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = LEN(Out3)
    CALL Get.Token4
    Last.Token = True
 CASE "LOG"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = LOG(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "PRM" ' calculate xth prime
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Prime.Number# = INT(Temp#)
    Prime# = 1
    IF Prime.Number# > FalseD THEN
       Prime.Counter# = False
       DO
	  Prime# = Prime# + 1
	  Prime.Flag = False
          FOR Loop.Count# = 2# TO INT(SQR(Prime#))
             IF Prime# / Loop.Count# = INT(Prime# / Loop.Count#) THEN
		Prime.Flag = True
		EXIT FOR
	     END IF
	  NEXT
	  IF Prime.Flag = False THEN
	     Prime.Counter# = Prime.Counter# + 1
	  END IF
	  IF Prime.Counter# = Prime.Number# THEN
	     EXIT DO
	  END IF
       LOOP
    END IF
    Temp# = Prime#
    CALL Get.Token4
    Last.Token = True
 CASE "SEC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = 1# / COS(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "SGN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = SGN(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "SIN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = SIN(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "SQR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = SQR(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "CBR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Temp# ^ (1#/3#)
    CALL Get.Token4
    Last.Token = True
 CASE "NTH"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Number# ^ (1#/Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "PWR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Number# ^ Temp#
    CALL Get.Token4
    Last.Token = True
 CASE "TAN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = TAN(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "VAL"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = VAL(Out3)
    CALL Get.Token4
    Last.Token = True
 ' misc. functions
 CASE "FORMAT$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Value# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = FORMAT$(Value#, Out3)
    CALL Get.Token4
    Last.Token = False
 ' disk functions
 CASE "DIR$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Out3 = Nul THEN
       Out3 = DIR$
    ELSE
       Out3 = DIR$(Out3)
    END IF
    CALL Get.Token4
    Last.Token = False
 CASE "CURDIR$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = CURDIR$(Out3)
    CALL Get.Token4
    Last.Token = False
 CASE "DRV"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Drive.Number% = CINT(Temp#)
    ELSE
       Drive.Number% = CINT(ASC(UCASE$(Out3))-64)
    END IF
    Last.Token = True
    CALL Get.Token4
    InregsX.AX = &H3600
    InregsX.DX = Drive.Number%
    CALL InterruptX(&H21, InregsX, OutregsX)
    Temp# = FalseD
    IF OutregsX.AX = &HFFFF THEN
       EXIT SUB
    END IF
    ASCIIZ = CHR$(Drive.Number% + 64) + ":\" + CHR$(0)
    InregsX.AX = &H7303
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    InregsX.ES = VARSEG(FAT32Struc)
    InregsX.DI = VARPTR(FAT32Struc)
    InregsX.CX = LEN(FAT32Struc)
    CALL InterruptX(&H21, InregsX, OutregsX)
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       IF (OutregsX.AX AND &HFF) <> 0 THEN
          Bytes# = CLNG(ASC(MID$(FAT32Struc.BytesPerSector, 1, 1)))
          Bytes# = Bytes# + CLNG(ASC(MID$(FAT32Struc.BytesPerSector, 2, 1))) * 256#
          Bytes# = Bytes# + CLNG(ASC(MID$(FAT32Struc.BytesPerSector, 3, 1))) * 65536#
          Bytes# = Bytes# + CLNG(ASC(MID$(FAT32Struc.BytesPerSector, 4, 1))) * 16777216#
          Sectors# = CLNG(ASC(MID$(FAT32Struc.FreeSectors, 1, 1)))
          Sectors# = Sectors# + CLNG(ASC(MID$(FAT32Struc.FreeSectors, 2, 1))) * 256#
          Sectors# = Sectors# + CLNG(ASC(MID$(FAT32Struc.FreeSectors, 3, 1))) * 65536#
          Sectors# = Sectors# + CLNG(ASC(MID$(FAT32Struc.FreeSectors, 4, 1))) * 16777216#
          Temp# = Bytes# * Sectors#
          EXIT SUB
       END IF
    END IF
    InregsX.AX = &H3600
    InregsX.DX = Drive.Number%
    CALL InterruptX(&H21, InregsX, OutregsX)
    IF OutregsX.AX < False THEN
       Sectors# = CDBL(OutregsX.AX + 65536)
    ELSE
       Sectors# = CDBL(OutregsX.AX)
    END IF
    IF OutregsX.BX < False THEN
       Clusters# = CDBL(OutregsX.BX + 65536)
    ELSE
       Clusters# = CDBL(OutregsX.BX)
    END IF
    IF OutregsX.CX < False THEN
       Bytes# = CDBL(OutregsX.CX + 65536)
    ELSE
       Bytes# = CDBL(OutregsX.CX)
    END IF
    Temp# = Sectors# * Clusters# * Bytes#
 CASE "TDRV"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Drive.Number% = CINT(Temp#)
    ELSE
       Drive.Number% = CINT(ASC(UCASE$(Out3))-64)
    END IF
    Last.Token = True
    CALL Get.Token4
    InregsX.AX = &H3600
    InregsX.DX = Drive.Number%
    CALL InterruptX(&H21, InregsX, OutregsX)
    Temp# = FalseD
    IF OutregsX.AX = &HFFFF THEN
       EXIT SUB
    END IF
    ASCIIZ = CHR$(Drive.Number% + 64) + ":\" + CHR$(0)
    InregsX.AX = &H7303
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    InregsX.ES = VARSEG(FAT32Struc)
    InregsX.DI = VARPTR(FAT32Struc)
    InregsX.CX = LEN(FAT32Struc)
    CALL InterruptX(&H21, InregsX, OutregsX)
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       IF (OutregsX.AX AND &HFF) <> 0 THEN
          Bytes# = CLNG(ASC(MID$(FAT32Struc.BytesPerSector, 1, 1)))
          Bytes# = Bytes# + CLNG(ASC(MID$(FAT32Struc.BytesPerSector, 2, 1))) * 256#
          Bytes# = Bytes# + CLNG(ASC(MID$(FAT32Struc.BytesPerSector, 3, 1))) * 65536#
          Bytes# = Bytes# + CLNG(ASC(MID$(FAT32Struc.BytesPerSector, 4, 1))) * 16777216#
          Sectors# = CLNG(ASC(MID$(FAT32Struc.TotalSectors, 1, 1)))
          Sectors# = Sectors# + CLNG(ASC(MID$(FAT32Struc.TotalSectors, 2, 1))) * 256#
          Sectors# = Sectors# + CLNG(ASC(MID$(FAT32Struc.TotalSectors, 3, 1))) * 65536#
          Sectors# = Sectors# + CLNG(ASC(MID$(FAT32Struc.TotalSectors, 4, 1))) * 16777216#
          Temp# = Bytes# * Sectors#
          EXIT SUB
       END IF
    END IF
    InregsX.AX = &H3600
    InregsX.DX = Drive.Number%
    CALL InterruptX(&H21, InregsX, OutregsX)
    IF OutregsX.AX < False THEN
       Sectors# = CDBL(OutregsX.AX + 65536)
    ELSE
       Sectors# = CDBL(OutregsX.AX)
    END IF
    IF OutregsX.CX < False THEN
       Clusters# = CDBL(OutregsX.CX + 65536)
    ELSE
       Clusters# = CDBL(OutregsX.CX)
    END IF
    IF OutregsX.DX < False THEN
       Bytes# = CDBL(OutregsX.DX + 65536)
    ELSE
       Bytes# = CDBL(OutregsX.DX)
    END IF
    Temp# = Sectors# * Clusters# * Bytes#
 CASE "FILESIZE"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = FalseD
    IF Last.Token = False THEN
       IF LEN(Out3) THEN
          Last.Token = True
          ASCIIZ = Out3 + CHR$(0)
          ' restore directory search dta
          InregsX.AX = &H1A00
          InregsX.DS = VARSEG(DTAfile)
          InregsX.DX = VARPTR(DTAfile)
          CALL InterruptX(&H21, InregsX, OutregsX)
          ' find first filename
          InregsX.AX = &H4E00
          InregsX.CX = &H27
          InregsX.DS = VARSEG(ASCIIZ)
          InregsX.DX = VARPTR(ASCIIZ)
          CALL InterruptX(&H21, InregsX, OutregsX)
          ' check findfirst error
          IF (OutregsX.Flags AND &H1) = &H0 THEN
             ' store file size
             Temp# = Dfalse
             Temp# = 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
          ' restore basic dta
          InregsX.AX = &H1A00
          InregsX.DS = BASIC.DTA.SEG
          InregsX.DX = BASIC.DTA.OFF
          CALL InterruptX(&H21, InregsX, OutregsX)
       END IF
    END IF
    CALL Get.Token4
 CASE "FILEATTR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = FalseD
    IF Last.Token = False THEN
       IF LEN(Out3) THEN
	  Last.Token = True
          ASCIIZ = Out3 + CHR$(0)
	  Out3 = SPACE$(8)
	  ' restore directory search dta
	  InregsX.AX = &H1A00
          InregsX.DS = VARSEG(DTAfile)
          InregsX.DX = VARPTR(DTAfile)
	  CALL InterruptX(&H21, InregsX, OutregsX)
	  ' find first filename
	  InregsX.AX = &H4E00
	  InregsX.CX = &H37
          InregsX.DS = VARSEG(ASCIIZ)
          InregsX.DX = VARPTR(ASCIIZ)
	  CALL InterruptX(&H21, InregsX, OutregsX)
	  ' store file attribute
          Temp# = ASC(DTAfile.FileBits)
	  Attribute = CINT(Temp#)
	  ' check findfirst error
	  IF (OutregsX.Flags AND &H1) = &H1 THEN
	     Out3 = Out3 + "E"
	  END IF
	  IF (OutregsX.Flags AND &H1) = &H0 THEN
	     IF (Attribute AND 1) = 1 THEN
		MID$(Out3, 1, 1) = "R"
	     END IF
	     IF (Attribute AND 2) = 2 THEN
		MID$(Out3, 2, 1) = "H"
	     END IF
	     IF (Attribute AND 4) = 4 THEN
		MID$(Out3, 3, 1) = "S"
	     END IF
	     IF (Attribute AND 8) = 8 THEN
		MID$(Out3, 4, 1) = "V"
	     END IF
	     IF (Attribute AND &H10) = &H10 THEN
		MID$(Out3, 5, 1) = "D"
	     END IF
	     IF (Attribute AND &H20) = &H20 THEN
		MID$(Out3, 6, 1) = "A"
	     END IF
	     IF (Attribute AND &H40) = &H40 THEN
		MID$(Out3, 7, 1) = "U"
	     END IF
	     IF (Attribute AND &H80) = &H80 THEN
		MID$(Out3, 8, 1) = "N"
	     END IF
	  END IF
	  ' restore basic dta
	  InregsX.AX = &H1A00
	  InregsX.DS = BASIC.DTA.SEG
	  InregsX.DX = BASIC.DTA.OFF
	  CALL InterruptX(&H21, InregsX, OutregsX)
       END IF
    END IF
    CALL Get.Token4
 CASE "FILEDATE"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = FalseD
    IF Last.Token = False THEN
       IF LEN(Out3) THEN
          ASCIIZ = Out3 + CHR$(0)
	  Out3 = Nul
	  ' restore directory search dta
	  InregsX.AX = &H1A00
          InregsX.DS = VARSEG(DTAfile)
          InregsX.DX = VARPTR(DTAfile)
	  CALL InterruptX(&H21, InregsX, OutregsX)
	  ' find first filename
	  InregsX.AX = &H4E00
	  InregsX.CX = &H27
          InregsX.DS = VARSEG(ASCIIZ)
          InregsX.DX = VARPTR(ASCIIZ)
	  CALL InterruptX(&H21, InregsX, OutregsX)
	  ' check findfirst error
	  IF (OutregsX.Flags AND &H1) = &H0 THEN
	     ' store file date
             Work! = ASC(MID$(DTAfile.FileDate, 2, 1))
             Work! = Work! * &H100 + ASC(MID$(DTAfile.FileDate, 1, 1))
             YearTemp! = INT(Work! / 512)
             MonthTemp! = INT((Work! AND &H1E0) / 32)
             DayTemp! = INT(Work! AND &H1F)
             YearTemp! = YearTemp! + 1980
             Out3 = RIGHT$(STR$(MonthTemp! + 100), 2) + "-" + RIGHT$(STR$(DayTemp! + 100), 2) + "-" + MID$(STR$(YearTemp!), 2)
	  END IF
	  ' restore basic dta
	  InregsX.AX = &H1A00
	  InregsX.DS = BASIC.DTA.SEG
	  InregsX.DX = BASIC.DTA.OFF
	  CALL InterruptX(&H21, InregsX, OutregsX)
       END IF
    END IF
    CALL Get.Token4
 CASE "FILETIME"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = FalseD
    IF Last.Token = False THEN
       IF LEN(Out3) THEN
          ASCIIZ = Out3 + CHR$(0)
	  Out3 = Nul
	  ' restore directory search dta
	  InregsX.AX = &H1A00
          InregsX.DS = VARSEG(DTAfile)
          InregsX.DX = VARPTR(DTAfile)
	  CALL InterruptX(&H21, InregsX, OutregsX)
	  ' find first filename
	  InregsX.AX = &H4E00
	  InregsX.CX = &H27
          InregsX.DS = VARSEG(ASCIIZ)
          InregsX.DX = VARPTR(ASCIIZ)
	  CALL InterruptX(&H21, InregsX, OutregsX)
	  ' check findfirst error
	  IF (OutregsX.Flags AND &H1) = &H0 THEN
             Work! = ASC(MID$(DTAfile.FileTime, 2, 1))
             Work! = Work! * &H100 + ASC(MID$(DTAfile.FileTime, 1, 1))
             HourTemp! = INT(Work! / 2048)
             MinuteTemp! = INT((Work! AND &H7E0) / 32)
             SecondsTemp! = INT((Work! AND &H1F) / 2)
             Out3 = RIGHT$(STR$(HourTemp! + 100), 2) + ":" + RIGHT$(STR$(MinuteTemp! + 100), 2) + ":" + RIGHT$(STR$(SecondsTemp! + 100), 2)
	  END IF
	  ' restore basic dta
	  InregsX.AX = &H1A00
	  InregsX.DS = BASIC.DTA.SEG
	  InregsX.DX = BASIC.DTA.OFF
	  CALL InterruptX(&H21, InregsX, OutregsX)
       END IF
    END IF
    CALL Get.Token4
 CASE "VLABEL"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Drive.Number% = CINT(Temp#)
    ELSE
       Drive.Number% = CINT(ASC(UCASE$(Out3)) - 64)
    END IF
    Last.Token = False
    ASCIIZ = CHR$(Drive.Number% + 64) + ":\*.*" + CHR$(0)
    Out3 = Nul
    ' restore directory search dta
    InregsX.AX = &H1A00
    InregsX.DS = VARSEG(DTAfile)
    InregsX.DX = VARPTR(DTAfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    ' find volume label
    InregsX.AX = &H4E00
    InregsX.CX = &H8
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
    ' check findfirst error
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       Out3 = DTAfile.ASCIIZfilename
    END IF
    ' restore basic dta
    InregsX.AX = &H1A00
    InregsX.DS = BASIC.DTA.SEG
    InregsX.DX = BASIC.DTA.OFF
    CALL InterruptX(&H21, InregsX, OutregsX)
    CALL Get.Token4
 CASE "VSERIAL"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Drive.Number% = CINT(Temp#)
    ELSE
       Drive.Number% = CINT(ASC(UCASE$(Out3)) - 64)
    END IF
    Last.Token = False
    Out3 = Nul
    ' get volume info
    InregsX.AX = &H6900
    InregsX.BX = Drive.Number%
    InregsX.DS = VARSEG(BPBfile)
    InregsX.DX = VARPTR(BPBfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    ' check flag error
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       ' store volume serial number
       FOR Serial.Digit = 4 TO 1 STEP -1
	  IF Serial.Digit = 2 THEN
	     Out3 = Out3 + "-"
	  END IF
          Serial.Value = ASC(MID$(BPBfile.Serial, Serial.Digit, 1))
          Out3 = Out3 + RIGHT$(HEX$(Serial.Value + &H100), 2)
       NEXT
    END IF
    CALL Get.Token4
 CASE ELSE
    ERROR 92
 END SELECT
END SUB

