changeset 98:6837d10b67fb

Add integer <-> float conversion routines and combine some code for parsing
author William Astle <lost@l-w.ca>
date Sun, 22 Oct 2023 23:54:24 -0600
parents b375a38b2b1a
children 4d7fa11ebe3f
files src/fps.s src/number.s
diffstat 2 files changed, 194 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/src/fps.s	Sun Oct 22 21:15:14 2023 -0600
+++ b/src/fps.s	Sun Oct 22 23:54:24 2023 -0600
@@ -340,7 +340,10 @@
 ; significand will have its leftmost digit set to 1.
 ;
 ; This will trigger an overflow if the decimal exponent exceeds the allowed range.
-fps_normalize   clrb                            ; initialize the exponent adjustment counter
+fps_normalize   bsr fps_normalizea0             ; do the normalization in fpa0
+                leax ,y                         ; point to the desired destination
+                jmp fps_pack0                   ; go pack the result to the correct destination
+fps_normalizea0 clrb                            ; initialize the exponent adjustment counter
 fps_normalize0  lda fpa0+fpa.sig                ; do we have a nonzero digit in the first pair?
                 bne fps_normalize1              ; brif so
                 lda fpa0+fpa.sig+1              ; shift everything left 2 spaces
@@ -367,8 +370,7 @@
                 bgt fps_normalize0              ; brif not
 fps_normalize4  clr fpa0+fpa.exp                ; set result to zero
                 clr fpa0+fpa.sign
-fps_normalize5  leax ,y                         ; point to return location
-                jmp fps_pack0                   ; return result
+fps_normalize5  rts
 fps_normalize1  bita #0xf0                      ; is the high digit zero?
                 bne fps_normalize3              ; brif not
                 lsl fpaextra                    ; only need to shift one extra position here since there won't be more shifts
