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}