tis_100/node/
exec.rs

1use super::Node;
2use core::{Program, Port, Instruction, Source, Register};
3use core::Port::*;
4use core::Instruction::*;
5use core::Source::*;
6use core::Register::*;
7use core::IoRegister::*;
8use io::IoBusView;
9
10/// A corrupted TIS-100 node. `step` and `sync` have no effect.
11#[derive(Debug)]
12pub struct DamagedExecutionNode;
13
14impl Node for DamagedExecutionNode {}
15
16/// An execution mode of a `BasicExecutionNode`.
17#[derive(Debug, PartialEq, Eq)]
18pub enum Mode {
19    Idle,
20    Run,
21    Read,
22    Wrte,
23}
24
25use self::Mode::*;
26
27/// Executes TIS-100 assembly code. Once a `Program` has been set on the node, the node may be
28/// executed using alternating calls of `step` and `sync`.
29///
30/// # Example
31///
32/// ```
33/// use tis_100::core::Port::*;
34/// use tis_100::io::IoBus;
35/// use tis_100::node::{Node, BasicExecutionNode};
36/// use tis_100::parse::parse_program;
37///
38/// let src = "MOV UP ACC\nADD 1\nMOV ACC DOWN\n";
39/// let prog = parse_program(src).unwrap();
40/// let mut bus = IoBus::new();
41/// let mut node = BasicExecutionNode::with_program(prog);
42///
43/// bus.connect_half(0, 1, DOWN)
44///     .connect_half(1, 2, DOWN)
45///     .view(0).write(DOWN, 1);
46/// bus.commit();
47///
48/// for _ in 0..3 {
49///     {
50///         let mut view = bus.view(1);
51///         node.step(&mut view);
52///         node.sync(&mut view);
53///     }
54///
55///     bus.commit();
56/// }
57///
58/// assert_eq!(bus.view(2).read(UP), Some(2));
59/// ```
60#[derive(Debug)]
61pub struct BasicExecutionNode {
62    program: Program,
63    pc: isize,
64    mode: Mode,
65    acc: isize,
66    bak: isize,
67    last: Option<Port>,
68}
69
70impl BasicExecutionNode {
71    /// Construct a new, empty `BasicExecutionNode`.
72    pub fn new() -> BasicExecutionNode {
73        BasicExecutionNode {
74            program: Program::new(),
75            pc: 0,
76            mode: Idle,
77            acc: 0,
78            bak: 0,
79            last: None,
80        }
81    }
82
83    /// Construct a new `BasicExecutionNode` and initialize it with the given program.
84    pub fn with_program(program: Program) -> BasicExecutionNode {
85        let mut node = BasicExecutionNode::new();
86        node.set_program(program);
87        node
88    }
89
90    /// Set the program on a `BasicExecutionNode`.
91    pub fn set_program(&mut self, program: Program) {
92        self.program = program;
93    }
94
95    pub fn get_mode(&self) -> &Mode {
96        &self.mode
97    }
98
99    /// Increment the program counter.
100    fn inc_pc(&mut self) {
101        self.pc += 1;
102        if self.pc >= self.program.len() as isize {
103            self.pc = 0;
104        }
105    }
106
107    /// Set the value of the program counter.
108    fn set_pc(&mut self, pc: isize) {
109        if pc < 0 {
110            self.pc = 0;
111        } else if pc as usize > self.program.len() {
112            self.pc = self.program.len() as isize - 1;
113        } else {
114            self.pc = pc;
115        }
116    }
117
118    /// Fetch the instruction at the current program counter.
119    fn fetch(&mut self) -> Option<Instruction> {
120        self.program.get(self.pc as usize).map(|&i| i)
121    }
122
123    /// Evaluate the given instruction.
124    fn eval(&mut self, instruction: Instruction, io: &mut IoBusView) {
125        match instruction {
126            Nop => (),
127            Mov(src, dst) => if let Some(val) = self.read(io, src) {
128                let value = clamp_value(val);
129                self.write(io, dst, value);
130            },
131            Swp => {
132                let tmp = self.bak;
133                self.bak = self.acc;
134                self.acc = tmp;
135            },
136            Sav => self.bak = self.acc,
137            Add(src) => if let Some(val) = self.read(io, src) {
138                self.acc += val;
139            },
140            Sub(src) => if let Some(val) = self.read(io, src) {
141                self.acc -= val;
142            },
143            Neg => self.acc = -self.acc,
144            Jmp(pc) => self.set_pc(pc),
145            Jez(pc) => if self.acc == 0 {
146                self.set_pc(pc);
147            },
148            Jnz(pc) => if self.acc != 0 {
149                self.set_pc(pc);
150            },
151            Jgz(pc) => if self.acc > 0 {
152                self.set_pc(pc);
153            },
154            Jlz(pc) => if self.acc < 0 {
155                self.set_pc(pc);
156            },
157            Jro(src) => if let Some(off) = self.read(io, src) {
158                let pc = self.pc + off;
159                self.set_pc(pc);
160            },
161        }
162    }
163
164    /// Read a value from the given register.
165    fn read(&mut self, io: &mut IoBusView, src: Source) -> Option<isize> {
166        let val = match src {
167            VAL(val) => Some(val),
168            REG(ACC) => Some(self.acc),
169            REG(NIL) => Some(0),
170            REG(IO(DIR(port))) => io.read(port),
171            REG(IO(ANY)) => io.read(LEFT)
172                .or_else(|| io.read(RIGHT))
173                .or_else(|| io.read(UP))
174                .or_else(|| io.read(DOWN)),
175            REG(IO(LAST)) => match self.last {
176                Some(port) => io.read(port),
177                None => Some(0),
178            },
179        };
180
181        val.or_else(|| {
182            self.mode = Read;
183            None
184        })
185    }
186
187    /// Write a value to the given register.
188    fn write(&mut self, io: &mut IoBusView, dst: Register, value: isize) {
189        match dst {
190            ACC => self.acc = value,
191            NIL => (),
192            IO(reg) => {
193                match reg {
194                    DIR(port) => io.write(port, value),
195                    ANY => {
196                        io.write(UP, value);
197                        io.write(DOWN, value);
198                        io.write(LEFT, value);
199                        io.write(RIGHT, value);
200                    },
201                    LAST => if let Some(port) = self.last {
202                        io.write(port, value);
203                    }
204                }
205                self.mode = Wrte;
206            }
207        }
208    }
209}
210
211impl Node for BasicExecutionNode {
212    /// Execute the next instruction, if possible.
213    fn step(&mut self, io: &mut IoBusView) {
214        if self.mode != Wrte {
215            if let Some(instruction) = self.fetch() {
216                self.mode = Run;
217                self.eval(instruction, io);
218                if self.mode == Run {
219                    self.inc_pc();
220                }
221            }
222        }
223    }
224
225    /// Synchronize this node with the `IoBus`. If the node was blocked on a write, and that value
226    /// was read during the previous cycle, then this will clear the block and allow the node to
227    /// proceed with execution.
228    fn sync(&mut self, io: &mut IoBusView) {
229        if self.mode == Wrte {
230            if !io.is_blocked() {
231                self.mode = Run;
232                self.inc_pc();
233            }
234        }
235    }
236
237    /// An execution node is stalled if it is waiting on an IO action or if it is not executing.
238    fn is_stalled(&self) -> bool {
239        self.mode != Run
240    }
241}
242
243
244/// Limit a value in a TIS-100 register to the range -999..999 inclusive.
245fn clamp_value(value: isize) -> isize {
246    if value > 999 {
247        999
248    } else if value < -999 {
249        -999
250    } else {
251        value
252    }
253}
254
255#[test]
256fn test_clamp_value() {
257    assert_eq!(clamp_value(1000), 999);
258    assert_eq!(clamp_value(999), 999);
259    assert_eq!(clamp_value(998), 998);
260
261    assert_eq!(clamp_value(-1000), -999);
262    assert_eq!(clamp_value(-999), -999);
263    assert_eq!(clamp_value(-998), -998);
264}