use core::{
cell::UnsafeCell,
fmt::Debug,
mem::{align_of, size_of},
num::{NonZeroI16, NonZeroI32, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU8},
panic::RefUnwindSafe,
};
use crate::{
fixed::Fixed,
interrupts::IrqFn,
keys::{KeyControl, KeyInput},
video::Color,
};
#[repr(transparent)]
pub struct GbaCell<T>(UnsafeCell<T>);
impl<T> Debug for GbaCell<T>
where
T: GbaCellSafe + Debug,
{
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
<T as Debug>::fmt(&self.read(), f)
}
}
impl<T> Default for GbaCell<T>
where
T: GbaCellSafe + Default,
{
#[inline]
#[must_use]
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> Clone for GbaCell<T>
where
T: GbaCellSafe + Default,
{
#[inline]
#[must_use]
fn clone(&self) -> Self {
Self::new(self.read())
}
}
unsafe impl<T> Send for GbaCell<T> {}
unsafe impl<T> Sync for GbaCell<T> {}
impl<T> RefUnwindSafe for GbaCell<T> {}
impl<T> GbaCell<T>
where
T: GbaCellSafe,
{
#[inline]
#[must_use]
pub const fn new(val: T) -> Self {
Self(UnsafeCell::new(val))
}
#[inline]
#[must_use]
pub const fn get_ptr(&self) -> *mut T {
self.0.get()
}
#[inline]
#[must_use]
pub fn read(&self) -> T {
match (size_of::<T>(), align_of::<T>()) {
(4, 4) => unsafe {
let val: u32;
core::arch::asm!(
"ldr {r}, [{addr}]",
r = lateout(reg) val,
addr = in(reg) self.get_ptr(),
options(readonly, preserves_flags, nostack)
);
core::mem::transmute_copy(&val)
},
(2, 2) => unsafe {
let val: u16;
core::arch::asm!(
"ldrh {r}, [{addr}]",
r = lateout(reg) val,
addr = in(reg) self.get_ptr(),
options(readonly, preserves_flags, nostack)
);
core::mem::transmute_copy(&val)
},
(1, 1) => unsafe {
let val: u8;
core::arch::asm!(
"ldrb {r}, [{addr}]",
r = lateout(reg) val,
addr = in(reg) self.get_ptr(),
options(readonly, preserves_flags, nostack)
);
core::mem::transmute_copy(&val)
},
_ => {
unimplemented!()
}
}
}
#[inline]
pub fn write(&self, val: T) {
match (size_of::<T>(), align_of::<T>()) {
(4, 4) => unsafe {
let u: u32 = core::mem::transmute_copy(&val);
core::arch::asm!(
"str {val}, [{addr}]",
val = in(reg) u,
addr = in(reg) self.get_ptr(),
options(preserves_flags, nostack)
)
},
(2, 2) => unsafe {
let u: u16 = core::mem::transmute_copy(&val);
core::arch::asm!(
"strh {val}, [{addr}]",
val = in(reg) u,
addr = in(reg) self.get_ptr(),
options(preserves_flags, nostack)
)
},
(1, 1) => unsafe {
let u: u8 = core::mem::transmute_copy(&val);
core::arch::asm!(
"strb {val}, [{addr}]",
val = in(reg) u,
addr = in(reg) self.get_ptr(),
options(preserves_flags, nostack)
)
},
_ => {
unimplemented!()
}
}
}
}
pub unsafe trait GbaCellSafe: Copy {}
unsafe impl GbaCellSafe for bool {}
unsafe impl GbaCellSafe for char {}
unsafe impl GbaCellSafe for Color {}
unsafe impl GbaCellSafe for i16 {}
unsafe impl GbaCellSafe for i32 {}
unsafe impl GbaCellSafe for i8 {}
unsafe impl GbaCellSafe for KeyInput {}
unsafe impl GbaCellSafe for KeyControl {}
unsafe impl GbaCellSafe for NonZeroI16 {}
unsafe impl GbaCellSafe for NonZeroI32 {}
unsafe impl GbaCellSafe for NonZeroI8 {}
unsafe impl GbaCellSafe for NonZeroU16 {}
unsafe impl GbaCellSafe for NonZeroU32 {}
unsafe impl GbaCellSafe for NonZeroU8 {}
unsafe impl GbaCellSafe for Option<bool> {}
unsafe impl GbaCellSafe for Option<char> {}
unsafe impl GbaCellSafe for Option<IrqFn> {}
unsafe impl GbaCellSafe for Option<NonZeroI16> {}
unsafe impl GbaCellSafe for Option<NonZeroI32> {}
unsafe impl GbaCellSafe for Option<NonZeroI8> {}
unsafe impl GbaCellSafe for Option<NonZeroU16> {}
unsafe impl GbaCellSafe for Option<NonZeroU32> {}
unsafe impl GbaCellSafe for Option<NonZeroU8> {}
unsafe impl GbaCellSafe for u16 {}
unsafe impl GbaCellSafe for u32 {}
unsafe impl GbaCellSafe for u8 {}
unsafe impl<I: GbaCellSafe, const B: u32> GbaCellSafe for Fixed<I, B> {}