mirror of https://github.com/icedland/iced.git
1397 lines
48 KiB
C#
1397 lines
48 KiB
C#
/*
|
|
Copyright (C) 2018-2019 de4dot@gmail.com
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
"Software"), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
using System;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
|
|
namespace Iced.Intel {
|
|
/// <summary>
|
|
/// A 16/32/64-bit instruction
|
|
/// </summary>
|
|
public partial struct Instruction : IEquatable<Instruction> {
|
|
internal const int TEST_OpKindBits = (int)OpKindFlags.OpKindBits;
|
|
internal const int TEST_CodeBits = (int)CodeFlags.CodeBits;
|
|
internal const int TEST_RegisterBits = 8;
|
|
|
|
/// <summary>
|
|
/// [1:0] = Scale
|
|
/// [4:2] = Size of displacement: 0, 1, 2, 4, 8
|
|
/// [7:5] = Segment register prefix: none, es, cs, ss, ds, fs, gs, reserved
|
|
/// [14:8] = Not used
|
|
/// [15] = Broadcasted memory
|
|
/// </summary>
|
|
[Flags]
|
|
enum MemoryFlags : ushort {
|
|
ScaleMask = 3,
|
|
DisplSizeShift = 2,
|
|
DisplSizeMask = 7,
|
|
SegmentPrefixShift = 5,
|
|
SegmentPrefixMask = 7,
|
|
// Unused bits here
|
|
BroadcastedMemory = 0x8000,
|
|
}
|
|
|
|
/// <summary>
|
|
/// [4:0] = Operand #0's <see cref="OpKind"/>
|
|
/// [9:5] = Operand #1's <see cref="OpKind"/>
|
|
/// [14:10] = Operand #2's <see cref="OpKind"/>
|
|
/// [19:15] = Operand #3's <see cref="OpKind"/>
|
|
/// [29:20] = Not used
|
|
/// [31:30] = CodeSize
|
|
/// </summary>
|
|
[Flags]
|
|
enum OpKindFlags : uint {
|
|
OpKindBits = 5,
|
|
OpKindMask = (1 << (int)OpKindBits) - 1,
|
|
Op1KindShift = 5,
|
|
Op2KindShift = 10,
|
|
Op3KindShift = 15,
|
|
// Unused bits here
|
|
CodeSizeMask = 3,
|
|
CodeSizeShift = 30,
|
|
|
|
// Bits ignored by Equals()
|
|
EqualsIgnoreMask = CodeSizeMask << (int)CodeSizeShift,
|
|
}
|
|
|
|
/// <summary>
|
|
/// [12:0] = <see cref="Intel.Code"/>
|
|
/// [15:13] = <see cref="Intel.RoundingControl"/>
|
|
/// [18:16] = Opmask register or 0 if none
|
|
/// [22:19] = Instruction length
|
|
/// [24:23] = Not used
|
|
/// [25] = Suppress all exceptions
|
|
/// [26] = Zeroing masking
|
|
/// [27] = xacquire prefix
|
|
/// [28] = xrelease prefix
|
|
/// [29] = repe prefix
|
|
/// [30] = repne prefix
|
|
/// [31] = lock prefix
|
|
/// </summary>
|
|
[Flags]
|
|
enum CodeFlags : uint {
|
|
CodeBits = 13,
|
|
CodeMask = (1 << (int)CodeBits) - 1,
|
|
RoundingControlMask = 7,
|
|
RoundingControlShift = 13,
|
|
OpMaskMask = 7,
|
|
OpMaskShift = 16,
|
|
InstrLengthMask = 0xF,
|
|
InstrLengthShift = 19,
|
|
// Unused bits here
|
|
SuppressAllExceptions = 0x02000000,
|
|
ZeroingMasking = 0x04000000,
|
|
XacquirePrefix = 0x08000000,
|
|
XreleasePrefix = 0x10000000,
|
|
RepePrefix = 0x20000000,
|
|
RepnePrefix = 0x40000000,
|
|
LockPrefix = 0x80000000,
|
|
|
|
// Bits ignored by Equals()
|
|
EqualsIgnoreMask = InstrLengthMask << (int)InstrLengthShift,
|
|
}
|
|
|
|
// All fields, size: 32 bytes with bits to spare
|
|
internal const int TOTAL_SIZE = 32;
|
|
// Next RIP is only needed by RIP relative memory operands. Without this field the user would have
|
|
// to pass this value to the formatter and encoder methods.
|
|
ulong nextRip;
|
|
uint codeFlags;// CodeFlags
|
|
uint opKindFlags;// OpKindFlags
|
|
// If it's a 64-bit immediate/offset/target, the high 32 bits is in memDispl
|
|
uint immediate;
|
|
// This is the high 32 bits if it's a 64-bit immediate/offset/target
|
|
uint memDispl;
|
|
ushort memoryFlags;// MemoryFlags
|
|
byte memBaseReg;// Register
|
|
byte memIndexReg;// Register
|
|
// If a Register will need 9 bits in the future, it's probably best to turn this into a
|
|
// uint (and move it below the other uint fields above). The remaining 4 bits of 'reg3'
|
|
// can be stored in some other field (it's rarely used)
|
|
byte reg0, reg1, reg2, reg3;// Register
|
|
|
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
public static bool operator ==(in Instruction left, in Instruction right) => EqualsInternal(left, right);
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
public static bool operator !=(in Instruction left, in Instruction right) => !EqualsInternal(left, right);
|
|
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
|
|
|
/// <summary>
|
|
/// Checks if this instance equals <paramref name="other"/>
|
|
/// </summary>
|
|
/// <param name="other">Other instruction</param>
|
|
/// <returns></returns>
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
public bool Equals(in Instruction other) => EqualsInternal(this, other);
|
|
bool IEquatable<Instruction>.Equals(Instruction other) => EqualsInternal(this, other);
|
|
|
|
static bool EqualsInternal(in Instruction a, in Instruction b) =>
|
|
((a.codeFlags ^ b.codeFlags) & ~(uint)CodeFlags.EqualsIgnoreMask) == 0 &&
|
|
((a.opKindFlags ^ b.opKindFlags) & ~(uint)OpKindFlags.EqualsIgnoreMask) == 0 &&
|
|
a.immediate == b.immediate &&
|
|
a.memDispl == b.memDispl &&
|
|
a.memoryFlags == b.memoryFlags &&
|
|
a.memBaseReg == b.memBaseReg &&
|
|
a.memIndexReg == b.memIndexReg &&
|
|
a.reg0 == b.reg0 &&
|
|
a.reg1 == b.reg1 &&
|
|
a.reg2 == b.reg2 &&
|
|
a.reg3 == b.reg3;
|
|
|
|
/// <summary>
|
|
/// Gets the hash code
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override int GetHashCode() {
|
|
uint c = codeFlags & ~(uint)CodeFlags.EqualsIgnoreMask;
|
|
c ^= opKindFlags & ~(uint)OpKindFlags.EqualsIgnoreMask;
|
|
c ^= immediate;
|
|
c ^= memDispl;
|
|
c ^= memoryFlags;
|
|
c ^= (uint)memBaseReg << 16;
|
|
c ^= (uint)memIndexReg << 24;
|
|
c ^= reg3;
|
|
c ^= (uint)reg2 << 8;
|
|
c ^= (uint)reg1 << 16;
|
|
c ^= (uint)reg0 << 24;
|
|
return (int)c;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if this instance equals <paramref name="obj"/>
|
|
/// </summary>
|
|
/// <param name="obj">Other instruction</param>
|
|
/// <returns></returns>
|
|
public override bool Equals(object obj) => obj is Instruction other && EqualsInternal(this, other);
|
|
|
|
/// <summary>
|
|
/// Checks if two instructions are equal, comparing all bits, not ignoring anything
|
|
/// </summary>
|
|
/// <param name="a">Instruction #1</param>
|
|
/// <param name="b">Instruction #2</param>
|
|
/// <returns></returns>
|
|
public static bool EqualsAllBits(in Instruction a, in Instruction b) =>
|
|
a.codeFlags == b.codeFlags &&
|
|
a.opKindFlags == b.opKindFlags &&
|
|
a.immediate == b.immediate &&
|
|
a.memDispl == b.memDispl &&
|
|
a.memoryFlags == b.memoryFlags &&
|
|
a.memBaseReg == b.memBaseReg &&
|
|
a.memIndexReg == b.memIndexReg &&
|
|
a.reg0 == b.reg0 &&
|
|
a.reg1 == b.reg1 &&
|
|
a.reg2 == b.reg2 &&
|
|
a.reg3 == b.reg3 &&
|
|
a.nextRip == b.nextRip;
|
|
|
|
internal static bool TEST_BitByBitEquals(in Instruction a, in Instruction b) => EqualsAllBits(a, b);
|
|
|
|
internal static string TEST_DumpDiff(in Instruction a, in Instruction b) {
|
|
var builder = new StringBuilder();
|
|
if (a.nextRip != b.nextRip)
|
|
builder.AppendLine($"a.nextRip={a.nextRip:X16} b.nextRip={b.nextRip:X16}");
|
|
if (a.codeFlags != b.codeFlags)
|
|
builder.AppendLine($"a.codeFlags={a.codeFlags:X} b.codeFlags={b.codeFlags:X}");
|
|
if (a.opKindFlags != b.opKindFlags)
|
|
builder.AppendLine($"a.opKindFlags={a.opKindFlags:X} b.opKindFlags={b.opKindFlags:X}");
|
|
if (a.immediate != b.immediate)
|
|
builder.AppendLine($"a.immediate={a.immediate:X} b.immediate={b.immediate:X}");
|
|
if (a.memDispl != b.memDispl)
|
|
builder.AppendLine($"a.memDispl={a.memDispl:X} b.memDispl={b.memDispl:X}");
|
|
if (a.memoryFlags != b.memoryFlags)
|
|
builder.AppendLine($"a.memoryFlags={a.memoryFlags:X} b.memoryFlags={b.memoryFlags:X}");
|
|
if (a.memBaseReg != b.memBaseReg)
|
|
builder.AppendLine($"a.MemoryBase={(Register)a.memBaseReg} b.MemoryBase={(Register)b.memBaseReg}");
|
|
if (a.memIndexReg != b.memIndexReg)
|
|
builder.AppendLine($"a.MemoryIndex={(Register)a.memIndexReg} b.MemoryIndex={(Register)b.memIndexReg}");
|
|
if (a.reg0 != b.reg0)
|
|
builder.AppendLine($"a.Op0Register={(Register)a.reg0} b.Op0Register={(Register)b.reg0}");
|
|
if (a.reg1 != b.reg1)
|
|
builder.AppendLine($"a.Op1Register={(Register)a.reg1} b.Op1Register={(Register)b.reg1}");
|
|
if (a.reg2 != b.reg2)
|
|
builder.AppendLine($"a.Op2Register={(Register)a.reg2} b.Op2Register={(Register)b.reg2}");
|
|
if (a.reg3 != b.reg3)
|
|
builder.AppendLine($"a.Op3Register={(Register)a.reg3} b.Op3Register={(Register)b.reg3}");
|
|
return builder.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 16-bit IP of the instruction
|
|
/// </summary>
|
|
public ushort IP16 {
|
|
get => (ushort)((uint)nextRip - (uint)ByteLength);
|
|
set => nextRip = value + (uint)ByteLength;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 32-bit IP of the instruction
|
|
/// </summary>
|
|
public uint IP32 {
|
|
get => (uint)nextRip - (uint)ByteLength;
|
|
set => nextRip = value + (uint)ByteLength;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 64-bit IP of the instruction
|
|
/// </summary>
|
|
[Obsolete("Use " + nameof(IP) + " instead of this property", false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public ulong IP64 {
|
|
get => nextRip - (uint)ByteLength;
|
|
set => nextRip = value + (uint)ByteLength;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 64-bit IP of the instruction
|
|
/// </summary>
|
|
public ulong IP {
|
|
get => nextRip - (uint)ByteLength;
|
|
set => nextRip = value + (uint)ByteLength;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 16-bit IP of the next instruction
|
|
/// </summary>
|
|
public ushort NextIP16 {
|
|
get => (ushort)nextRip;
|
|
set => nextRip = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 32-bit IP of the next instruction
|
|
/// </summary>
|
|
public uint NextIP32 {
|
|
get => (uint)nextRip;
|
|
set => nextRip = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 64-bit IP of the next instruction
|
|
/// </summary>
|
|
[Obsolete("Use " + nameof(NextIP) + " instead of this property", false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public ulong NextIP64 {
|
|
get => nextRip;
|
|
set => nextRip = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 64-bit IP of the next instruction
|
|
/// </summary>
|
|
public ulong NextIP {
|
|
get => nextRip;
|
|
set => nextRip = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the code size when the instruction was decoded. This value is informational and can
|
|
/// be used by a formatter.
|
|
/// </summary>
|
|
public CodeSize CodeSize {
|
|
get => (CodeSize)((opKindFlags >> (int)OpKindFlags.CodeSizeShift) & (uint)OpKindFlags.CodeSizeMask);
|
|
set => opKindFlags = ((opKindFlags & ~((uint)OpKindFlags.CodeSizeMask << (int)OpKindFlags.CodeSizeShift)) |
|
|
(((uint)value & (uint)OpKindFlags.CodeSizeMask) << (int)OpKindFlags.CodeSizeShift));
|
|
}
|
|
internal CodeSize InternalCodeSize {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => opKindFlags |= ((uint)value << (int)OpKindFlags.CodeSizeShift);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Instruction code
|
|
/// </summary>
|
|
public Code Code {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (Code)(codeFlags & (uint)CodeFlags.CodeMask);
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set {
|
|
if ((uint)value >= (uint)DecoderConstants.NumberOfCodeValues)
|
|
ThrowHelper.ThrowArgumentOutOfRangeException_value();
|
|
codeFlags = (codeFlags & ~(uint)CodeFlags.CodeMask) | (uint)value;
|
|
}
|
|
}
|
|
internal Code InternalCode {
|
|
// x86 jitter doesn't always inline some of these props that should be inlined. Force it.
|
|
// RyuJIT seems to be better and doesn't seem to require force-inline attrs.
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => codeFlags |= (uint)value;
|
|
}
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void SetCodeNoCheck(Code code) =>
|
|
codeFlags = (codeFlags & ~(uint)CodeFlags.CodeMask) | (uint)code;
|
|
|
|
/// <summary>
|
|
/// Gets the operand count. Up to 5 operands is allowed.
|
|
/// </summary>
|
|
public int OpCount {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => InstructionOpCounts.OpCount[(int)(codeFlags & (uint)CodeFlags.CodeMask)];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the length of the instruction, 0-15 bytes. This is just informational. If you modify the instruction
|
|
/// or create a new one, this property could return the wrong value.
|
|
/// </summary>
|
|
public int ByteLength {
|
|
get => (int)((codeFlags >> (int)CodeFlags.InstrLengthShift) & (uint)CodeFlags.InstrLengthMask);
|
|
set => codeFlags = (codeFlags & ~((uint)CodeFlags.InstrLengthMask << (int)CodeFlags.InstrLengthShift)) |
|
|
(((uint)value & (uint)CodeFlags.InstrLengthMask) << (int)CodeFlags.InstrLengthShift);
|
|
}
|
|
internal uint InternalByteLength {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => codeFlags |= (value << (int)CodeFlags.InstrLengthShift);
|
|
}
|
|
|
|
internal bool Internal_HasRepePrefix_HasXreleasePrefix {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)(CodeFlags.RepePrefix | CodeFlags.XreleasePrefix)) != 0;
|
|
}
|
|
internal bool Internal_HasRepnePrefix_HasXacquirePrefix {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)(CodeFlags.RepnePrefix | CodeFlags.XacquirePrefix)) != 0;
|
|
}
|
|
internal bool Internal_HasRepeOrRepnePrefix {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)(CodeFlags.RepePrefix | CodeFlags.RepnePrefix)) != 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the instruction has the XACQUIRE prefix (F2)
|
|
/// </summary>
|
|
public bool HasXacquirePrefix {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)CodeFlags.XacquirePrefix) != 0;
|
|
set {
|
|
if (value)
|
|
codeFlags |= (uint)CodeFlags.XacquirePrefix;
|
|
else
|
|
codeFlags &= ~(uint)CodeFlags.XacquirePrefix;
|
|
}
|
|
}
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalSetHasXacquirePrefix() => codeFlags |= (uint)CodeFlags.XacquirePrefix;
|
|
|
|
/// <summary>
|
|
/// Checks if the instruction has the XACQUIRE prefix (F3)
|
|
/// </summary>
|
|
public bool HasXreleasePrefix {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)CodeFlags.XreleasePrefix) != 0;
|
|
set {
|
|
if (value)
|
|
codeFlags |= (uint)CodeFlags.XreleasePrefix;
|
|
else
|
|
codeFlags &= ~(uint)CodeFlags.XreleasePrefix;
|
|
}
|
|
}
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalSetHasXreleasePrefix() => codeFlags |= (uint)CodeFlags.XreleasePrefix;
|
|
|
|
/// <summary>
|
|
/// Checks if the instruction has the REPE prefix (F3)
|
|
/// </summary>
|
|
public bool HasRepePrefix {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)CodeFlags.RepePrefix) != 0;
|
|
set {
|
|
if (value)
|
|
codeFlags |= (uint)CodeFlags.RepePrefix;
|
|
else
|
|
codeFlags &= ~(uint)CodeFlags.RepePrefix;
|
|
}
|
|
}
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalSetHasRepePrefix() => codeFlags |= (uint)CodeFlags.RepePrefix;
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalClearHasRepePrefix() => codeFlags &= ~(uint)CodeFlags.RepePrefix;
|
|
|
|
/// <summary>
|
|
/// Checks if the instruction has the REPNE prefix (F2)
|
|
/// </summary>
|
|
public bool HasRepnePrefix {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)CodeFlags.RepnePrefix) != 0;
|
|
set {
|
|
if (value)
|
|
codeFlags |= (uint)CodeFlags.RepnePrefix;
|
|
else
|
|
codeFlags &= ~(uint)CodeFlags.RepnePrefix;
|
|
}
|
|
}
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalSetHasRepnePrefix() => codeFlags |= (uint)CodeFlags.RepnePrefix;
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalClearHasRepnePrefix() => codeFlags &= ~(uint)CodeFlags.RepnePrefix;
|
|
|
|
/// <summary>
|
|
/// Checks if the instruction has the LOCK prefix (F0)
|
|
/// </summary>
|
|
public bool HasLockPrefix {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)CodeFlags.LockPrefix) != 0;
|
|
set {
|
|
if (value)
|
|
codeFlags |= (uint)CodeFlags.LockPrefix;
|
|
else
|
|
codeFlags &= ~(uint)CodeFlags.LockPrefix;
|
|
}
|
|
}
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalSetHasLockPrefix() => codeFlags |= (uint)CodeFlags.LockPrefix;
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalClearHasLockPrefix() => codeFlags &= ~(uint)CodeFlags.LockPrefix;
|
|
|
|
/// <summary>
|
|
/// Gets operand #0's kind if the operand exists (see <see cref="OpCount"/>)
|
|
/// </summary>
|
|
public OpKind Op0Kind {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (OpKind)(opKindFlags & (uint)OpKindFlags.OpKindMask);
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => opKindFlags = (opKindFlags & ~(uint)OpKindFlags.OpKindMask) | ((uint)value & (uint)OpKindFlags.OpKindMask);
|
|
}
|
|
internal OpKind InternalOp0Kind {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => opKindFlags |= (uint)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets operand #1's kind if the operand exists (see <see cref="OpCount"/>)
|
|
/// </summary>
|
|
public OpKind Op1Kind {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (OpKind)((opKindFlags >> (int)OpKindFlags.Op1KindShift) & (uint)OpKindFlags.OpKindMask);
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => opKindFlags = (opKindFlags & ~((uint)OpKindFlags.OpKindMask << (int)OpKindFlags.Op1KindShift)) |
|
|
(((uint)value & (uint)OpKindFlags.OpKindMask) << (int)OpKindFlags.Op1KindShift);
|
|
}
|
|
internal OpKind InternalOp1Kind {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => opKindFlags |= (uint)value << (int)OpKindFlags.Op1KindShift;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets operand #2's kind if the operand exists (see <see cref="OpCount"/>)
|
|
/// </summary>
|
|
public OpKind Op2Kind {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (OpKind)((opKindFlags >> (int)OpKindFlags.Op2KindShift) & (uint)OpKindFlags.OpKindMask);
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => opKindFlags = (opKindFlags & ~((uint)OpKindFlags.OpKindMask << (int)OpKindFlags.Op2KindShift)) |
|
|
(((uint)value & (uint)OpKindFlags.OpKindMask) << (int)OpKindFlags.Op2KindShift);
|
|
}
|
|
internal OpKind InternalOp2Kind {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => opKindFlags |= (uint)value << (int)OpKindFlags.Op2KindShift;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets operand #3's kind if the operand exists (see <see cref="OpCount"/>)
|
|
/// </summary>
|
|
public OpKind Op3Kind {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (OpKind)((opKindFlags >> (int)OpKindFlags.Op3KindShift) & (uint)OpKindFlags.OpKindMask);
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => opKindFlags = (opKindFlags & ~((uint)OpKindFlags.OpKindMask << (int)OpKindFlags.Op3KindShift)) |
|
|
(((uint)value & (uint)OpKindFlags.OpKindMask) << (int)OpKindFlags.Op3KindShift);
|
|
}
|
|
internal OpKind InternalOp3Kind {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => opKindFlags |= (uint)value << (int)OpKindFlags.Op3KindShift;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets operand #4's kind if the operand exists (see <see cref="OpCount"/>)
|
|
/// </summary>
|
|
public OpKind Op4Kind {
|
|
get => OpKind.Immediate8;
|
|
set {
|
|
if (value != OpKind.Immediate8)
|
|
ThrowHelper.ThrowArgumentOutOfRangeException_value();
|
|
}
|
|
}
|
|
internal OpKind InternalOp4Kind {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set {
|
|
if (value != OpKind.Immediate8)
|
|
ThrowHelper.ThrowArgumentOutOfRangeException_value();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an operand's kind if it exists (see <see cref="OpCount"/>)
|
|
/// </summary>
|
|
/// <param name="operand">Operand number, 0-4</param>
|
|
/// <returns></returns>
|
|
public OpKind GetOpKind(int operand) {
|
|
switch (operand) {
|
|
case 0: return Op0Kind;
|
|
case 1: return Op1Kind;
|
|
case 2: return Op2Kind;
|
|
case 3: return Op3Kind;
|
|
case 4: return Op4Kind;
|
|
default:
|
|
ThrowHelper.ThrowArgumentOutOfRangeException_operand();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets an operand's kind
|
|
/// </summary>
|
|
/// <param name="operand">Operand number, 0-4</param>
|
|
/// <param name="opKind">Operand kind</param>
|
|
public void SetOpKind(int operand, OpKind opKind) {
|
|
switch (operand) {
|
|
case 0: Op0Kind = opKind; break;
|
|
case 1: Op1Kind = opKind; break;
|
|
case 2: Op2Kind = opKind; break;
|
|
case 3: Op3Kind = opKind; break;
|
|
case 4: Op4Kind = opKind; break;
|
|
default: ThrowHelper.ThrowArgumentOutOfRangeException_operand(); break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the segment override prefix or <see cref="Register.None"/> if none. See also <see cref="MemorySegment"/>.
|
|
/// Use this property if the operand has kind <see cref="OpKind.Memory"/>, <see cref="OpKind.Memory64"/>,
|
|
/// <see cref="OpKind.MemorySegSI"/>, <see cref="OpKind.MemorySegESI"/>, <see cref="OpKind.MemorySegRSI"/>
|
|
/// </summary>
|
|
public Register SegmentPrefix {
|
|
get {
|
|
uint index = (((uint)memoryFlags >> (int)MemoryFlags.SegmentPrefixShift) & (uint)MemoryFlags.SegmentPrefixMask) - 1;
|
|
return index < 6 ? Register.ES + (int)index : Register.None;
|
|
}
|
|
set {
|
|
uint encValue;
|
|
if (value == Register.None)
|
|
encValue = 0;
|
|
else
|
|
encValue = (((uint)value - (uint)Register.ES) + 1) & (uint)MemoryFlags.SegmentPrefixMask;
|
|
memoryFlags = (ushort)((memoryFlags & ~((uint)MemoryFlags.SegmentPrefixMask << (int)MemoryFlags.SegmentPrefixShift)) |
|
|
(encValue << (int)MemoryFlags.SegmentPrefixShift));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the effective segment register used to reference the memory location.
|
|
/// Use this property if the operand has kind <see cref="OpKind.Memory"/>, <see cref="OpKind.Memory64"/>,
|
|
/// <see cref="OpKind.MemorySegSI"/>, <see cref="OpKind.MemorySegESI"/>, <see cref="OpKind.MemorySegRSI"/>
|
|
/// </summary>
|
|
public Register MemorySegment {
|
|
get {
|
|
var segReg = SegmentPrefix;
|
|
if (segReg != Register.None)
|
|
return segReg;
|
|
var baseReg = MemoryBase;
|
|
if (baseReg == Register.BP || baseReg == Register.EBP || baseReg == Register.ESP || baseReg == Register.RBP || baseReg == Register.RSP)
|
|
return Register.SS;
|
|
return Register.DS;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the size of the memory displacement in bytes. Valid values are 0, 1 (16/32/64-bit), 2 (16-bit), 4 (32-bit), 8 (64-bit).
|
|
/// Note that the return value can be 1 and <see cref="MemoryDisplacement"/> may still not fit in
|
|
/// a signed byte if it's an EVEX encoded instruction.
|
|
/// Use this property if the operand has kind <see cref="OpKind.Memory"/>
|
|
/// </summary>
|
|
public int MemoryDisplSize {
|
|
get {
|
|
switch (((uint)memoryFlags >> (int)MemoryFlags.DisplSizeShift) & (uint)MemoryFlags.DisplSizeMask) {
|
|
case 0: return 0;
|
|
case 1: return 1;
|
|
case 2: return 2;
|
|
case 3: return 4;
|
|
default:
|
|
case 4: return 8;
|
|
}
|
|
}
|
|
set {
|
|
uint encValue;
|
|
switch (value) {
|
|
case 0: encValue = 0; break;
|
|
case 1: encValue = 1; break;
|
|
case 2: encValue = 2; break;
|
|
case 4: encValue = 3; break;
|
|
default:
|
|
case 8: encValue = 4; break;
|
|
}
|
|
memoryFlags = (ushort)((memoryFlags & ~((uint)MemoryFlags.DisplSizeMask << (int)MemoryFlags.DisplSizeShift)) |
|
|
(encValue << (int)MemoryFlags.DisplSizeShift));
|
|
}
|
|
}
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalSetMemoryDisplSize(uint scale) {
|
|
Debug.Assert(0 <= scale && scale <= 4);
|
|
memoryFlags |= (ushort)(scale << (int)MemoryFlags.DisplSizeShift);
|
|
}
|
|
|
|
/// <summary>
|
|
/// true if the data is broadcasted (EVEX instructions only)
|
|
/// </summary>
|
|
public bool IsBroadcast {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (memoryFlags & (uint)MemoryFlags.BroadcastedMemory) != 0;
|
|
set {
|
|
if (value)
|
|
memoryFlags |= (ushort)MemoryFlags.BroadcastedMemory;
|
|
else
|
|
memoryFlags &= unchecked((ushort)~(ushort)MemoryFlags.BroadcastedMemory);
|
|
}
|
|
}
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void SetIsBroadcast() => memoryFlags |= (ushort)MemoryFlags.BroadcastedMemory;
|
|
|
|
/// <summary>
|
|
/// Gets the size of the memory location that is referenced by the operand. See also <see cref="IsBroadcast"/>.
|
|
/// Use this property if the operand has kind <see cref="OpKind.Memory"/>, <see cref="OpKind.Memory64"/>,
|
|
/// <see cref="OpKind.MemorySegSI"/>, <see cref="OpKind.MemorySegESI"/>, <see cref="OpKind.MemorySegRSI"/>,
|
|
/// <see cref="OpKind.MemoryESDI"/>, <see cref="OpKind.MemoryESEDI"/>, <see cref="OpKind.MemoryESRDI"/>
|
|
/// </summary>
|
|
public MemorySize MemorySize {
|
|
get {
|
|
int index = (int)Code * 2;
|
|
if (IsBroadcast)
|
|
index++;
|
|
return (MemorySize)InstructionMemorySizes.Sizes[index];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the index register scale value, valid values are *1, *2, *4, *8. Use this property if the operand has kind <see cref="OpKind.Memory"/>
|
|
/// </summary>
|
|
public int MemoryIndexScale {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => 1 << (int)(memoryFlags & (uint)MemoryFlags.ScaleMask);
|
|
set {
|
|
if (value == 1)
|
|
memoryFlags &= 0xFFFC;
|
|
else if (value == 2)
|
|
memoryFlags = (ushort)((memoryFlags & ~(uint)MemoryFlags.ScaleMask) | 1);
|
|
else if (value == 4)
|
|
memoryFlags = (ushort)((memoryFlags & ~(uint)MemoryFlags.ScaleMask) | 2);
|
|
else {
|
|
Debug.Assert(value == 8);
|
|
memoryFlags |= 3;
|
|
}
|
|
}
|
|
}
|
|
internal int InternalMemoryIndexScale {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (int)(memoryFlags & (uint)MemoryFlags.ScaleMask);
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => memoryFlags |= (ushort)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the memory operand's displacement. This should be sign extended to 64 bits if it's 64-bit addressing.
|
|
/// Use this property if the operand has kind <see cref="OpKind.Memory"/>
|
|
/// </summary>
|
|
public uint MemoryDisplacement {
|
|
get => memDispl;
|
|
set => memDispl = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an operand's immediate value
|
|
/// </summary>
|
|
/// <param name="operand">Operand number, 0-4</param>
|
|
/// <returns></returns>
|
|
public ulong GetImmediate(int operand) {
|
|
switch (GetOpKind(operand)) {
|
|
case OpKind.Immediate8: return Immediate8;
|
|
case OpKind.Immediate8_2nd: return Immediate8_2nd;
|
|
case OpKind.Immediate16: return Immediate16;
|
|
case OpKind.Immediate32: return Immediate32;
|
|
case OpKind.Immediate64: return Immediate64;
|
|
case OpKind.Immediate8to16: return (ulong)Immediate8to16;
|
|
case OpKind.Immediate8to32: return (ulong)Immediate8to32;
|
|
case OpKind.Immediate8to64: return (ulong)Immediate8to64;
|
|
case OpKind.Immediate32to64: return (ulong)Immediate32to64;
|
|
default:
|
|
throw new ArgumentException($"Op{operand} isn't an immediate operand", nameof(operand));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets an operand's immediate value
|
|
/// </summary>
|
|
/// <param name="operand">Operand number, 0-4</param>
|
|
/// <param name="immediate">New immediate</param>
|
|
/// <returns></returns>
|
|
public void SetImmediate(int operand, int immediate) => SetImmediate(operand, (ulong)immediate);
|
|
|
|
/// <summary>
|
|
/// Sets an operand's immediate value
|
|
/// </summary>
|
|
/// <param name="operand">Operand number, 0-4</param>
|
|
/// <param name="immediate">New immediate</param>
|
|
/// <returns></returns>
|
|
public void SetImmediate(int operand, uint immediate) => SetImmediate(operand, (ulong)immediate);
|
|
|
|
/// <summary>
|
|
/// Sets an operand's immediate value
|
|
/// </summary>
|
|
/// <param name="operand">Operand number, 0-4</param>
|
|
/// <param name="immediate">New immediate</param>
|
|
/// <returns></returns>
|
|
public void SetImmediate(int operand, long immediate) => SetImmediate(operand, (ulong)immediate);
|
|
|
|
/// <summary>
|
|
/// Sets an operand's immediate value
|
|
/// </summary>
|
|
/// <param name="operand">Operand number, 0-4</param>
|
|
/// <param name="immediate">New immediate</param>
|
|
/// <returns></returns>
|
|
public void SetImmediate(int operand, ulong immediate) {
|
|
switch (GetOpKind(operand)) {
|
|
case OpKind.Immediate8:
|
|
case OpKind.Immediate8to16:
|
|
case OpKind.Immediate8to32:
|
|
case OpKind.Immediate8to64:
|
|
this.immediate = (byte)immediate;
|
|
break;
|
|
case OpKind.Immediate8_2nd:
|
|
memDispl = (byte)immediate;
|
|
break;
|
|
case OpKind.Immediate16:
|
|
this.immediate = (ushort)immediate;
|
|
break;
|
|
case OpKind.Immediate32to64:
|
|
case OpKind.Immediate32:
|
|
this.immediate = (uint)immediate;
|
|
break;
|
|
case OpKind.Immediate64:
|
|
Immediate64 = immediate;
|
|
break;
|
|
default:
|
|
throw new ArgumentException($"Op{operand} isn't an immediate operand", nameof(operand));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's immediate value. Use this property if the operand has kind <see cref="OpKind.Immediate8"/>
|
|
/// </summary>
|
|
public byte Immediate8 {
|
|
get => (byte)immediate;
|
|
set => immediate = value;
|
|
}
|
|
internal uint InternalImmediate8 {
|
|
set => immediate = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's immediate value. Use this property if the operand has kind <see cref="OpKind.Immediate8_2nd"/>
|
|
/// </summary>
|
|
public byte Immediate8_2nd {
|
|
get => (byte)memDispl;
|
|
set => memDispl = value;
|
|
}
|
|
internal uint InternalImmediate8_2nd {
|
|
set => memDispl = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's immediate value. Use this property if the operand has kind <see cref="OpKind.Immediate16"/>
|
|
/// </summary>
|
|
public ushort Immediate16 {
|
|
get => (ushort)immediate;
|
|
set => immediate = value;
|
|
}
|
|
internal uint InternalImmediate16 {
|
|
set => immediate = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's immediate value. Use this property if the operand has kind <see cref="OpKind.Immediate32"/>
|
|
/// </summary>
|
|
public uint Immediate32 {
|
|
get => immediate;
|
|
set => immediate = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's immediate value. Use this property if the operand has kind <see cref="OpKind.Immediate64"/>
|
|
/// </summary>
|
|
public ulong Immediate64 {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => ((ulong)memDispl << 32) | immediate;
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set {
|
|
immediate = (uint)value;
|
|
memDispl = (uint)(value >> 32);
|
|
}
|
|
}
|
|
internal uint InternalImmediate64_lo {
|
|
set => immediate = value;
|
|
}
|
|
internal uint InternalImmediate64_hi {
|
|
set => memDispl = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's immediate value. Use this property if the operand has kind <see cref="OpKind.Immediate8to16"/>
|
|
/// </summary>
|
|
public short Immediate8to16 {
|
|
get => (sbyte)immediate;
|
|
set => immediate = (uint)(sbyte)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's immediate value. Use this property if the operand has kind <see cref="OpKind.Immediate8to32"/>
|
|
/// </summary>
|
|
public int Immediate8to32 {
|
|
get => (sbyte)immediate;
|
|
set => immediate = (uint)(sbyte)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's immediate value. Use this property if the operand has kind <see cref="OpKind.Immediate8to64"/>
|
|
/// </summary>
|
|
public long Immediate8to64 {
|
|
get => (sbyte)immediate;
|
|
set => immediate = (uint)(sbyte)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's immediate value. Use this property if the operand has kind <see cref="OpKind.Immediate32to64"/>
|
|
/// </summary>
|
|
public long Immediate32to64 {
|
|
get => (int)immediate;
|
|
set => immediate = (uint)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's 64-bit address value. Use this property if the operand has kind <see cref="OpKind.Memory64"/>
|
|
/// </summary>
|
|
public ulong MemoryAddress64 {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => ((ulong)memDispl << 32) | immediate;
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set {
|
|
immediate = (uint)value;
|
|
memDispl = (uint)(value >> 32);
|
|
}
|
|
}
|
|
internal uint InternalMemoryAddress64_lo {
|
|
set => immediate = value;
|
|
}
|
|
internal uint InternalMemoryAddress64_hi {
|
|
set => memDispl = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's branch target. Use this property if the operand has kind <see cref="OpKind.NearBranch16"/>
|
|
/// </summary>
|
|
public ushort NearBranch16 {
|
|
get => (ushort)immediate;
|
|
set => immediate = value;
|
|
}
|
|
internal uint InternalNearBranch16 {
|
|
set => immediate = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's branch target. Use this property if the operand has kind <see cref="OpKind.NearBranch32"/>
|
|
/// </summary>
|
|
public uint NearBranch32 {
|
|
get => immediate;
|
|
set => immediate = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's branch target. Use this property if the operand has kind <see cref="OpKind.NearBranch64"/>
|
|
/// </summary>
|
|
public ulong NearBranch64 {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => ((ulong)memDispl << 32) | immediate;
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set {
|
|
immediate = (uint)value;
|
|
memDispl = (uint)(value >> 32);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the near branch target if it's a call/jmp near branch instruction
|
|
/// </summary>
|
|
public ulong NearBranchTarget {
|
|
get {
|
|
switch (Op0Kind) {
|
|
case OpKind.NearBranch16: return NearBranch16;
|
|
case OpKind.NearBranch32: return NearBranch32;
|
|
case OpKind.NearBranch64: return NearBranch64;
|
|
default: return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's branch target. Use this property if the operand has kind <see cref="OpKind.FarBranch16"/>
|
|
/// </summary>
|
|
public ushort FarBranch16 {
|
|
get => (ushort)immediate;
|
|
set => immediate = value;
|
|
}
|
|
internal uint InternalFarBranch16 {
|
|
set => immediate = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's branch target. Use this property if the operand has kind <see cref="OpKind.FarBranch32"/>
|
|
/// </summary>
|
|
public uint FarBranch32 {
|
|
get => immediate;
|
|
set => immediate = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's branch target selector. Use this property if the operand has kind <see cref="OpKind.FarBranch16"/> or <see cref="OpKind.FarBranch32"/>
|
|
/// </summary>
|
|
public ushort FarBranchSelector {
|
|
get => (ushort)memDispl;
|
|
set => memDispl = value;
|
|
}
|
|
internal uint InternalFarBranchSelector {
|
|
set => memDispl = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the memory operand's base register or <see cref="Register.None"/> if none. Use this property if the operand has kind <see cref="OpKind.Memory"/>
|
|
/// </summary>
|
|
public Register MemoryBase {
|
|
get => (Register)memBaseReg;
|
|
set => memBaseReg = (byte)value;
|
|
}
|
|
internal Register InternalMemoryBase {
|
|
set => memBaseReg = (byte)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the memory operand's index register or <see cref="Register.None"/> if none. Use this property if the operand has kind <see cref="OpKind.Memory"/>
|
|
/// </summary>
|
|
public Register MemoryIndex {
|
|
get => (Register)memIndexReg;
|
|
set => memIndexReg = (byte)value;
|
|
}
|
|
internal Register InternalMemoryIndex {
|
|
set => memIndexReg = (byte)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets operand #0's register value. Use this property if operand #0 (<see cref="Op0Kind"/>) has kind <see cref="OpKind.Register"/>
|
|
/// </summary>
|
|
public Register Op0Register {
|
|
get => (Register)reg0;
|
|
set => reg0 = (byte)value;
|
|
}
|
|
internal Register InternalOp0Register {
|
|
set => reg0 = (byte)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets operand #1's register value. Use this property if operand #1 (<see cref="Op1Kind"/>) has kind <see cref="OpKind.Register"/>
|
|
/// </summary>
|
|
public Register Op1Register {
|
|
get => (Register)reg1;
|
|
set => reg1 = (byte)value;
|
|
}
|
|
internal Register InternalOp1Register {
|
|
set => reg1 = (byte)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets operand #2's register value. Use this property if operand #2 (<see cref="Op2Kind"/>) has kind <see cref="OpKind.Register"/>
|
|
/// </summary>
|
|
public Register Op2Register {
|
|
get => (Register)reg2;
|
|
set => reg2 = (byte)value;
|
|
}
|
|
internal Register InternalOp2Register {
|
|
set => reg2 = (byte)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets operand #3's register value. Use this property if operand #3 (<see cref="Op3Kind"/>) has kind <see cref="OpKind.Register"/>
|
|
/// </summary>
|
|
public Register Op3Register {
|
|
get => (Register)reg3;
|
|
set => reg3 = (byte)value;
|
|
}
|
|
internal Register InternalOp3Register {
|
|
set => reg3 = (byte)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets operand #4's register value. Use this property if operand #4 (<see cref="Op4Kind"/>) has kind <see cref="OpKind.Register"/>
|
|
/// </summary>
|
|
public Register Op4Register {
|
|
get => Register.None;
|
|
set {
|
|
if (value != Register.None)
|
|
ThrowHelper.ThrowArgumentOutOfRangeException_value();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the operand's register value. Use this property if the operand has kind <see cref="OpKind.Register"/>
|
|
/// </summary>
|
|
/// <param name="operand">Operand number, 0-4</param>
|
|
/// <returns></returns>
|
|
public Register GetOpRegister(int operand) {
|
|
switch (operand) {
|
|
case 0: return Op0Register;
|
|
case 1: return Op1Register;
|
|
case 2: return Op2Register;
|
|
case 3: return Op3Register;
|
|
case 4: return Op4Register;
|
|
default:
|
|
ThrowHelper.ThrowArgumentOutOfRangeException_operand();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the operand's register value. Use this property if the operand has kind <see cref="OpKind.Register"/>
|
|
/// </summary>
|
|
/// <param name="operand">Operand number, 0-4</param>
|
|
/// <param name="register">Register</param>
|
|
public void SetOpRegister(int operand, Register register) {
|
|
switch (operand) {
|
|
case 0: Op0Register = register; break;
|
|
case 1: Op1Register = register; break;
|
|
case 2: Op2Register = register; break;
|
|
case 3: Op3Register = register; break;
|
|
case 4: Op4Register = register; break;
|
|
default: ThrowHelper.ThrowArgumentOutOfRangeException_operand(); break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the opmask register (<see cref="Register.K1"/> - <see cref="Register.K7"/>) or <see cref="Register.None"/> if none
|
|
/// </summary>
|
|
public Register OpMask {
|
|
get {
|
|
int r = (int)(codeFlags >> (int)CodeFlags.OpMaskShift) & (int)CodeFlags.OpMaskMask;
|
|
return r == 0 ? Register.None : r + Register.K0;
|
|
}
|
|
set {
|
|
uint r;
|
|
if (value == Register.None)
|
|
r = 0;
|
|
else
|
|
r = (uint)((uint)(value - Register.K0) & (uint)CodeFlags.OpMaskMask);
|
|
codeFlags = (codeFlags & ~((uint)CodeFlags.OpMaskMask << (int)CodeFlags.OpMaskShift)) |
|
|
(r << (int)CodeFlags.OpMaskShift);
|
|
}
|
|
}
|
|
internal uint InternalOpMask {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags >> (int)CodeFlags.OpMaskShift) & (uint)CodeFlags.OpMaskMask;
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => codeFlags |= value << (int)CodeFlags.OpMaskShift;
|
|
}
|
|
|
|
/// <summary>
|
|
/// true if there's an opmask register (<see cref="OpMask"/>)
|
|
/// </summary>
|
|
public bool HasOpMask {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & ((uint)CodeFlags.OpMaskMask << (int)CodeFlags.OpMaskShift)) != 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// true if zeroing-masking, false if merging-masking.
|
|
/// Only used by most EVEX encoded instructions that use opmask registers.
|
|
/// </summary>
|
|
public bool ZeroingMasking {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)CodeFlags.ZeroingMasking) != 0;
|
|
set {
|
|
if (value)
|
|
codeFlags |= (uint)CodeFlags.ZeroingMasking;
|
|
else
|
|
codeFlags &= ~(uint)CodeFlags.ZeroingMasking;
|
|
}
|
|
}
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalSetZeroingMasking() => codeFlags |= (uint)CodeFlags.ZeroingMasking;
|
|
|
|
/// <summary>
|
|
/// true if merging-masking, false if zeroing-masking.
|
|
/// Only used by most EVEX encoded instructions that use opmask registers.
|
|
/// </summary>
|
|
public bool MergingMasking {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)CodeFlags.ZeroingMasking) == 0;
|
|
set {
|
|
if (value)
|
|
codeFlags &= ~(uint)CodeFlags.ZeroingMasking;
|
|
else
|
|
codeFlags |= (uint)CodeFlags.ZeroingMasking;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rounding control (<see cref="SuppressAllExceptions"/> is implied but still returns false)
|
|
/// or <see cref="RoundingControl.None"/> if the instruction doesn't use it.
|
|
/// </summary>
|
|
public RoundingControl RoundingControl {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (RoundingControl)((codeFlags >> (int)CodeFlags.RoundingControlShift) & (int)CodeFlags.RoundingControlMask);
|
|
set => codeFlags = (codeFlags & ~((uint)CodeFlags.RoundingControlMask << (int)CodeFlags.RoundingControlShift)) |
|
|
(((uint)value & (uint)CodeFlags.RoundingControlMask) << (int)CodeFlags.RoundingControlShift);
|
|
}
|
|
internal uint InternalRoundingControl {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
set => codeFlags |= value << (int)CodeFlags.RoundingControlShift;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if this is a VSIB instruction, see also <see cref="IsVsib32"/>, <see cref="IsVsib64"/>
|
|
/// </summary>
|
|
public bool IsVsib => TryGetVsib64(out _);
|
|
|
|
/// <summary>
|
|
/// VSIB instructions only (<see cref="IsVsib"/>): true if it's using 32-bit indexes, false if it's using 64-bit indexes
|
|
/// </summary>
|
|
public bool IsVsib32 => TryGetVsib64(out bool vsib64) && !vsib64;
|
|
|
|
/// <summary>
|
|
/// VSIB instructions only (<see cref="IsVsib"/>): true if it's using 64-bit indexes, false if it's using 32-bit indexes
|
|
/// </summary>
|
|
public bool IsVsib64 => TryGetVsib64(out bool vsib64) && vsib64;
|
|
|
|
/// <summary>
|
|
/// Checks if it's a VSIB instruction. If it's a VSIB instruction, it sets <paramref name="vsib64"/> to true if it's
|
|
/// a VSIB instruction with 64-bit indexes, and clears it if it's using 32-bit indexes.
|
|
/// </summary>
|
|
/// <param name="vsib64">If it's a VSIB instruction, set to true if it's using 64-bit indexes, set to false if it's using 32-bit indexes</param>
|
|
/// <returns></returns>
|
|
public bool TryGetVsib64(out bool vsib64) {
|
|
switch (Code) {
|
|
case Code.VEX_Vpgatherdd_xmm_vm32x_xmm:
|
|
case Code.VEX_Vpgatherdd_ymm_vm32y_ymm:
|
|
case Code.VEX_Vpgatherdq_xmm_vm32x_xmm:
|
|
case Code.VEX_Vpgatherdq_ymm_vm32x_ymm:
|
|
case Code.EVEX_Vpgatherdd_xmm_k1_vm32x:
|
|
case Code.EVEX_Vpgatherdd_ymm_k1_vm32y:
|
|
case Code.EVEX_Vpgatherdd_zmm_k1_vm32z:
|
|
case Code.EVEX_Vpgatherdq_xmm_k1_vm32x:
|
|
case Code.EVEX_Vpgatherdq_ymm_k1_vm32x:
|
|
case Code.EVEX_Vpgatherdq_zmm_k1_vm32y:
|
|
|
|
case Code.VEX_Vgatherdps_xmm_vm32x_xmm:
|
|
case Code.VEX_Vgatherdps_ymm_vm32y_ymm:
|
|
case Code.VEX_Vgatherdpd_xmm_vm32x_xmm:
|
|
case Code.VEX_Vgatherdpd_ymm_vm32x_ymm:
|
|
case Code.EVEX_Vgatherdps_xmm_k1_vm32x:
|
|
case Code.EVEX_Vgatherdps_ymm_k1_vm32y:
|
|
case Code.EVEX_Vgatherdps_zmm_k1_vm32z:
|
|
case Code.EVEX_Vgatherdpd_xmm_k1_vm32x:
|
|
case Code.EVEX_Vgatherdpd_ymm_k1_vm32x:
|
|
case Code.EVEX_Vgatherdpd_zmm_k1_vm32y:
|
|
|
|
case Code.EVEX_Vpscatterdd_vm32x_k1_xmm:
|
|
case Code.EVEX_Vpscatterdd_vm32y_k1_ymm:
|
|
case Code.EVEX_Vpscatterdd_vm32z_k1_zmm:
|
|
case Code.EVEX_Vpscatterdq_vm32x_k1_xmm:
|
|
case Code.EVEX_Vpscatterdq_vm32x_k1_ymm:
|
|
case Code.EVEX_Vpscatterdq_vm32y_k1_zmm:
|
|
|
|
case Code.EVEX_Vscatterdps_vm32x_k1_xmm:
|
|
case Code.EVEX_Vscatterdps_vm32y_k1_ymm:
|
|
case Code.EVEX_Vscatterdps_vm32z_k1_zmm:
|
|
case Code.EVEX_Vscatterdpd_vm32x_k1_xmm:
|
|
case Code.EVEX_Vscatterdpd_vm32x_k1_ymm:
|
|
case Code.EVEX_Vscatterdpd_vm32y_k1_zmm:
|
|
|
|
case Code.EVEX_Vgatherpf0dps_vm32z_k1:
|
|
case Code.EVEX_Vgatherpf0dpd_vm32y_k1:
|
|
case Code.EVEX_Vgatherpf1dps_vm32z_k1:
|
|
case Code.EVEX_Vgatherpf1dpd_vm32y_k1:
|
|
case Code.EVEX_Vscatterpf0dps_vm32z_k1:
|
|
case Code.EVEX_Vscatterpf0dpd_vm32y_k1:
|
|
case Code.EVEX_Vscatterpf1dps_vm32z_k1:
|
|
case Code.EVEX_Vscatterpf1dpd_vm32y_k1:
|
|
vsib64 = false;
|
|
return true;
|
|
|
|
case Code.VEX_Vpgatherqd_xmm_vm64x_xmm:
|
|
case Code.VEX_Vpgatherqd_xmm_vm64y_xmm:
|
|
case Code.VEX_Vpgatherqq_xmm_vm64x_xmm:
|
|
case Code.VEX_Vpgatherqq_ymm_vm64y_ymm:
|
|
case Code.EVEX_Vpgatherqd_xmm_k1_vm64x:
|
|
case Code.EVEX_Vpgatherqd_xmm_k1_vm64y:
|
|
case Code.EVEX_Vpgatherqd_ymm_k1_vm64z:
|
|
case Code.EVEX_Vpgatherqq_xmm_k1_vm64x:
|
|
case Code.EVEX_Vpgatherqq_ymm_k1_vm64y:
|
|
case Code.EVEX_Vpgatherqq_zmm_k1_vm64z:
|
|
|
|
case Code.VEX_Vgatherqps_xmm_vm64x_xmm:
|
|
case Code.VEX_Vgatherqps_xmm_vm64y_xmm:
|
|
case Code.VEX_Vgatherqpd_xmm_vm64x_xmm:
|
|
case Code.VEX_Vgatherqpd_ymm_vm64y_ymm:
|
|
case Code.EVEX_Vgatherqps_xmm_k1_vm64x:
|
|
case Code.EVEX_Vgatherqps_xmm_k1_vm64y:
|
|
case Code.EVEX_Vgatherqps_ymm_k1_vm64z:
|
|
case Code.EVEX_Vgatherqpd_xmm_k1_vm64x:
|
|
case Code.EVEX_Vgatherqpd_ymm_k1_vm64y:
|
|
case Code.EVEX_Vgatherqpd_zmm_k1_vm64z:
|
|
|
|
case Code.EVEX_Vpscatterqd_vm64x_k1_xmm:
|
|
case Code.EVEX_Vpscatterqd_vm64y_k1_xmm:
|
|
case Code.EVEX_Vpscatterqd_vm64z_k1_ymm:
|
|
case Code.EVEX_Vpscatterqq_vm64x_k1_xmm:
|
|
case Code.EVEX_Vpscatterqq_vm64y_k1_ymm:
|
|
case Code.EVEX_Vpscatterqq_vm64z_k1_zmm:
|
|
|
|
case Code.EVEX_Vscatterqps_vm64x_k1_xmm:
|
|
case Code.EVEX_Vscatterqps_vm64y_k1_xmm:
|
|
case Code.EVEX_Vscatterqps_vm64z_k1_ymm:
|
|
case Code.EVEX_Vscatterqpd_vm64x_k1_xmm:
|
|
case Code.EVEX_Vscatterqpd_vm64y_k1_ymm:
|
|
case Code.EVEX_Vscatterqpd_vm64z_k1_zmm:
|
|
|
|
case Code.EVEX_Vgatherpf0qps_vm64z_k1:
|
|
case Code.EVEX_Vgatherpf0qpd_vm64z_k1:
|
|
case Code.EVEX_Vgatherpf1qps_vm64z_k1:
|
|
case Code.EVEX_Vgatherpf1qpd_vm64z_k1:
|
|
case Code.EVEX_Vscatterpf0qps_vm64z_k1:
|
|
case Code.EVEX_Vscatterpf0qpd_vm64z_k1:
|
|
case Code.EVEX_Vscatterpf1qps_vm64z_k1:
|
|
case Code.EVEX_Vscatterpf1qpd_vm64z_k1:
|
|
vsib64 = true;
|
|
return true;
|
|
|
|
default:
|
|
vsib64 = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Suppress all exceptions (EVEX encoded instructions). Note that if <see cref="RoundingControl"/> is
|
|
/// not <see cref="RoundingControl.None"/>, SAE is implied but this property will still return false.
|
|
/// </summary>
|
|
public bool SuppressAllExceptions {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => (codeFlags & (uint)CodeFlags.SuppressAllExceptions) != 0;
|
|
set {
|
|
if (value)
|
|
codeFlags |= (uint)CodeFlags.SuppressAllExceptions;
|
|
else
|
|
codeFlags &= ~(uint)CodeFlags.SuppressAllExceptions;
|
|
}
|
|
}
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
internal void InternalSetSuppressAllExceptions() => codeFlags |= (uint)CodeFlags.SuppressAllExceptions;
|
|
|
|
/// <summary>
|
|
/// Checks if the memory operand is RIP/EIP relative
|
|
/// </summary>
|
|
[Obsolete("Use " + nameof(IsIPRelativeMemoryOperand) + " instead of this property", false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public bool IsIPRelativeMemoryOp {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => MemoryBase == Register.RIP || MemoryBase == Register.EIP;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the memory operand is RIP/EIP relative
|
|
/// </summary>
|
|
public bool IsIPRelativeMemoryOperand {
|
|
[MethodImpl(MethodImplOptions2.AggressiveInlining)]
|
|
get => MemoryBase == Register.RIP || MemoryBase == Register.EIP;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the RIP/EIP releative address ((<see cref="NextIP"/> or <see cref="NextIP32"/>) + <see cref="MemoryDisplacement"/>). This property is only valid if there's a memory operand with RIP/EIP relative addressing.
|
|
/// </summary>
|
|
public ulong IPRelativeMemoryAddress {
|
|
get {
|
|
ulong result = NextIP + (ulong)(int)MemoryDisplacement;
|
|
if (MemoryBase == Register.EIP)
|
|
result = (uint)result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Formats the instruction using the default formatter with default formatter options
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override string ToString() {
|
|
#if !NO_MASM_FORMATTER && !NO_FORMATTER
|
|
var output = new StringBuilderFormatterOutput();
|
|
new MasmFormatter().Format(ref this, output);
|
|
return output.ToString();
|
|
#elif !NO_NASM_FORMATTER && !NO_FORMATTER
|
|
var output = new StringBuilderFormatterOutput();
|
|
new NasmFormatter().Format(ref this, output);
|
|
return output.ToString();
|
|
#elif !NO_INTEL_FORMATTER && !NO_FORMATTER
|
|
var output = new StringBuilderFormatterOutput();
|
|
new IntelFormatter().Format(ref this, output);
|
|
return output.ToString();
|
|
#elif !NO_GAS_FORMATTER && !NO_FORMATTER
|
|
var output = new StringBuilderFormatterOutput();
|
|
new GasFormatter().Format(ref this, output);
|
|
return output.ToString();
|
|
#else
|
|
return base.ToString();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Default code size when an instruction was decoded
|
|
/// </summary>
|
|
public enum CodeSize {
|
|
/// <summary>
|
|
/// Unknown size
|
|
/// </summary>
|
|
Unknown = 0,
|
|
|
|
/// <summary>
|
|
/// 16-bit code
|
|
/// </summary>
|
|
Code16 = 1,
|
|
|
|
/// <summary>
|
|
/// 32-bit code
|
|
/// </summary>
|
|
Code32 = 2,
|
|
|
|
/// <summary>
|
|
/// 64-bit code
|
|
/// </summary>
|
|
Code64 = 3,
|
|
}
|
|
}
|