# HG changeset patch # User William Astle # Date 1698040464 21600 # Node ID 6837d10b67fb67b4368f669f58af41436e8ea98d # Parent b375a38b2b1a23d00cd09dffceba1cb7ef4282d5 Add integer <-> float conversion routines and combine some code for parsing diff -r b375a38b2b1a -r 6837d10b67fb src/fps.s --- 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 diff -r b375a38b2b1a -r 6837d10b67fb src/number.s --- 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