SH-2 free-running timer (FRT)
FRT functionality is not emulated; this module exists only so that the registers are R/W
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 9enum CompareRegister { 10 #[default] 11 A = 0, 12 B = 1, 13} 14 15impl CompareRegister { 16 fn from_bit(bit: bool) -> Self { 17 if bit { Self::B } else { Self::A } 18 } 19} 20 21#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 22enum ClockSource { 23 #[default] 24 InternalDiv8 = 0, 25 InternalDiv32 = 1, 26 InternalDiv128 = 2, 27 External = 3, 28} 29 30impl ClockSource { 31 fn from_byte(byte: u8) -> Self { 32 match byte & 3 { 33 0 => Self::InternalDiv8, 34 1 => Self::InternalDiv32, 35 2 => Self::InternalDiv128, 36 3 => Self::External, 37 _ => unreachable!("value & 3 is always <= 3"), 38 } 39 } 40} 41 42#[derive(Debug, Clone, Encode, Decode)] 43pub struct FreeRunTimer { 44 counter: u16, 45 clock_source: ClockSource, 46 compare_a: u16, 47 compare_b: u16, 48 compare_register: CompareRegister, 49 compare_a_flag: bool, 50 compare_b_flag: bool, 51 overflow_flag: bool, 52 clear_counter_on_a_match: bool, 53 compare_a_interrupt_enabled: bool, 54 compare_b_interrupt_enabled: bool, 55 overflow_interrupt_enabled: bool, 56 // Stored only to emulate these bits being R/W 57 compare_a_pin_output: bool, 58 compare_b_pin_output: bool, 59 // 16-bit temporary register used to prevent data races when the CPU accesses 16-bit registers, 60 // since the timer only supports 8-bit memory accesses 61 temp_register: u16, 62} 63 64impl FreeRunTimer { 65 pub fn new() -> Self { 66 Self { 67 counter: 0, 68 clock_source: ClockSource::default(), 69 compare_a: 0xFFFF, 70 compare_b: 0xFFFF, 71 compare_register: CompareRegister::default(), 72 compare_a_flag: false, 73 compare_b_flag: false, 74 overflow_flag: false, 75 clear_counter_on_a_match: false, 76 compare_a_interrupt_enabled: false, 77 compare_b_interrupt_enabled: false, 78 overflow_interrupt_enabled: false, 79 compare_a_pin_output: false, 80 compare_b_pin_output: false, 81 temp_register: 0, 82 } 83 } 84 85 pub fn read_register(&self, address: u32) -> u8 { 86 log::trace!("Timer register read: {address:08X}"); 87 88 match address & 0xF { 89 0x7 => self.read_tocr(), 90 _ => { 91 log::warn!("FRT register read {address:08X}"); 92 0 93 } 94 } 95 } 96 97 pub fn write_register(&mut self, address: u32, value: u8) { 98 match address & 0xF { 99 0x0 => self.write_tier(value), 100 0x1 => self.write_ftcsr(value), 101 0x2 | 0x4 => { 102 log::trace!("Temp register MSB write: {address:08X} {value:02X}"); 103 self.temp_register.set_msb(value); 104 } 105 0x3 => self.write_frc(value), 106 0x5 => self.write_ocr(value), 107 0x6 => self.write_tcr(value), 108 0x7 => self.write_tocr(value), 109 _ => log::warn!("FRT register write {address:08X} {value:02X}"), 110 } 111 } 112 113 // $FFFFFE10: TIER (Timer interrupt enable register) 114 fn write_tier(&mut self, value: u8) { 115 if value.bit(7) { 116 log::warn!("Input capture interrupt enabled; not implemented"); 117 } 118 119 self.compare_a_interrupt_enabled = value.bit(3); 120 self.compare_b_interrupt_enabled = value.bit(2); 121 self.overflow_interrupt_enabled = value.bit(1); 122 self.clear_counter_on_a_match = value.bit(0); 123 124 log::trace!("TIER write: {value:02X}"); 125 log::trace!(" Compare match A interrupt enabled: {}", self.compare_a_interrupt_enabled); 126 log::trace!(" Compare match B interrupt enabled: {}", self.compare_b_interrupt_enabled); 127 log::trace!(" Overflow interrupt enabled: {}", self.overflow_interrupt_enabled); 128 log::trace!(" Clear counter on compare match A: {}", self.clear_counter_on_a_match); 129 } 130 131 // $FFFFFE11: FTCSR (Free-running timer control/status register) 132 fn write_ftcsr(&mut self, value: u8) { 133 self.compare_a_flag &= value.bit(3); 134 self.compare_b_flag &= value.bit(2); 135 self.overflow_flag &= value.bit(1); 136 self.clear_counter_on_a_match = value.bit(0); 137 138 log::trace!("FTCSR write: {value:02X}"); 139 log::trace!(" Compare A flag clear: {}", !value.bit(3)); 140 log::trace!(" Compare B flag clear: {}", !value.bit(2)); 141 log::trace!(" Overflow flag clear: {}", !value.bit(1)); 142 log::trace!(" Clear counter on compare match A: {}", self.clear_counter_on_a_match); 143 } 144 145 // $FFFFFE12-$FFFFFE13: FRC (Free-running counter) 146 fn write_frc(&mut self, value: u8) { 147 self.counter = (self.temp_register & 0xFF00) | u16::from(value); 148 log::trace!("FRC write: {value:02X}"); 149 log::trace!(" Counter: {:04X}", self.counter); 150 } 151 152 // $FFFFFE14-$FFFFFE15: OCRA/B (Output compare register A/B) 153 fn write_ocr(&mut self, value: u8) { 154 let word = (self.temp_register & 0xFF00) | u16::from(value); 155 156 match self.compare_register { 157 CompareRegister::A => { 158 self.compare_a = word; 159 log::trace!("Compare match A value: {word:04X}"); 160 } 161 CompareRegister::B => { 162 self.compare_b = word; 163 log::trace!("Compare match B value: {word:04X}"); 164 } 165 } 166 } 167 168 // $FFFFFE16: TCR (Timer control register) 169 fn write_tcr(&mut self, value: u8) { 170 self.clock_source = ClockSource::from_byte(value); 171 172 if self.clock_source == ClockSource::External { 173 log::warn!("Timer clock source set to external clock; not implemented"); 174 } 175 176 log::trace!("TCR write: {value:02X}"); 177 log::trace!(" Clock source: {:?}", self.clock_source); 178 log::trace!(" Input edge select: {}", if value.bit(7) { "Rising" } else { "Falling" }); 179 } 180 181 // $FFFFFE17: TOCR (Timer output compare control register) 182 fn read_tocr(&self) -> u8 { 183 0xE0 | ((self.compare_register as u8) << 4) 184 | (u8::from(self.compare_a_pin_output) << 1) 185 | u8::from(self.compare_b_pin_output) 186 } 187 188 // $FFFFFE17: TOCR (Timer output compare control register) 189 fn write_tocr(&mut self, value: u8) { 190 self.compare_register = CompareRegister::from_bit(value.bit(4)); 191 self.compare_a_pin_output = value.bit(1); 192 self.compare_b_pin_output = value.bit(0); 193 194 log::trace!("TOCR write: {value:02X}"); 195 log::trace!(" Compare match register: {:?}", self.compare_register); 196 log::trace!(" Compare A output pin level: {}", self.compare_a_pin_output); 197 log::trace!(" Compare B output pin level: {}", self.compare_b_pin_output); 198 } 199}