diff options
Diffstat (limited to 'embassy-rp/src/float')
| -rw-r--r-- | embassy-rp/src/float/add_sub.rs | 92 | ||||
| -rw-r--r-- | embassy-rp/src/float/cmp.rs | 201 | ||||
| -rw-r--r-- | embassy-rp/src/float/conv.rs | 157 | ||||
| -rw-r--r-- | embassy-rp/src/float/div.rs | 141 | ||||
| -rw-r--r-- | embassy-rp/src/float/functions.rs | 239 | ||||
| -rw-r--r-- | embassy-rp/src/float/mod.rs | 149 | ||||
| -rw-r--r-- | embassy-rp/src/float/mul.rs | 70 |
7 files changed, 1049 insertions, 0 deletions
diff --git a/embassy-rp/src/float/add_sub.rs b/embassy-rp/src/float/add_sub.rs new file mode 100644 index 000000000..673544cfe --- /dev/null +++ b/embassy-rp/src/float/add_sub.rs | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | // Credit: taken from `rp-hal` (also licensed Apache+MIT) | ||
| 2 | // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/add_sub.rs | ||
| 3 | |||
| 4 | use super::{Float, Int}; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait ROMAdd { | ||
| 8 | fn rom_add(self, b: Self) -> Self; | ||
| 9 | } | ||
| 10 | |||
| 11 | impl ROMAdd for f32 { | ||
| 12 | fn rom_add(self, b: Self) -> Self { | ||
| 13 | rom_data::float_funcs::fadd(self, b) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | impl ROMAdd for f64 { | ||
| 18 | fn rom_add(self, b: Self) -> Self { | ||
| 19 | rom_data::double_funcs::dadd(self, b) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | fn add<F: Float + ROMAdd>(a: F, b: F) -> F { | ||
| 24 | if a.is_not_finite() { | ||
| 25 | if b.is_not_finite() { | ||
| 26 | let class_a = a.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK); | ||
| 27 | let class_b = b.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK); | ||
| 28 | |||
| 29 | if class_a == F::Int::ZERO && class_b == F::Int::ZERO { | ||
| 30 | // inf + inf = inf | ||
| 31 | return a; | ||
| 32 | } | ||
| 33 | if class_a == F::SIGN_MASK && class_b == F::SIGN_MASK { | ||
| 34 | // -inf + (-inf) = -inf | ||
| 35 | return a; | ||
| 36 | } | ||
| 37 | |||
| 38 | // Sign mismatch, or either is NaN already | ||
| 39 | return F::NAN; | ||
| 40 | } | ||
| 41 | |||
| 42 | // [-]inf/NaN + X = [-]inf/NaN | ||
| 43 | return a; | ||
| 44 | } | ||
| 45 | |||
| 46 | if b.is_not_finite() { | ||
| 47 | // X + [-]inf/NaN = [-]inf/NaN | ||
| 48 | return b; | ||
| 49 | } | ||
| 50 | |||
| 51 | a.rom_add(b) | ||
| 52 | } | ||
| 53 | |||
| 54 | intrinsics! { | ||
| 55 | #[alias = __addsf3vfp] | ||
| 56 | #[aeabi = __aeabi_fadd] | ||
| 57 | extern "C" fn __addsf3(a: f32, b: f32) -> f32 { | ||
| 58 | add(a, b) | ||
| 59 | } | ||
| 60 | |||
| 61 | #[bootrom_v2] | ||
| 62 | #[alias = __adddf3vfp] | ||
| 63 | #[aeabi = __aeabi_dadd] | ||
| 64 | extern "C" fn __adddf3(a: f64, b: f64) -> f64 { | ||
| 65 | add(a, b) | ||
| 66 | } | ||
| 67 | |||
| 68 | // The ROM just implements subtraction the same way, so just do it here | ||
| 69 | // and save the work of implementing more complicated NaN/inf handling. | ||
| 70 | |||
| 71 | #[alias = __subsf3vfp] | ||
| 72 | #[aeabi = __aeabi_fsub] | ||
| 73 | extern "C" fn __subsf3(a: f32, b: f32) -> f32 { | ||
| 74 | add(a, -b) | ||
| 75 | } | ||
| 76 | |||
| 77 | #[bootrom_v2] | ||
| 78 | #[alias = __subdf3vfp] | ||
| 79 | #[aeabi = __aeabi_dsub] | ||
| 80 | extern "C" fn __subdf3(a: f64, b: f64) -> f64 { | ||
| 81 | add(a, -b) | ||
| 82 | } | ||
| 83 | |||
| 84 | extern "aapcs" fn __aeabi_frsub(a: f32, b: f32) -> f32 { | ||
| 85 | add(b, -a) | ||
| 86 | } | ||
| 87 | |||
| 88 | #[bootrom_v2] | ||
| 89 | extern "aapcs" fn __aeabi_drsub(a: f64, b: f64) -> f64 { | ||
| 90 | add(b, -a) | ||
| 91 | } | ||
| 92 | } | ||
diff --git a/embassy-rp/src/float/cmp.rs b/embassy-rp/src/float/cmp.rs new file mode 100644 index 000000000..e540e3918 --- /dev/null +++ b/embassy-rp/src/float/cmp.rs | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | // Credit: taken from `rp-hal` (also licensed Apache+MIT) | ||
| 2 | // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/cmp.rs | ||
| 3 | |||
| 4 | use super::Float; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait ROMCmp { | ||
| 8 | fn rom_cmp(self, b: Self) -> i32; | ||
| 9 | } | ||
| 10 | |||
| 11 | impl ROMCmp for f32 { | ||
| 12 | fn rom_cmp(self, b: Self) -> i32 { | ||
| 13 | rom_data::float_funcs::fcmp(self, b) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | impl ROMCmp for f64 { | ||
| 18 | fn rom_cmp(self, b: Self) -> i32 { | ||
| 19 | rom_data::double_funcs::dcmp(self, b) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | fn le_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 { | ||
| 24 | if a.is_nan() || b.is_nan() { | ||
| 25 | 1 | ||
| 26 | } else { | ||
| 27 | a.rom_cmp(b) | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | fn ge_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 { | ||
| 32 | if a.is_nan() || b.is_nan() { | ||
| 33 | -1 | ||
| 34 | } else { | ||
| 35 | a.rom_cmp(b) | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | intrinsics! { | ||
| 40 | #[slower_than_default] | ||
| 41 | #[bootrom_v2] | ||
| 42 | #[alias = __eqsf2, __ltsf2, __nesf2] | ||
| 43 | extern "C" fn __lesf2(a: f32, b: f32) -> i32 { | ||
| 44 | le_abi(a, b) | ||
| 45 | } | ||
| 46 | |||
| 47 | #[slower_than_default] | ||
| 48 | #[bootrom_v2] | ||
| 49 | #[alias = __eqdf2, __ltdf2, __nedf2] | ||
| 50 | extern "C" fn __ledf2(a: f64, b: f64) -> i32 { | ||
| 51 | le_abi(a, b) | ||
| 52 | } | ||
| 53 | |||
| 54 | #[slower_than_default] | ||
| 55 | #[bootrom_v2] | ||
| 56 | #[alias = __gtsf2] | ||
| 57 | extern "C" fn __gesf2(a: f32, b: f32) -> i32 { | ||
| 58 | ge_abi(a, b) | ||
| 59 | } | ||
| 60 | |||
| 61 | #[slower_than_default] | ||
| 62 | #[bootrom_v2] | ||
| 63 | #[alias = __gtdf2] | ||
| 64 | extern "C" fn __gedf2(a: f64, b: f64) -> i32 { | ||
| 65 | ge_abi(a, b) | ||
| 66 | } | ||
| 67 | |||
| 68 | |||
| 69 | #[slower_than_default] | ||
| 70 | #[bootrom_v2] | ||
| 71 | extern "aapcs" fn __aeabi_fcmple(a: f32, b: f32) -> i32 { | ||
| 72 | (le_abi(a, b) <= 0) as i32 | ||
| 73 | } | ||
| 74 | |||
| 75 | #[slower_than_default] | ||
| 76 | #[bootrom_v2] | ||
| 77 | extern "aapcs" fn __aeabi_fcmpge(a: f32, b: f32) -> i32 { | ||
| 78 | (ge_abi(a, b) >= 0) as i32 | ||
| 79 | } | ||
| 80 | |||
| 81 | #[slower_than_default] | ||
| 82 | #[bootrom_v2] | ||
| 83 | extern "aapcs" fn __aeabi_fcmpeq(a: f32, b: f32) -> i32 { | ||
| 84 | (le_abi(a, b) == 0) as i32 | ||
| 85 | } | ||
| 86 | |||
| 87 | #[slower_than_default] | ||
| 88 | #[bootrom_v2] | ||
| 89 | extern "aapcs" fn __aeabi_fcmplt(a: f32, b: f32) -> i32 { | ||
| 90 | (le_abi(a, b) < 0) as i32 | ||
| 91 | } | ||
| 92 | |||
| 93 | #[slower_than_default] | ||
| 94 | #[bootrom_v2] | ||
| 95 | extern "aapcs" fn __aeabi_fcmpgt(a: f32, b: f32) -> i32 { | ||
| 96 | (ge_abi(a, b) > 0) as i32 | ||
| 97 | } | ||
| 98 | |||
| 99 | #[slower_than_default] | ||
| 100 | #[bootrom_v2] | ||
| 101 | extern "aapcs" fn __aeabi_dcmple(a: f64, b: f64) -> i32 { | ||
| 102 | (le_abi(a, b) <= 0) as i32 | ||
| 103 | } | ||
| 104 | |||
| 105 | #[slower_than_default] | ||
| 106 | #[bootrom_v2] | ||
| 107 | extern "aapcs" fn __aeabi_dcmpge(a: f64, b: f64) -> i32 { | ||
| 108 | (ge_abi(a, b) >= 0) as i32 | ||
| 109 | } | ||
| 110 | |||
| 111 | #[slower_than_default] | ||
| 112 | #[bootrom_v2] | ||
| 113 | extern "aapcs" fn __aeabi_dcmpeq(a: f64, b: f64) -> i32 { | ||
| 114 | (le_abi(a, b) == 0) as i32 | ||
| 115 | } | ||
| 116 | |||
| 117 | #[slower_than_default] | ||
| 118 | #[bootrom_v2] | ||
| 119 | extern "aapcs" fn __aeabi_dcmplt(a: f64, b: f64) -> i32 { | ||
| 120 | (le_abi(a, b) < 0) as i32 | ||
| 121 | } | ||
| 122 | |||
| 123 | #[slower_than_default] | ||
| 124 | #[bootrom_v2] | ||
| 125 | extern "aapcs" fn __aeabi_dcmpgt(a: f64, b: f64) -> i32 { | ||
| 126 | (ge_abi(a, b) > 0) as i32 | ||
| 127 | } | ||
| 128 | |||
| 129 | |||
| 130 | #[slower_than_default] | ||
| 131 | #[bootrom_v2] | ||
| 132 | extern "C" fn __gesf2vfp(a: f32, b: f32) -> i32 { | ||
| 133 | (ge_abi(a, b) >= 0) as i32 | ||
| 134 | } | ||
| 135 | |||
| 136 | #[slower_than_default] | ||
| 137 | #[bootrom_v2] | ||
| 138 | extern "C" fn __gedf2vfp(a: f64, b: f64) -> i32 { | ||
| 139 | (ge_abi(a, b) >= 0) as i32 | ||
| 140 | } | ||
| 141 | |||
| 142 | #[slower_than_default] | ||
| 143 | #[bootrom_v2] | ||
| 144 | extern "C" fn __gtsf2vfp(a: f32, b: f32) -> i32 { | ||
| 145 | (ge_abi(a, b) > 0) as i32 | ||
| 146 | } | ||
| 147 | |||
| 148 | #[slower_than_default] | ||
| 149 | #[bootrom_v2] | ||
| 150 | extern "C" fn __gtdf2vfp(a: f64, b: f64) -> i32 { | ||
| 151 | (ge_abi(a, b) > 0) as i32 | ||
| 152 | } | ||
| 153 | |||
| 154 | #[slower_than_default] | ||
| 155 | #[bootrom_v2] | ||
| 156 | extern "C" fn __ltsf2vfp(a: f32, b: f32) -> i32 { | ||
| 157 | (le_abi(a, b) < 0) as i32 | ||
| 158 | } | ||
| 159 | |||
| 160 | #[slower_than_default] | ||
| 161 | #[bootrom_v2] | ||
| 162 | extern "C" fn __ltdf2vfp(a: f64, b: f64) -> i32 { | ||
| 163 | (le_abi(a, b) < 0) as i32 | ||
| 164 | } | ||
| 165 | |||
| 166 | #[slower_than_default] | ||
| 167 | #[bootrom_v2] | ||
| 168 | extern "C" fn __lesf2vfp(a: f32, b: f32) -> i32 { | ||
| 169 | (le_abi(a, b) <= 0) as i32 | ||
| 170 | } | ||
| 171 | |||
| 172 | #[slower_than_default] | ||
| 173 | #[bootrom_v2] | ||
| 174 | extern "C" fn __ledf2vfp(a: f64, b: f64) -> i32 { | ||
| 175 | (le_abi(a, b) <= 0) as i32 | ||
| 176 | } | ||
| 177 | |||
| 178 | #[slower_than_default] | ||
| 179 | #[bootrom_v2] | ||
| 180 | extern "C" fn __nesf2vfp(a: f32, b: f32) -> i32 { | ||
| 181 | (le_abi(a, b) != 0) as i32 | ||
| 182 | } | ||
| 183 | |||
| 184 | #[slower_than_default] | ||
| 185 | #[bootrom_v2] | ||
| 186 | extern "C" fn __nedf2vfp(a: f64, b: f64) -> i32 { | ||
| 187 | (le_abi(a, b) != 0) as i32 | ||
| 188 | } | ||
| 189 | |||
| 190 | #[slower_than_default] | ||
| 191 | #[bootrom_v2] | ||
| 192 | extern "C" fn __eqsf2vfp(a: f32, b: f32) -> i32 { | ||
| 193 | (le_abi(a, b) == 0) as i32 | ||
| 194 | } | ||
| 195 | |||
| 196 | #[slower_than_default] | ||
| 197 | #[bootrom_v2] | ||
| 198 | extern "C" fn __eqdf2vfp(a: f64, b: f64) -> i32 { | ||
| 199 | (le_abi(a, b) == 0) as i32 | ||
| 200 | } | ||
| 201 | } | ||
diff --git a/embassy-rp/src/float/conv.rs b/embassy-rp/src/float/conv.rs new file mode 100644 index 000000000..021826e28 --- /dev/null +++ b/embassy-rp/src/float/conv.rs | |||
| @@ -0,0 +1,157 @@ | |||
| 1 | // Credit: taken from `rp-hal` (also licensed Apache+MIT) | ||
| 2 | // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs | ||
| 3 | |||
| 4 | use super::Float; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | // Some of these are also not connected in the Pico SDK. This is probably | ||
| 8 | // because the ROM version actually does a fixed point conversion, just with | ||
| 9 | // the fractional width set to zero. | ||
| 10 | |||
| 11 | intrinsics! { | ||
| 12 | // Not connected in the Pico SDK | ||
| 13 | #[slower_than_default] | ||
| 14 | #[aeabi = __aeabi_i2f] | ||
| 15 | extern "C" fn __floatsisf(i: i32) -> f32 { | ||
| 16 | rom_data::float_funcs::int_to_float(i) | ||
| 17 | } | ||
| 18 | |||
| 19 | // Not connected in the Pico SDK | ||
| 20 | #[slower_than_default] | ||
| 21 | #[aeabi = __aeabi_i2d] | ||
| 22 | extern "C" fn __floatsidf(i: i32) -> f64 { | ||
| 23 | rom_data::double_funcs::int_to_double(i) | ||
| 24 | } | ||
| 25 | |||
| 26 | // Questionable gain | ||
| 27 | #[aeabi = __aeabi_l2f] | ||
| 28 | extern "C" fn __floatdisf(i: i64) -> f32 { | ||
| 29 | rom_data::float_funcs::int64_to_float(i) | ||
| 30 | } | ||
| 31 | |||
| 32 | #[bootrom_v2] | ||
| 33 | #[aeabi = __aeabi_l2d] | ||
| 34 | extern "C" fn __floatdidf(i: i64) -> f64 { | ||
| 35 | rom_data::double_funcs::int64_to_double(i) | ||
| 36 | } | ||
| 37 | |||
| 38 | // Not connected in the Pico SDK | ||
| 39 | #[slower_than_default] | ||
| 40 | #[aeabi = __aeabi_ui2f] | ||
| 41 | extern "C" fn __floatunsisf(i: u32) -> f32 { | ||
| 42 | rom_data::float_funcs::uint_to_float(i) | ||
| 43 | } | ||
| 44 | |||
| 45 | // Questionable gain | ||
| 46 | #[bootrom_v2] | ||
| 47 | #[aeabi = __aeabi_ui2d] | ||
| 48 | extern "C" fn __floatunsidf(i: u32) -> f64 { | ||
| 49 | rom_data::double_funcs::uint_to_double(i) | ||
| 50 | } | ||
| 51 | |||
| 52 | // Questionable gain | ||
| 53 | #[bootrom_v2] | ||
| 54 | #[aeabi = __aeabi_ul2f] | ||
| 55 | extern "C" fn __floatundisf(i: u64) -> f32 { | ||
| 56 | rom_data::float_funcs::uint64_to_float(i) | ||
| 57 | } | ||
| 58 | |||
| 59 | #[bootrom_v2] | ||
| 60 | #[aeabi = __aeabi_ul2d] | ||
| 61 | extern "C" fn __floatundidf(i: u64) -> f64 { | ||
| 62 | rom_data::double_funcs::uint64_to_double(i) | ||
| 63 | } | ||
| 64 | |||
| 65 | |||
| 66 | // The Pico SDK does some optimization here (e.x. fast paths for zero and | ||
| 67 | // one), but we can just directly connect it. | ||
| 68 | #[aeabi = __aeabi_f2iz] | ||
| 69 | extern "C" fn __fixsfsi(f: f32) -> i32 { | ||
| 70 | rom_data::float_funcs::float_to_int(f) | ||
| 71 | } | ||
| 72 | |||
| 73 | #[bootrom_v2] | ||
| 74 | #[aeabi = __aeabi_f2lz] | ||
| 75 | extern "C" fn __fixsfdi(f: f32) -> i64 { | ||
| 76 | rom_data::float_funcs::float_to_int64(f) | ||
| 77 | } | ||
| 78 | |||
| 79 | // Not connected in the Pico SDK | ||
| 80 | #[slower_than_default] | ||
| 81 | #[bootrom_v2] | ||
| 82 | #[aeabi = __aeabi_d2iz] | ||
| 83 | extern "C" fn __fixdfsi(f: f64) -> i32 { | ||
| 84 | rom_data::double_funcs::double_to_int(f) | ||
| 85 | } | ||
| 86 | |||
| 87 | // Like with the 32 bit version, there's optimization that we just | ||
| 88 | // skip. | ||
| 89 | #[bootrom_v2] | ||
| 90 | #[aeabi = __aeabi_d2lz] | ||
| 91 | extern "C" fn __fixdfdi(f: f64) -> i64 { | ||
| 92 | rom_data::double_funcs::double_to_int64(f) | ||
| 93 | } | ||
| 94 | |||
| 95 | #[slower_than_default] | ||
| 96 | #[aeabi = __aeabi_f2uiz] | ||
| 97 | extern "C" fn __fixunssfsi(f: f32) -> u32 { | ||
| 98 | rom_data::float_funcs::float_to_uint(f) | ||
| 99 | } | ||
| 100 | |||
| 101 | #[slower_than_default] | ||
| 102 | #[bootrom_v2] | ||
| 103 | #[aeabi = __aeabi_f2ulz] | ||
| 104 | extern "C" fn __fixunssfdi(f: f32) -> u64 { | ||
| 105 | rom_data::float_funcs::float_to_uint64(f) | ||
| 106 | } | ||
| 107 | |||
| 108 | #[slower_than_default] | ||
| 109 | #[bootrom_v2] | ||
| 110 | #[aeabi = __aeabi_d2uiz] | ||
| 111 | extern "C" fn __fixunsdfsi(f: f64) -> u32 { | ||
| 112 | rom_data::double_funcs::double_to_uint(f) | ||
| 113 | } | ||
| 114 | |||
| 115 | #[slower_than_default] | ||
| 116 | #[bootrom_v2] | ||
| 117 | #[aeabi = __aeabi_d2ulz] | ||
| 118 | extern "C" fn __fixunsdfdi(f: f64) -> u64 { | ||
| 119 | rom_data::double_funcs::double_to_uint64(f) | ||
| 120 | } | ||
| 121 | |||
| 122 | #[bootrom_v2] | ||
| 123 | #[alias = __extendsfdf2vfp] | ||
| 124 | #[aeabi = __aeabi_f2d] | ||
| 125 | extern "C" fn __extendsfdf2(f: f32) -> f64 { | ||
| 126 | if f.is_not_finite() { | ||
| 127 | return f64::from_repr( | ||
| 128 | // Not finite | ||
| 129 | f64::EXPONENT_MASK | | ||
| 130 | // Preserve NaN or inf | ||
| 131 | ((f.repr() & f32::SIGNIFICAND_MASK) as u64) | | ||
| 132 | // Preserve sign | ||
| 133 | ((f.repr() & f32::SIGN_MASK) as u64) << (f64::BITS-f32::BITS) | ||
| 134 | ); | ||
| 135 | } | ||
| 136 | rom_data::float_funcs::float_to_double(f) | ||
| 137 | } | ||
| 138 | |||
| 139 | #[bootrom_v2] | ||
| 140 | #[alias = __truncdfsf2vfp] | ||
| 141 | #[aeabi = __aeabi_d2f] | ||
| 142 | extern "C" fn __truncdfsf2(f: f64) -> f32 { | ||
| 143 | if f.is_not_finite() { | ||
| 144 | let mut repr: u32 = | ||
| 145 | // Not finite | ||
| 146 | f32::EXPONENT_MASK | | ||
| 147 | // Preserve sign | ||
| 148 | ((f.repr() & f64::SIGN_MASK) >> (f64::BITS-f32::BITS)) as u32; | ||
| 149 | // Set NaN | ||
| 150 | if (f.repr() & f64::SIGNIFICAND_MASK) != 0 { | ||
| 151 | repr |= 1; | ||
| 152 | } | ||
| 153 | return f32::from_repr(repr); | ||
| 154 | } | ||
| 155 | rom_data::double_funcs::double_to_float(f) | ||
| 156 | } | ||
| 157 | } | ||
diff --git a/embassy-rp/src/float/div.rs b/embassy-rp/src/float/div.rs new file mode 100644 index 000000000..094dec446 --- /dev/null +++ b/embassy-rp/src/float/div.rs | |||
| @@ -0,0 +1,141 @@ | |||
| 1 | // Credit: taken from `rp-hal` (also licensed Apache+MIT) | ||
| 2 | // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs | ||
| 3 | |||
| 4 | use super::Float; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | // Make sure this stays as a separate call, because when it's inlined the | ||
| 8 | // compiler will move the save of the registers used to contain the divider | ||
| 9 | // state into the function prologue. That save and restore (push/pop) takes | ||
| 10 | // longer than the actual division, so doing it in the common case where | ||
| 11 | // they are not required wastes a lot of time. | ||
| 12 | #[inline(never)] | ||
| 13 | #[cold] | ||
| 14 | fn save_divider_and_call<F, R>(f: F) -> R | ||
| 15 | where | ||
| 16 | F: FnOnce() -> R, | ||
| 17 | { | ||
| 18 | let sio = rp_pac::SIO; | ||
| 19 | |||
| 20 | unsafe { | ||
| 21 | // Since we can't save the signed-ness of the calculation, we have to make | ||
| 22 | // sure that there's at least an 8 cycle delay before we read the result. | ||
| 23 | // The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads. | ||
| 24 | // Since we can't be sure the Rust implementation will optimize to the same, | ||
| 25 | // just use an explicit wait. | ||
| 26 | while !sio.div().csr().read().ready() {} | ||
| 27 | |||
| 28 | // Read the quotient last, since that's what clears the dirty flag | ||
| 29 | let dividend = sio.div().udividend().read(); | ||
| 30 | let divisor = sio.div().udivisor().read(); | ||
| 31 | let remainder = sio.div().remainder().read(); | ||
| 32 | let quotient = sio.div().quotient().read(); | ||
| 33 | |||
| 34 | // If we get interrupted here (before a write sets the DIRTY flag) its fine, since | ||
| 35 | // we have the full state, so the interruptor doesn't have to restore it. Once the | ||
| 36 | // write happens and the DIRTY flag is set, the interruptor becomes responsible for | ||
| 37 | // restoring our state. | ||
| 38 | let result = f(); | ||
| 39 | |||
| 40 | // If we are interrupted here, then the interruptor will start an incorrect calculation | ||
| 41 | // using a wrong divisor, but we'll restore the divisor and result ourselves correctly. | ||
| 42 | // This sets DIRTY, so any interruptor will save the state. | ||
| 43 | sio.div().udividend().write_value(dividend); | ||
| 44 | // If we are interrupted here, the the interruptor may start the calculation using | ||
| 45 | // incorrectly signed inputs, but we'll restore the result ourselves. | ||
| 46 | // This sets DIRTY, so any interruptor will save the state. | ||
| 47 | sio.div().udivisor().write_value(divisor); | ||
| 48 | // If we are interrupted here, the interruptor will have restored everything but the | ||
| 49 | // quotient may be wrongly signed. If the calculation started by the above writes is | ||
| 50 | // still ongoing it is stopped, so it won't replace the result we're restoring. | ||
| 51 | // DIRTY and READY set, but only DIRTY matters to make the interruptor save the state. | ||
| 52 | sio.div().remainder().write_value(remainder); | ||
| 53 | // State fully restored after the quotient write. This sets both DIRTY and READY, so | ||
| 54 | // whatever we may have interrupted can read the result. | ||
| 55 | sio.div().quotient().write_value(quotient); | ||
| 56 | |||
| 57 | result | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | fn save_divider<F, R>(f: F) -> R | ||
| 62 | where | ||
| 63 | F: FnOnce() -> R, | ||
| 64 | { | ||
| 65 | let sio = rp_pac::SIO; | ||
| 66 | if unsafe { !sio.div().csr().read().dirty() } { | ||
| 67 | // Not dirty, so nothing is waiting for the calculation. So we can just | ||
| 68 | // issue it directly without a save/restore. | ||
| 69 | f() | ||
| 70 | } else { | ||
| 71 | save_divider_and_call(f) | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | trait ROMDiv { | ||
| 76 | fn rom_div(self, b: Self) -> Self; | ||
| 77 | } | ||
| 78 | |||
| 79 | impl ROMDiv for f32 { | ||
| 80 | fn rom_div(self, b: Self) -> Self { | ||
| 81 | // ROM implementation uses the hardware divider, so we have to save it | ||
| 82 | save_divider(|| rom_data::float_funcs::fdiv(self, b)) | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | impl ROMDiv for f64 { | ||
| 87 | fn rom_div(self, b: Self) -> Self { | ||
| 88 | // ROM implementation uses the hardware divider, so we have to save it | ||
| 89 | save_divider(|| rom_data::double_funcs::ddiv(self, b)) | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | fn div<F: Float + ROMDiv>(a: F, b: F) -> F { | ||
| 94 | if a.is_not_finite() { | ||
| 95 | if b.is_not_finite() { | ||
| 96 | // inf/NaN / inf/NaN = NaN | ||
| 97 | return F::NAN; | ||
| 98 | } | ||
| 99 | |||
| 100 | if b.is_zero() { | ||
| 101 | // inf/NaN / 0 = NaN | ||
| 102 | return F::NAN; | ||
| 103 | } | ||
| 104 | |||
| 105 | return if b.is_sign_negative() { | ||
| 106 | // [+/-]inf/NaN / (-X) = [-/+]inf/NaN | ||
| 107 | a.negate() | ||
| 108 | } else { | ||
| 109 | // [-]inf/NaN / X = [-]inf/NaN | ||
| 110 | a | ||
| 111 | }; | ||
| 112 | } | ||
| 113 | |||
| 114 | if b.is_nan() { | ||
| 115 | // X / NaN = NaN | ||
| 116 | return b; | ||
| 117 | } | ||
| 118 | |||
| 119 | // ROM handles X / 0 = [-]inf and X / [-]inf = [-]0, so we only | ||
| 120 | // need to catch 0 / 0 | ||
| 121 | if b.is_zero() && a.is_zero() { | ||
| 122 | return F::NAN; | ||
| 123 | } | ||
| 124 | |||
| 125 | a.rom_div(b) | ||
| 126 | } | ||
| 127 | |||
| 128 | intrinsics! { | ||
| 129 | #[alias = __divsf3vfp] | ||
| 130 | #[aeabi = __aeabi_fdiv] | ||
| 131 | extern "C" fn __divsf3(a: f32, b: f32) -> f32 { | ||
| 132 | div(a, b) | ||
| 133 | } | ||
| 134 | |||
| 135 | #[bootrom_v2] | ||
| 136 | #[alias = __divdf3vfp] | ||
| 137 | #[aeabi = __aeabi_ddiv] | ||
| 138 | extern "C" fn __divdf3(a: f64, b: f64) -> f64 { | ||
| 139 | div(a, b) | ||
| 140 | } | ||
| 141 | } | ||
diff --git a/embassy-rp/src/float/functions.rs b/embassy-rp/src/float/functions.rs new file mode 100644 index 000000000..de29ce336 --- /dev/null +++ b/embassy-rp/src/float/functions.rs | |||
| @@ -0,0 +1,239 @@ | |||
| 1 | // Credit: taken from `rp-hal` (also licensed Apache+MIT) | ||
| 2 | // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/functions.rs | ||
| 3 | |||
| 4 | use crate::float::{Float, Int}; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait ROMFunctions { | ||
| 8 | fn sqrt(self) -> Self; | ||
| 9 | fn ln(self) -> Self; | ||
| 10 | fn exp(self) -> Self; | ||
| 11 | fn sin(self) -> Self; | ||
| 12 | fn cos(self) -> Self; | ||
| 13 | fn tan(self) -> Self; | ||
| 14 | fn atan2(self, y: Self) -> Self; | ||
| 15 | |||
| 16 | fn to_trig_range(self) -> Self; | ||
| 17 | } | ||
| 18 | |||
| 19 | impl ROMFunctions for f32 { | ||
| 20 | fn sqrt(self) -> Self { | ||
| 21 | rom_data::float_funcs::fsqrt(self) | ||
| 22 | } | ||
| 23 | |||
| 24 | fn ln(self) -> Self { | ||
| 25 | rom_data::float_funcs::fln(self) | ||
| 26 | } | ||
| 27 | |||
| 28 | fn exp(self) -> Self { | ||
| 29 | rom_data::float_funcs::fexp(self) | ||
| 30 | } | ||
| 31 | |||
| 32 | fn sin(self) -> Self { | ||
| 33 | rom_data::float_funcs::fsin(self) | ||
| 34 | } | ||
| 35 | |||
| 36 | fn cos(self) -> Self { | ||
| 37 | rom_data::float_funcs::fcos(self) | ||
| 38 | } | ||
| 39 | |||
| 40 | fn tan(self) -> Self { | ||
| 41 | rom_data::float_funcs::ftan(self) | ||
| 42 | } | ||
| 43 | |||
| 44 | fn atan2(self, y: Self) -> Self { | ||
| 45 | rom_data::float_funcs::fatan2(self, y) | ||
| 46 | } | ||
| 47 | |||
| 48 | fn to_trig_range(self) -> Self { | ||
| 49 | // -128 < X < 128, logic from the Pico SDK | ||
| 50 | let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS; | ||
| 51 | if exponent < 134 { | ||
| 52 | self | ||
| 53 | } else { | ||
| 54 | self % (core::f32::consts::PI * 2.0) | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | impl ROMFunctions for f64 { | ||
| 60 | fn sqrt(self) -> Self { | ||
| 61 | rom_data::double_funcs::dsqrt(self) | ||
| 62 | } | ||
| 63 | |||
| 64 | fn ln(self) -> Self { | ||
| 65 | rom_data::double_funcs::dln(self) | ||
| 66 | } | ||
| 67 | |||
| 68 | fn exp(self) -> Self { | ||
| 69 | rom_data::double_funcs::dexp(self) | ||
| 70 | } | ||
| 71 | |||
| 72 | fn sin(self) -> Self { | ||
| 73 | rom_data::double_funcs::dsin(self) | ||
| 74 | } | ||
| 75 | |||
| 76 | fn cos(self) -> Self { | ||
| 77 | rom_data::double_funcs::dcos(self) | ||
| 78 | } | ||
| 79 | fn tan(self) -> Self { | ||
| 80 | rom_data::double_funcs::dtan(self) | ||
| 81 | } | ||
| 82 | |||
| 83 | fn atan2(self, y: Self) -> Self { | ||
| 84 | rom_data::double_funcs::datan2(self, y) | ||
| 85 | } | ||
| 86 | |||
| 87 | fn to_trig_range(self) -> Self { | ||
| 88 | // -1024 < X < 1024, logic from the Pico SDK | ||
| 89 | let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS; | ||
| 90 | if exponent < 1033 { | ||
| 91 | self | ||
| 92 | } else { | ||
| 93 | self % (core::f64::consts::PI * 2.0) | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | fn is_negative_nonzero_or_nan<F: Float>(f: F) -> bool { | ||
| 99 | let repr = f.repr(); | ||
| 100 | if (repr & F::SIGN_MASK) != F::Int::ZERO { | ||
| 101 | // Negative, so anything other than exactly zero | ||
| 102 | return (repr & (!F::SIGN_MASK)) != F::Int::ZERO; | ||
| 103 | } | ||
| 104 | // NaN | ||
| 105 | (repr & (F::EXPONENT_MASK | F::SIGNIFICAND_MASK)) > F::EXPONENT_MASK | ||
| 106 | } | ||
| 107 | |||
| 108 | fn sqrt<F: Float + ROMFunctions>(f: F) -> F { | ||
| 109 | if is_negative_nonzero_or_nan(f) { | ||
| 110 | F::NAN | ||
| 111 | } else { | ||
| 112 | f.sqrt() | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | fn ln<F: Float + ROMFunctions>(f: F) -> F { | ||
| 117 | if is_negative_nonzero_or_nan(f) { | ||
| 118 | F::NAN | ||
| 119 | } else { | ||
| 120 | f.ln() | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | fn exp<F: Float + ROMFunctions>(f: F) -> F { | ||
| 125 | if f.is_nan() { | ||
| 126 | F::NAN | ||
| 127 | } else { | ||
| 128 | f.exp() | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | fn sin<F: Float + ROMFunctions>(f: F) -> F { | ||
| 133 | if f.is_not_finite() { | ||
| 134 | F::NAN | ||
| 135 | } else { | ||
| 136 | f.to_trig_range().sin() | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | fn cos<F: Float + ROMFunctions>(f: F) -> F { | ||
| 141 | if f.is_not_finite() { | ||
| 142 | F::NAN | ||
| 143 | } else { | ||
| 144 | f.to_trig_range().cos() | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | fn tan<F: Float + ROMFunctions>(f: F) -> F { | ||
| 149 | if f.is_not_finite() { | ||
| 150 | F::NAN | ||
| 151 | } else { | ||
| 152 | f.to_trig_range().tan() | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | fn atan2<F: Float + ROMFunctions>(x: F, y: F) -> F { | ||
| 157 | if x.is_nan() || y.is_nan() { | ||
| 158 | F::NAN | ||
| 159 | } else { | ||
| 160 | x.to_trig_range().atan2(y) | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | // Name collisions | ||
| 165 | mod intrinsics { | ||
| 166 | intrinsics! { | ||
| 167 | extern "C" fn sqrtf(f: f32) -> f32 { | ||
| 168 | super::sqrt(f) | ||
| 169 | } | ||
| 170 | |||
| 171 | #[bootrom_v2] | ||
| 172 | extern "C" fn sqrt(f: f64) -> f64 { | ||
| 173 | super::sqrt(f) | ||
| 174 | } | ||
| 175 | |||
| 176 | extern "C" fn logf(f: f32) -> f32 { | ||
| 177 | super::ln(f) | ||
| 178 | } | ||
| 179 | |||
| 180 | #[bootrom_v2] | ||
| 181 | extern "C" fn log(f: f64) -> f64 { | ||
| 182 | super::ln(f) | ||
| 183 | } | ||
| 184 | |||
| 185 | extern "C" fn expf(f: f32) -> f32 { | ||
| 186 | super::exp(f) | ||
| 187 | } | ||
| 188 | |||
| 189 | #[bootrom_v2] | ||
| 190 | extern "C" fn exp(f: f64) -> f64 { | ||
| 191 | super::exp(f) | ||
| 192 | } | ||
| 193 | |||
| 194 | #[slower_than_default] | ||
| 195 | extern "C" fn sinf(f: f32) -> f32 { | ||
| 196 | super::sin(f) | ||
| 197 | } | ||
| 198 | |||
| 199 | #[slower_than_default] | ||
| 200 | #[bootrom_v2] | ||
| 201 | extern "C" fn sin(f: f64) -> f64 { | ||
| 202 | super::sin(f) | ||
| 203 | } | ||
| 204 | |||
| 205 | #[slower_than_default] | ||
| 206 | extern "C" fn cosf(f: f32) -> f32 { | ||
| 207 | super::cos(f) | ||
| 208 | } | ||
| 209 | |||
| 210 | #[slower_than_default] | ||
| 211 | #[bootrom_v2] | ||
| 212 | extern "C" fn cos(f: f64) -> f64 { | ||
| 213 | super::cos(f) | ||
| 214 | } | ||
| 215 | |||
| 216 | #[slower_than_default] | ||
| 217 | extern "C" fn tanf(f: f32) -> f32 { | ||
| 218 | super::tan(f) | ||
| 219 | } | ||
| 220 | |||
| 221 | #[slower_than_default] | ||
| 222 | #[bootrom_v2] | ||
| 223 | extern "C" fn tan(f: f64) -> f64 { | ||
| 224 | super::tan(f) | ||
| 225 | } | ||
| 226 | |||
| 227 | // Questionable gain | ||
| 228 | #[bootrom_v2] | ||
| 229 | extern "C" fn atan2f(a: f32, b: f32) -> f32 { | ||
| 230 | super::atan2(a, b) | ||
| 231 | } | ||
| 232 | |||
| 233 | // Questionable gain | ||
| 234 | #[bootrom_v2] | ||
| 235 | extern "C" fn atan2(a: f64, b: f64) -> f64 { | ||
| 236 | super::atan2(a, b) | ||
| 237 | } | ||
| 238 | } | ||
| 239 | } | ||
diff --git a/embassy-rp/src/float/mod.rs b/embassy-rp/src/float/mod.rs new file mode 100644 index 000000000..945afff90 --- /dev/null +++ b/embassy-rp/src/float/mod.rs | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | // Credit: taken from `rp-hal` (also licensed Apache+MIT) | ||
| 2 | // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mod.rs | ||
| 3 | |||
| 4 | use core::ops; | ||
| 5 | |||
| 6 | // Borrowed and simplified from compiler-builtins so we can use bit ops | ||
| 7 | // on floating point without macro soup. | ||
| 8 | pub(crate) trait Int: | ||
| 9 | Copy | ||
| 10 | + core::fmt::Debug | ||
| 11 | + PartialEq | ||
| 12 | + PartialOrd | ||
| 13 | + ops::AddAssign | ||
| 14 | + ops::SubAssign | ||
| 15 | + ops::BitAndAssign | ||
| 16 | + ops::BitOrAssign | ||
| 17 | + ops::BitXorAssign | ||
| 18 | + ops::ShlAssign<i32> | ||
| 19 | + ops::ShrAssign<u32> | ||
| 20 | + ops::Add<Output = Self> | ||
| 21 | + ops::Sub<Output = Self> | ||
| 22 | + ops::Div<Output = Self> | ||
| 23 | + ops::Shl<u32, Output = Self> | ||
| 24 | + ops::Shr<u32, Output = Self> | ||
| 25 | + ops::BitOr<Output = Self> | ||
| 26 | + ops::BitXor<Output = Self> | ||
| 27 | + ops::BitAnd<Output = Self> | ||
| 28 | + ops::Not<Output = Self> | ||
| 29 | { | ||
| 30 | const ZERO: Self; | ||
| 31 | } | ||
| 32 | |||
| 33 | macro_rules! int_impl { | ||
| 34 | ($ty:ty) => { | ||
| 35 | impl Int for $ty { | ||
| 36 | const ZERO: Self = 0; | ||
| 37 | } | ||
| 38 | }; | ||
| 39 | } | ||
| 40 | |||
| 41 | int_impl!(u32); | ||
| 42 | int_impl!(u64); | ||
| 43 | |||
| 44 | pub(crate) trait Float: | ||
| 45 | Copy | ||
| 46 | + core::fmt::Debug | ||
| 47 | + PartialEq | ||
| 48 | + PartialOrd | ||
| 49 | + ops::AddAssign | ||
| 50 | + ops::MulAssign | ||
| 51 | + ops::Add<Output = Self> | ||
| 52 | + ops::Sub<Output = Self> | ||
| 53 | + ops::Div<Output = Self> | ||
| 54 | + ops::Rem<Output = Self> | ||
| 55 | { | ||
| 56 | /// A uint of the same with as the float | ||
| 57 | type Int: Int; | ||
| 58 | |||
| 59 | /// NaN representation for the float | ||
| 60 | const NAN: Self; | ||
| 61 | |||
| 62 | /// The bitwidth of the float type | ||
| 63 | const BITS: u32; | ||
| 64 | |||
| 65 | /// The bitwidth of the significand | ||
| 66 | const SIGNIFICAND_BITS: u32; | ||
| 67 | |||
| 68 | /// A mask for the sign bit | ||
| 69 | const SIGN_MASK: Self::Int; | ||
| 70 | |||
| 71 | /// A mask for the significand | ||
| 72 | const SIGNIFICAND_MASK: Self::Int; | ||
| 73 | |||
| 74 | /// A mask for the exponent | ||
| 75 | const EXPONENT_MASK: Self::Int; | ||
| 76 | |||
| 77 | /// Returns `self` transmuted to `Self::Int` | ||
| 78 | fn repr(self) -> Self::Int; | ||
| 79 | |||
| 80 | /// Returns a `Self::Int` transmuted back to `Self` | ||
| 81 | fn from_repr(a: Self::Int) -> Self; | ||
| 82 | |||
| 83 | /// Return a sign swapped `self` | ||
| 84 | fn negate(self) -> Self; | ||
| 85 | |||
| 86 | /// Returns true if `self` is either NaN or infinity | ||
| 87 | fn is_not_finite(self) -> bool { | ||
| 88 | (self.repr() & Self::EXPONENT_MASK) == Self::EXPONENT_MASK | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Returns true if `self` is infinity | ||
| 92 | fn is_infinity(self) -> bool { | ||
| 93 | (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) == Self::EXPONENT_MASK | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Returns true if `self is NaN | ||
| 97 | fn is_nan(self) -> bool { | ||
| 98 | (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) > Self::EXPONENT_MASK | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Returns true if `self` is negative | ||
| 102 | fn is_sign_negative(self) -> bool { | ||
| 103 | (self.repr() & Self::SIGN_MASK) != Self::Int::ZERO | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Returns true if `self` is zero (either sign) | ||
| 107 | fn is_zero(self) -> bool { | ||
| 108 | (self.repr() & (Self::SIGNIFICAND_MASK | Self::EXPONENT_MASK)) == Self::Int::ZERO | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | macro_rules! float_impl { | ||
| 113 | ($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => { | ||
| 114 | impl Float for $ty { | ||
| 115 | type Int = $ity; | ||
| 116 | |||
| 117 | const NAN: Self = <$ty>::NAN; | ||
| 118 | |||
| 119 | const BITS: u32 = $bits; | ||
| 120 | const SIGNIFICAND_BITS: u32 = $significand_bits; | ||
| 121 | |||
| 122 | const SIGN_MASK: Self::Int = 1 << (Self::BITS - 1); | ||
| 123 | const SIGNIFICAND_MASK: Self::Int = (1 << Self::SIGNIFICAND_BITS) - 1; | ||
| 124 | const EXPONENT_MASK: Self::Int = !(Self::SIGN_MASK | Self::SIGNIFICAND_MASK); | ||
| 125 | |||
| 126 | fn repr(self) -> Self::Int { | ||
| 127 | self.to_bits() | ||
| 128 | } | ||
| 129 | |||
| 130 | fn from_repr(a: Self::Int) -> Self { | ||
| 131 | Self::from_bits(a) | ||
| 132 | } | ||
| 133 | |||
| 134 | fn negate(self) -> Self { | ||
| 135 | -self | ||
| 136 | } | ||
| 137 | } | ||
| 138 | }; | ||
| 139 | } | ||
| 140 | |||
| 141 | float_impl!(f32, u32, 32, 23); | ||
| 142 | float_impl!(f64, u64, 64, 52); | ||
| 143 | |||
| 144 | mod add_sub; | ||
| 145 | mod cmp; | ||
| 146 | mod conv; | ||
| 147 | mod div; | ||
| 148 | mod functions; | ||
| 149 | mod mul; | ||
diff --git a/embassy-rp/src/float/mul.rs b/embassy-rp/src/float/mul.rs new file mode 100644 index 000000000..ceb0210e3 --- /dev/null +++ b/embassy-rp/src/float/mul.rs | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | // Credit: taken from `rp-hal` (also licensed Apache+MIT) | ||
| 2 | // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mul.rs | ||
| 3 | |||
| 4 | use super::Float; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait ROMMul { | ||
| 8 | fn rom_mul(self, b: Self) -> Self; | ||
| 9 | } | ||
| 10 | |||
| 11 | impl ROMMul for f32 { | ||
| 12 | fn rom_mul(self, b: Self) -> Self { | ||
| 13 | rom_data::float_funcs::fmul(self, b) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | impl ROMMul for f64 { | ||
| 18 | fn rom_mul(self, b: Self) -> Self { | ||
| 19 | rom_data::double_funcs::dmul(self, b) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | fn mul<F: Float + ROMMul>(a: F, b: F) -> F { | ||
| 24 | if a.is_not_finite() { | ||
| 25 | if b.is_zero() { | ||
| 26 | // [-]inf/NaN * 0 = NaN | ||
| 27 | return F::NAN; | ||
| 28 | } | ||
| 29 | |||
| 30 | return if b.is_sign_negative() { | ||
| 31 | // [+/-]inf/NaN * (-X) = [-/+]inf/NaN | ||
| 32 | a.negate() | ||
| 33 | } else { | ||
| 34 | // [-]inf/NaN * X = [-]inf/NaN | ||
| 35 | a | ||
| 36 | }; | ||
| 37 | } | ||
| 38 | |||
| 39 | if b.is_not_finite() { | ||
| 40 | if a.is_zero() { | ||
| 41 | // 0 * [-]inf/NaN = NaN | ||
| 42 | return F::NAN; | ||
| 43 | } | ||
| 44 | |||
| 45 | return if b.is_sign_negative() { | ||
| 46 | // (-X) * [+/-]inf/NaN = [-/+]inf/NaN | ||
| 47 | b.negate() | ||
| 48 | } else { | ||
| 49 | // X * [-]inf/NaN = [-]inf/NaN | ||
| 50 | b | ||
| 51 | }; | ||
| 52 | } | ||
| 53 | |||
| 54 | a.rom_mul(b) | ||
| 55 | } | ||
| 56 | |||
| 57 | intrinsics! { | ||
| 58 | #[alias = __mulsf3vfp] | ||
| 59 | #[aeabi = __aeabi_fmul] | ||
| 60 | extern "C" fn __mulsf3(a: f32, b: f32) -> f32 { | ||
| 61 | mul(a, b) | ||
| 62 | } | ||
| 63 | |||
| 64 | #[bootrom_v2] | ||
| 65 | #[alias = __muldf3vfp] | ||
| 66 | #[aeabi = __aeabi_dmul] | ||
| 67 | extern "C" fn __muldf3(a: f64, b: f64) -> f64 { | ||
| 68 | mul(a, b) | ||
| 69 | } | ||
| 70 | } | ||
