Skip to main content

gba_cell/
lib.rs

1//! Provides the [`GbaCell`] type.
2//! 
3//! ## Safety
4//! 
5//! **This crate is intended to only be used for writing software on the
6//! Nintendo Gameboy Advanced. Use on any other platform may lead to Undefined
7//! Behaviour.**
8
9use core::fmt::Debug;
10
11/// Marker trait bound for the methods of [`GbaCell`].
12///
13/// When a type implements this trait it indicates that the type can be
14/// atomically loaded/stored using a single volatile access.
15///
16/// ## Safety
17/// The type must fit in a single register, and have an alignment equal to its
18/// size. Generally that means it should be one of:
19///
20/// * an 8, 16, or 32 bit integer
21/// * a function pointer
22/// * a data pointer to a sized type
23/// * an optional non-null pointer (to function or sized data)
24/// * a `repr(transparent)` newtype over one of the above
25/// 
26/// Note that while the trait requirements are enforcable at the trait level,
27/// the size & alignment requirements are enforced using `const` assertions
28/// wherever a [`GbaCell`] is used.
29pub unsafe trait GbaCellSafe: Copy {}
30
31unsafe impl<T> GbaCellSafe for T where T: Copy {}
32
33/// A "cell" type suitable to hold a global on the GBA.
34#[repr(transparent)]
35pub struct GbaCell<T>(core::cell::UnsafeCell<T>);
36
37#[cfg(feature = "on_gba")]
38impl<T> Debug for GbaCell<T>
39where
40    T: GbaCellSafe + Debug,
41{
42    #[inline]
43    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44        <T as Debug>::fmt(&self.read(), f)
45    }
46}
47impl<T> Default for GbaCell<T>
48where
49    T: GbaCellSafe + Default,
50{
51    #[inline]
52    #[must_use]
53    fn default() -> Self {
54        Self::new(T::default())
55    }
56}
57#[cfg(feature = "on_gba")]
58impl<T> Clone for GbaCell<T>
59where
60    T: GbaCellSafe + Default,
61{
62    #[inline]
63    #[must_use]
64    fn clone(&self) -> Self {
65        Self::new(self.read())
66    }
67}
68
69#[cfg(feature = "on_gba")]
70unsafe impl<T> Sync for GbaCell<T> {}
71
72impl<T> GbaCell<T>
73where
74    T: GbaCellSafe,
75{
76    /// Helper to assert the size & alignment requirements of the wrapped value
77    /// at compile time. 
78    const _ASSERT_GBACELL_SAFE: () = {
79        let size = core::mem::size_of::<T>();
80        let align = core::mem::align_of::<T>();
81        match (size, align) {
82            (1, 1) | (2, 2) | (4, 4) => {}
83            _ => {
84                panic!("Provided type cannot be made GbaCell-safe! Expected a size & align of 1, 2, or 4.")
85            }
86        }
87    };
88
89    /// Constructs a new cell with the value given
90    #[inline]
91    #[must_use]
92    pub const fn new(t: T) -> Self {
93        Self(core::cell::UnsafeCell::new(t))
94    }
95
96    /// Read the value in the cell.
97    #[inline]
98    #[must_use]
99    #[cfg(feature = "on_gba")]
100    #[cfg_attr(feature = "track_caller", track_caller)]
101    pub fn read(&self) -> T {
102        // SAFETY: Guranteed to meet the size & alignment requirements of the
103        // GBA's single-instruction reads because of Self::_ASSERT_GBACELL_SAFE.
104        unsafe { self.0.get().read_volatile() }
105    }
106
107    /// Writes a new value to the cell.
108    #[inline]
109    #[cfg(feature = "on_gba")]
110    #[cfg_attr(feature = "track_caller", track_caller)]
111    pub fn write(&self, t: T) {
112        // SAFETY: Guranteed to meet the size & alignment requirements of the
113        // GBA's single-instruction reads because of Self::_ASSERT_GBACELL_SAFE.
114        unsafe { self.0.get().write_volatile(t) }
115    }
116}