use super::{ Instruction, Address }; static Z: Address = Address::Memory(0); static E: Address = Address::Memory(1); static N: Address = Address::Memory(2); static STACK_POINTER: Address = Address::Memory(4); #[derive(Debug)] pub(crate) enum ArgumentError { UnknownInstruction, TooMany, TooFew, NotLocation, } impl Instruction { fn new1(name: &str, a: &Address) -> Instruction { Instruction { name: name.to_string(), args: vec![a.clone()] } } fn new2(name: &str, a: &Address, b: &Address) -> Instruction { Instruction { name: name.to_string(), args: vec![a.clone(), b.clone()] } } fn new3(name: &str, a: &Address, b: &Address, c: &Address) -> Instruction { Instruction { name: name.to_string(), args: vec![a.clone(), b.clone(), c.clone()] } } fn new_subleq(a: &Address, b: &Address, c: &Address) -> Instruction { if let Address::Constants(_) = c { panic!("Constants cannot be the third subleq argument!"); } Self::new3("subleq", a, b, c) } fn check_args_len(&self, expected: usize) -> Result<(), ArgumentError> { if self.args.len() < expected { return Err(ArgumentError::TooFew); } else if self.args.len() > expected { return Err(ArgumentError::TooMany); } Ok(()) } pub(crate) fn check_args(&self) -> Result<(), ArgumentError> { match &*self.name { "subleq" => { self.check_args_len(3)?; for arg in &self.args { if ! arg.is_location() { return Err(ArgumentError::NotLocation); } } }, "jmp" | "jsr" => { self.check_args_len(1)?; if ! self.args[0].is_location() { return Err(ArgumentError::NotLocation); } }, "beq" => { self.check_args_len(2)?; if ! self.args[1].is_location() { return Err(ArgumentError::NotLocation); } }, "rts" => self.check_args_len(0)?, "inc" | "dec" => self.check_args_len(1)?, "add" | "sub" | "mov" => self.check_args_len(2)?, _ => return Err(ArgumentError::UnknownInstruction), } Ok(()) } pub(crate) fn collapse(&mut self, next: usize) -> Vec { self.name = self.name.to_lowercase(); match &*self.name { /* Primitive instrutions (need only one collapse) */ "subleq" => vec![self.clone()], "jmp" => vec![ Instruction::new_subleq(&Z, &Z, &self.args[0]), ], "inc" => vec![ Instruction::new_subleq(&N, &self.args[0], &Address::Instruction(next + 1)), ], "dec" => vec![ Instruction::new_subleq(&E, &self.args[0], &Address::Instruction(next + 1)), ], "add" => vec![ Instruction::new_subleq(&self.args[0], &Z, &Address::Instruction(next + 1)), Instruction::new_subleq(&Z, &self.args[1], &Address::Instruction(next + 2)), Instruction::new_subleq(&Z, &Z, &Address::Instruction(next + 3)), ], "sub" => vec![ Instruction::new_subleq(&self.args[0], &self.args[1], &Address::Instruction(next + 1)), ], "mov" => vec![ Instruction::new_subleq(&self.args[1], &self.args[1], &Address::Instruction(next + 1)), Instruction::new_subleq(&self.args[0], &Z, &Address::Instruction(next + 2)), Instruction::new_subleq(&Z, &self.args[1], &Address::Instruction(next + 3)), Instruction::new_subleq(&Z, &Z, &Address::Instruction(next + 4)), ], "beq" => vec![ Instruction::new_subleq(&self.args[0], &Z, &Address::Instruction(next + 2)), Instruction::new_subleq(&Z, &Z, &Address::Instruction(next + 4)), Instruction::new_subleq(&Z, &Z, &Address::Instruction(next + 3)), Instruction::new_subleq(&Z, &self.args[0], &self.args[1] ), ], /* Composite instructions (require two collapses) */ "jsr" => { vec![ Instruction::new1("inc", &STACK_POINTER), // We're modifying the generated mov instruction // Every &self.args[1] value gets replaced with the value which SP points to Instruction::new2("mov", &STACK_POINTER, &Address::InstructionOffset(next + 4, 0)), Instruction::new2("mov", &STACK_POINTER, &Address::InstructionOffset(next + 4, 1)), Instruction::new2("mov", &STACK_POINTER, &Address::InstructionOffset(next + 4, 7)), Instruction::new2("mov", &Address::InstructionConstant(next + 6), &Z), Instruction::new1("jmp", &self.args[0]), ] }, "rts" => vec![ Instruction::new2("mov", &STACK_POINTER, &Address::InstructionOffset(next + 1, 3)), Instruction::new2("mov", &Z, &Address::InstructionOffset(next + 3, 2)), Instruction::new1("dec", &STACK_POINTER), Instruction::new1("jmp", &Z), ], _ => panic!("Unknown instruction {}!", self.name), } } pub(crate) fn collapse_len(&self) -> usize { match &*self.name { "subleq" | "jmp" | "inc" | "dec" | "sub" => 1, "add" => 3, "mov" | "beq" | "rts" => 4, "jsr" => 6, _ => panic!("Unknown instruction {}!", self.name), } } }