Co-authored-by: Nilton Constantino <nilton.constantino@visma.com> Reviewed-on: #8
71 lines
2.5 KiB
Rust
71 lines
2.5 KiB
Rust
use crate::opcode::OpCode;
|
|
use crate::readwrite::*;
|
|
use std::io::{Cursor, Read};
|
|
|
|
/// Represents a disassembled instruction.
|
|
#[derive(Debug, Clone)]
|
|
pub struct Instr {
|
|
/// The absolute address (PC) where this instruction starts in ROM.
|
|
pub pc: u32,
|
|
/// The decoded OpCode.
|
|
pub opcode: OpCode,
|
|
/// The list of operands extracted from the byte stream.
|
|
pub operands: Vec<DisasmOperand>,
|
|
}
|
|
|
|
/// Represents an operand decoded from the byte stream.
|
|
#[derive(Debug, Clone)]
|
|
pub enum DisasmOperand {
|
|
U32(u32),
|
|
I32(i32),
|
|
I64(i64),
|
|
F64(f64),
|
|
Bool(bool),
|
|
}
|
|
|
|
/// Translates raw ROM bytes back into a human-readable list of instructions.
|
|
///
|
|
/// This is the inverse of the assembly process. It reads the bytes sequentially,
|
|
/// identifies the OpCode, and then reads the correct number of operand bytes
|
|
/// based on the ISA rules.
|
|
pub fn disasm(rom: &[u8]) -> Result<Vec<Instr>, String> {
|
|
let mut instructions = Vec::new();
|
|
let mut cursor = Cursor::new(rom);
|
|
|
|
while (cursor.position() as usize) < rom.len() {
|
|
let pc = cursor.position() as u32;
|
|
let op_val = read_u16_le(&mut cursor).map_err(|e| e.to_string())?;
|
|
let opcode = OpCode::try_from(op_val)?;
|
|
let mut operands = Vec::new();
|
|
|
|
match opcode {
|
|
OpCode::PushConst | OpCode::PushI32 | OpCode::PushBounded | OpCode::Jmp | OpCode::JmpIfFalse | OpCode::JmpIfTrue
|
|
| OpCode::GetGlobal | OpCode::SetGlobal | OpCode::GetLocal | OpCode::SetLocal
|
|
| OpCode::PopN | OpCode::Syscall | OpCode::GateLoad | OpCode::GateStore | OpCode::Call => {
|
|
let v = read_u32_le(&mut cursor).map_err(|e| e.to_string())?;
|
|
operands.push(DisasmOperand::U32(v));
|
|
}
|
|
OpCode::PushI64 | OpCode::PushF64 => {
|
|
let v = read_i64_le(&mut cursor).map_err(|e| e.to_string())?;
|
|
operands.push(DisasmOperand::I64(v));
|
|
}
|
|
OpCode::PushBool => {
|
|
let mut b_buf = [0u8; 1];
|
|
cursor.read_exact(&mut b_buf).map_err(|e| e.to_string())?;
|
|
operands.push(DisasmOperand::Bool(b_buf[0] != 0));
|
|
}
|
|
OpCode::Alloc => {
|
|
let v1 = read_u32_le(&mut cursor).map_err(|e| e.to_string())?;
|
|
let v2 = read_u32_le(&mut cursor).map_err(|e| e.to_string())?;
|
|
operands.push(DisasmOperand::U32(v1));
|
|
operands.push(DisasmOperand::U32(v2));
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
instructions.push(Instr { pc, opcode, operands });
|
|
}
|
|
|
|
Ok(instructions)
|
|
}
|