Emulation core for the Hitachi SH-2 CPU
Note that this core does not track timing. Callers can compute timing by assuming 1 cycle per
instruction plus memory access delays, using the BusInterface implementation to record memory
accesses.
19use crate::bus::{AccessContext, BusInterface}; 20use crate::cache::CpuCache; 21use crate::debug::{BusDebugExt, Sh2Debugger}; 22use crate::divu::DivisionUnit; 23use crate::dma::DmaController; 24use crate::frt::FreeRunTimer; 25use crate::registers::{Sh2Registers, Sh7604Registers}; 26use crate::sci::SerialInterface; 27use crate::wdt::WatchdogTimer; 28use bincode::{Decode, Encode}; 29pub use disassemble::{ 30 DisassembledInstruction, Displacement, MemoryAccess, MemoryAccessSize, ReadType, 31 disassemble_into, 32}; 33pub use instructions::OpcodeTable; 34use jgenesis_common::debug::DebugMemoryView; 35use std::env; 36use std::fmt::Debug; 37 38pub use cache::CacheMode; 39pub use dma::{DmaAddressMode, DmaChannel, DmaChannelControl, DmaTransferUnit}; 40 41const RESET_PC_VECTOR: u32 = 0x00000000; 42const RESET_SP_VECTOR: u32 = 0x00000004; 43 44const RESET_INTERRUPT_MASK: u8 = 15; 45const RESET_VBR: u32 = 0x00000000; 46 47const BASE_IRL_VECTOR_NUMBER: u32 = 64;
R15 is the hardware stack pointer
50const SP: usize = 15;
Only A0-28 are visible externally; A29-31 are handled internally
56#[derive(Debug, Clone, Encode, Decode)] 57pub struct Sh2 { 58 registers: Sh2Registers, 59 cache: CpuCache, 60 sh7604: Sh7604Registers, 61 dmac: DmaController, 62 free_run_timer: FreeRunTimer, 63 watchdog_timer: WatchdogTimer, 64 divu: DivisionUnit, 65 serial: SerialInterface, 66 reset_pending: bool, 67 data_ctx: AccessContext, 68 name: String, 69 trace_log_enabled: bool, 70} 71 72fn trace_log_enabled(name: &str) -> bool { 73 match env::var("SH2_LOG") { 74 Ok(log_name) => name == log_name, 75 Err(_) => true, 76 } 77} 78 79impl Sh2 { 80 #[must_use] 81 #[allow(clippy::missing_panics_doc)] 82 pub fn new(name: String) -> Self { 83 let trace_log_enabled = trace_log_enabled(&name); 84 85 Self { 86 registers: Sh2Registers::default(), 87 cache: CpuCache::new(), 88 sh7604: Sh7604Registers::new(), 89 dmac: DmaController::new(), 90 free_run_timer: FreeRunTimer::new(), 91 watchdog_timer: WatchdogTimer::new(), 92 divu: DivisionUnit::new(), 93 serial: SerialInterface::new(name.clone()), 94 reset_pending: true, 95 data_ctx: AccessContext::Data { pc: 0, opcode: 0 }, 96 name, 97 trace_log_enabled, 98 } 99 }
Execute up to ticks instructions.
Will not execute any instructions if a reset is performed or an interrupt is handled.
104 #[inline] 105 pub fn execute<Bus: BusInterface>(&mut self, mut ticks: u64, bus: &mut Bus) { 106 if ticks == 0 { 107 return; 108 } 109 110 if bus.reset() { 111 self.reset_pending = true; 112 // TODO how long does a reset take? 113 bus.increment_cycle_counter(5); 114 return; 115 } 116 117 if self.reset_pending { 118 self.reset_pending = false; 119 120 // First 8 bytes of the address space contain the reset vector and the initial SP 121 // TODO use different vectors for manual reset vs. power-on reset? 32X doesn't depend on this 122 self.registers.pc = bus.read_longword(RESET_PC_VECTOR, AccessContext::InterruptVector); 123 self.registers.next_pc = self.registers.pc.wrapping_add(2); 124 self.registers.next_op_in_delay_slot = false; 125 126 self.registers.gpr[SP] = 127 bus.read_longword(RESET_SP_VECTOR, AccessContext::InterruptVector); 128 129 self.registers.sr.interrupt_mask = RESET_INTERRUPT_MASK; 130 self.registers.vbr = RESET_VBR; 131 132 self.cache.purge_all(); 133 134 log::trace!( 135 "[{}] Reset SH-2; PC is {:08X} and SP is {:08X}", 136 self.name, 137 self.registers.pc, 138 self.registers.gpr[SP] 139 ); 140 141 // TODO how long should this take? 142 bus.increment_cycle_counter(5); 143 144 return; 145 } 146 147 for _ in 0..ticks { 148 if !self.try_tick_dma(bus) { 149 break; 150 } 151 } 152 153 // Interrupts cannot trigger in a delay slot per the SH7604 hardware manual 154 // Before checking for interrupts, make sure the CPU is not in a delay slot 155 if self.registers.next_op_in_delay_slot { 156 self.execute_single_instruction(bus); 157 ticks -= 1; 158 } 159 160 debug_assert!( 161 !self.registers.next_op_in_delay_slot, 162 "SH-2 executed two simultaneous delay slot instructions, PC={:08X}", 163 self.registers.pc 164 ); 165 166 let external_interrupt_level = bus.interrupt_level(); 167 let internal_interrupt_level = self.sh7604.internal_interrupt.priority; 168 let interrupt_mask = self.registers.sr.interrupt_mask; 169 170 if external_interrupt_level > interrupt_mask 171 && external_interrupt_level >= internal_interrupt_level 172 { 173 let vector_number = BASE_IRL_VECTOR_NUMBER + u32::from(external_interrupt_level >> 1); 174 self.handle_exception(Some(external_interrupt_level), vector_number, bus); 175 return; 176 } 177 178 if internal_interrupt_level > interrupt_mask { 179 let vector_number: u32 = self.sh7604.internal_interrupt.vector_number.into(); 180 self.handle_exception(Some(internal_interrupt_level), vector_number, bus); 181 return; 182 } 183 184 for _ in 0..ticks { 185 self.execute_single_instruction(bus); 186 if bus.should_stop_execution() 187 || self.registers.sr.interrupt_mask < bus.interrupt_level() 188 || self.registers.sr.interrupt_mask < self.sh7604.internal_interrupt.priority 189 { 190 return; 191 } 192 } 193 }
195 #[inline(always)] 196 fn execute_single_instruction<Bus: BusInterface>(&mut self, bus: &mut Bus) { 197 let pc = self.registers.pc; 198 let opcode = self.read_opcode(pc, bus); 199 200 bus.check_execute(pc, opcode, self); 201 202 self.registers.pc = self.registers.next_pc; 203 self.registers.next_pc = self.registers.pc.wrapping_add(2); 204 self.registers.next_op_in_delay_slot = false; 205 206 self.data_ctx = AccessContext::Data { pc, opcode }; 207 208 if log::log_enabled!(log::Level::Trace) && self.trace_log_enabled { 209 let mut disassembled = DisassembledInstruction::new(); 210 disassemble_into(pc, opcode, &mut disassembled); 211 log::trace!( 212 "[{}] Executing opcode {opcode:04X} at PC {pc:08X}: {}", 213 self.name, 214 disassembled.text 215 ); 216 log::trace!(" Registers: {:08X?}", self.registers.gpr); 217 log::trace!( 218 " GBR={:08X} VBR={:08X} PR={:08X}", 219 self.registers.gbr, 220 self.registers.vbr, 221 self.registers.pr 222 ); 223 log::trace!(" SR={:?}", self.registers.sr); 224 } 225 226 let opcode_table = Bus::opcode_table(); 227 opcode_table.decode(opcode)(self, opcode, bus); 228 bus.increment_cycle_counter(1); 229 }
Advance internal peripherals by system_cycles, specifically the watchdog timer (WDT) and
the serial interface (SCI). Also updates internal interrupt state.
240 fn read_byte<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u8 { 241 bus.check_read_byte(address, self); 242 243 match address >> 29 { 244 0 => self.cached_read_byte(address, bus), 245 1 => bus.apply_read_byte(address & EXTERNAL_ADDRESS_MASK, self.data_ctx, self), 246 2 => { 247 self.cache.associative_purge(address); 248 0 249 } 250 3..=5 => { 251 log::warn!( 252 "SH-2 {:?} invalid byte read: {address:08X}, ctx: {}", 253 self.name, 254 self.data_ctx 255 ); 256 0 257 } 258 6 => self.cache.read_data_array_u8(address), 259 7 => self.read_internal_register_byte(address, bus), 260 _ => unreachable!("u32 >> 29 is always 0-7"), 261 } 262 } 263 264 fn cached_read_byte<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u8 { 265 if let Some(value) = self.cache.read_u8(address) { 266 return value; 267 } 268 269 if self.cache.should_replace_data() { 270 let cache_line = 271 bus.apply_read_cache_line(address & CACHE_LINE_MASK, self.data_ctx, self); 272 let longword = self.cache.replace(address, cache_line); 273 longword.to_be_bytes()[(address & 3) as usize] 274 } else { 275 bus.apply_read_byte(address & EXTERNAL_ADDRESS_MASK, self.data_ctx, self) 276 } 277 } 278 279 fn read_word<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u16 { 280 self.read_word_generic::<false, _>(address, bus) 281 } 282 283 #[inline(always)] 284 fn read_opcode<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u16 { 285 self.read_word_generic::<true, _>(address, bus) 286 } 287 288 #[inline(always)] 289 fn read_word_generic<const INSTRUCTION: bool, Bus: BusInterface>( 290 &mut self, 291 address: u32, 292 bus: &mut Bus, 293 ) -> u16 { 294 if !INSTRUCTION { 295 bus.check_read_word(address, self); 296 } 297 298 match address >> 29 { 299 0 => self.cached_read_word::<INSTRUCTION, _>(address, bus), 300 1 => { 301 let ctx = if INSTRUCTION { AccessContext::Fetch } else { self.data_ctx }; 302 bus.apply_read_word(address & EXTERNAL_ADDRESS_MASK, ctx, self) 303 } 304 2 => { 305 self.cache.associative_purge(address); 306 0 307 } 308 3..=5 => { 309 let ctx = if INSTRUCTION { AccessContext::Fetch } else { self.data_ctx }; 310 log::warn!("SH-2 {:?} invalid word read: {address:08X}, ctx: {ctx}", self.name); 311 0 312 } 313 6 => self.cache.read_data_array_u16(address), 314 7 => self.read_internal_register_word(address), 315 _ => unreachable!("u32 >> 29 is always 0-7"), 316 } 317 } 318 319 #[inline(always)] 320 fn cached_read_word<const INSTRUCTION: bool, Bus: BusInterface>( 321 &mut self, 322 address: u32, 323 bus: &mut Bus, 324 ) -> u16 { 325 if let Some(value) = self.cache.read_u16(address) { 326 return value; 327 } 328 329 if (INSTRUCTION && self.cache.should_replace_instruction()) 330 || (!INSTRUCTION && self.cache.should_replace_data()) 331 { 332 let ctx = if INSTRUCTION { AccessContext::Fetch } else { self.data_ctx }; 333 let cache_line = bus.apply_read_cache_line(address & CACHE_LINE_MASK, ctx, self); 334 let longword = self.cache.replace(address, cache_line); 335 (longword >> (16 * (((address >> 1) & 1) ^ 1))) as u16 336 } else { 337 let ctx = if INSTRUCTION { AccessContext::Fetch } else { self.data_ctx }; 338 bus.apply_read_word(address & EXTERNAL_ADDRESS_MASK, ctx, self) 339 } 340 } 341 342 fn read_longword<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u32 { 343 bus.check_read_longword(address, self); 344 345 match address >> 29 { 346 0 => self.cached_read_longword(address, bus), 347 1 => bus.apply_read_longword(address & EXTERNAL_ADDRESS_MASK, self.data_ctx, self), 348 2 => { 349 // FIFA Soccer 96 reads from associative purge addresses and doesn't use the values read 350 // Seems like it expects reads to purge cache lines in addition to writes? 351 self.cache.associative_purge(address); 352 0 353 } 354 3 => self.cache.read_address_array(address), 355 4 | 5 => { 356 log::warn!( 357 "SH-2 {:?} invalid longword read: {address:08X}, ctx: {}", 358 self.name, 359 self.data_ctx 360 ); 361 0 362 } 363 6 => self.cache.read_data_array_u32(address), 364 7 => self.read_internal_register_longword(address), 365 _ => unreachable!("u32 >> 29 is always 0-7"), 366 } 367 } 368 369 fn cached_read_longword<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u32 { 370 if let Some(value) = self.cache.read_u32(address) { 371 return value; 372 } 373 374 if self.cache.should_replace_data() { 375 let cache_line = 376 bus.apply_read_cache_line(address & CACHE_LINE_MASK, self.data_ctx, self); 377 self.cache.replace(address, cache_line) 378 } else { 379 bus.apply_read_longword(address & EXTERNAL_ADDRESS_MASK, self.data_ctx, self) 380 } 381 } 382 383 fn write_byte<Bus: BusInterface>(&mut self, address: u32, value: u8, bus: &mut Bus) { 384 bus.check_write_byte(address, value, self); 385 386 match address >> 29 { 387 0 => { 388 bus.apply_write_byte(address & EXTERNAL_ADDRESS_MASK, value, self.data_ctx, self); 389 self.cache.write_through_u8(address, value); 390 } 391 1 => bus.apply_write_byte(address & EXTERNAL_ADDRESS_MASK, value, self.data_ctx, self), 392 2 => self.cache.associative_purge(address), 393 3..=5 => { 394 log::warn!( 395 "SH-2 {:?} invalid byte write: {address:08X} {value:02X}, ctx: {}", 396 self.name, 397 self.data_ctx 398 ); 399 } 400 6 => self.cache.write_data_array_u8(address, value), 401 7 => self.write_internal_register_byte(address, value, bus), 402 _ => unreachable!("u32 >> 29 is always 0-7"), 403 } 404 } 405 406 fn write_word<Bus: BusInterface>(&mut self, address: u32, value: u16, bus: &mut Bus) { 407 bus.check_write_word(address, value, self); 408 409 match address >> 29 { 410 0 => { 411 bus.apply_write_word(address & EXTERNAL_ADDRESS_MASK, value, self.data_ctx, self); 412 self.cache.write_through_u16(address, value); 413 } 414 1 => bus.apply_write_word(address & EXTERNAL_ADDRESS_MASK, value, self.data_ctx, self), 415 2 => self.cache.associative_purge(address), 416 3..=5 => { 417 log::warn!( 418 "SH-2 {:?} invalid word write: {address:08X} {value:04X}, ctx: {}", 419 self.name, 420 self.data_ctx 421 ); 422 } 423 6 => self.cache.write_data_array_u16(address, value), 424 7 => self.write_internal_register_word(address, value), 425 _ => unreachable!("u32 >> 29 is always 0-7"), 426 } 427 } 428 429 #[allow(clippy::match_same_arms)] 430 fn write_longword<Bus: BusInterface>(&mut self, address: u32, value: u32, bus: &mut Bus) { 431 bus.check_write_longword(address, value, self); 432 433 match address >> 29 { 434 0 => { 435 bus.apply_write_longword( 436 address & EXTERNAL_ADDRESS_MASK, 437 value, 438 self.data_ctx, 439 self, 440 ); 441 self.cache.write_through_u32(address, value); 442 } 443 1 => bus.apply_write_longword( 444 address & EXTERNAL_ADDRESS_MASK, 445 value, 446 self.data_ctx, 447 self, 448 ), 449 2 => self.cache.associative_purge(address), 450 3 => self.cache.write_address_array(address, value), 451 4 | 5 => { 452 log::warn!( 453 "SH-2 {:?} invalid longword write: {address:08X} {value:08X}, ctx: {}", 454 self.name, 455 self.data_ctx 456 ); 457 } 458 6 => self.cache.write_data_array_u32(address, value), 459 7 => self.write_internal_register_longword(address, value), 460 _ => unreachable!("u32 >> 29 is always 0-7"), 461 } 462 } 463 464 fn handle_exception<Bus: BusInterface>( 465 &mut self, 466 interrupt_level: Option<u8>, 467 vector_number: u32, 468 bus: &mut Bus, 469 ) { 470 if let Some(mut debug_view) = bus.debug_view() 471 && let Some(interrupt_level) = interrupt_level 472 { 473 debug_view.check_interrupt(interrupt_level, self); 474 } 475 476 let mut sp = self.registers.gpr[SP].wrapping_sub(4); 477 self.write_longword(sp, self.registers.sr.into(), bus); 478 479 sp = sp.wrapping_sub(4); 480 self.write_longword(sp, self.registers.pc, bus); 481 482 self.registers.gpr[SP] = sp; 483 if let Some(interrupt_level) = interrupt_level { 484 self.registers.sr.interrupt_mask = interrupt_level; 485 } 486 487 let vector_addr = self.registers.vbr.wrapping_add(vector_number << 2); 488 self.registers.pc = self.read_longword(vector_addr, bus); 489 self.registers.next_pc = self.registers.pc.wrapping_add(2); 490 self.registers.next_op_in_delay_slot = false; 491 492 // Interrupt handling takes 10 cycles, plus memory access time, plus time to flush the pipeline 493 // Memory access time is already accounted for, so arbitrarily say it will take 3 cycles to flush 494 bus.increment_cycle_counter(13); 495 496 log::debug!( 497 "[{}] Handled interrupt of level {interrupt_level:?} with vector number {vector_number}, jumped to {:08X}", 498 self.name, 499 self.registers.pc 500 ); 501 } 502 503 fn update_internal_interrupt_level(&mut self) { 504 self.sh7604.update_interrupt_level(&self.dmac, &self.watchdog_timer, &self.serial); 505 } 506 507 #[inline] 508 #[must_use] 509 pub fn pc(&self) -> u32 { 510 self.registers.pc 511 } 512 513 pub fn set_pc(&mut self, pc: u32) { 514 self.registers.pc = pc; 515 self.registers.next_pc = pc.wrapping_add(2); 516 } 517 518 #[inline] 519 #[must_use] 520 pub fn registers(&self) -> &Sh2Registers { 521 &self.registers 522 } 523 524 #[must_use] 525 pub fn peek_cache(&self, address: u32) -> Option<u16> { 526 self.cache.peek(address) 527 } 528 529 #[must_use] 530 pub fn peek_data_array(&self, address: u32) -> u16 { 531 self.cache.peek_data_array(address) 532 } 533 534 #[must_use] 535 pub fn debug_cache_view(&mut self) -> impl DebugMemoryView { 536 self.cache.debug_view() 537 } 538}