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}