@@ -885,4 +887,173 @@
                 stb fpaextra+1
                 sta fpaextra+3                  ; enable the "E" notation with the correct exponent
                 bra fps_toascii5                ; actually convert the number
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Convert a 32 bit integer at (X) to BCD floating point. This requires converting the 32 bit binary value to BCD which
+; can be done using the double/dabble algorithm. fpa0 and fpaextra is used as temporary storage. X must point to a value.
+; Enter at fps_fromuint to treat the 32 value as unsigned.
+fps_fromint     ldb val.int,x                   ; is the integer negative?
+                bpl fps_fromuint                ; brif not
+                sex                             ; set sign to negative
+                sta fpa0+fpa.sign
+                ldd zero                        ; negate the value and store it to temporary location
+                subd val.int+2,x
+                std fpaextra+2
+                ldd zero
+                sbcb val.int+1,x
+                sbca val.int,x
+                std fpaextra
+                bra fps_fromint0                ; go finish the conversion
+fps_fromuint    clr fpa0+fpa.sign               ; set sign to positive
+                ldd val.int+2,x                 ; copy value to temporary accumulator
+                std fpaextra+2
+                ldd val.int,x
+                std fpaextra
+fps_fromint0    leay ,x                         ; set result destination pointer
+                ldd zero                        ; zero out destination
+                std fpa0+fpa.sig
+                std fpa0+fpa.sig+2
+                sta fpa0+fpa.sig+4
+                ldd #0x5f20                     ; set exponent for decimal right of significand and 32 bit shifts
+                sta fpa0+fpa.exp                ; save exponent
+                stb fpaextra+5                  ; save counter
+                bra fps_fromint2                ; skip digit check on the first iteration since none need adjustment
+fps_fromint1    ldu #fpa0+fpa.sig               ; point to significand
+                bsr fps_fromint3                ; do adjustments, 5 bytes worth of digits
+                bsr fps_fromint3
+                bsr fps_fromint3
+                bsr fps_fromint3
+                bsr fps_fromint3
+fps_fromint2    lsl fpaextra+3                  ; shift left
+                rol fpaextra+2
+                rol fpaextra+1
+                rol fpaextra
+                rol fpa0+fpa.sig+4
+                rol fpa0+fpa.sig+3
+                rol fpa0+fpa.sig+2
+                rol fpa0+fpa.sig+1
+                rol fpa0+fpa.sig
+                dec fpaextra+5                  ; done all digits?
+                bne fps_fromint1                ; brif not
+                jsr fps_normalizea0             ; go normalize the result in fpa0
+                ldd fpa0+fpa.sig                ; copy result to destination
+                std val.fpssig,y
+                ldd fpa0+fpa.sig+2
+                std val.fpssig+2,y
+                lda fpa0+fpa.sig+4
+                sta val.fpssig+4,y
+                lda fpa0+fpa.exp
+                sta val.fpsexp,y
+                lda fpa0+fpa.sign
+                sta val.fpssign,y
+                lda #valtype_float              ; set result type to floating point
+                sta val.type,y
+                rts
+; This is the digit check for the double-dabble algorith. The left digit check is only concerned if the left digit
+; is 5 or higher. Since we can make that comparison without masking the bits, we don't bother. For the right digit,
+; however, we do have to mask the upper digit off. It saves and ANDA and a subsequent indexed reload.
+fps_fromint3    clrb                            ; set adjustment to none
+                lda ,u                          ; get digits
+                cmpa #0x50                      ; is digit 5 or higher?
+                blo fps_fromint4                ; brif not
+                ldb #0x30                       ; set adjustment factor for first digit
+fps_fromint4    anda #0x0f                      ; keep low digit
+                cmpa #5                         ; is it 5 or higher?
+                blo fps_fromint5                ; brif not
+                orb #0x03                       ; set adjustment for second digit
+fps_fromint5    addb ,u                         ; add adjustment to digits
+                stb ,u+                         ; save new digit values and move on
+                rts
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Convert the floating point value in the value accumulator at (X) to a 32 bit signed integer. Will trigger an overflow
+; if the magnitude is too large and it will truncate (round toward zero) any fractional value.
+fps_toint       lda val.fpsexp,x                ; copy the exponent, sign, and significand to fpa0
+                sta fpa0+fpa.exp
+                ldd val.fpssig,x
+                std fpa0+fpa.sig
+                ldd val.fpssig+2,x
+                std fpa0+fpa.sig+2
+                lda val.fpssig+4,x
+                sta fpa0+fpa.sig+4
+                lda val.fpssign,x
+                sta fpa0+fpa.sign
+                bsr fps_tointa0                 ; convert value to integer in fpa0 significand
+                lda #valtype_int                ; set result to integer
+                sta val.type,x
+                ldd fpa0+fpa.sig+1
+                std val.int,x
+                ldd fpa0+fpa.sig+3
+                std val.int+2,x
+                rts
+fps_tointa0     ldb fpa0+fpa.exp                ; compare exponent (is it too big?)
+                cmpb #64+9
+                bne fps_toint0
+                ldd fpa0+fpa.sig                ; compare top of significand
+                cmpd #0x2147
+                bne fps_toint0
+                ldd fpa0+fpa.sig+2              ; compare middle of significand
+                cmpd #0x4836
+                bne fps_toint0
+                ldd fpa0+fpa.sig+4              ; compare bottom of significand plus extra digits
+                cmpd #0x4800
+fps_toint0      blt fps_toint1                  ; brif it fits positive or negative
+                lbgt OVERROR                    ; brif it doesn't fit negative either
+                ldb fpa0+fpa.sign               ; do we want negative?
+                lbpl OVERROR                    ; brif not - it doesn't fit
+; Enter here if we already know that the value will fit
+fps_toint1      ldb #64+9                       ; biased exponent needed for decimal point to the right of significand
+                subb fpa0+fpa.exp               ; number of digit shifts needed to denormalize
+                beq fps_toint3                  ; brif already denormalized
+                lslb                            ; do 4 shifts per digit; we're going to simply lose extra digits
+                lslb
+fps_toint2      lsr fpa0+fpa.sig                ; shift a digit right
+                ror fpa0+fpa.sig+1
+                ror fpa0+fpa.sig+2
+                ror fpa0+fpa.sig+3
+                ror fpa0+fpa.sig+4
+                decb                            ; done all shifts?
+                bne fps_toint2
+; Now convert BCD digit sequence in fpa0 significand to binary value in the low 32 bits of the significand
+fps_toint3      ldb #32                         ; 32 bit shifts needed for whole significand
+                stb fpa0+fpa.extra              ; use extra precision byte as counter
+fps_toint4      lsr fpa0+fpa.sig                ; shift a bit into the binary result
+                ror fpa0+fpa.sig+1
+                ror fpa0+fpa.sig+2
+                ror fpa0+fpa.sig+3
+                ror fpa0+fpa.sig+4
+                ror fpa0+fpa.extra+1
+                ror fpa0+fpa.extra+2
+                ror fpa0+fpa.extra+3
+                ror fpa0+fpa.extra+4
+                ldu #fpa0+fpa.sig               ; point to BCD digits
+fps_toint5      lda ,u                          ; get byte to check
+                beq fps_toint8                  ; short circuit check if digits are 0
+                anda #0x88                      ; keep bit 3 of each digit; adjustment on >= 8
+                lsra                            ; shift over and mulply by adjustment factor
+                lsra
+                lsra
+                ldb #3                          ; the adjustment is a subtraction by 3
+                mul
+                negb                            ; now subtract from digit
+                addb ,u
+                stb ,u+
+fps_toint6      cmpu #fpa0+fpa.sig+5            ; done all 5 bytes?
+                blo fps_toint5                  ; brif not
+                dec fpa0+fpa.extra              ; done all bits?
+                bne fps_toint6                  ; brif not
+                ldb fpa0+fpa.sign               ; do we want negative?
+                bpl fps_toint7                  ; brif not
+                ldd zero                        ; negate the value through subtracting from 0
+                subd fpa0+fpa.extra+3
+                std fpa0+fpa.extra+3
+                ldd zero
+                sbcb fpa0+fpa.extra+1
+                sbca fpa0+fpa.extra
+                std fpa0+fpa.sig+1
+fps_toint7      ldd fpa0+fpa.extra+1            ; put result in the significand
+                std fpa0+fpa.sig+1
+                ldd fpa0+fpa.extra+3
+                std fpa0+fpa.sig+3
+                rts
+fps_toint8      leau 1,u                        ; move to next digit
+                bra fps_toint6                  ; go back to mainline
                 *pragmapop list
