ROM insertion into system
This commit is contained in:
parent
8d5192ff43
commit
d4715bbf62
@ -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<MemoryBus>,
|
||||
memory_bus: Rc<RefCell<MemoryBus>>,
|
||||
|
||||
/// 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<MemoryBus>) -> Self {
|
||||
pub fn new(memory_bus: Rc<RefCell<MemoryBus>>) -> Self {
|
||||
Cpu {
|
||||
registers: CpuRegisters {
|
||||
pc: 0x8000,
|
||||
|
@ -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
|
||||
|
31
core/src/mappers/mod.rs
Normal file
31
core/src/mappers/mod.rs
Normal file
@ -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<dyn Mapper> {
|
||||
let instance = match rom.mapper {
|
||||
0 => NRom::new(rom),
|
||||
_ => unreachable!("Unsupported mapper {}", rom.mapper),
|
||||
};
|
||||
|
||||
Box::new(instance)
|
||||
}
|
39
core/src/mappers/nrom.rs
Normal file
39
core/src/mappers/nrom.rs
Normal file
@ -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]
|
||||
}
|
||||
}
|
@ -1,13 +1,28 @@
|
||||
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<Box<dyn Mapper>>,
|
||||
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<dyn Mapper>) {
|
||||
self.mapper = Some(mapper);
|
||||
}
|
||||
|
||||
/// Gets a byte from memory.
|
||||
@ -15,7 +30,32 @@ impl MemoryBus {
|
||||
/// # 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.
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
@ -1,12 +1,19 @@
|
||||
use core::rom::Rom;
|
||||
use core::system::System;
|
||||
use simplelog::*;
|
||||
|
||||
fn main() {
|
||||
CombinedLogger::init(vec![
|
||||
TermLogger::new(LevelFilter::Debug, Config::default(), TerminalMode::Mixed, ColorChoice::Auto),
|
||||
]).unwrap();
|
||||
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 mut system = System::new();
|
||||
system.insert_rom(ROM_PATH).expect("Failed to insert ROM");
|
||||
|
||||
let path = "./roms/dk.nes";
|
||||
let rom = Rom::read(path).expect("Failed to read ROM file");
|
||||
print!("Do stuff");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user