SH-2 internal I/O registers (accessed using A29-31 = 111)
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}
User break functionality is not emulated, but After Burner Complete uses these R/W registers to store state in its audio processing code
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}
The SH7604 manual says there's a 3-12 cycle latency on FRT accesses, though it's unclear whether this applies to only FRC or to all FRT registers. Applying to all registers fixes some timing issues around line 224 HINT and VINT
385const FRT_WAIT_CYCLES: u64 = 7;
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}
$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}
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}