forked from AbleOS/holey-bytes
Memory access
This commit is contained in:
parent
0fd3aee6b5
commit
859e14daa6
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "*"
|
hashbrown = "0.13"
|
||||||
hashbrown = "0.13.2"
|
hbbytecode.path = "../hbbytecode"
|
||||||
|
log = "0.4"
|
||||||
static_assertions = "1.0"
|
static_assertions = "1.0"
|
||||||
hbbytecode = { path = "../hbbytecode" }
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use hbbytecode::opcode;
|
||||||
use hbvm::{validate::validate, vm::Vm, RuntimeErrors};
|
use hbvm::{validate::validate, vm::Vm, RuntimeErrors};
|
||||||
|
|
||||||
fn main() -> Result<(), RuntimeErrors> {
|
fn main() -> Result<(), RuntimeErrors> {
|
||||||
|
@ -11,6 +12,7 @@ fn main() -> Result<(), RuntimeErrors> {
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut vm = Vm::new_unchecked(prog);
|
let mut vm = Vm::new_unchecked(prog);
|
||||||
|
vm.memory.insert_test_page();
|
||||||
vm.run();
|
vm.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,25 +43,31 @@ pub fn validate(mut program: &[u8]) -> Result<(), Error> {
|
||||||
// RRR
|
// RRR
|
||||||
[ADD..=XOR | ADDF..=DIVF, _, _, _, rest @ ..] => {
|
[ADD..=XOR | ADDF..=DIVF, _, _, _, rest @ ..] => {
|
||||||
if let Some(n) = reg(&program[1..=3]) {
|
if let Some(n) = reg(&program[1..=3]) {
|
||||||
bail!(InvalidRegister, start, program, n + 1)
|
bail!(InvalidRegister, start, program, n + 1);
|
||||||
}
|
}
|
||||||
rest
|
rest
|
||||||
}
|
}
|
||||||
// RR
|
// RR
|
||||||
[NOT | CP, _, _, rest @ ..] => {
|
[NOT | CP, _, _, rest @ ..] => {
|
||||||
if let Some(n) = reg(&program[1..=2]) {
|
if let Some(n) = reg(&program[1..=2]) {
|
||||||
bail!(InvalidRegister, start, program, n + 1)
|
bail!(InvalidRegister, start, program, n + 1);
|
||||||
}
|
}
|
||||||
rest
|
rest
|
||||||
}
|
}
|
||||||
// RI
|
// RI
|
||||||
[LI, reg, _, _, _, _, _, _, _, _, rest @ ..] => {
|
[LI, reg, _, _, _, _, _, _, _, _, rest @ ..] => {
|
||||||
if *reg > 59 {
|
if *reg > 59 {
|
||||||
bail!(InvalidRegister, start, program, 1)
|
bail!(InvalidRegister, start, program, 1);
|
||||||
}
|
}
|
||||||
rest
|
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),
|
_ => 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;
|
use crate::validate;
|
||||||
|
|
||||||
|
mod mem;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
core::ops,
|
core::ops,
|
||||||
hbbytecode::{OpParam, ParamRI, ParamRR, ParamRRR},
|
hbbytecode::{OpParam, ParamRI, ParamRR, ParamRRI, ParamRRR},
|
||||||
|
mem::{ma_size, Memory},
|
||||||
static_assertions::assert_impl_one,
|
static_assertions::assert_impl_one,
|
||||||
value::Value,
|
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 struct Vm<'a> {
|
||||||
pub registers: [Value; 60],
|
pub registers: [Value; 60],
|
||||||
|
pub memory: Memory,
|
||||||
pc: usize,
|
pc: usize,
|
||||||
program: &'a [u8],
|
program: &'a [u8],
|
||||||
}
|
}
|
||||||
|
@ -40,6 +63,7 @@ impl<'a> Vm<'a> {
|
||||||
pub unsafe fn new_unchecked(program: &'a [u8]) -> Self {
|
pub unsafe fn new_unchecked(program: &'a [u8]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
registers: [Value::from(0); 60],
|
registers: [Value::from(0); 60],
|
||||||
|
memory: Default::default(),
|
||||||
pc: 0,
|
pc: 0,
|
||||||
program,
|
program,
|
||||||
}
|
}
|
||||||
|
@ -83,7 +107,14 @@ impl<'a> Vm<'a> {
|
||||||
let param = param!(self, ParamRI);
|
let param = param!(self, ParamRI);
|
||||||
*self.reg_mut(param.0) = param.1.into();
|
*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 => {
|
JMP => {
|
||||||
self.pc =
|
self.pc =
|
||||||
self.program.as_ptr().add(self.pc + 1).cast::<u64>().read() as usize;
|
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)
|
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