From fd610d8cc1accb7c59984b731845cdddea8d8e80 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 18 Nov 2021 22:06:57 +0000 Subject: [PATCH] Add support for timers --- agb/examples/stereo_sound.rs | 31 ++++++++++- agb/src/lib.rs | 4 ++ agb/src/timer.rs | 105 +++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 agb/src/timer.rs diff --git a/agb/examples/stereo_sound.rs b/agb/examples/stereo_sound.rs index b7dca810..b300029f 100644 --- a/agb/examples/stereo_sound.rs +++ b/agb/examples/stereo_sound.rs @@ -4,7 +4,7 @@ extern crate agb; use agb::sound::mixer::SoundChannel; -use agb::{include_wav, Gba}; +use agb::{include_wav, timer::Timer, Gba}; // Music - "Let it in" by Josh Woodward, free download at http://joshwoodward.com const LET_IT_IN: &[u8] = include_wav!("examples/JoshWoodward-LetItIn.wav"); @@ -14,6 +14,12 @@ fn main() -> ! { let mut gba = Gba::new(); let vblank_provider = agb::interrupt::VBlank::get(); + let mut timer_controller = gba.timer; + timer_controller.set_overflow_amount(Timer::Timer1, u16::MAX); + timer_controller.set_enabled(Timer::Timer1, true); + timer_controller.set_overflow_amount(Timer::Timer2, u16::MAX); + timer_controller.set_cascade(Timer::Timer2, true); + let mut mixer = gba.mixer.mixer(); mixer.enable(); @@ -21,8 +27,31 @@ fn main() -> ! { channel.stereo(); mixer.play_sound(channel).unwrap(); + let mut frame_counter = 0i32; loop { vblank_provider.wait_for_vblank(); + let before_mixing_cycles_lo = timer_controller.get_value(Timer::Timer1); + let before_mixing_cycles_hi = timer_controller.get_value(Timer::Timer2); mixer.vblank(); + let after_mixing_cycles_lo = timer_controller.get_value(Timer::Timer1); + let after_mixing_cycles_hi = timer_controller.get_value(Timer::Timer2); + + frame_counter = frame_counter.wrapping_add(1); + + if frame_counter % 128 == 0 { + let before_mixing_cycles = + ((before_mixing_cycles_hi as u32) << 16) | before_mixing_cycles_lo as u32; + let after_mixing_cycles = + ((after_mixing_cycles_hi as u32) << 16) | after_mixing_cycles_lo as u32; + + let total_cycles = after_mixing_cycles.wrapping_sub(before_mixing_cycles); + + let percent = (total_cycles * 100) / 280896; + agb::println!( + "Took {} cycles to calculate mixer ~= {}% of total frame", + total_cycles, + percent + ); + } } } diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 4dc39172..a3831b2e 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -39,6 +39,8 @@ mod single; pub mod sound; /// System BIOS calls / syscalls. pub mod syscall; +/// Interactions with the internal timers +pub mod timer; #[cfg(not(test))] use core::fmt::Write; @@ -61,6 +63,7 @@ pub struct Gba { pub display: display::Display, pub sound: sound::dmg::Sound, pub mixer: sound::mixer::MixerController, + pub timer: timer::TimerController, } impl Gba { @@ -73,6 +76,7 @@ impl Gba { display: display::Display::new(), sound: sound::dmg::Sound::new(), mixer: sound::mixer::MixerController::new(), + timer: timer::TimerController::new(), } } } diff --git a/agb/src/timer.rs b/agb/src/timer.rs new file mode 100644 index 00000000..26f820d4 --- /dev/null +++ b/agb/src/timer.rs @@ -0,0 +1,105 @@ +use crate::memory_mapped::MemoryMapped; + +const fn timer_data(timer: usize) -> MemoryMapped { + unsafe { MemoryMapped::new(0x0400_0100 + 4 * timer) } +} + +const fn timer_control(timer: usize) -> MemoryMapped { + unsafe { MemoryMapped::new(0x0400_0102 + 4 * timer) } +} + +#[derive(Clone, Copy)] +pub enum Timer { + Timer0, + Timer1, + Timer2, + Timer3, +} + +#[derive(Clone, Copy)] +pub enum Divider { + // 16.78MHz or 59.59ns + Divider1, + // 262.21kHz or 3.815us + Divider64, + // 65.536kHz or 15.26us + Divider256, + // 16.384kHz or 61.04us + Divider1024, +} + +impl Divider { + fn get_as_bits(&self) -> u16 { + use Divider::*; + + match self { + Divider1 => 0, + Divider64 => 1, + Divider256 => 2, + Divider1024 => 3, + } + } +} + +#[non_exhaustive] +pub struct TimerController {} + +impl TimerController { + pub(crate) const unsafe fn new() -> Self { + Self {} + } + + pub fn set_overflow_amount(&mut self, timer: Timer, n: u16) { + timer.set_overflow_amount(n); + } + + pub fn get_value(&mut self, timer: Timer) -> u16 { + timer.get_value() + } + + pub fn set_divider(&mut self, timer: Timer, divider: Divider) { + timer + .control_register() + .set_bits(divider.get_as_bits(), 2, 0); + } + + pub fn set_enabled(&mut self, timer: Timer, enabled: bool) { + let bit = if enabled { 1 } else { 0 }; + timer.control_register().set_bits(bit, 1, 7); + } + + pub fn set_cascade(&mut self, timer: Timer, cascade: bool) { + let bit = if cascade { 1 } else { 0 }; + timer.control_register().set_bits(bit, 1, 2); + } +} + +impl Timer { + fn set_overflow_amount(&self, n: u16) { + let count_up_value = 0u16.wrapping_sub(n); + self.data_register().set(count_up_value); + } + + fn get_value(&self) -> u16 { + self.data_register().get() + } + + fn data_register(&self) -> MemoryMapped { + timer_data(self.get_timer_number()) + } + + fn control_register(&self) -> MemoryMapped { + timer_control(self.get_timer_number()) + } + + fn get_timer_number(&self) -> usize { + use Timer::*; + + match self { + Timer0 => 0, + Timer1 => 1, + Timer2 => 2, + Timer3 => 3, + } + } +}