emu/src/rom.rs
2022-11-20 19:15:58 -06:00

156 lines
4.8 KiB
Rust

use std::{fmt::Display, fs::File, io::Read};
use anyhow::anyhow;
use human_repr::HumanCount;
use nullable_result::NullableResult;
use serde::Deserialize;
use toml::Value;
use crate::{
backplane::DMAHandler,
card::{u16_get_be_byte, u16_set_be_byte, Card},
m68k::BusError,
register,
};
#[derive(Deserialize)]
struct Config {
image: Option<String>,
}
#[derive(Debug)]
pub struct Rom {
data: Vec<u8>,
enabled: bool,
ram: [u8; 32 * 1024],
file_name: Option<String>,
start: u16,
}
impl Rom {}
impl Card for Rom {
fn new(data: Value) -> anyhow::Result<(Self, Option<Box<dyn DMAHandler>>)> {
let file_name = data.try_into::<Config>()?.image;
let mut data = Vec::new();
if let Some(file_name) = file_name.as_ref() {
File::open(file_name)
.map_err(|e| anyhow!("Could not open ROM image file {} ({})", file_name, e))?
.read_to_end(&mut data)
.map_err(|e| anyhow!("Failed to read ROM image file {} ({})", file_name, e))?;
};
Ok((
Self {
data,
enabled: true,
ram: [0; 32 * 1024],
file_name,
start: 0,
},
None,
))
}
fn read_byte(&mut self, address: u32) -> NullableResult<u8, BusError> {
if !self.enabled | ((address >> 16) as u16 != self.start) {
return NullableResult::Null;
}
let address = address as u16;
if address < 0x4000 {
self.data.get(address as usize).copied().into()
} else {
self.ram.get((address - 0x4000) as usize).copied().into()
}
}
fn write_byte(&mut self, address: u32, data: u8) -> NullableResult<(), BusError> {
if !self.enabled | ((address >> 16) as u16 != self.start) {
return NullableResult::Null;
}
let address = (address as u16).checked_sub(0x4000)?;
if address > self.data.len() as u16 {
return NullableResult::Null;
}
self.ram[address as usize] = data;
NullableResult::Ok(())
}
fn read_byte_io(&mut self, address: u8) -> NullableResult<u8, BusError> {
match address {
(0..=0xEF) => NullableResult::Ok(self.ram[address as usize]),
(0xF0..=0xF1) => NullableResult::Ok(u16_get_be_byte(self.start, address - 0xF0)),
0xFE => NullableResult::Ok(self.enabled as u8),
0xFF => NullableResult::Ok(1),
_ => NullableResult::Null,
}
}
fn write_byte_io(&mut self, address: u8, data: u8) -> NullableResult<(), BusError> {
match address {
(0..=0xEF) => {
self.ram[address as usize] = data;
}
(0xF0..=0xF1) => {
self.start = u16_set_be_byte(self.start, address - 0xF0, data);
}
0xFE => {
self.enabled = data > 0;
}
_ => (),
}
NullableResult::Ok(())
}
fn cmd(&mut self, cmd: &[&str]) -> anyhow::Result<()> {
if cmd[0] == "load" && cmd.len() >= 2 {
let mut file = File::open(cmd[1])
.map_err(|e| anyhow!("Couldn't open ROM image file {} ({})", cmd[1], e))?;
self.data.clear();
file.read_to_end(&mut self.data)
.map_err(|e| anyhow!("Failed to read ROM image file {} ({})", cmd[1], e))?;
self.file_name = Some(cmd[1].into());
println!("Read ROM image file {}", cmd[1]);
} else if cmd[0] == "reload" {
if let Some(file_name) = &self.file_name {
let mut file = File::open(cmd[1])
.map_err(|e| anyhow!("Couldn't open ROM image file {} ({})", cmd[1], e))?;
self.data.clear();
file.read_to_end(&mut self.data)
.map_err(|e| anyhow!("Failed to read ROM image file {} ({})", cmd[1], e))?;
println!("Reloaded ROM image file {}", file_name);
} else {
println!("No ROM image file to reload");
}
}
Ok(())
}
fn reset(&mut self) {
self.enabled = true;
}
}
impl Display for Rom {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("ROM card, ")?;
if let Some(name) = self.file_name.as_ref() {
f.write_fmt(format_args!(
"image {} ({})",
name,
self.data.len().human_count_bytes()
))?;
} else {
f.write_str("no image")?;
};
if self.enabled {
f.write_fmt(format_args!(
", enabled at base address {:#x}",
(self.start as u32) << 16
))?;
};
Ok(())
}
}
register!(Rom, "rom");