holey-bytes/hbasm/src/lib.rs

105 lines
3.1 KiB
Rust
Raw Normal View History

2023-07-25 22:12:50 +00:00
//! Holey Bytes Assembler
2023-08-01 20:13:22 +00:00
//!
2023-07-25 22:12:50 +00:00
//! Some people claim:
//! > 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
//! into a program. It's less error-prone and faster.
2023-08-01 20:13:22 +00:00
//!
2023-07-25 22:12:50 +00:00
//! So this crate contains both assembleer with API for programs and a text assembler
//! for humans to write
2023-06-26 10:18:14 +00:00
#![no_std]
extern crate alloc;
2023-07-10 21:18:23 +00:00
mod macros;
2023-08-01 20:13:22 +00:00
use {
alloc::{vec, vec::Vec},
hashbrown::HashSet,
};
2023-07-22 00:26:03 +00:00
/// Assembler
2023-08-01 20:13:22 +00:00
///
2023-07-22 00:26:03 +00:00
/// - Opcode-generic, instruction-type-specific methods are named `i_param_<type>`
/// - You likely won't need to use them, but they are here, just in case :)
/// - Instruction-specific methods are named `i_<instruction>`
2023-07-10 21:18:23 +00:00
pub struct Assembler {
pub buf: Vec<u8>,
2023-07-12 00:16:23 +00:00
pub sub: HashSet<usize>,
}
2023-08-01 20:13:22 +00:00
impl Default for Assembler {
fn default() -> Self {
Self {
buf: vec![0; 3],
sub: Default::default(),
}
}
}
2023-07-25 22:12:50 +00:00
hbbytecode::invoke_with_def!(macros::text::gen_text);
2023-07-12 00:23:47 +00:00
impl Assembler {
2023-07-25 22:12:50 +00:00
hbbytecode::invoke_with_def!(macros::asm::impl_asm);
2023-07-13 09:05:41 +00:00
2023-08-01 20:13:22 +00:00
/// Append 12 zeroes (UN) at the end and add magic to the begining
///
2023-07-22 00:34:41 +00:00
/// # HoleyBytes lore
2023-07-22 00:28:05 +00:00
///
/// In reference HBVM implementation checks are done in
/// a separate phase before execution.
///
/// This way execution will be much faster as they have to
/// be done only once.
///
/// There was an issue. You cannot statically check register values and
/// `JAL` instruction could hop at the end of program to some byte, which
2023-07-22 00:29:05 +00:00
/// will be interpreted as some valid opcode and VM in attempt to decode
/// the instruction performed out-of-bounds read which leads to undefined behaviour.
2023-07-22 00:28:05 +00:00
///
/// Several options were considered to overcome this, but inserting some data at
/// program's end which when executed would lead to undesired behaviour, though
/// not undefined behaviour.
///
/// Newly created `UN` (as UNreachable) was chosen as
/// - It was a good idea to add some equivalent to `ud2` anyways
/// - It was chosen to be zero
/// - What if you somehow reached that code, it will appropriately bail :)
/// - (yes, originally `NOP` was considered)
///
/// Why 12 bytes? That's the size of largest instruction parameter part.
2023-07-13 09:05:41 +00:00
pub fn finalise(&mut self) {
self.buf.extend([0; 12]);
2023-08-01 20:20:11 +00:00
self.buf[0..4].copy_from_slice(&0xAB1E0B_u32.to_le_bytes());
2023-07-13 09:05:41 +00:00
}
2023-07-12 00:23:47 +00:00
}
2023-07-22 00:26:03 +00:00
/// Immediate value
2023-08-01 20:13:22 +00:00
///
2023-07-22 00:26:03 +00:00
/// # Implementor notice
/// It should insert exactly 8 bytes, otherwise output will be malformed.
/// This is not checked in any way
2023-07-10 21:18:23 +00:00
pub trait Imm {
2023-07-22 00:26:03 +00:00
/// Insert immediate value
2023-07-11 00:08:55 +00:00
fn insert(&self, asm: &mut Assembler);
}
2023-07-22 00:26:03 +00:00
/// Implement immediate values
2023-07-11 00:08:55 +00:00
macro_rules! impl_imm_le_bytes {
($($ty:ty),* $(,)?) => {
$(
impl Imm for $ty {
#[inline(always)]
fn insert(&self, asm: &mut Assembler) {
2023-07-22 00:26:03 +00:00
// Convert to little-endian bytes, insert.
2023-07-11 00:08:55 +00:00
asm.buf.extend(self.to_le_bytes());
}
}
)*
};
}
2023-07-11 00:08:55 +00:00
impl_imm_le_bytes!(u64, i64, f64);