Rework card trait and settings UI

This commit is contained in:
pjht 2024-02-03 22:22:39 -06:00
parent 23d4723a31
commit f7d487951e
Signed by: pjht
GPG Key ID: CA239FC6934E6F3A
4 changed files with 166 additions and 17 deletions

21
Cargo.lock generated
View File

@ -126,6 +126,7 @@ dependencies = [
name = "altair_emu" name = "altair_emu"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"bitflags 2.4.2", "bitflags 2.4.2",
"device_query", "device_query",
"eframe", "eframe",
@ -134,7 +135,9 @@ dependencies = [
"env_logger", "env_logger",
"ihex", "ihex",
"image", "image",
"inventory",
"log", "log",
"mopa",
"parking_lot", "parking_lot",
"rand", "rand",
"rfd", "rfd",
@ -171,6 +174,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
[[package]]
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]] [[package]]
name = "arboard" name = "arboard"
version = "3.2.0" version = "3.2.0"
@ -1660,6 +1669,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "inventory"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767"
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.11" version = "1.0.11"
@ -1870,6 +1885,12 @@ dependencies = [
"simd-adler32", "simd-adler32",
] ]
[[package]]
name = "mopa"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915"
[[package]] [[package]]
name = "nanorand" name = "nanorand"
version = "0.7.0" version = "0.7.0"

View File

@ -6,6 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "1.0.79"
bitflags = "2.3.3" bitflags = "2.3.3"
device_query = "1.1.3" device_query = "1.1.3"
eframe = { version = "0.25.0", features = ["ron", "persistence"] } eframe = { version = "0.25.0", features = ["ron", "persistence"] }
@ -14,7 +15,9 @@ enum_dispatch = "0.3.12"
env_logger = "0.10.0" env_logger = "0.10.0"
ihex = "3.0.0" ihex = "3.0.0"
image = "0.24.6" image = "0.24.6"
inventory = "0.3.15"
log = "0.4.19" log = "0.4.19"
mopa = "0.2.2"
parking_lot = "0.12.1" parking_lot = "0.12.1"
rand = "0.8.5" rand = "0.8.5"
rfd = "0.13.0" rfd = "0.13.0"

View File

@ -1,16 +1,70 @@
use crate::ram::RamCard; use anyhow::anyhow;
use eframe::egui; use eframe::egui::Ui;
use enum_dispatch::enum_dispatch; use serde::Deserialize;
#[enum_dispatch(CardEnum)] #[derive(Deserialize, Debug)]
pub enum CardEnum { pub struct Config<'a> {
RamCard, #[serde(rename = "type")]
typ: &'a str,
#[serde(flatten)]
settings: ron::Value,
} }
#[enum_dispatch] impl Config<'_> {
pub fn to_card(&self) -> anyhow::Result<Box<dyn Card>> {
inventory::iter::<Type>
.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<Box<dyn SettingsUi>> {
inventory::iter::<Type>
.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<Box<dyn Card>>,
settings_ui: fn(settings: ron::Value) -> anyhow::Result<Box<dyn SettingsUi>>,
}
impl Type {
pub const fn new<T: Card + 'static>(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<Box<dyn Card>> {
(self.new)(settings)
}
fn settings_ui(&self, settings: ron::Value) -> anyhow::Result<Box<dyn SettingsUi>> {
(self.settings_ui)(settings)
}
}
inventory::collect!(Type);
pub trait Card { pub trait Card {
fn new(_settings: ron::Value) -> anyhow::Result<Self>
where
Self: Sized;
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new(_settings: ron::Value) -> CardEnum; fn new_dyn(settings: ron::Value) -> anyhow::Result<Box<dyn Card>>
where
Self: Sized + 'static,
{
let card = Self::new(settings)?;
Ok(Box::new(card))
}
fn read_mem(&mut self, _address: u16) -> Option<u8> { fn read_mem(&mut self, _address: u16) -> Option<u8> {
None None
@ -24,6 +78,28 @@ pub trait Card {
fn write_io(&mut self, _address: u8, _data: u8) -> Option<()> { fn write_io(&mut self, _address: u8, _data: u8) -> Option<()> {
None None
} }
fn draw_settings_ui(&mut self, _ui: egui::Ui) {}
fn settings_ui(_settings: ron::Value) -> anyhow::Result<impl SettingsUi>
where
Self: Sized;
#[allow(clippy::settings_ui_ret_no_self)]
fn settings_ui_dyn(settings: ron::Value) -> anyhow::Result<Box<dyn SettingsUi>>
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; fn serialize_settings(&self) -> String;
} }
#[macro_export]
macro_rules! register {
($typ: ty, $name: literal) => {
inventory::submit!($crate::card::Type::new::<$typ>($name));
};
}

View File

@ -1,32 +1,81 @@
use eframe::egui::{DragValue, Ui};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::card::{Card, CardEnum}; use crate::{card::{Card, SettingsUi}, register};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct RamCardSettings { pub struct RamCardSettings {
size: u16, size: u16,
start_addr: u16, start_addr: u16,
} }
pub struct RamCard { pub struct RamCard {
ram: Vec<u8>, ram: Vec<u8>,
start_addr: u16, start_addr: u16,
} }
impl Card for RamCard { impl Card for RamCard {
fn new(settings: ron::Value) -> CardEnum { fn new(settings: ron::Value) -> Result<RamCard, anyhow::Error> {
let settings: RamCardSettings = settings.into_rust().unwrap(); let settings: RamCardSettings = settings.into_rust().unwrap();
let ram = vec![0; settings.size as usize]; let ram = vec![0; settings.size as usize];
CardEnum::RamCard(Self { Ok(Self {
ram, ram,
start_addr: settings.start_addr, start_addr: settings.start_addr,
}) })
} }
fn serialize_settings(&self) -> String { fn read_mem(&mut self, address: u16) -> Option<u8> {
ron::to_string(&RamCardSettings { if address < self.start_addr {
size: self.ram.len() as u16, return None;
start_addr: self.start_addr, }
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<impl SettingsUi>
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");