mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
Add support for timers
This commit is contained in:
parent
b88cf39a67
commit
fd610d8cc1
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
105
agb/src/timer.rs
Normal file
105
agb/src/timer.rs
Normal file
|
@ -0,0 +1,105 @@
|
|||
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 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<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 {
|
||||
use Timer::*;
|
||||
|
||||
match self {
|
||||
Timer0 => 0,
|
||||
Timer1 => 1,
|
||||
Timer2 => 2,
|
||||
Timer3 => 3,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue