implemented working Cartridge for NROm/Mapper 0
This commit is contained in:
Generated
+7
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "rnes"
|
||||
version = "0.1.0"
|
||||
@@ -0,0 +1,85 @@
|
||||
|
||||
pub enum Mirroring { Horizontal, Vertical, FourScreen }
|
||||
|
||||
pub struct Cartridge {
|
||||
prg_rom: Vec<u8>,
|
||||
chr_rom: Vec<u8>,
|
||||
mirroring: Mirroring,
|
||||
battery: bool,
|
||||
trainer: bool,
|
||||
four_screen: bool,
|
||||
mapper: u8,
|
||||
}
|
||||
|
||||
impl Cartridge {
|
||||
pub fn from_file(path: &str) -> Result<Cartridge, String> {
|
||||
let data: Vec<u8> = std::fs::read(path).map_err(|e| format!("couldn't read {path}: {e}"))?;
|
||||
if data.len() < 16 {
|
||||
return Err("file is too short to be an iNES ROM".to_string())
|
||||
}
|
||||
|
||||
if &data[0..4] != b"NES\x1a" {
|
||||
return Err("not a valid iNES ROM (bad magic numbers)".to_string())
|
||||
}
|
||||
|
||||
// Extract the bytes we care about
|
||||
let prg_banks: u8 = data[4];
|
||||
let chr_banks: u8 = data[5];
|
||||
let flags6: u8 = data[6];
|
||||
let flags7: u8 = data[7];
|
||||
|
||||
// Decode bits from the bytes on the ROM
|
||||
let mirroring: Mirroring = if flags6 & 0b0001 != 0 { Mirroring::Vertical} else { Mirroring::Horizontal };
|
||||
let battery: bool = flags6 & 0b0010 != 0;
|
||||
let trainer: bool = flags6 & 0b0100 != 0;
|
||||
let four_screen: bool = flags6 & 0b1000 != 0; // this is meant to override mirroring but both will be stored
|
||||
let mapper: u8 = ((flags7 & 0xF0) >> 4) << 4 | ((flags6 & 0xF0) >> 4);
|
||||
|
||||
// Gate for Mapper as we are going NROM-only for now
|
||||
|
||||
if mapper != 0 {
|
||||
return Err(format!("mapper {mapper} not supported yet, this is temporal"));
|
||||
}
|
||||
|
||||
// Compute the ROM offsets for future use
|
||||
|
||||
let prg_start: usize = 16 + if trainer { 512 } else { 0 }; // i'd like to call this padding but on the spec it gets called a "Trainer" so it stays like that
|
||||
let prg_len: usize = (prg_banks as usize) * 0x4000;
|
||||
let chr_start: usize = prg_start + prg_len;
|
||||
let chr_len: usize = (chr_banks as usize) * 0x2000;
|
||||
|
||||
if prg_start + prg_len > data.len() || chr_start + chr_len > data.len() {
|
||||
return Err("ROM data truncated or changed".to_string());
|
||||
}
|
||||
|
||||
// Construct and return a valid Cartridge
|
||||
|
||||
Ok(Cartridge {
|
||||
prg_rom: data[prg_start..prg_start + prg_len].to_vec(),
|
||||
chr_rom: if chr_len == 0 { vec![0u8; 0x2000] } else { data[chr_start..chr_start + chr_len].to_vec() }, // Vector of 8KB filled with 0's or the actual CHR ROM data
|
||||
mirroring,
|
||||
battery,
|
||||
trainer,
|
||||
four_screen,
|
||||
mapper,
|
||||
})
|
||||
}
|
||||
|
||||
// read_* operations are for the CPU to work with, letting it read the ROM chips on the emulated cartridge
|
||||
pub fn read_prg(&self, address: u16) -> u8 {
|
||||
debug_assert!(address >= 0x8000, "PRG read outside of $8000-$FFFF; Address from CPU: {address:#06X}"); // #06X makes the Address readable similar to $1FFF
|
||||
let mut offset: u16 = address - 0x8000;
|
||||
if self.prg_rom.len() == 0x4000 {
|
||||
offset %= 0x4000;
|
||||
}
|
||||
self.prg_rom[offset as usize]
|
||||
}
|
||||
pub fn read_chr(&self, address: u16) -> u8 {
|
||||
debug_assert!(address < 0x2000, "CHR reads outside $0000-$1FFF; Address from CPU: {address:#06X}");
|
||||
self.chr_rom[address as usize]
|
||||
}
|
||||
pub fn write_chr(&mut self, address: u16, value: u8) {
|
||||
debug_assert!(address < 0x2000, "CHR writes outside $0000-$1FFF; Address from CPU: {address:#06X}");
|
||||
self.chr_rom[address as usize] = value
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
mod cartridge;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user