1use crate::api::PceEmulatorConfig; 2use bincode::{Decode, Encode}; 3use crc::Crc; 4use huc6280_emu::bus::{ClockSpeed, InterruptLines}; 5use jgenesis_common::boxedarray::BoxedByteArray; 6use jgenesis_common::num::GetBit; 7use jgenesis_proc_macros::{FakeDecode, FakeEncode, PartialClone}; 8use std::ops::Deref; 9use std::{cmp, iter, mem}; 10 11const WORKING_RAM_LEN: usize = 8 * 1024; 12 13const POPULOUS_SRAM_LEN: usize = 32 * 1024;
Timer ticks every 1024 cycles at ~7.16 MHz regardless of current CPU clock speed
16const TIMER_PRESCALER_DIVIDER: u64 = 3 * 1024;
18#[derive(Debug, Clone, FakeEncode, FakeDecode)] 19pub struct Rom(pub Box<[u8]>); 20 21impl Default for Rom { 22 fn default() -> Self { 23 Self(vec![].into_boxed_slice()) 24 } 25} 26 27impl Deref for Rom { 28 type Target = Box<[u8]>; 29 30 fn deref(&self) -> &Self::Target { 31 &self.0 32 } 33} 34 35#[derive(Debug, Clone, Encode, Decode)] 36enum Mapper { 37 // Standard linear ROM mapping in all banks 38 None, 39 // Standard linear ROM mapping in banks $00-$3F, 32KB of SRAM mapped to $40-$43 40 Populous { sram: BoxedByteArray<POPULOUS_SRAM_LEN>, sram_dirty: bool }, 41 // First 512KB of ROM in banks $00-$3F, mappable 512KB ROM bank in banks $40-$7F 42 StreetFighter2 { rom_bank: u32 }, 43} 44 45impl Mapper { 46 fn guess_from_rom(rom: &[u8], initial_sram: Option<Vec<u8>>) -> Self { 47 const CRC: Crc<u32> = Crc::<u32>::new(&crc::CRC_32_ISO_HDLC); 48 49 let checksum = CRC.checksum(rom); 50 51 // TODO is there a better way to do this than checksum matching? PCE games don't seem to 52 // have anything resembling a cartridge header 53 match checksum { 54 // Populous (Japan) (En) 55 0xDB5F97B3 => { 56 log::info!("Enabling Populous SRAM mapper (ROM checksum {checksum:08X})"); 57 58 let mut sram = BoxedByteArray::new(); 59 if let Some(initial_sram) = initial_sram 60 && initial_sram.len() >= POPULOUS_SRAM_LEN 61 { 62 sram.copy_from_slice(&initial_sram[..POPULOUS_SRAM_LEN]); 63 } 64 65 Self::Populous { sram, sram_dirty: false } 66 } 67 // Street Fighter II' - Champion Edition (Japan) 68 0x33DEB700 => { 69 log::info!( 70 "Enabling Street Fighter II bank-switching mapper (ROM checksum {checksum:08X})" 71 ); 72 Self::StreetFighter2 { rom_bank: 1 } 73 } 74 _ => { 75 log::info!("Using standard mapper"); 76 Self::None 77 } 78 } 79 } 80 81 fn read(&self, address: u32, rom: &[u8]) -> u8 { 82 debug_assert!(address <= 0x0FFFFF); 83 84 match self { 85 Self::None => read_rom_safely(rom, address), 86 Self::Populous { sram, .. } => match address >> 13 { 87 0x40..=0x43 => sram[(address & 0x7FFF) as usize], 88 _ => read_rom_safely(rom, address), 89 }, 90 &Self::StreetFighter2 { rom_bank } => match address { 91 0x000000..=0x07FFFF => read_rom_safely(rom, address), 92 0x080000..=0x0FFFFF => { 93 let banked_addr = (rom_bank << 19) | (address & 0x7FFFF); 94 read_rom_safely(rom, banked_addr) 95 } 96 _ => panic!("Invalid ROM address {address:06X}"), 97 }, 98 } 99 } 100 101 fn write(&mut self, address: u32, value: u8) { 102 match self { 103 Self::None => {} 104 Self::Populous { sram, sram_dirty } => { 105 let bank = address >> 13; 106 if (0x40..=0x43).contains(&bank) { 107 sram[(address & 0x7FFF) as usize] = value; 108 *sram_dirty = true; 109 } 110 } 111 Self::StreetFighter2 { rom_bank } => { 112 // Writing to $1FF0-$1FF3 changes the ROM bank based on the address written to 113 // Value does not matter 114 if (0x001FF0..=0x001FF3).contains(&address) { 115 *rom_bank = (address & 3) + 1; 116 } 117 } 118 } 119 } 120} 121 122#[inline(always)] 123fn read_rom_safely(rom: &[u8], address: u32) -> u8 { 124 rom[(address as usize) & (rom.len() - 1)] 125} 126 127#[derive(Debug, Clone, PartialClone, Encode, Decode)] 128pub struct HuCard { 129 #[partial_clone(default)] 130 rom: Rom, 131 mapper: Mapper, 132} 133 134impl HuCard { 135 pub fn new(mut rom: Vec<u8>, initial_sram: Option<Vec<u8>>) -> Self { 136 rom = mirror_hucard_rom(rom); 137 138 let mapper = Mapper::guess_from_rom(&rom, initial_sram); 139 140 Self { rom: Rom(rom.into_boxed_slice()), mapper } 141 } 142 143 pub fn read(&self, address: u32) -> u8 { 144 self.mapper.read(address, &self.rom) 145 } 146 147 pub fn write(&mut self, address: u32, value: u8) { 148 self.mapper.write(address, value); 149 } 150 151 pub fn clone_rom(&self) -> Vec<u8> { 152 self.rom.0.to_vec() 153 } 154 155 pub fn take_rom_from(&mut self, other: &mut Self) { 156 self.rom.0 = mem::take(&mut other.rom.0); 157 } 158 159 pub fn sram(&self) -> Option<&[u8]> { 160 match &self.mapper { 161 Mapper::Populous { sram, .. } => Some(sram.as_slice()), 162 _ => None, 163 } 164 } 165 166 pub fn is_sram_dirty(&self) -> bool { 167 match self.mapper { 168 Mapper::Populous { sram_dirty, .. } => sram_dirty, 169 _ => false, 170 } 171 } 172 173 pub fn clear_sram_dirty(&mut self) { 174 if let Mapper::Populous { sram_dirty, .. } = &mut self.mapper { 175 *sram_dirty = false; 176 } 177 } 178} 179 180fn mirror_hucard_rom(mut rom: Vec<u8>) -> Vec<u8> { 181 if rom.is_empty() { 182 // Nothing really reasonable to do here; just make the entire cartridge read 0xFF 183 rom.extend(iter::repeat_n(0xFF, 256 * 1024)); 184 } 185 186 let mut new_rom = if rom.len() == 384 * 1024 { 187 // 384KB HuCards contain two ROM chips, a 256KB chip and a 128KB chip, mapped like so: 188 // $000000-$07FFFF (banks $00-$3F): First 256KB of ROM, mirrored 2x 189 // $080000-$0FFFFF (banks $40-$7F): Last 128KB of ROM, mirrored 4x 190 let mut new_rom = Vec::with_capacity(1024 * 1024); 191 192 for _ in 0..2 { 193 new_rom.extend(&rom[..256 * 1024]); 194 } 195 new_rom.extend(&rom[256 * 1024..]); 196 197 new_rom 198 } else if rom.len() == 512 * 1024 { 199 // 512KB HuCards can apparently be one of two mappings. 200 // Mapping A (2x 256KB chips): 201 // $000000-$07FFFF (banks $00-$3F): First 256KB of ROM, mirrored 2x 202 // $080000-$0FFFFF (banks $40-$7F): Last 256KB of ROM, mirrored 2x 203 // Mapping B (1x 512KB chip): 204 // $000000-$0FFFFF (banks $00-$7F): Full 512KB of ROM, mirrored 2x 205 // It's virtually impossible to detect which mapping a game expects, so for highest 206 // compatibility, mirror the last 256KB of ROM 3x (inspired by what Mednafen does). 207 // Explicitly: 208 // $00-$1F: First 256KB 209 // $20-$3F: Second 256KB (important for games with 1x 512KB chip) 210 // $40-$5F: Second 256KB (important for games with 2x 256KB chips) 211 // $60-$7F: Second 256KB (probably never used?) 212 if rom.capacity() < 1024 * 1024 { 213 rom.reserve(1024 * 1024 - rom.capacity()); 214 } 215 216 for i in 256 * 1024..512 * 1024 { 217 rom.push(rom[i]); 218 } 219 220 rom 221 } else { 222 // For other sizes (e.g. 768KB or 1MB), normal mirroring up to the next power of two works 223 rom 224 }; 225 226 jgenesis_common::rom::mirror_to_next_power_of_two(&mut new_rom); 227 228 new_rom 229} 230 231#[derive(Debug, Clone, Encode, Decode)] 232struct Timer { 233 counter: u8, 234 reload: u8, 235 prescaler: u64, 236 enabled: bool, 237 cycles: u64, 238 next_overflow_cycles: u64, 239 just_overflowed: bool, 240} 241 242impl Timer { 243 fn new() -> Self { 244 Self { 245 reload: 0, 246 counter: 0, 247 prescaler: TIMER_PRESCALER_DIVIDER, 248 enabled: false, 249 cycles: 0, 250 next_overflow_cycles: u64::MAX, 251 just_overflowed: false, 252 } 253 } 254 255 fn step_to(&mut self, cycles: u64, tiq_pending: &mut bool) { 256 if cycles < self.next_overflow_cycles { 257 return; 258 } 259 260 self.force_step_to(cycles, tiq_pending); 261 } 262 263 fn force_step_to(&mut self, cycles: u64, tiq_pending: &mut bool) { 264 if !self.enabled { 265 self.cycles = cycles; 266 return; 267 } 268 269 let mut elapsed_cycles = cycles.saturating_sub(self.cycles); 270 self.cycles = cycles; 271 272 while elapsed_cycles != 0 { 273 if self.just_overflowed { 274 self.counter = self.reload; 275 self.just_overflowed = false; 276 } 277 278 let timer_elapsed = cmp::min(elapsed_cycles, self.prescaler); 279 self.prescaler -= timer_elapsed; 280 elapsed_cycles -= timer_elapsed; 281 282 if self.prescaler == 0 { 283 self.prescaler = TIMER_PRESCALER_DIVIDER; 284 285 if self.counter == 0 { 286 *tiq_pending = true; 287 self.just_overflowed = true; 288 289 // On overflow, allow the counter to read 0x7F for a single cycle afterwards. 290 // This fixes Battle Royale failing to boot; it depends on being able to eventually 291 // read a non-zero timer counter value while the timer reload is zero 292 } 293 self.counter = self.counter.wrapping_sub(1) & 0x7F; 294 } 295 } 296 297 let effective_counter = if self.just_overflowed { self.reload } else { self.counter }; 298 self.next_overflow_cycles = 299 cycles + self.prescaler + u64::from(effective_counter) * TIMER_PRESCALER_DIVIDER; 300 } 301 302 // $1FEC00: Timer reload value 303 fn write_reload(&mut self, value: u8) { 304 self.reload = value & 0x7F; 305 306 log::trace!("Timer reload: {}", self.reload); 307 } 308 309 // $1FEC01: Timer enabled 310 fn write_enabled(&mut self, value: u8, cycles: u64) { 311 let prev_enabled = self.enabled; 312 self.enabled = value.bit(0); 313 314 if !prev_enabled && self.enabled { 315 self.counter = self.reload; 316 self.prescaler = TIMER_PRESCALER_DIVIDER; 317 318 self.cycles = cycles; 319 self.next_overflow_cycles = 320 cycles + TIMER_PRESCALER_DIVIDER * u64::from(self.counter + 1); 321 self.just_overflowed = false; 322 323 log::trace!( 324 "Timer newly enabled at cycles {cycles}; next overflow at {}", 325 self.next_overflow_cycles 326 ); 327 } 328 329 if !self.enabled { 330 // Timer will never overflow until it's enabled again 331 self.next_overflow_cycles = u64::MAX; 332 } 333 334 log::trace!("Timer enabled: {} (cycles {cycles})", self.enabled); 335 } 336} 337 338#[derive(Debug, Clone, Encode, Decode)] 339pub struct CpuRegisters { 340 clock_speed: ClockSpeed, 341 tiq_disabled: bool, 342 tiq_pending: bool, 343 irq1_disabled: bool, 344 irq1_pending: bool, 345 irq2_disabled: bool, 346 irq2_pending: bool, 347 timer: Timer, 348 io_buffer: u8, 349} 350 351impl CpuRegisters { 352 fn new() -> Self { 353 Self { 354 clock_speed: ClockSpeed::default(), 355 tiq_disabled: false, 356 tiq_pending: false, 357 irq1_disabled: false, 358 irq1_pending: false, 359 irq2_disabled: false, 360 irq2_pending: false, 361 timer: Timer::new(), 362 io_buffer: 0xFF, 363 } 364 } 365 366 pub fn io_buffer(&self) -> u8 { 367 self.io_buffer 368 } 369 370 pub fn update_io_buffer(&mut self, value: u8, mask: u8) -> u8 { 371 self.io_buffer = (value & mask) | (self.io_buffer & !mask); 372 self.io_buffer 373 } 374 375 pub fn irq1_pending_mut(&mut self) -> &mut bool { 376 &mut self.irq1_pending 377 } 378 379 pub fn step_timer_to(&mut self, cycles: u64) { 380 self.timer.step_to(cycles, &mut self.tiq_pending); 381 } 382 383 // $1FF400-$1FF403: Interrupt registers 384 pub fn read_interrupt_register(&mut self, address: u32) -> u8 { 385 match address & 3 { 386 0 | 1 => {} // Unused 387 2 => { 388 self.io_buffer = (self.io_buffer & 0xF8) 389 | (u8::from(self.tiq_disabled) << 2) 390 | (u8::from(self.irq1_disabled) << 1) 391 | u8::from(self.irq2_disabled); 392 } 393 3 => { 394 self.io_buffer = (self.io_buffer & 0xF8) 395 | (u8::from(self.tiq_pending) << 2) 396 | (u8::from(self.irq1_pending) << 1) 397 | u8::from(self.irq2_pending); 398 } 399 _ => unreachable!("value & 3 is always <= 3"), 400 } 401 402 self.io_buffer 403 } 404 405 // $1FF400-$1FF403: Interrupt registers 406 pub fn write_interrupt_register(&mut self, address: u32, value: u8) { 407 log::trace!("Interrupt register write: {address:06X} {value:02X}"); 408 409 self.io_buffer = value; 410 411 match address & 3 { 412 0 | 1 => {} // Unused 413 2 => { 414 self.tiq_disabled = value.bit(2); 415 self.irq1_disabled = value.bit(1); 416 self.irq2_disabled = value.bit(0); 417 418 log::trace!("TIQ enabled: {}", !self.tiq_disabled); 419 log::trace!("IRQ1 enabled: {}", !self.irq1_disabled); 420 log::trace!("IRQ2 enabled: {}", !self.irq2_disabled); 421 } 422 3 => { 423 // All writes acknowledge the timer interrupt 424 self.tiq_pending = false; 425 426 log::trace!("TIQ acknowledged"); 427 } 428 _ => unreachable!("value & 3 is always <= 3"), 429 } 430 } 431 432 // $1FEC00-$1FEC01: Timer registers 433 pub fn read_timer_register(&mut self, cycles: u64) -> u8 { 434 self.timer.force_step_to(cycles, &mut self.tiq_pending); 435 436 self.io_buffer = (self.io_buffer & !0x7F) | (self.timer.counter & 0x7F); 437 self.io_buffer 438 } 439 440 // $1FEC00-$1FEC01: Timer registers 441 pub fn write_timer_register(&mut self, address: u32, value: u8, cycles: u64) { 442 self.timer.force_step_to(cycles, &mut self.tiq_pending); 443 444 self.io_buffer = value; 445 446 match address & 1 { 447 0 => self.timer.write_reload(value), 448 1 => self.timer.write_enabled(value, cycles), 449 _ => unreachable!("value & 1 is always <= 1"), 450 } 451 } 452 453 pub fn interrupt_lines(&self) -> InterruptLines { 454 InterruptLines { 455 irq1: self.irq1_pending && !self.irq1_disabled, 456 irq2: self.irq2_pending && !self.irq2_disabled, 457 tiq: self.tiq_pending && !self.tiq_disabled, 458 } 459 } 460} 461 462#[derive(Debug, Clone, Encode, Decode)] 463pub struct Memory { 464 working_ram: BoxedByteArray<WORKING_RAM_LEN>, 465 cpu_registers: CpuRegisters, 466 cpu_fast_clock_divider: u64, 467} 468 469impl Memory { 470 pub fn new(config: &PceEmulatorConfig) -> Self { 471 Self { 472 working_ram: BoxedByteArray::new_random(), 473 cpu_registers: CpuRegisters::new(), 474 cpu_fast_clock_divider: config.clamped_cpu_fast_divider(), 475 } 476 } 477 478 pub fn read_working_ram(&self, address: u32) -> u8 { 479 self.working_ram[(address as usize) & (WORKING_RAM_LEN - 1)] 480 } 481 482 pub fn write_working_ram(&mut self, address: u32, value: u8) { 483 self.working_ram[(address as usize) & (WORKING_RAM_LEN - 1)] = value; 484 } 485 486 pub fn cpu_clock_divider(&self) -> u64 { 487 match self.cpu_registers.clock_speed { 488 ClockSpeed::Low => 12, // ~1.79 MHz 489 ClockSpeed::High => self.cpu_fast_clock_divider, // ~7.16 MHz when not overclocking 490 } 491 } 492 493 pub fn set_clock_speed(&mut self, speed: ClockSpeed) { 494 log::trace!("Clock speed set to {speed:?}"); 495 self.cpu_registers.clock_speed = speed; 496 } 497 498 pub fn cpu_registers(&mut self) -> &mut CpuRegisters { 499 &mut self.cpu_registers 500 } 501 502 pub fn interrupt_lines(&self) -> InterruptLines { 503 self.cpu_registers.interrupt_lines() 504 } 505 506 pub fn reload_config(&mut self, config: &PceEmulatorConfig) { 507 self.cpu_fast_clock_divider = config.clamped_cpu_fast_divider(); 508 } 509}