Add timer support

This commit is contained in:
Lokathor 2018-12-27 01:05:21 -07:00
parent 72bbe412b6
commit cc8d4376f8
4 changed files with 89 additions and 2 deletions

View file

@ -230,7 +230,7 @@ fixed_point_unsigned_division! {u32}
pub type fx8_8 = Fx<i16, U8>; pub type fx8_8 = Fx<i16, U8>;
#[cfg(test)] #[cfg(test)]
mod fixed_tests { mod tests {
use super::*; use super::*;
#[test] #[test]

View file

@ -8,7 +8,8 @@
use super::*; use super::*;
pub mod background;
pub mod display; pub mod display;
pub mod dma; pub mod dma;
pub mod keypad; pub mod keypad;
pub mod background; pub mod timers;

View file

@ -115,6 +115,7 @@ pub const BG3VOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_00
// pub const WININ: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0048) }; // pub const WININ: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0048) };
// pub const WINOUT: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_004A) }; // pub const WINOUT: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_004A) };
// TODO: blending
// pub const BLDCNT: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0050) }; // pub const BLDCNT: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0050) };
// pub const BLDALPHA: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0052) }; // pub const BLDALPHA: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0052) };
// pub const BLDY: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0054) }; // pub const BLDY: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0054) };

85
src/io/timers.rs Normal file
View file

@ -0,0 +1,85 @@
//! Module for timers.
//!
//! The timers are slightly funny in that reading and writing from them works
//! somewhat differently than with basically any other part of memory.
//!
//! When you read a timer's counter you read the current value.
//!
//! When you write a timer's counter you write _the counter's reload value_.
//! This is used whenever you enable the timer or any time the timer overflows.
//! You cannot set a timer to a given counter value, but you can set a timer to
//! start at some particular value every time it reloads.
//!
//! The timer counters are `u16`, so if you want to set them to run for a
//! certain number of ticks before overflow you would write something like
//!
//! ```rust
//! let init_val: u16 = u32::wrapping_sub(0x1_0000, ticks) as u16;
//! ```
//!
//! A timer reloads any time it overflows _or_ goes from disabled to enabled. If
//! you want to "pause" a timer _without_ making it reload when resumed then you
//! should not disable it. Instead, you should set its `TimerTickRate` to
//! `Cascade` and disable _the next lower timer_ so that it won't overflow into
//! the timer you have on hold.
use super::*;
/// Timer 0 Counter/Reload. Special (see module).
pub const TM0CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0100) };
/// Timer 1 Counter/Reload. Special (see module).
pub const TM1CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0104) };
/// Timer 2 Counter/Reload. Special (see module).
pub const TM2CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0108) };
/// Timer 3 Counter/Reload. Special (see module).
pub const TM3CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_010C) };
/// Timer 0 Control. Read/Write.
pub const TM0CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new_unchecked(0x400_0102) };
/// Timer 1 Control. Read/Write.
pub const TM1CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new_unchecked(0x400_0106) };
/// Timer 2 Control. Read/Write.
pub const TM2CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new_unchecked(0x400_010A) };
/// Timer 3 Control. Read/Write.
pub const TM3CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new_unchecked(0x400_010E) };
newtype! {
/// Allows control of a timer unit.
///
/// * Bits 0-2: How often the timer should tick up one unit. You can either
/// specify a number of CPU cycles or "cascade" mode, where there's a single
/// tick per overflow of the next lower timer. For example, Timer 1 would
/// tick up once per overflow of Timer 0 if it were in cascade mode. Cascade
/// mode naturally does nothing when used with Timer 0.
/// * Bit 6: Raise a timer interrupt upon overflow.
/// * Bit 7: Enable the timer.
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
TimerControlSetting, u16
}
impl TimerControlSetting {
bool_bits!(u16, [(6, overflow_irq), (7, enabled)]);
multi_bits!(u16, [(0, 3, tick_rate, TimerTickRate, CPU1, CPU64, CPU256, CPU1024, Cascade),]);
}
/// Controls how often an enabled timer ticks upward.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum TimerTickRate {
/// Once every CPU cycle
CPU1 = 0,
/// Once per 64 CPU cycles
CPU64 = 1,
/// Once per 256 CPU cycles
CPU256 = 2,
/// Once per 1,024 CPU cycles
CPU1024 = 3,
/// Once per overflow of the next lower timer. (Useless with Timer 0)
Cascade = 4,
}