1//! S-RTC coprocessor, a Sharp real-time clock chip 2//! 3//! Used by Daikaijuu Monogatari II 4 5use bincode::{Decode, Encode}; 6use jgenesis_common::timeutils; 7use time::Weekday; 8 9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 10enum ReadState { 11 #[default] 12 Ack, 13 Digit { 14 idx: u8, 15 }, 16 End, 17} 18 19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 20enum WriteState { 21 #[default] 22 Start, 23 Command, 24 Digit { 25 idx: u8, 26 }, 27 End, 28} 29 30trait WeekdayExt { 31 fn to_srtc_u8(self) -> u8; 32} 33 34impl WeekdayExt for Weekday { 35 fn to_srtc_u8(self) -> u8 { 36 match self { 37 Self::Sunday => 0, 38 Self::Monday => 1, 39 Self::Tuesday => 2, 40 Self::Wednesday => 3, 41 Self::Thursday => 4, 42 Self::Friday => 5, 43 Self::Saturday => 6, 44 } 45 } 46} 47 48#[derive(Debug, Clone, Encode, Decode)] 49pub struct SRtc { 50 last_update_nanos: u128, 51 nanos: u32, 52 seconds: u8, 53 minutes: u8, 54 hours: u8, 55 day: u8, 56 month: u8, 57 year: u8, 58 century: u8, 59 day_of_week: u8, 60 read_state: ReadState, 61 write_state: WriteState, 62} 63 64impl Default for SRtc { 65 fn default() -> Self { 66 Self::new() 67 } 68} 69 70impl SRtc { 71 #[must_use] 72 pub fn new() -> Self { 73 Self { 74 last_update_nanos: timeutils::current_time_nanos(), 75 nanos: 0, 76 seconds: 0, 77 minutes: 0, 78 hours: 0, 79 day: 1, 80 month: 1, 81 year: 0, 82 century: 9, 83 day_of_week: 0, 84 read_state: ReadState::default(), 85 write_state: WriteState::default(), 86 } 87 } 88 89 #[allow(clippy::missing_panics_doc)] 90 #[inline] 91 #[must_use] 92 pub fn read(&mut self) -> u8 { 93 log::trace!("S-RTC read"); 94 95 self.write_state = WriteState::default(); 96 97 self.update_time(); 98 99 match self.read_state { 100 ReadState::Ack => { 101 self.read_state = ReadState::Digit { idx: 0 }; 102 0x0F 103 } 104 ReadState::Digit { idx } => { 105 let value = match idx { 106 0 => self.seconds % 10, 107 1 => self.seconds / 10, 108 2 => self.minutes % 10, 109 3 => self.minutes / 10, 110 4 => self.hours % 10, 111 5 => self.hours / 10, 112 6 => self.day % 10, 113 7 => self.day / 10, 114 8 => self.month, 115 9 => self.year % 10, 116 10 => self.year / 10, 117 11 => self.century, 118 12 => self.day_of_week, 119 _ => panic!("Invalid S-RTC timestamp index: {idx}"), 120 }; 121 122 self.read_state = match idx { 123 12 => ReadState::End, 124 _ => ReadState::Digit { idx: idx + 1 }, 125 }; 126 127 log::trace!( 128 "S-RTC read, sending value {value:X} for idx {idx}; current state is {self:?}" 129 ); 130 131 value 132 } 133 ReadState::End => { 134 self.read_state = ReadState::Ack; 135 0x0F 136 } 137 } 138 } 139 140 #[inline] 141 pub fn write(&mut self, value: u8) { 142 log::trace!("S-RTC write {value:X}"); 143 144 self.read_state = ReadState::default(); 145 146 self.update_time(); 147 148 // This is a 4-bit port 149 let value = value & 0x0F; 150 151 match self.write_state { 152 WriteState::Start => { 153 if value == 0x0E { 154 self.write_state = WriteState::Command; 155 } 156 } 157 WriteState::Command => { 158 match value { 159 0x04 => { 160 // Unknown command; possibly sets 24-hour mode? 161 self.write_state = WriteState::End; 162 } 163 0x00 => { 164 self.write_state = WriteState::Digit { idx: 0 }; 165 } 166 _ => {} 167 } 168 } 169 WriteState::Digit { idx } => { 170 self.write_timestamp_digit(idx, value); 171 172 self.write_state = match idx { 173 11 => WriteState::End, 174 _ => WriteState::Digit { idx: idx + 1 }, 175 }; 176 } 177 WriteState::End => { 178 if value == 0x0D { 179 self.write_state = WriteState::Start; 180 } 181 } 182 } 183 } 184 185 pub fn reset_state(&mut self) { 186 self.read_state = ReadState::default(); 187 self.write_state = WriteState::default(); 188 } 189 190 fn write_timestamp_digit(&mut self, idx: u8, value: u8) { 191 match idx { 192 0 => { 193 self.seconds = self.seconds / 10 * 10 + value; 194 } 195 1 => { 196 self.seconds = 10 * value + (self.seconds % 10); 197 } 198 2 => { 199 self.minutes = self.minutes / 10 * 10 + value; 200 } 201 3 => { 202 self.minutes = 10 * value + (self.minutes % 10); 203 } 204 4 => { 205 self.hours = self.hours / 10 * 10 + value; 206 } 207 5 => { 208 self.hours = 10 * value + (self.hours % 10); 209 } 210 6 => { 211 self.day = self.day / 10 * 10 + value; 212 self.update_day_of_week(); 213 } 214 7 => { 215 self.day = 10 * value + (self.day % 10); 216 self.update_day_of_week(); 217 } 218 8 => { 219 self.month = value; 220 self.update_day_of_week(); 221 } 222 9 => { 223 self.year = self.year / 10 * 10 + value; 224 self.update_day_of_week(); 225 } 226 10 => { 227 self.year = 10 * value + (self.year % 10); 228 self.update_day_of_week(); 229 } 230 11 => { 231 self.century = value; 232 self.update_day_of_week(); 233 } 234 _ => panic!("Invalid S-RTC timestamp index: {idx}"), 235 } 236 237 log::trace!("S-RTC timestamp write, index {idx} value {value:X}; new time is {self:?}"); 238 } 239 240 fn update_day_of_week(&mut self) { 241 self.day_of_week = 242 timeutils::day_of_week(self.day, self.month, four_digit_year(self.year, self.century)) 243 .to_srtc_u8(); 244 } 245 246 fn update_time(&mut self) { 247 let now_nanos = timeutils::current_time_nanos(); 248 let elapsed = now_nanos.saturating_sub(self.last_update_nanos); 249 self.last_update_nanos = now_nanos; 250 251 let new_nanos = u128::from(self.nanos) + elapsed; 252 self.nanos = (new_nanos % 1_000_000_000) as u32; 253 254 for _ in 0..new_nanos / 1_000_000_000 { 255 self.increment_seconds(); 256 } 257 } 258 259 fn increment_seconds(&mut self) { 260 self.seconds += 1; 261 if self.seconds >= 60 { 262 self.seconds = 0; 263 self.increment_minutes(); 264 } 265 } 266 267 fn increment_minutes(&mut self) { 268 self.minutes += 1; 269 if self.minutes >= 60 { 270 self.minutes = 0; 271 self.increment_hours(); 272 } 273 } 274 275 fn increment_hours(&mut self) { 276 self.hours += 1; 277 if self.hours >= 24 { 278 self.hours = 0; 279 self.increment_day(); 280 } 281 } 282 283 fn increment_day(&mut self) { 284 self.day += 1; 285 self.day_of_week = (self.day_of_week + 1) % 7; 286 287 if self.day > timeutils::days_in_month(self.month, self.year) { 288 self.day = 1; 289 self.increment_month(); 290 } 291 } 292 293 fn increment_month(&mut self) { 294 self.month += 1; 295 if self.month > 12 { 296 self.month = 1; 297 self.increment_year(); 298 } 299 } 300 301 fn increment_year(&mut self) { 302 self.year += 1; 303 if self.year > 99 { 304 self.year = 0; 305 self.century += 1; 306 } 307 } 308} 309 310fn four_digit_year(year: u8, century: u8) -> u16 { 311 1000 + 100 * u16::from(century) + u16::from(year) 312}