1//! SH-2 internal I/O registers (accessed using A29-31 = 111) 2 3use crate::bus::BusInterface; 4use crate::dma::DmaController; 5use crate::sci::SerialInterface; 6use crate::wdt::WatchdogTimer; 7use crate::{RESET_INTERRUPT_MASK, Sh2}; 8use bincode::{Decode, Encode}; 9use jgenesis_common::num::{GetBit, U16Ext}; 10 11#[derive(Debug, Clone, Copy, Encode, Decode)] 12pub struct StatusRegister { 13 // Interrupt levels <= this value are masked 14 pub interrupt_mask: u8, 15 // Used as a carry/test flag by many instructions 16 pub t: bool, 17 // Saturation flag used by multiply-accumulate instructions 18 pub s: bool, 19 // Flags used by division instructions 20 pub q: bool, 21 pub m: bool, 22} 23 24impl Default for StatusRegister { 25 fn default() -> Self { 26 Self { t: false, s: false, interrupt_mask: RESET_INTERRUPT_MASK, q: false, m: false } 27 } 28} 29 30impl From<u32> for StatusRegister { 31 fn from(value: u32) -> Self { 32 Self { 33 interrupt_mask: ((value >> 4) & 0xF) as u8, 34 t: value.bit(0), 35 s: value.bit(1), 36 q: value.bit(8), 37 m: value.bit(9), 38 } 39 } 40} 41 42impl From<StatusRegister> for u32 { 43 fn from(value: StatusRegister) -> Self { 44 (u32::from(value.m) << 9) 45 | (u32::from(value.q) << 8) 46 | (u32::from(value.interrupt_mask) << 4) 47 | (u32::from(value.s) << 1) 48 | u32::from(value.t) 49 } 50} 51 52#[derive(Debug, Clone, Default, Encode, Decode)] 53pub struct Sh2Registers { 54 // General-purpose registers 55 pub gpr: [u32; 16], 56 // Status register 57 pub sr: StatusRegister, 58 // Global base register (used with GBR addressing modes) 59 pub gbr: u32, 60 // Vector base register (base of exception vector area) 61 pub vbr: u32, 62 // Multiply-accumulator 63 pub macl: u32, 64 pub mach: u32, 65 // Procedure register (return address) 66 pub pr: u32, 67 // Program counter 68 pub pc: u32, 69 pub next_pc: u32, 70 // Set when next_pc is changed by an instruction with a branch delay slot 71 pub next_op_in_delay_slot: bool, 72} 73 74impl Sh2Registers { 75 pub fn mac(&self) -> i64 { 76 (i64::from(self.mach) << 32) | i64::from(self.macl) 77 } 78 79 pub fn set_mac(&mut self, mac: i64) { 80 self.macl = mac as u32; 81 self.mach = ((mac as u64) >> 32) as u32; 82 } 83} 84 85// User break functionality is not emulated, but After Burner Complete uses these R/W registers to 86// store state in its audio processing code 87#[derive(Debug, Clone, Default, Encode, Decode)] 88pub struct BreakRegisters { 89 pub break_address_a: u32, 90 pub break_address_b: u32, 91} 92 93impl BreakRegisters { 94 fn read_break_address_a_high(&self) -> u16 { 95 (self.break_address_a >> 16) as u16 96 } 97 98 fn read_break_address_a_low(&self) -> u16 { 99 self.break_address_a as u16 100 } 101 102 fn read_break_address_b_high(&self) -> u16 { 103 (self.break_address_b >> 16) as u16 104 } 105 106 fn read_break_address_b_low(&self) -> u16 { 107 self.break_address_b as u16 108 } 109 110 fn write_break_address_a(&mut self, value: u32) { 111 self.break_address_a = value; 112 log::trace!("Break address A write: {value:08X}"); 113 } 114 115 fn write_break_address_a_high(&mut self, value: u16) { 116 self.break_address_a = (self.break_address_a & 0xFFFF) | (u32::from(value) << 16); 117 log::trace!("Break address A high write: {value:04X}"); 118 } 119 120 fn write_break_address_a_low(&mut self, value: u16) { 121 self.break_address_a = (self.break_address_a & !0xFFFF) | u32::from(value); 122 log::trace!("Break address A low write: {value:04X}"); 123 } 124 125 fn write_break_address_b(&mut self, value: u32) { 126 self.break_address_b = value; 127 log::trace!("Break address B write: {value:08X}"); 128 } 129 130 fn write_break_address_b_high(&mut self, value: u16) { 131 self.break_address_b = (self.break_address_b & 0xFFFF) | (u32::from(value) << 16); 132 log::trace!("Break address B high write: {value:04X}"); 133 } 134 135 fn write_break_address_b_low(&mut self, value: u16) { 136 self.break_address_b = (self.break_address_b & !0xFFFF) | u32::from(value); 137 log::trace!("Break address B low write: {value:04X}"); 138 } 139} 140 141#[derive(Debug, Clone, Default, Encode, Decode)] 142pub struct InterruptRegisters { 143 pub divu_priority: u8, 144 pub dmac_priority: u8, 145 pub dma0_vector: u8, 146 pub dma1_vector: u8, 147 pub wdt_priority: u8, 148 pub wdt_vector: u8, 149 pub bsc_vector: u8, 150 pub sci_priority: u8, 151 pub sci_rx_error_vector: u8, 152 pub sci_rx_ok_vector: u8, 153 pub sci_tx_empty_vector: u8, 154 pub sci_transfer_end_vector: u8, 155 pub frt_priority: u8, 156} 157 158impl InterruptRegisters { 159 // $FFFFFEE2: IPRA (Interrupt priority A) 160 fn read_ipra(&self) -> u16 { 161 (u16::from(self.divu_priority) << 12) 162 | (u16::from(self.dmac_priority) << 8) 163 | (u16::from(self.wdt_priority) << 4) 164 } 165 166 // $FFFFFEE2: IPRA (Interrupt priority A) 167 fn write_ipra(&mut self, value: u16) { 168 self.divu_priority = (value >> 12) as u8; 169 self.dmac_priority = ((value >> 8) & 0xF) as u8; 170 self.wdt_priority = ((value >> 4) & 0xF) as u8; 171 172 log::debug!("IPRA write: {value:04X}"); 173 log::debug!(" DIVU interrupt priority: {}", self.divu_priority); 174 log::debug!(" DMAC interrupt priority: {}", self.dmac_priority); 175 log::debug!(" WDT interrupt priority: {}", self.wdt_priority); 176 } 177 178 fn write_ipra_high(&mut self, value: u8) { 179 self.divu_priority = value >> 4; 180 self.dmac_priority = value & 0xF; 181 182 log::debug!("IPRA high write: {value:02X}"); 183 log::debug!(" DIVU interrupt priority: {}", self.divu_priority); 184 log::debug!(" DMAC interrupt priority: {}", self.dmac_priority); 185 } 186 187 fn write_ipra_low(&mut self, value: u8) { 188 self.wdt_priority = value >> 4; 189 190 log::debug!("IPRA low write: {value:02X}"); 191 log::debug!(" WDT interrupt priority: {}", self.wdt_priority); 192 } 193 194 // $FFFFFE60: IPRB (Interrupt priority B) 195 fn read_iprb(&self) -> u16 { 196 (u16::from(self.sci_priority) << 12) | (u16::from(self.frt_priority) << 8) 197 } 198 199 // $FFFFFE60: IPRB (Interrupt priority B) 200 fn write_iprb(&mut self, value: u16) { 201 self.sci_priority = (value >> 12) as u8; 202 self.frt_priority = ((value >> 8) & 0xF) as u8; 203 204 log::debug!("IPRB write: {value:04X}"); 205 log::debug!(" SCI interrupt priority: {}", self.sci_priority); 206 log::debug!(" FRT interrupt priority: {}", self.frt_priority); 207 } 208 209 fn write_iprb_high(&mut self, value: u8) { 210 // IPRB low byte is not used; just shift the value 211 self.write_iprb(u16::from(value) << 8); 212 } 213 214 // $FFFFFE62: VCRA (Vector number register A) 215 fn read_vcra(&self) -> u16 { 216 (u16::from(self.sci_rx_error_vector) << 8) | u16::from(self.sci_rx_ok_vector) 217 } 218 219 // $FFFFFE62: VCRA (Vector number register A) 220 fn write_vcra(&mut self, value: u16) { 221 self.sci_rx_error_vector = ((value >> 8) & 0x7F) as u8; 222 self.sci_rx_ok_vector = (value & 0x7F) as u8; 223 224 log::debug!("VCRA write: {value:04X}"); 225 log::debug!(" SCI RX error vector number: {}", self.sci_rx_error_vector); 226 log::debug!(" SCI RX ok vector number: {}", self.sci_rx_ok_vector); 227 } 228 229 fn write_vcra_high(&mut self, value: u8) { 230 self.sci_rx_error_vector = value & 0x7F; 231 232 log::debug!("VCRA high write: {value:02X}"); 233 log::debug!(" SCI RX error vector number: {}", self.sci_rx_error_vector); 234 } 235 236 fn write_vcra_low(&mut self, value: u8) { 237 self.sci_rx_ok_vector = value & 0x7F; 238 239 log::debug!("VCRA low write: {value:02X}"); 240 log::debug!(" SCI RX ok vector number: {}", self.sci_rx_ok_vector); 241 } 242 243 // $FFFFFE64: VCRB (Vector number register B) 244 fn read_vcrb(&self) -> u16 { 245 (u16::from(self.sci_tx_empty_vector) << 8) | u16::from(self.sci_transfer_end_vector) 246 } 247 248 // $FFFFFE64: VCRB (Vector number register B) 249 fn write_vcrb(&mut self, value: u16) { 250 self.sci_tx_empty_vector = ((value >> 8) & 0x7F) as u8; 251 self.sci_transfer_end_vector = (value & 0x7F) as u8; 252 253 log::debug!("VCRB write: {value:04X}"); 254 log::debug!(" SCI TX empty vector number: {}", self.sci_tx_empty_vector); 255 log::debug!(" SCI transfer end vector number: {}", self.sci_transfer_end_vector); 256 } 257 258 fn write_vcrb_high(&mut self, value: u8) { 259 self.sci_tx_empty_vector = value & 0x7F; 260 261 log::debug!("VCRB high write: {value:02X}"); 262 log::debug!(" SCI TX empty vector number: {}", self.sci_tx_empty_vector); 263 } 264 265 fn write_vcrb_low(&mut self, value: u8) { 266 self.sci_transfer_end_vector = value & 0x7F; 267 268 log::debug!("VCRB low write: {value:02X}"); 269 log::debug!(" SCI transfer end vector number: {}", self.sci_transfer_end_vector); 270 } 271 272 // $FFFFFEE4: VCRWDT (WDT interrupt vector number) 273 fn read_vcrwdt(&self) -> u16 { 274 (u16::from(self.wdt_vector) << 8) | u16::from(self.bsc_vector) 275 } 276 277 // $FFFFFEE4: VCRWDT (WDT interrupt vector number) 278 fn write_vcrwdt(&mut self, value: u16) { 279 self.wdt_vector = ((value >> 8) & 0x7F) as u8; 280 self.bsc_vector = (value & 0x7F) as u8; 281 282 log::debug!("VCRWDT write: {value:04X}"); 283 log::debug!(" WDT interrupt vector number: {}", self.wdt_vector); 284 log::debug!(" BSC interrupt vector number: {}", self.bsc_vector); 285 } 286 287 fn write_vcrwdt_high(&mut self, value: u8) { 288 self.wdt_vector = value & 0x7F; 289 290 log::debug!("VCRWDT high write: {value:02X}"); 291 log::debug!(" WDT interrupt vector number: {}", self.wdt_vector); 292 } 293 294 fn write_vcrwdt_low(&mut self, value: u8) { 295 self.bsc_vector = value & 0x7F; 296 297 log::debug!("VCRWDT low write: {value:02X}"); 298 log::debug!(" BSC interrupt vector number: {}", self.bsc_vector); 299 } 300 301 // $FFFFFFA0: VCRDMA0 (Interrupt vector number for DMA0) 302 fn write_vcrdma0(&mut self, value: u32) { 303 self.dma0_vector = value as u8; 304 305 log::debug!("VCRDMA0 write: {value:08X}"); 306 log::debug!(" DMA0 vector number: {}", self.dma0_vector); 307 } 308 309 // $FFFFFFA8: VCRDMA1 (Interrupt vector number for DMA1) 310 fn write_vcrdma1(&mut self, value: u32) { 311 self.dma1_vector = value as u8; 312 313 log::debug!("VCRDMA1 write: {value:08X}"); 314 log::debug!(" DMA1 vector number: {}", self.dma1_vector); 315 } 316} 317 318#[derive(Debug, Clone, Copy, Default, Encode, Decode)] 319pub struct InternalInterrupt { 320 pub priority: u8, 321 pub vector_number: u8, 322} 323 324#[derive(Debug, Clone, Encode, Decode)] 325pub struct Sh7604Registers { 326 pub break_registers: BreakRegisters, 327 pub interrupts: InterruptRegisters, 328 pub internal_interrupt: InternalInterrupt, 329} 330 331impl Sh7604Registers { 332 pub fn new() -> Self { 333 Self { 334 break_registers: BreakRegisters::default(), 335 interrupts: InterruptRegisters::default(), 336 internal_interrupt: InternalInterrupt::default(), 337 } 338 } 339 340 pub fn update_interrupt_level( 341 &mut self, 342 dma_controller: &DmaController, 343 watchdog_timer: &WatchdogTimer, 344 serial: &SerialInterface, 345 ) { 346 self.internal_interrupt = InternalInterrupt::default(); 347 348 if self.interrupts.dmac_priority != 0 { 349 if dma_controller.channels[0].control.interrupt_pending() { 350 self.internal_interrupt = InternalInterrupt { 351 priority: self.interrupts.dmac_priority, 352 vector_number: self.interrupts.dma0_vector, 353 }; 354 } else if dma_controller.channels[1].control.interrupt_pending() { 355 self.internal_interrupt = InternalInterrupt { 356 priority: self.interrupts.dmac_priority, 357 vector_number: self.interrupts.dma1_vector, 358 }; 359 } 360 } 361 362 if serial.rx_interrupt_pending() 363 && self.interrupts.sci_priority > self.internal_interrupt.priority 364 { 365 self.internal_interrupt = InternalInterrupt { 366 priority: self.interrupts.sci_priority, 367 vector_number: self.interrupts.sci_rx_ok_vector, 368 }; 369 } 370 371 if watchdog_timer.overflow_flag() 372 && self.interrupts.wdt_priority > self.internal_interrupt.priority 373 { 374 self.internal_interrupt = InternalInterrupt { 375 priority: self.interrupts.wdt_priority, 376 vector_number: self.interrupts.wdt_vector, 377 }; 378 } 379 } 380} 381 382// The SH7604 manual says there's a 3-12 cycle latency on FRT accesses, though it's unclear whether 383// this applies to only FRC or to all FRT registers. 384// Applying to all registers fixes some timing issues around line 224 HINT and VINT 385const FRT_WAIT_CYCLES: u64 = 7; 386 387impl Sh2 { 388 #[allow(clippy::match_same_arms)] 389 pub(super) fn read_internal_register_byte( 390 &self, 391 address: u32, 392 bus: &mut impl BusInterface, 393 ) -> u8 { 394 log::trace!("[{}] Internal register byte read: {address:08X}", self.name); 395 396 match address { 397 0xFFFFFC17 => { 398 // Cosmic Carnage constantly accesses this address - not sure what it's supposed to be 399 0 400 } 401 0xFFFFFE00..=0xFFFFFE05 => self.serial.read_register(address), 402 0xFFFFFE10..=0xFFFFFE19 => { 403 bus.increment_cycle_counter(FRT_WAIT_CYCLES); 404 self.free_run_timer.read_register(address) 405 } 406 0xFFFFFE60 => self.sh7604.interrupts.read_iprb().msb(), 407 0xFFFFFE61 => self.sh7604.interrupts.read_iprb().lsb(), 408 0xFFFFFE62 => self.sh7604.interrupts.read_vcra().msb(), 409 0xFFFFFE63 => self.sh7604.interrupts.read_vcra().lsb(), 410 0xFFFFFE64 => self.sh7604.interrupts.read_vcrb().msb(), 411 0xFFFFFE65 => self.sh7604.interrupts.read_vcrb().lsb(), 412 0xFFFFFE80 => self.watchdog_timer.read_control(), 413 0xFFFFFE81 => self.watchdog_timer.read_counter(), 414 0xFFFFFE92 => self.cache.read_control(), 415 // Unmapped addresses; some games access them for unknown reasons 416 0xFFFFFE93..=0xFFFFFE9F => 0, 417 0xFFFFFEE2 => self.sh7604.interrupts.read_ipra().msb(), 418 0xFFFFFEE3 => self.sh7604.interrupts.read_ipra().lsb(), 419 0xFFFFFEE4 => self.sh7604.interrupts.read_vcrwdt().msb(), 420 0xFFFFFEE5 => self.sh7604.interrupts.read_vcrwdt().lsb(), 421 _ => { 422 log::warn!("[{}] Unexpected internal register byte read: {address:08X}", self.name); 423 0 424 } 425 } 426 } 427 428 pub(super) fn read_internal_register_word(&self, address: u32) -> u16 { 429 log::trace!("[{}] Internal register word read: {address:08X}", self.name); 430 431 match address { 432 0xFFFFFE60 => self.sh7604.interrupts.read_iprb(), 433 0xFFFFFE62 => self.sh7604.interrupts.read_vcra(), 434 0xFFFFFE64 => self.sh7604.interrupts.read_vcrb(), 435 0xFFFFFEE2 => self.sh7604.interrupts.read_ipra(), 436 0xFFFFFEE4 => self.sh7604.interrupts.read_vcrwdt(), 437 0xFFFFFF08 => (self.divu.read_control() >> 16) as u16, 438 0xFFFFFF0A => self.divu.read_control() as u16, 439 0xFFFFFF40 => self.sh7604.break_registers.read_break_address_a_high(), 440 0xFFFFFF42 => self.sh7604.break_registers.read_break_address_a_low(), 441 0xFFFFFF60 => self.sh7604.break_registers.read_break_address_b_high(), 442 0xFFFFFF62 => self.sh7604.break_registers.read_break_address_b_low(), 443 _ => { 444 log::warn!("[{}] Unexpected internal register word read: {address:08X}", self.name); 445 0 446 } 447 } 448 } 449 450 pub(super) fn read_internal_register_longword(&mut self, address: u32) -> u32 { 451 log::trace!("[{}] Internal register longword read: {address:08X}", self.name); 452 453 match address { 454 0xFFFFFF00..=0xFFFFFF1F => self.divu.read_register(address), 455 // Break registers; break functionality is not implemented but some games use the registers as R/W storage 456 0xFFFFFF40 => self.sh7604.break_registers.break_address_a, 457 0xFFFFFF60 => self.sh7604.break_registers.break_address_b, 458 0xFFFFFF80..=0xFFFFFF9F | 0xFFFFFFB0 => self.dmac.read_register(address), 459 0xFFFFFFA0 => self.sh7604.interrupts.dma0_vector.into(), 460 0xFFFFFFA8 => self.sh7604.interrupts.dma1_vector.into(), 461 // Bus control register; not emulated, 32X games only ever write this value to it before reading 462 0xFFFFFFE0 => 0xA55A0001, 463 _ => { 464 log::warn!( 465 "[{}] Unexpected internal register longword read: {address:08X}", 466 self.name 467 ); 468 0 469 } 470 } 471 } 472 473 #[allow(clippy::match_same_arms)] 474 pub(super) fn write_internal_register_byte( 475 &mut self, 476 address: u32, 477 value: u8, 478 bus: &mut impl BusInterface, 479 ) { 480 log::trace!("[{}] Internal register byte write: {address:08X} {value:02X}", self.name); 481 482 match address { 483 // Cosmic Carnage constantly accesses this address - not sure what it's supposed to be 484 0xFFFFFC17 => {} 485 0xFFFFFE00..=0xFFFFFE05 => self.serial.write_register(address, value), 486 0xFFFFFE10..=0xFFFFFE19 => { 487 bus.increment_cycle_counter(FRT_WAIT_CYCLES); 488 self.free_run_timer.write_register(address, value); 489 } 490 0xFFFFFE60 => self.sh7604.interrupts.write_iprb_high(value), 491 // IPRB low byte; does not do anything 492 0xFFFFFE61 => {} 493 0xFFFFFE62 => self.sh7604.interrupts.write_vcra_high(value), 494 0xFFFFFE63 => self.sh7604.interrupts.write_vcra_low(value), 495 0xFFFFFE64 => self.sh7604.interrupts.write_vcrb_high(value), 496 0xFFFFFE65 => self.sh7604.interrupts.write_vcrb_low(value), 497 // DMA request/response selection control registers; unusual only if a non-zero value is written 498 0xFFFFFE71 | 0xFFFFFE72 => { 499 if value != 0 { 500 log::warn!( 501 "[{}] Unexpected DRCR0/DRCR1 write: {address:08X} {value:02X}", 502 self.name 503 ); 504 } 505 } 506 0xFFFFFE91 => log_standby_control_write(value, &self.name), 507 0xFFFFFE92 => self.cache.write_control(value), 508 // Unmapped addresses; some games access them for unknown reasons 509 0xFFFFFE93..=0xFFFFFE9F => {} 510 0xFFFFFEE2 => self.sh7604.interrupts.write_ipra_high(value), 511 0xFFFFFEE3 => self.sh7604.interrupts.write_ipra_low(value), 512 0xFFFFFEE4 => self.sh7604.interrupts.write_vcrwdt_high(value), 513 0xFFFFFEE5 => self.sh7604.interrupts.write_vcrwdt_low(value), 514 _ => log::warn!( 515 "[{}] Unexpected internal register byte write: {address:08X} {value:02X}", 516 self.name 517 ), 518 } 519 520 self.update_internal_interrupt_level(); 521 } 522 523 pub(super) fn write_internal_register_word(&mut self, address: u32, value: u16) { 524 log::trace!("[{}] Internal register word write: {address:08X} {value:04X}", self.name); 525 526 match address { 527 // Writing to this address sets SDRAM 16-bit CAS latency; ignore 528 0xFFFF8446 => {} 529 0xFFFFFE60 => self.sh7604.interrupts.write_iprb(value), 530 0xFFFFFE62 => self.sh7604.interrupts.write_vcra(value), 531 0xFFFFFE64 => self.sh7604.interrupts.write_vcrb(value), 532 0xFFFFFE80 => self.watchdog_timer.write_control(value), 533 0xFFFFFE92 => self.cache.write_control(value as u8), 534 0xFFFFFEE2 => self.sh7604.interrupts.write_ipra(value), 535 0xFFFFFEE4 => self.sh7604.interrupts.write_vcrwdt(value), 536 // DIVU control register is writable 16-bit 537 0xFFFFFF08 => self.divu.write_register(address, value.into()), 538 0xFFFFFF40 => self.sh7604.break_registers.write_break_address_a_high(value), 539 0xFFFFFF42 => self.sh7604.break_registers.write_break_address_a_low(value), 540 0xFFFFFF60 => self.sh7604.break_registers.write_break_address_b_high(value), 541 0xFFFFFF62 => self.sh7604.break_registers.write_break_address_b_low(value), 542 _ => log::warn!( 543 "[{}] Unexpected internal register word write: {address:08X} {value:04X}", 544 self.name 545 ), 546 } 547 548 self.update_internal_interrupt_level(); 549 } 550 551 #[allow(clippy::match_same_arms)] 552 pub(super) fn write_internal_register_longword(&mut self, address: u32, value: u32) { 553 log::trace!("[{}] Internal register longword write: {address:08X} {value:08X}", self.name); 554 555 match address { 556 0xFFFFFF00..=0xFFFFFF1F => self.divu.write_register(address, value), 557 0xFFFFFF40 => self.sh7604.break_registers.write_break_address_a(value), 558 // Break bus cycle register A; ignore 559 0xFFFFFF48 => {} 560 0xFFFFFF60 => self.sh7604.break_registers.write_break_address_b(value), 561 // Break bus cycle register B; ignore 562 0xFFFFFF68 => {} 563 0xFFFFFF80..=0xFFFFFF9F | 0xFFFFFFB0 => self.dmac.write_register(address, value), 564 0xFFFFFFA0 => self.sh7604.interrupts.write_vcrdma0(value), 565 0xFFFFFFA8 => self.sh7604.interrupts.write_vcrdma1(value), 566 0xFFFFFFE0..=0xFFFFFFFF => log_bus_control_write(address, value), 567 _ => log::warn!( 568 "[{}] Unexpected internal register longword write: {address:08X} {value:08X}", 569 self.name 570 ), 571 } 572 573 self.update_internal_interrupt_level(); 574 } 575} 576 577// $FFFFFE91: SBYCR (Standby control register); not emulated 578fn log_standby_control_write(value: u8, name: &str) { 579 log::trace!("[{name}] SBYCR write: {value:02X}"); 580 log::trace!(" Standby mode enabled: {}", value.bit(7)); 581 log::trace!(" Pins at Hi-Z in standby: {}", value.bit(6)); 582 log::trace!(" DMAC clock halted: {}", value.bit(4)); 583 log::trace!(" MULT clock halted: {}", value.bit(3)); 584 log::trace!(" DIVU clock halted: {}", value.bit(2)); 585 log::trace!(" FRT clock halted: {}", value.bit(1)); 586 log::trace!(" SCI clock halted: {}", value.bit(0)); 587} 588 589fn log_bus_control_write(address: u32, value: u32) { 590 // TODO actually emulate these registers? 591 match address { 592 0xFFFFFFE0 => { 593 log::trace!("BCR1 write: {value:08X}"); 594 log::trace!(" Master mode: {}", !value.bit(15)); 595 log::trace!(" Big endian mode: {}", !value.bit(12)); 596 log::trace!(" Area 0 burst ROM enabled: {}", value.bit(11)); 597 log::trace!(" Partial-share master mode: {}", value.bit(10)); 598 log::trace!( 599 " Long wait specification for areas 2/3: {} waits", 600 ((value >> 8) & 3) + 3 601 ); 602 log::trace!(" Long wait specification for area 1: {} waits", ((value >> 6) & 3) + 3); 603 log::trace!(" Long wait specification for area 0: {} waits", ((value >> 4) & 3) + 3); 604 log::trace!(" DRAM specification bits: {}", value & 7); 605 } 606 0xFFFFFFE4 => { 607 log::trace!("BCR2 write: {value:08X}"); 608 log::trace!(" Size specification for area 3: {}", bus_area_size(value >> 6)); 609 log::trace!(" Size specification for area 2: {}", bus_area_size(value >> 4)); 610 log::trace!(" Size specification for area 1: {}", bus_area_size(value >> 2)); 611 } 612 0xFFFFFFE8 => { 613 log::trace!("WCR write: {value:08X}"); 614 log::trace!(" Idles between cycles for area 3: {}", idle_cycles(value >> 14)); 615 log::trace!(" Idles between cycles for area 2: {}", idle_cycles(value >> 12)); 616 log::trace!(" Idles between cycles for area 1: {}", idle_cycles(value >> 10)); 617 log::trace!(" Idles between cycles for area 0: {}", idle_cycles(value >> 8)); 618 log::trace!(" Wait control for area 3: {}", (value >> 6) & 3); 619 log::trace!(" Wait control for area 2: {}", (value >> 4) & 3); 620 log::trace!(" Wait control for area 1: {}", (value >> 2) & 3); 621 log::trace!(" Wait control for area 0: {}", value & 3); 622 } 623 0xFFFFFFEC => { 624 log::trace!("MCR write: {value:08X}"); 625 log::trace!(" RAS precharge time: {}", if value.bit(15) { 2 } else { 1 }); 626 log::trace!(" RAS-CAS delay: {}", if value.bit(14) { 2 } else { 1 }); 627 log::trace!(" Write precharge delay: {}", if value.bit(13) { 2 } else { 1 }); 628 log::trace!( 629 " CAS-before-RAS refresh RAS assert time: {}", 630 match (value >> 11) & 3 { 631 0 => "2 cycles", 632 1 => "3 cycles", 633 2 => "4 cycles", 634 3 => "(Reserved)", 635 _ => unreachable!(), 636 } 637 ); 638 log::trace!(" Burst enabled: {}", value.bit(10)); 639 log::trace!(" RAS down mode enabled: {}", value.bit(9)); 640 log::trace!( 641 " Address multiplexing bits: {}", 642 ((value >> 5) & 0x4) | ((value >> 4) & 0x3) 643 ); 644 log::trace!( 645 " DRAM memory data size: {}", 646 if value.bit(6) { "Longword" } else { "Word" } 647 ); 648 log::trace!(" DRAM refresh enabled: {}", value.bit(3)); 649 log::trace!(" Self-refresh enabled: {}", value.bit(2)); 650 } 651 0xFFFFFFF0 => { 652 log::trace!("RTCSR write: {value:08X}"); 653 log::trace!(" Compare match flag: {}", value.bit(7)); 654 log::trace!(" Compare match interrupt enabled: {}", value.bit(6)); 655 log::trace!(" Clock select bits: {}", (value >> 3) & 7); 656 657 if value.bit(6) { 658 log::error!("SH-2 FRT compare match interrupt was enabled; not emulated"); 659 } 660 } 661 0xFFFFFFF4 => { 662 log::trace!("RTCNT write: {value:08X}"); 663 log::trace!(" Refresh timer counter: 0x{:02X}", value & 0xFF); 664 } 665 0xFFFFFFF8 => { 666 log::trace!("RTCOR write: {value:08X}"); 667 log::trace!(" Refresh time constant for compare: 0x{:02X}", value & 0xFF); 668 } 669 _ => log::warn!("Bus control register write {address:08X} {value:08X}"), 670 } 671} 672 673fn bus_area_size(value: u32) -> &'static str { 674 match value & 3 { 675 0 => "(Reserved)", 676 1 => "Byte", 677 2 => "Word", 678 3 => "Longword", 679 _ => unreachable!("value & 3 is always <= 3"), 680 } 681} 682 683fn idle_cycles(value: u32) -> &'static str { 684 match value & 3 { 685 0 => "0 cycles", 686 1 => "1 cycle", 687 2 => "2 cycles", 688 3 => "(Reserved)", 689 _ => unreachable!("value & 3 is always <= 3"), 690 } 691}