SNES memory mapping and bus interface code
Accesses to address bus B (PPU/APU/WRAM ports) and internal CPU registers are "fast" (no waitstates) Accesses to the cartridge in the higher banks can also be fast depending on register $420D
12const FAST_MASTER_CYCLES: u64 = 6;
Accesses to WRAM and the cartridge are "slow" (+2 master cycles)
15const SLOW_MASTER_CYCLES: u64 = 8;
Accesses to the manual joypad read ports are "extra slow" (+6 master cycles)
18const XSLOW_MASTER_CYCLES: u64 = 12;
20impl Memory2Speed { 21 fn master_cycles(self) -> u64 { 22 match self { 23 Self::Fast => FAST_MASTER_CYCLES, 24 Self::Slow => SLOW_MASTER_CYCLES, 25 } 26 } 27} 28 29pub struct Bus<'a> { 30 pub memory: &'a mut Memory, 31 pub cpu_registers: &'a mut CpuInternalRegisters, 32 pub ppu: &'a mut Ppu, 33 pub apu: &'a mut Apu, 34 pub latched_interrupts: Option<LatchedInterrupts>, 35 pub access_master_cycles: u64, 36 pub pending_write: Option<(u32, u8)>, 37} 38 39impl Bus<'_> { 40 fn read_system_area(&mut self, full_address: u32) -> u8 { 41 let address = full_address & 0x7FFF; 42 match address { 43 0x0000..=0x1FFF => { 44 self.access_master_cycles = SLOW_MASTER_CYCLES; 45 46 // First 8KB of WRAM 47 self.memory.read_wram(address) 48 } 49 0x2100..=0x213F => { 50 self.access_master_cycles = FAST_MASTER_CYCLES; 51 52 // PPU ports 53 self.ppu.read_port(address).unwrap_or(self.memory.cpu_open_bus()) 54 } 55 0x2140..=0x217F => { 56 self.access_master_cycles = FAST_MASTER_CYCLES; 57 58 // APU ports 59 self.apu.read_port(address) 60 } 61 0x2180 => { 62 self.access_master_cycles = FAST_MASTER_CYCLES; 63 64 // WMDATA: WRAM port in address bus B 65 self.memory.read_wram_port() 66 } 67 0x4000..=0x41FF => { 68 self.access_master_cycles = XSLOW_MASTER_CYCLES; 69 70 // $4016 and $4017 are CPU I/O ports (manual joypad ports) 71 // The rest of this range is CPU open bus with XSlow memory speed 72 let cpu_open_bus = self.memory.cpu_open_bus(); 73 self.cpu_registers.read_register(address, cpu_open_bus).unwrap_or_else(|| { 74 self.memory.read_cartridge(full_address).unwrap_or(cpu_open_bus) 75 }) 76 } 77 0x4200..=0x5FFF => { 78 self.access_master_cycles = FAST_MASTER_CYCLES; 79 80 // CPU I/O ports (everything except manual joypad ports) 81 // $4220-$42FF and $4380-$5FFF are CPU open bus with Fast memory speed 82 let cpu_open_bus = self.memory.cpu_open_bus(); 83 self.cpu_registers.read_register(address, cpu_open_bus).unwrap_or_else(|| { 84 self.memory.read_cartridge(full_address).unwrap_or(cpu_open_bus) 85 }) 86 } 87 0x2000..=0x20FF | 0x2181..=0x3FFF => { 88 self.access_master_cycles = FAST_MASTER_CYCLES; 89 90 // Open bus with Fast memory speed 91 // Send to the cartridge first because some cartridges respond to these addresses 92 self.memory.read_cartridge(full_address).unwrap_or(self.memory.cpu_open_bus()) 93 } 94 0x6000..=0x7FFF => { 95 self.access_master_cycles = SLOW_MASTER_CYCLES; 96 97 // Open bus with Slow memory speed 98 // Send to the cartridge first because some cartridges respond to these addresses 99 self.memory.read_cartridge(full_address).unwrap_or(self.memory.cpu_open_bus()) 100 } 101 _ => panic!("invalid system area address: {full_address:06X}"), 102 } 103 } 104 105 fn write_system_area(&mut self, full_address: u32, value: u8) { 106 let address = full_address & 0x7FFF; 107 match address { 108 0x0000..=0x1FFF => { 109 // First 8KB of WRAM 110 self.memory.write_wram(address, value); 111 } 112 0x2000..=0x20FF | 0x2184..=0x21FF => { 113 // $2000-$2FFF: Open bus; do nothing (no coprocessors use this range) 114 // $2184-$21FF: Open bus in address bus B; do nothing 115 } 116 0x2100..=0x213F => { 117 // PPU ports 118 self.ppu.write_port(address, value); 119 } 120 0x2140..=0x217F => { 121 // APU ports 122 self.apu.write_port(address, value); 123 } 124 0x2180 => { 125 // WMDATA: WRAM port in address bus B 126 self.memory.write_wram_port(value); 127 } 128 0x2181 => { 129 // WMADDL: WRAM port address, low byte 130 self.memory.write_wram_port_address_low(value); 131 } 132 0x2182 => { 133 // WMADDM: WRAM port address, middle byte 134 self.memory.write_wram_port_address_mid(value); 135 } 136 0x2183 => { 137 // WMADDH: WRAM port address, high byte 138 self.memory.write_wram_port_address_high(value); 139 } 140 0x2200..=0x3FFF | 0x4400..=0x7FFF => { 141 // $2200-$3FFF: Normally open bus; some coprocessors map I/O ports or RAM to this range 142 // $4400-$5FFF: Normally open bus; some coprocessors map I/O ports to this range 143 // $6000-$7FFF: Cartridge expansion; some cartridges map RAM to this range 144 self.memory.write_cartridge(full_address, value); 145 } 146 0x4000..=0x41FF => { 147 // CPU I/O ports (manual joypad ports) 148 self.cpu_registers.write_register(address, value); 149 } 150 0x4200..=0x43FF => { 151 // CPU I/O ports (everything except manual joypad ports) 152 self.cpu_registers.write_register(address, value); 153 } 154 _ => unreachable!("value & 0x7FFF is always <= 0x7FFF"), 155 } 156 } 157 158 pub fn apply_write(&mut self, address: u32, value: u8) { 159 log::trace!("Bus write {address:06X} {value:02X}"); 160 161 let bank = (address >> 16) as u8; 162 let offset = address as u16; 163 match (bank, offset) { 164 (0x00..=0x3F | 0x80..=0xBF, 0x0000..=0x7FFF) => { 165 // System area 166 self.write_system_area(address, value); 167 } 168 (0x00..=0x3F | 0x80..=0xBF, 0x8000..=0xFFFF) | (0x40..=0x7D | 0xC0..=0xFF, _) => { 169 // Cartridge 170 self.memory.write_cartridge(address, value); 171 } 172 (0x7E..=0x7F, _) => { 173 // WRAM 174 self.memory.write_wram(address, value); 175 } 176 } 177 } 178} 179 180impl BusInterface for Bus<'_> { 181 #[inline] 182 fn read(&mut self, address: u32) -> u8 { 183 log::trace!("Bus read {address:06X}"); 184 185 let bank = (address >> 16) as u8; 186 let offset = address as u16; 187 match (bank, offset) { 188 (0x00..=0x3F | 0x80..=0xBF, 0x0000..=0x7FFF) => { 189 // System area 190 self.read_system_area(address) 191 } 192 (0x00..=0x3F, 0x8000..=0xFFFF) | (0x40..=0x7D, _) => { 193 self.access_master_cycles = SLOW_MASTER_CYCLES; 194 195 // Cartridge (Memory-1) 196 self.memory.read_cartridge(address).unwrap_or(self.memory.cpu_open_bus()) 197 } 198 (0x80..=0xBF, 0x8000..=0xFFFF) | (0xC0..=0xFF, _) => { 199 self.access_master_cycles = self.cpu_registers.memory_2_speed().master_cycles(); 200 201 // Cartridge (Memory-2) 202 self.memory.read_cartridge(address).unwrap_or(self.memory.cpu_open_bus()) 203 } 204 (0x7E..=0x7F, _) => { 205 self.access_master_cycles = SLOW_MASTER_CYCLES; 206 207 // WRAM 208 self.memory.read_wram(address) 209 } 210 } 211 } 212 213 #[inline] 214 fn write(&mut self, address: u32, value: u8) { 215 self.pending_write = Some((address, value)); 216 217 let bank = (address >> 16) as u8; 218 let offset = address as u16; 219 self.access_master_cycles = 220 address_master_cycles(self.cpu_registers.memory_2_speed(), bank, offset); 221 } 222 223 #[inline] 224 fn idle(&mut self) { 225 self.access_master_cycles = FAST_MASTER_CYCLES; 226 } 227 228 #[inline] 229 fn nmi(&self) -> bool { 230 self.latched_interrupts.map_or(self.cpu_registers.nmi_pending(), |latched| latched.nmi) 231 } 232 233 #[inline] 234 fn acknowledge_nmi(&mut self) { 235 self.cpu_registers.acknowledge_nmi(); 236 } 237 238 #[inline] 239 fn irq(&self) -> bool { 240 self.latched_interrupts 241 .map_or(self.cpu_registers.irq_pending() || self.memory.cartridge_irq(), |latched| { 242 latched.irq 243 }) 244 } 245 246 #[inline] 247 fn halt(&self) -> bool { 248 false 249 } 250 251 #[inline] 252 fn reset(&self) -> bool { 253 false 254 } 255} 256 257#[inline] 258fn address_master_cycles(memory_2_speed: Memory2Speed, bank: u8, offset: u16) -> u64 { 259 match (bank, offset) { 260 (0x00..=0x3F | 0x80..=0xBF, 0x2000..=0x3FFF | 0x4200..=0x5FFF) => FAST_MASTER_CYCLES, 261 (0x00..=0x3F | 0x80..=0xBF, 0x0000..=0x1FFF | 0x6000..=0x7FFF) 262 | (0x00..=0x3F, 0x8000..=0xFFFF) 263 | (0x40..=0x7F, _) => SLOW_MASTER_CYCLES, 264 (0x00..=0x3F | 0x80..=0xBF, 0x4000..=0x41FF) => XSLOW_MASTER_CYCLES, 265 (0x80..=0xBF, 0x8000..=0xFFFF) | (0xC0..=0xFF, _) => memory_2_speed.master_cycles(), 266 } 267} 268 269pub fn next_cpu_cycle_mclk(cpu: &Wdc65816) -> impl Fn(&Bus<'_>) -> u64 + Copy { 270 struct CapturingBus<'a, 'b>(&'a Bus<'b>, u64); 271 272 impl BusInterface for CapturingBus<'_, '_> { 273 #[inline] 274 fn read(&mut self, address: u32) -> u8 { 275 let bank = (address >> 16) as u8; 276 let offset = address as u16; 277 self.1 = address_master_cycles(self.0.cpu_registers.memory_2_speed(), bank, offset); 278 279 0 280 } 281 282 #[inline] 283 fn write(&mut self, address: u32, _value: u8) { 284 let bank = (address >> 16) as u8; 285 let offset = address as u16; 286 self.1 = address_master_cycles(self.0.cpu_registers.memory_2_speed(), bank, offset); 287 } 288 289 #[inline] 290 fn idle(&mut self) {} 291 292 #[inline] 293 fn nmi(&self) -> bool { 294 self.0.nmi() 295 } 296 297 #[inline] 298 fn acknowledge_nmi(&mut self) {} 299 300 #[inline] 301 fn irq(&self) -> bool { 302 self.0.irq() 303 } 304 305 #[inline] 306 fn halt(&self) -> bool { 307 self.0.halt() 308 } 309 310 #[inline] 311 fn reset(&self) -> bool { 312 self.0.reset() 313 } 314 } 315 316 // Determine the number of mclk cycles for the next CPU cycle by cloning all CPU state and 317 // letting the clone execute 1 cycle against a fake bus implementation that just records which 318 // address was accessed (if any). 319 |bus| { 320 let mut cpu = cpu.clone(); 321 let mut bus = CapturingBus(bus, FAST_MASTER_CYCLES); 322 cpu.tick(&mut bus); 323 bus.1 324 } 325}