From cc6cc355dc67385bca0e4b407b85f06fa1a16e42 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 15 May 2024 22:32:46 -0700 Subject: [PATCH] refactor(vm): cleanup borrow usage, impl slot logic --- ic10emu/src/device.rs | 907 ------------------ ic10emu/src/errors.rs | 4 + ic10emu/src/interpreter/instructions.rs | 208 +++- ic10emu/src/lib.rs | 1 - ic10emu/src/network.rs | 15 +- ic10emu/src/vm.rs | 185 ++-- ic10emu/src/vm/instructions/operands.rs | 12 +- ic10emu/src/vm/object.rs | 1 + ic10emu/src/vm/object/generic/macros.rs | 20 +- ic10emu/src/vm/object/generic/structs.rs | 8 + ic10emu/src/vm/object/generic/traits.rs | 249 ++++- .../structs/integrated_circuit.rs | 40 +- ic10emu/src/vm/object/templates.rs | 99 +- ic10emu/src/vm/object/traits.rs | 56 +- 14 files changed, 687 insertions(+), 1118 deletions(-) delete mode 100644 ic10emu/src/device.rs diff --git a/ic10emu/src/device.rs b/ic10emu/src/device.rs deleted file mode 100644 index d191f47..0000000 --- a/ic10emu/src/device.rs +++ /dev/null @@ -1,907 +0,0 @@ -use crate::{ - errors::ICError, - interpreter::ICState, - network::{CableConnectionType, Connection}, - vm::enums::{ - basic_enums::{Class as SlotClass, SortingClass}, - script_enums::{LogicReagentMode as ReagentMode, LogicSlotType, LogicType}, - }, - vm::VM, -}; -use std::{collections::BTreeMap, ops::Deref}; - -use itertools::Itertools; - -use serde_derive::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum MemoryAccess { - Read, - Write, - ReadWrite, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct LogicField { - pub field_type: MemoryAccess, - pub value: f64, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SlotOccupant { - pub id: u32, - pub prefab_hash: i32, - pub quantity: u32, - pub max_quantity: u32, - pub sorting_class: SortingClass, - pub damage: f64, - fields: BTreeMap, -} - -impl SlotOccupant { - pub fn from_template(template: SlotOccupantTemplate, id_fn: F) -> Self - where - F: FnOnce() -> u32, - { - let mut fields = template.fields; - SlotOccupant { - id: template.id.unwrap_or_else(id_fn), - prefab_hash: fields - .remove(&LogicSlotType::PrefabHash) - .map(|field| field.value as i32) - .unwrap_or(0), - quantity: fields - .remove(&LogicSlotType::Quantity) - .map(|field| field.value as u32) - .unwrap_or(1), - max_quantity: fields - .remove(&LogicSlotType::MaxQuantity) - .map(|field| field.value as u32) - .unwrap_or(1), - damage: fields - .remove(&LogicSlotType::Damage) - .map(|field| field.value) - .unwrap_or(0.0), - sorting_class: fields - .remove(&LogicSlotType::SortingClass) - .map(|field| SortingClass::from_repr(field.value as u8)) - .flatten() - .unwrap_or(SortingClass::Default), - fields, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SlotOccupantTemplate { - pub id: Option, - pub fields: BTreeMap, -} - -impl SlotOccupant { - pub fn new(id: u32, prefab_hash: i32) -> Self { - SlotOccupant { - id, - prefab_hash, - quantity: 1, - max_quantity: 1, - damage: 0.0, - sorting_class: SortingClass::Default, - fields: BTreeMap::new(), - } - } - - /// chainable constructor - pub fn with_quantity(mut self, quantity: u32) -> Self { - self.quantity = quantity; - self - } - - /// chainable constructor - pub fn with_max_quantity(mut self, max_quantity: u32) -> Self { - self.max_quantity = max_quantity; - self - } - - /// chainable constructor - pub fn with_damage(mut self, damage: f64) -> Self { - self.damage = damage; - self - } - - /// chainable constructor - pub fn with_fields(mut self, fields: BTreeMap) -> Self { - self.fields.extend(fields); - self - } - - /// chainable constructor - pub fn get_fields(&self) -> BTreeMap { - let mut copy = self.fields.clone(); - copy.insert( - LogicSlotType::PrefabHash, - LogicField { - field_type: MemoryAccess::Read, - value: self.prefab_hash as f64, - }, - ); - copy.insert( - LogicSlotType::SortingClass, - LogicField { - field_type: MemoryAccess::Read, - value: self.sorting_class as u32 as f64, - }, - ); - copy.insert( - LogicSlotType::Quantity, - LogicField { - field_type: MemoryAccess::Read, - value: self.quantity as f64, - }, - ); - copy.insert( - LogicSlotType::MaxQuantity, - LogicField { - field_type: MemoryAccess::Read, - value: self.max_quantity as f64, - }, - ); - copy.insert( - LogicSlotType::Damage, - LogicField { - field_type: MemoryAccess::Read, - value: self.damage, - }, - ); - copy - } - - pub fn set_field(&mut self, typ: LogicSlotType, val: f64, force: bool) -> Result<(), ICError> { - if (typ == LogicSlotType::Quantity) && force { - self.quantity = val as u32; - Ok(()) - } else if (typ == LogicSlotType::MaxQuantity) && force { - self.max_quantity = val as u32; - Ok(()) - } else if (typ == LogicSlotType::Damage) && force { - self.damage = val; - Ok(()) - } else if let Some(logic) = self.fields.get_mut(&typ) { - match logic.field_type { - MemoryAccess::ReadWrite | MemoryAccess::Write => { - logic.value = val; - Ok(()) - } - _ => { - if force { - logic.value = val; - Ok(()) - } else { - Err(ICError::ReadOnlyField(typ.to_string())) - } - } - } - } else if force { - self.fields.insert( - typ, - LogicField { - field_type: MemoryAccess::ReadWrite, - value: val, - }, - ); - Ok(()) - } else { - Err(ICError::ReadOnlyField(typ.to_string())) - } - } - - pub fn can_logic_read(&self, field: LogicSlotType) -> bool { - if let Some(logic) = self.fields.get(&field) { - matches!( - logic.field_type, - MemoryAccess::Read | MemoryAccess::ReadWrite - ) - } else { - false - } - } - - pub fn can_logic_write(&self, field: LogicSlotType) -> bool { - if let Some(logic) = self.fields.get(&field) { - matches!( - logic.field_type, - MemoryAccess::Write | MemoryAccess::ReadWrite - ) - } else { - false - } - } -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct Slot { - pub typ: SlotClass, - pub occupant: Option, -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct SlotTemplate { - pub typ: SlotClass, - pub occupant: Option, -} - -impl Slot { - pub fn new(typ: SlotClass) -> Self { - Slot { - typ, - occupant: None, - } - } - pub fn with_occupant(typ: SlotClass, occupant: SlotOccupant) -> Self { - Slot { - typ, - occupant: Some(occupant), - } - } - - pub fn get_fields(&self) -> BTreeMap { - let mut copy = self - .occupant - .as_ref() - .map(|occupant| occupant.get_fields()) - .unwrap_or_default(); - copy.insert( - LogicSlotType::Occupied, - LogicField { - field_type: MemoryAccess::Read, - value: if self.occupant.is_some() { 1.0 } else { 0.0 }, - }, - ); - copy.insert( - LogicSlotType::OccupantHash, - LogicField { - field_type: MemoryAccess::Read, - value: self - .occupant - .as_ref() - .map(|occupant| occupant.prefab_hash as f64) - .unwrap_or(0.0), - }, - ); - copy.insert( - LogicSlotType::Quantity, - LogicField { - field_type: MemoryAccess::Read, - value: self - .occupant - .as_ref() - .map(|occupant| occupant.quantity as f64) - .unwrap_or(0.0), - }, - ); - copy.insert( - LogicSlotType::Damage, - LogicField { - field_type: MemoryAccess::Read, - value: self - .occupant - .as_ref() - .map(|occupant| occupant.damage) - .unwrap_or(0.0), - }, - ); - copy.insert( - LogicSlotType::Class, - LogicField { - field_type: MemoryAccess::Read, - value: self.typ as u32 as f64, - }, - ); - copy.insert( - LogicSlotType::MaxQuantity, - LogicField { - field_type: MemoryAccess::Read, - value: self - .occupant - .as_ref() - .map(|occupant| occupant.max_quantity as f64) - .unwrap_or(0.0), - }, - ); - copy.insert( - LogicSlotType::PrefabHash, - LogicField { - field_type: MemoryAccess::Read, - value: self - .occupant - .as_ref() - .map(|occupant| occupant.prefab_hash as f64) - .unwrap_or(0.0), - }, - ); - copy.insert( - LogicSlotType::SortingClass, - LogicField { - field_type: MemoryAccess::Read, - value: self - .occupant - .as_ref() - .map(|occupant| occupant.sorting_class as u32 as f64) - .unwrap_or(0.0), - }, - ); - copy.insert( - LogicSlotType::ReferenceId, - LogicField { - field_type: MemoryAccess::Read, - value: self - .occupant - .as_ref() - .map(|occupant| occupant.id as f64) - .unwrap_or(0.0), - }, - ); - copy - } - - pub fn get_field(&self, field: LogicSlotType) -> f64 { - let fields = self.get_fields(); - fields - .get(&field) - .map(|field| match field.field_type { - MemoryAccess::Read | MemoryAccess::ReadWrite => field.value, - _ => 0.0, - }) - .unwrap_or(0.0) - } - - pub fn can_logic_read(&self, field: LogicSlotType) -> bool { - match field { - LogicSlotType::Pressure | LogicSlotType::Temperature | LogicSlotType::Volume => { - matches!( - self.typ, - SlotClass::GasCanister | SlotClass::LiquidCanister | SlotClass::LiquidBottle - ) - } - LogicSlotType::Charge | LogicSlotType::ChargeRatio => { - matches!(self.typ, SlotClass::Battery) - } - LogicSlotType::Open => matches!( - self.typ, - SlotClass::Helmet | SlotClass::Tool | SlotClass::Appliance - ), - LogicSlotType::Lock => matches!(self.typ, SlotClass::Helmet), - LogicSlotType::FilterType => matches!(self.typ, SlotClass::GasFilter), - _ => { - if let Some(occupant) = self.occupant.as_ref() { - occupant.can_logic_read(field) - } else { - false - } - } - } - } - - pub fn can_logic_write(&self, field: LogicSlotType) -> bool { - match field { - LogicSlotType::Open => matches!( - self.typ, - SlotClass::Helmet - | SlotClass::GasCanister - | SlotClass::LiquidCanister - | SlotClass::LiquidBottle - ), - LogicSlotType::On => matches!( - self.typ, - SlotClass::Helmet | SlotClass::Tool | SlotClass::Appliance - ), - LogicSlotType::Lock => matches!(self.typ, SlotClass::Helmet), - _ => { - if let Some(occupant) = self.occupant.as_ref() { - occupant.can_logic_write(field) - } else { - false - } - } - } - } - - pub fn set_field(&mut self, typ: LogicSlotType, val: f64, force: bool) -> Result<(), ICError> { - if matches!( - typ, - LogicSlotType::Occupied - | LogicSlotType::OccupantHash - | LogicSlotType::Class - | LogicSlotType::PrefabHash - | LogicSlotType::SortingClass - | LogicSlotType::ReferenceId - ) { - return Err(ICError::ReadOnlyField(typ.to_string())); - } - if let Some(occupant) = self.occupant.as_mut() { - occupant.set_field(typ, val, force) - } else { - Err(ICError::SlotNotOccupied) - } - } -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct Prefab { - pub name: String, - pub hash: i32, -} - -impl Prefab { - pub fn new(name: &str) -> Self { - Prefab { - name: name.to_owned(), - hash: const_crc32::crc32(name.as_bytes()) as i32, - } - } -} - -#[derive(Debug, Default)] -pub struct Device { - pub id: u32, - pub name: Option, - pub name_hash: Option, - pub prefab: Option, - pub slots: Vec, - pub reagents: BTreeMap>, - pub ic: Option, - pub connections: Vec, - fields: BTreeMap, -} - -impl Device { - pub fn new(id: u32) -> Self { - Device { - id, - name: None, - name_hash: None, - prefab: None, - fields: BTreeMap::new(), - slots: Vec::new(), - reagents: BTreeMap::new(), - ic: None, - connections: vec![Connection::CableNetwork { - net: None, - typ: CableConnectionType::default(), - }], - } - } - - pub fn with_ic(id: u32, ic: u32) -> Self { - let mut device = Device::new(id); - device.ic = Some(ic); - device.connections = vec![ - Connection::CableNetwork { - net: None, - typ: CableConnectionType::Data, - }, - Connection::CableNetwork { - net: None, - typ: CableConnectionType::Power, - }, - ]; - device.prefab = Some(Prefab::new("StructureCircuitHousing")); - device.fields.extend(vec![ - ( - LogicType::Setting, - LogicField { - field_type: MemoryAccess::ReadWrite, - value: 0.0, - }, - ), - ( - LogicType::RequiredPower, - LogicField { - field_type: MemoryAccess::Read, - value: 0.0, - }, - ), - ( - LogicType::PrefabHash, - LogicField { - field_type: MemoryAccess::Read, - value: -128473777.0, - }, - ), - ]); - let occupant = SlotOccupant::new(ic, -744098481); - device.slots.push(Slot::with_occupant( - SlotClass::ProgrammableChip, - // -744098481 = ItemIntegratedCircuit10 - occupant, - )); - - device - } - - pub fn get_fields(&self, vm: &VM) -> BTreeMap { - let mut copy = self.fields.clone(); - if let Some(ic_id) = &self.ic { - let ic = vm - .circuit_holders - .get(ic_id) - .expect("our own ic to exist") - .borrow(); - copy.insert( - LogicType::LineNumber, - LogicField { - field_type: MemoryAccess::ReadWrite, - value: ic.ip() as f64, - }, - ); - copy.insert( - LogicType::Error, - LogicField { - field_type: MemoryAccess::Read, - value: match *ic.state.borrow() { - ICState::Error(_) => 1.0, - _ => 0.0, - }, - }, - ); - } - if self.has_power_state() { - copy.insert( - LogicType::Power, - LogicField { - field_type: MemoryAccess::Read, - value: if self.has_power_connection() { - 1.0 - } else { - 0.0 - }, - }, - ); - } - copy.insert( - LogicType::ReferenceId, - LogicField { - field_type: MemoryAccess::Read, - value: self.id as f64, - }, - ); - copy - } - - pub fn get_network_id(&self, connection: usize) -> Result { - if connection >= self.connections.len() { - Err(ICError::ConnectionIndexOutOfRange( - connection, - self.connections.len(), - )) - } else if let Connection::CableNetwork { - net: network_id, .. - } = self.connections[connection] - { - if let Some(network_id) = network_id { - Ok(network_id) - } else { - Err(ICError::NetworkNotConnected(connection)) - } - } else { - Err(ICError::NotACableConnection(connection)) - } - } - - pub fn can_logic_read(&self, field: LogicType) -> bool { - match field { - LogicType::ReferenceId => true, - LogicType::LineNumber | LogicType::Error if self.ic.is_some() => true, - LogicType::Power if self.has_power_state() => true, - _ => { - if let Some(logic) = self.fields.get(&field) { - matches!( - logic.field_type, - MemoryAccess::Read | MemoryAccess::ReadWrite - ) - } else { - false - } - } - } - } - - pub fn can_logic_write(&self, field: LogicType) -> bool { - match field { - LogicType::ReferenceId => false, - LogicType::LineNumber if self.ic.is_some() => true, - _ => { - if let Some(logic) = self.fields.get(&field) { - matches!( - logic.field_type, - MemoryAccess::Write | MemoryAccess::ReadWrite - ) - } else { - false - } - } - } - } - - pub fn can_slot_logic_read(&self, field: LogicSlotType, slot: usize) -> bool { - if self.slots.is_empty() { - return false; - } - let Some(slot) = self.slots.get(slot) else { - return false; - }; - slot.can_logic_read(field) - } - - pub fn can_slot_logic_write(&self, field: LogicSlotType, slot: usize) -> bool { - if self.slots.is_empty() { - return false; - } - let Some(slot) = self.slots.get(slot) else { - return false; - }; - slot.can_logic_write(field) - } - - pub fn get_field(&self, typ: LogicType, vm: &VM) -> Result { - if typ == LogicType::LineNumber && self.ic.is_some() { - let ic = vm - .circuit_holders - .get(&self.ic.unwrap()) - .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? - .borrow(); - Ok(ic.ip() as f64) - } else if let Some(field) = self.get_fields(vm).get(&typ) { - if field.field_type == MemoryAccess::Read || field.field_type == MemoryAccess::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: LogicType, - val: f64, - vm: &VM, - force: bool, - ) -> Result<(), ICError> { - if typ == LogicType::ReferenceId - || (typ == LogicType::Error && self.ic.is_some()) - || (typ == LogicType::Power && self.has_power_state()) - { - Err(ICError::ReadOnlyField(typ.to_string())) - } else if typ == LogicType::LineNumber && self.ic.is_some() { - let ic = vm - .circuit_holders - .get(&self.ic.unwrap()) - .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? - .borrow(); - ic.set_ip(val as u32); - Ok(()) - } else if let Some(field) = self.fields.get_mut(&typ) { - if field.field_type == MemoryAccess::Write - || field.field_type == MemoryAccess::ReadWrite - || force - { - field.value = val; - Ok(()) - } else { - Err(ICError::ReadOnlyField(typ.to_string())) - } - } else if force { - self.fields.insert( - typ, - LogicField { - field_type: MemoryAccess::ReadWrite, - value: val, - }, - ); - Ok(()) - } else { - Err(ICError::DeviceHasNoField(typ.to_string())) - } - } - - pub fn get_slot_field(&self, index: f64, typ: LogicSlotType, vm: &VM) -> Result { - let slot = self - .slots - .get(index as usize) - .ok_or(ICError::SlotIndexOutOfRange(index))?; - if slot.typ == SlotClass::ProgrammableChip - && slot.occupant.is_some() - && self.ic.is_some() - && typ == LogicSlotType::LineNumber - { - let ic = vm - .circuit_holders - .get(&self.ic.unwrap()) - .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? - .borrow(); - Ok(ic.ip() as f64) - } else { - Ok(slot.get_field(typ)) - } - } - - pub fn get_slot_fields( - &self, - index: f64, - vm: &VM, - ) -> Result, ICError> { - let slot = self - .slots - .get(index as usize) - .ok_or(ICError::SlotIndexOutOfRange(index))?; - let mut fields = slot.get_fields(); - if slot.typ == SlotClass::ProgrammableChip && slot.occupant.is_some() && self.ic.is_some() { - let ic = vm - .circuit_holders - .get(&self.ic.unwrap()) - .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? - .borrow(); - fields.insert( - LogicSlotType::LineNumber, - LogicField { - field_type: MemoryAccess::ReadWrite, - value: ic.ip() as f64, - }, - ); - } - Ok(fields) - } - - pub fn set_slot_field( - &mut self, - index: f64, - typ: LogicSlotType, - val: f64, - _vm: &VM, - force: bool, - ) -> Result<(), ICError> { - let slot = self - .slots - .get_mut(index as usize) - .ok_or(ICError::SlotIndexOutOfRange(index))?; - slot.set_field(typ, val, force) - } - - pub fn get_slot(&self, index: f64) -> Result<&Slot, ICError> { - self.slots - .get(index as usize) - .ok_or(ICError::SlotIndexOutOfRange(index)) - } - - 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 - } - - pub fn set_name(&mut self, name: &str) { - self.name_hash = Some(const_crc32::crc32(name.as_bytes()) as i32); - self.name = Some(name.to_owned()); - } - - pub fn has_power_state(&self) -> bool { - self.connections.iter().any(|conn| { - matches!( - conn, - Connection::CableNetwork { - typ: CableConnectionType::Power | CableConnectionType::PowerAndData, - .. - } - ) - }) - } - - pub fn has_power_connection(&self) -> bool { - self.connections.iter().any(|conn| { - matches!( - conn, - Connection::CableNetwork { - net: Some(_), - typ: CableConnectionType::Power | CableConnectionType::PowerAndData, - } - ) - }) - } -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct DeviceTemplate { - pub id: Option, - pub name: Option, - pub prefab_name: Option, - pub slots: Vec, - // pub reagents: BTreeMap>, - pub connections: Vec, - pub fields: BTreeMap, -} - -impl Device { - /// create a devive from a template and return the device, does not create it's own IC - pub fn from_template(template: DeviceTemplate, mut id_fn: F) -> Self - where - F: FnMut() -> u32, - { - // id_fn *must* be captured not moved - #[allow(clippy::redundant_closure)] - let device_id = template.id.unwrap_or_else(|| id_fn()); - let name_hash = template - .name - .as_ref() - .map(|name| const_crc32::crc32(name.as_bytes()) as i32); - - #[allow(clippy::redundant_closure)] - let slots = template - .slots - .into_iter() - .map(|slot| Slot { - typ: slot.typ, - occupant: slot - .occupant - .map(|occupant| SlotOccupant::from_template(occupant, || id_fn())), - }) - .collect_vec(); - - let ic = slots - .iter() - .find_map(|slot| { - if slot.typ == SlotClass::ProgrammableChip && slot.occupant.is_some() { - Some(slot.occupant.clone()).flatten() - } else { - None - } - }) - .map(|occupant| occupant.id); - - let fields = template.fields; - - Device { - id: device_id, - name: template.name, - name_hash, - prefab: template.prefab_name.map(|name| Prefab::new(&name)), - slots, - // reagents: template.reagents, - reagents: BTreeMap::new(), - ic, - connections: template.connections, - fields, - } - } -} - -impl From for DeviceTemplate -where - T: Deref, -{ - fn from(device: T) -> Self { - DeviceTemplate { - id: Some(device.id), - name: device.name.clone(), - prefab_name: device.prefab.as_ref().map(|prefab| prefab.name.clone()), - slots: device - .slots - .iter() - .map(|slot| SlotTemplate { - typ: slot.typ, - occupant: slot.occupant.as_ref().map(|occupant| SlotOccupantTemplate { - id: Some(occupant.id), - fields: occupant.get_fields(), - }), - }) - .collect_vec(), - connections: device.connections.clone(), - fields: device.fields.clone(), - } - } -} diff --git a/ic10emu/src/errors.rs b/ic10emu/src/errors.rs index 1b74d5b..4259192 100644 --- a/ic10emu/src/errors.rs +++ b/ic10emu/src/errors.rs @@ -208,10 +208,14 @@ pub enum ICError { BadGeneratedValueParse(String, String), #[error("IC with id {0} is not sloted into a circuit holder")] NoCircuitHolder(ObjectID), + #[error("IC with id {0} is sloted into a circuit holder with no logic interface?")] + CircuitHolderNotLogicable(ObjectID), #[error("object {0} is not slot writeable")] NotSlotWriteable(ObjectID), #[error("object {0} does not use reagents ")] NotReagentReadable(ObjectID), + #[error("object {0} is not slot logicable")] + NotLogicable(ObjectID), #[error("{0} is not a valid number of sleep seconds")] SleepDurationError(f64), #[error("{0} can not be added to {1} ")] diff --git a/ic10emu/src/interpreter/instructions.rs b/ic10emu/src/interpreter/instructions.rs index 6fcb4c0..82d5833 100644 --- a/ic10emu/src/interpreter/instructions.rs +++ b/ic10emu/src/interpreter/instructions.rs @@ -7,7 +7,11 @@ use crate::{ operands::{InstOperand, RegisterSpec}, traits::*, }, - object::{errors::MemoryError, traits::*, ObjectID}, + object::{ + errors::{LogicError, MemoryError}, + traits::*, + ObjectID, + }, }, }; pub trait IC10Marker: IntegratedCircuit {} @@ -862,6 +866,9 @@ impl BdseInstruction for T { if self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection) .is_some() { @@ -879,6 +886,9 @@ impl BdsealInstruction for T { if self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection) .is_some() { @@ -897,6 +907,9 @@ impl BrdseInstruction for T { if self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection) .is_some() { @@ -914,6 +927,9 @@ impl BdnsInstruction for T { if self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection) .is_none() { @@ -931,6 +947,9 @@ impl BdnsalInstruction for T { if self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection) .is_none() { @@ -949,6 +968,9 @@ impl BrdnsInstruction for T { if self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection) .is_none() { @@ -1319,15 +1341,14 @@ impl SdseInstruction for T { target, } = r.as_register(self)?; let (device, connection) = d.as_device(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection); - self.set_register( - indirection, - target, - if logicable.is_some() { 1.0 } else { 0.0 }, - )?; + self.set_register(indirection, target, if obj.is_some() { 1.0 } else { 0.0 })?; Ok(()) } } @@ -1339,15 +1360,14 @@ impl SdnsInstruction for T { target, } = r.as_register(self)?; let (device, connection) = d.as_device(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection); - self.set_register( - indirection, - target, - if logicable.is_none() { 1.0 } else { 0.0 }, - )?; + self.set_register(indirection, target, if obj.is_none() { 1.0 } else { 0.0 })?; Ok(()) } } @@ -1968,12 +1988,16 @@ impl GetInstruction for T { } = r.as_register(self)?; let address = address.as_value(self)?; let (device, connection) = d.as_device(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection) .ok_or(ICError::DeviceNotSet)?; - let memory = logicable + let obj_ref = obj.borrow(); + let memory = obj_ref .as_memory_readable() .ok_or(MemoryError::NotReadable)?; self.set_register(indirection, target, memory.get_memory(address as i32)?)?; @@ -1995,12 +2019,16 @@ impl GetdInstruction for T { } = r.as_register(self)?; let id = id.as_value(self)?; let address = address.as_value(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_id(id as ObjectID, None) .ok_or(ICError::DeviceNotSet)?; - let memory = logicable + let obj_ref = obj.borrow(); + let memory = obj_ref .as_memory_readable() .ok_or(MemoryError::NotReadable)?; self.set_register(indirection, target, memory.get_memory(address as i32)?)?; @@ -2019,13 +2047,17 @@ impl PutInstruction for T { let (device, connection) = d.as_device(self)?; let address = address.as_value(self)?; let value = value.as_value(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? - .get_logicable_from_index_mut(device, connection) + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? + .get_logicable_from_index(device, connection) .ok_or(ICError::DeviceNotSet)?; - let memory = logicable - .as_memory_writable() + let mut obj_ref = obj.borrow_mut(); + let memory = obj_ref + .as_mut_memory_writable() .ok_or(MemoryError::NotWriteable)?; memory.set_memory(address as i32, value)?; Ok(()) @@ -2043,13 +2075,17 @@ impl PutdInstruction for T { let id = id.as_value(self)?; let address = address.as_value(self)?; let value = value.as_value(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? - .get_logicable_from_id_mut(id as ObjectID, None) + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? + .get_logicable_from_id(id as ObjectID, None) .ok_or(ICError::DeviceNotSet)?; - let memory = logicable - .as_memory_writable() + let mut obj_ref = obj.borrow_mut(); + let memory = obj_ref + .as_mut_memory_writable() .ok_or(MemoryError::NotWriteable)?; memory.set_memory(address as i32, value)?; Ok(()) @@ -2060,13 +2096,17 @@ impl ClrInstruction for T { /// clr d? fn execute_inner(&mut self, d: &InstOperand) -> Result<(), ICError> { let (device, connection) = d.as_device(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? - .get_logicable_from_index_mut(device, connection) + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? + .get_logicable_from_index(device, connection) .ok_or(ICError::DeviceNotSet)?; - let memory = logicable - .as_memory_writable() + let mut obj_ref = obj.borrow_mut(); + let memory = obj_ref + .as_mut_memory_writable() .ok_or(MemoryError::NotWriteable)?; memory.clear_memory()?; Ok(()) @@ -2076,13 +2116,17 @@ impl ClrdInstruction for T { /// clrd id(r?|num) fn execute_inner(&mut self, id: &InstOperand) -> Result<(), ICError> { let id = id.as_value(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? - .get_logicable_from_id_mut(id as ObjectID, None) + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? + .get_logicable_from_id(id as ObjectID, None) .ok_or(ICError::DeviceNotSet)?; - let memory = logicable - .as_memory_writable() + let mut obj_ref = obj.borrow_mut(); + let memory = obj_ref + .as_mut_memory_writable() .ok_or(MemoryError::NotWriteable)?; memory.clear_memory()?; Ok(()) @@ -2100,11 +2144,22 @@ impl SInstruction for T { let (device, connection) = d.as_device(self)?; let logic_type = logic_type.as_logic_type(self)?; let val = r.as_value(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? - .get_logicable_from_index_mut(device, connection) + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? + .get_logicable_from_index(device, connection) .ok_or(ICError::DeviceNotSet)?; + let mut obj_ref = obj.borrow_mut(); + let device_id = obj_ref.get_id(); + let logicable = obj_ref + .as_mut_logicable() + .ok_or(ICError::NotLogicable(device_id))?; + if !logicable.can_logic_write(logic_type) { + return Err(LogicError::CantWrite(logic_type).into()); + } logicable.set_logic(logic_type, val, false)?; Ok(()) } @@ -2121,11 +2176,22 @@ impl SdInstruction for T { let id = id.as_value(self)?; let logic_type = logic_type.as_logic_type(self)?; let val = r.as_value(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? - .get_logicable_from_id_mut(id as ObjectID, None) + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? + .get_logicable_from_id(id as ObjectID, None) .ok_or(ICError::DeviceNotSet)?; + let mut obj_ref = obj.borrow_mut(); + let device_id = obj_ref.get_id(); + let logicable = obj_ref + .as_mut_logicable() + .ok_or(ICError::NotLogicable(device_id))?; + if !logicable.can_logic_write(logic_type) { + return Err(LogicError::CantWrite(logic_type).into()); + } logicable.set_logic(logic_type, val, false)?; Ok(()) } @@ -2144,14 +2210,26 @@ impl SsInstruction for T { let slot_index = slot_index.as_value(self)?; let logic_slot_type = logic_slot_type.as_slot_logic_type(self)?; let val = r.as_value(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? - .get_logicable_from_index_mut(device, connection) + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? + .get_logicable_from_index(device, connection) .ok_or(ICError::DeviceNotSet)?; + let mut obj_ref = obj.borrow_mut(); + let device_id = obj_ref.get_id(); + let logicable = obj_ref + .as_mut_logicable() + .ok_or(ICError::NotLogicable(device_id))?; + let device_id = logicable.get_id(); let device = logicable .as_mut_device() - .ok_or(ICError::NotSlotWriteable(logicable.get_id()))?; + .ok_or(ICError::NotSlotWriteable(device_id))?; + if !device.can_slot_logic_write(logic_slot_type, slot_index) { + return Err(LogicError::CantSlotWrite(logic_slot_type, slot_index).into()); + } device.set_slot_logic(logic_slot_type, slot_index, val, false)?; Ok(()) } @@ -2242,11 +2320,21 @@ impl LInstruction for T { } = r.as_register(self)?; let (device, connection) = d.as_device(self)?; let logic_type = logic_type.as_logic_type(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection) .ok_or(ICError::DeviceNotSet)?; + let obj_ref = obj.borrow(); + let logicable = obj_ref + .as_logicable() + .ok_or(ICError::NotLogicable(obj_ref.get_id()))?; + if !logicable.can_logic_read(logic_type) { + return Err(LogicError::CantRead(logic_type).into()); + } self.set_register(indirection, target, logicable.get_logic(logic_type)?)?; Ok(()) } @@ -2266,11 +2354,21 @@ impl LdInstruction for T { } = r.as_register(self)?; let id = id.as_value(self)?; let logic_type = logic_type.as_logic_type(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_id(id as ObjectID, None) .ok_or(ICError::DeviceNotSet)?; + let obj_ref = obj.borrow(); + let logicable = obj_ref + .as_logicable() + .ok_or(ICError::NotLogicable(obj_ref.get_id()))?; + if !logicable.can_logic_read(logic_type) { + return Err(LogicError::CantRead(logic_type).into()); + } self.set_register(indirection, target, logicable.get_logic(logic_type)?)?; Ok(()) } @@ -2292,11 +2390,21 @@ impl LsInstruction for T { let (device, connection) = d.as_device(self)?; let slot_index = slot_index.as_value(self)?; let logic_slot_type = logic_slot_type.as_slot_logic_type(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection) .ok_or(ICError::DeviceNotSet)?; + let obj_ref = obj.borrow(); + let logicable = obj_ref + .as_logicable() + .ok_or(ICError::NotLogicable(obj_ref.get_id()))?; + if !logicable.can_slot_logic_read(logic_slot_type, slot_index) { + return Err(LogicError::CantSlotRead(logic_slot_type, slot_index).into()); + } self.set_register( indirection, target, @@ -2322,11 +2430,18 @@ impl LrInstruction for T { let (device, connection) = d.as_device(self)?; let reagent_mode = reagent_mode.as_reagent_mode(self)?; let int = int.as_value(self)?; - let logicable = self + let obj = self .get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow() + .as_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .get_logicable_from_index(device, connection) .ok_or(ICError::DeviceNotSet)?; + let obj_ref = obj.borrow(); + let logicable = obj_ref + .as_logicable() + .ok_or(ICError::NotLogicable(obj_ref.get_id()))?; let result = match reagent_mode { LogicReagentMode::Contents => { @@ -2505,6 +2620,9 @@ impl HcfInstruction for T { fn execute_inner(&mut self) -> Result<(), ICError> { self.get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.get_id()))? + .borrow_mut() + .as_mut_circuit_holder() + .ok_or(ICError::CircuitHolderNotLogicable(self.get_id()))? .hault_and_catch_fire(); self.set_state(ICState::HasCaughtFire); Ok(()) @@ -2513,7 +2631,7 @@ impl HcfInstruction for T { impl LabelInstruction for T { /// label d? str - fn execute_inner(&mut self, d: &InstOperand, str: &InstOperand) -> Result<(), ICError> { + fn execute_inner(&mut self, _d: &InstOperand, _str: &InstOperand) -> Result<(), ICError> { // No op, handled by program compilation, should never be called? Ok(()) } diff --git a/ic10emu/src/lib.rs b/ic10emu/src/lib.rs index cd2f896..ab218fc 100644 --- a/ic10emu/src/lib.rs +++ b/ic10emu/src/lib.rs @@ -1,4 +1,3 @@ -pub mod device; pub mod errors; pub mod grammar; pub mod interpreter; diff --git a/ic10emu/src/network.rs b/ic10emu/src/network.rs index 3b7654d..c4f8b0d 100644 --- a/ic10emu/src/network.rs +++ b/ic10emu/src/network.rs @@ -229,17 +229,17 @@ impl Storage for CableNetwork { fn slots_count(&self) -> usize { 0 } - fn get_slot(&self, index: usize) -> Option<&crate::vm::object::Slot> { + fn get_slot(&self, _index: usize) -> Option<&crate::vm::object::Slot> { None } - fn get_slot_mut(&mut self, index: usize) -> Option<&mut crate::vm::object::Slot> { + fn get_slot_mut(&mut self, _index: usize) -> Option<&mut crate::vm::object::Slot> { None } fn get_slots(&self) -> &[crate::vm::object::Slot] { - &vec![] + &[] } fn get_slots_mut(&mut self) -> &mut [crate::vm::object::Slot] { - &mut vec![] + &mut [] } } @@ -287,7 +287,7 @@ impl Logicable for CableNetwork { }; Ok(self.channels[index]) } - fn set_logic(&mut self, lt: LogicType, value: f64, force: bool) -> Result<(), LogicError> { + fn set_logic(&mut self, lt: LogicType, value: f64, _force: bool) -> Result<(), LogicError> { use LogicType::*; let index: usize = match lt { Channel0 => 0, @@ -305,8 +305,8 @@ impl Logicable for CableNetwork { } fn can_slot_logic_read( &self, - slt: crate::vm::enums::script_enums::LogicSlotType, - index: f64, + _slt: crate::vm::enums::script_enums::LogicSlotType, + _index: f64, ) -> bool { false } @@ -314,7 +314,6 @@ impl Logicable for CableNetwork { &self, slt: crate::vm::enums::script_enums::LogicSlotType, index: f64, - vm: &crate::vm::VM, ) -> Result { Err(LogicError::CantSlotRead(slt, index)) } diff --git a/ic10emu/src/vm.rs b/ic10emu/src/vm.rs index 4078960..3838e90 100644 --- a/ic10emu/src/vm.rs +++ b/ic10emu/src/vm.rs @@ -65,7 +65,7 @@ impl VM { let default_network_key = network_id_space.next(); let networks = BTreeMap::new(); - let mut vm = Rc::new(VM { + let vm = Rc::new(VM { objects: RefCell::new(BTreeMap::new()), circuit_holders: RefCell::new(Vec::new()), program_holders: RefCell::new(Vec::new()), @@ -120,17 +120,19 @@ impl VM { .networks .borrow() .get(&net_id) + .cloned() .expect(&format!( "desync between vm and transaction networks: {net_id}" - )) - .borrow_mut() + )); + let mut net_ref = net.borrow_mut(); + let net_interface = net_ref .as_mut_network() .expect(&format!("non network network: {net_id}")); for id in trans_net.devices { - net.add_data(id); + net_interface.add_data(id); } for id in trans_net.power_only { - net.add_power(id); + net_interface.add_power(id); } } @@ -173,11 +175,9 @@ impl VM { obj.borrow_mut().set_id(new_id); self.objects.borrow_mut().insert(new_id, obj); - self.objects - .borrow_mut() - .iter_mut() - .filter_map(|(_obj_id, obj)| obj.borrow_mut().as_mut_device()) - .for_each(|device| { + for obj in self.objects.borrow().values() { + let mut obj_ref = obj.borrow_mut(); + if let Some(device) = obj_ref.as_mut_device() { device.get_slots_mut().iter_mut().for_each(|slot| { if slot.parent == old_id { slot.parent = new_id; @@ -189,7 +189,8 @@ impl VM { slot.occupant = Some(new_id); } }); - }); + } + } self.circuit_holders.borrow_mut().iter_mut().for_each(|id| { if *id == old_id { @@ -202,15 +203,13 @@ impl VM { } }); self.networks.borrow().iter().for_each(|(_net_id, net)| { - let net_ref = net - .borrow_mut() - .as_mut_network() - .expect("non-network network"); - if net_ref.remove_data(old_id) { - net_ref.add_data(new_id); + let mut net_ref = net.borrow_mut(); + let net_interface = net_ref.as_mut_network().expect("non-network network"); + if net_interface.remove_data(old_id) { + net_interface.add_data(new_id); } - if net_ref.remove_power(old_id) { - net_ref.add_power(new_id); + if net_interface.remove_power(old_id) { + net_interface.add_power(new_id); } }); self.id_space.borrow_mut().free_id(old_id); @@ -219,12 +218,14 @@ impl VM { /// Set program code if it's valid pub fn set_code(self: &Rc, id: u32, code: &str) -> Result { - let programmable = self + let obj = self .objects .borrow() .get(&id) - .ok_or(VMError::UnknownId(id))? - .borrow_mut() + .cloned() + .ok_or(VMError::UnknownId(id))?; + let mut obj_ref = obj.borrow_mut(); + let programmable = obj_ref .as_mut_programmable() .ok_or(VMError::NotProgrammable(id))?; programmable.set_source_code(code)?; @@ -233,12 +234,14 @@ impl VM { /// Set program code and translate invalid lines to Nop, collecting errors pub fn set_code_invalid(self: &Rc, id: u32, code: &str) -> Result { - let programmable = self + let obj = self .objects .borrow() .get(&id) - .ok_or(VMError::UnknownId(id))? - .borrow_mut() + .cloned() + .ok_or(VMError::UnknownId(id))?; + let mut obj_ref = obj.borrow_mut(); + let programmable = obj_ref .as_mut_programmable() .ok_or(VMError::NotProgrammable(id))?; programmable.set_source_code_with_invalid(code); @@ -255,12 +258,14 @@ impl VM { id: u32, advance_ip_on_err: bool, ) -> Result<(), VMError> { - let programmable = self + let obj = self .objects .borrow() .get(&id) - .ok_or(VMError::UnknownId(id))? - .borrow_mut() + .cloned() + .ok_or(VMError::UnknownId(id))?; + let mut obj_ref = obj.borrow_mut(); + let programmable = obj_ref .as_mut_programmable() .ok_or(VMError::NotProgrammable(id))?; self.operation_modified.borrow_mut().clear(); @@ -275,18 +280,20 @@ impl VM { id: u32, ignore_errors: bool, ) -> Result { - let programmable = self + let obj = self .objects .borrow() .get(&id) - .ok_or(VMError::UnknownId(id))? - .borrow_mut() + .cloned() + .ok_or(VMError::UnknownId(id))?; + let mut obj_ref = obj.borrow_mut(); + let programmable = obj_ref .as_mut_programmable() .ok_or(VMError::NotProgrammable(id))?; self.operation_modified.borrow_mut().clear(); self.set_modified(id); for _i in 0..128 { - if let Err(err) = programmable.step( ignore_errors) { + if let Err(err) = programmable.step(ignore_errors) { if !ignore_errors { return Err(err.into()); } @@ -308,12 +315,14 @@ impl VM { } pub fn reset_programmable(self: &Rc, id: ObjectID) -> Result { - let programmable = self + let obj = self .objects .borrow() .get(&id) - .ok_or(VMError::UnknownId(id))? - .borrow_mut() + .cloned() + .ok_or(VMError::UnknownId(id))?; + let mut obj_ref = obj.borrow_mut(); + let programmable = obj_ref .as_mut_programmable() .ok_or(VMError::NotProgrammable(id))?; programmable.reset(); @@ -365,14 +374,15 @@ impl VM { .networks .borrow() .get(&id) + .cloned() .ok_or(ICError::BadNetworkId(id))?; if !(0..8).contains(&channel) { Err(ICError::ChannelIndexOutOfRange(channel)) } else { let channel_lt = LogicType::from_repr((LogicType::Channel0 as usize + channel) as u16) .expect("channel logictype repr out of range"); - let val = network - .borrow_mut() + let net_ref = network.borrow(); + let val = net_ref .as_network() .expect("non-network network") .get_logic(channel_lt)?; @@ -390,6 +400,7 @@ impl VM { .networks .borrow() .get(&(id)) + .cloned() .ok_or(ICError::BadNetworkId(id))?; if !(0..8).contains(&channel) { Err(ICError::ChannelIndexOutOfRange(channel)) @@ -425,9 +436,10 @@ impl VM { .borrow() .values() .filter_map(|net| { - let net_ref = net.borrow().as_network().expect("non-network network"); - if net_ref.contains_data(&source) { - Some(net_ref.data_visible(&source)) + let net_ref = net.borrow(); + let net_interface = net_ref.as_network().expect("non-network network"); + if net_interface.contains_data(&source) { + Some(net_interface.data_visible(&source)) } else { None } @@ -441,7 +453,7 @@ impl VM { pin: usize, val: Option, ) -> Result { - let Some(obj) = self.objects.borrow().get(&id) else { + let Some(obj) = self.objects.borrow().get(&id).cloned() else { return Err(VMError::UnknownId(id)); }; if let Some(other_device) = val { @@ -452,7 +464,8 @@ impl VM { return Err(VMError::DeviceNotVisible(other_device, id)); } } - let Some(device) = obj.borrow_mut().as_mut_device() else { + let mut obj_ref = obj.borrow_mut(); + let Some(device) = obj_ref.as_mut_device() else { return Err(VMError::NotADevice(id)); }; let Some(pins) = device.device_pins_mut() else { @@ -472,10 +485,11 @@ impl VM { connection: usize, target_net: Option, ) -> Result { - let Some(obj) = self.objects.borrow().get(&id) else { + let Some(obj) = self.objects.borrow().get(&id).cloned() else { return Err(VMError::UnknownId(id)); }; - let Some(device) = obj.borrow_mut().as_mut_device() else { + let mut obj_ref = obj.borrow_mut(); + let Some(device) = obj_ref.as_mut_device() else { return Err(VMError::NotADevice(id)); }; let connections = device.connection_list_mut(); @@ -567,10 +581,11 @@ impl VM { network_id: ObjectID, ) -> Result { if let Some(network) = self.networks.borrow().get(&network_id) { - let Some(obj) = self.objects.borrow().get(&id) else { + let Some(obj) = self.objects.borrow().get(&id).cloned() else { return Err(VMError::UnknownId(id)); }; - let Some(device) = obj.borrow_mut().as_mut_device() else { + let mut obj_ref = obj.borrow_mut(); + let Some(device) = obj_ref.as_mut_device() else { return Err(VMError::NotADevice(id)); }; @@ -629,7 +644,7 @@ impl VM { .borrow_mut() .as_mut_device() .expect("batch iter yielded non device") - .set_slot_logic(typ, index, val, write_readonly) + .set_slot_logic(typ, index, val, write_readonly) .map_err(Into::into) }) .try_collect() @@ -785,35 +800,44 @@ impl VM { target: Option, quantity: u32, ) -> Result, VMError> { - let Some(obj) = self.objects.borrow().get(&id) else { + let Some(obj) = self.objects.borrow().get(&id).cloned() else { return Err(VMError::UnknownId(id)); }; - let Some(storage) = obj.borrow_mut().as_mut_storage() else { + let mut obj_ref = obj.borrow_mut(); + let Some(storage) = obj_ref.as_mut_storage() else { return Err(VMError::NotStorage(id)); }; let slot = storage .get_slot_mut(index) .ok_or(ICError::SlotIndexOutOfRange(index as f64))?; if let Some(target) = target { - let Some(item_obj) = self.objects.borrow().get(&target) else { - return Err(VMError::UnknownId(id)); - }; - let Some(item) = item_obj.borrow_mut().as_mut_item() else { - return Err(VMError::NotAnItem(target)); - }; - if let Some(parent_slot_info) = item.get_parent_slot() { - self.remove_slot_occupant(parent_slot_info.parent, parent_slot_info.slot)?; + if slot.occupant.is_some_and(|occupant| occupant == target) { + slot.quantity = quantity; + Ok(None) + } else { + let Some(item_obj) = self.objects.borrow().get(&target).cloned() else { + return Err(VMError::UnknownId(id)); + }; + let mut item_obj_ref = item_obj.borrow_mut(); + let Some(item) = item_obj_ref.as_mut_item() else { + return Err(VMError::NotAnItem(target)); + }; + if let Some(parent_slot_info) = item.get_parent_slot() { + self.remove_slot_occupant(parent_slot_info.parent, parent_slot_info.slot)?; + } + item.set_parent_slot(Some(ParentSlotInfo { + parent: id, + slot: index, + })); + let last = slot.occupant; + slot.occupant = Some(target); + slot.quantity = quantity; + Ok(last) } - item.set_parent_slot(Some(ParentSlotInfo { - parent: id, - slot: index, - })); - let last = slot.occupant; - slot.occupant = Some(target); - Ok(last) } else { let last = slot.occupant; slot.occupant = None; + slot.quantity = 0; Ok(last) } } @@ -824,10 +848,11 @@ impl VM { id: ObjectID, index: usize, ) -> Result, VMError> { - let Some(obj) = self.objects.borrow().get(&id) else { + let Some(obj) = self.objects.borrow().get(&id).cloned() else { return Err(VMError::UnknownId(id)); }; - let Some(storage) = obj.borrow_mut().as_mut_storage() else { + let mut obj_ref = obj.borrow_mut(); + let Some(storage) = obj_ref.as_mut_storage() else { return Err(VMError::NotStorage(id)); }; let slot = storage @@ -845,7 +870,7 @@ impl VM { .objects .borrow() .iter() - .filter_map(|(obj_id, obj)| { + .filter_map(|(_obj_id, obj)| { if obj .borrow() .as_item() @@ -929,21 +954,19 @@ impl VM { .extend(transaction.wireless_receivers); for (net_id, trans_net) in transaction.networks.into_iter() { - let net = self - .networks - .borrow() - .get(&net_id) - .expect(&format!( - "desync between vm and transaction networks: {net_id}" - )) - .borrow_mut() + let networks_ref = self.networks.borrow(); + let net = networks_ref.get(&net_id).expect(&format!( + "desync between vm and transaction networks: {net_id}" + )); + let mut net_ref = net.borrow_mut(); + let net_interface = net_ref .as_mut_network() .expect(&format!("non network network: {net_id}")); for id in trans_net.devices { - net.add_data(id); + net_interface.add_data(id); } for id in trans_net.power_only { - net.add_power(id); + net_interface.add_power(id); } } @@ -1025,16 +1048,16 @@ impl VMTransaction { } } - if let Some(w_logicable) = obj.borrow().as_wireless_transmit() { + if let Some(_w_logicable) = obj.borrow().as_wireless_transmit() { self.wireless_transmitters.push(obj_id); } - if let Some(r_logicable) = obj.borrow().as_wireless_receive() { + if let Some(_r_logicable) = obj.borrow().as_wireless_receive() { self.wireless_receivers.push(obj_id); } - if let Some(circuit_holder) = obj.borrow().as_circuit_holder() { + if let Some(_circuit_holder) = obj.borrow().as_circuit_holder() { self.circuit_holders.push(obj_id); } - if let Some(programmable) = obj.borrow().as_programmable() { + if let Some(_programmable) = obj.borrow().as_programmable() { self.program_holders.push(obj_id); } if let Some(device) = obj.borrow_mut().as_mut_device() { diff --git a/ic10emu/src/vm/instructions/operands.rs b/ic10emu/src/vm/instructions/operands.rs index c951314..3ad3d88 100644 --- a/ic10emu/src/vm/instructions/operands.rs +++ b/ic10emu/src/vm/instructions/operands.rs @@ -75,25 +75,25 @@ impl InstOperand { } pub fn as_ident(&self) -> Result { - let &Operand::Identifier(ident) = &self.operand else { + let Operand::Identifier(ident) = &self.operand else { return Err(ICError::IncorrectOperandType { inst: self.inst, index: self.index, desired: "Name".to_owned(), }); }; - Ok(ident) + Ok(ident.clone()) } pub fn as_number(&self) -> Result { - let &Operand::Number(num) = &self.operand else { + let Operand::Number(num) = &self.operand else { return Err(ICError::IncorrectOperandType { inst: self.inst, index: self.index, desired: "Number".to_owned(), }); }; - Ok(num) + Ok(num.clone()) } pub fn as_aliasable(&self) -> Result { @@ -287,7 +287,7 @@ impl InstOperand { } pub fn translate_alias(&self, ic: &IC) -> Operand { - match self.operand { + match &self.operand { Operand::Identifier(id) | Operand::Type { identifier: id, .. } => { if let Some(alias) = ic.get_aliases().get(&id.name) { alias.clone() @@ -299,7 +299,7 @@ impl InstOperand { self.operand.clone() } } - _ => self.clone(), + _ => self.operand.clone(), } } } diff --git a/ic10emu/src/vm/object.rs b/ic10emu/src/vm/object.rs index a535ca7..3658820 100644 --- a/ic10emu/src/vm/object.rs +++ b/ic10emu/src/vm/object.rs @@ -123,4 +123,5 @@ pub struct Slot { pub readable_logic: Vec, pub writeable_logic: Vec, pub occupant: Option, + pub quantity: u32, } diff --git a/ic10emu/src/vm/object/generic/macros.rs b/ic10emu/src/vm/object/generic/macros.rs index 7232e7d..c536a92 100644 --- a/ic10emu/src/vm/object/generic/macros.rs +++ b/ic10emu/src/vm/object/generic/macros.rs @@ -102,18 +102,24 @@ macro_rules! GWDevice { fn device_info(&self) -> &DeviceInfo { &self.device_info } - fn device_connections(&self) -> &[Connection] { + fn connections(&self) -> &[Connection] { self.connections.as_slice() } - fn device_connections_mut(&mut self) -> &mut [Connection] { + fn connections_mut(&mut self) -> &mut [Connection] { self.connections.as_mut_slice() } - fn device_pins(&self) -> Option<&[Option]> { + fn pins(&self) -> Option<&[Option]> { self.pins.as_ref().map(|pins| pins.as_slice()) } - fn device_pins_mut(&mut self) -> Option<&mut [Option]> { + fn pins_mut(&mut self) -> Option<&mut [Option]> { self.pins.as_mut().map(|pins| pins.as_mut_slice()) } + fn reagents(&self) -> Option<&BTreeMap> { + self.reagents.as_ref() + } + fn reagents_mut(&mut self) -> &mut Option> { + &mut self.reagents + } } }; } @@ -136,6 +142,12 @@ macro_rules! GWItem { fn set_parent_slot(&mut self, info: Option) { self.parent_slot = info; } + fn damage(&self) -> &Option { + &self.damage + } + fn damage_mut(&mut self) -> &mut Option { + &mut self.damage + } } }; } diff --git a/ic10emu/src/vm/object/generic/structs.rs b/ic10emu/src/vm/object/generic/structs.rs index 93e74b0..6fbb4f4 100644 --- a/ic10emu/src/vm/object/generic/structs.rs +++ b/ic10emu/src/vm/object/generic/structs.rs @@ -80,6 +80,7 @@ pub struct GenericLogicableDevice { pub device_info: DeviceInfo, pub connections: Vec, pub pins: Option>>, + pub reagents: Option>, } #[derive(ObjectInterface!, GWStructure!, GWStorage!, GWLogicable!, GWDevice!, GWMemoryReadable!, GWMemoryWritable!)] @@ -100,6 +101,7 @@ pub struct GenericLogicableDeviceMemoryReadable { pub device_info: DeviceInfo, pub connections: Vec, pub pins: Option>>, + pub reagents: Option>, pub memory: Vec, } @@ -121,6 +123,7 @@ pub struct GenericLogicableDeviceMemoryReadWriteable { pub device_info: DeviceInfo, pub connections: Vec, pub pins: Option>>, + pub reagents: Option>, pub memory: Vec, } @@ -137,6 +140,7 @@ pub struct GenericItem { pub vm: Rc, pub item_info: ItemInfo, pub parent_slot: Option, + pub damage: Option, } #[derive(ObjectInterface!, GWItem!, GWStorage! )] @@ -152,6 +156,7 @@ pub struct GenericItemStorage { pub vm: Rc, pub item_info: ItemInfo, pub parent_slot: Option, + pub damage: Option, pub slots: Vec, } @@ -168,6 +173,7 @@ pub struct GenericItemLogicable { pub vm: Rc, pub item_info: ItemInfo, pub parent_slot: Option, + pub damage: Option, pub slots: Vec, pub fields: BTreeMap, pub modes: Option>, @@ -186,6 +192,7 @@ pub struct GenericItemLogicableMemoryReadable { pub vm: Rc, pub item_info: ItemInfo, pub parent_slot: Option, + pub damage: Option, pub slots: Vec, pub fields: BTreeMap, pub modes: Option>, @@ -205,6 +212,7 @@ pub struct GenericItemLogicableMemoryReadWriteable { pub vm: Rc, pub item_info: ItemInfo, pub parent_slot: Option, + pub damage: Option, pub slots: Vec, pub fields: BTreeMap, pub modes: Option>, diff --git a/ic10emu/src/vm/object/generic/traits.rs b/ic10emu/src/vm/object/generic/traits.rs index a991f42..780ab9b 100644 --- a/ic10emu/src/vm/object/generic/traits.rs +++ b/ic10emu/src/vm/object/generic/traits.rs @@ -11,7 +11,6 @@ use crate::{ traits::*, LogicField, MemoryAccess, ObjectID, Slot, }, - VM, }, }; use std::{collections::BTreeMap, usize}; @@ -120,28 +119,165 @@ impl Logicable for T { if index < 0.0 { false } else { + use LogicSlotType::*; + if matches!( + slt, + Occupied + | OccupantHash + | Quantity + | Class + | MaxQuantity + | PrefabHash + | SortingClass + | ReferenceId + ) { + return true; + } self.get_slot(index as usize) .map(|slot| slot.readable_logic.contains(&slt)) .unwrap_or(false) } } - fn get_slot_logic(&self, slt: LogicSlotType, index: f64, _vm: &VM) -> Result { + fn get_slot_logic(&self, slt: LogicSlotType, index: f64) -> Result { if index < 0.0 { return Err(LogicError::SlotIndexOutOfRange(index, self.slots_count())); } self.get_slot(index as usize) .ok_or_else(|| LogicError::SlotIndexOutOfRange(index, self.slots_count())) .and_then(|slot| { - if slot.readable_logic.contains(&slt) { - match slot.occupant { - Some(_id) => { - // FIXME: impliment by accessing VM to get occupant + use LogicSlotType::*; + let occupant = slot.occupant.and_then(|id| self.get_vm().get_object(id)); + match slt { + Occupied => { + if slot.occupant.is_some() { + Ok(1.0) + } else { Ok(0.0) } - None => Ok(0.0), } - } else { - Err(LogicError::CantSlotRead(slt, index)) + Quantity => { + if slot.occupant.is_some() { + Ok(slot.quantity as f64) + } else { + Ok(0.0) + } + } + Class => { + if slot.occupant.is_some() { + Ok(slot.typ as i32 as f64) + } else { + Ok(0.0) + } + } + OccupantHash | PrefabHash => { + if let Some(occupant) = occupant { + Ok(occupant.borrow().get_prefab().hash as f64) + } else { + Ok(0.0) + } + } + MaxQuantity => { + if let Some(occupant) = occupant { + Ok(occupant + .borrow() + .as_item() + .map(|item| item.max_quantity() as f64) + .ok_or(LogicError::CantSlotRead(slt, index))?) + } else { + Ok(0.0) + } + } + SortingClass => { + if let Some(occupant) = occupant { + Ok(occupant + .borrow() + .as_item() + .map(|item| item.sorting_class() as i32 as f64) + .ok_or(LogicError::CantSlotRead(slt, index))?) + } else { + Ok(0.0) + } + } + ReferenceId => { + if let Some(occupant) = occupant { + Ok(occupant.borrow().get_id() as f64) + } else { + Ok(0.0) + } + } + slt => { + if slot.readable_logic.contains(&slt) { + if let Some(occupant) = occupant { + let occupant_ref = occupant.borrow(); + let logicable = occupant_ref + .as_logicable() + .ok_or(LogicError::CantSlotRead(slt, index))?; + + match slt { + Occupied | Quantity | Class | OccupantHash | PrefabHash + | MaxQuantity | SortingClass | ReferenceId => Ok(0.0), // covered above + LineNumber => logicable.get_logic(LogicType::LineNumber), + + Charge => logicable.get_logic(LogicType::Charge), + ChargeRatio => logicable + .as_chargeable() + .map(|chargeable| chargeable.get_charge() as f64) + .ok_or(LogicError::CantSlotRead(slt, index)), + Open => logicable.get_logic(LogicType::Open), + On => logicable.get_logic(LogicType::Open), + Lock => logicable.get_logic(LogicType::Lock), + FilterType => Ok(logicable + .as_item() + .and_then(|item| item.filter_type()) + .ok_or(LogicError::CantSlotRead(slt, index))? + as i32 + as f64), + Damage => logicable + .as_item() + .map(|item| item.get_damage() as f64) + .ok_or(LogicError::CantSlotRead(slt, index)), + Volume => logicable.get_logic(LogicType::Volume), + Pressure => logicable.get_logic(LogicType::Pressure), + PressureAir => logicable + .as_suit() + .map(|suit| suit.pressure_air()) + .ok_or(LogicError::CantSlotRead(slt, index)), + PressureWaste => logicable + .as_suit() + .map(|suit| suit.pressure_waste()) + .ok_or(LogicError::CantSlotRead(slt, index)), + Temperature => logicable.get_logic(LogicType::Temperature), + Seeding => logicable + .as_plant() + .map(|plant| plant.is_seeding() as i32 as f64) + .ok_or(LogicError::CantSlotRead(slt, index)), + Mature => logicable + .as_plant() + .map(|plant| plant.is_mature() as i32 as f64) + .ok_or(LogicError::CantSlotRead(slt, index)), + Growth => logicable + .as_plant() + .map(|plant| plant.get_growth()) + .ok_or(LogicError::CantSlotRead(slt, index)), + Health => logicable + .as_plant() + .map(|plant| plant.get_health()) + .ok_or(LogicError::CantSlotRead(slt, index)), + Efficiency => logicable + .as_plant() + .map(|plant| plant.get_health()) + .ok_or(LogicError::CantSlotRead(slt, index)), + + // defaults + None => Ok(0.0), + } + } else { + Ok(0.0) + } + } else { + Err(LogicError::CantSlotRead(slt, index)) + } + } } }) } @@ -204,16 +340,18 @@ impl MemoryWritable for T { pub trait GWDevice: GWLogicable + Logicable { fn device_info(&self) -> &DeviceInfo; - fn device_connections(&self) -> &[Connection]; - fn device_connections_mut(&mut self) -> &mut [Connection]; - fn device_pins(&self) -> Option<&[Option]>; - fn device_pins_mut(&mut self) -> Option<&mut [Option]>; + fn connections(&self) -> &[Connection]; + fn connections_mut(&mut self) -> &mut [Connection]; + fn pins(&self) -> Option<&[Option]>; + fn pins_mut(&mut self) -> Option<&mut [Option]>; + fn reagents(&self) -> Option<&BTreeMap>; + fn reagents_mut(&mut self) -> &mut Option>; } -impl Device for T { +impl Device for T { fn can_slot_logic_write(&self, slt: LogicSlotType, index: f64) -> bool { if index < 0.0 { - return false; + false } else { self.get_slot(index as usize) .map(|slot| slot.writeable_logic.contains(&slt)) @@ -225,22 +363,47 @@ impl Device for T { slt: LogicSlotType, index: f64, value: f64, - vm: &VM, force: bool, ) -> Result<(), LogicError> { + let slots_count = self.slots_count(); if index < 0.0 { - return Err(LogicError::SlotIndexOutOfRange(index, self.slots_count())); + return Err(LogicError::SlotIndexOutOfRange(index, slots_count)); } + use LogicSlotType::*; + let vm = self.get_vm().clone(); + self.get_slot_mut(index as usize) - .ok_or_else(|| LogicError::SlotIndexOutOfRange(index, self.slots_count())) + .ok_or(LogicError::SlotIndexOutOfRange(index, slots_count)) .and_then(|slot| { + // special case, update slot quantity if >= 1 + if slt == Quantity && force && value >= 1.0 { + slot.quantity = value as u32; + return Ok(()); + } if slot.writeable_logic.contains(&slt) { - match slot.occupant { - Some(_id) => { - // FIXME: impliment by accessing VM to get occupant - Ok(()) + let occupant = slot.occupant.and_then(|id| vm.get_object(id)); + if let Some(occupant) = occupant { + let mut occupant_ref = occupant.borrow_mut(); + let logicable = occupant_ref + .as_mut_logicable() + .ok_or(LogicError::CantSlotWrite(slt, index))?; + match slt { + Open => logicable.set_logic(LogicType::Open, value, force), + On => logicable.set_logic(LogicType::On, value, force), + Lock => logicable.set_logic(LogicType::On, value, force), + // no other values are known to be writeable + Damage if force => { + logicable + .as_mut_item() + .map(|item| item.set_damage(value as f32)) + .ok_or(LogicError::CantSlotWrite(slt, index))?; + Ok(()) + } + + _ => Ok(()), } - None => Ok(()), + } else { + Ok(()) } } else { Err(LogicError::CantSlotWrite(slt, index)) @@ -248,16 +411,16 @@ impl Device for T { }) } fn connection_list(&self) -> &[crate::network::Connection] { - self.device_connections() + self.connections() } fn connection_list_mut(&mut self) -> &mut [Connection] { - self.device_connections_mut() + self.connections_mut() } fn device_pins(&self) -> Option<&[Option]> { - self.device_pins() + self.pins() } - fn device_pins_mut(&self) -> Option<&mut [Option]> { - self.device_pins_mut() + fn device_pins_mut(&mut self) -> Option<&mut [Option]> { + self.pins_mut() } fn has_reagents(&self) -> bool { self.device_info().has_reagents @@ -283,12 +446,36 @@ impl Device for T { fn has_atmosphere(&self) -> bool { self.device_info().has_atmosphere } + fn get_reagents(&self) -> Vec<(i32, f64)> { + self.reagents() + .map(|reagents| { + reagents + .iter() + .map(|(hash, quant)| (*hash, *quant)) + .collect() + }) + .unwrap_or_default() + } + fn set_reagents(&mut self, reagents: &[(i32, f64)]) { + let reagents_ref = self.reagents_mut(); + *reagents_ref = Some(reagents.iter().copied().collect()); + } + fn add_reagents(&mut self, reagents: &[(i32, f64)]) { + let reagents_ref = self.reagents_mut(); + if let Some(ref mut reagents_ref) = reagents_ref { + reagents_ref.extend(reagents.iter().map(|(hash, quant)| (hash, quant))); + } else { + *reagents_ref = Some(reagents.iter().copied().collect()); + } + } } pub trait GWItem { fn item_info(&self) -> &ItemInfo; fn parent_slot(&self) -> Option; fn set_parent_slot(&mut self, info: Option); + fn damage(&self) -> &Option; + fn damage_mut(&mut self) -> &mut Option; } impl Item for T { @@ -319,6 +506,12 @@ impl Item for T { fn set_parent_slot(&mut self, info: Option) { self.set_parent_slot(info); } + fn get_damage(&self) -> f32 { + self.damage().unwrap_or(0.0) + } + fn set_damage(&mut self, damage: f32) { + self.damage_mut().replace(damage); + } } pub trait GWCircuitHolder: Logicable {} diff --git a/ic10emu/src/vm/object/stationpedia/structs/integrated_circuit.rs b/ic10emu/src/vm/object/stationpedia/structs/integrated_circuit.rs index c517e50..96f2d02 100644 --- a/ic10emu/src/vm/object/stationpedia/structs/integrated_circuit.rs +++ b/ic10emu/src/vm/object/stationpedia/structs/integrated_circuit.rs @@ -11,7 +11,7 @@ use crate::{ errors::{LogicError, MemoryError}, macros::ObjectInterface, traits::*, - LogicField, MemoryAccess, Name, ObjectID, Slot, + LogicField, MemoryAccess, Name, ObjectID, Slot, VMObject, }, VM, }, @@ -48,6 +48,7 @@ pub struct ItemIntegratedCircuit10 { pub state: ICState, pub code: String, pub program: Program, + pub damage: f32, } impl Item for ItemIntegratedCircuit10 { @@ -78,16 +79,22 @@ impl Item for ItemIntegratedCircuit10 { fn set_parent_slot(&mut self, info: Option) { self.parent_slot = info; } + fn get_damage(&self) -> f32 { + self.damage + } + fn set_damage(&mut self, damage: f32) { + self.damage = damage; + } } impl Storage for ItemIntegratedCircuit10 { fn slots_count(&self) -> usize { 0 } - fn get_slot(&self, index: usize) -> Option<&Slot> { + fn get_slot(&self, _index: usize) -> Option<&Slot> { None } - fn get_slot_mut(&mut self, index: usize) -> Option<&mut Slot> { + fn get_slot_mut(&mut self, _index: usize) -> Option<&mut Slot> { None } fn get_slots(&self) -> &[Slot] { @@ -158,10 +165,10 @@ impl Logicable for ItemIntegratedCircuit10 { _ => Err(LogicError::CantWrite(lt)), }) } - fn can_slot_logic_read(&self, slt: LogicSlotType, index: f64) -> bool { + fn can_slot_logic_read(&self, _slt: LogicSlotType,_indexx: f64) -> bool { false } - fn get_slot_logic(&self, slt: LogicSlotType, index: f64) -> Result { + fn get_slot_logic(&self, _slt: LogicSlotType, index: f64) -> Result { return Err(LogicError::SlotIndexOutOfRange(index, self.slots_count())); } fn valid_logic_types(&self) -> Vec { @@ -226,14 +233,9 @@ impl SourceCode for ItemIntegratedCircuit10 { } impl IntegratedCircuit for ItemIntegratedCircuit10 { - fn get_circuit_holder(&self) -> Option { + fn get_circuit_holder(&self) -> Option { self.get_parent_slot() - .map(|parent_slot| { - self.get_vm() - .get_object(parent_slot.parent) - .map(|obj| obj.borrow().as_circuit_holder()) - .flatten() - }) + .map(|parent_slot| self.get_vm().get_object(parent_slot.parent)) .flatten() } fn get_instruction_pointer(&self) -> f64 { @@ -332,7 +334,7 @@ impl IntegratedCircuit for ItemIntegratedCircuit10 { Ok(val) } } - fn put_stack(&self, addr: f64, val: f64) -> Result { + fn put_stack(&mut self, addr: f64, val: f64) -> Result { let sp = addr.round() as i32; if !(0..(self.memory.len() as i32)).contains(&sp) { Err(ICError::StackIndexOutOfRange(addr)) @@ -358,7 +360,7 @@ impl IntegratedCircuit for ItemIntegratedCircuit10 { &self.program.labels } fn get_state(&self) -> crate::interpreter::ICState { - self.state + self.state.clone() } fn set_state(&mut self, state: crate::interpreter::ICState) { self.state = state; @@ -369,7 +371,10 @@ impl IC10Marker for ItemIntegratedCircuit10 {} impl Programmable for ItemIntegratedCircuit10 { fn step(&mut self, advance_ip_on_err: bool) -> Result<(), crate::errors::ICError> { - if matches!(&self.state, ICState::HasCaughtFire | ICState::Error(_)) { + if matches!(&self.state, ICState::HasCaughtFire ) { + return Ok(()); + } + if matches!(&self.state, ICState::Error(_)) && !advance_ip_on_err { return Ok(()); } if let ICState::Sleep(then, sleep_for) = &self.state { @@ -395,7 +400,7 @@ impl Programmable for ItemIntegratedCircuit10 { } self.next_ip = self.ip + 1; self.state = ICState::Running; - let line = self.program.get_line(self.ip)?; + let line = self.program.get_line(self.ip)?.clone(); let operands = &line.operands; let instruction = line.instruction; instruction.execute(self, operands)?; @@ -405,6 +410,9 @@ impl Programmable for ItemIntegratedCircuit10 { } self.get_circuit_holder() .ok_or(ICError::NoCircuitHolder(self.id))? + .borrow_mut() + .as_mut_logicable() + .ok_or(ICError::CircuitHolderNotLogicable(self.id))? .set_logic(LogicType::LineNumber, self.ip as f64, true)?; Ok(()) } diff --git a/ic10emu/src/vm/object/templates.rs b/ic10emu/src/vm/object/templates.rs index aa3da93..88052d5 100644 --- a/ic10emu/src/vm/object/templates.rs +++ b/ic10emu/src/vm/object/templates.rs @@ -223,6 +223,7 @@ impl ObjectTemplate { readable_logic: Vec::new(), writeable_logic: Vec::new(), occupant: None, + quantity: info.quantity.unwrap_or(0), }) .collect(), }), @@ -274,6 +275,7 @@ impl ObjectTemplate { }) .unwrap_or_else(|| Vec::new()), occupant: None, + quantity: info.quantity.unwrap_or(0), }) .collect(), fields: s @@ -289,6 +291,7 @@ impl ObjectTemplate { value: s .logic .logic_values + .as_ref() .map(|values| values.get(key)) .flatten() .copied() @@ -347,6 +350,7 @@ impl ObjectTemplate { }) .unwrap_or_else(|| Vec::new()), occupant: None, + quantity: info.quantity.unwrap_or(0), }) .collect(), fields: s @@ -362,6 +366,7 @@ impl ObjectTemplate { value: s .logic .logic_values + .as_ref() .map(|values| values.get(key)) .flatten() .copied() @@ -382,6 +387,7 @@ impl ObjectTemplate { pins: s .device .device_pins + .as_ref() .map(|pins| Some(pins.clone())) .unwrap_or_else(|| { s.device @@ -389,6 +395,7 @@ impl ObjectTemplate { .map(|pins_len| vec![None; pins_len]) }), device_info: s.device.clone(), + reagents: s.device.reagents.clone(), }), StructureLogicDeviceMemory(s) if matches!(s.memory.memory_access, MemoryAccess::Read) => @@ -445,6 +452,7 @@ impl ObjectTemplate { }) .unwrap_or_else(|| Vec::new()), occupant: None, + quantity: info.quantity.unwrap_or(0), }) .collect(), fields: s @@ -460,6 +468,7 @@ impl ObjectTemplate { value: s .logic .logic_values + .as_ref() .map(|values| values.get(key)) .flatten() .copied() @@ -480,6 +489,7 @@ impl ObjectTemplate { pins: s .device .device_pins + .as_ref() .map(|pins| Some(pins.clone())) .unwrap_or_else(|| { s.device @@ -487,6 +497,7 @@ impl ObjectTemplate { .map(|pins_len| vec![None; pins_len]) }), device_info: s.device.clone(), + reagents: s.device.reagents.clone(), memory: s .memory .values @@ -547,6 +558,7 @@ impl ObjectTemplate { }) .unwrap_or_else(|| Vec::new()), occupant: None, + quantity: info.quantity.unwrap_or(0), }) .collect(), fields: s @@ -562,6 +574,7 @@ impl ObjectTemplate { value: s .logic .logic_values + .as_ref() .map(|values| values.get(key)) .flatten() .copied() @@ -582,6 +595,7 @@ impl ObjectTemplate { pins: s .device .device_pins + .as_ref() .map(|pins| Some(pins.clone())) .unwrap_or_else(|| { s.device @@ -589,6 +603,7 @@ impl ObjectTemplate { .map(|pins_len| vec![None; pins_len]) }), device_info: s.device.clone(), + reagents: s.device.reagents.clone(), memory: s .memory .values @@ -603,6 +618,7 @@ impl ObjectTemplate { vm, item_info: i.item.clone(), parent_slot: None, + damage: i.item.damage, }), ItemSlots(i) => VMObject::new(GenericItemStorage { id, @@ -611,6 +627,7 @@ impl ObjectTemplate { vm, item_info: i.item.clone(), parent_slot: None, + damage: i.item.damage, slots: i .slots .iter() @@ -623,6 +640,7 @@ impl ObjectTemplate { readable_logic: Vec::new(), writeable_logic: Vec::new(), occupant: None, + quantity: info.quantity.unwrap_or(0), }) .collect(), }), @@ -633,6 +651,7 @@ impl ObjectTemplate { vm, item_info: i.item.clone(), parent_slot: None, + damage: i.item.damage, slots: i .slots .iter() @@ -675,6 +694,7 @@ impl ObjectTemplate { }) .unwrap_or_else(|| Vec::new()), occupant: None, + quantity: info.quantity.unwrap_or(0), }) .collect(), fields: i @@ -690,6 +710,7 @@ impl ObjectTemplate { value: i .logic .logic_values + .as_ref() .map(|values| values.get(key)) .flatten() .copied() @@ -708,6 +729,7 @@ impl ObjectTemplate { vm, item_info: i.item.clone(), parent_slot: None, + damage: i.item.damage, slots: i .slots .iter() @@ -754,6 +776,7 @@ impl ObjectTemplate { }) .unwrap_or_else(|| Vec::new()), occupant: None, + quantity: info.quantity.unwrap_or(0), }) .collect(), fields: i @@ -769,6 +792,7 @@ impl ObjectTemplate { value: i .logic .logic_values + .as_ref() .map(|values| values.get(key)) .flatten() .copied() @@ -792,6 +816,7 @@ impl ObjectTemplate { vm, item_info: i.item.clone(), parent_slot: None, + damage: i.item.damage, slots: i .slots .iter() @@ -834,6 +859,7 @@ impl ObjectTemplate { }) .unwrap_or_else(|| Vec::new()), occupant: None, + quantity: info.quantity.unwrap_or(0), }) .collect(), fields: i @@ -849,6 +875,7 @@ impl ObjectTemplate { value: i .logic .logic_values + .as_ref() .map(|values| values.get(key)) .flatten() .copied() @@ -888,6 +915,11 @@ impl ObjectTemplate { wireless_transmit: None, wireless_receive: None, network: None, + plant: None, + suit: None, + chargeable: None, + reagent_interface: None, + fabricator: None, } => { // completely generic structure? not sure how this got created but it technically // valid in the data model @@ -914,6 +946,11 @@ impl ObjectTemplate { wireless_transmit: None, wireless_receive: None, network: None, + plant: None, + suit: None, + chargeable: None, + reagent_interface: None, + fabricator: None, } => Ok(ObjectTemplate::StructureSlots(StructureSlotsTemplate { object: Some(obj.into()), prefab: obj.into(), @@ -937,6 +974,11 @@ impl ObjectTemplate { wireless_transmit: _wt, wireless_receive: _wr, network: None, + plant: None, + suit: None, + chargeable: None, + reagent_interface: None, + fabricator: None, } => Ok(ObjectTemplate::StructureLogic(StructureLogicTemplate { object: Some(obj.into()), prefab: obj.into(), @@ -961,6 +1003,11 @@ impl ObjectTemplate { wireless_transmit: _wt, wireless_receive: _wr, network: None, + plant: None, + suit: None, + chargeable: None, + reagent_interface: None, + fabricator: None, } => Ok(ObjectTemplate::StructureLogicDevice( StructureLogicDeviceTemplate { object: Some(obj.into()), @@ -988,6 +1035,11 @@ impl ObjectTemplate { wireless_transmit: _wt, wireless_receive: _wr, network: None, + plant: None, + suit: None, + chargeable: None, + reagent_interface: None, + fabricator: None, } => Ok(ObjectTemplate::StructureLogicDeviceMemory( StructureLogicDeviceMemoryTemplate { object: Some(obj.into()), @@ -1018,6 +1070,11 @@ impl ObjectTemplate { wireless_transmit: None, wireless_receive: None, network: None, + plant: None, + suit: None, + chargeable: None, + reagent_interface: None, + fabricator: None, } => Ok(ObjectTemplate::Item(ItemTemplate { object: Some(obj.into()), prefab: obj.into(), @@ -1040,6 +1097,11 @@ impl ObjectTemplate { wireless_transmit: None, wireless_receive: None, network: None, + plant: None, + suit: None, + chargeable: None, + reagent_interface: None, + fabricator: None, } => Ok(ObjectTemplate::ItemSlots(ItemSlotsTemplate { object: Some(obj.into()), prefab: obj.into(), @@ -1063,6 +1125,11 @@ impl ObjectTemplate { wireless_transmit: _wt, wireless_receive: _wr, network: None, + plant: None, + suit: None, + chargeable: None, + reagent_interface: None, + fabricator: None, } => Ok(ObjectTemplate::ItemLogic(ItemLogicTemplate { object: Some(obj.into()), prefab: obj.into(), @@ -1087,6 +1154,11 @@ impl ObjectTemplate { wireless_transmit: _wt, wireless_receive: _wr, network: None, + plant: None, + suit: None, + chargeable: None, + reagent_interface: None, + fabricator: None, } => Ok(ObjectTemplate::ItemLogicMemory(ItemLogicMemoryTemplate { object: Some(obj.into()), prefab: obj.into(), @@ -1117,6 +1189,11 @@ fn freeze_storage(storage: StorageRef<'_>, vm: &Rc) -> Result, ObjectTemplate::freeze_object(&occupant, vm) }) .map_or(Ok(None), |v| v.map(Some))?, + quantity: if slot.quantity == 0 { + None + } else { + Some(slot.quantity) + }, }) }) .collect::, _>>()?; @@ -1134,7 +1211,8 @@ pub struct PrefabInfo { impl From<&VMObject> for PrefabInfo { fn from(obj: &VMObject) -> Self { - let obj_prefab = obj.borrow().get_prefab(); + let obj_ref = obj.borrow(); + let obj_prefab = obj_ref.get_prefab(); let prefab_lookup = StationpediaPrefab::from_repr(obj_prefab.hash); PrefabInfo { prefab_name: obj_prefab.value.clone(), @@ -1177,6 +1255,8 @@ pub struct SlotInfo { pub typ: SlotClass, #[serde(skip_serializing_if = "Option::is_none")] pub occupant: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub quantity: Option, } #[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] @@ -1291,6 +1371,8 @@ pub struct ItemInfo { pub reagents: Option>, pub slot_class: SlotClass, pub sorting_class: SortingClass, + #[serde(skip_serializing_if = "Option::is_none")] + pub damage: Option, } impl From> for ItemInfo { @@ -1303,6 +1385,11 @@ impl From> for ItemInfo { reagents: item.reagents().cloned(), slot_class: item.slot_class(), sorting_class: item.sorting_class(), + damage: if item.get_damage() == 0.0 { + None + } else { + Some(item.get_damage()) + }, } } } @@ -1316,7 +1403,7 @@ pub struct ConnectionInfo { pub network: Option, } -#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct DeviceInfo { pub connection_list: Vec, @@ -1332,10 +1419,13 @@ pub struct DeviceInfo { pub has_on_off_state: bool, pub has_open_state: bool, pub has_reagents: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub reagents: Option>, } impl From> for DeviceInfo { fn from(device: DeviceRef) -> Self { + let reagents: BTreeMap = device.get_reagents().iter().copied().collect(); DeviceInfo { connection_list: device .connection_list() @@ -1354,6 +1444,11 @@ impl From> for DeviceInfo { has_color_state: device.has_color_state(), has_atmosphere: device.has_atmosphere(), has_activate_state: device.has_activate_state(), + reagents: if reagents.is_empty() { + None + } else { + Some(reagents) + }, } } } diff --git a/ic10emu/src/vm/object/traits.rs b/ic10emu/src/vm/object/traits.rs index 4473602..a85703d 100644 --- a/ic10emu/src/vm/object/traits.rs +++ b/ic10emu/src/vm/object/traits.rs @@ -11,9 +11,7 @@ use crate::{ }, instructions::{traits::ICInstructable, Instruction}, object::{ - errors::{LogicError, MemoryError}, - macros::tag_object_traits, - ObjectID, Slot, + errors::{LogicError, MemoryError}, macros::tag_object_traits, ObjectID, Slot, VMObject }, }, }; @@ -82,27 +80,15 @@ tag_object_traits! { &self, device: i32, connection: Option, - ) -> Option; + ) -> Option; /// i32::MAX is db - fn get_logicable_from_index_mut( - &self, - device: i32, - connection: Option, - ) -> Option; fn get_logicable_from_id( &self, device: ObjectID, connection: Option, - ) -> Option; - fn get_logicable_from_id_mut( - &self, - device: ObjectID, - connection: Option, - ) -> Option; + ) -> Option; fn get_source_code(&self) -> String; fn set_source_code(&self, code: String); - fn get_batch(&self) -> Vec; - fn get_batch_mut(&self) -> Vec; fn get_ic(&self) -> Option; fn hault_and_catch_fire(&mut self); } @@ -117,10 +103,25 @@ tag_object_traits! { fn sorting_class(&self) -> SortingClass; fn get_parent_slot(&self) -> Option; fn set_parent_slot(&mut self, info: Option); + fn get_damage(&self) -> f32; + fn set_damage(&mut self, damage: f32); + } + + pub trait Plant { + fn get_efficiency(&self) -> f64; + fn get_health(&self) -> f64; + fn get_growth(&self) -> f64; + fn is_mature(&self) -> bool; + fn is_seeding(&self) -> bool; + } + + pub trait Suit { + fn pressure_waste(&self) -> f64; + fn pressure_air(&self) -> f64; } pub trait IntegratedCircuit: Logicable + MemoryWritable + SourceCode + Item { - fn get_circuit_holder(&self) -> Option; + fn get_circuit_holder(&self) -> Option; fn get_instruction_pointer(&self) -> f64; fn set_next_instruction(&mut self, next_instruction: f64); fn set_next_instruction_relative(&mut self, offset: f64) { @@ -138,7 +139,7 @@ tag_object_traits! { fn pop_stack(&mut self) -> Result; fn peek_stack(&self) -> Result; fn get_stack(&self, addr: f64) -> Result; - fn put_stack(&self, addr: f64, val: f64) -> Result; + fn put_stack(&mut self, addr: f64, val: f64) -> Result; fn get_aliases(&self) -> &BTreeMap; fn get_aliases_mut(&mut self) -> &mut BTreeMap; fn get_defines(&self) -> &BTreeMap; @@ -152,6 +153,21 @@ tag_object_traits! { fn step(&mut self, advance_ip_on_err: bool) -> Result<(), crate::errors::ICError>; } + pub trait Chargeable { + fn get_charge(&self) -> f32; + fn set_charge(&mut self, charge: f32); + fn get_max_charge(&self) -> f32; + fn get_charge_ratio(&self) -> f32 { + self.get_charge() / self.get_max_charge() + } + fn get_charge_delta(&self) -> f32 { + self.get_charge() - self.get_max_charge() + } + fn is_empty(&self) -> bool { + self.get_charge() == 0.0 + } + } + pub trait Instructable: MemoryWritable { // fn get_instructions(&self) -> Vec } @@ -172,7 +188,7 @@ tag_object_traits! { fn connection_list(&self) -> &[Connection]; fn connection_list_mut(&mut self) -> &mut [Connection]; fn device_pins(&self) -> Option<&[Option]>; - fn device_pins_mut(&self) -> Option<&mut [Option]>; + fn device_pins_mut(&mut self) -> Option<&mut [Option]>; fn has_activate_state(&self) -> bool; fn has_atmosphere(&self) -> bool; fn has_color_state(&self) -> bool;