REM File: Dndsic3 for Dndbbs Version v5.0a Release r2.0 Tweak t6.0a

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

' accepts an operation and performs on two values, string or numeric
SUB Arith (Token.Parsed$, Temp#, Temp2#)
 IF Last.Token THEN ' process numerics
    SELECT CASE Token.Parsed$
    CASE "-"
       Temp# = Temp# - Temp2#
    CASE "+"
       Temp# = Temp# + Temp2#
    CASE "/"
       Temp# = Temp# / Temp2#
    CASE "\" ' int div
       Temp# = CInt(Temp# / Temp2#)
    CASE "*"
       Temp# = Temp# * Temp2#
    CASE "^"
       Temp# = Temp# ^ Temp2#
    CASE "<"
       'If Temp# < Temp2# Then
       '   Temp# = -1
       'Endif
       Temp# = (Temp# < Temp2#)
    CASE ">"
       'If Temp# > Temp2# Then
       '   Temp# = -1
       'Endif
       Temp# = (Temp# > Temp2#)
    CASE "<<"
       Temp# = Temp# * 2#
    CASE ">>"
       Temp# = Temp# \ 2#
    CASE "||"
       Temp# = Temp# * 10#
    CASE "##"
       Temp# = Temp# \ 10#
    CASE "^^"
       Temp# = Temp# ^ 10#
    CASE "++"
       Temp# = Temp# + 1#
    CASE "--"
       Temp# = Temp# - 1#
    CASE "**"
       Temp# = Temp# ^ 2#
    CASE "//"
       Temp# = SQR(Temp#)
    CASE "="
       'If Temp# = Temp2# Then
       '   Temp# = -1#
       'Else
       '   Temp# = 0#
       'Endif
       Temp# = (Temp# = Temp2#)
    CASE "<="
       'If Temp# <= Temp2# Then
       '   Temp# = -1
       'Endif
       Temp# = (Temp# <= Temp2#)
    CASE ">="
       'If Temp# >= Temp2# Then
       '   Temp# = -1
       'Endif
       Temp# = (Temp# >= Temp2#)
    CASE "<>"
       'If Temp# <> Temp2# Then
       '   Temp# = -1
       'Endif
       Temp# = (Temp# <> Temp2#)
    CASE "=="
       CaseValue1 = Temp#
       CaseValue2 = Temp2#
       Last.Token = 2
    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#)
    CASE "'", "_"
       Eat$=Nul
    CASE "^=", "^+="
       Temp# = Temp# ^ Temp2#
    CASE "^-="
       Temp# = Temp# ^ (-Temp2#)
    END SELECT
    EXIT SUB
 END IF
 ' process strings
 SELECT CASE Token.Parsed$
 CASE "+", "*" ' concatenate strings
    Out3 = Out3 + Out4
 CASE "-" ' deconcatenate end of string
    IF RIGHT$(Out3, LEN(Out4)) = Out4 THEN
       Out3 = LEFT$(Out3, LEN(Out3) - LEN(Out4))
    END IF
 CASE "/" ' deconcatenate middle of string
    IF LEN(Out3) THEN
       IF LEN(Out4) THEN
          Imbedded = INSTR(Out3, Out4)
          IF Imbedded THEN
             Out3 = LEFT$(Out3, Imbedded - 1) + MID$(Out3, Imbedded + LEN(Out4))
          END IF
       END IF
    END IF
 CASE "\" ' deconcatenate all middle of string
    IF LEN(Out3) THEN
       IF LEN(Out4) THEN
          Imbedded = INSTR(Out3, Out4)
          WHILE Imbedded
             Out3 = LEFT$(Out3, Imbedded - 1) + MID$(Out3, Imbedded + LEN(Out4))
             Imbedded = INSTR(Out3, Out4)
          WEND
       END IF
    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
 CASE "=="
    CaseValueS1 = Out3
    CaseValueS2 = Out4
    Last.Token = 2
 END SELECT
END SUB

' accepts an operation and performs on one numeric variable
SUB Assignment1 (TempS$, TempS3%)
 IF TempS$ = "<<" THEN
    Assign = True
    Strng = Nul
    Variables(TempS3%) = Variables(TempS3%) * 2#
    EXIT SUB
 END IF
 IF TempS$ = ">>" THEN
    Assign = True
    Strng = Nul
    Variables(TempS3%) = Variables(TempS3%) \ 2#
    EXIT SUB
 END IF
 IF TempS$ = "||" THEN
    Assign = True
    Strng = Nul
    Variables(TempS3%) = Variables(TempS3%) * 10#
    EXIT SUB
 END IF
 IF TempS$ = "##" THEN
    Assign = True
    Strng = Nul
    Variables(TempS3%) = Variables(TempS3%) \ 10#
    EXIT SUB
 END IF
 IF TempS$ = "^^" THEN
    Assign = True
    Strng = Nul
    Variables(TempS3%) = Variables(TempS3%) ^ 10#
    EXIT SUB
 END IF
 IF TempS$ = "--" THEN
    Assign = True
    Strng = Nul
    Variables(TempS3%) = Variables(TempS3%) - 1#
    EXIT SUB
 END IF
 IF TempS$ = "++" THEN
    Assign = True
    Strng = Nul
    Variables(TempS3%) = Variables(TempS3%) + 1#
    EXIT SUB
 END IF
 IF TempS$ = "**" THEN
    Assign = True
    Strng = Nul
    Variables(TempS3%) = Variables(TempS3%) ^ 2#
    EXIT SUB
 END IF
 IF TempS$ = "//" THEN
    Assign = True
    Strng = Nul
    Variables(TempS3%) = SQR(Variables(TempS3%))
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "-=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) - Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "+=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) + Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "/=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) / Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "\=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) \ Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "*=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) * Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "^=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) ^ Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 3) = "^-=" THEN
    Out2 = MID$(TempS$, 4)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) ^ (-Temp4#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 3) = "^+=" THEN
    Out2 = MID$(TempS$, 4)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) ^ Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "%=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) MOD Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "|=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) OR Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "&=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) AND Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "~=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) XOR Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "?=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) IMP Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = ":=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Variables(TempS3%) EQV Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "#=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = NOT (Variables(TempS3%) OR Temp4#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "@=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = NOT (Variables(TempS3%) IMP Temp4#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "`=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = NOT (Variables(TempS3%) AND Temp4#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 1) = "=" THEN
    Out2 = MID$(TempS$, 2)
    Out2 = STRIM$(Out2)
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Variables(TempS3%) = Temp4#
    END IF
    EXIT SUB
 END IF
END SUB

' accepts an operation and performs on one numeric array element
SUB Assignment2 (TempS$, TempS1%, TempS2%)
 IF TempS$ = "<<" THEN
    Assign = True
    Strng = Nul
    Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) * 2#
    EXIT SUB
 END IF
 IF TempS$ = ">>" THEN
    Assign = True
    Strng = Nul
    Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) \ 2#
    EXIT SUB
 END IF
 IF TempS$ = "||" THEN
    Assign = True
    Strng = Nul
    Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) * 10#
    EXIT SUB
 END IF
 IF TempS$ = "##" THEN
    Assign = True
    Strng = Nul
    Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) \ 10#
    EXIT SUB
 END IF
 IF TempS$ = "^^" THEN
    Assign = True
    Strng = Nul
    Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) ^ 10#
    EXIT SUB
 END IF
 IF TempS$ = "--" THEN
    Assign = True
    Strng = Nul
    Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) - 1#
    EXIT SUB
 END IF
 IF TempS$ = "++" THEN
    Assign = True
    Strng = Nul
    Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) + 1#
    EXIT SUB
 END IF
 IF TempS$ = "**" THEN
    Assign = True
    Strng = Nul
    Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) ^ 2#
    EXIT SUB
 END IF
 IF TempS$ = "//" THEN
    Assign = True
    Strng = Nul
    Arrays(TempS1%, TempS2%) = SQR(Arrays(TempS1%, TempS2%))
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "-=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) - Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "+=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) + Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "/=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) / Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "\=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) \ Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "*=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) * Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "^=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) ^ Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 3) = "^-=" THEN
    Out2 = MID$(TempS$, 4)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) ^ (-Temp4#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 3) = "^+=" THEN
    Out2 = MID$(TempS$, 4)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) ^ Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "%=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) MOD Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "|=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) OR Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "&=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) AND Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "~=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) XOR Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "?=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) IMP Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = ":=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Arrays(TempS1%, TempS2%) EQV Temp4#
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "#=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = NOT (Arrays(TempS1%, TempS2%) OR Temp4#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "@=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = NOT (Arrays(TempS1%, TempS2%) IMP Temp4#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 2) = "`=" THEN
    Out2 = MID$(TempS$, 3)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = NOT (Arrays(TempS1%, TempS2%) AND Temp4#)
    END IF
    EXIT SUB
 END IF
 IF LEFT$(TempS$, 1) = "=" THEN
    Out2 = MID$(TempS$, 2)
    Out2 = STRIM$(Out2)
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Arrays(TempS1%, TempS2%) = Temp4#
    END IF
 END IF
END SUB

' gets a value and assigns to one register variable
SUB Assignment3 (TempS1$, TempS2$)
 SELECT CASE TempS2$
 CASE "AX"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       AX = Temp4#
    END IF
    EXIT SUB
 CASE "BX"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       BX = Temp4#
    END IF
    EXIT SUB
 CASE "CX"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       CX = Temp4#
    END IF
    EXIT SUB
 CASE "DX"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       DX = Temp4#
    END IF
    EXIT SUB
 CASE "BP"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       BP = Temp4#
    END IF
    EXIT SUB
 CASE "SI"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       SI = Temp4#
    END IF
    EXIT SUB
 CASE "DI"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       DI = Temp4#
    END IF
    EXIT SUB
 CASE "FL"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       FL = Temp4#
    END IF
    EXIT SUB
 CASE "DS"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       DS = Temp4#
    END IF
    EXIT SUB
 CASE "ES"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       ES = Temp4#
    END IF
    EXIT SUB
 END SELECT
END SUB

' gets a value and assigns to one error variable
SUB Assignment4 (TempS1$, TempS2$)
 SELECT CASE TempS2$
 CASE "ERR"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       ErrorValue = CINT(Temp4#)
    END IF
    EXIT SUB
 CASE "ERL"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       ErrorLine = CINT(Temp4#)
    END IF
    EXIT SUB
 CASE "ERX"
    Out2 = TempS1$
    CALL Equate(Temp4#)
    IF Last.Token = True THEN
       Assign = True
       Last.Line = CINT(Temp4#)
    END IF
    EXIT SUB
 END SELECT
END SUB

' entry to expression parser,
' each following parser is higher precedence,
' this routine called by higher precedence routines recursively.

REM precedence of operators from highest to lowest:

REM   !, +, -   Single-unary (Not, Unary plus, Unary Minus)
REM   <<, >>, --, ++, **, //, ||, ##, ^^   Dual-unary
REM   ^, ^=, ^-=, ^+=   Power
REM   *, /   Multiplication, Division
REM   \   Integer division
REM   %   Modulo
REM   +, -   Addition, Subtraction
REM   =, <, >, <>, >=, <=, ==   Relational
REM   |, &, ~, ?, :, #, @, `   Logical (or, and, xor, imp, eqv, nor, non, xan)

Rem  Returns Last.Token equal to:
Rem    0  =  string
Rem   -1  =  numeric
Rem    1  =  token IS/ISNT
Rem    2  =  token is ==
Rem    3  =  token , or ;

SUB Equate (Temp#)
 Last.Token = False
 Out3 = Nul ' reset string storage
 Out4 = Nul ' reset string concatenate storage
 Temp# = DFalse ' reset result
 Token.Index = 1 ' reset pointer to expression token
 CALL Get.Token ' read next token
 CALL Parse1(Temp#) ' entry to parse the expression
 ' parse leftover tokens
 WHILE LEN(Strng)
    CALL Parse1(X#)
    ' toss away any unwanted tokens
    IF Last.Token = 3 THEN
       IF Allow.Extra = False THEN
          Strng = "<extra token>"
          ERROR 92
       END IF
    END IF
 WEND
END SUB

' verifies next token is opening parenthesis.
SUB Get.Token3
 Strng = Nul
 Token = False
 IF MID$(Out2, Token.Index, 1) = " " THEN
    Token.Index = Token.Index + 1
 END IF
 IF Token.Index > LEN(Out2) THEN
    ERROR 138
 END IF
 ' locate parenthesis symbol
 Token.Element$ = MID$(Out2, Token.Index, 1)
 IF Token.Element$ = "(" THEN
    Token = 1 ' store token type
    Strng = Token.Element$ ' store token
    Token.Index = Token.Index + 1 ' increment pointer
    EXIT SUB
 END IF
 ERROR 138
END SUB

' verifies last token is closing parenthesis.
SUB Get.Token4
 ' locate parenthesis symbol
 IF Strng = ")" THEN
    EXIT SUB
 END IF
 ERROR 139
END SUB

' returns next token in string form,
' increments pointer to next token,
' determines token type.
SUB Get.Token
 Strng = Nul ' reset token
 Token = False ' reset token type
 IF MID$(Out2, Token.Index, 1) = " " THEN
    Token.Index = Token.Index + 1
 END IF
 CALL Get.Token2(Token.Exists)
 IF Token.Exists THEN
    EXIT SUB
 END IF
 IF Token.Index > LEN(Out2) THEN
    Strng = Nul
    Token = False
    Token.Index = Token.Index + 1
    EXIT SUB
 END IF
 ' locate expression symbol
 Token.Element$ = MID$(Out2, Token.Index, 1)
 IF INSTR(Token.List, Token.Element$) THEN
    Token = 1 ' store token type
    Strng = Token.Element$ ' store token
    Token.Index = Token.Index + 1 ' increment pointer
    EXIT SUB
 END IF
 ' locate expression is number
 Token.Element$ = MID$(Out2, Token.Index, 1)
 IF Token.Element$ >= "0" AND Token.Element$ <= "9" THEN
    ' increment token until token is other than number
    DO
       IF LEN(Token.Element$) = False THEN
	  EXIT DO
       END IF
       IF INSTR(Token.List, Token.Element$) THEN
	  EXIT DO
       END IF
       Strng = Strng + Token.Element$
       Token.Index = Token.Index + 1
       Token.Element$ = MID$(Out2, Token.Index, 1)
    LOOP
    Token = 2 ' store token type
    EXIT SUB
 END IF
 ' locate expression is number beginning with decimal
 Token.Element$ = MID$(Out2, Token.Index, 1)
 IF Token.Element$ = "." THEN
    ' increment token until token is other than number
    DO
       IF LEN(Token.Element$) = False THEN
	  EXIT DO
       END IF
       IF INSTR(Token.List, Token.Element$) THEN
	  EXIT DO
       END IF
       Strng = Strng + Token.Element$
       Token.Index = Token.Index + 1
       Token.Element$ = MID$(Out2, Token.Index, 1)
    LOOP
    Token = 2 ' store token type
    EXIT SUB
 END IF
 ' locate expression is alphabetic
 Token.Element$ = UCASE$(MID$(Out2, Token.Index, 1))
 IF Token.Element$ >= "A" AND Token.Element$ <= "Z" THEN
    ' increment token until token is other than alphabetic
    DO
       IF LEN(Token.Element$) = False THEN
	  EXIT DO
       END IF
       IF INSTR(Token.List, Token.Element$) THEN
	  EXIT DO
       END IF
       Strng = Strng + Token.Element$
       Token.Index = Token.Index + 1
       Token.Element$ = UCASE$(MID$(Out2, Token.Index, 1))
    LOOP
    Token = 3 ' store token type
    EXIT SUB
 END IF
 ' store unknown token
 Token = False
 Strng = Token.Element$
 Token.Index = Token.Index + 1
END SUB

' returns next character token in string form,
' increments pointer to next token,
' determines token type.
SUB Get.Token2 (Token.Exists)
 Token.Exists = False
 Stored.Token$ = Out2
 IF MID$(Stored.Token$, Token.Index + 1, 1) = " " THEN
    Stored.Token$ = LEFT$(Stored.Token$, Token.Index) + MID$(Stored.Token$, Token.Index + 2)
 END IF
 ' locate tokens of length 3.
 IF MID$(Stored.Token$, Token.Index, 3) = "^-=" THEN
    Next.Token$ = MID$(Stored.Token$, Token.Index, 3)
    GOSUB Remove.Spaces
    Token = 1 ' store token type
    Strng = "^-=" ' store token
    Token.Index = Token.Index + 3 ' increment pointer
    Token.Exists = True
    EXIT SUB
 END IF
 IF MID$(Stored.Token$, Token.Index, 3) = "^+=" THEN
    Next.Token$ = MID$(Stored.Token$, Token.Index, 3)
    GOSUB Remove.Spaces
    Token = 1 ' store token type
    Strng = "^+=" ' store token
    Token.Index = Token.Index + 3 ' increment pointer
    Token.Exists = True
    EXIT SUB
 END IF
 ' locate expression symbols
 Next.Token$ = MID$(Stored.Token$, Token.Index, 2)
 SELECT CASE Next.Token$
 CASE "<<"
    Strng = "<<"
    Goto Next.Token2
 CASE ">>"
    Strng = ">>"
    Goto Next.Token2
 CASE "||"
    Strng = "||"
    Goto Next.Token2
 CASE "##"
    Strng = "##"
    Goto Next.Token2
 CASE "^^"
    Strng = "^^"
    Goto Next.Token2
 CASE "--"
    Strng = "--"
    Goto Next.Token2
 CASE "++"
    Strng = "++"
    Goto Next.Token2
 CASE "**"
    Strng = "**"
    Goto Next.Token2
 CASE "//"
    Strng = "//"
    Goto Next.Token2
 CASE ">=", "=>"
    Strng = ">="
    Goto Next.Token2
 CASE "<=", "=<"
    Strng = "<="
    Goto Next.Token2
 CASE "<>", "><"
    Strng = "<>"
    Goto Next.Token2
 CASE "=="
    Strng = "=="
    Goto Next.Token2
 CASE "^="
    Strng = "^="
    Goto Next.Token2
 END SELECT
 Exit Sub
Next.Token2:
 GOSUB Remove.Spaces
 Token = 1 ' store token type
 Token.Index = Token.Index + 2 ' increment pointer
 Token.Exists = True
 EXIT SUB
Remove.Spaces:
 ' locate space
 IF MID$(Out2, Token.Index + 1, 1) = " " THEN
    Out2 = LEFT$(Out2, Token.Index) + MID$(Out2, Token.Index + 2)
 END IF
 RETURN
END SUB

' parses out single variable 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
    IF Allow.Alpha THEN
       Out3 = Strng
    ELSE
       ERROR 92
    END IF
 END SELECT
END SUB

' parses out two-letter variable tokens
SUB Parse.Alphabetic2 (Temp#)
 SELECT CASE UCASE$(Strng)
 ' user defined functions
 CASE "FN"
    CALL ParseFN (Temp#)
 CASE "FZ"
    CALL ParseFZ (Temp#)
 ' interrupt function registers
 CASE "AX" ' A register
    Temp# = AX
    Last.Token = True
 CASE "BX" ' B register
    Temp# = BX
    Last.Token = True
 CASE "CX" ' C register
    Temp# = CX
    Last.Token = True
 CASE "DX" ' D register
    Temp# = DX
    Last.Token = True
 CASE "BP" ' Base pointer
    Temp# = BP
    Last.Token = True
 CASE "SI" ' Source index
    Temp# = SI
    Last.Token = True
 CASE "DI" ' Destination index
    Temp# = DI
    Last.Token = True
 CASE "FL" ' Flags register (See: Opcodes.txt)
    Temp# = FL
    Last.Token = True
 CASE "DS" ' Data segment
    Temp# = DS
    Last.Token = True
 CASE "ES" ' Extra segment
    Temp# = ES
    Last.Token = True
 ' functions w/o parameters
 CASE "PI" ' calculate PI
    Temp# = PI#
    Last.Token = True
 CASE "PX" ' calculate PI in radians
    Temp# = PI# * 2 / 180
    Last.Token = True
 CASE "EX" ' calculate E
    Temp# = EXP(1#)
    Last.Token = True
 ' special case functions
 CASE "IS"
    CALL Get.Token
    Strng2$ = Strng
    CALL Get.Token
    CALL Parse1(Temp4#)
    Out4 = Out3
    Temp3# = CaseValue
    Out3 = CaseStrng
    Strng = Strng2$
    CALL Arith(Strng, Temp3#, Temp4#)
    ValueIs = Temp3#
    Last.Token = 1
 CASE "OR"
    CALL Get.Token
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Number# OR Temp#
    Last.Token = True
 CASE ELSE
    IF MID$(Strng, 2, 1) = "$" THEN
       SELECT CASE MID$(Out2, Token.Index, 1)
       CASE "(", "[", "{"
          Strng = "<bad string token>"
          ERROR 92
       END SELECT
       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
       IF Allow.Alpha THEN
	  Out3 = Strng
       ELSE
          Strng = "[" + Strng + "]"
	  ERROR 92
       END IF
    END IF
 END SELECT
END SUB

' parses FN(<x>)
SUB ParseFN (Temp#)
 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% <= MaxFNs THEN
    Var2$ = MID$(Out2, Token.Index)
    Out2 = Definitions(Func%)
    Out2 = STRIM$(Out2)
    ' test for nul token
    IF Out2 = Nul THEN
       Strng = "<bad FN token>"
       ERROR 153
    END IF
    ' count recursing calls
    Recurse = Recurse + 1
    IF Recurse > MaxRecurse THEN
       Strng = "<bad FN recurse>"
       ERROR 153
    END IF
    ' recursively call functions
    Token.Index = 1
    CALL Get.Token
    CALL Parse1(Temp#)
    ' test recursed FN size
    IF LEN(Var$) + LEN(Var2$) >= MaxFNstring THEN
       Var$ = Nul
       Var2$ = Nul
       Strng = "<bad FN string>"
       ERROR 153
    END IF
    ' re-concatenate equation
    Out2 = Var$ + Var2$
    Token.Index = LEN(Var$) + 1
    EXIT SUB
 END IF
 Strng = "<bad FN range>"
 ERROR 153
END SUB

' parses FZ(<x>)
SUB ParseFZ (Temp#)
 CALL Get.Token3
 CALL Get.Token
 CALL Parse1(Temp4#)
 CALL Get.Token4
 Func% = CINT(Temp4#)
 IF Func% >= 1 AND Func% <= MaxFNs THEN
    Out3 = Definitions(Func%)
    Out3 = STRIM$(Out3)
    IF Out3 = Nul THEN
       Strng = "<bad FN token>"
       ERROR 153
    END IF
    Last.Token = False
    EXIT SUB
 END IF
 Strng = "<bad FN range>"
 ERROR 153
END SUB

' parses out three-letter and greater variable tokens
SUB Parse.Alphabetic3 (Temp#)
 DIM BPBfile AS BPBtype
 DIM DTAfile AS DTAtype
 DIM Default.Net AS STRING * 16
 SELECT CASE UCASE$(Strng)
 ' special constant values
    CASE "BEEP"
        SOUND Freq,Duration
        Out3 = "Beep"
        Last.Token = False
        Call Put.Modem(Chr$(7))
    CASE "MELODY$"
        Out3 = "o3 L8 E D+ E D+ E o2 B o3 D C L2 o2 A"
        Last.Token = False
    CASE "AUTHOR$"
        Out3 = "Erik Jon Oredson"
        Last.Token = False
    CASE "EMAIL$"
        Out3 = "dndbbs@gmail.com"
        Last.Token = False
    CASE "URL$"
        Out3 = "www.filegate.net"
        Last.Token = False
    CASE "TWEAK$"
        Out3 = "t"+Tweak$
        Last.Token = False
    CASE "VERSION$"
        Out3 = "v"+Version$
        Last.Token = False
    CASE "RELEASE$"
        Out3 = "r"+Release$
        Last.Token = False
    CASE "YEARRELEASE$"
        Out3 = YearRelease$
        Last.Token = False
    CASE "PROGRAM$"
        Out3 = ProgramName$
        Last.Token = False
    CASE "PUBLISH$"
        Out3 = Publish$
        Last.Token = False
    CASE "COOKIE$"
        Out3 = "Thank you for all the cookies!"
        Last.Token = False

 ' special purpose functions
 CASE "ISNT"
    CALL Get.Token
    Strng2$ = Strng
    CALL Get.Token
    CALL Parse1(Temp4#)
    Out4 = Out3
    Temp3# = CaseValue
    Out3 = CaseStrng
    Strng = Strng2$
    CALL Arith(Strng, Temp3#, Temp4#)
    ValueIs = NOT Temp3#
    Last.Token = 1
 CASE "SCREEN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Xcoor = cint(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    Ycoor = cint(Temp#)
    Temp# = SCREEN(Xcoor, Ycoor)
    CALL Get.Token4
    Last.Token = True
 CASE "SCREENX"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Xcoor = cint(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    Ycoor = cint(Temp#)
    Temp# = SCREEN(Xcoor, Ycoor, 1)
    CALL Get.Token4
    Last.Token = True
 CASE "POINT"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Xcoor = cint(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    Ycoor = cint(Temp#)
    Temp# = POINT(Xcoor, Ycoor)
    CALL Get.Token4
    Last.Token = True
 ' functions w/o parameters
 CASE "CRLF"
    Out3=CHR$(13)+CHR$(10)
    Last.Token = False
 CASE "NUL", "NULL"
    Out3=Nul
    Last.Token = False
 CASE "FALSE", "NO"
    Temp# = DFalse
    Last.Token = True
 CASE "TRUE", "YES"
    Temp# = DTrue
    Last.Token = True
    ' added 05/10/2023
    CASE "HISTORYDEBUG"
        History.Debug = Not History.Debug
        Temp# = History.Debug
        Last.Token = True
    CASE "HISTORYLIST"
        For Var1=1 To 10
           Var$=Rtrim$(History2(Temp0,Var1))
           If Len(Var$) Then
              Strng3=Str$(Var1)+": "+Var$
              Call IO.O
           Endif
        Next
        Temp# = MaxHistory
        Last.Token = True
    ' added 03-10-2020
    CASE "MAXARRAYS"
        Temp# = MaxArrays
        Last.Token = True
    CASE "MAXHISTORY"
        Temp# = MaxHistory
        Last.Token = True
    CASE "MAXRECURSE"
        Temp# = MaxRecurse
        Last.Token = True
    CASE "MAXFNS"
        Temp# = MaxFNs
        Last.Token = True
    CASE "MAXFNLENGTH"
        Temp# = MaxFNstring
        Last.Token = True
 CASE "TIMER"
    Temp# = Timeit!
    Last.Token = True
 CASE "FREEFILE"
    File.Num% = FREEFILE
    IF File.Num% >= 1 AND File.Num% <= MaxFiles THEN
       Temp# = CDBL(File.Num%)
    ELSE
       Temp# = DFalse
    END IF
    Last.Token = True
 CASE "RND"
    Temp# = RND
    Last.Token = True
 CASE "POS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Call Get.Cursor(VarX)
    Temp# = CDBL(VarX)
    CALL Get.Token4
    Out3 = Nul
    Last.Token = True
 CASE "CSRLIN"
    Call Get.Cursor.Row(VarX)
    Temp# = CDBL(VarX)
    Last.Token = True
 CASE "ERR"
    Temp# = CDBL(ErrorValue)
    Last.Token = True
 CASE "ERL"
    Temp# = CDBL(ErrorLine)
    Last.Token = True
 CASE "ERX"
    Temp# = CDBL(Last.Error)
    Last.Token = True
 CASE "ARRAYSIZE"
    Temp# = CDBL(Array.Size)
    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" ' not or
    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" ' not imp
    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" ' not and
    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" ' not xor
    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", "NOW"
    Temp# = NOW
    Last.Token = True
 CASE "CLOCK$", "NOW$"
    Out3 = Fclock$
    Last.Token = False
 CASE "DATE$"
    Out3 = DATES$
    Last.Token = False
 CASE "TIME$"
    Out3 = Format$(Now,"hh:mm:ss")
    Last.Token = False
 CASE "QUOTE$"
    Out3 = Quote$
    Last.Token = False
 ' input functions
 CASE "INKEY$"
    If StdinEnabled Then
       Var$ = Environ$("STDINCHAR")
       If Var$<>"" Then
          VarX$=Chr$(Val(Var$))
          Strng3 = VarX$
          Out3 = VarX$
          Buffer = Nul
          Last.Token = False
       Else
          X$ = Inkey$
          Strng3 = NUL
          Out3 = Nul
          Buffer = Nul
          Last.Token = False
       Endif
    Else
       Strng3 = NUL
       Call Keyboard
       Call Get.Modem
       Out3 = Buffer
       Buffer = Nul
       Last.Token = False
    Endif
 CASE "INPUT$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Chars% = CINT(Temp#)
    IF Strng = "," THEN
       CALL Get.Token
       CALL Parse1(Temp#)
       File.Num% = CINT(Temp#)
       IF File.Num% <= False OR File.Num% > MaxFiles THEN
          ERROR 126
          EXIT SUB
       END IF
       If File.Trap(File.Num%) Then
          Break = -2
          Exit Sub
       Endif
       Out3 = INPUT$(Chars%, File.Num%)
    ELSE
       LOCATE , , Visible
       Strng3 = NUL
       VarX = False
       If Chars% < False Then
          Error 5
       Endif
       If Chars% > MaxInt Then
          Error 5
       Endif
       If StdinEnabled Then
          Var$ = Environ$("STDINCHAR")
          If Var$<>"" Then
             VarX$=Chr$(Val(Var$))
             Strng3 = VarX$
             Out3 = VarX$
             Buffer = Nul
             Last.Token = False
          Else
             X$ = Inkey$
             Strng3 = NUL
             Out3 = Nul
             Buffer = Nul
             Last.Token = False
          Endif
       Else
          Out3 = Nul
          If Chars% > False Then
             Do Until VarX = Chars%
                CALL Check.Carrier
                CALL Check.Program
                Call Check.Break
                Call Keyboard
                Call Get.Modem
                If Break Then
                   Exit Do
                Endif
                If Len(Buffer) Then
                   VarX = VarX + 1
                   Out3 = Out3 + Buffer
                   Buffer = Nul
                Endif
             Loop
          Endif
       Endif
    END IF
    CALL Get.Token4
    Last.Token = False
 ' special input loop time slice releaser
 CASE "SLICE$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Release.Time(Cint(Temp#))
    CALL Get.Token4
    Out3 = Nul
    Last.Token = False
 ' special lprint function
 CASE "LPOS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    VarX=Cint(Temp#)
    If VarX>=0 And VarX<=3 Then
       Temp#=Lpos(VarX)
    Else
       Error 161
    Endif
    CALL Get.Token4
    Out3 = Nul
    Last.Token = True
 CASE "LPRINT"
    Out3 = "LPRINT"
    Last.Token = False
 CASE "DEBUG"
    Temp# = Debug.Active
    Last.Token = True
 CASE "STDINENABLED"
    Temp# = StdinEnabled
    Last.Token = True
 ' conversion/calculation functions
 CASE "BIN$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = Nul
    IF Temp# >= False 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 >= False 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$(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 "PARSESIZE$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = ParseSize$(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 "LTRIM$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LTRIM$(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 "TRIM$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LTRIM$(Out3)
    Out3 = RTRIM$(Out3)
    CALL Get.Token4
    Last.Token = False
 CASE "STRIM$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = STRIM$(Out3)
    CALL Get.Token4
    Last.Token = False
 CASE "TTRIM$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LTRIM$(Out3)
    Out3 = RTRIM$(Out3)
    Out3 = TTRIM$(Out3, True)
    CALL Get.Token4
    Last.Token = False
 CASE "UTRIM$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LTRIM$(Out3)
    Out3 = RTRIM$(Out3)
    Out3 = TTRIM$(Out3, False)
    CALL Get.Token4
    Last.Token = False
 CASE "XTRIM$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LTRIM$(Out3)
    Out3 = RTRIM$(Out3)
    Out3 = XTRIM$(Out3, True)
    CALL Get.Token4
    Last.Token = False
 CASE "ZTRIM$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LTRIM$(Out3)
    Out3 = RTRIM$(Out3)
    Out3 = XTRIM$(Out3, False)
    CALL Get.Token4
    Last.Token = False
 CASE "STRIP$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = STRIM$(Out3)
    WHILE INSTR(Out3, CHR$(9))
       Variable = INSTR(Out3, CHR$(9))
       Out3 = LEFT$(Out3, Variable - 1) + MID$(Out3, Variable + 1)
    WEND
    WHILE INSTR(Out3, CHR$(32))
       Variable = INSTR(Out3, CHR$(32))
       Out3 = LEFT$(Out3, Variable - 1) + MID$(Out3, Variable + 1)
    WEND
    CALL Get.Token4
    Last.Token = False
 CASE "SPACE$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = SPACE$(CINT(Temp#))
    CALL Get.Token4
    Last.Token = False
 CASE "ENCRYPTX$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Call Cryptograph(Out3)
    CALL Get.Token4
    Last.Token = False
 CASE "FORMATS$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = FormatString$(Temp#)
    CALL Get.Token4
    Last.Token = False
 CASE "FORMAT$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    IF Strng = "," THEN
        CALL Get.Token
        CALL Parse1(Temp#)
        IF Last.Token THEN
            ERROR 5
        ELSE
            Out3 = Format$(Number#, Out3)
        END IF
    ELSE
        Out3 = LTRIM$(STR$(Number#))
    END IF
    CALL Get.Token4
    Last.Token = False
 CASE "MID$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Stored.String$ = Out3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp4# = Temp#
    IF Strng = "," THEN
       CALL Get.Token
       CALL Parse1(Temp#)
       Length# = Temp#
       Out3 = MID$(Stored.String$, Temp4#, Length#)
    ELSE
       Out3 = MID$(Stored.String$, Temp4#)
    END IF
    CALL Get.Token4
    Last.Token = False
 CASE "REMID$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Stored.String$ = Out3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp4# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Length# = Temp#
    Out3 = LEFT$(Stored.String$, Temp4# - 1) + MID$(Stored.String$, Temp4# + 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$) > False AND LEN(Out3) > False 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#)
    Stored.String$ = Out3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = LEFT$(Stored.String$, Temp#)
    CALL Get.Token4
    Last.Token = False
 CASE "RIGHT$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Stored.String$ = Out3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = RIGHT$(Stored.String$, 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 "ENCRYPT$"
    Prime.Key = PrimeKey
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    V$ = Out3
    Token3 = Last.Token
    IF Strng = "," THEN
       CALL Get.Token
       CALL Parse1(Temp2#)
       Prime.Key = Temp2#
    END IF
    Out3 = V$
    IF Prime.Key >= 1 AND Prime.Key <= 255 THEN
       IF Token3 THEN
          Out3 = CHR$(Temp# Xor Prime.Key)
       Else
          For V = 1 To Len(Out3)
             Mid$(Out3,V,1) = Chr$(Asc(Mid$(Out3,V,1)) Xor Prime.Key)
          Next
       Endif
    Else
       Error 5
    Endif
    CALL Get.Token4
    Last.Token = False
 CASE "ERROR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    Call Get.Token
    If Strng=";" Then
       Number# = Temp#
       If Number#>=0# And Number#<=255# Then
          Strng="<error>"
          ERROR Number#
       Else
          Error 5
       Endif
    Else
       Strng="<extra closing token>"
       Error 92
    Endif
    Last.Token = True
 CASE "ERRORX"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    Call Get.Token
    If Strng=";" Then
       Number# = Temp#
       If Number#>=0# And Number#<=255# Then
          ErrorLine = Program.Line
          If ErrorLine > Max.Lines Then
             ErrorLine = False
          Endif
          ErrorValue = Number#
          CALL Display.Error
          Out3 = Strng3
       Else
          Error 5
       Endif
    Else
       Strng="<extra closing token>"
       Error 92
    Endif
    Last.Token = False
 CASE "PROGRAM$"
    Out3 = Program.Name
    Last.Token = False
 CASE "OS$"
    Call OS.Detect
    Out3 = OperatingSystem
    Last.Token = False
 CASE "MACHINE$"
    Default.Net = Nul
    InregsX.AX = &H5E00
    InregsX.DS = VARSEG(Default.Net)
    InregsX.DX = VARPTR(Default.Net)
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' check error flag
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       ' read CH
       VarS = (OutregsX.CX AND &HFF00) / 256
       IF VarS > 0 THEN ' valid name
          ' store default server name
          Imbedded = INSTR(Default.Net, CHR$(0))
          IF Imbedded THEN
             Default.Net = LEFT$(Default.Net, Imbedded - 1)
          END IF
       END IF
    END IF
    Out3 = UCASE$(RTRIM$(Default.Net))

    ' check net path.
    If Out3 = Nul Then
       Var$ = Environ$("COMPUTERNAME")
       If Len(Var$) Then
          Out3 = Var$
       Endif
    Endif
    Last.Token = False
 ' numeric/string constant functions
 CASE "STACK"
    Temp# = STACK
    Last.Token = True
 CASE "TICKS" ' bios timer ticks divisor
    Temp# = Ticks ' 18.20648
    Last.Token = True
 CASE "PRIMEKEY"
    Temp# = PrimeKey
    Last.Token = True
 CASE "NODEX"
    Out3 = NodeX
    Last.Token = False
 CASE "PROGRAMLINE"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Var2 = CINT(Temp#)
    IF Var2>False And Var2<=Max.Lines THEN
       IF LEN(Program(Var2))<>False THEN
          Out3=Program(Var2)
       END IF
    END IF
    CALL Get.Token4
    Last.Token = False
 CASE "ACCOUNTLOGIN"
    Out3=Account.Login
    Last.Token = False
 CASE "LINEBREAK"
    Program.Line = Max.Lines
    Last.Token = True
 CASE "LINENUMBER"
    Temp# = Program.Line
    Last.Token = True
 CASE "PROGRAMRUNTYPE"
    Temp# = Program.Running
    Last.Token = True
 CASE "CATALOGPROGRAM"
    Temp# = 0
    Last.Token = True
 CASE "LIBRARYPROGRAM"
    Temp# = 1
    Last.Token = True
 CASE "GROUPPROGRAM"
    Temp# = 2
    Last.Token = True
 CASE "MAXARRAYS"
    Temp# = MaxArrays
    Last.Token = True
 CASE "MAXFILES"
    Temp# = MaxFiles
    Last.Token = True
 CASE "MAXFUNCTIONS"
    Temp# = MaxFNs
    Last.Token = True
 CASE "MAXLINES"
    Temp# = Max.Lines
    Last.Token = True
 CASE "MAXROWS"
    Temp# = Max.Row
    Last.Token = True
 CASE "PRINTER"
    Temp# = PrinterSelected
    Last.Token = True
 CASE "SCREENWIDTH"
    Temp# = ScreenWidth
    Last.Token = True
 CASE "SCREENHEIGHT"
    Temp# = ScreenHeight
    Last.Token = True
 ' keyboard shift state
 CASE "FLAGS"
    DEF SEG = &H0
    Temp# = PEEK(&H417)
    DEF SEG
    Last.Token = True
 ' break flag
 CASE "BREAK"
    DEF SEG = &H40
    X = PEEK(&H71)
    POKE &H71, &H0
    DEF SEG
    IF X = 128 THEN
       Call Set.Break
       Temp# = -1#
    END IF
    Last.Token = True
 ' other functions
 CASE "PMAP"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number% = CINT(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    Map.Type% = CINT(Temp#)
    Temp# = PMAP(Number%, Map.Type%)
    CALL Get.Token4
    Last.Token = True
 CASE "VARSEG"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Var.Seg2 = Temp#
       Temp# = VARSEG(Var.Seg2)
    ELSE
       Length = LEN(Out3)
       REDIM Var.Seg3(1 TO Length) AS INTEGER
       FOR Count = 1 TO Length
	  Var.Seg3(Count) = ASC(MID$(Out3, Count, 1))
       NEXT
       Temp# = VARSEG(Var.Seg3(1))
    END IF
    CALL Get.Token4
    Last.Token = True
 CASE "VARPTR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Var.Seg2 = Temp#
       Temp# = VARPTR(Var.Seg2)
    ELSE
       Length = LEN(Out3)
       REDIM Var.Seg3(1 TO Length) AS INTEGER
       FOR Count = 1 TO Length
	  Var.Seg3(Count) = ASC(MID$(Out3, Count, 1))
       NEXT
       Temp# = VARPTR(Var.Seg3(1))
    END IF
    CALL Get.Token4
    Last.Token = True
 CASE "VARPTR$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Var.Seg1 = Out3
    Out3 = VARPTR$(Var.Seg1)
    CALL Get.Token4
    Last.Token = False
 CASE "BUFFER"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Case.Value = Temp#
    SELECT CASE Case.Value
    CASE 1
       Temp# = Var.Seg2
       Last.Token = True
    CASE 2
       Out3 = Nul
       Length = UBOUND(Var.Seg3)
       FOR Count = 1 TO Length
	  Out3 = Out3 + CHR$(Var.Seg3(Count))
       NEXT
       Last.Token = False
    CASE 3
       Elements = INT((Array.Size + 1) / 2)
       FOR Count = 1 TO Elements
	  Graphics.Screen(Count) = Var.Seg3(Count)
       NEXT
       Last.Token = True
    END SELECT
    CALL Get.Token4
 CASE "GRPHSEG"
    IF UserProfile.Commands(139) = False Then
       Error 143
       Exit Sub
    END IF
    Elements = INT((Array.Size + 1) / 2)
    REDIM Var.Seg3(1 TO Elements) AS INTEGER
    FOR Count = 1 TO Elements
       Var.Seg3(Count) = Graphics.Screen(Count)
    NEXT
    Temp# = VARSEG(Var.Seg3(1))
    Last.Token = True
 CASE "GRPHPTR"
    IF UserProfile.Commands(140) = False Then
       Error 143
       Exit Sub
    END IF
    Elements = INT((Array.Size + 1) / 2)
    REDIM Var.Seg3(1 TO Elements) AS INTEGER
    FOR Count = 1 TO Elements
       Var.Seg3(Count) = Graphics.Screen(Count)
    NEXT
    Temp# = VARPTR(Var.Seg3(1))
    Last.Token = True
 CASE "WAV"
    IF UserProfile.Commands(141) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN ' numeric
       VarX=Cint(Temp#)
       If VarX>=1 And VarX<=12 Then
          Call Playwav(VarX,"")
       Else
          Error 5
       Endif
    Else
       Error 5
    Endif
    CALL Get.Token4
    Temp#=-1#
    Out3 = Nul
    Last.Token = True
 CASE "WAV$"
    IF UserProfile.Commands(141) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token = 0 THEN ' string
       VarX$=Out3
       Call Playwav(0,VarX$)
    Else
       Error 5
    Endif
    CALL Get.Token4
    Temp#=-1#
    Out3 = Nul
    Last.Token = True
 CASE "PLAY"
    IF UserProfile.Commands(141) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = PLAY(Temp#)
    CALL Get.Token4
    Last.Token = True
 ' more numeric functions
 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 "CBR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = Temp# ^ (1# / 3#)
    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 "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 "INP"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = INP(CINT(Temp#))
    CALL Get.Token4
    Last.Token = True
 CASE "INSTR"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Temp4# = Temp#
       CALL Get.Token
       CALL Parse1(Temp#)
       Stored.String$ = Out3
       CALL Get.Token
       CALL Parse1(Temp#)
       IF Temp4#<=0# THEN
          Error 5
       END IF
    ELSE
       Temp4# = 1#
       Stored.String$ = Out3
       CALL Get.Token
       CALL Parse1(Temp#)
    END IF
    Temp# = INSTR(Temp4#, Stored.String$, Out3)
    CALL Get.Token4
    Last.Token = True
 CASE "INSTRX"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    String1$ = Out3
    CALL Get.Token
    CALL Parse1(Temp#)
    String2$ = Out3
    Temp# = 0#
    CALL InstrSUB(Var, String1$, String2$)
    IF Var THEN
       Temp# = CDBL(Var)
    ELSE
       Temp# = 0#
    END IF
    CALL Get.Token4
    Last.Token = True
 CASE "INSTRSUB"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    String1$ = Out3
    CALL Get.Token
    CALL Parse1(Temp#)
    String2$ = Out3
    Temp# = 0#
    CALL InstrSUB(Var, String1$, String2$)
    IF Var THEN
       Temp# = -1#
    ELSE
       Temp# = 0#
    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# = CDBL(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 "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 "ISEVEN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = (Temp# MOD 2#) = 0
    CALL Get.Token4
    Last.Token = True
 CASE "ISODD"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = (Temp# MOD 2#) = 1
    CALL Get.Token4
    Last.Token = True
 CASE "ISPRM" ' calculate x is prime
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Prime# = INT(Temp#)
    Prime.Flag = True
    Start.Timer!=Timeit!
    IF Prime# > 2# THEN
       FOR Loop.Count# = 2# TO INT(SQR(Prime#))
          IF Prime# / Loop.Count# = INT(Prime# / Loop.Count#) THEN
             Prime.Flag = False
             EXIT FOR
          END IF
          IF Time.Elapsed(Start.Timer!,1!) THEN
             Start.Timer!=Timeit!
             Call CheckAlarms(0)
             CALL Check.Carrier
             CALL Check.Program
             Call Check.Break
             CALL Keyboard
             CALL Get.Modem
             If Break Then
                Prime# = 0#
                Exit For
             Endif
          END IF
       NEXT
    END IF
    Call CheckAlarms(0)
    IF Prime.Flag = False THEN
       Temp# = 0#
    ELSE
       Temp# = -1#
    END IF
    CALL Get.Token4
    Last.Token = True
 CASE "PRMIS" ' calculate which x is prime
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Prime# = INT(Temp#)
    Prime.Count# = 0#
    Start.Timer!=Timeit!
    IF Prime# > 1# THEN
       FOR Prime2# = 2# TO Prime#
          Prime.Flag = True
          FOR Loop.Count# = 2# TO INT(SQR(Prime2#))
             IF Prime2# / Loop.Count# = INT(Prime2# / Loop.Count#) THEN
                Prime.Flag = False
                EXIT FOR
             END IF
          NEXT
          IF Prime.Flag THEN
             Prime.Count# = Prime.Count# + 1#
          END IF
          IF Time.Elapsed(Start.Timer!,1!) THEN
             Start.Timer!=Timeit!
             Call CheckAlarms(0)
             CALL Check.Carrier
             CALL Check.Program
             Call Check.Break
             CALL Keyboard
             CALL Get.Modem
             If Break Then
                Prime# = 0#
                Exit For
             Endif
          END IF
       NEXT
    END IF
    Call CheckAlarms(0)
    Temp# = Prime.Count#
    CALL Get.Token4
    Last.Token = True
 CASE "PRF" ' calculate nth perfect number
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Perfect# = INT(Temp#)
    SELECT CASE Perfect#
    CASE 1
       X# = 1#
    CASE 2
       X# = 2#
    CASE 3
       X# = 4#
    CASE 4
       X# = 6#
    CASE 5
       X# = 12#
    CASE 6
       X# = 16#
    CASE 7
       X# = 18#
    'CASE 8# ' overflows beyond double precision
    '  X# = 30#
    CASE ELSE
       ERROR 130
    END SELECT
    ' equation for perfect number
    Temp# = 2 ^ X# * (2 ^ (X# + 1) - 1)
    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
    Start.Timer!=Timeit!
    IF Prime.Number# > False THEN
       Prime.Counter# = False
       DO While Lost.Carrier=False
	  Prime# = Prime# + 1
	  Prime.Flag = False
	  FOR L# = 2 TO INT(SQR(Prime#))
	     IF Prime# / L# = INT(Prime# / L#) 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
          IF Time.Elapsed(Start.Timer!,1!) THEN
             Start.Timer!=Timeit!
             Call CheckAlarms(0)
             CALL Check.Carrier
             CALL Check.Program
             Call Check.Break
             CALL Keyboard
             CALL Get.Modem
             If Break Then
                Prime# = 0#
                Exit Do
             Endif
          END IF
       LOOP
    END IF
    Call CheckAlarms(0)
    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#)
    IF Temp#<0# THEN
       Error 157
       Exit Sub
    END IF
    Temp# = SQR(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
 CASE "PEEK"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = PEEK(Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE "MKI$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = MKI$(Temp#)
    CALL Get.Token4
    Last.Token = False
 CASE "MKS$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = MKS$(Temp#)
    CALL Get.Token4
    Last.Token = False
 CASE "MKD$"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = MKD$(Temp#)
    CALL Get.Token4
    Last.Token = False
 CASE "CVI"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = CVI(Out3)
    CALL Get.Token4
    Last.Token = True
 CASE "CVS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = CVS(Out3)
    CALL Get.Token4
    Last.Token = True
 CASE "CVD"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = CVD(Out3)
    CALL Get.Token4
    Last.Token = True
 CASE "EOF"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp3#)
    Temp# = EOF(CINT(Temp3#))
    CALL Get.Token4
    Last.Token = True
 CASE "LOC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp3#)
    Temp# = LOC(CINT(Temp3#))
    CALL Get.Token4
    Last.Token = True
 CASE "LOF"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp3#)
    Temp# = LOF(CINT(Temp3#))
    CALL Get.Token4
    Last.Token = True
 CASE "EOD" ' end-of-data
    Allow.Extra = 1
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp3#)
    CALL Get.Token4

    ' search for data statement
    X = 0
    FOR V = 1 To Last.Line
       V$ = Program(V)
       V$ = STRIM$(V$)
       V$ = UCASE$(V$)
       IF LEFT$(V$, 4) = "DATA" THEN
          X = -1
          EXIT FOR
       END IF
    NEXT
    IF X = 0 THEN
       Temp# = -1
       Out3 = "-1"
       Last.Token = True
       Allow.Extra = 0
       EXIT SUB
    END IF

    V1 = DataLine
    V2 = DataNumber

    Stored.Out2$ = Out2
    Stored.Token.Index = Token.Index

    CALL Read.Data.Element(More.Data)

    DataLine = V1
    DataNumber = V2

    Out2 = Stored.Out2$
    Token.Index = Stored.Token.Index

    Temp# = CDBL(More.Data)
    Out3 = Str$(More.Data)
    Last.Token = True
    Allow.Extra = 0
 CASE "TOD" ' type-of-data
    Allow.Extra = 1
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp3#)
    CALL Get.Token4

    ' search for data statement
    X = 0
    FOR V = 1 To Last.Line
       V$ = Program(V)
       V$ = STRIM$(V$)
       V$ = UCASE$(V$)
       IF LEFT$(V$, 4) = "DATA" THEN
          X = -1
          EXIT FOR
       END IF
    NEXT
    IF X = 0 THEN
       Temp# = 0
       Out3 = "0"
       Last.Token = True
       Allow.Extra = 0
       EXIT SUB
    END IF

    V1 = DataLine
    V2 = DataNumber

    Stored.Out2$ = Out2
    Stored.Token.Index = Token.Index

    CALL Read.Data.Element(More.Data)

    DataLine = V1
    DataNumber = V2

    Out2 = Stored.Out2$
    Token.Index = Stored.Token.Index

    IF More.Data = False THEN
       IF Last.Token THEN
          Temp# = 1#
       ELSE
          Temp# = 2#
       END IF
    ELSE
       Temp# = DFalse
    END IF
    Last.Token = True
    Out3 = Str$(Temp#)
    Allow.Extra = 0
 CASE "ISDATA" ' is-type-of-data
    Allow.Extra = 1
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp3#)
    CALL Get.Token4
    SearchType = Temp3#

    ' search for data statement
    X = 0
    FOR V = 1 To Last.Line
       V$ = Program(V)
       V$ = STRIM$(V$)
       V$ = UCASE$(V$)
       IF LEFT$(V$, 4) = "DATA" THEN
          X = -1
          EXIT FOR
       END IF
    NEXT
    IF X = 0 THEN
       If SearchType = 0 Then
          Temp# = -1
          Out3 = "-1"
       Else
          Temp# = 0
          Out3 = "0"
       Endif
       Last.Token = True
       Allow.Extra = 0
       EXIT SUB
    END IF

    V1 = DataLine
    V2 = DataNumber

    Stored.Out2$ = Out2
    Stored.Token.Index = Token.Index

    CALL Read.Data.Element(More.Data)

    DataLine = V1
    DataNumber = V2

    Out2 = Stored.Out2$
    Token.Index = Stored.Token.Index

    ' store datatype of next data element
    Temp# = 0#
    Select Case SearchType
    Case 0 ' more data
       IF More.Data THEN
          Temp# = -1
       END IF
    Case 1 ' numeric
       IF More.Data = False THEN
          IF Last.Token THEN
             Temp# = -1 ' numeric
          END IF
       END IF
    Case 2 ' string
       IF More.Data = False THEN
          IF Last.Token = 0 THEN
             Temp# = -1 ' string
          END IF
       END IF
    End Select

    Last.Token = True
    Out3 = Str$(Temp#)
    Allow.Extra = 0
 ' disk functions
 CASE "CAPITALIZE"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Capitalize(Out3)
    CALL Get.Token4
    Last.Token = False
 CASE "SUFFIX"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Suffix(Temp#, Out3)
    CALL Get.Token4
    Last.Token = False
 CASE "FILEEXISTS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL GetBits(Var#)
    IF (Var# AND &H10) THen
       Temp# = 0
    ELSE
       Var$ = DIR$(Out3)
       IF Var$ = Nul THEN
          Temp# = 0
       ELSE
          Temp# = -1
       END IF
    END IF
    CALL Get.Token4
    Last.Token = True
 CASE "DIREXISTS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL GetBits(Var#)
    IF (Var# AND &H10) THen
       Temp# = -1
    ELSE
       Temp# = 0
    END IF
    CALL Get.Token4
    Last.Token = True
 CASE "DRIVEEXISTS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Var# = Temp#
    Temp# = 0#
    IF Last.Token THEN
       Var = CINT(Var#)
       IF Var >= 1 AND Var <= 26 THEN
          CALL Check.Drive(Var, Temp#)
       END IF
    ELSE
       Var = ASC(UCASE$(Out3))
       IF Var >= 65 AND Var <= 90 THEN
          Var = Var - 64
          CALL Check.Drive(Var, Temp#)
       END IF
    END IF
    CALL Get.Token4
    Last.Token = True
 CASE "DIR$"
    IF UserProfile.Commands(142) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF LEN(Out3) THEN
       Out3 = DIR$(Out3)
    ELSE
       Out3 = DIR$
    END IF
    CALL Get.Token4
    Last.Token = False
 CASE "CREATE$"
    IF UserProfile.Commands(142) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Out3 = UCASE$(Out3)
    CALL Get.Token4
    Last.Token = False
    Program.Data$ = Out3
    Select Case Program.Running
    Case LibraryProgram 
       Eat$ = Nul
    Case GroupProgram 
       IF Instr(Out3, ":") Then
          Error 142
          EXIT SUB
       END IF
    Case CatalogProgram
       Call Strip.Program(Out3,Var2)
       If Var2 Then
          Error 142
          EXIT SUB
       END IF
       IF LEN(Out3) > 8 THEN
          ERROR 52
          EXIT SUB
       END IF
       Program.Data$ = Current.Directory + Account + ".SIC\" + Out3 + ".DAT"
    End Select
    CLOSE #TempFile2
    OPEN Program.Data$ FOR OUTPUT AS #TempFile2
    PRINT #TempFile2, "";
    CLOSE #TempFile2
 CASE "CURDIR$"
    IF UserProfile.Commands(143) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Var$ = CHR$(Temp# + 64) + ":"
    ELSE
       Var$ = Out3
    END IF
    IF LEN(Var$) THEN
       Out3 = CURDIR$(Var$)
    ELSE
       Out3 = CURDIR$
    END IF
    CALL Get.Token4
    Last.Token = False
 CASE "DRIVE$"
    IF UserProfile.Commands(143) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    IF Last.Token THEN
       Var$ = CHR$(Temp# + 64) + ":"
    ELSE
       Var$ = Out3
    END IF
    IF LEN(Var$) THEN
       Out3 = LEFT$(CURDIR$(Var$),3)
    ELSE
       Out3 = LEFT$(CURDIR$,3)
    END IF
    CALL Get.Token4
    Last.Token = False
 CASE "DRV"
    IF UserProfile.Commands(146) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    IF Last.Token THEN
       Drive.Number% = CINT(Temp#)
    ELSE
       Drive.Number% = CINT(ASC(UCASE$(Out3)) - 64)
    END IF
    Last.Token = True
    Temp# = 0#
    Var1$ = CHR$(Drive.Number% + 64)
    CALL FreeSpace(1, Var1$)
    Temp# = Val(Var1$)
 CASE "TDRV"
    IF UserProfile.Commands(146) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    IF Last.Token THEN
       Drive.Number% = CINT(Temp#)
    ELSE
       Drive.Number% = CINT(ASC(UCASE$(Out3)) - 64)
    END IF
    Last.Token = True
    Temp# = 0#
    Var1$ = CHR$(Drive.Number% + 64)
    CALL FreeSpace(2, Var1$)
    Temp# = Val(Var1$)
 CASE "FILESIZE"
    IF UserProfile.Commands(146) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    Temp# = DFalse
    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
             CALL GetFileSize2(Out3, Temp#)
             If Temp#=0# Then
                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))
             Endif
          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
 CASE "FILEATTR"
    IF UserProfile.Commands(147) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    Temp# = DFalse
    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 = &H37
          InregsX.DS = VARSEG(ASCIIZ)
          InregsX.DX = VARPTR(ASCIIZ)
	  CALL InterruptX(&H21, InregsX, OutregsX)
	  ' check findfirst error
          Temp# = 0#
	  IF (OutregsX.Flags AND &H1) = &H0 THEN
             Temp# = ASC(DTAfile.FileAttr)
	  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
 CASE "FILEBITS"
    IF UserProfile.Commands(147) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = DFalse
    IF Last.Token = False THEN
       IF LEN(Out3) THEN
          Last.Token = False
          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 = &H37
          InregsX.DS = VARSEG(ASCIIZ)
          InregsX.DX = VARPTR(ASCIIZ)
	  CALL InterruptX(&H21, InregsX, OutregsX)
	  ' check findfirst error
          Temp# = 0#
	  IF (OutregsX.Flags AND &H1) = &H0 THEN
             Temp# = ASC(DTAfile.FileAttr)
	  END IF
	  ' restore basic dta
	  InregsX.AX = &H1A00
	  InregsX.DS = BASIC.DTA.SEG
	  InregsX.DX = BASIC.DTA.OFF
	  CALL InterruptX(&H21, InregsX, OutregsX)
          ' store filebits
          Out3 = SPACE$(5)
          IF (Temp# AND &H20) = &H20 THEN
             MID$(Out3, 1, 1) = "A" ' archive
          END IF
          IF (Temp# AND &H10) = &H10 THEN
             MID$(Out3, 2, 1) = "D" ' directory
          END IF
          IF (Temp# AND &H4) = &H4 THEN
             MID$(Out3, 3, 1) = "S" ' system
          END IF
          IF (Temp# AND &H2) = &H2 THEN
             MID$(Out3, 4, 1) = "H" ' hidden
          END IF
          IF (Temp# AND &H1) = &H1 THEN
             MID$(Out3, 5, 1) = "R" ' read-only
          END IF
          Last.Token = False
       END IF
    END IF
    CALL Get.Token4
 CASE "FILEDATE"
    IF UserProfile.Commands(148) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    Temp# = DFalse
    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))
             WorkYear! = INT(Work! / 512)
             WorkMonth! = INT((Work! AND &H1E0) / 32)
             WorkDay! = INT(Work! AND &H1F)
             WorkYear! = WorkYear! + 1980
             Out3 = RIGHT$(STR$(WorkMonth! + 100), 2) + "-" + RIGHT$(STR$(WorkDay! + 100), 2) + "-" + MID$(STR$(WorkYear!), 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
 CASE "FILETIME"
    IF UserProfile.Commands(149) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    Temp# = DFalse
    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))
             WorkHour! = INT(Work! / 2048)
             WorkMinute! = INT((Work! AND &H7E0) / 32)
             WorkSeconds! = INT((Work! AND &H1F) / 2)
             Out3 = RIGHT$(STR$(WorkHour! + 100), 2) + ":" + RIGHT$(STR$(WorkMinute! + 100), 2) + ":" + RIGHT$(STR$(WorkSeconds! + 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
 CASE "VLABEL"
    IF UserProfile.Commands(150) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    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
       Out3 = LEFT$(Out3, INSTR(Out3, CHR$(0)) - 1)
       Out3 = RTRIM$(Out3)
    END IF
    ' restore basic dta
    InregsX.AX = &H1A00
    InregsX.DS = BASIC.DTA.SEG
    InregsX.DX = BASIC.DTA.OFF
    CALL InterruptX(&H21, InregsX, OutregsX)
 CASE "VSERIAL"
    IF UserProfile.Commands(151) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    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) = &H1 THEN
       Error 162
    END IF
    ' 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
          Digit = ASC(MID$(BPBfile.Serial, Serial.Digit, 1))
          Out3 = Out3 + RIGHT$(HEX$(Digit + &H100), 2)
       NEXT
    END IF
 CASE "VTYPE"
    IF UserProfile.Commands(151) = False Then
       Error 143
       Exit Sub
    END IF
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    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) = &H1 THEN
       Error 162
    END IF
    ' check flag error
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       ' store volume file system type
       Out3 = RTRIM$(BPBfile.System)
    END IF
 ' bubble sort variable array ascending
 CASE "SORT"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    Var = CINT(Temp#)
    IF Var>=1 AND Var<=26 THEN
       FOR Var1=1 TO MaxArrays
          FOR Var2=Var1+1 TO MaxArrays
             IF Arrays(Var, Var1)>Arrays(Var, Var2) Then
                SWAP Arrays(Var, Var1), Arrays(Var, Var2)
             END IF
          NEXT
       NEXT
       Last.Token = True
       EXIT SUB
    END IF
    ERROR 2
 ' bubble sort variable array descending
 CASE "ZSORT"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    Var = CINT(Temp#)
    IF Var>=1 AND Var<=26 THEN
       FOR Var1=1 TO MaxArrays
          FOR Var2=Var1+1 TO MaxArrays
             IF Arrays(Var, Var1)<Arrays(Var, Var2) Then
                SWAP Arrays(Var, Var1), Arrays(Var, Var2)
             END IF
          NEXT
       NEXT
       Last.Token = True
       EXIT SUB
    END IF
    ERROR 2
 ' bubble sort variables
 CASE "ZSORT2"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    Var = CINT(Temp#)
    IF Var=False THEN ' ascending
       FOR Var1=1 TO 26
          FOR Var2=Var1+1 TO 26
             IF Variables(Var1)>Variables(Var2) Then
                SWAP Variables(Var1), Variables(Var2)
             END IF
          NEXT
       NEXT
    ELSE ' descending
       FOR Var1=1 TO 26
          FOR Var2=Var1+1 TO 26
             IF Variables(Var1)<Variables(Var2) Then
                SWAP Variables(Var1), Variables(Var2)
             END IF
          NEXT
       NEXT
    END IF
    Temp#=DTrue
    Last.Token = True
    EXIT SUB
 ' fill array with random values
 CASE "RNDARRAY"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Var = CINT(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    VarX# = Temp#
    CALL Get.Token4
    IF Var>=1 AND Var<=26 THEN
       FOR Var2=1 To MaxArrays
          Arrays(Var, Var2) = INT(RND*VarX#+1)
       NEXT
       Temp# = Var
       Last.Token = True
       EXIT SUB
    END IF
    ERROR 2
 ' fill variables with random values
 CASE "RNDARRAY2"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    CALL Get.Token4
    FOR Var1=1 To 26
       Variables(Var1) = INT(RND*Temp#+1)
    NEXT
    Temp#=DTrue
    Last.Token = True
    EXIT SUB
 ' get array value
 CASE "ARRAY"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Var1 = CINT(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    Var2 = CINT(Temp#)
    CALL Get.Token4
    IF Var1>=1 AND Var1<=26 THEN
       IF Var2>=1 AND Var2<=MaxArrays THEN
          Temp# = Arrays(Var1, Var2)
          Last.Token = True
          EXIT SUB
       END IF
    END IF
    ERROR 2
 ' set array value
 CASE "SETARRAY"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Var1 = CINT(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    Var2 = CINT(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    VarX# = Temp#
    CALL Get.Token4
    IF Var1>=1 AND Var1<=26 THEN
       IF Var2>=1 AND Var2<=MaxArrays THEN
          Arrays(Var1, Var2) = VarX#
          Temp# = Var1
          Last.Token = True
          EXIT SUB
       END IF
    END IF
    ERROR 2
 ' get variable value
 CASE "VARIABLE"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Var1 = CINT(Temp#)
    CALL Get.Token4
    IF Var1>=1 AND Var1<=26 THEN
       Temp# = Variables(Var1)
       Last.Token = True
       EXIT SUB
    END IF
    ERROR 2
 ' set variable value
 CASE "SETVARIABLE"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Var1 = CINT(Temp#)
    CALL Get.Token
    CALL Parse1(Temp#)
    VarX# = Temp#
    CALL Get.Token4
    IF Var1>=1 AND Var1<=26 THEN
       Variables(Var1) = VarX#
       Temp# = Var1
       Last.Token = True
       EXIT SUB
    END IF
    ERROR 2

 REM Remaining derived nonintrinsic trigonometric functions..

 'Secant
 CASE "SEC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = 1 / COS(Temp#)
    CALL Get.Token4
    Last.Token = True
 'Secant
 CASE "SECANT"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = 1# / COS(Temp#)
    CALL Get.Token4
    Last.Token = True
 'Cosecant
 CASE "COSEC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = 1# / SIN(Temp#)
    CALL Get.Token4
    Last.Token = True
 'Cotangent
 CASE "COTAN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = 1# / TAN(Temp#)
    CALL Get.Token4
    Last.Token = True
 'Inverse Cosine
 CASE "ARCCOS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = ATN(-Temp# / SQR(-Temp# * Temp# + 1)) + 2 * ATN(1)
    CALL Get.Token4
    Last.Token = True
 'Inverse Cosecant
 CASE "ARCCOSEC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = ATN(Temp# / SQR(Temp# * Temp# - 1)) + (SGN(Temp#) - 1) * (2 * ATN(1))
    CALL Get.Token4
    Last.Token = True
 'Inverse Cotangent
 CASE "ARCCOTAN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = ATN(Temp#) + 2 * ATN(1)
    CALL Get.Token4
    Last.Token = True
 'Inverse Secant
 CASE "ARCSEC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = ATN(Temp# / SQR(Temp# * Temp# - 1)) + SGN((Temp#) - 1) * (2 * ATN(1))
    CALL Get.Token4
    Last.Token = True
 'Inverse Sine
 CASE "ARCSIN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = ATN(Temp# / SQR(-Temp# * Temp# + 1))
    CALL Get.Token4
    Last.Token = True
 'Inverse Hyperbolic Cosine
 CASE "HARCCOS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = LOG(Temp# + SQR(Temp# * Temp# - 1))
    CALL Get.Token4
    Last.Token = True
 'Inverse Hyperbolic Cosecant
 CASE "HARCCOSEC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = LOG((SGN(Temp#) * SQR(Temp# * Temp# + 1) + 1) / Temp#)
    CALL Get.Token4
    Last.Token = True
 'Inverse Hyperbolic Cotangent
 CASE "HARCCOTAN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = LOG((Temp# + 1) / (Temp# - 1)) / 2
    CALL Get.Token4
    Last.Token = True
 'Inverse Hyperbolic Secant
 CASE "HARCSEC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = LOG((SQR(-Temp# * Temp# + 1) + 1) / Temp#)
    CALL Get.Token4
    Last.Token = True
 'Inverse Hyperbolic Sine
 CASE "HARCSIN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = LOG(Temp# + SQR(Temp# * Temp# + 1))
    CALL Get.Token4
    Last.Token = True
 'Inverse Hyperbolic Tangent
 CASE "HARCTAN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = LOG((1 + Temp#) / (1 - Temp#)) / 2
    CALL Get.Token4
    Last.Token = True
 'Hyperbolic Cosine
 CASE "HCOS"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = (EXP(Temp#) + EXP(-Temp#)) / 2
    CALL Get.Token4
    Last.Token = True
 'Hyperbolic Cosecant
 CASE "HCOSEC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = 2 / (EXP(Temp#) - EXP(-Temp#))
    CALL Get.Token4
    Last.Token = True
 'Hyperbolic Cotangent
 CASE "HCOTAN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = (EXP(Temp#) + EXP(-Temp#)) / (EXP(Temp#) - EXP(-Temp#))
    CALL Get.Token4
    Last.Token = True
 'Hyperbolic Secant
 CASE "HSEC"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = 2 / (EXP(Temp#) + EXP(-Temp#))
    CALL Get.Token4
    Last.Token = True
 'Hyperbolic Sine
 CASE "HSIN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = (EXP(Temp#) - EXP(-Temp#)) / 2
    CALL Get.Token4
    Last.Token = True
 'Hyperbolic Tangent
 CASE "HTAN"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = (EXP(Temp#) - EXP(-Temp#)) / (EXP(Temp#) + EXP(-Temp#))
    CALL Get.Token4
    Last.Token = True
 'Convert Degrees to Radians
 CASE "DEGTORAD"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = (PI# / 180) * Temp#
    CALL Get.Token4
    Last.Token = True
 'Convert Radians to Degrees
 CASE "RADTODEG"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = (180 / PI#) * Temp#
    CALL Get.Token4
    Last.Token = True
 ' Hypotenuse
 CASE "HYPOT"
    CALL Get.Token3
    CALL Get.Token
    CALL Parse1(Temp#)
    Number# = Temp#
    CALL Get.Token
    CALL Parse1(Temp#)
    Temp# = SQR(Number# * Number# + Temp# * Temp#)
    CALL Get.Token4
    Last.Token = True
 CASE ELSE
    IF Allow.Alpha THEN
       Out3 = Strng
    ELSE
       ERROR 92
    END IF
 END SELECT
END SUB

' determine operating system
SUB OS.Detect
 If Windows.Detected Then
    InregsX.AX=&H160A
    CALL InterruptX(&H2F,InregsX,OutregsX)
    Major=(OutregsX.BX AND &HFF00)/256
    Minor=(OutregsX.BX AND &HFF)
    If Major>=4 Then
       If Minor>10 Then
          OperatingSystem = "Windows ME"
       Else
          If Minor=10 Then
             OperatingSystem = "Windows 98"
          Else
             If Minor=3 Then
                OperatingSystem = "Windows 95B"
             Else
                OperatingSystem = "Windows 95"
             Endif
          Endif
       Endif
    Else
       If Major = 0 And Minor = 0 Then
          OperatingSystem = "WinX"
       Else
          OperatingSystem = "Windows v"+Ltrim$(Str$(Major))+"."+Ltrim$(Str$(Minor))
       Endif
    Endif
    Exit Sub
 Endif
 InregsX.AX=&H3001
 CALL InterruptX(&H21,InregsX,OutregsX)
 Major=(OutregsX.AX AND &HFF)
 Minor=(OutregsX.AX AND &HFF00)/256
 If Major=10 Then
    OperatingSystem = "OS/2 v1.0"
 Else
    If Major=20 Then
       If Minor=40 Then
          OperatingSystem = "OS/2 v4.0"
       Else
          If Minor=30 Then
             OperatingSystem = "OS/2 v3.0"
          Else
             OperatingSystem = "OS/2 v2.0"
          Endif
       Endif
    Else
       OperatingSystem = "DOS v"+Ltrim$(Str$(Major))+"."+Ltrim$(Str$(Minor))
    Endif
 Endif
END SUB

' process a number
SUB Parse.Numeric (Temp#)
 SELECT CASE RIGHT$(UCASE$(Strng), 1) ' compare character after number
 CASE "Q" ' perfect number suffix
    FOR Digit = 1 TO LEN(Strng) - 1
       OutX$ = UCASE$(MID$(Strng, Digit, 1))
       SELECT CASE OutX$
       CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
          Eat$ = Nul
       CASE ELSE
          ERROR 130
       END SELECT
    NEXT
    Temp# = CDBL(VAL(LEFT$(Strng, LEN(Strng) - 1))+.5)
    Perfect# = 0#
    Perfect.Number# = INT(Temp#)
    'IF ABS(Perfect.Number#) > 7# THEN
    '   ERROR 130
    'END IF
    'IF Perfect.Number# > FalseD THEN
       SELECT CASE cint(Perfect.Number#)
       CASE 1
          X# = 1#
       CASE 2
          X# = 2#
       CASE 3
          X# = 4#
       CASE 4
          X# = 6#
       CASE 5
          X# = 12#
       CASE 6
          X# = 16#
       CASE 7
          X# = 18#
       'CASE 8# ' overflows beyond double precision
       '  X# = 30#
       CASE ELSE
          ERROR 130
       END SELECT
       ' equation for perfect number
       Perfect# = 2 ^ X# * (2 ^ (X# + 1) - 1)
    'END IF
    Temp# = Perfect#
 CASE "P" ' prime number suffix
    FOR Digit = 1 TO LEN(Strng) - 1
       OutX$ = UCASE$(MID$(Strng, Digit, 1))
       SELECT CASE OutX$
       CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
          Eat$ = Nul
       CASE ELSE
          ERROR 130
       END SELECT
    NEXT
    Temp# = CDBL(VAL(LEFT$(Strng, LEN(Strng) - 1)))
    Prime.Number# = INT(Temp#)
    Prime# = 1
    Start.Timer!=Timeit!
    IF Prime.Number# > False THEN
       Prime.Counter# = False
       DO
	  Prime# = Prime# + 1
	  Prime.Flag = False
          FOR Factor# = 2 TO INT(SQR(Prime#))
             IF Prime# / Factor# = INT(Prime# / Factor#) 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
          IF Time.Elapsed(Start.Timer!,1!) THEN
             Start.Timer!=Timeit!
             Call CheckAlarms(0)
             CALL Check.Carrier
             CALL Check.Program
             Call Check.Break
             CALL Keyboard
             CALL Get.Modem
             If Break Then
                Prime# = 0#
                Exit Do
             Endif
          END IF
       LOOP
    END IF
    Call CheckAlarms(0)
    Temp# = Prime#
 CASE "F" ' factorial
    Temp2 = False
    Temp3 = LEN(Strng)
    DO
       IF UCASE$(MID$(Strng, Temp3, 1)) <> "F" THEN
	  EXIT DO
       END IF
       Temp2 = Temp2 + 1
       Temp3 = Temp3 - 1
    LOOP
    FOR Decimal.Digit = 1 TO Temp3
       OutX$ = UCASE$(MID$(Strng, Decimal.Digit, 1))
       SELECT CASE OutX$
       CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
          Eat$ = Nul
       CASE ELSE
          ERROR 132
       END SELECT
    NEXT
    Temp4# = VAL(LEFT$(Strng, Temp3))
    FOR Number = 1 TO Temp2 ' multiple f allowed
       Temp# = 1#
       FOR Factorial# = 2# TO Temp4#
	  Temp# = Temp# * Factorial#
       NEXT
       Temp4# = Temp#
    NEXT
 CASE "H" ' hexidecimal suffix
    Temp# = False
    Hex.Power = False
    FOR Hex.Digit = LEN(Strng) - 1 TO 1 STEP -1
       OutX$ = UCASE$(MID$(Strng, Hex.Digit, 1))
       SELECT CASE OutX$
       CASE "0" TO "9", "A" TO "F"
          Hex.Value = VAL("&H" + OutX$)
          Temp# = Temp# + Hex.Value * 16 ^ Hex.Power
          Hex.Power = Hex.Power + 1
       CASE ELSE
          ERROR 127
       END SELECT
    NEXT
 CASE "O" ' octal suffix
    Temp# = False
    Octal.Power = False
    FOR Octal.Digit = LEN(Strng) - 1 TO 1 STEP -1
       OutX$ = MID$(Strng, Octal.Digit, 1)
       IF OutX$ >= "0" AND OutX$ <= "7" THEN
          Octal.Value = VAL(OutX$)
          Temp# = Temp# + Octal.Value * 8 ^ Octal.Power
          Octal.Power = Octal.Power + 1
       ELSE
          ERROR 128
       END IF
    NEXT
 CASE "B" ' binary suffix
    Temp# = False
    Binary.Power = False
    FOR Binary.Digit = LEN(Strng) - 1 TO 1 STEP -1
       OutX$ = MID$(Strng, Binary.Digit, 1)
       SELECT CASE OutX$
       CASE "0"
          Eat$ = Nul
       CASE "1"
	  Temp# = Temp# + 2 ^ Binary.Power
       CASE ELSE
          ERROR 129
       END SELECT
       Binary.Power = Binary.Power + 1
    NEXT
 CASE "0" TO "9", "D", "E" ' decimal/exponent suffix
    Decimal = False
    Exponent = False
    Number$ = Strng
 Start.Token:
    FOR Decimal.Digit = 1 TO LEN(Number$)
       OutX$ = UCASE$(MID$(Number$, Decimal.Digit, 1))
       SELECT CASE OutX$
       CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-", "+"
          Eat$ = Nul
       CASE "D", "E"
          IF Exponent THEN
             ERROR 148
          END IF
          Exponent = True
          Exponent$ = UCASE$(RIGHT$(Number$, 1))
          IF Exponent$ = "D" OR Exponent$ = "E" THEN
             IF MID$(Out2, Token.Index, 1) = " " THEN
                Token.Index = Token.Index + 1
             END IF
             ' concatenate pre-parsed tokens into exponent
             Unary$ = MID$(Out2, Token.Index, 1)
             IF Unary$ = "+" OR Unary$ = "-" THEN
                Var$ = LEFT$(Out2, Token.Index - 1)
                CALL Get.Token
                Out2 = Var$ + Strng + MID$(Out2, Token.Index)
                CALL Get.Token
                IF Strng = Nul THEN
                   ERROR 149
                END IF
                Number$ = Number$ + Unary$ + Strng
                IF Strng = "-" OR Strng = "+" THEN
                   ERROR 150
                END IF
                IF INSTR(Strng, ".") THEN
                   ERROR 151
                END IF
                ' remove pre-parsed leading unary signs 
                DO
                   IF LEFT$(Number$, 1) = "-" THEN
                      Number$ = MID$(Number$, 2)
                   ELSE
                      IF LEFT$(Number$, 1) = "+" THEN
                         Number$ = MID$(Number$, 2)
                      ELSE
                         EXIT DO
                      END IF
                   END IF
                LOOP
                Decimal = False
                Exponent = False
                GOTO Start.Token
             ELSE
                ERROR 150
             END IF
          END IF
       CASE "."
          IF Exponent THEN
             ERROR 151
          END IF
          IF Decimal THEN
             ERROR 151
          END IF
          Decimal = True
       CASE ELSE
          ERROR 130
       END SELECT
    NEXT
    Temp# = CDBL(VAL(Number$))
 CASE ELSE
    ERROR 131
 END SELECT
 CALL Get.Token
 Last.Token = True
END SUB

' parse string in quotes
SUB Parse.Quoted
 Out3 = Nul
 DO UNTIL MID$(Out2, Token.Index, 1) = Quote$
    Out3 = Out3 + MID$(Out2, Token.Index, 1)
    Token.Index = Token.Index + 1
    IF Token.Index > LEN(Out2) THEN
       ERROR 133
       EXIT DO
    END IF
 LOOP
 CALL Get.Token
 CALL Get.Token
 Last.Token = False
END SUB

' logical parser
SUB Parse1 (Temp#)
 CALL Parse2(Temp#) ' get next operator precedence
 Token.Parsed$ = Strng ' store token
 ' process token
 DO
    SELECT CASE Token.Parsed$
    CASE "|", "&", "~", "?", ":", "#", "@", "`", "'", "_"
       Eat$ = Nul
    CASE ELSE
       EXIT DO
    END SELECT
    Token.Stored$ = Out3
    CALL Get.Token ' read next token
    CALL Parse2(Temp2#) ' get next operator
    Out4 = Out3 ' reset current string
    Out3 = Token.Stored$ ' restore previous string
    CALL Arith(Token.Parsed$, Temp#, Temp2#) ' calculate expression
    Token.Parsed$ = Strng ' store next token
 LOOP
END SUB

' relational parser
SUB Parse2 (Temp#)
 CALL Parse3(Temp#) ' get next operator precedence
 Token.Parsed$ = Strng ' store token
 ' process token
 DO
    SELECT CASE Token.Parsed$
    CASE "<", ">", "=", ">=", "<=", "<>", "=="
       Eat$ = Nul
    CASE ELSE
       EXIT DO
    END SELECT
    Token.Stored$ = Out3 ' store current string
    CALL Get.Token ' read next token
    CALL Parse3(Temp2#) ' get next operator
    Out4 = Out3 ' reset current string
    Out3 = Token.Stored$ ' restore previous string
    CALL Arith(Token.Parsed$, Temp#, Temp2#) ' calculate expression
    Token.Parsed$ = Strng ' store next token
 LOOP
END SUB

' addition/subtraction parser
SUB Parse3 (Temp#)
 CALL Parse4(Temp#) ' get next operator precedence
 Token.Parsed$ = Strng ' store token
 ' process token
 DO
    SELECT CASE Token.Parsed$
    CASE "+", "-"
       Eat$ = Nul
    CASE ELSE
       EXIT DO
    END SELECT
    Token.Stored$ = Out3
    CALL Get.Token ' read next token
    CALL Parse4(Temp2#) ' get next operator
    Out4 = Out3
    Out3 = Token.Stored$
    CALL Arith(Token.Parsed$, Temp#, Temp2#) ' calculate expression
    Token.Parsed$ = Strng ' store next token
 LOOP
END SUB

' modulo parser
SUB Parse4 (Temp#)
 CALL Parse5(Temp#) ' get next operator precedence
 Token.Parsed$ = Strng ' store token
 ' process token
 DO
    SELECT CASE Token.Parsed$
    CASE "%"
       Eat$ = Nul
    CASE ELSE
       EXIT DO
    END SELECT
    Token.Stored$ = Out3
    CALL Get.Token ' read next token
    CALL Parse5(Temp2#) ' get next operator
    Out4 = Out3
    Out3 = Token.Stored$
    CALL Arith(Token.Parsed$, Temp#, Temp2#) ' calculate expression
    Token.Parsed$ = Strng ' store next token
 LOOP
END SUB

' integer division parser
SUB Parse5 (Temp#)
 CALL Parse6(Temp#) ' get next operator precedence
 Token.Parsed$ = Strng ' store token
 ' process token
 DO
    SELECT CASE Token.Parsed$
    CASE "\"
       Eat$ = Nul
    CASE ELSE
       EXIT DO
    END SELECT
    Token.Stored$ = Out3
    CALL Get.Token ' read next token
    CALL Parse6(Temp2#) ' get next operator
    Out4 = Out3
    Out3 = Token.Stored$
    CALL Arith(Token.Parsed$, Temp#, Temp2#) ' calculate expression
    Token.Parsed$ = Strng ' store next token
 LOOP
END SUB

' multiplication/division parser
SUB Parse6 (Temp#)
 CALL Parse7(Temp#) ' get next operator precedence
 Token.Parsed$ = Strng ' store token
 ' process token
 DO
    SELECT CASE Token.Parsed$
    CASE "*", "/"
       Eat$ = Nul
    CASE ELSE
       EXIT DO
    END SELECT
    Token.Stored$ = Out3
    CALL Get.Token ' read next token
    CALL Parse7(Temp2#) ' get next operator
    Out4 = Out3
    Out3 = Token.Stored$
    CALL Arith(Token.Parsed$, Temp#, Temp2#) ' calculate expression
    Token.Parsed$ = Strng ' store next token
 LOOP
END SUB

' power parser
SUB Parse7 (Temp#)
 CALL Parse7a(Temp#) ' get next operator precedence
 Token.Parsed$ = Strng ' store token
 ' process token
 DO
    SELECT CASE Token.Parsed$
    CASE "^", "^=", "^-=", "^+="
       Eat$ = Nul
    CASE ELSE
       EXIT DO
    END SELECT
    Token.Stored$ = Out3
    CALL Get.Token ' read next token
    CALL Parse7a(Temp2#) ' get next operator
    Out4 = Out3
    Out3 = Token.Stored$
    CALL Arith(Token.Parsed$, Temp#, Temp2#) ' calculate expression
    Token.Parsed$ = Strng ' store next token
 LOOP
END SUB

 ' dual-unary parser
SUB Parse7a (Temp#)
 CALL Parse8(Temp#) ' get next operator precedence
 Token.Parsed$ = Strng ' store token
 ' process token
 DO
    SELECT CASE Token.Parsed$
    CASE "<<", ">>", "--", "++", "**", "//", "||", "##", "^^"
       Eat$ = Nul
    CASE ELSE
       EXIT DO
    END SELECT
    Token.Stored$ = Out3 ' store current token
    CALL Get.Token ' read next token
    Out4 = Out3 ' reset current string
    Out3 = Token.Stored$ ' restore previous string
    CALL Arith(Token.Parsed$, Temp#, Temp2#) ' calculate expression
    Token.Parsed$ = Strng ' store next token
 LOOP
END SUB

' not/unary plus/unary negative parser
SUB Parse8 (Temp#)
 Token.Negate$ = Nul ' reset token storage
 Token.Parsed$ = Strng ' store token
 ' process token
 DO
    SELECT CASE Token.Parsed$
    CASE "!", "-", "+", "--", "++"
       Eat$ = Nul
    CASE ELSE
       EXIT DO
    END SELECT
    CALL Get.Token ' read next token
    Token.Negate$ = Token.Negate$ + Token.Parsed$
    Token.Parsed$ = Strng ' store token
 LOOP
 CALL Parse9(Temp#) ' get next operator
 ' process the combined operators in reverse
 FOR Token.Type = LEN(Token.Negate$) TO 1 STEP -1
    SELECT CASE MID$(Token.Negate$, Token.Type, 1) ' get previous token
    CASE "+"
       Eat$ = Nul
    CASE "-"
       Temp# = -Temp# ' perform negate
    CASE "!" ' not
       Temp# = NOT Temp# ' perform not calculation
    END SELECT
 NEXT
END SUB

' string/numeric expression parser,
' routine returns calculated expression string/value,
' calls Parse1 recursively for values inside parenthesis/functions.
SUB Parse9 (Temp#)
 SELECT CASE Token ' determine token type
 CASE 0 ' no terminating token
    IF Last.Token = 3 THEN
       IF Allow.Extra = False THEN
          Strng = "<extra token>"
          ERROR 92
       END IF
    ELSE
       IF Strng = Nul THEN
          Strng = "<missing token>"
       ELSE
          Strng = "<" + Strng + ">"
       END IF
       ERROR 92
    END IF
    Last.Token = False
 CASE 1 ' token is symbol
    Token.Parsed$ = Strng
    SELECT CASE Token.Parsed$
    CASE ",", ";", "'", "_" ' separaters
       Token = False
       Last.Token = 3
    CASE Quote$ ' parse string in quotes
       CALL Parse.Quoted
       Quotes = True
    CASE "(" ' calculate opening parenthesis
       CALL Get.Token ' read next token value inside parenthesis
       IF Strng = ")" THEN
          Strng = "<empty closing token>"
          ERROR 92
       END IF
       IF Strng <> ")" THEN
	  DO ' calculate value
	     CALL Parse1(Temp#) ' recursively call parse entry
	  LOOP UNTIL Strng = ")" OR Token = False ' check closing parenthesis
       END IF
       IF Token = False THEN
          Strng = "<missing closing token>"
          ERROR 92
       END IF
       CALL Get.Token ' read next token after parenthesis
    CASE "["
       CALL Get.Token
       IF Strng = "]" THEN
          Strng = "<empty closing token>"
          ERROR 92
       END IF
       IF Strng <> "]" THEN
	  DO
	     CALL Parse1(Temp#) ' recursively call parse entry
	  LOOP UNTIL Strng = "]" OR Token = False ' check closing parenthesis
       END IF
       IF Token = False THEN
          Strng = "<missing closing token>"
          ERROR 92
       END IF
       CALL Get.Token ' read next token after parenthesis
    CASE "{"
       CALL Get.Token
       IF Strng = "}" THEN
          Strng = "<empty closing token>"
          ERROR 92
       END IF
       IF Strng <> "}" THEN
	  DO
	     CALL Parse1(Temp#) ' recursively call parse entry
	  LOOP UNTIL Strng = "}" OR Token = False ' check closing parenthesis
       END IF
       IF Token = False THEN
          Strng = "<missing closing token>"
          ERROR 92
       END IF
       CALL Get.Token ' read next token after parenthesis
    CASE ")", "]", "}" ' check token is closing parenthesis
       IF Allow.Extra <= False THEN
          Strng = "<extra closing token>"
          ERROR 92
       END IF
    END SELECT
 CASE 2 ' token is numeric value
    CALL Parse.Numeric(Temp#)
 CASE 3 ' token type is alphabetic
    SELECT CASE LEN(Strng) ' check variable type
    CASE 1
       ' parse the token
       CALL Parse.Alphabetic1(Temp#)
    CASE 2
       SELECT CASE UCASE$(Strng)
       ' special tokens
       CASE "AS"
	  CALL Get.Token
	  EXIT SUB
       CASE "TO"
	  CALL Get.Token
	  CALL Parse1(Temp4#)
	  FinishFor = Temp4#
	  EXIT SUB
       END SELECT
       ' parse the token
       CALL Parse.Alphabetic2(Temp#)
    CASE ELSE
       SELECT CASE UCASE$(Strng)
       ' printing tokens
       CASE "SPC"
	  IF Printing = False THEN
	     ERROR 92
	  END IF
          IF PrinterLF = -2 THEN
             ERROR 92
          END IF
          CALL Get.Token3
	  CALL Get.Token
	  CALL Parse1(Temp#)
          If PrinterLF = True THEN
             If Temp#<0# Then
                Error 161
             Else
                LPRINT SPACE$(Temp#);
             Endif
          ELSE
             Strng3 = SPACE$(Temp#)
             Carriage.Input = True
             Call IO.O
          END IF
	  CALL Get.Token
          IF PrinterLF THEN
             CALL Get.Token
             CALL Parse1(Temp#)
          END IF
	  EXIT SUB
       CASE "TAB" ' move to column w/ wrapping
	  IF Printing = False THEN
	     ERROR 92
	  END IF
          IF PrinterLF = -2 THEN
             ERROR 92
          END IF
          CALL Get.Token3
	  CALL Get.Token
	  CALL Parse1(Temp#)
          If PrinterLF = True THEN
             LPRINT TAB(Temp#);
          ELSE
             VarX#=Temp#
             If VarX#>80# Then ' wrap
                VarX#=VarX# MOD 80#
             Endif
             If VarX#<1# Then ' column 1
                VarX#=1#
             Endif
             If VarX#>0# And VarX#<=80# Then
                Call Get.Cursor(Tmp2)
                If Tmp2>VarX# Then ' linefeed
                   Call IO.O
                   VarZ#=VarX#-1#
                   If VarZ#>0# Then
                      Strng3=Space$(VarZ#)
                      Carriage.Input = True
                      Call IO.O
                   Endif
                Else
                   VarZ#=VarX#-Tmp2
                   If VarZ#>0# Then
                      Strng3 = SPACE$(VarZ#)
                      Carriage.Input = True
                      Call IO.O
                   Endif
                Endif
             Endif
          END IF
          CALL Get.Token
          IF PrinterLF THEN
             CALL Get.Token
             CALL Parse1(Temp#)
          END IF
	  EXIT SUB
       ' special tokens
       CASE "THEN"
	  CALL Get.Token
	  EXIT SUB
       CASE "STEP"
	  CALL Get.Token
	  CALL Parse1(Temp4#)
	  StepTo = Temp4#
	  EXIT SUB
       CASE "EXCEPT"
	  CALL Get.Token
	  CALL Parse1(Temp4#)
	  ExceptStep = Temp4#
          ExceptStepIs = True
	  EXIT SUB
       CASE "UNLESS"
	  CALL Get.Token
	  CALL Parse1(Temp4#)
	  Unless.Branch = True
	  Unless.Value = Temp4#
	  EXIT SUB
       END SELECT
       ' parse the token
       CALL Parse.Alphabetic3(Temp#)
    END SELECT
    ' parse the last token
    ' after any alphabetic token
    ' except the special tokens above.
    CALL Get.Token
 END SELECT
END SUB

' parses data statements.
SUB Read.Data.Element (More.Data)
 More.Data = False
 VarX = DataLine
 Stored.Out2$ = Out2
 Stored.Token.Index = Token.Index
 DO
    Out2 = Program(VarX)
    Out2 = STRIM$(Out2)
    Out2 = UCASE$(Out2)
    IF LEFT$(Out2, 4) <> "DATA" THEN
       Var = DataLine
       Var2 = False
       DO
          IF Var >= Last.Line THEN
             EXIT DO
          END IF
          Var = Var + 1
          Strng2$ = Program(Var)
          Strng2$ = STRIM$(Strng2$)
          Strng2$ = UCASE$(Strng2$)
          IF LEFT$(Strng2$, 4) = "DATA" THEN
             DataLine = Var
             Var2 = True
             EXIT DO
          END IF
       LOOP
       IF Var2 = False THEN
          More.Data = True
          EXIT SUB
       END IF
       DataNumber = False
    END IF
    Out2 = Program(DataLine)
    Out2 = STRIM$(Out2)
    Out2 = MID$(Out2, 5)
    Out2 = STRIM$(Out2)

    ' trim trailing comment
    GOSUB Trim.Comment

    ' append trailing data seperator
    Out2 = Out2 + ","

    Data.Count = False
    Token.Index = 1
    DO
       Last.Token = False
       CALL Get.Token
       SELECT CASE Strng
       CASE ",", ";"
          DataValue = 0#
          Out3 = Nul
       CASE ELSE
          ' allow equations in DATA elements!?
          CALL Parse1(DataValue)

          ' next token should be data terminator
          SELECT CASE Strng
          CASE Quote
             ERROR 133
             EXIT SUB
          CASE ",", ";"
             Eat$ = Nul
          CASE ELSE
             ERROR 66
             EXIT SUB
          END SELECT
       END SELECT
       IF Data.Count = DataNumber THEN
	  DataNumber = DataNumber + 1
	  Out2 = Stored.Out2$
	  Token.Index = Stored.Token.Index
	  EXIT SUB
       END IF
       IF Token.Index > LEN(Out2) THEN
	  EXIT DO
       END IF
       Data.Count = Data.Count + 1
    LOOP
    VarX = VarX + 1
    DataNumber = False
 LOOP
 EXIT SUB

Trim.Comment:
 IF LEN(Out2) THEN
    Start.Char = 1
    DO
       Quote.Start = False
       FOR Temp = Start.Char TO LEN(Out2)
          IF MID$(Out2, Temp, 1) = Quote$ THEN
             Quote.Start = Temp
             EXIT FOR
          END IF
       NEXT
       IF Quote.Start = False THEN
          EXIT DO
       END IF
       Quote.Stop = False
       FOR Temp2 = Quote.Start + 1 TO LEN(Out2)
          IF MID$(Out2, Temp2, 1) = Quote$ THEN
             Quote.Stop = Temp2
             EXIT FOR
          END IF
       NEXT
       IF Quote.Stop = False THEN
          EXIT DO
       END IF
       Start.Char = Quote.Stop + 1
    LOOP
    FOR Temp = Start.Char TO LEN(Out2)
       IF MID$(Out2, Temp, 1) = "'" THEN
          Out2 = LEFT$(Out2, Temp - 1)
          EXIT FOR
       END IF
    NEXT
    Out2 = STRIM$(Out2)
 END IF

 ' trim continuation character
 DO
    V$ = RIGHT$(Out2, 1)
    IF V$ = CHR$(9) OR V$ = CHR$(32) OR V$ = "_" THEN
       Out2 = LEFT$(Out2, LEN(Out2) - 1)
    ELSE
       EXIT DO
    END IF
 LOOP
 RETURN
END SUB

' starts a local/remote MS-DOS shell
SUB Execute.Command (Shell.Statement$)
 ' On Error Goto Error.Routine
 DIM DTAfile AS DTAtype

 ' check shell type
 If Local.Mode=False Then
    If Port>=0 And Port<=3 Then
       Eat$ = Nul
    Else
       Error 154
       Exit Sub
    Endif
    If Len(Shell.Statement$) Then
       If Instr(Shell.Statement$,"|") Or Instr(Shell.Statement$,"<") Or Instr(Shell.Statement$,">") Then
          Error 154
          Exit Sub
       Endif
    Endif
 Endif

 ' store shell dta
 InregsX.DS = VARSEG(DTAfile)
 InregsX.DX = VARPTR(DTAfile)
 Call StdinInt(26,0)

 ' store current drive/directory
 Directory$ = CURDIR$

 ' clear display
 Call Clear.Status.Line

 ' start DOS command shell
 Comspec$=Environ$("COMSPEC")
 IF Local.Mode Then
    IF LEN(Shell.Statement$) THEN
       Shell.Command$ = Comspec$+" /C " + Shell.Statement$
       SHELL Shell.Command$
    ELSE
       SHELL
    END IF
 ELSE
    DOSexit2$="SIC64"+Rtrim$(NodeX)+".BAT"
    Close #TempFile2
    Open DOSexit2$ For Output Shared As #TempFile2
    Var$="COM"+Mid$(Str$(Port+1),2)+":"
    IF LEN(Shell.Statement$) THEN
       VarX$=Comspec$+" /C "+Shell.Statement$+" < "+Var$+" > "+Var$
       Print #TempFile2,VarX$
       Print #TempFile2,"EXIT"
    ELSE
       Print #TempFile2,"ECHO OFF"
       VarX$=Comspec$+" < "+Var$+" > "+Var$
       Print #TempFile2,VarX$
    END IF
    Close #TempFile2
    Call Extended.Driver(13) ' watchdog on
    Shell DOSexit2$
    Call Extended.Driver(14) ' watchdog off
 END IF

 ' restore current drive/directory
 CHDRIVE Directory$
 CHDIR Directory$

 ' restore basic dta
 InregsX.DS=BASIC.DTA.SEG
 InregsX.DX=BASIC.DTA.OFF
 Call StdinInt(26,0)

 ' restore display
 If Debug Then
    Call Sic.Status.Line2
 Else
    Call Sic.Status.Line
 Endif
END SUB

' lists specified .sic files
SUB List.FilesX (Var$, VarQ)
 ' VarQ = 0 list dirs only, VarQ = -1 list files and dirs

 ' declare file buffer
 DIM ParseDTA2 AS DTAtype
 DIM ASCIIZ2 AS STRING * 260, ASCIIZ3 AS STRING * 260

 ' declare file date and time work variables
 DIM File.Work.Date AS SINGLE, File.Work.Time AS SINGLE
 DIM HourTemp AS SINGLE, MinuteTemp AS SINGLE, SecondsTemp AS SINGLE
 DIM DayTemp AS SINGLE, MonthTemp AS SINGLE, YearTemp AS SINGLE
 DIM File.Date AS STRING * 10, File.Time AS STRING * 8

 ' declare file size work variables
 DIM File.Size AS DOUBLE, Total.Bytes AS DOUBLE
 DIM Total.Files AS DOUBLE, Total.Dirs AS DOUBLE

 ' reset counters
 Line.Count = 5
 Continuous.Display = False

 ' reset some variables
 Total.Bytes = Dfalse
 Total.Files = DFalse
 Total.Dirs = Dfalse

 ' store asciiz search filename
 V$ = CURDIR$
 IF RIGHT$(V$, 1) <> "\" THEN
    V$ = V$ + "\"
 END IF
 IF Var$ = Nul THEN
    Var$ = "*.sic"
    FileList$ = V$ + "*.SIC"
 ELSE
    IF INSTR(Var$, "\") = 0 THEN
       IF MID$(Var$, 2, 1) = ":" THEN
          IF LEN(Var$) = 2 THEN
             Var$ = Var$ + "\"
             FileList$ = Var$
          ELSE
             FileList$ = Var$
          END IF
       ELSE
          FileList$ = V$ + Var$
       END IF
    ELSE
       FileList$ = Var$
    END IF
    IF RIGHT$(Var$, 1) = "\" THEN
       Var$ = Var$ + "*.*"
       FileList$ = Var$
    END IF
 END IF
 ASCIIZ2 = Var$ + CHR$(0)

 ' display header
 Call ScreenANSI(4,37,15)
 Strng3 = "Searching: " + Chr$(34) + Var$ + Chr$(34)
 Call IO.O
 GOSUB Title.Header2

 ' restore directory search dta
 InregsX.AX = &H1A00
 InregsX.DS = VARSEG(ParseDTA2)
 InregsX.DX = VARPTR(ParseDTA2)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' loop dirs/files
 FOR Var1 = 1 TO 2

    ' list dirs only
    IF Var1 = 2 THEN
       IF VarQ = 0 THEN
          EXIT FOR
       END IF
    END IF

    ' find first directory/filename
    InregsX.AX = &H4E00
    InregsX.CX = &H37 ' dir/file
    InregsX.DS = VARSEG(ASCIIZ2)
    InregsX.DX = VARPTR(ASCIIZ2)
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' start file list loop
    DO
       ' check findfirst error
       IF (OutregsX.Flags AND &H1) = &H1 THEN
          EXIT DO
       END IF

       ' Bitfields for file attributes:

       ' Bits   Description
       '  7      shareable
       '  6      unused
       '  5      archive
       '  4      directory
       '
       '  3      volume label
       '  2      system
       '  1      hidden
       '  0      read-only

       ' store dir/file attribute
       Attribute% = ASC(ParseDTA2.FileAttr)

       ' check file lock
       IF Local.Mode=False THEN
          IF Account.Login <> Library.Login THEN
             IF (Attribute% AND &H1) = &H1 THEN ' read-only
                GOTO NextFile
             END IF
          END IF
       END IF

       Attr$ = Space$(6)
       IF (Attribute% AND &H20) = &H20 THEN
          Mid$(Attr$, 1, 1) = "A" ' archive
       END IF
       IF (Attribute% AND &H10) = &H10 THEN
          Mid$(Attr$, 2, 1) = "D" ' directory
       END IF
       IF (Attribute% AND &H4) = &H4 THEN
          Mid$(Attr$, 3, 1) = "S" ' system
       END IF
       IF (Attribute% AND &H2) = &H2 THEN
          Mid$(Attr$, 4, 1) = "H" ' hidden
       END IF
       IF (Attribute% AND &H1) = &H1 THEN
          Mid$(Attr$, 5, 1) = "R" ' read-only
       END IF

       ' determine search type
       VarX = 0
       IF (Attribute% AND &H10) = &H10 THEN
          IF Var1 = 1 THEN ' dir
             X$ = ParseDTA2.ASCIIZfilename
             IF INSTR(X$, CHR$(0)) THEN
                X$ = LEFT$(X$, INSTR(X$, CHR$(0)) - 1)
             END IF
             IF X$ = "." OR X$ = ".." THEN
                Eat$ = Nul
             ELSE
                ' count dirs
                Var4$ = FileList$
                For V = Len(Var4$) To 1 Step -1
                   If Mid$(Var4$, V, 1) = "\" Then
                      Var4$ = Mid$(Var4$, V + 1)
                      Exit For
                   Endif
                Next
                Var5$=Var4$
                Do
                   V=Instr(Var5$, ".")
                   If V Then
                      Var5$=Left$(Var5$,V-1)+Mid$(Var5$,V+1)
                   Else
                      Exit Do
                   Endif
                Loop
                ' string to search for = Var4$, Var5$
                ' string to search = X$
                '   not case-sensitive.
                CALL InstrSUB1(Flag1, Var4$, X$, 0)
                CALL InstrSUB1(Flag2, Var5$, X$, 0)
                IF Flag1 Or Flag2 THEN ' return match
                   FileSize$ = "     <DIR>"
                   Total.Dirs = Total.Dirs + 1#
                   VarX = -1
                END IF
             END IF
          END IF
       ELSE
          IF Var1 = 2 THEN ' file
             VarX = -1
             ' count filesize
             Temp# = ASC(MID$(ParseDTA2.FileSize, 4, 1))
             Temp# = Temp# * &H100 + ASC(MID$(ParseDTA2.FileSize, 3, 1))
             Temp# = Temp# * &H100 + ASC(MID$(ParseDTA2.FileSize, 2, 1))
             Temp# = Temp# * &H100 + ASC(MID$(ParseDta2.FileSize, 1, 1))
             ' count bytes/files
             Total.Bytes = Total.Bytes + Temp#
             Total.Files = Total.Files + 1#
             CALL Suffix(Temp#, FileSize$) ' 1,024.0 KB
             FileSize$ = Left$(FileSize$, 10)
             FileSize$ = Space$(10 - Len(FileSize$)) + FileSize$
          END IF
       END IF

       ' select search type
       IF VarX THEN
          IF q THEN
             GOSUB Title.Header2
             q = 0
          END IF

          ' store shortfilename
          Filename = ParseDTA2.ASCIIZfilename
          IF INSTR(Filename, CHR$(0)) THEN
             Filename = LEFT$(Filename, INSTR(Filename, CHR$(0)) - 1)
          END IF

          ' construct file date and time for display
          File.Work.Date = ASC(MID$(ParseDTA2.FileDate, 2, 1))
          File.Work.Date = File.Work.Date * &H100 + ASC(MID$(ParseDTA2.FileDate, 1, 1))

          YearTemp = INT(File.Work.Date / 512)
          MonthTemp = INT((File.Work.Date AND &H1E0) / 32)
          DayTemp = INT(File.Work.Date AND &H1F)

          File.Work.Time = ASC(MID$(ParseDTA2.FileTime, 2, 1))
          File.Work.Time = File.Work.Time * &H100 + ASC(MID$(ParseDTA2.FileTime, 1, 1))

          HourTemp = INT(File.Work.Time / 2048)
          MinuteTemp = INT((File.Work.Time AND &H7E0) / 32)
          SecondsTemp = INT((File.Work.Time AND &H1F) / 2)
          YearTemp = YearTemp + 1980

          File.Date = RIGHT$(STR$(MonthTemp + 100), 2) + "-" + RIGHT$(STR$(DayTemp + 100), 2) + "-" + MID$(STR$(YearTemp), 2)
          File.Time = RIGHT$(STR$(HourTemp + 100), 2) + ":" + RIGHT$(STR$(MinuteTemp + 100), 2) + ":" + RIGHT$(STR$(SecondsTemp + 100), 2)

          ' display shortfilename
          Display.Line$ = Filename + SPACE$(13 - LEN(Filename))
          Display.Line$ = Display.Line$ + " " + File.Date + "  " + File.Time
          Display.Line$ = Display.Line$ + " " + Attr$
          Display.Line$ = Display.Line$ + RTRIM$(FileSize$) ' 1,024.0 KB
          Strng3 = Display.Line$
          Call IO.O

          ' check page length
          If Continuous.Display = False Then
             Line.Count = Line.Count + 1
             IF Line.Count >= User.PageLength THEN
                Line.Count = 4
                CALL More.Prompt("More(y/n/c)", "ync" + CHR$(13), Output.Char$, "y")
                SELECT CASE Output.Char$
                Case "c"
                   Continuous.Display = True
                CASE "y", CHR$(13)
                   q = -1
                CASE "n"
                   EXIT FOR
                END SELECT
             END IF
          Endif
       END IF
NextFile:
       ' restore directory search dta
       InregsX.AX = &H1A00
       InregsX.DS = VARSEG(ParseDTA2)
       InregsX.DX = VARPTR(ParseDTA2)
       CALL InterruptX(&H21, InregsX, OutregsX)

       ' find next filename
       InregsX.AX = &H4F00
       CALL InterruptX(&H21, InregsX, OutregsX)
    LOOP
 NEXT

 ' restore basic dta
 InregsX.AX = &H1A00
 InregsX.DS = BASIC.DTA.SEG
 InregsX.DX = BASIC.DTA.OFF
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' check page length
 If Continuous.Display = False Then
    IF Line.Count >= User.PageLength - 3 THEN
       IF Continuous.Display = False THEN
          CALL More.Prompt("More(y/n)", "yn" + CHR$(13), Output.Char$, "y")
          IF Output.Char$ = "n" THEN
             EXIT SUB
          END IF
       END IF
    END IF
 Endif

 ' check totals
 IF Total.Files = 0 AND Total.Dirs = 0 THEN
    Call ScreenANSI(4,33,14)
    Strng3 = "<none>"
    Call IO.O
 END IF

 ' print totals
 Call ScreenANSI(4,37,15)
 Strng3 = String$(12,"-") + Space$(28) + STRING$(11,"-")
 Call IO.O
 Total.Line$ = "Files " + FORMAT$(Total.Files, "#,##0")
 Total.Line$ = Total.Line$ + " Dirs " + FORMAT$(Total.Dirs, "#,##0")
 Total.Line$ = LEFT$(Total.Line$, 40)
 Total.Line$ = Total.Line$ + SPACE$(40 - LEN(Total.Line$))

 CALL Suffix(Total.Bytes, Var3$)
 If Len(Var3$) <= 11 Then
    Total.Line$ = Total.Line$ + SPACE$(11 - LEN(Var3$)) + Var3$
 ELSE
    Total.Line$ = Total.Line$ + Var3$
 END IF
 Strng3 = Total.Line$
 Call IO.O
 Call ScreenANSI(4,33,14)
 EXIT SUB

Title.Header2:
 Call ScreenANSI(4,37,15)
 Strng3 = "Filename      Date        Time     Attr        Size"
 Call IO.O
 Strng3 = "------------  ----------  -------- ----- ----------"
 Call IO.O
 Call ScreenANSI(4,33,14)
 RETURN
END SUB

' counts total bytes in a file spec
FUNCTION AsciizCount# (AsciiFile$)
 On Local Error Goto Error.Trap10

 ' declare file search variables
 DIM DTAfile AS DTAtype

 ' store filename in asciiz form
 ASCIIZ = RTRIM$(AsciiFile$) + CHR$(0)

 ' restore file search dta
 InregsX.DS = VARSEG(DTAfile)
 InregsX.DX = VARPTR(DTAfile)
 Call StdinInt(26,0)

 ' find first filename
 InregsX.CX=&H27
 InregsX.DS=VARSEG(ASCIIZ)
 InregsX.DX=VARPTR(ASCIIZ)
 Call StdinInt(78,0)

 ' reset byte counter
 Bytes# = Dfalse

 DO
    ' check findfirst error
    IF (OutregsX.Flags AND 1) = 1 THEN
       EXIT DO
    END IF

    ' store file size
    File.Size# = Dfalse
    File.Size# = File.Size# + ASC(MID$(DTAfile.FileSize, 4, 1))
    File.Size# = File.Size# * &H100 + ASC(MID$(DTAfile.FileSize, 3, 1))
    File.Size# = File.Size# * &H100 + ASC(MID$(DTAfile.FileSize, 2, 1))
    File.Size# = File.Size# * &H100 + ASC(MID$(DTAfile.FileSize, 1, 1))

    ' count bytes
    Bytes# = Bytes# + File.Size#

    ' find next filename
    Call StdinInt(79,0)
 LOOP

 ' restore basic dta
 InregsX.DS = BASIC.DTA.SEG
 InregsX.DX = BASIC.DTA.OFF
 Call StdinInt(26,0)

 ' assign total bytes to function
 AsciizCount# = Bytes#
Error.Resume10:
 Exit Function
Error.Trap10:
 Resume Error.Resume10
END FUNCTION

Sub Amount(Var#,Var$)
 IF Var# = 0# THEN
    Var$ = "0"
 ELSE
    If Instr(Str$(Var#),"D") Then
       Var$=Ltrim$(Str$(Var#))
    Else
       Var$=Form$(Var#)
    Endif
 END IF
End Sub

Sub Make.Format(Var#, Var$)
 On Local Error Goto ErrTrap13
 Var2#=Var#
 If Var2#<=0 Then
    Var$="0.00 B"
    Exit Sub
 Endif
 TempA=False
 Do
    If Var2#<1024# Then
       Exit Do
    Endif
    Var2#=Var2#/1024#
    TempA=TempA+1
    If TempA=14 Then
       Exit Do
    Endif
 Loop
 Strng$=Nul
 If TempA>=1 And TempA<=14 Then
    Strng$=MID$("KMGTPEZYSDXQAU",TempA,1)
 Endif
 Var$=Format$(Var2#,"#,##0.00;;")+" "+Strng$+"B"
ErrResume13:
 Exit Sub
ErrTrap13:
 Resume ErrResume13
End Sub

SUB GetFileSize(Var4$, Size#)
 DIM ASCIIZ3 AS STRING * 260
 DIM DTAfile AS DTAtype
 ASCIIZ3 = Var4$ + CHR$(0)
 ' store 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(ASCIIZ3)
 InregsX.DX = VARPTR(ASCIIZ3)
 CALL InterruptX(&H21, InregsX, OutregsX)
 ' check findfirst error
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    Size# = 0#
 END IF
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' store file size
    Size# = ASC(MID$(DTAfile.FileSize, 4, 1))
    Size# = Size# * &H100 + ASC(MID$(DTAfile.FileSize, 3, 1))
    Size# = Size# * &H100 + ASC(MID$(DTAfile.FileSize, 2, 1))
    Size# = Size# * &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 SUB

SUB GetFileSize2(Var4$, Size#)
 ' declare file buffer
 DIM ASCIIZ3 AS STRING * 260
 DIM WDTAFile AS WDTAtype
 DIM Wfile.Handle AS INTEGER

 ASCIIZ3 = Var4$ + CHR$(0)
 ' get filesize
 InregsX.AX = &H714E
 InregsX.CX = &H27
 InregsX.SI = &H1
 InregsX.DS = VARSEG(ASCIIZ3)
 InregsX.DX = VARPTR(ASCIIZ3)
 InregsX.ES = VARSEG(WDTAfile)
 InregsX.DI = VARPTR(WDTAfile)
 CALL InterruptX(&H21, InregsX, OutregsX)
 Wfile.Handle = OutregsX.AX
 ' store filesize.
 Size# = ASC(MID$(WDTAfile.FileSizeHigh, 4, 1))
 Size# = Size# * &H100 + ASC(MID$(WDTAfile.FileSizeHigh, 3, 1))
 Size# = Size# * &H100 + ASC(MID$(WDTAfile.FileSizeHigh, 2, 1))
 Size# = Size# * &H100 + ASC(MID$(WDTAfile.FileSizeHigh, 1, 1))
 Size# = Size# * &H100 + ASC(MID$(WDTAfile.FileSizeLow, 4, 1))
 Size# = Size# * &H100 + ASC(MID$(WDTAfile.FileSizeLow, 3, 1))
 Size# = Size# * &H100 + ASC(MID$(WDTAfile.FileSizeLow, 2, 1))
 Size# = Size# * &H100 + ASC(MID$(WDTAfile.FileSizeLow, 1, 1))
 ' close long filename search.
 InregsX.AX = &H71A1
 InregsX.BX = Wfile.Handle
 CALL InterruptX(&H21, InregsX, OutregsX)
END SUB

' formats a double numeric string
FUNCTION FormatString$ (s#)
x$ = Nul
s$ = STR$(s#)
IF INSTR(s$, "D") THEN
   FormatString$ = s$
   EXIT FUNCTION
END IF
IF LEFT$(s$, 1) = "-" THEN
   e$ = "-"
   s$ = MID$(s$, 2)
END IF
s$ = LTRIM$(s$)
IF INSTR(s$, ".") THEN
   q$ = MID$(s$, INSTR(s$, "."))
   s$ = LEFT$(s$, INSTR(s$, ".") - 1)
END IF
FOR l = LEN(s$) TO 3 STEP -3
   x$ = MID$(s$, l - 2, 3) + "," + x$
NEXT
IF l > 0 THEN
   x$ = MID$(s$, 1, l) + "," + x$
END IF
IF LEN(s$) < 3 THEN
   x$ = s$
END IF
IF RIGHT$(x$, 1) = "," THEN
   x$ = LEFT$(x$, LEN(x$) - 1)
END IF
x$ = e$ + x$ + q$
FormatString$ = x$
END FUNCTION

' calculate byte suffix
SUB Suffix(Var#, Var3$)

 REM B  (Byte) = 00x - 0FFx (hexidecimal zero-based) 
 REM KB (Kilobyte) = 1024 B
 REM MB (Megabyte) = 1024 KB (1 MB B) 
 REM GB (Gigabyte) = 1024 MB
 REM TB (Terabyte) = 1024 GB (1 MB MB) 
 REM PB (Petabyte) = 1024 TB
 REM EB (Exabyte) = 1024 PB (1 MB TB) 
 REM ZB (Zettabyte) = 1024 EB
 REM YB (Yottabyte) = 1024 ZB (1 MB EB) 

 REM experimental suffixes:
 REM SB (Superbyte) = 1024 YB
 REM DB (Duperbyte) = 1024 SB (1 MB YB) 
 REM XB (Xeabyte) = 1024 DB
 REM QB (Zeabyte) = 1024 XB (1 MB DB) 
 REM AB (Ackbyte) = 1024 QB
 REM UB (Ughbyte) = 1024 AB (1 MB QB)
 REM LB (LowByte)   = 1024 UB
 REM HB (HighByte)  = 1024 LB (1 MB UB)

 ' store double
 VarX# = Var#

 ' 1.#INF and -1.#INF ad -1.#IND and 1#QNAN
 IF INSTR(Str$(VarX#),"#") Then
    Var3$ = Ltrim$(Str$(VarX#))
    Exit Sub
 Endif

 ' get sign
 IF VarX# < 0# THEN
    Sign = -1
    VarX# = ABS(VarX#)
 END IF

 ' calculate bytes
 TempA = False
 DO
    IF VarX# >= 1024 THEN
       VarX# = VarX# / 1024
       TempA = TempA + 1
       IF TempA = 16 THEN
	  EXIT DO
       END IF
    ELSE
       EXIT DO
    END IF
 LOOP

 ' calculate byte string
 Var3$ = FORMAT$(VarX#, "#,##0.0;;")

 ' calculate byte suffix
 Var$ = Nul
 IF TempA > 0 THEN
    Var$ = MID$("KMGTPEZYSDXQAULH", TempA, 1)
 END IF
 Var3$ = Var3$ + " " + Var$ + "B"

 ' calculate byte sign
 IF Sign THEN
    Var3$ = "-" + Var3$
 END IF
END SUB

SUB GetBits (Temp#)
 DIM DTAfile AS DTAtype
 ' store directory search dta
 InregsX.AX = &H1A00
 InregsX.DS = VARSEG(DTAfile)
 InregsX.DX = VARPTR(DTAfile)
 CALL InterruptX(&H21, InregsX, OutregsX)
 ' find first filename
 ASCIIZ = Out3 + CHR$(0)
 InregsX.AX = &H4E00
 InregsX.CX = &H37
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
 ' check findfirst error
 Temp# = 0#
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' store file attributes
    Temp# = ASC(DTAfile.FileAttr)
 END IF
 ' restore basic dta
 InregsX.AX = &H1A00
 InregsX.DS = BASIC.DTA.SEG
 InregsX.DX = BASIC.DTA.OFF
 CALL InterruptX(&H21, InregsX, OutregsX)
END SUB

SUB Capitalize (Var3$)
Var3$ = RTRIM$(LTRIM$(Var3$))
IF Var3$ = Nul THEN
   EXIT SUB
END IF
MID$(Var3$, 1, 1) = UCASE$(MID$(Var3$, 1, 1))
Var = INSTR(Var3$, " ")
IF Var < LEN(Var3$) THEN
   DO WHILE Var
      MID$(Var3$, Var + 1, 1) = UCASE$(MID$(Var3$, Var + 1, 1))
      Var = INSTR(Var + 1, Var3$, " ")
   LOOP
END IF
END SUB

' check drive exists
SUB Check.Drive(V, T#)
 ON LOCAL ERROR GOTO Disk.Error
 ' check disk space.
 InregsX.AX = &H3600
 InregsX.DX = V
 CALL InterruptX(&H21, InregsX, OutregsX)
 IF OutregsX.AX = &HFFFF THEN
    T# = 0#
 ELSE
    T# = -1#
 END IF
 EXIT SUB
Disk.Error:
 T# = 0#
 EXIT SUB
END SUB

' tests drive
SUB Check.Drive2(V, T#)
 ON LOCAL ERROR GOTO Disk.Error2
 DIM BPBfile AS BPBtype
 ' get volume info
 InregsX.AX = &H6900
 InregsX.BX = V
 InregsX.DS = VARSEG(BPBfile)
 InregsX.DX = VARPTR(BPBfile)
 CALL InterruptX(&H21, InregsX, OutregsX)
 ' check flag error
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    T# = -1#
 ELSE
    T# = 0#
 END IF
 EXIT SUB
Disk.Error2:
 T# = 0#
 EXIT SUB
END SUB
