use crate::mappers::Mapper; use crate::{MemoryBus, Observable, Observer}; use simplelog::warn; use std::cell::RefCell; use std::rc::Weak; const MEMORY_MAX_ADDR: u16 = 0xFFFF; const PPU_MAX_ADDR: u16 = 0x4000; const APU_MAX_ADDR: u16 = 0x4020; const UNMAPPED_MAX_ADDR: u16 = 0x6000; const CARTRIDGE_RAM_MAX_ADDR: u16 = 0x8000; const RAM_SIZE: usize = 0x0800; const RAM_MAX_ADDR: u16 = 0x2000; pub struct CpuMemoryBus { mapper: Option>, ram: [u8; RAM_SIZE], observers: Vec>>>, } impl CpuMemoryBus { pub fn new() -> Self { let ram = [0; RAM_SIZE]; Self { mapper: None, ram, observers: vec![], } } /// Register a mapper in the memory bus, simulates plugging in a cartridge. pub fn register_mapper(&mut self, mapper: Box) { self.mapper = Some(mapper); } /// Gets a word from memory. /// /// # Arguments /// * `addr` - The address of the word pub fn get_word(&self, addr: u16) -> u16 { let low = self.read_byte(addr) as u16; let high = self.read_byte(addr + 1) as u16; (high << 8) | low } fn notify_observers(&self, addr: u16, value: u8) { for observer in self.observers.iter() { match observer.upgrade() { None => {} Some(rc) => rc.borrow_mut().notify_updated(addr, value), } } } } impl MemoryBus for CpuMemoryBus { fn read_byte(&self, addr: u16) -> u8 { assert!(addr < MEMORY_MAX_ADDR); if addr < RAM_MAX_ADDR { let ram_addr = (addr as usize) % RAM_SIZE; self.ram[ram_addr] } else if addr < PPU_MAX_ADDR { let relative_addr = addr - RAM_MAX_ADDR; let ppu_register = relative_addr % 8; // todo!() 0 } else if addr < APU_MAX_ADDR { // todo!() 0 } else if addr < UNMAPPED_MAX_ADDR { assert!(false); 0 } else if addr < CARTRIDGE_RAM_MAX_ADDR { todo!() } else { let prg_addr = addr - CARTRIDGE_RAM_MAX_ADDR; if self.mapper.is_none() { warn!( "Tried to read CPU memory address {} before setting a mapper", addr ); return 0; } let mapper = self.mapper.as_ref().unwrap(); mapper.get_prg_byte(prg_addr) } } fn write_byte(&mut self, addr: u16, value: u8) { assert!(addr < MEMORY_MAX_ADDR); if addr < RAM_MAX_ADDR { let ram_addr = (addr as usize) % RAM_SIZE; self.ram[ram_addr] = value; } else if addr < PPU_MAX_ADDR { let relative_addr = addr - RAM_MAX_ADDR; let ppu_register = relative_addr % 8; // todo!() } else if addr < APU_MAX_ADDR { } else if addr < UNMAPPED_MAX_ADDR { assert!(false); } else if addr < CARTRIDGE_RAM_MAX_ADDR { assert!(false); } else { // Cannot write to ROM assert!(false); } self.notify_observers(addr, value); } } impl Observable for CpuMemoryBus { fn register_observer(&mut self, observer: Weak>>) { self.observers.push(observer); } }