1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
//! The implementation of the SBrain VM.
use crate::{MAddr, MData};
use std::io;
use std::io::{Read, Write};
use std::u16::MAX as u16MAX;

/// A virtual machine modelling the SBrain Turing machine.
/// This machine implements the specification relatively strictly, providing exactly 2^16 (65536)
/// data and instruction cells. Thus, all pointers are 16 bits and all data is 8 bits.
/// The main deviation from the minimum specification is the jump stack, which is indefinitely
/// expandable.

pub struct SBrainVM<'a> {
    // Data containers
    /// The data tape contains the primary data on which the program will operate
    /// 16-bit addresses with a single dead address
    data_tape: [MData; 65536],
    /// The data stack allows the position-independent storage of data
    data_stack: Vec<MData>,
    /// Auxiliary register (auxi_r)
    auxi_r: MData,

    // Machine Internals
    /// The instruction tape contains instructions. This VM uses the recommended 6-bit binary
    /// format, but Rust does not have a 6-bit datatype, so u8 is used instead
    exec_tape: [u8; 65536],
    /// Pointer to the current data cell
    data_p: MAddr,
    /// Pointer to the current instruction
    inst_p: MAddr,

    // I/O Tapes
    input_t: Option<&'a mut dyn Read>,
    output_t: Option<&'a mut dyn Write>,
}

impl<'a> SBrainVM<'a> {
    /// Return a new SBrainVM, with no data in any tapes.
    /// If given a `None` `input`, all reads read 0.
    /// If given a `None` `output`, all writes are discarded.
    pub fn new(
        input: Option<&'a mut dyn Read>,
        output: Option<&'a mut dyn Write>,
        program: &[u8],
    ) -> Result<SBrainVM<'a>, String> {
        let mut new = SBrainVM {
            data_tape: [0; 65536],
            data_stack: vec![0; 256],
            auxi_r: 0,
            exec_tape: [0; 65536],
            data_p: 0,
            inst_p: 0,

            input_t: input,
            output_t: output,
        };
        new.load_program(program)?;
        Ok(new)
    }

    /// Load a program tape: copy data from the given slice into the executable tape,
    /// starting at address zero.
    /// On error, the Err(s) return will contain a message describing the error.
    pub fn load_program(&mut self, program: &[u8]) -> Result<(), String> {
        // No program can be longer than the tape the VM stores programs on.
        if program.len() > 65536 {
            return Err(String::from("Provided program exceeds VM tape length."));
        }

        // Target is a slice of the VMs executable tape of the same size as the program
        // This is required from clone_from_slice
        self.exec_tape[0..program.len()].clone_from_slice(program);
        return Ok(());
    }

    fn get_input(&mut self) -> io::Result<MData> {
        let mut buf = [0; 1];
        if let Some(ref mut r) = self.input_t {
            r.read(&mut buf)?;
            Ok(buf[0])
        } else {
            Ok(0)
        }
    }

    fn put_output(&mut self, output: MData) -> io::Result<()> {
        match &mut self.output_t {
            &mut Some(ref mut w) => {
                w.write(&[output])?;
                Ok(())
            }
            &mut None => Ok(()),
        }
    }

    /// Execute an instruction on the current virtual machine
    /// Returns true if execution is finished and false if not
    fn do_instruction(&mut self) -> io::Result<bool> {
        match self.exec_tape[self.inst_p as usize] {
            // wrapping_add() and wrapping_sub are used in order to never overflow the bounds
            // of unsigned int types
            //
            // Decr. and incr. for data_p
            0 => {
                self.data_p = self.data_p.wrapping_sub(1);
            }
            1 => {
                self.data_p = self.data_p.wrapping_add(1);
            }
            // Decr. and incr. for *data_p
            2 => {
                self.data_tape[self.data_p as usize] =
                    self.data_tape[self.data_p as usize].wrapping_sub(1);
            }
            3 => {
                self.data_tape[self.data_p as usize] =
                    self.data_tape[self.data_p as usize].wrapping_add(1);
            }
            // Jump instructions
            4 => {
                // If *data_p is 0, skip forward to the corresponding 5
                let this_inst = self.inst_p;
                if self.data_tape[self.data_p as usize] == 0 {
                    let mut nest_level = 1;
                    while nest_level > 0 {
                        self.inst_p = self.inst_p.wrapping_add(1);
                        if self.inst_p == 0 {
                            self.inst_p = this_inst;
                            break;
                        }
                        if self.exec_tape[self.inst_p as usize] == 4 {
                            nest_level += 1;
                        } else if self.exec_tape[self.inst_p as usize] == 5 {
                            nest_level -= 1;
                        }
                    }
                }
            }
            5 => {
                // If *data_p isn't 0, skip backward to the corresponding 4
                let this_inst = self.inst_p;
                if self.data_tape[self.data_p as usize] != 0 {
                    let mut nest_level = 1;
                    while nest_level > 0 {
                        self.inst_p = self.inst_p.wrapping_sub(1);
                        if self.inst_p == u16MAX {
                            self.inst_p = this_inst;
                            break;
                        }
                        if self.exec_tape[self.inst_p as usize] == 5 {
                            nest_level += 1;
                        } else if self.exec_tape[self.inst_p as usize] == 4 {
                            nest_level -= 1;
                        }
                    }
                }
            }
            // I/O commands
            6 => {
                let temp = self.data_tape[self.data_p as usize];
                self.put_output(temp)?;
            }
            7 => {
                let temp = self.get_input()?;
                self.data_tape[self.data_p as usize] = temp;
            }
            // Stack instructions
            8 => {
                self.data_stack.push(self.data_tape[self.data_p as usize]);
            }
            9 => {
                self.data_tape[self.data_p as usize] = match self.data_stack.pop() {
                    Some(n) => n,
                    None => 0,
                };
            }
            // Aux register instructions
            10 => {
                self.auxi_r = self.data_tape[self.data_p as usize];
            }
            11 => {
                self.data_tape[self.data_p as usize] = self.auxi_r;
            }
            12 => {
                self.auxi_r = 0;
            }
            // Bitwise auxi_r instructions
            //  NOT
            13 => self.auxi_r = !self.auxi_r,
            //  AND
            14 => {
                self.auxi_r = self.data_tape[self.data_p as usize] & self.auxi_r;
            }
            15 => {
                return Ok(true);
            }
            _ => {}
        }
        return Ok(false);
    }

    fn nexti(&mut self) -> bool {
        // increment the PC
        self.inst_p = self.inst_p.wrapping_add(1);
        // if it went over, wrap it and inform the caller
        if self.inst_p as usize == self.exec_tape.len() - 1 {
            self.inst_p = 0;
            return true;
        }
        return false;
    }

    /// Run the machine, until completion (cycles = None) or for n cycles (cycles = Some(n)).
    /// Return values are number of cycles run and the return code, or None if the code simply ran
    /// out of cycles.
    pub fn run(&mut self, cycles: Option<u32>) -> io::Result<(u32, Option<u8>)> {
        let mut done_cycles = 0;

        // The main execution loop
        loop {
            // Execute the current instruction.
            if self.do_instruction()? {
                return Ok((done_cycles, Some(self.auxi_r)));
            } else {
                self.nexti();
            }

            // Increment the cycle count
            done_cycles += 1;
            if let Some(n) = cycles {
                if done_cycles >= n {
                    return Ok((done_cycles, None));
                }
            }
        }
    }
}