aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2024-06-23 12:53:41 -0700
committeriximeow <me@iximeow.net>2024-06-23 12:53:41 -0700
commitb1213132953ff18efaa126e0b3f9f22e7ea44ed3 (patch)
treedf09affb52bbe8ac2ef7e2308dc3f37db23844c3
parenteec945bb991989a9dfcef2d5334746ba863e0abc (diff)
adapt the rest of formating changes to protected_mode
-rw-r--r--src/long_mode/display.rs6
-rw-r--r--src/protected_mode/display.rs469
-rw-r--r--src/protected_mode/mod.rs165
3 files changed, 480 insertions, 160 deletions
diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs
index bcea94a..869ac0b 100644
--- a/src/long_mode/display.rs
+++ b/src/long_mode/display.rs
@@ -272,7 +272,7 @@ struct DisplayingOperandVisitor<'a, T> {
f: &'a mut T,
}
-impl <T: DisplaySink> crate::long_mode::OperandVisitor for DisplayingOperandVisitor<'_, T> {
+impl <T: DisplaySink> super::OperandVisitor for DisplayingOperandVisitor<'_, T> {
type Ok = ();
type Error = core::fmt::Error;
@@ -4237,7 +4237,7 @@ struct RelativeBranchPrinter<'a, F: DisplaySink> {
out: &'a mut F,
}
-impl<'a, F: DisplaySink> crate::long_mode::OperandVisitor for RelativeBranchPrinter<'a, F> {
+impl<'a, F: DisplaySink> super::OperandVisitor for RelativeBranchPrinter<'a, F> {
// return true if we printed a relative branch offset, false otherwise
type Ok = bool;
// but errors are errors
@@ -4336,7 +4336,7 @@ impl<'a, F: DisplaySink> crate::long_mode::OperandVisitor for RelativeBranchPrin
fn visit_reg_mask_merge(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode) -> Result<Self::Ok, Self::Error> {
Ok(false)
}
- fn visit_reg_mask_merge_sae(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode, _sae_mode: crate::long_mode::SaeMode) -> Result<Self::Ok, Self::Error> {
+ fn visit_reg_mask_merge_sae(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode, _sae_mode: super::SaeMode) -> Result<Self::Ok, Self::Error> {
Ok(false)
}
fn visit_reg_mask_merge_sae_noround(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode) -> Result<Self::Ok, Self::Error> {
diff --git a/src/protected_mode/display.rs b/src/protected_mode/display.rs
index 635893c..46bb185 100644
--- a/src/protected_mode/display.rs
+++ b/src/protected_mode/display.rs
@@ -187,6 +187,19 @@ impl fmt::Display for PrefixVex {
}
}
+impl Segment {
+ fn name(&self) -> &'static [u8; 2] {
+ match self {
+ Segment::CS => b"cs",
+ Segment::DS => b"ds",
+ Segment::ES => b"es",
+ Segment::FS => b"fs",
+ Segment::GS => b"gs",
+ Segment::SS => b"ss",
+ }
+ }
+}
+
impl fmt::Display for Segment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@@ -257,7 +270,7 @@ struct DisplayingOperandVisitor<'a, T> {
f: &'a mut T,
}
-impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperandVisitor<'_, T> {
+impl <T: DisplaySink> super::OperandVisitor for DisplayingOperandVisitor<'_, T> {
type Ok = ();
type Error = core::fmt::Error;
@@ -3628,155 +3641,140 @@ impl Instruction {
}
}
-fn contextualize_intel<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result {
+pub(crate) fn contextualize_intel<T: DisplaySink>(instr: &Instruction, out: &mut T) -> fmt::Result {
if instr.xacquire() {
- out.write_str("xacquire ")?;
+ out.write_fixed_size("xacquire ")?;
}
if instr.xrelease() {
- out.write_str("xrelease ")?;
+ out.write_fixed_size("xrelease ")?;
}
if instr.prefixes.lock() {
- out.write_str("lock ")?;
+ out.write_fixed_size("lock ")?;
}
if instr.prefixes.rep_any() {
if instr.opcode.can_rep() {
if instr.prefixes.rep() {
- write!(out, "rep ")?;
+ out.write_fixed_size("rep ")?;
} else if instr.prefixes.repnz() {
- write!(out, "repnz ")?;
+ out.write_fixed_size("repnz ")?;
}
}
}
- out.write_str(instr.opcode.name())?;
-
- if instr.opcode == Opcode::XBEGIN {
- if (instr.imm as i32) >= 0 {
- return write!(out, " $+{}", colors.number(signed_i32_hex(instr.imm as i32)));
- } else {
- return write!(out, " ${}", colors.number(signed_i32_hex(instr.imm as i32)));
- }
- }
+ out.write_opcode(instr.opcode)?;
if instr.operand_count > 0 {
- out.write_str(" ")?;
-
- let x = Operand::from_spec(instr, instr.operands[0]);
-
- const RELATIVE_BRANCHES: [Opcode; 21] = [
- Opcode::JMP, Opcode::JECXZ,
- Opcode::LOOP, Opcode::LOOPZ, Opcode::LOOPNZ,
- Opcode::JO, Opcode::JNO,
- Opcode::JB, Opcode::JNB,
- Opcode::JZ, Opcode::JNZ,
- Opcode::JNA, Opcode::JA,
- Opcode::JS, Opcode::JNS,
- Opcode::JP, Opcode::JNP,
- Opcode::JL, Opcode::JGE,
- Opcode::JLE, Opcode::JG,
- ];
-
- if instr.operands[0] == OperandSpec::ImmI8 || instr.operands[0] == OperandSpec::ImmI32 {
- if RELATIVE_BRANCHES.contains(&instr.opcode) {
- return match x {
- Operand::ImmediateI8(rel) => {
- if rel >= 0 {
- write!(out, "$+{}", colors.number(signed_i32_hex(rel as i32)))
- } else {
- write!(out, "${}", colors.number(signed_i32_hex(rel as i32)))
- }
- }
- Operand::ImmediateI32(rel) => {
- if rel >= 0 {
- write!(out, "$+{}", colors.number(signed_i32_hex(rel)))
- } else {
- write!(out, "${}", colors.number(signed_i32_hex(rel)))
- }
- }
- _ => { unreachable!() }
- };
- }
- }
+ out.write_fixed_size(" ")?;
- if x.is_memory() {
- out.write_str(MEM_SIZE_STRINGS[instr.mem_size as usize])?;
- out.write_str(" ")?;
+ if instr.visit_operand(0, &mut RelativeBranchPrinter {
+ inst: instr,
+ out,
+ })? {
+ return Ok(());
}
- if let Some(prefix) = instr.segment_override_for_op(0) {
- write!(out, "{}:", prefix)?;
+ if instr.operands[0 as usize].is_memory() {
+ out.write_mem_size_label(instr.mem_size)?;
+ if let Some(prefix) = instr.segment_override_for_op(0) {
+ let name = prefix.name();
+ out.write_char(' ')?;
+ out.write_char(name[0] as char)?;
+ out.write_char(name[1] as char)?;
+ out.write_fixed_size(":")?;
+ } else {
+ out.write_fixed_size(" ")?;
+ }
}
- x.colorize(colors, out)?;
+
+ let mut displayer = DisplayingOperandVisitor {
+ f: out,
+ };
+ instr.visit_operand(0 as u8, &mut displayer)?;
for i in 1..instr.operand_count {
- match instr.opcode {
- _ => {
- match &instr.operands[i as usize] {
- &OperandSpec::Nothing => {
- return Ok(());
- },
- _ => {
- out.write_str(", ")?;
- let x = Operand::from_spec(instr, instr.operands[i as usize]);
- if x.is_memory() {
- out.write_str(MEM_SIZE_STRINGS[instr.mem_size as usize])?;
- out.write_str(" ")?;
+ // don't worry about checking for `instr.operands[i] != Nothing`, it would be a bug to
+ // reach that while iterating only to `operand_count`..
+ out.write_fixed_size(", ")?;
+ if i >= 4 {
+ unsafe { core::hint::unreachable_unchecked(); }
+ }
+
+ if instr.operands[i as usize].is_memory() {
+ out.write_mem_size_label(instr.mem_size)?;
+ if i >= 4 {
+ unsafe { core::hint::unreachable_unchecked(); }
+ }
+ if let Some(prefix) = instr.segment_override_for_op(i) {
+ let name = prefix.name();
+ out.write_char(' ')?;
+ out.write_char(name[0] as char)?;
+ out.write_char(name[1] as char)?;
+ out.write_fixed_size(":")?;
+ } else {
+ out.write_fixed_size(" ")?;
+ }
+ }
+
+ let mut displayer = DisplayingOperandVisitor {
+ f: out,
+ };
+
+ instr.visit_operand(i as u8, &mut displayer)?;
+ if let Some(evex) = instr.prefixes.evex() {
+ if evex.broadcast() && instr.operands[i as usize].is_memory() {
+ let scale = if instr.opcode == Opcode::VCVTPD2PS || instr.opcode == Opcode::VCVTTPD2UDQ || instr.opcode == Opcode::VCVTPD2UDQ || instr.opcode == Opcode::VCVTUDQ2PD || instr.opcode == Opcode::VCVTPS2PD || instr.opcode == Opcode::VCVTQQ2PS || instr.opcode == Opcode::VCVTDQ2PD || instr.opcode == Opcode::VCVTTPD2DQ || instr.opcode == Opcode::VFPCLASSPS || instr.opcode == Opcode::VFPCLASSPD || instr.opcode == Opcode::VCVTNEPS2BF16 || instr.opcode == Opcode::VCVTUQQ2PS || instr.opcode == Opcode::VCVTPD2DQ || instr.opcode == Opcode::VCVTTPS2UQQ || instr.opcode == Opcode::VCVTPS2UQQ || instr.opcode == Opcode::VCVTTPS2QQ || instr.opcode == Opcode::VCVTPS2QQ {
+ if instr.opcode == Opcode::VFPCLASSPS || instr.opcode == Opcode::VCVTNEPS2BF16 {
+ if evex.vex().l() {
+ 8
+ } else if evex.lp() {
+ 16
+ } else {
+ 4
}
- if let Some(prefix) = instr.segment_override_for_op(i) {
- write!(out, "{}:", prefix)?;
+ } else if instr.opcode == Opcode::VFPCLASSPD {
+ if evex.vex().l() {
+ 4
+ } else if evex.lp() {
+ 8
+ } else {
+ 2
}
- x.colorize(colors, out)?;
- if let Some(evex) = instr.prefixes.evex() {
- if evex.broadcast() && x.is_memory() {
- let scale = if instr.opcode == Opcode::VCVTPD2PS || instr.opcode == Opcode::VCVTTPD2UDQ || instr.opcode == Opcode::VCVTPD2UDQ || instr.opcode == Opcode::VCVTUDQ2PD || instr.opcode == Opcode::VCVTPS2PD || instr.opcode == Opcode::VCVTQQ2PS || instr.opcode == Opcode::VCVTDQ2PD || instr.opcode == Opcode::VCVTTPD2DQ || instr.opcode == Opcode::VFPCLASSPS || instr.opcode == Opcode::VFPCLASSPD || instr.opcode == Opcode::VCVTNEPS2BF16 || instr.opcode == Opcode::VCVTUQQ2PS || instr.opcode == Opcode::VCVTPD2DQ || instr.opcode == Opcode::VCVTTPS2UQQ || instr.opcode == Opcode::VCVTPS2UQQ || instr.opcode == Opcode::VCVTTPS2QQ || instr.opcode == Opcode::VCVTPS2QQ {
- if instr.opcode == Opcode::VFPCLASSPS || instr.opcode == Opcode::VCVTNEPS2BF16 {
- if evex.vex().l() {
- 8
- } else if evex.lp() {
- 16
- } else {
- 4
- }
- } else if instr.opcode == Opcode::VFPCLASSPD {
- if evex.vex().l() {
- 4
- } else if evex.lp() {
- 8
- } else {
- 2
- }
- } else {
- // vcvtpd2ps is "cool": in broadcast mode, it can read a
- // double-precision float (qword), resize to single-precision,
- // then broadcast that to the whole destination register. this
- // means we need to show `xmm, qword [addr]{1to4}` if vector
- // size is 256. likewise, scale of 8 for the same truncation
- // reason if vector size is 512.
- // vcvtudq2pd is the same story.
- // vfpclassp{s,d} is a mystery to me.
- if evex.vex().l() {
- 4
- } else if evex.lp() {
- 8
- } else {
- 2
- }
- }
- } else {
- // this should never be `None` - that would imply two
- // memory operands for a broadcasted operation.
- if let Some(width) = Operand::from_spec(instr, instr.operands[i as usize - 1]).width() {
- width / instr.mem_size
- } else {
- 0
- }
- };
- write!(out, "{{1to{}}}", scale)?;
- }
+ } else {
+ // vcvtpd2ps is "cool": in broadcast mode, it can read a
+ // double-precision float (qword), resize to single-precision,
+ // then broadcast that to the whole destination register. this
+ // means we need to show `xmm, qword [addr]{1to4}` if vector
+ // size is 256. likewise, scale of 8 for the same truncation
+ // reason if vector size is 512.
+ // vcvtudq2pd is the same story.
+ // vfpclassp{s,d} is a mystery to me.
+ if evex.vex().l() {
+ 4
+ } else if evex.lp() {
+ 8
+ } else {
+ 2
}
}
+ } else {
+ // this should never be `None` - that would imply two
+ // memory operands for a broadcasted operation.
+ if let Some(width) = Operand::from_spec(instr, instr.operands[i as usize - 1]).width() {
+ width / instr.mem_size
+ } else {
+ 0
+ }
+ };
+ out.write_fixed_size("{1to")?;
+ static STRING_LUT: &'static [&'static str] = &[
+ "0", "1", "2", "3", "4", "5", "6", "7", "8",
+ "9", "10", "11", "12", "13", "14", "15", "16",
+ ];
+ unsafe {
+ out.write_lt_16(STRING_LUT.get_kinda_unchecked(scale as usize))?;
}
+ out.write_char('}')?;
}
}
}
@@ -3784,7 +3782,7 @@ fn contextualize_intel<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors:
Ok(())
}
-fn contextualize_c<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result {
+pub(crate) fn contextualize_c<T: DisplaySink>(instr: &Instruction, out: &mut T) -> fmt::Result {
let mut brace_count = 0;
let mut prefixed = false;
@@ -3808,7 +3806,7 @@ fn contextualize_c<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y,
}
if instr.prefixes.rep_any() {
- if [Opcode::MOVS, Opcode::CMPS, Opcode::LODS, Opcode::STOS, Opcode::INS, Opcode::OUTS].contains(&instr.opcode) {
+ if instr.opcode.can_rep() {
let word_str = match instr.mem_size {
1 => "byte",
2 => "word",
@@ -3830,21 +3828,29 @@ fn contextualize_c<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y,
}
}
- fn write_jmp_operand<T: fmt::Write, Y: YaxColors>(op: Operand, colors: &Y, out: &mut T) -> fmt::Result {
+ fn write_jmp_operand<T: fmt::Write>(op: Operand, out: &mut T) -> fmt::Result {
+ let mut out = yaxpeax_arch::display::FmtSink::new(out);
+ use core::fmt::Write;
match op {
Operand::ImmediateI8(rel) => {
- if rel >= 0 {
- write!(out, "$+{}", colors.number(signed_i32_hex(rel as i32)))
+ let rel = if rel >= 0 {
+ out.write_str("$+")?;
+ rel as u8
} else {
- write!(out, "${}", colors.number(signed_i32_hex(rel as i32)))
- }
+ out.write_str("$-")?;
+ rel.unsigned_abs()
+ };
+ out.write_prefixed_u8(rel)
}
Operand::ImmediateI32(rel) => {
- if rel >= 0 {
- write!(out, "$+{}", colors.number(signed_i32_hex(rel)))
+ let rel = if rel >= 0 {
+ out.write_str("$+")?;
+ rel as u32
} else {
- write!(out, "${}", colors.number(signed_i32_hex(rel)))
- }
+ out.write_str("$-")?;
+ rel.unsigned_abs()
+ };
+ out.write_prefixed_u32(rel)
}
other => {
write!(out, "{}", other)
@@ -4007,87 +4013,87 @@ fn contextualize_c<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y,
}
Opcode::JMP => {
out.write_str("jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JECXZ => {
out.write_str("if ecx == 0 then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::LOOP => {
out.write_str("ecx--; if ecx != 0 then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::LOOPZ => {
out.write_str("ecx--; if ecx != 0 and zero(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::LOOPNZ => {
out.write_str("ecx--; if ecx != 0 and !zero(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JO => {
out.write_str("if _(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JNO => {
out.write_str("if _(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JB => {
out.write_str("if /* unsigned */ below(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JNB => {
out.write_str("if /* unsigned */ above_or_equal(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JZ => {
out.write_str("if zero(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JNZ => {
out.write_str("if !zero(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JNA => {
out.write_str("if /* unsigned */ below_or_equal(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JA => {
out.write_str("if /* unsigned */ above(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JS => {
out.write_str("if signed(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JNS => {
out.write_str("if !signed(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JP => {
out.write_str("if parity(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JNP => {
out.write_str("if !parity(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JL => {
out.write_str("if /* signed */ less(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JGE => {
out.write_str("if /* signed */ greater_or_equal(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JLE => {
out.write_str("if /* signed */ less_or_equal(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::JG => {
out.write_str("if /* signed */ greater(rflags) then jmp ")?;
- write_jmp_operand(instr.operand(0), colors, out)?;
+ write_jmp_operand(instr.operand(0), out)?;
},
Opcode::NOP => {
write!(out, "nop")?;
@@ -4119,18 +4125,20 @@ fn contextualize_c<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y,
}
impl <'instr, T: fmt::Write, Y: YaxColors> ShowContextual<u32, NoContext, T, Y> for InstructionDisplayer<'instr> {
- fn contextualize(&self, colors: &Y, address: u32, context: Option<&NoContext>, out: &mut T) -> fmt::Result {
+ fn contextualize(&self, _colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result {
let InstructionDisplayer {
instr,
style,
} = self;
+ let mut out = yaxpeax_arch::display::FmtSink::new(out);
+
match style {
DisplayStyle::Intel => {
- contextualize_intel(instr, colors, address, context, out)
+ contextualize_intel(instr, &mut out)
}
DisplayStyle::C => {
- contextualize_c(instr, colors, address, context, out)
+ contextualize_c(instr, &mut out)
}
}
}
@@ -4196,3 +4204,150 @@ impl <T: fmt::Write, Y: YaxColors> ShowContextual<u64, [Option<alloc::string::St
Ok(())
}
}
+
+// TODO: should include CALL
+static RELATIVE_BRANCHES: [Opcode; 21] = [
+ Opcode::JMP, Opcode::JECXZ,
+ Opcode::LOOP, Opcode::LOOPZ, Opcode::LOOPNZ,
+ Opcode::JO, Opcode::JNO,
+ Opcode::JB, Opcode::JNB,
+ Opcode::JZ, Opcode::JNZ,
+ Opcode::JNA, Opcode::JA,
+ Opcode::JS, Opcode::JNS,
+ Opcode::JP, Opcode::JNP,
+ Opcode::JL, Opcode::JGE,
+ Opcode::JLE, Opcode::JG,
+];
+
+struct RelativeBranchPrinter<'a, F: DisplaySink> {
+ inst: &'a Instruction,
+ out: &'a mut F,
+}
+
+impl<'a, F: DisplaySink> super::OperandVisitor for RelativeBranchPrinter<'a, F> {
+ // return true if we printed a relative branch offset, false otherwise
+ type Ok = bool;
+ // but errors are errors
+ type Error = fmt::Error;
+
+ fn visit_reg(&mut self, _reg: RegSpec) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_deref(&mut self, _reg: RegSpec) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_disp(&mut self, _reg: RegSpec, _disp: i32) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ #[cfg_attr(feature="profiling", inline(never))]
+ fn visit_i8(&mut self, rel: i8) -> Result<Self::Ok, Self::Error> {
+ if RELATIVE_BRANCHES.contains(&self.inst.opcode) {
+ self.out.write_char('$')?;
+ // danger_anguished_string_write(self.out, "$");
+ let mut v = rel as u8;
+ if rel < 0 {
+ self.out.write_char('-')?;
+ //danger_anguished_string_write(&mut self.out, "-");
+ v = -rel as u8;
+ } else {
+ self.out.write_char('+')?;
+ // danger_anguished_string_write(&mut self.out, "+");
+ }
+ self.out.write_fixed_size("0x")?;
+ self.out.write_u8(v)?;
+ Ok(true)
+ } else {
+ Ok(false)
+ }
+ }
+ #[cfg_attr(feature="profiling", inline(never))]
+ fn visit_i32(&mut self, rel: i32) -> Result<Self::Ok, Self::Error> {
+ if RELATIVE_BRANCHES.contains(&self.inst.opcode) || self.inst.opcode == Opcode::XBEGIN {
+ self.out.write_char('$')?;
+ // danger_anguished_string_write(self.out, "$");
+ let mut v = rel as u32;
+ if rel < 0 {
+ self.out.write_char('-')?;
+ // danger_anguished_string_write(&mut self.out, "-");
+ v = -rel as u32;
+ } else {
+ self.out.write_char('+')?;
+ // danger_anguished_string_write(&mut self.out, "+");
+ }
+ self.out.write_fixed_size("0x")?;
+ self.out.write_u32(v)?;
+ Ok(true)
+ } else {
+ Ok(false)
+ }
+ }
+ fn visit_u8(&mut self, _imm: u8) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_i16(&mut self, _imm: i16) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_u16(&mut self, _imm: u16) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_u32(&mut self, _imm: u32) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_abs_u16(&mut self, _imm: u16) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_abs_u32(&mut self, _imm: u32) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_reg_scale(&mut self, _reg: RegSpec, _scale: u8) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_index_base_scale(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_reg_scale_disp(&mut self, _reg: RegSpec, _scale: u8, _disp: i32) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_index_base_scale_disp(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _disp: i32) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_other(&mut self) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_reg_mask_merge(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_reg_mask_merge_sae(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode, _sae_mode: super::SaeMode) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_reg_mask_merge_sae_noround(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_reg_disp_masked(&mut self, _spec: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_reg_deref_masked(&mut self, _spec: RegSpec, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_reg_scale_masked(&mut self, _spec: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_reg_scale_disp_masked(&mut self, _spec: RegSpec, _scale: u8, _disp: i32, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_index_base_masked(&mut self, _base: RegSpec, _index: RegSpec, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_index_base_disp_masked(&mut self, _base: RegSpec, _index: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_index_base_scale_masked(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_index_base_scale_disp_masked(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _disp: i32, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+ fn visit_absolute_far_address(&mut self, _segment: u16, _address: u32) -> Result<Self::Ok, Self::Error> {
+ Ok(false)
+ }
+}
diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs
index 7c7a4c0..74522d9 100644
--- a/src/protected_mode/mod.rs
+++ b/src/protected_mode/mod.rs
@@ -4332,6 +4332,171 @@ impl Instruction {
Operand::from_spec(self, self.operands[i as usize])
}
+ /// TODO: make public, document, etc...
+ ///
+ /// `visit_operand` allows code using operands to better specialize and inline with the logic
+ /// that would construct an [`Operand`] variant, without having to necessarily construct an
+ /// `Operand` (including the attendant move of the enum).
+ ///
+ /// if the work you expect to do per-operand is very small, constructing an `Operand` and
+ /// dispatching on tags may be a substantial factor of overall runtime. `visit_operand` can
+ /// reduce total overhead in such cases.
+ #[cfg_attr(features="profiling", inline(never))]
+ fn visit_operand<T: OperandVisitor>(&self, i: u8, visitor: &mut T) -> Result<T::Ok, T::Error> {
+ assert!(i < 4);
+ let spec = self.operands[i as usize];
+ match spec {
+ OperandSpec::Nothing => {
+ visitor.visit_other()
+ }
+ OperandSpec::RegRRR => {
+ visitor.visit_reg(self.regs[0])
+ }
+ OperandSpec::RegMMM => {
+ visitor.visit_reg(self.regs[1])
+ }
+ OperandSpec::RegVex => {
+ visitor.visit_reg(self.regs[3])
+ }
+ OperandSpec::Reg4 => {
+ visitor.visit_reg(RegSpec { num: self.imm as u8, bank: self.regs[3].bank })
+ }
+ OperandSpec::Deref => {
+ visitor.visit_deref(self.regs[1])
+ }
+ OperandSpec::Deref_si => {
+// visitor.visit_other()
+ visitor.visit_deref(RegSpec::si())
+ }
+ OperandSpec::Deref_di => {
+// visitor.visit_other()
+ visitor.visit_deref(RegSpec::di())
+ }
+ OperandSpec::Deref_esi => {
+// visitor.visit_other()
+ visitor.visit_deref(RegSpec::esi())
+ }
+ OperandSpec::Deref_edi => {
+// visitor.visit_other()
+ visitor.visit_deref(RegSpec::edi())
+ }
+ OperandSpec::RegDisp => {
+ visitor.visit_disp(self.regs[1], self.disp as i32)
+ }
+ OperandSpec::RegRRR_maskmerge => {
+ visitor.visit_reg_mask_merge(
+ self.regs[0],
+ RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()),
+ MergeMode::from(self.prefixes.evex_unchecked().merge()),
+ )
+ }
+ OperandSpec::RegRRR_maskmerge_sae => {
+ visitor.visit_reg_mask_merge_sae(
+ self.regs[0],
+ RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()),
+ MergeMode::from(self.prefixes.evex_unchecked().merge()),
+ SaeMode::from(self.prefixes.evex_unchecked().vex().l(), self.prefixes.evex_unchecked().lp()),
+ )
+ }
+ OperandSpec::RegRRR_maskmerge_sae_noround => {
+ visitor.visit_reg_mask_merge_sae_noround(
+ self.regs[0],
+ RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()),
+ MergeMode::from(self.prefixes.evex_unchecked().merge()),
+ )
+ }
+ OperandSpec::RegMMM_maskmerge => {
+ visitor.visit_reg_mask_merge(
+ self.regs[1],
+ RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()),
+ MergeMode::from(self.prefixes.evex_unchecked().merge()),
+ )
+ }
+ OperandSpec::RegMMM_maskmerge_sae_noround => {
+ visitor.visit_reg_mask_merge_sae_noround(
+ self.regs[1],
+ RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()),
+ MergeMode::from(self.prefixes.evex_unchecked().merge()),
+ )
+ }
+ OperandSpec::RegVex_maskmerge => {
+ visitor.visit_reg_mask_merge(
+ self.regs[3],
+ RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()),
+ MergeMode::from(self.prefixes.evex_unchecked().merge()),
+ )
+ }
+ OperandSpec::ImmI8 => visitor.visit_i8(self.imm as i8),
+ OperandSpec::ImmU8 => visitor.visit_u8(self.imm as u8),
+ OperandSpec::ImmI16 => visitor.visit_i16(self.imm as i16),
+ OperandSpec::ImmU16 => visitor.visit_u16(self.imm as u16),
+ OperandSpec::ImmI32 => visitor.visit_i32(self.imm as i32),
+ OperandSpec::ImmInDispField => visitor.visit_u16(self.disp as u16),
+ OperandSpec::DispU16 => visitor.visit_abs_u16(self.disp as u16),
+ OperandSpec::DispU32 => visitor.visit_abs_u32(self.disp as u32),
+ OperandSpec::RegScale => {
+ visitor.visit_reg_scale(self.regs[2], self.scale)
+ }
+ OperandSpec::RegScaleDisp => {
+ visitor.visit_reg_scale_disp(self.regs[2], self.scale, self.disp as i32)
+ }
+ OperandSpec::RegIndexBaseScale => {
+ visitor.visit_index_base_scale(self.regs[1], self.regs[2], self.scale)
+ /*
+ Operand::RegIndexBaseScale(self.regs[1], self.regs[2], self.scale)
+ */
+ }
+ OperandSpec::RegIndexBaseScaleDisp => {
+ visitor.visit_index_base_scale_disp(self.regs[1], self.regs[2], self.scale, self.disp as i32)
+ }
+ OperandSpec::Deref_mask => {
+ if self.prefixes.evex_unchecked().mask_reg() != 0 {
+ visitor.visit_reg_deref_masked(self.regs[1], RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()))
+ } else {
+ visitor.visit_deref(self.regs[1])
+ }
+ }
+ OperandSpec::RegDisp_mask => {
+ if self.prefixes.evex_unchecked().mask_reg() != 0 {
+ visitor.visit_reg_disp_masked(self.regs[1], self.disp as i32, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()))
+ } else {
+ visitor.visit_disp(self.regs[1], self.disp as i32)
+ }
+ }
+ OperandSpec::RegScale_mask => {
+ if self.prefixes.evex_unchecked().mask_reg() != 0 {
+ visitor.visit_reg_scale_masked(self.regs[2], self.scale, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()))
+ } else {
+ visitor.visit_reg_scale(self.regs[2], self.scale)
+ }
+ }
+ OperandSpec::RegScaleDisp_mask => {
+ if self.prefixes.evex_unchecked().mask_reg() != 0 {
+ visitor.visit_reg_scale_disp_masked(self.regs[2], self.scale, self.disp as i32, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()))
+ } else {
+ visitor.visit_reg_scale_disp(self.regs[2], self.scale, self.disp as i32)
+ }
+ }
+ OperandSpec::RegIndexBaseScale_mask => {
+ if self.prefixes.evex_unchecked().mask_reg() != 0 {
+ visitor.visit_index_base_scale_masked(self.regs[1], self.regs[2], self.scale, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()))
+ } else {
+ visitor.visit_index_base_scale(self.regs[1], self.regs[2], self.scale)
+ }
+ }
+ OperandSpec::RegIndexBaseScaleDisp_mask => {
+ if self.prefixes.evex_unchecked().mask_reg() != 0 {
+ visitor.visit_index_base_scale_disp_masked(self.regs[1], self.regs[2], self.scale, self.disp as i32, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()))
+ } else {
+ visitor.visit_index_base_scale_disp(self.regs[1], self.regs[2], self.scale, self.disp as i32)
+ }
+ }
+ OperandSpec::AbsoluteFarAddress => {
+ visitor.visit_absolute_far_address(self.disp as u16, self.imm as u32)
+ }
+ }
+ }
+
/// get the number of operands in this instruction. useful in iterating an instruction's
/// operands generically.
pub fn operand_count(&self) -> u8 {