rtc.rsannotatedrtc.rssource661 lines · 18.4 KB · raw
1//! RTC-4513 real-time clock chip, used by Tengai Makyou Zero
2
3use bincode::{Decode, Encode};
4use jgenesis_common::num::GetBit;
5use jgenesis_common::timeutils;
6use std::cmp::Ordering;
7use std::fmt::{Display, Formatter};
8
9// Bit 7 indicates status, 0=busy and 1=ready; hardcode to 1
10pub const STATUS_BYTE: u8 = 0x80;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
13enum HourType {
14    #[default]
15    Am,
16    Pm,
17}
18
19impl HourType {
20    #[must_use]
21    const fn toggle(self) -> Self {
22        match self {
23            Self::Am => Self::Pm,
24            Self::Pm => Self::Am,
25        }
26    }
27
28    const fn from_bit(bit: bool) -> Self {
29        if bit { Self::Pm } else { Self::Am }
30    }
31
32    fn to_bit(self) -> bool {
33        self == Self::Pm
34    }
35}
36
37impl Display for HourType {
38    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
39        match self {
40            Self::Am => write!(f, "AM"),
41            Self::Pm => write!(f, "PM"),
42        }
43    }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
47enum ClockHours {
48    #[default]
49    Twelve,
50    TwentyFour,
51}
52
53impl ClockHours {
54    fn from_bit(bit: bool) -> Self {
55        if bit { Self::TwentyFour } else { Self::Twelve }
56    }
57
58    fn to_bit(self) -> bool {
59        self == Self::TwentyFour
60    }
61}
62
63impl Display for ClockHours {
64    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
65        match self {
66            Self::Twelve => write!(f, "12-hour"),
67            Self::TwentyFour => write!(f, "24-hour"),
68        }
69    }
70}
71
72#[derive(Debug, Clone, Encode, Decode)]
73struct RtcTime {
74    last_update_nanos: u128,
75    nanos: u32,
76    seconds: u8,
77    minutes: u8,
78    hours: u8,
79    hour_type: HourType,
80    day: u8,
81    month: u8,
82    year: u8,
83    day_of_week: u8,
84    clock_hours: ClockHours,
85    calendar_enabled: bool,
86}
87
88impl RtcTime {
89    fn new(now_nanos: u128) -> Self {
90        Self {
91            last_update_nanos: now_nanos,
92            nanos: 0,
93            seconds: 0,
94            minutes: 0,
95            hours: 0,
96            hour_type: HourType::default(),
97            day: 0,
98            month: 0,
99            year: 0,
100            day_of_week: 0,
101            clock_hours: ClockHours::default(),
102            calendar_enabled: false,
103        }
104    }
105
106    fn increment_seconds(&mut self) {
107        self.seconds += 1;
108        if self.seconds >= 60 {
109            self.seconds = 0;
110            self.increment_minutes();
111        }
112
113        log::trace!("Incremented seconds, new time: {self:#?}");
114    }
115
116    fn increment_minutes(&mut self) {
117        self.minutes += 1;
118        if self.minutes >= 60 {
119            self.minutes = 0;
120            self.increment_hours();
121        }
122    }
123
124    fn increment_hours(&mut self) {
125        self.hours += 1;
126
127        match self.clock_hours {
128            ClockHours::TwentyFour => {
129                if self.hours >= 24 {
130                    self.hours = 0;
131                    self.increment_day();
132                }
133            }
134            ClockHours::Twelve => match self.hours.cmp(&12) {
135                Ordering::Equal => {
136                    self.hour_type = self.hour_type.toggle();
137                    if self.hour_type == HourType::Am {
138                        self.increment_day();
139                    }
140                }
141                Ordering::Greater => {
142                    self.hours = 1;
143                }
144                Ordering::Less => {}
145            },
146        }
147    }
148
149    fn increment_day(&mut self) {
150        if !self.calendar_enabled {
151            self.day_of_week = (self.day_of_week + 1) % 7;
152            return;
153        }
154
155        self.day += 1;
156        self.day_of_week = (self.day_of_week + 1) % 7;
157
158        if self.day > timeutils::days_in_month(self.month, self.year) {
159            self.day = 1;
160            self.increment_month();
161        }
162    }
163
164    fn increment_month(&mut self) {
165        self.month += 1;
166        if self.month > 12 {
167            self.month = 1;
168            self.increment_year();
169        }
170    }
171
172    fn increment_year(&mut self) {
173        // This chip only stores years in the range 0-99
174        self.year = (self.year + 1) % 100;
175    }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
179enum Command {
180    #[default]
181    None,
182    Read,
183    Write,
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
187enum InterruptDuty {
188    #[default]
189    FixedTime,
190    UntilAck,
191}
192
193impl InterruptDuty {
194    fn from_bit(bit: bool) -> Self {
195        if bit { Self::UntilAck } else { Self::FixedTime }
196    }
197
198    fn to_bit(self) -> bool {
199        self == Self::UntilAck
200    }
201}
202
203#[derive(Debug, Clone, Encode, Decode)]
204pub struct Rtc4513 {
205    command: Command,
206    register: Option<u8>,
207    time: RtcTime,
208    selected: bool,
209    wrapped: bool,
210    paused: bool,
211    pending_seconds_increment: bool,
212    stopped: bool,
213    reset: bool,
214    time_lost: bool,
215    irq: bool,
216    irq_enabled: bool,
217    irq_rate_bits: u8,
218    irq_rate_nanos: u128,
219    last_irq_nanos: u128,
220    irq_duty: InterruptDuty,
221    last_day_write_high: u8,
222    last_month_write_high: u8,
223}
224
225impl Rtc4513 {
226    pub fn new() -> Self {
227        let now_nanos = timeutils::current_time_nanos();
228        Self {
229            command: Command::default(),
230            register: None,
231            time: RtcTime::new(now_nanos),
232            selected: false,
233            wrapped: false,
234            paused: false,
235            pending_seconds_increment: false,
236            stopped: false,
237            reset: false,
238            time_lost: true,
239            irq: false,
240            irq_enabled: true,
241            irq_rate_bits: 0,
242            irq_rate_nanos: irq_rate_nanos_from_bits(0),
243            last_irq_nanos: now_nanos,
244            irq_duty: InterruptDuty::default(),
245            last_day_write_high: 0,
246            last_month_write_high: 0,
247        }
248    }
249
250    pub fn read_chip_select(&self) -> u8 {
251        log::trace!("Chip select read");
252        self.selected.into()
253    }
254
255    pub fn write_chip_select(&mut self, value: u8) {
256        log::trace!("Chip select write {value:02X}");
257
258        let prev_selected = self.selected;
259        self.selected = value.bit(0);
260
261        if prev_selected && !self.selected {
262            self.wrapped = false;
263            self.reset = false;
264            self.command = Command::None;
265            self.register = None;
266        }
267    }
268
269    pub fn read_data_port(&mut self) -> u8 {
270        if !self.selected || self.command != Command::Read {
271            return 0;
272        }
273
274        self.update_time();
275
276        let Some(register) = self.register else { return 0 };
277
278        let byte = self.read_register(register);
279        log::trace!(
280            "RTC data port read, current register is {:X?}, returning {byte:X}",
281            self.register
282        );
283
284        self.register = Some((register + 1) & 0x0F);
285        byte
286    }
287
288    pub fn write_data_port(&mut self, value: u8) {
289        if !self.selected {
290            return;
291        }
292
293        self.update_time();
294
295        log::trace!(
296            "RTC data port write {value:X}, current register is {:X?} and command is {:?}",
297            self.register,
298            self.command
299        );
300
301        match (self.command, self.register) {
302            (Command::None, _) => {
303                // First write since select: set command ($03 = write, $0C = read)
304                self.command = match value {
305                    0x03 => Command::Write,
306                    0x0C => Command::Read,
307                    _ => Command::None,
308                };
309            }
310            (_, None) => {
311                // Second write since select: set register index
312                self.register = Some(value & 0x0F);
313            }
314            (Command::Write, Some(register)) => {
315                // Write to selected register and increment register index
316                self.write_register(register, value);
317                self.register = Some((register + 1) & 0x0F);
318            }
319            (Command::Read, Some(_)) => {}
320        }
321    }
322
323    fn read_register(&mut self, register: u8) -> u8 {
324        match register {
325            0x0 => self.read_seconds_low(),
326            0x1 => self.read_seconds_high(),
327            0x2 => self.read_minutes_low(),
328            0x3 => self.read_minutes_high(),
329            0x4 => self.read_hours_low(),
330            0x5 => self.read_hours_high(),
331            0x6 => self.read_day_low(),
332            0x7 => self.read_day_high(),
333            0x8 => self.read_month_low(),
334            0x9 => self.read_month_high(),
335            0xA => self.read_year_low(),
336            0xB => self.read_year_high(),
337            0xC => self.read_day_of_week(),
338            0xD => self.read_control_1(),
339            0xE => self.read_control_2(),
340            0xF => self.read_control_3(),
341            _ => panic!("Invalid RTC-4513 register: {register:02X}"),
342        }
343    }
344
345    fn write_register(&mut self, register: u8, value: u8) {
346        match register {
347            0x0 => self.write_seconds_low(value),
348            0x1 => self.write_seconds_high(value),
349            0x2 => self.write_minutes_low(value),
350            0x3 => self.write_minutes_high(value),
351            0x4 => self.write_hours_low(value),
352            0x5 => self.write_hours_high(value),
353            0x6 => self.write_day_low(value),
354            0x7 => self.write_day_high(value),
355            0x8 => self.write_month_low(value),
356            0x9 => self.write_month_high(value),
357            0xA => self.write_year_low(value),
358            0xB => self.write_year_high(value),
359            0xC => self.write_day_of_week(value),
360            0xD => self.write_control_1(value),
361            0xE => self.write_control_2(value),
362            0xF => self.write_control_3(value),
363            _ => panic!("Invalid RTC-4513 register: {register:02X}"),
364        }
365    }
366
367    fn read_seconds_low(&self) -> u8 {
368        self.time.seconds % 10
369    }
370
371    fn read_seconds_high(&self) -> u8 {
372        let lost = u8::from(self.time_lost) << 3;
373        lost | (self.time.seconds / 10)
374    }
375
376    fn write_seconds_low(&mut self, value: u8) {
377        self.time.seconds = self.time.seconds / 10 * 10 + (value & 0x0F);
378        log::trace!("  seconds={}", self.time.seconds);
379    }
380
381    fn write_seconds_high(&mut self, value: u8) {
382        self.time.seconds = 10 * (value & 0x07) + (self.time.seconds % 10);
383        self.time_lost = value.bit(3);
384        log::trace!("  seconds={}, time_lost={}", self.time.seconds, self.time_lost);
385    }
386
387    fn read_minutes_low(&self) -> u8 {
388        self.time.minutes % 10
389    }
390
391    fn read_minutes_high(&self) -> u8 {
392        let wrapped = u8::from(self.wrapped) << 3;
393        wrapped | (self.time.minutes / 10)
394    }
395
396    fn write_minutes_low(&mut self, value: u8) {
397        self.time.minutes = self.time.minutes / 10 * 10 + (value & 0x0F);
398        log::trace!("  minutes={}", self.time.minutes);
399    }
400
401    fn write_minutes_high(&mut self, value: u8) {
402        self.time.minutes = 10 * (value & 0x07) + (self.time.minutes % 10);
403        log::trace!("  minutes={}", self.time.minutes);
404    }
405
406    fn read_hours_low(&self) -> u8 {
407        self.time.hours % 10
408    }
409
410    fn read_hours_high(&self) -> u8 {
411        let wrapped = u8::from(self.wrapped) << 3;
412        let am_pm_bit = u8::from(self.time.hour_type.to_bit()) << 2;
413        wrapped | am_pm_bit | (self.time.hours / 10)
414    }
415
416    fn write_hours_low(&mut self, value: u8) {
417        self.time.hours = self.time.hours / 10 * 10 + (value & 0x0F);
418        log::trace!("  hours={}", self.time.hours);
419    }
420
421    fn write_hours_high(&mut self, value: u8) {
422        let mask = match self.time.clock_hours {
423            ClockHours::TwentyFour => 0x03,
424            ClockHours::Twelve => 0x01,
425        };
426        self.time.hours = 10 * (value & mask) + (self.time.hours % 10);
427
428        if self.time.clock_hours == ClockHours::Twelve {
429            self.time.hour_type = HourType::from_bit(value.bit(2));
430        }
431
432        log::trace!("  hours={}, hour_type={}", self.time.hours, self.time.hour_type);
433    }
434
435    fn read_day_low(&self) -> u8 {
436        self.time.day % 10
437    }
438
439    fn read_day_high(&self) -> u8 {
440        let wrapped = u8::from(self.wrapped) << 3;
441        let ram_bit = self.last_day_write_high & 0x04;
442        wrapped | ram_bit | (self.time.day / 10)
443    }
444
445    fn write_day_low(&mut self, value: u8) {
446        self.time.day = self.time.day / 10 * 10 + (value & 0x0F);
447        log::trace!("  day={}", self.time.day);
448    }
449
450    fn write_day_high(&mut self, value: u8) {
451        self.time.day = 10 * (value & 0x03) + (self.time.day % 10);
452        self.last_day_write_high = value & 0x0F;
453        log::trace!("  day={}", self.time.day);
454    }
455
456    fn read_month_low(&self) -> u8 {
457        self.time.month % 10
458    }
459
460    fn read_month_high(&self) -> u8 {
461        let wrapped = u8::from(self.wrapped) << 3;
462        let ram_bits = self.last_month_write_high & 0x06;
463        wrapped | ram_bits | (self.time.month / 10)
464    }
465
466    fn write_month_low(&mut self, value: u8) {
467        self.time.month = self.time.month / 10 * 10 + (value & 0x0F);
468        log::trace!("  month={}", self.time.month);
469    }
470
471    fn write_month_high(&mut self, value: u8) {
472        self.time.month = 10 * (value & 0x01) + (self.time.month % 10);
473        self.last_month_write_high = value & 0x0F;
474        log::trace!("  month={}", self.time.month);
475    }
476
477    fn read_year_low(&self) -> u8 {
478        self.time.year % 10
479    }
480
481    fn read_year_high(&self) -> u8 {
482        self.time.year / 10
483    }
484
485    fn write_year_low(&mut self, value: u8) {
486        self.time.year = self.time.year / 10 * 10 + (value & 0x0F);
487        log::trace!("  year={}", self.time.year);
488    }
489
490    fn write_year_high(&mut self, value: u8) {
491        self.time.year = 10 * (value & 0x0F) + (self.time.year % 10);
492        log::trace!("  year={}", self.time.year);
493    }
494
495    fn read_day_of_week(&self) -> u8 {
496        let wrapped = u8::from(self.wrapped) << 3;
497        wrapped | self.time.day_of_week
498    }
499
500    fn write_day_of_week(&mut self, value: u8) {
501        self.time.day_of_week = value & 0x07;
502        log::trace!("  day_of_week={}", self.time.day_of_week);
503    }
504
505    fn read_control_1(&mut self) -> u8 {
506        let irq = self.irq;
507        self.irq = false;
508
509        u8::from(self.paused)
510            | (u8::from(self.time.calendar_enabled) << 1)
511            | (u8::from(irq && self.irq_enabled) << 2)
512    }
513
514    fn write_control_1(&mut self, value: u8) {
515        // HOLD: Pause clock while set
516        self.paused = value.bit(0);
517
518        // CAL/HW: Calendar enabled (day/month/year)
519        self.time.calendar_enabled = value.bit(1);
520
521        // 30ADJ: Clear seconds, and increment minutes if seconds was >= 30
522        if value.bit(3) {
523            let prev_seconds = self.time.seconds;
524            self.time.seconds = 0;
525
526            if prev_seconds >= 30 {
527                self.time.increment_minutes();
528            }
529        }
530
531        if !self.paused && self.pending_seconds_increment {
532            self.pending_seconds_increment = false;
533            self.time.increment_seconds();
534        }
535
536        log::trace!(
537            "  HOLD={}, CAL/HW={}, 30ADJ={}",
538            self.paused,
539            self.time.calendar_enabled,
540            value.bit(3)
541        );
542    }
543
544    fn read_control_2(&self) -> u8 {
545        u8::from(!self.irq_enabled)
546            | (u8::from(self.irq_duty.to_bit()) << 1)
547            | (self.irq_rate_bits << 2)
548    }
549
550    fn write_control_2(&mut self, value: u8) {
551        // MASK: Mask interrupts
552        self.irq_enabled = !value.bit(0);
553
554        // DUTY: Interrupt duty
555        self.irq_duty = InterruptDuty::from_bit(value.bit(1));
556
557        // RATE: Interrupt rate
558        self.irq_rate_bits = (value >> 2) & 0x03;
559        self.irq_rate_nanos = irq_rate_nanos_from_bits(self.irq_rate_bits);
560
561        log::trace!(
562            "  MASK={}, DUTY={:?}, RATE={}",
563            value.bit(0),
564            self.irq_duty,
565            (value >> 2) & 0x03
566        );
567    }
568
569    fn read_control_3(&self) -> u8 {
570        u8::from(self.reset)
571            | (u8::from(self.stopped) << 1)
572            | (u8::from(self.time.clock_hours.to_bit()) << 2)
573    }
574
575    fn write_control_3(&mut self, value: u8) {
576        // RESET: Clear seconds and stop clock (automatically cleared on chip deselect)
577        self.reset = value.bit(0);
578        if self.reset {
579            self.time.seconds = 0;
580            self.pending_seconds_increment = false;
581        }
582
583        // STOP: Stop clock
584        self.stopped = value.bit(1);
585
586        // 24/12: Select 12-hour clock vs. 24-hour clock
587        self.time.clock_hours = ClockHours::from_bit(value.bit(2));
588        if self.time.clock_hours == ClockHours::TwentyFour {
589            self.time.hour_type = HourType::from_bit(false);
590        }
591
592        log::trace!(
593            "  RESET={}, STOP={}, 24/12={}",
594            self.reset,
595            self.stopped,
596            self.time.clock_hours
597        );
598    }
599
600    fn update_time(&mut self) {
601        if self.stopped || self.reset {
602            self.time.last_update_nanos = timeutils::current_time_nanos();
603            return;
604        }
605
606        let now_nanos = timeutils::current_time_nanos();
607        let elapsed = now_nanos.saturating_sub(self.time.last_update_nanos);
608        let new_time_nanos = u128::from(self.time.nanos) + elapsed;
609        self.time.nanos = (new_time_nanos % 1_000_000_000) as u32;
610        self.time.last_update_nanos = now_nanos;
611
612        for _ in 0..new_time_nanos / 1_000_000_000 {
613            if self.paused {
614                self.pending_seconds_increment = true;
615                break;
616            }
617
618            self.time.increment_seconds();
619            if self.command != Command::None {
620                self.wrapped = true;
621            }
622        }
623
624        if now_nanos - self.last_irq_nanos >= self.irq_rate_nanos {
625            if self.irq_enabled {
626                log::trace!("Flagging IRQ; now = {now_nanos}, last_irq = {}", self.last_irq_nanos);
627                self.irq = true;
628            }
629
630            while self.last_irq_nanos + self.irq_rate_nanos <= now_nanos {
631                self.last_irq_nanos += self.irq_rate_nanos;
632            }
633        }
634
635        // Interrupts with fixed duty expire after 7.8ms
636        if self.irq_duty == InterruptDuty::FixedTime && now_nanos - self.last_irq_nanos >= 7_800_000
637        {
638            self.irq = false;
639        }
640    }
641}
642
643impl Default for Rtc4513 {
644    fn default() -> Self {
645        Self::new()
646    }
647}
648
649fn irq_rate_nanos_from_bits(rate: u8) -> u128 {
650    match rate {
651        // 64 times per second
652        0 => 1_000_000_000 / 64,
653        // Once per second
654        1 => 1_000_000_000,
655        // Once per minute
656        2 => 60 * 1_000_000_000,
657        // Once per hour
658        3 => 60 * 60 * 1_000_000_000,
659        _ => panic!("Invalid RTC-4513 interrupt rate: {rate}"),
660    }
661}