wdt.rsannotatedwdt.rssource154 lines · 4.4 KB · raw

SH7604 watchdog timer (WDT)

Can also be used as an interval timer, and that is the only mode that is emulated. No 32X software uses the WDT as a watchdog timer

6use crate::debug::WatchdogTimerState;
7use bincode::{Decode, Encode};
8use jgenesis_common::num::GetBit;
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
11pub enum WatchdogTimerMode {
12    #[default]
13    Interval = 0,
14    Watchdog = 1,
15}
16
17impl WatchdogTimerMode {
18    fn from_bit(bit: bool) -> Self {
19        if bit { Self::Watchdog } else { Self::Interval }
20    }
21}
22
23fn clock_shift_select(byte: u8) -> u8 {
24    match byte & 7 {
25        // sysclk/2
26        0 => 1,
27        // sysclk/64
28        1 => 6,
29        // sysclk/128
30        2 => 7,
31        // sysclk/256
32        3 => 8,
33        // sysclk/512
34        4 => 9,
35        // sysclk/1024
36        5 => 10,
37        // sysclk/4096
38        6 => 12,
39        // sysclk/8192
40        7 => 13,
41        _ => unreachable!("value & 7 is always <= 7"),
42    }
43}
44
45#[derive(Debug, Clone, Encode, Decode)]
46pub struct WatchdogTimer {
47    timer_counter: u8,
48    mode: WatchdogTimerMode,
49    enabled: bool,
50    raw_clock_select: u8,
51    system_clock_shift: u8,
52    system_clock_counter: u64,
53    interval_overflow_flag: bool,
54}
55
56impl WatchdogTimer {
57    pub fn new() -> Self {
58        Self {
59            timer_counter: 0,
60            mode: WatchdogTimerMode::default(),
61            enabled: false,
62            raw_clock_select: 0,
63            system_clock_shift: clock_shift_select(0),
64            system_clock_counter: 0,
65            interval_overflow_flag: false,
66        }
67    }
68
69    pub fn tick(&mut self, system_cycles: u64) {
70        if !self.enabled {
71            return;
72        }
73
74        self.system_clock_counter += system_cycles;
75        let elapsed = self.system_clock_counter >> self.system_clock_shift;
76        self.system_clock_counter &= (1 << self.system_clock_shift) - 1;
77
78        let exceeds_byte = elapsed >= 256;
79        let (counter, overflowed) = self.timer_counter.overflowing_add(elapsed as u8);
80        self.timer_counter = counter;
81
82        let overflow_flag = exceeds_byte || overflowed;
83        self.interval_overflow_flag |= overflow_flag;
84    }
85
86    // $FFFFFE80: WTCSR (Watchdog timer control/status)
87    pub fn read_control(&self) -> u8 {
88        (u8::from(self.interval_overflow_flag) << 7)
89            | ((self.mode as u8) << 6)
90            | (u8::from(self.enabled) << 5)
91    }
92
93    // $FFFFFE81: WTCNT (Watchdog timer counter)
94    pub fn read_counter(&self) -> u8 {
95        self.timer_counter
96    }
97
98    // $FFFFFE80: WTCSR (Watchdog timer control/status) / WTCNT (Watchdog timer counter)
99    // Upper byte determines which register is written to
100    pub fn write_control(&mut self, value: u16) {
101        log::debug!("Watchdog timer control write: {value:04X}");
102
103        let [msb, lsb] = value.to_be_bytes();
104        match msb {
105            0x5A => self.write_wtcnt(lsb),
106            0xA5 => self.write_wtcsr(lsb),
107            _ => {
108                log::warn!("Invalid watchdog timer write to $FFFFFE80: {value:04X}");
109            }
110        }
111    }
112
113    fn write_wtcnt(&mut self, value: u8) {
114        self.timer_counter = value;
115
116        log::debug!("WTCNT write: {value:02X}");
117    }
118
119    fn write_wtcsr(&mut self, value: u8) {
120        self.interval_overflow_flag &= value.bit(7);
121        self.mode = WatchdogTimerMode::from_bit(value.bit(6));
122        self.enabled = value.bit(5);
123        self.raw_clock_select = value & 7;
124        self.system_clock_shift = clock_shift_select(value);
125
126        if !self.enabled {
127            // Counter is reset when timer is disabled
128            self.timer_counter = 0;
129        }
130
131        log::debug!("WTCSR write: {value:02X}");
132        log::debug!("  Clear overflow flag: {}", !value.bit(7));
133        log::debug!("  Timer mode: {:?}", self.mode);
134        log::debug!("  Timer enabled: {}", self.enabled);
135        log::debug!("  System clock divider: {}", 1 << self.system_clock_shift);
136
137        if self.mode == WatchdogTimerMode::Watchdog {
138            log::error!("Watchdog timer mode not implemented");
139        }
140    }
141
142    pub fn overflow_flag(&self) -> bool {
143        self.interval_overflow_flag
144    }
145
146    pub(crate) fn debug_state(&self) -> WatchdogTimerState {
147        WatchdogTimerState {
148            enabled: self.enabled,
149            counter: self.timer_counter,
150            system_clock_divider: 1 << self.system_clock_shift,
151            overflow_flag: self.interval_overflow_flag,
152        }
153    }
154}