use std::ops::Rem; use num_traits::PrimInt; use crsn::asm::data::{Rd, Wr}; use crsn::module::{EvalRes, OpTrait}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; use crsn::sexp; use crsn::sexp::Sexp; use crsn::utils::A; use crate::defs::ArithOp; use crsn::asm::instr::cond::Flag; impl OpTrait for ArithOp { fn execute(&self, _ti: &ThreadInfo, state: &mut RunState) -> Result { let eres = EvalRes::default(); match self { ArithOp::Test { a } => { state.clear_status(); let res = state.read(a)?; state.update_status(res); } ArithOp::Compare { a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; state.set_flag(Flag::Equal, x == y); state.set_flag(Flag::Lower, x < y); state.set_flag(Flag::Greater, x > y); // Test flags are set when both arguments have the property if x == y { state.update_status(x); } } ArithOp::RangeTest { val, a, b } => { state.clear_status(); let val = state.read(val)?; let a = state.read(a)?; let b = state.read(b)?; state.set_flag(Flag::Equal, val >= a && val <= b); state.set_flag(Flag::Lower, val < a); state.set_flag(Flag::Greater, val > b); state.update_status(val); } ArithOp::Add { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let (res, ov) = if let Some(v) = x.checked_add(y) { (v, false) } else { (x.wrapping_add(y), true) }; state.update_status(res); state.set_flag(Flag::Overflow, ov); state.write(dst, res)?; } ArithOp::Sub { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let (res, ov) = if let Some(v) = x.checked_sub(y) { (v, false) } else { (x.wrapping_sub(y), true) }; state.update_status(res); state.set_flag(Flag::Overflow, ov); state.write(dst, res)?; } ArithOp::Mul { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let res = if let Some(v) = x.checked_mul(y) { v } else { state.set_flag(Flag::Overflow, true); x.wrapping_mul(y) }; state.update_status(res); state.write(dst, res)?; } ArithOp::Div { dst, rem, a, div } => { state.clear_status(); let x = state.read(a)?; let d = state.read(div)?; if d == 0 { state.set_flag(Flag::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) }; state.update_status(res); state.set_flag(Flag::Overflow, ov); state.write(dst, res)?; state.write(rem, remainder)?; } } ArithOp::Mod { dst, a, div } => { state.clear_status(); let x = state.read(a)?; let d = state.read(div)?; if d == 0 { state.set_flag(Flag::Invalid, true); } else { let (remainder, ov) = if let Some(v) = x.checked_rem(d) { (v, false) } else { (x.wrapping_rem(d), true) }; state.update_status(remainder); state.set_flag(Flag::Overflow, ov); state.write(dst, remainder)?; } } ArithOp::And { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let res = x & y; state.update_status(res); state.write(dst, res)?; } ArithOp::Or { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let res = x | y; state.update_status(res); state.write(dst, res)?; } ArithOp::Xor { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let res = x ^ y; state.update_status(res); state.write(dst, res)?; } ArithOp::Cpl { dst, a } => { state.clear_status(); let x = state.read(a)?; let res = !x; state.update_status(res); state.write(dst, res)?; } ArithOp::Rol { dst, a, n } => { state.clear_status(); let x = state.read(a)?; let y = state.read(n)?; if y > u32::MAX as u64 { state.set_flag(Flag::Invalid, true); } else { let res = x.rotate_left(y as u32); state.update_status(res); state.write(dst, res)?; } } ArithOp::Ror { dst, a, n } => { state.clear_status(); let x = state.read(a)?; let y = state.read(n)?; if y > u32::MAX as u64 { state.set_flag(Flag::Invalid, true); } else { let res = x.rotate_right(y as u32); state.update_status(res); state.write(dst, res)?; } } ArithOp::Lsl { dst, a, n } => { state.clear_status(); let x = state.read(a)?; let y = state.read(n)?; let res = x << y; state.update_status(res); state.write(dst, res)?; } ArithOp::Lsr { dst, a, n } => { state.clear_status(); let x = state.read(a)?; let y = state.read(n)?; let res = x >> y; state.update_status(res); state.write(dst, res)?; } ArithOp::Asr { dst, a, n } => { state.clear_status(); let x = state.read(a)?; let y = state.read(n)?; if y > u32::MAX as u64 { state.set_flag(Flag::Invalid, true); } else { let res = x.signed_shr(y as u32); state.update_status(res); state.write(dst, res)?; } } } Ok(eres) } fn to_sexp(&self) -> Sexp { match self { ArithOp::Add { dst, a, b } => to_sexp_2_or_3("add", dst, a, b), ArithOp::Test { a } => sexp::list(&[A("test"), A(a)]), ArithOp::Compare { a, b } => sexp::list(&[A("cmp"), A(a), A(b)]), ArithOp::RangeTest { val, a: start, b: end } => sexp::list(&[A("rcmp"), A(val), A(start), A(end)]), ArithOp::Sub { dst, a, b } => to_sexp_2_or_3("sub", dst, a, b), ArithOp::Mul { dst, a, b } => to_sexp_2_or_3("mul", dst, a, b), ArithOp::Div { dst, rem, a, div } => { if rem.is_discard() { if &dst.as_rd() == a { sexp::list(&[A("div"), A(dst), A(div)]) } else { sexp::list(&[A("div"), A(dst), A(a), A(div)]) } } else { if &dst.as_rd() == a { sexp::list(&[A("divr"), A(dst), A(rem), A(div)]) } else { sexp::list(&[A("divr"), A(dst), A(rem), A(a), A(div)]) } } } ArithOp::Mod { dst, a, div } => to_sexp_2_or_3("mod", dst, a, div), ArithOp::And { dst, a, b } => to_sexp_2_or_3("and", dst, a, b), ArithOp::Or { dst, a, b } => to_sexp_2_or_3("or", dst, a, b), ArithOp::Xor { dst, a, b } => to_sexp_2_or_3("xor", dst, a, b), ArithOp::Cpl { dst, a } => { if &dst.as_rd() == a { sexp::list(&[A("cpl"), A(dst)]) } else { sexp::list(&[A("cpl"), A(dst), A(a)]) } } ArithOp::Rol { dst, a, n } => to_sexp_2_or_3("rol", dst, a, n), ArithOp::Ror { dst, a, n } => to_sexp_2_or_3("ror", dst, a, n), ArithOp::Lsl { dst, a, n } => to_sexp_2_or_3("lsl", dst, a, n), ArithOp::Lsr { dst, a, n } => to_sexp_2_or_3("lsr", dst, a, n), ArithOp::Asr { dst, a, n } => to_sexp_2_or_3("asr", dst, a, n), } } } fn to_sexp_2_or_3(name: &str, dst: &Wr, a: &Rd, b: &Rd) -> Sexp { if &dst.as_rd() == a { sexp::list(&[A(name), A(dst), A(b)]) } else { sexp::list(&[A(name), A(dst), A(a), A(b)]) } }