Major refactor

- CPU is now it's own module
- Memory object is now shared to support mapper chips
- ROM is now stored as `Arc<[u8]>` to support mapper chips
This commit is contained in:
2026-01-24 03:38:42 -06:00
parent b5e1d1a4c3
commit f861f75b21
16 changed files with 3071 additions and 2450 deletions

View File

@@ -8,7 +8,7 @@ use iced::{
use crate::{
hex_view::Memory,
mem::{Mapper, MemoryMap, Segment},
mem::{Mapped, PpuMem},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -72,15 +72,18 @@ impl<const W: usize, const H: usize> RenderBuffer<W, H> {
}
}
pub(crate) enum PPUMMRegisters {
#[derive(Debug, Clone, Copy)]
pub enum PPUMMRegisters {
Palette,
}
#[derive(Debug, Clone)]
pub struct OAM {
mem: Vec<u8>,
addr: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum BackgroundState {
NameTableBytePre,
NameTableByte,
@@ -92,6 +95,7 @@ enum BackgroundState {
PatternTableTileHigh,
}
#[derive(Debug, Clone)]
pub struct Background {
/// Current vram address, 15 bits
pub v: u16,
@@ -119,6 +123,7 @@ pub struct Background {
pub cur_shift_low: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct Mask {
pub grayscale: bool,
pub background_on_left_edge: bool,
@@ -453,6 +458,7 @@ const COLORS: &'static [Color; 0b100_0000] = &[
}, // 3F
];
#[derive(Debug, Clone)]
pub struct Palette {
colors: &'static [Color; 0x40],
ram: [u8; 0x20],
@@ -465,6 +471,7 @@ impl Palette {
}
}
#[derive(Clone)]
pub struct PPU {
// registers: PPURegisters,
pub frame_count: usize,
@@ -477,7 +484,6 @@ pub struct PPU {
pub mask: Mask,
pub vblank: bool,
pub(crate) memory: MemoryMap<PPUMMRegisters>,
palette: Palette,
pub background: Background,
oam: OAM,
@@ -505,7 +511,10 @@ impl std::fmt::Debug for PPU {
}
impl PPU {
pub fn with_chr_rom(rom: &[u8], mapper: Mapper) -> Self {
// pub fn with_chr_rom(rom: &[u8], mapper: Mapper) -> Self {
// todo!()
// }
pub fn init() -> Self {
Self {
cycle: 25,
dbg_int: false,
@@ -536,7 +545,6 @@ impl PPU {
scanline: 0,
pixel: 25,
render_buffer: RenderBuffer::empty(),
memory: mapper.ppu_map(rom),
background: Background {
v: 0,
t: 0,
@@ -561,17 +569,14 @@ impl PPU {
}
}
pub fn reset(&mut self) {
*self = Self {
memory: std::mem::replace(&mut self.memory, MemoryMap::new(vec![])),
..Self::with_chr_rom(&[], Mapper::from_flags(0))
};
*self = Self::init();
}
pub fn rendering_enabled(&self) -> bool {
self.mask.enable_background || self.mask.enable_sprites
}
pub fn read_reg(&mut self, offset: u16) -> u8 {
pub fn read_reg(&mut self, mem: &mut PpuMem<'_>, offset: u16) -> u8 {
match offset {
0 => panic!("ppuctrl is write-only"),
1 => panic!("ppumask is write-only"),
@@ -587,12 +592,9 @@ impl PPU {
6 => panic!("ppuaddr is write-only"),
7 => {
// println!("Updating v for ppudata read");
let val = self
.memory
.read(self.background.v)
.reg_map(|a, off| match a {
PPUMMRegisters::Palette => self.palette.ram[off as usize],
});
let val = mem.read(self.background.v).reg_map(|a, off| match a {
PPUMMRegisters::Palette => self.palette.ram[off as usize],
});
// if self.background
self.increment_v();
val
@@ -601,7 +603,7 @@ impl PPU {
_ => panic!("No register at {:02X}", offset),
}
}
pub fn write_reg(&mut self, offset: u16, val: u8) {
pub fn write_reg(&mut self, mem: &mut PpuMem, offset: u16, val: u8) {
match offset {
0x00 => {
self.nmi_enabled = val & 0b1000_0000 != 0;
@@ -664,13 +666,12 @@ impl PPU {
}
0x07 => {
// println!("Writing: {:02X}, @{:04X}", val, self.background.v);
self.memory
.write(self.background.v, val, |r, o, v| match r {
PPUMMRegisters::Palette => {
// println!("Writing {:02X} to {:02X}", v & 0x3F, o);
self.palette.ram[o as usize] = v & 0x3F;
}
});
mem.write(self.background.v, val, |r, o, v| match r {
PPUMMRegisters::Palette => {
// println!("Writing {:02X} to {:02X}", v & 0x3F, o);
self.palette.ram[o as usize] = v & 0x3F;
}
});
self.increment_v();
// self.background.v += 1; // TODO: implement inc behavior
}
@@ -694,7 +695,7 @@ impl PPU {
// // TODO: OAM high addr
// }
pub fn run_one_clock_cycle(&mut self) -> bool {
pub fn run_one_clock_cycle(&mut self, mem: &mut PpuMem<'_>) -> bool {
self.cycle += 1;
self.pixel += 1;
if self.scanline == 261
@@ -761,7 +762,7 @@ impl PPU {
// let addr = 0x2000 + self.pixel / 8 + self.scanline / 8;
let addr = 0x2000 | (self.background.v & 0x0FFF);
// println!("Cur: {:04X}, comp: {:04X}", addr, addr_2);
let val = self.memory.read(addr).reg_map(|_, _| 0);
let val = mem.read(addr).reg_map(|_, _| 0);
self.background.cur_nametable = val;
} else if self.pixel % 8 == 4 {
// Attr table fetch
@@ -772,7 +773,7 @@ impl PPU {
| ((self.background.v >> 2) & 0x07);
// println!("Cur: {:04X}, comp: {:04X}", addr, addr_2);
// assert_eq!(addr, addr_2);
let val = self.memory.read(addr).reg_map(|_, _| 0); // TODO: handle reg reads
let val = mem.read(addr).reg_map(|_, _| 0); // TODO: handle reg reads
self.background.next_attr_2 = val;
} else if self.pixel % 8 == 6 {
// BG pattern low
@@ -784,7 +785,7 @@ impl PPU {
} else {
0
};
let val = self.memory.read(addr).reg_map(|_, _| 0);
let val = mem.read(addr).reg_map(|_, _| 0);
self.background.cur_low = val;
} else if self.pixel % 8 == 0 && self.pixel > 0 {
let addr = self.background.cur_nametable as u16 * 16
@@ -796,7 +797,7 @@ impl PPU {
} else {
0
};
let val = self.memory.read(addr).reg_map(|_, _| todo!());
let val = mem.read(addr).reg_map(|_, _| todo!());
self.background.cur_high = val;
self.background.cur_shift_low |= self.background.cur_low as u32;
self.background.cur_shift_high |= self.background.cur_high as u32;
@@ -842,7 +843,6 @@ impl PPU {
}
if self.scanline == 241 && self.pixel == 1 {
self.vblank = true;
// self.nmi_waiting = self.nmi_enabled;
self.frame_count += 1;
self.background.state = BackgroundState::NameTableBytePre;
return true;
@@ -858,12 +858,6 @@ impl PPU {
}
pub fn nmi_waiting(&mut self) -> bool {
self.vblank && self.nmi_enabled
// if self.nmi_waiting {
// self.nmi_waiting = false;
// return true;
// } else {
// return false;
// }
}
pub fn peek_irq(&self) -> bool {
false
@@ -872,30 +866,29 @@ impl PPU {
false
}
pub fn render_name_table<R: Renderer>(&self, frame: &mut Frame<R>) {
pub fn render_name_table<R: Renderer>(&self, mem: &Mapped, frame: &mut Frame<R>) {
for y in 0..60 {
for x in 0..64 {
let row = y % 30;
let col = x % 32;
let off = 0x2000 + 0x400 * (y / 30 * 2 + x / 32);
let name = self.memory.peek((off + col + row * 32) as u16).unwrap() as u16 * 16
let name = mem.peek_ppu((off + col + row * 32) as u16).unwrap() as u16 * 16
+ if self.background.second_pattern {
0x1000
} else {
0
};
let attr = self
.memory
.peek((col / 4 + row / 4 * 8 + 0x3C0 + off) as u16)
let attr = mem
.peek_ppu((col / 4 + row / 4 * 8 + 0x3C0 + off) as u16)
.unwrap();
// attr << (((col & 1) << 1) | ((row & 1) << 0)) * 2
// let h_off = ((self.pixel - 1) / 16) % 2;
// let v_off = (self.scanline / 16) % 2;
// let off = v_off * 4 + h_off * 2;
let palette = (attr >> (((col & 1) << 1) | ((row & 1) << 2))) & 0x3;
let palette = (attr >> (((col & 2) << 0) | ((row & 2) << 1))) & 0x3;
for y_off in 0..8 {
let low = self.memory.peek(name + y_off).unwrap();
let high = self.memory.peek(name + y_off + 8).unwrap();
let low = mem.peek_ppu(name + y_off).unwrap();
let high = mem.peek_ppu(name + y_off + 8).unwrap();
for bit in 0..8 {
frame.fill_rectangle(
Point::new(
@@ -929,21 +922,20 @@ impl PPU {
}
}
// for
// let pat = self.memory.peek();
// let pat = mem.peek();
}
}
}
pub fn name_cursor_info(&self, cursor: Point) -> Option<String> {
pub fn name_cursor_info(&self, mem: &Mapped, cursor: Point) -> Option<String> {
let x = (cursor.x / 8.) as usize;
let y = (cursor.y / 8.) as usize;
if x < 64 && y < 60 {
let row = y % 30;
let col = x % 32;
let off = 0x2000 + 0x400 * (y / 30 * 2 + x / 32);
let name = self.memory.peek((off + col + row * 32) as u16).unwrap() as usize;
let attr = self
.memory
.peek((col / 4 + row / 4 * 8 + 0x3C0 + off) as u16)
let name = mem.peek_ppu((off + col + row * 32) as u16).unwrap() as usize;
let attr = mem
.peek_ppu((col / 4 + row / 4 * 8 + 0x3C0 + off) as u16)
.unwrap();
Some(format!(
"Row, Column: {}, {}
@@ -988,13 +980,13 @@ Attribute data: ${:02X}
}
}
pub fn render_pattern_tables<R: Renderer>(&self, frame: &mut Frame<R>) {
pub fn render_pattern_tables<R: Renderer>(&self, mem: &Mapped, frame: &mut Frame<R>) {
for y in 0..16 {
for x in 0..16 {
let name = (y * 16 + x) * 16;
for y_off in 0..8 {
let low = self.memory.peek(name + y_off).unwrap();
let high = self.memory.peek(name + y_off + 8).unwrap();
let low = mem.peek_ppu(name + y_off).unwrap();
let high = mem.peek_ppu(name + y_off + 8).unwrap();
for bit in 0..8 {
frame.fill_rectangle(
Point::new(
@@ -1030,8 +1022,8 @@ Attribute data: ${:02X}
for x in 0..16 {
let name = (y * 16 + x) * 16;
for y_off in 0..8 {
let low = self.memory.peek(name + y_off + 0x1000).unwrap();
let high = self.memory.peek(name + y_off + 8 + 0x1000).unwrap();
let low = mem.peek_ppu(name + y_off + 0x1000).unwrap();
let high = mem.peek_ppu(name + y_off + 8 + 0x1000).unwrap();
for bit in 0..8 {
frame.fill_rectangle(
Point::new(
@@ -1061,7 +1053,7 @@ Attribute data: ${:02X}
}
}
// for
// let pat = self.memory.peek();
// let pat = mem.peek();
}
}
}
@@ -1105,10 +1097,6 @@ Attribute data: ${:02X}
None
}
}
pub fn mem(&self) -> &impl Memory {
&self.memory
}
}
#[cfg(test)]
@@ -1117,14 +1105,18 @@ mod tests {
#[test]
fn ppu_registers() {
let mut ppu = PPU::with_chr_rom(&[0u8; 8192], Mapper::from_flags(0));
// let mut ppu = PPU::with_chr_rom(&[0u8; 8192], Mapper::from_header(0));
let mut ppu = PPU::init();
// let mut mem = MemoryMap::new(vec![Segment::ram("")]);
let mut mem = Mapped::test_ram();
let mut mem = PpuMem::new(&mut mem);
assert_eq!(ppu.background.v, 0);
assert_eq!(ppu.background.t, 0);
assert_eq!(ppu.background.x, 0);
assert_eq!(ppu.background.w, false);
ppu.write_reg(0, 0);
ppu.write_reg(&mut mem, 0, 0);
assert_eq!(ppu.background.v, 0);
ppu.write_reg(0, 0b11);
ppu.write_reg(&mut mem, 0, 0b11);
assert_eq!(
ppu.background.t, 0b0001100_00000000,
"Actual: {:016b}",
@@ -1132,7 +1124,7 @@ mod tests {
);
assert_eq!(ppu.background.w, false);
ppu.write_reg(5, 0x7D);
ppu.write_reg(&mut mem, 5, 0x7D);
assert_eq!(
ppu.background.t, 0b0001100_00001111,
"Actual: {:016b}",
@@ -1140,7 +1132,7 @@ mod tests {
);
assert_eq!(ppu.background.x, 0b101);
assert_eq!(ppu.background.w, true);
ppu.write_reg(5, 0x5E);
ppu.write_reg(&mut mem, 5, 0x5E);
assert_eq!(
ppu.background.t, 0b1101101_01101111,
"Actual: {:016b}",
@@ -1149,7 +1141,7 @@ mod tests {
assert_eq!(ppu.background.x, 0b101);
assert_eq!(ppu.background.w, false);
ppu.write_reg(5, 0x7D);
ppu.write_reg(&mut mem, 5, 0x7D);
assert_eq!(
ppu.background.t, 0b1101101_01101111,
"Actual: {:016b}",
@@ -1157,7 +1149,7 @@ mod tests {
);
assert_eq!(ppu.background.x, 0b101);
assert_eq!(ppu.background.w, true);
ppu.read_reg(2);
ppu.read_reg(&mut mem, 2);
assert_eq!(ppu.background.w, false);
}
}