From 9792fddf1f081f1bd70f767ecc6b3ffdafa9bf06 Mon Sep 17 00:00:00 2001 From: Rachel <508861+Ryex@users.noreply.github.com> Date: Tue, 26 Mar 2024 21:03:19 -0700 Subject: [PATCH] finished implimenting instrucitons --- ic10emu/Cargo.lock | 90 +++- ic10emu/Cargo.toml | 1 + ic10emu/build.rs | 252 +++++++--- ic10emu/data/instructions.txt | 1 - ic10emu/data/logictypes.txt | 38 +- ic10emu/src/grammar.rs | 217 ++++++--- ic10emu/src/interpreter.rs | 871 ++++++++++++++++++++++++++++------ ic10emu/src/lib.rs | 571 +++++++++++++++++++++- ic10emu_wasm/Cargo.lock | 92 +++- 9 files changed, 1810 insertions(+), 323 deletions(-) diff --git a/ic10emu/Cargo.lock b/ic10emu/Cargo.lock index f08f6e9..11fa26a 100644 --- a/ic10emu/Cargo.lock +++ b/ic10emu/Cargo.lock @@ -71,10 +71,11 @@ dependencies = [ "convert_case", "getrandom", "itertools", - "phf", + "phf 0.11.2", "phf_codegen", "rand", "regex", + "strum", "strum_macros", "thiserror", "web-time", @@ -122,13 +123,24 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared 0.10.0", + "proc-macro-hack", +] + [[package]] name = "phf" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ - "phf_shared", + "phf_shared 0.11.2", ] [[package]] @@ -137,8 +149,18 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand", ] [[package]] @@ -147,10 +169,33 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ - "phf_shared", + "phf_shared 0.11.2", "rand", ] +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + [[package]] name = "phf_shared" version = "0.11.2" @@ -166,6 +211,12 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.79" @@ -255,6 +306,16 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "phf 0.10.1", + "strum_macros", +] + [[package]] name = "strum_macros" version = "0.26.2" @@ -265,7 +326,18 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.53", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -296,7 +368,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.53", ] [[package]] @@ -338,7 +410,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.53", "wasm-bindgen-shared", ] @@ -360,7 +432,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/ic10emu/Cargo.toml b/ic10emu/Cargo.toml index 9243a13..eccc4f8 100644 --- a/ic10emu/Cargo.toml +++ b/ic10emu/Cargo.toml @@ -17,6 +17,7 @@ itertools = "0.12.1" phf = "0.11.2" rand = "0.8.5" regex = "1.10.3" +strum = { version = "0.26.2", features = ["derive", "phf", "strum_macros"] } strum_macros = "0.26.2" thiserror = "1.0.58" diff --git a/ic10emu/build.rs b/ic10emu/build.rs index c7a53ca..99bd640 100644 --- a/ic10emu/build.rs +++ b/ic10emu/build.rs @@ -1,20 +1,90 @@ use convert_case::{Case, Casing}; use std::{ - collections::HashSet, + collections::{HashMap, HashSet}, env, + fmt::Display, fs::{self, File}, io::{BufWriter, Write}, path::Path, + str::FromStr, }; -fn write_logictypes(logictypes_grammar: &mut HashSet) { +trait PrimitiveRepr {} +impl PrimitiveRepr for u8 {} +impl PrimitiveRepr for u16 {} +impl PrimitiveRepr for u32 {} +impl PrimitiveRepr for u64 {} +impl PrimitiveRepr for u128 {} +impl PrimitiveRepr for usize {} +impl PrimitiveRepr for i8 {} +impl PrimitiveRepr for i16 {} +impl PrimitiveRepr for i32 {} +impl PrimitiveRepr for i64 {} +impl PrimitiveRepr for i128 {} +impl PrimitiveRepr for isize {} + +struct EnumVariant

+where + P: Display + FromStr, +{ + pub aliases: Vec, + pub value: Option

, + pub depricated: bool, +} + +fn write_repr_enum( + writer: &mut BufWriter, + name: &str, + variants: &I, + use_phf: bool, +) where + P: Display + FromStr, + for<'a> &'a I: IntoIterator)>, +{ + let additional_strum = if use_phf { "#[strum(use_phf)]\n" } else { "" }; + write!( + writer, + "#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash, EnumString, AsRefStr, EnumProperty, EnumIter)]\n\ + {additional_strum}\ + pub enum {name} {{\n" + ) + .unwrap(); + for (name, variant) in variants.into_iter() { + let variant_name = name.to_case(Case::Pascal); + let mut serialize = vec![name.clone()]; + serialize.extend(variant.aliases.iter().cloned()); + let serialize_str = serialize + .into_iter() + .map(|s| format!("serialize = \"{s}\"")) + .collect::>() + .join(", "); + let depricated_str = if variant.depricated { + ", depricated = \"true\"".to_string() + } else { + "".to_string() + }; + let props_str = if let Some(val) = &variant.value { + format!(", props( value = \"{val}\"{depricated_str})") + } else { + "".to_string() + }; + write!( + writer, + " #[strum({serialize_str}{props_str})] {variant_name},\n" + ) + .unwrap(); + } + write!(writer, "}}\n").unwrap(); +} + +fn write_logictypes() { let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("logictypes.rs"); let output_file = File::create(dest_path).unwrap(); let mut writer = BufWriter::new(&output_file); - let mut logictype_lookup_map_builder = ::phf_codegen::Map::new(); + let mut logictypes: HashMap> = HashMap::new(); let l_infile = Path::new("data/logictypes.txt"); let l_contents = fs::read_to_string(l_infile).unwrap(); @@ -23,14 +93,41 @@ fn write_logictypes(logictypes_grammar: &mut HashSet) { let name = it.next().unwrap(); let val_str = it.next().unwrap(); let val: Option = val_str.parse().ok(); + let docs = it.next(); + let depricated = docs + .map(|docs| docs.trim().to_uppercase() == "DEPRECATED") + .unwrap_or(false); - logictypes_grammar.insert(name.to_string()); - if let Some(v) = val { - logictype_lookup_map_builder.entry(name, &format!("{}u8", v)); + if let Some(val) = val { + if let Some((_other_name, variant)) = logictypes + .iter_mut() + .find(|(_, variant)| variant.value == Some(val)) + { + variant.aliases.push(name.to_string()); + variant.depricated = depricated; + } else { + logictypes.insert( + name.to_string(), + EnumVariant { + aliases: Vec::new(), + value: Some(val), + depricated, + }, + ); + } + } else { + logictypes.insert( + name.to_string(), + EnumVariant { + aliases: Vec::new(), + value: val, + depricated, + }, + ); } } - let mut slotlogictype_lookup_map_builder = ::phf_codegen::Map::new(); + let mut slotlogictypes: HashMap> = HashMap::new(); let sl_infile = Path::new("data/slotlogictypes.txt"); let sl_contents = fs::read_to_string(sl_infile).unwrap(); @@ -39,32 +136,50 @@ fn write_logictypes(logictypes_grammar: &mut HashSet) { let name = it.next().unwrap(); let val_str = it.next().unwrap(); let val: Option = val_str.parse().ok(); + let docs = it.next(); + let depricated = docs + .map(|docs| docs.trim().to_uppercase() == "DEPRECATED") + .unwrap_or(false); - logictypes_grammar.insert(name.to_string()); - if let Some(v) = val { - slotlogictype_lookup_map_builder.entry(name, &format!("{}u8", v)); + if let Some(val) = val { + if let Some((_other_name, variant)) = slotlogictypes + .iter_mut() + .find(|(_, variant)| variant.value == Some(val)) + { + variant.aliases.push(name.to_string()); + variant.depricated = depricated; + } else { + slotlogictypes.insert( + name.to_string(), + EnumVariant { + aliases: Vec::new(), + value: Some(val), + depricated, + }, + ); + } + } else { + slotlogictypes.insert( + name.to_string(), + EnumVariant { + aliases: Vec::new(), + value: val, + depricated, + }, + ); } } - write!( - &mut writer, - "pub(crate) const LOGIC_TYPE_LOOKUP: phf::Map<&'static str, u8> = {};\n", - logictype_lookup_map_builder.build() - ) - .unwrap(); + write_repr_enum(&mut writer, "LogicType", &logictypes, true); + println!("cargo:rerun-if-changed=data/logictypes.txt"); - write!( - &mut writer, - "pub(crate) const SLOT_TYPE_LOOKUP: phf::Map<&'static str, u8> = {};\n", - slotlogictype_lookup_map_builder.build() - ) - .unwrap(); + write_repr_enum(&mut writer, "SlotLogicType", &slotlogictypes, true); println!("cargo:rerun-if-changed=data/slotlogictypes.txt"); } -fn write_enums(enums_grammar: &mut HashSet) { +fn write_enums() { let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("enums.rs"); @@ -83,7 +198,6 @@ fn write_enums(enums_grammar: &mut HashSet) { let val: Option = val_str.parse().ok(); if !check_set.contains(name) { - enums_grammar.insert(name.to_string()); check_set.insert(name); } @@ -102,14 +216,14 @@ fn write_enums(enums_grammar: &mut HashSet) { println!("cargo:rerun-if-changed=data/enums.txt"); } -fn write_modes(logictypes_grammar: &mut HashSet) { +fn write_modes() { let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("modes.rs"); let output_file = File::create(dest_path).unwrap(); let mut writer = BufWriter::new(&output_file); - let mut batchmode_lookup_map_builder = ::phf_codegen::Map::new(); + let mut batchmodes: HashMap> = HashMap::new(); let b_infile = Path::new("data/batchmodes.txt"); let b_contents = fs::read_to_string(b_infile).unwrap(); @@ -119,13 +233,35 @@ fn write_modes(logictypes_grammar: &mut HashSet) { let val_str = it.next().unwrap(); let val: Option = val_str.parse().ok(); - logictypes_grammar.insert(name.to_string()); - if let Some(v) = val { - batchmode_lookup_map_builder.entry(name, &format!("{}u8", v)); + if let Some(val) = val { + if let Some((_other_name, variant)) = batchmodes + .iter_mut() + .find(|(_, variant)| variant.value == Some(val)) + { + variant.aliases.push(name.to_string()); + } else { + batchmodes.insert( + name.to_string(), + EnumVariant { + aliases: Vec::new(), + value: Some(val), + depricated: false, + }, + ); + } + } else { + batchmodes.insert( + name.to_string(), + EnumVariant { + aliases: Vec::new(), + value: val, + depricated: false, + }, + ); } } - let mut reagentmode_lookup_map_builder = ::phf_codegen::Map::new(); + let mut reagentmodes: HashMap> = HashMap::new(); let r_infile = Path::new("data/reagentmodes.txt"); let r_contents = fs::read_to_string(r_infile).unwrap(); @@ -135,32 +271,44 @@ fn write_modes(logictypes_grammar: &mut HashSet) { let val_str = it.next().unwrap(); let val: Option = val_str.parse().ok(); - logictypes_grammar.insert(name.to_string()); - if let Some(v) = val { - reagentmode_lookup_map_builder.entry(name, &format!("{}u8", v)); + if let Some(val) = val { + if let Some((_other_name, variant)) = reagentmodes + .iter_mut() + .find(|(_, variant)| variant.value == Some(val)) + { + variant.aliases.push(name.to_string()); + } else { + reagentmodes.insert( + name.to_string(), + EnumVariant { + aliases: Vec::new(), + value: Some(val), + depricated: false, + }, + ); + } + } else { + reagentmodes.insert( + name.to_string(), + EnumVariant { + aliases: Vec::new(), + value: val, + depricated: false, + }, + ); } } - write!( - &mut writer, - "pub(crate) const BATCH_MODE_LOOKUP: phf::Map<&'static str, u8> = {};\n", - batchmode_lookup_map_builder.build() - ) - .unwrap(); + write_repr_enum(&mut writer, "BatchMode", &batchmodes, false); println!("cargo:rerun-if-changed=data/batchmodes.txt"); - write!( - &mut writer, - "pub(crate) const REAGENT_MODE_LOOKUP: phf::Map<&'static str, u8> = {};\n", - reagentmode_lookup_map_builder.build() - ) - .unwrap(); + write_repr_enum(&mut writer, "ReagentMode", &reagentmodes, false); println!("cargo:rerun-if-changed=data/reagentmodes.txt"); } -fn write_constants(constants_grammar: &mut HashSet) { +fn write_constants() { let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("constants.rs"); @@ -176,7 +324,6 @@ fn write_constants(constants_grammar: &mut HashSet) { let name = it.next().unwrap(); let constant = it.next().unwrap(); - constants_grammar.insert(name.to_string()); constants_lookup_map_builder.entry(name, constant); } @@ -248,14 +395,11 @@ fn write_instructions_enum() { } fn main() { - let mut logictype_grammar = HashSet::new(); - let mut enums_grammar = HashSet::new(); - let mut constants_grammar = HashSet::new(); // write_instructions(); - write_logictypes(&mut logictype_grammar); - write_modes(&mut logictype_grammar); - write_constants(&mut constants_grammar); - write_enums(&mut enums_grammar); + write_logictypes(); + write_modes(); + write_constants(); + write_enums(); write_instructions_enum(); } diff --git a/ic10emu/data/instructions.txt b/ic10emu/data/instructions.txt index 831ed38..de0552b 100644 --- a/ic10emu/data/instructions.txt +++ b/ic10emu/data/instructions.txt @@ -75,7 +75,6 @@ j VALUE jal VALUE jr VALUE l REGISTER DEVICE LOGIC_TYPE -label NAME REGISTER_DEVICE lb REGISTER DEVICE_TYPE LOGIC_TYPE BATCH_MODE lbn REGISTER DEVICE_TYPE DEVICE_NAME LOGIC_TYPE BATCH_MODE lbns REGISTER DEVICE_TYPE DEVICE_NAME INDEX SLOT_LOGIC_TYPE BATCH_MODE diff --git a/ic10emu/data/logictypes.txt b/ic10emu/data/logictypes.txt index b71a75d..3dba671 100644 --- a/ic10emu/data/logictypes.txt +++ b/ic10emu/data/logictypes.txt @@ -11,14 +11,14 @@ Bypass None Bypasses some internal behavoiur of the atmospherics device. CelestialHash 242 CelestialParentHash 250 Channel None Channel on a cable network which should be considered volatile -Channel0 165 -Channel1 166 -Channel2 167 -Channel3 168 -Channel4 169 -Channel5 170 -Channel6 171 -Channel7 172 +Channel0 165 Channel on a cable network which should be considered volatile +Channel1 166 Channel on a cable network which should be considered volatile +Channel2 167 Channel on a cable network which should be considered volatile +Channel3 168 Channel on a cable network which should be considered volatile +Channel4 169 Channel on a cable network which should be considered volatile +Channel5 170 Channel on a cable network which should be considered volatile +Channel6 171 Channel on a cable network which should be considered volatile +Channel7 172 Channel on a cable network which should be considered volatile Charge 11 The current charge the device has ClearMemory 62 When set to 1, clears the counter memory (e.g. ExportCount). Will set itself back to 0 when actioned CollectableGoods 101 @@ -45,9 +45,9 @@ EnvironmentEfficiency 104 The Environment Efficiency reported by the machine, as Error 4 1 if device is in error state, otherwise 0 ExhaustVelocity 235 ExportCount 63 How many items exported since last ClearMemory -ExportQuantity None Total quantity of items exported by the device -ExportSlotHash None DEPRECATED -ExportSlotOccupant None DEPRECATED +ExportQuantity 31 Total quantity of items exported by the device +ExportSlotHash 42 DEPRECATED +ExportSlotOccupant 32 DEPRECATED Filtration 74 The current state of the filtration system, for example Filtration = 1 for a Hardsuit sets filtration to On FlightControlRule 236 Flush 174 Set to 1 to activate the flush function on the device @@ -61,9 +61,9 @@ Horizontal 20 Horizontal setting of the device HorizontalRatio 34 Radio of horizontal setting for device Idle 37 Returns 1 if the device is currently idle, otherwise 0 ImportCount 64 How many items imported since last ClearMemory -ImportQuantity None Total quantity of items imported by the device -ImportSlotHash None DEPRECATED -ImportSlotOccupant None DEPRECATED +ImportQuantity 29 Total quantity of items imported by the device +ImportSlotHash 43 DEPRECATED +ImportSlotOccupant 30 DEPRECATED Inclination 246 Index 241 InterrogationProgress 157 Progress of this sattellite dish's interrogation of its current target, as a ratio from 0-1 @@ -72,10 +72,10 @@ Lock 10 1 if device is locked, otherwise 0, can be set in most devices and preve ManualResearchRequiredPod 94 Mass 219 The total Mass of the rocket in kilograms including fuel and cargo. The more massive the rocket the more fuel will be required to move to a new location in space. Maximum 23 Maximum setting of the device -MinWattsToContact None Minimum required amount of watts from the dish hitting the target trader contact to start interrogating the contact +MinWattsToContact 163 Minimum required amount of watts from the dish hitting the target trader contact to start interrogating the contact MineablesInQueue 96 MineablesInVicinity 95 -MinimumWattsToContact 163 +MinimumWattsToContact 163 Minimum required amount of watts from the dish hitting the target trader contact to start interrogating the contact Mode 3 Integer for mode state, different devices will have different mode states available to them NextWeatherEventTime 97 None None No description @@ -105,7 +105,7 @@ PlantHealth2 None DEPRECATED PlantHealth3 None DEPRECATED PlantHealth4 None DEPRECATED PositionX 76 The current position in X dimension in world coordinates -PositionY None The current position in Y dimension in world coordinates +PositionY 77 The current position in Y dimension in world coordinates PositionZ 78 The current position in Z dimension in world coordinates Power 1 Can be read to return if the device is correctly powered or not, set via the power system, return 1 if powered and 0 if not PowerActual 26 How much energy the device or network is actually using @@ -206,9 +206,9 @@ Rpm 155 The number of revolutions per minute that the device's spinning mechanis SemiMajorAxis 248 Setting 12 A variable setting that can be read or written, depending on the device SettingInput 91 -SettingInputHash None The input setting for the device +SettingInputHash 91 The input setting for the device SettingOutput 92 -SettingOutputHash None The output setting for the device +SettingOutputHash 91 The output setting for the device SignalID 87 Returns the contact ID of the strongest signal from this Satellite SignalStrength 86 Returns the degree offset of the strongest contact SizeX 160 Size on the X (right) axis of the object in largeGrids (a largeGrid is 2meters) diff --git a/ic10emu/src/grammar.rs b/ic10emu/src/grammar.rs index a27bce7..d900c1d 100644 --- a/ic10emu/src/grammar.rs +++ b/ic10emu/src/grammar.rs @@ -4,6 +4,87 @@ use itertools::Itertools; use std::error::Error; use std::fmt::Display; use std::str::FromStr; +use strum::EnumProperty; + +pub mod generated { + use super::ParseError; + use crate::interpreter::ICError; + use std::str::FromStr; + use strum::AsRefStr; + use strum::Display; + use strum::EnumIter; + use strum::EnumProperty; + use strum::EnumString; + use strum::IntoEnumIterator; + + include!(concat!(env!("OUT_DIR"), "/instructions.rs")); + include!(concat!(env!("OUT_DIR"), "/logictypes.rs")); + include!(concat!(env!("OUT_DIR"), "/modes.rs")); + include!(concat!(env!("OUT_DIR"), "/constants.rs")); + include!(concat!(env!("OUT_DIR"), "/enums.rs")); + + impl TryFrom for LogicType { + type Error = ICError; + fn try_from(value: f64) -> Result>::Error> { + if let Some(lt) = LogicType::iter().find(|lt| { + lt.get_str("value") + .map(|val| val.parse::().unwrap() as f64 == value) + .unwrap_or(false) + }) { + Ok(lt) + } else { + Err(crate::interpreter::ICError::UnknownLogicType(value)) + } + } + } + + impl TryFrom for SlotLogicType { + type Error = ICError; + fn try_from(value: f64) -> Result>::Error> { + if let Some(slt) = SlotLogicType::iter().find(|lt| { + lt.get_str("value") + .map(|val| val.parse::().unwrap() as f64 == value) + .unwrap_or(false) + }) { + Ok(slt) + } else { + Err(crate::interpreter::ICError::UnknownSlotLogicType(value)) + } + } + } + + impl TryFrom for BatchMode { + type Error = ICError; + fn try_from(value: f64) -> Result>::Error> { + if let Some(bm) = BatchMode::iter().find(|lt| { + lt.get_str("value") + .map(|val| val.parse::().unwrap() as f64 == value) + .unwrap_or(false) + }) { + Ok(bm) + } else { + Err(crate::interpreter::ICError::UnknownBatchMode(value)) + } + } + } + + impl TryFrom for ReagentMode { + type Error = ICError; + fn try_from(value: f64) -> Result>::Error> { + if let Some(rm) = ReagentMode::iter().find(|lt| { + lt.get_str("value") + .map(|val| val.parse::().unwrap() as f64 == value) + .unwrap_or(false) + }) { + Ok(rm) + } else { + Err(crate::interpreter::ICError::UnknownReagentMode(value)) + } + } + } +} + +pub use generated::*; #[derive(Debug, Clone)] pub struct ParseError { @@ -57,12 +138,6 @@ impl ParseError { } } -include!(concat!(env!("OUT_DIR"), "/instructions.rs")); -include!(concat!(env!("OUT_DIR"), "/logictypes.rs")); -include!(concat!(env!("OUT_DIR"), "/modes.rs")); -include!(concat!(env!("OUT_DIR"), "/constants.rs")); -include!(concat!(env!("OUT_DIR"), "/enums.rs")); - pub fn parse(code: &str) -> Result, ParseError> { code.lines() .enumerate() @@ -203,10 +278,13 @@ pub enum Operand { }, DeviceSpec { device: Device, - channel: Option, + connection: Option, }, Number(Number), LogicType(LogicType), + SlotLogicType(SlotLogicType), + BatchMode(BatchMode), + ReagentMode(ReagentMode), Identifier(Identifier), } @@ -218,16 +296,34 @@ impl Operand { target, } => ic.get_register(*indirection, *target), &Operand::Number(num) => Ok(num.value()), - &Operand::LogicType(lt) => Ok(lt.value), + &Operand::LogicType(lt) => lt + .get_str("value") + .map(|val| val.parse::().unwrap() as f64) + .ok_or(interpreter::ICError::TypeValueNotKnown), + &Operand::SlotLogicType(slt) => slt + .get_str("value") + .map(|val| val.parse::().unwrap() as f64) + .ok_or(interpreter::ICError::TypeValueNotKnown), + &Operand::BatchMode(bm) => bm + .get_str("value") + .map(|val| val.parse::().unwrap() as f64) + .ok_or(interpreter::ICError::TypeValueNotKnown), + &Operand::ReagentMode(rm) => rm + .get_str("value") + .map(|val| val.parse::().unwrap() as f64) + .ok_or(interpreter::ICError::TypeValueNotKnown), &Operand::Identifier(ident) => ic.get_ident_value(&ident.name), &Operand::DeviceSpec { .. } => Err(interpreter::ICError::DeviceNotValue), } } - - pub fn get_value_i64(&self, ic: &interpreter::IC, signed: bool) -> Result { + pub fn get_value_i64( + &self, + ic: &interpreter::IC, + signed: bool, + ) -> Result { let val = self.get_value(ic)?; - if val < -9.223372036854776E+18 { + if val < -9.223372036854776E+18 { Err(interpreter::ICError::ShiftUnderflowI64) } else if val <= 9.223372036854776E+18 { Ok(interpreter::f64_to_i64(val, signed)) @@ -252,15 +348,15 @@ impl Operand { ic: &interpreter::IC, ) -> Result<(Option, Option), interpreter::ICError> { match &self { - &Operand::DeviceSpec { device, channel } => match device { - Device::Db => Ok((Some(ic.id), *channel)), + &Operand::DeviceSpec { device, connection } => match device { + Device::Db => Ok((Some(ic.id), *connection)), Device::Numbered(p) => { let dp = ic .pins .get(*p as usize) .ok_or(interpreter::ICError::DeviceIndexOutOfRange(*p as f64)) .copied()?; - Ok((dp, *channel)) + Ok((dp, *connection)) } Device::Indirect { indirection, @@ -272,7 +368,7 @@ impl Operand { .get(val as usize) .ok_or(interpreter::ICError::DeviceIndexOutOfRange(val)) .copied()?; - Ok((dp, *channel)) + Ok((dp, *connection)) } }, &Operand::Identifier(id) => ic.get_ident_device_id(&id.name), @@ -322,20 +418,20 @@ impl FromStr for Operand { ['d', rest @ ..] => match rest { ['b'] => Ok(Operand::DeviceSpec { device: Device::Db, - channel: None, + connection: None, }), ['b', ':', chan @ ..] => { if chan.into_iter().all(|c| c.is_digit(10)) { Ok(Operand::DeviceSpec { device: Device::Db, - channel: Some(String::from_iter(chan).parse().unwrap()), + connection: Some(String::from_iter(chan).parse().unwrap()), }) } else { Err(ParseError { line: 0, start: 3, end: 3, - msg: format!("Invalid device channel specifier"), + msg: format!("Invalid device connection specifier"), }) } } @@ -346,25 +442,25 @@ impl FromStr for Operand { .take_while_ref(|c| c.is_digit(10)) .collect::(); let target = target_str.parse::().ok(); - let channel = { + let connection = { if rest_iter.peek() == Some(&&':') { // take off ':' rest_iter.next(); - let channel_str = rest_iter + let connection_str = rest_iter .take_while_ref(|c| c.is_digit(10)) .collect::(); - let channel = channel_str.parse::().unwrap(); + let connection = connection_str.parse::().unwrap(); let trailing = rest_iter.clone().collect::>(); if trailing.len() == 0 { - Ok(Some(channel)) + Ok(Some(connection)) } else { let start = - 2 + indirection + target_str.len() + 1 + channel_str.len(); + 2 + indirection + target_str.len() + 1 + connection_str.len(); Err(ParseError { line: 0, start, end: start, - msg: format!("Invalid device channel specifier"), + msg: format!("Invalid device connection specifier"), }) } } else { @@ -379,7 +475,7 @@ impl FromStr for Operand { indirection: indirection as u32, target, }, - channel, + connection: connection, }) } else { Err(ParseError { @@ -399,24 +495,24 @@ impl FromStr for Operand { .take_while_ref(|c| c.is_digit(10)) .collect::(); let target = target_str.parse::().ok(); - let channel = { + let connection = { if rest_iter.peek() == Some(&&':') { // take off ':' rest_iter.next(); - let channel_str = rest_iter + let connection_str = rest_iter .take_while_ref(|c| c.is_digit(10)) .collect::(); - let channel = channel_str.parse::().unwrap(); + let connection = connection_str.parse::().unwrap(); let trailing = rest_iter.clone().collect::>(); if trailing.len() == 0 { - Ok(Some(channel)) + Ok(Some(connection)) } else { - let start = 1 + target_str.len() + 1 + channel_str.len(); + let start = 1 + target_str.len() + 1 + connection_str.len(); Err(ParseError { line: 0, start, end: start, - msg: format!("Invalid device channel specifier"), + msg: format!("Invalid device connection specifier"), }) } } else { @@ -428,7 +524,7 @@ impl FromStr for Operand { if trailing.len() == 0 { Ok(Operand::DeviceSpec { device: Device::Numbered(target), - channel, + connection: connection, }) } else { Err(ParseError { @@ -537,26 +633,14 @@ impl FromStr for Operand { Ok(Operand::Number(Number::Constant(*val))) } else if let Some(val) = ENUM_LOOKUP.get(s) { Ok(Operand::Number(Number::Enum(*val as f64))) - } else if let Some(val) = LOGIC_TYPE_LOOKUP.get(s) { - Ok(Operand::LogicType(LogicType { - name: s.to_string(), - value: *val as f64, - })) - } else if let Some(val) = SLOT_TYPE_LOOKUP.get(s) { - Ok(Operand::LogicType(LogicType { - name: s.to_string(), - value: *val as f64, - })) - } else if let Some(val) = BATCH_MODE_LOOKUP.get(s) { - Ok(Operand::LogicType(LogicType { - name: s.to_string(), - value: *val as f64, - })) - } else if let Some(val) = REAGENT_MODE_LOOKUP.get(s) { - Ok(Operand::LogicType(LogicType { - name: s.to_string(), - value: *val as f64, - })) + } else if let Ok(lt) = LogicType::from_str(s) { + Ok(Operand::LogicType(lt)) + } else if let Ok(slt) = SlotLogicType::from_str(s) { + Ok(Operand::SlotLogicType(slt)) + } else if let Ok(bm) = BatchMode::from_str(s) { + Ok(Operand::BatchMode(bm)) + } else if let Ok(rm) = ReagentMode::from_str(s) { + Ok(Operand::ReagentMode(rm)) } else { Ok(Operand::Identifier(s.parse::()?)) } @@ -565,11 +649,11 @@ impl FromStr for Operand { } } -#[derive(PartialEq, Debug)] -pub struct LogicType { - pub name: String, - pub value: f64, -} +// #[derive(PartialEq, Debug)] +// pub struct LogicType { +// pub name: String, +// pub value: f64, +// } #[derive(PartialEq, Debug)] pub struct Label { @@ -673,6 +757,7 @@ impl Number { #[cfg(test)] mod tests { + use super::generated::*; use super::*; #[test] @@ -687,12 +772,9 @@ mod tests { operands: vec![ Operand::DeviceSpec { device: Device::Numbered(0), - channel: None, + connection: None, }, - Operand::LogicType(LogicType { - name: "Setting".to_string(), - value: 12.0, - },), + Operand::LogicType(LogicType::Setting), Operand::Number(Number::Float(0.0)), ], },),), @@ -800,7 +882,7 @@ mod tests { },), Operand::DeviceSpec { device: Device::Numbered(0), - channel: None, + connection: None, }, ], },),), @@ -812,7 +894,7 @@ mod tests { operands: vec![ Operand::DeviceSpec { device: Device::Numbered(0), - channel: None, + connection: None, }, Operand::Number(Number::Float(12.0)), Operand::Number(Number::Float(0.0)), @@ -871,12 +953,9 @@ mod tests { indirection: 0, target: 15, }, - channel: None, + connection: None, }, - Operand::LogicType(LogicType { - name: "RatioWater".to_string(), - value: 19.0, - },), + Operand::LogicType(LogicType::RatioWater), ], },),), comment: None, diff --git a/ic10emu/src/interpreter.rs b/ic10emu/src/interpreter.rs index 3139b23..7fa1648 100644 --- a/ic10emu/src/interpreter.rs +++ b/ic10emu/src/interpreter.rs @@ -30,9 +30,11 @@ pub enum ICError { #[error("")] StackIndexOutOfRange(f64), #[error("")] + SlotIndexOutOfRange(f64), + #[error("")] UnknownDeviceID(f64), #[error("")] - ToFewOperands { provided: u32, desired: u32 }, + TooFewOperands { provided: u32, desired: u32 }, #[error("")] TooManyOperands { provided: u32, desired: u32 }, #[error("")] @@ -61,6 +63,38 @@ pub enum ICError { StackOverflow, #[error("")] DuplicateDefine(String), + #[error("")] + ReadOnlyField(String), + #[error("")] + WriteOnlyField(String), + #[error("")] + DeviceHasNoField(String), + #[error("")] + DeviceHasNoIC, + #[error("")] + UnknownDeviceId(f64), + #[error("")] + UnknownLogicType(f64), + #[error("")] + UnknownSlotLogicType(f64), + #[error("")] + UnknownBatchMode(f64), + #[error("")] + UnknownReagentMode(f64), + #[error("")] + TypeValueNotKnown, + #[error("")] + EmptyDeviceList, + #[error("")] + ConnecitonIndexOutOFRange(u32), + #[error("")] + MissingConnecitonSpecifier, + #[error("")] + NotDataConnection(u32), + #[error("")] + NetworkNotConnected(u32), + #[error("")] + BadNetworkId(u32), } #[derive(Debug)] @@ -286,7 +320,7 @@ impl IC { } } - fn peek(&mut self) -> Result { + fn peek(&self) -> Result { let sp = (self.registers[16]) as i32; if sp < 0 { Err(ICError::StackUnderflow) @@ -298,6 +332,18 @@ impl IC { } } + fn peek_addr(&self, addr: f64) -> Result { + let sp = (addr) as i32; + if sp < 0 { + Err(ICError::StackUnderflow) + } else if sp >= 512 { + Err(ICError::StackOverflow) + } else { + let last = self.stack[sp as usize]; + Ok(last) + } + } + /// processes one line of the contained program pub fn step( &mut self, @@ -314,8 +360,7 @@ impl IC { let operands = &line.operands; match line.instruction { Nop => Ok(()), - Label => Ok(()), // Not used - Hcf => Ok(()), // TODO + Hcf => Ok(()), // TODO Sleep => match &operands[..] { [a] => { let a = a.get_value(self)?; @@ -340,7 +385,7 @@ impl IC { } } Define => match &operands[..] { - [_op1] => Err(ToFewOperands { + [_op1] => Err(TooFewOperands { provided: 1, desired: 2, }), @@ -370,7 +415,7 @@ impl IC { }), }, Alias => match &operands[..] { - [_op1] => Err(ToFewOperands { + [_op1] => Err(TooFewOperands { provided: 1, desired: 2, }), @@ -389,9 +434,12 @@ impl IC { indirection: *indirection, target: *target, }, - &Operand::DeviceSpec { device, channel } => Operand::DeviceSpec { + &Operand::DeviceSpec { + device, + connection, + } => Operand::DeviceSpec { device: *device, - channel: *channel, + connection: *connection, }, _ => { break 'inst Err(IncorrectOperandType { @@ -409,7 +457,7 @@ impl IC { }), }, Move => match &operands[..] { - [_op1] => Err(ToFewOperands { + [_op1] => Err(TooFewOperands { provided: 1, desired: 2, }), @@ -436,7 +484,7 @@ impl IC { }, Beq => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -453,7 +501,7 @@ impl IC { }), }, Beqal => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -471,7 +519,7 @@ impl IC { }), }, Breq => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -492,7 +540,7 @@ impl IC { }), }, Beqz => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -508,7 +556,7 @@ impl IC { }), }, Beqzal => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -525,7 +573,7 @@ impl IC { }), }, Breqz => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -545,7 +593,7 @@ impl IC { }), }, Bne => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -562,7 +610,7 @@ impl IC { }), }, Bneal => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -580,7 +628,7 @@ impl IC { }), }, Brne => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -601,7 +649,7 @@ impl IC { }), }, Bnez => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -617,7 +665,7 @@ impl IC { }), }, Bnezal => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -634,7 +682,7 @@ impl IC { }), }, Brnez => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -654,7 +702,7 @@ impl IC { }), }, Blt => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -671,7 +719,7 @@ impl IC { }), }, Bltal => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -689,7 +737,7 @@ impl IC { }), }, Brlt => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -710,7 +758,7 @@ impl IC { }), }, Ble => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -727,7 +775,7 @@ impl IC { }), }, Bleal => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -745,7 +793,7 @@ impl IC { }), }, Brle => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -766,7 +814,7 @@ impl IC { }), }, Blez => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -782,7 +830,7 @@ impl IC { }), }, Blezal => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -799,7 +847,7 @@ impl IC { }), }, Brlez => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -819,7 +867,7 @@ impl IC { }), }, Bltz => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -835,7 +883,7 @@ impl IC { }), }, Bltzal => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -852,7 +900,7 @@ impl IC { }), }, Brltz => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -872,7 +920,7 @@ impl IC { }), }, Bgt => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -889,7 +937,7 @@ impl IC { }), }, Bgtal => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -907,7 +955,7 @@ impl IC { }), }, Brgt => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -928,7 +976,7 @@ impl IC { }), }, Bgtz => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -944,7 +992,7 @@ impl IC { }), }, Bgtzal => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -961,7 +1009,7 @@ impl IC { }), }, Brgtz => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -981,7 +1029,7 @@ impl IC { }), }, Bge => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -998,7 +1046,7 @@ impl IC { }), }, Bgeal => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1016,7 +1064,7 @@ impl IC { }), }, Brge => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1037,7 +1085,7 @@ impl IC { }), }, Bgez => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -1053,7 +1101,7 @@ impl IC { }), }, Bgezal => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -1070,7 +1118,7 @@ impl IC { }), }, Brgez => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -1090,7 +1138,7 @@ impl IC { }), }, Bap => match &operands[..] { - oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 4, }), @@ -1114,7 +1162,7 @@ impl IC { }), }, Bapal => match &operands[..] { - oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 4, }), @@ -1139,7 +1187,7 @@ impl IC { }), }, Brap => match &operands[..] { - oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 4, }), @@ -1164,7 +1212,7 @@ impl IC { }), }, Bapz => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1185,7 +1233,7 @@ impl IC { }), }, Bapzal => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1207,7 +1255,7 @@ impl IC { }), }, Brapz => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1228,7 +1276,7 @@ impl IC { }), }, Bna => match &operands[..] { - oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 4, }), @@ -1252,7 +1300,7 @@ impl IC { }), }, Bnaal => match &operands[..] { - oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 4, }), @@ -1277,7 +1325,7 @@ impl IC { }), }, Brna => match &operands[..] { - oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 4, }), @@ -1301,7 +1349,7 @@ impl IC { }), }, Bnaz => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1322,7 +1370,7 @@ impl IC { }), }, Bnazal => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1344,7 +1392,7 @@ impl IC { }), }, Brnaz => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1365,12 +1413,12 @@ impl IC { }), }, Bdse => match &operands[..] { - [_] => Err(ToFewOperands { + [_] => Err(TooFewOperands { provided: 1, desired: 2, }), [d, a] => { - let (device, _channel) = d.get_device_id(self)?; + let (device, _connection) = d.get_device_id(self)?; let a = a.get_value(self)?; next_ip = if device.is_some() { a as u32 } else { next_ip }; Ok(()) @@ -1381,12 +1429,12 @@ impl IC { }), }, Bdseal => match &operands[..] { - [_] => Err(ToFewOperands { + [_] => Err(TooFewOperands { provided: 1, desired: 2, }), [d, a] => { - let (device, _channel) = d.get_device_id(self)?; + let (device, _connection) = d.get_device_id(self)?; let a = a.get_value(self)?; next_ip = if device.is_some() { a as u32 } else { next_ip }; self.al(); @@ -1398,12 +1446,12 @@ impl IC { }), }, Brdse => match &operands[..] { - [_] => Err(ToFewOperands { + [_] => Err(TooFewOperands { provided: 1, desired: 2, }), [d, a] => { - let (device, _channel) = d.get_device_id(self)?; + let (device, _connection) = d.get_device_id(self)?; let a = a.get_value(self)?; next_ip = if device.is_some() { (self.ip as f64 + a) as u32 @@ -1418,12 +1466,12 @@ impl IC { }), }, Bdns => match &operands[..] { - [_] => Err(ToFewOperands { + [_] => Err(TooFewOperands { provided: 1, desired: 2, }), [d, a] => { - let (device, _channel) = d.get_device_id(self)?; + let (device, _connection) = d.get_device_id(self)?; let a = a.get_value(self)?; next_ip = if device.is_none() { a as u32 } else { next_ip }; Ok(()) @@ -1434,12 +1482,12 @@ impl IC { }), }, Bdnsal => match &operands[..] { - [_] => Err(ToFewOperands { + [_] => Err(TooFewOperands { provided: 1, desired: 2, }), [d, a] => { - let (device, _channel) = d.get_device_id(self)?; + let (device, _connection) = d.get_device_id(self)?; let a = a.get_value(self)?; next_ip = if device.is_none() { a as u32 } else { next_ip }; self.al(); @@ -1451,12 +1499,12 @@ impl IC { }), }, Brdns => match &operands[..] { - [_] => Err(ToFewOperands { + [_] => Err(TooFewOperands { provided: 1, desired: 2, }), [d, a] => { - let (device, _channel) = d.get_device_id(self)?; + let (device, _connection) = d.get_device_id(self)?; let a = a.get_value(self)?; next_ip = if device.is_none() { (self.ip as f64 + a) as u32 @@ -1471,7 +1519,7 @@ impl IC { }), }, Bnan => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -1487,7 +1535,7 @@ impl IC { }), }, Brnan => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -1543,7 +1591,7 @@ impl IC { }, Seq => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1566,7 +1614,7 @@ impl IC { }), }, Seqz => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1588,7 +1636,7 @@ impl IC { }), }, Sne => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1611,7 +1659,7 @@ impl IC { }), }, Snez => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1633,7 +1681,7 @@ impl IC { }), }, Slt => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1656,7 +1704,7 @@ impl IC { }), }, Sltz => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1678,7 +1726,7 @@ impl IC { }), }, Sle => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1701,7 +1749,7 @@ impl IC { }), }, Slez => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1723,7 +1771,7 @@ impl IC { }), }, Sgt => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1746,7 +1794,7 @@ impl IC { }), }, Sgtz => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1768,7 +1816,7 @@ impl IC { }), }, Sge => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1791,7 +1839,7 @@ impl IC { }), }, Sgez => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1813,7 +1861,7 @@ impl IC { }), }, Sap => match &operands[..] { - oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1847,7 +1895,7 @@ impl IC { }), }, Sapz => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1878,7 +1926,7 @@ impl IC { }), }, Sna => match &operands[..] { - oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 4, }), @@ -1912,7 +1960,7 @@ impl IC { }), }, Snaz => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -1943,7 +1991,7 @@ impl IC { }), }, Sdse => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -1955,7 +2003,7 @@ impl IC { else { break 'inst Err(OperandNotRegister); }; - let (device, _channel) = device.get_device_id(self)?; + let (device, _connection) = device.get_device_id(self)?; self.set_register( indirection, target, @@ -1969,7 +2017,7 @@ impl IC { }), }, Sdns => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -1981,7 +2029,7 @@ impl IC { else { break 'inst Err(OperandNotRegister); }; - let (device, _channel) = device.get_device_id(self)?; + let (device, _connection) = device.get_device_id(self)?; self.set_register( indirection, target, @@ -1995,7 +2043,7 @@ impl IC { }), }, Snan => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2017,7 +2065,7 @@ impl IC { }), }, Snanz => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2040,7 +2088,7 @@ impl IC { }, Select => match &operands[..] { - oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 4, }), @@ -2065,7 +2113,7 @@ impl IC { }, Add => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2088,7 +2136,7 @@ impl IC { }), }, Sub => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2111,7 +2159,7 @@ impl IC { }), }, Mul => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2134,7 +2182,7 @@ impl IC { }), }, Div => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2157,7 +2205,7 @@ impl IC { }), }, Mod => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2180,7 +2228,7 @@ impl IC { }), }, Exp => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2202,7 +2250,7 @@ impl IC { }), }, Log => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2224,7 +2272,7 @@ impl IC { }), }, Sqrt => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2247,7 +2295,7 @@ impl IC { }, Max => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2270,7 +2318,7 @@ impl IC { }), }, Min => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2293,7 +2341,7 @@ impl IC { }), }, Ceil => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2315,7 +2363,7 @@ impl IC { }), }, Floor => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2337,7 +2385,7 @@ impl IC { }), }, Abs => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2359,7 +2407,7 @@ impl IC { }), }, Round => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2381,7 +2429,7 @@ impl IC { }), }, Trunc => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2423,7 +2471,7 @@ impl IC { }, Sin => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2445,7 +2493,7 @@ impl IC { }), }, Cos => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2467,7 +2515,7 @@ impl IC { }), }, Tan => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2489,7 +2537,7 @@ impl IC { }), }, Asin => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2511,7 +2559,7 @@ impl IC { }), }, Acos => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2533,7 +2581,7 @@ impl IC { }), }, Atan => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2555,7 +2603,7 @@ impl IC { }), }, Atan2 => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2579,7 +2627,7 @@ impl IC { }, Sll | Sla => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2602,7 +2650,7 @@ impl IC { }), }, Srl => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2625,7 +2673,7 @@ impl IC { }), }, Sra => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2649,7 +2697,7 @@ impl IC { }, And => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2672,7 +2720,7 @@ impl IC { }), }, Or => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2695,7 +2743,7 @@ impl IC { }), }, Xor => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2718,7 +2766,7 @@ impl IC { }), }, Nor => match &operands[..] { - oprs @ [_] | oprs @ [_, _] => Err(ToFewOperands { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 3, }), @@ -2741,7 +2789,7 @@ impl IC { }), }, Not => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2793,7 +2841,7 @@ impl IC { }), }, Poke => match &operands[..] { - oprs @ [_] => Err(ToFewOperands { + oprs @ [_] => Err(TooFewOperands { provided: oprs.len() as u32, desired: 2, }), @@ -2827,26 +2875,537 @@ impl IC { }), }, - Get => Ok(()), - Getd => Ok(()), - Put => Ok(()), - Putd => Ok(()), + Get => match &operands[..] { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 3, + }), + [reg, dev_id, addr] => { + let &Operand::RegisterSpec { + indirection, + target, + } = reg + else { + break 'inst Err(OperandNotRegister); + }; + let (Some(device_id), _connection) = dev_id.get_device_id(self)? else { + break 'inst Err(DeviceNotSet); + }; + let device = vm.get_device_same_network(self.id, device_id); + match device { + Some(device) => match &mut device.ic { + Some(ic) => { + let addr = addr.get_value(self)?; + let val = ic.peek_addr(addr)?; + self.set_register(indirection, target, val)?; + Ok(()) + } + None => Err(DeviceHasNoIC), + }, + None => Err(UnknownDeviceID(device_id as f64)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 3, + }), + }, + Getd => match &operands[..] { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 3, + }), + [reg, dev_id, addr] => { + let &Operand::RegisterSpec { + indirection, + target, + } = reg + else { + break 'inst Err(OperandNotRegister); + }; + let (Some(device_id), _connection) = dev_id.get_device_id(self)? else { + break 'inst Err(DeviceNotSet); + }; + let device = vm.get_device_same_network(self.id, device_id); + match device { + Some(device) => match &mut device.ic { + Some(ic) => { + let addr = addr.get_value(self)?; + let val = ic.peek_addr(addr)?; + self.set_register(indirection, target, val)?; + Ok(()) + } + None => Err(DeviceHasNoIC), + }, + None => Err(UnknownDeviceID(device_id as f64)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 3, + }), + }, + Put => match &operands[..] { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 3, + }), + [dev_id, addr, val] => { + let (Some(device_id), _connection) = dev_id.get_device_id(self)? else { + break 'inst Err(DeviceNotSet); + }; + let device = vm.get_device_same_network(self.id, device_id as u16); + match device { + Some(device) => match &mut device.ic { + Some(ic) => { + let addr = addr.get_value(self)?; + let val = val.get_value(self)?; + ic.poke(addr, val)?; + Ok(()) + } + None => Err(DeviceHasNoIC), + }, + None => Err(UnknownDeviceID(device_id as f64)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 3, + }), + }, + Putd => match &operands[..] { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 3, + }), + [dev_id, addr, val] => { + let device_id = dev_id.get_value(self)?; + if device_id >= u16::MAX as f64 || device_id < u16::MIN as f64 { + break 'inst Err(DeviceIndexOutOfRange(device_id)); + } + let device = vm.get_device_same_network(self.id, device_id as u16); + match device { + Some(device) => match &mut device.ic { + Some(ic) => { + let addr = addr.get_value(self)?; + let val = val.get_value(self)?; + ic.poke(addr, val)?; + Ok(()) + } + None => Err(DeviceHasNoIC), + }, + None => Err(UnknownDeviceID(device_id)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 3, + }), + }, - S => Ok(()), - Sd => Ok(()), - Ss => Ok(()), - Sb => Ok(()), - Sbs => Ok(()), - Sbn => Ok(()), + S => match &operands[..] { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 3, + }), + [dev, lt, val] => { + let (Some(device_id), connection) = dev.get_device_id(self)? else { + break 'inst Err(DeviceNotSet); + }; + let lt = LogicType::try_from(lt.get_value(self)?)?; + if CHANNEL_LOGIC_TYPES.contains(<) { + let channel = channel_logic_to_channel(lt).unwrap(); + let Some(connection) = connection else { + break 'inst Err(MissingConnecitonSpecifier); + }; + let network_id = vm + .get_device_same_network(self.id, device_id as u16) + .map(|device| device.get_network_id(connection as usize)) + .unwrap_or(Err(UnknownDeviceID(device_id as f64)))?; + let val = val.get_value(self)?; + vm.set_network_channel(network_id as usize, channel, val)?; + return Ok(()); + } + let device = vm.get_device_same_network(self.id, device_id as u16); + match device { + Some(device) => { + let val = val.get_value(self)?; + device.set_field(lt, val)?; + Ok(()) + } + None => Err(UnknownDeviceID(device_id as f64)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 3, + }), + }, + Sd => match &operands[..] { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 3, + }), + [dev, lt, val] => { + let device_id = dev.get_value(self)?; + if device_id >= u16::MAX as f64 || device_id < u16::MIN as f64 { + break 'inst Err(DeviceIndexOutOfRange(device_id)); + } + let device = vm.get_device_same_network(self.id, device_id as u16); + match device { + Some(device) => { + let lt = LogicType::try_from(lt.get_value(self)?)?; + let val = val.get_value(self)?; + device.set_field(lt, val)?; + Ok(()) + } + None => Err(UnknownDeviceID(device_id as f64)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 3, + }), + }, + Ss => match &operands[..] { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 4, + }), + [dev, index, lt, val] => { + let (Some(device_id), _connection) = dev.get_device_id(self)? else { + break 'inst Err(DeviceNotSet); + }; + let device = vm.get_device_same_network(self.id, device_id as u16); + match device { + Some(device) => { + let index = index.get_value(self)?; + let lt = SlotLogicType::try_from(lt.get_value(self)?)?; + let val = val.get_value(self)?; + device.set_slot_field(index, lt, val)?; + Ok(()) + } + None => Err(UnknownDeviceID(device_id as f64)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 4, + }), + }, + Sb => match &operands[..] { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 3, + }), + [prefab, lt, val] => { + let prefab = prefab.get_value(self)?; + let lt = LogicType::try_from(lt.get_value(self)?)?; + let val = val.get_value(self)?; + vm.set_batch_device_field(self.id, prefab, lt, val)?; + Ok(()) + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 3, + }), + }, + Sbs => match &operands[..] { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 4, + }), + [prefab, index, lt, val] => { + let prefab = prefab.get_value(self)?; + let index = index.get_value(self)?; + let lt = SlotLogicType::try_from(lt.get_value(self)?)?; + let val = val.get_value(self)?; + vm.set_batch_device_slot_field(self.id, prefab, index, lt, val)?; + Ok(()) + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 4, + }), + }, + Sbn => match &operands[..] { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 4, + }), + [prefab, name, lt, val] => { + let prefab = prefab.get_value(self)?; + let name = name.get_value(self)?; + let lt = LogicType::try_from(lt.get_value(self)?)?; + let val = val.get_value(self)?; + vm.set_batch_name_device_field(self.id, prefab, name, lt, val)?; + Ok(()) + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 4, + }), + }, - L => Ok(()), - Ld => Ok(()), - Ls => Ok(()), - Lr => Ok(()), - Lb => Ok(()), - Lbn => Ok(()), - Lbns => Ok(()), - Lbs => Ok(()), + L => match &operands[..] { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 3, + }), + [reg, dev, lt] => { + let &Operand::RegisterSpec { + indirection, + target, + } = reg + else { + break 'inst Err(OperandNotRegister); + }; + let (Some(device_id), connection) = dev.get_device_id(self)? else { + break 'inst Err(DeviceNotSet); + }; + let lt = LogicType::try_from(lt.get_value(self)?)?; + if CHANNEL_LOGIC_TYPES.contains(<) { + let channel = channel_logic_to_channel(lt).unwrap(); + let Some(connection) = connection else { + break 'inst Err(MissingConnecitonSpecifier); + }; + let network_id = vm + .get_device_same_network(self.id, device_id as u16) + .map(|device| device.get_network_id(connection as usize)) + .unwrap_or(Err(UnknownDeviceID(device_id as f64)))?; + let val = vm.get_network_channel(network_id as usize, channel)?; + self.set_register(indirection, target, val)?; + return Ok(()); + } + let device = vm.get_device_same_network(self.id, device_id as u16); + match device { + Some(device) => { + let val = device.get_field(lt)?; + self.set_register(indirection, target, val)?; + Ok(()) + } + None => Err(UnknownDeviceID(device_id as f64)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 3, + }), + }, + Ld => match &operands[..] { + oprs @ [_] | oprs @ [_, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 3, + }), + [reg, dev, lt] => { + let &Operand::RegisterSpec { + indirection, + target, + } = reg + else { + break 'inst Err(OperandNotRegister); + }; + let device_id = dev.get_value(self)?; + if device_id >= u16::MAX as f64 || device_id < u16::MIN as f64 { + break 'inst Err(DeviceIndexOutOfRange(device_id)); + } + let device = vm.get_device_same_network(self.id, device_id as u16); + match device { + Some(device) => { + let lt = LogicType::try_from(lt.get_value(self)?)?; + let val = device.get_field(lt)?; + self.set_register(indirection, target, val)?; + Ok(()) + } + None => Err(UnknownDeviceID(device_id as f64)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 3, + }), + }, + Ls => match &operands[..] { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 4, + }), + [reg, dev, index, lt] => { + let &Operand::RegisterSpec { + indirection, + target, + } = reg + else { + break 'inst Err(OperandNotRegister); + }; + let (Some(device_id), _connection) = dev.get_device_id(self)? else { + break 'inst Err(DeviceNotSet); + }; + let device = vm.get_device_same_network(self.id, device_id as u16); + match device { + Some(device) => { + let index = index.get_value(self)?; + let lt = SlotLogicType::try_from(lt.get_value(self)?)?; + let val = device.get_slot_field(index, lt)?; + self.set_register(indirection, target, val)?; + Ok(()) + } + None => Err(UnknownDeviceID(device_id as f64)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 4, + }), + }, + Lr => match &operands[..] { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 4, + }), + [reg, dev, rm, name] => { + let &Operand::RegisterSpec { + indirection, + target, + } = reg + else { + break 'inst Err(OperandNotRegister); + }; + let (Some(device_id), _connection) = dev.get_device_id(self)? else { + break 'inst Err(DeviceNotSet); + }; + let device = vm.get_device_same_network(self.id, device_id as u16); + match device { + Some(device) => { + let rm = ReagentMode::try_from(rm.get_value(self)?)?; + let name = name.get_value(self)?; + let val = device.get_reagent(&rm, name); + self.set_register(indirection, target, val)?; + Ok(()) + } + None => Err(UnknownDeviceID(device_id as f64)), + } + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 4, + }), + }, + Lb => match &operands[..] { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 4, + }), + [reg, prefab, lt, bm] => { + let &Operand::RegisterSpec { + indirection, + target, + } = reg + else { + break 'inst Err(OperandNotRegister); + }; + let prefab = prefab.get_value(self)?; + let lt = LogicType::try_from(lt.get_value(self)?)?; + let bm = BatchMode::try_from(bm.get_value(self)?)?; + let val = vm.get_batch_device_field(self.id, prefab, lt, bm)?; + self.set_register(indirection, target, val)?; + Ok(()) + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 4, + }), + }, + Lbn => match &operands[..] { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] | oprs @ [_, _, _, _] => { + Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 5, + }) + } + [reg, prefab, name, lt, bm] => { + let &Operand::RegisterSpec { + indirection, + target, + } = reg + else { + break 'inst Err(OperandNotRegister); + }; + let prefab = prefab.get_value(self)?; + let name = name.get_value(self)?; + let lt = LogicType::try_from(lt.get_value(self)?)?; + let bm = BatchMode::try_from(bm.get_value(self)?)?; + let val = vm.get_batch_name_device_field(self.id, prefab, name, lt, bm)?; + self.set_register(indirection, target, val)?; + Ok(()) + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 5, + }), + }, + Lbns => match &operands[..] { + oprs @ [_] + | oprs @ [_, _] + | oprs @ [_, _, _] + | oprs @ [_, _, _, _] + | oprs @ [_, _, _, _, _] => Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 6, + }), + [reg, prefab, name, index, slt, bm] => { + let &Operand::RegisterSpec { + indirection, + target, + } = reg + else { + break 'inst Err(OperandNotRegister); + }; + let prefab = prefab.get_value(self)?; + let name = name.get_value(self)?; + let index = index.get_value(self)?; + let slt = SlotLogicType::try_from(slt.get_value(self)?)?; + let bm = BatchMode::try_from(bm.get_value(self)?)?; + let val = vm.get_batch_name_device_slot_field( + self.id, prefab, name, index, slt, bm, + )?; + self.set_register(indirection, target, val)?; + Ok(()) + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 6, + }), + }, + Lbs => match &operands[..] { + oprs @ [_] | oprs @ [_, _] | oprs @ [_, _, _] | oprs @ [_, _, _, _] => { + Err(TooFewOperands { + provided: oprs.len() as u32, + desired: 5, + }) + } + [reg, prefab, index, slt, bm] => { + let &Operand::RegisterSpec { + indirection, + target, + } = reg + else { + break 'inst Err(OperandNotRegister); + }; + let prefab = prefab.get_value(self)?; + let index = index.get_value(self)?; + let slt = SlotLogicType::try_from(slt.get_value(self)?)?; + let bm = BatchMode::try_from(bm.get_value(self)?)?; + let val = + vm.get_batch_device_slot_field(self.id, prefab, index, slt, bm)?; + self.set_register(indirection, target, val)?; + Ok(()) + } + oprs => Err(TooManyOperands { + provided: oprs.len() as u32, + desired: 5, + }), + }, } }; self.ip = next_ip; @@ -2854,6 +3413,32 @@ impl IC { } } +#[allow(dead_code)] +const CHANNEL_LOGIC_TYPES: [grammar::LogicType; 8] = [ + grammar::LogicType::Channel0, + grammar::LogicType::Channel1, + grammar::LogicType::Channel2, + grammar::LogicType::Channel3, + grammar::LogicType::Channel4, + grammar::LogicType::Channel5, + grammar::LogicType::Channel6, + grammar::LogicType::Channel7, +]; + +fn channel_logic_to_channel(lt: grammar::LogicType) -> Option { + match lt { + grammar::LogicType::Channel0 => Some(0), + grammar::LogicType::Channel1 => Some(1), + grammar::LogicType::Channel2 => Some(2), + grammar::LogicType::Channel3 => Some(3), + grammar::LogicType::Channel4 => Some(4), + grammar::LogicType::Channel5 => Some(5), + grammar::LogicType::Channel6 => Some(6), + grammar::LogicType::Channel7 => Some(7), + _ => None, + } +} + pub fn f64_to_i64(f: f64, signed: bool) -> i64 { let mut num: i64 = (f % 9007199254740992.0) as i64; if !signed { diff --git a/ic10emu/src/lib.rs b/ic10emu/src/lib.rs index ea51beb..3b1d9d1 100644 --- a/ic10emu/src/lib.rs +++ b/ic10emu/src/lib.rs @@ -1,12 +1,14 @@ use core::f64; use std::collections::{HashMap, HashSet}; - -mod tokens; mod grammar; mod interpreter; mod rand_mscorlib; +mod tokens; +use grammar::{BatchMode, LogicType, ReagentMode, SlotLogicType}; +use interpreter::ICError; +use itertools::Itertools; use thiserror::Error; #[derive(Error, Debug)] @@ -16,29 +18,47 @@ pub enum VMError { #[error("Device with id '{0}' does not have a IC Slot")] NoIC(u16), #[error("IC encoutered an error: {0}")] - ICError(#[from] interpreter::ICError) + ICError(#[from] ICError), + #[error("Invalid network ID {0}")] + InvalidNetwork(u16), } - -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum FieldType { Read, Write, - ReadWrite + ReadWrite, } #[derive(Debug)] pub struct LogicField { pub field_type: FieldType, pub value: f64, +} +#[derive(Debug, Default)] +pub struct Slot { + pub fields: HashMap, +} + +#[derive(Debug, Default, Clone, Copy)] +pub enum Connection { + CableNetwork(Option), + #[default] + Other, } #[derive(Debug, Default)] pub struct Device { pub id: u16, - pub fields: HashMap, - pub ic: Option + pub name: Option, + pub name_hash: Option, + pub fields: HashMap, + pub slots: Vec, + pub reagents: HashMap>, + pub ic: Option, + pub connections: [Connection; 8], + pub prefab_hash: Option, } #[derive(Debug)] @@ -70,8 +90,10 @@ impl IdSequenceGenerator { pub struct VM { pub ics: HashSet, pub devices: HashMap, - pub networks: Vec, + pub networks: HashMap, + pub default_network: u16, id_gen: IdSequenceGenerator, + network_id_gen: IdSequenceGenerator, random: crate::rand_mscorlib::Random, } @@ -84,9 +106,57 @@ impl Default for Network { } } +#[derive(Debug, Error)] +pub enum NetworkError { + #[error("")] + ChannelIndexOutOfRange, +} + +impl Network { + pub fn contains(&self, ids: &[u16]) -> bool { + ids.iter().all(|id| self.devices.contains(id)) + } + + pub fn add(&mut self, id: u16) -> bool { + self.devices.insert(id) + } + + pub fn remove(&mut self, id: u16) -> bool { + self.devices.remove(&id) + } + + pub fn set_channel(&mut self, chan: usize, val: f64) -> Result { + if chan > 7 { + Err(NetworkError::ChannelIndexOutOfRange) + } else { + let last = self.channels[chan]; + self.channels[chan] = val; + Ok(last) + } + } + + pub fn get_channel(&self, chan: usize) -> Result { + if chan > 7 { + Err(NetworkError::ChannelIndexOutOfRange) + } else { + Ok(self.channels[chan]) + } + } +} + impl Device { pub fn new(id: u16) -> Self { - Device { id, fields: HashMap::new(), ic: None } + Device { + id, + name: None, + name_hash: None, + fields: HashMap::new(), + slots: Vec::new(), + reagents: HashMap::new(), + ic: None, + connections: [Connection::default(); 8], + prefab_hash: None, + } } pub fn with_ic(id: u16) -> Self { @@ -94,32 +164,207 @@ impl Device { device.ic = Some(interpreter::IC::new(id)); device } -} + pub fn get_network_id(&self, connection: usize) -> Result { + if connection >= 8 { + Err(ICError::ConnecitonIndexOutOFRange(connection as u32)) + } else { + if let Connection::CableNetwork(network_id) = self.connections[connection] { + if let Some(network_id) = network_id { + Ok(network_id) + } else { + Err(ICError::NetworkNotConnected(connection as u32)) + } + } else { + Err(ICError::NotDataConnection(connection as u32)) + } + } + } + + pub fn get_field(&self, typ: grammar::LogicType) -> Result { + if let Some(field) = self.fields.get(&typ) { + if field.field_type == FieldType::Read || field.field_type == FieldType::ReadWrite { + Ok(field.value) + } else { + Err(ICError::WriteOnlyField(typ.to_string())) + } + } else { + Err(ICError::DeviceHasNoField(typ.to_string())) + } + } + + pub fn set_field(&mut self, typ: grammar::LogicType, val: f64) -> Result<(), ICError> { + if let Some(field) = self.fields.get_mut(&typ) { + if field.field_type == FieldType::Write || field.field_type == FieldType::ReadWrite { + field.value = val; + Ok(()) + } else { + Err(ICError::ReadOnlyField(typ.to_string())) + } + } else { + Err(ICError::DeviceHasNoField(typ.to_string())) + } + } + + pub fn get_slot_field(&self, index: f64, typ: grammar::SlotLogicType) -> Result { + if let Some(field) = self + .slots + .get(index as usize) + .ok_or(ICError::SlotIndexOutOfRange(index))? + .fields + .get(&typ) + { + if field.field_type == FieldType::Read || field.field_type == FieldType::ReadWrite { + Ok(field.value) + } else { + Err(ICError::WriteOnlyField(typ.to_string())) + } + } else { + Err(ICError::DeviceHasNoField(typ.to_string())) + } + } + + pub fn set_slot_field( + &mut self, + index: f64, + typ: grammar::SlotLogicType, + val: f64, + ) -> Result<(), ICError> { + if let Some(field) = self + .slots + .get_mut(index as usize) + .ok_or(ICError::SlotIndexOutOfRange(index))? + .fields + .get_mut(&typ) + { + if field.field_type == FieldType::Write || field.field_type == FieldType::ReadWrite { + field.value = val; + Ok(()) + } else { + Err(ICError::ReadOnlyField(typ.to_string())) + } + } else { + Err(ICError::DeviceHasNoField(typ.to_string())) + } + } + + pub fn get_reagent(&self, rm: &ReagentMode, reagent: f64) -> f64 { + if let Some(mode) = self.reagents.get(rm) { + if let Some(val) = mode.get(&(reagent as i32)) { + return *val; + } + } + 0.0 + } +} impl VM { pub fn new() -> Self { let id_gen = IdSequenceGenerator::default(); + let mut network_id_gen = IdSequenceGenerator::default(); let default_network = Network::default(); + let mut networks = HashMap::new(); + let default_network_key = network_id_gen.next(); + networks.insert(default_network_key, default_network); + let mut vm = VM { ics: HashSet::new(), devices: HashMap::new(), - networks: vec![default_network], + networks, + default_network: default_network_key, id_gen, + network_id_gen, random: crate::rand_mscorlib::Random::new(), }; - vm.add_ic(); + let _ = vm.add_ic(None); vm } - pub fn add_ic(&mut self) { - let device = Device::with_ic(self.id_gen.next()); - self.ics.insert(device.id); - self.devices.insert(device.id, device); + fn new_device(&mut self) -> Device { + Device::new(self.id_gen.next()) + } + + fn new_ic(&mut self) -> Device { + Device::with_ic(self.id_gen.next()) + } + + pub fn add_device(&mut self, network: Option) -> Result { + if let Some(n) = &network { + if !self.networks.contains_key(n) { + return Err(VMError::InvalidNetwork(*n)); + } + } + let mut device = self.new_device(); + if let Some(first_network) = device + .connections + .iter_mut() + .filter_map(|c| { + if let Connection::CableNetwork(c) = c { + Some(c) + } else { + None + } + }) + .next() + { + first_network.replace(if let Some(network) = network { + network + } else { + self.default_network + }); + } + let id = device.id; + self.devices.insert(id, device); + Ok(id) + } + + pub fn add_ic(&mut self, network: Option) -> Result { + if let Some(n) = &network { + if !self.networks.contains_key(n) { + return Err(VMError::InvalidNetwork(*n)); + } + } + let mut device = self.new_ic(); + if let Some(first_network) = device + .connections + .iter_mut() + .filter_map(|c| { + if let Connection::CableNetwork(c) = c { + Some(c) + } else { + None + } + }) + .next() + { + first_network.replace(if let Some(network) = network { + network + } else { + self.default_network + }); + } + let id = device.id; + self.devices.insert(id, device); + self.ics.insert(id); + Ok(id) + } + + pub fn add_network(&mut self) -> u16 { + let next_id = self.network_id_gen.next(); + self.networks.insert(next_id, Network::default()); + next_id + } + + pub fn get_default_network(&mut self) -> &mut Network { + self.networks.get_mut(&self.default_network).unwrap() + } + + pub fn get_network(&mut self, id: u16) -> Option<&mut Network> { + self.networks.get_mut(&id) } pub fn remove_ic(&mut self, id: u16) { - if self.ics.remove(&id) { + if self.ics.remove(&id) { self.devices.remove(&id); } } @@ -133,4 +378,294 @@ impl VM { Ok(true) } + pub fn get_device(&mut self, id: u16) -> Option<&mut Device> { + self.devices.get_mut(&id) + } + + pub fn get_device_same_network(&mut self, source: u16, other: u16) -> Option<&mut Device> { + if self.devices_on_same_network(&[source, other]) { + self.get_device(other) + } else { + None + } + } + + pub fn get_network_channel(&self, id: usize, channel: usize) -> Result { + let network = self + .networks + .get(&(id as u16)) + .ok_or(ICError::BadNetworkId(id as u32))?; + Ok(network.channels[channel]) + } + + pub fn set_network_channel(&mut self, id: usize, channel: usize, val: f64) -> Result<(), ICError> { + let network = self + .networks + .get_mut(&(id as u16)) + .ok_or(ICError::BadNetworkId(id as u32))?; + network.channels[channel] = val; + Ok(()) + } + + pub fn devices_on_same_network(&self, ids: &[u16]) -> bool { + for (_id, net) in self.networks.iter() { + if net.contains(ids) { + return true; + } + } + false + } + + pub fn set_batch_device_field( + &mut self, + source: u16, + prefab: f64, + typ: LogicType, + val: f64, + ) -> Result<(), ICError> { + let networks = &self.networks; + self.devices + .iter_mut() + .map(|(id, device)| { + if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + && networks + .iter() + .any(|(_net_id, net)| net.contains(&[source, *id])) + { + device.set_field(typ, val) + } else { + Ok(()) + } + }) + .try_collect() + } + + pub fn set_batch_device_slot_field( + &mut self, + source: u16, + prefab: f64, + index: f64, + typ: SlotLogicType, + val: f64, + ) -> Result<(), ICError> { + let networks = &self.networks; + self.devices + .iter_mut() + .map(|(id, device)| { + if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + && networks + .iter() + .any(|(_net_id, net)| net.contains(&[source, *id])) + { + device.set_slot_field(index, typ, val) + } else { + Ok(()) + } + }) + .try_collect() + } + + pub fn set_batch_name_device_field( + &mut self, + source: u16, + prefab: f64, + name: f64, + typ: LogicType, + val: f64, + ) -> Result<(), ICError> { + let networks = &self.networks; + self.devices + .iter_mut() + .map(|(id, device)| { + if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + && Some(name) == device.name_hash + && networks + .iter() + .any(|(_net_id, net)| net.contains(&[source, *id])) + { + device.set_field(typ, val) + } else { + Ok(()) + } + }) + .try_collect() + } + + pub fn get_batch_device_field( + &mut self, + source: u16, + prefab: f64, + typ: LogicType, + mode: BatchMode, + ) -> Result { + let networks = &self.networks; + let samples = self + .devices + .iter_mut() + .map(|(id, device)| { + if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + && networks + .iter() + .any(|(_net_id, net)| net.contains(&[source, *id])) + { + device.get_field(typ).map(|val| Some(val)) + } else { + Ok(None) + } + }) + .collect::, ICError>>()? + .into_iter() + .filter_map(|val| { + val.map(|val| if val.is_nan() { None } else { Some(val) }) + .flatten() + }) + .collect_vec(); + match mode { + BatchMode::Sum => Ok(samples.iter().sum()), + BatchMode::Average => Ok(samples.iter().copied().sum::() / samples.len() as f64), + BatchMode::Minimum => Ok(*samples + .iter() + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(&0.0)), + BatchMode::Maximum => Ok(*samples + .iter() + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(&0.0)), + } + } + + pub fn get_batch_name_device_field( + &mut self, + source: u16, + prefab: f64, + name: f64, + typ: LogicType, + mode: BatchMode, + ) -> Result { + let networks = &self.networks; + let samples = self + .devices + .iter_mut() + .map(|(id, device)| { + if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + && Some(name) == device.name_hash + && networks + .iter() + .any(|(_net_id, net)| net.contains(&[source, *id])) + { + device.get_field(typ).map(|val| Some(val)) + } else { + Ok(None) + } + }) + .collect::, ICError>>()? + .into_iter() + .filter_map(|val| { + val.map(|val| if val.is_nan() { None } else { Some(val) }) + .flatten() + }) + .collect_vec(); + match mode { + BatchMode::Sum => Ok(samples.iter().sum()), + BatchMode::Average => Ok(samples.iter().copied().sum::() / samples.len() as f64), + BatchMode::Minimum => Ok(*samples + .iter() + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(&0.0)), + BatchMode::Maximum => Ok(*samples + .iter() + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(&0.0)), + } + } + + pub fn get_batch_name_device_slot_field( + &mut self, + source: u16, + prefab: f64, + name: f64, + index: f64, + typ: SlotLogicType, + mode: BatchMode, + ) -> Result { + let networks = &self.networks; + let samples = self + .devices + .iter_mut() + .map(|(id, device)| { + if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + && Some(name) == device.name_hash + && networks + .iter() + .any(|(_net_id, net)| net.contains(&[source, *id])) + { + device.get_slot_field(index, typ).map(|val| Some(val)) + } else { + Ok(None) + } + }) + .collect::, ICError>>()? + .into_iter() + .filter_map(|val| { + val.map(|val| if val.is_nan() { None } else { Some(val) }) + .flatten() + }) + .collect_vec(); + match mode { + BatchMode::Sum => Ok(samples.iter().sum()), + BatchMode::Average => Ok(samples.iter().copied().sum::() / samples.len() as f64), + BatchMode::Minimum => Ok(*samples + .iter() + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(&0.0)), + BatchMode::Maximum => Ok(*samples + .iter() + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(&0.0)), + } + } + + pub fn get_batch_device_slot_field( + &mut self, + source: u16, + prefab: f64, + index: f64, + typ: SlotLogicType, + mode: BatchMode, + ) -> Result { + let networks = &self.networks; + let samples = self + .devices + .iter_mut() + .map(|(id, device)| { + if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + && networks + .iter() + .any(|(_net_id, net)| net.contains(&[source, *id])) + { + device.get_slot_field(index, typ).map(|val| Some(val)) + } else { + Ok(None) + } + }) + .collect::, ICError>>()? + .into_iter() + .filter_map(|val| { + val.map(|val| if val.is_nan() { None } else { Some(val) }) + .flatten() + }) + .collect_vec(); + match mode { + BatchMode::Sum => Ok(samples.iter().sum()), + BatchMode::Average => Ok(samples.iter().copied().sum::() / samples.len() as f64), + BatchMode::Minimum => Ok(*samples + .iter() + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(&0.0)), + BatchMode::Maximum => Ok(*samples + .iter() + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(&0.0)), + } + } } diff --git a/ic10emu_wasm/Cargo.lock b/ic10emu_wasm/Cargo.lock index fffd9ce..584392b 100644 --- a/ic10emu_wasm/Cargo.lock +++ b/ic10emu_wasm/Cargo.lock @@ -80,7 +80,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.53", ] [[package]] @@ -139,10 +139,11 @@ dependencies = [ "convert_case", "getrandom", "itertools", - "phf", + "phf 0.11.2", "phf_codegen", "rand", "regex", + "strum", "strum_macros", "thiserror", "web-time", @@ -203,13 +204,24 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared 0.10.0", + "proc-macro-hack", +] + [[package]] name = "phf" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ - "phf_shared", + "phf_shared 0.11.2", ] [[package]] @@ -218,8 +230,18 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand", ] [[package]] @@ -228,10 +250,33 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ - "phf_shared", + "phf_shared 0.11.2", "rand", ] +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + [[package]] name = "phf_shared" version = "0.11.2" @@ -259,6 +304,12 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.79" @@ -357,6 +408,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "phf 0.10.1", + "strum_macros", +] + [[package]] name = "strum_macros" version = "0.26.2" @@ -367,7 +428,18 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.53", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -398,7 +470,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.53", ] [[package]] @@ -440,7 +512,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.53", "wasm-bindgen-shared", ] @@ -475,7 +547,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ]