view src/expr.s @ 80:bb50ac9fdf37

Checkpoint with very basic integer and floating point arithmetic, untested This commit has implementations for floating point add, subtract, multiply, and divide, along with 32 bit signed integer equivalents. These can probably be optimized and they are untested.
author William Astle <lost@l-w.ca>
date Sat, 07 Oct 2023 02:56:59 -0600
parents ba559f231929
children f959c92bc329
line wrap: on
line source

                *pragmapush list
                *pragma list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The LET command which is the default if no token begins a statement
cmd_let         jmp SNERROR                     ; not yet implemented
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Expression Evaluation Package
;
; This is the expression evaluator. It handles everything from parsing numbers to dispatching function calls. The main
; entry point is eval_expr which will evaluate an arbitrary expression. It returns as soon as it reaches something it
; doesn't understand as part of an expression.
;
; The special handling for relational operators is required because Basic allows them in all 
eval_expr       clrb                            ; flag previous operator as minimum precdence (end of expression)
eval_expraux    jsr eval_term                   ; evaluate the first term of the expression
eval_expr0      jsr curchar                     ; fetch current input
                beq eval_expr1                  ; brif end of expression - we're done
                cmpa #tok_or                    ; is it above operators?
                bhi eval_expr1                  ; brif so
                suba #tok_plus                  ; offset to zero for first operator token
                bcc eval_expr2                  ; brif it is an operator
eval_expr1      rts
eval_expr2      pshs b                          ; save previous operator precedence
                ldx #oper_tab                   ; point to operator table
                tfr a,b                         ; shift to B for "ABX"
                abx                             ; add three times (3 bytes per entry)
                abx                             ; OBS: TFR + ABX + ABX + ABX is faster than LDB + MUL + ABX
                abx                             ; now X points to the operator entry in the table
                ldb ,x                          ; get precedence of current operation
                cmpb ,s                         ; is it higher than the current operation?
                bhi eval_expr3                  ; brif so - process this operator
                puls b,pc                       ; return current value to complete previous operation
eval_expr3      jsr nextchar                    ; eat the operator token
                ldx 1,x                         ; get handler address of this operator
                leas -val.size,s                ; make room for the result accumulator
                pshs x                          ; save handler address for later
                lda val0+val.type               ; get current value type
                ldx val0                        ; get value accumlator contents (6 bytes)
                ldy val0+2
                ldu val0+4
                pshs a,x,y,u                    ; save it on the stack
                jsr eval_expraux                ; evaluate the following term and higher precedence expressions
                puls a,x,y,u                    ; get back saved value
                stx val1                        ; save it to the second value accumulator
                sty val1+2
                stu val1+4
                sta val1+val.type               ; save previous value type
                ldx #val1                       ; point to left operand
                ldu #val0                       ; point to right operand
                leay 2,s                        ; point to return value location
                jsr [,s++]                      ; go handle the operator
                puls a,x,y,u                    ; get return value
                sta val0
                stx val0+1
                sty val0+3
                stu val0+5
                puls b                          ; get back the previous operator precedence
                bra eval_expr0                  ; go process another operator or end of expression
eval_term       jsr curchar                     ; get current input character
                beq eval_term0                  ; brif end of input - this is an error
                bcs eval_number                 ; brif digit - we have a number
;                bmi eval_func                   ; brif we have a token - handle function call
                cmpa #'.                        ; decimal point?
                beq eval_number                 ; brif so - evaluate number
                cmpa #'-                        ; negative sign?
                beq eval_number                 ; brif so - evaluate number
                cmpa #'+                        ; positive sign?
                beq eval_number                 ; brif so - evaluate number
eval_term0      jmp SNERROR                     ; we have something unrecognized - raise error
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Evaluate a numeric constant. This process works as follows:
;
; 0. Clear the value to a zero integer
; 1. Check for signs and flag appropriately
; 2. Read character
; 3. If decimal or exponential indicator, go to step 6
; 4. If not digit, return integer result
; 5. Multiply accumulator by 10 and add digit value; go back to step 2
; 6. Convert accumulator to floating point; set accumulated decimal exponent and decimal flag to 0
; 7. If decimal point, flag decimal seen (0xff) and go to step 15 (or raise error if second decimal point)
; 8. If digit, multiply by 10 and add digit value; go to step 15
; 9. If E or e, go handle decimal exponent at step 12
; 10. Apply accumulated decimal exponent to the result (through multiplication/division by 10)
; 11. Return floating point result
; 12. Read character
; 13. If not digit, go handle return at step 10
; 14. Multiply exponent accumulator by 10 and add digit value; raise error on overflow or go back to step 12
; 15. Read a character and go to step 7
;
; If the result ends up being larger than a floating point value can hold, return Overflow
eval_number     
                if 0
                ldb #valtype_int                ; flag result as an integer
                stb val0+val.type
                ldx zero                        ; blank out the value except type
                stx val0
                stx val0+2
                stx val0+4
                bra eval_number1                ; go do the parsing
