mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-11 17:41:33 +11:00
Merge pull request #198 from corwinkuiper/boxed-interrupts
Boxed interrupts
This commit is contained in:
commit
3c09a86f88
|
@ -55,6 +55,11 @@ b .Initialise_mb
|
||||||
ldr r2, =__iwram_rom_length_halfwords
|
ldr r2, =__iwram_rom_length_halfwords
|
||||||
swi 0x000B0000
|
swi 0x000B0000
|
||||||
|
|
||||||
|
@ enable interrupts
|
||||||
|
ldr r0, =0x04000208
|
||||||
|
ldr r1, =1
|
||||||
|
str r1, [r0]
|
||||||
|
|
||||||
@ put zero in both r0 and r1
|
@ put zero in both r0 and r1
|
||||||
@ This corresponds to zero for argc and argv (which would technically be required for a c runtime)
|
@ This corresponds to zero for argc and argv (which would technically be required for a c runtime)
|
||||||
ldr r0, =0
|
ldr r0, =0
|
||||||
|
|
|
@ -8,13 +8,13 @@ use bare_metal::{CriticalSection, Mutex};
|
||||||
#[agb::entry]
|
#[agb::entry]
|
||||||
fn main(_gba: agb::Gba) -> ! {
|
fn main(_gba: agb::Gba) -> ! {
|
||||||
let count = Mutex::new(RefCell::new(0));
|
let count = Mutex::new(RefCell::new(0));
|
||||||
agb::add_interrupt_handler!(
|
let _a = agb::interrupt::add_interrupt_handler(
|
||||||
agb::interrupt::Interrupt::VBlank,
|
agb::interrupt::Interrupt::VBlank,
|
||||||
|key: &CriticalSection| {
|
|key: &CriticalSection| {
|
||||||
let mut count = count.borrow(*key).borrow_mut();
|
let mut count = count.borrow(*key).borrow_mut();
|
||||||
agb::println!("Hello, world, frame = {}", *count);
|
agb::println!("Hello, world, frame = {}", *count);
|
||||||
*count += 1;
|
*count += 1;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ fn main(mut gba: agb::Gba) -> ! {
|
||||||
|
|
||||||
let back = Mutex::new(RefCell::new(BackCosines { cosines, row: 0 }));
|
let back = Mutex::new(RefCell::new(BackCosines { cosines, row: 0 }));
|
||||||
|
|
||||||
agb::add_interrupt_handler!(Interrupt::HBlank, |key: &CriticalSection| {
|
let _a = agb::interrupt::add_interrupt_handler(Interrupt::HBlank, |key: &CriticalSection| {
|
||||||
let mut backc = back.borrow(*key).borrow_mut();
|
let mut backc = back.borrow(*key).borrow_mut();
|
||||||
let deflection = backc.cosines[backc.row % 32];
|
let deflection = backc.cosines[backc.row % 32];
|
||||||
unsafe { ((0x0400_0010) as *mut u16).write_volatile(deflection) }
|
unsafe { ((0x0400_0010) as *mut u16).write_volatile(deflection) }
|
||||||
|
|
|
@ -4,6 +4,7 @@ use core::{
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use alloc::boxed::Box;
|
||||||
use bare_metal::CriticalSection;
|
use bare_metal::CriticalSection;
|
||||||
|
|
||||||
use crate::{display::DISPLAY_STATUS, memory_mapped::MemoryMapped};
|
use crate::{display::DISPLAY_STATUS, memory_mapped::MemoryMapped};
|
||||||
|
@ -94,7 +95,7 @@ fn disable_interrupts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InterruptRoot {
|
struct InterruptRoot {
|
||||||
next: Cell<*const InterruptClosure>,
|
next: Cell<*const InterruptInner>,
|
||||||
count: Cell<i32>,
|
count: Cell<i32>,
|
||||||
interrupt: Interrupt,
|
interrupt: Interrupt,
|
||||||
}
|
}
|
||||||
|
@ -153,16 +154,60 @@ extern "C" fn __RUST_INTERRUPT_HANDLER(interrupt: u16) -> u16 {
|
||||||
interrupt
|
interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InterruptClosureBounded<'a> {
|
struct InterruptInner {
|
||||||
c: InterruptClosure,
|
next: Cell<*const InterruptInner>,
|
||||||
_phantom: PhantomData<&'a ()>,
|
root: *const InterruptRoot,
|
||||||
_unpin: PhantomPinned,
|
closure: *const dyn Fn(&CriticalSection),
|
||||||
|
_pin: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InterruptClosure {
|
unsafe fn create_interrupt_inner(
|
||||||
closure: *const (dyn Fn(&CriticalSection)),
|
c: impl Fn(&CriticalSection),
|
||||||
next: Cell<*const InterruptClosure>,
|
|
||||||
root: *const InterruptRoot,
|
root: *const InterruptRoot,
|
||||||
|
) -> Pin<Box<InterruptInner>> {
|
||||||
|
let c = Box::new(c);
|
||||||
|
let c: &dyn Fn(&CriticalSection) = Box::leak(c);
|
||||||
|
let c: &dyn Fn(&CriticalSection) = core::mem::transmute(c);
|
||||||
|
Box::pin(InterruptInner {
|
||||||
|
next: Cell::new(core::ptr::null()),
|
||||||
|
root,
|
||||||
|
closure: c,
|
||||||
|
_pin: PhantomPinned,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for InterruptInner {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
inner_drop(unsafe { Pin::new_unchecked(self) });
|
||||||
|
fn inner_drop(this: Pin<&mut InterruptInner>) {
|
||||||
|
// drop the closure allocation safely
|
||||||
|
let _closure_box =
|
||||||
|
unsafe { Box::from_raw(this.closure as *mut dyn Fn(&CriticalSection)) };
|
||||||
|
|
||||||
|
// perform the rest of the drop sequence
|
||||||
|
let root = unsafe { &*this.root };
|
||||||
|
root.reduce();
|
||||||
|
let mut c = root.next.get();
|
||||||
|
let own_pointer = &*this as *const _;
|
||||||
|
if c == own_pointer {
|
||||||
|
unsafe { &*this.root }.next.set(this.next.get());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
let p = unsafe { &*c }.next.get();
|
||||||
|
if p == own_pointer {
|
||||||
|
unsafe { &*c }.next.set(this.next.get());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
c = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InterruptHandler<'a> {
|
||||||
|
_inner: Pin<Box<InterruptInner>>,
|
||||||
|
_lifetime: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterruptRoot {
|
impl InterruptRoot {
|
||||||
|
@ -177,103 +222,64 @@ impl InterruptRoot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for InterruptClosure {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let root = unsafe { &*self.root };
|
|
||||||
root.reduce();
|
|
||||||
let mut c = root.next.get();
|
|
||||||
let own_pointer = self as *const _;
|
|
||||||
if c == own_pointer {
|
|
||||||
unsafe { &*self.root }.next.set(self.next.get());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
let p = unsafe { &*c }.next.get();
|
|
||||||
if p == own_pointer {
|
|
||||||
unsafe { &*c }.next.set(self.next.get());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
c = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn interrupt_to_root(interrupt: Interrupt) -> &'static InterruptRoot {
|
fn interrupt_to_root(interrupt: Interrupt) -> &'static InterruptRoot {
|
||||||
unsafe { &INTERRUPT_TABLE[interrupt as usize] }
|
unsafe { &INTERRUPT_TABLE[interrupt as usize] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_interrupt_handle_root<'a>(
|
#[must_use]
|
||||||
f: &'a dyn Fn(&CriticalSection),
|
/// Adds an interrupt handler as long as the returned value is alive. The
|
||||||
root: &InterruptRoot,
|
/// closure takes a [`CriticalSection`] which can be used for mutexes.
|
||||||
) -> InterruptClosureBounded<'a> {
|
///
|
||||||
InterruptClosureBounded {
|
/// [`CriticalSection`]: bare_metal::CriticalSection
|
||||||
c: InterruptClosure {
|
///
|
||||||
closure: unsafe { core::mem::transmute(f as *const _) },
|
/// # Examples
|
||||||
next: Cell::new(core::ptr::null()),
|
///
|
||||||
root: root as *const _,
|
/// ```
|
||||||
},
|
/// let _a = add_interrupt_handler(Interrupt::VBlank, |_: &CriticalSection| {
|
||||||
_phantom: PhantomData,
|
/// println!("Woah there! There's been a vblank!");
|
||||||
_unpin: PhantomPinned,
|
/// });
|
||||||
}
|
/// ```
|
||||||
}
|
pub fn add_interrupt_handler<'a>(
|
||||||
|
|
||||||
/// The [add_interrupt_handler!] macro should be used instead of this function.
|
|
||||||
/// Creates an interrupt handler from a closure.
|
|
||||||
pub fn get_interrupt_handle(
|
|
||||||
f: &(dyn Fn(&CriticalSection) + Send + Sync),
|
|
||||||
interrupt: Interrupt,
|
interrupt: Interrupt,
|
||||||
) -> InterruptClosureBounded {
|
handler: impl Fn(&CriticalSection) + 'a,
|
||||||
let root = interrupt_to_root(interrupt);
|
) -> InterruptHandler<'a> {
|
||||||
|
fn do_with_inner<'a>(
|
||||||
get_interrupt_handle_root(f, root)
|
interrupt: Interrupt,
|
||||||
}
|
inner: Pin<Box<InterruptInner>>,
|
||||||
|
) -> InterruptHandler<'a> {
|
||||||
/// The [add_interrupt_handler!] macro should be used instead of this.
|
|
||||||
/// Adds an interrupt handler to the interrupt table such that when that
|
|
||||||
/// interrupt is triggered the closure is called.
|
|
||||||
pub fn add_interrupt<'a>(interrupt: Pin<&'a InterruptClosureBounded<'a>>) {
|
|
||||||
free(|_| {
|
free(|_| {
|
||||||
let root = unsafe { &*interrupt.c.root };
|
let root = interrupt_to_root(interrupt);
|
||||||
root.add();
|
root.add();
|
||||||
let mut c = root.next.get();
|
let mut c = root.next.get();
|
||||||
if c.is_null() {
|
if c.is_null() {
|
||||||
root.next.set((&interrupt.c) as *const _);
|
root.next.set((&*inner) as *const _);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
let p = unsafe { &*c }.next.get();
|
let p = unsafe { &*c }.next.get();
|
||||||
if p.is_null() {
|
if p.is_null() {
|
||||||
unsafe { &*c }.next.set((&interrupt.c) as *const _);
|
unsafe { &*c }.next.set((&*inner) as *const _);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = p;
|
c = p;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
InterruptHandler {
|
||||||
|
_inner: inner,
|
||||||
|
_lifetime: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let root = interrupt_to_root(interrupt) as *const _;
|
||||||
|
let inner = unsafe { create_interrupt_inner(handler, root) };
|
||||||
|
do_with_inner(interrupt, inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
/// How you can access mutexes outside of interrupts by being given a
|
||||||
/// Creates a new interrupt handler in the current scope, when this scope drops
|
/// [`CriticalSection`]
|
||||||
/// the interrupt handler is removed. Note that this returns nothing, but some
|
|
||||||
/// stack space is used. The interrupt handler is of the form `Fn(Key) + Send +
|
|
||||||
/// Sync` where Key can be used to unlock a mutex without checking whether
|
|
||||||
/// interrupts need to be disabled, as during an interrupt interrupts are
|
|
||||||
/// disabled.
|
|
||||||
///
|
///
|
||||||
/// # Usage
|
/// [`CriticalSection`]: bare_metal::CriticalSection
|
||||||
/// ```
|
|
||||||
/// add_interrupt_handler!(Interrupt::VBlank, |key| agb::println!("hello world!"));
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
macro_rules! add_interrupt_handler {
|
|
||||||
($interrupt: expr, $handler: expr) => {
|
|
||||||
let a = $handler;
|
|
||||||
let a = $crate::interrupt::get_interrupt_handle(&a, $interrupt);
|
|
||||||
let a = unsafe { core::pin::Pin::new_unchecked(&a) };
|
|
||||||
$crate::interrupt::add_interrupt(a);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn free<F, R>(f: F) -> R
|
pub fn free<F, R>(f: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(&CriticalSection) -> R,
|
F: FnOnce(&CriticalSection) -> R,
|
||||||
|
@ -322,14 +328,12 @@ mod tests {
|
||||||
{
|
{
|
||||||
let counter = Mutex::new(RefCell::new(0));
|
let counter = Mutex::new(RefCell::new(0));
|
||||||
let counter_2 = Mutex::new(RefCell::new(0));
|
let counter_2 = Mutex::new(RefCell::new(0));
|
||||||
add_interrupt_handler!(Interrupt::VBlank, |key: &CriticalSection| *counter
|
let _a = add_interrupt_handler(Interrupt::VBlank, |key: &CriticalSection| {
|
||||||
.borrow(*key)
|
*counter.borrow(*key).borrow_mut() += 1
|
||||||
.borrow_mut() +=
|
});
|
||||||
1);
|
let _b = add_interrupt_handler(Interrupt::VBlank, |key: &CriticalSection| {
|
||||||
add_interrupt_handler!(Interrupt::VBlank, |key: &CriticalSection| *counter_2
|
*counter_2.borrow(*key).borrow_mut() += 1
|
||||||
.borrow(*key)
|
});
|
||||||
.borrow_mut() +=
|
|
||||||
1);
|
|
||||||
|
|
||||||
let vblank = VBlank::get();
|
let vblank = VBlank::get();
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ pub mod display;
|
||||||
mod dma;
|
mod dma;
|
||||||
/// Button inputs to the system.
|
/// Button inputs to the system.
|
||||||
pub mod input;
|
pub mod input;
|
||||||
#[doc(hidden)] // hide for now as the implementation in here is unsound
|
/// Interacting with the GBA interrupts
|
||||||
pub mod interrupt;
|
pub mod interrupt;
|
||||||
mod memory_mapped;
|
mod memory_mapped;
|
||||||
/// Implements logging to the mgba emulator.
|
/// Implements logging to the mgba emulator.
|
||||||
|
|
Loading…
Reference in a new issue