[???] Lots of work

Cleanup un-needed elements, simplify memory management
This commit is contained in:
2024-02-19 12:30:27 +05:30
parent 56bf1bbd4d
commit fbf38e4925
11 changed files with 227 additions and 343 deletions

View File

@@ -1,6 +1,7 @@
use crate::emu::mmu::{MappedMemory, Memory};
use crate::emu::program_counter::ProgramCounter;
use crate::misc::endian::read_big_endian_u24;
use crate::emu::graphics::GraphicsProcessor;
use crate::emu::mmu::{ Memory};
use crate::emu::ram::RamMemory;
use crate::misc::endian::{read_big_endian_u24, write_big_endian_u24};
use crate::misc::result::EmulatorResult;
#[derive(Debug)]
@@ -11,56 +12,75 @@ pub enum CpuState{
#[derive(Debug)]
pub struct Cpu<'a>{
mapped_memory:&'a MappedMemory<'a>,
program_counter: &'a ProgramCounter
pub memory: &'a RamMemory,
pub graphics_processor: &'a GraphicsProcessor<'a>
}
impl <'a> Cpu<'a>{
pub fn new(mapped_memory: &'a MappedMemory<'a>,program_counter: &'a ProgramCounter)->Cpu<'a>{
const PC_START:usize = 2;
const PC_LEN:usize = 3;
const PC_ZERO:[u8;3] = [0;3];
pub fn new(memory: &'a RamMemory, graphics_processor: &'a GraphicsProcessor<'a>) ->Cpu<'a>{
Cpu{
mapped_memory,
program_counter
graphics_processor,
memory
}
}
pub fn get_pc(&self)->u32{
let memory_slice = self.memory.data.borrow();
let data = memory_slice.get(Self::PC_START..(Self::PC_START+Self::PC_LEN)).unwrap();
read_big_endian_u24(data.try_into().unwrap())
}
pub fn set_pc(&self,address:u32){
let mut memory_slice = self.memory.data.borrow_mut();
let mut pc_big_endian_slice = Self::PC_ZERO;
write_big_endian_u24(address,&mut pc_big_endian_slice);
memory_slice[Self::PC_START..(Self::PC_START + Self::PC_LEN)].copy_from_slice(&pc_big_endian_slice);
}
pub fn cycle(&self)->EmulatorResult<()>{
for _i in 0..65536{
let address_to_execute = self.program_counter.get_pc_value();
let address_to_execute = self.get_pc();
//fetch
let num1 = self.fetch_triplet(address_to_execute)?;
self.set_triplet(address_to_execute+3,&num1)?;
let new_pc = read_big_endian_u24(&self.fetch_triplet(address_to_execute+6)?);
self.program_counter.set_address(new_pc)?;
self.copy_u24(address_to_execute)?;
let new_pc_location = address_to_execute+2*(Self::PC_LEN as u32) ;
let new_pc = self.memory.try_get_u24(new_pc_location)?;
self.set_pc(new_pc);
}
// TODO send graphics
log::debug!("Finished internal loop");
self.graphics_processor.draw()?;
// TODO send audio
Ok(())
}
fn copy_u24(&self, address_to_execute: u32) -> EmulatorResult<()> {
let aloc = self.memory.try_get_u24(address_to_execute)?;
let bloc = self.memory.try_get_u24(address_to_execute+Self::PC_LEN as u32)?;
self.memory.try_set_byte(bloc,self.memory.try_get_byte(aloc)?)
}
fn fetch_triplet(&self, address: u32) -> EmulatorResult<[u8;3]> {
let first_byte = self.mapped_memory.try_get_byte(address)?;
let second_byte = self.mapped_memory.try_get_byte(address+1)?;
let third_byte = self.mapped_memory.try_get_byte(address+2)?;
let first_byte = self.memory.try_get_byte(address)?;
let second_byte = self.memory.try_get_byte(address+1)?;
let third_byte = self.memory.try_get_byte(address+2)?;
let num = [first_byte,second_byte,third_byte];
Ok(num)
}
fn set_triplet(&self, address: u32, val:&[u8;3]) -> EmulatorResult<()> {
self.mapped_memory.try_set_byte(address, val[0])?;
self.mapped_memory.try_set_byte(address + 1, val[1])?;
self.mapped_memory.try_set_byte(address + 2, val[2])?;
self.memory.try_set_byte(address, val[0])?;
self.memory.try_set_byte(address + 1, val[1])?;
self.memory.try_set_byte(address + 2, val[2])?;
Ok(())
}
fn set_pc(&self,value: &[u8;3])->EmulatorResult<()>{
let x = read_big_endian_u24(value);
self.program_counter.set_address(x)
}
}
#[cfg(test)]
mod test{

View File

@@ -1,5 +1,6 @@
use std::cell::RefCell;
use crate::graphics::graphics_adapter::GraphicsAdapter;
use std::cell::{Ref, RefCell};
use crate::emu::mmu::Memory;
use crate::emu::ram::RamMemory;
use crate::misc::emulator_error::DeviceType::GRAPHICS;
use crate::misc::emulator_error::EmulatorError;
use crate::misc::result::EmulatorResult;
@@ -7,34 +8,45 @@ use crate::misc::result::EmulatorResult;
pub const DEVICE_FRAMEBUFFER_SIZE: usize = 256 * 256;
#[derive(Debug)]
pub struct GraphicsProcessor {
pub struct GraphicsProcessor<'a> {
ram: &'a RamMemory,
frame_buffer: RefCell<Box<[u8; DEVICE_FRAMEBUFFER_SIZE]>>,
graphics_adapter: Box<dyn GraphicsAdapter>
}
/// Abstracted graphics processor. Calls `[GraphicsAdapter]`
impl GraphicsProcessor {
pub fn try_new(graphics_adapter: Box<dyn GraphicsAdapter>) -> EmulatorResult<GraphicsProcessor> {
/// Abstracted graphics processor. Calls `[GraphicsAdapter]` with the associated framebuffer
impl <'a> GraphicsProcessor<'a> {
pub fn try_new(ram_memory: &'a RamMemory) -> EmulatorResult<GraphicsProcessor> {
let framebuffer = vec![0; DEVICE_FRAMEBUFFER_SIZE].into_boxed_slice()
.try_into()
.map_err(|_| {
EmulatorError::AllocationFailure(GRAPHICS, "Failed to allocate graphics")
})?;
Ok(GraphicsProcessor {
ram: ram_memory,
frame_buffer: RefCell::new(framebuffer),
graphics_adapter
})
}
/// take a copy of FB and
pub fn draw(&self, memory_slice: &[u8;DEVICE_FRAMEBUFFER_SIZE])->EmulatorResult<()>{
self.set_framebuffer(memory_slice);
pub fn draw(&self)->EmulatorResult<()>{
self.copy_indicated_pixel_data_block()?;
let fb_immut = self.frame_buffer.borrow();
self.graphics_adapter.draw(fb_immut)
Ok(())
}
fn copy_indicated_pixel_data_block(&self) -> Result<(), EmulatorError> {
let fb_base_register = (self.ram.try_get_byte(5)? as u32) << 16;
let mut fb = self.frame_buffer.borrow_mut();
self.ram.get_block(fb_base_register, fb.as_mut())?;
Ok(())
}
fn set_framebuffer(&self, memory_slice: &[u8;DEVICE_FRAMEBUFFER_SIZE]) {
let mut fb = self.frame_buffer.borrow_mut();
fb.copy_from_slice(memory_slice);
}
pub fn get_framebuffer(&self)->Ref<Box<[u8; DEVICE_FRAMEBUFFER_SIZE]>>{
self.frame_buffer.borrow()
}
}
#[cfg(test)]

View File

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

View File

@@ -1,17 +1,5 @@
use crate::emu::iomem::MemoryMappedIO;
use crate::emu::ram::RamMemory;
use crate::misc::emulator_error::DeviceType::MMU;
use crate::misc::emulator_error::EmulatorError::UnreachableMemory;
use crate::misc::result::EmulatorResult;
// 8 bytes that are memory mapped i/o
pub const MMAPPEDIO_END: u32 = RAM_MEM_START - 1;
pub const RAM_MEM_START: u32 = 8;
/// Last index of ram
pub const RAM_MEM_END: u32 = 2 << 24 - 1;
///
/// mapped I/O + RAM.
pub trait Memory {
/// Get the value (24bit) at the address(24bit)
@@ -20,47 +8,4 @@ pub trait Memory {
fn try_set_byte(&self, address: u32, value: u8) -> EmulatorResult<()>;
}
#[derive(Debug)]
pub struct MappedMemory<'a> {
memory_mapped_io: &'a MemoryMappedIO<'a>,
ram_memory: &'a RamMemory,
}
impl <'a> MappedMemory<'a> {
pub fn new(memory_mapped_io: &'a MemoryMappedIO<'a>, ram_memory:&'a RamMemory) -> MappedMemory<'a> {
MappedMemory {
memory_mapped_io,
ram_memory,
}
}
}
impl <'a> Memory for MappedMemory<'a> {
fn try_get_byte(&self, address: u32) -> EmulatorResult<u8> {
let byte_at_addr = match address {
0..=MMAPPEDIO_END => {
self.memory_mapped_io.try_get_byte(address)
}
RAM_MEM_START..=RAM_MEM_END => {
let memory_index = address - RAM_MEM_START;
self.ram_memory.try_get_byte(memory_index)
}
_ => { Err(UnreachableMemory(MMU, address)) }
}?;
Ok(byte_at_addr)
}
fn try_set_byte(&self, address: u32, value: u8) -> EmulatorResult<()> {
match address {
0..=MMAPPEDIO_END => {
self.memory_mapped_io.try_set_byte(address,value)
}
RAM_MEM_START..=RAM_MEM_END => {
let memory_index = address - RAM_MEM_START;
self.ram_memory.try_set_byte(memory_index,value)
}
_ => { Err(UnreachableMemory(MMU, address)) }
}
}
}

View File

@@ -1,6 +1,4 @@
pub mod cpu;
pub mod mmu;
pub mod ram;
pub mod iomem;
pub mod program_counter;
pub mod graphics;

View File

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

View File

@@ -3,14 +3,15 @@ use std::cell::RefCell;
use crate::emu::mmu::Memory;
use crate::misc::emulator_error::DeviceType::RAM;
use crate::misc::emulator_error::EmulatorError;
use crate::misc::endian::read_big_endian_u24;
use crate::misc::result::EmulatorResult;
const MAPPED_MEMORY_NOT_REQUIRED: u8 = 8;
const MEM_LENGTH: usize = (2 << 24) - (MAPPED_MEMORY_NOT_REQUIRED as usize);
pub const MEM_LENGTH: usize = 2 << 23;
#[derive(Clone, Debug)]
pub struct RamMemory {
data: RefCell<Box<[u8; MEM_LENGTH]>>,
pub data: RefCell<Box<[u8; MEM_LENGTH]>>,
}
impl RamMemory {
@@ -24,6 +25,34 @@ impl RamMemory {
data: data_refcell
})
}
pub fn try_from(existing_data:&[u8]) -> EmulatorResult<RamMemory>{
let alloc_result = vec![0u8; MEM_LENGTH].into_boxed_slice();
// get box of fixed size
let mut fixed_size_alloc_box:Box<[u8;MEM_LENGTH]> = alloc_result.try_into().unwrap();
fixed_size_alloc_box.copy_from_slice(&existing_data);
Ok(RamMemory{
data: RefCell::new(fixed_size_alloc_box)
})
}
pub fn try_get_u24(&self,index:u32)->EmulatorResult<u32>{
const U24_LEN:usize = 3;
let index_usize = index as usize;
let data = self.data.borrow();
let a = data.get(index_usize..(index_usize +U24_LEN)).ok_or(EmulatorError::UnreachableMemory(RAM, index))?;
Ok(read_big_endian_u24(a.try_into()?))
}
pub fn get_block(&self,address:u32,output:&mut [u8])->EmulatorResult<()>{
let address = address as usize;
let data = self.data.borrow();
let data_requested_slice = data.get(address..address+output.len()).ok_or(EmulatorError::UnreachableMemory(RAM,address as u32))?;
output.copy_from_slice(data_requested_slice);
Ok(())
}
}