pub type RomFnTableCode = [u8; 2];
type RomTableLookupFn<T> = unsafe extern "C" fn(*const u16, u32) -> T;
const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _;
const FUNC_TABLE: *const u16 = 0x0000_0014 as _;
const DATA_TABLE: *const u16 = 0x0000_0016 as _;
const VERSION_NUMBER: *const u8 = 0x0000_0013 as _;
fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T {
unsafe {
let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR);
let rom_table_lookup: RomTableLookupFn<T> = core::mem::transmute(rom_table_lookup_ptr);
rom_table_lookup(
rom_hword_as_ptr(table) as *const u16,
u16::from_le_bytes(tag) as u32,
)
}
}
unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 {
let ptr: u16 = *rom_address;
ptr as *const u32
}
macro_rules! declare_rom_function {
(
$(#[$outer:meta])*
fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
$lookup:block
) => {
#[doc = r"Additional access for the `"]
#[doc = stringify!($name)]
#[doc = r"` ROM function."]
pub mod $name {
#[cfg(not(feature = "rom-func-cache"))]
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
let p: *const u32 = $lookup;
unsafe {
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
#[cfg(feature = "rom-func-cache")]
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
use core::sync::atomic::{AtomicU16, Ordering};
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
0 => {
let raw: *const u32 = $lookup;
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
raw
},
val => val as *const u32,
};
unsafe {
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
}
$(#[$outer])*
pub extern "C" fn $name( $($argname: $ty),* ) -> $ret {
$name::ptr()($($argname),*)
}
};
(
$(#[$outer:meta])*
unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
$lookup:block
) => {
#[doc = r"Additional access for the `"]
#[doc = stringify!($name)]
#[doc = r"` ROM function."]
pub mod $name {
#[cfg(not(feature = "rom-func-cache"))]
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
let p: *const u32 = $lookup;
unsafe {
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
#[cfg(feature = "rom-func-cache")]
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
use core::sync::atomic::{AtomicU16, Ordering};
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
0 => {
let raw: *const u32 = $lookup;
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
raw
},
val => val as *const u32,
};
unsafe {
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
}
$(#[$outer])*
pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret {
$name::ptr()($($argname),*)
}
};
}
macro_rules! rom_functions {
() => {};
(
$(#[$outer:meta])*
$c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
$($rest:tt)*
) => {
declare_rom_function! {
$(#[$outer])*
fn $name( $($argname: $ty),* ) -> $ret {
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
}
}
rom_functions!($($rest)*);
};
(
$(#[$outer:meta])*
$c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
$($rest:tt)*
) => {
declare_rom_function! {
$(#[$outer])*
unsafe fn $name( $($argname: $ty),* ) -> $ret {
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
}
}
rom_functions!($($rest)*);
};
}
rom_functions! {
b"P3" fn popcount32(value: u32) -> u32;
b"R3" fn reverse32(value: u32) -> u32;
b"L3" fn clz32(value: u32) -> u32;
b"T3" fn ctz32(value: u32) -> u32;
b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> ();
b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8;
b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32;
b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8;
b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8;
b"IF" unsafe fn connect_internal_flash() -> ();
b"EX" unsafe fn flash_exit_xip() -> ();
b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ();
b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> ();
b"FC" unsafe fn flash_flush_cache() -> ();
b"CX" unsafe fn flash_enter_cmd_xip() -> ();
b"WV" unsafe fn wait_for_vector() -> !;
}
intrinsics! {
#[alias = __popcountdi2]
extern "C" fn __popcountsi2(x: u32) -> u32 {
popcount32(x)
}
#[alias = __clzdi2]
extern "C" fn __clzsi2(x: u32) -> u32 {
clz32(x)
}
#[alias = __ctzdi2]
extern "C" fn __ctzsi2(x: u32) -> u32 {
ctz32(x)
}
#[alias = __rbitl]
extern "C" fn __rbit(x: u32) -> u32 {
reverse32(x)
}
unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () {
memset(dest, c as u8, n as u32);
}
#[alias = __aeabi_memset8]
unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () {
memset4(dest as *mut u32, c as u8, n as u32);
}
unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () {
memset(dest, 0, n as u32);
}
#[alias = __aeabi_memclr8]
unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () {
memset4(dest as *mut u32, 0, n as u32);
}
unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () {
memcpy(dest, src, n as u32);
}
#[alias = __aeabi_memcpy8]
unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () {
memcpy44(dest as *mut u32, src as *const u32, n as u32);
}
}
unsafe fn convert_str(s: *const u8) -> &'static str {
let mut end = s;
while *end != 0 {
end = end.add(1);
}
let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize);
core::str::from_utf8_unchecked(s)
}
pub fn rom_version_number() -> u8 {
unsafe { *VERSION_NUMBER }
}
pub fn copyright_string() -> &'static str {
let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR");
unsafe { convert_str(s) }
}
pub fn git_revision() -> u32 {
let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR");
unsafe { *s }
}
pub fn fplib_start() -> *const u8 {
rom_table_lookup(DATA_TABLE, *b"FS")
}
pub fn soft_float_table() -> *const usize {
rom_table_lookup(DATA_TABLE, *b"SF")
}
pub fn fplib_end() -> *const u8 {
rom_table_lookup(DATA_TABLE, *b"FE")
}
pub fn soft_double_table() -> *const usize {
if rom_version_number() < 2 {
panic!(
"Double precision operations require V2 bootrom (found: V{})",
rom_version_number()
);
}
rom_table_lookup(DATA_TABLE, *b"SD")
}
pub mod float_funcs {
macro_rules! make_functions {
(
$(
$(#[$outer:meta])*
$offset:literal $name:ident (
$( $aname:ident : $aty:ty ),*
) -> $ret:ty;
)*
) => {
$(
declare_rom_function! {
$(#[$outer])*
fn $name( $( $aname : $aty ),* ) -> $ret {
let table: *const usize = $crate::rom_data::soft_float_table();
unsafe {
let entry: *const usize = table.offset($offset / 4);
core::ptr::read(entry) as *const u32
}
}
}
)*
}
}
make_functions! {
0x00 fadd(a: f32, b: f32) -> f32;
0x04 fsub(a: f32, b: f32) -> f32;
0x08 fmul(a: f32, b: f32) -> f32;
0x0c fdiv(a: f32, b: f32) -> f32;
0x18 fsqrt(v: f32) -> f32;
0x1c float_to_int(v: f32) -> i32;
0x20 float_to_fix(v: f32, n: i32) -> i32;
0x24 float_to_uint(v: f32) -> u32;
0x28 float_to_ufix(v: f32, n: i32) -> u32;
0x2c int_to_float(v: i32) -> f32;
0x30 fix_to_float(v: i32, n: i32) -> f32;
0x34 uint_to_float(v: u32) -> f32;
0x38 ufix_to_float(v: u32, n: i32) -> f32;
0x3c fcos(angle: f32) -> f32;
0x40 fsin(angle: f32) -> f32;
0x44 ftan(angle: f32) -> f32;
0x4c fexp(v: f32) -> f32;
0x50 fln(v: f32) -> f32;
}
macro_rules! make_functions_v2 {
(
$(
$(#[$outer:meta])*
$offset:literal $name:ident (
$( $aname:ident : $aty:ty ),*
) -> $ret:ty;
)*
) => {
$(
declare_rom_function! {
$(#[$outer])*
fn $name( $( $aname : $aty ),* ) -> $ret {
if $crate::rom_data::rom_version_number() < 2 {
panic!(
"Floating point function requires V2 bootrom (found: V{})",
$crate::rom_data::rom_version_number()
);
}
let table: *const usize = $crate::rom_data::soft_float_table();
unsafe {
let entry: *const usize = table.offset($offset / 4);
core::ptr::read(entry) as *const u32
}
}
}
)*
}
}
make_functions_v2! {
0x54 fcmp(a: f32, b: f32) -> i32;
0x58 fatan2(y: f32, x: f32) -> f32;
0x5c int64_to_float(v: i64) -> f32;
0x60 fix64_to_float(v: i64, n: i32) -> f32;
0x64 uint64_to_float(v: u64) -> f32;
0x68 ufix64_to_float(v: u64, n: i32) -> f32;
0x6c float_to_int64(v: f32) -> i64;
0x70 float_to_fix64(v: f32, n: i32) -> f32;
0x74 float_to_uint64(v: f32) -> u64;
0x78 float_to_ufix64(v: f32, n: i32) -> u64;
0x7c float_to_double(v: f32) -> f64;
}
}
pub mod double_funcs {
macro_rules! make_double_funcs {
(
$(
$(#[$outer:meta])*
$offset:literal $name:ident (
$( $aname:ident : $aty:ty ),*
) -> $ret:ty;
)*
) => {
$(
declare_rom_function! {
$(#[$outer])*
fn $name( $( $aname : $aty ),* ) -> $ret {
let table: *const usize = $crate::rom_data::soft_double_table();
unsafe {
let entry: *const usize = table.offset($offset / 4);
core::ptr::read(entry) as *const u32
}
}
}
)*
}
}
make_double_funcs! {
0x00 dadd(a: f64, b: f64) -> f64;
0x04 dsub(a: f64, b: f64) -> f64;
0x08 dmul(a: f64, b: f64) -> f64;
0x0c ddiv(a: f64, b: f64) -> f64;
0x18 dsqrt(v: f64) -> f64;
0x1c double_to_int(v: f64) -> i32;
0x20 double_to_fix(v: f64, n: i32) -> i32;
0x24 double_to_uint(v: f64) -> u32;
0x28 double_to_ufix(v: f64, n: i32) -> u32;
0x2c int_to_double(v: i32) -> f64;
0x30 fix_to_double(v: i32, n: i32) -> f64;
0x34 uint_to_double(v: u32) -> f64;
0x38 ufix_to_double(v: u32, n: i32) -> f64;
0x3c dcos(angle: f64) -> f64;
0x40 dsin(angle: f64) -> f64;
0x44 dtan(angle: f64) -> f64;
0x4c dexp(v: f64) -> f64;
0x50 dln(v: f64) -> f64;
0x54 dcmp(a: f64, b: f64) -> i32;
0x58 datan2(y: f64, x: f64) -> f64;
0x5c int64_to_double(v: i64) -> f64;
0x60 fix64_to_doubl(v: i64, n: i32) -> f64;
0x64 uint64_to_double(v: u64) -> f64;
0x68 ufix64_to_double(v: u64, n: i32) -> f64;
0x6c double_to_int64(v: f64) -> i64;
0x70 double_to_fix64(v: f64, n: i32) -> i64;
0x74 double_to_uint64(v: f64) -> u64;
0x78 double_to_ufix64(v: f64, n: i32) -> u64;
0x7c double_to_float(v: f64) -> f32;
}
}