aboutsummaryrefslogtreecommitdiff
path: root/src/long_mode
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2026-03-19 12:09:46 +0000
committeriximeow <me@iximeow.net>2026-03-19 12:09:46 +0000
commitc129dde3e513980e209297956e1f95eec1fb7e26 (patch)
treed0ddd7b77ca899e81f0d3e2b26a63a52abca642f /src/long_mode
parent396b60f6a927c0fa757c275dfe9226e4e5160203 (diff)
more instructions, figured out mul/imul
Diffstat (limited to 'src/long_mode')
-rw-r--r--src/long_mode/behavior.rs234
1 files changed, 185 insertions, 49 deletions
diff --git a/src/long_mode/behavior.rs b/src/long_mode/behavior.rs
index d70bc15..9f69a40 100644
--- a/src/long_mode/behavior.rs
+++ b/src/long_mode/behavior.rs
@@ -29,9 +29,50 @@ pub struct InstBehavior<'inst> {
impl Instruction {
pub fn behavior<'inst>(&'inst self) -> InstBehavior<'inst> {
+ let behavior = if let Some(behavior) = opcode2behavior(&self.opcode) {
+ behavior
+ } else {
+ // mul and imul are incredibly frustrating, with multiple behaviors corresponding to
+ // different encodings with different opcode counts. fix up behaviors here..
+ if self.opcode == Opcode::MUL || (self.opcode == Opcode::IMUL && self.operand_count == 1) {
+ let op_width = if self.operands[0] == OperandSpec::RegMMM {
+ self.regs[1].width()
+ } else {
+ self.mem_size
+ };
+ let ops_idx = match op_width {
+ 1 => MUL_IDX_1OP_BYTE,
+ 2 => MUL_IDX_1OP_WORD,
+ 4 => MUL_IDX_1OP_DWORD,
+ _ /* 8 */ => MUL_IDX_1OP_QWORD,
+ };
+ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Write)
+ .set_operand(0, Access::Read)
+ .set_implicit_ops(ops_idx)
+ } else if self.opcode == Opcode::IMUL {
+ let base_digest = BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Write);
+ if self.operand_count == 2 {
+ base_digest
+ .set_operand(0, Access::ReadWrite)
+ .set_operand(1, Access::Read)
+ } else {
+ base_digest
+ .set_operand(0, Access::Write)
+ .set_operand(1, Access::Read)
+ .set_operand(2, Access::Read)
+ }
+ } else {
+ // TODO: words
+ unreachable!();
+ }
+ };
InstBehavior {
inst: self,
- behavior: opcode2behavior(&self.opcode)
+ behavior
}
}
}
@@ -251,7 +292,7 @@ impl<'inst> Iterator for AccessIter<'inst> {
// the implicit operand list might be empty, there may be no flags, etc. so we need to
// consider the implicit operand iterator states up to two times before falling through to
- // the first element of the explicit operand list (if there is one). using a loop here
+ // the first is this the first reported case of buttelement of the explicit operand list (if there is one). using a loop here
// seems like the least-gross way to go...
while !self.explicit {
if self.next == 0 {
@@ -959,6 +1000,10 @@ mod test {
}
}
+/// no operations, but you can run it anywhere.
+const GENERAL: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any();
+
/// instructions that can execute at all privilege levels, have two operands, read/write the first,
/// and read the second.
const GENERAL_RW_R: BehaviorDigest = BehaviorDigest::empty()
@@ -1022,11 +1067,12 @@ const GENERAL_RW_RW: BehaviorDigest = GENERAL_RW_R
const GENERAL_RW_RW_FLAGWRITE: BehaviorDigest = GENERAL_RW_RW
.set_flags_access(Access::Write);
- /*
const GENERAL_R: BehaviorDigest = BehaviorDigest::empty()
.set_pl_any()
.set_operand(0, Access::Read);
- */
+
+const GENERAL_R_FLAGREAD: BehaviorDigest = GENERAL_R
+ .set_flags_access(Access::Read);
// TODO: seems incredibly funky that jcc's operand is an immediate, when written like this..
const JCC: BehaviorDigest = BehaviorDigest::empty()
@@ -1454,6 +1500,82 @@ static CLTS_OPS: &'static [ImplicitOperand] = &[
},
];
+// the actual implicit operands of `{i,}mul` are broken out by operand count and operation size..
+static MUL_OPS_1OP_BYTE: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::al(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: true,
+ }
+];
+static MUL_OPS_1OP_WORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::dx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+static MUL_OPS_1OP_DWORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+static MUL_OPS_1OP_QWORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rdx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+
const PUSH_OPS_IDX: u16 = 1;
const POP_OPS_IDX: u16 = 2;
const JCC_OPS_IDX: u16 = 3;
@@ -1475,8 +1597,12 @@ const RETURN_IDX: u16 = 18;
const LEAVE_IDX: u16 = 19;
const XLAT_IDX: u16 = 20;
const CLTS_IDX: u16 = 21;
+const MUL_IDX_1OP_BYTE: u16 = 22;
+const MUL_IDX_1OP_WORD: u16 = 23;
+const MUL_IDX_1OP_DWORD: u16 = 24;
+const MUL_IDX_1OP_QWORD: u16 = 25;
-static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 22] = [
+static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 26] = [
&[], // implicit ops list 0 is not used
PUSH_OPS,
POP_OPS,
@@ -1499,11 +1625,18 @@ static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 22] = [
LEAVE_OPS,
XLAT_OPS,
CLTS_OPS,
+ MUL_OPS_1OP_BYTE,
+ MUL_OPS_1OP_WORD,
+ MUL_OPS_1OP_DWORD,
+ MUL_OPS_1OP_QWORD,
];
-fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
+fn opcode2behavior(opc: &Opcode) -> Option<BehaviorDigest> {
use Opcode::*;
- match opc {
+ if opc == &MUL || opc == &IMUL {
+ return None;
+ }
+ let behavior = match opc {
ADD => GENERAL_RW_R_FLAGWRITE,
OR => GENERAL_RW_R_FLAGWRITE,
ADC => GENERAL_RW_R_FLAGRW,
@@ -1608,8 +1741,7 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
.set_pl_any()
.set_operand(0, Access::Write),
LEA => GENERAL_W_R,
- NOP => BehaviorDigest::empty()
- .set_pl_any(),
+ NOP => GENERAL,
PREFETCHNTA => { panic!("todo: prefetchnta"); },
PREFETCH0 => { panic!("todo: prefetch0"); },
PREFETCH1 => { panic!("todo: prefetch1"); },
@@ -1617,8 +1749,8 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
POPF => BehaviorDigest::empty()
.set_implicit_ops(POPF_IDX)
.set_pl_any(),
- INT => { panic!("todo: int"); },
- INTO => { panic!("todo: into"); },
+ INT => GENERAL_R,
+ INTO => GENERAL_R_FLAGREAD,
IRET => { panic!("todo: iret"); },
IRETD => { panic!("todo: iretd"); },
IRETQ => { panic!("todo: iretq"); },
@@ -1634,7 +1766,7 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
PUSHF => BehaviorDigest::empty()
.set_implicit_ops(PUSHF_IDX)
.set_pl_any(),
- WAIT => BehaviorDigest::empty().set_pl_any(),
+ WAIT => GENERAL,
CBW => BehaviorDigest::empty()
.set_implicit_ops(CBW_IDX)
.set_pl_any(),
@@ -1662,7 +1794,7 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
TEST => GENERAL_R_R_FLAGWRITE,
IN => { panic!("todo: in"); },
OUT => { panic!("todo: out"); },
- IMUL => { panic!("todo: imul"); },
+ IMUL => BehaviorDigest::empty(), // unreachable due to branch above match
JO => JCC,
JNO => JCC,
JB => JCC,
@@ -1697,7 +1829,7 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
CMOVZ => { panic!("todo: cmovz"); },
DIV => { panic!("todo: div"); },
IDIV => { panic!("todo: idiv"); },
- MUL => { panic!("todo: mul"); },
+ MUL => BehaviorDigest::empty(), // unreachable due to branch above match
SETO => { panic!("todo: seto"); },
SETNO => { panic!("todo: setno"); },
SETB => { panic!("todo: setb"); },
@@ -1715,11 +1847,13 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
SETLE => { panic!("todo: setle"); },
SETG => { panic!("todo: setg"); },
CPUID => { panic!("todo: cpuid"); },
- UD0 => { panic!("todo: ud0"); },
- UD1 => { panic!("todo: ud1"); },
- UD2 => { panic!("todo: ud2"); },
- WBINVD => { panic!("todo: wbinvd"); },
- INVD => { panic!("todo: invd"); },
+ UD0 => GENERAL,
+ UD1 => GENERAL,
+ UD2 => GENERAL,
+ WBINVD => BehaviorDigest::empty()
+ .set_pl0(),
+ INVD => BehaviorDigest::empty()
+ .set_pl0(),
SYSRET => { panic!("todo: sysret"); },
CLTS => BehaviorDigest::empty()
.set_implicit_ops(CLTS_IDX)
@@ -1846,7 +1980,7 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
CVTTPD2DQ => { panic!("todo: cvttpd2dq"); },
DIVPS => { panic!("todo: divps"); },
DIVPD => { panic!("todo: divpd"); },
- EMMS => { panic!("todo: emms"); },
+ EMMS => GENERAL,
GETSEC => { panic!("todo: getsec"); },
LFS => { panic!("todo: lfs"); },
LGS => { panic!("todo: lgs"); },
@@ -2456,11 +2590,12 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
.set_pl_any(),
// TODO: none of x87 is verified well.. and what about the bits in the FPU status word..
+ // and what about pushes/pops from the x87 operand stack..
F2XM1 => { panic!("todo: f2xm1"); },
FABS => { panic!("todo: fabs"); },
FADD => GENERAL_RW_R,
FADDP => GENERAL_RW_R,
- FBLD => { panic!("todo: fbld"); },
+ FBLD => GENERAL_W_R,
FBSTP => { panic!("todo: fbstp"); },
FCHS => { panic!("todo: fchs"); },
FCMOVB => { panic!("todo: fcmovb"); },
@@ -2471,15 +2606,15 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
FCMOVNE => { panic!("todo: fcmovne"); },
FCMOVNU => { panic!("todo: fcmovnu"); },
FCMOVU => { panic!("todo: fcmovu"); },
- FCOM => { panic!("todo: fcom"); },
+ FCOM => GENERAL_R_R,
FCOMI => { panic!("todo: fcomi"); },
FCOMIP => { panic!("todo: fcomip"); },
- FCOMP => { panic!("todo: fcomp"); },
- FCOMPP => { panic!("todo: fcompp"); },
+ FCOMP => GENERAL_R_R,
+ FCOMPP => GENERAL_R_R,
FCOS => { panic!("todo: fcos"); },
FDECSTP => { panic!("todo: fdecstp"); },
FDISI8087_NOP => { panic!("todo: fdisi8087_nop"); },
- FDIV => { panic!("todo: fdiv"); },
+ FDIV => GENERAL_RW_R,
FDIVP => { panic!("todo: fdivp"); },
FDIVR => { panic!("todo: fdivr"); },
FDIVRP => { panic!("todo: fdivrp"); },
@@ -2487,23 +2622,23 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
FFREE => { panic!("todo: ffree"); },
FFREEP => { panic!("todo: ffreep"); },
FIADD => GENERAL_RW_R,
- FICOM => { panic!("todo: ficom"); },
- FICOMP => { panic!("todo: ficomp"); },
+ FICOM => GENERAL_R_R,
+ FICOMP => GENERAL_R_R,
FIDIV => { panic!("todo: fidiv"); },
FIDIVR => { panic!("todo: fidivr"); },
// TODO: writing to st(0) is only kind of accurate, this *pushes* to the operand stack..
FILD => GENERAL_W_R,
- FIMUL => { panic!("todo: fimul"); },
+ FIMUL => GENERAL_RW_R,
FINCSTP => { panic!("todo: fincstp"); },
- FIST => { panic!("todo: fist"); },
- FISTP => { panic!("todo: fistp"); },
- FISTTP => { panic!("todo: fisttp"); },
- FISUB => { panic!("todo: fisub"); },
- FISUBR => { panic!("todo: fisubr"); },
+ FIST => GENERAL_W_R,
+ FISTP => GENERAL_W_R,
+ FISTTP => GENERAL_W_R,
+ FISUB => GENERAL_RW_R,
+ FISUBR => GENERAL_RW_R,
// TODO: writing to st(0) is only kind of accurate, this *pushes* to the operand stack..
FLD => GENERAL_W_R,
FLD1 => { panic!("todo: fld1"); },
- FLDCW => { panic!("todo: fldcw"); },
+ FLDCW => GENERAL_R,
FLDENV => { panic!("todo: fldenv"); },
FLDL2E => { panic!("todo: fldl2e"); },
FLDL2T => { panic!("todo: fldl2t"); },
@@ -2511,7 +2646,7 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
FLDLN2 => { panic!("todo: fldln2"); },
FLDPI => { panic!("todo: fldpi"); },
FLDZ => { panic!("todo: fldz"); },
- FMUL => { panic!("todo: fmul"); },
+ FMUL => GENERAL_RW_R,
FMULP => { panic!("todo: fmulp"); },
FNCLEX => { panic!("todo: fnclex"); },
FNINIT => { panic!("todo: fninit"); },
@@ -2532,19 +2667,19 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
FSIN => { panic!("todo: fsin"); },
FSINCOS => { panic!("todo: fsincos"); },
FSQRT => { panic!("todo: fsqrt"); },
- FST => { panic!("todo: fst"); },
- FSTP => { panic!("todo: fstp"); },
+ FST => GENERAL_W_R,
+ FSTP => GENERAL_W_R,
FSTPNCE => { panic!("todo: fstpnce"); },
- FSUB => { panic!("todo: fsub"); },
- FSUBP => { panic!("todo: fsubp"); },
- FSUBR => { panic!("todo: fsubr"); },
- FSUBRP => { panic!("todo: fsubrp"); },
+ FSUB => GENERAL_RW_R,
+ FSUBP => GENERAL_RW_R,
+ FSUBR => GENERAL_RW_R,
+ FSUBRP => GENERAL_RW_R,
FTST => { panic!("todo: ftst"); },
- FUCOM => { panic!("todo: fucom"); },
- FUCOMI => { panic!("todo: fucomi"); },
- FUCOMIP => { panic!("todo: fucomip"); },
- FUCOMP => { panic!("todo: fucomp"); },
- FUCOMPP => { panic!("todo: fucompp"); },
+ FUCOM => GENERAL_R_R,
+ FUCOMI => GENERAL_R_R_FLAGWRITE,
+ FUCOMIP => GENERAL_R_R_FLAGWRITE,
+ FUCOMP => GENERAL_R_R,
+ FUCOMPP => GENERAL_R_R,
FXAM => { panic!("todo: fxam"); },
FXCH => { panic!("todo: fxch"); },
FXTRACT => { panic!("todo: fxtract"); },
@@ -2576,8 +2711,8 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
// unsure
HRESET => { panic!("todo: hreset"); },
- // 3dnow
- FEMMS => { panic!("todo: femms"); },
+ // 3dnow. note these are yet untested!
+ FEMMS => GENERAL,
PI2FW => { panic!("todo: pi2fw"); },
PI2FD => { panic!("todo: pi2fd"); },
PF2IW => { panic!("todo: pf2iw"); },
@@ -3029,5 +3164,6 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
RMPADJUST => { panic!("todo: rmpadjust"); },
RMPUPDATE => { panic!("todo: rmpupdate"); },
- }
+ };
+ Some(behavior)
}