2026-03-24 13:40:31 +00:00

48 lines
1.3 KiB
Rust

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<DecodedInstr<'_>, 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,
})
}