Mercurial > hg > index.cgi
view src/number.s @ 84:f959c92bc329
New first pass implementation of number parsing, untested
Rewrite number parsing using recently constructed infrastructure. The result
is untested.
author | William Astle <lost@l-w.ca> |
---|---|
date | Sun, 08 Oct 2023 00:17:20 -0600 |
parents | bb50ac9fdf37 |
children | 663d8e77b579 |
line wrap: on
line source
*pragmapush list *pragma list ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Arithmetic package ; ; This section contains routines that handle floating point and integer arithmetic. It mostly delegates to int.s and ; fps.s. ; ; Most routines take a pointer to a value accumulator in X. Some take two pointers with the second in U. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Match operands for a numeric calculation. This works as follows: ; ; * If both operands are the same, ensure the type is numeric and return ; * If one operand is floating point, convert the other to floating point, as long as it is numeric ; * If one or both operands are not numeric, raise a type mismatch ; The operands are in (X) and (U) val_matchtypes ldb val.type,x ; get the type of first argument cmpb #valtype_int ; is it integer? beq val_matchtypes0 ; brif so cmpb #valtype_float ; is it floating point? beq val_matchtypes1 ; brif so TMERROR ldb #err_tm ; raise a type mismatch jmp ERROR val_matchtypes0 ldb val.type,u ; get type of second operand cmpb #valtype_int ; is it integer? bne val_matchtypes2 ; brif not val_matchtypes3 rts ; both types int - we're good so return val_matchtypes2 cmpb #valtype_float ; is it floating point? bne TMERROR ; brif not - raise error pshs x ; save X which may be clobbered leay ,x ; point to input operand as destination for conversion jsr fps_fromint32 ; convert first argument to floating point puls x,pc ; restore second operand pointer and return val_matchtypes1 ldb val.type,u ; get second argument type cmpb #valtype_float ; is it floating point? beq val_matchtypes3 ; brif so - we're good cmpb #valtype_int ; is it integer? bne TMERROR ; brif not - invalid type combination pshs x ; save X which mill be clobbered leax ,u ; convert (U) to floating point leay ,u jsr fps_fromint32 puls x,pc ; restore argument pointer and return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Addition and subtraction of values; must enter with values of matching types and the result type already set ; to the correct type. ; ; Calculates (X) + (U) -> (Y) (addition) ; Calculates (X) - (U) -> (Y) (subtraction) val_add ldb val.type,x ; get type of left operand cmpb valtype_int ; is it integer? lbeq int32_add ; brif so - do integer addition cmpb #valtype_float ; floating point? lbeq fps_add ; brif so - do floating point addition jmp TMERROR ; we have a type we don't understand val_sub ldb val.type,x ; get type of left operand cmpb valtype_int ; is it integer? lbeq int32_sub ; brif so - do integer addition cmpb #valtype_float ; floating point? lbeq fps_sub ; brif so - do floating point addition jmp TMERROR ; we have a type we don't understand ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Multiplication ; ; Calculates (X) × (U) -> (Y) ; ; The result might overflow the integer type. In this case, an actual overflow error will occur. val_mul ldb val.type,x ; get type of left operand cmpb #valtype_int ; integer? lbeq int32_mul ; brif so - do integer multiplication cmpb #valtype_float ; is it float? lbeq fps_mul ; brif so - do floating point multiplication jmp TMERROR ; have an unhandled type - bail on it ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Division ; ; Calculates (X) ÷ (U) -> (Y) ; ; The integer operation simply truncates the result ("rounds toward zero") val_div ldb val.type,x ; get type of left operand cmpb #valtype_int ; integer? lbeq int32_div ; brif so - do integerdivision cmpb #valtype_float ; floating point? lbeq fps_div ; brif so - do floating point division jmp TMERROR ; unsupported type if 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Modulus - note that this is a division operator returning effectively the remainder, not an absolute value as is ; sometimes meant by "modulus". ; ; Calculates (X) <MOD> (U) -> (Y) ; ; Note: modulus will have the same sign as the quotient so that (U) * [(X) / (U)] + [(X) MOD (U)] gives (X) (integer) ; Note2: the modulus can be calculated on floating point values in which case it will represent the fraction part ; of the quotient multiplied by the divisor, again with the same sign as the quotient val_mod ldb val.type,x ; get type of left operand cmpb #valtype_int ; integer? lbeq int32_mod ; do integer modulus cmpb #valtype_float ; floating point? lbeq fps_mod ; floating point modulus jmp TMERROR ; unsupported type endc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Parse a number to either an integer or a floating point value ; ; First, identify any sign present. Then parse the remainder as an integer until either a decimal point, an exponential ; indicator, or the value gets larger than 32 bits. If any of those eventualities happens, convert to floating point ; and then continue parsing the number as floating point. The result will be stored to (Y). val_parsenum lbeq SNERROR ; brif no numberr to parse ldd zero ; zero out integer value accumulator std fpa0extra std fpa0extra+2 std fpa0extra+4 std fpa0extra+6 sta fpa0extra12 ; zero out result sign to default positive jsr curchar ; get current input character bra val_parsenum1 ; parse flags val_parsenum0 jsr nextchar ; get next input character val_parsenum1 bcs val_parsenum5 ; brif digit beq val_parsenum ; brif end of input cmpa #'. ; decimal? lbeq val_parsefloat ; switch to parsing floating point cmpa #'- ; minus? beq val_parsenum2 ; brif so cmpa #tok_minus ; unary minus operator? bne val_parsenum3 ; brif not val_parsenum2 com fpa0extra12 ; invert current sign bra val_parsenum0 ; go handle more stuff at the start of the number val_parsenum3 cmpa #'+ ; unary +? beq val_parsenum0 ; brif so - skip it cmpa #tok_plus ; unary + operator? beq val_parsenum0 ; brif so - skip it val_parsenum4 lda fpa0extra4 ; is bit 7 of high byte set? bpl val_parsenum4a ; brif not - no overflow ldb fpa0extra12 ; do we want negative? lbpl val_parsefloat ; brif not - we overflowed so convert to floating point anda #0x7f ; lose sign bit then see if any other bits are set ora fpa0extra5 ora fpa0extra6 ora fpa0extra7 lbne val_parsefloat ; brif nonzero bits - too big for max negative 2's complement val_parsenum4a lda fpa0extra12 ; do we want negative? bpl val_parsenum4b ; brif not ldd zero ; negate it subd fpa0extra6 std fpa0extra6 ldd zero sbcb fpa0extra5 sbca fpa0extra4 std fpa0extra4 val_parsenum4b ldd fpa0extra6 ; copy value to result location std val.int+2,y ldd fpa0extra4 std val.int,y lda #valtype_int ; set value type to integer sta val.type,y rts val_parsenum4c jsr nextchar ; fetch next character (after a digit) bcs val_parsenum5 ; it's a digit cmpa #'. ; decimal? beq val_parsefloat ; brif so - handle floating point cmpa #'E ; exponent? beq val_parsefloat ; brif so - handle floating point cmpa #'e ; exponent but lower case? beq val_parsefloat ; brif so - handle floating point bra val_parsenum4 ; unrecognized character - treat as end of number val_parsenum5 suba #'0 ; offset digit to binary pshs a ; save it for later addition ldx fpa0extra4 ; save original value stx fpa0extra8 ldx fpa0extra6 stx fpa0extra10 lsl fpa0extra7 ; shift partial result left (times 2) rol fpa0extra6 rol fpa0extra5 rol fpa0extra4 rol fpa0extra3 lsl fpa0extra7 ; shift partial result left (times 4) rol fpa0extra6 rol fpa0extra5 rol fpa0extra4 rol fpa0extra3 ldd fpa0extra6 ; add in original value (time 5) addd fpa0extra10 std fpa0extra6 ldd fpa0extra8 adcb fpa0extra5 adca fpa0extra4 std fpa0extra4 ldb fpa0extra3 adcb #0 stb fpa0extra3 lsl fpa0extra7 ; shift partial result left (times 10) rol fpa0extra6 rol fpa0extra5 rol fpa0extra4 rol fpa0extra3 ldd fpa0extra6 ; add in new digit addb ,s+ adca #0 std fpa0extra6 ldd fpa0extra4 ; and propagate carry adcb #0 adca #0 std fpa0extra4 ldb fpa0extra3 adcb #0 stb fpa0extra3 beq val_parsenum4c ; go handle next digit if we didn't overflow past 32 bits jsr nextchar ; eat the digit we just handled val_parsefloat pshs y ; save destination pointer lda #valtype_float ; set return type to floating point sta val.type,y ldx #fpa0extra ; point to integer accumulator jsr fps_fromuint64 ; convert to floating point clr fpa0extra11 ; zero out decimal counter clr fpa0extra10 ; zero out decimal exponent counter clr fpa0extra9 ; flag for decimal seen jsr curchar ; fetch current character bra val_parsefloat1 ; go handle character val_parsefloat0 jsr nextchar ; fetch next character val_parsefloat1 bcc val_parsefloat2 ; brif not digit suba #'0 ; adjust digit to binary sta fpa0extra3 ; save it for later (upper 3 bytes of 32 bit value already 0) ldx ,s ; get destination value jsr fps_mul10 ; do a quick multiply by 10 ldx #fpa0extra ; convert digit to floating point ldy #fpa1 jsr fps_fromuint32 ldu #fpa1 ; add digit to accumulated value ldx ,s leay ,x jsr fps_add lda fpa0extra11 ; update decimal counter suba fpa0extra9 sta fpa0extra11 bra val_parsefloat0 ; go handle another digit val_parsefloat2 cmpa #'. ; decimal? bne val_parsefloat7 ; brif not com fpa0extra9 ; flag for decimal bne val_parsefloat0 ; brif not two decimals - keep parsing val_parsefloat3 ldb fpa0extra10 ; fetch decimal exponent counter subb fpa0extra11 ; subtract out decimal places provided beq val_parsefloat6 ; brif no adjustment needed stb fpa0extra9 ; save counter bmi val_parsefloat5 ; brif negative exponent - need to do divisions val_parsefloat4 ldx ,s ; point to destination value jsr fps_mul10 ; multiply by 10 dec fpa0extra9 ; done all of them? bne val_parsefloat4 ; brif not bra val_parsefloat6 val_parsefloat5 ldx ,s ; point to destination value jsr fps_div10 ; divide by 10 inc fpa0extra9 ; done all of them? bne val_parsefloat5 ; brif not val_parsefloat6 puls y ; get back destination pointer lda fpa0extra12 ; get desired sign sta val.fpssign,y ; set in result rts val_parsefloat7 cmpa #'E ; decimal exponent? beq val_parsefloat8 ; brif so cmpa #'e ; decimal exponent, lower case edition? bne val_parsefloat3 ; brif not - must be end of number val_parsefloat8 clr fpa0extra9 ; set sign of exponent to positive jsr nextchar ; fetch exponent character bcs val_parsefloat11 ; brif digit cmpa #'+ ; positive exponent? beq val_parsefloat10 ; brif so - skip it cmpa #tok_plus ; positive exponent, operator style? beq val_parsefloat10 ; brif so - skip it cmpa #'- ; negative exponent? beq val_parsefloat9 ; brif so cmpa #tok_minus ; negative exponent, operator style? bne val_parsefloat3 ; brif not - must be end of exponent val_parsefloat9 com fpa0extra9 ; set exponent to negative val_parsefloat10 jsr nextchar ; eat exponent sign bcc val_parsefloat12 ; brif end of exponent - apply sign val_parsefloat11 suba #'0 ; binary-ize digit sta fpa0extra8 ; save digit for later lda #10 ; mutiply current decimal exponent by 10 ldb fpa0extra10 ; get current exponent mul adca #0 ; set A if we overflowed *or* bit 7 of B is set lbne OVERROR ; brif exponent overflow addb fpa0extra8 ; add in digit lbvs OVERROR ; brif exponent overflow stb fpa0extra10 ; save new exponent bra val_parsefloat10 ; go handle next exponent digit val_parsefloat12 ldb fpa0extra9 ; do we have a negative exponent? beq val_parsefloat3 ; brif not, go adjust value by exponent and return neg fpa0extra10 ; set base 10 exponent negative bra val_parsefloat3 ; go adjust value by exponent and return *pragmapop list