Memory observer
This commit is contained in:
parent
ea6a4e1680
commit
eb7e842a81
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
/target
|
||||
/roms
|
||||
/logs
|
@ -1,7 +1,7 @@
|
||||
use crate::cpu::op::AddressingMode::*;
|
||||
use crate::cpu::op::{AddressingMode, Instruction};
|
||||
use crate::cpu::Cpu;
|
||||
use simplelog::debug;
|
||||
use simplelog::{trace};
|
||||
|
||||
impl Cpu {
|
||||
pub fn disassemble_next_instr(&self) {
|
||||
@ -12,7 +12,7 @@ impl Cpu {
|
||||
let operation = instr.op().to_string();
|
||||
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}",
|
||||
op_code,
|
||||
registers.pc,
|
||||
@ -27,7 +27,7 @@ impl Cpu {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
match addr_mode {
|
||||
@ -40,10 +40,13 @@ impl Cpu {
|
||||
IND => format!("(${:04X})", word_peek),
|
||||
IDX => format!("(${:04X},x)", 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'),
|
||||
IMP => String::from("I"),
|
||||
IMM => format!("{:04X}", byte_peek),
|
||||
IMM => format!("#${:02X}", byte_peek),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,16 +260,10 @@ impl Cpu {
|
||||
|
||||
/// label - PC + byte
|
||||
fn operand_addr_rel(&mut self) -> u16 {
|
||||
let offset = self.program_get_next_byte() as u8;
|
||||
let base_addr = self.registers.pc;
|
||||
let offset = self.program_get_next_byte() as u16;
|
||||
|
||||
// The offset is signed
|
||||
let positive_offset = offset & 0x7F;
|
||||
if offset & 0x80 > 0 {
|
||||
base_addr.wrapping_sub(positive_offset)
|
||||
} else {
|
||||
base_addr.wrapping_add(positive_offset)
|
||||
}
|
||||
base_addr.wrapping_add_signed(offset as i8 as i16)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,4 +8,4 @@ mod ppu;
|
||||
pub trait Clock {
|
||||
/// Run a clock cycle
|
||||
fn cycle(&mut self);
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
use simplelog::warn;
|
||||
use std::cell::RefCell;
|
||||
use crate::mappers::Mapper;
|
||||
use simplelog::warn;
|
||||
use std::rc::{Weak};
|
||||
|
||||
const MEMORY_MAX_ADDR: u16 = 0xFFFF;
|
||||
const PPU_MAX_ADDR: u16 = 0x4000;
|
||||
@ -12,12 +14,18 @@ const RAM_MAX_ADDR: u16 = 0x2000;
|
||||
pub struct MemoryBus {
|
||||
mapper: Option<Box<dyn Mapper>>,
|
||||
ram: [u8; RAM_SIZE],
|
||||
|
||||
observers: Vec<Weak<RefCell<dyn MemoryObserver>>>,
|
||||
}
|
||||
|
||||
impl MemoryBus {
|
||||
pub fn new() -> Self {
|
||||
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.
|
||||
@ -51,8 +59,11 @@ impl MemoryBus {
|
||||
let prg_addr = addr - CARTRIDGE_RAM_MAX_ADDR;
|
||||
|
||||
if self.mapper.is_none() {
|
||||
warn!("Tried to read memory address {} before setting a mapper", addr);
|
||||
return 0
|
||||
warn!(
|
||||
"Tried to read memory address {} before setting a mapper",
|
||||
addr
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mapper = self.mapper.as_ref().unwrap();
|
||||
@ -94,5 +105,29 @@ impl MemoryBus {
|
||||
// Cannot write to ROM
|
||||
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);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::memory::MemoryBus;
|
||||
use crate::memory::{MemoryBus, MemoryObserver};
|
||||
use crate::Clock;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use simplelog::info;
|
||||
|
||||
pub struct Ppu {
|
||||
memory_bus: Rc<RefCell<MemoryBus>>,
|
||||
@ -9,7 +10,17 @@ pub struct Ppu {
|
||||
|
||||
impl Ppu {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,15 +19,18 @@ const REFRESH_RATE: usize = 60; // 60 Hz
|
||||
pub struct System {
|
||||
cpu: Cpu,
|
||||
memory_bus: Rc<RefCell<MemoryBus>>,
|
||||
ppu: Ppu
|
||||
ppu: Rc<RefCell<Ppu>>
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub fn new() -> Self {
|
||||
let memory_bus = Rc::new(RefCell::new(MemoryBus::new()));
|
||||
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 }
|
||||
}
|
||||
|
||||
@ -49,7 +52,7 @@ impl Clock for System {
|
||||
self.cpu.cycle();
|
||||
|
||||
for _ppu_cycle in 0usize..PPU_CYCLE_PER_REFRESH {
|
||||
self.ppu.cycle();
|
||||
self.ppu.borrow_mut().cycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,26 @@
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use core::system::System;
|
||||
use core::Clock;
|
||||
use simplelog::*;
|
||||
use std::fs::File;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
const ROM_PATH: &'static str = "./roms/dk.nes";
|
||||
|
||||
fn main() {
|
||||
CombinedLogger::init(vec![TermLogger::new(
|
||||
LevelFilter::Debug,
|
||||
Config::default(),
|
||||
TerminalMode::Mixed,
|
||||
ColorChoice::Auto,
|
||||
)])
|
||||
CombinedLogger::init(vec![
|
||||
TermLogger::new(
|
||||
LevelFilter::Info,
|
||||
Config::default(),
|
||||
TerminalMode::Mixed,
|
||||
ColorChoice::Auto,
|
||||
),
|
||||
WriteLogger::new(
|
||||
LevelFilter::Debug,
|
||||
Config::default(),
|
||||
File::create("./logs/latest.log").unwrap(),
|
||||
),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let mut system = System::new();
|
||||
|
Loading…
Reference in New Issue
Block a user