diff --git a/hbvm/src/bytecode.rs b/hbvm/src/bytecode.rs index 12129b36..a9770c63 100644 --- a/hbvm/src/bytecode.rs +++ b/hbvm/src/bytecode.rs @@ -9,7 +9,7 @@ macro_rules! constmod { } constmod!(pub opcode(u8) { - NOP = 0, // _ + NOP = 0, // N ADD = 1, // RRR SUB = 2, // RRR MUL = 3, // RRR @@ -37,8 +37,8 @@ constmod!(pub opcode(u8) { JMP = 100, // I JMPCOND = 101, // I - RET = 103, // _ - ECALL = 255, // _ + RET = 103, // N + ECALL = 255, // N }); #[repr(packed)] pub struct ParamRRR(pub u8, pub u8, pub u8); diff --git a/hbvm/src/main.rs b/hbvm/src/main.rs index 27895f39..5ee03afe 100644 --- a/hbvm/src/main.rs +++ b/hbvm/src/main.rs @@ -1,13 +1,18 @@ -use hbvm::{vm::Vm, RuntimeErrors}; +use hbvm::{validate::validate, vm::Vm, RuntimeErrors}; fn main() -> Result<(), RuntimeErrors> { // TODO: Grab program from cmdline #[rustfmt::skip] let prog = &[]; - unsafe { - let mut vm = Vm::new_unchecked(prog); - vm.run(); + if let Err(e) = validate(prog) { + eprintln!("Program validation error: {e:?}"); + return Ok(()); + } else { + unsafe { + let mut vm = Vm::new_unchecked(prog); + vm.run(); + } } Ok(()) } diff --git a/hbvm/src/validate.rs b/hbvm/src/validate.rs index e69de29b..d6aacaa4 100644 --- a/hbvm/src/validate.rs +++ b/hbvm/src/validate.rs @@ -0,0 +1,68 @@ +macro_rules! bail { + ($kind:ident, $start:expr, $curr:expr, $offset:expr) => { + return Err(Error { + kind: ErrorKind::$kind, + index: ($curr.as_ptr() as usize) - ($start.as_ptr() as usize) + $offset + }) + }; + ($kind:ident, $start:expr, $curr:expr) => { + bail!($kind, $start, $curr, 0) + }; +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ErrorKind { + InvalidInstruction, + InvalidRegister, + Unimplemented, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Error { + kind: ErrorKind, + index: usize, +} + +pub fn validate(mut program: &[u8]) -> Result<(), Error> { + use crate::bytecode::opcode::*; + + #[inline] + fn reg(regs: &[u8]) -> Option { + regs.iter() + .enumerate() + .filter_map(|(n, &r)| (r > 59).then_some(n)) + .next() + } + + let start = program; + loop { + program = match program { + [] => return Ok(()), + // N + [NOP | RET | ECALL, rest @ ..] => rest, + // RRR + [ADD..=XOR | ADDF..=DIVF, _, _, _, rest @ ..] => { + if let Some(n) = reg(&program[1..=3]) { + bail!(InvalidRegister, start, program, n + 1) + } + rest + } + // RR + [NOT, _, _, rest @ ..] => { + if let Some(n) = reg(&program[1..=2]) { + bail!(InvalidRegister, start, program, n + 1) + } + rest + } + // RI + [LI, reg, _, _, _, _, _, _, _, _, rest @ ..] => { + if *reg > 59 { + bail!(InvalidRegister, start, program, 1) + } + rest + } + [LD | ST, ..] => bail!(Unimplemented, start, program), + _ => bail!(InvalidInstruction, start, program), + } + } +}