rtc.rsannotatedrtc.rssource734 lines · 23.1 KB · raw

Seiko S-3511A real-time clock chip

Used by Pokemon, Boktai, Rockman EXE 4.5, and maybe others

5use crate::interrupts::{InterruptRegisters, InterruptType};
6use bincode::{Decode, Encode};
7use jgenesis_common::num::{GetBit, U16Ext};
8use jgenesis_common::{define_bit_enum, timeutils};
10define_bit_enum!(Hours, [Twelve, TwentyFour]);
11define_bit_enum!(BeforeNoon, [Am, Pm]);
12
13impl BeforeNoon {
14    #[must_use]
15    fn toggle(self) -> Self {
16        match self {
17            Self::Am => Self::Pm,
18            Self::Pm => Self::Am,
19        }
20    }
21}
22
23#[derive(Debug, Clone, Encode, Decode)]
24struct Control {
25    power_cycled: bool,         // POWER
26    hours: Hours,               // 12/24
27    alarm_interrupt: bool,      // INTAE
28    per_minute_interrupt: bool, // INTME
29    frequency_interrupt: bool,  // INTFE
30}
31
32impl Default for Control {
33    fn default() -> Self {
34        // Per the datasheet, defaults to 0x82 at power-on (POWER and INTFE set)
35        // However, Sennen Kazoku depends on the chip initially being in 24-hour mode, and some games
36        // assume the chip is broken if they ever see the POWER bit set after first boot
37        Self {
38            power_cycled: false,
39            hours: Hours::TwentyFour,
40            alarm_interrupt: false,
41            per_minute_interrupt: false,
42            frequency_interrupt: false,
43        }
44    }
45}
46
47impl Control {
48    fn read(&self) -> u8 {
49        (u8::from(self.power_cycled) << 7)
50            | ((self.hours as u8) << 6)
51            | (u8::from(self.alarm_interrupt) << 5)
52            | (u8::from(self.per_minute_interrupt) << 3)
53            | (u8::from(self.frequency_interrupt) << 1)
54    }
55
56    fn write(&mut self, value: u8) {
57        self.hours = Hours::from_bit(value.bit(6));
58        self.alarm_interrupt = value.bit(5);
59        self.per_minute_interrupt = value.bit(3);
60        self.frequency_interrupt = value.bit(1);
61
62        log::trace!("Control write: {value:02X}");
63        log::trace!("  12/24: {:?}", self.hours);
64        log::trace!("  INTAE: {}", self.alarm_interrupt);
65        log::trace!("  INTME: {}", self.per_minute_interrupt);
66        log::trace!("  INTFE: {}", self.frequency_interrupt);
67
68        if self.frequency_interrupt && !self.per_minute_interrupt {
69            log::error!("Frequency interrupts are not implemented");
70        }
71    }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
75struct DateTime {
76    year: u8,
77    month: u8,
78    day: u8,
79    day_of_week: u8,
80    hour: u8,
81    before_noon: BeforeNoon,
82    minute: u8,
83    second: u8,
84    nanos: u128,
85}
86
87impl Default for DateTime {
88    fn default() -> Self {
89        Self {
90            year: 0,
91            month: 1,
92            day: 1,
93            day_of_week: 0,
94            hour: 0,
95            before_noon: BeforeNoon::Am,
96            minute: 0,
97            second: 0,
98            nanos: 0,
99        }
100    }
101}
102
103define_bit_enum!(CommandDirection, [Write, Read]);
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
106enum Command {
107    Reset,
108    Status,
109    DataFromYear,
110    DataFromHour,
111    InterruptRegisterLow,
112    InterruptRegisterHigh,
113}
114
115impl Command {
116    fn parse(command_byte: u8) -> Option<Self> {
117        // All command bytes must start with 0110
118        if command_byte >> 4 != 0b0110 {
119            return None;
120        }
121
122        match (command_byte >> 1) & 7 {
123            0b000 => Some(Self::Reset),
124            0b001 => Some(Self::Status),
125            0b010 => Some(Self::DataFromYear),
126            0b011 => Some(Self::DataFromHour),
127            0b100 => Some(Self::InterruptRegisterLow),
128            0b101 => Some(Self::InterruptRegisterHigh),
129            0b110 | 0b111 => {
130                log::error!("RTC test mode not implemented; ignoring command");
131                None
132            }
133            _ => unreachable!("value & 7 is always 0-7"),
134        }
135    }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
139enum ReadTarget {
140    Status,
141    Year,
142    Month,
143    Day,
144    DayOfWeek,
145    Hour,
146    Minute,
147    Second,
148    InterruptLow,
149    InterruptHigh,
150}
151
152impl ReadTarget {
153    fn read(self, rtc: &SeikoRealTimeClock) -> u8 {
154        match self {
155            Self::Status => rtc.control.read(),
156            Self::Year => binary_to_bcd(rtc.datetime.year),
157            Self::Month => binary_to_bcd(rtc.datetime.month),
158            Self::Day => binary_to_bcd(rtc.datetime.day),
159            Self::DayOfWeek => rtc.datetime.day_of_week,
160            Self::Hour => {
161                binary_to_bcd(rtc.datetime.hour) | ((rtc.datetime.before_noon as u8) << 7)
162            }
163            Self::Minute => binary_to_bcd(rtc.datetime.minute),
164            Self::Second => binary_to_bcd(rtc.datetime.second),
165            Self::InterruptLow => rtc.interrupt_register.lsb(),
166            Self::InterruptHigh => rtc.interrupt_register.msb(),
167        }
168    }
169
170    fn next(self) -> Option<Self> {
171        match self {
172            Self::Year => Some(Self::Month),
173            Self::Month => Some(Self::Day),
174            Self::Day => Some(Self::DayOfWeek),
175            Self::DayOfWeek => Some(Self::Hour),
176            Self::Hour => Some(Self::Minute),
177            Self::Minute => Some(Self::Second),
178            // TODO is this right?
179            Self::InterruptLow => Some(Self::InterruptHigh),
180            _ => None,
181        }
182    }
183}
184
185fn binary_to_bcd(value: u8) -> u8 {
186    let low = value % 10;
187    let high = value / 10;
188    low | (high << 4)
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
192enum WriteTarget {
193    Status,
194    Year,
195    Month,
196    Day,
197    DayOfWeek,
198    Hour,
199    Minute,
200    Second,
201    InterruptLow,
202    InterruptHigh,
203}
204
205impl WriteTarget {
206    fn write(self, rtc: &mut SeikoRealTimeClock, value: u8) {
207        match self {
208            Self::Status => rtc.control.write(value),
209            Self::Year => {
210                let year = bcd_to_binary(value);
211                rtc.datetime.year = if year < 100 { year } else { 0 };
212            }
213            Self::Month => {
214                let month = bcd_to_binary(value & 0x1F);
215                rtc.datetime.month = if (1..=12).contains(&month) { month } else { 1 };
216            }
217            Self::Day => {
218                let day = bcd_to_binary(value & 0x3F);
219                if day > timeutils::days_in_month(rtc.datetime.month, rtc.datetime.year) {
220                    rtc.tick_month();
221                    rtc.datetime.day = 1;
222                } else if day == 0 {
223                    rtc.datetime.day = 1;
224                } else {
225                    rtc.datetime.day = day;
226                }
227            }
228            Self::DayOfWeek => {
229                rtc.datetime.day_of_week = value & 0x7;
230            }
231            Self::Hour => {
232                let hour = bcd_to_binary(value & 0x3F);
233                match rtc.control.hours {
234                    Hours::Twelve => {
235                        let before_noon = BeforeNoon::from_bit(value.bit(7));
236                        rtc.datetime.hour = if hour < 12 { hour } else { 0 };
237                        rtc.datetime.before_noon = before_noon;
238                    }
239                    Hours::TwentyFour => {
240                        rtc.datetime.hour = if hour < 24 { hour } else { 0 };
241                        rtc.datetime.before_noon = BeforeNoon::from_bit(false);
242                    }
243                }
244            }
245            Self::Minute => {
246                let minute = bcd_to_binary(value & 0x7F);
247                rtc.datetime.minute = if minute < 60 { minute } else { 0 };
248            }
249            Self::Second => {
250                rtc.datetime.second = bcd_to_binary(value & 0x7F);
251                // Invalid values will get cleared at the next second tick
252            }
253            Self::InterruptLow => {
254                rtc.interrupt_register.set_lsb(value);
255            }
256            Self::InterruptHigh => {
257                rtc.interrupt_register.set_msb(value);
258            }
259        }
260    }
261
262    fn next(self) -> Option<Self> {
263        match self {
264            Self::Year => Some(Self::Month),
265            Self::Month => Some(Self::Day),
266            Self::Day => Some(Self::DayOfWeek),
267            Self::DayOfWeek => Some(Self::Hour),
268            Self::Hour => Some(Self::Minute),
269            Self::Minute => Some(Self::Second),
270            // TODO is this right?
271            Self::InterruptLow => Some(Self::InterruptHigh),
272            _ => None,
273        }
274    }
275}
276
277fn bcd_to_binary(value: u8) -> u8 {
278    (value & 0xF) + 10 * (value >> 4)
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
282enum CommandState {
283    Idle,
284    ReceivingCommand { bits: u8, remaining: u8 },
285    PreparingSend { target: ReadTarget },
286    SendingData { bits: u8, remaining: u8, next: Option<ReadTarget> },
287    ReceivingData { destination: WriteTarget, bits: u8, remaining: u8 },
288    Finished,
289}
290
291pub struct RtcWrite {
292    pub chip_select: bool,
293    pub clock: bool,
294    pub data: bool,
295}
296
297#[derive(Debug, Clone, Encode, Decode)]
298pub struct SeikoRealTimeClock {
299    datetime: DateTime,
300    control: Control,
301    interrupt_register: u16,
302    interrupt_line: bool,
303    last_update_time_nanos: u128,
304    command_state: CommandState,
305    prev_clock: bool,
306}
307
308impl SeikoRealTimeClock {
309    pub fn new() -> Self {
310        Self {
311            datetime: DateTime::default(),
312            control: Control::default(),
313            interrupt_register: 0x8000,
314            interrupt_line: false,
315            last_update_time_nanos: timeutils::current_time_nanos(),
316            command_state: CommandState::Idle,
317            prev_clock: false,
318        }
319    }
320
321    pub fn read(&self) -> bool {
322        log::debug!("RTC read; current command state {:?}", self.command_state);
323
324        match self.command_state {
325            CommandState::SendingData { bits, .. } => bits.bit(0),
326            _ => true,
327        }
328    }
329
330    pub fn write(&mut self, RtcWrite { chip_select, clock, data }: RtcWrite) {
331        log::trace!(
332            "RTC write: CS={}, SCK={}, SIO={}, current state {:?}",
333            u8::from(chip_select),
334            u8::from(clock),
335            u8::from(data),
336            self.command_state
337        );
338
339        let prev_clock = self.prev_clock;
340        self.prev_clock = clock;
341
342        if !chip_select {
343            self.command_state = CommandState::Idle;
344            return;
345        }
346
347        if self.command_state == CommandState::Finished {
348            return;
349        }
350
351        if self.command_state == CommandState::Idle {
352            self.command_state = CommandState::ReceivingCommand { bits: 0, remaining: 8 };
353            return;
354        }
355
356        // Data reads/writes only progress on falling clock edges
357        let falling_clock_edge = prev_clock && !clock;
358        if !falling_clock_edge {
359            return;
360        }
361
362        self.command_state = match self.command_state {
363            CommandState::ReceivingCommand { mut bits, remaining: 1 } => {
364                // Command bytes are received MSB first
365                bits = (bits << 1) | u8::from(data);
366
367                match Command::parse(bits) {
368                    Some(Command::Reset) => {
369                        log::debug!("Received RTC reset command");
370
371                        self.reset();
372                        CommandState::Finished
373                    }
374                    Some(command) => {
375                        let direction = CommandDirection::from_bit(bits.bit(0));
376
377                        log::debug!("Received RTC command {command:?}, direction {direction:?}");
378
379                        match direction {
380                            CommandDirection::Read => CommandState::PreparingSend {
381                                target: match command {
382                                    Command::Status => ReadTarget::Status,
383                                    Command::DataFromYear => ReadTarget::Year,
384                                    Command::DataFromHour => ReadTarget::Hour,
385                                    Command::InterruptRegisterLow => ReadTarget::InterruptLow,
386                                    Command::InterruptRegisterHigh => ReadTarget::InterruptHigh,
387                                    Command::Reset => unreachable!("already a reset match arm"),
388                                },
389                            },
390                            CommandDirection::Write => CommandState::ReceivingData {
391                                destination: match command {
392                                    Command::Status => WriteTarget::Status,
393                                    Command::DataFromYear => WriteTarget::Year,
394                                    Command::DataFromHour => WriteTarget::Hour,
395                                    Command::InterruptRegisterLow => WriteTarget::InterruptLow,
396                                    Command::InterruptRegisterHigh => WriteTarget::InterruptHigh,
397                                    Command::Reset => unreachable!("already a reset match arm"),
398                                },
399                                bits: 0,
400                                remaining: 8,
401                            },
402                        }
403                    }
404                    None => CommandState::Finished,
405                }
406            }
407            CommandState::ReceivingCommand { mut bits, mut remaining } => {
408                // Command bytes are received MSB first
409                bits = (bits << 1) | u8::from(data);
410                remaining -= 1;
411
412                CommandState::ReceivingCommand { bits, remaining }
413            }
414            CommandState::PreparingSend { target } => CommandState::SendingData {
415                bits: target.read(self),
416                remaining: 8,
417                next: target.next(),
418            },
419            CommandState::SendingData { remaining: 1, next: Some(next), .. } => {
420                let bits = next.read(self);
421                CommandState::SendingData { bits, remaining: 8, next: next.next() }
422            }
423            CommandState::SendingData { remaining: 1, next: None, .. } => CommandState::Finished,
424            CommandState::SendingData { mut bits, mut remaining, next } => {
425                bits >>= 1;
426                remaining -= 1;
427
428                CommandState::SendingData { bits, remaining, next }
429            }
430            CommandState::ReceivingData { destination, mut bits, remaining: 1 } => {
431                // Data is received LSB first
432                bits = (bits >> 1) | (u8::from(data) << 7);
433
434                log::debug!("Applying write {bits:02X} to destination {destination:?}");
435
436                destination.write(self, bits);
437
438                match destination.next() {
439                    Some(next) => {
440                        CommandState::ReceivingData { destination: next, bits: 0, remaining: 8 }
441                    }
442                    None => CommandState::Finished,
443                }
444            }
445            CommandState::ReceivingData { destination, mut bits, mut remaining } => {
446                // Data is received LSB first
447                bits = (bits >> 1) | (u8::from(data) << 7);
448                remaining -= 1;
449
450                CommandState::ReceivingData { destination, bits, remaining }
451            }
452            CommandState::Idle | CommandState::Finished => unreachable!(),
453        };
454    }
455
456    fn reset(&mut self) {
457        self.control.write(0);
458        self.control.power_cycled = false;
459        self.interrupt_register = 0;
460        self.datetime = DateTime::default();
461        self.interrupt_line = false;
462    }
463
464    pub fn update_time(&mut self, cycles: u64, interrupts: &mut InterruptRegisters) {
465        let current_time_nanos = timeutils::current_time_nanos();
466        let elapsed_nanos = current_time_nanos.saturating_sub(self.last_update_time_nanos);
467        self.last_update_time_nanos = current_time_nanos;
468
469        self.update_time_internal(elapsed_nanos);
470
471        let prev_interrupt_line = self.interrupt_line;
472        self.update_interrupt_line();
473
474        // RTC interrupt line is inverted; raise interrupt when line is de-asserted
475        if prev_interrupt_line && !self.interrupt_line {
476            interrupts.set_flag(InterruptType::GamePak, cycles);
477        }
478    }
479
480    fn update_time_internal(&mut self, elapsed_nanos: u128) {
481        self.datetime.nanos += elapsed_nanos;
482        let mut elapsed_seconds = (self.datetime.nanos / 1_000_000_000) as u64;
483        self.datetime.nanos %= 1_000_000_000;
484
485        if elapsed_seconds == 0 {
486            return;
487        }
488
489        if self.datetime.second >= 60 {
490            // Can happen if software wrote an invalid second
491            self.datetime.second = 0;
492            self.tick_minute();
493            elapsed_seconds -= 1;
494        }
495
496        let second = u64::from(self.datetime.second) + elapsed_seconds;
497        let elapsed_minutes = second / 60;
498        self.datetime.second = (second % 60) as u8;
499        if elapsed_minutes == 0 {
500            return;
501        }
502
503        let minute = u64::from(self.datetime.minute) + elapsed_minutes;
504        let elapsed_hours = minute / 60;
505        self.datetime.minute = (minute % 60) as u8;
506
507        for _ in 0..elapsed_hours {
508            self.tick_hour();
509        }
510    }
511
512    fn tick_minute(&mut self) {
513        self.datetime.minute += 1;
514        if self.datetime.minute >= 60 {
515            self.datetime.minute = 0;
516            self.tick_hour();
517        }
518    }
519
520    fn tick_hour(&mut self) {
521        self.datetime.hour += 1;
522
523        match self.control.hours {
524            Hours::Twelve => {
525                if self.datetime.hour >= 12 {
526                    self.datetime.hour = 0;
527                    self.datetime.before_noon = self.datetime.before_noon.toggle();
528                    if self.datetime.before_noon == BeforeNoon::Am {
529                        self.tick_day();
530                    }
531                }
532            }
533            Hours::TwentyFour => {
534                if self.datetime.hour >= 24 {
535                    self.datetime.hour = 0;
536                    self.tick_day();
537                }
538            }
539        }
540    }
541
542    fn tick_day(&mut self) {
543        self.datetime.day_of_week = (self.datetime.day_of_week + 1) % 7;
544
545        self.datetime.day += 1;
546        if self.datetime.day > timeutils::days_in_month(self.datetime.month, self.datetime.year) {
547            self.datetime.day = 1;
548            self.tick_month();
549        }
550    }
551
552    fn tick_month(&mut self) {
553        self.datetime.month += 1;
554        if self.datetime.month > 12 {
555            self.datetime.month = 1;
556            self.tick_year();
557        }
558    }
559
560    fn tick_year(&mut self) {
561        // Chip only has a 2-digit year (represents 2000-2099)
562        self.datetime.year = (self.datetime.year + 1) % 100;
563    }
564
565    fn update_interrupt_line(&mut self) {
566        // TODO this is not tested - I don't think any official releases use RTC interrupts
567        if self.control.per_minute_interrupt {
568            if self.control.frequency_interrupt {
569                // Per-minute steady interrupt: goes low at 0sec, goes high at 30sec
570                match self.datetime.second {
571                    0 => self.interrupt_line = true,
572                    30 => self.interrupt_line = false,
573                    _ => {}
574                }
575            } else {
576                // Per-minute edge interrupt: goes low at 0sec, never goes high
577                self.interrupt_line |= self.datetime.second == 0;
578            }
579        } else if self.control.frequency_interrupt {
580            // TODO frequency steady interrupts - does anything use these?
581        } else if self.control.alarm_interrupt {
582            // Alarm interrupt: goes low when hour/minute matches alarm time
583            let alarm_hour = bcd_to_binary(self.interrupt_register.lsb() & 0x3F);
584            let alarm_before_noon = BeforeNoon::from_bit(self.interrupt_register.bit(7));
585            let alarm_minute = bcd_to_binary(self.interrupt_register.msb() & 0x7F);
586
587            self.interrupt_line = alarm_hour == self.datetime.hour
588                && alarm_before_noon == self.datetime.before_noon
589                && alarm_minute == self.datetime.minute;
590        } else {
591            // No interrupts enabled
592            self.interrupt_line = false;
593        }
594    }
595}
596
597#[cfg(test)]
598mod tests {
599    use super::*;
600
601    #[test]
602    fn everything_overflows_24h() {
603        let mut rtc = SeikoRealTimeClock::new();
604        rtc.control.hours = Hours::TwentyFour;
605        rtc.datetime = DateTime {
606            year: 99,
607            month: 12,
608            day: 31,
609            day_of_week: 6,
610            hour: 23,
611            before_noon: BeforeNoon::Am,
612            minute: 59,
613            second: 59,
614            nanos: 0,
615        };
616
617        rtc.update_time_internal(1_000_000_000);
618
619        assert_eq!(
620            rtc.datetime,
621            DateTime {
622                year: 0,
623                month: 1,
624                day: 1,
625                day_of_week: 0,
626                hour: 0,
627                before_noon: BeforeNoon::Am,
628                minute: 0,
629                second: 0,
630                nanos: 0,
631            }
632        );
633    }
634
635    #[test]
636    fn everything_overflows_12h() {
637        let mut rtc = SeikoRealTimeClock::new();
638        rtc.control.hours = Hours::Twelve;
639        rtc.datetime = DateTime {
640            year: 99,
641            month: 12,
642            day: 31,
643            day_of_week: 6,
644            hour: 11,
645            before_noon: BeforeNoon::Pm,
646            minute: 59,
647            second: 59,
648            nanos: 0,
649        };
650
651        rtc.update_time_internal(1_000_000_000);
652
653        assert_eq!(
654            rtc.datetime,
655            DateTime {
656                year: 0,
657                month: 1,
658                day: 1,
659                day_of_week: 0,
660                hour: 0,
661                before_noon: BeforeNoon::Am,
662                minute: 0,
663                second: 0,
664                nanos: 0,
665            }
666        );
667    }
668
669    #[test]
670    fn am_to_pm_12h() {
671        let mut rtc = SeikoRealTimeClock::new();
672        rtc.control.hours = Hours::Twelve;
673        rtc.datetime = DateTime {
674            year: 0,
675            month: 1,
676            day: 5,
677            day_of_week: 0,
678            hour: 11,
679            before_noon: BeforeNoon::Am,
680            minute: 59,
681            second: 59,
682            nanos: 0,
683        };
684
685        rtc.update_time_internal(1_000_000_000);
686
687        assert_eq!(
688            rtc.datetime,
689            DateTime {
690                year: 0,
691                month: 1,
692                day: 5,
693                day_of_week: 0,
694                hour: 0,
695                before_noon: BeforeNoon::Pm,
696                minute: 0,
697                second: 0,
698                nanos: 0,
699            }
700        );
701    }
702
703    fn leap_year_test(year: u8, expected_day: u8) {
704        let mut rtc = SeikoRealTimeClock::new();
705        rtc.control.hours = Hours::TwentyFour;
706        rtc.datetime = DateTime {
707            year,
708            month: 2,
709            day: 28,
710            day_of_week: 0,
711            hour: 23,
712            before_noon: BeforeNoon::Am,
713            minute: 59,
714            second: 59,
715            nanos: 1_000_000_000 - 1,
716        };
717
718        rtc.update_time_internal(1);
719
720        assert_eq!(rtc.datetime.day, expected_day);
721    }
722
723    #[test]
724    #[rustfmt::skip]
725    fn leap_years() {
726        leap_year_test(0, 29);  // 2000
727        leap_year_test(3, 1);   // 2003
728        leap_year_test(4, 29);  // 2004
729        leap_year_test(5, 1);   // 2005
730        leap_year_test(95, 1);  // 2095
731        leap_year_test(96, 29); // 2096
732        leap_year_test(97, 1);  // 2097
733    }
734}