; > Evaluate
; BASIC expression evaluator

; 30-Aug-2008: Recursive Expression Evaluator and binary operator dispatch written
; 01-Sep-2008: Hex values, double quotes, octal values
; 03-Mar-2009: 31-bit decimal numbers working
; 15-Jun-2010: Parsing comparisons done
; 25-Jun-2013: FloatToInteger written, convert to &80000000 working.
;              Bug: FloatToInt should round negative numbers down, INT(-PI) should be -4.
; 15-Aug-2013: ABS moved here with speeded up Negate
; 20-Nov-2013: Evaluate checks free memory before starting
; 09-Dec-2013: AddressOf skips any following () to allow ^PROCname(), ^FNname()
; 28-Jul-2016: Evaluator uses some (r5)+ instead of (r5)/inc, optimised bne/rts pairs
; 29-Jul-2016: INT(negative) rounds down, INT(-PI) is -4.
; 31-Jul-2016: Scanning fractional decimals and exponential decimals implemented.
;              E<num> scanned but fnPower only implements positive exponents.
; 07-Aug-2016: EnsureInt preserves r0/r1, -0 to -1 correctly rounds to -1.
;              Bug: now causes A=-7/2:A%=A to round incorrectly.
; 09-Jul-2016: EnsureInteger truncates, INT rounds downwards.
;              Bug: EvalDecimal fails with num>10e9 unless E format used.
; 16-Mar-2021: EvalDecimalVAL correctly returns 0 for non-numbers.
;              EvalDecimalPrefix and EvalDecimalVAL can't merge as VAL allows spaces, E+num doesn't.
; 18-Mar-2024: EvalHash corrected to EvalHashVal. Added @octal.
; 10-May-2025: Optimised Bin/Oct/Hex constants.


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Check for syntax character and evaluate following expression ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


; Check for and step past ')'
; ===========================
.CheckClose
jsr  pc,SkipSpaceNext ; This ; Next
.CheckClose1
cmpb r0,#ASC")"
beq  CheckOk
.errMissingClose
jsr  pc,Error
equb 27,tknMissing,")",0
align

; Check for and step past ','
; ===========================
.CheckComma
jsr  pc,SkipSpaceNext ; This ; Next
cmpb r0,#ASC","
beq  CheckOk
.errMissingComma
jsr  pc,Error
equb 5,tknMissing,44,0
align

; Check for ',' evaluate and return following integer
; ===================================================
.EvalComma
jsr  pc,CheckComma
br   EvalInteger

; Check for and step past '='
; ===========================
.CheckEqual
jsr  pc,SkipSpaceNext ; This ; Next
.CheckEqual1
cmpb r0,#ASC"="
beq  CheckOk
;bne  errMissingEqual
;.CheckOk
;inc  r5
;rts  pc
.errMissingEqual
jsr  pc,Error
equb 4,tknMissing,"=",0
align

; Fetch next and check for <,=,>
; ==============================
.CheckCompare
movb (r5),r0
cmpb r0,#ASC"<"
beq  ChkCmpOk
cmpb r0,#ASC"="
beq  ChkCmpOk
cmpb r0,#ASC">"
.ChkCmpOk
.CheckOk
rts  pc

; Check for '=', evaluate and return following integer
; ====================================================
.EvalEqual
jsr  pc,CheckEqual
br   EvalInteger

; Check for '#', evaluate and return following integer expression
; ===============================================================
;.EvalHash
;jsr  pc,SkipSpaceNext
;cmpb r0,#ASC"#"
;beq  EvalInteger
;.errMissingHash
;jsr  pc,Error
;equb 45,tknMissing,"#",0
;align

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                          ;;
;; Evaluate expression and check for expected returned type ;;
;;                                                          ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; INT - convert to integer, rounding down if negative
; ===================================================
.fnINT
jsr  pc,EvalNumVal	; Evaluate numeric value
beq  EnsureIntOk	; Already an integer
tst  -(sp)		; Stack dummy value
mov  #&7FFF,r1		; r1=nothing to round yet
br   EnsureInteger2	; Convert and round down


