holey-bytes/hbvm/src/vm/mod.rs
2023-06-08 00:25:38 +02:00

145 lines
4.5 KiB
Rust

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)
.unwrap();
}};
}
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))
.unwrap();
}};
}
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<Self, validate::Error> {
validate::validate(program)?;
Ok(unsafe { Self::new_unchecked(program) })
}
pub fn run(&mut self) {
use hbbytecode::opcode::*;
loop {
let Some(&opcode) = self.program.get(self.pc)
else { return };
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::<u64>().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,
}