[brw] Use cell and refcell to implement shared behaviour

This commit is contained in:
2024-02-18 22:03:10 +05:30
parent f6eca76b0c
commit 04d1d9160d
5 changed files with 68 additions and 56 deletions

View File

@@ -1,23 +1,24 @@
use std::cell::Cell;
use crate::emu::mmu::Memory; use crate::emu::mmu::Memory;
use crate::emu::program_counter::ProgramCounter; use crate::emu::program_counter::ProgramCounter;
use crate::misc::emulator_error::{DeviceType, EmulatorError}; use crate::misc::emulator_error::{DeviceType, EmulatorError};
use crate::misc::emulator_error::DeviceType::MMU; use crate::misc::emulator_error::DeviceType::MMU;
use crate::misc::endian::{read_big_endian_u16, read_big_endian_u24}; use crate::misc::endian::{read_big_endian_u16};
use crate::misc::result::EmulatorResult; use crate::misc::result::EmulatorResult;
#[derive(Debug)] #[derive(Debug)]
pub struct MemoryMappedIO<'a> { pub struct MemoryMappedIO<'a> {
//FIXME use a keyboard //FIXME use a keyboard
keyboard_bytes: [u8; 2], keyboard_bytes: Cell<[u8; 2]>,
program_counter: &'a mut ProgramCounter, program_counter: &'a ProgramCounter,
//FIXME use a device //FIXME use a device
pixel_reg: u8, pixel_reg: Cell<u8>,
//FIXME use a device //FIXME use a device
audio_sample_address_base: [u8; 2], audio_sample_address_base: Cell<[u8; 2]>,
} }
/// Represents the memory mapped segment of IO. Aggregates the mapping logic. /// Represents the memory mapped segment of IO. Aggregates the mapping logic.
impl <'a> MemoryMappedIO<'a> { impl<'a> MemoryMappedIO<'a> {
// 2 byte keyboard bits // 2 byte keyboard bits
pub const KEYBOARD_BIT_START: u32 = 0; pub const KEYBOARD_BIT_START: u32 = 0;
const KEYBOARD_BIT_END: u32 = 1; const KEYBOARD_BIT_END: u32 = 1;
@@ -35,24 +36,25 @@ impl <'a> MemoryMappedIO<'a> {
pub const AUDIO_SAMPLE_BASE_LEN: u32 = 2; pub const AUDIO_SAMPLE_BASE_LEN: u32 = 2;
const AUDIO_SAMPLE_BASE_END: u32 = Self::AUDIO_SAMPLE_BASE_START + Self::AUDIO_SAMPLE_BASE_LEN - 1; const AUDIO_SAMPLE_BASE_END: u32 = Self::AUDIO_SAMPLE_BASE_START + Self::AUDIO_SAMPLE_BASE_LEN - 1;
pub fn new(program_counter: &'a mut ProgramCounter) -> MemoryMappedIO<'a> { pub fn new(program_counter: &'a ProgramCounter) -> MemoryMappedIO<'a> {
MemoryMappedIO { MemoryMappedIO {
keyboard_bytes: [0, 0], keyboard_bytes: Cell::new([0, 0]),
program_counter, program_counter,
pixel_reg: 0, pixel_reg: Cell::new(0),
audio_sample_address_base: [0, 0], audio_sample_address_base: Cell::new([0, 0]),
} }
} }
} }
impl <'a> Memory for MemoryMappedIO<'a> { impl<'a> Memory for MemoryMappedIO<'a> {
fn try_get_byte(&self, address: u32) -> EmulatorResult<u8> { fn try_get_byte(&self, address: u32) -> EmulatorResult<u8> {
let byte = match address { let byte = match address {
Self::KEYBOARD_BIT_START..=Self::KEYBOARD_BIT_END => { Self::KEYBOARD_BIT_START..=Self::KEYBOARD_BIT_END => {
let addr_usize = address as usize; let addr_usize = address as usize;
let keyboard_byte = self.keyboard_bytes[addr_usize]; let keyboard_bytes = self.keyboard_bytes.get();
log::trace!("Fetching keyboard({}) byte segment {} -> {}",read_big_endian_u16(&self.keyboard_bytes),address,keyboard_byte); let keyboard_byte = keyboard_bytes[addr_usize];
log::trace!("Fetching keyboard({}) byte segment {} -> {}",read_big_endian_u16(&keyboard_bytes),address,keyboard_byte);
Ok(keyboard_byte) Ok(keyboard_byte)
} }
Self::PC_START_ADDR..=Self::PC_END_ADDR => { Self::PC_START_ADDR..=Self::PC_END_ADDR => {
@@ -60,36 +62,40 @@ impl <'a> Memory for MemoryMappedIO<'a> {
self.program_counter.try_get_byte(pc_index) self.program_counter.try_get_byte(pc_index)
} }
Self::PIXEL_BASE => { Self::PIXEL_BASE => {
log::trace!("Fetching pixel base reg {}",self.pixel_reg); log::trace!("Fetching pixel base reg {}",self.pixel_reg.get());
Ok(self.pixel_reg) Ok(self.pixel_reg.get())
} }
Self::AUDIO_SAMPLE_BASE_START => Ok(self.audio_sample_address_base[0]), Self::AUDIO_SAMPLE_BASE_START => Ok(self.audio_sample_address_base.get()[0]),
Self::AUDIO_SAMPLE_BASE_END => Ok(self.audio_sample_address_base[1]), Self::AUDIO_SAMPLE_BASE_END => Ok(self.audio_sample_address_base.get()[1]),
address => { address => {
Err(EmulatorError::UnreachableMemory(DeviceType::MMU, address)) Err(EmulatorError::UnreachableMemory(MMU, address))
} }
}; };
byte byte
} }
fn try_set_byte(&mut self, address: u32, byte_value: u8) -> EmulatorResult<()> { fn try_set_byte(&self, address: u32, byte_value: u8) -> EmulatorResult<()> {
match address { match address {
Self::KEYBOARD_BIT_START..=Self::KEYBOARD_BIT_END => { Self::KEYBOARD_BIT_START..=Self::KEYBOARD_BIT_END => {
let addr_usize = address as usize; let mut keyboard_bytes = self.keyboard_bytes.get();
let keyboard_byte = self.keyboard_bytes[addr_usize]; log::trace!("Setting keyboard({}) byte segment {} -> {}",read_big_endian_u16(&keyboard_bytes),address,byte_value);
log::trace!("Setting keyboard({}) byte segment {} -> {}",read_big_endian_u16(&self.keyboard_bytes),address,keyboard_byte); keyboard_bytes[address as usize] = byte_value;
self.keyboard_bytes[addr_usize] = byte_value; self.keyboard_bytes.set(keyboard_bytes);
} }
Self::PC_START_ADDR..=Self::PC_END_ADDR => { Self::PC_START_ADDR..=Self::PC_END_ADDR => {
let pc_index = address - Self::PC_START_ADDR; let pc_index = address - Self::PC_START_ADDR;
self.program_counter.try_set_byte(pc_index, byte_value)? self.program_counter.try_set_byte(pc_index, byte_value)?
} }
Self::PIXEL_BASE => { Self::PIXEL_BASE => {
log::trace!("Fetching pixel base reg {}",self.pixel_reg); log::trace!("Setting pixel base reg to {}",byte_value);
self.pixel_reg = byte_value self.pixel_reg.set(byte_value);
}
Self::AUDIO_SAMPLE_BASE_START..=Self::AUDIO_SAMPLE_BASE_END => {
let audio_reg_index = (address - Self::AUDIO_SAMPLE_BASE_START) as usize;
let mut audio_sample_address_base = self.audio_sample_address_base.get();
audio_sample_address_base[audio_reg_index] = byte_value;
self.audio_sample_address_base.set(audio_sample_address_base);
} }
Self::AUDIO_SAMPLE_BASE_START => { self.audio_sample_address_base[0] = byte_value }
Self::AUDIO_SAMPLE_BASE_END => { self.audio_sample_address_base[1] = byte_value }
_ => { return Err(EmulatorError::UnreachableMemory(MMU, address)); } _ => { return Err(EmulatorError::UnreachableMemory(MMU, address)); }
}; };
Ok(()) Ok(())

View File

@@ -17,17 +17,17 @@ pub trait Memory {
/// Get the value (24bit) at the address(24bit) /// Get the value (24bit) at the address(24bit)
fn try_get_byte(&self, address: u32) -> EmulatorResult<u8>; fn try_get_byte(&self, address: u32) -> EmulatorResult<u8>;
/// Set the value at the 24bit address /// Set the value at the 24bit address
fn try_set_byte(&mut self, address: u32, value: u8) -> EmulatorResult<()>; fn try_set_byte(&self, address: u32, value: u8) -> EmulatorResult<()>;
} }
#[derive(Debug)] #[derive(Debug)]
pub struct MappedMemory<'a> { pub struct MappedMemory<'a> {
memory_mapped_io: &'a mut MemoryMappedIO<'a>, memory_mapped_io: &'a MemoryMappedIO<'a>,
ram_memory: &'a mut RamMemory, ram_memory: &'a RamMemory,
} }
impl <'a> MappedMemory<'a> { impl <'a> MappedMemory<'a> {
pub fn new(memory_mapped_io: &'a mut MemoryMappedIO<'a>, ram_memory:&'a mut RamMemory) -> MappedMemory<'a> { pub fn new(memory_mapped_io: &'a MemoryMappedIO<'a>, ram_memory:&'a RamMemory) -> MappedMemory<'a> {
MappedMemory { MappedMemory {
memory_mapped_io, memory_mapped_io,
ram_memory, ram_memory,
@@ -50,7 +50,7 @@ impl <'a> Memory for MappedMemory<'a> {
Ok(byte_at_addr) Ok(byte_at_addr)
} }
fn try_set_byte(&mut self, address: u32, value: u8) -> EmulatorResult<()> { fn try_set_byte(&self, address: u32, value: u8) -> EmulatorResult<()> {
match address { match address {
0..=MMAPPEDIO_END => { 0..=MMAPPEDIO_END => {
self.memory_mapped_io.try_set_byte(address,value) self.memory_mapped_io.try_set_byte(address,value)

View File

@@ -1,3 +1,4 @@
use std::cell::Cell;
use crate::emu::mmu::{Memory, RAM_MEM_END}; use crate::emu::mmu::{Memory, RAM_MEM_END};
use crate::misc::emulator_error::DeviceType::PC; use crate::misc::emulator_error::DeviceType::PC;
use crate::misc::emulator_error::EmulatorError; use crate::misc::emulator_error::EmulatorError;
@@ -8,26 +9,29 @@ use crate::misc::result::EmulatorResult;
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct ProgramCounter { pub struct ProgramCounter {
/// 24bit location register /// 24bit location register
program_counter_register: [u8; 3], program_counter_register: Cell<[u8; 3]>,
} }
impl ProgramCounter { impl ProgramCounter {
const PROGRAM_COUNTER_ZERO:[u8;3] = [0;3];
pub fn new() -> ProgramCounter { pub fn new() -> ProgramCounter {
ProgramCounter { ProgramCounter {
program_counter_register: [0; 3] program_counter_register: Cell::new(Self::PROGRAM_COUNTER_ZERO)
} }
} }
// get the current program counter as an address // get the current program counter as an address
pub fn get_pc_value(&self) -> u32 { pub fn get_pc_value(&self) -> u32 {
read_big_endian_u24(&self.program_counter_register) read_big_endian_u24(&self.program_counter_register.get())
} }
// assign a value of PC to start execution // assign a value of PC to start execution
pub fn set_address(&mut self, address: u32) -> EmulatorResult<()> { pub fn set_address(&self, address: u32) -> EmulatorResult<()> {
log::debug!("Setting PC as {}",address); log::debug!("Setting PC as {}",address);
if address >= (2 << 24) { if address >= (2 << 24) {
return Err(EmulatorError::UnreachableMemory(PC, address)); return Err(EmulatorError::UnreachableMemory(PC, address));
} }
write_big_endian_u24(address, &mut self.program_counter_register); let mut data = self.program_counter_register.get();
write_big_endian_u24(address, &mut data);
self.program_counter_register.set(data);
Ok(()) Ok(())
} }
} }
@@ -35,15 +39,19 @@ impl ProgramCounter {
/// Allow Using Program counter as mapped memory /// Allow Using Program counter as mapped memory
impl Memory for ProgramCounter { impl Memory for ProgramCounter {
fn try_get_byte(&self, address: u32) -> EmulatorResult<u8> { fn try_get_byte(&self, address: u32) -> EmulatorResult<u8> {
log::trace!("Fetching PC({}) byte segment index {}",read_big_endian_u24(&self.program_counter_register),address); log::trace!("Fetching PC({}) byte segment index {}",read_big_endian_u24(&self.program_counter_register.get()),address);
self.program_counter_register.get(address as usize) self.program_counter_register.get().get(address as usize)
.map(|e| *e) .map(|e| *e)
.ok_or(EmulatorError::UnreachableMemory(PC, address)) .ok_or(EmulatorError::UnreachableMemory(PC, address))
} }
/// TODO: Set a byte of PC from the memory. /// TODO: Set a byte of PC from the memory.
fn try_set_byte(&mut self, address: u32, value: u8) -> EmulatorResult<()> { fn try_set_byte(&self, address: u32, value: u8) -> EmulatorResult<()> {
match address { match address {
0..=2 => { self.program_counter_register[address as usize] = value } 0..=2 => {
let mut data = self.program_counter_register.get();
data[address as usize] = value;
self.program_counter_register.set(data);
}
_ => { return Err(EmulatorError::UnreachableMemory(PC, address)); } _ => { return Err(EmulatorError::UnreachableMemory(PC, address)); }
} }
Ok(()) Ok(())

View File

@@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::ops::Index; use std::ops::Index;
use crate::emu::mmu::Memory; use crate::emu::mmu::Memory;
use crate::misc::emulator_error::DeviceType::RAM; use crate::misc::emulator_error::DeviceType::RAM;
@@ -9,7 +10,7 @@ const MEM_LENGTH: usize = (2 << 24) - (MAPPED_MEMORY_NOT_REQUIRED as usize);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct RamMemory { pub struct RamMemory {
data: Box<[u8; MEM_LENGTH]>, data: RefCell<Box<[u8; MEM_LENGTH]>>,
} }
impl RamMemory { impl RamMemory {
@@ -18,8 +19,9 @@ impl RamMemory {
let data = alloc_result.try_into().map_err(|err|{ let data = alloc_result.try_into().map_err(|err|{
EmulatorError::AllocationFailure(RAM, "Allocation failed") EmulatorError::AllocationFailure(RAM, "Allocation failed")
})?; })?;
let data_refcell = RefCell::new(data);
Ok(RamMemory { Ok(RamMemory {
data data: data_refcell
}) })
} }
} }
@@ -28,15 +30,17 @@ impl RamMemory {
impl Memory for RamMemory { impl Memory for RamMemory {
fn try_get_byte(&self, address: u32) -> EmulatorResult<u8> { fn try_get_byte(&self, address: u32) -> EmulatorResult<u8> {
log::trace!("Fetch RAM memory at address {}",address); log::trace!("Fetch RAM memory at address {}",address);
let x = *self.data.get(address as usize).ok_or(EmulatorError::UnreachableMemory(RAM, address))?; let mut data = self.data.borrow_mut();
let x = *data.get(address as usize).ok_or(EmulatorError::UnreachableMemory(RAM, address))?;
Ok(x) Ok(x)
} }
fn try_set_byte(&mut self, address: u32, value: u8) -> EmulatorResult<()> { fn try_set_byte(&self, address: u32, value: u8) -> EmulatorResult<()> {
if address>= MEM_LENGTH as u32 { if address>= MEM_LENGTH as u32 {
return Err(EmulatorError::UnreachableMemory(RAM, address)) return Err(EmulatorError::UnreachableMemory(RAM, address))
} }
self.data[address as usize] = value; let mut data = self.data.borrow_mut();
data[address as usize] = value;
Ok(()) Ok(())
} }
} }

View File

@@ -17,16 +17,10 @@ mod graphics;
fn main() -> EmulatorResult<()> { fn main() -> EmulatorResult<()> {
SimpleLogger::new().env().init().unwrap(); SimpleLogger::new().env().init().unwrap();
let mut program_counter = ProgramCounter::new(); let program_counter = ProgramCounter::new();
let mut mmio = MemoryMappedIO::new(&mut program_counter); let mmio = MemoryMappedIO::new(&program_counter);
let mut ram = RamMemory::try_new()?; let ram = RamMemory::try_new()?;
let mut mmu = MappedMemory::new(&mut mmio,&mut ram); let mmu = MappedMemory::new(&mmio,&ram);
// for i in 0..10 {
// log::info!("Memory at {} is {}",i,mmu.try_get_byte(i)?);
// }
mmu.try_set_byte(0x2,0x1)?;
let data = program_counter.try_get_byte(0x0)?;
log::info!("Computed data {}",data);
// let sdl_context = sdl2::init().unwrap(); // let sdl_context = sdl2::init().unwrap();