diff --git a/Cargo.lock b/Cargo.lock index 007c080..708a868 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,9 +130,16 @@ dependencies = [ "hbbytecode", "log", "paste", + "sealed", "static_assertions", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "lasso" version = "0.7.2" @@ -246,6 +253,18 @@ dependencies = [ "semver", ] +[[package]] +name = "sealed" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", +] + [[package]] name = "semver" version = "1.0.17" diff --git a/hbasm/src/lib.rs b/hbasm/src/lib.rs index dfdda48..022f8d0 100644 --- a/hbasm/src/lib.rs +++ b/hbasm/src/lib.rs @@ -27,8 +27,10 @@ macros::impl_both!( bbdh(p0: R, p1: R, p2: I, p3: u16) => [LD, ST], bbd(p0: R, p1: R, p2: I) - => [ADDI, MULI, ANDI, ORI, XORI, SLI, SRI, SRSI, CMPI, CMPUI, - BMC, JAL, JEQ, JNE, JLT, JGT, JLTU, JGTU, ADDFI, MULFI], + => [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) diff --git a/hbasm/src/macros/text.rs b/hbasm/src/macros/text.rs index 3234228..d26e7e1 100644 --- a/hbasm/src/macros/text.rs +++ b/hbasm/src/macros/text.rs @@ -215,37 +215,39 @@ macro_rules! extract_pat { }; } -/// Extract operand from code -macro_rules! extract { - // Register (require prefixing with r) - ($self:expr, R, $id:ident) => { - extract_pat!($self, Token::Register($id)); - }; +/// Generate extract macro +macro_rules! gen_extract { + // Integer types have same body + ($($int:ident),* $(,)?) => { + /// Extract operand from code + macro_rules! extract { + // Register (require prefixing with r) + ($self:expr, R, $id:ident) => { + extract_pat!($self, Token::Register($id)); + }; - // Immediate - ($self:expr, I, $id:ident) => { - let $id = match $self.next()? { - // Either straight up integer - Token::Integer(a) => InternalImm::Const(a), - // …or a label - Token::Symbol(a) => InternalImm::Named(a), - _ => return Err(ErrorKind::UnexpectedToken), - }; - }; + // Immediate + ($self:expr, I, $id:ident) => { + let $id = match $self.next()? { + // Either straight up integer + Token::Integer(a) => InternalImm::Const(a), + // …or a label + Token::Symbol(a) => InternalImm::Named(a), + _ => return Err(ErrorKind::UnexpectedToken), + }; + }; - // Get u8, if not fitting, the token is claimed invalid - ($self:expr, u8, $id:ident) => { - extract_pat!($self, Token::Integer($id)); - let $id = u8::try_from($id).map_err(|_| ErrorKind::InvalidToken)?; - }; - - // Get u16, if not fitting, the token is claimed invalid - ($self:expr, u16, $id:ident) => { - extract_pat!($self, Token::Integer($id)); - let $id = u16::try_from($id).map_err(|_| ErrorKind::InvalidToken)?; + // Get $int, if not fitting, the token is claimed invalid + $(($self:expr, $int, $id:ident) => { + extract_pat!($self, Token::Integer($id)); + let $id = $int::try_from($id).map_err(|_| ErrorKind::InvalidToken)?; + });*; + } }; } +gen_extract!(u8, u16, u32); + /// Parameter extract incremental token-tree muncher /// /// What else would it mean? diff --git a/hbbytecode/hbbytecode.h b/hbbytecode/hbbytecode.h index 307ca92..5694afa 100644 --- a/hbbytecode/hbbytecode.h +++ b/hbbytecode/hbbytecode.h @@ -44,6 +44,11 @@ struct hbbc_ParamBBD typedef hbbc_ParamBBD; static_assert(sizeof(hbbc_ParamBBD) == 80 / 8); +struct hbbc_ParamBBW + { uint8_t _0; uint8_t _1; uint32_t _2; } + typedef hbbc_ParamBBW; + static_assert(sizeof(hbbc_ParamBBW) == 48 / 8); + struct hbbc_ParamBB { uint8_t _0; uint8_t _1; } typedef hbbc_ParamBB; diff --git a/hbbytecode/src/lib.rs b/hbbytecode/src/lib.rs index 400cab1..1cf0b21 100644 --- a/hbbytecode/src/lib.rs +++ b/hbbytecode/src/lib.rs @@ -20,7 +20,7 @@ constmod!(pub opcode(u8) { UN = 0, "N; Raises a trap"; NOP = 1, "N; Do nothing"; - + ADD = 2, "BBB; #0 ← #1 + #2"; SUB = 3, "BBB; #0 ← #1 - #2"; MUL = 4, "BBB; #0 ← #1 × #2"; @@ -41,9 +41,9 @@ constmod!(pub opcode(u8) { ANDI = 18, "BBD; #0 ← #1 & imm #2"; ORI = 19, "BBD; #0 ← #1 | imm #2"; XORI = 20, "BBD; #0 ← #1 ^ imm #2"; - SLI = 21, "BBD; #0 ← #1 « imm #2"; - SRI = 22, "BBD; #0 ← #1 » imm #2"; - SRSI = 23, "BBD; #0 ← #1 » imm #2 (signed)"; + SLI = 21, "BBW; #0 ← #1 « imm #2"; + SRI = 22, "BBW; #0 ← #1 » imm #2"; + SRSI = 23, "BBW; #0 ← #1 » imm #2 (signed)"; CMPI = 24, "BBD; #0 ← #1 <=> imm #2"; CMPUI = 25, "BBD; #0 ← #1 <=> imm #2 (unsigned)"; @@ -89,6 +89,9 @@ pub struct ParamBBDH(pub u8, pub u8, pub u64, pub u16); #[repr(packed)] pub struct ParamBBD(pub u8, pub u8, pub u64); +#[repr(packed)] +pub struct ParamBBW(pub u8, pub u8, pub u32); + #[repr(packed)] pub struct ParamBB(pub u8, pub u8); @@ -102,6 +105,7 @@ unsafe impl OpParam for ParamBBBB {} unsafe impl OpParam for ParamBBB {} unsafe impl OpParam for ParamBBDH {} unsafe impl OpParam for ParamBBD {} +unsafe impl OpParam for ParamBBW {} unsafe impl OpParam for ParamBB {} unsafe impl OpParam for ParamBD {} unsafe impl OpParam for u64 {} diff --git a/hbvm/Cargo.toml b/hbvm/Cargo.toml index a6b6a50..0346a40 100644 --- a/hbvm/Cargo.toml +++ b/hbvm/Cargo.toml @@ -13,4 +13,5 @@ hashbrown = "0.13" hbbytecode.path = "../hbbytecode" log = "0.4" paste = "1.0" +sealed = "0.5" static_assertions = "1.0" diff --git a/hbvm/src/validate.rs b/hbvm/src/validate.rs index 41b1636..103a2d8 100644 --- a/hbvm/src/validate.rs +++ b/hbvm/src/validate.rs @@ -69,7 +69,8 @@ pub fn validate(mut program: &[u8]) -> Result<(), Error> { | [ADD..=CMPU | BRC | ADDF..=MULF, _, _, _, rest @ ..] | [NEG..=NOT | CP..=SWA | NEGF..=FTI, _, _, rest @ ..] | [LI, _, _, _, _, _, _, _, _, _, rest @ ..] - | [ADDI..=CMPUI | BMC | JAL..=JGTU | ADDFI..=MULFI, _, _, _, _, _, _, _, _, _, _, rest @ ..] + | [ADDI..=XORI | CMPI..=CMPUI | BMC | JAL..=JGTU | ADDFI..=MULFI, _, _, _, _, _, _, _, _, _, _, rest @ ..] + | [SLI..=SRSI, _, _, _, _, rest @ ..] | [LD..=ST, _, _, _, _, _, _, _, _, _, _, _, _, rest @ ..] => rest, _ => { return Err(Error { diff --git a/hbvm/src/vm/mod.rs b/hbvm/src/vm/mod.rs index ce1096e..797d396 100644 --- a/hbvm/src/vm/mod.rs +++ b/hbvm/src/vm/mod.rs @@ -7,91 +7,18 @@ // - Instructions have to be valid as specified (values and sizes) // - Mapped pages should be at least 4 KiB -use self::mem::HandlePageFault; - pub mod mem; pub mod value; use { + self::{mem::HandlePageFault, value::ValueVariant}, crate::validate, - core::ops, - hbbytecode::{OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBD}, + core::{cmp::Ordering, ops}, + hbbytecode::{OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD}, mem::Memory, - static_assertions::assert_impl_one, value::Value, }; -/// Extract a parameter from program -macro_rules! param { - ($self:expr, $ty:ty) => {{ - assert_impl_one!($ty: OpParam); - let data = $self - .program - .as_ptr() - .add($self.pc + 1) - .cast::<$ty>() - .read(); - $self.pc += 1 + core::mem::size_of::<$ty>(); - data - }}; -} - -/// Perform binary operation `#0 ← #1 OP #2` -macro_rules! binary_op { - ($self:expr, $ty:ident, $handler:expr) => {{ - let ParamBBB(tg, a0, a1) = param!($self, ParamBBB); - $self.write_reg( - tg, - $handler( - Value::$ty(&$self.read_reg(a0)), - Value::$ty(&$self.read_reg(a1)), - ), - ); - }}; - - ($self:expr, $ty:ident, $handler:expr, $con:ty) => {{ - let ParamBBB(tg, a0, a1) = param!($self, ParamBBB); - $self.write_reg( - tg, - $handler( - Value::$ty(&$self.read_reg(a0)), - Value::$ty(&$self.read_reg(a1)) as $con, - ), - ); - }}; -} - -/// Perform binary operation with immediate `#0 ← #1 OP imm #2` -macro_rules! binary_op_imm { - ($self:expr, $ty:ident, $handler:expr) => {{ - let ParamBBD(tg, a0, imm) = param!($self, ParamBBD); - $self.write_reg( - tg, - $handler(Value::$ty(&$self.read_reg(a0)), Value::$ty(&imm.into())), - ); - }}; - - ($self:expr, $ty:ident, $handler:expr, $con:ty) => {{ - let ParamBBD(tg, a0, imm) = param!($self, ParamBBD); - $self.write_reg( - tg, - $handler(Value::$ty(&$self.read_reg(a0)), Value::$ty(&imm.into()) as $con), - ); - }}; -} - -/// Jump at `#3` if ordering on `#0 <=> #1` is equal to expected -macro_rules! cond_jump { - ($self:expr, $ty:ident, $expected:ident) => {{ - let ParamBBD(a0, a1, jt) = param!($self, ParamBBD); - if core::cmp::Ord::cmp(&$self.read_reg(a0).as_u64(), &$self.read_reg(a1).as_u64()) - == core::cmp::Ordering::$expected - { - $self.pc = jt as usize; - } - }}; -} - /// HoleyBytes Virtual Machine pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> { /// Holds 256 registers @@ -107,7 +34,7 @@ pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> { pub pfhandler: PfHandler, /// Program counter - pc: usize, + pub pc: usize, /// Program program: &'a [u8], @@ -177,50 +104,56 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> unsafe { match *self.program.get_unchecked(self.pc) { UN => { - param!(self, ()); + self.decode::<()>(); return Err(VmRunError::Unreachable); } - NOP => param!(self, ()), - ADD => binary_op!(self, as_u64, u64::wrapping_add), - SUB => binary_op!(self, as_u64, u64::wrapping_sub), - MUL => binary_op!(self, as_u64, u64::wrapping_mul), - AND => binary_op!(self, as_u64, ops::BitAnd::bitand), - OR => binary_op!(self, as_u64, ops::BitOr::bitor), - XOR => binary_op!(self, as_u64, ops::BitXor::bitxor), - SL => binary_op!(self, as_u64, u64::wrapping_shl, u32), - SR => binary_op!(self, as_u64, u64::wrapping_shr, u32), - SRS => binary_op!(self, as_i64, i64::wrapping_shr, u32), + NOP => self.decode::<()>(), + ADD => self.binary_op(u64::wrapping_add), + SUB => self.binary_op(u64::wrapping_sub), + MUL => self.binary_op(u64::wrapping_mul), + AND => self.binary_op::(ops::BitAnd::bitand), + OR => self.binary_op::(ops::BitOr::bitor), + XOR => self.binary_op::(ops::BitXor::bitxor), + SL => self.binary_op(|l, r| u64::wrapping_shl(l, r as u32)), + SR => self.binary_op(|l, r| u64::wrapping_shr(l, r as u32)), + SRS => self.binary_op(|l, r| i64::wrapping_shl(l, r as u32)), CMP => { // Compare a0 <=> a1 // < → -1 // > → 1 // = → 0 - let ParamBBB(tg, a0, a1) = param!(self, ParamBBB); + let ParamBBB(tg, a0, a1) = self.decode(); self.write_reg( tg, - self.read_reg(a0).as_i64().cmp(&self.read_reg(a1).as_i64()) as i64, + self.read_reg(a0) + .cast::() + .cmp(&self.read_reg(a1).cast::()) + as i64, ); } CMPU => { // Unsigned comparsion - let ParamBBB(tg, a0, a1) = param!(self, ParamBBB); + let ParamBBB(tg, a0, a1) = self.decode(); self.write_reg( tg, - self.read_reg(a0).as_u64().cmp(&self.read_reg(a1).as_u64()) as i64, + self.read_reg(a0) + .cast::() + .cmp(&self.read_reg(a1).cast::()) + as i64, ); } NOT => { // Logical negation - let param = param!(self, ParamBB); - self.write_reg(param.0, !self.read_reg(param.1).as_u64()); + let ParamBB(tg, a0) = self.decode(); + self.write_reg(tg, !self.read_reg(a0).cast::()); } NEG => { // Bitwise negation - let param = param!(self, ParamBB); + let ParamBB(tg, a0) = self.decode(); self.write_reg( - param.0, - match self.read_reg(param.1).as_u64() { + tg, + match self.read_reg(a0).cast::() { 0 => 1_u64, _ => 0, }, @@ -228,38 +161,41 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> } DIR => { // Fused Division-Remainder - let ParamBBBB(dt, rt, a0, a1) = param!(self, ParamBBBB); - let a0 = self.read_reg(a0).as_u64(); - let a1 = self.read_reg(a1).as_u64(); + let ParamBBBB(dt, rt, a0, a1) = self.decode(); + let a0 = self.read_reg(a0).cast::(); + let a1 = self.read_reg(a1).cast::(); self.write_reg(dt, a0.checked_div(a1).unwrap_or(u64::MAX)); self.write_reg(rt, a0.checked_rem(a1).unwrap_or(u64::MAX)); } - ADDI => binary_op_imm!(self, as_u64, ops::Add::add), - MULI => binary_op_imm!(self, as_u64, ops::Mul::mul), - ANDI => binary_op_imm!(self, as_u64, ops::BitAnd::bitand), - ORI => binary_op_imm!(self, as_u64, ops::BitOr::bitor), - XORI => binary_op_imm!(self, as_u64, ops::BitXor::bitxor), - SLI => binary_op_imm!(self, as_u64, u64::wrapping_shl, u32), - SRI => binary_op_imm!(self, as_u64, u64::wrapping_shr, u32), - SRSI => binary_op_imm!(self, as_i64, i64::wrapping_shr, u32), + ADDI => self.binary_op_imm(u64::wrapping_add), + MULI => self.binary_op_imm(u64::wrapping_sub), + ANDI => self.binary_op_imm::(ops::BitAnd::bitand), + ORI => self.binary_op_imm::(ops::BitOr::bitor), + XORI => self.binary_op_imm::(ops::BitXor::bitxor), + SLI => self.binary_op_ims(u64::wrapping_shl), + SRI => self.binary_op_ims(u64::wrapping_shr), + SRSI => self.binary_op_ims(i64::wrapping_shr), CMPI => { - let ParamBBD(tg, a0, imm) = param!(self, ParamBBD); + let ParamBBD(tg, a0, imm) = self.decode(); self.write_reg( tg, - self.read_reg(a0).as_i64().cmp(&Value::from(imm).as_i64()) as i64, + self.read_reg(a0) + .cast::() + .cmp(&Value::from(imm).cast::()) + as i64, ); } CMPUI => { - let ParamBBD(tg, a0, imm) = param!(self, ParamBBD); - self.write_reg(tg, self.read_reg(a0).as_u64().cmp(&imm) as i64); + let ParamBBD(tg, a0, imm) = self.decode(); + self.write_reg(tg, self.read_reg(a0).cast::().cmp(&imm) as i64); } CP => { - let param = param!(self, ParamBB); - self.write_reg(param.0, self.read_reg(param.1)); + let ParamBB(tg, a0) = self.decode(); + self.write_reg(tg, self.read_reg(a0)); } SWA => { // Swap registers - let ParamBB(r0, r1) = param!(self, ParamBB); + let ParamBB(r0, r1) = self.decode(); match (r0, r1) { (0, 0) => (), (dst, 0) | (0, dst) => self.write_reg(dst, 0_u64), @@ -272,19 +208,19 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> } } LI => { - let param = param!(self, ParamBD); - self.write_reg(param.0, param.1); + let ParamBD(tg, imm) = self.decode(); + self.write_reg(tg, imm); } LD => { // Load. If loading more than register size, continue on adjecent registers - let ParamBBDH(dst, base, off, count) = param!(self, ParamBBDH); + let ParamBBDH(dst, base, off, count) = self.decode(); let n: usize = match dst { 0 => 1, _ => 0, }; self.memory.load( - self.read_reg(base).as_u64() + off + n as u64, + self.read_reg(base).cast::() + off + n as u64, self.registers.as_mut_ptr().add(usize::from(dst) + n).cast(), usize::from(count).saturating_sub(n), &mut self.pfhandler, @@ -292,9 +228,9 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> } ST => { // Store. Same rules apply as to LD - let ParamBBDH(dst, base, off, count) = param!(self, ParamBBDH); + let ParamBBDH(dst, base, off, count) = self.decode(); self.memory.store( - self.read_reg(base).as_u64() + off, + self.read_reg(base).cast::() + off, self.registers.as_ptr().add(usize::from(dst)).cast(), count.into(), &mut self.pfhandler, @@ -302,17 +238,17 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> } BMC => { // Block memory copy - let ParamBBD(src, dst, count) = param!(self, ParamBBD); + let ParamBBD(src, dst, count) = self.decode(); self.memory.block_copy( - self.read_reg(src).as_u64(), - self.read_reg(dst).as_u64(), + self.read_reg(src).cast::(), + self.read_reg(dst).cast::(), count as _, &mut self.pfhandler, )?; } BRC => { // Block register copy - let ParamBBB(src, dst, count) = param!(self, ParamBBB); + let ParamBBB(src, dst, count) = self.decode(); core::ptr::copy( self.registers.get_unchecked(usize::from(src)), self.registers.get_unchecked_mut(usize::from(dst)), @@ -322,24 +258,24 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> JAL => { // Jump and link. Save PC after this instruction to // specified register and jump to reg + offset. - let ParamBBD(save, reg, offset) = param!(self, ParamBBD); + let ParamBBD(save, reg, offset) = self.decode(); self.write_reg(save, self.pc as u64); - self.pc = (self.read_reg(reg).as_u64() + offset) as usize; + self.pc = (self.read_reg(reg).cast::() + offset) as usize; } // Conditional jumps, jump only to immediates - JEQ => cond_jump!(self, int, Equal), + JEQ => self.cond_jmp::(Ordering::Equal), JNE => { - let ParamBBD(a0, a1, jt) = param!(self, ParamBBD); - if self.read_reg(a0).as_u64() != self.read_reg(a1).as_u64() { + let ParamBBD(a0, a1, jt) = self.decode(); + if self.read_reg(a0).cast::() != self.read_reg(a1).cast::() { self.pc = jt as usize; } } - JLT => cond_jump!(self, int, Less), - JGT => cond_jump!(self, int, Greater), - JLTU => cond_jump!(self, sint, Less), - JGTU => cond_jump!(self, sint, Greater), + JLT => self.cond_jmp::(Ordering::Less), + JGT => self.cond_jmp::(Ordering::Greater), + JLTU => self.cond_jmp::(Ordering::Less), + JGTU => self.cond_jmp::(Ordering::Greater), ECALL => { - param!(self, ()); + self.decode::<()>(); // So we don't get timer interrupt after ECALL if TIMER_QUOTIENT != 0 { @@ -347,38 +283,38 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> } return Ok(VmRunOk::Ecall); } - ADDF => binary_op!(self, as_f64, ops::Add::add), - SUBF => binary_op!(self, as_f64, ops::Sub::sub), - MULF => binary_op!(self, as_f64, ops::Mul::mul), + ADDF => self.binary_op::(ops::Add::add), + SUBF => self.binary_op::(ops::Sub::sub), + MULF => self.binary_op::(ops::Mul::mul), DIRF => { - let ParamBBBB(dt, rt, a0, a1) = param!(self, ParamBBBB); - let a0 = self.read_reg(a0).as_f64(); - let a1 = self.read_reg(a1).as_f64(); + let ParamBBBB(dt, rt, a0, a1) = self.decode(); + let a0 = self.read_reg(a0).cast::(); + let a1 = self.read_reg(a1).cast::(); self.write_reg(dt, a0 / a1); self.write_reg(rt, a0 % a1); } FMAF => { - let ParamBBBB(dt, a0, a1, a2) = param!(self, ParamBBBB); + let ParamBBBB(dt, a0, a1, a2) = self.decode(); self.write_reg( dt, - self.read_reg(a0).as_f64() * self.read_reg(a1).as_f64() - + self.read_reg(a2).as_f64(), + self.read_reg(a0).cast::() * self.read_reg(a1).cast::() + + self.read_reg(a2).cast::(), ); } NEGF => { - let ParamBB(dt, a0) = param!(self, ParamBB); - self.write_reg(dt, -self.read_reg(a0).as_f64()); + let ParamBB(dt, a0) = self.decode(); + self.write_reg(dt, -self.read_reg(a0).cast::()); } ITF => { - let ParamBB(dt, a0) = param!(self, ParamBB); - self.write_reg(dt, self.read_reg(a0).as_i64() as f64); + let ParamBB(dt, a0) = self.decode(); + self.write_reg(dt, self.read_reg(a0).cast::() as f64); } FTI => { - let ParamBB(dt, a0) = param!(self, ParamBB); - self.write_reg(dt, self.read_reg(a0).as_f64() as i64); + let ParamBB(dt, a0) = self.decode(); + self.write_reg(dt, self.read_reg(a0).cast::() as i64); } - ADDFI => binary_op_imm!(self, as_f64, ops::Add::add), - MULFI => binary_op_imm!(self, as_f64, ops::Mul::mul), + ADDFI => self.binary_op_imm::(ops::Add::add), + MULFI => self.binary_op_imm::(ops::Mul::mul), op => return Err(VmRunError::InvalidOpcode(op)), } } @@ -392,6 +328,55 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> } } + /// Decode instruction operands + #[inline] + unsafe fn decode(&mut self) -> T { + let data = self.program.as_ptr().add(self.pc + 1).cast::().read(); + self.pc += 1 + core::mem::size_of::(); + data + } + + /// Perform binary operating over two registers + #[inline] + unsafe fn binary_op(&mut self, op: impl Fn(T, T) -> T) { + let ParamBBB(tg, a0, a1) = self.decode(); + self.write_reg( + tg, + op(self.read_reg(a0).cast::(), self.read_reg(a1).cast::()), + ); + } + + /// Perform binary operation over register and immediate + #[inline] + unsafe fn binary_op_imm(&mut self, op: impl Fn(T, T) -> T) { + let ParamBBD(tg, reg, imm) = self.decode(); + self.write_reg( + tg, + op(self.read_reg(reg).cast::(), Value::from(imm).cast::()), + ); + } + + /// Perform binary operation over register and shift immediate + #[inline] + unsafe fn binary_op_ims(&mut self, op: impl Fn(T, u32) -> T) { + let ParamBBW(tg, reg, imm) = self.decode(); + self.write_reg(tg, op(self.read_reg(reg).cast::(), imm)); + } + + /// Jump at `#3` if ordering on `#0 <=> #1` is equal to expected + #[inline] + unsafe fn cond_jmp(&mut self, expected: Ordering) { + let ParamBBD(a0, a1, ja) = self.decode(); + if self + .read_reg(a0) + .cast::() + .cmp(&self.read_reg(a1).cast::()) + == expected + { + self.pc = ja as usize; + } + } + /// Read register #[inline] unsafe fn read_reg(&self, n: u8) -> Value { diff --git a/hbvm/src/vm/value.rs b/hbvm/src/vm/value.rs index ffc069c..fb8738e 100644 --- a/hbvm/src/vm/value.rs +++ b/hbvm/src/vm/value.rs @@ -1,6 +1,6 @@ //! HoleyBytes register value definition -use core::fmt::Debug; +use sealed::sealed; /// Define [`Value`] union /// @@ -16,15 +16,6 @@ macro_rules! value_def { $(pub $ty: $ty),* } - paste::paste! { - impl Value {$( - #[doc = "Byte-reinterpret [`Value`] as [`" $ty "`]"] - #[inline] - pub fn [](&self) -> $ty { - unsafe { self.$ty } - } - )*} - } $( impl From<$ty> for Value { @@ -33,16 +24,41 @@ macro_rules! value_def { Self { $ty: value } } } + + static_assertions::const_assert_eq!( + core::mem::size_of::<$ty>(), + core::mem::size_of::(), + ); + + #[sealed] + unsafe impl ValueVariant for $ty {} )* }; } +impl Value { + #[inline] + pub fn cast(self) -> Variant { + union Transmute { + src: Value, + variant: Variant, + } + + unsafe { Transmute { src: self }.variant } + } +} + +/// # Safety +/// - N/A, not to be implemented manually +#[sealed] +pub unsafe trait ValueVariant: Copy + Into {} + value_def!(u64, i64, f64); static_assertions::const_assert_eq!(core::mem::size_of::(), 8); -impl Debug for Value { +impl core::fmt::Debug for Value { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { // Print formatted as hexadecimal, unsigned integer - write!(f, "{:x}", self.as_u64()) + write!(f, "{:x}", self.cast::()) } } diff --git a/spec.md b/spec.md index b9909e6..67094ea 100644 --- a/spec.md +++ b/spec.md @@ -21,6 +21,7 @@ | BBB | 24 bits | | BBDH | 96 bits | | BBD | 80 bits | +| BBW | 48 bits | | BB | 16 bits | | BD | 72 bits | | D | 64 bits | @@ -99,6 +100,11 @@ | 18 | ANDI | Bitand | | 19 | ORI | Bitor | | 20 | XORI | Bitxor | + +### Bitshifts +- Type BBW +| Opcode | Name | Action | +|:------:|:----:|:-----------------------:| | 21 | SLI | Unsigned left bitshift | | 22 | SRI | Unsigned right bitshift | | 23 | SRSI | Signed right bitshift |