Memory access

wip/its-not-my-fault
ondra05 2023-06-08 00:25:38 +02:00
parent 16d9becbe2
commit e57fcdf89a
No known key found for this signature in database
GPG Key ID: 0DA6D2BB2285E881
5 changed files with 150 additions and 9 deletions

View File

@ -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" }

View File

@ -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();
}
}

View File

@ -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),
}
}

95
hbvm/src/vm/mem.rs Normal file
View File

@ -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<u64, Box<[u8; PAGE_SIZE as usize]>>,
}
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<S: MemAccessSize>(&self, addr: u64) -> Option<Value> {
let (page, offset) = split_addr(addr);
if offset + u16::from(S::BYTES) <= (PAGE_SIZE as u16 - 1) {
let mut value = MaybeUninit::<Value>::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<S: MemAccessSize>(&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::<u8>(),
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,
}

View File

@ -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::<u64>().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,
}