Merge pull request #132 from gwilymk/add-timer-support

Add first-class timer support
This commit is contained in:
Gwilym Kuiper 2021-11-23 21:50:49 +00:00 committed by GitHub
commit c6504343a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 167 additions and 18 deletions

View file

@ -17,7 +17,8 @@ fn main() -> ! {
let mut input = ButtonController::new(); let mut input = ButtonController::new();
let vblank_provider = agb::interrupt::VBlank::get(); let vblank_provider = agb::interrupt::VBlank::get();
let mut mixer = gba.mixer.mixer(); let mut timers = gba.timers.timers();
let mut mixer = gba.mixer.mixer(&mut timers.timer0);
mixer.enable(); mixer.enable();
let channel = SoundChannel::new(DEAD_CODE); let channel = SoundChannel::new(DEAD_CODE);

View file

@ -14,15 +14,35 @@ fn main() -> ! {
let mut gba = Gba::new(); let mut gba = Gba::new();
let vblank_provider = agb::interrupt::VBlank::get(); let vblank_provider = agb::interrupt::VBlank::get();
let mut mixer = gba.mixer.mixer(); let mut timer_controller = gba.timers.timers();
let mut timer = timer_controller.timer1;
timer.set_enabled(true);
let mut mixer = gba.mixer.mixer(&mut timer_controller.timer0);
mixer.enable(); mixer.enable();
let mut channel = SoundChannel::new(LET_IT_IN); let mut channel = SoundChannel::new(LET_IT_IN);
channel.stereo(); channel.stereo();
mixer.play_sound(channel).unwrap(); mixer.play_sound(channel).unwrap();
let mut frame_counter = 0i32;
loop { loop {
vblank_provider.wait_for_vblank(); vblank_provider.wait_for_vblank();
let before_mixing_cycles = timer.get_value();
mixer.vblank(); mixer.vblank();
let after_mixing_cycles = timer.get_value();
frame_counter = frame_counter.wrapping_add(1);
if frame_counter % 128 == 0 {
let total_cycles = after_mixing_cycles.wrapping_sub(before_mixing_cycles) as u32;
let percent = (total_cycles * 100) / 280896;
agb::println!(
"Took {} cycles to calculate mixer ~= {}% of total frame",
total_cycles,
percent
);
}
} }
} }

View file

@ -39,6 +39,8 @@ mod single;
pub mod sound; pub mod sound;
/// System BIOS calls / syscalls. /// System BIOS calls / syscalls.
pub mod syscall; pub mod syscall;
/// Interactions with the internal timers
pub mod timer;
#[cfg(not(test))] #[cfg(not(test))]
use core::fmt::Write; use core::fmt::Write;
@ -61,6 +63,7 @@ pub struct Gba {
pub display: display::Display, pub display: display::Display,
pub sound: sound::dmg::Sound, pub sound: sound::dmg::Sound,
pub mixer: sound::mixer::MixerController, pub mixer: sound::mixer::MixerController,
pub timers: timer::TimerController,
} }
impl Gba { impl Gba {
@ -73,6 +76,7 @@ impl Gba {
display: display::Display::new(), display: display::Display::new(),
sound: sound::dmg::Sound::new(), sound: sound::dmg::Sound::new(),
mixer: sound::mixer::MixerController::new(), mixer: sound::mixer::MixerController::new(),
timers: timer::TimerController::new(),
} }
} }
} }
@ -169,6 +173,12 @@ mod test {
assert_eq!(1, 1); assert_eq!(1, 1);
} }
#[test_case]
fn gba_struct_is_zero_sized(_gba: &mut Gba) {
use core::mem;
assert_eq!(mem::size_of::<Gba>(), 0);
}
#[test_case] #[test_case]
fn wait_30_frames(_gba: &mut Gba) { fn wait_30_frames(_gba: &mut Gba) {
let vblank = crate::interrupt::VBlank::get(); let vblank = crate::interrupt::VBlank::get();

View file

@ -1,4 +1,5 @@
use crate::memory_mapped::MemoryMapped; use crate::memory_mapped::MemoryMapped;
use crate::timer::Timer;
const fn dma_source_addr(dma: usize) -> usize { const fn dma_source_addr(dma: usize) -> usize {
0x0400_00b0 + 0x0c * dma 0x0400_00b0 + 0x0c * dma
@ -24,10 +25,6 @@ const DMA2_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(dma_control_a
const FIFOA_DEST_ADDR: u32 = 0x0400_00a0; const FIFOA_DEST_ADDR: u32 = 0x0400_00a0;
const FIFOB_DEST_ADDR: u32 = 0x0400_00a4; const FIFOB_DEST_ADDR: u32 = 0x0400_00a4;
// Similarly for proper timer support
const TIMER0_COUNTER: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0100) };
const TIMER0_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0102) };
const SOUND_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0082) }; const SOUND_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0082) };
const SOUND_CONTROL_X: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0084) }; const SOUND_CONTROL_X: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0084) };
@ -94,9 +91,7 @@ pub(super) fn set_sound_control_register_for_mixer() {
SOUND_CONTROL_X.set(1 << 7); SOUND_CONTROL_X.set(1 << 7);
} }
pub(super) fn set_timer_counter_for_frequency_and_enable(frequency: i32) { pub(super) fn set_timer_counter_for_frequency_and_enable(timer: &mut Timer, frequency: i32) {
let counter = 65536 - (16777216 / frequency); timer.set_overflow_amount((16777216 / frequency) as u16);
TIMER0_COUNTER.set(counter as u16); timer.set_enabled(true);
TIMER0_CONTROL.set(1 << 7); // enable the timer
} }

