|
|
|
@ -1,3 +1,4 @@
|
|
|
|
|
use std::fmt;
|
|
|
|
|
use std::ops;
|
|
|
|
|
use std::str;
|
|
|
|
|
|
|
|
|
@ -9,6 +10,10 @@ pub struct Assembunny {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Assembunny {
|
|
|
|
|
fn instruction(&self, i: usize) -> Option<Instruction> {
|
|
|
|
|
self.instructions.get(i).cloned()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn value<V: Into<Variable>>(&self, v: V) -> isize {
|
|
|
|
|
let v: Variable = v.into();
|
|
|
|
|
match v {
|
|
|
|
@ -16,6 +21,22 @@ impl Assembunny {
|
|
|
|
|
Variable::Value(i) => i,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn toggle(&mut self, i: usize) {
|
|
|
|
|
let instruction = match self.instruction(i) {
|
|
|
|
|
Some(x) => x,
|
|
|
|
|
None => return,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let replacement = match instruction {
|
|
|
|
|
Instruction::Cpy(a, b) => Instruction::Jnz(a, b),
|
|
|
|
|
Instruction::Inc(x) => Instruction::Dec(x),
|
|
|
|
|
Instruction::Dec(x) => Instruction::Inc(x),
|
|
|
|
|
Instruction::Jnz(a, b) => Instruction::Cpy(a, b),
|
|
|
|
|
Instruction::Tgl(x) => Instruction::Inc(x),
|
|
|
|
|
};
|
|
|
|
|
self.instructions[i] = replacement;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Iterator for Assembunny {
|
|
|
|
@ -23,41 +44,43 @@ impl Iterator for Assembunny {
|
|
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Registers> {
|
|
|
|
|
let pc = self.value(Register::PC) as usize;
|
|
|
|
|
let instruction = match self.instructions.0.get(pc) {
|
|
|
|
|
let instruction = match self.instruction(pc) {
|
|
|
|
|
Some(i) => i,
|
|
|
|
|
None => {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
match *instruction {
|
|
|
|
|
Instruction::Cpy(v, r) => {
|
|
|
|
|
self.registers[Register::PC] += 1;
|
|
|
|
|
match instruction {
|
|
|
|
|
Instruction::Cpy(v, Variable::Register(r)) => {
|
|
|
|
|
let value = self.value(v);
|
|
|
|
|
self.registers[r] = value;
|
|
|
|
|
self.registers[Register::PC] += 1;
|
|
|
|
|
}
|
|
|
|
|
Instruction::Inc(r) => {
|
|
|
|
|
Instruction::Inc(Variable::Register(r)) => {
|
|
|
|
|
self.registers[r] += 1;
|
|
|
|
|
self.registers[Register::PC] += 1;
|
|
|
|
|
}
|
|
|
|
|
Instruction::Dec(r) => {
|
|
|
|
|
Instruction::Dec(Variable::Register(r)) => {
|
|
|
|
|
self.registers[r] -= 1;
|
|
|
|
|
self.registers[Register::PC] += 1;
|
|
|
|
|
}
|
|
|
|
|
Instruction::Jnz(v, i) => {
|
|
|
|
|
let delta = if self.value(v) == 0 { 1 } else { i };
|
|
|
|
|
let pc = self.value(Register::PC) + delta;
|
|
|
|
|
Instruction::Jnz(v, delta) if self.value(v) != 0 => {
|
|
|
|
|
let delta = self.value(delta);
|
|
|
|
|
let pc = self.value(Register::PC) + delta - 1;
|
|
|
|
|
self.registers[Register::PC] = pc;
|
|
|
|
|
}
|
|
|
|
|
Instruction::Tgl(v) => {
|
|
|
|
|
let index = (pc as isize) + self.value(v);
|
|
|
|
|
self.toggle(index as usize);
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(self.registers.clone())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct Registers(Vec<isize>);
|
|
|
|
|
pub struct Instructions(Vec<Instruction>);
|
|
|
|
|
|
|
|
|
|
impl Registers {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
@ -88,6 +111,40 @@ impl ops::IndexMut<Register> for Registers {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Instructions(Vec<Instruction>);
|
|
|
|
|
|
|
|
|
|
impl Instructions {
|
|
|
|
|
fn get(&self, i: usize) -> Option<&Instruction> {
|
|
|
|
|
self.0.get(i)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for Instructions {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(f,
|
|
|
|
|
"{}",
|
|
|
|
|
self.0
|
|
|
|
|
.iter()
|
|
|
|
|
.map(Instruction::to_string)
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.join("\n"))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ops::Index<usize> for Instructions {
|
|
|
|
|
type Output = Instruction;
|
|
|
|
|
|
|
|
|
|
fn index(&self, _index: usize) -> &Instruction {
|
|
|
|
|
self.0.index(_index)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ops::IndexMut<usize> for Instructions {
|
|
|
|
|
fn index_mut(&mut self, _index: usize) -> &mut Instruction {
|
|
|
|
|
self.0.index_mut(_index)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
|
|
|
|
|
pub enum Register {
|
|
|
|
|
PC,
|
|
|
|
@ -98,19 +155,43 @@ pub enum Register {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
|
|
|
enum Instruction {
|
|
|
|
|
Cpy(Variable, Register),
|
|
|
|
|
Inc(Register),
|
|
|
|
|
Dec(Register),
|
|
|
|
|
Jnz(Variable, isize),
|
|
|
|
|
pub enum Instruction {
|
|
|
|
|
Cpy(Variable, Variable),
|
|
|
|
|
Inc(Variable),
|
|
|
|
|
Dec(Variable),
|
|
|
|
|
Jnz(Variable, Variable),
|
|
|
|
|
Tgl(Variable),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for Instruction {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
&Instruction::Cpy(a, b) => write!(f, "cpy {} {}", a, b),
|
|
|
|
|
&Instruction::Inc(a) => write!(f, "cpy {}", a),
|
|
|
|
|
&Instruction::Dec(a) => write!(f, "cpy {}", a),
|
|
|
|
|
&Instruction::Jnz(a, b) => write!(f, "cpy {} {}", a, b),
|
|
|
|
|
&Instruction::Tgl(a) => write!(f, "cpy {}", a),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
|
|
|
enum Variable {
|
|
|
|
|
pub enum Variable {
|
|
|
|
|
Register(Register),
|
|
|
|
|
Value(isize),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for Variable {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
&Variable::Register(r) => {
|
|
|
|
|
write!(f, "{}", format!("{:?}", r).to_lowercase())
|
|
|
|
|
}
|
|
|
|
|
&Variable::Value(v) => write!(f, "{:?}", v),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Register> for Variable {
|
|
|
|
|
fn from(r: Register) -> Self {
|
|
|
|
|
Variable::Register(r)
|
|
|
|
@ -149,22 +230,26 @@ impl str::FromStr for Instruction {
|
|
|
|
|
match tokens.next() {
|
|
|
|
|
Some("cpy") => {
|
|
|
|
|
let v = tokens.read_variable()?;
|
|
|
|
|
let r = tokens.read_register()?;
|
|
|
|
|
let r = tokens.read_variable()?;
|
|
|
|
|
Ok(Instruction::Cpy(v, r))
|
|
|
|
|
}
|
|
|
|
|
Some("inc") => {
|
|
|
|
|
let r = tokens.read_register()?;
|
|
|
|
|
Ok(Instruction::Inc(r))
|
|
|
|
|
Ok(Instruction::Inc(Variable::Register(r)))
|
|
|
|
|
}
|
|
|
|
|
Some("dec") => {
|
|
|
|
|
let r = tokens.read_register()?;
|
|
|
|
|
Ok(Instruction::Dec(r))
|
|
|
|
|
Ok(Instruction::Dec(Variable::Register(r)))
|
|
|
|
|
}
|
|
|
|
|
Some("jnz") => {
|
|
|
|
|
let var = tokens.read_variable()?;
|
|
|
|
|
let val = tokens.read_value()?;
|
|
|
|
|
let val = tokens.read_variable()?;
|
|
|
|
|
Ok(Instruction::Jnz(var, val))
|
|
|
|
|
}
|
|
|
|
|
Some("tgl") => {
|
|
|
|
|
let var = tokens.read_variable()?;
|
|
|
|
|
Ok(Instruction::Tgl(var))
|
|
|
|
|
}
|
|
|
|
|
Some(inst) => Err(format!("invalid instruction '{}'", inst).into()),
|
|
|
|
|
None => Err("no instruction".into()),
|
|
|
|
|
}
|
|
|
|
@ -210,7 +295,6 @@ impl<'a> SplitWhitespaceExt for str::SplitWhitespace<'a> {
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
use super::{Instruction, Variable};
|
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
@ -241,6 +325,52 @@ dec a"
|
|
|
|
|
assert_eq!(registers[Register::PC], 6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_toggle_integration() {
|
|
|
|
|
let instructions: Instructions = "cpy 2 a
|
|
|
|
|
tgl a
|
|
|
|
|
tgl a
|
|
|
|
|
tgl a
|
|
|
|
|
cpy 1 a
|
|
|
|
|
dec a
|
|
|
|
|
dec a"
|
|
|
|
|
.parse()
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let assembunny = Assembunny {
|
|
|
|
|
registers: Registers::new(),
|
|
|
|
|
instructions: instructions,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let registers = assembunny.last().unwrap();
|
|
|
|
|
assert_eq!(registers[Register::A], 3);
|
|
|
|
|
assert_eq!(registers[Register::PC], 7);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_toggle() {
|
|
|
|
|
let instructions: Instructions = "tgl a"
|
|
|
|
|
.parse()
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let mut assembunny = Assembunny {
|
|
|
|
|
registers: Registers::new(),
|
|
|
|
|
instructions: instructions,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
assembunny.toggle(0);
|
|
|
|
|
assert_eq!(assembunny.instruction(0),
|
|
|
|
|
Some(Instruction::Inc(Variable::Register(Register::A))));
|
|
|
|
|
|
|
|
|
|
assembunny.toggle(0);
|
|
|
|
|
assert_eq!(assembunny.instruction(0),
|
|
|
|
|
Some(Instruction::Dec(Variable::Register(Register::A))));
|
|
|
|
|
|
|
|
|
|
assembunny.toggle(0);
|
|
|
|
|
assert_eq!(assembunny.instruction(0),
|
|
|
|
|
Some(Instruction::Inc(Variable::Register(Register::A))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_instructions_from_str() {
|
|
|
|
|
let i: Instructions = "cpy 41 a
|
|
|
|
@ -262,12 +392,16 @@ dec a"
|
|
|
|
|
assert!(Instruction::from_str("inc 5").is_err());
|
|
|
|
|
|
|
|
|
|
assert_eq!(Instruction::from_str("cpy 41 a").unwrap(),
|
|
|
|
|
Instruction::Cpy(Variable::Value(41), Register::A));
|
|
|
|
|
Instruction::Cpy(Variable::Value(41),
|
|
|
|
|
Variable::Register(Register::A)));
|
|
|
|
|
assert_eq!(Instruction::from_str("inc a").unwrap(),
|
|
|
|
|
Instruction::Inc(Register::A));
|
|
|
|
|
Instruction::Inc(Variable::Register(Register::A)));
|
|
|
|
|
assert_eq!(Instruction::from_str("dec b").unwrap(),
|
|
|
|
|
Instruction::Dec(Register::B));
|
|
|
|
|
Instruction::Dec(Variable::Register(Register::B)));
|
|
|
|
|
assert_eq!(Instruction::from_str("jnz c 2").unwrap(),
|
|
|
|
|
Instruction::Jnz(Variable::Register(Register::C), 2));
|
|
|
|
|
Instruction::Jnz(Variable::Register(Register::C),
|
|
|
|
|
Variable::Value(2)));
|
|
|
|
|
assert_eq!(Instruction::from_str("tgl a").unwrap(),
|
|
|
|
|
Instruction::Tgl(Variable::Register(Register::A)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|