Memory access
This commit is contained in:
parent
0fd3aee6b5
commit
859e14daa6
|
@ -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" }
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
95
hbvm/src/vm/mem.rs
Normal 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,
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue