diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index c6d993bb..64fd66d4 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -4,7 +4,7 @@ use alloc::boxed::Box; use critical_section::{CriticalSection, RawRestoreState}; use portable_atomic::{AtomicBool, AtomicUsize, Ordering}; -use crate::{display::DISPLAY_STATUS, memory_mapped::MemoryMapped}; +use crate::{display::DISPLAY_STATUS, memory_mapped::MemoryMapped, util::SyncUnsafeCell}; #[derive(Clone, Copy)] pub enum Interrupt { @@ -123,7 +123,7 @@ impl InterruptRoot { } } -static mut INTERRUPT_TABLE: [InterruptRoot; 14] = [ +static INTERRUPT_TABLE: SyncUnsafeCell<[InterruptRoot; 14]> = SyncUnsafeCell::new([ InterruptRoot::new(Interrupt::VBlank), InterruptRoot::new(Interrupt::HBlank), InterruptRoot::new(Interrupt::VCounter), @@ -138,11 +138,12 @@ static mut INTERRUPT_TABLE: [InterruptRoot; 14] = [ InterruptRoot::new(Interrupt::Dma3), InterruptRoot::new(Interrupt::Keypad), InterruptRoot::new(Interrupt::Gamepak), -]; +]); #[no_mangle] -extern "C" fn __RUST_INTERRUPT_HANDLER(interrupt: u16) -> u16 { - for (i, root) in unsafe { INTERRUPT_TABLE.iter().enumerate() } { +#[export_name = "__RUST_INTERRUPT_HANDLER"] +extern "C" fn interrupt_handler(interrupt: u16) -> u16 { + for (i, root) in unsafe { &mut *INTERRUPT_TABLE.get() }.iter().enumerate() { if (1 << i) & interrupt != 0 { root.trigger_interrupts(); } @@ -219,8 +220,8 @@ impl InterruptRoot { } } -fn interrupt_to_root(interrupt: Interrupt) -> &'static InterruptRoot { - unsafe { &INTERRUPT_TABLE[interrupt as usize] } +unsafe fn interrupt_to_root(interrupt: Interrupt) -> &'static InterruptRoot { + &(unsafe { &mut *INTERRUPT_TABLE.get() })[interrupt as usize] } #[must_use] @@ -259,7 +260,7 @@ pub unsafe fn add_interrupt_handler( ) -> InterruptHandler { fn do_with_inner(interrupt: Interrupt, inner: Pin>) -> InterruptHandler { critical_section::with(|_| { - let root = interrupt_to_root(interrupt); + let root = unsafe { interrupt_to_root(interrupt) }; root.add(); let mut c = root.next.get(); if c.is_null() { @@ -373,7 +374,7 @@ mod tests { #[test_case] fn test_interrupt_table_length(_gba: &mut crate::Gba) { assert_eq!( - unsafe { INTERRUPT_TABLE.len() }, + unsafe { (*INTERRUPT_TABLE.get()).len() }, Interrupt::Gamepak as usize + 1, "interrupt table should be able to store gamepak interrupt" ); diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 9c66f36d..b657221f 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -190,6 +190,7 @@ mod sync; pub mod syscall; /// Interactions with the internal timers pub mod timer; +pub(crate) mod util; mod no_game; @@ -326,6 +327,8 @@ impl Gba { /// You can run the tests using `cargo test`, but it will work better through `mgba-test-runner` by /// running something along the lines of `CARGO_TARGET_THUMBV4T_NONE_EABI_RUNNER=mgba-test-runner cargo test`. pub mod test_runner { + use util::SyncUnsafeCell; + use super::*; #[doc(hidden)] @@ -373,7 +376,7 @@ pub mod test_runner { } } - static mut TEST_GBA: Option = None; + static TEST_GBA: SyncUnsafeCell> = SyncUnsafeCell::new(None); #[doc(hidden)] pub fn test_runner(tests: &[&dyn Testable]) { @@ -384,7 +387,7 @@ pub mod test_runner { ) .unwrap(); - let gba = unsafe { TEST_GBA.as_mut() }.unwrap(); + let gba = unsafe { &mut *TEST_GBA.get() }.as_mut().unwrap(); for test in tests { test.run(gba); @@ -414,7 +417,7 @@ pub mod test_runner { #[doc(hidden)] pub fn agb_start_tests(gba: Gba, test_main: impl Fn()) -> ! { - unsafe { TEST_GBA = Some(gba) }; + *unsafe { &mut *TEST_GBA.get() } = Some(gba); test_main(); #[allow(clippy::empty_loop)] loop {} diff --git a/agb/src/util.rs b/agb/src/util.rs new file mode 100644 index 00000000..657031c8 --- /dev/null +++ b/agb/src/util.rs @@ -0,0 +1,16 @@ +use core::cell::UnsafeCell; + +pub struct SyncUnsafeCell(UnsafeCell); + +unsafe impl Sync for SyncUnsafeCell {} +unsafe impl Send for SyncUnsafeCell {} + +impl SyncUnsafeCell { + pub const fn new(t: T) -> Self { + Self(UnsafeCell::new(t)) + } + + pub unsafe fn get(&self) -> *mut T { + self.0.get() + } +} diff --git a/emulator/mgba-sys/src/lib.rs b/emulator/mgba-sys/src/lib.rs index cefe05ce..f090f49f 100644 --- a/emulator/mgba-sys/src/lib.rs +++ b/emulator/mgba-sys/src/lib.rs @@ -1,6 +1,7 @@ #[allow(non_upper_case_globals)] #[allow(non_camel_case_types)] #[allow(non_snake_case)] +#[allow(improper_ctypes)] mod ffi { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); }