1//! 32X memory mapping for the 68000 and SH-2s 2 3pub mod debug; 4 5use crate::pwm::PwmChip; 6use crate::registers::{Access, SystemRegisters}; 7use crate::vdp::Vdp; 8use crate::{WhichCpu, bootrom}; 9use bincode::{Decode, Encode}; 10use genesis_components::cartridge::Cartridge; 11use genesis_config::Sega32XEmulatorConfig; 12use jgenesis_common::boxedarray::BoxedWordArray; 13use jgenesis_common::frontend::TimingMode; 14use jgenesis_common::num::{GetBit, U16Ext}; 15use jgenesis_proc_macros::PartialClone; 16use sh2_emu::Sh2; 17use sh2_emu::bus::{AccessContext, BusInterface, OpSize}; 18use sh2_emu::debug::DummySh2Debugger; 19use std::marker::PhantomData; 20use std::ops::{Deref, DerefMut}; 21use std::ptr::NonNull; 22use std::{array, cmp}; 23 24const SDRAM_LEN_WORDS: usize = 256 * 1024 / 2; 25const SDRAM_MASK: u32 = 0x3FFFF; 26 27#[derive(Debug, Clone, Default, Encode, Decode)] 28pub struct SerialInterface { 29 pub master_to_slave: Option<u8>, 30 pub slave_to_master: Option<u8>, 31} 32 33#[derive(Debug, Clone, Encode, Decode, PartialClone)] 34pub struct Sega32XBus { 35 #[partial_clone(partial)] 36 pub cartridge: Option<Cartridge>, 37 pub vdp: Vdp, 38 pub pwm: PwmChip, 39 pub registers: SystemRegisters, 40 pub sdram: BoxedWordArray<SDRAM_LEN_WORDS>, 41 pub serial: SerialInterface, 42 pub stalled_on_cartridge_access: bool, 43} 44 45impl Sega32XBus { 46 pub fn new( 47 timing_mode: TimingMode, 48 cartridge: Option<Cartridge>, 49 config: &Sega32XEmulatorConfig, 50 ) -> Self { 51 let cartridge_present = cartridge.is_some(); 52 53 Self { 54 cartridge, 55 vdp: Vdp::new(timing_mode, config), 56 pwm: PwmChip::new(timing_mode), 57 registers: SystemRegisters::new(cartridge_present), 58 sdram: BoxedWordArray::new(), 59 serial: SerialInterface::default(), 60 stalled_on_cartridge_access: false, 61 } 62 } 63} 64 65pub struct OtherCpu { 66 cpu: NonNull<Sh2>, 67 cycle_counter: NonNull<u64>, 68} 69 70// SH-2 memory map 71pub struct Sh2Bus { 72 s32x_bus: NonNull<Sega32XBus>, 73 other_sh2: Option<OtherCpu>, 74 pub which: WhichCpu, 75 pub cycle_counter: u64, 76 pub cycle_limit: u64, 77} 78 79pub struct Sh2BusGuard<'bus, 'cartridge, 'other> { 80 bus: Sh2Bus, 81 _bus_marker: PhantomData<&'bus ()>, 82 _cartridge_marker: PhantomData<&'cartridge ()>, 83 _other_marker: PhantomData<&'other ()>, 84} 85 86impl Deref for Sh2BusGuard<'_, '_, '_> { 87 type Target = Sh2Bus; 88 89 fn deref(&self) -> &Self::Target { 90 &self.bus 91 } 92} 93 94impl DerefMut for Sh2BusGuard<'_, '_, '_> { 95 fn deref_mut(&mut self) -> &mut Self::Target { 96 &mut self.bus 97 } 98} 99 100// All values are minus one because every access takes at least 1 cycle 101const SH2_CARTRIDGE_CYCLES: u64 = 7; 102const SH2_FRAME_BUFFER_READ_CYCLES: u64 = 4; 103const SH2_VDP_CYCLES: u64 = 4; 104// SDRAM burst reads take between 10 and 12 cycles; assume always 10 for simplicity 105const SH2_SDRAM_READ_CYCLES: u64 = 9; 106// SDRAM writes take between 1 and 3 cycles; assume always 1 for simplicity 107const SH2_SDRAM_WRITE_CYCLES: u64 = 0; 108 109macro_rules! invalid_size { 110 ($size:expr) => { 111 panic!("invalid size {}", $size) 112 }; 113} 114 115fn sh2_read_register<const SIZE: u8>(address: u32, mut read_word: impl FnMut(u32) -> u16) -> u32 { 116 match SIZE { 117 OpSize::BYTE => { 118 let word = read_word(address & !1); 119 if !address.bit(0) { word.msb().into() } else { word.lsb().into() } 120 } 121 OpSize::WORD => read_word(address).into(), 122 OpSize::LONGWORD => { 123 let high: u32 = read_word(address).into(); 124 let low: u32 = read_word(address | 2).into(); 125 low | (high << 16) 126 } 127 _ => invalid_size!(SIZE), 128 } 129} 130 131fn sh2_write_register<const SIZE: u8>( 132 bus: &mut Sega32XBus, 133 address: u32, 134 value: u32, 135 read_word: impl Fn(&mut Sega32XBus, u32) -> u16, 136 write_word: impl Fn(&mut Sega32XBus, u32, u16), 137) { 138 match SIZE { 139 OpSize::BYTE => { 140 let mut word = read_word(bus, address & !1); 141 if !address.bit(0) { 142 word.set_msb(value as u8); 143 } else { 144 word.set_lsb(value as u8); 145 } 146 write_word(bus, address & !1, word); 147 } 148 OpSize::WORD => { 149 write_word(bus, address, value as u16); 150 } 151 OpSize::LONGWORD => { 152 write_word(bus, address, (value >> 16) as u16); 153 write_word(bus, address | 2, value as u16); 154 } 155 _ => invalid_size!(SIZE), 156 } 157} 158 159fn sh2_read_memory<const SIZE: u8, const N: usize>(memory: &[u8; N], address: u32) -> u32 { 160 match SIZE { 161 OpSize::BYTE => read_u8(memory, address).into(), 162 OpSize::WORD => read_u16(memory, address).into(), 163 OpSize::LONGWORD => read_u32(memory, address), 164 _ => invalid_size!(SIZE), 165 } 166} 167 168fn sh2_read_memory_u16<const SIZE: u8, const N: usize>(memory: &[u16; N], address: u32) -> u32 { 169 match SIZE { 170 OpSize::BYTE => { 171 let memory_addr = ((address >> 1) as usize) & (N - 1); 172 let word = memory[memory_addr]; 173 if !address.bit(0) { word.msb().into() } else { word.lsb().into() } 174 } 175 OpSize::WORD => { 176 let memory_addr = ((address >> 1) as usize) & (N - 1); 177 memory[memory_addr].into() 178 } 179 OpSize::LONGWORD => { 180 let memory_addr = ((address >> 1) as usize) & (N - 1) & !1; 181 let high: u32 = memory[memory_addr].into(); 182 let low: u32 = memory[memory_addr + 1].into(); 183 low | (high << 16) 184 } 185 _ => invalid_size!(SIZE), 186 } 187} 188 189fn sh2_write_memory_u16<const SIZE: u8, const N: usize>( 190 memory: &mut [u16; N], 191 address: u32, 192 value: u32, 193) { 194 match SIZE { 195 OpSize::BYTE => { 196 let memory_addr = ((address >> 1) as usize) & (N - 1); 197 if !address.bit(0) { 198 memory[memory_addr].set_msb(value as u8); 199 } else { 200 memory[memory_addr].set_lsb(value as u8); 201 } 202 } 203 OpSize::WORD => { 204 let memory_addr = ((address >> 1) as usize) & (N - 1); 205 memory[memory_addr] = value as u16; 206 } 207 OpSize::LONGWORD => { 208 let memory_addr = ((address >> 1) as usize) & (N - 1) & !1; 209 memory[memory_addr] = (value >> 16) as u16; 210 memory[memory_addr + 1] = value as u16; 211 } 212 _ => invalid_size!(SIZE), 213 } 214} 215 216fn sh2_vdp_cycles<const SIZE: u8>() -> u64 { 217 match SIZE { 218 OpSize::BYTE | OpSize::WORD => SH2_VDP_CYCLES, 219 OpSize::LONGWORD => 2 * SH2_VDP_CYCLES, 220 _ => invalid_size!(SIZE), 221 } 222} 223 224impl Sh2Bus { 225 #[inline] 226 pub fn create<'bus, 'cartridge, 'other>( 227 s32x_bus: &'bus mut Sega32XBus, 228 which: WhichCpu, 229 cycle_counter: u64, 230 cycle_limit: u64, 231 other_sh2: Option<(&'other mut Sh2, &'other mut u64)>, 232 ) -> Sh2BusGuard<'bus, 'cartridge, 'other> { 233 // SAFETY: Sh2Bus contains raw pointers that are created from mutable references here. The 234 // returned bus is only accessible through a guard so that the caller cannot reborrow or 235 // move the underlying values until after dropping the guard. 236 let other_sh2 = other_sh2.map(|(other_cpu, other_cycles)| OtherCpu { 237 cpu: other_cpu.into(), 238 cycle_counter: other_cycles.into(), 239 }); 240 241 Sh2BusGuard { 242 bus: Sh2Bus { s32x_bus: s32x_bus.into(), other_sh2, which, cycle_counter, cycle_limit }, 243 _bus_marker: PhantomData, 244 _cartridge_marker: PhantomData, 245 _other_marker: PhantomData, 246 } 247 } 248 249 fn s32x_bus(&mut self) -> &mut Sega32XBus { 250 // SAFETY: Mutable reference created from a raw pointer that was originally created from a 251 // mutable reference. 252 // This method mutably borrows the Sh2Bus so only one mutable reference can be created this 253 // way at a time. Other code should not create mutable references directly, and must not 254 // touch the pointer while a mutable reference is alive. 255 unsafe { self.s32x_bus.as_mut() } 256 } 257 258 fn s32x_bus_shared(&self) -> &Sega32XBus { 259 // SAFETY: Same as above but returns a shared reference from a shared Sh2Bus borrow 260 unsafe { self.s32x_bus.as_ref() } 261 } 262 263 // Brutal Unleashed: Above the Claw requires fairly close synchronization to prevent 264 // the game from freezing due to the master SH-2 missing a communication port write from 265 // the slave SH-2. After the slave SH-2 sees a specific value from the master SH-2, it 266 // writes to the communication port twice in quick succession, and the master SH-2 must 267 // read the first value before it's overwritten 268 fn need_to_sync_other(&self, address: u32) -> bool { 269 (0x4020..0x4030).contains(&address) && self.other_sh2.is_some() 270 } 271 272 fn maybe_sync_other_cpu(&mut self, address: u32) { 273 if !self.need_to_sync_other(address) { 274 return; 275 } 276 277 let Some(OtherCpu { cpu: mut other_cpu, cycle_counter: other_cycle_counter }) = 278 self.other_sh2 279 else { 280 return; 281 }; 282 283 // SAFETY: All raw pointers used here were created from mutable references and are 284 // guaranteed non-null. 285 // The original Sh2Bus instance is not used while the other CPU is executing against the 286 // bus copy. 287 unsafe { 288 let limit = cmp::min(self.cycle_limit, self.cycle_counter); 289 let mut bus = Sh2Bus { 290 s32x_bus: self.s32x_bus, 291 which: self.which.other(), 292 cycle_counter: other_cycle_counter.read(), 293 cycle_limit: limit, 294 other_sh2: None, 295 }; 296 297 while bus.cycle_counter < bus.cycle_limit { 298 other_cpu.as_mut().execute(crate::api::SH2_EXECUTION_SLICE_LEN, &mut bus); 299 } 300 other_cycle_counter.write(bus.cycle_counter); 301 } 302 } 303 304 // $00000000-$01FFFFFF: Boot ROM, 32X registers, 32X CRAM 305 fn read_00<const SIZE: u8>(&mut self, address: u32, ctx: AccessContext) -> u32 { 306 self.cycle_counter += if SIZE == OpSize::LONGWORD { 2 } else { 1 }; 307 308 match address { 309 0x4000..=0x402F => { 310 // 32X system registers 311 log::trace!( 312 "SH-2 {:?} read {} {address:08X}", 313 self.which, 314 OpSize::display::<SIZE>() 315 ); 316 317 self.maybe_sync_other_cpu(address); 318 319 let which = self.which; 320 sh2_read_register::<SIZE>(address, |address| { 321 let bus = self.s32x_bus(); 322 bus.registers.sh2_read(address, which, &bus.vdp) 323 }) 324 } 325 0x4030..=0x403F => { 326 // 32X PWM registers 327 log::trace!( 328 "SH-2 {:?} PWM register {} read {address:08X}", 329 self.which, 330 OpSize::display::<SIZE>() 331 ); 332 333 sh2_read_register::<SIZE>(address, |address| { 334 self.s32x_bus().pwm.read_register(address) 335 }) 336 } 337 0x4100..=0x41FF => { 338 // 32X VDP registers 339 self.cycle_counter += sh2_vdp_cycles::<SIZE>(); 340 341 if self.s32x_bus().registers.vdp_access == Access::Sh2 { 342 sh2_read_register::<SIZE>(address, |address| { 343 self.s32x_bus().vdp.read_register(address) 344 }) 345 } else { 346 log::warn!( 347 "VDP register {} read with FM=0: {address:08X}", 348 OpSize::display::<SIZE>() 349 ); 350 // TODO open bus? 351 OpSize::mask::<SIZE>() 352 } 353 } 354 0x4200..=0x43FF => { 355 // 32X CRAM 356 self.cycle_counter += sh2_vdp_cycles::<SIZE>(); 357 358 if self.s32x_bus().registers.vdp_access == Access::Sh2 { 359 sh2_read_register::<SIZE>(address, |address| { 360 self.s32x_bus().vdp.read_cram(address) 361 }) 362 } else { 363 log::warn!("CRAM {} read with FM=0: {address:08X}", OpSize::display::<SIZE>()); 364 // TODO open bus? 365 OpSize::mask::<SIZE>() 366 } 367 } 368 0x0000..=0x3FFF => { 369 // Boot ROM 370 match self.which { 371 WhichCpu::Master => sh2_read_memory::<SIZE, _>(bootrom::SH2_MASTER, address), 372 WhichCpu::Slave => sh2_read_memory::<SIZE, _>(bootrom::SH2_SLAVE, address), 373 } 374 } 375 _ => { 376 log::debug!( 377 "SH-2 {:?} invalid address {} read {address:08X}, ctx: {ctx}", 378 self.which, 379 OpSize::display::<SIZE>() 380 ); 381 382 // TODO open bus? 383 0 384 } 385 } 386 } 387 388 // $02000000-$03FFFFFF: Cartridge 389 fn read_02<const SIZE: u8>(&mut self, address: u32) -> u32 { 390 // SH-2s stall if they access the cartridge while RV=1 391 self.s32x_bus().stalled_on_cartridge_access |= self.s32x_bus().registers.dma.rom_to_vram; 392 393 if self.s32x_bus().registers.dma.rom_to_vram { 394 log::debug!("SH-2 {:?} accessed ROM {address:08X} while RV=1", self.which); 395 } 396 397 self.cycle_counter += if SIZE == OpSize::LONGWORD { 398 2 * (1 + SH2_CARTRIDGE_CYCLES) 399 } else { 400 1 + SH2_CARTRIDGE_CYCLES 401 }; 402 403 let Some(cartridge) = &mut self.s32x_bus().cartridge else { 404 // TODO open bus? 405 return !0; 406 }; 407 408 // Only A21-A0 are connected to the cartridge on the SH-2 side 409 let cartridge_addr = address & 0x3FFFFF; 410 411 match SIZE { 412 OpSize::BYTE => cartridge.read_byte(cartridge_addr, !0).into(), 413 OpSize::WORD => cartridge.read_word(cartridge_addr, !0).into(), 414 OpSize::LONGWORD => { 415 let high: u32 = cartridge.read_word(cartridge_addr, !0).into(); 416 let low: u32 = cartridge.read_word(cartridge_addr | 2, !0).into(); 417 low | (high << 16) 418 } 419 _ => invalid_size!(SIZE), 420 } 421 } 422 423 // $04000000-$05FFFFFF: Frame buffer 424 fn read_04<const SIZE: u8>(&mut self, address: u32, ctx: AccessContext) -> u32 { 425 self.cycle_counter += if SIZE == OpSize::LONGWORD { 426 2 * (1 + SH2_FRAME_BUFFER_READ_CYCLES) 427 } else { 428 1 + SH2_FRAME_BUFFER_READ_CYCLES 429 }; 430 431 if self.s32x_bus().registers.vdp_access == Access::Sh2 { 432 sh2_read_register::<SIZE>(address, |address| { 433 self.s32x_bus().vdp.read_frame_buffer(address) 434 }) 435 } else { 436 log::warn!( 437 "SH-2 {:?} frame buffer {} read with FM=0: {address:08X}, ctx: {ctx}", 438 self.which, 439 OpSize::display::<SIZE>() 440 ); 441 442 // TODO open bus? 443 OpSize::mask::<SIZE>() 444 } 445 } 446 447 // $06000000-$07FFFFFF: SDRAM 448 fn read_06<const SIZE: u8>(&mut self, address: u32, ctx: AccessContext) -> u32 { 449 if address >= 0x06040000 { 450 log::debug!( 451 "SH-2 {:?} invalid {} read {address:08X}, ctx: {ctx}", 452 self.which, 453 OpSize::display::<SIZE>() 454 ); 455 return 0; 456 } 457 458 // SDRAM access times are not doubled for longword reads 459 self.cycle_counter += 1 + SH2_SDRAM_READ_CYCLES; 460 461 sh2_read_memory_u16::<SIZE, _>(&self.s32x_bus().sdram, address) 462 } 463 464 // $00000000-$01FFFFFF: Boot ROM, 32X registers, 32X CRAM 465 fn write_00<const SIZE: u8>(&mut self, address: u32, value: u32, ctx: AccessContext) { 466 self.cycle_counter += if SIZE == OpSize::LONGWORD { 2 } else { 1 }; 467 468 match address { 469 0x4000..=0x402F => { 470 // 32X system registers 471 log::trace!( 472 "SH-2 {:?} {} write {address:08X} {value:08X}", 473 self.which, 474 OpSize::display::<SIZE>() 475 ); 476 477 self.maybe_sync_other_cpu(address); 478 479 let which = self.which; 480 sh2_write_register::<SIZE>( 481 self.s32x_bus(), 482 address, 483 value, 484 |bus, address| bus.registers.sh2_read(address, which, &bus.vdp), 485 |bus, address, word| { 486 bus.registers.sh2_write(address, word, which, &mut bus.vdp); 487 }, 488 ); 489 } 490 0x4030..=0x403F => { 491 // 32X PWM registers 492 log::trace!( 493 "SH-2 {:?} PWM register {} write {address:08X} {value:08X}", 494 self.which, 495 OpSize::display::<SIZE>() 496 ); 497 498 sh2_write_register::<SIZE>( 499 self.s32x_bus(), 500 address, 501 value, 502 |bus, address| bus.pwm.read_register(address), 503 |bus, address, word| bus.pwm.sh2_write_register(address, word), 504 ); 505 } 506 0x4100..=0x41FF => { 507 // 32X VDP registers 508 self.cycle_counter += sh2_vdp_cycles::<SIZE>(); 509 510 if self.s32x_bus().registers.vdp_access == Access::Sh2 { 511 sh2_write_register::<SIZE>( 512 self.s32x_bus(), 513 address, 514 value, 515 |bus, address| bus.vdp.read_register(address), 516 |bus, address, word| bus.vdp.write_register(address, word), 517 ); 518 } else { 519 log::warn!( 520 "VDP register {} write with FM=0: {address:08X} {value:08X}", 521 OpSize::display::<SIZE>() 522 ); 523 } 524 } 525 0x4200..=0x43FF => { 526 // 32X CRAM 527 self.cycle_counter += sh2_vdp_cycles::<SIZE>(); 528 529 if self.s32x_bus().registers.vdp_access == Access::Sh2 { 530 sh2_write_register::<SIZE>( 531 self.s32x_bus(), 532 address, 533 value, 534 |bus, address| bus.vdp.read_cram(address), 535 |bus, address, word| bus.vdp.write_cram(address, word), 536 ); 537 } else { 538 log::warn!( 539 "CRAM {} write with FM=0: {address:08X} {value:08X}", 540 OpSize::display::<SIZE>() 541 ); 542 } 543 } 544 _ => { 545 log::debug!( 546 "SH-2 {:?} invalid address {} write: {address:08X} {value:08X}, ctx: {ctx}", 547 self.which, 548 OpSize::display::<SIZE>() 549 ); 550 } 551 } 552 } 553 554 // $04000000-$05FFFFFF: Frame buffer 555 fn write_04<const SIZE: u8>(&mut self, address: u32, value: u32, ctx: AccessContext) { 556 if self.s32x_bus().registers.vdp_access != Access::Sh2 { 557 log::warn!( 558 "SH-2 {:?} frame buffer {} write with FM=0: {address:08X} {value:08X}, ctx: {ctx}", 559 self.which, 560 OpSize::display::<SIZE>() 561 ); 562 return; 563 } 564 565 let cycle_counter = self.cycle_counter; 566 self.cycle_counter += self.s32x_bus().vdp.frame_buffer_write_latency(cycle_counter); 567 if SIZE == OpSize::LONGWORD { 568 let cycle_counter = self.cycle_counter; 569 self.cycle_counter += self.s32x_bus().vdp.frame_buffer_write_latency(cycle_counter); 570 } 571 572 if SIZE == OpSize::BYTE { 573 // Treat normal mapping and overwrite image identically because 0 bytes are never 574 // written in either case 575 self.s32x_bus().vdp.write_frame_buffer_byte(address, value as u8); 576 return; 577 } 578 579 sh2_write_register::<SIZE>( 580 self.s32x_bus(), 581 address, 582 value, 583 |_, _| panic!("read_word should never be called for frame buffer writes"), 584 |bus, address, word| { 585 if address & 0x20000 == 0 { 586 // Normal frame buffer mapping 587 bus.vdp.write_frame_buffer_word(address, word); 588 } else { 589 // Overwrite image 590 bus.vdp.frame_buffer_overwrite_word(address, word); 591 } 592 }, 593 ); 594 } 595 596 // $06000000-$07FFFFFF: SDRAM 597 fn write_06<const SIZE: u8>(&mut self, address: u32, value: u32, ctx: AccessContext) { 598 if address >= 0x06040000 { 599 log::debug!( 600 "SH-2 {:?} invalid {} write {address:08X} {value:08X}, ctx: {ctx}", 601 self.which, 602 OpSize::display::<SIZE>() 603 ); 604 return; 605 } 606 607 // No latency difference between 16-bit SDRAM writes and 32-bit SDRAM writes 608 self.cycle_counter += 1 + SH2_SDRAM_WRITE_CYCLES; 609 610 sh2_write_memory_u16::<SIZE, _>(&mut self.s32x_bus().sdram, address, value); 611 } 612} 613 614impl BusInterface for Sh2Bus { 615 type DebugView<'a> 616 = DummySh2Debugger 617 where 618 Self: 'a; 619 620 #[inline] 621 fn read<const SIZE: u8>(&mut self, address: u32, ctx: AccessContext) -> u32 { 622 const BYTE_FNS: [fn(&mut Sh2Bus, u32, AccessContext) -> u32; 4] = [ 623 |bus, address, ctx| bus.read_00::<{ OpSize::BYTE }>(address, ctx), 624 |bus, address, _ctx| bus.read_02::<{ OpSize::BYTE }>(address), 625 |bus, address, ctx| bus.read_04::<{ OpSize::BYTE }>(address, ctx), 626 |bus, address, ctx| bus.read_06::<{ OpSize::BYTE }>(address, ctx), 627 ]; 628 629 const WORD_FNS: [fn(&mut Sh2Bus, u32, AccessContext) -> u32; 4] = [ 630 |bus, address, ctx| bus.read_00::<{ OpSize::WORD }>(address, ctx), 631 |bus, address, _ctx| bus.read_02::<{ OpSize::WORD }>(address), 632 |bus, address, ctx| bus.read_04::<{ OpSize::WORD }>(address, ctx), 633 |bus, address, ctx| bus.read_06::<{ OpSize::WORD }>(address, ctx), 634 ]; 635 636 const LONGWORD_FNS: [fn(&mut Sh2Bus, u32, AccessContext) -> u32; 4] = [ 637 |bus, address, ctx| bus.read_00::<{ OpSize::LONGWORD }>(address, ctx), 638 |bus, address, _ctx| bus.read_02::<{ OpSize::LONGWORD }>(address), 639 |bus, address, ctx| bus.read_04::<{ OpSize::LONGWORD }>(address, ctx), 640 |bus, address, ctx| bus.read_06::<{ OpSize::LONGWORD }>(address, ctx), 641 ]; 642 643 let idx = ((address >> 25) & 3) as usize; 644 match SIZE { 645 OpSize::BYTE => BYTE_FNS[idx](self, address, ctx), 646 OpSize::WORD => WORD_FNS[idx](self, address, ctx), 647 OpSize::LONGWORD => LONGWORD_FNS[idx](self, address, ctx), 648 _ => invalid_size!(SIZE), 649 } 650 } 651 652 #[inline] 653 fn read_cache_line(&mut self, address: u32, ctx: AccessContext) -> [u16; 8] { 654 if (0x06000000..0x06040000).contains(&address) { 655 // The SH-2s can read a full 16-byte cache line in 12 cycles 656 self.cycle_counter += SH2_SDRAM_READ_CYCLES + 1; 657 658 let base_addr = ((address & SDRAM_MASK & !0xF) >> 1) as usize; 659 let sdram = &self.s32x_bus().sdram; 660 return array::from_fn(|i| sdram[base_addr + i]); 661 } 662 663 array::from_fn(|i| self.read_word(address | ((i as u32) << 1), ctx)) 664 } 665 666 #[inline] 667 fn write<const SIZE: u8>(&mut self, address: u32, value: u32, ctx: AccessContext) { 668 const BYTE_FNS: [fn(&mut Sh2Bus, u32, u32, AccessContext); 4] = [ 669 |bus, address, value, ctx| bus.write_00::<{ OpSize::BYTE }>(address, value, ctx), 670 |_, _, _, _| {}, // SH-2s cannot write to the cartridge 671 |bus, address, value, ctx| bus.write_04::<{ OpSize::BYTE }>(address, value, ctx), 672 |bus, address, value, ctx| bus.write_06::<{ OpSize::BYTE }>(address, value, ctx), 673 ]; 674 675 const WORD_FNS: [fn(&mut Sh2Bus, u32, u32, AccessContext); 4] = [ 676 |bus, address, value, ctx| bus.write_00::<{ OpSize::WORD }>(address, value, ctx), 677 |_, _, _, _| {}, // SH-2s cannot write to the cartridge 678 |bus, address, value, ctx| bus.write_04::<{ OpSize::WORD }>(address, value, ctx), 679 |bus, address, value, ctx| bus.write_06::<{ OpSize::WORD }>(address, value, ctx), 680 ]; 681 682 const LONGWORD_FNS: [fn(&mut Sh2Bus, u32, u32, AccessContext); 4] = [ 683 |bus, address, value, ctx| bus.write_00::<{ OpSize::LONGWORD }>(address, value, ctx), 684 |_, _, _, _| {}, // SH-2s cannot write to the cartridge 685 |bus, address, value, ctx| bus.write_04::<{ OpSize::LONGWORD }>(address, value, ctx), 686 |bus, address, value, ctx| bus.write_06::<{ OpSize::LONGWORD }>(address, value, ctx), 687 ]; 688 689 let idx = ((address >> 25) & 3) as usize; 690 match SIZE { 691 OpSize::BYTE => BYTE_FNS[idx](self, address, value, ctx), 692 OpSize::WORD => WORD_FNS[idx](self, address, value, ctx), 693 OpSize::LONGWORD => LONGWORD_FNS[idx](self, address, value, ctx), 694 _ => invalid_size!(SIZE), 695 } 696 } 697 698 #[inline] 699 fn reset(&self) -> bool { 700 self.s32x_bus_shared().registers.reset_sh2 701 } 702 703 #[inline] 704 fn interrupt_level(&self) -> u8 { 705 match self.which { 706 WhichCpu::Master => { 707 self.s32x_bus_shared().registers.master_interrupts.current_interrupt_level 708 } 709 WhichCpu::Slave => { 710 self.s32x_bus_shared().registers.slave_interrupts.current_interrupt_level 711 } 712 } 713 } 714 715 #[inline] 716 fn dma_request_0(&self) -> bool { 717 !self.s32x_bus_shared().registers.dma.fifo.sh2_is_empty() 718 } 719 720 #[inline] 721 fn dma_request_1(&self) -> bool { 722 self.s32x_bus_shared().pwm.dma_request_1() 723 } 724 725 #[inline] 726 fn acknowledge_dreq_1(&mut self) { 727 self.s32x_bus().pwm.acknowledge_dreq_1(); 728 } 729 730 #[inline] 731 fn serial_rx(&mut self) -> Option<u8> { 732 match self.which { 733 WhichCpu::Master => self.s32x_bus().serial.slave_to_master.take(), 734 WhichCpu::Slave => self.s32x_bus().serial.master_to_slave.take(), 735 } 736 } 737 738 #[inline] 739 fn serial_tx(&mut self, value: u8) { 740 match self.which { 741 WhichCpu::Master => self.s32x_bus().serial.master_to_slave = Some(value), 742 WhichCpu::Slave => self.s32x_bus().serial.slave_to_master = Some(value), 743 } 744 } 745 746 #[inline] 747 fn increment_cycle_counter(&mut self, cycles: u64) { 748 self.cycle_counter += cycles; 749 } 750 751 #[inline] 752 fn should_stop_execution(&self) -> bool { 753 self.cycle_counter >= self.cycle_limit 754 } 755 756 sh2_emu::impl_sh2_opcode_table!(Sh2Bus); 757} 758 759#[inline] 760fn read_u8<const LEN: usize>(slice: &[u8; LEN], address: u32) -> u8 { 761 slice[(address as usize) & (LEN - 1)] 762} 763 764#[inline] 765fn read_u16<const LEN: usize>(slice: &[u8; LEN], address: u32) -> u16 { 766 let address = (address as usize) & (LEN - 1) & !1; 767 u16::from_be_bytes([slice[address], slice[address + 1]]) 768} 769 770#[inline] 771fn read_u32<const LEN: usize>(slice: &[u8; LEN], address: u32) -> u32 { 772 let address = (address as usize) & (LEN - 1) & !3; 773 u32::from_be_bytes(slice[address..address + 4].try_into().unwrap()) 774}