From f8fb6ba7b4858a0d13eef3effa987dfc59bdaf86 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Mon, 28 Jun 2021 01:03:08 +0100 Subject: [PATCH] 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 } }