diff --git a/Cargo.lock b/Cargo.lock index bb7c8d8..5812cea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -126,6 +126,7 @@ dependencies = [ name = "altair_emu" version = "0.1.0" dependencies = [ + "anyhow", "bitflags 2.4.2", "device_query", "eframe", @@ -134,7 +135,9 @@ dependencies = [ "env_logger", "ihex", "image", + "inventory", "log", + "mopa", "parking_lot", "rand", "rfd", @@ -171,6 +174,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + [[package]] name = "arboard" version = "3.2.0" @@ -1660,6 +1669,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -1870,6 +1885,12 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mopa" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" + [[package]] name = "nanorand" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 99c896f..01fae19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.79" bitflags = "2.3.3" device_query = "1.1.3" eframe = { version = "0.25.0", features = ["ron", "persistence"] } @@ -14,7 +15,9 @@ enum_dispatch = "0.3.12" env_logger = "0.10.0" ihex = "3.0.0" image = "0.24.6" +inventory = "0.3.15" log = "0.4.19" +mopa = "0.2.2" parking_lot = "0.12.1" rand = "0.8.5" rfd = "0.13.0" diff --git a/src/card.rs b/src/card.rs index 899c856..b99e2b7 100644 --- a/src/card.rs +++ b/src/card.rs @@ -1,16 +1,70 @@ -use crate::ram::RamCard; -use eframe::egui; -use enum_dispatch::enum_dispatch; +use anyhow::anyhow; +use eframe::egui::Ui; +use serde::Deserialize; -#[enum_dispatch(CardEnum)] -pub enum CardEnum { - RamCard, +#[derive(Deserialize, Debug)] +pub struct Config<'a> { + #[serde(rename = "type")] + typ: &'a str, + #[serde(flatten)] + settings: ron::Value, } -#[enum_dispatch] +impl Config<'_> { + pub fn to_card(&self) -> anyhow::Result> { + inventory::iter:: + .into_iter() + .find(|card_type| card_type.name == self.typ) + .ok_or_else(|| anyhow!("Invalid card type {}", self.typ))? + .new_card(self.settings.clone()) + } + pub fn to_settings_ui(&self) -> anyhow::Result> { + inventory::iter:: + .into_iter() + .find(|card_type| card_type.name == self.typ) + .ok_or_else(|| anyhow!("Invalid card type {}", self.typ))? + .settings_ui(self.settings.clone()) + } +} + +pub struct Type { + name: &'static str, + new: fn(settings: ron::Value) -> anyhow::Result>, + settings_ui: fn(settings: ron::Value) -> anyhow::Result>, +} + +impl Type { + pub const fn new(name: &'static str) -> Self { + Self { + name, + new: T::new_dyn, + settings_ui: T::settings_ui_dyn, + } + } + + fn new_card(&self, settings: ron::Value) -> anyhow::Result> { + (self.new)(settings) + } + + fn settings_ui(&self, settings: ron::Value) -> anyhow::Result> { + (self.settings_ui)(settings) + } +} + +inventory::collect!(Type); + pub trait Card { + fn new(_settings: ron::Value) -> anyhow::Result + where + Self: Sized; #[allow(clippy::new_ret_no_self)] - fn new(_settings: ron::Value) -> CardEnum; + fn new_dyn(settings: ron::Value) -> anyhow::Result> + where + Self: Sized + 'static, + { + let card = Self::new(settings)?; + Ok(Box::new(card)) + } fn read_mem(&mut self, _address: u16) -> Option { None @@ -24,6 +78,28 @@ pub trait Card { fn write_io(&mut self, _address: u8, _data: u8) -> Option<()> { None } - fn draw_settings_ui(&mut self, _ui: egui::Ui) {} + + fn settings_ui(_settings: ron::Value) -> anyhow::Result + where + Self: Sized; + #[allow(clippy::settings_ui_ret_no_self)] + fn settings_ui_dyn(settings: ron::Value) -> anyhow::Result> + where + Self: Sized + 'static, + { + let settings_ui = Self::settings_ui(settings)?; + Ok(Box::new(settings_ui)) + } +} + +pub trait SettingsUi { + fn draw_ui(&mut self, ui: &mut Ui); fn serialize_settings(&self) -> String; } + +#[macro_export] +macro_rules! register { + ($typ: ty, $name: literal) => { + inventory::submit!($crate::card::Type::new::<$typ>($name)); + }; +} diff --git a/src/ram.rs b/src/ram.rs index e7413d6..11b7a11 100644 --- a/src/ram.rs +++ b/src/ram.rs @@ -1,32 +1,81 @@ +use eframe::egui::{DragValue, Ui}; use serde::{Deserialize, Serialize}; -use crate::card::{Card, CardEnum}; +use crate::{card::{Card, SettingsUi}, register}; #[derive(Serialize, Deserialize)] pub struct RamCardSettings { size: u16, start_addr: u16, } + pub struct RamCard { ram: Vec, start_addr: u16, } impl Card for RamCard { - fn new(settings: ron::Value) -> CardEnum { + fn new(settings: ron::Value) -> Result { let settings: RamCardSettings = settings.into_rust().unwrap(); let ram = vec![0; settings.size as usize]; - CardEnum::RamCard(Self { + Ok(Self { ram, start_addr: settings.start_addr, }) } - fn serialize_settings(&self) -> String { - ron::to_string(&RamCardSettings { - size: self.ram.len() as u16, - start_addr: self.start_addr, + fn read_mem(&mut self, address: u16) -> Option { + if address < self.start_addr { + return None; + } + let offset = (address - self.start_addr) as usize; + if offset >= self.ram.len() { + return None; + } + Some(self.ram[offset]) + } + + fn write_mem(&mut self, address: u16, data: u8) -> Option<()> { + if address < self.start_addr { + return None; + } + let offset = (address - self.start_addr) as usize; + if offset >= self.ram.len() { + return None; + } + self.ram[offset] = data; + Some(()) + } + + fn settings_ui(settings: ron::Value) -> anyhow::Result + where + Self: Sized, + { + Ok(RamCardSettingsUi { + settings: settings.into_rust().unwrap(), }) - .unwrap() } } + +pub struct RamCardSettingsUi { + settings: RamCardSettings, +} + +impl SettingsUi for RamCardSettingsUi { + fn draw_ui(&mut self, ui: &mut Ui) { + ui.horizontal(|ui| { + ui.label("Size"); + ui.add(DragValue::new(&mut self.settings.size).hexadecimal(4, false, true)); + }); + ui.horizontal(|ui| { + ui.label("Start address"); + ui.add(DragValue::new(&mut self.settings.start_addr).hexadecimal(4, false, true)); + }); + } + + fn serialize_settings(&self) -> String { + ron::to_string(&self.settings).unwrap() + } +} + +register!(RamCard, "RAM card");