[???] Lots of work
Cleanup un-needed elements, simplify memory management
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
use crate::emu::mmu::{MappedMemory, Memory};
|
use crate::emu::graphics::GraphicsProcessor;
|
||||||
use crate::emu::program_counter::ProgramCounter;
|
use crate::emu::mmu::{ Memory};
|
||||||
use crate::misc::endian::read_big_endian_u24;
|
use crate::emu::ram::RamMemory;
|
||||||
|
use crate::misc::endian::{read_big_endian_u24, write_big_endian_u24};
|
||||||
use crate::misc::result::EmulatorResult;
|
use crate::misc::result::EmulatorResult;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -11,56 +12,75 @@ pub enum CpuState{
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Cpu<'a>{
|
pub struct Cpu<'a>{
|
||||||
mapped_memory:&'a MappedMemory<'a>,
|
pub memory: &'a RamMemory,
|
||||||
program_counter: &'a ProgramCounter
|
pub graphics_processor: &'a GraphicsProcessor<'a>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> Cpu<'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{
|
Cpu{
|
||||||
mapped_memory,
|
graphics_processor,
|
||||||
program_counter
|
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<()>{
|
pub fn cycle(&self)->EmulatorResult<()>{
|
||||||
for _i in 0..65536{
|
for _i in 0..65536{
|
||||||
let address_to_execute = self.program_counter.get_pc_value();
|
let address_to_execute = self.get_pc();
|
||||||
|
|
||||||
//fetch
|
//fetch
|
||||||
let num1 = self.fetch_triplet(address_to_execute)?;
|
self.copy_u24(address_to_execute)?;
|
||||||
self.set_triplet(address_to_execute+3,&num1)?;
|
let new_pc_location = address_to_execute+2*(Self::PC_LEN as u32) ;
|
||||||
|
let new_pc = self.memory.try_get_u24(new_pc_location)?;
|
||||||
let new_pc = read_big_endian_u24(&self.fetch_triplet(address_to_execute+6)?);
|
self.set_pc(new_pc);
|
||||||
self.program_counter.set_address(new_pc)?;
|
|
||||||
}
|
}
|
||||||
// TODO send graphics
|
log::debug!("Finished internal loop");
|
||||||
|
self.graphics_processor.draw()?;
|
||||||
// TODO send audio
|
// TODO send audio
|
||||||
Ok(())
|
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]> {
|
fn fetch_triplet(&self, address: u32) -> EmulatorResult<[u8;3]> {
|
||||||
let first_byte = self.mapped_memory.try_get_byte(address)?;
|
let first_byte = self.memory.try_get_byte(address)?;
|
||||||
let second_byte = self.mapped_memory.try_get_byte(address+1)?;
|
let second_byte = self.memory.try_get_byte(address+1)?;
|
||||||
let third_byte = self.mapped_memory.try_get_byte(address+2)?;
|
let third_byte = self.memory.try_get_byte(address+2)?;
|
||||||
let num = [first_byte,second_byte,third_byte];
|
let num = [first_byte,second_byte,third_byte];
|
||||||
Ok(num)
|
Ok(num)
|
||||||
}
|
}
|
||||||
fn set_triplet(&self, address: u32, val:&[u8;3]) -> EmulatorResult<()> {
|
fn set_triplet(&self, address: u32, val:&[u8;3]) -> EmulatorResult<()> {
|
||||||
self.mapped_memory.try_set_byte(address, val[0])?;
|
self.memory.try_set_byte(address, val[0])?;
|
||||||
self.mapped_memory.try_set_byte(address + 1, val[1])?;
|
self.memory.try_set_byte(address + 1, val[1])?;
|
||||||
self.mapped_memory.try_set_byte(address + 2, val[2])?;
|
self.memory.try_set_byte(address + 2, val[2])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn set_pc(&self,value: &[u8;3])->EmulatorResult<()>{
|
|
||||||
let x = read_big_endian_u24(value);
|
|
||||||
self.program_counter.set_address(x)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test{
|
mod test{
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::{Ref, RefCell};
|
||||||
use crate::graphics::graphics_adapter::GraphicsAdapter;
|
use crate::emu::mmu::Memory;
|
||||||
|
use crate::emu::ram::RamMemory;
|
||||||
use crate::misc::emulator_error::DeviceType::GRAPHICS;
|
use crate::misc::emulator_error::DeviceType::GRAPHICS;
|
||||||
use crate::misc::emulator_error::EmulatorError;
|
use crate::misc::emulator_error::EmulatorError;
|
||||||
use crate::misc::result::EmulatorResult;
|
use crate::misc::result::EmulatorResult;
|
||||||
@@ -7,34 +8,45 @@ use crate::misc::result::EmulatorResult;
|
|||||||
pub const DEVICE_FRAMEBUFFER_SIZE: usize = 256 * 256;
|
pub const DEVICE_FRAMEBUFFER_SIZE: usize = 256 * 256;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GraphicsProcessor {
|
pub struct GraphicsProcessor<'a> {
|
||||||
|
ram: &'a RamMemory,
|
||||||
frame_buffer: RefCell<Box<[u8; DEVICE_FRAMEBUFFER_SIZE]>>,
|
frame_buffer: RefCell<Box<[u8; DEVICE_FRAMEBUFFER_SIZE]>>,
|
||||||
graphics_adapter: Box<dyn GraphicsAdapter>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Abstracted graphics processor. Calls `[GraphicsAdapter]`
|
/// Abstracted graphics processor. Calls `[GraphicsAdapter]` with the associated framebuffer
|
||||||
impl GraphicsProcessor {
|
impl <'a> GraphicsProcessor<'a> {
|
||||||
pub fn try_new(graphics_adapter: Box<dyn GraphicsAdapter>) -> EmulatorResult<GraphicsProcessor> {
|
pub fn try_new(ram_memory: &'a RamMemory) -> EmulatorResult<GraphicsProcessor> {
|
||||||
let framebuffer = vec![0; DEVICE_FRAMEBUFFER_SIZE].into_boxed_slice()
|
let framebuffer = vec![0; DEVICE_FRAMEBUFFER_SIZE].into_boxed_slice()
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EmulatorError::AllocationFailure(GRAPHICS, "Failed to allocate graphics")
|
EmulatorError::AllocationFailure(GRAPHICS, "Failed to allocate graphics")
|
||||||
})?;
|
})?;
|
||||||
Ok(GraphicsProcessor {
|
Ok(GraphicsProcessor {
|
||||||
|
ram: ram_memory,
|
||||||
frame_buffer: RefCell::new(framebuffer),
|
frame_buffer: RefCell::new(framebuffer),
|
||||||
graphics_adapter
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// take a copy of FB and
|
/// take a copy of FB and
|
||||||
pub fn draw(&self, memory_slice: &[u8;DEVICE_FRAMEBUFFER_SIZE])->EmulatorResult<()>{
|
pub fn draw(&self)->EmulatorResult<()>{
|
||||||
self.set_framebuffer(memory_slice);
|
self.copy_indicated_pixel_data_block()?;
|
||||||
let fb_immut = self.frame_buffer.borrow();
|
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]) {
|
fn set_framebuffer(&self, memory_slice: &[u8;DEVICE_FRAMEBUFFER_SIZE]) {
|
||||||
let mut fb = self.frame_buffer.borrow_mut();
|
let mut fb = self.frame_buffer.borrow_mut();
|
||||||
fb.copy_from_slice(memory_slice);
|
fb.copy_from_slice(memory_slice);
|
||||||
}
|
}
|
||||||
|
pub fn get_framebuffer(&self)->Ref<Box<[u8; DEVICE_FRAMEBUFFER_SIZE]>>{
|
||||||
|
self.frame_buffer.borrow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
103
src/emu/iomem.rs
103
src/emu/iomem.rs
@@ -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(())
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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;
|
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.
|
/// mapped I/O + RAM.
|
||||||
pub trait Memory {
|
pub trait Memory {
|
||||||
/// Get the value (24bit) at the address(24bit)
|
/// 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<()>;
|
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)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod mmu;
|
pub mod mmu;
|
||||||
pub mod ram;
|
pub mod ram;
|
||||||
pub mod iomem;
|
|
||||||
pub mod program_counter;
|
|
||||||
pub mod graphics;
|
pub mod graphics;
|
@@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,14 +3,15 @@ use std::cell::RefCell;
|
|||||||
use crate::emu::mmu::Memory;
|
use crate::emu::mmu::Memory;
|
||||||
use crate::misc::emulator_error::DeviceType::RAM;
|
use crate::misc::emulator_error::DeviceType::RAM;
|
||||||
use crate::misc::emulator_error::EmulatorError;
|
use crate::misc::emulator_error::EmulatorError;
|
||||||
|
use crate::misc::endian::read_big_endian_u24;
|
||||||
use crate::misc::result::EmulatorResult;
|
use crate::misc::result::EmulatorResult;
|
||||||
|
|
||||||
const MAPPED_MEMORY_NOT_REQUIRED: u8 = 8;
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RamMemory {
|
pub struct RamMemory {
|
||||||
data: RefCell<Box<[u8; MEM_LENGTH]>>,
|
pub data: RefCell<Box<[u8; MEM_LENGTH]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RamMemory {
|
impl RamMemory {
|
||||||
@@ -24,6 +25,34 @@ impl RamMemory {
|
|||||||
data: data_refcell
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
use crate::misc::emulator_error::EmulatorError;
|
use crate::misc::emulator_error::EmulatorError;
|
||||||
use crate::misc::result::EmulatorResult;
|
use crate::misc::result::EmulatorResult;
|
||||||
|
|
||||||
|
/// A Color as represented in BytePusher.
|
||||||
|
/// It is a byte that contains a packed 6 digit red, green and blue
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Color(u8);
|
pub struct Color(u8);
|
||||||
|
|
||||||
@@ -26,16 +28,24 @@ impl Color {
|
|||||||
}
|
}
|
||||||
Ok(Color(in_mem_color))
|
Ok(Color(in_mem_color))
|
||||||
}
|
}
|
||||||
|
/// wrap to black if needed
|
||||||
|
pub fn new(in_mem_color: u8)->Color{
|
||||||
|
if in_mem_color > Self::COLOR_MAX {
|
||||||
|
Color(0)
|
||||||
|
}else{
|
||||||
|
Color(in_mem_color)
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn get_mem_byte(&self) -> u8 {
|
pub fn get_mem_byte(&self) -> u8 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
/// This fetches the rgb triplet
|
/// This fetches the rgb triplet
|
||||||
pub fn get_rgb(self) -> [u8; 3] {
|
pub fn get_rgb(self) -> (u8,u8,u8) {
|
||||||
let r = self.0 / Self::RED_MULT;
|
let r = self.0 / Self::RED_MULT;
|
||||||
let gb_byte_remainder = self.0 % Self::RED_MULT;
|
let gb_byte_remainder = self.0 % Self::RED_MULT;
|
||||||
let g = gb_byte_remainder / Self::GREEN_MULT;
|
let g = gb_byte_remainder / Self::GREEN_MULT;
|
||||||
let b = gb_byte_remainder % Self::GREEN_MULT;
|
let b = gb_byte_remainder % Self::GREEN_MULT;
|
||||||
[r * Self::COLOR_FACTOR_8_BIT, g * Self::COLOR_FACTOR_8_BIT, b * Self::COLOR_FACTOR_8_BIT]
|
(r * Self::COLOR_FACTOR_8_BIT, g * Self::COLOR_FACTOR_8_BIT, b * Self::COLOR_FACTOR_8_BIT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +57,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
pub fn test_from_mem_zero() {
|
pub fn test_from_mem_zero() {
|
||||||
let color = Color::try_new(0).unwrap();
|
let color = Color::try_new(0).unwrap();
|
||||||
assert_eq!([0u8; 3], color.get_rgb())
|
assert_eq!((0,0,0), color.get_rgb())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -59,7 +69,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
pub fn test_from_mem_max() {
|
pub fn test_from_mem_max() {
|
||||||
let color = Color::try_new(Color::COLOR_MAX).unwrap();
|
let color = Color::try_new(Color::COLOR_MAX).unwrap();
|
||||||
assert_eq!([255u8; 3], color.get_rgb())
|
assert_eq!((255,255,255), color.get_rgb())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@@ -1,18 +1,53 @@
|
|||||||
use std::cell::Ref;
|
use std::cell::Ref;
|
||||||
use std::fmt::Debug;
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use sdl2::render::{Canvas, WindowCanvas};
|
||||||
|
use crate::emu::graphics::{DEVICE_FRAMEBUFFER_SIZE, GraphicsProcessor};
|
||||||
|
use crate::graphics::color::Color;
|
||||||
|
use crate::misc::emulator_error::EmulatorError;
|
||||||
use crate::misc::result::EmulatorResult;
|
use crate::misc::result::EmulatorResult;
|
||||||
|
|
||||||
pub trait GraphicsAdapter: Debug {
|
|
||||||
fn draw(&self, frame_buf: Ref<Box<[u8; 65536]>>) -> EmulatorResult<()>;
|
#[derive(Clone)]
|
||||||
|
pub struct SDLGraphicsAdapter<'a> {
|
||||||
|
pub graphics_processor: &'a GraphicsProcessor<'a>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl <'a> Debug for SDLGraphicsAdapter<'a> {
|
||||||
pub struct SDLGraphicsAdapter {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
}
|
f.write_str("SDL2 adapter")
|
||||||
|
}
|
||||||
impl GraphicsAdapter for SDLGraphicsAdapter {
|
}
|
||||||
fn draw(&self, frame_buffer: Ref<Box<[u8; 65536]>>) -> EmulatorResult<()> {
|
|
||||||
todo!()
|
impl <'a> SDLGraphicsAdapter<'a> {
|
||||||
|
pub fn new(graphics_processor: &'a GraphicsProcessor)->SDLGraphicsAdapter<'a>{
|
||||||
|
SDLGraphicsAdapter{
|
||||||
|
graphics_processor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn draw(&self, canvas:&mut WindowCanvas) -> EmulatorResult<()> {
|
||||||
|
let mut canvas_ref = &canvas;
|
||||||
|
let fb = self.graphics_processor.get_framebuffer();
|
||||||
|
for i in 0..fb.len() {
|
||||||
|
let i = i as u32;
|
||||||
|
let y_coord = i&0xff00;
|
||||||
|
let x_coord = i&0x00ff;
|
||||||
|
|
||||||
|
}
|
||||||
|
let xyc = fb.iter().enumerate().map(|(i,e)| {
|
||||||
|
let i = i as u32;
|
||||||
|
let y_coord = i&0xff00;
|
||||||
|
let x_coord = i&0x00ff;
|
||||||
|
let color = Color::new(*e);
|
||||||
|
(x_coord,y_coord,color)
|
||||||
|
});
|
||||||
|
for (x,y,c) in xyc{
|
||||||
|
canvas.set_draw_color(c.get_rgb());
|
||||||
|
let coordinates = (x as i32,y as i32);
|
||||||
|
let draw_result = canvas.draw_point(coordinates).map_err(|str|EmulatorError::OtherError(str));
|
||||||
|
|
||||||
|
draw_result?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
103
src/main.rs
103
src/main.rs
@@ -1,13 +1,17 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use sdl2::event::Event;
|
use sdl2::event::Event;
|
||||||
|
use sdl2::EventPump;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::pixels::Color;
|
use sdl2::pixels::Color;
|
||||||
|
use sdl2::render::WindowCanvas;
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
use crate::emu::cpu::Cpu;
|
use crate::emu::cpu::Cpu;
|
||||||
use crate::emu::iomem::MemoryMappedIO;
|
use crate::emu::graphics::GraphicsProcessor;
|
||||||
use crate::emu::ram::RamMemory;
|
use crate::emu::ram::{MEM_LENGTH, RamMemory};
|
||||||
use crate::emu::mmu::{Memory, MappedMemory};
|
use crate::emu::mmu::{Memory};
|
||||||
use crate::emu::program_counter::ProgramCounter;
|
use crate::graphics::graphics_adapter::SDLGraphicsAdapter;
|
||||||
|
|
||||||
use crate::misc::result::EmulatorResult;
|
use crate::misc::result::EmulatorResult;
|
||||||
|
|
||||||
@@ -17,51 +21,60 @@ mod misc;
|
|||||||
mod graphics;
|
mod graphics;
|
||||||
|
|
||||||
fn main() -> EmulatorResult<()> {
|
fn main() -> EmulatorResult<()> {
|
||||||
|
let mut fileLoc = File::open("rom.BytePusher").unwrap();
|
||||||
|
let mut filebytes = vec![0u8; MEM_LENGTH];
|
||||||
|
let x = fileLoc.read(&mut filebytes).unwrap();
|
||||||
|
assert!(x < MEM_LENGTH);
|
||||||
SimpleLogger::new().env().init().unwrap();
|
SimpleLogger::new().env().init().unwrap();
|
||||||
let program_counter = ProgramCounter::new();
|
let ram = RamMemory::try_from(filebytes.as_slice())?;
|
||||||
let mmio = MemoryMappedIO::new(&program_counter);
|
|
||||||
let ram = RamMemory::try_new()?;
|
|
||||||
let mmu = MappedMemory::new(&mmio,&ram);
|
|
||||||
|
|
||||||
let mut cpu = Cpu::new(&mmu,&program_counter);
|
let (mut canvas,mut event_pump) = initiate_sdl();
|
||||||
for i in 0..1{
|
|
||||||
cpu.cycle()?;
|
let graphics_processor = GraphicsProcessor::try_new(&ram)?;
|
||||||
|
let sdl2_graphics_adapter = SDLGraphicsAdapter::new(&graphics_processor);
|
||||||
|
let cpu = Cpu::new(&ram,&graphics_processor);
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
'running: loop {
|
||||||
|
i = (i + 1) % 255;
|
||||||
|
canvas.set_draw_color(Color::RGB(0,0,0));
|
||||||
|
canvas.clear();
|
||||||
|
for event in event_pump.poll_iter() {
|
||||||
|
match event {
|
||||||
|
Event::Quit {..} |
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
|
||||||
|
break 'running
|
||||||
|
},
|
||||||
|
event => {
|
||||||
|
log::info!("Received window event {:?}",event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// let sdl_context = sdl2::init().unwrap();
|
}
|
||||||
// let video_subsystem = sdl_context.video().unwrap();
|
|
||||||
//
|
cpu.cycle()?;
|
||||||
// let window = video_subsystem.window("rust-sdl2 demo", 512, 512)
|
// The rest of the game loop goes here...
|
||||||
// .position_centered()
|
sdl2_graphics_adapter.draw(&mut canvas)?;
|
||||||
// .build()
|
canvas.present();
|
||||||
// .unwrap();
|
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
||||||
// let mut canvas = window.into_canvas().build().unwrap();
|
}
|
||||||
//
|
|
||||||
// canvas.set_draw_color(Color::RGB(0, 255, 255));
|
|
||||||
// canvas.clear();
|
|
||||||
// canvas.present();
|
|
||||||
// let mut event_pump = sdl_context.event_pump().unwrap();
|
|
||||||
// let mut i = 0;
|
|
||||||
// 'running: loop {
|
|
||||||
// i = (i + 1) % 255;
|
|
||||||
// canvas.set_draw_color(Color::RGB(i, 64, 255 - i));
|
|
||||||
// canvas.clear();
|
|
||||||
// for event in event_pump.poll_iter() {
|
|
||||||
// match event {
|
|
||||||
// Event::Quit {..} |
|
|
||||||
// Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
|
|
||||||
// break 'running
|
|
||||||
// },
|
|
||||||
// event => {
|
|
||||||
// log::info!("Received window event {:?}",event);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // The rest of the game loop goes here...
|
|
||||||
//
|
|
||||||
// canvas.present();
|
|
||||||
// ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
|
||||||
// }
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn initiate_sdl() -> (WindowCanvas, EventPump) {
|
||||||
|
let sdl_context = sdl2::init().unwrap();
|
||||||
|
let video_subsystem = sdl_context.video().unwrap();
|
||||||
|
|
||||||
|
let window = video_subsystem.window("rust-sdl2 demo", 512, 512)
|
||||||
|
.position_centered()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let mut canvas = window.into_canvas().build().unwrap();
|
||||||
|
|
||||||
|
canvas.set_draw_color(Color::RGB(0, 255, 255));
|
||||||
|
canvas.clear();
|
||||||
|
canvas.present();
|
||||||
|
let mut event_pump = sdl_context.event_pump().unwrap();
|
||||||
|
(canvas, event_pump)
|
||||||
|
}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
use std::array::TryFromSliceError;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@@ -16,10 +17,16 @@ pub enum DeviceType {
|
|||||||
pub enum EmulatorError {
|
pub enum EmulatorError {
|
||||||
AllocationFailure(DeviceType, &'static str),
|
AllocationFailure(DeviceType, &'static str),
|
||||||
UnreachableMemory(DeviceType, u32),
|
UnreachableMemory(DeviceType, u32),
|
||||||
InvalidColor(u8)
|
InvalidColor(u8),
|
||||||
|
OtherError(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TryFromSliceError> for EmulatorError{
|
||||||
|
fn from(value: TryFromSliceError) -> Self {
|
||||||
|
EmulatorError::UnreachableMemory(DeviceType::RAM,0x5a5a)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user