forked from AbleOS/holey-bytes
Implemented some portion of VM, missing validation
This commit is contained in:
parent
a4b22e2053
commit
e67d512f89
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -38,6 +38,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"log",
|
"log",
|
||||||
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -55,6 +56,12 @@ version = "1.17.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
|
|
@ -3,6 +3,10 @@ name = "hbvm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "*"
|
log = "*"
|
||||||
hashbrown = "0.13.2"
|
hashbrown = "0.13.2"
|
||||||
|
static_assertions = "1.0"
|
||||||
|
|
56
hbvm/src/bytecode.rs
Normal file
56
hbvm/src/bytecode.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
pub const REG_COUNT: usize = 60;
|
||||||
|
|
||||||
|
macro_rules! constmod {
|
||||||
|
($vis:vis $mname:ident($repr:ty) { $($cname:ident = $val:expr),* $(,)? }) => {
|
||||||
|
$vis mod $mname {
|
||||||
|
$(pub const $cname: $repr = $val;)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constmod!(pub opcode(u8) {
|
||||||
|
NOP = 0, // _
|
||||||
|
ADD = 1, // RRR
|
||||||
|
SUB = 2, // RRR
|
||||||
|
MUL = 3, // RRR
|
||||||
|
DIV = 4, // RRR
|
||||||
|
REM = 5, // RRR
|
||||||
|
AND = 6, // RRR
|
||||||
|
OR = 7, // RRR
|
||||||
|
XOR = 8, // RRR
|
||||||
|
NOT = 9, // RR
|
||||||
|
|
||||||
|
// TODO: Add instruction for integer and float
|
||||||
|
// reg ← reg + imm instructions
|
||||||
|
|
||||||
|
ADDF = 10, // RRR
|
||||||
|
SUBF = 11, // RRR
|
||||||
|
MULF = 12, // RRR
|
||||||
|
DIVF = 13, // RRR
|
||||||
|
|
||||||
|
LI = 14, // RI
|
||||||
|
LD = 15, // RI
|
||||||
|
ST = 16, // RI
|
||||||
|
|
||||||
|
MAPPAGE = 17, // ?
|
||||||
|
UNMAPPAGE = 18, // ?
|
||||||
|
|
||||||
|
JMP = 100, // I
|
||||||
|
JMPCOND = 101, // I
|
||||||
|
RET = 103, // _
|
||||||
|
ECALL = 255, // _
|
||||||
|
});
|
||||||
|
|
||||||
|
#[repr(packed)] pub struct ParamRRR(pub u8, pub u8, pub u8);
|
||||||
|
#[repr(packed)] pub struct ParamRR(pub u8, pub u8);
|
||||||
|
|
||||||
|
#[repr(packed)] pub struct ParamRI(pub u8, pub u64);
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// TODO.
|
||||||
|
pub unsafe trait OpParam {}
|
||||||
|
unsafe impl OpParam for ParamRRR {}
|
||||||
|
unsafe impl OpParam for ParamRR {}
|
||||||
|
unsafe impl OpParam for ParamRI {}
|
||||||
|
unsafe impl OpParam for u64 {}
|
||||||
|
unsafe impl OpParam for () {}
|
|
@ -1,39 +0,0 @@
|
||||||
mod validate;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum Instruction {
|
|
||||||
Nop = 0,
|
|
||||||
Add(Register, Register, Register),
|
|
||||||
Sub(Register, Register, Register) = 2,
|
|
||||||
Mul(Register, Register, Register) = 3,
|
|
||||||
Div(Register, Register, Register) = 4,
|
|
||||||
Mod(Register, Register, Register) = 5,
|
|
||||||
|
|
||||||
And(Register, Register, Register) = 6,
|
|
||||||
Or(Register, Register, Register) = 7,
|
|
||||||
Xor(Register, Register, Register) = 8,
|
|
||||||
Not(Register, Register) = 9,
|
|
||||||
|
|
||||||
Li(Register, u64) = 10,
|
|
||||||
Ld(Register, u64) = 15,
|
|
||||||
St(Register, u64) = 16,
|
|
||||||
|
|
||||||
MapPage = 17,
|
|
||||||
UnmapPage = 18,
|
|
||||||
|
|
||||||
Jmp = 100,
|
|
||||||
JumpCond = 101,
|
|
||||||
Ret = 103,
|
|
||||||
Ecall = 255,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
#[repr(u8)]
|
|
||||||
#[rustfmt::skip]
|
|
||||||
pub enum Register {
|
|
||||||
X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20,
|
|
||||||
X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39,
|
|
||||||
X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58,
|
|
||||||
X59,
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
pub type CallStack = Vec<FnCall>;
|
|
||||||
pub struct FnCall {
|
|
||||||
pub ret: usize,
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
pub struct EngineConfig {
|
|
||||||
pub call_stack_depth: usize,
|
|
||||||
pub quantum: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EngineConfig {
|
|
||||||
pub fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
call_stack_depth: 32,
|
|
||||||
quantum: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
use super::Engine;
|
|
||||||
|
|
||||||
pub type EnviromentCall = fn(&mut Engine) -> Result<&mut Engine, u64>;
|
|
|
@ -1,102 +0,0 @@
|
||||||
use log::info;
|
|
||||||
|
|
||||||
pub mod call_stack;
|
|
||||||
pub mod config;
|
|
||||||
pub mod enviroment_calls;
|
|
||||||
pub mod regs;
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod tests;
|
|
||||||
|
|
||||||
use {
|
|
||||||
self::call_stack::CallStack,
|
|
||||||
crate::{engine::enviroment_calls::EnviromentCall, memory, HaltStatus, RuntimeErrors},
|
|
||||||
alloc::vec::Vec,
|
|
||||||
config::EngineConfig,
|
|
||||||
log::trace,
|
|
||||||
regs::Registers,
|
|
||||||
};
|
|
||||||
|
|
||||||
// pub const PAGE_SIZE: usize = 8192;
|
|
||||||
|
|
||||||
pub struct RealPage {
|
|
||||||
pub ptr: *mut u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct VMPage {
|
|
||||||
pub data: [u8; 8192],
|
|
||||||
}
|
|
||||||
impl Default for VMPage {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
data: [0; 4096 * 2],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Page {
|
|
||||||
VMPage(VMPage),
|
|
||||||
RealPage(RealPage),
|
|
||||||
}
|
|
||||||
impl Page {
|
|
||||||
pub fn data(&self) -> [u8; 4096 * 2] {
|
|
||||||
match self {
|
|
||||||
Page::VMPage(vmpage) => vmpage.data,
|
|
||||||
Page::RealPage(_) => {
|
|
||||||
unimplemented!("Memmapped hw page not yet supported")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty_enviroment_call(engine: &mut Engine) -> Result<&mut Engine, u64> {
|
|
||||||
trace!("Registers {:?}", engine.registers);
|
|
||||||
Err(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Engine {
|
|
||||||
pub pc: usize,
|
|
||||||
pub program: Vec<u8>,
|
|
||||||
pub registers: Registers,
|
|
||||||
pub config: EngineConfig,
|
|
||||||
|
|
||||||
/// BUG: This DOES NOT account for overflowing
|
|
||||||
pub last_timer_count: u32,
|
|
||||||
pub timer_callback: Option<fn() -> u32>,
|
|
||||||
pub memory: memory::Memory,
|
|
||||||
pub enviroment_call_table: [Option<EnviromentCall>; 256],
|
|
||||||
pub call_stack: CallStack,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Engine {
|
|
||||||
pub fn set_timer_callback(&mut self, func: fn() -> u32) {
|
|
||||||
self.timer_callback = Some(func);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Engine {
|
|
||||||
pub fn new(program: Vec<u8>) -> Self {
|
|
||||||
let mut mem = memory::Memory::new();
|
|
||||||
for (addr, byte) in program.clone().into_iter().enumerate() {
|
|
||||||
let _ = mem.set_addr8(addr as u64, byte);
|
|
||||||
}
|
|
||||||
trace!("{:?}", mem.read_addr8(0));
|
|
||||||
let ecall_table: [Option<EnviromentCall>; 256] = [None; 256];
|
|
||||||
Self {
|
|
||||||
pc: 0,
|
|
||||||
program,
|
|
||||||
registers: Registers::default(),
|
|
||||||
config: EngineConfig::default(),
|
|
||||||
last_timer_count: 0,
|
|
||||||
timer_callback: None,
|
|
||||||
enviroment_call_table: ecall_table,
|
|
||||||
memory: mem,
|
|
||||||
call_stack: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dump(&self) {}
|
|
||||||
pub fn run(&mut self) -> Result<HaltStatus, RuntimeErrors> {
|
|
||||||
Ok(HaltStatus::Running)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
use {
|
|
||||||
super::Engine,
|
|
||||||
crate::{HaltStatus, RuntimeErrors},
|
|
||||||
alloc::vec,
|
|
||||||
RuntimeErrors::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn invalid_program() {
|
|
||||||
let prog = vec![1, 0];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let ret = eng.run();
|
|
||||||
assert_eq!(ret, Err(InvalidOpcodePair(1, 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn empty_program() {
|
|
||||||
let prog = vec![];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let ret = eng.run();
|
|
||||||
assert_eq!(ret, Ok(HaltStatus::Halted));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn max_quantum_reached() {
|
|
||||||
let prog = vec![0, 0, 0, 0];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
eng.set_timer_callback(|| {
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
eng.config.quantum = 1;
|
|
||||||
let ret = eng.run();
|
|
||||||
assert_eq!(ret, Ok(HaltStatus::Running));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn jump_out_of_bounds() {
|
|
||||||
use crate::bytecode::ops::Operations::JUMP;
|
|
||||||
let prog = vec![JUMP as u8, 0, 0, 0, 0, 0, 0, 1, 0];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let ret = eng.run();
|
|
||||||
assert_eq!(ret, Err(InvalidJumpAddress(256)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn invalid_system_call() {
|
|
||||||
let prog = vec![255, 0];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let ret = eng.run();
|
|
||||||
assert_eq!(ret, Err(InvalidSystemCall(0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_u8() {
|
|
||||||
use crate::bytecode::ops::{MathOpSides::ConstantConstant, Operations::ADD};
|
|
||||||
|
|
||||||
let prog = vec![ADD as u8, ConstantConstant as u8, 100, 98, 0xA0];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let _ = eng.run();
|
|
||||||
assert_eq!(eng.registers.a0, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sub_u8() {
|
|
||||||
use crate::bytecode::ops::Operations::SUB;
|
|
||||||
|
|
||||||
let prog = vec![SUB as u8];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let _ = eng.run();
|
|
||||||
assert_eq!(eng.registers.a0, 1);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn mul_u8() {
|
|
||||||
use crate::bytecode::ops::{MathOpSides::ConstantConstant, Operations::MUL};
|
|
||||||
|
|
||||||
let prog = vec![MUL as u8, ConstantConstant as u8, 1, 2, 0xA0];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let _ = eng.run();
|
|
||||||
assert_eq!(eng.registers.a0, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn div_u8() {
|
|
||||||
use crate::bytecode::ops::Operations::DIV;
|
|
||||||
|
|
||||||
let prog = vec![DIV as u8];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let _ = eng.run();
|
|
||||||
assert_eq!(eng.registers.a0, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_register() {
|
|
||||||
let prog = alloc::vec![];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
eng.set_register(0xA0, 1);
|
|
||||||
assert_eq!(eng.registers.a0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn load_u8() {
|
|
||||||
use crate::bytecode::ops::{Operations::LOAD, RWSubTypes::AddrToReg};
|
|
||||||
|
|
||||||
let prog = vec![LOAD as u8, AddrToReg as u8, 0, 0, 0, 0, 0, 0, 1, 0, 0xA0];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let ret = eng.memory.set_addr8(256, 1);
|
|
||||||
assert_eq!(ret, Ok(()));
|
|
||||||
let _ = eng.run();
|
|
||||||
assert_eq!(eng.registers.a0, 1);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn set_memory_8() {
|
|
||||||
let prog = vec![];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let ret = eng.memory.set_addr8(256, 1);
|
|
||||||
assert_eq!(ret, Ok(()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_memory_64() {
|
|
||||||
let prog = vec![];
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
let ret = eng.memory.set_addr64(256, 1);
|
|
||||||
assert_eq!(ret, Ok(()));
|
|
||||||
}
|
|
|
@ -2,8 +2,8 @@
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub mod bytecode;
|
pub mod bytecode;
|
||||||
pub mod engine;
|
pub mod validate;
|
||||||
pub mod memory;
|
pub mod vm;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum RuntimeErrors {
|
pub enum RuntimeErrors {
|
||||||
|
|
|
@ -1,30 +1,17 @@
|
||||||
use hbvm::{
|
use hbvm::{vm::Vm, RuntimeErrors};
|
||||||
bytecode::Instruction,
|
|
||||||
engine::Engine,
|
|
||||||
RuntimeErrors, HaltStatus,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() -> Result<(), RuntimeErrors> {
|
fn main() -> Result<(), RuntimeErrors> {
|
||||||
// TODO: Grab program from cmdline
|
// TODO: Grab program from cmdline
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let prog: Vec<u8> = vec![
|
let prog = &[];
|
||||||
];
|
|
||||||
|
|
||||||
let mut eng = Engine::new(prog);
|
|
||||||
// eng.set_timer_callback(time);
|
|
||||||
eng.enviroment_call_table[10] = Some(print_fn);
|
|
||||||
while eng.run()? != HaltStatus::Halted {}
|
|
||||||
eng.dump();
|
|
||||||
println!("{:#?}", eng.registers);
|
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut vm = Vm::new_unchecked(prog);
|
||||||
|
vm.run();
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn time() -> u32 {
|
pub fn time() -> u32 {
|
||||||
9
|
9
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_fn(engine: &mut Engine) -> Result<&mut Engine, u64> {
|
|
||||||
println!("hello");
|
|
||||||
Ok(engine)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
use crate::engine::VMPage;
|
|
||||||
|
|
||||||
use {
|
|
||||||
crate::{engine::Page, RuntimeErrors},
|
|
||||||
alloc::vec::Vec,
|
|
||||||
hashbrown::HashMap,
|
|
||||||
log::trace,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Memory {
|
|
||||||
inner: HashMap<u64, Page>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Memory {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: HashMap::new(),
|
|
||||||
}
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map_vec(&mut self, address: u64, vec: Vec<u8>) {
|
|
||||||
panic!("Mapping vectors into pages is not supported yet");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Memory {
|
|
||||||
pub fn read_addr8(&mut self, address: u64) -> Result<u8, RuntimeErrors> {
|
|
||||||
let (page, offset) = addr_to_page(address);
|
|
||||||
trace!("page {} offset {}", page, offset);
|
|
||||||
match self.inner.get(&page) {
|
|
||||||
Some(page) => {
|
|
||||||
let val = page.data()[offset as usize];
|
|
||||||
trace!("Value {}", val);
|
|
||||||
Ok(val)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
trace!("page not mapped");
|
|
||||||
Err(RuntimeErrors::PageNotMapped(page))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn read_addr64(&mut self, address: u64) -> u64 {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_addr8(&mut self, address: u64, value: u8) -> Result<(), RuntimeErrors> {
|
|
||||||
let (page, offset) = addr_to_page(address);
|
|
||||||
let ret: Option<(&u64, &mut Page)> = self.inner.get_key_value_mut(&page);
|
|
||||||
match ret {
|
|
||||||
Some((_, page)) => {
|
|
||||||
page.data()[offset as usize] = value;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let mut pg = VMPage::default();
|
|
||||||
pg.data[offset as usize] = value;
|
|
||||||
self.inner.insert(page, Page::VMPage(pg));
|
|
||||||
trace!("Mapped page {}", page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn set_addr64(&mut self, address: u64, value: u64) -> Result<(), RuntimeErrors> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn addr_to_page(addr: u64) -> (u64, u64) {
|
|
||||||
(addr / 8192, addr % 8192)
|
|
||||||
}
|
|
95
hbvm/src/vm/mod.rs
Normal file
95
hbvm/src/vm/mod.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
mod value;
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::bytecode::{OpParam, ParamRI, ParamRR, ParamRRR},
|
||||||
|
core::ops,
|
||||||
|
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();
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Vm<'a> {
|
||||||
|
pub registers: [Value; 60],
|
||||||
|
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],
|
||||||
|
pc: 0,
|
||||||
|
program,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) {
|
||||||
|
use crate::bytecode::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),
|
||||||
|
LI => {
|
||||||
|
let param = param!(self, ParamRI);
|
||||||
|
*self.reg_mut(param.0) = param.1.into();
|
||||||
|
}
|
||||||
|
// TODO: LD, ST
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,31 +3,6 @@ use core::{
|
||||||
ops::{Index, IndexMut},
|
ops::{Index, IndexMut},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct Registers([Value; 60]);
|
|
||||||
|
|
||||||
impl Index<u8> for Registers {
|
|
||||||
type Output = Value;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: u8) -> &Self::Output {
|
|
||||||
&self.0[index as usize]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexMut<u8> for Registers {
|
|
||||||
#[inline]
|
|
||||||
fn index_mut(&mut self, index: u8) -> &mut Self::Output {
|
|
||||||
&mut self.0[index as usize]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Registers {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self([Value { i: 0 }; 60])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// The macro invoker shall make sure that byte reinterpret-cast
|
/// The macro invoker shall make sure that byte reinterpret-cast
|
||||||
/// won't cause undefined behaviour.
|
/// won't cause undefined behaviour.
|
Loading…
Reference in a new issue