cambridge_asm/exec/
arith.rs

1// Copyright (c) 2021 Saadi Save
2// This Source Code Form is subject to the terms of the Mozilla Public
3// License, v. 2.0. If a copy of the MPL was not distributed with this
4// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
6use super::{Context, RtError::*, RtResult};
7use crate::inst::Op::{self, *};
8
9#[inline]
10fn checked_add(dest: &mut usize, val: usize, mar: usize) {
11    if let Some(res) = dest.checked_add(val) {
12        *dest = res;
13    } else {
14        warn!("Addition overflow detected at line {}", mar + 1);
15        *dest += val;
16    }
17}
18
19/// Add values
20///
21/// # Syntax
22/// 1. `ADD [lit | reg | addr]` - add to `ACC`
23/// 2. `ADD [reg | addr],[lit | reg | addr]` - add second value to first
24/// 3. `ADD [reg | addr],[lit | reg | addr],[lit | reg | addr]` - add second and third value, store to first
25pub fn add(ctx: &mut Context, op: &Op) -> RtResult {
26    match op {
27        MultiOp(ops) => match ops[..] {
28            [ref dest, ref val] if dest.is_read_write() && val.is_usizeable() => {
29                let line = ctx.mar;
30                let val = ctx.read(val)?;
31                ctx.modify(dest, |d| checked_add(d, val, line))?;
32            }
33            [ref dest, ref a, ref b]
34                if dest.is_read_write() && a.is_usizeable() && b.is_usizeable() =>
35            {
36                let mut a = ctx.read(a)?;
37                checked_add(&mut a, ctx.read(b)?, ctx.mar);
38                ctx.modify(dest, |d| *d = a)?;
39            }
40            _ => return Err(InvalidMultiOp),
41        },
42        Null => return Err(NoOperand),
43        val if val.is_usizeable() => {
44            let val = ctx.read(val)?;
45            checked_add(&mut ctx.acc, val, ctx.mar);
46        }
47        _ => return Err(InvalidOperand),
48    }
49
50    Ok(())
51}
52
53#[inline]
54fn checked_sub(dest: &mut usize, val: usize, mar: usize) {
55    if let Some(res) = dest.checked_sub(val) {
56        *dest = res;
57    } else {
58        warn!("Subtraction overflow detected at line {}", mar + 1);
59        *dest -= val;
60    }
61}
62
63/// Subtract values
64///
65/// # Syntax
66/// 1. `ADD [lit | reg | addr]` - subtract from `ACC`
67/// 2. `ADD [reg | addr],[lit | reg | addr]` - subtract second value from first
68/// 3. `ADD [reg | addr],[lit | reg | addr],[lit | reg | addr]` - subtract third from second value, store to first
69pub fn sub(ctx: &mut Context, op: &Op) -> RtResult {
70    match op {
71        MultiOp(ops) => match ops[..] {
72            [ref dest, ref val] if dest.is_read_write() && val.is_usizeable() => {
73                let line = ctx.mar;
74                let val = ctx.read(val)?;
75                ctx.modify(dest, |d| checked_sub(d, val, line))?;
76            }
77            [ref dest, ref a, ref b]
78                if dest.is_read_write() && a.is_usizeable() && b.is_usizeable() =>
79            {
80                let mut a = ctx.read(a)?;
81                checked_sub(&mut a, ctx.read(b)?, ctx.mar);
82                ctx.modify(dest, |d| *d = a)?;
83            }
84            _ => return Err(InvalidMultiOp),
85        },
86        val if val.is_usizeable() => {
87            let val = ctx.read(val)?;
88            checked_sub(&mut ctx.acc, val, ctx.mar);
89        }
90        Null => return Err(NoOperand),
91        _ => return Err(InvalidOperand),
92    }
93
94    Ok(())
95}
96
97/// Increment register or memory address
98///
99/// # Syntax
100/// `INC [reg | addr]`
101pub fn inc(ctx: &mut Context, op: &Op) -> RtResult {
102    match op {
103        dest if dest.is_read_write() => {
104            let line = ctx.mar;
105            ctx.modify(dest, |d| checked_add(d, 1, line))?;
106        }
107        Null => return Err(NoOperand),
108        _ => return Err(InvalidOperand),
109    }
110
111    Ok(())
112}
113
114/// Decrement register or memory address
115///
116/// # Syntax
117/// `DEC [reg | addr]`
118pub fn dec(ctx: &mut Context, op: &Op) -> RtResult {
119    match op {
120        dest if dest.is_read_write() => {
121            let line = ctx.mar;
122            ctx.modify(dest, |d| checked_sub(d, 1, line))?;
123        }
124        Null => return Err(NoOperand),
125        _ => return Err(InvalidOperand),
126    }
127
128    Ok(())
129}
130
131/// Zero a register or memory address
132///
133/// # Syntax
134/// `ZERO` - zeroes `ACC`
135/// `ZERO [reg | addr]` - zeroes the given register or memory address
136/// `ZERO [reg | addr], ...` - zeroes all operands
137#[cfg(feature = "extended")]
138pub fn zero(ctx: &mut Context, op: &Op) -> RtResult {
139    match op {
140        MultiOp(ops) => {
141            for op in ops.iter().filter(|op| op.is_read_write()) {
142                ctx.modify(op, |val| *val = 0)?;
143            }
144        }
145        Null => ctx.acc = 0,
146        op if op.is_read_write() => ctx.modify(op, |val| *val = 0)?,
147        _ => return Err(InvalidOperand),
148    }
149
150    Ok(())
151}