From e50540752ca148ec9e59be8ff8dc1951e48279c5 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 12 Jan 2023 21:20:54 +0000 Subject: [PATCH 1/4] Make missing a vblank interrupt wait less painful --- agb/src/interrupt.rs | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index f728f7e4..454a20d9 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -7,7 +7,7 @@ use core::{ use alloc::boxed::Box; use bare_metal::CriticalSection; -use crate::{display::DISPLAY_STATUS, memory_mapped::MemoryMapped}; +use crate::{display::DISPLAY_STATUS, memory_mapped::MemoryMapped, sync::Static}; #[derive(Clone, Copy)] pub enum Interrupt { @@ -308,27 +308,43 @@ where r } +static NUM_VBLANKS: Static = Static::new(0); // overflows after 2.27 years +static HAS_CREATED_INTERRUPT: Static = Static::new(false); + #[non_exhaustive] -pub struct VBlank {} +pub struct VBlank { + last_waited_number: Cell, +} impl VBlank { /// Handles setting up everything required to be able to use the wait for /// interrupt syscall. #[must_use] pub fn get() -> Self { - interrupt_to_root(Interrupt::VBlank).add(); - VBlank {} + if !HAS_CREATED_INTERRUPT.read() { + let handler = add_interrupt_handler(Interrupt::VBlank, |_| { + NUM_VBLANKS.write(NUM_VBLANKS.read() + 1); + }); + core::mem::forget(handler); + + HAS_CREATED_INTERRUPT.write(true); + } + + VBlank { + last_waited_number: Cell::new(NUM_VBLANKS.read()), + } } /// Pauses CPU until vblank interrupt is triggered where code execution is /// resumed. pub fn wait_for_vblank(&self) { - crate::syscall::wait_for_vblank(); - } -} + let last_waited_number = self.last_waited_number.get(); + self.last_waited_number.set(NUM_VBLANKS.read() + 1); -impl Drop for VBlank { - fn drop(&mut self) { - interrupt_to_root(Interrupt::VBlank).reduce(); + if last_waited_number < NUM_VBLANKS.read() { + return; + } + + crate::syscall::wait_for_vblank(); } } From ba8655070d7a733175f88181f0fb9c1928c6d69a Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 12 Jan 2023 21:24:28 +0000 Subject: [PATCH 2/4] Add a changelog entry for the vblank change --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11497da3..564ea13e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Text renderer can now be re-used which is useful for rpg style character/word at a time text boxes. +- If a vblank happens outside of `wait_for_vblank`, then next call will immediately return. ## [0.12.2] - 2022/10/22 From 827afd87b98d8a0ef65d1add20e3eae076c35b11 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 12 Jan 2023 21:24:37 +0000 Subject: [PATCH 3/4] Make wait_for_vblank syscall pub(crate) --- agb/src/syscall.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/syscall.rs b/agb/src/syscall.rs index 5d8f66a2..059bb66c 100644 --- a/agb/src/syscall.rs +++ b/agb/src/syscall.rs @@ -56,7 +56,7 @@ 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() { +pub(crate) fn wait_for_vblank() { unsafe { asm!( "swi {SWI}", From 009de4e660a83353e39067b9f6efd2953f8161e5 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 12 Jan 2023 22:02:07 +0000 Subject: [PATCH 4/4] Update test to be more relevant --- agb/src/interrupt.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 454a20d9..68aa0789 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -355,10 +355,13 @@ mod tests { use core::cell::RefCell; #[test_case] - fn test_vblank_interrupt_handler(_gba: &mut crate::Gba) { + fn test_can_create_and_destroy_interrupt_handlers(_gba: &mut crate::Gba) { + let mut counter = Mutex::new(RefCell::new(0)); + let counter_2 = Mutex::new(RefCell::new(0)); + + let vblank = VBlank::get(); + { - let counter = Mutex::new(RefCell::new(0)); - let counter_2 = Mutex::new(RefCell::new(0)); let _a = add_interrupt_handler(Interrupt::VBlank, |key: CriticalSection| { *counter.borrow(key).borrow_mut() += 1; }); @@ -366,8 +369,6 @@ mod tests { *counter_2.borrow(key).borrow_mut() += 1; }); - let vblank = VBlank::get(); - while free(|key| { *counter.borrow(key).borrow() < 100 || *counter_2.borrow(key).borrow() < 100 }) { @@ -375,11 +376,10 @@ mod tests { } } - assert_eq!( - interrupt_to_root(Interrupt::VBlank).next.get(), - core::ptr::null(), - "expected the interrupt table for vblank to be empty" - ); + vblank.wait_for_vblank(); + vblank.wait_for_vblank(); + + assert_eq!(*counter.get_mut().get_mut(), 100); } #[test_case]