Skip to main content

cambridge_asm/exec/
mod.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
6#![allow(clippy::module_name_repetitions)]
7
8use crate::inst::{InstSet, Op};
9use std::{
10    collections::BTreeMap,
11    fmt::{Debug, Display, Formatter, Result as FmtResult},
12    io::{stdin, stdout, BufReader, Read, Write},
13    str::FromStr,
14};
15
16/// # Arithmetic
17/// Arithmetic instructions
18#[allow(clippy::needless_pass_by_value, clippy::enum_glob_use)]
19pub mod arith;
20
21/// # I/O
22/// I/O, debugging, function call and return instructions
23#[allow(clippy::needless_pass_by_value, clippy::enum_glob_use)]
24pub mod io;
25
26/// # Data movement
27/// Instructions for moving data between registers and memory addresses
28#[allow(clippy::needless_pass_by_value, clippy::enum_glob_use)]
29pub mod mov;
30
31/// # Comparison
32/// Instructions for making logical comparisons
33#[allow(clippy::needless_pass_by_value, clippy::enum_glob_use)]
34pub mod cmp;
35
36/// # Bit manipulation
37/// Instructions for logical bit manipulation
38#[allow(clippy::needless_pass_by_value, clippy::enum_glob_use)]
39pub mod bitman;
40
41#[allow(clippy::enum_glob_use)]
42mod error;
43
44mod memory;
45
46mod debug;
47
48#[allow(clippy::enum_glob_use)]
49mod inst;
50
51pub use error::{RtError, RtResult, Source};
52
53pub use memory::Memory;
54
55pub use inst::{ExecFunc, ExecInst};
56
57pub use debug::DebugInfo;
58
59/// For platform independent I/O
60///
61/// Boxed for convenience.
62pub struct Io {
63    pub read: BufReader<Box<dyn Read + Send + Sync>>,
64    pub write: Box<dyn Write + Send + Sync>,
65}
66
67/// Quickly makes an [`Io`] struct
68///
69/// # Arguments (optional)
70///
71/// * `$read`: must implement [`Read`].
72/// * `$write`: must implement [`Write`].
73///
74/// # Example
75/// ```
76/// use cambridge_asm::make_io;
77///
78/// let default_io = make_io!(); // no macro arguments will give the default I/O provider, i.e. stdio
79/// let io = make_io!(std::io::stdin(), std::io::sink()); // you can use your own providers too
80/// ```
81#[macro_export]
82macro_rules! make_io {
83    () => {
84        $crate::exec::Io::default()
85    };
86    ($read:expr, $write:expr) => {{
87        $crate::exec::Io {
88            read: std::io::BufReader::new(Box::new($read)),
89            write: Box::new($write),
90        }
91    }};
92}
93
94impl Debug for Io {
95    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
96        f.write_str("<struct Io>")
97    }
98}
99
100impl Default for Io {
101    fn default() -> Self {
102        Self {
103            read: BufReader::new(Box::new(stdin())),
104            write: Box::new(stdout()),
105        }
106    }
107}
108
109/// Tracks state of the registers and memory during execution
110#[derive(Debug, Default)]
111pub struct Context {
112    pub cmp: bool,
113    pub mar: usize,
114    pub acc: usize,
115    pub ix: usize,
116    pub flow_override_reg: bool,
117    pub mem: Memory,
118    pub ret: usize,
119    pub gprs: [usize; 30],
120    pub end: bool,
121    pub io: Io,
122}
123
124impl Context {
125    pub fn new(mem: Memory) -> Self {
126        Self {
127            mem,
128            ..Self::default()
129        }
130    }
131
132    pub fn with_io(mem: Memory, io: Io) -> Self {
133        Self {
134            mem,
135            io,
136            ..Self::default()
137        }
138    }
139
140    #[inline]
141    pub fn override_flow_control(&mut self) {
142        self.flow_override_reg = true;
143    }
144
145    /// # Panics
146    /// If `op` is not a `usize` register. To avoid this, check `op` using [`Op::is_register`].
147    #[inline]
148    pub fn get_mut_register(&mut self, op: &Op) -> &mut usize {
149        match op {
150            Op::Acc => &mut self.acc,
151            Op::Ix => &mut self.ix,
152            Op::Ar => &mut self.ret,
153            Op::Gpr(x) => &mut self.gprs[*x],
154            _ => unreachable!(),
155        }
156    }
157
158    /// # Panics
159    /// If `op` is not a `usize` register. To avoid this, check `op` using [`Op::is_register`].
160    #[inline]
161    pub fn get_register(&self, op: &Op) -> usize {
162        match op {
163            Op::Acc => self.acc,
164            Op::Ix => self.ix,
165            Op::Ar => self.ret,
166            Op::Gpr(x) => self.gprs[*x],
167            _ => unreachable!(),
168        }
169    }
170
171    /// Read the given operand from the context
172    ///
173    /// # Arguments
174    ///
175    /// * `op`:
176    ///
177    /// returns: `RtResult`
178    ///
179    /// # Panics
180    ///
181    /// If `op` is not usizeable. To avoid this, check `op` using [`Op::is_usizeable`]
182    ///
183    /// # Example
184    ///
185    /// ```no_run
186    /// # use cambridge_asm::inst;
187    /// inst!(print (ctx, op) {
188    ///     if op.is_usizeable() {
189    ///         println!("{}", ctx.read(op)?);
190    ///     }
191    /// });
192    /// ```
193    #[inline]
194    pub fn read(&self, op: &Op) -> RtResult<usize> {
195        match op {
196            &Op::Literal(val) => Ok(val),
197            Op::Addr(addr) => self.mem.get(addr).copied(),
198            Op::Indirect(op) if op.is_usizeable() => {
199                let addr = self.read(op)?;
200                self.mem.get(&addr).copied()
201            }
202            reg if reg.is_register() => Ok(self.get_register(reg)),
203            _ => unreachable!(),
204        }
205    }
206
207    /// # Panics
208    /// If `op` is not an address. To avoid this, check `op` using [`Op::is_address`].
209    pub fn as_address(&self, op: &Op) -> RtResult<usize> {
210        match op {
211            &Op::Addr(addr) => Ok(addr),
212            Op::Indirect(op) if op.is_read_write() => self.read(op),
213            _ => unreachable!(),
214        }
215    }
216
217    /// Modify the given operand in the context if it is writeable
218    ///
219    /// # Arguments
220    ///
221    /// * `op`: operand
222    /// * `f`: closure to modify the value
223    ///
224    /// returns: [`RtResult`]
225    ///
226    /// # Panics
227    ///
228    /// If `op` is not writeable. To avoid this, check `op` using [`Op::is_read_write`].
229    ///
230    /// # Example
231    ///
232    /// ```no_run
233    /// # use cambridge_asm::inst;
234    /// inst!(double_inc (ctx, op) {
235    ///     if op.is_read_write() {
236    ///         ctx.modify(op, |val| *val += 2)?;
237    ///     }
238    /// });
239    /// ```
240    #[inline]
241    pub fn modify(&mut self, op: &Op, f: impl Fn(&mut usize)) -> RtResult {
242        match op {
243            Op::Addr(x) => f(self.mem.get_mut(x)?),
244            Op::Indirect(op) if op.is_usizeable() => {
245                let addr = self.read(op)?;
246                f(self.mem.get_mut(&addr)?);
247            }
248            op if op.is_register() => f(self.get_mut_register(op)),
249            _ => unreachable!(),
250        }
251
252        Ok(())
253    }
254}
255
256impl Display for Context {
257    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
258        f.write_str("Context {\n")?;
259        writeln!(f, "{:>6}: {}", "mar", self.mar)?;
260        writeln!(f, "{:>6}: {}", "acc", self.acc)?;
261        writeln!(f, "{:>6}: {}", "ix", self.ix)?;
262        writeln!(f, "{:>6}: {}", "cmp", self.cmp)?;
263        write!(f, "{:>6}: [", "gprs")?;
264
265        for (idx, val) in self.gprs.iter().enumerate() {
266            if idx == self.gprs.len() - 1 {
267                writeln!(f, "r{idx} = {val}]")?;
268            } else {
269                write!(f, "r{idx} = {val}, ")?;
270            }
271        }
272
273        writeln!(f, "{:>6}: Memory {{", "mem")?;
274
275        for (addr, entry) in &self.mem {
276            writeln!(f, "{addr:>8}: {entry},")?;
277        }
278
279        writeln!(f, "{:>6}}}", "")?;
280
281        f.write_str("}")
282    }
283}
284
285/// Runtime representation of a program
286pub type ExTree = BTreeMap<usize, ExecInst>;
287
288/// Executes a program
289pub struct Executor {
290    pub debug_info: DebugInfo,
291    pub source: Source,
292    pub prog: ExTree,
293    pub ctx: Context,
294    count: u64,
295}
296
297/// Shows execution status
298pub enum Status {
299    /// Program has finished execution
300    Complete,
301    /// Program has not finished execution
302    Continue,
303    /// An error has been encountered during execution
304    Error(RtError),
305}
306
307impl Executor {
308    pub fn new(
309        source: impl Into<Source>,
310        prog: ExTree,
311        ctx: Context,
312        debug_info: DebugInfo,
313    ) -> Self {
314        Self {
315            debug_info,
316            source: source.into(),
317            prog,
318            ctx,
319            count: 0,
320        }
321    }
322
323    /// Advance execution by one instruction
324    ///
325    /// # Example
326    /// ```no_run
327    ///
328    /// ```
329    pub fn step<T>(&mut self) -> Status
330    where
331        T: InstSet,
332        <T as FromStr>::Err: Display,
333    {
334        if self.ctx.mar == self.prog.len() || self.ctx.end {
335            Status::Complete
336        } else {
337            self.count += 1;
338
339            let inst = if let Some(inst) = self.prog.get(&self.ctx.mar) {
340                inst
341            } else {
342                panic!("Unable to fetch instruction. Please report this as a bug with full debug logs attached.")
343            };
344
345            trace!(
346                "Executing instruction {} {}",
347                T::from_id(inst.id).unwrap_or_else(|msg| panic!("{msg}")),
348                inst.op
349            );
350
351            match (inst.func)(&mut self.ctx, &inst.op) {
352                Ok(()) => {
353                    if self.ctx.flow_override_reg {
354                        self.ctx.flow_override_reg = false;
355                    } else {
356                        self.ctx.mar += 1;
357                    }
358
359                    Status::Continue
360                }
361                Err(e) => Status::Error(e),
362            }
363        }
364    }
365
366    pub fn exec<T>(&mut self)
367    where
368        T: InstSet,
369        <T as FromStr>::Err: Display,
370    {
371        #[allow(clippy::needless_continue)]
372        let err = loop {
373            match self.step::<T>() {
374                Status::Complete => break None,
375                Status::Continue => continue,
376                Status::Error(e) => break Some(e),
377            }
378        };
379
380        if let Some(e) = err {
381            self.source
382                .handle_err(&mut self.ctx.io.write, &e, self.ctx.mar)
383                .unwrap();
384        } else {
385            info!("Total instructions executed: {}", self.count);
386        }
387    }
388
389    pub fn display_with_opcodes<T>(&self) -> Result<String, <T as FromStr>::Err>
390    where
391        T: InstSet,
392        <T as FromStr>::Err: Display,
393    {
394        use std::fmt::Write;
395
396        let mut s = String::new();
397
398        s.reserve(self.prog.len() * 15);
399
400        writeln!(s, "Executor {{").unwrap();
401
402        for (addr, ExecInst { id, op, .. }) in &self.prog {
403            writeln!(s, "{addr:>6}: {func} {op}", func = T::from_id(*id)?).unwrap();
404        }
405
406        s.push('}');
407
408        Ok(s)
409    }
410}
411
412impl Display for Executor {
413    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
414        f.write_str("Executor {")?;
415        for (addr, ExecInst { op, .. }) in &self.prog {
416            writeln!(f, "{addr:>6}: {op}")?;
417        }
418        f.write_str("}")
419    }
420}
421
422impl Debug for Executor {
423    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
424        f.debug_struct("Executor")
425            .field("source", &self.source)
426            .field(
427                "prog",
428                &self
429                    .prog
430                    .iter()
431                    .map(|(addr, ExecInst { op, .. })| (addr, op))
432                    .collect::<Vec<_>>(),
433            )
434            .field("ctx", &self.ctx)
435            .field("count", &self.count)
436            .finish_non_exhaustive()
437    }
438}
439
440#[cfg(test)]
441mod tests {
442    use super::*;
443
444    #[test]
445    fn ensure_send_sync() {
446        fn assert_send_sync<T: Send + Sync>() {}
447        assert_send_sync::<Executor>();
448    }
449
450    #[test]
451    fn exec() {
452        let prog =
453            // Division algorithm from examples/division.pasm
454            [
455                (0, ExecInst::new(0, arith::inc, "202".into())),
456                (1, ExecInst::new(0, arith::add, "203,201".into())),
457                (2, ExecInst::new(0, cmp::cmp, "203,204".into())),
458                (3, ExecInst::new(0, cmp::jpn, "0".into())),
459                (4, ExecInst::new(0, mov::ldd, "202".into())),
460                (5, ExecInst::new(0, io::end, "".into())),
461            ].into();
462
463        let mem = [(200, 0), (201, 5), (202, 0), (203, 0), (204, 15)].into();
464
465        let mut exec = Executor::new(
466            "None",
467            prog,
468            Context::new(Memory::new(mem)),
469            DebugInfo::default(),
470        );
471
472        exec.exec::<crate::parse::DefaultSet>();
473
474        assert_eq!(exec.ctx.acc, 3);
475    }
476}