use core::panic; use crate::interpreter::{MemAddress, StackAddress}; use crate::opcodes::OpCode; #[derive(Debug)] pub struct Instruction { pub opcode: OpCode, pub in_operands: Vec, pub out_operands: Vec, } enum Nybble { High, Low, } #[derive(PartialEq, Debug, Clone)] pub enum Operand { Constant(i32), PopStack, FrameIndirect(StackAddress), MemIndirect(MemAddress), MemRamIndirect(MemAddress), } impl Instruction { pub fn read<'a, I>(bytes: &mut I) -> Result<(Instruction, u32), u32> where I: IntoIterator + Copy { let mut bytes = bytes.into_iter().enumerate().peekable(); let instruction_length = { let first_byte = *bytes.peek().unwrap().1; match first_byte >> 6 { 0b00 | 0b01 => 1, 0b10 => 2, 0b11 => 4, _ => unreachable!() } }; let mask = match instruction_length { 1 => 0, 2 => 0x8000, 4 => 0xC0000000, _ => panic!("Invalid opcode byte array"), }; let instruction_bytes = (0..4-instruction_length).into_iter().map(|_n| 0).chain( (0..instruction_length).into_iter().map(|_n| *bytes.next().unwrap().1) ).collect::>(); let opcode_num = u32::from_be_bytes(instruction_bytes.clone().try_into().unwrap()) & !mask; println!("instruction length: {instruction_length}, number: 0x{opcode_num:x}, bytes: {instruction_bytes:?}"); let opcode = OpCode::get_from_code(opcode_num); if let None = opcode { return Err(opcode_num); } let opcode = opcode.unwrap(); let input_operand_count = OpCode::get_input_arg_count(opcode.clone()); let output_operand_count = OpCode::get_output_arg_count(opcode.clone()); let operand_count = input_operand_count + output_operand_count; let operand_bytes = (operand_count + 1) / 2; let skip_last_half = operand_bytes != operand_count / 2; let operand_bytes = (0..operand_bytes).into_iter().map(|_n| *bytes.next().unwrap().1); let halfbytes : Vec<_> = operand_bytes.flat_map(|byte| vec![(byte, Nybble::Low), (byte, Nybble::High)]).collect(); // Just ensure other borrows to the byte array have closed by rebinding it // This verifies we're at the operand data position let mut data_bytes = bytes; let mut operands : Vec = halfbytes.iter().map(|(byte, nybble)| { let value = match nybble { Nybble::High => byte >> 4, Nybble::Low => byte & 0x0f, }; match value { 0 => Operand::Constant(0), 1 => Operand::Constant(i8::from_be_bytes([*data_bytes.next().unwrap().1]) as i32), 2 => Operand::Constant(i16::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1]) as i32), 3 => Operand::Constant(i32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])), 4 => panic!("Invalid operand addressing mode: {value:x}"), 5 => Operand::MemIndirect(u32::from_be_bytes([0, 0, 0, *data_bytes.next().unwrap().1])), 6 => Operand::MemIndirect(u32::from_be_bytes([0, 0, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])), 7 => Operand::MemIndirect(u32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])), 8 => Operand::PopStack, 9 => Operand::FrameIndirect(u32::from_be_bytes([0, 0, 0, *data_bytes.next().unwrap().1])), 0xa => Operand::FrameIndirect(u32::from_be_bytes([0, 0, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])), 0xb => Operand::FrameIndirect(u32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])), 0xc => panic!("Invalid operand addressing mode: {value:x}"), 0xd => Operand::MemRamIndirect(u32::from_be_bytes([0, 0, 0, *data_bytes.next().unwrap().1])), 0xe => Operand::MemRamIndirect(u32::from_be_bytes([0, 0, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])), 0xf => Operand::MemRamIndirect(u32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])), _ => unreachable!() } }).collect(); // Should only trim it off if it was all 0 if skip_last_half && operands.len() > 0 { assert!(*operands.last().unwrap() == Operand::Constant(0)); operands.remove(operands.len() - 1); } let in_operands = operands[0..input_operand_count].try_into().unwrap(); let out_operands = operands[input_operand_count..].try_into().unwrap(); // Return instruction along with the number of bytes we've read Ok((Instruction { opcode, in_operands, out_operands, }, data_bytes.peek().unwrap().0 as u32)) } }