Created
August 21, 2015 22:31
-
-
Save stefanoc/2c1cbcd3bb1e0bd5c294 to your computer and use it in GitHub Desktop.
tis-100
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![allow(dead_code)] | |
use std::thread; | |
use std::sync::mpsc::{sync_channel, SyncSender, Receiver}; | |
use std::collections::HashMap; | |
struct Port { | |
rx: Receiver<i32>, | |
tx: SyncSender<i32> | |
} | |
#[derive(Clone,Debug,PartialEq,Eq)] | |
enum Reg { | |
ACC, | |
BAK, | |
NIL | |
} | |
#[derive(Clone,Debug,PartialEq,Eq)] | |
enum Prt { | |
LEFT, | |
UP, | |
RIGHT, | |
DOWN | |
} | |
#[derive(Clone,Debug,PartialEq,Eq)] | |
enum Op { | |
Reg(Reg), | |
Prt(Prt), | |
Val(i32) | |
} | |
#[derive(Clone,Debug,PartialEq,Eq)] | |
enum Instr { | |
NOP, | |
MOV(Op, Op), | |
SAV, | |
SWP, | |
ADD(Op), | |
SUB(Op), | |
NEG, | |
JMP(&'static str), | |
JEZ(&'static str), | |
OUT | |
} | |
#[derive(Clone,Debug,PartialEq,Eq)] | |
enum I { | |
Label(&'static str), | |
Instr(Instr) | |
} | |
struct CPU { | |
id: &'static str, | |
program: Vec<I>, | |
labels: HashMap<&'static str, usize>, | |
acc: i32, | |
bak: i32, | |
pc: usize, | |
jmp: bool, | |
left: Option<Port>, | |
up: Option<Port>, | |
right: Option<Port>, | |
down: Option<Port>, | |
} | |
impl CPU { | |
fn new(id: &'static str) -> CPU { | |
CPU { id: id, program: vec![], labels: HashMap::new(), acc: 0, bak: 0, pc: 0, jmp: false, left: None, up: None, right: None, down: None } | |
} | |
fn connect(cpu1: &mut CPU, side1: Prt, cpu2: &mut CPU, side2: Prt) { | |
let (tx1, rx1) = sync_channel::<i32>(0); | |
let (tx2, rx2) = sync_channel::<i32>(0); | |
match side1 { | |
Prt::LEFT => cpu1.left = Some(Port { tx: tx1, rx: rx2 }), | |
Prt::UP => cpu1.up = Some(Port { tx: tx1, rx: rx2 }), | |
Prt::RIGHT => cpu1.right = Some(Port { tx: tx1, rx: rx2 }), | |
Prt::DOWN => cpu1.down = Some(Port { tx: tx1, rx: rx2 }) | |
} | |
match side2 { | |
Prt::LEFT => cpu2.left = Some(Port { tx: tx2, rx: rx1 }), | |
Prt::UP => cpu2.up = Some(Port { tx: tx2, rx: rx1 }), | |
Prt::RIGHT => cpu2.right = Some(Port { tx: tx2, rx: rx1 }), | |
Prt::DOWN => cpu2.down = Some(Port { tx: tx2, rx: rx1 }) | |
} | |
} | |
fn read(&mut self, src: &Op) -> i32 { | |
match src { | |
&Op::Reg(ref reg) => match reg { | |
&Reg::ACC => self.acc, | |
&Reg::BAK => self.bak, | |
&Reg::NIL => 0 | |
}, | |
&Op::Prt(ref port) => self.read_port(&port), | |
&Op::Val(v) => v | |
} | |
} | |
fn write(&mut self, dst: &Op, value: i32) { | |
match dst { | |
&Op::Reg(ref reg) => match reg { | |
&Reg::ACC => self.acc = value, | |
&Reg::NIL => {}, | |
&Reg::BAK => panic!("Can't write to BAK") | |
}, | |
&Op::Prt(ref port) => self.write_port(port, value), | |
&Op::Val(v) => panic!("Invalid destination operand {}", v) | |
} | |
} | |
fn write_port(&mut self, p: &Prt, value: i32) { | |
match *p { | |
Prt::LEFT => self.left.as_ref().expect(&format!("{} > LEFT?", self.id)).tx.send(value).unwrap(), | |
Prt::UP => self.up.as_ref().expect(&format!("{} > UP?", self.id)).tx.send(value).unwrap(), | |
Prt::RIGHT => self.right.as_ref().expect(&format!("{} > RIGHT?", self.id)).tx.send(value).unwrap(), | |
Prt::DOWN => self.down.as_ref().expect(&format!("{} > DOWN?", self.id)).tx.send(value).unwrap() | |
} | |
} | |
fn read_port(&self, p: &Prt) -> i32 { | |
match *p { | |
Prt::LEFT => self.left.as_ref().expect(&format!("{} < LEFT?", self.id)).rx.recv().unwrap(), | |
Prt::UP => self.up.as_ref().expect(&format!("{} < UP?", self.id)).rx.recv().unwrap(), | |
Prt::RIGHT => self.right.as_ref().expect(&format!("{} < RIGHT?", self.id)).rx.recv().unwrap(), | |
Prt::DOWN => self.down.as_ref().expect(&format!("{} < DOWN?", self.id)).rx.recv().unwrap() | |
} | |
} | |
fn instr(&mut self, instr: I) { | |
self.program.push(instr); | |
} | |
fn execute(&mut self, instr: &Instr) { | |
match *instr { | |
Instr::NOP => { println!("NOP") }, | |
Instr::MOV(ref src, ref dst) => { | |
let val = self.read(src); | |
self.write(dst, val); | |
// println!("{} MOV {:?} {:?} [{}]", self.id, val, dst, self.acc); | |
}, | |
Instr::ADD(ref src) => { | |
let value = self.read(src); | |
self.acc += value; | |
// println!("{} ADD {:?} [{}]", self.id, src, self.acc); | |
}, | |
Instr::SAV => { | |
self.bak = self.acc; | |
}, | |
Instr::SWP => { | |
let acc = self.acc; | |
self.acc = self.bak; | |
self.bak = acc; | |
}, | |
Instr::SUB(ref src) => { | |
let value = self.read(src); | |
self.acc -= value; | |
// println!("{} SUB {:?} [{}]", self.id, src, self.acc); | |
}, | |
Instr::NEG => { | |
self.acc = -self.acc; | |
}, | |
Instr::JMP(lab) => { | |
self.pc = self.get_label(lab); | |
self.jmp = true; | |
// println!("{} JMP {}({})", self.id, lab, self.pc); | |
}, | |
Instr::JEZ(lab) => { | |
if self.acc == 0 { | |
self.pc = self.get_label(lab); | |
self.jmp = true; | |
// println!("{} JEZ {}({})", self.id, lab, self.pc); | |
} | |
}, | |
Instr::OUT => { | |
println!("{} ACC={} BAK={}", self.id, self.acc, self.bak); | |
} | |
}; | |
} | |
fn cache_label(&mut self, lab: &'static str, pc: usize) { | |
match self.labels.get(lab) { | |
Some(_) => {}, | |
None => { self.labels.insert(lab, pc); } | |
} | |
} | |
fn get_label(&self, lab: &'static str) -> usize { | |
match self.labels.get(lab) { | |
Some(pc) => { *pc }, | |
None => { panic!("Undefined label: {}", lab); } | |
} | |
} | |
fn run(&mut self) { | |
let prog = self.program.clone(); | |
let len = prog.len(); | |
for (pc, i) in prog.iter().enumerate() { | |
match *i { | |
I::Label(lab) => { self.cache_label(lab, pc); } | |
I::Instr(_) => {} | |
} | |
} | |
loop { | |
let i = &prog[self.pc]; | |
match *i { | |
I::Label(_) => {} | |
I::Instr(ref i) => { self.execute(i); } | |
} | |
if self.jmp { | |
self.jmp = false; | |
} else { | |
self.pc += 1; | |
if self.pc >= len { self.pc = 0 }; | |
} | |
} | |
} | |
} | |
fn mov(src: Op, dst: Op) -> I { I::Instr(Instr::MOV(src, dst)) } | |
fn add(src: Op) -> I { I::Instr(Instr::ADD(src)) } | |
fn sub(src: Op) -> I { I::Instr(Instr::SUB(src)) } | |
fn sav() -> I { I::Instr(Instr::SAV) } | |
fn swp() -> I { I::Instr(Instr::SWP) } | |
fn nop() -> I { I::Instr(Instr::NOP) } | |
fn out() -> I { I::Instr(Instr::OUT) } | |
fn jmp(lab: &'static str) -> I { I::Instr(Instr::JMP(lab)) } | |
fn jez(lab: &'static str) -> I { I::Instr(Instr::JEZ(lab)) } | |
fn acc() -> Op { Op::Reg(Reg::ACC) } | |
fn bak() -> Op { Op::Reg(Reg::BAK) } | |
fn nil() -> Op { Op::Reg(Reg::NIL) } | |
fn left() -> Op { Op::Prt(Prt::LEFT) } | |
fn up() -> Op { Op::Prt(Prt::UP) } | |
fn right() -> Op { Op::Prt(Prt::RIGHT) } | |
fn down() -> Op { Op::Prt(Prt::DOWN) } | |
fn val(v: i32) -> Op { Op::Val(v) } | |
fn lab(l: &'static str) -> I { I::Label(l) } | |
macro_rules! program { | |
( $cpu:ident, $( $instr:expr )* ) => { | |
$( | |
$cpu.instr($instr); | |
)* | |
} | |
} | |
macro_rules! connect { | |
( $( $cpu1:ident ~ $side1:ident => $cpu2:ident ~ $side2:ident )* ) => { | |
$( CPU::connect(&mut $cpu1, Prt::$side1, &mut $cpu2, Prt::$side2); )* | |
} | |
} | |
fn main() { | |
let mut cpu1 = CPU::new("cpu1"); | |
let mut cpu2 = CPU::new("cpu2"); | |
let mut cpu3 = CPU::new("cpu3"); | |
connect! { | |
cpu1~RIGHT => cpu2~LEFT | |
cpu2~DOWN => cpu3~UP | |
} | |
program! { cpu1, | |
mov(val(44), acc()) | |
sav() | |
sub(right()) | |
swp() | |
mov(acc(), right()) | |
swp() | |
out() | |
} | |
program! { cpu2, | |
mov(val(93), acc()) | |
mov(acc(), left()) | |
sub(left()) | |
out() | |
} | |
let t1 = thread::spawn(move || cpu1.run()); | |
let t2 = thread::spawn(move || cpu2.run()); | |
t1.join().unwrap(); | |
t2.join().unwrap(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment