aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2026-03-09 06:41:51 +0000
committeriximeow <me@iximeow.net>2026-03-09 06:41:51 +0000
commit542edc60acdd5d06f46702775f966d194b615c13 (patch)
treea969b51b1adf97f006be2db66ef91092c7e40ba0
parent7b90eec718c9e911232e9d586b7f063255dabf88 (diff)
api and more inst behavior
-rw-r--r--src/long_mode/behavior.rs238
1 files changed, 209 insertions, 29 deletions
diff --git a/src/long_mode/behavior.rs b/src/long_mode/behavior.rs
index 3eb616f..c0910c3 100644
--- a/src/long_mode/behavior.rs
+++ b/src/long_mode/behavior.rs
@@ -214,7 +214,7 @@ pub struct InstOperands<'inst> {
}
impl<'inst> InstOperands<'inst> {
- fn iter(self) -> AccessIter<'inst> {
+ pub fn iter(self) -> AccessIter<'inst> {
AccessIter::new(self)
}
}
@@ -234,7 +234,7 @@ impl<'inst> AccessIter<'inst> {
}
}
- fn operands(self) -> OperandIter<'inst> {
+ pub fn operands(self) -> OperandIter<'inst> {
OperandIter { inner: self }
}
}
@@ -978,6 +978,12 @@ const GENERAL_RW_RW: BehaviorDigest = GENERAL_RW_R
const GENERAL_RW_RW_FLAGWRITE: BehaviorDigest = GENERAL_RW_RW
.set_flags_access(Access::Write);
+// TODO: seems incredibly funky that jcc's operand is an immediate, when written like this..
+const JCC: BehaviorDigest = BehaviorDigest::empty()
+ .set_implicit_ops(JCC_OPS_IDX)
+ .set_pl_any()
+ .set_operand(0, Access::Read);
+
static PUSH_OPS: &'static [ImplicitOperand] = &[
ImplicitOperand {
spec: OperandSpec::Disp,
@@ -1021,13 +1027,173 @@ static POP_OPS: &'static [ImplicitOperand] = &[
}
];
+static JCC_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rflags(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rip(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CBW_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::al(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CWDE_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CDQE_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rax(),
+ disp: 0,
+ write: true,
+ }
+];
+
+// note CQD, CDQ, CQO:
+//
+// these are writes to dx/edx/rdx but *not* `*ax`. this is because while these registers "write"
+// sign-extended *ax to *ax:*dx, "writes" to eax:edx do not modify the upper 32 bits of rax. that
+// is to say, that if `rax` is 0x8000_1234_c000_0000 and a `cdq` is executed, the result is:
+// ```
+// rax = 0x8000_1234_c000_0000
+// rdx = 0x0000_0000_ffff_ffff
+// ```
+//
+// cool, huh!!
+static CWD_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::dx(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CDQ_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CQO_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rdx(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static PUSHF_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rflags(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Disp,
+ reg: RegSpec::rsp(),
+ disp: -8i32,
+ write: true,
+ },
+ // push.. pushes the value (above), then does a RMW on rsp.
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rsp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::rsp(),
+ disp: 0,
+ write: true,
+ }
+];
+
const PUSH_OPS_IDX: u16 = 1;
const POP_OPS_IDX: u16 = 2;
-
-static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 3] = [
+const JCC_OPS_IDX: u16 = 3;
+const CBW_IDX: u16 = 4;
+const CWDE_IDX: u16 = 5;
+const CDQE_IDX: u16 = 6;
+const CWD_IDX: u16 = 7;
+const CDQ_IDX: u16 = 8;
+const CQO_IDX: u16 = 9;
+const PUSHF_IDX: u16 = 10;
+
+static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 11] = [
&[], // implicit ops list 0 is not used
PUSH_OPS,
POP_OPS,
+ JCC_OPS,
+ CBW_OPS,
+ CWDE_OPS,
+ CDQE_OPS,
+ CWD_OPS,
+ CDQ_OPS,
+ CQO_OPS,
+ PUSHF_OPS,
];
fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
@@ -1147,36 +1313,50 @@ fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
LEAVE => { panic!("todo: leave"); },
MOV => GENERAL_RW_R,
RETURN => { panic!("todo: return"); },
- PUSHF => { panic!("todo: pushf"); },
- WAIT => { panic!("todo: wait"); },
- CBW => { panic!("todo: cbw"); },
- CWDE => { panic!("todo: cwde"); },
- CDQE => { panic!("todo: cdqe"); },
- CWD => { panic!("todo: cwd"); },
- CDQ => { panic!("todo: cdq"); },
- CQO => { panic!("todo: cqo"); },
+ PUSHF => BehaviorDigest::empty()
+ .set_implicit_ops(PUSHF_IDX)
+ .set_pl_any(),
+ WAIT => BehaviorDigest::empty().set_pl_any(),
+ CBW => BehaviorDigest::empty()
+ .set_implicit_ops(CBW_IDX)
+ .set_pl_any(),
+ CWDE => BehaviorDigest::empty()
+ .set_implicit_ops(CWDE_IDX)
+ .set_pl_any(),
+ CDQE => BehaviorDigest::empty()
+ .set_implicit_ops(CDQE_IDX)
+ .set_pl_any(),
+ CWD => BehaviorDigest::empty()
+ .set_implicit_ops(CWD_IDX)
+ .set_pl_any(),
+ CDQ => BehaviorDigest::empty()
+ .set_implicit_ops(CDQ_IDX)
+ .set_pl_any(),
+ CQO => BehaviorDigest::empty()
+ .set_implicit_ops(CQO_IDX)
+ .set_pl_any(),
LAHF => { panic!("todo: lahf"); },
SAHF => { panic!("todo: sahf"); },
- TEST => { panic!("todo: test"); },
+ TEST => GENERAL_R_R_FLAGWRITE,
IN => { panic!("todo: in"); },
OUT => { panic!("todo: out"); },
IMUL => { panic!("todo: imul"); },
- JO => { panic!("todo: jo"); },
- JNO => { panic!("todo: jno"); },
- JB => { panic!("todo: jb"); },
- JNB => { panic!("todo: jnb"); },
- JZ => { panic!("todo: jz"); },
- JNZ => { panic!("todo: jnz"); },
- JA => { panic!("todo: ja"); },
- JNA => { panic!("todo: jna"); },
- JS => { panic!("todo: js"); },
- JNS => { panic!("todo: jns"); },
- JP => { panic!("todo: jp"); },
- JNP => { panic!("todo: jnp"); },
- JL => { panic!("todo: jl"); },
- JGE => { panic!("todo: jge"); },
- JLE => { panic!("todo: jle"); },
- JG => { panic!("todo: jg"); },
+ JO => JCC,
+ JNO => JCC,
+ JB => JCC,
+ JNB => JCC,
+ JZ => JCC,
+ JNZ => JCC,
+ JA => JCC,
+ JNA => JCC,
+ JS => JCC,
+ JNS => JCC,
+ JP => JCC,
+ JNP => JCC,
+ JL => JCC,
+ JGE => JCC,
+ JLE => JCC,
+ JG => JCC,
CMOVA => { panic!("todo: cmova"); },
CMOVB => { panic!("todo: cmovb"); },
CMOVG => { panic!("todo: cmovg"); },