Memory observer

This commit is contained in:
william 2025-01-31 21:33:41 -05:00
parent ea6a4e1680
commit eb7e842a81
8 changed files with 87 additions and 32 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/target /target
/roms /roms
/logs

View File

@ -1,7 +1,7 @@
use crate::cpu::op::AddressingMode::*; use crate::cpu::op::AddressingMode::*;
use crate::cpu::op::{AddressingMode, Instruction}; use crate::cpu::op::{AddressingMode, Instruction};
use crate::cpu::Cpu; use crate::cpu::Cpu;
use simplelog::debug; use simplelog::{trace};
impl Cpu { impl Cpu {
pub fn disassemble_next_instr(&self) { pub fn disassemble_next_instr(&self) {
@ -12,7 +12,7 @@ impl Cpu {
let operation = instr.op().to_string(); let operation = instr.op().to_string();
let addr_mode = self.disassemble_addr_mode(instr.addr_mode(), registers.pc); let addr_mode = self.disassemble_addr_mode(instr.addr_mode(), registers.pc);
debug!( trace!(
"{:#04X} - ${:04X} {} {}\tA {:#04X}, X {:#04X}, Y {:#04X}, SP: {:#04X}, F: {:#04X}", "{:#04X} - ${:04X} {} {}\tA {:#04X}, X {:#04X}, Y {:#04X}, SP: {:#04X}, F: {:#04X}",
op_code, op_code,
registers.pc, registers.pc,
@ -27,7 +27,7 @@ impl Cpu {
} }
fn disassemble_addr_mode(&self, addr_mode: AddressingMode, pc: u16) -> String { fn disassemble_addr_mode(&self, addr_mode: AddressingMode, pc: u16) -> String {
let word_peek = self.memory_bus.borrow().get_word(pc); let word_peek = self.memory_bus.borrow().get_word(pc + 1);
let byte_peek = (word_peek & 0xFF) as u8; let byte_peek = (word_peek & 0xFF) as u8;
match addr_mode { match addr_mode {
@ -40,10 +40,13 @@ impl Cpu {
IND => format!("(${:04X})", word_peek), IND => format!("(${:04X})", word_peek),
IDX => format!("(${:04X},x)", word_peek), IDX => format!("(${:04X},x)", word_peek),
IDY => format!("(${:04X}),y", word_peek), IDY => format!("(${:04X}),y", word_peek),
REL => format!("#${:04X}", word_peek), REL => format!(
"${:04X}",
(pc + 2).wrapping_add_signed(byte_peek as i8 as i16)
),
ACC => String::from('A'), ACC => String::from('A'),
IMP => String::from("I"), IMP => String::from("I"),
IMM => format!("{:04X}", byte_peek), IMM => format!("#${:02X}", byte_peek),
} }
} }
} }

View File

@ -260,16 +260,10 @@ impl Cpu {
/// label - PC + byte /// label - PC + byte
fn operand_addr_rel(&mut self) -> u16 { fn operand_addr_rel(&mut self) -> u16 {
let offset = self.program_get_next_byte() as u8;
let base_addr = self.registers.pc; let base_addr = self.registers.pc;
let offset = self.program_get_next_byte() as u16;
// The offset is signed base_addr.wrapping_add_signed(offset as i8 as i16)
let positive_offset = offset & 0x7F;
if offset & 0x80 > 0 {
base_addr.wrapping_sub(positive_offset)
} else {
base_addr.wrapping_add(positive_offset)
}
} }
} }

View File

@ -8,4 +8,4 @@ mod ppu;
pub trait Clock { pub trait Clock {
/// Run a clock cycle /// Run a clock cycle
fn cycle(&mut self); fn cycle(&mut self);
} }

View File

@ -1,5 +1,7 @@
use simplelog::warn; use std::cell::RefCell;
use crate::mappers::Mapper; use crate::mappers::Mapper;
use simplelog::warn;
use std::rc::{Weak};
const MEMORY_MAX_ADDR: u16 = 0xFFFF; const MEMORY_MAX_ADDR: u16 = 0xFFFF;
const PPU_MAX_ADDR: u16 = 0x4000; const PPU_MAX_ADDR: u16 = 0x4000;
@ -12,12 +14,18 @@ const RAM_MAX_ADDR: u16 = 0x2000;
pub struct MemoryBus { pub struct MemoryBus {
mapper: Option<Box<dyn Mapper>>, mapper: Option<Box<dyn Mapper>>,
ram: [u8; RAM_SIZE], ram: [u8; RAM_SIZE],
observers: Vec<Weak<RefCell<dyn MemoryObserver>>>,
} }
impl MemoryBus { impl MemoryBus {
pub fn new() -> Self { pub fn new() -> Self {
let ram = [0; RAM_SIZE]; let ram = [0; RAM_SIZE];
Self { mapper: None, ram } Self {
mapper: None,
ram,
observers: vec![],
}
} }
/// Register a mapper in the memory bus, simulates plugging in a cartridge. /// Register a mapper in the memory bus, simulates plugging in a cartridge.
@ -51,8 +59,11 @@ impl MemoryBus {
let prg_addr = addr - CARTRIDGE_RAM_MAX_ADDR; let prg_addr = addr - CARTRIDGE_RAM_MAX_ADDR;
if self.mapper.is_none() { if self.mapper.is_none() {
warn!("Tried to read memory address {} before setting a mapper", addr); warn!(
return 0 "Tried to read memory address {} before setting a mapper",
addr
);
return 0;
} }
let mapper = self.mapper.as_ref().unwrap(); let mapper = self.mapper.as_ref().unwrap();
@ -94,5 +105,29 @@ impl MemoryBus {
// Cannot write to ROM // Cannot write to ROM
assert!(false); assert!(false);
} }
// Notify the observers
for observer in self.observers.iter() {
match observer.upgrade() {
None => {}
Some(rc) => rc.borrow_mut().notify_mem_byte_updated(addr, value)
}
}
}
pub fn register_observer(&mut self, observer: Weak<RefCell<dyn MemoryObserver>>) {
self.observers.push(observer);
} }
} }
// pub type MemoryObserver = impl Fn(u16, u8) -> ();
// A memory observer. Will be notified when a byte was modified in memory.
pub trait MemoryObserver {
/// Called when a byte was updated in the memory bus.
///
/// # Arguments
/// * addr - The address of the byte
/// * value - The updated value
fn notify_mem_byte_updated(&mut self, addr: u16, value: u8);
}

View File

@ -1,7 +1,8 @@
use crate::memory::MemoryBus; use crate::memory::{MemoryBus, MemoryObserver};
use crate::Clock; use crate::Clock;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use simplelog::info;
pub struct Ppu { pub struct Ppu {
memory_bus: Rc<RefCell<MemoryBus>>, memory_bus: Rc<RefCell<MemoryBus>>,
@ -9,7 +10,17 @@ pub struct Ppu {
impl Ppu { impl Ppu {
pub fn new(memory_bus: Rc<RefCell<MemoryBus>>) -> Self { pub fn new(memory_bus: Rc<RefCell<MemoryBus>>) -> Self {
Self { memory_bus } Self { memory_bus: memory_bus.clone() }
}
fn set_register(&self, addr: u16, value: u8) {
}
}
impl MemoryObserver for Ppu {
fn notify_mem_byte_updated(&mut self, addr: u16, value: u8) {
info!("MemChange: {:#04x} -> {:#02x}", addr, value);
} }
} }

View File

@ -19,15 +19,18 @@ const REFRESH_RATE: usize = 60; // 60 Hz
pub struct System { pub struct System {
cpu: Cpu, cpu: Cpu,
memory_bus: Rc<RefCell<MemoryBus>>, memory_bus: Rc<RefCell<MemoryBus>>,
ppu: Ppu ppu: Rc<RefCell<Ppu>>
} }
impl System { impl System {
pub fn new() -> Self { pub fn new() -> Self {
let memory_bus = Rc::new(RefCell::new(MemoryBus::new())); let memory_bus = Rc::new(RefCell::new(MemoryBus::new()));
let cpu = Cpu::new(Rc::clone(&memory_bus)); let cpu = Cpu::new(Rc::clone(&memory_bus));
let ppu = Ppu::new(Rc::clone(&memory_bus)); let ppu = Rc::new(RefCell::new(Ppu::new(Rc::clone(&memory_bus))));
let weak_ppu = Rc::downgrade(&ppu);
memory_bus.borrow_mut().register_observer(weak_ppu);
Self { cpu, memory_bus, ppu } Self { cpu, memory_bus, ppu }
} }
@ -49,7 +52,7 @@ impl Clock for System {
self.cpu.cycle(); self.cpu.cycle();
for _ppu_cycle in 0usize..PPU_CYCLE_PER_REFRESH { for _ppu_cycle in 0usize..PPU_CYCLE_PER_REFRESH {
self.ppu.cycle(); self.ppu.borrow_mut().cycle();
} }
} }
} }

View File

@ -1,18 +1,26 @@
use std::thread::sleep;
use std::time::Duration;
use core::system::System; use core::system::System;
use core::Clock; use core::Clock;
use simplelog::*; use simplelog::*;
use std::fs::File;
use std::thread::sleep;
use std::time::Duration;
const ROM_PATH: &'static str = "./roms/dk.nes"; const ROM_PATH: &'static str = "./roms/dk.nes";
fn main() { fn main() {
CombinedLogger::init(vec![TermLogger::new( CombinedLogger::init(vec![
LevelFilter::Debug, TermLogger::new(
Config::default(), LevelFilter::Info,
TerminalMode::Mixed, Config::default(),
ColorChoice::Auto, TerminalMode::Mixed,
)]) ColorChoice::Auto,
),
WriteLogger::new(
LevelFilter::Debug,
Config::default(),
File::create("./logs/latest.log").unwrap(),
),
])
.unwrap(); .unwrap();
let mut system = System::new(); let mut system = System::new();