diff options
| -rw-r--r-- | embassy-rp/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-rp/src/intrinsics.rs | 276 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 3 | ||||
| -rw-r--r-- | embassy-rp/src/rom_data.rs | 733 |
4 files changed, 1016 insertions, 0 deletions
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 885a4746d..df0af8dfb 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml | |||
| @@ -22,6 +22,10 @@ unstable-pac = [] | |||
| 22 | 22 | ||
| 23 | time-driver = [] | 23 | time-driver = [] |
| 24 | 24 | ||
| 25 | rom-func-cache = [] | ||
| 26 | disable-intrinsics = [] | ||
| 27 | rom-v2-intrinsics = [] | ||
| 28 | |||
| 25 | # Enable nightly-only features | 29 | # Enable nightly-only features |
| 26 | nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] | 30 | nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] |
| 27 | 31 | ||
diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs new file mode 100644 index 000000000..ac1f54800 --- /dev/null +++ b/embassy-rp/src/intrinsics.rs | |||
| @@ -0,0 +1,276 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | // Credit: taken from `rp-hal` (also licensed Apache+MIT) | ||
| 4 | // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/intrinsics.rs | ||
| 5 | |||
| 6 | /// Generate a series of aliases for an intrinsic function. | ||
| 7 | macro_rules! intrinsics_aliases { | ||
| 8 | ( | ||
| 9 | extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, | ||
| 10 | ) => {}; | ||
| 11 | ( | ||
| 12 | unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, | ||
| 13 | ) => {}; | ||
| 14 | |||
| 15 | ( | ||
| 16 | extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, | ||
| 17 | $alias:ident | ||
| 18 | $($rest:ident)* | ||
| 19 | ) => { | ||
| 20 | #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] | ||
| 21 | intrinsics! { | ||
| 22 | extern $abi fn $alias( $($argname: $ty),* ) -> $ret { | ||
| 23 | $name($($argname),*) | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | intrinsics_aliases! { | ||
| 28 | extern $abi fn $name( $($argname: $ty),* ) -> $ret, | ||
| 29 | $($rest)* | ||
| 30 | } | ||
| 31 | }; | ||
| 32 | |||
| 33 | ( | ||
| 34 | unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, | ||
| 35 | $alias:ident | ||
| 36 | $($rest:ident)* | ||
| 37 | ) => { | ||
| 38 | #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] | ||
| 39 | intrinsics! { | ||
| 40 | unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret { | ||
| 41 | $name($($argname),*) | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | intrinsics_aliases! { | ||
| 46 | unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret, | ||
| 47 | $($rest)* | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | } | ||
| 51 | |||
| 52 | /// The macro used to define overridden intrinsics. | ||
| 53 | /// | ||
| 54 | /// This is heavily inspired by the macro used by compiler-builtins. The idea | ||
| 55 | /// is to abstract anything special that needs to be done to override an | ||
| 56 | /// intrinsic function. Intrinsic generation is disabled for non-ARM targets | ||
| 57 | /// so things like CI and docs generation do not have problems. Additionally | ||
| 58 | /// they can be disabled with the crate feature `disable-intrinsics` for | ||
| 59 | /// testing or comparing performance. | ||
| 60 | /// | ||
| 61 | /// Like the compiler-builtins macro, it accepts a series of functions that | ||
| 62 | /// looks like normal Rust code: | ||
| 63 | /// | ||
| 64 | /// intrinsics! { | ||
| 65 | /// extern "C" fn foo(a: i32) -> u32 { | ||
| 66 | /// // ... | ||
| 67 | /// } | ||
| 68 | /// | ||
| 69 | /// #[nonstandard_attribute] | ||
| 70 | /// extern "C" fn bar(a: i32) -> u32 { | ||
| 71 | /// // ... | ||
| 72 | /// } | ||
| 73 | /// } | ||
| 74 | /// | ||
| 75 | /// Each function can also be decorated with nonstandard attributes to control | ||
| 76 | /// additional behaviour: | ||
| 77 | /// | ||
| 78 | /// * `slower_than_default` - indicates that the override is slower than the | ||
| 79 | /// default implementation. Currently this just disables the override | ||
| 80 | /// entirely. | ||
| 81 | /// * `bootrom_v2` - indicates that the override is only available | ||
| 82 | /// on a V2 bootrom or higher. Only enabled when the feature | ||
| 83 | /// `rom-v2-intrinsics` is set. | ||
| 84 | /// * `alias` - accepts a list of names to alias the intrinsic to. | ||
| 85 | /// * `aeabi` - accepts a list of ARM EABI names to alias to. | ||
| 86 | /// | ||
| 87 | macro_rules! intrinsics { | ||
| 88 | () => {}; | ||
| 89 | |||
| 90 | ( | ||
| 91 | #[slower_than_default] | ||
| 92 | $(#[$($attr:tt)*])* | ||
| 93 | extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { | ||
| 94 | $($body:tt)* | ||
| 95 | } | ||
| 96 | |||
| 97 | $($rest:tt)* | ||
| 98 | ) => { | ||
| 99 | // Not exported, but defined so the actual implementation is | ||
| 100 | // considered used | ||
| 101 | #[allow(dead_code)] | ||
| 102 | fn $name( $($argname: $ty),* ) -> $ret { | ||
| 103 | $($body)* | ||
| 104 | } | ||
| 105 | |||
| 106 | intrinsics!($($rest)*); | ||
| 107 | }; | ||
| 108 | |||
| 109 | ( | ||
| 110 | #[bootrom_v2] | ||
| 111 | $(#[$($attr:tt)*])* | ||
| 112 | extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { | ||
| 113 | $($body:tt)* | ||
| 114 | } | ||
| 115 | |||
| 116 | $($rest:tt)* | ||
| 117 | ) => { | ||
| 118 | // Not exported, but defined so the actual implementation is | ||
| 119 | // considered used | ||
| 120 | #[cfg(not(feature = "rom-v2-intrinsics"))] | ||
| 121 | #[allow(dead_code)] | ||
| 122 | fn $name( $($argname: $ty),* ) -> $ret { | ||
| 123 | $($body)* | ||
| 124 | } | ||
| 125 | |||
| 126 | #[cfg(feature = "rom-v2-intrinsics")] | ||
| 127 | intrinsics! { | ||
| 128 | $(#[$($attr)*])* | ||
| 129 | extern $abi fn $name( $($argname: $ty),* ) -> $ret { | ||
| 130 | $($body)* | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | intrinsics!($($rest)*); | ||
| 135 | }; | ||
| 136 | |||
| 137 | ( | ||
| 138 | #[alias = $($alias:ident),*] | ||
| 139 | $(#[$($attr:tt)*])* | ||
| 140 | extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { | ||
| 141 | $($body:tt)* | ||
| 142 | } | ||
| 143 | |||
| 144 | $($rest:tt)* | ||
| 145 | ) => { | ||
| 146 | intrinsics! { | ||
| 147 | $(#[$($attr)*])* | ||
| 148 | extern $abi fn $name( $($argname: $ty),* ) -> $ret { | ||
| 149 | $($body)* | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | intrinsics_aliases! { | ||
| 154 | extern $abi fn $name( $($argname: $ty),* ) -> $ret, | ||
| 155 | $($alias) * | ||
| 156 | } | ||
| 157 | |||
| 158 | intrinsics!($($rest)*); | ||
| 159 | }; | ||
| 160 | |||
| 161 | ( | ||
| 162 | #[alias = $($alias:ident),*] | ||
| 163 | $(#[$($attr:tt)*])* | ||
| 164 | unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { | ||
| 165 | $($body:tt)* | ||
| 166 | } | ||
| 167 | |||
| 168 | $($rest:tt)* | ||
| 169 | ) => { | ||
| 170 | intrinsics! { | ||
| 171 | $(#[$($attr)*])* | ||
| 172 | unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { | ||
| 173 | $($body)* | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | intrinsics_aliases! { | ||
| 178 | unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret, | ||
| 179 | $($alias) * | ||
| 180 | } | ||
| 181 | |||
| 182 | intrinsics!($($rest)*); | ||
| 183 | }; | ||
| 184 | |||
| 185 | ( | ||
| 186 | #[aeabi = $($alias:ident),*] | ||
| 187 | $(#[$($attr:tt)*])* | ||
| 188 | extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { | ||
| 189 | $($body:tt)* | ||
| 190 | } | ||
| 191 | |||
| 192 | $($rest:tt)* | ||
| 193 | ) => { | ||
| 194 | intrinsics! { | ||
| 195 | $(#[$($attr)*])* | ||
| 196 | extern $abi fn $name( $($argname: $ty),* ) -> $ret { | ||
| 197 | $($body)* | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | intrinsics_aliases! { | ||
| 202 | extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret, | ||
| 203 | $($alias) * | ||
| 204 | } | ||
| 205 | |||
| 206 | intrinsics!($($rest)*); | ||
| 207 | }; | ||
| 208 | |||
| 209 | ( | ||
| 210 | $(#[$($attr:tt)*])* | ||
| 211 | extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { | ||
| 212 | $($body:tt)* | ||
| 213 | } | ||
| 214 | |||
| 215 | $($rest:tt)* | ||
| 216 | ) => { | ||
| 217 | #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] | ||
| 218 | $(#[$($attr)*])* | ||
| 219 | extern $abi fn $name( $($argname: $ty),* ) -> $ret { | ||
| 220 | $($body)* | ||
| 221 | } | ||
| 222 | |||
| 223 | #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] | ||
| 224 | mod $name { | ||
| 225 | #[no_mangle] | ||
| 226 | $(#[$($attr)*])* | ||
| 227 | pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { | ||
| 228 | super::$name($($argname),*) | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | // Not exported, but defined so the actual implementation is | ||
| 233 | // considered used | ||
| 234 | #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] | ||
| 235 | #[allow(dead_code)] | ||
| 236 | fn $name( $($argname: $ty),* ) -> $ret { | ||
| 237 | $($body)* | ||
| 238 | } | ||
| 239 | |||
| 240 | intrinsics!($($rest)*); | ||
| 241 | }; | ||
| 242 | |||
| 243 | ( | ||
| 244 | $(#[$($attr:tt)*])* | ||
| 245 | unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { | ||
| 246 | $($body:tt)* | ||
| 247 | } | ||
| 248 | |||
| 249 | $($rest:tt)* | ||
| 250 | ) => { | ||
| 251 | #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] | ||
| 252 | $(#[$($attr)*])* | ||
| 253 | unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { | ||
| 254 | $($body)* | ||
| 255 | } | ||
| 256 | |||
| 257 | #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] | ||
| 258 | mod $name { | ||
| 259 | #[no_mangle] | ||
| 260 | $(#[$($attr)*])* | ||
| 261 | pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { | ||
| 262 | super::$name($($argname),*) | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | // Not exported, but defined so the actual implementation is | ||
| 267 | // considered used | ||
| 268 | #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] | ||
| 269 | #[allow(dead_code)] | ||
| 270 | unsafe fn $name( $($argname: $ty),* ) -> $ret { | ||
| 271 | $($body)* | ||
| 272 | } | ||
| 273 | |||
| 274 | intrinsics!($($rest)*); | ||
| 275 | }; | ||
| 276 | } | ||
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9ce09064a..9ac98d226 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -4,9 +4,12 @@ | |||
| 4 | // This mod MUST go first, so that the others see its macros. | 4 | // This mod MUST go first, so that the others see its macros. |
| 5 | pub(crate) mod fmt; | 5 | pub(crate) mod fmt; |
| 6 | 6 | ||
| 7 | mod intrinsics; | ||
| 8 | |||
| 7 | pub mod dma; | 9 | pub mod dma; |
| 8 | pub mod gpio; | 10 | pub mod gpio; |
| 9 | pub mod interrupt; | 11 | pub mod interrupt; |
| 12 | pub mod rom_data; | ||
| 10 | pub mod rtc; | 13 | pub mod rtc; |
| 11 | pub mod spi; | 14 | pub mod spi; |
| 12 | #[cfg(feature = "time-driver")] | 15 | #[cfg(feature = "time-driver")] |
diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs new file mode 100644 index 000000000..757a27114 --- /dev/null +++ b/embassy-rp/src/rom_data.rs | |||
| @@ -0,0 +1,733 @@ | |||
| 1 | //! Functions and data from the RPI Bootrom. | ||
| 2 | //! | ||
| 3 | //! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1: | ||
| 4 | //! | ||
| 5 | //! > The Bootrom contains a number of public functions that provide useful | ||
| 6 | //! > RP2040 functionality that might be needed in the absence of any other code | ||
| 7 | //! > on the device, as well as highly optimized versions of certain key | ||
| 8 | //! > functionality that would otherwise have to take up space in most user | ||
| 9 | //! > binaries. | ||
| 10 | |||
| 11 | // Credit: taken from `rp-hal` (also licensed Apache+MIT) | ||
| 12 | // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs | ||
| 13 | |||
| 14 | /// A bootrom function table code. | ||
| 15 | pub type RomFnTableCode = [u8; 2]; | ||
| 16 | |||
| 17 | /// This function searches for (table) | ||
| 18 | type RomTableLookupFn<T> = unsafe extern "C" fn(*const u16, u32) -> T; | ||
| 19 | |||
| 20 | /// The following addresses are described at `2.8.2. Bootrom Contents` | ||
| 21 | /// Pointer to the lookup table function supplied by the rom. | ||
| 22 | const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _; | ||
| 23 | |||
| 24 | /// Pointer to helper functions lookup table. | ||
| 25 | const FUNC_TABLE: *const u16 = 0x0000_0014 as _; | ||
| 26 | |||
| 27 | /// Pointer to the public data lookup table. | ||
| 28 | const DATA_TABLE: *const u16 = 0x0000_0016 as _; | ||
| 29 | |||
| 30 | /// Address of the version number of the ROM. | ||
| 31 | const VERSION_NUMBER: *const u8 = 0x0000_0013 as _; | ||
| 32 | |||
| 33 | /// Retrive rom content from a table using a code. | ||
| 34 | fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T { | ||
| 35 | unsafe { | ||
| 36 | let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR); | ||
| 37 | let rom_table_lookup: RomTableLookupFn<T> = core::mem::transmute(rom_table_lookup_ptr); | ||
| 38 | rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32) | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | /// To save space, the ROM likes to store memory pointers (which are 32-bit on | ||
| 43 | /// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the | ||
| 44 | /// values they point at live in the first 64 KiB of ROM, and the ROM is mapped | ||
| 45 | /// to address `0x0000_0000` and so 16-bits are always sufficient. | ||
| 46 | /// | ||
| 47 | /// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer. | ||
| 48 | unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 { | ||
| 49 | let ptr: u16 = *rom_address; | ||
| 50 | ptr as *const u32 | ||
| 51 | } | ||
| 52 | |||
| 53 | macro_rules! declare_rom_function { | ||
| 54 | ( | ||
| 55 | $(#[$outer:meta])* | ||
| 56 | fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty | ||
| 57 | $lookup:block | ||
| 58 | ) => { | ||
| 59 | #[doc = r"Additional access for the `"] | ||
| 60 | #[doc = stringify!($name)] | ||
| 61 | #[doc = r"` ROM function."] | ||
| 62 | pub mod $name { | ||
| 63 | /// Retrieve a function pointer. | ||
| 64 | #[cfg(not(feature = "rom-func-cache"))] | ||
| 65 | pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { | ||
| 66 | let p: *const u32 = $lookup; | ||
| 67 | unsafe { | ||
| 68 | let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); | ||
| 69 | func | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Retrieve a function pointer. | ||
| 74 | #[cfg(feature = "rom-func-cache")] | ||
| 75 | pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { | ||
| 76 | use core::sync::atomic::{AtomicU16, Ordering}; | ||
| 77 | |||
| 78 | // All pointers in the ROM fit in 16 bits, so we don't need a | ||
| 79 | // full width word to store the cached value. | ||
| 80 | static CACHED_PTR: AtomicU16 = AtomicU16::new(0); | ||
| 81 | // This is safe because the lookup will always resolve | ||
| 82 | // to the same value. So even if an interrupt or another | ||
| 83 | // core starts at the same time, it just repeats some | ||
| 84 | // work and eventually writes back the correct value. | ||
| 85 | let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { | ||
| 86 | 0 => { | ||
| 87 | let raw: *const u32 = $lookup; | ||
| 88 | CACHED_PTR.store(raw as u16, Ordering::Relaxed); | ||
| 89 | raw | ||
| 90 | }, | ||
| 91 | val => val as *const u32, | ||
| 92 | }; | ||
| 93 | unsafe { | ||
| 94 | let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); | ||
| 95 | func | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | $(#[$outer])* | ||
| 101 | pub extern "C" fn $name( $($argname: $ty),* ) -> $ret { | ||
| 102 | $name::ptr()($($argname),*) | ||
| 103 | } | ||
| 104 | }; | ||
| 105 | |||
| 106 | ( | ||
| 107 | $(#[$outer:meta])* | ||
| 108 | unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty | ||
| 109 | $lookup:block | ||
| 110 | ) => { | ||
| 111 | #[doc = r"Additional access for the `"] | ||
| 112 | #[doc = stringify!($name)] | ||
| 113 | #[doc = r"` ROM function."] | ||
| 114 | pub mod $name { | ||
| 115 | /// Retrieve a function pointer. | ||
| 116 | #[cfg(not(feature = "rom-func-cache"))] | ||
| 117 | pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { | ||
| 118 | let p: *const u32 = $lookup; | ||
| 119 | unsafe { | ||
| 120 | let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); | ||
| 121 | func | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | /// Retrieve a function pointer. | ||
| 126 | #[cfg(feature = "rom-func-cache")] | ||
| 127 | pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { | ||
| 128 | use core::sync::atomic::{AtomicU16, Ordering}; | ||
| 129 | |||
| 130 | // All pointers in the ROM fit in 16 bits, so we don't need a | ||
| 131 | // full width word to store the cached value. | ||
| 132 | static CACHED_PTR: AtomicU16 = AtomicU16::new(0); | ||
| 133 | // This is safe because the lookup will always resolve | ||
| 134 | // to the same value. So even if an interrupt or another | ||
| 135 | // core starts at the same time, it just repeats some | ||
| 136 | // work and eventually writes back the correct value. | ||
| 137 | let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { | ||
| 138 | 0 => { | ||
| 139 | let raw: *const u32 = $lookup; | ||
| 140 | CACHED_PTR.store(raw as u16, Ordering::Relaxed); | ||
| 141 | raw | ||
| 142 | }, | ||
| 143 | val => val as *const u32, | ||
| 144 | }; | ||
| 145 | unsafe { | ||
| 146 | let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); | ||
| 147 | func | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | $(#[$outer])* | ||
| 153 | pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret { | ||
| 154 | $name::ptr()($($argname),*) | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | } | ||
| 158 | |||
| 159 | macro_rules! rom_functions { | ||
| 160 | () => {}; | ||
| 161 | |||
| 162 | ( | ||
| 163 | $(#[$outer:meta])* | ||
| 164 | $c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty; | ||
| 165 | |||
| 166 | $($rest:tt)* | ||
| 167 | ) => { | ||
| 168 | declare_rom_function! { | ||
| 169 | $(#[$outer])* | ||
| 170 | fn $name( $($argname: $ty),* ) -> $ret { | ||
| 171 | $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | rom_functions!($($rest)*); | ||
| 176 | }; | ||
| 177 | |||
| 178 | ( | ||
| 179 | $(#[$outer:meta])* | ||
| 180 | $c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty; | ||
| 181 | |||
| 182 | $($rest:tt)* | ||
| 183 | ) => { | ||
| 184 | declare_rom_function! { | ||
| 185 | $(#[$outer])* | ||
| 186 | unsafe fn $name( $($argname: $ty),* ) -> $ret { | ||
| 187 | $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | rom_functions!($($rest)*); | ||
| 192 | }; | ||
| 193 | } | ||
| 194 | |||
| 195 | rom_functions! { | ||
| 196 | /// Return a count of the number of 1 bits in value. | ||
| 197 | b"P3" fn popcount32(value: u32) -> u32; | ||
| 198 | |||
| 199 | /// Return the bits of value in the reverse order. | ||
| 200 | b"R3" fn reverse32(value: u32) -> u32; | ||
| 201 | |||
| 202 | /// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32. | ||
| 203 | b"L3" fn clz32(value: u32) -> u32; | ||
| 204 | |||
| 205 | /// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32. | ||
| 206 | b"T3" fn ctz32(value: u32) -> u32; | ||
| 207 | |||
| 208 | /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode: | ||
| 209 | /// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED | ||
| 210 | /// for the USB Mass Storage Device: | ||
| 211 | /// * 0 No pins are used as per cold boot. | ||
| 212 | /// * Otherwise a single bit set indicating which GPIO pin should be set to output and | ||
| 213 | /// raised whenever there is mass storage activity from the host. | ||
| 214 | /// * disable_interface_mask may be used to control the exposed USB interfaces: | ||
| 215 | /// * 0 To enable both interfaces (as per cold boot). | ||
| 216 | /// * 1 To disable the USB Mass Storage Interface. | ||
| 217 | /// * 2 to Disable the USB PICOBOOT Interface. | ||
| 218 | b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> (); | ||
| 219 | |||
| 220 | /// Sets n bytes start at ptr to the value c and returns ptr | ||
| 221 | b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8; | ||
| 222 | |||
| 223 | /// Sets n bytes start at ptr to the value c and returns ptr. | ||
| 224 | /// | ||
| 225 | /// Note this is a slightly more efficient variant of _memset that may only | ||
| 226 | /// be used if ptr is word aligned. | ||
| 227 | // Note the datasheet does not match the actual ROM for the code here, see | ||
| 228 | // https://github.com/raspberrypi/pico-feedback/issues/217 | ||
| 229 | b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32; | ||
| 230 | |||
| 231 | /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the | ||
| 232 | /// regions overlap. | ||
| 233 | b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8; | ||
| 234 | |||
| 235 | /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the | ||
| 236 | /// regions overlap. | ||
| 237 | /// | ||
| 238 | /// Note this is a slightly more efficient variant of _memcpy that may only be | ||
| 239 | /// used if dest and src are word aligned. | ||
| 240 | b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8; | ||
| 241 | |||
| 242 | /// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads. | ||
| 243 | b"IF" unsafe fn connect_internal_flash() -> (); | ||
| 244 | |||
| 245 | /// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence. | ||
| 246 | /// | ||
| 247 | /// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be | ||
| 248 | /// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This | ||
| 249 | /// function configures the SSI with a fixed SCK clock divisor of /6. | ||
| 250 | b"EX" unsafe fn flash_exit_xip() -> (); | ||
| 251 | |||
| 252 | /// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a | ||
| 253 | /// block erase command e.g. D8h block erase, and the size of the block erased by this | ||
| 254 | /// command — this function will use the larger block erase where possible, for much higher | ||
| 255 | /// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of | ||
| 256 | /// 4096 bytes. | ||
| 257 | b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> (); | ||
| 258 | |||
| 259 | /// Program data to a range of flash addresses starting at `addr` (and | ||
| 260 | /// offset from the start of flash) and `count` bytes in size. The value | ||
| 261 | /// `addr` must be aligned to a 256-byte boundary, and `count` must be a | ||
| 262 | /// multiple of 256. | ||
| 263 | b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> (); | ||
| 264 | |||
| 265 | /// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can | ||
| 266 | /// drive the flashchip select as normal. | ||
| 267 | b"FC" unsafe fn flash_flush_cache() -> (); | ||
| 268 | |||
| 269 | /// Configure the SSI to generate a standard 03h serial read command, with 24 address bits, | ||
| 270 | /// upon each XIP access. This is a very slow XIP configuration, but is very widely supported. | ||
| 271 | /// The debugger calls this function after performing a flash erase/programming operation, so | ||
| 272 | /// that the freshly-programmed code and data is visible to the debug host, without having to | ||
| 273 | /// know exactly what kind of flash device is connected. | ||
| 274 | b"CX" unsafe fn flash_enter_cmd_xip() -> (); | ||
| 275 | |||
| 276 | /// This is the method that is entered by core 1 on reset to wait to be launched by core 0. | ||
| 277 | /// There are few cases where you should call this method (resetting core 1 is much better). | ||
| 278 | /// This method does not return and should only ever be called on core 1. | ||
| 279 | b"WV" unsafe fn wait_for_vector() -> !; | ||
| 280 | } | ||
| 281 | |||
| 282 | // Various C intrinsics in the ROM | ||
| 283 | intrinsics! { | ||
| 284 | #[alias = __popcountdi2] | ||
| 285 | extern "C" fn __popcountsi2(x: u32) -> u32 { | ||
| 286 | popcount32(x) | ||
| 287 | } | ||
| 288 | |||
| 289 | #[alias = __clzdi2] | ||
| 290 | extern "C" fn __clzsi2(x: u32) -> u32 { | ||
| 291 | clz32(x) | ||
| 292 | } | ||
| 293 | |||
| 294 | #[alias = __ctzdi2] | ||
| 295 | extern "C" fn __ctzsi2(x: u32) -> u32 { | ||
| 296 | ctz32(x) | ||
| 297 | } | ||
| 298 | |||
| 299 | // __rbit is only unofficial, but it show up in the ARM documentation, | ||
| 300 | // so may as well hook it up. | ||
| 301 | #[alias = __rbitl] | ||
| 302 | extern "C" fn __rbit(x: u32) -> u32 { | ||
| 303 | reverse32(x) | ||
| 304 | } | ||
| 305 | |||
| 306 | unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () { | ||
| 307 | // Different argument order | ||
| 308 | memset(dest, c as u8, n as u32); | ||
| 309 | } | ||
| 310 | |||
| 311 | #[alias = __aeabi_memset8] | ||
| 312 | unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () { | ||
| 313 | // Different argument order | ||
| 314 | memset4(dest as *mut u32, c as u8, n as u32); | ||
| 315 | } | ||
| 316 | |||
| 317 | unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () { | ||
| 318 | memset(dest, 0, n as u32); | ||
| 319 | } | ||
| 320 | |||
| 321 | #[alias = __aeabi_memclr8] | ||
| 322 | unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () { | ||
| 323 | memset4(dest as *mut u32, 0, n as u32); | ||
| 324 | } | ||
| 325 | |||
| 326 | unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () { | ||
| 327 | memcpy(dest, src, n as u32); | ||
| 328 | } | ||
| 329 | |||
| 330 | #[alias = __aeabi_memcpy8] | ||
| 331 | unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () { | ||
| 332 | memcpy44(dest as *mut u32, src as *const u32, n as u32); | ||
| 333 | } | ||
| 334 | } | ||
| 335 | |||
| 336 | unsafe fn convert_str(s: *const u8) -> &'static str { | ||
| 337 | let mut end = s; | ||
| 338 | while *end != 0 { | ||
| 339 | end = end.add(1); | ||
| 340 | } | ||
| 341 | let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize); | ||
| 342 | core::str::from_utf8_unchecked(s) | ||
| 343 | } | ||
| 344 | |||
| 345 | /// The version number of the rom. | ||
| 346 | pub fn rom_version_number() -> u8 { | ||
| 347 | unsafe { *VERSION_NUMBER } | ||
| 348 | } | ||
| 349 | |||
| 350 | /// The Raspberry Pi Trading Ltd copyright string. | ||
| 351 | pub fn copyright_string() -> &'static str { | ||
| 352 | let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR"); | ||
| 353 | unsafe { convert_str(s) } | ||
| 354 | } | ||
| 355 | |||
| 356 | /// The 8 most significant hex digits of the Bootrom git revision. | ||
| 357 | pub fn git_revision() -> u32 { | ||
| 358 | let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR"); | ||
| 359 | unsafe { *s } | ||
| 360 | } | ||
| 361 | |||
| 362 | /// The start address of the floating point library code and data. | ||
| 363 | /// | ||
| 364 | /// This and fplib_end along with the individual function pointers in | ||
| 365 | /// soft_float_table can be used to copy the floating point implementation into | ||
| 366 | /// RAM if desired. | ||
| 367 | pub fn fplib_start() -> *const u8 { | ||
| 368 | rom_table_lookup(DATA_TABLE, *b"FS") | ||
| 369 | } | ||
| 370 | |||
| 371 | /// See Table 180 in the RP2040 datasheet for the contents of this table. | ||
| 372 | pub fn soft_float_table() -> *const usize { | ||
| 373 | rom_table_lookup(DATA_TABLE, *b"SF") | ||
| 374 | } | ||
| 375 | |||
| 376 | /// The end address of the floating point library code and data. | ||
| 377 | pub fn fplib_end() -> *const u8 { | ||
| 378 | rom_table_lookup(DATA_TABLE, *b"FE") | ||
| 379 | } | ||
| 380 | |||
| 381 | /// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. | ||
| 382 | pub fn soft_double_table() -> *const usize { | ||
| 383 | if rom_version_number() < 2 { | ||
| 384 | panic!( | ||
| 385 | "Double precision operations require V2 bootrom (found: V{})", | ||
| 386 | rom_version_number() | ||
| 387 | ); | ||
| 388 | } | ||
| 389 | rom_table_lookup(DATA_TABLE, *b"SD") | ||
| 390 | } | ||
| 391 | |||
| 392 | /// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms) | ||
| 393 | pub mod float_funcs { | ||
| 394 | |||
| 395 | macro_rules! make_functions { | ||
| 396 | ( | ||
| 397 | $( | ||
| 398 | $(#[$outer:meta])* | ||
| 399 | $offset:literal $name:ident ( | ||
| 400 | $( $aname:ident : $aty:ty ),* | ||
| 401 | ) -> $ret:ty; | ||
| 402 | )* | ||
| 403 | ) => { | ||
| 404 | $( | ||
| 405 | declare_rom_function! { | ||
| 406 | $(#[$outer])* | ||
| 407 | fn $name( $( $aname : $aty ),* ) -> $ret { | ||
| 408 | let table: *const usize = $crate::rom_data::soft_float_table(); | ||
| 409 | unsafe { | ||
| 410 | // This is the entry in the table. Our offset is given as a | ||
| 411 | // byte offset, but we want the table index (each pointer in | ||
| 412 | // the table is 4 bytes long) | ||
| 413 | let entry: *const usize = table.offset($offset / 4); | ||
| 414 | // Read the pointer from the table | ||
| 415 | core::ptr::read(entry) as *const u32 | ||
| 416 | } | ||
| 417 | } | ||
| 418 | } | ||
| 419 | )* | ||
| 420 | } | ||
| 421 | } | ||
| 422 | |||
| 423 | make_functions! { | ||
| 424 | /// Calculates `a + b` | ||
| 425 | 0x00 fadd(a: f32, b: f32) -> f32; | ||
| 426 | /// Calculates `a - b` | ||
| 427 | 0x04 fsub(a: f32, b: f32) -> f32; | ||
| 428 | /// Calculates `a * b` | ||
| 429 | 0x08 fmul(a: f32, b: f32) -> f32; | ||
| 430 | /// Calculates `a / b` | ||
| 431 | 0x0c fdiv(a: f32, b: f32) -> f32; | ||
| 432 | |||
| 433 | // 0x10 and 0x14 are deprecated | ||
| 434 | |||
| 435 | /// Calculates `sqrt(v)` (or return -Infinity if v is negative) | ||
| 436 | 0x18 fsqrt(v: f32) -> f32; | ||
| 437 | /// Converts an f32 to a signed integer, | ||
| 438 | /// rounding towards -Infinity, and clamping the result to lie within the | ||
| 439 | /// range `-0x80000000` to `0x7FFFFFFF` | ||
| 440 | 0x1c float_to_int(v: f32) -> i32; | ||
| 441 | /// Converts an f32 to an signed fixed point | ||
| 442 | /// integer representation where n specifies the position of the binary | ||
| 443 | /// point in the resulting fixed point representation, e.g. | ||
| 444 | /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, | ||
| 445 | /// and clamps the resulting integer to lie within the range `0x00000000` to | ||
| 446 | /// `0xFFFFFFFF` | ||
| 447 | 0x20 float_to_fix(v: f32, n: i32) -> i32; | ||
| 448 | /// Converts an f32 to an unsigned integer, | ||
| 449 | /// rounding towards -Infinity, and clamping the result to lie within the | ||
| 450 | /// range `0x00000000` to `0xFFFFFFFF` | ||
| 451 | 0x24 float_to_uint(v: f32) -> u32; | ||
| 452 | /// Converts an f32 to an unsigned fixed point | ||
| 453 | /// integer representation where n specifies the position of the binary | ||
| 454 | /// point in the resulting fixed point representation, e.g. | ||
| 455 | /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, | ||
| 456 | /// and clamps the resulting integer to lie within the range `0x00000000` to | ||
| 457 | /// `0xFFFFFFFF` | ||
| 458 | 0x28 float_to_ufix(v: f32, n: i32) -> u32; | ||
| 459 | /// Converts a signed integer to the nearest | ||
| 460 | /// f32 value, rounding to even on tie | ||
| 461 | 0x2c int_to_float(v: i32) -> f32; | ||
| 462 | /// Converts a signed fixed point integer | ||
| 463 | /// representation to the nearest f32 value, rounding to even on tie. `n` | ||
| 464 | /// specifies the position of the binary point in fixed point, so `f = | ||
| 465 | /// nearest(v/(2^n))` | ||
| 466 | 0x30 fix_to_float(v: i32, n: i32) -> f32; | ||
| 467 | /// Converts an unsigned integer to the nearest | ||
| 468 | /// f32 value, rounding to even on tie | ||
| 469 | 0x34 uint_to_float(v: u32) -> f32; | ||
| 470 | /// Converts an unsigned fixed point integer | ||
| 471 | /// representation to the nearest f32 value, rounding to even on tie. `n` | ||
| 472 | /// specifies the position of the binary point in fixed point, so `f = | ||
| 473 | /// nearest(v/(2^n))` | ||
| 474 | 0x38 ufix_to_float(v: u32, n: i32) -> f32; | ||
| 475 | /// Calculates the cosine of `angle`. The value | ||
| 476 | /// of `angle` is in radians, and must be in the range `-1024` to `1024` | ||
| 477 | 0x3c fcos(angle: f32) -> f32; | ||
| 478 | /// Calculates the sine of `angle`. The value of | ||
| 479 | /// `angle` is in radians, and must be in the range `-1024` to `1024` | ||
| 480 | 0x40 fsin(angle: f32) -> f32; | ||
| 481 | /// Calculates the tangent of `angle`. The value | ||
| 482 | /// of `angle` is in radians, and must be in the range `-1024` to `1024` | ||
| 483 | 0x44 ftan(angle: f32) -> f32; | ||
| 484 | |||
| 485 | // 0x48 is deprecated | ||
| 486 | |||
| 487 | /// Calculates the exponential value of `v`, | ||
| 488 | /// i.e. `e ** v` | ||
| 489 | 0x4c fexp(v: f32) -> f32; | ||
| 490 | /// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity | ||
| 491 | 0x50 fln(v: f32) -> f32; | ||
| 492 | } | ||
| 493 | |||
| 494 | macro_rules! make_functions_v2 { | ||
| 495 | ( | ||
| 496 | $( | ||
| 497 | $(#[$outer:meta])* | ||
| 498 | $offset:literal $name:ident ( | ||
| 499 | $( $aname:ident : $aty:ty ),* | ||
| 500 | ) -> $ret:ty; | ||
| 501 | )* | ||
| 502 | ) => { | ||
| 503 | $( | ||
| 504 | declare_rom_function! { | ||
| 505 | $(#[$outer])* | ||
| 506 | fn $name( $( $aname : $aty ),* ) -> $ret { | ||
| 507 | if $crate::rom_data::rom_version_number() < 2 { | ||
| 508 | panic!( | ||
| 509 | "Floating point function requires V2 bootrom (found: V{})", | ||
| 510 | $crate::rom_data::rom_version_number() | ||
| 511 | ); | ||
| 512 | } | ||
| 513 | let table: *const usize = $crate::rom_data::soft_float_table(); | ||
| 514 | unsafe { | ||
| 515 | // This is the entry in the table. Our offset is given as a | ||
| 516 | // byte offset, but we want the table index (each pointer in | ||
| 517 | // the table is 4 bytes long) | ||
| 518 | let entry: *const usize = table.offset($offset / 4); | ||
| 519 | // Read the pointer from the table | ||
| 520 | core::ptr::read(entry) as *const u32 | ||
| 521 | } | ||
| 522 | } | ||
| 523 | } | ||
| 524 | )* | ||
| 525 | } | ||
| 526 | } | ||
| 527 | |||
| 528 | // These are only on BootROM v2 or higher | ||
| 529 | make_functions_v2! { | ||
| 530 | /// Compares two floating point numbers, returning: | ||
| 531 | /// • 0 if a == b | ||
| 532 | /// • -1 if a < b | ||
| 533 | /// • 1 if a > b | ||
| 534 | 0x54 fcmp(a: f32, b: f32) -> i32; | ||
| 535 | /// Computes the arc tangent of `y/x` using the | ||
| 536 | /// signs of arguments to determine the correct quadrant | ||
| 537 | 0x58 fatan2(y: f32, x: f32) -> f32; | ||
| 538 | /// Converts a signed 64-bit integer to the | ||
| 539 | /// nearest f32 value, rounding to even on tie | ||
| 540 | 0x5c int64_to_float(v: i64) -> f32; | ||
| 541 | /// Converts a signed fixed point 64-bit integer | ||
| 542 | /// representation to the nearest f32 value, rounding to even on tie. `n` | ||
| 543 | /// specifies the position of the binary point in fixed point, so `f = | ||
| 544 | /// nearest(v/(2^n))` | ||
| 545 | 0x60 fix64_to_float(v: i64, n: i32) -> f32; | ||
| 546 | /// Converts an unsigned 64-bit integer to the | ||
| 547 | /// nearest f32 value, rounding to even on tie | ||
| 548 | 0x64 uint64_to_float(v: u64) -> f32; | ||
| 549 | /// Converts an unsigned fixed point 64-bit | ||
| 550 | /// integer representation to the nearest f32 value, rounding to even on | ||
| 551 | /// tie. `n` specifies the position of the binary point in fixed point, so | ||
| 552 | /// `f = nearest(v/(2^n))` | ||
| 553 | 0x68 ufix64_to_float(v: u64, n: i32) -> f32; | ||
| 554 | /// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity, | ||
| 555 | /// and clamping the result to lie within the range `-0x8000000000000000` to | ||
| 556 | /// `0x7FFFFFFFFFFFFFFF` | ||
| 557 | 0x6c float_to_int64(v: f32) -> i64; | ||
| 558 | /// Converts an f32 to a signed fixed point | ||
| 559 | /// 64-bit integer representation where n specifies the position of the | ||
| 560 | /// binary point in the resulting fixed point representation - e.g. `f(0.5f, | ||
| 561 | /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the | ||
| 562 | /// resulting integer to lie within the range `-0x8000000000000000` to | ||
| 563 | /// `0x7FFFFFFFFFFFFFFF` | ||
| 564 | 0x70 float_to_fix64(v: f32, n: i32) -> f32; | ||
| 565 | /// Converts an f32 to an unsigned 64-bit | ||
| 566 | /// integer, rounding towards -Infinity, and clamping the result to lie | ||
| 567 | /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` | ||
| 568 | 0x74 float_to_uint64(v: f32) -> u64; | ||
| 569 | /// Converts an f32 to an unsigned fixed point | ||
| 570 | /// 64-bit integer representation where n specifies the position of the | ||
| 571 | /// binary point in the resulting fixed point representation, e.g. `f(0.5f, | ||
| 572 | /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the | ||
| 573 | /// resulting integer to lie within the range `0x0000000000000000` to | ||
| 574 | /// `0xFFFFFFFFFFFFFFFF` | ||
| 575 | 0x78 float_to_ufix64(v: f32, n: i32) -> u64; | ||
| 576 | /// Converts an f32 to an f64. | ||
| 577 | 0x7c float_to_double(v: f32) -> f64; | ||
| 578 | } | ||
| 579 | } | ||
| 580 | |||
| 581 | /// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms) | ||
| 582 | pub mod double_funcs { | ||
| 583 | |||
| 584 | macro_rules! make_double_funcs { | ||
| 585 | ( | ||
| 586 | $( | ||
| 587 | $(#[$outer:meta])* | ||
| 588 | $offset:literal $name:ident ( | ||
| 589 | $( $aname:ident : $aty:ty ),* | ||
| 590 | ) -> $ret:ty; | ||
| 591 | )* | ||
| 592 | ) => { | ||
| 593 | $( | ||
| 594 | declare_rom_function! { | ||
| 595 | $(#[$outer])* | ||
| 596 | fn $name( $( $aname : $aty ),* ) -> $ret { | ||
| 597 | let table: *const usize = $crate::rom_data::soft_double_table(); | ||
| 598 | unsafe { | ||
| 599 | // This is the entry in the table. Our offset is given as a | ||
| 600 | // byte offset, but we want the table index (each pointer in | ||
| 601 | // the table is 4 bytes long) | ||
| 602 | let entry: *const usize = table.offset($offset / 4); | ||
| 603 | // Read the pointer from the table | ||
| 604 | core::ptr::read(entry) as *const u32 | ||
| 605 | } | ||
| 606 | } | ||
| 607 | } | ||
| 608 | )* | ||
| 609 | } | ||
| 610 | } | ||
| 611 | |||
| 612 | make_double_funcs! { | ||
| 613 | /// Calculates `a + b` | ||
| 614 | 0x00 dadd(a: f64, b: f64) -> f64; | ||
| 615 | /// Calculates `a - b` | ||
| 616 | 0x04 dsub(a: f64, b: f64) -> f64; | ||
| 617 | /// Calculates `a * b` | ||
| 618 | 0x08 dmul(a: f64, b: f64) -> f64; | ||
| 619 | /// Calculates `a / b` | ||
| 620 | 0x0c ddiv(a: f64, b: f64) -> f64; | ||
| 621 | |||
| 622 | // 0x10 and 0x14 are deprecated | ||
| 623 | |||
| 624 | /// Calculates `sqrt(v)` (or return -Infinity if v is negative) | ||
| 625 | 0x18 dsqrt(v: f64) -> f64; | ||
| 626 | /// Converts an f64 to a signed integer, | ||
| 627 | /// rounding towards -Infinity, and clamping the result to lie within the | ||
| 628 | /// range `-0x80000000` to `0x7FFFFFFF` | ||
| 629 | 0x1c double_to_int(v: f64) -> i32; | ||
| 630 | /// Converts an f64 to an signed fixed point | ||
| 631 | /// integer representation where n specifies the position of the binary | ||
| 632 | /// point in the resulting fixed point representation, e.g. | ||
| 633 | /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, | ||
| 634 | /// and clamps the resulting integer to lie within the range `0x00000000` to | ||
| 635 | /// `0xFFFFFFFF` | ||
| 636 | 0x20 double_to_fix(v: f64, n: i32) -> i32; | ||
| 637 | /// Converts an f64 to an unsigned integer, | ||
| 638 | /// rounding towards -Infinity, and clamping the result to lie within the | ||
| 639 | /// range `0x00000000` to `0xFFFFFFFF` | ||
| 640 | 0x24 double_to_uint(v: f64) -> u32; | ||
| 641 | /// Converts an f64 to an unsigned fixed point | ||
| 642 | /// integer representation where n specifies the position of the binary | ||
| 643 | /// point in the resulting fixed point representation, e.g. | ||
| 644 | /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, | ||
| 645 | /// and clamps the resulting integer to lie within the range `0x00000000` to | ||
| 646 | /// `0xFFFFFFFF` | ||
| 647 | 0x28 double_to_ufix(v: f64, n: i32) -> u32; | ||
| 648 | /// Converts a signed integer to the nearest | ||
| 649 | /// double value, rounding to even on tie | ||
| 650 | 0x2c int_to_double(v: i32) -> f64; | ||
| 651 | /// Converts a signed fixed point integer | ||
| 652 | /// representation to the nearest double value, rounding to even on tie. `n` | ||
| 653 | /// specifies the position of the binary point in fixed point, so `f = | ||
| 654 | /// nearest(v/(2^n))` | ||
| 655 | 0x30 fix_to_double(v: i32, n: i32) -> f64; | ||
| 656 | /// Converts an unsigned integer to the nearest | ||
| 657 | /// double value, rounding to even on tie | ||
| 658 | 0x34 uint_to_double(v: u32) -> f64; | ||
| 659 | /// Converts an unsigned fixed point integer | ||
| 660 | /// representation to the nearest double value, rounding to even on tie. `n` | ||
| 661 | /// specifies the position of the binary point in fixed point, so f = | ||
| 662 | /// nearest(v/(2^n)) | ||
| 663 | 0x38 ufix_to_double(v: u32, n: i32) -> f64; | ||
| 664 | /// Calculates the cosine of `angle`. The value | ||
| 665 | /// of `angle` is in radians, and must be in the range `-1024` to `1024` | ||
| 666 | 0x3c dcos(angle: f64) -> f64; | ||
| 667 | /// Calculates the sine of `angle`. The value of | ||
| 668 | /// `angle` is in radians, and must be in the range `-1024` to `1024` | ||
| 669 | 0x40 dsin(angle: f64) -> f64; | ||
| 670 | /// Calculates the tangent of `angle`. The value | ||
| 671 | /// of `angle` is in radians, and must be in the range `-1024` to `1024` | ||
| 672 | 0x44 dtan(angle: f64) -> f64; | ||
| 673 | |||
| 674 | // 0x48 is deprecated | ||
| 675 | |||
| 676 | /// Calculates the exponential value of `v`, | ||
| 677 | /// i.e. `e ** v` | ||
| 678 | 0x4c dexp(v: f64) -> f64; | ||
| 679 | /// Calculates the natural logarithm of v. If v <= 0 return -Infinity | ||
| 680 | 0x50 dln(v: f64) -> f64; | ||
| 681 | |||
| 682 | // These are only on BootROM v2 or higher | ||
| 683 | |||
| 684 | /// Compares two floating point numbers, returning: | ||
| 685 | /// • 0 if a == b | ||
| 686 | /// • -1 if a < b | ||
| 687 | /// • 1 if a > b | ||
| 688 | 0x54 dcmp(a: f64, b: f64) -> i32; | ||
| 689 | /// Computes the arc tangent of `y/x` using the | ||
| 690 | /// signs of arguments to determine the correct quadrant | ||
| 691 | 0x58 datan2(y: f64, x: f64) -> f64; | ||
| 692 | /// Converts a signed 64-bit integer to the | ||
| 693 | /// nearest double value, rounding to even on tie | ||
| 694 | 0x5c int64_to_double(v: i64) -> f64; | ||
| 695 | /// Converts a signed fixed point 64-bit integer | ||
| 696 | /// representation to the nearest double value, rounding to even on tie. `n` | ||
| 697 | /// specifies the position of the binary point in fixed point, so `f = | ||
| 698 | /// nearest(v/(2^n))` | ||
| 699 | 0x60 fix64_to_doubl(v: i64, n: i32) -> f64; | ||
| 700 | /// Converts an unsigned 64-bit integer to the | ||
| 701 | /// nearest double value, rounding to even on tie | ||
| 702 | 0x64 uint64_to_double(v: u64) -> f64; | ||
| 703 | /// Converts an unsigned fixed point 64-bit | ||
| 704 | /// integer representation to the nearest double value, rounding to even on | ||
| 705 | /// tie. `n` specifies the position of the binary point in fixed point, so | ||
| 706 | /// `f = nearest(v/(2^n))` | ||
| 707 | 0x68 ufix64_to_double(v: u64, n: i32) -> f64; | ||
| 708 | /// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity, | ||
| 709 | /// and clamping the result to lie within the range `-0x8000000000000000` to | ||
| 710 | /// `0x7FFFFFFFFFFFFFFF` | ||
| 711 | 0x6c double_to_int64(v: f64) -> i64; | ||
| 712 | /// Converts an f64 to a signed fixed point | ||
| 713 | /// 64-bit integer representation where n specifies the position of the | ||
| 714 | /// binary point in the resulting fixed point representation - e.g. `f(0.5f, | ||
| 715 | /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the | ||
| 716 | /// resulting integer to lie within the range `-0x8000000000000000` to | ||
| 717 | /// `0x7FFFFFFFFFFFFFFF` | ||
| 718 | 0x70 double_to_fix64(v: f64, n: i32) -> i64; | ||
| 719 | /// Converts an f64 to an unsigned 64-bit | ||
| 720 | /// integer, rounding towards -Infinity, and clamping the result to lie | ||
| 721 | /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` | ||
| 722 | 0x74 double_to_uint64(v: f64) -> u64; | ||
| 723 | /// Converts an f64 to an unsigned fixed point | ||
| 724 | /// 64-bit integer representation where n specifies the position of the | ||
| 725 | /// binary point in the resulting fixed point representation, e.g. `f(0.5f, | ||
| 726 | /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the | ||
| 727 | /// resulting integer to lie within the range `0x0000000000000000` to | ||
| 728 | /// `0xFFFFFFFFFFFFFFFF` | ||
| 729 | 0x78 double_to_ufix64(v: f64, n: i32) -> u64; | ||
| 730 | /// Converts an f64 to an f32 | ||
| 731 | 0x7c double_to_float(v: f64) -> f32; | ||
| 732 | } | ||
| 733 | } | ||
