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 hbbytecode::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..=CMPU | ADDF..=DIVF, _, _, _, rest @ ..] => { if let Some(n) = reg(&program[1..=3]) { bail!(InvalidRegister, start, program, n + 1); } rest } // RR [NOT | CP, _, _, rest @ ..] => { if let Some(n) = reg(&program[1..=2]) { bail!(InvalidRegister, start, program, n + 1); } rest } // RI [LI | JMP, reg, _, _, _, _, _, _, _, _, rest @ ..] => { if *reg > 59 { bail!(InvalidRegister, start, program, 1); } rest } // RRI [ADDI..=MULFI | LB..=SO | JEQ..=JGTU, _, _, _, _, _, _, _, _, _, _, rest @ ..] => { if let Some(n) = reg(&program[1..=2]) { bail!(InvalidRegister, start, program, n + 1); } rest } _ => bail!(InvalidInstruction, start, program), } } }