Memory observer
This commit is contained in:
parent
ea6a4e1680
commit
eb7e842a81
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
/roms
|
/roms
|
||||||
|
/logs
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,14 +19,17 @@ 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user