eval_number0    jsr nextchar                    ; fetch next input
                beq eval_number6                ; brif end of expression - bail
eval_number1    cmpa #'-                        ; negative (ascii sign)?
                beq eval_number3                ; brif so
                cmpa #tok_minus                 ; negative (operator negative)?
                bne eval_number2                ; brif not
eval_number3    com val0+val.fpssign             ; invert sign (multiple negatives will flip this multiple times)
                bra eval_number0                ; deal with next input
eval_number2    cmpa #'+                        ; unary +?
                beq eval_number0                ; brif so - skip it
                cmpa #tok_plus                  ; unary + (operator plus)?
                beq eval_number0                ; brif so - skip it
eval_number5    cmpa #'.        	; decimal point?
	beq eval_number8                ; brif decimal - force float
                cmpa #'0                        ; is it a number?
                blo eval_number6                ; brif below digit
                cmpa #'9                        ; is it still a number?
                bhi eval_number6                ; brif above digit
                suba #'0                        ; offset to binary digit value
                pshs a                          ; save digit value
                ldx val0+val.int                ; get current value for later (for quick multiply by 10)
                ldd val0+val.int+2
                pshs d,x                        ; stored with words swapped on stack for efficiency for later
                lsl val0+val.int+3              ; times 2
                rol val0+val.int+2
                rol val0+val.int+1
                rol val0+val.int
                rol val0+val.fpsexp              ; overflow into fp exponent
                lsl val0+val.int+3              ; times 4
                rol val0+val.int+2
                rol val0+val.int+1
                rol val0+val.int
                rol val0+val.fpsexp              ; brif overflowed
                ldd val0+val.int+2              ; times 5 (add original value)
                addd ,s++
                std val0+val.int+2
                ldd val0+val.int
                adcb 1,s
                adca ,s++
                std val0+val.int
                ldb val0+val.fpsexp              ; and handle overflow bits
                adcb #0
                stb val0+val.fpsexp
                lsl val0+val.int+3              ; times 10
                rol val0+val.int+2
                rol val0+val.int+1
                rol val0+val.int
                rol val0+val.fpsexp
                ldd val0+val.int+2              ; get low word
                addb ,s+                        ; add in current digit
                adca #0
                std val0+val.int+2
                ldd val0+val.int
                adcb #0
                adca #0
                std val0+val.int
                lda val0+val.fpsexp              ; and handle overflow
                adca #0
                sta val0+val.fpsexp
                bne eval_number11               ; if we overflowed, go continue parsing as floating point
                lda val0+val.int                ; get back high byte and check for overflow
                bpl eval_number4                ; brif we haven't wrapped negative
                cmpd #0x8000                    ; is it valid negative two's complement?
                bhi eval_number11               ; brif not - we're in floating point territory
                ldd val0+val.int+2              ; is it still valid two's complement (max negative)?
                bne eval_number11               ; brif not - we're in floating point territory
eval_number4    jsr nextchar                    ; fetch next input character
                bra eval_number5                ; go handle it
eval_number6    cmpa #'E                        ; base 10 exponent?
                beq eval_number8                ; brif so
                cmpa #'e                        ; base 10 exponent in lower case?
                beq eval_number8                ; brif so
                ldb val0+val.fpssign             ; did we want a negative value?
                beq eval_number7                ; brif not
                jsr val_negint32                ; negate the 32 bit integer to correct two's complement
eval_number7    clr val0+val.fpssign             ; clear sign bits for book keeping
                rts
eval_number11   jsr nextchar                    ; each the character already processed
eval_number8    lda #0x9f                       ; exponent if binary point is to the right of the mantissa                           
                clr val0extra                   ; clear extra precision bits for val0
                ldb #valtype_float              ; flag value as floating point
                stb val0+val.type
                ldb val0+val.fpsexp              ; do we have overflow bits to shift?
                beq eval_number10               ; brif not
eval_number9    inca                            ; bump exponent to account for extra bits
                lsrb                            ; shift some bits over
                ror val0+val.fpssig
                ror val0+val.fpssig+1
                ror val0+val.fpssig+2
                ror val0+val.fpssig+3
                ror val0extra
                tstb                            ; all bits shifted into mantissa?
                bne eval_number9                ; brif not
eval_number10   sta val0+val.fpsexp              ; save adjusted exponent
                ldx #val0                       ; normalize the result for further operations
                jsr fp_normalize
                clr ,-s                         ; flag for decimal point seen
                clr ,-s                         ; current decimal exponent value
                jsr curchar                     ; get current input character
                bra eval_number20               ; go evaluate the floating point value
