use crate::{MAddr, MData};
use std::io;
use std::io::{Read, Write};
use std::u16::MAX as u16MAX;
pub struct SBrainVM<'a> {
data_tape: [MData; 65536],
data_stack: Vec<MData>,
auxi_r: MData,
exec_tape: [u8; 65536],
data_p: MAddr,
inst_p: MAddr,
input_t: Option<&'a mut dyn Read>,
output_t: Option<&'a mut dyn Write>,
}
impl<'a> SBrainVM<'a> {
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)
}
pub fn load_program(&mut self, program: &[u8]) -> Result<(), String> {
if program.len() > 65536 {
return Err(String::from("Provided program exceeds VM tape length."));
}
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(()),
}
}
fn do_instruction(&mut self) -> io::Result<bool> {
match self.exec_tape[self.inst_p as usize] {
0 => {
self.data_p = self.data_p.wrapping_sub(1);
}
1 => {
self.data_p = self.data_p.wrapping_add(1);
}
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);
}
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_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 => {
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;
}
}
}
}
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;
}
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,
};
}
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;
}
13 => self.auxi_r = !self.auxi_r,
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 {
self.inst_p = self.inst_p.wrapping_add(1);
if self.inst_p as usize == self.exec_tape.len() - 1 {
self.inst_p = 0;
return true;
}
return false;
}
pub fn run(&mut self, cycles: Option<u32>) -> io::Result<(u32, Option<u8>)> {
let mut done_cycles = 0;
loop {
if self.do_instruction()? {
return Ok((done_cycles, Some(self.auxi_r)));
} else {
self.nexti();
}
done_cycles += 1;
if let Some(n) = cycles {
if done_cycles >= n {
return Ok((done_cycles, None));
}
}
}
}
}