; EvalInteger - Evaluate numeric expression and return integer
; ============================================================
.EvalInteger1
inc  r5			; Step past prefix character
.EvalInteger
jsr  pc,EvalNumeric	; Call expression evaluator
			; Fall through to convert float to integer


; EnsureInteger - If float, denormalise into an integer
; =====================================================
; On entry, r2/r3/r3=value
; On exit,  r2/r3/r4=integer value, flags set from r2
;           r0/r1 preserved
;
; Conversion to integer truncates, so +3.5 -> +3, -3.5 -> -3
; The INT function rounds downwards, so +3.5 -> +3, -3.5 -> -4
;
.EnsureInteger
tst  r2			; Check if float
beq  EnsureIntOk	; Already an integer
bmi  errTypeMismatch	; Error if a string
mov  r1,-(sp)		; Save R1
clr  r1			; r1=fraction count, prevent rounding
.EnsureInteger2
;
; To convert float to integer we repeatedly divide the mantissa by 2
; while incrementing the exponent, which multiplies the number by 2,
; so the value remains the same. This loops until the mantissa is &9F,
; which is an exponent of +31. At this point the mantissa will be the
; binary value of the integer version of the float.
;
sub  #&9F,r2		; Reduce exponent
bcs  EnsureIntNotBig	; Exponent<&9F, 2^31+x or smaller, ok
bne  EnsureIntTooBig	; Exponent>&9F, 2^32 or bigger, too big to convert
tst  r4			; Exponent=&9F, check for mantissa=&80000000
bne  EnsureIntTooBig	; &9F,&....XXXX, too big
cmp  r3,#&8000
beq  EnsureIntDone	; &9F,&80000000 is only convertable &9F value
.EnsureIntTooBig
jmp  errTooBig		; ABS(float) is 2^32 or bigger, too big to convert
.EnsureIntNotBig
mov  r3,-(sp)		; Save sign bit
bis  #&8000,r3		; Put top bit in
clc
.EnsureIntLp
ror  r3			; Will always divide at least once
ror  r4			; Divide mantissa by two
adc  r1			; Add fractional bits, clear carry
inc  r2			; Increment exponent to multiply by two
bne  EnsureIntLp	; Loop until exp=+32
tst  (sp)+		; Test sign bit
bpl  EnsureIntDone	; Positive number, return it
jsr  pc,NegateInteger	; Negate negative number
cmp  #&8000,r1		; Is there anything to round
sbc  r4			; Round negative number down for INT
sbc  r3
.EnsureIntDone
mov  (sp)+,r1		; Restore R1
tst  r2			; Set flags from type=int
.EnsureIntOk
rts  pc


; EvalFloatVal - Evaluate real value
; ==================================
.EvalFloatVal
jsr pc,EvalNumVal	; Call level 1 expression evaluator
;br  EnsureFloat	; Fall through to convert to float


