use std::io::{Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use types::*;
use instructions::*;
#[derive(PartialEq, Debug, Clone)]
pub enum Outcome {
Halt,
Fault(String),
Continue
}
pub struct Machine<'mach> {
max_words: usize,
registers: [Word; 8],
sp: Word,
bp: Word,
ip: usize,
memory: Vec<Word>,
program: Program,
input: &'mach mut Read,
output: &'mach mut Write
}
impl <'mach> Machine <'mach> {
pub fn new(max_words: usize, input: &'mach mut Read, output: &'mach mut Write) -> Self {
Self {
max_words: max_words,
registers: [0; 8],
sp: (max_words - 1) as u64,
bp: (max_words - 1) as u64,
ip: 0,
memory: Vec::with_capacity(max_words),
program: vec![Instruction::Illegal],
input: input,
output: output,
}
}
pub fn load_program(&mut self, new: Vec<Instruction>) {
self.program = new;
self.ip = 0;
}
pub fn get_memory(&self) -> &[Word] { &self.memory }
pub fn load_memory(&mut self, new: Vec<Word>) { self.memory = new; }
pub fn next_instr(&mut self) -> Outcome {
self.ip += 1;
if self.ip >= self.program.len() {
Outcome::Fault(format!("IP beyond program length. IP = {}, length = {}", self.ip, self.program.len()))
} else {
Outcome::Continue
}
}
pub fn write_addr(&mut self, a: Address, v: Word) -> Outcome {
use self::Address::*;
match a {
Literal(l) => { Outcome::Fault(
format!("Tried to write {} to literal {}.", v, l)) },
RegAbs(r) => { self.write_register(r, v); Outcome::Continue },
MemAbs(l) => { self.write_memory(l, v) },
MemReg(r) => {
let location = self.read_register(r);
self.write_memory(location, v)
}
}
}
pub fn read_addr(&self, a: Address) -> Word {
use self::Address::*;
match a {
Literal(v) => { v },
RegAbs(r) => { self.read_register(r) },
MemAbs(l) => { self.read_memory(l) },
MemReg(r) => { self.read_memory(self.read_register(r)) }
}
}
fn read_register(&self, r: Register) -> Word {
match r {
Register::R0 => {self.registers[0]},
Register::R1 => {self.registers[1]},
Register::R2 => {self.registers[2]},
Register::R3 => {self.registers[3]},
Register::R4 => {self.registers[4]},
Register::R5 => {self.registers[5]},
Register::R6 => {self.registers[6]},
Register::R7 => {self.registers[7]},
Register::SP => {self.sp},
Register::BP => {self.bp},
}
}
fn write_register(&mut self, r: Register, v: Word) {
match r {
Register::R0 => {self.registers[0] = v;},
Register::R1 => {self.registers[1] = v;},
Register::R2 => {self.registers[2] = v;},
Register::R3 => {self.registers[3] = v;},
Register::R4 => {self.registers[4] = v;},
Register::R5 => {self.registers[5] = v;},
Register::R6 => {self.registers[6] = v;},
Register::R7 => {self.registers[7] = v;},
Register::SP => {self.sp = v;},
Register::BP => {self.bp = v;},
}
}
fn write_memory(&mut self, l: Word, v: Word) -> Outcome {
let l = l as usize;
if l > self.max_words { return Outcome::Fault(
format!("Tried to write out of available memory: {}", l)); }
if l > self.memory.len() { self.memory.resize(l+1 as usize, 0); }
self.memory[l] = v;
Outcome::Continue
}
fn read_memory(&self, l: Word) -> Word {
let l = l as usize;
if l > self.max_words { 0 }
else if l > self.memory.len() { 0 }
else { self.memory[l] }
}
fn absolute_jump(&mut self, l: JumpLocation) -> Outcome {
if l < self.program.len() {
self.ip = l;
Outcome::Continue
}
else {
Outcome::Fault(
format!("Attempt to jump to {} would overrun program of length {}.", l, self.program.len()))
}
}
pub fn execute_next(&mut self) -> Outcome {
use Instruction::*;
match self.program[self.ip] {
NoOp => { self.ins_no_op() },
Zero(a) => { self.ins_zero(a) },
Move(a, b) => { self.ins_move(a, b) },
Output(a) => { self.ins_output(a) },
Input(a) => { self.ins_input(a) },
Add(a, b) => { self.ins_generic_scalar(a, b, |va, vb| va.wrapping_add(vb)) },
Sub(a, b) => { self.ins_generic_scalar(a, b, |va, vb| va.wrapping_sub(vb)) },
Jump(a) => { self.ins_jump(a) },
JumpIfZero(a, b) => { self.ins_generic_jump_single(a, b, |v| v == 0) },
JumpNotZero(a, b) => { self.ins_generic_jump_single(a, b, |v| v != 0) },
Push(a) => { self.ins_push(a) },
Pop(a) => { self.ins_pop(a) },
Halt => { self.ins_halt() },
Illegal => { Outcome::Fault("Illegal instruction encountered.".into()) },
}
}
pub fn run(&mut self) -> Outcome {
loop {
match self.execute_next() {
Outcome::Continue => { },
other => { return other; }
}
}
}
pub fn run_for(&mut self, cycles: u64) -> (Outcome, u64) {
let mut instructions_remaining = cycles;
while instructions_remaining > 0 {
match self.execute_next() {
Outcome::Continue => { instructions_remaining -= 1; },
other => { return (other, cycles - instructions_remaining); }
}
}
(Outcome::Continue, cycles - instructions_remaining)
}
fn ins_no_op(&mut self) -> Outcome {
self.next_instr()
}
fn ins_halt(&mut self) -> Outcome { Outcome::Halt }
fn ins_move(&mut self, a: Address, b: Address) -> Outcome {
let v = self.read_addr(a);
match self.write_addr(b, v) {
Outcome::Continue => { self.next_instr() },
o => o
}
}
fn ins_zero(&mut self, a: Address) -> Outcome {
match self.write_addr(a, 0) {
Outcome::Continue => { self.next_instr() },
o => o
}
}
fn ins_output(&mut self, a: Address) -> Outcome {
let v = self.read_addr(a);
match self.output.write_u64::<BigEndian>(v) {
Ok(_) => { self.next_instr() }
Err(e) => { Outcome::Fault(format!("Failed to write on output instruction: {}.", e)) }
}
}
fn ins_input(&mut self, a: Address) -> Outcome {
match self.input.read_u64::<BigEndian>() {
Ok(v) => {
match self.write_addr(a, v) {
Outcome::Continue => { self.next_instr() },
o => o
}
},
Err(e) => {
Outcome::Fault(format!("Failed to read on input instruction: {}.", e))
}
}
}
fn ins_generic_scalar<F: FnOnce(Word, Word) -> Word>(&mut self, a: Address, b: Address, f: F) -> Outcome {
let value_a = self.read_addr(a);
let value_b = self.read_addr(b);
match self.write_addr(a, f(value_a, value_b)) {
Outcome::Continue => { self.next_instr() },
other => other
}
}
fn ins_jump(&mut self, a: Address) -> Outcome {
let addr = self.read_addr(a) as JumpLocation;
self.absolute_jump(addr)
}
fn ins_generic_jump_single<F: FnOnce(Word) -> bool>(&mut self, a: Address, b: Address, f: F) -> Outcome {
let value_a = self.read_addr(a) as JumpLocation;
let value_b = self.read_addr(b);
if f(value_b) {
self.absolute_jump(value_a)
} else {
self.next_instr()
}
}
fn ins_push(&mut self, a: Address) -> Outcome {
let val = self.read_addr(a);
self.sp -= 1;
if self.sp <= 0 { Outcome::Fault("Stack has overrun available memory!".into()) }
else {
let location = self.sp;
self.write_memory(location, val);
self.next_instr()
}
}
fn ins_pop(&mut self, a: Address) -> Outcome {
let val = if self.sp >= self.bp {
self.sp = self.bp;
0
} else {
self.read_memory(self.sp)
};
self.sp += 1;
match self.write_addr(a, val) {
Outcome::Continue => { self.next_instr() },
other => other
}
}
}