timer.rsannotatedtimer.rssource182 lines · 4.8 KB · raw
1use bincode::{Decode, Encode};
2use jgenesis_common::frontend::TimingMode;
3use jgenesis_common::num::{GetBit, U16Ext};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
6pub enum TimerMode {
7    #[default]
8    HV,
9    Linear,
10}
11
12impl TimerMode {
13    fn from_bit(bit: bool) -> Self {
14        if bit { Self::Linear } else { Self::HV }
15    }
16
17    const fn max_h(self) -> u16 {
18        match self {
19            Self::HV => 341,
20            Self::Linear => 512,
21        }
22    }
23
24    fn max_v(self, timing_mode: TimingMode) -> u16 {
25        match (self, timing_mode) {
26            (Self::HV, TimingMode::Ntsc) => 262,
27            (Self::HV, TimingMode::Pal) => 312,
28            (Self::Linear, _) => 512,
29        }
30    }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
34pub enum TimerIrqMode {
35    #[default]
36    Off,
37    H,
38    V,
39    HV,
40}
41
42impl TimerIrqMode {
43    fn from_byte(byte: u8) -> Self {
44        match byte & 0x03 {
45            0x00 => Self::Off,
46            0x01 => Self::H,
47            0x02 => Self::V,
48            0x03 => Self::HV,
49            _ => unreachable!("value & 0x03 is always <= 0x03"),
50        }
51    }
52}
53
54#[derive(Debug, Clone, Encode, Decode)]
55pub struct Sa1Timer {
56    pub mode: TimerMode,
57    pub irq_mode: TimerIrqMode,
58    pub timing_mode: TimingMode,
59    pub h_cpu_ticks: u16,
60    pub v: u16,
61    pub max_h_cpu_ticks: u16,
62    pub max_v: u16,
63    pub irq_htime_cpu_ticks: u16,
64    pub irq_vtime: u16,
65    pub irq_pending: bool,
66    pub latched_h: u16,
67    pub latched_v: u16,
68}
69
70impl Sa1Timer {
71    pub fn new(timing_mode: TimingMode) -> Self {
72        Self {
73            mode: TimerMode::default(),
74            irq_mode: TimerIrqMode::default(),
75            timing_mode,
76            h_cpu_ticks: 0,
77            v: 0,
78            max_h_cpu_ticks: TimerMode::default().max_h() << 1,
79            max_v: TimerMode::default().max_v(timing_mode),
80            irq_htime_cpu_ticks: 0,
81            irq_vtime: 0,
82            irq_pending: false,
83            latched_h: 0,
84            latched_v: 0,
85        }
86    }
87
88    pub fn read_hcr_low(&mut self) -> u8 {
89        // Reading HCR low byte latches both H and V
90        self.latched_h = self.h_cpu_ticks >> 1;
91        self.latched_v = self.v;
92
93        self.latched_h.lsb()
94    }
95
96    pub fn read_hcr_high(&self) -> u8 {
97        self.latched_h.msb()
98    }
99
100    pub fn read_vcr_low(&self) -> u8 {
101        self.latched_v.lsb()
102    }
103
104    pub fn read_vcr_high(&self) -> u8 {
105        self.latched_v.msb()
106    }
107
108    pub fn write_tmc(&mut self, value: u8) {
109        self.mode = TimerMode::from_bit(value.bit(7));
110        self.irq_mode = TimerIrqMode::from_byte(value);
111
112        if self.irq_mode == TimerIrqMode::Off {
113            self.irq_pending = false;
114        }
115
116        self.max_h_cpu_ticks = self.mode.max_h() << 1;
117        self.max_v = self.mode.max_v(self.timing_mode);
118
119        log::trace!("  H/V timer mode: {:?}", self.mode);
120        log::trace!("  H/V IRQ mode: {:?}", self.irq_mode);
121    }
122
123    pub fn write_hcnt_low(&mut self, value: u8) {
124        let htime = ((self.irq_htime_cpu_ticks >> 1) & 0x100) | u16::from(value);
125        self.irq_htime_cpu_ticks = (htime << 1) | (self.irq_htime_cpu_ticks & 0x1);
126
127        log::trace!("  IRQ HTIME: {}", self.irq_htime_cpu_ticks >> 1);
128    }
129
130    pub fn write_hcnt_high(&mut self, value: u8) {
131        let htime = ((self.irq_htime_cpu_ticks >> 1) & 0x0FF) | (u16::from(value & 0x01) << 8);
132        self.irq_htime_cpu_ticks = (htime << 1) | (self.irq_htime_cpu_ticks & 0x1);
133
134        log::trace!("  IRQ HTIME: {}", self.irq_htime_cpu_ticks >> 1);
135    }
136
137    pub fn write_vcnt_low(&mut self, value: u8) {
138        self.irq_vtime = (self.irq_vtime & 0x100) | u16::from(value);
139
140        log::trace!("  IRQ VTIME: {}", self.irq_vtime);
141    }
142
143    pub fn write_vcnt_high(&mut self, value: u8) {
144        self.irq_vtime = (self.irq_vtime & 0x0FF) | (u16::from(value & 0x01) << 8);
145
146        log::trace!("  IRQ VTIME: {}", self.irq_vtime);
147    }
148
149    pub fn tick(&mut self) {
150        self.h_cpu_ticks += 1;
151
152        if self.h_cpu_ticks >= self.max_h_cpu_ticks {
153            self.h_cpu_ticks -= self.max_h_cpu_ticks;
154            self.v += 1;
155
156            if self.v >= self.max_v {
157                self.v = 0;
158            }
159
160            if self.irq_mode == TimerIrqMode::V && self.v == self.irq_vtime {
161                self.irq_pending = true;
162            }
163        }
164
165        match self.irq_mode {
166            TimerIrqMode::H if self.h_cpu_ticks == self.irq_htime_cpu_ticks => {
167                self.irq_pending = true;
168            }
169            TimerIrqMode::HV
170                if self.h_cpu_ticks == self.irq_htime_cpu_ticks && self.v == self.irq_vtime =>
171            {
172                self.irq_pending = true;
173            }
174            _ => {}
175        }
176    }
177
178    pub fn reset(&mut self) {
179        self.h_cpu_ticks = 0;
180        self.v = 0;
181    }
182}