Files
snes/snes-core/src/cpu/bus.rs

184 lines
6.4 KiB
Rust

use crate::ppu::PPU;
use crate::cpu::dma::DMA;
use crate::cpu::internal_registers::InternalRegisters;
use crate::rom::ROM;
use crate::rom::lo_rom::LoROM;
pub struct Bus {
wram: [u8; 0x10000],
pub ppu: PPU,
pub rom: Box<dyn ROM>,
pub internal_registers: InternalRegisters,
pub dma: DMA,
pub force_cart_lookup: bool,
}
#[derive(PartialEq, Debug)]
pub enum MemoryMap {
WRAM,
PPU,
CPU,
DMA,
Joypad,
Cartridge,
}
impl Bus {
pub fn new() -> Self {
Self {
wram: [0; 0x10000],
ppu: PPU::new(),
rom: Box::new(LoROM::new()),
internal_registers: InternalRegisters::new(),
dma: DMA::new(),
force_cart_lookup: false,
}
}
fn read_wram(&self, address: u32) -> u8 {
self.wram[(address & 0xFFFF) as usize]
}
fn write_wram(&mut self, address: u32, value: u8) {
self.wram[(address & 0xFFFF) as usize] = value;
}
fn map_address(&self, address: u32) -> MemoryMap {
if self.force_cart_lookup {
return MemoryMap::Cartridge;
}
let (bank, sub_address) = {
let bank = (address >> 16) as u8;
let sub_address = address as u16;
(bank, sub_address)
};
match bank {
0x7E..=0x7F => MemoryMap::WRAM,
0x80..=0xBF | 0x00..=0x3F => match sub_address {
0x0000..=0x1FFF => MemoryMap::WRAM,
0x2100..=0x21FF => MemoryMap::PPU,
0x4016..=0x4017 => MemoryMap::Joypad,
0x4200..=0x42FF => MemoryMap::CPU,
0x4300..=0x5FFF => MemoryMap::DMA,
_ => MemoryMap::Cartridge,
},
_ => MemoryMap::Cartridge,
}
}
/// This function is meant to be used by external parts of the code,
/// for example, to render register info without mutating them
pub fn read_external(&self, address: u32) -> u8 {
let section = self.map_address(address);
match section {
MemoryMap::WRAM => self.read_wram(address),
MemoryMap::PPU => self.ppu.registers.read_external(address as u16),
MemoryMap::CPU => self.internal_registers.read_external(
address as u16,
&self.ppu.registers,
),
MemoryMap::DMA => self.dma.read(address as u16),
MemoryMap::Joypad => 0x00, // TODO: Placeholder
MemoryMap::Cartridge => self.rom.read(address),
}
}
pub fn read(&mut self, address: u32) -> u8 {
let section = self.map_address(address);
match section {
MemoryMap::WRAM => self.read_wram(address),
MemoryMap::PPU => self.ppu.registers.read(address as u16),
MemoryMap::CPU => self.internal_registers.read(
address as u16,
&mut self.ppu.registers,
),
MemoryMap::DMA => self.dma.read(address as u16),
MemoryMap::Joypad => 0x00, // TODO: Placeholder
MemoryMap::Cartridge => self.rom.read(address),
}
}
pub fn write(&mut self, address: u32, value: u8) {
let section = self.map_address(address);
match section {
MemoryMap::WRAM => self.write_wram(address, value),
MemoryMap::PPU => self.ppu.registers.write(address as u16, value),
MemoryMap::CPU => self.internal_registers.write(
address as u16,
value,
&mut self.dma,
),
MemoryMap::DMA => self.dma.write(address as u16, value),
MemoryMap::Joypad => {}, // TODO: Placeholder
MemoryMap::Cartridge => self.rom.write(address, value),
}
}
}
impl Default for Bus {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod bus_tests {
use super::*;
#[test]
fn test_memory_map() {
let bus = Bus::new();
assert_eq!(bus.map_address(0x7E0000), MemoryMap::WRAM);
assert_eq!(bus.map_address(0x7F0000), MemoryMap::WRAM);
assert_eq!(bus.map_address(0x7E0500), MemoryMap::WRAM);
assert_eq!(bus.map_address(0x000000), MemoryMap::WRAM);
assert_eq!(bus.map_address(0x3F0000), MemoryMap::WRAM);
assert_eq!(bus.map_address(0x3F1FFF), MemoryMap::WRAM);
assert_eq!(bus.map_address(0x3F0000), MemoryMap::WRAM);
assert_eq!(bus.map_address(0x800000), MemoryMap::WRAM);
assert_eq!(bus.map_address(0xBF0000), MemoryMap::WRAM);
assert_eq!(bus.map_address(0xBF1FFF), MemoryMap::WRAM);
assert_eq!(bus.map_address(0x3F0000), MemoryMap::WRAM);
assert_eq!(bus.map_address(0x002100), MemoryMap::PPU);
assert_eq!(bus.map_address(0x0021FF), MemoryMap::PPU);
assert_eq!(bus.map_address(0x3F2100), MemoryMap::PPU);
assert_eq!(bus.map_address(0x3F21FF), MemoryMap::PPU);
assert_eq!(bus.map_address(0x802100), MemoryMap::PPU);
assert_eq!(bus.map_address(0x8021FF), MemoryMap::PPU);
assert_eq!(bus.map_address(0xBF2100), MemoryMap::PPU);
assert_eq!(bus.map_address(0xBF21FF), MemoryMap::PPU);
assert_eq!(bus.map_address(0x004200), MemoryMap::CPU);
assert_eq!(bus.map_address(0x00420F), MemoryMap::CPU);
assert_eq!(bus.map_address(0x3F4200), MemoryMap::CPU);
assert_eq!(bus.map_address(0x3F420F), MemoryMap::CPU);
assert_eq!(bus.map_address(0x804200), MemoryMap::CPU);
assert_eq!(bus.map_address(0x80420F), MemoryMap::CPU);
assert_eq!(bus.map_address(0xBF4200), MemoryMap::CPU);
assert_eq!(bus.map_address(0xBF420F), MemoryMap::CPU);
assert_eq!(bus.map_address(0x004016), MemoryMap::Joypad);
assert_eq!(bus.map_address(0x004017), MemoryMap::Joypad);
assert_eq!(bus.map_address(0x3F4016), MemoryMap::Joypad);
assert_eq!(bus.map_address(0x3F4017), MemoryMap::Joypad);
assert_eq!(bus.map_address(0x804016), MemoryMap::Joypad);
assert_eq!(bus.map_address(0x804017), MemoryMap::Joypad);
assert_eq!(bus.map_address(0xBF4016), MemoryMap::Joypad);
assert_eq!(bus.map_address(0xBF4017), MemoryMap::Joypad);
}
#[test]
fn test_wram_mirror() {
let mut bus = Bus::new();
bus.write(0x00_0000, 0x1F);
assert_eq!(bus.read(0x7E_0000), 0x1F);
assert_eq!(bus.read(0x80_0000), 0x1F);
bus.write(0x80_0000, 0xEE);
assert_eq!(bus.read(0x7E_0000), 0xEE);
assert_eq!(bus.read(0x00_0000), 0xEE);
}
}