diff --git a/core/src/cpu/mod.rs b/core/src/cpu/mod.rs index 31bb372..1acb21a 100644 --- a/core/src/cpu/mod.rs +++ b/core/src/cpu/mod.rs @@ -3,6 +3,7 @@ mod op; mod operations; use std::cell::RefCell; +use std::rc::Rc; use crate::memory::MemoryBus; use crate::Clock; use bitflags::bitflags; @@ -15,7 +16,7 @@ pub struct Cpu { registers: CpuRegisters, /// The memory bus accessible by the CPU - memory_bus: RefCell, + memory_bus: Rc>, /// The number of cycles ran on the CPU cycle: usize, @@ -133,7 +134,7 @@ pub trait CpuInternals { } impl Cpu { - pub fn new(memory_bus: RefCell) -> Self { + pub fn new(memory_bus: Rc>) -> Self { Cpu { registers: CpuRegisters { pc: 0x8000, diff --git a/core/src/lib.rs b/core/src/lib.rs index e421a3a..c8b619c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,7 +1,8 @@ mod cpu; +mod mappers; mod memory; -pub mod rom; -mod system; +mod rom; +pub mod system; pub trait Clock { /// Run a clock cycle diff --git a/core/src/mappers/mod.rs b/core/src/mappers/mod.rs new file mode 100644 index 0000000..2a97ec2 --- /dev/null +++ b/core/src/mappers/mod.rs @@ -0,0 +1,31 @@ +use crate::mappers::nrom::NRom; +use crate::rom::Rom; + +mod nrom; + +pub trait Mapper { + /// Gets a byte from the program ROM. + /// + /// # Arguments + /// * addr - The address of the byte to read + fn get_prg_byte(&self, addr: u16) -> u8; + + /// Gets a byte from the character ROM. + /// + /// # Arguments + /// * addr - The address of the byte to read + fn get_chr_byte(&self, addr: u16) -> u8; +} + +/// Gets a mapper for a ROM. +/// +/// # Arguments +/// * rom - The ROM to get the mapper for +pub fn get_mapper(rom: Rom) -> Box { + let instance = match rom.mapper { + 0 => NRom::new(rom), + _ => unreachable!("Unsupported mapper {}", rom.mapper), + }; + + Box::new(instance) +} \ No newline at end of file diff --git a/core/src/mappers/nrom.rs b/core/src/mappers/nrom.rs new file mode 100644 index 0000000..0599807 --- /dev/null +++ b/core/src/mappers/nrom.rs @@ -0,0 +1,39 @@ +use simplelog::info; +use crate::mappers::Mapper; +use crate::rom::Rom; + +const PRG_BANK_SIZE: usize = 0x4000; +const CHR_BANK_SIZE: usize = 0x2000; + +pub struct NRom { + rom: Rom, +} + +impl NRom { + /// Creates an instance of the NRom mapper. + /// + /// # Arguments + /// * rom - The ROM + pub fn new(rom: Rom) -> Self { + info!("Using NRom mapper"); + Self { rom } + } +} + +impl Mapper for NRom { + fn get_prg_byte(&self, addr: u16) -> u8 { + let addr_usize = addr as usize; + if addr_usize < PRG_BANK_SIZE || self.rom.prg_rom_size > PRG_BANK_SIZE { + return self.rom.prg_rom[addr_usize]; + } + + self.rom.prg_rom[addr_usize - PRG_BANK_SIZE] + } + + fn get_chr_byte(&self, addr: u16) -> u8 { + let addr_usize = addr as usize; + + assert!(addr_usize < CHR_BANK_SIZE); + self.rom.chr_rom[addr_usize] + } +} diff --git a/core/src/memory.rs b/core/src/memory.rs index 4faf703..a3a938d 100644 --- a/core/src/memory.rs +++ b/core/src/memory.rs @@ -1,21 +1,61 @@ -const MEMORY_SIZE: usize = 0x0800; +use simplelog::warn; +use crate::mappers::Mapper; + +const MEMORY_MAX_ADDR: u16 = 0xFFFF; +const PPU_MAX_ADDR: u16 = 0x4000; +const APU_MAX_ADDR: u16 = 0x4020; +const UNMAPPED_MAX_ADDR: u16 = 0x6000; +const CARTRIDGE_RAM_MAX_ADDR: u16 = 0x8000; +const RAM_SIZE: usize = 0x0800; +const RAM_MAX_ADDR: u16 = 0x2000; pub struct MemoryBus { - ram: [u8; MEMORY_SIZE], + mapper: Option>, + ram: [u8; RAM_SIZE], } impl MemoryBus { pub fn new() -> Self { - let ram = [0; MEMORY_SIZE]; - Self { ram } + let ram = [0; RAM_SIZE]; + Self { mapper: None, ram } } - + + /// Register a mapper in the memory bus, simulates plugging in a cartridge. + pub fn register_mapper(&mut self, mapper: Box) { + self.mapper = Some(mapper); + } + /// Gets a byte from memory. /// /// # Arguments /// * `addr` - The address of the byte pub fn get_byte(&self, addr: u16) -> u8 { - unimplemented!("MemoryBus::get_byte"); + assert!(addr < MEMORY_MAX_ADDR); + if addr < RAM_MAX_ADDR { + let ram_addr = (addr as usize) % RAM_SIZE; + self.ram[ram_addr] + } else if addr < PPU_MAX_ADDR { + let relative_addr = addr - RAM_MAX_ADDR; + let ppu_register = relative_addr % 8; + todo!() + } else if addr < APU_MAX_ADDR { + todo!() + } else if addr < UNMAPPED_MAX_ADDR { + assert!(false); + 0 + } else if addr < CARTRIDGE_RAM_MAX_ADDR { + todo!() + } else { + let prg_addr = addr - CARTRIDGE_RAM_MAX_ADDR; + + if self.mapper.is_none() { + warn!("Tried to read memory address {} before setting a mapper", addr); + return 0 + } + + let mapper = self.mapper.as_ref().unwrap(); + mapper.get_prg_byte(prg_addr) + } } /// Gets a word from memory. diff --git a/core/src/rom/ines.rs b/core/src/rom/ines.rs index b6515fa..a0618e0 100644 --- a/core/src/rom/ines.rs +++ b/core/src/rom/ines.rs @@ -202,6 +202,18 @@ impl INesHeader { }) } + fn get_mapper(&self) -> u16 { + let mapper_low = (self.flag_6 >> 4) as u16; + let mapper_mid = (self.flag_7 & 0xF0) as u16; + let mapper_high = (self.mapper_msb_submapper & 0x0F) as u16; + + mapper_high << 8 | mapper_mid | mapper_low + } + + fn get_sub_mapper(&self) -> u8 { + self.mapper_msb_submapper >> 4 + } + fn parse(header: &[u8]) -> Self { Self { prg_rom_size_lsb: header[4], @@ -252,6 +264,8 @@ impl RomLoader for INesHeader { let (chr_ram_size, chr_nvram_size) = ines_header.get_chr_ram_size(); let nametable_mirroring = ines_header.get_nametable_mirroring(); let cpu_timing = ines_header.get_cpu_timing()?; + let mapper = ines_header.get_mapper(); + let sub_mapper = ines_header.get_sub_mapper(); let prg_rom = file_data[trainer_size..prg_rom_size] .to_vec() @@ -272,6 +286,8 @@ impl RomLoader for INesHeader { chr_rom, nametable_mirroring, cpu_timing, + mapper, + sub_mapper, }) } } diff --git a/core/src/rom/mod.rs b/core/src/rom/mod.rs index 1b384ed..88c4416 100644 --- a/core/src/rom/mod.rs +++ b/core/src/rom/mod.rs @@ -35,6 +35,13 @@ pub struct Rom { /// The timing of the CPU, also the region of the ROM pub cpu_timing: CpuTiming, + + /// The number of the mapper + pub mapper: u16, + + /// The number of the sub-mapper + /// Not supported for now + pub sub_mapper: u8 } pub enum NametableMirroring { @@ -106,6 +113,7 @@ pub enum RomReadError { InvalidHeader(String), MissingHeader, RomLengthMismatch(usize, usize), + UnsupportedMapper(u8) } impl Display for RomReadError { @@ -117,6 +125,7 @@ impl Display for RomReadError { RomReadError::MissingHeader => write!(f, "File was too short to contain a header"), RomReadError::RomLengthMismatch(required_length, actual_length) => write!(f, "ROM didn't contain enough data according to the header; required {} bytes but contained {} bytes", required_length, actual_length), + RomReadError::UnsupportedMapper(mapper) => write!(f, "Unsupported mapper: {}", mapper), } } } diff --git a/core/src/system.rs b/core/src/system.rs index 15535c3..f027792 100644 --- a/core/src/system.rs +++ b/core/src/system.rs @@ -1,7 +1,10 @@ use std::cell::RefCell; use std::rc::Rc; +use simplelog::info; use crate::cpu::Cpu; +use crate::mappers::get_mapper; use crate::memory::MemoryBus; +use crate::rom::{Rom, RomReadError}; pub struct System { cpu: Cpu, @@ -11,11 +14,21 @@ pub struct System { impl System { pub fn new() -> Self { let memory_bus = Rc::new(RefCell::new(MemoryBus::new())); - let cpu = Cpu::new(memory_bus); + let cpu = Cpu::new(Rc::clone(&memory_bus)); Self { cpu, memory_bus } } + + pub fn insert_rom(&self, path: &str) -> Result<(), RomReadError> { + let rom = Rom::read(path)?; + let mapper = get_mapper(rom); + + self.memory_bus.borrow_mut().register_mapper(mapper); + + info!("Successfully inserted ROM into system"); + Ok(()) + } } \ No newline at end of file diff --git a/ui/src/main.rs b/ui/src/main.rs index 9e68ea0..8f14a65 100644 --- a/ui/src/main.rs +++ b/ui/src/main.rs @@ -1,12 +1,19 @@ -use core::rom::Rom; +use core::system::System; use simplelog::*; +const ROM_PATH: &'static str = "./roms/dk.nes"; + fn main() { - CombinedLogger::init(vec![ - TermLogger::new(LevelFilter::Debug, Config::default(), TerminalMode::Mixed, ColorChoice::Auto), - ]).unwrap(); - - let path = "./roms/dk.nes"; - let rom = Rom::read(path).expect("Failed to read ROM file"); + CombinedLogger::init(vec![TermLogger::new( + LevelFilter::Debug, + Config::default(), + TerminalMode::Mixed, + ColorChoice::Auto, + )]) + .unwrap(); + + let mut system = System::new(); + system.insert_rom(ROM_PATH).expect("Failed to insert ROM"); + print!("Do stuff"); }