1use thiserror::Error;
2
3use super::constants::{INSTRUCTION_SIZE, MEMORY_SIZE, UNPROTECTED_MEMORY_START};
4use super::graphic::BitSlicePixelView;
5use super::instruction::Instruction;
6use crate::chip8::instruction::parser::InstructionParsingError;
7
8pub struct Memory {
9 pub raw_data: [u8; MEMORY_SIZE],
10}
11
12#[derive(Error, Debug)]
13pub enum ReadInstructionError {
14 #[error("instruction parsing error")]
15 ParseError(#[from] InstructionParsingError),
16 #[error("instruction address in protected memory area")]
17 AddressInProtectedMemoryArea(usize),
18 #[error("instruction address outside of the memory")]
19 AddressOutOfRange(usize),
20}
21
22#[derive(Error, Debug)]
23pub enum WriteError {
24 #[error("address range is inside of protected memory area")]
25 InProtectedMemoryArea(usize, usize),
26 #[error("address range is outside of the memory")]
27 OutOfRange(usize, usize),
28}
29
30impl Default for Memory {
31 fn default() -> Self {
32 Memory {
33 raw_data: [0; MEMORY_SIZE],
34 }
35 }
36}
37
38impl Memory {
39 pub fn new() -> Self {
40 Self::default()
41 }
42
43 pub fn clear(&mut self) {
44 self.raw_data.fill(0);
45 }
46
47 pub fn write_unrestricted(
48 &mut self,
49 data: &[u8],
50 write_address: usize,
51 ) -> Result<(), WriteError> {
52 self.raw_data
53 .get_mut(write_address..write_address + data.len())
54 .ok_or(WriteError::OutOfRange(
55 write_address,
56 write_address + data.len(),
57 ))?
58 .copy_from_slice(data);
59
60 Ok(())
61 }
62
63 pub fn write_restricted(
64 &mut self,
65 data: &[u8],
66 write_address: usize,
67 ) -> Result<(), WriteError> {
68 if write_address < UNPROTECTED_MEMORY_START {
69 return Err(WriteError::InProtectedMemoryArea(
70 write_address,
71 write_address + data.len(),
72 ));
73 }
74
75 self.write_unrestricted(data, write_address)
76 }
77
78 pub fn read_instruction(&self, address: usize) -> Result<Instruction, ReadInstructionError> {
79 if address < UNPROTECTED_MEMORY_START {
80 return Err(ReadInstructionError::AddressInProtectedMemoryArea(address));
81 }
82
83 let instruction_slice: &[u8; INSTRUCTION_SIZE] = self
84 .raw_data
85 .get(address..address + INSTRUCTION_SIZE)
86 .ok_or(ReadInstructionError::AddressOutOfRange(address))?
87 .try_into()
88 .unwrap();
89
90 Ok(Instruction::try_from(instruction_slice)?)
91 }
92
93 pub fn read_sprite(&self, address: usize, byte_count: usize) -> Option<BitSlicePixelView> {
94 let slice = self.raw_data.get(address..address + byte_count)?;
95
96 Some(BitSlicePixelView::new_from_byte_slice(slice, 8, byte_count))
97 }
98}