use prometeu_bytecode::opcode::OpCode; use prometeu_bytecode::opcode_spec::{OpCodeSpecExt, OpcodeSpec}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum DecodeError { TruncatedOpcode { pc: usize }, UnknownOpcode { pc: usize, opcode: u16 }, TruncatedImmediate { pc: usize, opcode: OpCode, need: usize, have: usize }, } #[derive(Debug, Clone)] pub struct DecodedInstr<'a> { pub opcode: OpCode, pub spec: OpcodeSpec, pub imm: &'a [u8], pub next_pc: usize, } pub fn decode_at(rom: &[u8], pc: usize) -> Result, DecodeError> { if pc + 2 > rom.len() { return Err(DecodeError::TruncatedOpcode { pc }); } let opcode_val = u16::from_le_bytes([rom[pc], rom[pc+1]]); let opcode = OpCode::try_from(opcode_val).map_err(|_| DecodeError::UnknownOpcode { pc, opcode: opcode_val })?; let spec = opcode.spec(); let imm_start = pc + 2; let imm_end = imm_start + spec.imm_bytes as usize; if imm_end > rom.len() { return Err(DecodeError::TruncatedImmediate { pc, opcode, need: spec.imm_bytes as usize, have: rom.len().saturating_sub(imm_start) }); } let imm = &rom[imm_start..imm_end]; Ok(DecodedInstr { opcode, spec, imm, next_pc: imm_end, }) }