mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 08:41:34 +11:00
switch interrupts to be in boxes
This commit is contained in:
parent
1852591a99
commit
1f3d3380f1
|
@ -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,101 +222,46 @@ 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),
|
pub fn add_interrupt_handler<'a>(
|
||||||
root: &InterruptRoot,
|
|
||||||
) -> InterruptClosureBounded<'a> {
|
|
||||||
InterruptClosureBounded {
|
|
||||||
c: InterruptClosure {
|
|
||||||
closure: unsafe { core::mem::transmute(f as *const _) },
|
|
||||||
next: Cell::new(core::ptr::null()),
|
|
||||||
root: root as *const _,
|
|
||||||
},
|
|
||||||
_phantom: PhantomData,
|
|
||||||
_unpin: PhantomPinned,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
free(|_| {
|
||||||
/// Adds an interrupt handler to the interrupt table such that when that
|
let root = interrupt_to_root(interrupt);
|
||||||
/// interrupt is triggered the closure is called.
|
root.add();
|
||||||
pub fn add_interrupt<'a>(interrupt: Pin<&'a InterruptClosureBounded<'a>>) {
|
let mut c = root.next.get();
|
||||||
free(|_| {
|
if c.is_null() {
|
||||||
let root = unsafe { &*interrupt.c.root };
|
root.next.set((&*inner) as *const _);
|
||||||
root.add();
|
|
||||||
let mut c = root.next.get();
|
|
||||||
if c.is_null() {
|
|
||||||
root.next.set((&interrupt.c) as *const _);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
let p = unsafe { &*c }.next.get();
|
|
||||||
if p.is_null() {
|
|
||||||
unsafe { &*c }.next.set((&interrupt.c) as *const _);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
loop {
|
||||||
|
let p = unsafe { &*c }.next.get();
|
||||||
|
if p.is_null() {
|
||||||
|
unsafe { &*c }.next.set((&*inner) as *const _);
|
||||||
|
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) };
|
||||||
#[macro_export]
|
do_with_inner(interrupt, inner)
|
||||||
/// Creates a new interrupt handler in the current scope, when this scope drops
|
|
||||||
/// 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
|
|
||||||
/// ```
|
|
||||||
/// 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
|
||||||
|
@ -322,14 +312,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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue