1use crate::api::debug::{DebugPendingWrite, GenesisDebuggerWithCpus, genesis_components}; 2use crate::input::InputState; 3use crate::timing::CycleCounters; 4use bincode::{Decode, Encode}; 5use genesis_components::cartridge::{Cartridge, GenesisRegionExt}; 6use genesis_components::memory::Memory; 7use genesis_components::vdp::{Vdp, VdpBusView, VdpTickEffect}; 8use genesis_components::ym2612::Ym2612; 9use genesis_components::{GenesisEmulatorConfigExt, vdp}; 10use genesis_config::{GenesisEmulatorConfig, GenesisRegion}; 11use jgenesis_common::frontend::{PartialClone, TimingMode}; 12use jgenesis_common::num::{GetBit, U16Ext}; 13use m68000_emu::debug::DummyM68000Debugger; 14use s32x_core::api::debug::Dummy32XDebugger; 15use s32x_core::api::{GenesisVdpInfo, Sega32X, Sega32XAudioOutput}; 16use segacd_core::api::debug::DummySegaCdDebugger; 17use segacd_core::api::{SegaCd, SegaCdAudioOutput, SegaCdLoadResult}; 18use std::{cmp, mem}; 19use ti_sn76489::{Sn76489, Sn76489TickEffect, Sn76489Version}; 20use z80_emu::debug::DummyZ80Debugger; 21use z80_emu::traits::InterruptLine; 22 23pub mod debug; 24 25const MARS: [u8; 4] = *b"MARS"; 26 27pub trait GenesisAudioOutput: SegaCdAudioOutput + Sega32XAudioOutput { 28 fn collect_ym2612(&mut self, sample: (f64, f64)); 29 30 fn collect_psg(&mut self, sample: f64); 31} 32 33#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 34pub struct Z80BankRegister { 35 bank_number: u32, 36 current_bit: u8, 37} 38 39impl Z80BankRegister { 40 const BITS: u8 = 9; 41 42 pub fn value(self) -> u32 { 43 self.bank_number 44 } 45 46 pub fn map_to_68k_address(self, z80_address: u16) -> u32 { 47 (self.bank_number << 15) | u32::from(z80_address & 0x7FFF) 48 } 49 50 pub fn write_bit(&mut self, bit: bool) { 51 self.bank_number = (self.bank_number >> 1) | (u32::from(bit) << (Self::BITS - 1)); 52 } 53} 54 55#[derive(Debug, Clone, Copy, Encode, Decode)] 56pub struct Z80Signals { 57 busreq: bool, 58 reset: bool, 59} 60 61impl Default for Z80Signals { 62 fn default() -> Self { 63 Self { busreq: false, reset: true } 64 } 65} 66 67impl Z80Signals { 68 fn busack(self) -> bool { 69 self.busreq && !self.reset 70 } 71} 72 73#[derive(Debug, Clone, Default, Encode, Decode)] 74pub struct PendingWrites { 75 byte: Vec<(u32, u8)>, 76 word: Vec<(u32, u16)>, 77} 78 79impl PendingWrites { 80 #[inline] 81 #[must_use] 82 pub fn new() -> Self { 83 Self { byte: Vec::with_capacity(20), word: Vec::with_capacity(20) } 84 } 85 86 fn clear(&mut self) { 87 self.byte.clear(); 88 self.word.clear(); 89 } 90 91 pub fn to_debug_vec(&self) -> Vec<DebugPendingWrite> { 92 self.byte 93 .iter() 94 .map(|&(address, value)| DebugPendingWrite::Byte { address, value }) 95 .chain( 96 self.word 97 .iter() 98 .map(|&(address, value)| DebugPendingWrite::Word { address, value }), 99 ) 100 .collect() 101 } 102} 103 104struct VdpView<'bus> { 105 sega_cd: Option<&'bus mut SegaCd>, 106 sega_32x: Option<&'bus mut Sega32X>, 107 cartridge: Option<&'bus mut Cartridge>, 108 memory: &'bus mut Memory, 109 open_bus: &'bus mut u16, 110} 111 112impl VdpBusView for VdpView<'_> { 113 fn read_word_for_dma(&mut self, address: u32) -> u16 { 114 match address { 115 0x000000..=0x3FFFFF => { 116 if let Some(sega_32x) = &mut self.sega_32x 117 && sega_32x.cartridge_mut().is_some() 118 { 119 // 32X or Sega CD 32X Mode 1 120 // $000000-$3FFFFF is only accessible while adapter is disabled or RV=1 121 if (!sega_32x.adapter_enabled() || sega_32x.rom_to_vram_dma()) 122 && let Some(cartridge) = sega_32x.cartridge_mut() 123 { 124 cartridge.read_word_for_dma(address, self.open_bus) 125 } else { 126 *self.open_bus 127 } 128 } else if let Some(cartridge) = &mut self.cartridge { 129 // Standalone Genesis or Sega CD Mode 1 130 cartridge.read_word_for_dma(address, self.open_bus) 131 } else if let Some(sega_cd) = &mut self.sega_cd { 132 // Sega CD [32X] Mode 2 133 sega_cd.read_word_for_dma(address, self.open_bus) 134 } else { 135 *self.open_bus 136 } 137 } 138 0xE00000..=0xFFFFFF => self.memory.read_main_ram_word(address), 139 _ => *self.open_bus, 140 } 141 } 142} 143 144#[derive(Debug, Encode, Decode, PartialClone)] 145pub struct GenesisBus { 146 #[partial_clone(partial)] 147 pub sega_cd: Option<SegaCd>, 148 #[partial_clone(partial)] 149 pub sega_32x: Option<Sega32X>, 150 #[partial_clone(partial)] 151 pub cartridge: Option<Cartridge>, 152 pub memory: Memory, 153 pub vdp: Vdp, 154 pub psg: Sn76489, 155 pub ym2612: Ym2612, 156 pub input: InputState, 157 pub z80_bank: Z80BankRegister, 158 pub z80_signals: Z80Signals, 159 pub pending_writes: PendingWrites, 160 pub cycles: CycleCounters, 161 pub timing_mode: TimingMode, 162 pub open_bus: u16, 163 pub m68k_opcode: u16, 164 pub m68k_reset: bool, 165} 166 167impl GenesisBus { 168 pub fn new( 169 timing_mode: TimingMode, 170 cartridge: Option<Cartridge>, 171 sega_cd: Option<SegaCd>, 172 sega_32x: Option<Sega32X>, 173 config: &GenesisEmulatorConfig, 174 ) -> Self { 175 // At most one of Genesis+32X should have a cartridge 176 debug_assert!( 177 cartridge.is_none() 178 || sega_32x.as_ref().is_none_or(|sega_32x| sega_32x.cartridge().is_none()) 179 ); 180 181 let memory = Memory::new(config); 182 let vdp = Vdp::new(timing_mode, config.to_vdp_config(sega_32x.is_some())); 183 let psg = Sn76489::new(Sn76489Version::Standard); 184 let ym2612 = Ym2612::new(config); 185 186 let six_button_incompatible = { 187 let cartridge = sega_32x.as_ref().and_then(Sega32X::cartridge).or(cartridge.as_ref()); 188 189 cartridge 190 .map(|cartridge| cartridge.metadata().six_button_incompatible) 191 .or_else(|| sega_cd.as_ref().map(SegaCd::has_six_button_incompatible_game)) 192 .unwrap_or(false) 193 }; 194 let input = InputState::new(config, six_button_incompatible); 195 196 let cycles = CycleCounters::new(config.clamped_m68k_divider()); 197 198 Self { 199 sega_cd, 200 sega_32x, 201 cartridge, 202 memory, 203 vdp, 204 psg, 205 ym2612, 206 input, 207 z80_bank: Z80BankRegister::default(), 208 z80_signals: Z80Signals::default(), 209 pending_writes: PendingWrites::new(), 210 cycles, 211 timing_mode, 212 open_bus: 0, 213 m68k_opcode: 0, 214 m68k_reset: true, 215 } 216 } 217 218 #[inline] 219 pub fn tick_components<const DEBUG: bool>( 220 &mut self, 221 m68k_cycles: u32, 222 mclk_cycles: u64, 223 m68k_wait: bool, 224 audio_output: &mut impl GenesisAudioOutput, 225 mut debugger: Option<GenesisDebuggerWithCpus<'_, '_>>, 226 ) -> SegaCdLoadResult<VdpTickEffect> { 227 if let Some(cartridge) = &mut self.cartridge { 228 cartridge.tick(m68k_cycles); 229 } 230 231 self.input.tick(m68k_cycles); 232 233 while self.cycles.should_tick_psg() { 234 if self.psg.tick() == Sn76489TickEffect::Clocked { 235 // PSG only has mono output in the Genesis; stereo output is only for Game Gear 236 let (psg_sample, _) = self.psg.sample(); 237 audio_output.collect_psg(psg_sample); 238 } 239 240 self.cycles.psg_cycle(); 241 } 242 243 let vdp_tick_effect = self.vdp.tick( 244 mclk_cycles, 245 &mut VdpView { 246 sega_cd: self.sega_cd.as_mut(), 247 sega_32x: self.sega_32x.as_mut(), 248 cartridge: self.cartridge.as_mut(), 249 memory: &mut self.memory, 250 open_bus: &mut self.open_bus, 251 }, 252 ); 253 254 if self.cycles.ym2612_sync_needed() || vdp_tick_effect == VdpTickEffect::FrameComplete { 255 self.sync_ym2612(); 256 257 for sample in self.ym2612.drain_output_samples() { 258 audio_output.collect_ym2612(sample); 259 } 260 } 261 262 if let Some(sega_cd) = &mut self.sega_cd { 263 if DEBUG && let Some(debugger) = &mut debugger { 264 let mut sega_cd_debugger = debugger.for_sega_cd( 265 self.sega_32x.as_mut(), 266 genesis_components!(self), 267 self.cartridge.as_mut(), 268 ); 269 sega_cd.tick::<true>(mclk_cycles, audio_output, &mut sega_cd_debugger)?; 270 } else { 271 sega_cd.tick::<false>(mclk_cycles, audio_output, &mut DummySegaCdDebugger)?; 272 } 273 } 274 275 if let Some(sega_32x) = &mut self.sega_32x { 276 let genesis_vdp_info = GenesisVdpInfo { 277 scanline: self.vdp.scanline(), 278 scanline_mclk: self.vdp.scanline_mclk(), 279 frame_size: self.vdp.frame_size(), 280 border_size: self.vdp.border_size(), 281 scanlines_in_current_frame: self.vdp.scanlines_in_current_frame(), 282 }; 283 284 if DEBUG && let Some(debugger) = &mut debugger { 285 let mut s32x_debugger = 286 debugger.for_32x(self.sega_cd.as_mut(), genesis_components!(self)); 287 sega_32x.tick::<true>( 288 mclk_cycles, 289 genesis_vdp_info, 290 audio_output, 291 &mut *s32x_debugger, 292 ); 293 } else { 294 sega_32x.tick::<false>( 295 mclk_cycles, 296 genesis_vdp_info, 297 audio_output, 298 &mut Dummy32XDebugger, 299 ); 300 } 301 } 302 303 check_for_long_dma_skip(&self.vdp, &mut self.cycles); 304 305 if !m68k_wait { 306 self.vdp.update_interrupt_latches(); 307 } 308 309 self.apply_pending_writes(); 310 311 Ok(vdp_tick_effect) 312 } 313 314 pub(crate) fn cartridge(&self) -> Option<&Cartridge> { 315 self.sega_32x.as_ref().and_then(Sega32X::cartridge).or(self.cartridge.as_ref()) 316 } 317 318 pub(crate) fn cartridge_mut(&mut self) -> Option<&mut Cartridge> { 319 self.sega_32x.as_mut().and_then(Sega32X::cartridge_mut).or(self.cartridge.as_mut()) 320 } 321 322 pub fn game_title(&mut self) -> Option<String> { 323 self.cartridge() 324 .map(|cartridge| cartridge.program_title().to_owned()) 325 .or_else(|| self.sega_cd.as_ref().and_then(SegaCd::disc_title)) 326 } 327 328 pub fn has_persistent_ram(&self) -> bool { 329 self.sega_cd.is_some() || self.cartridge().is_some_and(Cartridge::is_ram_persistent) 330 } 331 332 pub fn get_and_clear_persistent_ram_dirty(&mut self) -> bool { 333 let mut dirty = self.sega_cd.as_mut().is_some_and(SegaCd::take_backup_ram_dirty); 334 335 dirty |= self.cartridge_mut().is_some_and(Cartridge::get_and_clear_ram_dirty); 336 337 dirty 338 } 339 340 pub fn reload_config(&mut self, config: &GenesisEmulatorConfig) { 341 if let Some(sega_cd) = &mut self.sega_cd { 342 sega_cd.reload_config(config); 343 } 344 345 if let Some(sega_32x) = &mut self.sega_32x { 346 sega_32x.reload_config(config); 347 } 348 349 if let Some(cartridge) = &mut self.cartridge { 350 cartridge.reload_config(config); 351 } 352 353 self.memory.reload_config(config); 354 self.vdp.reload_config(config.to_vdp_config(self.sega_32x.is_some())); 355 self.ym2612.reload_config(config); 356 self.input.reload_config(config); 357 self.cycles.update_m68k_divider(config.clamped_m68k_divider()); 358 } 359 360 pub fn reset(&mut self) { 361 if let Some(sega_cd) = &mut self.sega_cd { 362 sega_cd.reset(); 363 } 364 365 if let Some(sega_32x) = &mut self.sega_32x { 366 sega_32x.reset(); 367 } 368 369 self.z80_signals = Z80Signals::default(); 370 self.ym2612.reset(); 371 } 372 373 fn apply_pending_writes(&mut self) { 374 let mut pending_writes = mem::take(&mut self.pending_writes); 375 376 for &(address, value) in &pending_writes.byte { 377 self.apply_write::<false>(address, value.into()); 378 } 379 380 for &(address, value) in &pending_writes.word { 381 self.apply_write::<true>(address, value); 382 } 383 384 pending_writes.clear(); 385 self.pending_writes = pending_writes; 386 } 387 388 fn m68k_read<const WORD: bool>(&mut self, address: u32) -> u16 { 389 let address = address & 0xFFFFFF; 390 log::trace!("Main bus {} read, address={address:06X}", if WORD { "word" } else { "byte" }); 391 392 let value = match address { 393 0x000000..=0x3FFFFF => { 394 // If cartridge is present, maps to cartridge 395 // Otherwise maps to Sega CD BIOS ROM / PRG RAM / Word RAM 396 if let Some(sega_32x) = &mut self.sega_32x 397 && sega_32x.cartridge_mut().is_some() 398 { 399 if sega_32x.adapter_enabled() { 400 sega_32x.m68k_read_cartridge::<WORD>(address, self.open_bus) 401 } else if let Some(cartridge) = sega_32x.cartridge_mut() { 402 cartridge.read::<WORD>(address, self.open_bus) 403 } else { 404 self.read_open_bus::<WORD>(address) 405 } 406 } else if let Some(cartridge) = &mut self.cartridge { 407 cartridge.read::<WORD>(address, self.open_bus) 408 } else if let Some(sega_cd) = &mut self.sega_cd { 409 sega_cd.main_read_memory::<WORD>(address) 410 } else { 411 self.read_open_bus::<WORD>(address) 412 } 413 } 414 0x400000..=0x7FFFFF => { 415 // If cartridge is present, maps to Sega CD if present, otherwise let it go through to the cartridge 416 // Otherwise, assuming Sega CD is present, maps to Sega CD RAM cartridge 417 let cartridge = self 418 .sega_32x 419 .as_mut() 420 .and_then(Sega32X::cartridge_mut) 421 .or(self.cartridge.as_mut()); 422 423 match (cartridge, &mut self.sega_cd) { 424 (Some(_cartridge), Some(sega_cd)) => sega_cd.main_read_memory::<WORD>(address), 425 (Some(cartridge), None) => cartridge.read::<WORD>(address, self.open_bus), 426 (None, Some(sega_cd)) => sega_cd.read_ram_cartridge::<WORD>(address), 427 (None, None) => self.read_open_bus::<WORD>(address), 428 } 429 } 430 0x800000..=0x9FFFFF 431 if let Some(sega_32x) = &mut self.sega_32x 432 && sega_32x.adapter_enabled() => 433 { 434 sega_32x.m68k_read_memory::<WORD>(address, self.open_bus) 435 } 436 0xA00000..=0xA0FFFF if self.z80_signals.busack() => { 437 // Z80 memory map; 68k can only access when the Z80 is running and removed from the bus 438 self.cycles.record_68k_z80_bus_access(); 439 440 // For 68k access, $8000-$FFFF mirrors $0000-$7FFF 441 let byte = 442 <Self as z80_emu::BusInterface>::read_memory(self, (address & 0x7FFF) as u16); 443 if WORD { 444 // All Z80 access is byte-size; word reads mirror the byte in both MSB and LSB 445 u16::from_ne_bytes([byte; 2]) 446 } else { 447 byte.into() 448 } 449 } 450 0xA10000..=0xA1001F => { 451 // I/O ports, even addresses mirror odd addresses 452 let byte = self.read_io_register(address | 1); 453 if WORD { u16::from_ne_bytes([byte; 2]) } else { byte.into() } 454 } 455 0xA11100 => { 456 // Bit 8 is Z80 BUSACK (active low), other bits are unused 457 let busack_bit = u16::from(!self.z80_signals.busack()) << 8; 458 459 // Unused bits should read open bus; Danny Sullivan's Indy Heat (Proto) and Time Killers 460 // depend on this or they will fail to boot 461 let busack_word = busack_bit | (self.open_bus & !(1 << 8)); 462 if WORD { busack_word } else { busack_word.msb().into() } 463 } 464 0xA12000..=0xA12FFF if let Some(sega_cd) = &mut self.sega_cd => { 465 sega_cd.main_read_register::<WORD>(address) 466 } 467 0xA130EC..=0xA130EF if self.sega_32x.is_some() => { 468 // Always reads the ASCII string "MARS"; used by 32X games to detect 32X hardware 469 let idx = (address & 3) as usize; 470 if WORD { 471 u16::from_be_bytes(MARS[idx..idx + 2].try_into().unwrap()) 472 } else { 473 MARS[idx].into() 474 } 475 } 476 0xA15000..=0xA15FFF => { 477 if let Some(sega_32x) = &mut self.sega_32x { 478 sega_32x.m68k_read_register::<WORD>(address, self.open_bus) 479 } else if let Some(cartridge) = &mut self.cartridge { 480 cartridge.read::<WORD>(address, self.open_bus) 481 } else { 482 self.read_open_bus::<WORD>(address) 483 } 484 } 485 0xC00000..=0xC0001F => self.read_vdp::<WORD>(address), 486 0xE00000..=0xFFFFFF => self.memory.read_main_ram::<WORD>(address), 487 _ => self.read_open_bus::<WORD>(address), 488 }; 489 490 if WORD { 491 self.open_bus = value; 492 } else { 493 // TODO this is probably not right, probably depends on memory region 494 self.open_bus.set_msb(value as u8); 495 } 496 497 value 498 } 499 500 #[inline(always)] 501 fn read_open_bus<const WORD: bool>(&self, address: u32) -> u16 { 502 if WORD { self.open_bus } else { self.open_bus.be_byte(address & 1).into() } 503 } 504 505 fn apply_write<const WORD: bool>(&mut self, address: u32, value: u16) { 506 let address = address & 0xFFFFFF; 507 508 if WORD { 509 log::trace!("Main bus word write: address={address:06X}, value={value:04X}"); 510 } else { 511 log::trace!("Main bus byte write: address={address:06X}, value={:02X}", value & 0xFF); 512 } 513 514 match address { 515 0x000000..=0x3FFFFF => { 516 // If cartridge is present, maps to cartridge 517 // Otherwise maps to Sega CD BIOS ROM / PRG RAM / Word RAM 518 if let Some(sega_32x) = &mut self.sega_32x 519 && sega_32x.cartridge_mut().is_some() 520 { 521 if sega_32x.adapter_enabled() { 522 sega_32x.m68k_write_cartridge::<WORD>(address, value); 523 } else if let Some(cartridge) = sega_32x.cartridge_mut() { 524 cartridge.write::<WORD>(address, value); 525 } 526 } else if let Some(cartridge) = &mut self.cartridge { 527 cartridge.write::<WORD>(address, value); 528 } else if let Some(sega_cd) = &mut self.sega_cd { 529 sega_cd.main_write_memory::<WORD>(address, value); 530 } 531 } 532 0x400000..=0x7FFFFF => { 533 // If cartridge is present, maps to Sega CD if present, otherwise let it go through to the cartridge 534 // Otherwise, assuming Sega CD is present, maps to Sega CD RAM cartridge 535 let cartridge = self 536 .sega_32x 537 .as_mut() 538 .and_then(Sega32X::cartridge_mut) 539 .or(self.cartridge.as_mut()); 540 541 match (cartridge, &mut self.sega_cd) { 542 (Some(_cartridge), Some(sega_cd)) => { 543 sega_cd.main_write_memory::<WORD>(address, value); 544 } 545 (Some(cartridge), None) => { 546 cartridge.write::<WORD>(address, value); 547 } 548 (None, Some(sega_cd)) => { 549 sega_cd.write_ram_cartridge::<WORD>(address, value); 550 } 551 (None, None) => {} 552 } 553 } 554 0x800000..=0x9FFFFF 555 if let Some(sega_32x) = &mut self.sega_32x 556 && sega_32x.adapter_enabled() => 557 { 558 sega_32x.m68k_write_memory::<WORD>(address, value); 559 } 560 0xA00000..=0xA0FFFF if self.z80_signals.busack() => { 561 // Z80 memory map; writable by the 68k only when the Z80 is removed from the bus 562 // and not reset 563 self.cycles.record_68k_z80_bus_access(); 564 565 // Word-size writes write the MSB as a byte-size write 566 let byte = if WORD { value.msb() } else { value as u8 }; 567 568 // For 68k access, $8000-$FFFF mirrors $0000-$7FFF 569 <Self as z80_emu::BusInterface>::write_memory( 570 self, 571 (address & 0x7FFF) as u16, 572 byte, 573 ); 574 } 575 0xA10000..=0xA1001F => { 576 // I/O ports, even addresses mirror odd addresses 577 self.write_io_register(address | 1, value as u8); 578 } 579 0xA11100 => { 580 self.z80_signals.busreq = if WORD { value.bit(8) } else { value.bit(0) }; 581 log::trace!("Set Z80 BUSREQ to {}", self.z80_signals.busreq); 582 } 583 0xA11200 => { 584 let z80_reset = if WORD { !value.bit(8) } else { !value.bit(0) }; 585 self.set_z80_reset(z80_reset); 586 } 587 0xA12000..=0xA12FFF if let Some(sega_cd) = &mut self.sega_cd => { 588 sega_cd.main_write_register::<WORD>(address, value); 589 } 590 0xA13000..=0xA13FFF if let Some(cartridge) = self.cartridge_mut() => { 591 // TIME signal; used by various cartridge mapper registers 592 cartridge.write::<WORD>(address, value); 593 } 594 0xA15000..=0xA15FFF => { 595 if let Some(sega_32x) = &mut self.sega_32x { 596 sega_32x.m68k_write_register::<WORD>(address, value); 597 } else if let Some(cartridge) = &mut self.cartridge { 598 // SVP maps registers to $A15xxx 599 cartridge.write::<WORD>(address, value); 600 } 601 } 602 0xC00000..=0xC0001F => { 603 self.write_vdp_psg::<WORD>(address, value); 604 } 605 0xE00000..=0xFFFFFF => { 606 self.memory.write_main_ram::<WORD>(address, value); 607 } 608 _ => {} 609 } 610 } 611 612 fn read_io_register(&mut self, address: u32) -> u8 { 613 match address { 614 // Version register 615 0xA10001 => { 616 let region = self 617 .cartridge() 618 .map(Cartridge::region) 619 .or_else(|| self.sega_cd.as_ref().map(SegaCd::region)) 620 .unwrap_or(GenesisRegion::Americas); 621 622 // TODO version (lowest 4 bits) hardcoded to 0 623 (u8::from(region.version_bit()) << 7) 624 | (u8::from(self.timing_mode == TimingMode::Pal) << 6) 625 | (u8::from(self.sega_cd.is_none()) << 5) 626 } 627 0xA10003 => self.input.read_p1_data(), 628 0xA10005 => self.input.read_p2_data(), 629 0xA10007 => self.input.read_ext_data(), 630 0xA10009 => self.input.read_p1_ctrl(), 631 0xA1000B => self.input.read_p2_ctrl(), 632 0xA1000D => self.input.read_ext_ctrl(), 633 0xA1000F => self.input.read_p1_tx_data(), 634 0xA10015 => self.input.read_p2_tx_data(), 635 0xA1001B => self.input.read_ext_tx_data(), 636 // Other I/O registers return 0x00 by default 637 _ => 0x00, 638 } 639 } 640 641 fn write_io_register(&mut self, address: u32, value: u8) { 642 match address { 643 0xA10003 => self.input.write_p1_data(value), 644 0xA10005 => self.input.write_p2_data(value), 645 0xA10007 => self.input.write_ext_data(value), 646 0xA10009 => self.input.write_p1_ctrl(value), 647 0xA1000B => self.input.write_p2_ctrl(value), 648 0xA1000D => self.input.write_ext_ctrl(value), 649 0xA1000F => self.input.write_p1_tx_data(value), 650 0xA10015 => self.input.write_p2_tx_data(value), 651 0xA1001B => self.input.write_ext_tx_data(value), 652 _ => {} 653 } 654 } 655 656 fn read_vdp<const WORD: bool>(&mut self, address: u32) -> u16 { 657 let word = match address & 0x1F { 658 0x00..=0x03 => self.vdp.read_data(), 659 0x04..=0x07 => { 660 // Highest 6 bits of VDP status register are open bus; VDPFIFOTesting DMA busy flag tests 661 // depend on this 662 self.vdp.read_status(self.m68k_opcode, self.cycles.m68k_divider.get()) 663 | (self.open_bus & 0xFC00) 664 } 665 0x08..=0x0B => self.vdp.hv_counter(self.m68k_opcode, self.cycles.m68k_divider.get()), 666 0x0C..=0x1F => { 667 // PSG / unused space; PSG is not readable 668 self.open_bus 669 } 670 _ => unreachable!("address & 0x1F is always <= 0x1F"), 671 }; 672 673 if WORD { word } else { word.to_be_bytes()[(address & 1) as usize].into() } 674 } 675 676 fn write_vdp_psg<const WORD: bool>(&mut self, address: u32, value: u16) { 677 // Byte-size VDP writes duplicate the byte into a word 678 let vdp_word = if WORD { value } else { (value & 0xFF) | (value << 8) }; 679 680 match address & 0x1F { 681 0x00..=0x03 => self.vdp.write_data(vdp_word), 682 0x04..=0x07 => self.vdp.write_control(vdp_word), 683 0x10..=0x17 if WORD || address.bit(0) => self.psg.write(value as u8), 684 0x1C..=0x1D => self.vdp.write_debug_register(vdp_word), 685 _ => {} 686 } 687 } 688 689 fn set_z80_reset(&mut self, z80_reset: bool) { 690 if !self.z80_signals.reset && z80_reset { 691 // Z80 RESET also resets the YM2612 692 // Fantastic Dizzy depends on this or music will not mute correctly when you pause the game 693 self.ym2612.reset(); 694 } 695 696 self.z80_signals.reset = z80_reset; 697 log::trace!("Set Z80 RESET to {}", self.z80_signals.reset); 698 } 699 700 fn sync_ym2612(&mut self) { 701 if self.cycles.has_ym2612_ticks() { 702 let ticks = self.cycles.take_ym2612_ticks(); 703 self.ym2612.tick(ticks); 704 } 705 } 706} 707 708// If a long DMA is in progress (i.e. the DMA will not finish on this line), preemptively skip the 709// 68000 forward by a large number of mclk cycles (up to 1250). 710// 711// This function is public so that it can be used by the Sega CD core 712#[inline] 713fn check_for_long_dma_skip(vdp: &Vdp, cycles: &mut CycleCounters) { 714 if !vdp.long_halting_dma_in_progress() { 715 return; 716 } 717 718 if !cycles.z80_halt { 719 // Don't advance for very long time slices if the Z80 is still active; doing so causes 720 // video/audio desync in Overdrive 2. 721 // 8 68K cycles is slightly less than 4 Z80 cycles 722 cycles.m68k_wait_cpu_cycles = 8; 723 return; 724 } 725 726 // Skip as close as possible to the end of the current scanline 727 let wait_cycles = cmp::max( 728 cycles.m68k_wait_cpu_cycles, 729 cmp::min( 730 cycles.max_wait_cpu_cycles, 731 (vdp::MCLK_CYCLES_PER_SCANLINE - vdp.scanline_mclk()) as u32 732 / cycles.m68k_divider_u32.get(), 733 ), 734 ); 735 cycles.m68k_wait_cpu_cycles = wait_cycles; 736 737 log::trace!( 738 "Skipping {wait_cycles} 68000 CPU cycles in long DMA optimization, scanline mclk is {}", 739 vdp.scanline_mclk() 740 ); 741} 742 743impl m68000_emu::BusInterface for GenesisBus { 744 type DebugView<'a> 745 = DummyM68000Debugger 746 where 747 Self: 'a; 748 749 #[inline] 750 fn read_byte(&mut self, address: u32) -> u8 { 751 self.m68k_read::<false>(address) as u8 752 } 753 754 #[inline] 755 fn read_word(&mut self, address: u32) -> u16 { 756 self.m68k_read::<true>(address) 757 } 758 759 #[inline] 760 fn write_byte(&mut self, address: u32, value: u8) { 761 self.pending_writes.byte.push((address, value)); 762 } 763 764 #[inline] 765 fn write_word(&mut self, address: u32, value: u16) { 766 self.pending_writes.word.push((address, value)); 767 } 768 769 #[inline] 770 fn interrupt_level(&self) -> u8 { 771 self.vdp.m68k_interrupt_level() 772 } 773 774 #[inline] 775 fn acknowledge_interrupt(&mut self, _interrupt_level: u8) { 776 // When the 68000 acknowledges a VDP interrupt, the VDP acknowledges whatever level it is 777 // currently raising rather than paying attention to the 68000's IACK lines. This is noted 778 // in official documentation which describes this hardware bug: If both HINT are VINT are 779 // enabled and the 68000 executes a long instruction right before HINT would trigger on line 780 // 224, it's possible for the following sequence of events to happen: 781 // 1. The VDP sets IPL2 to indicate a level 4 interrupt (HINT) 782 // 2. After the 68000 finishes its long instruction, it begins to handle the level 4 interrupt 783 // 3. Before the acknowledge, VBlank begins and the VDP sets IPL2+IPL1 for level 6 interrupt (VINT) 784 // 4. The 68000 sets its VPA signal to acknowledge the interrupt, and the VDP acknowledges VINT instead of HINT 785 // 5. After the 68000 returns from its HINT handler, it immediately handles the HINT a second time (having missed VINT) 786 self.vdp.acknowledge_m68k_interrupt(); 787 } 788 789 #[inline] 790 fn halt(&self) -> bool { 791 self.vdp.should_halt_cpu() 792 } 793 794 #[inline] 795 fn reset(&self) -> bool { 796 self.m68k_reset 797 } 798} 799 800impl z80_emu::BusInterface for GenesisBus { 801 type DebugView<'a> 802 = DummyZ80Debugger 803 where 804 Self: 'a; 805 806 #[inline] 807 #[allow(clippy::match_same_arms)] 808 fn read_memory(&mut self, address: u16) -> u8 { 809 log::trace!("Z80 bus read from {address:04X}"); 810 811 match address { 812 0x0000..=0x3FFF => { 813 // Z80 RAM (mirrored at $2000-$3FFF) 814 self.memory.read_audio_ram(address) 815 } 816 0x4000..=0x5FFF => { 817 // YM2612 registers/ports (mirrored every 4 addresses) 818 self.sync_ym2612(); 819 self.ym2612.read_register(address) 820 } 821 0x6000..=0x60FF => { 822 // Bank number register 823 // TODO what should this do on reads? 824 0xFF 825 } 826 0x6100..=0x7EFF => { 827 // Unused address space 828 0xFF 829 } 830 0x7F00..=0x7F1F => { 831 // VDP ports 832 self.cycles.record_z80_68k_bus_access(); 833 self.read_vdp::<false>(address.into()) as u8 834 } 835 0x7F20..=0x7FFF => { 836 // Invalid addresses 837 0xFF 838 } 839 0x8000..=0xFFFF => { 840 self.cycles.record_z80_68k_bus_access(); 841 842 let m68k_addr = self.z80_bank.map_to_68k_address(address); 843 match m68k_addr { 844 0xA00000..=0xA0FFFF => { 845 // TODO this should lock up the system 846 log::error!( 847 "Z80 attempted to read its own memory from the 68k bus; z80_addr={address:04X}, m68k_addr={m68k_addr:08X}" 848 ); 849 0xFF 850 } 851 0xE00000..=0xFFFFFF => { 852 // Z80 cannot read from 68000 working RAM 853 // TODO should probably return Z80 open bus instead? 854 0xFF 855 } 856 _ => <Self as m68000_emu::BusInterface>::read_byte(self, m68k_addr), 857 } 858 } 859 } 860 } 861 862 #[inline] 863 fn write_memory(&mut self, address: u16, value: u8) { 864 log::trace!("Z80 bus write at {address:04X}"); 865 866 match address { 867 0x0000..=0x3FFF => { 868 // Z80 RAM (mirrored at $2000-$3FFF) 869 self.memory.write_audio_ram(address, value); 870 } 871 0x4000..=0x5FFF => { 872 // YM2612 registers/ports (mirrored every 4 addresses) 873 self.sync_ym2612(); 874 match address & 0x03 { 875 0x00 => self.ym2612.write_address_1(value), 876 0x02 => self.ym2612.write_address_2(value), 877 0x01 | 0x03 => self.ym2612.write_data(value), 878 _ => unreachable!("value & 0x03 is always <= 0x03"), 879 } 880 } 881 0x6000..=0x60FF => { 882 self.z80_bank.write_bit(value.bit(0)); 883 } 884 0x6100..=0x7EFF | 0x7F20..=0x7FFF => { 885 // Unused / invalid addresses 886 // TODO writes to $7F20-$7FFF should halt the system 887 } 888 0x7F00..=0x7F1F => { 889 // VDP addresses 890 self.cycles.record_z80_68k_bus_access(); 891 self.write_vdp_psg::<false>(address.into(), value.into()); 892 } 893 0x8000..=0xFFFF => { 894 self.cycles.record_z80_68k_bus_access(); 895 896 let m68k_addr = self.z80_bank.map_to_68k_address(address); 897 if !(0xA00000..=0xA0FFFF).contains(&m68k_addr) { 898 self.apply_write::<false>(m68k_addr, value.into()); 899 } else { 900 // TODO this should lock up the system 901 log::error!( 902 "Z80 attempted to write to its own memory from the 68k bus; z80_addr={address:04X}, m68k_addr={m68k_addr:08X}, value={value:02X}" 903 ); 904 } 905 } 906 } 907 } 908 909 #[inline] 910 fn read_io(&mut self, _address: u16) -> u8 { 911 // I/O ports are not wired up to the Z80 912 0xFF 913 } 914 915 #[inline] 916 fn write_io(&mut self, _address: u16, _value: u8) { 917 // I/O ports are not wired up to the Z80 918 } 919 920 #[inline] 921 fn nmi(&self) -> InterruptLine { 922 // The NMI line is not connected to anything 923 InterruptLine::High 924 } 925 926 #[inline] 927 fn int(&self) -> InterruptLine { 928 self.vdp.z80_interrupt_line() 929 } 930 931 #[inline] 932 fn busreq(&self) -> bool { 933 self.z80_signals.busreq 934 } 935 936 #[inline] 937 fn reset(&self) -> bool { 938 self.z80_signals.reset 939 } 940}