base:kernal_floating_point_mathematics

This shows you the differences between two versions of the page.

Both sides previous revision Previous revision Next revision | Previous revision | ||

base:kernal_floating_point_mathematics [2016-02-06 01:17] malcontent |
base:kernal_floating_point_mathematics [2020-12-06 20:48] (current) pararaum [7.1 FP to integer] |
||
---|---|---|---|

Line 3: | Line 3: | ||

[[http:// | [[http:// | ||

- | Floating point numbers are stored with 5 bytes. The first byte is the exponent, stored in excess $80 format (E = E - $80). Second byte holds the sign of the mantissa in its uppermost bit. The normalised mantissa (1 =< M < 2) is assumed to have a leading 1, and the following bits hold the fraction. If the exponent is 0, the number is interpreted as 0 regardless of the contents of the mantissa. | + | Floating point numbers are stored with 5 bytes. The first byte is the exponent, stored in excess $80 format (E = E - $80). Second byte holds the sign of the mantissa in its uppermost bit. The normalized mantissa (0 =< M < 1) is assumed to have a leading 1, and the following bits hold the fraction. If the exponent is 0, the number is interpreted as 0 regardless of the contents of the mantissa. |

81 00 00 00 00 = 1 = 2< | 81 00 00 00 00 = 1 = 2< | ||

Line 30: | Line 30: | ||

**$69** holds the exponent\\ | **$69** holds the exponent\\ | ||

**$6a-$6d** holds the mantissa\\ | **$6a-$6d** holds the mantissa\\ | ||

- | **$63** holds the sign in bit 7\\ | + | **$6e** holds the sign in bit 7\\ |

Line 41: | Line 41: | ||

**$68** is used during normalisation, | **$68** is used during normalisation, | ||

- | **$6f** holds the sign result of arithmetic operations. This byte is set when FAC2 is loaded, not when FAC1 is loaded. FAC2 should be loaded after FAC1 before addition, or multiplication. Division does its own compare, so copying FAC1 into FAC2 and then loading FAC1 does not produce erroneous signs. Subtraction negates FAC1 and then compares, setting this byte. | + | **$6f** holds the sign result of arithmetic operations. This byte is set when FAC2 is loaded, not when FAC1 is loaded. FAC2 should be loaded after FAC1 before addition, or multiplication. Subtraction negates FAC1 and then compares, setting this byte. Unless called at FDIVT ($bb12), division does its own compare, so copying FAC1 into FAC2 and then loading FAC1 does not produce erroneous signs. When FDIVT is used, value of this byte may contain incorrect sign, leading to errors in later operations. Please note that this address is used also by other routines – as string pointer for example – and therefore should be treated as volatile, even if no FP routines are invoked after its value is established. |

**$70** is the low order rounding byte for FAC1, essentially an extension of the mantissa. It is cleared whenever a variable is loaded into FAC1. Storing 0 here after obtaining a result can speed execution (at the cost of accuracy) by avoiding the rounding routines. | **$70** is the low order rounding byte for FAC1, essentially an extension of the mantissa. It is cleared whenever a variable is loaded into FAC1. Storing 0 here after obtaining a result can speed execution (at the cost of accuracy) by avoiding the rounding routines. | ||

Line 68: | Line 68: | ||

---- | ---- | ||

- | |||

====== The Floating Point Routines ====== | ====== The Floating Point Routines ====== | ||

Line 130: | Line 129: | ||

**$bb0f** = Divide the memory contents pointed to by A/Y (low/high) by FAC1.\\ | **$bb0f** = Divide the memory contents pointed to by A/Y (low/high) by FAC1.\\ | ||

- | **$bb12** = Entry if FAC2 already loaded. Accumulator must load FAC1 exponent ($61) beforehand to set the zero flag. | + | **$bb12** = Entry if FAC2 already loaded. Accumulator must load FAC1 exponent ($61) beforehand to set the zero flag. Sign comparison is not performed and ARISGN byte at $6f is not set, which has to be accounted for when using this entry point. Hard to debug sign errors may occur otherwise. |

Divides FAC2 by FAC1, leaving the quotient in FAC1, and the remainder in FAC2. | Divides FAC2 by FAC1, leaving the quotient in FAC1, and the remainder in FAC2. | ||

Line 225: | Line 224: | ||

==== 7.1 FP to integer ==== | ==== 7.1 FP to integer ==== | ||

+ | There seems to be only a single routine in the ROMs, see Lothar Englisch, "The Advanced Machine Language Book", Abacus Software, 1984, p 28. IT is located at $BC9B and converts the floating-point value in the FAC (the fractional portion is truncated) into a whole number. The four mantissa bytes ($62-$65) contain the value in a big-endian representation. | ||

==== 7.2 FP to string ==== | ==== 7.2 FP to string ==== | ||

Line 235: | Line 235: | ||

**$bc44** = Convert signed 16-bit integer held in FAC1 at $63 (lo) and $62 (high) to FP\\ | **$bc44** = Convert signed 16-bit integer held in FAC1 at $63 (lo) and $62 (high) to FP\\ | ||

**$b391** = Convert signed 16-bit integer held in Y/A (lo/high) to a FP number in FAC1\\ | **$b391** = Convert signed 16-bit integer held in Y/A (lo/high) to a FP number in FAC1\\ | ||

- | **$bc3c** = Convert unsigned 8-bit integer held in A to a FP number in FAC1\\ | + | **$bc3c** = Convert signed 8-bit integer held in A to a FP number in FAC1\\ |

**$b3a2** = Convert unsigned 8-bit integer held in Y to a FP number in FAC1 | **$b3a2** = Convert unsigned 8-bit integer held in Y to a FP number in FAC1 | ||

The first routine is the actual code. The other entries simply set up $62/$63 for you. This routine wrecks FAC2, so if it is needed, it should be loaded after the conversion. It also uselessly (from our ML standpoint) sets the data type flag at $0d. Skipping these routines and jumping straight to the normalization routine at **$b8d2** gives greater control, i.e. 24-bit and 32-bit integer inputs are possible, as are signed 8-bit inputs. For example, to convert a 24-bit number, load FAC1 with the number from the most-significant byte to the least. Store zero in the last mantissa byte, and also in the sign and rounding bytes ($66 and $70). Then set the exponent to 24 (24 + $80 = $98). Jump to $b8d2 with the carry indicating whether the result should be positive (set) or negative (clear). | The first routine is the actual code. The other entries simply set up $62/$63 for you. This routine wrecks FAC2, so if it is needed, it should be loaded after the conversion. It also uselessly (from our ML standpoint) sets the data type flag at $0d. Skipping these routines and jumping straight to the normalization routine at **$b8d2** gives greater control, i.e. 24-bit and 32-bit integer inputs are possible, as are signed 8-bit inputs. For example, to convert a 24-bit number, load FAC1 with the number from the most-significant byte to the least. Store zero in the last mantissa byte, and also in the sign and rounding bytes ($66 and $70). Then set the exponent to 24 (24 + $80 = $98). Jump to $b8d2 with the carry indicating whether the result should be positive (set) or negative (clear). | ||

+ | |||

+ | The above process is basically what the KERNAL authors did to implement the 24 bit TI variable in BASIC (though they zero the high byte instead of the low, and hence use an exponent of $a0). | ||

+ | |||

+ | You can take advantage of their work with the following pair of calls to convert a 24 bit unsigned integer held in YXA: | ||

+ | |||

+ | sec | ||

+ | jsr $af87 ; sets mantissa to 00yyxxaa | ||

+ | jsr $af7e ; set rest of FAC1 and JMP to $b8d2 | ||

+ | |||

==== 7.4 String to FP ==== | ==== 7.4 String to FP ==== | ||

Line 423: | Line 432: | ||

lda value ;get guess based on argument | lda value ;get guess based on argument | ||

+ | clc | ||

ror | ror | ||

bcs sqrtadd | bcs sqrtadd | ||

ldx #$80 | ldx #$80 | ||

- | sta result+1 | + | stx result+1 |

sqrtadd adc #$40 | sqrtadd adc #$40 | ||

sta result | sta result |

base/kernal_floating_point_mathematics.1454717847.txt.gz · Last modified: 2016-02-06 01:17 by malcontent