From baf67b2c6a1b59fa25fb40e5a54f1258e84546bb Mon Sep 17 00:00:00 2001 From: Rachel <508861+Ryex@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:31:09 -0700 Subject: [PATCH] Vm working. simple interface built - still need stack view - still ned to work with devices --- ic10emu/Cargo.lock | 104 ++++- ic10emu/Cargo.toml | 15 +- ic10emu/build.rs | 2 +- ic10emu/src/grammar.rs | 61 +-- ic10emu/src/interpreter.rs | 572 ++++++++++++++++++++-------- ic10emu/src/lib.rs | 339 ++++++++++++----- ic10emu/src/rand_mscorlib.rs | 63 +-- ic10emu/src/tokens.rs | 6 +- ic10emu/verify_mscorlib_rand.ipynb | 147 +++++++ ic10emu_wasm/Cargo.lock | 427 ++++++++++++++++++++- ic10emu_wasm/Cargo.toml | 14 +- ic10emu_wasm/src/lib.rs | 315 ++++++++++++++- ic10emu_wasm/src/utils.rs | 6 +- ic10lsp_wasm/Cargo.lock | 4 +- ic10lsp_wasm/Cargo.toml | 3 +- www/src/index.html | 86 +++-- www/src/js/editor/ace.js | 2 + www/src/js/editor/index.js | 264 +++++++------ www/src/js/editor/lspWorker.js | 4 +- www/src/js/editor/ui.js | 167 ++++---- www/src/js/index.js | 8 +- www/src/js/session.js | 39 +- www/src/js/virtual_machine/index.js | 201 ++++++++++ www/src/scss/dark.scss | 30 ++ www/src/scss/styles.scss | 2 +- 25 files changed, 2295 insertions(+), 586 deletions(-) create mode 100644 ic10emu/verify_mscorlib_rand.ipynb create mode 100644 www/src/js/virtual_machine/index.js diff --git a/ic10emu/Cargo.lock b/ic10emu/Cargo.lock index 11fa26a..4e22aaf 100644 --- a/ic10emu/Cargo.lock +++ b/ic10emu/Cargo.lock @@ -38,6 +38,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "either" version = "1.10.0" @@ -75,10 +85,11 @@ dependencies = [ "phf_codegen", "rand", "regex", + "serde", "strum", "strum_macros", "thiserror", - "web-time", + "time", ] [[package]] @@ -90,6 +101,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "js-sys" version = "0.3.69" @@ -117,6 +134,21 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -205,6 +237,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -300,6 +338,26 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -371,6 +429,40 @@ dependencies = [ "syn 2.0.53", ] +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "js-sys", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -442,13 +534,3 @@ name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] diff --git a/ic10emu/Cargo.toml b/ic10emu/Cargo.toml index eccc4f8..4ce57c2 100644 --- a/ic10emu/Cargo.toml +++ b/ic10emu/Cargo.toml @@ -9,21 +9,30 @@ edition = "2021" crate-type = ["lib"] - - [dependencies] const-crc32 = "1.3.0" itertools = "0.12.1" phf = "0.11.2" rand = "0.8.5" regex = "1.10.3" +serde = { version = "1.0.197", features = ["derive"] } strum = { version = "0.26.2", features = ["derive", "phf", "strum_macros"] } strum_macros = "0.26.2" thiserror = "1.0.58" +time = { version = "0.3.34", features = [ + "formatting", + "serde", + "local-offset", +] } [target.'cfg(target_arch = "wasm32")'.dependencies] -web-time = "1.1.0" getrandom = { version = "0.2", features = ["js"] } +time = { version = "0.3.34", features = [ + "formatting", + "serde", + "local-offset", + "wasm-bindgen", +] } [build-dependencies] diff --git a/ic10emu/build.rs b/ic10emu/build.rs index 99bd640..3c5a452 100644 --- a/ic10emu/build.rs +++ b/ic10emu/build.rs @@ -44,7 +44,7 @@ fn write_repr_enum( 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\ + "#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash, EnumString, AsRefStr, EnumProperty, EnumIter, Serialize, Deserialize)]\n\ {additional_strum}\ pub enum {name} {{\n" ) diff --git a/ic10emu/src/grammar.rs b/ic10emu/src/grammar.rs index d900c1d..3e3e38e 100644 --- a/ic10emu/src/grammar.rs +++ b/ic10emu/src/grammar.rs @@ -1,5 +1,5 @@ use crate::interpreter; -use crate::tokens::SplitConsecutiveIndicesExt; +use crate::tokens::{SplitConsecutiveIndicesExt, SplitConsecutiveWithIndices}; use itertools::Itertools; use std::error::Error; use std::fmt::Display; @@ -16,6 +16,7 @@ pub mod generated { use strum::EnumProperty; use strum::EnumString; use strum::IntoEnumIterator; + use serde::{Deserialize, Serialize}; include!(concat!(env!("OUT_DIR"), "/instructions.rs")); include!(concat!(env!("OUT_DIR"), "/logictypes.rs")); @@ -85,8 +86,9 @@ pub mod generated { } pub use generated::*; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ParseError { pub line: usize, pub start: usize, @@ -233,29 +235,15 @@ impl FromStr for Instruction { }) } }?; - let mut operand_tokens = Vec::new(); - let mut string_start = None; - for (index, token) in tokens_iter { - if token.starts_with("HASH(\"") { - string_start = Some(index); - } - if let Some(start) = string_start { - if token.ends_with("\")") { - operand_tokens.push((start, &s[start..(index + token.len())])); - string_start = None; - } - } else { - operand_tokens.push((index, token)); - } - } - let operands = operand_tokens - .into_iter() + + let operands = get_operand_tokens(s, tokens_iter) + .iter() .map(|(index, token)| { token .parse::() - .map_err(|e| e.offset(index).span(token.len())) + .map_err(|e| e.offset(*index).span(token.len())) }) - .collect::, ParseError>>()?; + .try_collect()?; Ok(Instruction { instruction, operands, @@ -263,14 +251,33 @@ impl FromStr for Instruction { } } -#[derive(PartialEq, Debug, Clone, Copy)] +fn get_operand_tokens<'a>(s: &'a str, tokens_iter: SplitConsecutiveWithIndices<'a>) -> Vec<(usize, &'a str)> { + let mut operand_tokens = Vec::with_capacity(8); + let mut string_start = None; + for (index, token) in tokens_iter { + if token.starts_with("HASH(\"") { + string_start = Some(index); + } + if let Some(start) = string_start { + if token.ends_with("\")") { + operand_tokens.push((start, &s[start..(index + token.len())])); + string_start = None; + } + } else { + operand_tokens.push((index, token)); + } + } + operand_tokens +} + +#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)] pub enum Device { Db, Numbered(u32), Indirect { indirection: u32, target: u32 }, } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] pub enum Operand { RegisterSpec { indirection: u32, @@ -349,7 +356,7 @@ impl Operand { ) -> Result<(Option, Option), interpreter::ICError> { match &self { &Operand::DeviceSpec { device, connection } => match device { - Device::Db => Ok((Some(ic.id), *connection)), + Device::Db => Ok((Some(ic.device), *connection)), Device::Numbered(p) => { let dp = ic .pins @@ -598,6 +605,7 @@ impl FromStr for Operand { .collect::(); if !float_str.is_empty() { if rest_iter.peek() == Some(&&'.') { + rest_iter.next(); let decimal_str = rest_iter .take_while_ref(|c| c.is_digit(10)) .collect::(); @@ -684,7 +692,7 @@ impl FromStr for Label { } } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct Identifier { // #[rust_sitter::leaf(pattern = r"[a-zA-Z_.][\w\d.]*", transform = |id| id.to_string())] pub name: String, @@ -732,7 +740,7 @@ impl FromStr for Identifier { } } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] pub enum Number { Float(f64), Binary(f64), @@ -757,7 +765,6 @@ impl Number { #[cfg(test)] mod tests { - use super::generated::*; use super::*; #[test] diff --git a/ic10emu/src/interpreter.rs b/ic10emu/src/interpreter.rs index 7fa1648..f8d871f 100644 --- a/ic10emu/src/interpreter.rs +++ b/ic10emu/src/interpreter.rs @@ -1,112 +1,148 @@ use core::f64; +use serde::{Deserialize, Serialize}; use std::{ collections::{HashMap, HashSet}, + error::Error, + fmt::Display, u32, }; use itertools::Itertools; -#[cfg(target_arch = "wasm32")] -use web_time as time; -#[cfg(not(target_arch = "wasm32"))] -use std::time; +use time::format_description; use crate::grammar::{self, ParseError}; use thiserror::Error; -#[derive(Debug, Error)] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LineError { + error: ICError, + line: u32, +} + +impl Display for LineError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Error on line {}: {}", self.line, self.error) + } +} + +impl Error for LineError {} + +#[derive(Debug, Error, Clone, Serialize, Deserialize)] pub enum ICError { #[error("Error Compileing Code: {0}")] ParseError(#[from] ParseError), - #[error("")] + #[error("Duplicate label {0}")] DuplicateLabel(String), - #[error("")] + #[error("Instruction Pointer out of range: '{0}'")] InstructionPointerOutOfRange(u32), - #[error("")] + #[error("Register Pointer out of range: '{0}'")] RegisterIndexOutOfRange(f64), - #[error("")] + #[error("Device Pointer out of range: '{0}'")] DeviceIndexOutOfRange(f64), - #[error("")] + #[error("Stack index out of range: '{0}'")] StackIndexOutOfRange(f64), - #[error("")] + #[error("slot index out of range: '{0}'")] SlotIndexOutOfRange(f64), - #[error("")] + #[error("Unknown device ID '{0}'")] UnknownDeviceID(f64), - #[error("")] + #[error("Too few operands!: provide: '{provided}', desired: '{desired}'")] TooFewOperands { provided: u32, desired: u32 }, - #[error("")] + #[error("Too many operands!: provide: '{provided}', desired: '{desired}'")] TooManyOperands { provided: u32, desired: u32 }, - #[error("")] + #[error("Incorrect Operand Type for operand {index}, not a {desired} ")] IncorrectOperandType { index: u32, desired: String }, - #[error("")] + #[error("Unknown identifier '{0}")] UnknownIdentifier(String), - #[error("")] + #[error("A Device is not a Value")] DeviceNotValue, - #[error("")] + #[error("A Value is not a Device")] ValueNotDevice, - #[error("")] + #[error("Device Not Set")] DeviceNotSet, - #[error("")] - OperandNotRegister, - #[error("")] + #[error("Shift Underflow i64(signed long)")] ShiftUnderflowI64, - #[error("")] + #[error("Shift Overflow i64(signed long)")] ShiftOverflowI64, - #[error("")] + #[error("Shift Underflow i32(signed int)")] ShiftUnderflowI32, - #[error("")] + #[error("Shift Overflow i32(signed int)")] ShiftOverflowI32, - #[error("")] + #[error("Stack Underflow")] StackUnderflow, - #[error("")] + #[error("Stack Overflow")] StackOverflow, - #[error("")] + #[error("Duplicate Define '{0}'")] DuplicateDefine(String), - #[error("")] + #[error("Read Only field '{0}'")] ReadOnlyField(String), - #[error("")] + #[error("Write Only field '{0}'")] WriteOnlyField(String), - #[error("")] + #[error("Device Has No Field '{0}'")] DeviceHasNoField(String), - #[error("")] + #[error("Device has not IC")] DeviceHasNoIC, - #[error("")] + #[error("Unknown Device '{0}'")] UnknownDeviceId(f64), - #[error("")] + #[error("Unknown Logic Type '{0}'")] UnknownLogicType(f64), - #[error("")] + #[error("Unknown Slot Logic Type '{0}'")] UnknownSlotLogicType(f64), - #[error("")] + #[error("Unknown Batch Mode '{0}'")] UnknownBatchMode(f64), - #[error("")] + #[error("Unknown Reagent Mode '{0}'")] UnknownReagentMode(f64), - #[error("")] + #[error("Type Value Not Known")] TypeValueNotKnown, - #[error("")] + #[error("Empty Device List")] EmptyDeviceList, - #[error("")] + #[error("Connection index out of range: '{0}'")] ConnecitonIndexOutOFRange(u32), - #[error("")] + #[error("Connection specifier missing")] MissingConnecitonSpecifier, - #[error("")] + #[error("No data network on connection '{0}'")] NotDataConnection(u32), - #[error("")] + #[error("Network not connected on connection '{0}'")] NetworkNotConnected(u32), - #[error("")] + #[error("Bad Network Id '{0}'")] BadNetworkId(u32), } -#[derive(Debug)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum ICState { + Start, Running, Yield, - Sleep(time::SystemTime, f64), + Sleep(time::OffsetDateTime, f64), HasCaughtFire, + Error(LineError), +} + +impl Display for ICState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let out = match self { + ICState::Start => "Not Run".to_string(), + ICState::Running => "Running".to_string(), + ICState::Yield => "Ic has yielded, Resume on next tick".to_string(), + ICState::Sleep(then, sleep_for) => { + let format = format_description::parse("[hour]:[minute]:[second]").unwrap(); + let resume = then.clone() + time::Duration::new(*sleep_for as i64, 0); + format!( + "Sleeping for {sleep_for} seconds, will resume at {}", + resume.format(&format).unwrap() + ) + } + ICState::Error(err) => format!("{err}"), + ICState::HasCaughtFire => "IC has caught fire! this is not a joke!".to_string(), + }; + write!(f, "{out}") + } } #[derive(Debug)] pub struct IC { + pub device: u16, pub id: u16, pub registers: [f64; 18], pub ip: u32, @@ -181,8 +217,9 @@ impl Program { } impl IC { - pub fn new(id: u16) -> Self { + pub fn new(id: u16, device: u16) -> Self { IC { + device, id, ip: 0, ic: 0, @@ -193,7 +230,7 @@ impl IC { code: String::new(), aliases: HashMap::new(), defines: HashMap::new(), - state: ICState::Running, + state: ICState::Start, } } @@ -204,7 +241,7 @@ impl IC { self.stack = [0.0; 512]; self.aliases = HashMap::new(); self.defines = HashMap::new(); - self.state = ICState::Running; + self.state = ICState::Start; } pub fn set_code(&mut self, code: &str) -> Result<(), ICError> { @@ -279,7 +316,7 @@ impl IC { /// save ip to 'ra' or register 18 fn al(&mut self) { - self.registers[17] = self.ip as f64; + self.registers[17] = self.ip as f64 + 1.0; } fn push(&mut self, val: f64) -> Result { @@ -345,11 +382,21 @@ impl IC { } /// processes one line of the contained program - pub fn step( - &mut self, - _housing: &mut crate::Device, - vm: &mut crate::VM, - ) -> Result<(), ICError> { + pub fn step(&mut self, vm: &crate::VM) -> Result { + // TODO: handle sleep + self.state = ICState::Running; + let line = self.ip; + let result = self.internal_step(vm); + if let Err(error) = result { + let error = LineError { error, line }; + self.state = ICState::Error(error.clone()); + Err(error) + } else { + Ok(true) + } + } + + fn internal_step(&mut self, vm: &crate::VM) -> Result<(), ICError> { use grammar::*; use ICError::*; @@ -364,7 +411,8 @@ impl IC { Sleep => match &operands[..] { [a] => { let a = a.get_value(self)?; - let now = time::SystemTime::now(); + let now = time::OffsetDateTime::now_local() + .unwrap_or(time::OffsetDateTime::now_utc()); self.state = ICState::Sleep(now, a); Ok(()) } @@ -434,10 +482,7 @@ impl IC { indirection: *indirection, target: *target, }, - &Operand::DeviceSpec { - device, - connection, - } => Operand::DeviceSpec { + &Operand::DeviceSpec { device, connection } => Operand::DeviceSpec { device: *device, connection: *connection, }, @@ -1601,7 +1646,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -1624,7 +1672,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, if a == 0.0 { 1.0 } else { 0.0 })?; @@ -1646,7 +1697,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -1669,7 +1723,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, if a != 0.0 { 1.0 } else { 0.0 })?; @@ -1691,7 +1748,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -1714,7 +1774,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, if a < 0.0 { 1.0 } else { 0.0 })?; @@ -1736,7 +1799,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -1759,7 +1825,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, if a <= 0.0 { 1.0 } else { 0.0 })?; @@ -1781,7 +1850,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -1804,7 +1876,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, if a > 0.0 { 1.0 } else { 0.0 })?; @@ -1826,7 +1901,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -1849,7 +1927,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, if a >= 0.0 { 1.0 } else { 0.0 })?; @@ -1871,7 +1952,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -1905,7 +1989,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -1936,7 +2023,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -1970,7 +2060,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -2001,7 +2094,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let (device, _connection) = device.get_device_id(self)?; self.set_register( @@ -2027,7 +2123,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let (device, _connection) = device.get_device_id(self)?; self.set_register( @@ -2053,7 +2152,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, if a.is_nan() { 1.0 } else { 0.0 })?; @@ -2075,7 +2177,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, if a.is_nan() { 0.0 } else { 1.0 })?; @@ -2098,7 +2203,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -2123,7 +2231,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -2146,7 +2257,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -2169,7 +2283,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -2192,7 +2309,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -2215,7 +2335,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -2238,7 +2361,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::exp(a))?; @@ -2260,7 +2386,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::ln(a))?; @@ -2282,7 +2411,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::sqrt(a))?; @@ -2305,7 +2437,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -2328,7 +2463,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -2351,7 +2489,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::ceil(a))?; @@ -2373,7 +2514,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::floor(a))?; @@ -2395,7 +2539,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::abs(a))?; @@ -2417,7 +2564,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::round(a))?; @@ -2439,7 +2589,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::trunc(a))?; @@ -2458,9 +2611,12 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; - let val = vm.random.next_f64(); + let val = vm.random.clone().borrow_mut().next_f64(); self.set_register(indirection, target, val)?; Ok(()) } @@ -2481,7 +2637,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::sin(a))?; @@ -2503,7 +2662,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::cos(a))?; @@ -2525,7 +2687,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::tan(a))?; @@ -2547,7 +2712,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::asin(a))?; @@ -2569,7 +2737,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::acos(a))?; @@ -2591,7 +2762,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; self.set_register(indirection, target, f64::atan(a))?; @@ -2613,7 +2787,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value(self)?; let b = b.get_value(self)?; @@ -2637,7 +2814,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value_i64(self, true)?; let b = b.get_value_i32(self)?; @@ -2660,7 +2840,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value_i64(self, false)?; let b = b.get_value_i32(self)?; @@ -2683,7 +2866,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value_i64(self, true)?; let b = b.get_value_i32(self)?; @@ -2707,7 +2893,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value_i64(self, true)?; let b = b.get_value_i64(self, true)?; @@ -2730,7 +2919,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value_i64(self, true)?; let b = b.get_value_i64(self, true)?; @@ -2753,7 +2945,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value_i64(self, true)?; let b = b.get_value_i64(self, true)?; @@ -2776,7 +2971,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value_i64(self, true)?; let b = b.get_value_i64(self, true)?; @@ -2799,7 +2997,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let a = a.get_value_i64(self, true)?; self.set_register(indirection, target, i64_to_f64(!a))?; @@ -2829,7 +3030,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let val = self.pop()?; self.set_register(indirection, target, val)?; @@ -2863,7 +3067,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let val = self.peek()?; self.set_register(indirection, target, val)?; @@ -2886,16 +3093,20 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; 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); + let device = vm.get_device_same_network(self.device, device_id); match device { - Some(device) => match &mut device.ic { - Some(ic) => { + Some(device) => match device.borrow().ic.as_ref() { + Some(ic_id) => { let addr = addr.get_value(self)?; + let ic = vm.ics.get(&ic_id).unwrap().borrow(); let val = ic.peek_addr(addr)?; self.set_register(indirection, target, val)?; Ok(()) @@ -2921,16 +3132,20 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; 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); + let device = vm.get_device_same_network(self.device, device_id); match device { - Some(device) => match &mut device.ic { - Some(ic) => { + Some(device) => match device.borrow().ic.as_ref() { + Some(ic_id) => { let addr = addr.get_value(self)?; + let ic = vm.ics.get(&ic_id).unwrap().borrow(); let val = ic.peek_addr(addr)?; self.set_register(indirection, target, val)?; Ok(()) @@ -2954,12 +3169,13 @@ impl IC { 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); + let device = vm.get_device_same_network(self.device, device_id as u16); match device { - Some(device) => match &mut device.ic { - Some(ic) => { + Some(device) => match device.borrow().ic.as_ref() { + Some(ic_id) => { let addr = addr.get_value(self)?; let val = val.get_value(self)?; + let mut ic = vm.ics.get(&ic_id).unwrap().borrow_mut(); ic.poke(addr, val)?; Ok(()) } @@ -2983,12 +3199,13 @@ impl IC { 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); + let device = vm.get_device_same_network(self.device, device_id as u16); match device { - Some(device) => match &mut device.ic { - Some(ic) => { + Some(device) => match device.borrow().ic.as_ref() { + Some(ic_id) => { let addr = addr.get_value(self)?; let val = val.get_value(self)?; + let mut ic = vm.ics.get(&ic_id).unwrap().borrow_mut(); ic.poke(addr, val)?; Ok(()) } @@ -3019,18 +3236,18 @@ impl IC { 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)) + .get_device_same_network(self.device, device_id as u16) + .map(|device| device.borrow().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); + let device = vm.get_device_same_network(self.device, device_id as u16); match device { Some(device) => { let val = val.get_value(self)?; - device.set_field(lt, val)?; + device.borrow_mut().set_field(lt, val)?; Ok(()) } None => Err(UnknownDeviceID(device_id as f64)), @@ -3051,12 +3268,12 @@ impl IC { 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); + let device = vm.get_device_same_network(self.device, 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)?; + device.borrow_mut().set_field(lt, val)?; Ok(()) } None => Err(UnknownDeviceID(device_id as f64)), @@ -3076,13 +3293,13 @@ impl IC { 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); + let device = vm.get_device_same_network(self.device, 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)?; + device.borrow_mut().set_slot_field(index, lt, val)?; Ok(()) } None => Err(UnknownDeviceID(device_id as f64)), @@ -3102,7 +3319,7 @@ impl IC { 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)?; + vm.set_batch_device_field(self.device, prefab, lt, val)?; Ok(()) } oprs => Err(TooManyOperands { @@ -3120,7 +3337,7 @@ impl IC { 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)?; + vm.set_batch_device_slot_field(self.device, prefab, index, lt, val)?; Ok(()) } oprs => Err(TooManyOperands { @@ -3138,7 +3355,7 @@ impl IC { 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)?; + vm.set_batch_name_device_field(self.device, prefab, name, lt, val)?; Ok(()) } oprs => Err(TooManyOperands { @@ -3158,7 +3375,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let (Some(device_id), connection) = dev.get_device_id(self)? else { break 'inst Err(DeviceNotSet); @@ -3170,17 +3390,17 @@ impl IC { 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)) + .get_device_same_network(self.device, device_id as u16) + .map(|device| device.borrow().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); + let device = vm.get_device_same_network(self.device, device_id as u16); match device { Some(device) => { - let val = device.get_field(lt)?; + let val = device.borrow().get_field(lt)?; self.set_register(indirection, target, val)?; Ok(()) } @@ -3203,17 +3423,20 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; 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); + let device = vm.get_device_same_network(self.device, device_id as u16); match device { Some(device) => { let lt = LogicType::try_from(lt.get_value(self)?)?; - let val = device.get_field(lt)?; + let val = device.borrow().get_field(lt)?; self.set_register(indirection, target, val)?; Ok(()) } @@ -3236,17 +3459,20 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; 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); + let device = vm.get_device_same_network(self.device, 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)?; + let val = device.borrow().get_slot_field(index, lt)?; self.set_register(indirection, target, val)?; Ok(()) } @@ -3269,17 +3495,20 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; 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); + let device = vm.get_device_same_network(self.device, 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); + let val = device.borrow().get_reagent(&rm, name); self.set_register(indirection, target, val)?; Ok(()) } @@ -3302,12 +3531,15 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; 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)?; + let val = vm.get_batch_device_field(self.device, prefab, lt, bm)?; self.set_register(indirection, target, val)?; Ok(()) } @@ -3329,13 +3561,17 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; 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)?; + let val = + vm.get_batch_name_device_field(self.device, prefab, name, lt, bm)?; self.set_register(indirection, target, val)?; Ok(()) } @@ -3359,7 +3595,10 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; let prefab = prefab.get_value(self)?; let name = name.get_value(self)?; @@ -3367,7 +3606,12 @@ impl IC { 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.device, + prefab, + name, + index, + slt, + bm, )?; self.set_register(indirection, target, val)?; Ok(()) @@ -3390,14 +3634,17 @@ impl IC { target, } = reg else { - break 'inst Err(OperandNotRegister); + break 'inst Err(IncorrectOperandType { + index: 1, + desired: "Register".to_string(), + }); }; 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)?; + vm.get_batch_device_slot_field(self.device, prefab, index, slt, bm)?; self.set_register(indirection, target, val)?; Ok(()) } @@ -3408,6 +3655,7 @@ impl IC { }, } }; + self.ic += 1; self.ip = next_ip; result } diff --git a/ic10emu/src/lib.rs b/ic10emu/src/lib.rs index 3b1d9d1..45389e3 100644 --- a/ic10emu/src/lib.rs +++ b/ic10emu/src/lib.rs @@ -1,47 +1,56 @@ use core::f64; -use std::collections::{HashMap, HashSet}; +use std::{ + cell::RefCell, + collections::{HashMap, HashSet}, + rc::Rc, +}; -mod grammar; -mod interpreter; +pub mod grammar; +pub mod interpreter; mod rand_mscorlib; -mod tokens; +pub mod tokens; use grammar::{BatchMode, LogicType, ReagentMode, SlotLogicType}; -use interpreter::ICError; +use interpreter::{ICError, LineError}; use itertools::Itertools; +use serde::{Deserialize, Serialize}; use thiserror::Error; -#[derive(Error, Debug)] +#[derive(Error, Debug, Serialize, Deserialize)] pub enum VMError { #[error("Device with id '{0}' does not exist")] UnknownId(u16), + #[error("IC with id '{0}' does not exist")] + UnknownIcId(u16), #[error("Device with id '{0}' does not have a IC Slot")] NoIC(u16), #[error("IC encoutered an error: {0}")] ICError(#[from] ICError), + #[error("IC encoutered an error: {0}")] + LineError(#[from] LineError), #[error("Invalid network ID {0}")] InvalidNetwork(u16), } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum FieldType { Read, Write, ReadWrite, } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct LogicField { pub field_type: FieldType, pub value: f64, } -#[derive(Debug, Default)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct Slot { pub fields: HashMap, } -#[derive(Debug, Default, Clone, Copy)] +#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)] pub enum Connection { CableNetwork(Option), #[default] @@ -56,12 +65,12 @@ pub struct Device { pub fields: HashMap, pub slots: Vec, pub reagents: HashMap>, - pub ic: Option, + pub ic: Option, pub connections: [Connection; 8], pub prefab_hash: Option, } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct Network { pub devices: HashSet, pub channels: [f64; 8], @@ -74,7 +83,7 @@ struct IdSequenceGenerator { impl Default for IdSequenceGenerator { fn default() -> Self { - IdSequenceGenerator { next: 1 } + IdSequenceGenerator { next: 0 } } } @@ -88,13 +97,13 @@ impl IdSequenceGenerator { #[derive(Debug)] pub struct VM { - pub ics: HashSet, - pub devices: HashMap, - pub networks: HashMap, + pub ics: HashMap>>, + pub devices: HashMap>>, + pub networks: HashMap>>, pub default_network: u16, id_gen: IdSequenceGenerator, network_id_gen: IdSequenceGenerator, - random: crate::rand_mscorlib::Random, + random: Rc>, } impl Default for Network { @@ -146,7 +155,7 @@ impl Network { impl Device { pub fn new(id: u16) -> Self { - Device { + let mut device = Device { id, name: None, name_hash: None, @@ -156,12 +165,36 @@ impl Device { ic: None, connections: [Connection::default(); 8], prefab_hash: None, - } + }; + device.connections[0] = Connection::CableNetwork(None); + device } - pub fn with_ic(id: u16) -> Self { + pub fn with_ic(id: u16, ic: u16) -> Self { let mut device = Device::new(id); - device.ic = Some(interpreter::IC::new(id)); + device.ic = Some(ic); + device.fields.insert( + LogicType::Setting, + LogicField { + field_type: FieldType::ReadWrite, + value: 0.0, + }, + ); + device.fields.insert( + LogicType::Error, + LogicField { + field_type: FieldType::ReadWrite, + value: 0.0, + }, + ); + device.fields.insert( + LogicType::PrefabHash, + LogicField { + field_type: FieldType::Read, + value: -128473777.0, + }, + ); + device.prefab_hash = Some(-128473777); device } @@ -262,19 +295,19 @@ impl VM { pub fn new() -> Self { let id_gen = IdSequenceGenerator::default(); let mut network_id_gen = IdSequenceGenerator::default(); - let default_network = Network::default(); + let default_network = Rc::new(RefCell::new(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(), + ics: HashMap::new(), devices: HashMap::new(), networks, default_network: default_network_key, id_gen, network_id_gen, - random: crate::rand_mscorlib::Random::new(), + random: Rc::new(RefCell::new(crate::rand_mscorlib::Random::new())), }; let _ = vm.add_ic(None); vm @@ -284,8 +317,11 @@ impl VM { Device::new(self.id_gen.next()) } - fn new_ic(&mut self) -> Device { - Device::with_ic(self.id_gen.next()) + fn new_ic(&mut self) -> (Device, interpreter::IC) { + let id = self.id_gen.next(); + let ic = interpreter::IC::new(id, id); + let device = Device::with_ic(id, id); + (device, ic) } pub fn add_device(&mut self, network: Option) -> Result { @@ -314,7 +350,15 @@ impl VM { }); } let id = device.id; - self.devices.insert(id, device); + self.devices.insert(id, Rc::new(RefCell::new(device))); + let _ = self.add_device_to_network( + id, + if let Some(network) = network { + network + } else { + self.default_network + }, + ); Ok(id) } @@ -324,7 +368,7 @@ impl VM { return Err(VMError::InvalidNetwork(*n)); } } - let mut device = self.new_ic(); + let (mut device, ic) = self.new_ic(); if let Some(first_network) = device .connections .iter_mut() @@ -344,45 +388,117 @@ impl VM { }); } let id = device.id; - self.devices.insert(id, device); - self.ics.insert(id); + let ic_id = ic.id; + self.devices.insert(id, Rc::new(RefCell::new(device))); + self.ics.insert(ic_id, Rc::new(RefCell::new(ic))); + let _ = self.add_device_to_network( + id, + if let Some(network) = network { + network + } else { + self.default_network + }, + ); Ok(id) } pub fn add_network(&mut self) -> u16 { let next_id = self.network_id_gen.next(); - self.networks.insert(next_id, Network::default()); + self.networks + .insert(next_id, Rc::new(RefCell::new(Network::default()))); next_id } - pub fn get_default_network(&mut self) -> &mut Network { - self.networks.get_mut(&self.default_network).unwrap() + pub fn get_default_network(&self) -> Rc> { + self.networks.get(&self.default_network).cloned().unwrap() } - pub fn get_network(&mut self, id: u16) -> Option<&mut Network> { - self.networks.get_mut(&id) + pub fn get_network(&self, id: u16) -> Option>> { + self.networks.get(&id).cloned() } pub fn remove_ic(&mut self, id: u16) { - if self.ics.remove(&id) { + if self.ics.remove(&id).is_some() { self.devices.remove(&id); } } - pub fn set_code(&mut self, id: u16, code: &str) -> Result { - let device = self.devices.get_mut(&id).ok_or(VMError::UnknownId(id))?; - let ic = device.ic.as_mut().ok_or(VMError::NoIC(id))?; + pub fn set_code(&self, id: u16, code: &str) -> Result { + let device = self + .devices + .get(&id) + .ok_or(VMError::UnknownId(id))? + .borrow(); + let ic_id = *device.ic.as_ref().ok_or(VMError::NoIC(id))?; + let mut ic = self + .ics + .get(&ic_id) + .ok_or(VMError::UnknownIcId(ic_id))? + .borrow_mut(); let new_prog = interpreter::Program::try_from_code(code)?; ic.program = new_prog; + ic.ip = 0; ic.code = code.to_string(); Ok(true) } - pub fn get_device(&mut self, id: u16) -> Option<&mut Device> { - self.devices.get_mut(&id) + pub fn step_ic(&self, id: u16) -> Result { + let device = self.devices.get(&id).ok_or(VMError::UnknownId(id))?.clone(); + let ic_id = *device.borrow().ic.as_ref().ok_or(VMError::NoIC(id))?; + let ic = self + .ics + .get(&ic_id) + .ok_or(VMError::UnknownIcId(ic_id))? + .clone(); + ic.borrow_mut().ic = 0; + let result = ic.borrow_mut().step(self)?; + Ok(result) } - pub fn get_device_same_network(&mut self, source: u16, other: u16) -> Option<&mut Device> { + /// returns true if exacuted 128 lines, false if returned early. + pub fn run_ic(&self, id: u16, ignore_errors: bool) -> Result { + let device = self.devices.get(&id).ok_or(VMError::UnknownId(id))?.clone(); + let ic_id = *device.borrow().ic.as_ref().ok_or(VMError::NoIC(id))?; + let ic = self + .ics + .get(&ic_id) + .ok_or(VMError::UnknownIcId(ic_id))? + .clone(); + ic.borrow_mut().ic = 0; + for _i in 0..128 { + if let Err(err) = ic.borrow_mut().step(self) { + if !ignore_errors { + return Err(err.into()); + } + } + if let interpreter::ICState::Yield = ic.borrow().state { + return Ok(false); + } else if let interpreter::ICState::Sleep(_then, _sleep_for) = ic.borrow().state { + return Ok(false); + } + } + ic.borrow_mut().state = interpreter::ICState::Yield; + Ok(true) + } + + pub fn reset_ic(&self, id: u16) -> Result { + let device = self.devices.get(&id).ok_or(VMError::UnknownId(id))?.clone(); + let ic_id = *device.borrow().ic.as_ref().ok_or(VMError::NoIC(id))?; + let ic = self + .ics + .get(&ic_id) + .ok_or(VMError::UnknownIcId(ic_id))? + .clone(); + ic.borrow_mut().ic = 0; + ic.borrow_mut().reset(); + Ok(true) + } + + pub fn get_device(&self, id: u16) -> Option>> { + self.devices.get(&id).cloned() + } + + pub fn get_device_same_network(&self, source: u16, other: u16) -> Option>> { if self.devices_on_same_network(&[source, other]) { self.get_device(other) } else { @@ -395,29 +511,41 @@ impl VM { .networks .get(&(id as u16)) .ok_or(ICError::BadNetworkId(id as u32))?; - Ok(network.channels[channel]) + Ok(network.borrow().channels[channel]) } - pub fn set_network_channel(&mut self, id: usize, channel: usize, val: f64) -> Result<(), ICError> { + pub fn set_network_channel(&self, id: usize, channel: usize, val: f64) -> Result<(), ICError> { let network = self .networks - .get_mut(&(id as u16)) + .get(&(id as u16)) .ok_or(ICError::BadNetworkId(id as u32))?; - network.channels[channel] = val; + network.borrow_mut().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) { + if net.borrow().contains(ids) { return true; } } false } + fn add_device_to_network(&self, id: u16, network_id: u16) -> Result { + if !self.devices.contains_key(&id) { + return Err(VMError::UnknownId(id)); + }; + if let Some(network) = self.networks.get(&network_id) { + network.borrow_mut().add(id); + Ok(true) + } else { + Err(VMError::InvalidNetwork(network_id)) + } + } + pub fn set_batch_device_field( - &mut self, + &self, source: u16, prefab: f64, typ: LogicType, @@ -425,14 +553,19 @@ impl VM { ) -> Result<(), ICError> { let networks = &self.networks; self.devices - .iter_mut() + .iter() .map(|(id, device)| { - if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + if device + .borrow() + .fields + .get(&LogicType::PrefabHash) + .map(|f| f.value) + == Some(prefab) && networks .iter() - .any(|(_net_id, net)| net.contains(&[source, *id])) + .any(|(_net_id, net)| net.borrow().contains(&[source, *id])) { - device.set_field(typ, val) + device.clone().borrow_mut().set_field(typ, val) } else { Ok(()) } @@ -441,7 +574,7 @@ impl VM { } pub fn set_batch_device_slot_field( - &mut self, + &self, source: u16, prefab: f64, index: f64, @@ -450,14 +583,19 @@ impl VM { ) -> Result<(), ICError> { let networks = &self.networks; self.devices - .iter_mut() + .iter() .map(|(id, device)| { - if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + if device + .borrow() + .fields + .get(&LogicType::PrefabHash) + .map(|f| f.value) + == Some(prefab) && networks .iter() - .any(|(_net_id, net)| net.contains(&[source, *id])) + .any(|(_net_id, net)| net.borrow().contains(&[source, *id])) { - device.set_slot_field(index, typ, val) + device.borrow_mut().set_slot_field(index, typ, val) } else { Ok(()) } @@ -466,7 +604,7 @@ impl VM { } pub fn set_batch_name_device_field( - &mut self, + &self, source: u16, prefab: f64, name: f64, @@ -475,15 +613,20 @@ impl VM { ) -> Result<(), ICError> { let networks = &self.networks; self.devices - .iter_mut() + .iter() .map(|(id, device)| { - if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) - && Some(name) == device.name_hash + if device + .borrow() + .fields + .get(&LogicType::PrefabHash) + .map(|f| f.value) + == Some(prefab) + && Some(name) == device.borrow().name_hash && networks .iter() - .any(|(_net_id, net)| net.contains(&[source, *id])) + .any(|(_net_id, net)| net.borrow().contains(&[source, *id])) { - device.set_field(typ, val) + device.borrow_mut().set_field(typ, val) } else { Ok(()) } @@ -492,7 +635,7 @@ impl VM { } pub fn get_batch_device_field( - &mut self, + &self, source: u16, prefab: f64, typ: LogicType, @@ -501,14 +644,19 @@ impl VM { let networks = &self.networks; let samples = self .devices - .iter_mut() + .iter() .map(|(id, device)| { - if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + if device + .borrow() + .fields + .get(&LogicType::PrefabHash) + .map(|f| f.value) + == Some(prefab) && networks .iter() - .any(|(_net_id, net)| net.contains(&[source, *id])) + .any(|(_net_id, net)| net.borrow().contains(&[source, *id])) { - device.get_field(typ).map(|val| Some(val)) + device.borrow_mut().get_field(typ).map(|val| Some(val)) } else { Ok(None) } @@ -535,7 +683,7 @@ impl VM { } pub fn get_batch_name_device_field( - &mut self, + &self, source: u16, prefab: f64, name: f64, @@ -545,15 +693,20 @@ impl VM { let networks = &self.networks; let samples = self .devices - .iter_mut() + .iter() .map(|(id, device)| { - if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) - && Some(name) == device.name_hash + if device + .borrow() + .fields + .get(&LogicType::PrefabHash) + .map(|f| f.value) + == Some(prefab) + && Some(name) == device.borrow().name_hash && networks .iter() - .any(|(_net_id, net)| net.contains(&[source, *id])) + .any(|(_net_id, net)| net.borrow().contains(&[source, *id])) { - device.get_field(typ).map(|val| Some(val)) + device.borrow().get_field(typ).map(|val| Some(val)) } else { Ok(None) } @@ -580,7 +733,7 @@ impl VM { } pub fn get_batch_name_device_slot_field( - &mut self, + &self, source: u16, prefab: f64, name: f64, @@ -591,15 +744,23 @@ impl VM { let networks = &self.networks; let samples = self .devices - .iter_mut() + .iter() .map(|(id, device)| { - if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) - && Some(name) == device.name_hash + if device + .borrow() + .fields + .get(&LogicType::PrefabHash) + .map(|f| f.value) + == Some(prefab) + && Some(name) == device.borrow().name_hash && networks .iter() - .any(|(_net_id, net)| net.contains(&[source, *id])) + .any(|(_net_id, net)| net.borrow().contains(&[source, *id])) { - device.get_slot_field(index, typ).map(|val| Some(val)) + device + .borrow() + .get_slot_field(index, typ) + .map(|val| Some(val)) } else { Ok(None) } @@ -626,7 +787,7 @@ impl VM { } pub fn get_batch_device_slot_field( - &mut self, + &self, source: u16, prefab: f64, index: f64, @@ -636,14 +797,22 @@ impl VM { let networks = &self.networks; let samples = self .devices - .iter_mut() + .iter() .map(|(id, device)| { - if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab) + if device + .borrow() + .fields + .get(&LogicType::PrefabHash) + .map(|f| f.value) + == Some(prefab) && networks .iter() - .any(|(_net_id, net)| net.contains(&[source, *id])) + .any(|(_net_id, net)| net.borrow().contains(&[source, *id])) { - device.get_slot_field(index, typ).map(|val| Some(val)) + device + .borrow() + .get_slot_field(index, typ) + .map(|val| Some(val)) } else { Ok(None) } diff --git a/ic10emu/src/rand_mscorlib.rs b/ic10emu/src/rand_mscorlib.rs index 6d8e145..e449969 100644 --- a/ic10emu/src/rand_mscorlib.rs +++ b/ic10emu/src/rand_mscorlib.rs @@ -1,16 +1,15 @@ use std::usize; const MSEED: i32 = 161803398; -// const MZ: i32 = 0; +// const MZ: i32 = 0; #[derive(Debug)] pub struct Random { - inext: i32, - inextp: i32, + inext: usize, + inextp: usize, seed_array: [i32; 56], } - /// Partial implementation of mscorlib System.Random /// https://github.com/microsoft/referencesource/blob/master/mscorlib/system/random.cs#L94 impl Random { @@ -29,20 +28,20 @@ impl Random { seed_array[55] = mj; let mut mk: i32 = 1; - for i in 1_usize..55 { - let ii = (21 * i) % 55; + for i in 1..55 { + let ii = 21 * i % 55; seed_array[ii] = mk; - mk = mj - mk; + mk = mj.wrapping_sub(mk); if mk < 0 { - mk += i32::MAX; + mk = mk.wrapping_add(i32::MAX); } mj = seed_array[ii]; } for _k in 1_usize..5 { for i in 1_usize..56 { - seed_array[i] -= seed_array[1 + (i + 30) % 55]; + seed_array[i] = seed_array[i].wrapping_sub(seed_array[1 + (i + 30) % 55]); if seed_array[i] < 0 { - seed_array[i] += i32::MAX; + seed_array[i] = seed_array[i].wrapping_add(i32::MAX); } } } @@ -64,28 +63,24 @@ impl Random { let mut inext = self.inext; let mut inextp = self.inextp; - if { - inext += 1; - inext - } >= 56 - { + inext += 1; + + if inext >= 56 { inext = 1; } - if { - inextp += 1; - inextp - } >= 56 - { + inextp += 1; + if inextp >= 56 { inextp = 1; } - let mut retval = self.seed_array[inext as usize] - self.seed_array[inextp as usize]; + let mut retval = + self.seed_array[inext as usize].wrapping_sub(self.seed_array[inextp as usize]); if retval == i32::MAX { retval -= 1; } if retval < 0 { - retval += i32::MAX; + retval = retval.wrapping_add(i32::MAX); } self.seed_array[inext as usize] = retval; @@ -103,6 +98,26 @@ impl Random { pub fn next_f64(&mut self) -> f64 { self.sample() } - - +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn create() { + let mut rand = Random::with_seed(0); + assert_eq!(rand.next(), 1559595546); + assert_eq!(rand.next(), 1755192844); + } + + #[test] + fn verify() { + let mut rand = Random::with_seed(1919810); + assert_eq!(rand.next(), 147482110); + assert_eq!(rand.next(), 1747108798); + assert_eq!(rand.next(), 1937076328); + assert_eq!(rand.next(), 924982271); + assert_eq!(rand.next_f64(), 0.044092261252967765); + assert_eq!(rand.next(), 659561101); + } } diff --git a/ic10emu/src/tokens.rs b/ic10emu/src/tokens.rs index 9e0bf90..b33cb22 100644 --- a/ic10emu/src/tokens.rs +++ b/ic10emu/src/tokens.rs @@ -47,14 +47,16 @@ impl<'a> Iterator for SplitConsecutiveWithIndices<'a> { } } else { let s = &self.haystack[self.start..start]; + let index = self.start; self.start = start; - Some((self.start, s)) + Some((index, s)) } } None => { let s = &self.haystack[self.start..]; + let index = self.start; self.start = self.haystack.len(); - Some((self.start, s)) + Some((index, s)) } } } diff --git a/ic10emu/verify_mscorlib_rand.ipynb b/ic10emu/verify_mscorlib_rand.ipynb new file mode 100644 index 0000000..792f2fb --- /dev/null +++ b/ic10emu/verify_mscorlib_rand.ipynb @@ -0,0 +1,147 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
[ 1559595546, 1755192844 ]
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "var rand = new System.Random(0);\n", + "int[] two_rands = [rand.Next(), rand.Next()];\n", + "\n", + "two_rands" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
[ 147482110, 1747108798, 1937076328, 924982271, 0.044092261252967765, 659561101 ]
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "var rand = new System.Random(1919810);\n", + "double[] samples = [rand.Next(), rand.Next(), rand.Next(), rand.Next(), rand.NextDouble(), rand.Next()];\n", + "\n", + "samples" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "polyglot-notebook" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/ic10emu_wasm/Cargo.lock b/ic10emu_wasm/Cargo.lock index 584392b..21ec29c 100644 --- a/ic10emu_wasm/Cargo.lock +++ b/ic10emu_wasm/Cargo.lock @@ -12,10 +12,31 @@ dependencies = [ ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bumpalo" @@ -23,12 +44,31 @@ version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -54,12 +94,75 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.55", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.55", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "either" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "futures-core" version = "0.3.30" @@ -80,7 +183,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.55", ] [[package]] @@ -125,12 +228,53 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ic10emu" version = "0.1.0" @@ -143,10 +287,11 @@ dependencies = [ "phf_codegen", "rand", "regex", + "serde", "strum", "strum_macros", "thiserror", - "web-time", + "time", ] [[package]] @@ -155,13 +300,45 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "ic10emu", + "itertools", "js-sys", + "serde", + "serde-wasm-bindgen", + "serde_with", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + [[package]] name = "itertools" version = "0.12.1" @@ -171,6 +348,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "js-sys" version = "0.3.69" @@ -198,6 +381,30 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -298,6 +505,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -383,9 +596,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustversion" @@ -393,6 +606,84 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.55", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.55", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -408,6 +699,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.26.2" @@ -428,7 +725,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.53", + "syn 2.0.55", ] [[package]] @@ -444,9 +741,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.53" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", @@ -470,7 +767,41 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.55", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "js-sys", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", ] [[package]] @@ -512,7 +843,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.55", "wasm-bindgen-shared", ] @@ -547,7 +878,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.55", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -560,9 +891,9 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-streams" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", @@ -582,11 +913,67 @@ dependencies = [ ] [[package]] -name = "web-time" -version = "1.1.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "js-sys", - "wasm-bindgen", + "windows-targets", ] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" diff --git a/ic10emu_wasm/Cargo.toml b/ic10emu_wasm/Cargo.toml index c18bd6c..fcc9552 100644 --- a/ic10emu_wasm/Cargo.toml +++ b/ic10emu_wasm/Cargo.toml @@ -6,17 +6,27 @@ edition = "2021" [dependencies] ic10emu = { path = "../ic10emu" } -console_error_panic_hook = "0.1.7" +console_error_panic_hook = {version = "0.1.7", optional = true} js-sys = "0.3.69" web-sys = { version = "0.3.69", features = ["WritableStream", "console"] } wasm-bindgen = "0.2.81" wasm-bindgen-futures = { version = "0.4.30", features = [ "futures-core-03-stream", ] } -wasm-streams = "0.3" +wasm-streams = "0.4" +serde-wasm-bindgen = "0.6.5" +itertools = "0.12.1" +serde = { version = "1.0.197", features = ["derive"] } +serde_with = "3.7.0" + +[features] +default = ["console_error_panic_hook"] +console_error_panic_hook = ["dep:console_error_panic_hook"] [lib] crate-type = ["cdylib", "rlib"] + [profile.release] # Tell `rustc` to optimize for small code size. opt-level = "s" +lto = true diff --git a/ic10emu_wasm/src/lib.rs b/ic10emu_wasm/src/lib.rs index 1340e98..0b74980 100644 --- a/ic10emu_wasm/src/lib.rs +++ b/ic10emu_wasm/src/lib.rs @@ -1,9 +1,18 @@ - #[macro_use] mod utils; +use std::{cell::RefCell, rc::Rc}; + +use itertools::Itertools; +// use itertools::Itertools; +use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; -use ic10emu::VM; + +use serde_with::serde_as; + +#[serde_as] +#[derive(Serialize, Deserialize)] +struct Stack(#[serde_as(as = "[_; 512]")] [f64; 512]); #[wasm_bindgen] extern "C" { @@ -11,8 +20,302 @@ extern "C" { } #[wasm_bindgen] -pub fn init() { - utils::set_panic_hook(); - let _vm = VM::new(); - log!("Hello from ic10emu!"); +pub struct DeviceRef { + device: Rc>, + vm: Rc>, +} + +#[wasm_bindgen] +impl DeviceRef { + fn from_device(device: Rc>, vm: Rc>) -> Self { + DeviceRef { device, vm } + } + + #[wasm_bindgen(getter)] + pub fn id(&self) -> u16 { + self.device.borrow().id + } + + #[wasm_bindgen(getter)] + pub fn name(&self) -> Option { + self.device.borrow().name.clone() + } + + #[wasm_bindgen(getter, js_name = "nameHash")] + pub fn name_hash(&self) -> Option { + self.device.borrow().name_hash + } + + #[wasm_bindgen(getter)] + pub fn fields(&self) -> JsValue { + serde_wasm_bindgen::to_value(&self.device.borrow().fields).unwrap() + } + + #[wasm_bindgen(getter)] + pub fn slots(&self) -> JsValue { + serde_wasm_bindgen::to_value(&self.device.borrow().slots).unwrap() + } + + #[wasm_bindgen(getter)] + pub fn reagents(&self) -> JsValue { + serde_wasm_bindgen::to_value(&self.device.borrow().reagents).unwrap() + } + + #[wasm_bindgen(getter)] + pub fn connections(&self) -> JsValue { + serde_wasm_bindgen::to_value(&self.device.borrow().connections).unwrap() + } + + #[wasm_bindgen(getter, js_name = "prefabHash")] + pub fn prefab_hash(&self) -> Option { + self.device.borrow().prefab_hash + } + + #[wasm_bindgen(getter, js_name = "ip")] + pub fn ic_ip(&self) -> Option { + self.device + .borrow() + .ic + .as_ref() + .map(|ic| { + self.vm + .borrow() + .ics + .get(ic) + .map(|ic| ic.as_ref().borrow().ip) + }) + .flatten() + } + + #[wasm_bindgen(getter, js_name = "instructionCount")] + pub fn ic_instruction_count(&self) -> Option { + self.device + .borrow() + .ic + .as_ref() + .map(|ic| { + self.vm + .borrow() + .ics + .get(ic) + .map(|ic| ic.as_ref().borrow().ic) + }) + .flatten() + } + + #[wasm_bindgen(getter, js_name = "stack")] + pub fn ic_stack(&self) -> JsValue { + serde_wasm_bindgen::to_value( + &self + .device + .borrow() + .ic + .as_ref() + .map(|ic| { + self.vm + .borrow() + .ics + .get(ic) + .map(|ic| Stack(ic.as_ref().borrow().stack)) + }) + .flatten(), + ) + .unwrap() + } + + #[wasm_bindgen(getter, js_name = "registers")] + pub fn ic_registers(&self) -> JsValue { + serde_wasm_bindgen::to_value( + &self + .device + .borrow() + .ic + .as_ref() + .map(|ic| { + self.vm + .borrow() + .ics + .get(ic) + .map(|ic| ic.as_ref().borrow().registers) + }) + .flatten(), + ) + .unwrap() + } + + #[wasm_bindgen(getter, js_name = "aliases")] + pub fn ic_aliases(&self) -> JsValue { + serde_wasm_bindgen::to_value( + &self + .device + .borrow() + .ic + .as_ref() + .map(|ic| { + self.vm + .borrow() + .ics + .get(ic) + .map(|ic| ic.as_ref().borrow().aliases.clone()) + }) + .flatten(), + ) + .unwrap() + } + + #[wasm_bindgen(getter, js_name = "defines")] + pub fn ic_defines(&self) -> JsValue { + serde_wasm_bindgen::to_value( + &self + .device + .borrow() + .ic + .as_ref() + .map(|ic| { + self.vm + .borrow() + .ics + .get(ic) + .map(|ic| ic.as_ref().borrow().defines.clone()) + }) + .flatten(), + ) + .unwrap() + } + + #[wasm_bindgen(getter, js_name = "pins")] + pub fn ic_pins(&self) -> JsValue { + serde_wasm_bindgen::to_value( + &self + .device + .borrow() + .ic + .as_ref() + .map(|ic| { + self.vm + .borrow() + .ics + .get(ic) + .map(|ic| ic.as_ref().borrow().pins) + }) + .flatten(), + ) + .unwrap() + } + + #[wasm_bindgen(getter, js_name = "state")] + pub fn ic_state(&self) -> Option { + self + .device + .borrow() + .ic + .as_ref() + .map(|ic| { + self.vm + .borrow() + .ics + .get(ic) + .map(|ic| ic.borrow().state.clone()) + }) + .flatten() + .map(|state| state.to_string()) + } + + #[wasm_bindgen(js_name = "step")] + pub fn step_ic(&self) -> Result { + let id = self.device.borrow().id; + Ok(self.vm.borrow().step_ic(id)?) + } + + #[wasm_bindgen(js_name = "run")] + pub fn run_ic(&self, ignore_errors: bool) -> Result { + let id = self.device.borrow().id; + Ok(self.vm.borrow().run_ic(id, ignore_errors)?) + } + + #[wasm_bindgen(js_name = "reset")] + pub fn reset_ic(&self) -> Result { + let id = self.device.borrow().id; + Ok(self.vm.borrow().reset_ic(id)?) + } + + #[wasm_bindgen(js_name = "setCode")] + pub fn set_code(&self, code: &str) -> Result { + let id = self.device.borrow().id; + Ok(self.vm.borrow().set_code(id, code)?) + } +} + +#[wasm_bindgen] +#[derive(Debug)] +pub struct VM { + vm: Rc>, +} +#[wasm_bindgen] +impl VM { + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + VM { + vm: Rc::new(RefCell::new(ic10emu::VM::new())), + } + } + + #[wasm_bindgen(js_name = "addDevice")] + pub fn add_device(&self, network: Option) -> Result { + Ok(self.vm.borrow_mut().add_device(network)?) + } + + #[wasm_bindgen(js_name = "getDevice")] + pub fn get_device(&self, id: u16) -> Option { + let device = self.vm.borrow().get_device(id); + device.map(|d| DeviceRef::from_device(d.clone(), self.vm.clone())) + } + + #[wasm_bindgen(js_name = "setCode")] + pub fn set_code(&self, id: u16, code: &str) -> Result { + Ok(self.vm.borrow().set_code(id, code)?) + } + + #[wasm_bindgen(js_name = "stepIC")] + pub fn step_ic(&self, id: u16) -> Result { + Ok(self.vm.borrow().step_ic(id)?) + } + + #[wasm_bindgen(js_name = "runIC")] + pub fn run_ic(&self, id: u16, ignore_errors: bool) -> Result { + Ok(self.vm.borrow().run_ic(id, ignore_errors)?) + } + + #[wasm_bindgen(js_name = "resetIC")] + pub fn reset_ic(&self, id: u16) -> Result { + Ok(self.vm.borrow().reset_ic(id)?) + } + + #[wasm_bindgen(getter, js_name = "defaultNetwork")] + pub fn defualt_network(&self) -> u16 { + self.vm.borrow().default_network + } + + #[wasm_bindgen(getter)] + pub fn devices(&self) -> Vec { + self.vm.borrow().devices.keys().copied().collect_vec() + } + + #[wasm_bindgen(getter)] + pub fn networks(&self) -> Vec { + self.vm.borrow().networks.keys().copied().collect_vec() + } + + #[wasm_bindgen(getter)] + pub fn ics(&self) -> Vec { + self.vm.borrow().ics.keys().copied().collect_vec() + } +} + +#[wasm_bindgen] +pub fn init() -> VM { + utils::set_panic_hook(); + let vm = VM::new(); + log!("Hello from ic10emu!"); + vm } diff --git a/ic10emu_wasm/src/utils.rs b/ic10emu_wasm/src/utils.rs index dcdf7b3..745f704 100644 --- a/ic10emu_wasm/src/utils.rs +++ b/ic10emu_wasm/src/utils.rs @@ -6,7 +6,11 @@ pub fn set_panic_hook() { // For more details see // https://github.com/rustwasm/console_error_panic_hook#readme #[cfg(feature = "console_error_panic_hook")] - console_error_panic_hook::set_once(); + { + console_error_panic_hook::set_once(); + web_sys::console::log_1(&format!("Panic hook set...").into()); + } + } diff --git a/ic10lsp_wasm/Cargo.lock b/ic10lsp_wasm/Cargo.lock index 9b464d8..8245208 100644 --- a/ic10lsp_wasm/Cargo.lock +++ b/ic10lsp_wasm/Cargo.lock @@ -1174,9 +1174,9 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-streams" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", diff --git a/ic10lsp_wasm/Cargo.toml b/ic10lsp_wasm/Cargo.toml index 8e4acef..6e5b111 100644 --- a/ic10lsp_wasm/Cargo.toml +++ b/ic10lsp_wasm/Cargo.toml @@ -19,11 +19,12 @@ tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime # tree-sitter = { version = "0.9.0", package = "tree-sitter-facade" } wasm-bindgen = "0.2.81" wasm-bindgen-futures = { version = "0.4.30", features = ["futures-core-03-stream"] } -wasm-streams = "0.3" +wasm-streams = "0.4" # web-tree-sitter-sys = "1.3" ic10lsp = { git = "https://github.com/Ryex/ic10lsp.git", branch = "wasm" } [profile.release] # Tell `rustc` to optimize for small code size. opt-level = "s" +lto = true diff --git a/www/src/index.html b/www/src/index.html index f83a230..5204ec3 100644 --- a/www/src/index.html +++ b/www/src/index.html @@ -35,7 +35,7 @@ -
+