use crate::validate; mod mem; mod value; use { core::ops, hbbytecode::{OpParam, ParamRI, ParamRR, ParamRRI, ParamRRR}, mem::{ma_size, Memory}, static_assertions::assert_impl_one, value::Value, }; macro_rules! param { ($self:expr, $ty:ty) => {{ assert_impl_one!($ty: OpParam); let data = $self.program.as_ptr() .add($self.pc + 1) .cast::<$ty>() .read(); $self.pc += 1 + core::mem::size_of::<$ty>(); data }}; } macro_rules! binary_op { ($self:expr, $ty:ident, $handler:expr) => {{ let ParamRRR(tg, a0, a1) = param!($self, ParamRRR); *$self.reg_mut(tg) = $handler(Value::$ty($self.reg(a0)), Value::$ty($self.reg(a1))).into(); }}; } macro_rules! load { ($self:expr, $size:ty) => {{ let ParamRRI(tg, a0, offset) = param!($self, ParamRRI); *$self.reg_mut(tg) = $self .memory .load::<$size>($self.reg(a0).int() + offset) .ok_or(Exception::LoadAccess)?; }}; } macro_rules! store { ($self:expr, $size:ty) => {{ let ParamRRI(src, a0, offset) = param!($self, ParamRRI); $self .memory .store::<$size>($self.reg(a0).int() + offset, *$self.reg(src)) .map_err(|_| Exception::StoreAccess)?; }}; } pub struct Vm<'a> { pub registers: [Value; 60], pub memory: Memory, pc: usize, program: &'a [u8], } impl<'a> Vm<'a> { /// # Safety /// Program code has to be validated pub unsafe fn new_unchecked(program: &'a [u8]) -> Self { Self { registers: [Value::from(0); 60], memory: Default::default(), pc: 0, program, } } pub fn new_validated(program: &'a [u8]) -> Result { validate::validate(program)?; Ok(unsafe { Self::new_unchecked(program) }) } pub fn run(&mut self) -> Result<(), Exception> { use hbbytecode::opcode::*; loop { let Some(&opcode) = self.program.get(self.pc) else { return Ok(()) }; unsafe { match opcode { NOP => param!(self, ()), ADD => binary_op!(self, int, u64::wrapping_add), SUB => binary_op!(self, int, u64::wrapping_sub), MUL => binary_op!(self, int, u64::wrapping_mul), DIV => binary_op!(self, int, u64::wrapping_div), REM => binary_op!(self, int, u64::wrapping_rem), AND => binary_op!(self, int, ops::BitAnd::bitand), OR => binary_op!(self, int, ops::BitOr::bitor), XOR => binary_op!(self, int, ops::BitXor::bitxor), NOT => { let param = param!(self, ParamRR); *self.reg_mut(param.0) = (!self.reg(param.1).int()).into(); } ADDF => binary_op!(self, float, ops::Add::add), SUBF => binary_op!(self, float, ops::Sub::sub), MULF => binary_op!(self, float, ops::Mul::mul), DIVF => binary_op!(self, float, ops::Div::div), CP => { let param = param!(self, ParamRR); *self.reg_mut(param.0) = *self.reg(param.1); } LI => { let param = param!(self, ParamRI); *self.reg_mut(param.0) = param.1.into(); } LB => load!(self, ma_size::Byte), LD => load!(self, ma_size::Doublet), LQ => load!(self, ma_size::Quadlet), LO => load!(self, ma_size::Octlet), SB => store!(self, ma_size::Byte), SD => store!(self, ma_size::Doublet), SQ => store!(self, ma_size::Quadlet), SO => store!(self, ma_size::Octlet), JMP => { self.pc = self.program.as_ptr().add(self.pc + 1).cast::().read() as usize; } _ => core::hint::unreachable_unchecked(), } } } } #[inline] unsafe fn reg(&self, n: u8) -> &Value { self.registers.get_unchecked(n as usize) } #[inline] unsafe fn reg_mut(&mut self, n: u8) -> &mut Value { self.registers.get_unchecked_mut(n as usize) } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u8)] pub enum Exception { LoadAccess, StoreAccess, }