diff --git a/agb/src/lib.rs b/agb/src/lib.rs index c3184e4d..a91a6c67 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -203,6 +203,9 @@ pub use {agb_alloc::ExternalAllocator, agb_alloc::InternalAllocator}; #[panic_handler] #[allow(unused_must_use)] fn panic_implementation(info: &core::panic::PanicInfo) -> ! { + avoid_double_panic(info); + + use core::fmt::Write; if let Some(mut mgba) = mgba::Mgba::new() { let _ = mgba.print(format_args!("{info}"), mgba::DebugLevel::Fatal); } @@ -211,6 +214,26 @@ fn panic_implementation(info: &core::panic::PanicInfo) -> ! { loop {} } +// If we panic during the panic handler, then there isn't much we can do any more. So this code +// just infinite loops halting the CPU. +fn avoid_double_panic(info: &core::panic::PanicInfo) { + static IS_PANICKING: portable_atomic::AtomicBool = portable_atomic::AtomicBool::new(false); + + if IS_PANICKING.load(portable_atomic::Ordering::SeqCst) { + if let Some(mut mgba) = mgba::Mgba::new() { + let _ = mgba.print( + format_args!("Double panic: {info}"), + mgba::DebugLevel::Fatal, + ); + } + loop { + syscall::halt(); + } + } else { + IS_PANICKING.store(true, portable_atomic::Ordering::SeqCst); + } +} + /// The Gba struct is used to control access to the Game Boy Advance's hardware in a way which makes it the /// borrow checker's responsibility to ensure no clashes of global resources. /// @@ -325,6 +348,8 @@ pub mod test_runner { #[panic_handler] fn panic_implementation(info: &core::panic::PanicInfo) -> ! { + avoid_double_panic(info); + #[cfg(feature = "backtrace")] let frames = backtrace::unwind_exception();