use std::{error, fmt, fs::File, io::prelude::*};
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum SnapshotError {
InvalidHeader,
NonMatchingMemSize,
IOError,
}
impl fmt::Display for SnapshotError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Snapshot error : ")?;
f.write_str(match self {
SnapshotError::InvalidHeader => "Invalid header",
SnapshotError::IOError => "I/O Error",
SnapshotError::NonMatchingMemSize => "Memory size is not matching",
})
}
}
impl From<std::io::Error> for SnapshotError {
fn from(_e: std::io::Error) -> SnapshotError {
SnapshotError::IOError
}
}
impl From<SnapshotError> for std::io::Error {
fn from(e: SnapshotError) -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::Other, e)
}
}
impl error::Error for SnapshotError {}
pub struct Bus {
address_space: Vec<u8>,
rom_space: Option<ROMSpace>,
}
struct ROMSpace {
start: u16,
end: u16,
}
impl Bus {
#[doc(hidden)]
pub fn new(size: u16) -> Bus {
Bus {
address_space: vec![0; (size as usize) + 1],
rom_space: None,
}
}
pub fn set_romspace(&mut self, start: u16, end: u16) {
self.rom_space = Some(ROMSpace { start, end });
}
pub fn get_romspace(&self) -> (u16, u16) {
match &self.rom_space {
Some(r) => (r.start, r.end),
None => (0, 0),
}
}
pub fn export_address_space(&self) -> Vec<u8> {
self.address_space.clone()
}
pub fn read_byte(&self, address: u16) -> u8 {
if address as usize >= self.address_space.len() {
return 0;
}
self.address_space[usize::from(address)]
}
pub fn write_byte(&mut self, address: u16, data: u8) {
if address as usize >= self.address_space.len() {
return;
}
if self.rom_space.is_some()
&& address >= self.rom_space.as_ref().unwrap().start
&& address <= self.rom_space.as_ref().unwrap().end
{
return;
};
self.address_space[usize::from(address)] = data;
}
pub fn read_word(&self, address: u16) -> u16 {
if address as usize >= self.address_space.len() {
return 0;
}
u16::from(self.address_space[usize::from(address)])
| (u16::from(self.address_space[usize::from(address + 1)]) << 8)
}
pub fn read_le_word(&self, address: u16) -> u16 {
if address as usize >= self.address_space.len() {
return 0;
}
u16::from(self.address_space[usize::from(address)]) << 8
| (u16::from(self.address_space[usize::from(address + 1)]))
}
pub fn write_word(&mut self, address: u16, data: u16) {
if address as usize >= self.address_space.len() {
return;
}
if self.rom_space.is_some()
&& address >= self.rom_space.as_ref().unwrap().start
&& address <= self.rom_space.as_ref().unwrap().end
{
return;
};
self.address_space[usize::from(address)] = (data & 0xFF) as u8;
self.address_space[usize::from(address + 1)] = (data >> 8) as u8;
}
pub fn load_bin(&mut self, file: &str, org: u16) -> Result<(), std::io::Error> {
let mut f = File::open(file)?;
let mut buf = Vec::new();
f.read_to_end(&mut buf)?;
self.address_space[org as usize..(buf.len() + org as usize)].clone_from_slice(&buf[..]);
Ok(())
}
pub fn load_from_vec(&mut self, source: Vec<u8>, org: u16) -> Result<(), SnapshotError> {
if source.len() > self.address_space.len() {
return Err(SnapshotError::NonMatchingMemSize);
}
self.address_space[org as usize..(source.len() + org as usize)]
.clone_from_slice(&source[..]);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rw_byte() {
let mut b = Bus::new(0xFFFF);
b.write_byte(0x0000, 0xFF);
assert_eq!(b.read_byte(0x0000), 0xFF);
}
#[test]
fn rw_word() {
let mut b = Bus::new(0xFFFF);
b.write_word(0x0000, 0x1be3);
assert_eq!(b.read_word(0x0000), 0x1be3);
}
#[test]
fn rw_le_word() {
let mut b = Bus::new(0xFFFF);
b.write_word(0x0000, 0x1be3);
assert_eq!(b.read_le_word(0x0000), 0xe31b);
}
}