holey-bytes/hbasm/src/macros/asm.rs

90 lines
2.7 KiB
Rust
Raw Normal View History

2023-07-21 19:26:03 -05:00
//! Macros to generate [`crate::Assembler`]
/// Incremental token-tree muncher to implement specific instruction
/// functions based on generic function for instruction type
2023-07-10 16:18:23 -05:00
macro_rules! impl_asm_opcodes {
2023-07-21 19:26:03 -05:00
( // End case
2023-07-10 16:18:23 -05:00
$generic:ident
($($param_i:ident: $param_ty:ty),*)
=> []
) => {};
(
$generic:ident
($($param_i:ident: $param_ty:ty),*)
=> [$opcode:ident, $($rest:tt)*]
) => {
2023-07-21 19:26:03 -05:00
// Instruction-specific function
2023-07-10 16:18:23 -05:00
paste::paste! {
#[inline(always)]
pub fn [<i_ $opcode:lower>](&mut self, $($param_i: $param_ty),*) {
self.$generic(hbbytecode::opcode::$opcode, $($param_i),*)
}
}
2023-07-21 19:26:03 -05:00
// And recurse!
2023-07-11 19:16:23 -05:00
macros::asm::impl_asm_opcodes!(
2023-07-10 16:18:23 -05:00
$generic($($param_i: $param_ty),*)
=> [$($rest)*]
);
};
}
2023-07-21 19:26:03 -05:00
/// Numeric value insert
2023-07-11 19:16:23 -05:00
macro_rules! impl_asm_insert {
2023-07-21 19:26:03 -05:00
// Immediate - this is trait-based,
// the insertion is delegated to its implementation
2023-07-11 19:16:23 -05:00
($self:expr, $id:ident, I) => {
Imm::insert(&$id, $self)
};
2023-07-10 16:18:23 -05:00
2023-07-24 13:41:10 -05:00
// Length - cannot be more than 2048
($self:expr, $id:ident, L) => {{
assert!($id <= 2048);
$self.buf.extend($id.to_le_bytes())
}};
2023-07-21 19:26:03 -05:00
// Other numbers, just insert their bytes, little endian
2023-07-11 19:16:23 -05:00
($self:expr, $id:ident, $_:ident) => {
$self.buf.extend($id.to_le_bytes())
2023-07-10 16:18:23 -05:00
};
}
2023-07-21 19:26:03 -05:00
/// Implement assembler
2023-07-10 16:18:23 -05:00
macro_rules! impl_asm {
(
$(
$ityn:ident
2023-07-11 19:16:23 -05:00
($($param_i:ident: $param_ty:ident),* $(,)?)
2023-07-10 16:18:23 -05:00
=> [$($opcode:ident),* $(,)?],
)*
) => {
paste::paste! {
$(
2023-07-21 19:26:03 -05:00
// Opcode-generic functions specific for instruction types
pub fn [<i_param_ $ityn>](&mut self, opcode: u8, $($param_i: macros::asm::ident_map_ty!($param_ty)),*) {
2023-07-10 16:18:23 -05:00
self.buf.push(opcode);
2023-07-11 19:16:23 -05:00
$(macros::asm::impl_asm_insert!(self, $param_i, $param_ty);)*
2023-07-10 16:18:23 -05:00
}
2023-07-21 19:26:03 -05:00
// Generate opcode-specific functions calling the opcode-generic ones
2023-07-11 19:16:23 -05:00
macros::asm::impl_asm_opcodes!(
[<i_param_ $ityn>]($($param_i: macros::asm::ident_map_ty!($param_ty)),*)
2023-07-10 16:18:23 -05:00
=> [$($opcode,)*]
);
)*
}
};
}
2023-07-21 19:26:03 -05:00
/// Map operand type to Rust type
2023-07-11 19:16:23 -05:00
#[rustfmt::skip]
macro_rules! ident_map_ty {
2023-07-21 19:26:03 -05:00
(R) => { u8 }; // Register is just u8
(I) => { impl Imm }; // Immediate is anything implementing the trait
2023-07-24 13:41:10 -05:00
(L) => { u16 }; // Copy count
2023-07-21 19:26:03 -05:00
($id:ident) => { $id }; // Anything else → identity map
2023-07-11 19:16:23 -05:00
}
2023-07-21 19:26:03 -05:00
pub(crate) use {ident_map_ty, impl_asm, impl_asm_insert, impl_asm_opcodes};