2023-06-06 15:03:37 -05:00
|
|
|
use {
|
|
|
|
core::ops,
|
2023-06-07 17:25:38 -05:00
|
|
|
hbbytecode::{OpParam, ParamRI, ParamRR, ParamRRI, ParamRRR},
|
|
|
|
mem::{ma_size, Memory},
|
2023-06-06 15:03:37 -05:00
|
|
|
static_assertions::assert_impl_one,
|
|
|
|
value::Value,
|
|
|
|
};
|
|
|
|
|
2023-06-08 16:23:23 -05:00
|
|
|
use crate::validate;
|
|
|
|
|
|
|
|
mod mem;
|
|
|
|
mod value;
|
|
|
|
|
2023-06-06 15:03:37 -05:00
|
|
|
macro_rules! param {
|
|
|
|
($self:expr, $ty:ty) => {{
|
|
|
|
assert_impl_one!($ty: OpParam);
|
2023-06-09 11:25:37 -05:00
|
|
|
let data = $self
|
|
|
|
.program
|
|
|
|
.as_ptr()
|
2023-06-06 15:03:37 -05:00
|
|
|
.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);
|
2023-06-08 16:23:23 -05:00
|
|
|
$self.write_reg(
|
|
|
|
tg,
|
|
|
|
$handler(
|
|
|
|
Value::$ty(&$self.read_reg(a0)),
|
|
|
|
Value::$ty(&$self.read_reg(a1)),
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
);
|
2023-06-06 15:03:37 -05:00
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2023-06-08 17:10:46 -05:00
|
|
|
macro_rules! binary_op_imm {
|
|
|
|
($self:expr, $ty:ident, $handler:expr) => {{
|
|
|
|
let ParamRRI(tg, a0, imm) = param!($self, ParamRRI);
|
|
|
|
$self.write_reg(
|
|
|
|
tg,
|
|
|
|
$handler(Value::$ty(&$self.read_reg(a0)), Value::$ty(&imm.into())).into(),
|
|
|
|
);
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2023-06-07 17:25:38 -05:00
|
|
|
macro_rules! load {
|
|
|
|
($self:expr, $size:ty) => {{
|
|
|
|
let ParamRRI(tg, a0, offset) = param!($self, ParamRRI);
|
2023-06-07 17:52:24 -05:00
|
|
|
$self.write_reg(
|
|
|
|
tg,
|
2023-06-08 17:10:46 -05:00
|
|
|
match $self
|
2023-06-08 16:23:23 -05:00
|
|
|
.memory
|
2023-06-07 17:52:24 -05:00
|
|
|
.load::<$size>($self.read_reg(a0).int() + offset)
|
2023-06-08 17:10:46 -05:00
|
|
|
{
|
|
|
|
Some(x) => x,
|
|
|
|
None => return HaltReason::LoadAccessEx,
|
|
|
|
},
|
2023-06-07 17:52:24 -05:00
|
|
|
);
|
2023-06-07 17:25:38 -05:00
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! store {
|
|
|
|
($self:expr, $size:ty) => {{
|
|
|
|
let ParamRRI(src, a0, offset) = param!($self, ParamRRI);
|
2023-06-08 17:10:46 -05:00
|
|
|
if let Err(()) = $self
|
2023-06-07 17:25:38 -05:00
|
|
|
.memory
|
2023-06-07 17:52:24 -05:00
|
|
|
.store::<$size>($self.read_reg(a0).int() + offset, $self.read_reg(src))
|
2023-06-08 17:10:46 -05:00
|
|
|
{
|
|
|
|
return HaltReason::StoreAccessEx;
|
|
|
|
}
|
2023-06-07 17:25:38 -05:00
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2023-06-08 18:55:09 -05:00
|
|
|
macro_rules! cond_jump {
|
|
|
|
($self:expr, $ty:ident, $expected:ident) => {{
|
|
|
|
let ParamRRI(a0, a1, jt) = param!($self, ParamRRI);
|
|
|
|
if core::cmp::Ord::cmp(&$self.read_reg(a0), &$self.read_reg(a1))
|
|
|
|
== core::cmp::Ordering::$expected
|
|
|
|
{
|
|
|
|
$self.pc = jt as usize;
|
|
|
|
}
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2023-06-06 15:03:37 -05:00
|
|
|
pub struct Vm<'a> {
|
|
|
|
pub registers: [Value; 60],
|
2023-06-07 17:25:38 -05:00
|
|
|
pub memory: Memory,
|
2023-06-06 15:03:37 -05:00
|
|
|
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 {
|
2023-06-08 17:10:46 -05:00
|
|
|
registers: [Value::from(0_u64); 60],
|
2023-06-07 17:25:38 -05:00
|
|
|
memory: Default::default(),
|
2023-06-06 15:03:37 -05:00
|
|
|
pc: 0,
|
|
|
|
program,
|
|
|
|
}
|
|
|
|
}
|
2023-06-06 16:08:26 -05:00
|
|
|
|
|
|
|
pub fn new_validated(program: &'a [u8]) -> Result<Self, validate::Error> {
|
|
|
|
validate::validate(program)?;
|
|
|
|
Ok(unsafe { Self::new_unchecked(program) })
|
|
|
|
}
|
2023-06-06 15:03:37 -05:00
|
|
|
|
2023-06-08 17:10:46 -05:00
|
|
|
pub fn run(&mut self) -> HaltReason {
|
2023-06-07 08:07:37 -05:00
|
|
|
use hbbytecode::opcode::*;
|
2023-06-06 15:03:37 -05:00
|
|
|
loop {
|
|
|
|
let Some(&opcode) = self.program.get(self.pc)
|
2023-06-08 17:10:46 -05:00
|
|
|
else { return HaltReason::ProgramEnd };
|
2023-06-06 15:03:37 -05:00
|
|
|
|
|
|
|
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),
|
2023-06-08 17:10:46 -05:00
|
|
|
SL => binary_op!(self, int, ops::Shl::shl),
|
|
|
|
SR => binary_op!(self, int, ops::Shr::shr),
|
|
|
|
SRS => binary_op!(self, sint, ops::Shr::shr),
|
2023-06-09 11:25:37 -05:00
|
|
|
CMP => {
|
|
|
|
let ParamRRR(tg, a0, a1) = param!(self, ParamRRR);
|
|
|
|
self.write_reg(
|
|
|
|
tg,
|
|
|
|
(self.read_reg(a0).sint().cmp(&self.read_reg(a1).sint()) as i64).into(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
CMPU => {
|
|
|
|
let ParamRRR(tg, a0, a1) = param!(self, ParamRRR);
|
|
|
|
self.write_reg(
|
|
|
|
tg,
|
|
|
|
(self.read_reg(a0).int().cmp(&self.read_reg(a1).int()) as i64).into(),
|
|
|
|
);
|
|
|
|
}
|
2023-06-06 15:03:37 -05:00
|
|
|
NOT => {
|
|
|
|
let param = param!(self, ParamRR);
|
2023-06-07 17:52:24 -05:00
|
|
|
self.write_reg(param.0, (!self.read_reg(param.1).int()).into());
|
2023-06-06 15:03:37 -05:00
|
|
|
}
|
|
|
|
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),
|
2023-06-08 17:10:46 -05:00
|
|
|
ADDI => binary_op_imm!(self, int, ops::Add::add),
|
|
|
|
MULI => binary_op_imm!(self, int, ops::Mul::mul),
|
|
|
|
REMI => binary_op_imm!(self, int, ops::Rem::rem),
|
|
|
|
ANDI => binary_op_imm!(self, int, ops::BitAnd::bitand),
|
|
|
|
ORI => binary_op_imm!(self, int, ops::BitOr::bitor),
|
|
|
|
XORI => binary_op_imm!(self, int, ops::BitXor::bitxor),
|
|
|
|
SLI => binary_op_imm!(self, int, ops::Shl::shl),
|
|
|
|
SRI => binary_op_imm!(self, int, ops::Shr::shr),
|
|
|
|
SRSI => binary_op_imm!(self, sint, ops::Shr::shr),
|
|
|
|
ADDFI => binary_op_imm!(self, float, ops::Add::add),
|
|
|
|
MULFI => binary_op_imm!(self, float, ops::Mul::mul),
|
2023-06-09 11:25:37 -05:00
|
|
|
CMPI => {
|
|
|
|
let ParamRRI(tg, a0, imm) = param!(self, ParamRRI);
|
|
|
|
self.write_reg(
|
|
|
|
tg,
|
|
|
|
(self.read_reg(a0).sint().cmp(&Value::from(imm).sint()) as i64).into(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
CMPUI => {
|
|
|
|
let ParamRRI(tg, a0, imm) = param!(self, ParamRRI);
|
|
|
|
self.write_reg(tg, (self.read_reg(a0).int().cmp(&imm) as i64).into());
|
|
|
|
}
|
2023-06-06 17:02:27 -05:00
|
|
|
CP => {
|
|
|
|
let param = param!(self, ParamRR);
|
2023-06-07 17:52:24 -05:00
|
|
|
self.write_reg(param.0, self.read_reg(param.1));
|
2023-06-06 17:02:27 -05:00
|
|
|
}
|
2023-06-06 15:03:37 -05:00
|
|
|
LI => {
|
|
|
|
let param = param!(self, ParamRI);
|
2023-06-07 17:52:24 -05:00
|
|
|
self.write_reg(param.0, param.1.into());
|
2023-06-06 15:03:37 -05:00
|
|
|
}
|
2023-06-07 17:25:38 -05:00
|
|
|
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),
|
2023-06-06 15:03:37 -05:00
|
|
|
JMP => {
|
2023-06-07 17:52:24 -05:00
|
|
|
let ParamRI(reg, offset) = param!(self, ParamRI);
|
|
|
|
self.pc = (self.read_reg(reg).int() + offset) as usize;
|
2023-06-06 15:03:37 -05:00
|
|
|
}
|
2023-06-08 18:55:09 -05:00
|
|
|
JEQ => cond_jump!(self, int, Equal),
|
|
|
|
JNE => {
|
|
|
|
let ParamRRI(a0, a1, jt) = param!(self, ParamRRI);
|
|
|
|
if self.read_reg(a0) != self.read_reg(a1) {
|
|
|
|
self.pc = jt as usize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
JLT => cond_jump!(self, int, Less),
|
|
|
|
JGT => cond_jump!(self, int, Greater),
|
|
|
|
JLTU => cond_jump!(self, sint, Less),
|
|
|
|
JGTU => cond_jump!(self, sint, Greater),
|
2023-06-08 17:10:46 -05:00
|
|
|
ECALL => {
|
|
|
|
param!(self, ());
|
|
|
|
return HaltReason::Ecall;
|
|
|
|
}
|
2023-06-06 15:03:37 -05:00
|
|
|
_ => core::hint::unreachable_unchecked(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2023-06-07 17:52:24 -05:00
|
|
|
unsafe fn read_reg(&self, n: u8) -> Value {
|
|
|
|
if n == 0 {
|
2023-06-08 17:10:46 -05:00
|
|
|
0_u64.into()
|
2023-06-07 17:52:24 -05:00
|
|
|
} else {
|
|
|
|
*self.registers.get_unchecked(n as usize)
|
|
|
|
}
|
2023-06-06 15:03:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2023-06-07 17:52:24 -05:00
|
|
|
unsafe fn write_reg(&mut self, n: u8, value: Value) {
|
|
|
|
if n != 0 {
|
|
|
|
*self.registers.get_unchecked_mut(n as usize) = value;
|
|
|
|
}
|
2023-06-06 15:03:37 -05:00
|
|
|
}
|
|
|
|
}
|
2023-06-07 17:25:38 -05:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
#[repr(u8)]
|
2023-06-08 17:10:46 -05:00
|
|
|
pub enum HaltReason {
|
|
|
|
ProgramEnd,
|
|
|
|
Ecall,
|
|
|
|
LoadAccessEx,
|
|
|
|
StoreAccessEx,
|
2023-06-07 17:25:38 -05:00
|
|
|
}
|