Skip to content

Instantly share code, notes, and snippets.

@stefanoc
Created August 21, 2015 22:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stefanoc/2c1cbcd3bb1e0bd5c294 to your computer and use it in GitHub Desktop.
Save stefanoc/2c1cbcd3bb1e0bd5c294 to your computer and use it in GitHub Desktop.
tis-100
#![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