From 2cc0bee7c3c29f2194138f28e11fc81838fb3cbe Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 18:47:03 +0100 Subject: [PATCH 01/45] example first version of an interrupt handler implemented with closures --- agb/examples/just_build.rs | 3 + agb/interrupt_simple.s | 19 ++++- agb/src/interrupt.rs | 158 +++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+), 1 deletion(-) diff --git a/agb/examples/just_build.rs b/agb/examples/just_build.rs index 2287d816..b19262db 100644 --- a/agb/examples/just_build.rs +++ b/agb/examples/just_build.rs @@ -6,6 +6,9 @@ fn panic_handler(_info: &core::panic::PanicInfo) -> ! { loop {} } +#[no_mangle] +pub extern "C" fn __RUST_INTERRUPT_HANDLER(_: u16) {} + // implementation of tonc's "My first GBA demo" // https://coranac.com/tonc/text/first.htm diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index de64d181..817fd0b1 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -13,6 +13,23 @@ InterruptHandlerSimple: ldrh r1, [r2] @ load bios interrupt requests orr r1, r1, r0 @ or with enabled and requested interrupts strh r1, [r2] @ acknowlege bios requests - + + mrs r2, cpsr + orr r2, r2, #0xD + msr cpsr_c, r2 + + + ldr r1, =__RUST_INTERRUPT_HANDLER + push {lr} + adr lr, .IReturn + bx r1 +.IReturn: + pop {lr} + + mrs r2, cpsr + bic r2, r2, #0xD + orr r2, r2, #0x92 + msr cpsr_c, r2 + bx lr @ return to bios .pool diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 772e8a08..7db37619 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -1,3 +1,9 @@ +use core::{ + cell::Cell, + marker::{PhantomData, PhantomPinned}, + pin::Pin, +}; + use crate::memory_mapped::MemoryMapped; #[allow(dead_code)] @@ -55,3 +61,155 @@ pub fn enable_interrupts() { pub(crate) fn disable_interrupts() { INTERRUPTS_ENABLED.set(0); } + +pub struct InterruptRoot { + next: Cell<*const InterruptClosure>, +} + +impl InterruptRoot { + const fn new() -> Self { + InterruptRoot { + next: Cell::new(core::ptr::null()), + } + } +} + +static mut INTERRUPT_TABLE: Interrupts = Interrupts { + vblank: InterruptRoot::new(), + hblank: InterruptRoot::new(), +}; + +#[no_mangle] +pub extern "C" fn __RUST_INTERRUPT_HANDLER(interrupt: u16) { + if interrupt & 1 != 0 { + unsafe { INTERRUPT_TABLE.vblank.trigger_interrupts() }; + }; +} + +struct Interrupts { + vblank: InterruptRoot, + hblank: InterruptRoot, +} + +pub struct InterruptClosureBounded<'a> { + c: InterruptClosure, + _phantom: PhantomData<&'a ()>, + _unpin: PhantomPinned, +} + +pub struct InterruptClosure { + closure: *mut (dyn FnMut()), + next: Cell<*const InterruptClosure>, + root: *const InterruptRoot, +} + +impl InterruptRoot { + fn trigger_interrupts(&self) { + let mut count = 0; + let mut c = self.next.get(); + while !c.is_null() { + count += 1; + let closure_ptr = unsafe { &*c }.closure; + let closure_ref = unsafe { &mut *closure_ptr }; + closure_ref(); + c = unsafe { &*c }.next.get(); + } + } +} + +impl Drop for InterruptClosure { + fn drop(&mut self) { + let mut c = unsafe { &*self.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 get_interrupt_handle_root<'a>( + f: &'a mut dyn FnMut(), + root: &InterruptRoot, +) -> InterruptClosureBounded<'a> { + InterruptClosureBounded { + c: InterruptClosure { + closure: unsafe { core::mem::transmute(f as *mut _) }, + next: Cell::new(core::ptr::null()), + root: root as *const _, + }, + _phantom: PhantomData, + _unpin: PhantomPinned, + } +} + +pub fn get_interrupt_handle<'a>( + f: &'a mut dyn FnMut(), + interrupt: Interrupt, +) -> InterruptClosureBounded<'a> { + let root = match interrupt { + Interrupt::VBlank => unsafe { &INTERRUPT_TABLE.vblank }, + _ => unimplemented!( + "sorry, I haven't yet added this interrupt. Please request it if you need it" + ), + }; + + get_interrupt_handle_root(f, root) +} + +pub fn add_interrupt<'a>(interrupt: Pin<&'a InterruptClosureBounded<'a>>) { + let root = unsafe { &*interrupt.c.root }; + 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; + } + + c = p; + } +} + +#[test_case] +fn test_vblank_interrupt_handler(gba: &mut crate::Gba) { + { + let mut counter = 0; + let mut counter_2 = 0; + + let mut vblank_interrupt = || counter += 1; + let mut vblank_interrupt_2 = || counter_2 += 1; + + let interrupt_closure = get_interrupt_handle(&mut vblank_interrupt, Interrupt::VBlank); + let interrupt_closure = unsafe { Pin::new_unchecked(&interrupt_closure) }; + add_interrupt(interrupt_closure); + + let interrupt_closure_2 = get_interrupt_handle(&mut vblank_interrupt_2, Interrupt::VBlank); + let interrupt_closure_2 = unsafe { Pin::new_unchecked(&interrupt_closure_2) }; + add_interrupt(interrupt_closure_2); + + let vblank = gba.display.vblank.get(); + + while counter < 100 || counter_2 < 100 { + vblank.wait_for_VBlank(); + } + } + + assert_eq!( + unsafe { INTERRUPT_TABLE.vblank.next.get() }, + core::ptr::null(), + "expected the interrupt table for vblank to be empty" + ); +} From ebfd6373004eb6c240c4a3d61d23d89fc0cd7064 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 18:53:37 +0100 Subject: [PATCH 02/45] satisfy linter by removing elidable lifetime --- agb/src/interrupt.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 7db37619..7eecbfb6 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -151,10 +151,7 @@ fn get_interrupt_handle_root<'a>( } } -pub fn get_interrupt_handle<'a>( - f: &'a mut dyn FnMut(), - interrupt: Interrupt, -) -> InterruptClosureBounded<'a> { +pub fn get_interrupt_handle(f: &mut dyn FnMut(), interrupt: Interrupt) -> InterruptClosureBounded { let root = match interrupt { Interrupt::VBlank => unsafe { &INTERRUPT_TABLE.vblank }, _ => unimplemented!( From dacdf51dcaec87807c568819b65e5f78abb54ed8 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 18:56:21 +0100 Subject: [PATCH 03/45] remove unused debug counter --- agb/src/interrupt.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 7eecbfb6..af0c7e9f 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -105,10 +105,8 @@ pub struct InterruptClosure { impl InterruptRoot { fn trigger_interrupts(&self) { - let mut count = 0; let mut c = self.next.get(); while !c.is_null() { - count += 1; let closure_ptr = unsafe { &*c }.closure; let closure_ref = unsafe { &mut *closure_ptr }; closure_ref(); From 2ff96443107914a1233284db6b67a32e3057393d Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 19:06:24 +0100 Subject: [PATCH 04/45] disable interrupts during the interrupt handler --- agb/interrupt_simple.s | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index 817fd0b1..c86495a5 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -3,33 +3,43 @@ .global InterruptHandlerSimple .align InterruptHandlerSimple: - ldr r2, =0x04000200 @ interrupt enable register location - ldrh r1, [r2] @ load 16 bit interrupt enable to r1 - ldrh r3, [r2, #2] @ load 16 bit interrupt request to r3 + ldr r4, =0x04000200 @ interrupt enable register location + + @ disable interrupts by setting bit 0 to 0 + strh r2, [r4, #8] + + ldrh r1, [r4] @ load 16 bit interrupt enable to r1 + ldrh r3, [r4, #2] @ load 16 bit interrupt request to r3 and r0, r1, r3 @ interrupts both enabled and requested - strh r0, [r2, #2] @ store to interrupt request + strh r0, [r4, #2] @ store to interrupt request ldr r2, =0x03007FF8 @ load bios interrupt request location ldrh r1, [r2] @ load bios interrupt requests orr r1, r1, r0 @ or with enabled and requested interrupts strh r1, [r2] @ acknowlege bios requests + @ change to user mode mrs r2, cpsr orr r2, r2, #0xD msr cpsr_c, r2 - + @ call the rust interrupt handler with r0 set to the triggered interrupts ldr r1, =__RUST_INTERRUPT_HANDLER - push {lr} + push {lr, r4} adr lr, .IReturn bx r1 .IReturn: - pop {lr} + pop {lr, r4} + @ change back to interuupt mode mrs r2, cpsr bic r2, r2, #0xD orr r2, r2, #0x92 msr cpsr_c, r2 + @ reenable interrupts by setting bit 0 to 1 + mov r0, #1 + strh r0, [r4, #8] + bx lr @ return to bios .pool From c2d4c99f6697814ca8f4d8921355dff6a5d2617e Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 19:08:50 +0100 Subject: [PATCH 05/45] correct exportation of functions --- agb/src/interrupt.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index af0c7e9f..e531e8c8 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -27,21 +27,21 @@ pub enum Interrupt { const ENABLED_INTERRUPTS: MemoryMapped = unsafe { MemoryMapped::new(0x04000200) }; const INTERRUPTS_ENABLED: MemoryMapped = unsafe { MemoryMapped::new(0x04000208) }; -pub fn enable(interrupt: Interrupt) { +pub(crate) fn enable(interrupt: Interrupt) { let _interrupt_token = temporary_interrupt_disable(); let interrupt = interrupt as usize; let enabled = ENABLED_INTERRUPTS.get() | (1 << (interrupt as u16)); ENABLED_INTERRUPTS.set(enabled); } -pub fn disable(interrupt: Interrupt) { +pub(crate) fn disable(interrupt: Interrupt) { let _interrupt_token = temporary_interrupt_disable(); let interrupt = interrupt as usize; let enabled = ENABLED_INTERRUPTS.get() & !(1 << (interrupt as u16)); ENABLED_INTERRUPTS.set(enabled); } -pub struct Disable {} +pub(crate) struct Disable {} impl Drop for Disable { fn drop(&mut self) { @@ -49,12 +49,12 @@ impl Drop for Disable { } } -pub fn temporary_interrupt_disable() -> Disable { +pub(crate) fn temporary_interrupt_disable() -> Disable { disable_interrupts(); Disable {} } -pub fn enable_interrupts() { +pub(crate) fn enable_interrupts() { INTERRUPTS_ENABLED.set(1); } @@ -62,7 +62,7 @@ pub(crate) fn disable_interrupts() { INTERRUPTS_ENABLED.set(0); } -pub struct InterruptRoot { +pub(crate) struct InterruptRoot { next: Cell<*const InterruptClosure>, } @@ -97,7 +97,7 @@ pub struct InterruptClosureBounded<'a> { _unpin: PhantomPinned, } -pub struct InterruptClosure { +struct InterruptClosure { closure: *mut (dyn FnMut()), next: Cell<*const InterruptClosure>, root: *const InterruptRoot, From 30c5a0eea9df5595a78d79552a550d6349e859fb Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 19:09:10 +0100 Subject: [PATCH 06/45] export interrupt module --- agb/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/lib.rs b/agb/src/lib.rs index dc7a73ab..574b55f5 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -23,7 +23,7 @@ pub mod sound; pub use agb_image_converter::include_gfx; mod bitarray; -mod interrupt; +pub mod interrupt; mod memory_mapped; /// Implements logging to the mgba emulator. pub mod mgba; From b98e6cbb026e3560d42ecdc2dd3fe73ae823643e Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 23:01:51 +0100 Subject: [PATCH 07/45] add all other interrupts --- agb/src/interrupt.rs | 74 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index e531e8c8..c8ffa02a 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -74,21 +74,55 @@ impl InterruptRoot { } } -static mut INTERRUPT_TABLE: Interrupts = Interrupts { - vblank: InterruptRoot::new(), - hblank: InterruptRoot::new(), -}; +static mut INTERRUPT_TABLE: Interrupts = Interrupts::new(); #[no_mangle] pub extern "C" fn __RUST_INTERRUPT_HANDLER(interrupt: u16) { - if interrupt & 1 != 0 { - unsafe { INTERRUPT_TABLE.vblank.trigger_interrupts() }; - }; + for i in 0..=13_u8 { + if (1 << (i as u16)) & interrupt != 0 { + let interrupt = unsafe { core::mem::transmute(i) }; + let root = interrupt_to_root(interrupt); + root.trigger_interrupts(); + } + } } struct Interrupts { vblank: InterruptRoot, hblank: InterruptRoot, + vcounter: InterruptRoot, + timer0: InterruptRoot, + timer1: InterruptRoot, + timer2: InterruptRoot, + timer3: InterruptRoot, + serial: InterruptRoot, + dma0: InterruptRoot, + dma1: InterruptRoot, + dma2: InterruptRoot, + dma3: InterruptRoot, + keypad: InterruptRoot, + gamepak: InterruptRoot, +} + +impl Interrupts { + const fn new() -> Self { + Interrupts { + vblank: InterruptRoot::new(), + hblank: InterruptRoot::new(), + vcounter: InterruptRoot::new(), + timer0: InterruptRoot::new(), + timer1: InterruptRoot::new(), + timer2: InterruptRoot::new(), + timer3: InterruptRoot::new(), + serial: InterruptRoot::new(), + dma0: InterruptRoot::new(), + dma1: InterruptRoot::new(), + dma2: InterruptRoot::new(), + dma3: InterruptRoot::new(), + keypad: InterruptRoot::new(), + gamepak: InterruptRoot::new(), + } + } } pub struct InterruptClosureBounded<'a> { @@ -134,6 +168,25 @@ impl Drop for InterruptClosure { } } +fn interrupt_to_root(interrupt: Interrupt) -> &'static InterruptRoot { + match interrupt { + Interrupt::VBlank => unsafe { &INTERRUPT_TABLE.vblank }, + Interrupt::HBlank => unsafe { &INTERRUPT_TABLE.hblank }, + Interrupt::VCounter => unsafe { &INTERRUPT_TABLE.vcounter }, + Interrupt::Timer0 => unsafe { &INTERRUPT_TABLE.timer0 }, + Interrupt::Timer1 => unsafe { &INTERRUPT_TABLE.timer1 }, + Interrupt::Timer2 => unsafe { &INTERRUPT_TABLE.timer2 }, + Interrupt::Timer3 => unsafe { &INTERRUPT_TABLE.timer3 }, + Interrupt::Serial => unsafe { &INTERRUPT_TABLE.serial }, + Interrupt::Dma0 => unsafe { &INTERRUPT_TABLE.dma0 }, + Interrupt::Dma1 => unsafe { &INTERRUPT_TABLE.dma1 }, + Interrupt::Dma2 => unsafe { &INTERRUPT_TABLE.dma2 }, + Interrupt::Dma3 => unsafe { &INTERRUPT_TABLE.dma3 }, + Interrupt::Keypad => unsafe { &INTERRUPT_TABLE.keypad }, + Interrupt::Gamepak => unsafe { &INTERRUPT_TABLE.gamepak }, + } +} + fn get_interrupt_handle_root<'a>( f: &'a mut dyn FnMut(), root: &InterruptRoot, @@ -150,12 +203,7 @@ fn get_interrupt_handle_root<'a>( } pub fn get_interrupt_handle(f: &mut dyn FnMut(), interrupt: Interrupt) -> InterruptClosureBounded { - let root = match interrupt { - Interrupt::VBlank => unsafe { &INTERRUPT_TABLE.vblank }, - _ => unimplemented!( - "sorry, I haven't yet added this interrupt. Please request it if you need it" - ), - }; + let root = interrupt_to_root(interrupt); get_interrupt_handle_root(f, root) } From 597b9370fc0c03c05d815e094cf8f080363c3c88 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 23:08:38 +0100 Subject: [PATCH 08/45] don't disable and reenable interrupts --- agb/interrupt_simple.s | 7 ------- 1 file changed, 7 deletions(-) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index c86495a5..4837aed1 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -5,9 +5,6 @@ InterruptHandlerSimple: ldr r4, =0x04000200 @ interrupt enable register location - @ disable interrupts by setting bit 0 to 0 - strh r2, [r4, #8] - ldrh r1, [r4] @ load 16 bit interrupt enable to r1 ldrh r3, [r4, #2] @ load 16 bit interrupt request to r3 and r0, r1, r3 @ interrupts both enabled and requested @@ -37,9 +34,5 @@ InterruptHandlerSimple: orr r2, r2, #0x92 msr cpsr_c, r2 - @ reenable interrupts by setting bit 0 to 1 - mov r0, #1 - strh r0, [r4, #8] - bx lr @ return to bios .pool From 466d0844c5023acf8a8917dafa2650d649dfb4e1 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 23:09:36 +0100 Subject: [PATCH 09/45] push all registers, as rust may be using them --- agb/interrupt_simple.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index 4837aed1..17b8c0bc 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -22,11 +22,11 @@ InterruptHandlerSimple: @ call the rust interrupt handler with r0 set to the triggered interrupts ldr r1, =__RUST_INTERRUPT_HANDLER - push {lr, r4} + push {lr, r4-r11} adr lr, .IReturn bx r1 .IReturn: - pop {lr, r4} + pop {lr, r4-r11} @ change back to interuupt mode mrs r2, cpsr From 086a4385be27859c0b33f504edbf2d5a4a884b98 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 23:33:09 +0100 Subject: [PATCH 10/45] correct comments to accurately reflect what is going on --- agb/interrupt_simple.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index 17b8c0bc..2c672206 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -15,7 +15,7 @@ InterruptHandlerSimple: orr r1, r1, r0 @ or with enabled and requested interrupts strh r1, [r2] @ acknowlege bios requests - @ change to user mode + @ change to system mode mrs r2, cpsr orr r2, r2, #0xD msr cpsr_c, r2 @@ -28,7 +28,7 @@ InterruptHandlerSimple: .IReturn: pop {lr, r4-r11} - @ change back to interuupt mode + @ change back to interrupt mode mrs r2, cpsr bic r2, r2, #0xD orr r2, r2, #0x92 From f54a539296bcc024f234817e9befb140c5fed986 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 23:33:32 +0100 Subject: [PATCH 11/45] use better calling convention --- agb/interrupt_simple.s | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index 2c672206..2cb154ba 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -23,9 +23,8 @@ InterruptHandlerSimple: @ call the rust interrupt handler with r0 set to the triggered interrupts ldr r1, =__RUST_INTERRUPT_HANDLER push {lr, r4-r11} - adr lr, .IReturn + mov lr, pc bx r1 -.IReturn: pop {lr, r4-r11} @ change back to interrupt mode From a5488fab56edfa37fb1e8f3eeeb5df50950941d9 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 23 Jun 2021 23:39:34 +0100 Subject: [PATCH 12/45] review of documentation suggests that this does nothing --- agb/interrupt_simple.s | 1 - 1 file changed, 1 deletion(-) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index 2cb154ba..83e51e1e 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -30,7 +30,6 @@ InterruptHandlerSimple: @ change back to interrupt mode mrs r2, cpsr bic r2, r2, #0xD - orr r2, r2, #0x92 msr cpsr_c, r2 bx lr @ return to bios From a9aad11dd72a1c3c7e99ef40cf29cf8d1936b324 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Thu, 24 Jun 2021 00:58:25 +0100 Subject: [PATCH 13/45] make interrupt system interrupt safe(r) FnMut is not something that can be used in a interrupt safe manner. Instead use Fn with a Mutex that disables interrupts with a lock. --- agb/src/interrupt.rs | 90 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index c8ffa02a..9f73970e 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -1,6 +1,7 @@ use core::{ - cell::Cell, + cell::{Cell, UnsafeCell}, marker::{PhantomData, PhantomPinned}, + ops::{Deref, DerefMut}, pin::Pin, }; @@ -132,7 +133,7 @@ pub struct InterruptClosureBounded<'a> { } struct InterruptClosure { - closure: *mut (dyn FnMut()), + closure: *const (dyn Fn()), next: Cell<*const InterruptClosure>, root: *const InterruptRoot, } @@ -142,7 +143,7 @@ impl InterruptRoot { let mut c = self.next.get(); while !c.is_null() { let closure_ptr = unsafe { &*c }.closure; - let closure_ref = unsafe { &mut *closure_ptr }; + let closure_ref = unsafe { &*closure_ptr }; closure_ref(); c = unsafe { &*c }.next.get(); } @@ -188,12 +189,12 @@ fn interrupt_to_root(interrupt: Interrupt) -> &'static InterruptRoot { } fn get_interrupt_handle_root<'a>( - f: &'a mut dyn FnMut(), + f: &'a dyn Fn(), root: &InterruptRoot, ) -> InterruptClosureBounded<'a> { InterruptClosureBounded { c: InterruptClosure { - closure: unsafe { core::mem::transmute(f as *mut _) }, + closure: unsafe { core::mem::transmute(f as *const _) }, next: Cell::new(core::ptr::null()), root: root as *const _, }, @@ -202,7 +203,10 @@ fn get_interrupt_handle_root<'a>( } } -pub fn get_interrupt_handle(f: &mut dyn FnMut(), interrupt: Interrupt) -> InterruptClosureBounded { +pub fn get_interrupt_handle( + f: &(dyn Fn() + Send + Sync), + interrupt: Interrupt, +) -> InterruptClosureBounded { let root = interrupt_to_root(interrupt); get_interrupt_handle_root(f, root) @@ -229,11 +233,11 @@ pub fn add_interrupt<'a>(interrupt: Pin<&'a InterruptClosureBounded<'a>>) { #[test_case] fn test_vblank_interrupt_handler(gba: &mut crate::Gba) { { - let mut counter = 0; - let mut counter_2 = 0; + let counter = Mutex::new(0); + let counter_2 = Mutex::new(0); - let mut vblank_interrupt = || counter += 1; - let mut vblank_interrupt_2 = || counter_2 += 1; + let mut vblank_interrupt = || *counter.lock() += 1; + let mut vblank_interrupt_2 = || *counter_2.lock() += 1; let interrupt_closure = get_interrupt_handle(&mut vblank_interrupt, Interrupt::VBlank); let interrupt_closure = unsafe { Pin::new_unchecked(&interrupt_closure) }; @@ -245,7 +249,7 @@ fn test_vblank_interrupt_handler(gba: &mut crate::Gba) { let vblank = gba.display.vblank.get(); - while counter < 100 || counter_2 < 100 { + while *counter.lock() < 100 || *counter_2.lock() < 100 { vblank.wait_for_VBlank(); } } @@ -256,3 +260,67 @@ fn test_vblank_interrupt_handler(gba: &mut crate::Gba) { "expected the interrupt table for vblank to be empty" ); } + +#[derive(Clone, Copy)] +enum MutexState { + Locked, + Unlocked(bool), +} + +pub struct Mutex { + internal: UnsafeCell, + state: UnsafeCell, +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +impl Mutex { + pub fn lock(&self) -> MutexRef { + let state = INTERRUPTS_ENABLED.get(); + INTERRUPTS_ENABLED.set(0); + unsafe { *self.state.get() = MutexState::Unlocked(state != 0) }; + MutexRef { + internal: &self.internal, + state: &self.state, + } + } + pub fn new(val: T) -> Self { + Mutex { + internal: UnsafeCell::new(val), + state: UnsafeCell::new(MutexState::Locked), + } + } +} + +pub struct MutexRef<'a, T> { + internal: &'a UnsafeCell, + state: &'a UnsafeCell, +} + +impl<'a, T> Drop for MutexRef<'a, T> { + fn drop(&mut self) { + unsafe { + let state = &mut *self.state.get(); + let prev_state = *state; + *state = MutexState::Locked; + match prev_state { + MutexState::Unlocked(b) => INTERRUPTS_ENABLED.set(b as u16), + MutexState::Locked => {} + } + } + } +} + +impl<'a, T> Deref for MutexRef<'a, T> { + type Target = T; + fn deref(&self) -> &Self::Target { + unsafe { &*self.internal.get() } + } +} + +impl<'a, T> DerefMut for MutexRef<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.internal.get() } + } +} From 1318e7eca45b4db2e71ae1231f2b86ff863aad2a Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Thu, 24 Jun 2021 01:02:41 +0100 Subject: [PATCH 14/45] ensure mutex is unlocked before locking --- agb/src/interrupt.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 9f73970e..7834dbff 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -261,7 +261,7 @@ fn test_vblank_interrupt_handler(gba: &mut crate::Gba) { ); } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] enum MutexState { Locked, Unlocked(bool), @@ -279,6 +279,11 @@ impl Mutex { pub fn lock(&self) -> MutexRef { let state = INTERRUPTS_ENABLED.get(); INTERRUPTS_ENABLED.set(0); + assert_eq!( + unsafe { *self.state.get() }, + MutexState::Locked, + "mutex must be unlocked to be able to lock it" + ); unsafe { *self.state.get() = MutexState::Unlocked(state != 0) }; MutexRef { internal: &self.internal, From 47b02d29cfbea0399cef0fd7064ce6fbfb95b4b2 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Thu, 24 Jun 2021 01:02:55 +0100 Subject: [PATCH 15/45] correct which way round locked and unlocked is --- agb/src/interrupt.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 7834dbff..12266c6f 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -263,8 +263,8 @@ fn test_vblank_interrupt_handler(gba: &mut crate::Gba) { #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum MutexState { - Locked, - Unlocked(bool), + Unlocked, + Locked(bool), } pub struct Mutex { @@ -281,10 +281,10 @@ impl Mutex { INTERRUPTS_ENABLED.set(0); assert_eq!( unsafe { *self.state.get() }, - MutexState::Locked, + MutexState::Unlocked, "mutex must be unlocked to be able to lock it" ); - unsafe { *self.state.get() = MutexState::Unlocked(state != 0) }; + unsafe { *self.state.get() = MutexState::Locked(state != 0) }; MutexRef { internal: &self.internal, state: &self.state, @@ -293,7 +293,7 @@ impl Mutex { pub fn new(val: T) -> Self { Mutex { internal: UnsafeCell::new(val), - state: UnsafeCell::new(MutexState::Locked), + state: UnsafeCell::new(MutexState::Unlocked), } } } @@ -308,10 +308,10 @@ impl<'a, T> Drop for MutexRef<'a, T> { unsafe { let state = &mut *self.state.get(); let prev_state = *state; - *state = MutexState::Locked; + *state = MutexState::Unlocked; match prev_state { - MutexState::Unlocked(b) => INTERRUPTS_ENABLED.set(b as u16), - MutexState::Locked => {} + MutexState::Locked(b) => INTERRUPTS_ENABLED.set(b as u16), + MutexState::Unlocked => {} } } } From a959e8e06b6696be17a0d4957704f76f5371cfb2 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Thu, 24 Jun 2021 12:30:40 +0100 Subject: [PATCH 16/45] don't use register 4 --- agb/interrupt_simple.s | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index 83e51e1e..2b4b220d 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -3,12 +3,12 @@ .global InterruptHandlerSimple .align InterruptHandlerSimple: - ldr r4, =0x04000200 @ interrupt enable register location + ldr r2, =0x04000200 @ interrupt enable register location - ldrh r1, [r4] @ load 16 bit interrupt enable to r1 - ldrh r3, [r4, #2] @ load 16 bit interrupt request to r3 + ldrh r1, [r2] @ load 16 bit interrupt enable to r1 + ldrh r3, [r2, #2] @ load 16 bit interrupt request to r3 and r0, r1, r3 @ interrupts both enabled and requested - strh r0, [r4, #2] @ store to interrupt request + strh r0, [r2, #2] @ store to interrupt request ldr r2, =0x03007FF8 @ load bios interrupt request location ldrh r1, [r2] @ load bios interrupt requests @@ -22,10 +22,10 @@ InterruptHandlerSimple: @ call the rust interrupt handler with r0 set to the triggered interrupts ldr r1, =__RUST_INTERRUPT_HANDLER - push {lr, r4-r11} + push {lr, r4-r9} mov lr, pc bx r1 - pop {lr, r4-r11} + pop {lr, r4-r9} @ change back to interrupt mode mrs r2, cpsr From d607f892e36ca1c57b44ba0d375ffe294f49c9ae Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Thu, 24 Jun 2021 19:50:33 +0100 Subject: [PATCH 17/45] change to array for efficient lookup table --- agb/src/interrupt.rs | 81 ++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 60 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 12266c6f..8811b817 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -75,57 +75,33 @@ impl InterruptRoot { } } -static mut INTERRUPT_TABLE: Interrupts = Interrupts::new(); +static mut INTERRUPT_TABLE: [InterruptRoot; 14] = [ + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), + InterruptRoot::new(), +]; #[no_mangle] pub extern "C" fn __RUST_INTERRUPT_HANDLER(interrupt: u16) { - for i in 0..=13_u8 { - if (1 << (i as u16)) & interrupt != 0 { - let interrupt = unsafe { core::mem::transmute(i) }; - let root = interrupt_to_root(interrupt); + for i in 0..=13_u16 { + if (1 << i) & interrupt != 0 { + let root = unsafe { &INTERRUPT_TABLE[i as usize] }; root.trigger_interrupts(); } } } -struct Interrupts { - vblank: InterruptRoot, - hblank: InterruptRoot, - vcounter: InterruptRoot, - timer0: InterruptRoot, - timer1: InterruptRoot, - timer2: InterruptRoot, - timer3: InterruptRoot, - serial: InterruptRoot, - dma0: InterruptRoot, - dma1: InterruptRoot, - dma2: InterruptRoot, - dma3: InterruptRoot, - keypad: InterruptRoot, - gamepak: InterruptRoot, -} - -impl Interrupts { - const fn new() -> Self { - Interrupts { - vblank: InterruptRoot::new(), - hblank: InterruptRoot::new(), - vcounter: InterruptRoot::new(), - timer0: InterruptRoot::new(), - timer1: InterruptRoot::new(), - timer2: InterruptRoot::new(), - timer3: InterruptRoot::new(), - serial: InterruptRoot::new(), - dma0: InterruptRoot::new(), - dma1: InterruptRoot::new(), - dma2: InterruptRoot::new(), - dma3: InterruptRoot::new(), - keypad: InterruptRoot::new(), - gamepak: InterruptRoot::new(), - } - } -} - pub struct InterruptClosureBounded<'a> { c: InterruptClosure, _phantom: PhantomData<&'a ()>, @@ -170,22 +146,7 @@ impl Drop for InterruptClosure { } fn interrupt_to_root(interrupt: Interrupt) -> &'static InterruptRoot { - match interrupt { - Interrupt::VBlank => unsafe { &INTERRUPT_TABLE.vblank }, - Interrupt::HBlank => unsafe { &INTERRUPT_TABLE.hblank }, - Interrupt::VCounter => unsafe { &INTERRUPT_TABLE.vcounter }, - Interrupt::Timer0 => unsafe { &INTERRUPT_TABLE.timer0 }, - Interrupt::Timer1 => unsafe { &INTERRUPT_TABLE.timer1 }, - Interrupt::Timer2 => unsafe { &INTERRUPT_TABLE.timer2 }, - Interrupt::Timer3 => unsafe { &INTERRUPT_TABLE.timer3 }, - Interrupt::Serial => unsafe { &INTERRUPT_TABLE.serial }, - Interrupt::Dma0 => unsafe { &INTERRUPT_TABLE.dma0 }, - Interrupt::Dma1 => unsafe { &INTERRUPT_TABLE.dma1 }, - Interrupt::Dma2 => unsafe { &INTERRUPT_TABLE.dma2 }, - Interrupt::Dma3 => unsafe { &INTERRUPT_TABLE.dma3 }, - Interrupt::Keypad => unsafe { &INTERRUPT_TABLE.keypad }, - Interrupt::Gamepak => unsafe { &INTERRUPT_TABLE.gamepak }, - } + unsafe { &INTERRUPT_TABLE[interrupt as usize] } } fn get_interrupt_handle_root<'a>( @@ -255,7 +216,7 @@ fn test_vblank_interrupt_handler(gba: &mut crate::Gba) { } assert_eq!( - unsafe { INTERRUPT_TABLE.vblank.next.get() }, + interrupt_to_root(Interrupt::VBlank).next.get(), core::ptr::null(), "expected the interrupt table for vblank to be empty" ); From ac5fd06106db84f601b65a8b230a6dca5e62b6d3 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Thu, 24 Jun 2021 19:52:45 +0100 Subject: [PATCH 18/45] add test for length of interrupt table --- agb/src/interrupt.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 8811b817..60d15704 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -222,6 +222,15 @@ fn test_vblank_interrupt_handler(gba: &mut crate::Gba) { ); } +#[test_case] +fn test_interrupt_table_length(_gba: &mut crate::Gba) { + assert_eq!( + unsafe { INTERRUPT_TABLE.len() }, + Interrupt::Gamepak as usize + 1, + "interrupt table should be able to store gamepak interrupt" + ); +} + #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum MutexState { Unlocked, From bd1f5f6a5a7eaaa9c2f347b283f7c228ea421945 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Thu, 24 Jun 2021 20:05:23 +0100 Subject: [PATCH 19/45] store gbatek recommended registers --- agb/interrupt_simple.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index 2b4b220d..a678206e 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -22,10 +22,10 @@ InterruptHandlerSimple: @ call the rust interrupt handler with r0 set to the triggered interrupts ldr r1, =__RUST_INTERRUPT_HANDLER - push {lr, r4-r9} + push {lr, r4-r11} mov lr, pc bx r1 - pop {lr, r4-r9} + pop {lr, r4-r11} @ change back to interrupt mode mrs r2, cpsr From 1ac33bfbc84212ab89c21907db70bbbf090145f3 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Fri, 25 Jun 2021 21:40:07 +0100 Subject: [PATCH 20/45] put interrupt handler in iwram, 32 bit bus --- agb/interrupt_simple.s | 1 + 1 file changed, 1 insertion(+) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index a678206e..d6cb80d7 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -1,6 +1,7 @@ @ An interrupt handler that simply acknowledges all interrupts .arm .global InterruptHandlerSimple + .section .iwram, "ax" .align InterruptHandlerSimple: ldr r2, =0x04000200 @ interrupt enable register location From 8b8b2b17a2b66cbde222793d018e42f00a25bbf1 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Fri, 25 Jun 2021 21:40:25 +0100 Subject: [PATCH 21/45] change to using iter --- agb/src/interrupt.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 60d15704..59bfa3c0 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -94,9 +94,8 @@ static mut INTERRUPT_TABLE: [InterruptRoot; 14] = [ #[no_mangle] pub extern "C" fn __RUST_INTERRUPT_HANDLER(interrupt: u16) { - for i in 0..=13_u16 { + for (i, root) in unsafe { INTERRUPT_TABLE.iter().enumerate() } { if (1 << i) & interrupt != 0 { - let root = unsafe { &INTERRUPT_TABLE[i as usize] }; root.trigger_interrupts(); } } From 9bab2a75d008249aad5e98f11911ee693a2c8d85 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Fri, 25 Jun 2021 22:47:24 +0100 Subject: [PATCH 22/45] add macro for much easier interrupt handler adding --- agb/src/interrupt.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 59bfa3c0..deb4e18b 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -190,22 +190,23 @@ pub fn add_interrupt<'a>(interrupt: Pin<&'a InterruptClosureBounded<'a>>) { } } +#[macro_export] +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); + }; +} + #[test_case] fn test_vblank_interrupt_handler(gba: &mut crate::Gba) { { let counter = Mutex::new(0); let counter_2 = Mutex::new(0); - - let mut vblank_interrupt = || *counter.lock() += 1; - let mut vblank_interrupt_2 = || *counter_2.lock() += 1; - - let interrupt_closure = get_interrupt_handle(&mut vblank_interrupt, Interrupt::VBlank); - let interrupt_closure = unsafe { Pin::new_unchecked(&interrupt_closure) }; - add_interrupt(interrupt_closure); - - let interrupt_closure_2 = get_interrupt_handle(&mut vblank_interrupt_2, Interrupt::VBlank); - let interrupt_closure_2 = unsafe { Pin::new_unchecked(&interrupt_closure_2) }; - add_interrupt(interrupt_closure_2); + add_interrupt_handler!(Interrupt::VBlank, || *counter.lock() += 1); + add_interrupt_handler!(Interrupt::VBlank, || *counter_2.lock() += 1); let vblank = gba.display.vblank.get(); From e22564a29c1662fbdbca5dd8c008136f11a6faa7 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Fri, 25 Jun 2021 23:34:58 +0100 Subject: [PATCH 23/45] disable interrupts during handler --- agb/interrupt_simple.s | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index d6cb80d7..940f75b4 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -6,6 +6,11 @@ InterruptHandlerSimple: ldr r2, =0x04000200 @ interrupt enable register location + mov r1, #0 + strh r1, [r2, #8] + + push {r2} + ldrh r1, [r2] @ load 16 bit interrupt enable to r1 ldrh r3, [r2, #2] @ load 16 bit interrupt request to r3 and r0, r1, r3 @ interrupts both enabled and requested @@ -33,5 +38,10 @@ InterruptHandlerSimple: bic r2, r2, #0xD msr cpsr_c, r2 + pop {r2} + + mov r1, #0 + strh r1, [r2, #8] + bx lr @ return to bios .pool From f1a2b76d285ddab6f61f16bc3bf8f30b5d5073ea Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 26 Jun 2021 21:59:10 +0100 Subject: [PATCH 24/45] arm is callee save --- agb/interrupt_simple.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb/interrupt_simple.s b/agb/interrupt_simple.s index 940f75b4..e34e2cd2 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_simple.s @@ -28,10 +28,10 @@ InterruptHandlerSimple: @ call the rust interrupt handler with r0 set to the triggered interrupts ldr r1, =__RUST_INTERRUPT_HANDLER - push {lr, r4-r11} + push {lr} mov lr, pc bx r1 - pop {lr, r4-r11} + pop {lr} @ change back to interrupt mode mrs r2, cpsr From 007d4d3ff8dcf0d335de6b66060f1ee21e5a8b7e Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 27 Jun 2021 02:36:47 +0100 Subject: [PATCH 25/45] handler does not need to be public to be exposed --- agb/src/interrupt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index deb4e18b..353fd048 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -93,7 +93,7 @@ static mut INTERRUPT_TABLE: [InterruptRoot; 14] = [ ]; #[no_mangle] -pub extern "C" fn __RUST_INTERRUPT_HANDLER(interrupt: u16) { +extern "C" fn __RUST_INTERRUPT_HANDLER(interrupt: u16) { for (i, root) in unsafe { INTERRUPT_TABLE.iter().enumerate() } { if (1 << i) & interrupt != 0 { root.trigger_interrupts(); From 7b05da478346a08131597269a294c569c98c4c4b Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 27 Jun 2021 02:46:32 +0100 Subject: [PATCH 26/45] change name of interrupt handler it is no longer simple --- agb/build.rs | 2 +- agb/crt0.s | 4 ++-- agb/{interrupt_simple.s => interrupt_handler.s} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename agb/{interrupt_simple.s => interrupt_handler.s} (94%) diff --git a/agb/build.rs b/agb/build.rs index a2c32735..b72b8b5d 100644 --- a/agb/build.rs +++ b/agb/build.rs @@ -1,8 +1,8 @@ fn main() { println!("cargo:rerun-if-changed=crt0.s"); println!("cargo:rerun-if-changed=gba_mb.ld"); - println!("cargo:rerun-if-changed=interrupt_simple.s"); println!("cargo:rerun-if-changed=src/sound/mixer/mixer.s"); + println!("cargo:rerun-if-changed=interrupt_handler.s"); println!("cargo:rerun-if-changed=gfx/test_logo.png"); let out_file_name = "crt0.o"; diff --git a/agb/crt0.s b/agb/crt0.s index 1b51ec83..26c2236b 100644 --- a/agb/crt0.s +++ b/agb/crt0.s @@ -18,7 +18,7 @@ b .Initialise_mb swi 0x00250000 @ Set interrupt handler - ldr r0, =InterruptHandlerSimple + ldr r0, =InterruptHandler ldr r1, =0x03007FFC str r0, [r1] @@ -62,5 +62,5 @@ b .Initialise_mb b 1b .pool -.include "interrupt_simple.s" .include "src/sound/mixer/mixer.s" +.include "interrupt_handler.s" diff --git a/agb/interrupt_simple.s b/agb/interrupt_handler.s similarity index 94% rename from agb/interrupt_simple.s rename to agb/interrupt_handler.s index e34e2cd2..9f062bc3 100644 --- a/agb/interrupt_simple.s +++ b/agb/interrupt_handler.s @@ -1,9 +1,9 @@ @ An interrupt handler that simply acknowledges all interrupts .arm - .global InterruptHandlerSimple + .global InterruptHandler .section .iwram, "ax" .align -InterruptHandlerSimple: +InterruptHandler: ldr r2, =0x04000200 @ interrupt enable register location mov r1, #0 From f8fb6ba7b4858a0d13eef3effa987dfc59bdaf86 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Mon, 28 Jun 2021 01:03:08 +0100 Subject: [PATCH 27/45] change implementation of vblank to use new interrupt handler also fixes all examples --- agb/examples/bitmap3.rs | 4 +- agb/examples/bitmap4.rs | 4 +- agb/examples/chicken.rs | 4 +- agb/examples/mixer_basic.rs | 4 +- agb/examples/multiple_video.rs | 14 ++-- agb/examples/output.rs | 4 +- agb/examples/wave.rs | 62 +++++++++++++++++ agb/src/display/mod.rs | 7 +- agb/src/display/tiled0.rs | 9 ++- agb/src/display/vblank.rs | 52 -------------- agb/src/interrupt.rs | 122 +++++++++++++++++++++++++-------- agb/src/lib.rs | 6 +- 12 files changed, 185 insertions(+), 107 deletions(-) create mode 100644 agb/examples/wave.rs delete mode 100644 agb/src/display/vblank.rs diff --git a/agb/examples/bitmap3.rs b/agb/examples/bitmap3.rs index b9708962..5c45544c 100644 --- a/agb/examples/bitmap3.rs +++ b/agb/examples/bitmap3.rs @@ -14,7 +14,7 @@ struct Vector2D { pub fn main() -> ! { let mut gba = agb::Gba::new(); let mut bitmap = gba.display.video.bitmap3(); - let vblank = gba.display.vblank.get(); + let vblank = agb::interrupt::VBlank::new(); let mut input = agb::input::ButtonController::new(); let mut pos = Vector2D { @@ -23,7 +23,7 @@ pub fn main() -> ! { }; loop { - vblank.wait_for_VBlank(); + vblank.wait_for_vblank(); input.update(); pos.x += input.x_tri() as i32; diff --git a/agb/examples/bitmap4.rs b/agb/examples/bitmap4.rs index a9ffd6e6..4c40fafc 100644 --- a/agb/examples/bitmap4.rs +++ b/agb/examples/bitmap4.rs @@ -9,7 +9,7 @@ use agb::display; pub fn main() -> ! { let mut gba = agb::Gba::new(); let mut bitmap = gba.display.video.bitmap4(); - let vblank = gba.display.vblank.get(); + let vblank = agb::interrupt::VBlank::new(); bitmap.set_palette_entry(1, 0x001F); bitmap.set_palette_entry(2, 0x03E0); @@ -30,7 +30,7 @@ pub fn main() -> ! { let mut count = 0; loop { - vblank.wait_for_VBlank(); + vblank.wait_for_vblank(); count += 1; if count % 6 == 0 { bitmap.flip_page(); diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index e732faff..47f531f0 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -45,7 +45,7 @@ pub fn main() -> ! { let mut gba = agb::Gba::new(); let mut gfx = gba.display.video.tiled0(); - let vblank = gba.display.vblank.get(); + let vblank = agb::interrupt::VBlank::new(); let mut input = agb::input::ButtonController::new(); gfx.set_sprite_palette_raw(&CHICKEN_PALETTE); @@ -90,7 +90,7 @@ pub fn main() -> ! { let terminal_velocity = (1 << 8) / 2; loop { - vblank.wait_for_VBlank(); + vblank.wait_for_vblank(); frame_count += 1; input.update(); diff --git a/agb/examples/mixer_basic.rs b/agb/examples/mixer_basic.rs index c234642f..a5bc18d7 100644 --- a/agb/examples/mixer_basic.rs +++ b/agb/examples/mixer_basic.rs @@ -15,7 +15,7 @@ const I_WILL_NOT_LET_YOU_LET_ME_DOWN: &[u8] = include_bytes!("i-will-not-let-you pub fn main() -> ! { let mut gba = Gba::new(); let mut input = ButtonController::new(); - let vblank_provider = gba.display.vblank.get(); + let vblank_provider = agb::interrupt::VBlank::new(); let mut mixer = gba.mixer.mixer(); mixer.enable(); @@ -50,7 +50,7 @@ pub fn main() -> ! { } } - vblank_provider.wait_for_VBlank(); + vblank_provider.wait_for_vblank(); mixer.vblank(); } } diff --git a/agb/examples/multiple_video.rs b/agb/examples/multiple_video.rs index 04f18e99..c8b1a847 100644 --- a/agb/examples/multiple_video.rs +++ b/agb/examples/multiple_video.rs @@ -13,18 +13,18 @@ struct Vector2D { #[no_mangle] pub fn main() -> ! { let mut gba = agb::Gba::new(); - let mut vblank = gba.display.vblank.get(); + let mut vblank = agb::interrupt::VBlank::new(); let mut input = agb::input::ButtonController::new(); loop { - bitmap3_mode(&mut gba.display.video.bitmap3(), &mut vblank, &mut input); - bitmap4_mode(&mut gba.display.video.bitmap4(), &mut vblank, &mut input); + bitmap3_mode(&mut gba.display.video.bitmap3(), &vblank, &mut input); + bitmap4_mode(&mut gba.display.video.bitmap4(), &vblank, &mut input); } } fn bitmap3_mode( bitmap: &mut display::bitmap3::Bitmap3, - vblank: &mut display::vblank::VBlank, + vblank: &agb::interrupt::VBlank, input: &mut agb::input::ButtonController, ) { let mut pos = Vector2D { @@ -33,7 +33,7 @@ fn bitmap3_mode( }; loop { - vblank.wait_for_VBlank(); + vblank.wait_for_vblank(); input.update(); if input.is_just_pressed(agb::input::Button::B) { @@ -51,7 +51,7 @@ fn bitmap3_mode( fn bitmap4_mode( bitmap: &mut display::bitmap4::Bitmap4, - vblank: &mut display::vblank::VBlank, + vblank: &agb::interrupt::VBlank, input: &mut agb::input::ButtonController, ) { bitmap.set_palette_entry(1, 0x001F); @@ -72,7 +72,7 @@ fn bitmap4_mode( let mut count = 0; loop { - vblank.wait_for_VBlank(); + vblank.wait_for_vblank(); input.update(); if input.is_just_pressed(agb::input::Button::B) { diff --git a/agb/examples/output.rs b/agb/examples/output.rs index 4c8d91e3..888e3d66 100644 --- a/agb/examples/output.rs +++ b/agb/examples/output.rs @@ -6,11 +6,11 @@ extern crate agb; pub fn main() -> ! { let mut gba = agb::Gba::new(); - let vblank = gba.display.vblank.get(); + let vblank = agb::interrupt::VBlank::new(); let mut count = 0; loop { - vblank.wait_for_VBlank(); + vblank.wait_for_vblank(); agb::println!("Hello, world, frame = {}", count); diff --git a/agb/examples/wave.rs b/agb/examples/wave.rs new file mode 100644 index 00000000..ef665bd6 --- /dev/null +++ b/agb/examples/wave.rs @@ -0,0 +1,62 @@ +#![no_std] +#![no_main] + +extern crate agb; + +use agb::{ + display::{example_logo, tiled0::Background}, + interrupt::{Interrupt, Mutex}, + number::FixedNum, +}; + +struct BackCosines { + cosines: [u16; 32], + row: usize, +} + +#[no_mangle] +pub fn main() -> ! { + let mut gba = agb::Gba::new(); + let mut gfx = gba.display.video.tiled0(); + + gfx.set_background_palettes(example_logo::PALETTE_DATA); + gfx.set_background_tilemap(0, example_logo::TILE_DATA); + + let mut back = gfx.get_background().unwrap(); + + let mut entries: [u16; 30 * 20] = [0; 30 * 20]; + for tile_id in 0..(30 * 20) { + let palette_entry = example_logo::PALETTE_ASSIGNMENT[tile_id as usize] as u16; + entries[tile_id as usize] = tile_id | (palette_entry << 12); + } + + back.draw_full_map(&entries, (30_u32, 20_u32).into(), 0); + back.show(); + + let mut time = 0; + let cosines = [0_u16; 32]; + + let back = Mutex::new(BackCosines { cosines, row: 0 }); + + agb::add_interrupt_handler!(Interrupt::HBlank, || { + let mut backc = back.lock(); + let deflection = backc.cosines[backc.row % 32]; + unsafe { ((0x0400_0010) as *mut u16).write_volatile(deflection) } + backc.row += 1; + }); + + let vblank = agb::interrupt::VBlank::new(); + + loop { + vblank.wait_for_vblank(); + let mut backc = back.lock(); + backc.row = 0; + time += 1; + for (r, a) in backc.cosines.iter_mut().enumerate() { + let n: FixedNum<8> = (FixedNum::new(r as i32) / 32 + FixedNum::new(time) / 128).cos() + * (256 * 4 - 1) + / 256; + *a = (n.trunc() % (32 * 8)) as u16; + } + } +} diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index b3d68bad..6fcfb493 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -1,7 +1,6 @@ use crate::memory_mapped::MemoryMapped; use bitflags::bitflags; -use vblank::VBlankGiver; use video::Video; use self::object::ObjectControl; @@ -20,13 +19,11 @@ pub mod palette16; pub mod tile_data; /// Graphics mode 0. Four regular backgrounds. pub mod tiled0; -/// Syscall for waiting for vblank. -pub mod vblank; /// Giving out graphics mode. pub mod video; const DISPLAY_CONTROL: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0000) }; -const DISPLAY_STATUS: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0004) }; +pub(crate) const DISPLAY_STATUS: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0004) }; const VCOUNT: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0006) }; bitflags! { @@ -65,7 +62,6 @@ enum DisplayMode { /// Manages distribution of display modes, obtained from the gba struct pub struct Display { pub video: Video, - pub vblank: VBlankGiver, pub object: ObjectDistribution, } @@ -82,7 +78,6 @@ impl Display { pub(crate) const unsafe fn new() -> Self { Display { video: Video {}, - vblank: VBlankGiver {}, object: ObjectDistribution {}, } } diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index 91d998b7..0ee5e8d9 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -118,10 +118,10 @@ impl Background { } fn set_x(&self, x: u16) { - unsafe { *((0x0400_0010 + 4 * self.background as usize) as *mut u16) = x } + unsafe { ((0x0400_0010 + 4 * self.background as usize) as *mut u16).write_volatile(x) } } fn set_y(&self, y: u16) { - unsafe { *((0x0400_0012 + 4 * self.background as usize) as *mut u16) = y } + unsafe { ((0x0400_0012 + 4 * self.background as usize) as *mut u16).write_volatile(y) } } /// Forces the portion of the map in current view to be copied to the map @@ -189,6 +189,11 @@ impl Background { } } + pub fn set_offset(&mut self, position: Vector2D) { + self.set_x(position.x as u16); + self.set_y(position.y as u16); + } + /// Sets the position of the map to be shown on screen. This automatically /// manages copying the correct portion to the map block and moving the map /// registers. diff --git a/agb/src/display/vblank.rs b/agb/src/display/vblank.rs deleted file mode 100644 index 6a8bae03..00000000 --- a/agb/src/display/vblank.rs +++ /dev/null @@ -1,52 +0,0 @@ -use super::DISPLAY_STATUS; - -#[non_exhaustive] -pub struct VBlankGiver {} - -impl VBlankGiver { - /// Gets a vblank handle where only one can be obtained at a time - pub fn get(&mut self) -> VBlank { - unsafe { VBlank::new() } - } -} - -/// Once obtained, this guarentees that interrupts are enabled and set up to -/// allow for waiting for vblank -pub struct VBlank {} - -impl VBlank { - unsafe fn new() -> Self { - crate::interrupt::enable_interrupts(); - crate::interrupt::enable(crate::interrupt::Interrupt::VBlank); - enable_VBlank_interrupt(); - VBlank {} - } - - #[allow(non_snake_case)] - /// Waits for VBlank using interrupts. This is the preferred method for - /// waiting until the next frame. - pub fn wait_for_VBlank(&self) { - crate::syscall::wait_for_VBlank(); - } -} - -impl Drop for VBlank { - fn drop(&mut self) { - unsafe { - disable_VBlank_interrupt(); - crate::interrupt::disable(crate::interrupt::Interrupt::VBlank); - } - } -} - -#[allow(non_snake_case)] -unsafe fn enable_VBlank_interrupt() { - let status = DISPLAY_STATUS.get() | (1 << 3); - DISPLAY_STATUS.set(status); -} - -#[allow(non_snake_case)] -unsafe fn disable_VBlank_interrupt() { - let status = DISPLAY_STATUS.get() & !(1 << 3); - DISPLAY_STATUS.set(status); -} diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 353fd048..c68e34c5 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -5,9 +5,9 @@ use core::{ pin::Pin, }; -use crate::memory_mapped::MemoryMapped; +use crate::{display::DISPLAY_STATUS, memory_mapped::MemoryMapped}; -#[allow(dead_code)] +#[derive(Clone, Copy)] pub enum Interrupt { VBlank = 0, HBlank = 1, @@ -28,21 +28,47 @@ pub enum Interrupt { const ENABLED_INTERRUPTS: MemoryMapped = unsafe { MemoryMapped::new(0x04000200) }; const INTERRUPTS_ENABLED: MemoryMapped = unsafe { MemoryMapped::new(0x04000208) }; -pub(crate) fn enable(interrupt: Interrupt) { +fn enable(interrupt: Interrupt) { let _interrupt_token = temporary_interrupt_disable(); + other_things_to_enable_interrupt(interrupt); let interrupt = interrupt as usize; let enabled = ENABLED_INTERRUPTS.get() | (1 << (interrupt as u16)); ENABLED_INTERRUPTS.set(enabled); } -pub(crate) fn disable(interrupt: Interrupt) { +fn disable(interrupt: Interrupt) { let _interrupt_token = temporary_interrupt_disable(); + other_things_to_disable_interrupt(interrupt); let interrupt = interrupt as usize; let enabled = ENABLED_INTERRUPTS.get() & !(1 << (interrupt as u16)); ENABLED_INTERRUPTS.set(enabled); } -pub(crate) struct Disable {} +fn other_things_to_enable_interrupt(interrupt: Interrupt) { + match interrupt { + Interrupt::VBlank => { + DISPLAY_STATUS.set_bits(1, 1, 3); + } + Interrupt::HBlank => { + DISPLAY_STATUS.set_bits(1, 1, 4); + } + _ => {} + } +} + +fn other_things_to_disable_interrupt(interrupt: Interrupt) { + match interrupt { + Interrupt::VBlank => { + DISPLAY_STATUS.set_bits(0, 1, 3); + } + Interrupt::HBlank => { + DISPLAY_STATUS.set_bits(0, 1, 4); + } + _ => {} + } +} + +struct Disable {} impl Drop for Disable { fn drop(&mut self) { @@ -50,46 +76,66 @@ impl Drop for Disable { } } -pub(crate) fn temporary_interrupt_disable() -> Disable { +fn temporary_interrupt_disable() -> Disable { disable_interrupts(); Disable {} } -pub(crate) fn enable_interrupts() { +fn enable_interrupts() { INTERRUPTS_ENABLED.set(1); } -pub(crate) fn disable_interrupts() { +fn disable_interrupts() { INTERRUPTS_ENABLED.set(0); } pub(crate) struct InterruptRoot { next: Cell<*const InterruptClosure>, + count: Cell, + interrupt: Interrupt, } impl InterruptRoot { - const fn new() -> Self { + const fn new(interrupt: Interrupt) -> Self { InterruptRoot { next: Cell::new(core::ptr::null()), + count: Cell::new(0), + interrupt, } } + + fn reduce(&self) { + let new_count = self.count.get() - 1; + if new_count == 0 { + disable(self.interrupt); + } + self.count.set(new_count); + } + + fn add(&self) { + let count = self.count.get(); + if count == 0 { + enable(self.interrupt); + } + self.count.set(count + 1); + } } static mut INTERRUPT_TABLE: [InterruptRoot; 14] = [ - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), - InterruptRoot::new(), + InterruptRoot::new(Interrupt::VBlank), + InterruptRoot::new(Interrupt::HBlank), + InterruptRoot::new(Interrupt::VCounter), + InterruptRoot::new(Interrupt::Timer0), + InterruptRoot::new(Interrupt::Timer1), + InterruptRoot::new(Interrupt::Timer2), + InterruptRoot::new(Interrupt::Timer3), + InterruptRoot::new(Interrupt::Serial), + InterruptRoot::new(Interrupt::Dma0), + InterruptRoot::new(Interrupt::Dma1), + InterruptRoot::new(Interrupt::Dma2), + InterruptRoot::new(Interrupt::Dma3), + InterruptRoot::new(Interrupt::Keypad), + InterruptRoot::new(Interrupt::Gamepak), ]; #[no_mangle] @@ -127,7 +173,9 @@ impl InterruptRoot { impl Drop for InterruptClosure { fn drop(&mut self) { - let mut c = unsafe { &*self.root }.next.get(); + 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()); @@ -174,6 +222,7 @@ pub fn get_interrupt_handle( pub fn add_interrupt<'a>(interrupt: Pin<&'a InterruptClosureBounded<'a>>) { let root = unsafe { &*interrupt.c.root }; + root.add(); let mut c = root.next.get(); if c.is_null() { root.next.set((&interrupt.c) as *const _); @@ -201,17 +250,17 @@ macro_rules! add_interrupt_handler { } #[test_case] -fn test_vblank_interrupt_handler(gba: &mut crate::Gba) { +fn test_vblank_interrupt_handler(_gba: &mut crate::Gba) { { let counter = Mutex::new(0); let counter_2 = Mutex::new(0); add_interrupt_handler!(Interrupt::VBlank, || *counter.lock() += 1); add_interrupt_handler!(Interrupt::VBlank, || *counter_2.lock() += 1); - let vblank = gba.display.vblank.get(); + let vblank = VBlank::new(); while *counter.lock() < 100 || *counter_2.lock() < 100 { - vblank.wait_for_VBlank(); + vblank.wait_for_vblank(); } } @@ -299,3 +348,22 @@ impl<'a, T> DerefMut for MutexRef<'a, T> { unsafe { &mut *self.internal.get() } } } + +#[non_exhaustive] +pub struct VBlank {} + +impl VBlank { + pub fn new() -> Self { + interrupt_to_root(Interrupt::VBlank).add(); + VBlank {} + } + pub fn wait_for_vblank(&self) { + crate::syscall::wait_for_VBlank(); + } +} + +impl Drop for VBlank { + fn drop(&mut self) { + interrupt_to_root(Interrupt::VBlank).reduce(); + } +} diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 574b55f5..dfa25595 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -163,14 +163,14 @@ mod test { } #[test_case] - fn wait_30_frames(gba: &mut Gba) { - let vblank = gba.display.vblank.get(); + fn wait_30_frames(_gba: &mut Gba) { + let vblank = crate::interrupt::VBlank::new(); let mut counter = 0; loop { if counter > 30 { break; } - vblank.wait_for_VBlank(); + vblank.wait_for_vblank(); counter += 1 } } From 47f8ee5b700a70fe8cbc8a82031a9fc9f16ec097 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Mon, 28 Jun 2021 01:32:10 +0100 Subject: [PATCH 28/45] satisfy linter --- agb/examples/multiple_video.rs | 2 +- agb/examples/output.rs | 2 -- agb/examples/wave.rs | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/agb/examples/multiple_video.rs b/agb/examples/multiple_video.rs index c8b1a847..226c1afe 100644 --- a/agb/examples/multiple_video.rs +++ b/agb/examples/multiple_video.rs @@ -13,7 +13,7 @@ struct Vector2D { #[no_mangle] pub fn main() -> ! { let mut gba = agb::Gba::new(); - let mut vblank = agb::interrupt::VBlank::new(); + let vblank = agb::interrupt::VBlank::new(); let mut input = agb::input::ButtonController::new(); loop { diff --git a/agb/examples/output.rs b/agb/examples/output.rs index 888e3d66..6eb91406 100644 --- a/agb/examples/output.rs +++ b/agb/examples/output.rs @@ -4,8 +4,6 @@ extern crate agb; #[no_mangle] pub fn main() -> ! { - let mut gba = agb::Gba::new(); - let vblank = agb::interrupt::VBlank::new(); let mut count = 0; diff --git a/agb/examples/wave.rs b/agb/examples/wave.rs index ef665bd6..9c45ff4b 100644 --- a/agb/examples/wave.rs +++ b/agb/examples/wave.rs @@ -4,7 +4,7 @@ extern crate agb; use agb::{ - display::{example_logo, tiled0::Background}, + display::example_logo, interrupt::{Interrupt, Mutex}, number::FixedNum, }; From 37c29b7ec3c198a05f5e6fd3d7c805e9211d4e43 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 3 Jul 2021 18:41:59 +0100 Subject: [PATCH 29/45] should enable interrupts by writing a 1 --- agb/interrupt_handler.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/interrupt_handler.s b/agb/interrupt_handler.s index 9f062bc3..da041afd 100644 --- a/agb/interrupt_handler.s +++ b/agb/interrupt_handler.s @@ -40,7 +40,7 @@ InterruptHandler: pop {r2} - mov r1, #0 + mov r1, #1 strh r1, [r2, #8] bx lr @ return to bios From 7b46bc9703ddbc5037ec7cb127264ce69a7e9dbe Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 3 Jul 2021 18:57:17 +0100 Subject: [PATCH 30/45] change name to "get" --- agb/examples/bitmap3.rs | 2 +- agb/examples/bitmap4.rs | 2 +- agb/examples/chicken.rs | 2 +- agb/examples/mixer_basic.rs | 2 +- agb/examples/multiple_video.rs | 2 +- agb/examples/output.rs | 2 +- agb/examples/wave.rs | 2 +- agb/src/interrupt.rs | 4 ++-- agb/src/lib.rs | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/agb/examples/bitmap3.rs b/agb/examples/bitmap3.rs index 5c45544c..06fb89c6 100644 --- a/agb/examples/bitmap3.rs +++ b/agb/examples/bitmap3.rs @@ -14,7 +14,7 @@ struct Vector2D { pub fn main() -> ! { let mut gba = agb::Gba::new(); let mut bitmap = gba.display.video.bitmap3(); - let vblank = agb::interrupt::VBlank::new(); + let vblank = agb::interrupt::VBlank::get(); let mut input = agb::input::ButtonController::new(); let mut pos = Vector2D { diff --git a/agb/examples/bitmap4.rs b/agb/examples/bitmap4.rs index 4c40fafc..b097e688 100644 --- a/agb/examples/bitmap4.rs +++ b/agb/examples/bitmap4.rs @@ -9,7 +9,7 @@ use agb::display; pub fn main() -> ! { let mut gba = agb::Gba::new(); let mut bitmap = gba.display.video.bitmap4(); - let vblank = agb::interrupt::VBlank::new(); + let vblank = agb::interrupt::VBlank::get(); bitmap.set_palette_entry(1, 0x001F); bitmap.set_palette_entry(2, 0x03E0); diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 47f531f0..8650730d 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -45,7 +45,7 @@ pub fn main() -> ! { let mut gba = agb::Gba::new(); let mut gfx = gba.display.video.tiled0(); - let vblank = agb::interrupt::VBlank::new(); + let vblank = agb::interrupt::VBlank::get(); let mut input = agb::input::ButtonController::new(); gfx.set_sprite_palette_raw(&CHICKEN_PALETTE); diff --git a/agb/examples/mixer_basic.rs b/agb/examples/mixer_basic.rs index a5bc18d7..b26deda6 100644 --- a/agb/examples/mixer_basic.rs +++ b/agb/examples/mixer_basic.rs @@ -15,7 +15,7 @@ const I_WILL_NOT_LET_YOU_LET_ME_DOWN: &[u8] = include_bytes!("i-will-not-let-you pub fn main() -> ! { let mut gba = Gba::new(); let mut input = ButtonController::new(); - let vblank_provider = agb::interrupt::VBlank::new(); + let vblank_provider = agb::interrupt::VBlank::get(); let mut mixer = gba.mixer.mixer(); mixer.enable(); diff --git a/agb/examples/multiple_video.rs b/agb/examples/multiple_video.rs index 226c1afe..174241e1 100644 --- a/agb/examples/multiple_video.rs +++ b/agb/examples/multiple_video.rs @@ -13,7 +13,7 @@ struct Vector2D { #[no_mangle] pub fn main() -> ! { let mut gba = agb::Gba::new(); - let vblank = agb::interrupt::VBlank::new(); + let vblank = agb::interrupt::VBlank::get(); let mut input = agb::input::ButtonController::new(); loop { diff --git a/agb/examples/output.rs b/agb/examples/output.rs index 6eb91406..01702656 100644 --- a/agb/examples/output.rs +++ b/agb/examples/output.rs @@ -4,7 +4,7 @@ extern crate agb; #[no_mangle] pub fn main() -> ! { - let vblank = agb::interrupt::VBlank::new(); + let vblank = agb::interrupt::VBlank::get(); let mut count = 0; loop { diff --git a/agb/examples/wave.rs b/agb/examples/wave.rs index 9c45ff4b..3ebec1aa 100644 --- a/agb/examples/wave.rs +++ b/agb/examples/wave.rs @@ -45,7 +45,7 @@ pub fn main() -> ! { backc.row += 1; }); - let vblank = agb::interrupt::VBlank::new(); + let vblank = agb::interrupt::VBlank::get(); loop { vblank.wait_for_vblank(); diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index c68e34c5..db91d961 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -257,7 +257,7 @@ fn test_vblank_interrupt_handler(_gba: &mut crate::Gba) { add_interrupt_handler!(Interrupt::VBlank, || *counter.lock() += 1); add_interrupt_handler!(Interrupt::VBlank, || *counter_2.lock() += 1); - let vblank = VBlank::new(); + let vblank = VBlank::get(); while *counter.lock() < 100 || *counter_2.lock() < 100 { vblank.wait_for_vblank(); @@ -353,7 +353,7 @@ impl<'a, T> DerefMut for MutexRef<'a, T> { pub struct VBlank {} impl VBlank { - pub fn new() -> Self { + pub fn get() -> Self { interrupt_to_root(Interrupt::VBlank).add(); VBlank {} } diff --git a/agb/src/lib.rs b/agb/src/lib.rs index dfa25595..618021a7 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -164,7 +164,7 @@ mod test { #[test_case] fn wait_30_frames(_gba: &mut Gba) { - let vblank = crate::interrupt::VBlank::new(); + let vblank = crate::interrupt::VBlank::get(); let mut counter = 0; loop { if counter > 30 { From 3b26c0b2626f6d8c53c3a51d9f9a83a11d88bc17 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 3 Jul 2021 20:42:12 +0100 Subject: [PATCH 31/45] acknowledge requests after handling them --- agb/interrupt_handler.s | 48 ++++++++++++++++++++--------------------- agb/src/interrupt.rs | 4 +++- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/agb/interrupt_handler.s b/agb/interrupt_handler.s index da041afd..a68814e8 100644 --- a/agb/interrupt_handler.s +++ b/agb/interrupt_handler.s @@ -4,16 +4,36 @@ .section .iwram, "ax" .align InterruptHandler: - ldr r2, =0x04000200 @ interrupt enable register location + mov r2, #0x04000000 @ interrupt enable register location + add r2, #0x200 mov r1, #0 strh r1, [r2, #8] - push {r2} - ldrh r1, [r2] @ load 16 bit interrupt enable to r1 ldrh r3, [r2, #2] @ load 16 bit interrupt request to r3 and r0, r1, r3 @ interrupts both enabled and requested + + @ change to system mode + mrs r1, cpsr + orr r1, r1, #0xD + msr cpsr_c, r1 + + @ call the rust interrupt handler with r0 set to the triggered interrupts + ldr r1, =__RUST_INTERRUPT_HANDLER + push {lr, r2} + mov lr, pc + bx r1 + pop {lr, r2} + + @ change back to interrupt mode + mrs r1, cpsr + bic r1, r1, #0xD + msr cpsr_c, r1 + + mov r1, #1 + strh r1, [r2, #8] + strh r0, [r2, #2] @ store to interrupt request ldr r2, =0x03007FF8 @ load bios interrupt request location @@ -21,27 +41,5 @@ InterruptHandler: orr r1, r1, r0 @ or with enabled and requested interrupts strh r1, [r2] @ acknowlege bios requests - @ change to system mode - mrs r2, cpsr - orr r2, r2, #0xD - msr cpsr_c, r2 - - @ call the rust interrupt handler with r0 set to the triggered interrupts - ldr r1, =__RUST_INTERRUPT_HANDLER - push {lr} - mov lr, pc - bx r1 - pop {lr} - - @ change back to interrupt mode - mrs r2, cpsr - bic r2, r2, #0xD - msr cpsr_c, r2 - - pop {r2} - - mov r1, #1 - strh r1, [r2, #8] - bx lr @ return to bios .pool diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index db91d961..be05baac 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -139,12 +139,14 @@ static mut INTERRUPT_TABLE: [InterruptRoot; 14] = [ ]; #[no_mangle] -extern "C" fn __RUST_INTERRUPT_HANDLER(interrupt: u16) { +extern "C" fn __RUST_INTERRUPT_HANDLER(interrupt: u16) -> u16 { for (i, root) in unsafe { INTERRUPT_TABLE.iter().enumerate() } { if (1 << i) & interrupt != 0 { root.trigger_interrupts(); } } + + interrupt } pub struct InterruptClosureBounded<'a> { From 4467d0cf182cff6708cfc1b886542341bf6adf4d Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Wed, 28 Jul 2021 20:50:56 +0100 Subject: [PATCH 32/45] fix incorrect label from rebase --- agb/crt0.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/crt0.s b/agb/crt0.s index 26c2236b..6696eca6 100644 --- a/agb/crt0.s +++ b/agb/crt0.s @@ -26,7 +26,7 @@ b .Initialise_mb .Initialise: @ Set interrupt handler - ldr r0, =InterruptHandlerSimple + ldr r0, =InterruptHandler ldr r1, =0x03007FFC str r0, [r1] From 013522f642271fd4e013a502aff7aee2ea5c2b8d Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Thu, 29 Jul 2021 17:48:13 +0100 Subject: [PATCH 33/45] setting lto to thin works? --- agb/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 37f4da0e..d375d56e 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -9,9 +9,11 @@ license = "MPL-2.0" [profile.dev] opt-level = 3 debug = true +lto = "thin" [profile.release] lto = true +debug = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 00a5b2487c032d080106c0ded7ef0daf840456ed Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 1 Aug 2021 00:19:56 +0100 Subject: [PATCH 34/45] change to using extern in linker --- agb/Cargo.toml | 1 - agb/gba.ld | 1 + agb/gba_mb.ld | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/agb/Cargo.toml b/agb/Cargo.toml index d375d56e..01164dce 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -9,7 +9,6 @@ license = "MPL-2.0" [profile.dev] opt-level = 3 debug = true -lto = "thin" [profile.release] lto = true diff --git a/agb/gba.ld b/agb/gba.ld index 7e2907be..ba54bc5a 100644 --- a/agb/gba.ld +++ b/agb/gba.ld @@ -2,6 +2,7 @@ OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(__start) +EXTERN(__RUST_INTERRUPT_HANDLER) MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K diff --git a/agb/gba_mb.ld b/agb/gba_mb.ld index df1a784a..9fdd23c1 100644 --- a/agb/gba_mb.ld +++ b/agb/gba_mb.ld @@ -2,6 +2,7 @@ OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(__start) +EXTERN(__RUST_INTERRUPT_HANDLER) MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K From 2e670c5d4ec5eeafb29225b1667e12455d4b087d Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 1 Aug 2021 00:20:58 +0100 Subject: [PATCH 35/45] template consistency --- template/gba.ld | 1 + template/gba_mb.ld | 1 + 2 files changed, 2 insertions(+) diff --git a/template/gba.ld b/template/gba.ld index 7e2907be..ba54bc5a 100644 --- a/template/gba.ld +++ b/template/gba.ld @@ -2,6 +2,7 @@ OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(__start) +EXTERN(__RUST_INTERRUPT_HANDLER) MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K diff --git a/template/gba_mb.ld b/template/gba_mb.ld index df1a784a..9fdd23c1 100644 --- a/template/gba_mb.ld +++ b/template/gba_mb.ld @@ -2,6 +2,7 @@ OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(__start) +EXTERN(__RUST_INTERRUPT_HANDLER) MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K From 1b971b899328eb9a29491d0c229aefd8c66046f2 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Fri, 6 Aug 2021 23:12:55 +0100 Subject: [PATCH 36/45] add lock with key --- agb/examples/wave.rs | 2 +- agb/src/interrupt.rs | 29 +++++++++++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/agb/examples/wave.rs b/agb/examples/wave.rs index 3ebec1aa..cd990a4c 100644 --- a/agb/examples/wave.rs +++ b/agb/examples/wave.rs @@ -38,7 +38,7 @@ pub fn main() -> ! { let back = Mutex::new(BackCosines { cosines, row: 0 }); - agb::add_interrupt_handler!(Interrupt::HBlank, || { + agb::add_interrupt_handler!(Interrupt::HBlank, |_| { let mut backc = back.lock(); let deflection = backc.cosines[backc.row % 32]; unsafe { ((0x0400_0010) as *mut u16).write_volatile(deflection) } diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index be05baac..08caeca1 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -156,7 +156,7 @@ pub struct InterruptClosureBounded<'a> { } struct InterruptClosure { - closure: *const (dyn Fn()), + closure: *const (dyn Fn(Key)), next: Cell<*const InterruptClosure>, root: *const InterruptRoot, } @@ -167,7 +167,7 @@ impl InterruptRoot { while !c.is_null() { let closure_ptr = unsafe { &*c }.closure; let closure_ref = unsafe { &*closure_ptr }; - closure_ref(); + closure_ref(Key()); c = unsafe { &*c }.next.get(); } } @@ -199,7 +199,7 @@ fn interrupt_to_root(interrupt: Interrupt) -> &'static InterruptRoot { } fn get_interrupt_handle_root<'a>( - f: &'a dyn Fn(), + f: &'a dyn Fn(Key), root: &InterruptRoot, ) -> InterruptClosureBounded<'a> { InterruptClosureBounded { @@ -214,7 +214,7 @@ fn get_interrupt_handle_root<'a>( } pub fn get_interrupt_handle( - f: &(dyn Fn() + Send + Sync), + f: &(dyn Fn(Key) + Send + Sync), interrupt: Interrupt, ) -> InterruptClosureBounded { let root = interrupt_to_root(interrupt); @@ -256,8 +256,8 @@ fn test_vblank_interrupt_handler(_gba: &mut crate::Gba) { { let counter = Mutex::new(0); let counter_2 = Mutex::new(0); - add_interrupt_handler!(Interrupt::VBlank, || *counter.lock() += 1); - add_interrupt_handler!(Interrupt::VBlank, || *counter_2.lock() += 1); + add_interrupt_handler!(Interrupt::VBlank, |key| *counter.lock_with_key(&key) += 1); + add_interrupt_handler!(Interrupt::VBlank, |_| *counter_2.lock() += 1); let vblank = VBlank::get(); @@ -293,6 +293,9 @@ pub struct Mutex { state: UnsafeCell, } +#[non_exhaustive] +pub struct Key(); + unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} @@ -311,6 +314,20 @@ impl Mutex { state: &self.state, } } + + pub fn lock_with_key(&self, _key: &Key) -> MutexRef { + assert_eq!( + unsafe { *self.state.get() }, + MutexState::Unlocked, + "mutex must be unlocked to be able to lock it" + ); + unsafe { *self.state.get() = MutexState::Locked(false) }; + MutexRef { + internal: &self.internal, + state: &self.state, + } + } + pub fn new(val: T) -> Self { Mutex { internal: UnsafeCell::new(val), From da6ff40a32c9d51fd57460e2e356e6b56bf86f14 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Fri, 6 Aug 2021 23:21:50 +0100 Subject: [PATCH 37/45] add docs --- agb/src/interrupt.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 08caeca1..8e1b7549 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -242,6 +242,18 @@ pub fn add_interrupt<'a>(interrupt: Pin<&'a InterruptClosureBounded<'a>>) { } #[macro_export] +/// 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; From 99f992353a02e3e1890ed52f668a7311378ea396 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Fri, 6 Aug 2021 23:26:51 +0100 Subject: [PATCH 38/45] fix wave example --- agb/examples/wave.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/agb/examples/wave.rs b/agb/examples/wave.rs index cd990a4c..c25f3260 100644 --- a/agb/examples/wave.rs +++ b/agb/examples/wave.rs @@ -19,19 +19,7 @@ pub fn main() -> ! { let mut gba = agb::Gba::new(); let mut gfx = gba.display.video.tiled0(); - gfx.set_background_palettes(example_logo::PALETTE_DATA); - gfx.set_background_tilemap(0, example_logo::TILE_DATA); - - let mut back = gfx.get_background().unwrap(); - - let mut entries: [u16; 30 * 20] = [0; 30 * 20]; - for tile_id in 0..(30 * 20) { - let palette_entry = example_logo::PALETTE_ASSIGNMENT[tile_id as usize] as u16; - entries[tile_id as usize] = tile_id | (palette_entry << 12); - } - - back.draw_full_map(&entries, (30_u32, 20_u32).into(), 0); - back.show(); + example_logo::display_logo(&mut gfx); let mut time = 0; let cosines = [0_u16; 32]; From 476b030b41e9820b2a9e9818e004573bdb08af4b Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Fri, 6 Aug 2021 23:28:19 +0100 Subject: [PATCH 39/45] rename wait for vblank functions --- agb/src/display/mod.rs | 2 +- agb/src/interrupt.rs | 2 +- agb/src/lib.rs | 6 +++--- agb/src/syscall.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index 6fcfb493..a05b137f 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -104,7 +104,7 @@ unsafe fn set_graphics_settings(settings: GraphicsSettings) { /// Waits until vblank using a busy wait loop, this should almost never be used. /// I only say almost because whilst I don't believe there to be a reason to use /// this I can't rule it out. -pub fn busy_wait_for_VBlank() { +pub fn busy_wait_for_vblank() { while VCOUNT.get() >= 160 {} while VCOUNT.get() < 160 {} } diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 8e1b7549..3965e43b 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -389,7 +389,7 @@ impl VBlank { VBlank {} } pub fn wait_for_vblank(&self) { - crate::syscall::wait_for_VBlank(); + crate::syscall::wait_for_vblank(); } } diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 618021a7..ae534632 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -142,15 +142,15 @@ pub extern "C" fn main() -> ! { #[cfg(test)] fn assert_image_output(image: &str) { - display::busy_wait_for_VBlank(); - display::busy_wait_for_VBlank(); + display::busy_wait_for_vblank(); + display::busy_wait_for_vblank(); let mut mgba = crate::mgba::Mgba::new().unwrap(); mgba.print( format_args!("image:{}", image), crate::mgba::DebugLevel::Info, ) .unwrap(); - display::busy_wait_for_VBlank(); + display::busy_wait_for_vblank(); } #[cfg(test)] diff --git a/agb/src/syscall.rs b/agb/src/syscall.rs index b0496ca7..b4a912e5 100644 --- a/agb/src/syscall.rs +++ b/agb/src/syscall.rs @@ -40,7 +40,7 @@ pub fn wait_for_interrupt() { } #[allow(non_snake_case)] -pub fn wait_for_VBlank() { +pub fn wait_for_vblank() { unsafe { asm!( "swi 0x05", From 509388461246213c081c72250c98648a292d9395 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Fri, 6 Aug 2021 23:32:06 +0100 Subject: [PATCH 40/45] move enable and disable to impl --- agb/src/interrupt.rs | 86 ++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 3965e43b..b138d716 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -25,49 +25,51 @@ pub enum Interrupt { Gamepak = 13, } +impl Interrupt { + fn enable(self) { + let _interrupt_token = temporary_interrupt_disable(); + self.other_things_to_enable_interrupt(); + let interrupt = self as usize; + let enabled = ENABLED_INTERRUPTS.get() | (1 << (interrupt as u16)); + ENABLED_INTERRUPTS.set(enabled); + } + + fn disable(self) { + let _interrupt_token = temporary_interrupt_disable(); + self.other_things_to_disable_interrupt(); + let interrupt = self as usize; + let enabled = ENABLED_INTERRUPTS.get() & !(1 << (interrupt as u16)); + ENABLED_INTERRUPTS.set(enabled); + } + + fn other_things_to_enable_interrupt(self) { + match self { + Interrupt::VBlank => { + DISPLAY_STATUS.set_bits(1, 1, 3); + } + Interrupt::HBlank => { + DISPLAY_STATUS.set_bits(1, 1, 4); + } + _ => {} + } + } + + fn other_things_to_disable_interrupt(self) { + match self { + Interrupt::VBlank => { + DISPLAY_STATUS.set_bits(0, 1, 3); + } + Interrupt::HBlank => { + DISPLAY_STATUS.set_bits(0, 1, 4); + } + _ => {} + } + } +} + const ENABLED_INTERRUPTS: MemoryMapped = unsafe { MemoryMapped::new(0x04000200) }; const INTERRUPTS_ENABLED: MemoryMapped = unsafe { MemoryMapped::new(0x04000208) }; -fn enable(interrupt: Interrupt) { - let _interrupt_token = temporary_interrupt_disable(); - other_things_to_enable_interrupt(interrupt); - let interrupt = interrupt as usize; - let enabled = ENABLED_INTERRUPTS.get() | (1 << (interrupt as u16)); - ENABLED_INTERRUPTS.set(enabled); -} - -fn disable(interrupt: Interrupt) { - let _interrupt_token = temporary_interrupt_disable(); - other_things_to_disable_interrupt(interrupt); - let interrupt = interrupt as usize; - let enabled = ENABLED_INTERRUPTS.get() & !(1 << (interrupt as u16)); - ENABLED_INTERRUPTS.set(enabled); -} - -fn other_things_to_enable_interrupt(interrupt: Interrupt) { - match interrupt { - Interrupt::VBlank => { - DISPLAY_STATUS.set_bits(1, 1, 3); - } - Interrupt::HBlank => { - DISPLAY_STATUS.set_bits(1, 1, 4); - } - _ => {} - } -} - -fn other_things_to_disable_interrupt(interrupt: Interrupt) { - match interrupt { - Interrupt::VBlank => { - DISPLAY_STATUS.set_bits(0, 1, 3); - } - Interrupt::HBlank => { - DISPLAY_STATUS.set_bits(0, 1, 4); - } - _ => {} - } -} - struct Disable {} impl Drop for Disable { @@ -107,7 +109,7 @@ impl InterruptRoot { fn reduce(&self) { let new_count = self.count.get() - 1; if new_count == 0 { - disable(self.interrupt); + self.interrupt.disable(); } self.count.set(new_count); } @@ -115,7 +117,7 @@ impl InterruptRoot { fn add(&self) { let count = self.count.get(); if count == 0 { - enable(self.interrupt); + self.interrupt.enable(); } self.count.set(count + 1); } From c60eaaaff00c06cb3187290c58b69ea9c1fc159a Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 13:03:11 +0100 Subject: [PATCH 41/45] doesn't need to be public --- agb/src/interrupt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index b138d716..adab80b4 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -91,7 +91,7 @@ fn disable_interrupts() { INTERRUPTS_ENABLED.set(0); } -pub(crate) struct InterruptRoot { +struct InterruptRoot { next: Cell<*const InterruptClosure>, count: Cell, interrupt: Interrupt, From b559b0eb54cc67d8ba8f679d5590edcdaeb1d58b Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 13:10:51 +0100 Subject: [PATCH 42/45] add some docs --- agb/src/interrupt.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index adab80b4..c8dc7fb0 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -215,6 +215,8 @@ fn get_interrupt_handle_root<'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(Key) + Send + Sync), interrupt: Interrupt, @@ -224,6 +226,9 @@ pub fn get_interrupt_handle( get_interrupt_handle_root(f, root) } +/// 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>>) { let root = unsafe { &*interrupt.c.root }; root.add(); From fa250cee60bee97963bcf189e494ff10dadd4efa Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 13:53:59 +0100 Subject: [PATCH 43/45] remove allow warning --- agb/src/syscall.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/agb/src/syscall.rs b/agb/src/syscall.rs index b4a912e5..17df4bf3 100644 --- a/agb/src/syscall.rs +++ b/agb/src/syscall.rs @@ -39,7 +39,6 @@ pub fn wait_for_interrupt() { } } -#[allow(non_snake_case)] pub fn wait_for_vblank() { unsafe { asm!( From 477b0465fbf91516f68b206d28258cdcfa32a3ea Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 13:57:50 +0100 Subject: [PATCH 44/45] add docs for vblank interrupt --- agb/src/interrupt.rs | 4 ++++ agb/src/syscall.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index c8dc7fb0..21e65016 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -391,10 +391,14 @@ impl<'a, T> DerefMut for MutexRef<'a, T> { pub struct VBlank {} impl VBlank { + /// Handles setting up everything reqired to be able to use the wait for + /// interrupt syscall. pub fn get() -> Self { interrupt_to_root(Interrupt::VBlank).add(); VBlank {} } + /// Pauses CPU until vblank interrupt is triggered where code execution is + /// resumed. pub fn wait_for_vblank(&self) { crate::syscall::wait_for_vblank(); } diff --git a/agb/src/syscall.rs b/agb/src/syscall.rs index 17df4bf3..f3869a1e 100644 --- a/agb/src/syscall.rs +++ b/agb/src/syscall.rs @@ -39,6 +39,8 @@ pub fn wait_for_interrupt() { } } +/// The vblank interrupt handler [VBlank][crate::interrupt::VBlank] should be +/// used instead of calling this function directly. pub fn wait_for_vblank() { unsafe { asm!( From ff51341679d90d47fcd4ffd5c0cfa3acb1816cbb Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 14:16:41 +0100 Subject: [PATCH 45/45] print in interrupt --- agb/examples/output.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/agb/examples/output.rs b/agb/examples/output.rs index 01702656..747c806e 100644 --- a/agb/examples/output.rs +++ b/agb/examples/output.rs @@ -4,14 +4,11 @@ extern crate agb; #[no_mangle] pub fn main() -> ! { - let vblank = agb::interrupt::VBlank::get(); - - let mut count = 0; - loop { - vblank.wait_for_vblank(); - - agb::println!("Hello, world, frame = {}", count); - - count += 1; - } + let count = agb::interrupt::Mutex::new(0); + agb::add_interrupt_handler!(agb::interrupt::Interrupt::VBlank, |key| { + let mut count = count.lock_with_key(&key); + agb::println!("Hello, world, frame = {}", *count); + *count += 1; + }); + loop {} }