use std::ops::Rem; use num_traits::PrimInt; use asm::data::literal::Addr; use asm::instr::Op; use crate::fault::Fault; use crate::frame::StackFrame; use crate::run_thread::RunThread; pub type CyclesSpent = usize; pub struct EvalRes { pub cycles: u8, pub advance: i64, } impl RunThread { // TODO unit tests pub fn eval_op(&mut self) -> Result { let mut cycles = 1; let mut advance = 1; let mut frame = &mut self.frame; let op = self.program.read(frame.pc); debug!("------------------------"); debug!("{} | {:?}", frame.pc, op); /* Operations can be given different execution times when run in slow mode. */ /* Presently, all that do anything use 1 cycle. */ match op { Op::Nop => {} Op::FarLabel(_) | Op::Routine(_) => { /* this is nop, but without any cost - just markers */ cycles = 0; } Op::Barrier(msg) => { return Err(Fault::Barrier { msg: msg.clone().unwrap_or_else(|| "No msg".into()) }); } Op::Fault(msg) => { return Err(Fault::FaultInstr { msg: msg.clone().unwrap_or_else(|| "No msg".into()) }); } Op::FarJump(name) => { debug!("Far jump to {}", name); match self.program.find_far_label(name) { Ok(pos) => { debug!("label is at {}", pos); self.frame.pc = pos; } Err(e) => { return Err(e); } } } Op::Call(name, args) => { debug!("Call routine {}", name); match self.program.find_routine(name) { Ok(pos) => { debug!("routine is at {}", pos); let mut values = Vec::with_capacity(args.len()); for arg in args { values.push(self.frame.read(*arg)?); } let mut frame2 = StackFrame::new(pos, &values); std::mem::swap(&mut self.frame, &mut frame2); self.call_stack.push(frame2); advance = 0; } Err(e) => { return Err(e); } } } Op::Ret(retvals) => { match self.call_stack.pop() { Some(previous) => { let mut values = Vec::with_capacity(retvals.len()); for arg in retvals { values.push(frame.read(*arg)?); } *frame = previous; frame.set_retvals(&values); advance = 1; // advance past the call } None => { return Err(Fault::CallStackUnderflow); } } } Op::Skip(val) => { let steps = frame.read(*val)?; advance = i64::from_ne_bytes(steps.to_ne_bytes()); self.program.validate_jump(frame.pc, Addr((frame.pc.0 as i64 + advance) as u64))?; } Op::SkipIf(cond, val) => { if frame.status.test(*cond) { let steps = frame.read(*val)?; advance = i64::from_ne_bytes(steps.to_ne_bytes()); self.program.validate_jump(frame.pc, Addr((frame.pc.0 as i64 + advance) as u64))?; } } Op::Move { dst, src } => { frame.status.clear(); let val = frame.read(*src)?; frame.status.update(val); frame.write(*dst, val)?; } Op::Test { a } => { frame.status.clear(); let res = frame.read(*a)?; frame.status.update(res); } Op::Compare { a, b } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*b)?; frame.status.equal = x == y; frame.status.lower = x < y; frame.status.greater = x > y; // Test flags are set when both arguments have the property if x == y { frame.status.update(x); } } Op::Add { dst, a, b } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*b)?; let (res, ov) = if let Some(v) = x.checked_add(y) { (v, false) } else { (x.wrapping_add(y), true) }; frame.status.update(res); frame.status.overflow = ov; frame.write(*dst, res)?; } Op::Sub { dst, a, b } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*b)?; let (res, ov) = if let Some(v) = x.checked_sub(y) { (v, false) } else { (x.wrapping_sub(y), true) }; frame.status.update(res); frame.status.overflow = ov; frame.write(*dst, res)?; } Op::Mul { dst, a, b } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*b)?; let (res, ov) = if let Some(v) = x.checked_mul(y) { (v, false) } else { (x.wrapping_mul(y), true) }; frame.status.update(res); frame.status.overflow = ov; frame.write(*dst, res)?; } Op::Div { dst, rem, a, div } => { frame.status.clear(); let x = frame.read(*a)?; let d = frame.read(*div)?; if d == 0 { frame.status.invalid = true; } else { let (res, remainder, ov) = if let Some(v) = x.checked_div(d) { (v, x.rem(d), false) } else { (x.wrapping_div(d), x.wrapping_rem(d), true) }; frame.status.update(res); frame.status.overflow = ov; frame.write(*dst, res)?; frame.write(*rem, remainder)?; } } Op::Mod { dst, a, div } => { frame.status.clear(); let x = frame.read(*a)?; let d = frame.read(*div)?; if d == 0 { frame.status.invalid = true; } else { let (remainder, ov) = if let Some(v) = x.checked_rem(d) { (v, false) } else { (x.wrapping_rem(d), true) }; frame.status.update(remainder); frame.status.overflow = ov; frame.write(*dst, remainder)?; } } Op::And { dst, a, b } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*b)?; let res = x & y; frame.status.update(res); frame.write(*dst, res)?; } Op::Or { dst, a, b } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*b)?; let res = x | y; frame.status.update(res); frame.write(*dst, res)?; } Op::Xor { dst, a, b } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*b)?; let res = x ^ y; frame.status.update(res); frame.write(*dst, res)?; } Op::Cpl { dst, a } => { frame.status.clear(); let x = frame.read(*a)?; let res = !x; frame.status.update(res); frame.write(*dst, res)?; } Op::Rol { dst, a, n } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*n)?; if y > u32::MAX as u64 { frame.status.invalid = true; } else { let res = x.rotate_left(y as u32); frame.status.update(res); frame.write(*dst, res)?; } } Op::Ror { dst, a, n } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*n)?; if y > u32::MAX as u64 { frame.status.invalid = true; } else { let res = x.rotate_right(y as u32); frame.status.update(res); frame.write(*dst, res)?; } } Op::Lsl { dst, a, n } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*n)?; let res = x << y; frame.status.update(res); frame.write(*dst, res)?; } Op::Lsr { dst, a, n } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*n)?; let res = x >> y; frame.status.update(res); frame.write(*dst, res)?; } Op::Asr { dst, a, n } => { frame.status.clear(); let x = frame.read(*a)?; let y = frame.read(*n)?; if y > u32::MAX as u64 { frame.status.invalid = true; } else { let res = x.signed_shr(y as u32); frame.status.update(res); frame.write(*dst, res)?; } } Op::StoreStatus { dst } => { let packed = frame.status.store(); frame.write(*dst, packed)?; } Op::LoadStatus { src } => { let x = frame.read(*src)?; frame.status.load(x); } } Ok(EvalRes { cycles, advance, }) } }