//! Shared CPU module //! //! Day 16 and day 19 use the same CPU, so that code is shared (and documented) here. /// Representation an OpCode for the AoC 2018 CPU. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub enum OpCode { ADDR, ADDI, MULR, MULI, BANR, BANI, BORR, BORI, SETR, SETI, GTIR, GTRI, GTRR, EQIR, EQRI, EQRR, } impl OpCode { /// Check if the given before/after state is valid for a particular OpCode. /// /// For this, a possible op is represented as an array of integers, where the first /// integer (the possible op-code) is ignored. /// /// # Examples /// /// ``` /// use aoc_2018::cpu::OpCode; /// /// assert_eq!(true, OpCode::ADDI.is_valid(&[0, 0, 3, 0], &[0; 6], &[3, 0, 0, 0, 0, 0])) /// ``` pub fn is_valid(self, op: &[i32; 4], before: &[i32; 6], after: &[i32; 6]) -> bool { let mut cpu = CPU::new(); cpu.registers.copy_from_slice(before); if let Ok(val) = cpu.execute(self, &op[1..4]) { if val == after[op[3] as usize] { return true; } } false } /// Iterator over all possible OpCode values. /// /// This iterator is backed internally by a static array of all op codes. pub fn values() -> impl Iterator { OP_LIST.iter().cloned() } } impl<'a> From<&'a str> for OpCode { fn from(name: &str) -> Self { match name { "addr" => OpCode::ADDR, "addi" => OpCode::ADDI, "mulr" => OpCode::MULR, "muli" => OpCode::MULI, "banr" => OpCode::BANR, "bani" => OpCode::BANI, "borr" => OpCode::BORR, "bori" => OpCode::BORI, "setr" => OpCode::SETR, "seti" => OpCode::SETI, "gtir" => OpCode::GTIR, "gtri" => OpCode::GTRI, "gtrr" => OpCode::GTRR, "eqir" => OpCode::EQIR, "eqri" => OpCode::EQRI, "eqrr" => OpCode::EQRR, _ => panic!("Invalid opcode {}", name), } } } const OP_LIST: [OpCode; 16] = [ OpCode::ADDR, OpCode::ADDI, OpCode::MULR, OpCode::MULI, OpCode::BANR, OpCode::BANI, OpCode::BORR, OpCode::BORI, OpCode::SETR, OpCode::SETI, OpCode::GTIR, OpCode::GTRI, OpCode::GTRR, OpCode::EQIR, OpCode::EQRI, OpCode::EQRR, ]; /// CPU Error /// /// This can be returned as an error when the CPU cannot execute /// an instruction. #[derive(Debug)] pub enum CPUErr { /// An invalid register was requested, with this register number. InvalidRegister(i32), } #[derive(Default)] pub struct CPU { /// Internal register state. Can be modified freely. pub registers: [i32; 6], } impl CPU { /// Construct a new CPU instance. pub fn new() -> Self { Default::default() } /// Execute an OpCode on the CPU /// /// This method will return the result of the operation executed, or a /// CPUErr when execution fails. /// /// # Panics /// /// This function does not check bounds on the operands slice (var) and the output register. /// Thus, when the operands slice has less than 3 elements or the target register does not /// exist, this function will panic. pub fn execute(&mut self, op: OpCode, var: &[i32]) -> Result { use self::OpCode::*; let res = match op { ADDR => self.reg(var[0])? + self.reg(var[1])?, ADDI => self.reg(var[0])? + var[1], MULR => self.reg(var[0])? * self.reg(var[1])?, MULI => self.reg(var[0])? * var[1], BANR => self.reg(var[0])? & self.reg(var[1])?, BANI => self.reg(var[0])? & var[1], BORR => self.reg(var[0])? | self.reg(var[1])?, BORI => self.reg(var[0])? | var[1], SETR => self.reg(var[0])?, SETI => var[0], GTRR => (self.reg(var[0])? > self.reg(var[1])?).into(), GTIR => (var[0] > self.reg(var[1])?).into(), GTRI => (self.reg(var[0])? > var[1]).into(), EQRR => (self.reg(var[0])? == self.reg(var[1])?).into(), EQIR => (var[0] == self.reg(var[1])?).into(), EQRI => (self.reg(var[0])? == var[1]).into(), }; self.registers[var[2] as usize] = res; Ok(res) } fn reg(&self, index: i32) -> Result { if let Some(val) = self.registers.get(index as usize) { Ok(*val) } else { Err(CPUErr::InvalidRegister(index)) } } }