From f1b68e18b8b409c8b5777b7a6a4fbf19cbdab8d5 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 21 Apr 2024 14:01:16 -0700 Subject: [PATCH] move mutability within IC struct to limit borrow panics --- ic10emu/src/device.rs | 95 +++-------- ic10emu/src/grammar.rs | 8 +- ic10emu/src/interpreter.rs | 340 +++++++++++++++++++++++-------------- ic10emu/src/vm.rs | 48 +++--- ic10emu_wasm/src/lib.rs | 44 ++--- 5 files changed, 287 insertions(+), 248 deletions(-) diff --git a/ic10emu/src/device.rs b/ic10emu/src/device.rs index d5c3cf5..199b0a7 100644 --- a/ic10emu/src/device.rs +++ b/ic10emu/src/device.rs @@ -628,14 +628,14 @@ impl Device { LogicType::LineNumber, LogicField { field_type: FieldType::ReadWrite, - value: ic.ip as f64, + value: ic.ip() as f64, }, ); copy.insert( LogicType::Error, LogicField { field_type: FieldType::Read, - value: match ic.state { + value: match *ic.state.borrow() { ICState::Error(_) => 1.0, _ => 0.0, }, @@ -736,19 +736,12 @@ impl Device { pub fn get_field(&self, typ: LogicType, vm: &VM) -> Result { if typ == LogicType::LineNumber && self.ic.is_some() { - if let Ok(ic) = vm + let ic = vm .ics .get(&self.ic.unwrap()) .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? - .try_borrow() - { - Ok(ic.ip as f64) - } else { - // HACK: the game succeeds in getting the correct line number - // when reading it's own IC, but we'll panic trying to do it here - // this is worked around in internal_step so just return 0 here - Ok(0.0) - } + .borrow(); + Ok(ic.ip() as f64) } else if let Some(field) = self.get_fields(vm).get(&typ) { if field.field_type == FieldType::Read || field.field_type == FieldType::ReadWrite { Ok(field.value) @@ -773,16 +766,12 @@ impl Device { { Err(ICError::ReadOnlyField(typ.to_string())) } else if typ == LogicType::LineNumber && self.ic.is_some() { - // try borrow to set ip, we should only fail if the ic is in use aka is is *our* ic - // in game trying to set your own ic's LineNumber appears to be a Nop so this is fine. - if let Ok(mut ic) = vm + let ic = vm .ics .get(&self.ic.unwrap()) .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? - .try_borrow_mut() - { - ic.ip = val as u32; - } + .borrow(); + ic.set_ip(val as u32); Ok(()) } else if let Some(field) = self.fields.get_mut(&typ) { if field.field_type == FieldType::Write @@ -818,20 +807,12 @@ impl Device { && self.ic.is_some() && typ == SlotLogicType::LineNumber { - // try borrow to get ip, we should only fail if the ic is in us aka is is *our* ic - if let Ok(ic) = vm + let ic = vm .ics .get(&self.ic.unwrap()) .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? - .try_borrow() - { - Ok(ic.ip as f64) - } else { - // HACK: the game succeeds in getting the correct line number - // when reading it's own IC, but we'll panic trying to do it here - // this is worked around in internal_step so just return 0 here - Ok(0.0) - } + .borrow(); + Ok(ic.ip() as f64) } else { Ok(slot.get_field(typ)) } @@ -848,32 +829,18 @@ impl Device { .ok_or(ICError::SlotIndexOutOfRange(index))?; let mut fields = slot.get_fields(); if slot.typ == SlotType::ProgrammableChip && slot.occupant.is_some() && self.ic.is_some() { - // try borrow to get ip, we should only fail if the ic is in us aka is is *our* ic - if let Ok(ic) = vm + let ic = vm .ics .get(&self.ic.unwrap()) .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? - .try_borrow() - { - fields.insert( - SlotLogicType::LineNumber, - LogicField { - field_type: FieldType::ReadWrite, - value: ic.ip as f64, - }, - ); - } else { - // HACK: the game succeeds in getting the correct line number - // when reading it's own IC, but we'll panic trying to do it here - // this is worked around in internal_step so just return 0 here - fields.insert( - SlotLogicType::LineNumber, - LogicField { - field_type: FieldType::ReadWrite, - value: 0.0, - }, - ); - } + .borrow(); + fields.insert( + SlotLogicType::LineNumber, + LogicField { + field_type: FieldType::ReadWrite, + value: ic.ip() as f64, + }, + ); } Ok(fields) } @@ -883,32 +850,14 @@ impl Device { index: f64, typ: SlotLogicType, val: f64, - vm: &VM, + _vm: &VM, force: bool, ) -> Result<(), ICError> { let slot = self .slots .get_mut(index as usize) .ok_or(ICError::SlotIndexOutOfRange(index))?; - if slot.typ == SlotType::ProgrammableChip - && slot.occupant.is_some() - && self.ic.is_some() - && typ == SlotLogicType::LineNumber - { - // try borrow to set ip, we shoudl only fail if the ic is in us aka is is *our* ic - // in game trying to set your own ic's LineNumber appears to be a Nop so this is fine. - if let Ok(mut ic) = vm - .ics - .get(&self.ic.unwrap()) - .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? - .try_borrow_mut() - { - ic.ip = val as u32; - } - Ok(()) - } else { - slot.set_field(typ, val, force) - } + slot.set_field(typ, val, force) } pub fn get_slot(&self, index: f64) -> Result<&Slot, ICError> { diff --git a/ic10emu/src/grammar.rs b/ic10emu/src/grammar.rs index 24ec601..a0d5568 100644 --- a/ic10emu/src/grammar.rs +++ b/ic10emu/src/grammar.rs @@ -431,6 +431,7 @@ impl Operand { Device::Numbered(p) => { let dp = ic .pins + .borrow() .get(p as usize) .ok_or(interpreter::ICError::DeviceIndexOutOfRange(p as f64)) .copied()?; @@ -443,6 +444,7 @@ impl Operand { let val = ic.get_register(indirection, target)?; let dp = ic .pins + .borrow() .get(val as usize) .ok_or(interpreter::ICError::DeviceIndexOutOfRange(val)) .copied()?; @@ -522,11 +524,11 @@ impl Operand { pub fn translate_alias(&self, ic: &interpreter::IC) -> Self { match &self { Operand::Identifier(id) => { - if let Some(alias) = ic.aliases.get(&id.name) { + if let Some(alias) = ic.aliases.borrow().get(&id.name) { alias.clone() - } else if let Some(define) = ic.defines.get(&id.name) { + } else if let Some(define) = ic.defines.borrow().get(&id.name) { Operand::Number(Number::Float(*define)) - } else if let Some(label) = ic.program.labels.get(&id.name) { + } else if let Some(label) = ic.program.borrow().labels.get(&id.name) { Operand::Number(Number::Float(*label as f64)) } else { self.clone() diff --git a/ic10emu/src/interpreter.rs b/ic10emu/src/interpreter.rs index 933f256..81bc0ec 100644 --- a/ic10emu/src/interpreter.rs +++ b/ic10emu/src/interpreter.rs @@ -1,6 +1,6 @@ use core::f64; use serde::{Deserialize, Serialize}; -use std::{ops::Deref, string::ToString}; +use std::{cell::{Cell, RefCell}, ops::Deref, string::ToString}; use std::{ collections::{HashMap, HashSet}, error::Error, @@ -180,21 +180,20 @@ impl Display for ICState { pub struct IC { pub device: u32, pub id: u32, - pub registers: [f64; 18], + pub registers: RefCell<[f64; 18]>, /// Instruction Pointer - pub ip: u32, + pub ip: Cell, /// Instruction Count since last yield - pub ic: u16, - pub stack: [f64; 512], - pub aliases: HashMap, - pub defines: HashMap, - pub pins: [Option; 6], - pub code: String, - pub program: Program, - pub state: ICState, + pub ic: Cell, + pub stack: RefCell<[f64; 512]>, + pub aliases: RefCell>, + pub defines: RefCell>, + pub pins: RefCell<[Option; 6]>, + pub code: RefCell, + pub program: RefCell, + pub state: RefCell, } - #[serde_as] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FrozenIC { @@ -215,21 +214,22 @@ pub struct FrozenIC { } impl From for FrozenIC - where T: Deref +where + T: Deref, { fn from(ic: T) -> Self { FrozenIC { device: ic.device, id: ic.id, - registers: ic.registers, - ip: ic.ip, - ic: ic.ic, - stack: ic.stack, - aliases: ic.aliases.clone(), - defines: ic.defines.clone(), - pins: ic.pins, - state: ic.state.clone(), - code: ic.code.clone(), + registers: *ic.registers.borrow(), + ip: ic.ip.get(), + ic: ic.ic.get(), + stack: *ic.stack.borrow(), + aliases: ic.aliases.borrow().clone(), + defines: ic.defines.borrow().clone(), + pins: *ic.pins.borrow(), + state: ic.state.borrow().clone(), + code: ic.code.borrow().clone(), } } } @@ -239,16 +239,16 @@ impl From for IC { IC { device: value.device, id: value.id, - registers: value.registers, - ip: value.ip, - ic: value.ic, - stack: value.stack, - aliases: value.aliases, - defines: value.defines, - pins: value.pins, - state: value.state, - code: value.code.clone(), - program: Program::from_code_with_invalid(&value.code), + registers: RefCell::new(value.registers), + ip: Cell::new(value.ip), + ic: Cell::new(value.ic), + stack: RefCell::new(value.stack), + aliases: RefCell::new(value.aliases), + defines: RefCell::new(value.defines), + pins: RefCell::new(value.pins), + state: RefCell::new(value.state), + code: RefCell::new(value.code.clone()), + program: RefCell::new(Program::from_code_with_invalid(&value.code)), } } } @@ -369,57 +369,49 @@ impl IC { IC { device, id, - ip: 0, - ic: 0, - registers: [0.0; 18], - stack: [0.0; 512], - pins: [None; 6], - program: Program::new(), - code: String::new(), - aliases: HashMap::new(), - defines: HashMap::new(), - state: ICState::Start, + ip: Cell::new(0), + ic: Cell::new(0), + registers: RefCell::new([0.0; 18]), + stack: RefCell::new([0.0; 512]), + pins: RefCell::new([None; 6]), + program: RefCell::new(Program::new()), + code: RefCell::new(String::new()), + aliases: RefCell::new(HashMap::new()), + defines: RefCell::new(HashMap::new()), + state: RefCell::new(ICState::Start), } } - pub fn reset(&mut self) { - self.ip = 0; - self.ic = 0; - self.registers = [0.0; 18]; - self.stack = [0.0; 512]; - self.aliases = HashMap::new(); - self.defines = HashMap::new(); - self.state = ICState::Start; + pub fn reset(&self) { + self.ip.replace(0); + self.ic.replace(0); + self.registers.replace([0.0; 18]); + self.stack.replace([0.0; 512]); + self.aliases.replace(HashMap::new()); + self.defines.replace(HashMap::new()); + self.state.replace(ICState::Start); } /// Set program code if it's valid - pub fn set_code(&mut self, code: &str) -> Result<(), ICError> { + pub fn set_code(&self, code: &str) -> Result<(), ICError> { let prog = Program::try_from_code(code)?; - self.ip = 0; - self.ic = 0; - self.aliases = HashMap::new(); - self.defines = HashMap::new(); - self.program = prog; - self.code = code.to_string(); + self.program.replace(prog); + self.code.replace(code.to_string()); Ok(()) } /// Set program code and translate invalid lines to Nop, collecting errors pub fn set_code_invalid(&mut self, code: &str) { let prog = Program::from_code_with_invalid(code); - self.ip = 0; - self.ic = 0; - self.aliases = HashMap::new(); - self.defines = HashMap::new(); - self.program = prog; - self.code = code.to_string(); + self.program.replace(prog); + self.code.replace(code.to_string()); } pub fn get_real_target(&self, indirection: u32, target: u32) -> Result { let mut i = indirection; let mut t = target as f64; while i > 0 { - if let Some(new_t) = self.registers.get(t as usize) { + if let Some(new_t) = self.registers.borrow().get(t as usize) { t = *new_t; } else { return Err(ICError::RegisterIndexOutOfRange(t)); @@ -432,6 +424,7 @@ impl IC { pub fn get_register(&self, indirection: u32, target: u32) -> Result { let t = self.get_real_target(indirection, target)?; self.registers + .borrow() .get(t as usize) .ok_or(ICError::RegisterIndexOutOfRange(t)) .copied() @@ -439,72 +432,76 @@ impl IC { /// sets a register thorough, recursing through provided indirection, returns value previously pub fn set_register( - &mut self, + &self, indirection: u32, target: u32, val: f64, ) -> Result { let t = self.get_real_target(indirection, target)?; - let old_val = self - .registers + let mut registers = self.registers.borrow_mut(); + let old_val = registers .get(t as usize) .ok_or(ICError::RegisterIndexOutOfRange(t)) .copied()?; - self.registers[t as usize] = val; + registers[t as usize] = val; Ok(old_val) } /// save ip to 'ra' or register 18 - fn al(&mut self) { - self.registers[17] = self.ip as f64 + 1.0; + fn al(&self) { + self.registers.borrow_mut()[17] = self.ip() as f64 + 1.0; } - pub fn push(&mut self, val: f64) -> Result { - let sp = (self.registers[16].round()) as i32; + pub fn push(&self, val: f64) -> Result { + let mut registers = self.registers.borrow_mut(); + let mut stack = self.stack.borrow_mut(); + let sp = (registers[16].round()) as i32; if sp < 0 { Err(ICError::StackUnderflow) } else if sp >= 512 { Err(ICError::StackOverflow) } else { - let last = self.stack[sp as usize]; - self.stack[sp as usize] = val; - self.registers[16] += 1.0; + let last = stack[sp as usize]; + stack[sp as usize] = val; + registers[16] += 1.0; Ok(last) } } - pub fn pop(&mut self) -> Result { - self.registers[16] -= 1.0; - let sp = (self.registers[16].round()) as i32; + pub fn pop(&self) -> Result { + let mut registers = self.registers.borrow_mut(); + registers[16] -= 1.0; + let sp = (registers[16].round()) as i32; if sp < 0 { Err(ICError::StackUnderflow) } else if sp >= 512 { Err(ICError::StackOverflow) } else { - let last = self.stack[sp as usize]; + let last = self.stack.borrow()[sp as usize]; Ok(last) } } - pub fn poke(&mut self, address: f64, val: f64) -> Result { + pub fn poke(&self, address: f64, val: f64) -> Result { let sp = address.round() as i32; if !(0..512).contains(&sp) { Err(ICError::StackIndexOutOfRange(address)) } else { - let last = self.stack[sp as usize]; - self.stack[sp as usize] = val; + let mut stack = self.stack.borrow_mut(); + let last = stack[sp as usize]; + stack[sp as usize] = val; Ok(last) } } pub fn peek(&self) -> Result { - let sp = (self.registers[16] - 1.0).round() as i32; + let sp = (self.registers.borrow()[16] - 1.0).round() as i32; if sp < 0 { Err(ICError::StackUnderflow) } else if sp >= 512 { Err(ICError::StackOverflow) } else { - let last = self.stack[sp as usize]; + let last = self.stack.borrow()[sp as usize]; Ok(last) } } @@ -516,37 +513,49 @@ impl IC { } else if sp >= 512 { Err(ICError::StackOverflow) } else { - let last = self.stack[sp as usize]; + let last = self.stack.borrow()[sp as usize]; Ok(last) } } /// processes one line of the contained program - pub fn step(&mut self, vm: &VM, advance_ip_on_err: bool) -> Result { + pub fn step(&self, vm: &VM, advance_ip_on_err: bool) -> Result { // TODO: handle sleep - self.state = ICState::Running; - let line = self.ip; + self.state.replace(ICState::Running); + let line = self.ip(); let result = self.internal_step(vm, advance_ip_on_err); if let Err(error) = result { let error = LineError { error, line }; - self.state = ICState::Error(error.clone()); + self.state.replace(ICState::Error(error.clone())); Err(error) } else { Ok(true) } } - fn internal_step(&mut self, vm: &VM, advance_ip_on_err: bool) -> Result<(), ICError> { + pub fn ip(&self) -> u32 { + self.ip.get() + } + + pub fn set_ip(&self, val: u32) { + self.ip.replace(val); + } + + fn internal_step(&self, vm: &VM, advance_ip_on_err: bool) -> Result<(), ICError> { use grammar::*; use ICError::*; - let mut next_ip = self.ip + 1; + let mut next_ip = self.ip() + 1; // XXX: This closure should be replaced with a try block // https://github.com/rust-lang/rust/issues/31436 - let mut process_op = |this: &mut Self| -> Result<(), ICError> { + let mut process_op = |this: &Self| -> Result<(), ICError> { use grammar::InstructionOp::*; - let line = this.program.get_line(this.ip)?; + // force the program borrow to drop + let line = { + let prog = this.program.borrow(); + prog.get_line(this.ip())?.clone() + }; let operands = &line.operands; let inst = line.instruction; match inst { @@ -557,14 +566,14 @@ impl IC { let a = a.as_value(this, inst, 1)?; let now = time::OffsetDateTime::now_local() .unwrap_or_else(|_| time::OffsetDateTime::now_utc()); - this.state = ICState::Sleep(now, a); + this.state.replace(ICState::Sleep(now, a)); Ok(()) } oprs => Err(ICError::mismatch_operands(oprs.len(), 1)), }, // TODO Yield => match &operands[..] { [] => { - this.state = ICState::Yield; + this.state.replace(ICState::Yield); Ok(()) } oprs => Err(ICError::mismatch_operands(oprs.len(), 0)), @@ -585,10 +594,11 @@ impl IC { desired: "Number".to_owned(), }); }; - if this.defines.contains_key(&ident.name) { + let mut defines = this.defines.borrow_mut(); + if defines.contains_key(&ident.name) { Err(DuplicateDefine(ident.name.clone())) } else { - this.defines.insert(ident.name.clone(), num.value()); + defines.insert(ident.name.clone(), num.value()); Ok(()) } } @@ -625,7 +635,7 @@ impl IC { }) } }; - this.aliases.insert(ident.name.clone(), alias); + this.aliases.borrow_mut().insert(ident.name.clone(), alias); Ok(()) } oprs => Err(ICError::mismatch_operands(oprs.len(), 2)), @@ -671,7 +681,7 @@ impl IC { let b = b.as_value(this, inst, 2)?; let c = c.as_value(this, inst, 3)?; next_ip = if a == b { - (this.ip as f64 + c) as u32 + (this.ip() as f64 + c) as u32 } else { next_ip }; @@ -703,7 +713,7 @@ impl IC { let a = a.as_value(this, inst, 1)?; let b = b.as_value(this, inst, 2)?; next_ip = if a == 0.0 { - (this.ip as f64 + b) as u32 + (this.ip() as f64 + b) as u32 } else { next_ip }; @@ -738,7 +748,7 @@ impl IC { let b = b.as_value(this, inst, 2)?; let c = c.as_value(this, inst, 3)?; next_ip = if a != b { - (this.ip as f64 + c) as u32 + (this.ip() as f64 + c) as u32 } else { next_ip }; @@ -770,7 +780,7 @@ impl IC { let a = a.as_value(this, inst, 1)?; let b = b.as_value(this, inst, 2)?; next_ip = if a != 0.0 { - (this.ip as f64 + b) as u32 + (this.ip() as f64 + b) as u32 } else { next_ip }; @@ -805,7 +815,7 @@ impl IC { let b = b.as_value(this, inst, 2)?; let c = c.as_value(this, inst, 3)?; next_ip = if a < b { - (this.ip as f64 + c) as u32 + (this.ip() as f64 + c) as u32 } else { next_ip }; @@ -840,7 +850,7 @@ impl IC { let b = b.as_value(this, inst, 2)?; let c = c.as_value(this, inst, 3)?; next_ip = if a <= b { - (this.ip as f64 + c) as u32 + (this.ip() as f64 + c) as u32 } else { next_ip }; @@ -872,7 +882,7 @@ impl IC { let a = a.as_value(this, inst, 1)?; let b = b.as_value(this, inst, 2)?; next_ip = if a <= 0.0 { - (this.ip as f64 + b) as u32 + (this.ip() as f64 + b) as u32 } else { next_ip }; @@ -904,7 +914,7 @@ impl IC { let a = a.as_value(this, inst, 1)?; let b = b.as_value(this, inst, 2)?; next_ip = if a < 0.0 { - (this.ip as f64 + b) as u32 + (this.ip() as f64 + b) as u32 } else { next_ip }; @@ -939,7 +949,7 @@ impl IC { let b = b.as_value(this, inst, 2)?; let c = c.as_value(this, inst, 3)?; next_ip = if a > b { - (this.ip as f64 + c) as u32 + (this.ip() as f64 + c) as u32 } else { next_ip }; @@ -971,7 +981,7 @@ impl IC { let a = a.as_value(this, inst, 1)?; let b = b.as_value(this, inst, 2)?; next_ip = if a > 0.0 { - (this.ip as f64 + b) as u32 + (this.ip() as f64 + b) as u32 } else { next_ip }; @@ -1006,7 +1016,7 @@ impl IC { let b = b.as_value(this, inst, 2)?; let c = c.as_value(this, inst, 3)?; next_ip = if a >= b { - (this.ip as f64 + c) as u32 + (this.ip() as f64 + c) as u32 } else { next_ip }; @@ -1038,7 +1048,7 @@ impl IC { let a = a.as_value(this, inst, 1)?; let b = b.as_value(this, inst, 2)?; next_ip = if a >= 0.0 { - (this.ip as f64 + b) as u32 + (this.ip() as f64 + b) as u32 } else { next_ip }; @@ -1090,7 +1100,7 @@ impl IC { next_ip = if f64::abs(a - b) <= f64::max(c * f64::max(a.abs(), b.abs()), f64::EPSILON * 8.0) { - (this.ip as f64 + d) as u32 + (this.ip() as f64 + d) as u32 } else { next_ip }; @@ -1134,7 +1144,7 @@ impl IC { let b = b.as_value(this, inst, 2)?; let c = c.as_value(this, inst, 3)?; next_ip = if a.abs() <= f64::max(b * a.abs(), f64::EPSILON * 8.0) { - (this.ip as f64 + c) as u32 + (this.ip() as f64 + c) as u32 } else { next_ip }; @@ -1186,7 +1196,7 @@ impl IC { next_ip = if f64::abs(a - b) > f64::max(c * f64::max(a.abs(), b.abs()), f64::EPSILON * 8.0) { - (this.ip as f64 + d) as u32 + (this.ip() as f64 + d) as u32 } else { next_ip }; @@ -1229,7 +1239,7 @@ impl IC { let b = b.as_value(this, inst, 2)?; let c = c.as_value(this, inst, 3)?; next_ip = if a.abs() > f64::max(b * a.abs(), f64::EPSILON * 8.0) { - (this.ip as f64 + c) as u32 + (this.ip() as f64 + c) as u32 } else { next_ip }; @@ -1261,7 +1271,7 @@ impl IC { let (device, _connection) = d.as_device(this, inst, 1)?; let a = a.as_value(this, inst, 2)?; next_ip = if device.is_some() { - (this.ip as f64 + a) as u32 + (this.ip() as f64 + a) as u32 } else { next_ip }; @@ -1293,7 +1303,7 @@ impl IC { let (device, _connection) = d.as_device(this, inst, 1)?; let a = a.as_value(this, inst, 2)?; next_ip = if device.is_none() { - (this.ip as f64 + a) as u32 + (this.ip() as f64 + a) as u32 } else { next_ip }; @@ -1315,7 +1325,7 @@ impl IC { let a = a.as_value(this, inst, 1)?; let b = b.as_value(this, inst, 2)?; next_ip = if a.is_nan() { - (this.ip as f64 + b) as u32 + (this.ip() as f64 + b) as u32 } else { next_ip }; @@ -1344,7 +1354,7 @@ impl IC { Jr => match &operands[..] { [a] => { let a = a.as_value(this, inst, 1)?; - next_ip = (this.ip as f64 + a) as u32; + next_ip = (this.ip() as f64 + a) as u32; Ok(()) } oprs => Err(ICError::too_many_operands(oprs.len(), 1)), @@ -2178,7 +2188,7 @@ impl IC { if ic_id == &this.id { this.poke(addr, val)?; } else { - let mut ic = vm.ics.get(ic_id).unwrap().borrow_mut(); + let ic = vm.ics.get(ic_id).unwrap().borrow(); ic.poke(addr, val)?; } vm.set_modified(device_id); @@ -2206,7 +2216,7 @@ impl IC { if ic_id == &this.id { this.poke(addr, val)?; } else { - let mut ic = vm.ics.get(ic_id).unwrap().borrow_mut(); + let ic = vm.ics.get(ic_id).unwrap().borrow(); ic.poke(addr, val)?; } vm.set_modified(device_id as u32); @@ -2360,7 +2370,7 @@ impl IC { if lt == LogicType::LineNumber && this.device == device_id { // HACK: we can't use device.get_field as that will try to reborrow our // ic which will panic - this.set_register(indirection, target, this.ip as f64)?; + this.set_register(indirection, target, this.ip() as f64)?; Ok(()) } else { let device = vm.get_device_same_network(this.device, device_id); @@ -2390,7 +2400,7 @@ impl IC { if lt == LogicType::LineNumber && this.device == device_id as u32 { // HACK: we can't use device.get_field as that will try to reborrow our // ic which will panic - this.set_register(indirection, target, this.ip as f64)?; + this.set_register(indirection, target, this.ip() as f64)?; Ok(()) } else { let device = vm.get_device_same_network(this.device, device_id as u32); @@ -2419,7 +2429,7 @@ impl IC { if slt == SlotLogicType::LineNumber && this.device == device_id { // HACK: we can't use device.get_slot_field as that will try to reborrow our // ic which will panic - this.set_register(indirection, target, this.ip as f64)?; + this.set_register(indirection, target, this.ip() as f64)?; Ok(()) } else { let device = vm.get_device_same_network(this.device, device_id); @@ -2536,8 +2546,8 @@ impl IC { }; let result = process_op(self); if result.is_ok() || advance_ip_on_err { - self.ic += 1; - self.ip = next_ip; + self.ic.set(self.ic.get() + 1); + self.set_ip(next_ip); } result } @@ -2590,3 +2600,83 @@ pub fn i64_to_f64(i: i64) -> f64 { } i as f64 } + +#[cfg(test)] +mod tests { + use crate::vm::VMError; + + use super::*; + + #[test] + fn batch_modes() -> Result<(), VMError> { + let mut vm = VM::new(); + let ic = vm.add_ic(None).unwrap(); + let ic_id = { + let device = vm.devices.get(&ic).unwrap(); + let device_ref = device.borrow(); + device_ref.ic.unwrap() + }; + let ic_chip = vm.ics.get(&ic_id).unwrap().borrow(); + vm.set_code( + ic, + r#"lb r0 HASH("ItemActiveVent") On Sum + #lb r1 HASH("ItemActiveVent") On Maximum + lb r2 HASH("ItemActiveVent") On Minimum"#, + )?; + vm.step_ic(ic, false)?; + let r0 = ic_chip.get_register(0, 0).unwrap(); + assert_eq!(r0, 0.0); + vm.step_ic(ic, false)?; + // let r1 = ic_chip.get_register(0, 1).unwrap(); + // assert_eq!(r1, f64::NEG_INFINITY); + vm.step_ic(ic, false)?; + let r2 = ic_chip.get_register(0, 2).unwrap(); + assert_eq!(r2, f64::INFINITY); + Ok(()) + } + + #[test] + fn stack() -> Result<(), VMError> { + let mut vm = VM::new(); + let ic = vm.add_ic(None).unwrap(); + let ic_id = { + let device = vm.devices.get(&ic).unwrap(); + let device_ref = device.borrow(); + device_ref.ic.unwrap() + }; + let ic_chip = vm.ics.get(&ic_id).unwrap().borrow(); + vm.set_code( + ic, + r#"push 100 + push 10 + pop r0 + push 1000 + peek r1 + poke 1 20 + pop r2 + "#, + )?; + vm.step_ic(ic, false)?; + let stack0 = ic_chip.peek_addr(0.0)?; + assert_eq!(stack0, 100.0); + vm.step_ic(ic, false)?; + let stack1 = ic_chip.peek_addr(1.0)?; + assert_eq!(stack1, 10.0); + vm.step_ic(ic, false)?; + let r0 = ic_chip.get_register(0, 0).unwrap(); + assert_eq!(r0, 10.0); + vm.step_ic(ic, false)?; + let stack1 = ic_chip.peek_addr(1.0)?; + assert_eq!(stack1, 1000.0); + vm.step_ic(ic, false)?; + let r1 = ic_chip.get_register(0, 1).unwrap(); + assert_eq!(r1, 1000.0); + vm.step_ic(ic, false)?; + let stack1 = ic_chip.peek_addr(1.0)?; + assert_eq!(stack1, 20.0); + vm.step_ic(ic, false)?; + let r2 = ic_chip.get_register(0, 2).unwrap(); + assert_eq!(r2, 20.0); + Ok(()) + } +} diff --git a/ic10emu/src/vm.rs b/ic10emu/src/vm.rs index 832ee65..813e6d2 100644 --- a/ic10emu/src/vm.rs +++ b/ic10emu/src/vm.rs @@ -285,13 +285,11 @@ impl VM { device.borrow_mut().id = new_id; self.devices.insert(new_id, device); self.ics.iter().for_each(|(_id, ic)| { - if let Ok(mut ic_ref) = ic.try_borrow_mut() { - ic_ref.pins.iter_mut().for_each(|pin| { - if pin.is_some_and(|d| d == old_id) { - pin.replace(new_id); - } - }) - } + ic.borrow().pins.borrow_mut().iter_mut().for_each(|pin| { + if pin.is_some_and(|d| d == old_id) { + pin.replace(new_id); + } + }) }); self.networks.iter().for_each(|(_net_id, net)| { if let Ok(mut net_ref) = net.try_borrow_mut() { @@ -312,15 +310,14 @@ impl VM { .ok_or(VMError::UnknownId(id))? .borrow(); let ic_id = *device.ic.as_ref().ok_or(VMError::NoIC(id))?; - let mut ic = self + let ic = self .ics .get(&ic_id) .ok_or(VMError::UnknownIcId(ic_id))? - .borrow_mut(); + .borrow(); let new_prog = interpreter::Program::try_from_code(code)?; - ic.program = new_prog; - ic.ip = 0; - ic.code = code.to_string(); + ic.program.replace(new_prog); + ic.code.replace(code.to_string()); Ok(true) } @@ -332,15 +329,14 @@ impl VM { .ok_or(VMError::UnknownId(id))? .borrow(); let ic_id = *device.ic.as_ref().ok_or(VMError::NoIC(id))?; - let mut ic = self + let ic = self .ics .get(&ic_id) .ok_or(VMError::UnknownIcId(ic_id))? .borrow_mut(); let new_prog = interpreter::Program::from_code_with_invalid(code); - ic.program = new_prog; - ic.ip = 0; - ic.code = code.to_string(); + ic.program.replace(new_prog); + ic.code.replace(code.to_string()); Ok(true) } @@ -363,8 +359,8 @@ impl VM { .get(&ic_id) .ok_or(VMError::UnknownIcId(ic_id))? .clone(); - ic.borrow_mut().ic = 0; - let result = ic.borrow_mut().step(self, advance_ip_on_err)?; + ic.borrow().ic.replace(0); + let result = ic.borrow().step(self, advance_ip_on_err)?; Ok(result) } @@ -378,7 +374,7 @@ impl VM { .get(&ic_id) .ok_or(VMError::UnknownIcId(ic_id))? .clone(); - ic.borrow_mut().ic = 0; + ic.borrow().ic.replace(0); self.set_modified(id); for _i in 0..128 { if let Err(err) = ic.borrow_mut().step(self, ignore_errors) { @@ -386,13 +382,15 @@ impl VM { return Err(err.into()); } } - if let interpreter::ICState::Yield = ic.borrow().state { + if let interpreter::ICState::Yield = *ic.borrow().state.borrow() { return Ok(false); - } else if let interpreter::ICState::Sleep(_then, _sleep_for) = ic.borrow().state { + } else if let interpreter::ICState::Sleep(_then, _sleep_for) = + *ic.borrow().state.borrow() + { return Ok(false); } } - ic.borrow_mut().state = interpreter::ICState::Yield; + ic.borrow().state.replace(interpreter::ICState::Yield); Ok(true) } @@ -408,8 +406,8 @@ impl VM { .get(&ic_id) .ok_or(VMError::UnknownIcId(ic_id))? .clone(); - ic.borrow_mut().ic = 0; - ic.borrow_mut().reset(); + ic.borrow().ic.replace(0); + ic.borrow().reset(); Ok(true) } @@ -506,7 +504,7 @@ impl VM { let Some(ic_id) = device.borrow().ic else { return Err(VMError::NoIC(id)); }; - self.ics.get(&ic_id).unwrap().borrow_mut().pins[pin] = val; + self.ics.get(&ic_id).unwrap().borrow().pins.borrow_mut()[pin] = val; Ok(true) } } diff --git a/ic10emu_wasm/src/lib.rs b/ic10emu_wasm/src/lib.rs index 703501d..5c851c4 100644 --- a/ic10emu_wasm/src/lib.rs +++ b/ic10emu_wasm/src/lib.rs @@ -116,7 +116,7 @@ impl DeviceRef { .borrow() .ics .get(ic) - .map(|ic| ic.as_ref().borrow().ip) + .map(|ic| ic.as_ref().borrow().ip()) }) } @@ -127,7 +127,7 @@ impl DeviceRef { .borrow() .ics .get(ic) - .map(|ic| ic.as_ref().borrow().ic) + .map(|ic| ic.as_ref().borrow().ic.get()) }) } @@ -138,7 +138,7 @@ impl DeviceRef { .borrow() .ics .get(ic) - .map(|ic| Stack(ic.as_ref().borrow().stack)) + .map(|ic| Stack(*ic.as_ref().borrow().stack.borrow())) }) } @@ -149,44 +149,44 @@ impl DeviceRef { .borrow() .ics .get(ic) - .map(|ic| Registers(ic.as_ref().borrow().registers)) + .map(|ic| Registers(*ic.as_ref().borrow().registers.borrow())) }) } #[wasm_bindgen(getter, js_name = "aliases", skip_typescript)] pub fn ic_aliases(&self) -> JsValue { - serde_wasm_bindgen::to_value(&self.device.borrow().ic.as_ref().and_then(|ic| { + let aliases = &self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() .ics .get(ic) - .map(|ic| ic.as_ref().borrow().aliases.clone()) - })) - .unwrap() + .map(|ic| ic.as_ref().borrow().aliases.borrow().clone()) + }); + serde_wasm_bindgen::to_value(aliases).unwrap() } #[wasm_bindgen(getter, js_name = "defines", skip_typescript)] pub fn ic_defines(&self) -> JsValue { - serde_wasm_bindgen::to_value(&self.device.borrow().ic.as_ref().and_then(|ic| { + let defines = &self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() .ics .get(ic) - .map(|ic| ic.as_ref().borrow().defines.clone()) - })) - .unwrap() + .map(|ic| ic.as_ref().borrow().defines.borrow().clone()) + }); + serde_wasm_bindgen::to_value(defines).unwrap() } #[wasm_bindgen(getter, js_name = "pins", skip_typescript)] pub fn ic_pins(&self) -> JsValue { - serde_wasm_bindgen::to_value(&self.device.borrow().ic.as_ref().and_then(|ic| { + let pins = &self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() .ics .get(ic) - .map(|ic| ic.as_ref().borrow().pins) - })) - .unwrap() + .map(|ic| *ic.as_ref().borrow().pins.borrow()) + }); + serde_wasm_bindgen::to_value(pins).unwrap() } #[wasm_bindgen(getter, js_name = "state")] @@ -202,19 +202,19 @@ impl DeviceRef { .get(ic) .map(|ic| ic.borrow().state.clone()) }) - .map(|state| state.to_string()) + .map(|state| state.borrow().to_string()) } #[wasm_bindgen(getter, js_name = "program", skip_typescript)] pub fn ic_program(&self) -> JsValue { - serde_wasm_bindgen::to_value(&self.device.borrow().ic.as_ref().and_then(|ic| { + let prog = &self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() .ics .get(ic) - .map(|ic| ic.borrow().program.clone()) - })) - .unwrap() + .map(|ic| ic.borrow().program.borrow().clone()) + }); + serde_wasm_bindgen::to_value(prog).unwrap() } #[wasm_bindgen(getter, js_name = "code")] @@ -224,7 +224,7 @@ impl DeviceRef { .borrow() .ics .get(ic) - .map(|ic| ic.borrow().code.clone()) + .map(|ic| ic.borrow().code.borrow().clone()) }) }