View file

@ -5,6 +5,7 @@ pub use sw_mixer::ChannelId;
pub use sw_mixer::Mixer; pub use sw_mixer::Mixer;
use crate::number::Num; use crate::number::Num;
use crate::timer::Timer;
#[non_exhaustive] #[non_exhaustive]
pub struct MixerController {} pub struct MixerController {}
@ -14,8 +15,8 @@ impl MixerController {
MixerController {} MixerController {}
} }
pub fn mixer(&mut self) -> Mixer { pub fn mixer<'a>(&mut self, timer: &'a mut Timer) -> Mixer<'a> {
Mixer::new() Mixer::new(timer)
} }
} }

View file

@ -2,6 +2,7 @@ use super::hw;
use super::hw::LeftOrRight; use super::hw::LeftOrRight;
use super::{SoundChannel, SoundPriority}; use super::{SoundChannel, SoundPriority};
use crate::number::Num; use crate::number::Num;
use crate::timer::Timer;
// Defined in mixer.s // Defined in mixer.s
extern "C" { extern "C" {
@ -18,25 +19,29 @@ extern "C" {
fn agb_rs__mixer_collapse(sound_buffer: *mut i8, input_buffer: *const Num<i16, 4>); fn agb_rs__mixer_collapse(sound_buffer: *mut i8, input_buffer: *const Num<i16, 4>);
} }
pub struct Mixer { pub struct Mixer<'a> {
buffer: MixerBuffer, buffer: MixerBuffer,
channels: [Option<SoundChannel>; 8], channels: [Option<SoundChannel>; 8],
indices: [i32; 8], indices: [i32; 8],
timer: &'a mut Timer,
} }
pub struct ChannelId(usize, i32); pub struct ChannelId(usize, i32);
impl Mixer { impl<'a> Mixer<'a> {
pub(super) fn new() -> Self { pub(super) fn new(timer: &'a mut Timer) -> Self {
Mixer { Mixer {
buffer: MixerBuffer::new(), buffer: MixerBuffer::new(),
channels: Default::default(), channels: Default::default(),
indices: Default::default(), indices: Default::default(),
timer,
} }
} }
pub fn enable(&self) { pub fn enable(&mut self) {
hw::set_timer_counter_for_frequency_and_enable(SOUND_FREQUENCY); hw::set_timer_counter_for_frequency_and_enable(self.timer, SOUND_FREQUENCY);
hw::set_sound_control_register_for_mixer(); hw::set_sound_control_register_for_mixer();
} }

117
agb/src/timer.rs Normal file
View file

@ -0,0 +1,117 @@
use crate::memory_mapped::MemoryMapped;
const fn timer_data(timer: usize) -> MemoryMapped<u16> {
unsafe { MemoryMapped::new(0x0400_0100 + 4 * timer) }
}
const fn timer_control(timer: usize) -> MemoryMapped<u16> {
unsafe { MemoryMapped::new(0x0400_0102 + 4 * timer) }
}
#[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 Timer {
timer_number: u16,
}
#[non_exhaustive]
pub struct Timers {
pub timer0: Timer,
pub timer1: Timer,
pub timer2: Timer,
pub timer3: Timer,
}
impl Timers {
pub(crate) unsafe fn new() -> Self {
Self {
timer0: Timer::new(0),
timer1: Timer::new(1),
timer2: Timer::new(2),
timer3: Timer::new(3),
}
}
}
impl Timer {
unsafe fn new(timer_number: u16) -> Self {
let new_timer = Self { timer_number };
new_timer.data_register().set(0);
new_timer.control_register().set(0);
new_timer
}
pub fn set_overflow_amount(&mut self, n: u16) {
let count_up_value = 0u16.wrapping_sub(n);
self.data_register().set(count_up_value);
}
pub fn get_value(&self) -> u16 {
self.data_register().get()
}
pub fn set_divider(&mut self, divider: Divider) {
self.control_register()
.set_bits(divider.get_as_bits(), 2, 0);
}
pub fn set_enabled(&mut self, enabled: bool) {
let bit = if enabled { 1 } else { 0 };
self.control_register().set_bits(bit, 1, 7);
}
pub fn set_cascade(&mut self, cascade: bool) {
let bit = if cascade { 1 } else { 0 };
self.control_register().set_bits(bit, 1, 2);
}
fn data_register(&self) -> MemoryMapped<u16> {
timer_data(self.get_timer_number())
}
fn control_register(&self) -> MemoryMapped<u16> {
timer_control(self.get_timer_number())
}
fn get_timer_number(&self) -> usize {
self.timer_number as usize
}
}
#[non_exhaustive]
pub struct TimerController {}
impl TimerController {
pub(crate) const fn new() -> Self {
Self {}
}
pub fn timers(&mut self) -> Timers {
unsafe { Timers::new() }
}
}