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; use rand::Rng; 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::Rng { dst, min, max } => { state.clear_status(); let min = state.read(min)?; let mut max = state.read(max)?; let val = if min == max { min } else if min > max { state.set_flag(Flag::Invalid, true); min } else { let offset = min; max -= min; offset + if max == u64::MAX { rand::thread_rng().gen() } else { rand::thread_rng().gen_range(0, max) } }; state.write(dst, val)?; 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)?; } } ArithOp::Sw32 { dst, src } => { let val = state.read(src)?; let res = ((val & 0xFFFF_FFFF) << 32) | ((val & !0xFFFF_FFFF) >> 32); state.write(dst, res)?; } ArithOp::Sw16 { dst, src } => { let val = state.read(src)?; let res = ((val & 0x0000_FFFF_0000_FFFF) << 16) | ((val & 0xFFFF_0000_FFFF_0000) >> 16); state.write(dst, res)?; } ArithOp::Sw8 { dst, src } => { let val = state.read(src)?; let res = ((val & 0x00FF_00FF_00FF_00FF) << 8) | ((val & 0xFF00_FF00_FF00_FF00) >> 8); state.write(dst, res)?; } ArithOp::Rev { dst, src } => { let val = state.read(src)?; let res = val.swap_bytes(); state.write(dst, res)?; } ArithOp::Rbit { dst, src } => { let val = state.read(src)?; let res = val.reverse_bits(); state.write(dst, res)?; } ArithOp::Clz { dst, src, slice } => { state.clear_status(); let mut val = ((state.read(src)? >> slice.src_pos) << (64 - slice.width)); if !slice.is_full() { val |= ((1 << slice.width) - 1); } let res = val.leading_zeros() as u64; state.update_status(res); state.write(dst, res)?; } ArithOp::Clo { dst, src, slice } => { state.clear_status(); let mut val = ((state.read(src)? >> slice.src_pos) << (64 - slice.width)); if !slice.is_full() { val &= ((1 << slice.width) - 1) << slice.src_pos; } let res = val.leading_ones() as u64; state.update_status(res); state.write(dst, res)?; } ArithOp::SignExtend { dst, src, slice } => { state.clear_status(); let val = state.read(src)?; let res = if 0 != (val & (1 << (slice.width - 1))) { val | (((1 << (64 - slice.width)) - 1) << slice.width) } else { val & ((1 << slice.width) - 1) }; 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::Rng { dst, min, max } => { if min.is_immediate_equal(0) && max.is_immediate_equal(u64::MAX) { sexp::list(&[A("rng"), A(dst)]) } else if min.is_immediate_equal(0) { sexp::list(&[A("rng"), A(dst), A(max)]) } else { sexp::list(&[A("rng"), A(dst), A(min), A(max)]) } } 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() { to_sexp_2_or_3("div", dst, 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 } => to_sexp_1_or_2("cpl", dst, 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), ArithOp::Sw32 { dst, src } => to_sexp_1_or_2("sw32", dst, src), ArithOp::Sw16 { dst, src } => to_sexp_1_or_2("sw16", dst, src), ArithOp::Sw8 { dst, src } => to_sexp_1_or_2("sw8", dst, src), ArithOp::Rev { dst, src } => to_sexp_1_or_2("rev", dst, src), ArithOp::Rbit { dst, src } => to_sexp_1_or_2("rbit", dst, src), ArithOp::Clz { dst, src, slice } => to_sexp_1_or_2(&format!("clz{}", slice), dst, src), ArithOp::Clo { dst, src, slice } => to_sexp_1_or_2(&format!("clo{}", slice), dst, src), ArithOp::SignExtend { dst, src, slice } => to_sexp_1_or_2(&format!("se{}", slice), dst, src), } } } 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)]) } } fn to_sexp_1_or_2(name: &str, dst: &Wr, src: &Rd) -> Sexp { if &dst.as_rd() == src { sexp::list(&[A(name), A(dst)]) } else { sexp::list(&[A(name), A(dst), A(src)]) } }