diff --git a/hbasm/src/lib.rs b/hbasm/src/lib.rs index 81eddc85..73546a59 100644 --- a/hbasm/src/lib.rs +++ b/hbasm/src/lib.rs @@ -52,9 +52,9 @@ macro_rules! tokendef { #[rustfmt::skip] tokendef![ - "nop", "add", "sub", "mul", "rem", "and", "or", "xor", "sl", "sr", "srs", - "cmp", "cmpu", "not", "addf", "subf", "mulf", "divf", "addi", "muli", "remi", "andi", - "ori", "xori", "sli", "sri", "srsi", "cmpi", "cmpui", "addfi", "mulfi", "cp", "li", "lb", + "nop", "add", "sub", "mul", "and", "or", "xor", "sl", "sr", "srs", "cmp", "cmpu", "not", + "dir", "addf", "subf", "mulf", "dirf", "addi", "muli", "andi", "ori", + "xori", "sli", "sri", "srsi", "cmpi", "cmpui", "addfi", "mulfi", "cp", "li", "lb", "ld", "lq", "lo", "sb", "sd", "sq", "so", "jmp", "jeq", "jne", "jlt", "jgt", "jltu", "jgtu", "ret", "ecall", ]; @@ -113,7 +113,8 @@ pub fn assembly(code: &str, buf: &mut Vec) -> Result<(), Error> { self.buf.push(op); match op { NOP | RET | ECALL => Ok(()), - ADD..=CMPU | ADDF..=DIVF => self.rrr(), + DIR | DIRF => self.rrrr(), + ADD..=CMPU | ADDF..=MULF => self.rrr(), NOT | CP => self.rr(), LI | JMP => self.ri(), ADDI..=MULFI | LB..=SO | JEQ..=JGTU => self.rri(), @@ -192,6 +193,21 @@ pub fn assembly(code: &str, buf: &mut Vec) -> Result<(), Error> { Ok(()) } + fn rrrr(&mut self) -> Result<(), ErrorKind> { + expect_matches!( + self, + Token::Register(r0), + Token::PSep, + Token::Register(r1), + Token::PSep, + Token::Register(r2), + Token::PSep, + Token::Register(r3), + ); + self.buf.extend([r0, r1, r2, r3]); + Ok(()) + } + fn insert_imm(&mut self) -> Result<(), ErrorKind> { let imm = match self.next()? { Token::Integer(i) => i.to_le_bytes(), diff --git a/hbbytecode/src/lib.rs b/hbbytecode/src/lib.rs index 595e3f4c..872ab568 100644 --- a/hbbytecode/src/lib.rs +++ b/hbbytecode/src/lib.rs @@ -23,59 +23,62 @@ constmod!(pub opcode(u8) { ADD = 1, "RRR; #0 ← #1 + #2"; SUB = 2, "RRR; #0 ← #1 - #2"; MUL = 3, "RRR; #0 ← #1 × #2"; - DIV = 4, "RRR; #0 ← #1 ÷ #2"; - REM = 5, "RRR; #0 ← #1 % #2"; - AND = 6, "RRR; #0 ← #1 & #2"; - OR = 7, "RRR; #0 ← #1 | #2"; - XOR = 8, "RRR; #0 ← #1 ^ #2"; - SL = 9, "RRR; #0 ← #1 « #2"; - SR = 10, "RRR; #0 ← #1 » #2"; - SRS = 11, "RRR; #0 ← #1 » #2 (signed)"; - CMP = 12, "RRR; #0 ← #1 <=> #2"; - CMPU = 13, "RRR; #0 ← #1 <=> #2 (unsigned)"; - NOT = 14, "RR; #0 ← !#1"; + AND = 4, "RRR; #0 ← #1 & #2"; + OR = 5, "RRR; #0 ← #1 | #2"; + XOR = 6, "RRR; #0 ← #1 ^ #2"; + SL = 7, "RRR; #0 ← #1 « #2"; + SR = 8, "RRR; #0 ← #1 » #2"; + SRS = 9, "RRR; #0 ← #1 » #2 (signed)"; + CMP = 10, "RRR; #0 ← #1 <=> #2"; + CMPU = 11, "RRR; #0 ← #1 <=> #2 (unsigned)"; + DIR = 12, "RRRR; #0 ← #2 / #3, #1 ← #2 % #3"; + NOT = 13, "RR; #0 ← !#1"; - ADDF = 15, "RRR; #0 ← #1 +. #2"; - SUBF = 16, "RRR; #0 ← #1 +. #2"; - MULF = 17, "RRR; #0 ← #1 +. #2"; - DIVF = 18, "RRR; #0 ← #1 +. #2"; - ADDI = 19, "RRI; #0 ← #1 + imm #2"; - MULI = 20, "RRI; #0 ← #1 × imm #2"; - REMI = 21, "RRI; #0 ← #1 % imm #2"; - ANDI = 22, "RRI; #0 ← #1 & imm #2"; - ORI = 23, "RRI; #0 ← #1 | imm #2"; - XORI = 24, "RRI; #0 ← #1 ^ imm #2"; - SLI = 25, "RRI; #0 ← #1 « imm #2"; - SRI = 26, "RRI; #0 ← #1 » imm #2"; - SRSI = 27, "RRI; #0 ← #1 » imm #2 (signed)"; - CMPI = 28, "RRI; #0 ← #1 <=> imm #2"; - CMPUI = 29, "RRI; #0 ← #1 <=> imm #2 (unsigned)"; + ADDF = 14, "RRR; #0 ← #1 +. #2"; + SUBF = 15, "RRR; #0 ← #1 +. #2"; + MULF = 16, "RRR; #0 ← #1 +. #2"; + ADDI = 17, "RRI; #0 ← #1 + imm #2"; + MULI = 18, "RRI; #0 ← #1 × imm #2"; + ANDI = 19, "RRI; #0 ← #1 & imm #2"; + ORI = 20, "RRI; #0 ← #1 | imm #2"; + XORI = 21, "RRI; #0 ← #1 ^ imm #2"; + SLI = 22, "RRI; #0 ← #1 « imm #2"; + SRI = 23, "RRI; #0 ← #1 » imm #2"; + SRSI = 24, "RRI; #0 ← #1 » imm #2 (signed)"; + CMPI = 25, "RRI; #0 ← #1 <=> imm #2"; + CMPUI = 26, "RRI; #0 ← #1 <=> imm #2 (unsigned)"; + DIRF = 27, "RRRR; #0 ← #2 / #3, #1 ← #2 % #3"; + + ADDFI = 28, "RRI; #0 ← #1 +. imm #2"; + MULFI = 29, "RRI; #0 ← #1 *. imm #2"; - ADDFI = 30, "RRI; #0 ← #1 +. imm #2"; - MULFI = 31, "RRI; #0 ← #1 *. imm #2"; + CP = 30, "RR; Copy #0 ← #1"; + LI = 31, "RI; Load immediate, #0 ← imm #1"; + LB = 32, "RRI; Load byte (8 bits), #0 ← [#1 + imm #2]"; + LD = 33, "RRI; Load doublet (16 bits)"; + LQ = 34, "RRI; Load quadlet (32 bits)"; + LO = 35, "RRI; Load octlet (64 bits)"; + SB = 36, "RRI; Store byte, [#1 + imm #2] ← #0"; + SD = 37, "RRI; Store doublet"; + SQ = 38, "RRI; Store quadlet"; + SO = 39, "RRI; Store octlet"; - CP = 32, "RR; Copy #0 ← #1"; - LI = 33, "RI; Load immediate, #0 ← imm #1"; - LB = 34, "RRI; Load byte (8 bits), #0 ← [#1 + imm #2]"; - LD = 35, "RRI; Load doublet (16 bits)"; - LQ = 36, "RRI; Load quadlet (32 bits)"; - LO = 37, "RRI; Load octlet (64 bits)"; - SB = 38, "RRI; Store byte, [#1 + imm #2] ← #0"; - SD = 39, "RRI; Store doublet"; - SQ = 40, "RRI; Store quadlet"; - SO = 41, "RRI; Store octlet"; - - JMP = 42, "RI; Unconditional jump [#0 + imm #1]"; - JEQ = 43, "RRI; if #0 = #1 → jump imm #2"; - JNE = 44, "RRI; if #0 ≠ #1 → jump imm #2"; - JLT = 45, "RRI; if #0 < #1 → jump imm #2"; - JGT = 46, "RRI; if #0 > #1 → jump imm #2"; - JLTU = 47, "RRI; if #0 < #1 → jump imm #2 (unsigned)"; - JGTU = 48, "RRI; if #0 > #1 → jump imm #2 (unsigned)"; - RET = 49, "N; Return"; - ECALL = 50, "N; Issue system call"; + JMP = 40, "RI; Unconditional jump [#0 + imm #1]"; + JEQ = 41, "RRI; if #0 = #1 → jump imm #2"; + JNE = 42, "RRI; if #0 ≠ #1 → jump imm #2"; + JLT = 43, "RRI; if #0 < #1 → jump imm #2"; + JGT = 44, "RRI; if #0 > #1 → jump imm #2"; + JLTU = 45, "RRI; if #0 < #1 → jump imm #2 (unsigned)"; + JGTU = 46, "RRI; if #0 > #1 → jump imm #2 (unsigned)"; + RET = 47, "N; Return"; + ECALL = 48, "N; Issue system call"; }); + +/// Register-register-register-register instruction parameter +#[repr(packed)] +pub struct ParamRRRR(pub u8, pub u8, pub u8, pub u8); + /// Register-register-register instruction parameter #[repr(packed)] pub struct ParamRRR(pub u8, pub u8, pub u8); @@ -95,6 +98,7 @@ pub struct ParamRI(pub u8, pub u64); /// # Safety /// Has to be valid to be decoded from bytecode. pub unsafe trait OpParam {} +unsafe impl OpParam for ParamRRRR {} unsafe impl OpParam for ParamRRR {} unsafe impl OpParam for ParamRRI {} unsafe impl OpParam for ParamRR {} diff --git a/hbvm/src/validate.rs b/hbvm/src/validate.rs index 35bb917a..f38f4a05 100644 --- a/hbvm/src/validate.rs +++ b/hbvm/src/validate.rs @@ -40,8 +40,15 @@ pub fn validate(mut program: &[u8]) -> Result<(), Error> { [] => return Ok(()), // N [NOP | RET | ECALL, rest @ ..] => rest, + // RRRR + [DIR | DIRF, _, _, _, _, rest @ ..] => { + if let Some(n) = reg(&program[1..=4]) { + bail!(InvalidRegister, start, program, n + 1); + } + rest + } // RRR - [ADD..=CMPU | ADDF..=DIVF, _, _, _, rest @ ..] => { + [ADD..=CMPU | ADDF..=MULF, _, _, _, rest @ ..] => { if let Some(n) = reg(&program[1..=3]) { bail!(InvalidRegister, start, program, n + 1); } diff --git a/hbvm/src/vm/mod.rs b/hbvm/src/vm/mod.rs index 1798b4e4..f1ad9369 100644 --- a/hbvm/src/vm/mod.rs +++ b/hbvm/src/vm/mod.rs @@ -1,5 +1,5 @@ //! HoleyBytes Virtual Machine -//! +//! //! All unsafe code here should be sound, if input bytecode passes validation. // # General safety notice: @@ -11,6 +11,8 @@ // program size. If you are (rightfully) worried about the UB, for now just // append your program with 11 zeroes. +use hbbytecode::ParamRRRR; + mod mem; mod value; @@ -136,8 +138,6 @@ impl<'a> Vm<'a> { ADD => binary_op!(self, int, u64::wrapping_add), SUB => binary_op!(self, int, u64::wrapping_sub), MUL => binary_op!(self, int, u64::wrapping_mul), - DIV => binary_op!(self, int, u64::wrapping_div), - REM => binary_op!(self, int, u64::wrapping_rem), AND => binary_op!(self, int, ops::BitAnd::bitand), OR => binary_op!(self, int, ops::BitOr::bitor), XOR => binary_op!(self, int, ops::BitXor::bitxor), @@ -162,13 +162,25 @@ impl<'a> Vm<'a> { let param = param!(self, ParamRR); self.write_reg(param.0, (!self.read_reg(param.1).int()).into()); } + DIR => { + let ParamRRRR(dt, rt, a0, a1) = param!(self, ParamRRRR); + let a0 = self.read_reg(a0).int(); + let a1 = self.read_reg(a1).int(); + self.write_reg(dt, (a0.checked_div(a1).unwrap_or(u64::MAX)).into()); + self.write_reg(rt, (a0.checked_rem(a1).unwrap_or(u64::MAX)).into()); + } ADDF => binary_op!(self, float, ops::Add::add), SUBF => binary_op!(self, float, ops::Sub::sub), MULF => binary_op!(self, float, ops::Mul::mul), - DIVF => binary_op!(self, float, ops::Div::div), + DIRF => { + let ParamRRRR(dt, rt, a0, a1) = param!(self, ParamRRRR); + let a0 = self.read_reg(a0).float(); + let a1 = self.read_reg(a1).float(); + self.write_reg(dt, (a0 / a1).into()); + self.write_reg(rt, (a0 % a1).into()); + } ADDI => binary_op_imm!(self, int, ops::Add::add), MULI => binary_op_imm!(self, int, ops::Mul::mul), - REMI => binary_op_imm!(self, int, ops::Rem::rem), ANDI => binary_op_imm!(self, int, ops::BitAnd::bitand), ORI => binary_op_imm!(self, int, ops::BitOr::bitor), XORI => binary_op_imm!(self, int, ops::BitXor::bitxor),