eval_number40   jsr nextchar                    ; fetch next input
eval_number20   bcs eval_number29               ; brif digit
                cmpa #'.                        ; is it a decimal?
                bne eval_number21               ; brif not
                com 1,s                         ; flag decimal seen
                bne eval_number40
                jmp SNERROR                     ; brif unexpected second decimal point
eval_number21   cmpa #'E                        ; decimal exponent?
                beq eval_number26               ; brif so
                cmpa #'e                        ; decimal exponent lower case?
                beq eval_number26
eval_number22   ldb ,s                          ; get decimal exponent count and set flags
                beq eval_number25               ; brif no adjustment needed
                bmi eval_number24               ; brif we need to divide
eval_number23   jsr fp_mul10                    ; multiply by 10
                dec ,s                          ; done?
                bne eval_number23               ; brif not
                rts
eval_number24   jsr fp_div10                    ; divide by 10
                inc ,s                          ; done?
                bne eval_number24               ; brif not
eval_number25   rts
eval_number26   clrb                            ; blank out decimal exponent accumulator
                clr ,-s                         ; set sign positive
                jsr nextchar                    ; get next input
                bcs eval_number28               ; brif digit - positive exponent
                cmpa #'+                        ; positive?
                beq eval_number27               ; brif so - skip it
                cmpa #tok_plus                  ; positive (plus operator)?
                beq eval_number27
                cmpa #'-                        ; negative?
                beq eval_number30               ; brif so
                cmpa #tok_minus                 ; negative (minus operator)?
                bne eval_number31               ; brif not
eval_number30   com ,s                          ; get sign negative
eval_number27   jsr nextchar                    ; get next character
                bcs eval_number28               ; brif digit
eval_number31   lda ,s+                         ; get negative flag, set flags, and clean up the stack
                beq eval_number32               ; brif positive
                negb                            ; we have a negative decimal exponent - handle it
eval_number32   addb ,s                         ; add in decimal exponent adjustment
                stb ,s                          ; save it for cleanup
                bra eval_number22               ; go finish up
eval_number28   suba #'0                        ; digit-ize it
                pshs a                          ; save it for later
                lda #10                         ; multiply value by 10 and add digit
                mul
                addb ,s+
                bpl eval_number27               ; go handle another digit if we didn't overflow negative
OVERROR         ldb #err_ov                     ; flag overflow
                jmp ERROR
eval_number29   ldb ,s                          ; get exponent adjustment
                addb 1,s                        ; subtract if decimal point was seen for later fixup
                stb 1,s
                suba #'0                        ; digit-ize the character
                pshs a                          ; save it for later
                jsr fp_mul10                    ; multiply by 10
                jsr val0toval1                  ; save residue
                puls b                          ; get back digit value
                clra                            ; make it floating point
                std val0+val.int+2
                sta val0+val.int+1
                sta val0+val.int
                jsr val_int32tofloat            ; convert to floating point
                jsr fp_add                      ; add val1 to val0
                bra eval_number40               ; go handle another character
                endc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Operator table
;
; Each entry starts with the precedence value followed by the handler routine. Each handler will receive its left
; operand in val1 and its right operand in val0 and should return its result in val0.
oper_tab        fcb 0x79                        ; addition
                fdb oper_plus
                fcb 0x79                        ; subtraction
                fdb oper_minus
                fcb 0x7b                        ; multiplication
                fdb SNERROR
                fcb 0x7b                        ; division
                fdb SNERROR
                fcb 0x7f                        ; exponentiation
                fdb SNERROR
                fcb 0x64                        ; less than
                fdb SNERROR
                fcb 0x64                        ; equal to
                fdb SNERROR
                fcb 0x64                        ; greater than
                fdb SNERROR
                fcb 0x64                        ; less than or equal to
                fdb SNERROR
                fcb 0x64                        ; greater than or equal to
                fdb SNERROR
                fcb 0x64                        ; not equal to
                fdb SNERROR
                fcb 0x50                        ; boolean AND
                fdb SNERROR
                fcb 0x46                        ; boolean OR
                fdb SNERROR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Operator handling routines
;
; binary plus: addition and concatenation
oper_plus       ldb val.type,x                  ; get type of the left operand
                cmpb valtype_string             ; is it string?
                bne oper_plus0                  ; brif not
                cmpb val.type,u                 ; is right operand also string?
                lbeq SNERROR                    ; brif so - do string concatenation
oper_plus0      jsr val_matchtypes              ; go match data types
                jmp val_add                     ; go add the values
; binary minus: subtraction
oper_minus      jsr val_matchtypes              ; go match data types
                jmp val_sub                     ; do subtraction
                *pragmapop list