From 41acfe2743a4d12b13b8dc19151ec784031d11fa Mon Sep 17 00:00:00 2001 From: Daniel Bauer Date: Wed, 25 Dec 2019 12:44:58 +0100 Subject: [PATCH] cartrige, mapper, bus --- src/main.rs | 34 ++++---- src/nes.rs | 4 +- src/nes/bus.rs | 121 +++++++++++++++++++++------ src/nes/cartridge.rs | 193 +++++++++++++++++++++++++++++++++++++++++++ src/nes/cpu.rs | 141 +++++++++++++++---------------- src/nes/mappers.rs | 45 ++++++++++ 6 files changed, 427 insertions(+), 111 deletions(-) create mode 100644 src/nes/cartridge.rs create mode 100644 src/nes/mappers.rs diff --git a/src/main.rs b/src/main.rs index cfec351..363a988 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,10 +8,10 @@ mod nes; use std::path::Path; use piston_window::*; -use gfx_core::Device; use nes::types::*; use nes::cpu::*; use nes::bus::*; +use nes::cartridge::*; use nes::disasm::*; use opengl_graphics::OpenGL; use log::Level; @@ -33,23 +33,27 @@ fn get_glyphs(window: &mut PistonWindow) -> Glyphs { fn main() { simple_logger::init_with_level(Level::Debug).unwrap(); - + + let cartridge = Path::new("test_roms/registers.nes"); + let cartridge = Cartridge::new(cartridge).unwrap(); + let mut bus = MemoryBus::new(); + bus.insert_cartrige(cartridge); // Load little test program into ram - let test_prog: [Byte; 28] = [0xA2, 0x0A, 0x8E, 0x00, 0x00, 0xA2, 0x03, 0x8E, 0x01, 0x00, 0xAC, 0x00, 0x00, 0xA9, - 0x00, 0x18, 0x6D, 0x01, 0x00, 0x88, 0xD0, 0xFA, 0x8D, 0x02, 0x00, 0xEA, 0xEA, 0xEA]; - let offset: Addr = 0x8000; - for i in 0..test_prog.len() { - let addr = (i as Addr) + offset; - bus.writeb(addr, test_prog[i]) - } + // let test_prog: [Byte; 28] = [0xA2, 0x0A, 0x8E, 0x00, 0x00, 0xA2, 0x03, 0x8E, 0x01, 0x00, 0xAC, 0x00, 0x00, 0xA9, + // 0x00, 0x18, 0x6D, 0x01, 0x00, 0x88, 0xD0, 0xFA, 0x8D, 0x02, 0x00, 0xEA, 0xEA, 0xEA]; + // let offset: Addr = 0x8000; + // for i in 0..test_prog.len() { + // let addr = (i as Addr) + offset; + // bus.writeb(addr, test_prog[i]) + // } + // // hint program location to processor + // bus.writew(0xfffc, offset); // disassemble instructions - let disasm = Disasm::disassemble(&test_prog, offset).unwrap(); - - // hint program location to processor - bus.writew(0xfffc, offset); + // let disasm = Disasm::disassemble(&test_prog, offset).unwrap(); + let disasm = Disasm::disassemble(&[], 0x000).unwrap(); // Create and reset CPU let mut cpu: CPU = CPU::new(); @@ -83,7 +87,7 @@ fn main() { } } -fn render(window: &mut PistonWindow, event: &Event, glyphs: &mut Glyphs, cpu: &CPU, bus: &dyn Bus, disasm: &Disasm) { +fn render(window: &mut PistonWindow, event: &Event, glyphs: &mut Glyphs, cpu: &CPU, bus: &MemoryBus, disasm: &Disasm) { window.draw_2d(event, |c, g, d| { clear([0.0, 0.0, 1.0, 1.0], g); @@ -225,7 +229,7 @@ fn render_disasm(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, disasm: &Disasm, } -fn render_memory(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, bus: &dyn Bus, offset: [f64; 2]) { +fn render_memory(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, bus: &MemoryBus, offset: [f64; 2]) { let mut transform_y = c.transform.trans(offset[0], offset[1]); for page in (0x0000..0x00F0).step_by(16) { transform_y = transform_y.trans(0.0, FT_LINE_DISTANCE+FT_SIZE_PX); diff --git a/src/nes.rs b/src/nes.rs index 0eef856..e85242e 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -2,4 +2,6 @@ pub mod cpu; pub mod bus; pub mod types; -pub mod disasm; \ No newline at end of file +pub mod disasm; +pub mod cartridge; +pub mod mappers; \ No newline at end of file diff --git a/src/nes/bus.rs b/src/nes/bus.rs index a3a3d37..c0133d6 100644 --- a/src/nes/bus.rs +++ b/src/nes/bus.rs @@ -1,65 +1,136 @@ use crate::nes::types::*; +use crate::nes::cartridge::Cartridge; -// Generic interface describing a Bus -pub trait Bus { +const RAM_ADDR_RANGE: [Addr; 2] = [0x0000, 0x1fff]; +const RAM_PHYS_RANGE: [Addr; 2] = [0x0000, 0x07ff]; +const CART_ADDR_RANGE: [Addr; 2] = [0x4020, 0xffff]; + +// Generic interface for a device allowing to read/write memory +pub trait Memory { fn readb(&self, addr: Addr) -> Byte; - fn readw(&self, addr: Addr) -> Word; fn writeb(&mut self, addr: Addr, data: Byte); - fn writew(&mut self, addr: Addr, data: Word); + fn readw(&self, addr: Addr) -> Word { + let lo = self.readb(addr); + let hi = self.readb(addr+1); + (hi as Word) << 8 | lo as Word + } + fn writew(&mut self, addr: Addr, data: Word) { + self.writeb(addr, data as Byte); + self.writeb(addr + 1, (data >> 8) as Byte); + } } // Impl by devices to access the Bus pub trait BusDevice { - fn readb(&self, bus: &T, addr: Addr) -> Byte { + fn readb(&self, bus: &T, addr: Addr) -> Byte { bus.readb(addr) } - fn readw(&self, bus: &T, addr: Addr) -> Word { + fn readw(&self, bus: &T, addr: Addr) -> Word { bus.readw(addr) } - fn writeb(&mut self, bus: &mut T, addr: Addr, data: Byte) { + fn writeb(&mut self, bus: &mut T, addr: Addr, data: Byte) { bus.writeb(addr, data) } } // Impl by devices to do stuff on bus clock pub trait Clockable { - fn clock(&mut self, bus: &mut T); + fn clock(&mut self, bus: &mut T); } // A simple bus giving access to a chunk of memory +// and the cartrige pub struct MemoryBus { - ram: [Byte; 0xFFFF] + ram: [Byte; 0x07ff], // 2kb + cartrige: Option } impl MemoryBus { pub fn new() -> MemoryBus { MemoryBus { - ram: [0; 0xFFFF] + ram: [0; 0x07ff], + cartrige: None, + } + } + + pub fn insert_cartrige(&mut self, cartrige: Cartridge) { + self.cartrige = Some(cartrige); + } +} + +impl Memory for MemoryBus { + + fn readb(&self, addr: Addr) -> Byte { + if let Some(cartrige) = &self.cartrige { + if CART_ADDR_RANGE[0] <= addr && addr < CART_ADDR_RANGE[1] { + return cartrige.readb(addr) + } + } + + if RAM_ADDR_RANGE[0] <= addr && addr < RAM_ADDR_RANGE[1] { + // Ram is 3x mirrored after 07ff + return self.ram[(addr & 0x07ff) as usize] + } + 0x0000 // generic response + } + + fn writeb(&mut self, addr: Addr, data: Byte) { + if let Some(cartrige) = &mut self.cartrige { + if CART_ADDR_RANGE[0] <= addr && addr < CART_ADDR_RANGE[1] { + cartrige.writeb(addr, data) + } + } + + if RAM_ADDR_RANGE[0] <= addr && addr < RAM_ADDR_RANGE[1] { + // Ram is 3x mirrored after 07ff + self.ram[(addr & 0x07ff) as usize] = data } } } -impl Bus for MemoryBus { - - fn readb(&self, addr: Addr) -> Byte { - self.ram[addr as usize] + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_consts() { + assert_eq!(RAM_ADDR_RANGE[1] - RAM_ADDR_RANGE[0] + 1, 2048*4); + assert_eq!(RAM_PHYS_RANGE[1] - RAM_PHYS_RANGE[0] + 1, 2048); + } - fn readw(&self, addr: Addr) -> Word { - let lo = self.readb(addr); - let hi = self.readb(addr+1); - (hi as Word) << 8 | lo as Word + #[test] + fn test_read_write_ram() { + let mut bus = MemoryBus::new(); + + // read/write to ram addr + bus.writeb(0x0000, 1); + assert_eq!(1, bus.readb(0x0000)); + assert_eq!(1, bus.readb(0x0800)); + + // read/write to mirrored ram + bus.writeb(0x0801, 2); + assert_eq!(2, bus.readb(0x0801)); + assert_eq!(2, bus.readb(0x0001)); } - fn writeb(&mut self, addr: Addr, data: Byte) { - self.ram[addr as usize] = data; + #[test] + fn test_read_write_cartrige() { + let mut bus = MemoryBus::new(); + + // read/write non-existant to cartrige + bus.writeb(0x4030, 3); + assert_eq!(0, bus.readb(0x4030)); + + let cartrige = Cartridge::dummy(); + bus.insert_cartrige(cartrige); + bus.writeb(0x4030, 3); + assert_eq!(3, bus.readb(0x4030)); } - fn writew(&mut self, addr: Addr, data: Word) { - self.writeb(addr, data as Byte); - self.writeb(addr + 1, (data >> 8) as Byte); - } -} + +} \ No newline at end of file diff --git a/src/nes/cartridge.rs b/src/nes/cartridge.rs new file mode 100644 index 0000000..f352d87 --- /dev/null +++ b/src/nes/cartridge.rs @@ -0,0 +1,193 @@ +use crate::nes::mappers::*; +use crate::nes::bus::Memory; +use failure::Error; +use std::io::prelude::*; +use std::fs::File; +use crate::nes::types::*; +use std::path::Path; +use std::io::SeekFrom; + +#[derive(Debug)] +struct Header { + prg_rom_chunks: Byte, // 16K chunks + chr_rom_chunks: Byte, // 8K chunks + mapper1: Byte, + mapper2: Byte, + prg_ram_size: Byte, + tv1: Byte, + tv2: Byte +} + +impl Header { + fn new(f: &mut File) -> Result { + f.seek(SeekFrom::Start(0))?; + + // assert NES "NES" + f.seek(SeekFrom::Start(4))?; + + let mut rom_sizes = [0; 2]; // prg size in 16K and chr size in 8K + f.read_exact(&mut rom_sizes)?; + + let mut flags = [0; 5]; + f.read_exact(&mut flags)?; + + // Unused + f.seek(SeekFrom::Current(5)); + + Ok(Header { + prg_rom_chunks: rom_sizes[0], + chr_rom_chunks: rom_sizes[1], + mapper1: flags[0], + mapper2: flags[1], + prg_ram_size: flags[2], + tv1: flags[3], + tv2: flags[4], + }) + } + + pub fn has_trainer(&self) -> bool { + self.mapper1 & (1 << 2) != 0 + } + + pub fn get_mapper_id(&self) -> Byte { + let hi = (self.mapper2 >> 4) << 4; + let lo = self.mapper1 >> 4; + hi | lo + } + +} + +pub struct Cartridge { + prg_rom: Vec, + chr_rom: Vec, + mapper: Box, +} + +impl Cartridge { + pub fn new(path: &Path) -> Result { + let mut f = File::open(path)?; + let header = Header::new(&mut f)?; + debug!("{:?}", header); + + let mut current_pos = 0; + current_pos = f.seek(SeekFrom::Start(16))?; + + if header.has_trainer() { + current_pos = f.seek(SeekFrom::Current(512))?; + } + + let mut prg_rom = vec!(0; header.prg_rom_chunks as usize * 16384); + f.read_exact(&mut prg_rom)?; + let mut chr_rom = vec!(0; header.chr_rom_chunks as usize * 8192); + f.read_exact(&mut chr_rom)?; + + + let mapper = match header.get_mapper_id() { + 0 => { Mapper0::new(header.prg_rom_chunks, header.chr_rom_chunks) } + id => bail!("Mapper {:04} not supported", id) + }; + + Ok(Cartridge { + prg_rom: prg_rom, + chr_rom: chr_rom, + mapper: Box::new(mapper) + }) + } + + pub fn dummy() -> Self { + Cartridge { + prg_rom: vec![0; 16384], + chr_rom: vec![0; 8192], + mapper: Box::new(Mapper0::new(1, 1)) + } + } +} + +impl Memory for Cartridge { + fn readb(&self, addr: Addr) -> Byte { + let mapped_addr = self.mapper.map_read_addr(addr); + self.prg_rom[mapped_addr as usize] + } + + fn writeb(&mut self, addr: Addr, data: Byte) { + let mapped_addr = self.mapper.map_write_addr(addr); + self.prg_rom[mapped_addr as usize] = data; + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_header_get_mapper_id() { + let header = Header { + prg_rom_chunks: 1, + chr_rom_chunks: 1, + mapper1: 0x00, + mapper2: 0x00, + prg_ram_size: 0x00, + tv1: 0x00, + tv2: 0x00 + }; + assert_eq!(0, header.get_mapper_id()); + + let header = Header { + prg_rom_chunks: 1, + chr_rom_chunks: 1, + mapper1: 0x10, + mapper2: 0x00, + prg_ram_size: 0x00, + tv1: 0x00, + tv2: 0x00 + }; + assert_eq!(1, header.get_mapper_id()); + + + let header = Header { + prg_rom_chunks: 1, + chr_rom_chunks: 1, + mapper1: 0xff, + mapper2: 0xff, + prg_ram_size: 0x00, + tv1: 0x00, + tv2: 0x00 + }; + assert_eq!(255, header.get_mapper_id()); + } + + #[test] + fn test_header_has_trainer() { + let header = Header { + prg_rom_chunks: 1, + chr_rom_chunks: 1, + mapper1: (1 << 2), + mapper2: 0x00, + prg_ram_size: 0x00, + tv1: 0x00, + tv2: 0x00 + }; + assert!(header.has_trainer()); + + let header = Header { + prg_rom_chunks: 1, + chr_rom_chunks: 1, + mapper1: 0x00, + mapper2: 0x00, + prg_ram_size: 0x00, + tv1: 0x00, + tv2: 0x00 + }; + assert!(!header.has_trainer()); + + + } + + #[test] + fn test_cartridge_new() { + let path = Path::new("test_roms/registers.nes"); + Cartridge::new(&path).unwrap(); + } + +} \ No newline at end of file diff --git a/src/nes/cpu.rs b/src/nes/cpu.rs index 43585dc..6d384bd 100644 --- a/src/nes/cpu.rs +++ b/src/nes/cpu.rs @@ -57,7 +57,7 @@ impl BusDevice for CPU { } // A cpu is clockable impl Clockable for CPU { - fn clock(&mut self, bus: &mut T) { + fn clock(&mut self, bus: &mut T) { if self.cycles == 0 { let opcode = self.readb_pc(bus); self.curr_op = opcode; @@ -81,7 +81,7 @@ impl CPU { // Brings the CPU to a known state. Resets all registers and flags // Read location of pc from 0xfffc - pub fn reset(&mut self, bus: &T) { + pub fn reset(&mut self, bus: &T) { // reset registers self.regs.a = 0; self.regs.x = 0; @@ -96,6 +96,7 @@ impl CPU { let lo = bus.readb(addr); let hi = bus.readb(addr + 1); self.regs.pc = (hi as u16) << 8 | lo as u16; + debug!("PC after reset: {:#06x}", self.regs.pc); // A reset takes 8 CPU clocks self.cycles = 8; @@ -110,14 +111,14 @@ impl CPU { } // read the next opcode and increment pc - fn readb_pc(&mut self, bus: &T) -> Byte { + fn readb_pc(&mut self, bus: &T) -> Byte { let val = self.readb(bus, self.regs.pc); self.regs.pc += 1; val } // read whole Word from pc - fn readw_pc(&mut self, bus: &T) -> Word { + fn readw_pc(&mut self, bus: &T) -> Word { let val = self.readw(bus, self.regs.pc); self.regs.pc += 2; val @@ -141,7 +142,7 @@ impl CPU { } } - fn run_instruction(&mut self, bus: &mut T, i: &Instruction) -> u8 { + fn run_instruction(&mut self, bus: &mut T, i: &Instruction) -> u8 { let (value, page_cross) = match &i.addr_mode { AddrMode::IMP => self.am_IMP(bus), AddrMode::IMM => self.am_IMM(bus), @@ -225,30 +226,30 @@ impl CPU { } // Implied aka no target - fn am_IMP(&mut self, bus: &T) -> (Word, bool) { + fn am_IMP(&mut self, bus: &T) -> (Word, bool) { (0, false) } // Immediate, next byte comes from pc - fn am_IMM(&mut self, bus: &T) -> (Word, bool) { + fn am_IMM(&mut self, bus: &T) -> (Word, bool) { let addr = self.regs.pc; self.regs.pc += 1; (addr, false) } - fn am_ZP0(&mut self, bus: &T) -> (Word, bool) { + fn am_ZP0(&mut self, bus: &T) -> (Word, bool) { unimplemented!() } - fn am_ZPX(&mut self, bus: &T) -> (Word, bool) { + fn am_ZPX(&mut self, bus: &T) -> (Word, bool) { unimplemented!() } - fn am_ZPY(&mut self, bus: &T) -> (Word, bool) { + fn am_ZPY(&mut self, bus: &T) -> (Word, bool) { unimplemented!() } - fn am_REL(&mut self, bus: &T) -> (Word, bool) { + fn am_REL(&mut self, bus: &T) -> (Word, bool) { let rel_addr = self.readb_pc(bus) as Word; if rel_addr < 0x80 { (self.regs.pc + rel_addr, false) @@ -257,33 +258,33 @@ impl CPU { } } - fn am_ABS(&mut self, bus: &T) -> (Word, bool) { + fn am_ABS(&mut self, bus: &T) -> (Word, bool) { let addr = self.readw_pc(bus); (addr, false) } - fn am_ABX(&mut self, bus: &T) -> (Word, bool) { + fn am_ABX(&mut self, bus: &T) -> (Word, bool) { unimplemented!() } - fn am_ABY(&mut self, bus: &T) -> (Word, bool) { + fn am_ABY(&mut self, bus: &T) -> (Word, bool) { unimplemented!() } - fn am_IND(&mut self, bus: &T) -> (Word, bool) { + fn am_IND(&mut self, bus: &T) -> (Word, bool) { unimplemented!() } - fn am_IZX(&mut self, bus: &T) -> (Word, bool) { + fn am_IZX(&mut self, bus: &T) -> (Word, bool) { unimplemented!() } - fn am_IZY(&mut self, bus: &T) -> (Word, bool) { + fn am_IZY(&mut self, bus: &T) -> (Word, bool) { unimplemented!() } // Operations - fn op_ADC(&mut self, bus: &T, addr: Word) { + fn op_ADC(&mut self, bus: &T, addr: Word) { let val = self.readb(bus, addr) as Word; let tmp = (self.regs.a as Word) + val + (self.get_flag(CARRY) as Word); self.set_flag(CARRY, (tmp & 0xFF) > 255); @@ -293,35 +294,35 @@ impl CPU { self.regs.a = tmp as Byte; } - fn op_AND(&mut self, bus: &T, val: Word) { + fn op_AND(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_ASL(&mut self, bus: &T, val: Word) { + fn op_ASL(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_BCC(&mut self, bus: &T, val: Word) { + fn op_BCC(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_BCS(&mut self, bus: &T, val: Word) { + fn op_BCS(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_BEQ(&mut self, bus: &T, val: Word) { + fn op_BEQ(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_BIT(&mut self, bus: &T, val: Word) { + fn op_BIT(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_BMI(&mut self, bus: &T, val: Word) { + fn op_BMI(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_BNE(&mut self, bus: &T, addr: Word) { + fn op_BNE(&mut self, bus: &T, addr: Word) { if self.get_flag(ZERO) == 0 { let old_addr = self.regs.pc; self.regs.pc = addr; @@ -329,19 +330,19 @@ impl CPU { } } - fn op_BPL(&mut self, bus: &T, val: Word) { + fn op_BPL(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_BRK(&mut self, bus: &T, val: Word) { + fn op_BRK(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_BVC(&mut self, bus: &T, val: Word) { + fn op_BVC(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_BVS(&mut self, bus: &T, val: Word) { + fn op_BVS(&mut self, bus: &T, val: Word) { unimplemented!() } @@ -349,31 +350,31 @@ impl CPU { self.set_flag(CARRY, false); } - fn op_CLD(&mut self, bus: &T, val: Word) { + fn op_CLD(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_CLI(&mut self, bus: &T, val: Word) { + fn op_CLI(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_CLV(&mut self, bus: &T, val: Word) { + fn op_CLV(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_CMP(&mut self, bus: &T, val: Word) { + fn op_CMP(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_CPX(&mut self, bus: &T, val: Word) { + fn op_CPX(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_CPY(&mut self, bus: &T, val: Word) { + fn op_CPY(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_DEC(&mut self, bus: &T, val: Word) { + fn op_DEC(&mut self, bus: &T, val: Word) { unimplemented!() } @@ -389,144 +390,144 @@ impl CPU { self.set_flag(NEGATIVE, (self.regs.y & 0x80) != 0) } - fn op_EOR(&mut self, bus: &T, val: Word) { + fn op_EOR(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_INC(&mut self, bus: &T, val: Word) { + fn op_INC(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_INX(&mut self, bus: &T, val: Word) { + fn op_INX(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_INY(&mut self, bus: &T, val: Word) { + fn op_INY(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_JMP(&mut self, bus: &T, val: Word) { + fn op_JMP(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_JSR(&mut self, bus: &T, val: Word) { + fn op_JSR(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_LDA(&mut self, bus: &T, addr: Word) { + fn op_LDA(&mut self, bus: &T, addr: Word) { let val = bus.readb(addr); self.regs.a = val; self.set_flag(ZERO, val == 0); self.set_flag(NEGATIVE, (val & 0x80) != 0); } - fn op_LDX(&mut self, bus: &T, addr: Word) { + fn op_LDX(&mut self, bus: &T, addr: Word) { let val = bus.readb(addr); self.regs.x = val; self.set_flag(ZERO, val == 0); self.set_flag(NEGATIVE, (val & 0x80) != 0); } - fn op_LDY(&mut self, bus: &T, addr: Word) { + fn op_LDY(&mut self, bus: &T, addr: Word) { let val = bus.readb(addr); self.regs.y = val; self.set_flag(ZERO, val == 0); self.set_flag(NEGATIVE, (val & 0x80) != 0); } - fn op_LSR(&mut self, bus: &T, val: Word) { + fn op_LSR(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_NOP(&mut self, bus: &T, val: Word) { + fn op_NOP(&mut self, bus: &T, val: Word) { // does nothing } - fn op_ORA(&mut self, bus: &T, val: Word) { + fn op_ORA(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_PHA(&mut self, bus: &T, val: Word) { + fn op_PHA(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_PHP(&mut self, bus: &T, val: Word) { + fn op_PHP(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_PLA(&mut self, bus: &T, val: Word) { + fn op_PLA(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_PLP(&mut self, bus: &T, val: Word) { + fn op_PLP(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_ROL(&mut self, bus: &T, val: Word) { + fn op_ROL(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_ROR(&mut self, bus: &T, val: Word) { + fn op_ROR(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_RTI(&mut self, bus: &T, val: Word) { + fn op_RTI(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_RTS(&mut self, bus: &T, val: Word) { + fn op_RTS(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_SBC(&mut self, bus: &T, val: Word) { + fn op_SBC(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_SEC(&mut self, bus: &T, val: Word) { + fn op_SEC(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_SED(&mut self, bus: &T, val: Word) { + fn op_SED(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_SEI(&mut self, bus: &T, val: Word) { + fn op_SEI(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_STA(&mut self, bus: &mut T, addr: Word) { + fn op_STA(&mut self, bus: &mut T, addr: Word) { self.writeb(bus, addr, self.regs.a) } - fn op_STX(&mut self, bus: &mut T, addr: Word) { + fn op_STX(&mut self, bus: &mut T, addr: Word) { self.writeb(bus, addr, self.regs.x) } - fn op_STY(&mut self, bus: &mut T, addr: Word) { + fn op_STY(&mut self, bus: &mut T, addr: Word) { self.writeb(bus, addr, self.regs.y) } - fn op_TAX(&mut self, bus: &T, val: Word) { + fn op_TAX(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_TAY(&mut self, bus: &T, val: Word) { + fn op_TAY(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_TSX(&mut self, bus: &T, val: Word) { + fn op_TSX(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_TXA(&mut self, bus: &T, val: Word) { + fn op_TXA(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_TXS(&mut self, bus: &T, val: Word) { + fn op_TXS(&mut self, bus: &T, val: Word) { unimplemented!() } - fn op_TYA(&mut self, bus: &T, val: Word) { + fn op_TYA(&mut self, bus: &T, val: Word) { unimplemented!() } } diff --git a/src/nes/mappers.rs b/src/nes/mappers.rs new file mode 100644 index 0000000..87bffc5 --- /dev/null +++ b/src/nes/mappers.rs @@ -0,0 +1,45 @@ +use crate::nes::types::*; + +pub trait Mapper { + fn map_read_addr(&self, addr: Addr) -> Addr; + fn map_write_addr(&self, addr: Addr) -> Addr; +} + +// Mapper 0 +// prg rom: 16K or 32K +// chr rom: 8K +// All banks are fixed: +// CPU: +// 0x8000 - 0xbfff // first 16k +// 0xc000 - 0xffff // second 16k +// +// Cartrige: +// 0x4000-0xffff -> first 16K +// 0xc000-0xffff -> last 16K or mirror or 0x8000-0xbfff +pub struct Mapper0 { + prg_banks: Byte, + chr_banks: Byte, +} + +impl Mapper0 { + pub fn new(prg_banks: Byte, chr_banks: Byte) -> Self { + Mapper0 { prg_banks, chr_banks } + } +} + +impl Mapper for Mapper0 { + fn map_read_addr(&self, addr: Addr) -> Addr { + if self.prg_banks > 1 { + addr & 0x7fff + } else { + addr & 0x3fff + } + } + fn map_write_addr(&self, addr: Addr) -> Addr { + if self.prg_banks > 1 { + addr & 0x7fff + } else { + addr & 0x3fff + } + } +}