Skip to main content
← PSoXide SDK guides and examples · SDK f2fec1c301f1 · All Cargo features enabled; check feature gates before use.

psx_io/
timers.rs

1//! Root counter (timer) MMIO. Three 16-bit counters, each with counter /
2//! mode / target registers. Mode bits select the clock source and IRQ
3//! behaviour. Register addresses and the mode bits live in
4//! [`psx_hw::timers`].
5
6use psx_hw::timers as reg;
7
8/// One of the three root counters.
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10#[repr(u8)]
11pub enum Timer {
12    /// Dot-clock or system-clock, HSync-gatable.
13    Timer0 = 0,
14    /// System-clock or HBlank, VSync-gatable.
15    Timer1 = 1,
16    /// System-clock or system-clock/8.
17    Timer2 = 2,
18}
19
20impl Timer {
21    #[inline(always)]
22    const fn base(self) -> u32 {
23        reg::block(self as u32)
24    }
25}
26
27/// Current counter value (0..=65535).
28#[inline(always)]
29pub fn counter(t: Timer) -> u16 {
30    // SAFETY: `t.base() + reg::COUNTER` is root counter n's current-value register for n in 0..=2
31    // (the `Timer` discriminants): aligned MMIO on every PS1. Reading the counter has no side
32    // effects.
33    unsafe { crate::read_u32(t.base() + reg::COUNTER) as u16 }
34}
35
36/// Set the counter directly.
37#[inline(always)]
38pub fn set_counter(t: Timer, value: u16) {
39    // SAFETY: root counter `t`'s current-value register (see `counter`); a write only reloads the
40    // count.
41    unsafe { crate::write_u32(t.base() + reg::COUNTER, value as u32) }
42}
43
44/// Mode / control register; the bits are [`psx_hw::timers::mode`]. Writing
45/// it reconfigures the counter and resets it to 0.
46#[inline(always)]
47pub fn set_mode(t: Timer, mode: u16) {
48    // SAFETY: `t.base() + reg::MODE` is root counter `t`'s mode register (see `counter`); a write
49    // reconfigures and resets that counter only.
50    unsafe { crate::write_u32(t.base() + reg::MODE, mode as u32) }
51}
52
53/// Read the mode register (includes the sticky "reached target" and
54/// "reached wrap" bits, [`psx_hw::timers::mode::REACHED_TARGET`] and
55/// [`psx_hw::timers::mode::REACHED_WRAP`]).
56#[inline(always)]
57pub fn mode(t: Timer) -> u16 {
58    // SAFETY: `t.base() + reg::MODE` is root counter `t`'s mode register (see `counter`); the read
59    // clears the sticky reached bits and touches no memory.
60    unsafe { crate::read_u32(t.base() + reg::MODE) as u16 }
61}
62
63/// Target value for `reset-on-target` mode.
64#[inline(always)]
65pub fn set_target(t: Timer, value: u16) {
66    // SAFETY: `t.base() + reg::TARGET` is root counter `t`'s target register (see `counter`).
67    unsafe { crate::write_u32(t.base() + reg::TARGET, value as u32) }
68}