rust8 0.2.1

A Chip-8 Emulator Library in Rust
Documentation
use thiserror::Error;

use super::constants::{INSTRUCTION_SIZE, MEMORY_SIZE, UNPROTECTED_MEMORY_START};
use super::graphic::BitSlicePixelView;
use super::instruction::Instruction;
use crate::chip8::instruction::parser::InstructionParsingError;

pub struct Memory {
    pub raw_data: [u8; MEMORY_SIZE],
}

#[derive(Error, Debug)]
pub enum ReadInstructionError {
    #[error("instruction parsing error")]
    ParseError(#[from] InstructionParsingError),
    #[error("instruction address in protected memory area")]
    AddressInProtectedMemoryArea(usize),
    #[error("instruction address outside of the memory")]
    AddressOutOfRange(usize),
}

#[derive(Error, Debug)]
pub enum WriteError {
    #[error("address range is inside of protected memory area")]
    InProtectedMemoryArea(usize, usize),
    #[error("address range is outside of the memory")]
    OutOfRange(usize, usize),
}

impl Default for Memory {
    fn default() -> Self {
        Memory {
            raw_data: [0; MEMORY_SIZE],
        }
    }
}

impl Memory {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn clear(&mut self) {
        self.raw_data.fill(0);
    }

    pub fn write_unrestricted(
        &mut self,
        data: &[u8],
        write_address: usize,
    ) -> Result<(), WriteError> {
        self.raw_data
            .get_mut(write_address..write_address + data.len())
            .ok_or(WriteError::OutOfRange(
                write_address,
                write_address + data.len(),
            ))?
            .copy_from_slice(data);

        Ok(())
    }

    pub fn write_restricted(
        &mut self,
        data: &[u8],
        write_address: usize,
    ) -> Result<(), WriteError> {
        if write_address < UNPROTECTED_MEMORY_START {
            return Err(WriteError::InProtectedMemoryArea(
                write_address,
                write_address + data.len(),
            ));
        }

        self.write_unrestricted(data, write_address)
    }

    pub fn read_instruction(&self, address: usize) -> Result<Instruction, ReadInstructionError> {
        if address < UNPROTECTED_MEMORY_START {
            return Err(ReadInstructionError::AddressInProtectedMemoryArea(address));
        }

        let instruction_slice: &[u8; INSTRUCTION_SIZE] = self
            .raw_data
            .get(address..address + INSTRUCTION_SIZE)
            .ok_or(ReadInstructionError::AddressOutOfRange(address))?
            .try_into()
            .unwrap();

        Ok(Instruction::try_from(instruction_slice)?)
    }

    pub fn read_sprite(&self, address: usize, byte_count: usize) -> Option<BitSlicePixelView> {
        let slice = self.raw_data.get(address..address + byte_count)?;

        Some(BitSlicePixelView::new_from_byte_slice(slice, 8, byte_count))
    }
}