; EnsureFloat - If integer, normalise into a float
; ================================================
; On entry, r2/r3/r3=value
; On exit,  r2/r3/r4=float value, flags set from r2, EQ=zero, NE=non-zero
;           r0/r1 preserved
;
.EnsureFloat
tst  r2
bne  EnsureFloatDone	; Already a float
.IntegerToFloat
bis  r3,r2
bis  r4,r2
beq  EnsureFloatDone	; Zero, no float representation, return
;
;   int  00, 00 00 00 01
; float  80, 80 00 00 00, then remove sign
;
;   int  00, 00 00 01 00
; float  88, 80 00 00 00, then remove sign
;
;   int  00, 40 00 00 00
; float  9F, 80 00 00 00, then remove sign
;
;   int  00, FF FF FF FF
; float  80, 80 00 00 00, then remove sign
;
mov  r3,-(sp)		; Test and stack sign
bpl  EnsureFloatPlus	; Positive number, convert it
jsr  pc,NegateInteger	; Negate negative number
.EnsureFloatPlus
mov  #&9F,r2		; Initial exponent
.EnsureFloatLp
;bit  #&8000,r3		; Has top bit moved to top?
;bne  EnsureFloat2
tst  r3			; Has top bit moved to top?
bmi  EnsureFloat2
clc			; previous TST clears Carry
rol  r4			; Double mantissa
rol  r3
dec  r2			; Decrement exponent
br   EnsureFloatLp	; Loop until top bit set
.EnsureFloat2
tst  (sp)+		; Unstack and test sign
bmi  EnsureFloat3	; Negative, leave top bit set
bic  #&8000,r3		; Remove implied top bit
.EnsureFloat3
tst  r2			; Set flags
.EnsureFloatDone
rts pc

; EvalNumeric - Evaluate numeric expression
; =========================================
.EvalNumeric
jsr  pc,Evaluate	; Call expression evaluator
bmi  errTypeMismatch	; Returned string, we wanted a number
rts  pc

; EvalString - Evaluate string expression
; =======================================
.EvalString
jsr  pc,Evaluate	; Call expression evaluator
bpl  errTypeMismatch	; Returned number, we wanted a string
rts  pc

; EvalStringCR - Evaluate string expression and return CR-terminated
; ==================================================================
; On exit, r4=>cr-string with leading spaces skipped
;          r3=string length with <cr>
;
.EvalStringCR
jsr  pc,EvalString	; Call expression evaluator
.EvalStoreCR
bit  #&0100,r2
bne  EvalStringCRlp	; Already <cr>-string
.EvalStoreCRString
mov  r3,-(sp)		; Save length
;mov  r3,r2		; r2=length
;mov  r4,r3		; r3=source string
;jsr  pc,CopyString	; Copy to string buffer
jsr  pc,EnsureString	; Copy to string buffer
mov  (sp)+,r3		; Get length back
add  r4,r3		; r3=>end of string
movb #13,(r3)		; Put terminating CR in
sub  r4,r3		; Restore r3=length
.EvalStringCRlp
dec  r3			; Decrement length for leading spaces
cmpb (r4)+,#ASC" "
beq  EvalStringCRlp	; Skip leading spaces
dec  r4			; Point back to first non-space character
inc  r3			; Balance extra dec r3
inc  r3			; Add <cr> to length
rts  pc

; EvalStrValCR - Evaluate string value and return CR-terminated
; =============================================================
.EvalStrValCR
jsr  pc,EvalLevel1	; Call level 1 expression evaluator
bmi  EvalStoreCR	; Returned string, put terminating CR in

