srtc.rsannotatedsrtc.rssource312 lines · 8.0 KB · raw

S-RTC coprocessor, a Sharp real-time clock chip

Used by Daikaijuu Monogatari II

5use bincode::{Decode, Encode};
6use jgenesis_common::timeutils;
7use time::Weekday;
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
10enum ReadState {
11    #[default]
12    Ack,
13    Digit {
14        idx: u8,
15    },
16    End,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
20enum WriteState {
21    #[default]
22    Start,
23    Command,
24    Digit {
25        idx: u8,
26    },
27    End,
28}
29
30trait WeekdayExt {
31    fn to_srtc_u8(self) -> u8;
32}
33
34impl WeekdayExt for Weekday {
35    fn to_srtc_u8(self) -> u8 {
36        match self {
37            Self::Sunday => 0,
38            Self::Monday => 1,
39            Self::Tuesday => 2,
40            Self::Wednesday => 3,
41            Self::Thursday => 4,
42            Self::Friday => 5,
43            Self::Saturday => 6,
44        }
45    }
46}
47
48#[derive(Debug, Clone, Encode, Decode)]
49pub struct SRtc {
50    last_update_nanos: u128,
51    nanos: u32,
52    seconds: u8,
53    minutes: u8,
54    hours: u8,
55    day: u8,
56    month: u8,
57    year: u8,
58    century: u8,
59    day_of_week: u8,
60    read_state: ReadState,
61    write_state: WriteState,
62}
63
64impl Default for SRtc {
65    fn default() -> Self {
66        Self::new()
67    }
68}
69
70impl SRtc {
71    #[must_use]
72    pub fn new() -> Self {
73        Self {
74            last_update_nanos: timeutils::current_time_nanos(),
75            nanos: 0,
76            seconds: 0,
77            minutes: 0,
78            hours: 0,
79            day: 1,
80            month: 1,
81            year: 0,
82            century: 9,
83            day_of_week: 0,
84            read_state: ReadState::default(),
85            write_state: WriteState::default(),
86        }
87    }
88
89    #[allow(clippy::missing_panics_doc)]
90    #[inline]
91    #[must_use]
92    pub fn read(&mut self) -> u8 {
93        log::trace!("S-RTC read");
94
95        self.write_state = WriteState::default();
96
97        self.update_time();
98
99        match self.read_state {
100            ReadState::Ack => {
101                self.read_state = ReadState::Digit { idx: 0 };
102                0x0F
103            }
104            ReadState::Digit { idx } => {
105                let value = match idx {
106                    0 => self.seconds % 10,
107                    1 => self.seconds / 10,
108                    2 => self.minutes % 10,
109                    3 => self.minutes / 10,
110                    4 => self.hours % 10,
111                    5 => self.hours / 10,
112                    6 => self.day % 10,
113                    7 => self.day / 10,
114                    8 => self.month,
115                    9 => self.year % 10,
116                    10 => self.year / 10,
117                    11 => self.century,
118                    12 => self.day_of_week,
119                    _ => panic!("Invalid S-RTC timestamp index: {idx}"),
120                };
121
122                self.read_state = match idx {
123                    12 => ReadState::End,
124                    _ => ReadState::Digit { idx: idx + 1 },
125                };
126
127                log::trace!(
128                    "S-RTC read, sending value {value:X} for idx {idx}; current state is {self:?}"
129                );
130
131                value
132            }
133            ReadState::End => {
134                self.read_state = ReadState::Ack;
135                0x0F
136            }
137        }
138    }
139
140    #[inline]
141    pub fn write(&mut self, value: u8) {
142        log::trace!("S-RTC write {value:X}");
143
144        self.read_state = ReadState::default();
145
146        self.update_time();
147
148        // This is a 4-bit port
149        let value = value & 0x0F;
150
151        match self.write_state {
152            WriteState::Start => {
153                if value == 0x0E {
154                    self.write_state = WriteState::Command;
155                }
156            }
157            WriteState::Command => {
158                match value {
159                    0x04 => {
160                        // Unknown command; possibly sets 24-hour mode?
161                        self.write_state = WriteState::End;
162                    }
163                    0x00 => {
164                        self.write_state = WriteState::Digit { idx: 0 };
165                    }
166                    _ => {}
167                }
168            }
169            WriteState::Digit { idx } => {
170                self.write_timestamp_digit(idx, value);
171
172                self.write_state = match idx {
173                    11 => WriteState::End,
174                    _ => WriteState::Digit { idx: idx + 1 },
175                };
176            }
177            WriteState::End => {
178                if value == 0x0D {
179                    self.write_state = WriteState::Start;
180                }
181            }
182        }
183    }
184
185    pub fn reset_state(&mut self) {
186        self.read_state = ReadState::default();
187        self.write_state = WriteState::default();
188    }
189
190    fn write_timestamp_digit(&mut self, idx: u8, value: u8) {
191        match idx {
192            0 => {
193                self.seconds = self.seconds / 10 * 10 + value;
194            }
195            1 => {
196                self.seconds = 10 * value + (self.seconds % 10);
197            }
198            2 => {
199                self.minutes = self.minutes / 10 * 10 + value;
200            }
201            3 => {
202                self.minutes = 10 * value + (self.minutes % 10);
203            }
204            4 => {
205                self.hours = self.hours / 10 * 10 + value;
206            }
207            5 => {
208                self.hours = 10 * value + (self.hours % 10);
209            }
210            6 => {
211                self.day = self.day / 10 * 10 + value;
212                self.update_day_of_week();
213            }
214            7 => {
215                self.day = 10 * value + (self.day % 10);
216                self.update_day_of_week();
217            }
218            8 => {
219                self.month = value;
220                self.update_day_of_week();
221            }
222            9 => {
223                self.year = self.year / 10 * 10 + value;
224                self.update_day_of_week();
225            }
226            10 => {
227                self.year = 10 * value + (self.year % 10);
228                self.update_day_of_week();
229            }
230            11 => {
231                self.century = value;
232                self.update_day_of_week();
233            }
234            _ => panic!("Invalid S-RTC timestamp index: {idx}"),
235        }
236
237        log::trace!("S-RTC timestamp write, index {idx} value {value:X}; new time is {self:?}");
238    }
239
240    fn update_day_of_week(&mut self) {
241        self.day_of_week =
242            timeutils::day_of_week(self.day, self.month, four_digit_year(self.year, self.century))
243                .to_srtc_u8();
244    }
245
246    fn update_time(&mut self) {
247        let now_nanos = timeutils::current_time_nanos();
248        let elapsed = now_nanos.saturating_sub(self.last_update_nanos);
249        self.last_update_nanos = now_nanos;
250
251        let new_nanos = u128::from(self.nanos) + elapsed;
252        self.nanos = (new_nanos % 1_000_000_000) as u32;
253
254        for _ in 0..new_nanos / 1_000_000_000 {
255            self.increment_seconds();
256        }
257    }
258
259    fn increment_seconds(&mut self) {
260        self.seconds += 1;
261        if self.seconds >= 60 {
262            self.seconds = 0;
263            self.increment_minutes();
264        }
265    }
266
267    fn increment_minutes(&mut self) {
268        self.minutes += 1;
269        if self.minutes >= 60 {
270            self.minutes = 0;
271            self.increment_hours();
272        }
273    }
274
275    fn increment_hours(&mut self) {
276        self.hours += 1;
277        if self.hours >= 24 {
278            self.hours = 0;
279            self.increment_day();
280        }
281    }
282
283    fn increment_day(&mut self) {
284        self.day += 1;
285        self.day_of_week = (self.day_of_week + 1) % 7;
286
287        if self.day > timeutils::days_in_month(self.month, self.year) {
288            self.day = 1;
289            self.increment_month();
290        }
291    }
292
293    fn increment_month(&mut self) {
294        self.month += 1;
295        if self.month > 12 {
296            self.month = 1;
297            self.increment_year();
298        }
299    }
300
301    fn increment_year(&mut self) {
302        self.year += 1;
303        if self.year > 99 {
304            self.year = 0;
305            self.century += 1;
306        }
307    }
308}
309
310fn four_digit_year(year: u8, century: u8) -> u16 {
311    1000 + 100 * u16::from(century) + u16::from(year)
312}