cambridge_asm/exec/
io.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 crate::{exec::RtError::*, inst};
7use std::io::{Read, Write};
8
9inst!(
10    /// No-op
11    ///
12    /// Start functions with this if you don't want to compromise readability
13    ///
14    /// # Syntax
15    /// `NOP`
16    pub nop {}
17);
18
19inst!(
20    /// End a program
21    /// Note that this is **NOT A NO-OP**. It will have effects on execution flow in code that uses functions
22    pub end (ctx) {
23        ctx.end = true;
24    }
25);
26
27inst!(
28    /// Output
29    ///
30    /// Convert an ASCII code to a character and print to STDOUT
31    ///
32    /// # Syntax
33    /// 1. `OUT` - output `ACC`
34    /// 2. `OUT [lit | reg | addr]`
35    pub out (ctx, op) {
36        match op {
37            Null => {
38                let x = ctx.acc;
39
40                if x > 255 {
41                    return Err(InvalidUtf8Byte(x));
42                }
43
44                #[allow(clippy::cast_possible_truncation)]
45                let out = x as u8;
46
47                ctx.io.write.write_all(&[out])?;
48            }
49            src if src.is_usizeable() => {
50                let src = ctx.read(src)?;
51
52                if src > 255 {
53                    return Err(InvalidUtf8Byte(src));
54                }
55
56                #[allow(clippy::cast_possible_truncation)]
57                let out = src as u8;
58
59                ctx.io.write.write_all(&[out])?;
60            }
61            _ => return Err(InvalidOperand),
62        }
63    }
64);
65
66inst!(
67    /// Input
68    ///
69    /// Read a single character from input, convert to ASCII code and
70    /// store
71    ///
72    /// # Panics
73    /// If error is encountered when reading input
74    ///
75    /// # Syntax
76    /// 1. `INP` - read to `ACC`
77    /// 2. `INP [reg | addr]`
78    pub inp (ctx, op) {
79        match op {
80            Null => {
81                let mut buf = [0; 1];
82
83                ctx.io.read.read_exact(&mut buf)?;
84
85                ctx.acc = buf[0] as usize;
86            }
87            dest if dest.is_read_write() => {
88                let mut buf = [0; 1];
89
90                ctx.io.read.read_exact(&mut buf)?;
91
92                ctx.modify(dest, |d| *d = buf[0] as usize)?;
93            }
94            _ => return Err(InvalidOperand),
95        }
96    }
97);
98
99// Custom instruction for debug logging
100inst!(
101    /// Print debug representation
102    ///
103    /// # Syntax
104    /// 1. `DBG` - print entire execution context
105    /// 2. `DBG [lit | reg | addr]` - print value
106    /// 3. `DBG [lit | reg | addr], ...` - print value of all ops
107    #[cfg(feature = "extended")]
108    pub dbg (ctx, op) {
109        let out = match op {
110            Null => format!("{ctx:?}"),
111            src if src.is_usizeable() => format!("{}", ctx.read(src)?),
112            MultiOp(ops) if ops.iter().all(inst::Op::is_usizeable) => ops
113                .iter()
114                .filter_map(|op| ctx.read(op).ok())
115                .enumerate()
116                .fold(String::new(), |acc, (idx, op)| {
117                    if idx == ops.len() - 1 {
118                        format!("{acc}{op}")
119                    } else {
120                        format!("{acc}{op}, ")
121                    }
122                }),
123            MultiOp(_) => return Err(InvalidMultiOp),
124            _ => return Err(InvalidOperand),
125        };
126
127        writeln!(ctx.io.write, "{out}")?;
128    }
129);
130
131// Raw input - directly input integers
132inst!(
133    /// Raw input
134    /// Take integer input and store
135    ///
136    /// # Syntax
137    /// 1. `RIN` - store to `ACC`
138    /// 2. `RIN [reg | addr]`
139    #[cfg(feature = "extended")]
140    pub rin (ctx, op) {
141        use std::io::BufRead;
142        use super::RtResult;
143        const LF: u8 = 0xA;
144
145        fn input(inp: &mut impl BufRead) -> RtResult<usize> {
146            let mut buf = Vec::with_capacity(32);
147            inp.read_until(LF, &mut buf)?;
148
149            let str = String::from_utf8_lossy(&buf);
150            let str = str.trim();
151            let res = str.parse()
152                .map_err(|e| format!("Unable to parse {str:?} because {e}"))?;
153
154            Ok(res)
155        }
156
157        match op {
158            Null => ctx.acc = input(&mut ctx.io.read)?,
159            dest if dest.is_read_write() => {
160                let input = input(&mut ctx.io.read)?;
161                ctx.modify(dest, |d| *d = input)?;
162            }
163            _ => return Err(InvalidOperand),
164        }
165    }
166);
167
168inst!(
169    /// Call a function
170    ///
171    /// # Syntax
172    /// `CALL [addr]`
173    #[cfg(feature = "extended")]
174    pub call (ctx, op) {
175        match op {
176            &Addr(addr) => {
177                ctx.ret = ctx.mar + 1;
178                ctx.override_flow_control();
179                ctx.mar = addr;
180            }
181            _ => return Err(InvalidOperand),
182        }
183    }
184);
185
186inst!(
187    /// Return to address in `Ar`
188    ///
189    /// # Syntax
190    /// `RET`
191    #[cfg(feature = "extended")]
192    pub ret (ctx) {
193        ctx.override_flow_control();
194        ctx.mar = ctx.ret;
195    }
196);