forked from AbleOS/holey-bytes
Valider is now generated from macro (not done yet)
This commit is contained in:
parent
92793dc93b
commit
759514686a
|
@ -19,25 +19,7 @@ pub struct Assembler {
|
||||||
|
|
||||||
|
|
||||||
// Implement both assembler and generate module for text-code-based one
|
// Implement both assembler and generate module for text-code-based one
|
||||||
macros::impl_both!(
|
hbbytecode::invoke_with_def!(macros::impl_all);
|
||||||
bbbb(p0: R, p1: R, p2: R, p3: R)
|
|
||||||
=> [DIR, DIRF, FMAF],
|
|
||||||
bbb(p0: R, p1: R, p2: R)
|
|
||||||
=> [ADD, SUB, MUL, AND, OR, XOR, SL, SR, SRS, CMP, CMPU, /*BRC,*/ ADDF, SUBF, MULF],
|
|
||||||
bbdh(p0: R, p1: R, p2: I, p3: L)
|
|
||||||
=> [LD, ST],
|
|
||||||
bbd(p0: R, p1: R, p2: I)
|
|
||||||
=> [ADDI, MULI, ANDI, ORI, XORI, CMPI, CMPUI, BMC, JAL, JEQ, JNE, JLT, JGT, JLTU,
|
|
||||||
JGTU, ADDFI, MULFI],
|
|
||||||
bbw(p0: R, p1: R, p2: u32)
|
|
||||||
=> [SLI, SRI, SRSI],
|
|
||||||
bb(p0: R, p1: R)
|
|
||||||
=> [NEG, NOT, CP, SWA, NEGF, ITF, FTI],
|
|
||||||
bd(p0: R, p1: I)
|
|
||||||
=> [LI],
|
|
||||||
n()
|
|
||||||
=> [UN, NOP, ECALL],
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Assembler {
|
impl Assembler {
|
||||||
// Special-cased for text-assembler
|
// Special-cased for text-assembler
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//! And here the land of macros begin.
|
//! And here the land of macros begin.
|
||||||
//!
|
//!
|
||||||
//! They do not bite, really. Have you seen what Yandros is writing?
|
//! They do not bite, really. Have you seen what Yandros is writing?
|
||||||
|
|
||||||
pub mod asm;
|
pub mod asm;
|
||||||
|
@ -8,16 +8,16 @@ pub mod text;
|
||||||
#[allow(rustdoc::invalid_rust_codeblocks)]
|
#[allow(rustdoc::invalid_rust_codeblocks)]
|
||||||
/// Generate code for both programmatic-interface assembler and
|
/// Generate code for both programmatic-interface assembler and
|
||||||
/// textural interface.
|
/// textural interface.
|
||||||
///
|
///
|
||||||
/// Some people claim:
|
/// Some people claim:
|
||||||
/// > Write programs to handle text streams, because that is a universal interface.
|
/// > Write programs to handle text streams, because that is a universal interface.
|
||||||
///
|
///
|
||||||
/// We at AbleCorp believe that nice programatic API is nicer than piping some text
|
/// We at AbleCorp believe that nice programatic API is nicer than piping some text
|
||||||
/// into a program. It's less error-prone and faster.
|
/// into a program. It's less error-prone and faster.
|
||||||
///
|
///
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// impl_both!(
|
/// impl_all!(
|
||||||
/// INSTRUCTION_TYPE(p0: TYPE, p1: TYPE, …)
|
/// INSTRUCTION_TYPE(p0: TYPE, p1: TYPE, …)
|
||||||
/// => [INSTRUCTION_A, INSTRUCTION_B, …],
|
/// => [INSTRUCTION_A, INSTRUCTION_B, …],
|
||||||
/// …
|
/// …
|
||||||
|
@ -30,7 +30,7 @@ pub mod text;
|
||||||
/// - R: Register (u8)
|
/// - R: Register (u8)
|
||||||
/// - I: Immediate (implements [`crate::Imm`] trait)
|
/// - I: Immediate (implements [`crate::Imm`] trait)
|
||||||
/// - Other types are identity-mapped
|
/// - Other types are identity-mapped
|
||||||
///
|
///
|
||||||
/// # Text assembler
|
/// # Text assembler
|
||||||
/// Text assembler generated simply calls methods in the [`crate::Assembler`] type.
|
/// Text assembler generated simply calls methods in the [`crate::Assembler`] type.
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
|
@ -45,7 +45,7 @@ pub mod text;
|
||||||
/// - Labels are defined by their names followed by colon `label:`
|
/// - Labels are defined by their names followed by colon `label:`
|
||||||
/// - Labels are referenced simply by their names
|
/// - Labels are referenced simply by their names
|
||||||
/// - Immediates are numbers, can be negative, floats are not yet supported
|
/// - Immediates are numbers, can be negative, floats are not yet supported
|
||||||
macro_rules! impl_both {
|
macro_rules! impl_all {
|
||||||
($($tt:tt)*) => {
|
($($tt:tt)*) => {
|
||||||
impl Assembler {
|
impl Assembler {
|
||||||
$crate::macros::asm::impl_asm!($($tt)*);
|
$crate::macros::asm::impl_asm!($($tt)*);
|
||||||
|
@ -55,4 +55,4 @@ macro_rules! impl_both {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use impl_both;
|
pub(crate) use impl_all;
|
||||||
|
|
158
hbbytecode/src/gen_valider.rs
Normal file
158
hbbytecode/src/gen_valider.rs
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
//! Generate HoleyBytes code validator
|
||||||
|
|
||||||
|
macro_rules! gen_valider {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
$ityn:ident
|
||||||
|
($($param_i:ident: $param_ty:ident),* $(,)?)
|
||||||
|
=> [$($opcode:ident),* $(,)?],
|
||||||
|
)*
|
||||||
|
) => {
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
pub mod valider {
|
||||||
|
//! Validate if program is sound to execute
|
||||||
|
|
||||||
|
/// Program validation error kind
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
/// Unknown opcode
|
||||||
|
InvalidInstruction,
|
||||||
|
/// VM doesn't implement this valid opcode
|
||||||
|
Unimplemented,
|
||||||
|
/// Attempted to copy over register boundary
|
||||||
|
RegisterArrayOverflow,
|
||||||
|
/// Program is not validly terminated
|
||||||
|
InvalidEnd,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Error {
|
||||||
|
/// Kind
|
||||||
|
pub kind: ErrorKind,
|
||||||
|
/// Location in bytecode
|
||||||
|
pub index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform bytecode validation. If it passes, the program should be
|
||||||
|
/// sound to execute.
|
||||||
|
pub fn validate(mut program: &[u8]) -> Result<(), Error> {
|
||||||
|
// Program has to end with 12 zeroes, if there is less than
|
||||||
|
// 12 bytes, program is invalid.
|
||||||
|
if program.len() < 12 {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::InvalidEnd,
|
||||||
|
index: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that program ends with 12 zeroes
|
||||||
|
for (index, item) in program.iter().enumerate().skip(program.len() - 12) {
|
||||||
|
if *item != 0 {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::InvalidEnd,
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = program;
|
||||||
|
loop {
|
||||||
|
use crate::opcode::*;
|
||||||
|
program = match program {
|
||||||
|
// End of program
|
||||||
|
[] => return Ok(()),
|
||||||
|
|
||||||
|
// Memory load/store cannot go out-of-bounds register array
|
||||||
|
[LD..=ST, reg, _, _, _, _, _, _, _, _, count_0, count_1, ..]
|
||||||
|
if usize::from(*reg) * 8
|
||||||
|
+ usize::from(u16::from_le_bytes([*count_1, *count_0]))
|
||||||
|
> 2048 =>
|
||||||
|
{
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::RegisterArrayOverflow,
|
||||||
|
index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block register copy cannot go out-of-bounds register array
|
||||||
|
[BRC, src, dst, count, ..]
|
||||||
|
if src.checked_add(*count).is_none()
|
||||||
|
|| dst.checked_add(*count).is_none() =>
|
||||||
|
{
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::RegisterArrayOverflow,
|
||||||
|
index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
$crate::gen_valider::inst_chk!(
|
||||||
|
rest, $ityn, $($opcode),*
|
||||||
|
)
|
||||||
|
)|* => rest,
|
||||||
|
|
||||||
|
// The plebs
|
||||||
|
_ => {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::InvalidInstruction,
|
||||||
|
index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate instruction check pattern
|
||||||
|
macro_rules! inst_chk {
|
||||||
|
// Sadly this has hardcoded instruction types,
|
||||||
|
// as I cannot generate parts of patterns+
|
||||||
|
|
||||||
|
($rest:ident, bbbb, $($opcode:ident),*) => {
|
||||||
|
// B B B B
|
||||||
|
[$($opcode)|*, _, _, _, _, $rest @ ..]
|
||||||
|
};
|
||||||
|
|
||||||
|
($rest:ident, bbb, $($opcode:ident),*) => {
|
||||||
|
// B B B
|
||||||
|
[$($opcode)|*, _, _, _, $rest @ ..]
|
||||||
|
};
|
||||||
|
|
||||||
|
($rest:ident, bbdh, $($opcode:ident),*) => {
|
||||||
|
// B B D1 D2 D3 D4 D5 D6 D7 D8 H1 H2
|
||||||
|
[$($opcode)|*, _, _, _, _, _, _, _, _, _, _, _, _, $rest @ ..]
|
||||||
|
};
|
||||||
|
|
||||||
|
($rest:ident, bbd, $($opcode:ident),*) => {
|
||||||
|
// B B D1 D2 D3 D4 D5 D6 D7 D8
|
||||||
|
[$($opcode)|*, _, _, _, _, _, _, _, _, _, _, $rest @ ..]
|
||||||
|
};
|
||||||
|
|
||||||
|
($rest:ident, bbw, $($opcode:ident),*) => {
|
||||||
|
// B B W1 W2 W3 W4
|
||||||
|
[$($opcode)|*, _, _, _, _, _, _, $rest @ ..]
|
||||||
|
};
|
||||||
|
|
||||||
|
($rest:ident, bb, $($opcode:ident),*) => {
|
||||||
|
// B B
|
||||||
|
[$($opcode)|*, _, _, $rest @ ..]
|
||||||
|
};
|
||||||
|
|
||||||
|
($rest:ident, bd, $($opcode:ident),*) => {
|
||||||
|
// B D1 D2 D3 D4 D5 D6 D7 D8
|
||||||
|
[$($opcode)|*, _, _, _, _, _, _, _, _, _, $rest @ ..]
|
||||||
|
};
|
||||||
|
|
||||||
|
($rest:ident, n, $($opcode:ident),*) => {
|
||||||
|
[$($opcode)|*, $rest @ ..]
|
||||||
|
};
|
||||||
|
|
||||||
|
($_0:ident, $($_1:ident),*) => {
|
||||||
|
compile_error!("Invalid instruction type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use {gen_valider, inst_chk};
|
|
@ -1,5 +1,7 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
mod gen_valider;
|
||||||
|
|
||||||
macro_rules! constmod {
|
macro_rules! constmod {
|
||||||
($vis:vis $mname:ident($repr:ty) {
|
($vis:vis $mname:ident($repr:ty) {
|
||||||
$(#![doc = $mdoc:literal])?
|
$(#![doc = $mdoc:literal])?
|
||||||
|
@ -15,6 +17,34 @@ macro_rules! constmod {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Invoke macro with bytecode definition format
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! invoke_with_def {
|
||||||
|
($macro:path) => {
|
||||||
|
$macro!(
|
||||||
|
bbbb(p0: R, p1: R, p2: R, p3: R)
|
||||||
|
=> [DIR, DIRF, FMAF],
|
||||||
|
bbb(p0: R, p1: R, p2: R)
|
||||||
|
=> [ADD, SUB, MUL, AND, OR, XOR, SL, SR, SRS, CMP, CMPU, /*BRC,*/ ADDF, SUBF, MULF],
|
||||||
|
bbdh(p0: R, p1: R, p2: I, p3: L)
|
||||||
|
=> [LD, ST],
|
||||||
|
bbd(p0: R, p1: R, p2: I)
|
||||||
|
=> [ADDI, MULI, ANDI, ORI, XORI, CMPI, CMPUI, BMC, JAL, JEQ, JNE, JLT, JGT, JLTU,
|
||||||
|
JGTU, ADDFI, MULFI],
|
||||||
|
bbw(p0: R, p1: R, p2: u32)
|
||||||
|
=> [SLI, SRI, SRSI],
|
||||||
|
bb(p0: R, p1: R)
|
||||||
|
=> [NEG, NOT, CP, SWA, NEGF, ITF, FTI],
|
||||||
|
bd(p0: R, p1: I)
|
||||||
|
=> [LI],
|
||||||
|
n()
|
||||||
|
=> [UN, NOP, ECALL],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke_with_def!(gen_valider::gen_valider);
|
||||||
|
|
||||||
constmod!(pub opcode(u8) {
|
constmod!(pub opcode(u8) {
|
||||||
//! Opcode constant module
|
//! Opcode constant module
|
||||||
|
|
||||||
|
|
|
@ -3,5 +3,4 @@
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub mod validate;
|
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use hbvm::vm::mem::{HandlePageFault, Memory, MemoryAccessReason, PageSize};
|
use hbvm::vm::mem::{HandlePageFault, Memory, MemoryAccessReason, PageSize};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
hbvm::{validate::validate, vm::Vm},
|
hbbytecode::valider::validate,
|
||||||
|
hbvm::vm::Vm,
|
||||||
std::io::{stdin, Read},
|
std::io::{stdin, Read},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
//! Validate if program is sound to execute
|
|
||||||
|
|
||||||
/// Program validation error kind
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum ErrorKind {
|
|
||||||
/// Unknown opcode
|
|
||||||
InvalidInstruction,
|
|
||||||
/// VM doesn't implement this valid opcode
|
|
||||||
Unimplemented,
|
|
||||||
/// Attempted to copy over register boundary
|
|
||||||
RegisterArrayOverflow,
|
|
||||||
/// Program is not validly terminated
|
|
||||||
InvalidEnd,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Error {
|
|
||||||
/// Kind
|
|
||||||
pub kind: ErrorKind,
|
|
||||||
/// Location in bytecode
|
|
||||||
pub index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform bytecode validation. If it passes, the program should be
|
|
||||||
/// sound to execute.
|
|
||||||
pub fn validate(mut program: &[u8]) -> Result<(), Error> {
|
|
||||||
// Program has to end with 12 zeroes, if there is less than
|
|
||||||
// 12 bytes, program is invalid.
|
|
||||||
if program.len() < 12 {
|
|
||||||
return Err(Error {
|
|
||||||
kind: ErrorKind::InvalidEnd,
|
|
||||||
index: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that program ends with 12 zeroes
|
|
||||||
for (index, item) in program.iter().enumerate().skip(program.len() - 12) {
|
|
||||||
if *item != 0 {
|
|
||||||
return Err(Error {
|
|
||||||
kind: ErrorKind::InvalidEnd,
|
|
||||||
index,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let start = program;
|
|
||||||
loop {
|
|
||||||
use hbbytecode::opcode::*;
|
|
||||||
// Match on instruction types and perform necessary checks
|
|
||||||
program = match program {
|
|
||||||
// End of program
|
|
||||||
[] => return Ok(()),
|
|
||||||
|
|
||||||
// Memory load/store cannot go out-of-bounds register array
|
|
||||||
[LD..=ST, reg, _, _, _, _, _, _, _, _, count_0, count_1, ..]
|
|
||||||
if usize::from(*reg) * 8
|
|
||||||
+ usize::from(u16::from_le_bytes([*count_1, *count_0]))
|
|
||||||
> 2048 =>
|
|
||||||
{
|
|
||||||
return Err(Error {
|
|
||||||
kind: ErrorKind::RegisterArrayOverflow,
|
|
||||||
index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block register copy cannot go out-of-bounds register array
|
|
||||||
[BRC, src, dst, count, ..]
|
|
||||||
if src.checked_add(*count).is_none() || dst.checked_add(*count).is_none() =>
|
|
||||||
{
|
|
||||||
return Err(Error {
|
|
||||||
kind: ErrorKind::RegisterArrayOverflow,
|
|
||||||
index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid instructions
|
|
||||||
[DIR | DIRF | FMAF, _, _, _, _, rest @ ..] // BBBB
|
|
||||||
| [ADD | SUB | MUL | AND | OR | XOR | SL | SR | SRS | CMP | CMPU | BRC | ADDF | SUBF | MULF, _, _, _, rest @ ..]
|
|
||||||
| [LD | ST, _, _, _, _, _, _, _, _, _, _, _, rest @ ..] // BBDH
|
|
||||||
| [
|
|
||||||
ADDI | MULI | ANDI | ORI | XORI | CMPI | CMPUI | BMC | JAL | JEQ | JNE | JLT | JGT | JLTU | JGTU | ADDFI | MULFI, _, _, _, _, _, _, _, _, _, _, rest @ ..] // BBD
|
|
||||||
| [SLI | SRI | SRSI, _, _, _, _, _, _, rest @ ..] // BBW
|
|
||||||
| [NEG | NOT | CP | SWA | NEGF | ITF | FTI, _, _, rest @ ..] // BB
|
|
||||||
| [LI, _, _, _, _, _, _, _, _, _, rest @ ..] // BD
|
|
||||||
| [UN | NOP | ECALL, rest @ ..] // N
|
|
||||||
=> rest,
|
|
||||||
|
|
||||||
// The rest
|
|
||||||
_ => {
|
|
||||||
return Err(Error {
|
|
||||||
kind: ErrorKind::InvalidInstruction,
|
|
||||||
index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,9 +12,10 @@ pub mod value;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
self::{mem::HandlePageFault, value::ValueVariant},
|
self::{mem::HandlePageFault, value::ValueVariant},
|
||||||
crate::validate,
|
|
||||||
core::{cmp::Ordering, ops},
|
core::{cmp::Ordering, ops},
|
||||||
hbbytecode::{OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD},
|
hbbytecode::{
|
||||||
|
valider, OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD,
|
||||||
|
},
|
||||||
mem::Memory,
|
mem::Memory,
|
||||||
value::Value,
|
value::Value,
|
||||||
};
|
};
|
||||||
|
@ -66,8 +67,8 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new VM with program and trap handler only if it passes validation
|
/// Create a new VM with program and trap handler only if it passes validation
|
||||||
pub fn new_validated(program: &'a [u8], traph: PfHandler) -> Result<Self, validate::Error> {
|
pub fn new_validated(program: &'a [u8], traph: PfHandler) -> Result<Self, valider::Error> {
|
||||||
validate::validate(program)?;
|
valider::validate(program)?;
|
||||||
Ok(unsafe { Self::new_unchecked(program, traph) })
|
Ok(unsafe { Self::new_unchecked(program, traph) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +250,7 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
BRC => {
|
BRC => {
|
||||||
// Block register copy
|
// Block register copy
|
||||||
let ParamBBB(src, dst, count) = self.decode();
|
let ParamBBB(src, dst, count) = self.decode();
|
||||||
|
extern crate std;
|
||||||
core::ptr::copy(
|
core::ptr::copy(
|
||||||
self.registers.get_unchecked(usize::from(src)),
|
self.registers.get_unchecked(usize::from(src)),
|
||||||
self.registers.get_unchecked_mut(usize::from(dst)),
|
self.registers.get_unchecked_mut(usize::from(dst)),
|
||||||
|
|
Loading…
Reference in a new issue