diff --git a/hbvm/Cargo.toml b/hbvm/Cargo.toml index be2d47a..bbb2a26 100644 --- a/hbvm/Cargo.toml +++ b/hbvm/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" lto = true [dependencies] -log = "*" -hashbrown = "0.13.2" +hashbrown = "0.13" +hbbytecode.path = "../hbbytecode" +log = "0.4" static_assertions = "1.0" -hbbytecode = { path = "../hbbytecode" } diff --git a/hbvm/src/main.rs b/hbvm/src/main.rs index 5ee03af..b484a9c 100644 --- a/hbvm/src/main.rs +++ b/hbvm/src/main.rs @@ -1,3 +1,4 @@ +use hbbytecode::opcode; use hbvm::{validate::validate, vm::Vm, RuntimeErrors}; fn main() -> Result<(), RuntimeErrors> { @@ -11,6 +12,7 @@ fn main() -> Result<(), RuntimeErrors> { } else { unsafe { let mut vm = Vm::new_unchecked(prog); + vm.memory.insert_test_page(); vm.run(); } } diff --git a/hbvm/src/validate.rs b/hbvm/src/validate.rs index 086a4dc..d2de30b 100644 --- a/hbvm/src/validate.rs +++ b/hbvm/src/validate.rs @@ -43,25 +43,31 @@ pub fn validate(mut program: &[u8]) -> Result<(), Error> { // RRR [ADD..=XOR | ADDF..=DIVF, _, _, _, rest @ ..] => { if let Some(n) = reg(&program[1..=3]) { - bail!(InvalidRegister, start, program, n + 1) + 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) + bail!(InvalidRegister, start, program, n + 1); } rest } // RI [LI, reg, _, _, _, _, _, _, _, _, rest @ ..] => { if *reg > 59 { - bail!(InvalidRegister, start, program, 1) + bail!(InvalidRegister, start, program, 1); } rest } - [LD..=SO, ..] => bail!(Unimplemented, start, program), + // RRI + [LD..=SO, _, _, _, _, _, _, _, _, _, _, rest @ ..] => { + if let Some(n) = reg(&program[1..=2]) { + bail!(InvalidRegister, start, program, n + 1); + } + rest + }, _ => bail!(InvalidInstruction, start, program), } } diff --git a/hbvm/src/vm/mem.rs b/hbvm/src/vm/mem.rs new file mode 100644 index 0000000..6ef2ef4 --- /dev/null +++ b/hbvm/src/vm/mem.rs @@ -0,0 +1,95 @@ +// HACK: This is temporary implementation so we can have memory instructions working + +use { + crate::vm::value::Value, alloc::boxed::Box, core::mem::MaybeUninit, hashbrown::HashMap, + ma_size::MemAccessSize, +}; + +pub const PAGE_SIZE: u64 = 8192; + +#[derive(Clone, Debug, Default)] +pub struct Memory { + pages: HashMap>, +} + +impl Memory { + // HACK: Just for allocation testing, will be removed when proper memory interfaces + // implemented. + pub fn insert_test_page(&mut self) { + self.pages.insert(0, unsafe { + use alloc::alloc::{alloc_zeroed, Layout, handle_alloc_error}; + let layout = Layout::new::<[u8; PAGE_SIZE as usize]>(); + let ptr = alloc_zeroed(layout); + if ptr.is_null() { + handle_alloc_error(layout); + } + Box::from_raw(ptr.cast()) + }); + } + + pub fn load(&self, addr: u64) -> Option { + let (page, offset) = split_addr(addr); + if offset + u16::from(S::BYTES) <= (PAGE_SIZE as u16 - 1) { + let mut value = MaybeUninit::::zeroed(); + unsafe { + core::ptr::copy_nonoverlapping( + self.pages.get(&page)?.as_ptr().add(usize::from(offset)), + value.as_mut_ptr().cast(), + S::BYTES.into(), + ); + Some(value.assume_init()) + } + } else { + None + } + } + + pub fn store(&mut self, addr: u64, value: Value) -> Result<(), ()> { + let (page, offset) = split_addr(addr); + if offset + u16::from(S::BYTES) <= (PAGE_SIZE as u16 - 1) { + unsafe { + core::ptr::copy_nonoverlapping( + (&value as *const Value).cast::(), + self.pages + .get_mut(&page) + .ok_or(())? + .as_mut_ptr() + .add(usize::from(offset)), + S::BYTES.into(), + ) + }; + Ok(()) + } else { + Err(()) + } + } +} + +#[inline] +pub const fn split_addr(addr: u64) -> (u64, u16) { + (addr >> PAGE_SIZE.count_ones(), (addr & PAGE_SIZE) as u16) +} + +macro_rules! size_markers { + ($($name:ident = $size:expr),* $(,)?) => { + pub mod ma_size { + pub unsafe trait MemAccessSize { + const BYTES: u8; + } + + $( + pub struct $name; + unsafe impl MemAccessSize for $name { + const BYTES: u8 = $size; + } + )* + } + }; +} + +size_markers! { + Byte = 1, + Doublet = 2, + Quadlet = 4, + Octlet = 8, +} diff --git a/hbvm/src/vm/mod.rs b/hbvm/src/vm/mod.rs index 67654ac..69fe6b6 100644 --- a/hbvm/src/vm/mod.rs +++ b/hbvm/src/vm/mod.rs @@ -1,10 +1,12 @@ use crate::validate; +mod mem; mod value; use { core::ops, - hbbytecode::{OpParam, ParamRI, ParamRR, ParamRRR}, + hbbytecode::{OpParam, ParamRI, ParamRR, ParamRRI, ParamRRR}, + mem::{ma_size, Memory}, static_assertions::assert_impl_one, value::Value, }; @@ -28,8 +30,29 @@ macro_rules! binary_op { }}; } +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], } @@ -40,6 +63,7 @@ impl<'a> Vm<'a> { pub unsafe fn new_unchecked(program: &'a [u8]) -> Self { Self { registers: [Value::from(0); 60], + memory: Default::default(), pc: 0, program, } @@ -83,7 +107,14 @@ impl<'a> Vm<'a> { let param = param!(self, ParamRI); *self.reg_mut(param.0) = param.1.into(); } - // TODO: Loads and stores + 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; @@ -104,3 +135,10 @@ impl<'a> Vm<'a> { self.registers.get_unchecked_mut(n as usize) } } + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum Exception { + LoadAccess, + StoreAccess, +}