48 lines
1.3 KiB
Rust
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,
|
|
})
|
|
}
|