cartrige, mapper, bus
This commit is contained in:
34
src/main.rs
34
src/main.rs
@@ -8,10 +8,10 @@ mod nes;
|
|||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use piston_window::*;
|
use piston_window::*;
|
||||||
use gfx_core::Device;
|
|
||||||
use nes::types::*;
|
use nes::types::*;
|
||||||
use nes::cpu::*;
|
use nes::cpu::*;
|
||||||
use nes::bus::*;
|
use nes::bus::*;
|
||||||
|
use nes::cartridge::*;
|
||||||
use nes::disasm::*;
|
use nes::disasm::*;
|
||||||
use opengl_graphics::OpenGL;
|
use opengl_graphics::OpenGL;
|
||||||
use log::Level;
|
use log::Level;
|
||||||
@@ -33,23 +33,27 @@ fn get_glyphs(window: &mut PistonWindow) -> Glyphs {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
simple_logger::init_with_level(Level::Debug).unwrap();
|
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();
|
let mut bus = MemoryBus::new();
|
||||||
|
bus.insert_cartrige(cartridge);
|
||||||
|
|
||||||
// Load little test program into ram
|
// 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,
|
// 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];
|
// 0x00, 0x18, 0x6D, 0x01, 0x00, 0x88, 0xD0, 0xFA, 0x8D, 0x02, 0x00, 0xEA, 0xEA, 0xEA];
|
||||||
let offset: Addr = 0x8000;
|
// let offset: Addr = 0x8000;
|
||||||
for i in 0..test_prog.len() {
|
// for i in 0..test_prog.len() {
|
||||||
let addr = (i as Addr) + offset;
|
// let addr = (i as Addr) + offset;
|
||||||
bus.writeb(addr, test_prog[i])
|
// bus.writeb(addr, test_prog[i])
|
||||||
}
|
// }
|
||||||
|
// // hint program location to processor
|
||||||
|
// bus.writew(0xfffc, offset);
|
||||||
|
|
||||||
// disassemble instructions
|
// disassemble instructions
|
||||||
let disasm = Disasm::disassemble(&test_prog, offset).unwrap();
|
// let disasm = Disasm::disassemble(&test_prog, offset).unwrap();
|
||||||
|
let disasm = Disasm::disassemble(&[], 0x000).unwrap();
|
||||||
// hint program location to processor
|
|
||||||
bus.writew(0xfffc, offset);
|
|
||||||
|
|
||||||
// Create and reset CPU
|
// Create and reset CPU
|
||||||
let mut cpu: CPU = CPU::new();
|
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| {
|
window.draw_2d(event, |c, g, d| {
|
||||||
clear([0.0, 0.0, 1.0, 1.0], g);
|
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]);
|
let mut transform_y = c.transform.trans(offset[0], offset[1]);
|
||||||
for page in (0x0000..0x00F0).step_by(16) {
|
for page in (0x0000..0x00F0).step_by(16) {
|
||||||
transform_y = transform_y.trans(0.0, FT_LINE_DISTANCE+FT_SIZE_PX);
|
transform_y = transform_y.trans(0.0, FT_LINE_DISTANCE+FT_SIZE_PX);
|
||||||
|
|||||||
@@ -2,4 +2,6 @@
|
|||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod bus;
|
pub mod bus;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod disasm;
|
pub mod disasm;
|
||||||
|
pub mod cartridge;
|
||||||
|
pub mod mappers;
|
||||||
121
src/nes/bus.rs
121
src/nes/bus.rs
@@ -1,65 +1,136 @@
|
|||||||
use crate::nes::types::*;
|
use crate::nes::types::*;
|
||||||
|
use crate::nes::cartridge::Cartridge;
|
||||||
|
|
||||||
// Generic interface describing a Bus
|
const RAM_ADDR_RANGE: [Addr; 2] = [0x0000, 0x1fff];
|
||||||
pub trait Bus {
|
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 readb(&self, addr: Addr) -> Byte;
|
||||||
fn readw(&self, addr: Addr) -> Word;
|
|
||||||
fn writeb(&mut self, addr: Addr, data: Byte);
|
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
|
// Impl by devices to access the Bus
|
||||||
pub trait BusDevice {
|
pub trait BusDevice {
|
||||||
fn readb<T: Bus>(&self, bus: &T, addr: Addr) -> Byte {
|
fn readb<T: Memory>(&self, bus: &T, addr: Addr) -> Byte {
|
||||||
bus.readb(addr)
|
bus.readb(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn readw<T: Bus>(&self, bus: &T, addr: Addr) -> Word {
|
fn readw<T: Memory>(&self, bus: &T, addr: Addr) -> Word {
|
||||||
bus.readw(addr)
|
bus.readw(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeb<T: Bus>(&mut self, bus: &mut T, addr: Addr, data: Byte) {
|
fn writeb<T: Memory>(&mut self, bus: &mut T, addr: Addr, data: Byte) {
|
||||||
bus.writeb(addr, data)
|
bus.writeb(addr, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Impl by devices to do stuff on bus clock
|
// Impl by devices to do stuff on bus clock
|
||||||
pub trait Clockable {
|
pub trait Clockable {
|
||||||
fn clock<T: Bus>(&mut self, bus: &mut T);
|
fn clock<T: Memory>(&mut self, bus: &mut T);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// A simple bus giving access to a chunk of memory
|
// A simple bus giving access to a chunk of memory
|
||||||
|
// and the cartrige
|
||||||
pub struct MemoryBus {
|
pub struct MemoryBus {
|
||||||
ram: [Byte; 0xFFFF]
|
ram: [Byte; 0x07ff], // 2kb
|
||||||
|
cartrige: Option<Cartridge>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemoryBus {
|
impl MemoryBus {
|
||||||
pub fn new() -> MemoryBus {
|
pub fn new() -> MemoryBus {
|
||||||
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 {
|
|
||||||
|
#[cfg(test)]
|
||||||
fn readb(&self, addr: Addr) -> Byte {
|
mod tests {
|
||||||
self.ram[addr as usize]
|
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 {
|
#[test]
|
||||||
let lo = self.readb(addr);
|
fn test_read_write_ram() {
|
||||||
let hi = self.readb(addr+1);
|
let mut bus = MemoryBus::new();
|
||||||
(hi as Word) << 8 | lo as Word
|
|
||||||
|
// 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) {
|
#[test]
|
||||||
self.ram[addr as usize] = data;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
193
src/nes/cartridge.rs
Normal file
193
src/nes/cartridge.rs
Normal file
@@ -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<Self, Error> {
|
||||||
|
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<Byte>,
|
||||||
|
chr_rom: Vec<Byte>,
|
||||||
|
mapper: Box<dyn Mapper>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cartridge {
|
||||||
|
pub fn new(path: &Path) -> Result<Self, Error> {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
141
src/nes/cpu.rs
141
src/nes/cpu.rs
@@ -57,7 +57,7 @@ impl BusDevice for CPU { }
|
|||||||
|
|
||||||
// A cpu is clockable
|
// A cpu is clockable
|
||||||
impl Clockable for CPU {
|
impl Clockable for CPU {
|
||||||
fn clock<T: Bus>(&mut self, bus: &mut T) {
|
fn clock<T: Memory>(&mut self, bus: &mut T) {
|
||||||
if self.cycles == 0 {
|
if self.cycles == 0 {
|
||||||
let opcode = self.readb_pc(bus);
|
let opcode = self.readb_pc(bus);
|
||||||
self.curr_op = opcode;
|
self.curr_op = opcode;
|
||||||
@@ -81,7 +81,7 @@ impl CPU {
|
|||||||
|
|
||||||
// Brings the CPU to a known state. Resets all registers and flags
|
// Brings the CPU to a known state. Resets all registers and flags
|
||||||
// Read location of pc from 0xfffc
|
// Read location of pc from 0xfffc
|
||||||
pub fn reset<T: Bus>(&mut self, bus: &T) {
|
pub fn reset<T: Memory>(&mut self, bus: &T) {
|
||||||
// reset registers
|
// reset registers
|
||||||
self.regs.a = 0;
|
self.regs.a = 0;
|
||||||
self.regs.x = 0;
|
self.regs.x = 0;
|
||||||
@@ -96,6 +96,7 @@ impl CPU {
|
|||||||
let lo = bus.readb(addr);
|
let lo = bus.readb(addr);
|
||||||
let hi = bus.readb(addr + 1);
|
let hi = bus.readb(addr + 1);
|
||||||
self.regs.pc = (hi as u16) << 8 | lo as u16;
|
self.regs.pc = (hi as u16) << 8 | lo as u16;
|
||||||
|
debug!("PC after reset: {:#06x}", self.regs.pc);
|
||||||
|
|
||||||
// A reset takes 8 CPU clocks
|
// A reset takes 8 CPU clocks
|
||||||
self.cycles = 8;
|
self.cycles = 8;
|
||||||
@@ -110,14 +111,14 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read the next opcode and increment pc
|
// read the next opcode and increment pc
|
||||||
fn readb_pc<T: Bus>(&mut self, bus: &T) -> Byte {
|
fn readb_pc<T: Memory>(&mut self, bus: &T) -> Byte {
|
||||||
let val = self.readb(bus, self.regs.pc);
|
let val = self.readb(bus, self.regs.pc);
|
||||||
self.regs.pc += 1;
|
self.regs.pc += 1;
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
// read whole Word from pc
|
// read whole Word from pc
|
||||||
fn readw_pc<T: Bus>(&mut self, bus: &T) -> Word {
|
fn readw_pc<T: Memory>(&mut self, bus: &T) -> Word {
|
||||||
let val = self.readw(bus, self.regs.pc);
|
let val = self.readw(bus, self.regs.pc);
|
||||||
self.regs.pc += 2;
|
self.regs.pc += 2;
|
||||||
val
|
val
|
||||||
@@ -141,7 +142,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_instruction<T: Bus>(&mut self, bus: &mut T, i: &Instruction) -> u8 {
|
fn run_instruction<T: Memory>(&mut self, bus: &mut T, i: &Instruction) -> u8 {
|
||||||
let (value, page_cross) = match &i.addr_mode {
|
let (value, page_cross) = match &i.addr_mode {
|
||||||
AddrMode::IMP => self.am_IMP(bus),
|
AddrMode::IMP => self.am_IMP(bus),
|
||||||
AddrMode::IMM => self.am_IMM(bus),
|
AddrMode::IMM => self.am_IMM(bus),
|
||||||
@@ -225,30 +226,30 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Implied aka no target
|
// Implied aka no target
|
||||||
fn am_IMP<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_IMP<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
(0, false)
|
(0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Immediate, next byte comes from pc
|
// Immediate, next byte comes from pc
|
||||||
fn am_IMM<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_IMM<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
let addr = self.regs.pc;
|
let addr = self.regs.pc;
|
||||||
self.regs.pc += 1;
|
self.regs.pc += 1;
|
||||||
(addr, false)
|
(addr, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_ZP0<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ZP0<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_ZPX<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ZPX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_ZPY<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ZPY<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_REL<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_REL<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
let rel_addr = self.readb_pc(bus) as Word;
|
let rel_addr = self.readb_pc(bus) as Word;
|
||||||
if rel_addr < 0x80 {
|
if rel_addr < 0x80 {
|
||||||
(self.regs.pc + rel_addr, false)
|
(self.regs.pc + rel_addr, false)
|
||||||
@@ -257,33 +258,33 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_ABS<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ABS<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
let addr = self.readw_pc(bus);
|
let addr = self.readw_pc(bus);
|
||||||
(addr, false)
|
(addr, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_ABX<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ABX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_ABY<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ABY<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_IND<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_IND<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_IZX<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_IZX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_IZY<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_IZY<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operations
|
// Operations
|
||||||
fn op_ADC<T: Bus>(&mut self, bus: &T, addr: Word) {
|
fn op_ADC<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||||
let val = self.readb(bus, addr) as Word;
|
let val = self.readb(bus, addr) as Word;
|
||||||
let tmp = (self.regs.a as Word) + val + (self.get_flag(CARRY) as Word);
|
let tmp = (self.regs.a as Word) + val + (self.get_flag(CARRY) as Word);
|
||||||
self.set_flag(CARRY, (tmp & 0xFF) > 255);
|
self.set_flag(CARRY, (tmp & 0xFF) > 255);
|
||||||
@@ -293,35 +294,35 @@ impl CPU {
|
|||||||
self.regs.a = tmp as Byte;
|
self.regs.a = tmp as Byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_AND<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_AND<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_ASL<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_ASL<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_BCC<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_BCC<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_BCS<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_BCS<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_BEQ<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_BEQ<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_BIT<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_BIT<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_BMI<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_BMI<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_BNE<T: Bus>(&mut self, bus: &T, addr: Word) {
|
fn op_BNE<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||||
if self.get_flag(ZERO) == 0 {
|
if self.get_flag(ZERO) == 0 {
|
||||||
let old_addr = self.regs.pc;
|
let old_addr = self.regs.pc;
|
||||||
self.regs.pc = addr;
|
self.regs.pc = addr;
|
||||||
@@ -329,19 +330,19 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_BPL<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_BPL<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_BRK<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_BRK<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_BVC<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_BVC<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_BVS<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_BVS<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,31 +350,31 @@ impl CPU {
|
|||||||
self.set_flag(CARRY, false);
|
self.set_flag(CARRY, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_CLD<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_CLD<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_CLI<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_CLI<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_CLV<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_CLV<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_CMP<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_CMP<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_CPX<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_CPX<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_CPY<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_CPY<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_DEC<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_DEC<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,144 +390,144 @@ impl CPU {
|
|||||||
self.set_flag(NEGATIVE, (self.regs.y & 0x80) != 0)
|
self.set_flag(NEGATIVE, (self.regs.y & 0x80) != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_EOR<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_EOR<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_INC<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_INC<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_INX<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_INX<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_INY<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_INY<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_JMP<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_JMP<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_JSR<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_JSR<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_LDA<T: Bus>(&mut self, bus: &T, addr: Word) {
|
fn op_LDA<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||||
let val = bus.readb(addr);
|
let val = bus.readb(addr);
|
||||||
self.regs.a = val;
|
self.regs.a = val;
|
||||||
self.set_flag(ZERO, val == 0);
|
self.set_flag(ZERO, val == 0);
|
||||||
self.set_flag(NEGATIVE, (val & 0x80) != 0);
|
self.set_flag(NEGATIVE, (val & 0x80) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_LDX<T: Bus>(&mut self, bus: &T, addr: Word) {
|
fn op_LDX<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||||
let val = bus.readb(addr);
|
let val = bus.readb(addr);
|
||||||
self.regs.x = val;
|
self.regs.x = val;
|
||||||
self.set_flag(ZERO, val == 0);
|
self.set_flag(ZERO, val == 0);
|
||||||
self.set_flag(NEGATIVE, (val & 0x80) != 0);
|
self.set_flag(NEGATIVE, (val & 0x80) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_LDY<T: Bus>(&mut self, bus: &T, addr: Word) {
|
fn op_LDY<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||||
let val = bus.readb(addr);
|
let val = bus.readb(addr);
|
||||||
self.regs.y = val;
|
self.regs.y = val;
|
||||||
self.set_flag(ZERO, val == 0);
|
self.set_flag(ZERO, val == 0);
|
||||||
self.set_flag(NEGATIVE, (val & 0x80) != 0);
|
self.set_flag(NEGATIVE, (val & 0x80) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_LSR<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_LSR<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_NOP<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_NOP<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
// does nothing
|
// does nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_ORA<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_ORA<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_PHA<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_PHA<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_PHP<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_PHP<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_PLA<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_PLA<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_PLP<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_PLP<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_ROL<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_ROL<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_ROR<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_ROR<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_RTI<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_RTI<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_RTS<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_RTS<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_SBC<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_SBC<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_SEC<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_SEC<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_SED<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_SED<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_SEI<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_SEI<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_STA<T: Bus>(&mut self, bus: &mut T, addr: Word) {
|
fn op_STA<T: Memory>(&mut self, bus: &mut T, addr: Word) {
|
||||||
self.writeb(bus, addr, self.regs.a)
|
self.writeb(bus, addr, self.regs.a)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_STX<T: Bus>(&mut self, bus: &mut T, addr: Word) {
|
fn op_STX<T: Memory>(&mut self, bus: &mut T, addr: Word) {
|
||||||
self.writeb(bus, addr, self.regs.x)
|
self.writeb(bus, addr, self.regs.x)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_STY<T: Bus>(&mut self, bus: &mut T, addr: Word) {
|
fn op_STY<T: Memory>(&mut self, bus: &mut T, addr: Word) {
|
||||||
self.writeb(bus, addr, self.regs.y)
|
self.writeb(bus, addr, self.regs.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_TAX<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_TAX<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_TAY<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_TAY<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_TSX<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_TSX<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_TXA<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_TXA<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_TXS<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_TXS<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_TYA<T: Bus>(&mut self, bus: &T, val: Word) {
|
fn op_TYA<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/nes/mappers.rs
Normal file
45
src/nes/mappers.rs
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user