changeset 72:f492fa6f6dc8

First pass implementation of addition and subtraction
author William Astle <lost@l-w.ca>
date Sun, 02 Jul 2023 17:21:11 -0600
parents f4b2406d7352
children 2d52cd154ed1
files src/lwbasic.s
diffstat 1 files changed, 140 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/lwbasic.s	Sun Jul 02 02:33:53 2023 -0600
+++ b/src/lwbasic.s	Sun Jul 02 17:21:11 2023 -0600
@@ -1707,6 +1707,7 @@
                 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)
@@ -1719,7 +1720,15 @@
                 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
@@ -1814,9 +1823,9 @@
 ; 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 SNERROR
+                fdb oper_plus
                 fcb 0x79                        ; subtraction
-                fdb SNERROR
+                fdb oper_minus
                 fcb 0x7b                        ; multiplication
                 fdb SNERROR
                 fcb 0x7b                        ; division
@@ -1859,6 +1868,20 @@
                 suba #-'0
 setcifdigit0    rts
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; 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      bsr val_matchtypes              ; go match data types
+                jmp val_add                     ; go add the values
+; binary minus: subtraction
+oper_minus      bsr val_matchtypes              ; go match data types
+                jmp val_sub                     ; do subtraction
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Arithmetic package
 ;
 ; This section contains routines that handle floating point and integer arithmetic.
@@ -1993,6 +2016,121 @@
                 clr val.fpsign,x
                 puls b,pc                       ; clean up stack and return
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Addition and subtraction of values; must enter with values of matching types
+;
+; Calculates (X) + (U) -> (Y) (addition)
+; Calculates (X) - (U) -> (Y) (subtraction)
+val_add         ldb val.type,x                  ; get type of left operand
+                stb val.type,y                  ; set result type
+                cmpb #valtype_float             ; is it float?
+                beq fp_add                      ; brif so
+                ldd val.int+2,x                 ; do the addition
+                addd val.int+2,u
+                std val.int+2,y
+                ldd val.int,x
+                adcb val.int+1,u
+                adca val.int,u
+                std val.int,y
+                lbvs OVERROR                    ; brif calculation overflowed
+                rts
+val_sub         ldb val.type,x                  ; get type of left operand
+                stb val.type,y                  ; set result type
+                cmpb #valtype_float             ; floating point?
+                beq fp_sub                      ; brif so
+                ldd val.int+2,x                 ; do the subtraction
+                subd val.int+2,u
+                std val.int+2,y
+                ldd val.int,x
+                sbcb val.int+1,u
+                sbca val.int,u
+                std val.int,y
+                lbvs OVERROR                    ; brif overflow
+                rts
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; FP subtraction: just invert the sign of the second operand and add; operands must be writable and they should be
+; considered to be clobbered
+fp_sub          com val.fpsign,u                ; negate right operand
+                ; fall through to addition
+; FP addition: this requires that *both operands* are writable and they may be clobbered
+fp_add          ldb val.fpexp,u                 ; is the second operand zero?
+                beq fp_add0                     ; brif so - it's a no-op - copy the left operand to the output
+                lda val.fpexp,x                 ; is left operand zero?
+                bne fp_add1                     ; brif not - we have to do the add
+                leau ,x                         ; copy the right operand to the output
+fp_add0         ldd ,u                          ; copy the value across
+                std ,y
+                ldd 2,u
+                std 2,y
+                ldd 4,u
+                std 4,y
+                rts
+fp_add1         subb val.fpexp,x                ; get difference in exponents
+                beq fp_add6                     ; brif they're the same - no denormalizing is needed
+                bhi fp_add2                     ; brif second one is bigger, need to right-shift the mantissa of first
+                exg x,u                         ; swap the operands (we can do that for addition)l second is now biggest
+                negb                            ; invert the shift count
+fp_add2         cmpb #32                        ; are we shifting more than 32 bits?
+                blo fp_add0                     ; brif so - we're effectively adding zero so bail out
+fp_add3         cmpb #8                         ; have 8 bits to move?
+                bhs fp_add5                     ; brif not
+                lda val.fpmant+2,x              ; shift 8 bits right
+                sta val.fpmant+3,x
+                lda val.fpmant+1,x
+                sta val.fpmant+2,x
+                lda val.fpmant,x
+                sta val.fpmant+1,x
+                clr val.fpmant,x
+                subb #8                         ; account for 8 shifts
+                bra fp_add3                     ; see if we have a whole byte to shift
+fp_add4         lsr val.fpmant,x                ; shift right one bit
+                ror val.fpmant+1,x
+                ror val.fpmant+2,x
+                ror val.fpmant+3,x
+fp_add5         decb                            ; done all shifts?
+                bmi fp_add4                     ; brif not - do a shift
+fp_add6         ldb val.fpexp,u                 ; set exponent of result
+                stb val.fpexp,y
+                ldb val.fpsign,u                ; fetch sign of larger value
+                stb val.fpsign,y                ; set result sign
+                cmpb val.fpsign,x
+                bne fp_add8                     ; brif not - need to subtract the operands
+                ldd val.fpmant+2,u              ; add the mantissas
+                addd val.fpmant+2,x
+                std val.fpmant+2,y
+                ldd val.fpmant,u
+                adcb val.fpmant+1,x
+                adca val.fpmant,x
+                std val.fpmant,y
+                clrb                            ; clear extra precision bits
+                bcc fp_add7                     ; brif no carry
+                ror val.fpmant,y                ; shift carry into mantissa
+                ror val.fpmant+1,y
+                ror val.fpmant+2,y
+                ror val.fpmant+3,y
+                rorb                            ; keep bits for founding
+                inc val.fpexp,y                 ; bump exponent to account for shift
+                lbeq OVERROR                    ; brif it overflowed
+fp_add7         leax ,y                         ; point to result
+                jmp fp_normalize                ; go normalize the result
+fp_add8         ldd val.fpmant+2,u              ; subtract operands
+                subd val.fpmant+2,x
+                std val.fpmant+2,y
+                ldd val.fpmant,u
+                sbcb val.fpmant+1,x
+                sbca val.fpmant,x
+                std val.fpmant,y
+                bcc fp_add7                     ; brif we didn't carry - no need to fix up
+                ldd zero                        ; negate the mantissa bits since we use sign+magnitude
+                subd val.fpmant+2,y
+                std val.fpmant+2,y
+                ldd zero
+                sbcb val.fpmant+1,y
+                sbca val.fpmant,y
+                std val.fpmant,y
+                neg val.fpsign,y                ; invert sign of result since we went past zero
+                clrb                            ; clear extra precision bits
+                bra fp_add7                     ; go normalize the result and return
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Pack a floating point value at (X)
 fp_packval      ldb val.fpsign,x                ; get sign
                 bmi fp_packval                  ; brif negative - the default 1 bit will do