--- a/src/number.s	Sun Oct 22 21:15:14 2023 -0600
+++ b/src/number.s	Sun Oct 22 23:54:24 2023 -0600
@@ -267,60 +267,27 @@
                 lda fpa0+fpa.exp                ; put the bias into the exponent
                 adda #64
                 sta fpa0+fpa.exp
-                ldy #val0+val.value             ; normalize/round and return the result
-                jmp fps_normalize
+                jsr fps_normalizea0             ; normalize fpa0
+                ldd fpa0+fpa.sig                ; copy result to the right place
+                std val0+val.fpssig
+                ldd fpa0+fpa.sig+2
+                std val0+val.fpssig+2
+                lda fpa0+fpa.sig+4
+                sta val0+val.fpssig+4
+                lda fpa0+fpa.exp
+                sta val0+val.fpsexp
+                lda fpa0+fpa.sign
+                sta val0+val.fpssign
+                rts
 val_parsenum14  lda #valtype_int                ; set value type to integer
                 sta val0+val.type
-                ldb #9                          ; exponent needed for decimal point to the right of significand
-                subb fpa0+fpa.exp               ; number of digit shifts needed to denormalize
-                beq val_parsenum16              ; brif already denormalized
-                lslb                            ; do 4 shifts per digit
-                lslb
-val_parsenum15  lsr fpa0+fpa.sig                ; shift a digit right
-                ror fpa0+fpa.sig+1
-                ror fpa0+fpa.sig+2
-                ror fpa0+fpa.sig+3
-                ror fpa0+fpa.sig+4
-                decb                            ; done all shifts?
-                bne val_parsenum15
-; Now convert BCD digit sequence in fpa0 significand to binary value in val0
-val_parsenum16  ldb #32                         ; 40 bit shifts needed for whole significand
-                stb fpa0+fpa.extra              ; use extra precision byte as counter
-val_parsenum17  lsr fpa0+fpa.sig                ; shift a bit into the binary result
-                ror fpa0+fpa.sig+1
-                ror fpa0+fpa.sig+2
-                ror fpa0+fpa.sig+3
-                ror fpa0+fpa.sig+4
-                ror val0+val.int
-                ror val0+val.int+1
-                ror val0+val.int+2
-                ror val0+val.int+3
-                ldx #fpa0+fpa.sig               ; point to BCD digits
-val_parsenum18  lda ,x                          ; get byte to check
-                beq val_parsenum20              ; short circuit check if digits are 0
-                anda #0x88                      ; keep bit 3 of each digit; adjustment on >= 8
-                lsra                            ; shift over and mulply by adjustment factor
-                lsra
-                lsra
-                ldb #3                          ; the adjustment is a subtraction by 3
-                mul
-                negb                            ; now subtract from digit
-                addb ,x
-                stb ,x+
-val_parsenum18a cmpx #fpa0+fpa.sig+5            ; done all 5 bytes?
-                blo val_parsenum18              ; brif not
-                dec fpa0+fpa.extra              ; done all bits?
-                bne val_parsenum17              ; brif not
-                ldb fpa0+fpa.sign               ; do we want negative?
-                bpl val_parsenum19              ; brif not
-                ldd zero                        ; negate the value through subtracting from 0
-                subd val0+val.int+2
+                ldb fpa0+fpa.exp                ; add the exponent bias in
+                addb #64
+                stb fpa0+fpa.exp
+                jsr fps_toint1                  ; go convert to an integer
+                ldd fpa0+fpa.sig+1              ; copy result to val0
+                std val0+val.int
+                ldd fpa0+fpa.sig+3
                 std val0+val.int+2
-                ldd zero
-                sbcb val0+val.int+1
-                sbca val0+val.int
-                std val0+val.int
-val_parsenum19  rts
-val_parsenum20  leax 1,x                        ; move to next digit
-                bra val_parsenum18a             ; go back to mainline
+                rts
                 *pragmapop list