use super::{Context, PasmError::*, PasmResult};
use crate::inst::Op::{self, *};
pub fn and(ctx: &mut Context, op: &Op) -> PasmResult {
match op {
MultiOp(ops) => match ops[..] {
[ref dest, ref val] if dest.is_read_write() && val.is_usizeable() => {
let val = val.get_val(ctx)?;
ctx.modify(dest, |d| *d &= val)?;
}
[ref dest, ref a, ref b]
if dest.is_read_write() && a.is_usizeable() && b.is_usizeable() =>
{
let val = a.get_val(ctx)? & b.get_val(ctx)?;
ctx.modify(dest, |d| *d = val)?;
}
_ => return Err(InvalidMultiOp),
},
val if val.is_usizeable() => ctx.acc &= val.get_val(ctx)?,
Null => return Err(NoOperand),
_ => return Err(InvalidOperand),
}
Ok(())
}
pub fn or(ctx: &mut Context, op: &Op) -> PasmResult {
match op {
MultiOp(ops) => match ops[..] {
[ref dest, ref val] if dest.is_read_write() && val.is_usizeable() => {
let val = val.get_val(ctx)?;
ctx.modify(dest, |d| *d |= val)?;
}
[ref dest, ref a, ref b]
if dest.is_read_write() && a.is_usizeable() && b.is_usizeable() =>
{
let val = a.get_val(ctx)? | b.get_val(ctx)?;
ctx.modify(dest, |d| *d = val)?;
}
_ => return Err(InvalidMultiOp),
},
val if val.is_usizeable() => ctx.acc |= val.get_val(ctx)?,
Null => return Err(NoOperand),
_ => return Err(InvalidOperand),
}
Ok(())
}
pub fn xor(ctx: &mut Context, op: &Op) -> PasmResult {
match op {
MultiOp(ops) => match ops[..] {
[ref dest, ref val] if dest.is_read_write() && val.is_usizeable() => {
let val = val.get_val(ctx)?;
ctx.modify(dest, |d| *d ^= val)?;
}
[ref dest, ref a, ref b]
if dest.is_read_write() && a.is_usizeable() && b.is_usizeable() =>
{
let val = a.get_val(ctx)? ^ b.get_val(ctx)?;
ctx.modify(dest, |d| *d = val)?;
}
_ => return Err(InvalidMultiOp),
},
val if val.is_usizeable() => ctx.acc ^= val.get_val(ctx)?,
Null => return Err(NoOperand),
_ => return Err(InvalidOperand),
}
Ok(())
}
pub fn lsl(ctx: &mut Context, op: &Op) -> PasmResult {
#[allow(clippy::cast_possible_truncation)]
fn checked_shl(dest: &mut usize, val: usize, mar: usize) {
if let Some(res) = dest.checked_shl(val as u32) {
*dest = res;
} else {
warn!("Shift left overflow detected at line {}", mar + 1);
*dest <<= val;
}
}
match op {
MultiOp(ops) => {
let line = ctx.mar;
match ops[..] {
[ref dest, ref val] if dest.is_read_write() && val.is_usizeable() => {
let val = val.get_val(ctx)?;
ctx.modify(dest, |d| checked_shl(d, val, line))
}
[ref dest, ref a, ref b]
if dest.is_read_write() && a.is_usizeable() && b.is_usizeable() =>
{
let mut a = a.get_val(ctx)?;
checked_shl(&mut a, b.get_val(ctx)?, line);
ctx.modify(dest, |d| *d = a)
}
_ => Err(InvalidMultiOp),
}
}
val if val.is_usizeable() => {
let x = val.get_val(ctx)?;
checked_shl(&mut ctx.acc, x, ctx.mar);
Ok(())
}
Null => Err(NoOperand),
_ => Err(InvalidOperand),
}
}
pub fn lsr(ctx: &mut Context, op: &Op) -> PasmResult {
match op {
MultiOp(ops) => match ops[..] {
[ref dest, ref val] if dest.is_read_write() && val.is_usizeable() => {
let val = val.get_val(ctx)?;
ctx.modify(dest, |d| *d >>= val)
}
[ref dest, ref a, ref b]
if dest.is_read_write() && a.is_usizeable() && b.is_usizeable() =>
{
let val = a.get_val(ctx)? >> b.get_val(ctx)?;
ctx.modify(dest, |d| *d = val)
}
_ => Err(InvalidMultiOp),
},
val if val.is_usizeable() => {
ctx.acc >>= val.get_val(ctx)?;
Ok(())
}
Null => Err(NoOperand),
_ => Err(InvalidOperand),
}
}