use super::{Context, PasmError::*, PasmResult};
use crate::inst::Op::{self, *};
#[inline]
fn checked_add(dest: &mut usize, val: usize, mar: usize) {
if let Some(res) = dest.checked_add(val) {
*dest = res;
} else {
warn!("Addition overflow detected at line {}", mar + 1);
*dest += val;
}
}
pub fn add(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 line = ctx.mar;
let val = ctx.read(val)?;
ctx.modify(dest, |d| checked_add(d, val, line))?;
}
[ref dest, ref a, ref b]
if dest.is_read_write() && a.is_usizeable() && b.is_usizeable() =>
{
let mut a = ctx.read(a)?;
checked_add(&mut a, ctx.read(b)?, ctx.mar);
ctx.modify(dest, |d| *d = a)?;
}
_ => return Err(InvalidMultiOp),
},
Null => return Err(NoOperand),
val if val.is_usizeable() => {
let val = ctx.read(val)?;
checked_add(&mut ctx.acc, val, ctx.mar);
}
_ => return Err(InvalidOperand),
}
Ok(())
}
#[inline]
fn checked_sub(dest: &mut usize, val: usize, mar: usize) {
if let Some(res) = dest.checked_sub(val) {
*dest = res;
} else {
warn!("Subtraction overflow detected at line {}", mar + 1);
*dest -= val;
}
}
pub fn sub(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 line = ctx.mar;
let val = ctx.read(val)?;
ctx.modify(dest, |d| checked_sub(d, val, line))?;
}
[ref dest, ref a, ref b]
if dest.is_read_write() && a.is_usizeable() && b.is_usizeable() =>
{
let mut a = ctx.read(a)?;
checked_sub(&mut a, ctx.read(b)?, ctx.mar);
ctx.modify(dest, |d| *d = a)?;
}
_ => return Err(InvalidMultiOp),
},
val if val.is_usizeable() => {
let val = ctx.read(val)?;
checked_sub(&mut ctx.acc, val, ctx.mar);
}
Null => return Err(NoOperand),
_ => return Err(InvalidOperand),
}
Ok(())
}
pub fn inc(ctx: &mut Context, op: &Op) -> PasmResult {
match op {
dest if dest.is_read_write() => {
let line = ctx.mar;
ctx.modify(dest, |d| checked_add(d, 1, line))?;
}
Null => return Err(NoOperand),
_ => return Err(InvalidOperand),
}
Ok(())
}
pub fn dec(ctx: &mut Context, op: &Op) -> PasmResult {
match op {
dest if dest.is_read_write() => {
let line = ctx.mar;
ctx.modify(dest, |d| checked_sub(d, 1, line))?;
}
Null => return Err(NoOperand),
_ => return Err(InvalidOperand),
}
Ok(())
}
#[cfg(feature = "extended")]
pub fn zero(ctx: &mut Context, op: &Op) -> PasmResult {
match op {
MultiOp(ops) => {
for op in ops.iter().filter(|op| op.is_read_write()) {
ctx.modify(op, |val| *val = 0)?;
}
}
Null => ctx.acc = 0,
op if op.is_read_write() => ctx.modify(op, |val| *val = 0)?,
_ => return Err(InvalidOperand),
}
Ok(())
}