Added basic mapper infrastructure
This commit is contained in:
parent
123ef70487
commit
f916f77641
1
doc/nes_architecture
Normal file
1
doc/nes_architecture
Normal file
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2022-05-21T12:58:33.623Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/17.4.2 Chrome/96.0.4664.174 Electron/16.2.2 Safari/537.36" etag="4GrUG8G62nWZSU5-rLoW" version="17.4.2" type="device"><diagram id="ump8jJu03wSbJk_0SnHp" name="Page-1">7VvbcqM4EP0aqmYeMgXiYvzoS7JTtfFuKs5sdh4Vo9hMMPIK2bHn61eAxE2YYGOMJ5OnWE1LQPfp090SUfTRcvsHgavFBDvIU4DqbBV9rACgaT2d/Qklu1jSA1YsmBPX4UqpYOr+RFyocunadVCQU6QYe9Rd5YUz7PtoRnMySAh+zas9Yy9/1xWcI0kwnUFPlj66Dl2I97L66YWvyJ0v+K1t0IsvLKFQ5m8SLKCDXzMi/VrRRwRjGv9abkfIC40n7BLPu9lzNXkwgnxaZ4L3aNkb76/5K/J3wx9TePsyxFfhhOjh6E68MXKYAfgQE7rAc+xD7zqVDgle+w4Kl1XZKNW5xXjFhBoT/kCU7rg34ZpiJlrQpcevoq1L/w2nfwEmH37nq4W/x9vsYCcGPiW77Kxw/D17MZ0XjcTEgEJCByEWmMDHPhKyG9fzEh2CXxIPM98MY7uExthrby4K8JrMUIWRBW4hmSNa5YwEFSycEF4i9hZsHkEepO4m/xyQ43qe6PGp7EXhLqOwwq5Pg8zKd6GAKfAQtTk8eXxaBRAVtI1+lTr7Ed9ejDLvkYoiXB6CUf0DoxeDUe0XAKmtnh+kuhHfcgO9NTcCdBxSitxb+MSyZA5t0HPnPvs9Y45DhAk2iFCXpaEBv7B0HScGNgrcn/ApWi9EBTcdW9wcKuY4QUW4ANrm3MFzJJ+cZqYsXioiUHY6X56B3TCsnMkFng6DRepJoYKfnwOGx7z7TuOwj8x3OaxiXj6pgL55dlLRJE4Z3X2TYBu8uksPRs7dG/qS8/YGs563iia49DWtfoGQLTKFb2LOMv/kgvfgSJWsMKi2QgRxHoKMumqaZb8H9trKKNjKkG2lgRJbJcKT2woY3bLaUaR2PKfNPBgE7qxAa9ppSQzULY26IDGzgEG7msU0vUq9pfrd6hKTWhaRadZ9C5Q5SGbS7qVm2togbVq/N6Mn+wMKFwMFuwu+sgpVl2ZWE1ZR39B7lfo9UKnfUvNnSSXKO2r+9uAkaf408/KbPbXUF53wznG0cxDr7C+L8sTTUaHUDfFYeWIQTcxe4inoG2b/DETSk4jEgRS+DyLR1XK/J0Si2ma+xwaXzitActdd6w26Uew6a3foaltdpzisy9jhig2ZLUYP97cKsDz2CMMnlhCtOY2sEEscd1MUBSvoCxlfYzKY/ikus8fLamTEJWvVW376MHj4Nm3xBuPBw6DF5Qfj8f2ByxfwybBIy4nEQ8+0GY2EWTGIE2Saf0bYwyStjp9ZfiqIasXJIYdoYpV87Ohy6PRLIqe9rS1NcsbvuV3Tal1i1qxLmm7gNIKCKVHo4/1gwiSfwMvnllMKsN5OKcnmZjYwrLYCw5as8c/ZrCG1kJohW8MqsUa/tfwqHwTkckTGFtZ/6/CDkmHI6Vecxln2USMmT65WpRUfsocOOT04Pmmd9IEgpcR9WlMUxfAFPdgKeoxnD36c2tn3FGX82fNv9W5Bvr4vSb9aWeVqtnZcYn7k31NnW+1c5yXNKi85376fThvsOU/ny1+pX3SRUkSOUy690+51GqnKETt4mvLL7eDVjt2mp0in2cLjQVx3C6+w5dfSFp78ucIs6DbL5D410uqBt0maqXXo1RVym24+Nyvk5Q3DETMScUMTnLerKevx9LKPVUCvtbZG/mbyjuA5gUsmvP970tgiB52Ai1640PzV/KSnvd5PPltkkIEzKmx0wxtj+wSN8QkMlnTG3XXL8iFKez0gdb2wL41rx4voSkHkGqAC04pgsbU/K/H/qXw0qnJgNdgpLuUBvTVUy1ti2cBXP+EVdcMCoRsW0IrHpTV3EFtjAeGcjL0mcLViMO2EJLW3WbLs5OEI87Bh+k9lcVWb/muefv0/</diagram></mxfile>
|
||||
37
src/bus.rs
37
src/bus.rs
@ -1,15 +1,24 @@
|
||||
use std::fmt;
|
||||
use std::{
|
||||
fmt,
|
||||
rc::Rc,
|
||||
cell::RefCell,
|
||||
};
|
||||
|
||||
use crate::peripherals::{Peripheral, Ram};
|
||||
use crate::peripherals::{
|
||||
Peripheral,
|
||||
Ram,
|
||||
Mapper,
|
||||
Ppu,
|
||||
};
|
||||
|
||||
pub struct Bus {
|
||||
pub struct Bus<M: Mapper> {
|
||||
ram: Ram<0x800>,
|
||||
ppu: Ram<0x8>,
|
||||
apu: Ram<0x1F>,
|
||||
cartridge: Ram<0xBFE0>,
|
||||
ppu: Ppu<M>,
|
||||
apu: Ram<0x1F>,//TODO
|
||||
cartridge: Rc<RefCell<M>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Bus {
|
||||
impl<M: Mapper> fmt::Debug for Bus<M> {
|
||||
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Bus")
|
||||
@ -18,7 +27,7 @@ impl fmt::Debug for Bus {
|
||||
}
|
||||
}
|
||||
|
||||
impl Peripheral for Bus {
|
||||
impl<M: Mapper> Peripheral for Bus<M> {
|
||||
|
||||
fn read_addr(&self, addr: u16) -> u8 {
|
||||
|
||||
@ -26,7 +35,7 @@ impl Peripheral for Bus {
|
||||
0x0000..=0x1FFF => self.ram.read_addr(addr % 0x0800), //RAM is mirrored 3 times
|
||||
0x2000..=0x3FFF => self.ppu.read_addr(addr % 0x8), //PPU is mirrored every 8 bytes
|
||||
0x4000..=0x401F => self.apu.read_addr(addr),
|
||||
0x4020..=0xFFFF => self.cartridge.read_addr(addr),
|
||||
0x4020..=0xFFFF => self.cartridge.borrow().read_addr(addr),
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,14 +45,14 @@ impl Peripheral for Bus {
|
||||
0x0000..=0x1FFF => self.ram.write_addr(addr % 0x0800, data),
|
||||
0x2000..=0x3FFF => self.ppu.write_addr(addr % 0x8, data),
|
||||
0x4000..=0x401F => self.apu.write_addr(addr, data),
|
||||
0x4020..=0xFFFF => self.cartridge.write_addr(addr, data),
|
||||
0x4020..=0xFFFF => self.cartridge.borrow_mut().write_addr(addr, data),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Bus {
|
||||
impl<M: Mapper> Bus<M> {
|
||||
|
||||
pub fn new() -> Self {
|
||||
pub fn new(mapper: Rc<RefCell<M>>) -> Self {
|
||||
|
||||
let mut ram = Ram::<0x800>::new();
|
||||
|
||||
@ -106,9 +115,9 @@ impl Bus {
|
||||
|
||||
Bus {
|
||||
ram,
|
||||
ppu: Ram::<0x8>::new(),
|
||||
ppu: Ppu::new(mapper.clone()),
|
||||
apu: Ram::<0x1F>::new(),
|
||||
cartridge: Ram::<0xBFE0>::new(),
|
||||
cartridge: mapper,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
src/cpu.rs
22
src/cpu.rs
@ -1,14 +1,18 @@
|
||||
#[allow(unused_imports)]
|
||||
use log::{debug, error, info, trace, warn};
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
rc::Rc,
|
||||
cell::RefCell,
|
||||
};
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::{
|
||||
bus::FileBus,
|
||||
peripherals::Peripheral,
|
||||
bus::Bus,
|
||||
peripherals::{Peripheral, Mapper},
|
||||
};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -138,17 +142,17 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Cpu {
|
||||
pub struct Cpu<M: Mapper> {
|
||||
a: u8,
|
||||
x: u8,
|
||||
y: u8,
|
||||
pc: u16,
|
||||
s: u8,
|
||||
p: StatusReg,
|
||||
bus: FileBus,
|
||||
bus: Bus<M>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Cpu {
|
||||
impl<M: Mapper> fmt::Debug for Cpu<M> {
|
||||
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("Cpu")
|
||||
@ -164,12 +168,12 @@ impl fmt::Debug for Cpu {
|
||||
}
|
||||
|
||||
|
||||
impl Cpu {
|
||||
impl<M: Mapper> Cpu<M> {
|
||||
|
||||
pub fn new() -> Self {
|
||||
pub fn new(mapper: Rc<RefCell<M>>) -> Self {
|
||||
|
||||
// read initial PC value from reset vector
|
||||
let bus = FileBus::new();
|
||||
let bus = Bus::new(mapper);
|
||||
let pc_lo = bus.read_addr(0xFFFC) as u16;
|
||||
let pc_hi = bus.read_addr(0xFFFD) as u16;
|
||||
|
||||
|
||||
10
src/main.rs
10
src/main.rs
@ -6,6 +6,7 @@ use cpu::Cpu;
|
||||
|
||||
mod bus;
|
||||
mod peripherals;
|
||||
use peripherals::mapper::Nrom;
|
||||
mod utils;
|
||||
|
||||
fn main() -> Result<(), &'static str> {
|
||||
@ -15,7 +16,11 @@ fn main() -> Result<(), &'static str> {
|
||||
dpi::LogicalSize,
|
||||
};
|
||||
use mini_gl_fb::config;
|
||||
use std::fmt::Write;
|
||||
use std::{
|
||||
fmt::Write,
|
||||
rc::Rc,
|
||||
cell::RefCell,
|
||||
};
|
||||
|
||||
// setup
|
||||
setup_logger().map_err(|_| "Failed to setup logger")?;
|
||||
@ -31,7 +36,8 @@ fn main() -> Result<(), &'static str> {
|
||||
let mut renderer = FontRenderer::new(20.0, [0u8, 0, 0]);
|
||||
|
||||
// first image
|
||||
let mut cpu = Cpu::new();
|
||||
let mapper = Rc::new(RefCell::new(Nrom::new()));
|
||||
let mut cpu = Cpu::new(mapper);
|
||||
let mut debug_str = String::new();
|
||||
write!(debug_str, "{:#?}", cpu).unwrap();
|
||||
buffer.fill([20, 20], [760, 560], Pixel::rgba(255, 255, 255, 255));
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
use std::fmt;
|
||||
|
||||
mod ram;
|
||||
mod ppu;
|
||||
pub mod mapper;
|
||||
|
||||
pub use ram::Ram;
|
||||
pub use ppu::Ppu;
|
||||
pub use mapper::Mapper;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub trait Peripheral {
|
||||
@ -8,57 +15,3 @@ pub trait Peripheral {
|
||||
fn write_addr(&mut self, addr: u16, data: u8);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub struct Ram<const SIZE: usize> {
|
||||
pub buffer: [u8; SIZE],
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> fmt::Debug for Ram<SIZE> {
|
||||
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for i in 0x100..=0x1FF {
|
||||
if i%0x10 == 0 {
|
||||
f.write_fmt(format_args!("\n {:0>4X}", i))?;
|
||||
}
|
||||
f.write_fmt(format_args!(" {:0>2X}", self.buffer[i]))?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Peripheral for Ram<SIZE> {
|
||||
|
||||
fn read_addr(&self, addr: u16) -> u8 {
|
||||
self.buffer[addr as usize]
|
||||
}
|
||||
|
||||
fn write_addr(&mut self, addr: u16, data: u8) {
|
||||
self.buffer[addr as usize] = data;
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Ram<SIZE> {
|
||||
|
||||
pub fn new() -> Self {
|
||||
|
||||
Ram {
|
||||
buffer: [0u8; SIZE],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file(file: &str) -> Self {
|
||||
use std::{
|
||||
fs::File,
|
||||
io::Read,
|
||||
};
|
||||
|
||||
let mut f = File::open(file).unwrap();
|
||||
let mut buffer = [0u8; SIZE];
|
||||
f.read_exact(&mut buffer).unwrap();
|
||||
|
||||
Ram {
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
src/peripherals/mapper.rs
Normal file
42
src/peripherals/mapper.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use super::Peripheral;
|
||||
|
||||
//--Mapper struct-----------------------------------------------------------------------------------
|
||||
///A trait used as API for the different implementations of mappers in cartridges
|
||||
///
|
||||
///A Mapper is a special `Peripheral` that can also be read by the `Ppu`. The exact memory layout
|
||||
///is defined by the implementation
|
||||
pub trait Mapper: Peripheral {
|
||||
|
||||
///read the specified 16 bits address using the ppu bus
|
||||
fn ppu_read_addr(&self, addr: u16) -> u8;
|
||||
}
|
||||
|
||||
pub struct Nrom {
|
||||
|
||||
}
|
||||
|
||||
impl Peripheral for Nrom {
|
||||
|
||||
fn read_addr(&self, _addr: u16) -> u8 {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn write_addr(&mut self, _addr: u16, _data: u8) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl Mapper for Nrom {
|
||||
|
||||
fn ppu_read_addr(&self, _addr: u16) -> u8 {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl Nrom {
|
||||
|
||||
pub fn new() -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
78
src/peripherals/ppu.rs
Normal file
78
src/peripherals/ppu.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use std::{
|
||||
rc::Rc,
|
||||
cell::RefCell,
|
||||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::{
|
||||
peripherals::{Peripheral, Ram, Mapper},
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bitflags! {
|
||||
struct Ctrl: u8 {
|
||||
const Nl = 0x1 << 0;
|
||||
const Nh = 0x1 << 1;
|
||||
const I = 0x1 << 2;
|
||||
const S = 0x1 << 3;
|
||||
const B = 0x1 << 4;
|
||||
const H = 0x1 << 5;
|
||||
const P = 0x1 << 6;
|
||||
const V = 0x1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct Mask: u8 {
|
||||
const Gr = 0x1 << 0;
|
||||
const m = 0x1 << 1;
|
||||
const M = 0x1 << 2;
|
||||
const b = 0x1 << 3;
|
||||
const s = 0x1 << 4;
|
||||
const R = 0x1 << 5;
|
||||
const G = 0x1 << 6;
|
||||
const B = 0x1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct Status: u8 {
|
||||
const O = 0x1 << 5;
|
||||
const S = 0x1 << 6;
|
||||
const V = 0x1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Ppu<M: Mapper> {
|
||||
vbus: Rc<RefCell<M>>,
|
||||
ctrl: Ctrl,
|
||||
mask: Mask,
|
||||
status: Status,
|
||||
scroll: u8,
|
||||
addr: u8,
|
||||
data: u8,
|
||||
oam_addr: u8,
|
||||
oam_data: u8,
|
||||
oam_dma: u8,
|
||||
oam: Ram<0x100>,
|
||||
blanking: bool,
|
||||
}
|
||||
|
||||
impl<M: Mapper> Peripheral for Ppu<M> {
|
||||
|
||||
fn read_addr(&self, _addr: u16) -> u8 {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn write_addr(&mut self, _addr: u16, _data: u8) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Mapper> Ppu<M> {
|
||||
|
||||
pub fn new(_mapper: Rc<RefCell<M>>) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
59
src/peripherals/ram.rs
Normal file
59
src/peripherals/ram.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use std::fmt;
|
||||
|
||||
use super::Peripheral;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub struct Ram<const SIZE: usize> {
|
||||
pub buffer: [u8; SIZE],
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> fmt::Debug for Ram<SIZE> {
|
||||
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for i in 0x100..=0x1FF {
|
||||
if i%0x10 == 0 {
|
||||
f.write_fmt(format_args!("\n {:0>4X}", i))?;
|
||||
}
|
||||
f.write_fmt(format_args!(" {:0>2X}", self.buffer[i]))?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Peripheral for Ram<SIZE> {
|
||||
|
||||
fn read_addr(&self, addr: u16) -> u8 {
|
||||
self.buffer[addr as usize]
|
||||
}
|
||||
|
||||
fn write_addr(&mut self, addr: u16, data: u8) {
|
||||
self.buffer[addr as usize] = data;
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Ram<SIZE> {
|
||||
|
||||
pub fn new() -> Self {
|
||||
|
||||
Ram {
|
||||
buffer: [0u8; SIZE],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file(file: &str) -> Self {
|
||||
use std::{
|
||||
fs::File,
|
||||
io::Read,
|
||||
};
|
||||
|
||||
let mut f = File::open(file).unwrap();
|
||||
let mut buffer = [0u8; SIZE];
|
||||
f.read_exact(&mut buffer).unwrap();
|
||||
|
||||
Ram {
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user