use crate::data::*; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum HaltResult { Halt, WaitRead, } #[derive(Debug, Clone)] pub struct Program { program: Vec, pc: usize, rel_base: isize, } impl Program { pub fn new(program: Vec) -> Self { Program { program, pc: 0, rel_base: 0, } } fn ensure_addr(&mut self, addr: usize) { if self.program.len() <= addr { let new_size = ((addr as f64) * 1.5) as usize; self.program.resize_with(new_size, Default::default); } } fn get(&mut self, param: Param) -> isize { match param { Param::Pos(p) => { self.ensure_addr(p); self.program[p] }, Param::Imm(v) => v, Param::Rel(r) => { let addr = self.rel_base + r; assert!(addr >= 0, "invalid relative address"); let addr = addr as usize; self.ensure_addr(addr); self.program[addr] } } } fn get_addr(&mut self, param: Param) -> usize { match param { Param::Pos(p) => p, Param::Imm(_) => panic!("illegal addressing mode for target address"), Param::Rel(r) => { let addr = self.rel_base + r; assert!(addr >= 0, "invalid relative address"); addr as usize } } } fn set(&mut self, addr: usize, what: isize) { self.ensure_addr(addr); self.program[addr] = what; } pub fn run(&mut self, value_queue: &mut dyn IntQueue) -> HaltResult { loop { let op = Op::from(&self.program[self.pc..]); let mut next_pc = self.pc + op.len(); match op { Op::Add { in1, in2, out } => { let v1 = self.get(in1); let v2 = self.get(in2); let out = self.get_addr(out); self.set(out, v1 + v2); } Op::Mul { in1, in2, out } => { let v1 = self.get(in1); let v2 = self.get(in2); let out = self.get_addr(out); self.set(out, v1 * v2); } Op::Read(pos) => { if let Some(value) = value_queue.next() { let addr = self.get_addr(pos); self.set(addr, value); } else { // PC is not incremented, so this should do the read again break HaltResult::WaitRead; } } Op::Write(pos) => { let value = self.get(pos); value_queue.feed(value); } Op::JumpTrue { test, jump } => { let value = self.get(test); if value != 0 { let addr = self.get(jump); assert!(addr >= 0, "invalid jump address: {}", addr); next_pc = addr as usize; } } Op::JumpFalse { test, jump } => { let value = self.get(test); if value == 0 { let addr = self.get(jump); assert!(addr >= 0, "invalid jump address: {}", addr); next_pc = addr as usize; } } Op::LessThan { in1, in2, out } => { let v1 = self.get(in1); let v2 = self.get(in2); let out = self.get_addr(out); self.set(out, (v1 < v2) as isize); } Op::Equals { in1, in2, out } => { let v1 = self.get(in1); let v2 = self.get(in2); let out = self.get_addr(out); self.set(out, (v1 == v2) as isize); } Op::SetRel(rel) => { self.rel_base += self.get(rel); } Op::Halt => { self.pc = next_pc; break HaltResult::Halt; } } self.pc = next_pc; } } } #[derive(Debug, Clone, Copy)] enum Param { Pos(usize), Imm(isize), Rel(isize), } fn param_mode(op: usize, param: u32) -> usize { let mode = op / 100; (mode / (10usize.pow(param))) % 10 } #[test] fn test_param_mode() { assert_eq!(param_mode(1100, 0), 1); assert_eq!(param_mode(2100, 1), 2); assert_eq!(param_mode(2200, 0), 2); assert_eq!(param_mode(1000, 0), 0); assert_eq!(param_mode(1100, 0), 1); } fn make_param(op: usize, param: u32, value: isize) -> Param { let mode = param_mode(op, param); match mode { 0 => { assert!( value >= 0, "invalid position parameter for mode 0 op={} param={} value={}", op, param, value ); Param::Pos(value as usize) } 1 => Param::Imm(value), 2 => Param::Rel(value), _ => panic!("invalid mode {} for op={} param={}", mode, op, param), } } #[derive(Debug, Clone, Copy)] enum Op { Add { in1: Param, in2: Param, out: Param }, Mul { in1: Param, in2: Param, out: Param }, Read(Param), Write(Param), JumpTrue { test: Param, jump: Param }, JumpFalse { test: Param, jump: Param }, LessThan { in1: Param, in2: Param, out: Param }, Equals { in1: Param, in2: Param, out: Param }, SetRel(Param), Halt, } impl Op { fn len(&self) -> usize { match self { Op::Add { .. } | Op::Mul { .. } | Op::LessThan { .. } | Op::Equals { .. } => 4, Op::JumpTrue { .. } | Op::JumpFalse { .. } => 3, Op::Read(_) | Op::Write(_) | Op::SetRel(_) => 2, Op::Halt => 1, } } } impl From<&'_ [isize]> for Op { fn from(other: &'_ [isize]) -> Self { let op = other[0] as usize; match op % 100 { 1 => { Op::Add { in1: make_param(op, 0, other[1]), in2: make_param(op, 1, other[2]), out: make_param(op, 2, other[3]), } } 2 => { Op::Mul { in1: make_param(op, 0, other[1]), in2: make_param(op, 1, other[2]), out: make_param(op, 2, other[3]), } } 3 => Op::Read(make_param(op, 0, other[1])), 4 => Op::Write(make_param(op, 0, other[1])), 5 => Op::JumpTrue { test: make_param(op, 0, other[1]), jump: make_param(op, 1, other[2]), }, 6 => Op::JumpFalse { test: make_param(op, 0, other[1]), jump: make_param(op, 1, other[2]), }, 7 => { Op::LessThan { in1: make_param(op, 0, other[1]), in2: make_param(op, 1, other[2]), out: make_param(op, 2, other[3]), } } 8 => { Op::Equals { in1: make_param(op, 0, other[1]), in2: make_param(op, 1, other[2]), out: make_param(op, 2, other[3]), } } 9 => Op::SetRel(make_param(op, 0, other[1])), 99 => Op::Halt, op => panic!("invalid op: {}", op), } } }