.errTypeMismatch
jsr pc,Error
equb 6,"Type mismatch",0
align

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                          ;;
;; Evaluate value and check for expected returned type      ;;
;;                                                          ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Check for '#', evaluate and return following integer value
; ==========================================================
; Handles are a value, not an expression, so that PTR#n+1 is
; (PTR#n)+1 and not PTR#(n+1).
;
.EvalHashVal
jsr  pc,SkipSpaceNext
cmpb r0,#ASC"#"
beq  EvalIntVal
.errMissingHash
jsr  pc,Error
equb 45,tknMissing,"#",0
align

; EvalIntVal - Evaluate integer value
; ===================================
.EvalHashInt1
inc r5			; Step past '#'
.EvalIntVal
jsr pc,EvalNumVal	; Call level 1 expression evaluator
br  EnsureInteger	; If float, convert to integer

; EvalNumVal - Evaluate numeric  value
; ====================================
.EvalNumVal
jsr pc,EvalLevel1	; Call level 1 expression evaluator
bmi errTypeMismatch	; Returned string, we wanted a number
rts pc

; EvalStrVal - Evaluate string value
; ==================================
.EvalStrVal
jsr pc,EvalLevel1	; Call level 1 expression evaluator
bpl errTypeMismatch	; Returned number, we wanted a string
rts pc


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                       ;;
;; EXPRESSION EVALUATOR                                                  ;;
;; --------------------                                                  ;;
;; Recursively calls seven expression levels, evaluating expressions at  ;;
;; each level, looping within each level until all operators at that     ;;
;; level are exhausted.                                                  ;;
;;                                                                       ;;
;; On entry, r5=>start of expression to evaluate                         ;;
;; On exit,  r5=>first character after evaluated expression              ;;
;;           r4/r3/r2=returned value, flags set from r2                  ;;
;;           MI, r2=&80xx - dynamic string, r3=length, r4=start          ;;
;;           MI, r2=&81xx - <cr>-string,    r3=length, r4=start          ;;
;;           MI, r2=&82xx - <null>-string,  r3=length, r4=start          ;;
;;           PL, r2=&00xx - number                                       ;;
;;           PL, EQ, r2=&0000 - integer, r3=b31-b16, b4=b15-b0           ;;
;;           PL, NE, r2=&00xx - real, r2=exponent,                       ;;
;;                              r3=mantissa b31-b16, r4=mantissa b15-b0  ;;
;;                                                                       ;;
;; Within the evaluator, r0 and (r5)=next matched character              ;;
;;                                                                       ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.Evaluate1
inc  r5			; Step past current character
.Evaluate
jsr pc,CheckFreeMemory

; Evaluator Level 7 - OR, EOR
; ===========================
.EvalLevel7
jsr  pc,EvalLevel6	; Call level 6 - AND
.EvalLevel7More
movb (r5),r0
cmpb r0,#tknOR
beq  EvalOR
cmpb r0,#tknEOR
beq  EvalEOR
tst  r2			; Set flags from result type
rts  pc
.EvalOR
.EvalEOR
inc  r5			; Step past current character
jsr  pc,StackIntAndOp
jsr  pc,EvalLevel6	; Evaluate RHS parameter
jsr  pc,UnstackIntAndCallOp
;movb (r5),r0
br   EvalLevel7More	; Loop to check for more OR/EOR


; Evaluator Level 6 - AND
; =======================
.EvalLevel6
jsr  pc,EvalLevel5	; Call level 5 - < <= = >= > <>
.EvalLevel6More
;movb (r5),r0
movb (r5)+,r0
cmpb r0,#tknAND
;bne  EvalDone
bne  EvalDoneDec
;beq  EvalAND
;rts  pc
;.EvalAND
;inc  r5		; Step past current character
jsr  pc,StackIntAndOp
jsr  pc,EvalLevel5	; Evaluate RHS parameter
jsr  pc,UnstackIntAndCallOp
;movb (r5),r0
br   EvalLevel6More	; Loop to check for more AND


; Evaluator Level 5 - < <= = >= > <>
; ==================================
.EvalLevel5
jsr  pc,EvalLevel4	; Check for +, -
jsr  pc,CheckCompare	; Fetch character and check for <,=,>
bne  EvalDone
;beq  EvalCompare
;rts  pc
;.EvalCompare
mov  r0,r1		; Save first character in R1
inc  r5			; Step to next character
jsr  pc,CheckCompare	; Fetch and check for <,=,>
bne  EvalCompare1	; Not <=, >=, <>, jump to to <,=,>
cmp  r0,r1		; Is it <<, ==, >>
beq  errSyntax2
inc  r5			; Step past second character
add  r0,r1		; Combine to form offset value
.EvalCompare1
bis  #&70,r1		; r1=79..7E for <=..>
mov  r1,r0		; r0=operator
jsr  pc,StackValAndOp
jsr  pc,EvalLevel4	; Evaluate RHS paraneter
jsr  pc,UnstackValAndCallOp
rts  pc
.errSyntax2
jmp  errSyntax


; Evaluator Level 4 - + -
; =======================
.EvalLevel4
jsr  pc,EvalLevel3	; Call level 3 - * / DIV MOD
.EvalLevel4More
;movb (r5),r0		; Get current character
movb (r5)+,r0		; Get current character
cmpb r0,#ASC"+"
beq  EvalPlus
cmpb r0,#ASC"-"
;bne  EvalDone
bne  EvalDoneDec
;beq  EvalMinus
;rts  pc		; Return
.EvalPlus
.EvalMinus
;inc  r5		; Step past current character
jsr  pc,StackValAndOp
jsr  pc,EvalLevel3	; Evaluate RHS parameter
jsr  pc,UnstackValAndCallOp
;movb (r5),r0		; Get current character
br   EvalLevel4More	; Loop to check for more + -


; Evaluator Level 3 - * / DIV MOD
; ===============================
.EvalLevel3
jsr  pc,EvalLevel2	; Call level 2 - ^
.EvalLevel3More
;movb (r5),r0		; Get current character
movb (r5)+,r0		; Get current character
cmpb r0,#ASC"*"
beq  EvalTimes
cmpb r0,#ASC"/"
beq  EvalDivide
cmpb r0,#tknDIV
beq  EvalDIV
cmpb r0,#tknMOD
bne  EvalDoneDec
;bne  EvalDone
;beq  EvalMOD
;rts  pc		; Return
.EvalTimes
.EvalDivide
.EvalDIV
.EvalMOD
;inc  r5		; Step past current character
jsr  pc,StackValAndOp
jsr  pc,EvalLevel2	; Evaluate RHS parameter
jsr  pc,UnstackValAndCallOp
;movb (r5),r0		; Get current character
br   EvalLevel3More	; Loop to check for more * / DIV MOD


; Evaluator Level 2 - ^
; =====================
.EvalLevel2
jsr  pc,EvalLevel1	; Call level 1 - eveything else
.EvalLevel2More
movb (r5)+,r0		; Get current character
cmpb r0,#32
beq  EvalLevel2More	; Skip spaces
cmpb r0,#ASC"^"
bne  EvalDoneDec
;beq  EvalPower
;dec  r5
;rts  pc
.EvalPower
jsr  pc,StackValAndOp
jsr  pc,EvalLevel1	; Evaluate RHS parameter
jsr  pc,UnstackValAndCallOp
br   EvalLevel2More	; Loop to check for more ^
.EvalDoneDec
dec  r5
.EvalDone
rts  pc


.errMissingQuote
jsr  pc,Error
equb 9,tknMissing,34,0
align

; EvalBracket - bracketed expression
; ----------------------------------
.EvalBracket1
inc  r5			; Step past '('
.EvalBracket
jsr  pc,Evaluate	; Evalute everything within brackets
jsr  pc,CheckClose	; Check closing bracket
tst  r2			; Set flags from returned type
rts  pc

; EvalUnaryMinus - -<value>
; -------------------------
.EvalUnaryMinus
jsr  pc,EvalNumVal	; Get numeric value

; Negate number
; -------------
; Preserves r0/r1 as called from elsewhere
.NegateNumber
tst  r2			; Check if integer or float
beq  NegateInteger
;mov  r0,-(sp)		; Save r0
;mov  #&8000,r0
;xor  r0,r3		; Toggle mantissa sign bit
;mov  (sp)+,r0		; Restore r0
sub  #&8000,r3		; Toggle mantissa sign bit
tst  r2			; Set flags
rts  pc

.fnABS
jsr pc,EvalNumVal
beq fnABSint		; Jump if integer
bic #&8000,r3		; Ensure float sign bit=0
.fnABS1
tst r2			; Set flags
rts pc
.fnABSint
tst r3			; Check integer b31
bpl fnABS1		; Positive, exit with flags set
.NegateInteger
sub #1,r4		; Do abs=NOT(num-1)
sbc r3
com r4
com r3
tst r2			; Set flags
rts pc

; EvalQuote - an immediate string
; -------------------------------
.EvalQuote
adr  SV_STRING,r4	; Point to string buffer
clr  r3			; Length=0
.EvalQuoteLp
movb (r5)+,r0		; Get character
cmpb r0,#13
beq  errMissingQuote
movb r0,(r4)+		; Store in string buffer
inc  r3			; Increment length
cmpb r0,#34		; Is this a quote?
bne  EvalQuoteLp	; Loop until terminating quote
movb (r5)+,r0
cmpb r0,#34		; Double quote?
beq  EvalQuoteLp
dec  r5
adr  SV_STRING,r4	; Point to string buffer
dec  r3			; Balance final inc
mov  #&8000,r2		; Type=string, set flags
rts  pc

; ^<variable> - address of identifier
; -----------------------------------
.EvalAddrOf
jsr  pc,SkipSpaceThis
jsr  pc,VarFindCreateAddress	; Search for variable/FN/PROC, creating if nonexistant
clr  r3
cmpb (r5)+,#ASC"("	; Is it ^PROCname()
bne  EvalHexDone	; If no following (), jump to return
jsr  pc,CheckClose	; Ensure closing bracket present
br   EvalHexOk		; Jump to return


; Evaluator Level 1 - & - + () " ? ! | $ function variable
; ========================================================
; Called by other functions, so must set flags on exit
; EvalLevel1 doesn't check free memory as uses very little stack
; Free memory checked on entry to Level7Eval
;
.EvalLevel1
clr  r4			; Set initial accumulator to 0
clr  r3
clr  r2

; EvalUnaryPlus - +<value>
; ------------------------
.EvalUnaryPlus
.EvalLevel1Spc
movb (r5)+,r0		; Get current character, step to next
cmp  r0,#32
beq  EvalLevel1Spc	; Skip spaces
cmpb r0,#ASC"("
beq  EvalBracket
cmpb r0,#ASC"^"
beq  EvalAddrOf
cmpb r0,#&22
beq  EvalQuote
;mov  #&6F,r1		; Highest hex digit+flags
cmpb r0,#ASC"&"
beq  EvalHex
cmpb r0,#ASC"@"
beq  EvalOct
mov  #&31,r1		; Highest binary digit+flags
cmpb r0,#ASC"%"
beq  EvalBinary
cmpb r0,#&8D
bcc  EvalFunction1	; Token, jump via dispatch table
jsr  pc,CheckDigit
bcc  EvalDecimal	; '0'..'9', decimal digit
cmpb r0,#ASC"-"
beq  EvalUnaryMinus
cmpb r0,#ASC"+"
beq  EvalUnaryPlus
cmpb r0,#ASC"."
beq  EvalFraction	; .<frac>

; Must be !, $, ?, |, variable, variable!offset or variable?offset
; ----------------------------------------------------------------
.EvalVariable
dec  r5			; Point to first character of variable
jmp  VarFindVal		; Returns full value, including length with $addr, $$addr

.EvalFunction1
jmp  EvalFunction	; Within 'Stack' module

; EvalOct - @<octnumber>
; ----------------------
.EvalOct
movb (r5),r0
jsr  pc,CheckDigit
bcs  EvalVariable	; @varname - not octal constant
br   EvalOct2

; EvalHex - &<hexnumber>
;     also &o<octnumber>
; ----------------------
.EvalHexGo		; Call here from OSCLI to scan addresses
.EvalHex
mov  #&6F,r1		; Highest hex digit+flags
movb (r5),r0
bic  #&20,r0		; Force upper case
cmpb r0,#ASC"O"
bne  EvalHexEtc		; Scan hex value
inc  r5			; Step past 'o'
.EvalOct2
mov  #&37,r1		; Highest octal digit+flags
;asr  r1 		; Becomes &37, highest octal digit+flags
;
; EvalBinary - %<number>, r1 already set
; --------------------------------------
.EvalBinary
.EvalHexEtc
jsr  pc,GetHexOctBin	; Get and check first digit
bcc  EvalHexEtc3	; Starts with an valid digit
jsr  pc,Error
equb 28,"Bad HEX, OCT or BIN",0
align

.EvalHexEtc1
jsr  pc,GetHexOctBin	; Get and check another digit
bcs  EvalHexDone	; Not bin/oct/hex, exit all done
.EvalHexEtc3
mov  r1,r2		; Use max valid char as bitcounter
br   EvalHexEtc5
.EvalEvalHexEtc4
asl  r4			; Multiply current value by 2
rol  r3
bcs  jmpTooBig		; Overflowed out of b31
.EvalHexEtc5
ror  r2
bcs  EvalEvalHexEtc4	; Loop to multiply by 2, 8 or 16
bic  #&FFF0,r0		; Reduce to binary
bis  r0,r4		; Add in current digit
br   EvalHexEtc1	; Loop for more


; EvalDecimalPrefix - read integer decimal exponent
; -------------------------------------------------
; Called to read E<decimal> from Decimal parser.
; Reads 31-bit decimal number checking for +/- prefix
; r5=>first character
;
.EvalDecimalNeg
jsr  pc,EvalDecimalNext	; Step past '-' and evaluate
jmp  NegateNumber	; Negate number and return
.EvalDecimalPrefix
movb (r5),r0
cmpb r0,#ASC"-"
beq  EvalDecimalNeg	; -number
cmpb r0,#ASC"+"		; +number
bne  EvalDecimalInt
.EvalDecimalNext
inc  r5			; Step past '+' or '-'

; EvalDecimalInt - read integer decimal number
; --------------------------------------------
; Reads 31-bit decimal number to r4:r3
; r5=>first character
; Generates error if number too big
;
.EvalDecimalInt
clr  r4
clr  r3			; Clear accumulator
.EvalDecimalLp
jsr  pc,FetchCheckDigit
bcs  EvalDecimalDone	; CS=no more digits
jsr  pc,EvalTimes10	; r3:r4=r3:r4*10
.EvalDecimalDigit
bic  #&FFF0,r0
add  r0,r4
adc  r3			; r3:r4=r3:r4*10+n
bpl  EvalDecimalLp	; Decimal number only up to b30
.jmpTooBig
jmp  errTooBig
;rts  pc		; MI=Number too big
.EvalDecimalDone
.EvalHexDone
dec  r5			; Point to terminating non-digit
.EvalHexOk
clr  r2			; Type=integer, set flags, PL, VC, EQ, CC
rts  pc

; EvalDecimalVAL - called from VAL to deal with +/- prefix
; --------------------------------------------------------
.EvalDecimalVALNeg
jsr  pc,EvalDecimalVALNext
jmp  NegateNumber
.EvalDecimalVAL
jsr  pc,SkipSpaceThis ; Next	; Skip spaces and get character
cmpb r0,#ASC"-"
beq  EvalDecimalVALNeg	; VAL"- number"
cmpb r0,#ASC"+"
bne  EvalDecimal2	; VAL"number"
.EvalDecimalVALNext
jsr  pc,FetchNextChar	; Step past '-' or '+'
inc  r5			; Balance next dec

; EvalDecimal - <int> <int>.<frac> <int>E<exp> <int>.<frac>E<exp> 
; ---------------------------------------------------------------
; r5=>after first digit
; NB: E<exp> parsed correctly, but fnPower only does positive powers.
;
.EvalDecimal
dec  r5			; Point back to first digit
.EvalDecimal2
jsr  pc,EvalDecimalInt	; Evaluate decimal string
cmpb r0,#ASC"."
bne  EvalCheckExponent	; Not '.', check for 'E'
inc  r5			; Step past '.'
jsr  pc,EnsureFloat

; Parse fractional digits
; -----------------------
.EvalFraction
mov  r3,-(sp)
mov  r4,-(sp)
mov  r2,-(sp)		;            acc=num   sp=>num
clr  r4
clr  r3
mov  #&80,r2		;            acc=1     sp=>num
.EvalFractionLp		;            acc=1     sp=>num
mov  r3,-(sp)
mov  r4,-(sp)
mov  r2,-(sp)		; stack:     acc=1     sp=>1, num
mov  #&CCCD,r4
mov  #&4CCC,r3
mov  #&007C,r2		;            acc=1/10  sp=>1,   num
jsr  pc,fnMultiply	; multiply:  acc=1/10  sp=>num
tst  -(sp)		;            acc=1/10  sp=>xxx, num
jsr  pc,SwapStack	; swap:      acc=num   sp=>xxx, 1/10
mov  6(sp),-(sp)
mov  6(sp),-(sp)
mov  6(sp),-(sp)
tst  -(sp)		; dup:       acc=num   sp=>xxx,  1/10, xxx, 1/10
jsr  pc,SwapStack	; swap:      acc=1/10  sp=>xxx,  num,  xxx, 1/10
tst  (sp)+		;                      sp=>num,  xxx,  1/10
mov  r3,-(sp)		;                      sp=>x,    num,  xxx, 1/10
;mov  r3,(sp)
mov  r4,-(sp)		;                      sp=>xx,   num,  xxx, 1/10
mov  r2,-(sp)		; stack:     acc=1/10  sp=>1/10, num,  xxx, 1/10
jsr  pc,FetchCheckDigit	; Get next digit
bcs  EvalFractionDone
bic  #&FFF0,r0
mov  r0,r4
clr  r3
clr  r2			;            acc=d         sp=>1/10, num, xxx, 1/10
jsr  pc,fnMultiply	; multiply:  acc=d/10      sp=>num,  xxx, 1/10
mov  #&80,r0		; b7=1, not compare
jsr  pc,fnAdd		; add:       acc=num+d/10  sp=>xxx,  1/10
jsr  pc,SwapStack	; swap:      acc=1/10      sp=>xxx,  num+d/10
tst  (sp)+		;            acc=1/10      sp=>num+d/10
br   EvalFractionLp

.EvalFractionDone	;            acc=1/10      sp=>1/10, num,  xxx, 1/10
add  #6,sp		; Drop 1/10
mov  (sp)+,r2		; Pop stacked number
mov  (sp)+,r4
mov  (sp)+,r3
add  #8,sp		; Drop xxx and 1/10
dec  r5			; Point to terminating character
.EvalCheckExponent
bic  #&20,r0
cmpb r0,#ASC"E"		; Check for E<num>
bne  EvalExponentDone	; Return with <num>
;.EvalExponent
inc  r5			; Step past 'E'
jsr  pc,EnsureFloat	; Ensure floating point format
mov  r3,-(sp)
mov  r4,-(sp)
mov  r2,-(sp)		; Stack number
jsr  pc,EvalDecimalPrefix ; Evalute integer exponent
clr  -(sp)
mov  #10,-(sp)
clr  -(sp)		; Stack 10
jsr  pc,fnPower		; Do 10^(exponent) - NB Power currently only does +ve powers
jsr  pc,fnMultiply	; Do 10^(exponent) * <decimal>
.EvalExponentDone
tst  r2			; Set flags
rts  pc

.EvalTimes10		; r3:r4=r3:r4*10, corrupts r1,r2
mov r4,r2
mov r3,r1
clc
rol r4
rol r3			; r3:r4=*2
rol r4
rol r3			; r3:r4=*4
add r2,r4
adc r3
add r1,r3		; r3:r4=*5
rol r4
rol r3			; r3:r4=*10
;.EvalTimes10Over
rts pc			; CC=number ok, CS=too big


