holey-bytes/vm/src/lib.rs

171 lines
4.4 KiB
Rust
Raw Normal View History

2023-07-25 18:11:21 -05:00
//! HoleyBytes Virtual Machine
//!
//! # Alloc feature
//! - Enabled by default
2023-08-07 20:03:15 -05:00
//! - Provides mapping / unmapping, as well as [`Default`] and [`Drop`]
//! implementations for soft-paged memory implementation
2023-07-25 18:11:21 -05:00
// # General safety notice:
// - Validation has to assure there is 256 registers (r0 - r255)
// - Instructions have to be valid as specified (values and sizes)
// - Mapped pages should be at least 4 KiB
2024-06-23 06:55:48 -05:00
#![no_std]
2023-07-26 13:54:24 -05:00
#![cfg_attr(feature = "nightly", feature(fn_align))]
2023-11-15 12:03:56 -06:00
#![deny(unsafe_op_in_unsafe_fn)]
2023-07-26 13:54:24 -05:00
2023-07-25 18:11:21 -05:00
#[cfg(feature = "alloc")]
2023-04-22 16:06:33 -05:00
extern crate alloc;
2023-08-15 09:32:59 -05:00
pub mod mem;
2023-07-25 18:11:21 -05:00
pub mod value;
2023-08-07 20:03:15 -05:00
mod bmc;
mod float;
2023-08-19 16:46:47 -05:00
mod utils;
2023-09-26 16:36:27 -05:00
mod vmrun;
2023-08-07 20:03:15 -05:00
pub use float::FL_ARCH_SPECIFIC_SUPPORTED;
2023-11-03 03:01:26 -05:00
use {
bmc::BlockCopier,
mem::{Address, Memory},
value::{Value, ValueVariant},
};
2023-07-25 18:11:21 -05:00
/// HoleyBytes Virtual Machine
2023-08-08 19:33:03 -05:00
pub struct Vm<Mem, const TIMER_QUOTIENT: usize> {
2023-07-25 18:11:21 -05:00
/// Holds 256 registers
///
/// Writing to register 0 is considered idk behaviour
2023-07-25 18:11:21 -05:00
/// in terms of HoleyBytes program execution
pub registers: [Value; 256],
/// Memory implementation
2023-08-07 20:03:15 -05:00
pub memory: Mem,
2023-07-25 18:11:21 -05:00
/// Program counter
pub pc: Address,
2023-07-25 18:11:21 -05:00
/// Program timer
timer: usize,
2023-07-25 19:04:26 -05:00
/// Saved block copier
copier: Option<BlockCopier>,
2023-07-25 18:11:21 -05:00
}
impl<Mem: Default, const TIMER_QUOTIENT: usize> Default for Vm<Mem, TIMER_QUOTIENT> {
fn default() -> Self {
Self {
registers: [Value::from(0_u64); 256],
2024-07-08 00:22:53 -05:00
memory: Mem::default(),
pc: Address::default(),
timer: 0,
copier: None,
}
}
}
2023-08-08 19:33:03 -05:00
impl<Mem, const TIMER_QUOTIENT: usize> Vm<Mem, TIMER_QUOTIENT>
2023-08-07 19:48:47 -05:00
where
2023-08-07 20:03:15 -05:00
Mem: Memory,
2023-07-25 18:11:21 -05:00
{
/// Create a new VM with program and trap handler
///
/// # Safety
/// Program code has to be validated
pub unsafe fn new(memory: Mem, entry: Address) -> Self {
2024-07-08 00:22:53 -05:00
Self { registers: [Value::from(0_u64); 256], memory, pc: entry, timer: 0, copier: None }
2023-07-25 18:11:21 -05:00
}
2023-11-02 10:53:44 -05:00
/// Read register
#[inline(always)]
pub fn read_reg(&self, n: u8) -> Value {
unsafe { *self.registers.get_unchecked(n as usize) }
}
/// Write a register.
/// Writing to register 0 is no-op.
#[inline(always)]
pub fn write_reg<T: ValueVariant>(&mut self, n: u8, value: T) {
if n != 0 {
unsafe {
core::ptr::copy_nonoverlapping(
(&value as *const T).cast::<u8>(),
self.registers.as_mut_ptr().add(n.into()).cast::<u8>(),
core::mem::size_of::<T>(),
);
};
}
}
2023-07-25 18:11:21 -05:00
}
/// Virtual machine halt error
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum VmRunError {
/// Tried to execute invalid instruction
InvalidOpcode(u8),
/// Unhandled load access exception
LoadAccessEx(Address),
2023-07-25 18:11:21 -05:00
2023-08-08 19:33:03 -05:00
/// Unhandled instruction load access exception
ProgramFetchLoadEx(Address),
2023-08-08 19:33:03 -05:00
2023-07-25 18:11:21 -05:00
/// Unhandled store access exception
StoreAccessEx(Address),
2023-07-25 18:11:21 -05:00
/// Register out-of-bounds access
RegOutOfBounds,
2023-07-25 19:28:14 -05:00
/// Address out-of-bounds
AddrOutOfBounds,
2023-07-25 18:11:21 -05:00
/// Reached unreachable code
Unreachable,
/// Invalid operand
InvalidOperand,
2024-10-12 06:07:49 -05:00
}
2024-10-12 06:07:49 -05:00
impl core::fmt::Display for VmRunError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
VmRunError::InvalidOpcode(op) => {
f.write_str("invalid op code: ").and_then(|_| op.fmt(f))
}
VmRunError::LoadAccessEx(address) => {
f.write_str("falied to load at ").and_then(|_| address.fmt(f))
}
VmRunError::ProgramFetchLoadEx(address) => {
f.write_str("falied to load instruction at ").and_then(|_| address.fmt(f))
}
VmRunError::StoreAccessEx(address) => {
f.write_str("falied to store at ").and_then(|_| address.fmt(f))
}
VmRunError::RegOutOfBounds => f.write_str("reg out of bounds"),
VmRunError::AddrOutOfBounds => f.write_str("addr out-of-bounds"),
VmRunError::Unreachable => f.write_str("unreachable"),
VmRunError::InvalidOperand => f.write_str("invalud operand"),
}
}
2023-07-25 18:11:21 -05:00
}
2024-10-12 06:07:49 -05:00
impl core::error::Error for VmRunError {}
2023-07-25 18:11:21 -05:00
/// Virtual machine halt ok
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum VmRunOk {
/// Program has eached its end
End,
/// Program was interrupted by a timer
Timer,
/// Environment call
Ecall,
2023-09-30 18:51:51 -05:00
/// Breakpoint
Breakpoint,
2023-07-25 18:11:21 -05:00
}