/*
Copyright (C) 2018 de4dot@gmail.com
This file is part of Iced.
Iced is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Iced is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Iced. If not, see .
*/
#if !NO_INSTR_INFO
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using Iced.Intel.InstructionInfoInternal;
namespace Iced.Intel {
///
/// Contains information about an instruction, eg. read/written registers, read/written RFLAGS bits, CPUID feature bit, etc
///
public readonly struct InstructionInfo {
readonly UsedRegister[] usedRegisters;
readonly UsedMemory[] usedMemoryLocations;
readonly ushort usedRegistersLength;
readonly ushort usedMemoryLocationsLength;
readonly ushort opMaskFlags;
readonly byte cpuidFeature;
readonly byte flowControl;
readonly byte encoding;
readonly byte rflagsInfo;
readonly byte flags;
[Flags]
internal enum OpMaskFlags : ushort {
OpAccessMask = 7,
Op1AccessShift = 3,
Op2AccessShift = 6,
Op3AccessShift = 9,
Op4AccessShift = 12,
}
[Flags]
enum Flags : byte {
SaveRestore = 0x01,
StackInstruction = 0x02,
ProtectedMode = 0x04,
Privileged = 0x08,
}
internal InstructionInfo(ref SimpleList usedRegisters, ref SimpleList usedMemoryLocations, uint opMaskFlags, RflagsInfo rflagsInfo, uint flags1, uint flags2) {
Debug.Assert(DecoderConstants.MaxOpCount == 5);
Debug.Assert(usedRegisters.Array != null);
this.usedRegisters = usedRegisters.Array;
Debug.Assert(usedMemoryLocations.Array != null);
this.usedMemoryLocations = usedMemoryLocations.Array;
Debug.Assert((uint)usedRegisters.ValidLength <= ushort.MaxValue);
usedRegistersLength = (ushort)usedRegisters.ValidLength;
Debug.Assert((uint)usedMemoryLocations.ValidLength <= ushort.MaxValue);
usedMemoryLocationsLength = (ushort)usedMemoryLocations.ValidLength;
this.opMaskFlags = (ushort)opMaskFlags;
Debug.Assert(((flags2 >> (int)InfoFlags2.CpuidFeatureShift) & (uint)InfoFlags2.CpuidFeatureMask) <= byte.MaxValue);
cpuidFeature = (byte)((flags2 >> (int)InfoFlags2.CpuidFeatureShift) & (uint)InfoFlags2.CpuidFeatureMask);
Debug.Assert(((flags2 >> (int)InfoFlags2.FlowControlShift) & (uint)InfoFlags2.FlowControlMask) <= byte.MaxValue);
flowControl = (byte)((flags2 >> (int)InfoFlags2.FlowControlShift) & (uint)InfoFlags2.FlowControlMask);
Debug.Assert(((flags2 >> (int)InfoFlags2.EncodingShift) & (uint)InfoFlags2.EncodingMask) <= byte.MaxValue);
encoding = (byte)((flags2 >> (int)InfoFlags2.EncodingShift) & (uint)InfoFlags2.EncodingMask);
Debug.Assert((uint)rflagsInfo <= byte.MaxValue);
Debug.Assert((uint)rflagsInfo < (uint)RflagsInfo.Last);
this.rflagsInfo = (byte)rflagsInfo;
Debug.Assert((uint)InfoFlags1.SaveRestore == 0x08000000);
Debug.Assert((uint)InfoFlags1.StackInstruction == 0x10000000);
Debug.Assert((uint)InfoFlags1.ProtectedMode == 0x20000000);
Debug.Assert((uint)InfoFlags1.Privileged == 0x40000000);
Debug.Assert((uint)Flags.SaveRestore == 0x01);
Debug.Assert((uint)Flags.StackInstruction == 0x02);
Debug.Assert((uint)Flags.ProtectedMode == 0x04);
Debug.Assert((uint)Flags.Privileged == 0x08);
// Bit 4 could be set but we don't use it so we don't need to mask it out
flags = (byte)(flags1 >> 27);
}
void ThrowArgumentOutOfRangeException(string paramName) => throw new ArgumentOutOfRangeException(paramName);
///
/// Gets a struct iterator that returns all read and written registers. There are some exceptions, this method doesn't return all used registers:
///
/// 1) If is true, or
///
/// 2) If it's a or instruction (call, sysenter, int n etc), it can read and write any register (including RFLAGS).
///
///
public UsedRegisterIterator GetUsedRegisters() => new UsedRegisterIterator(usedRegisters, usedRegistersLength);
///
/// Gets a struct iterator that returns all read and written memory locations
///
///
public UsedMemoryIterator GetUsedMemory() => new UsedMemoryIterator(usedMemoryLocations, usedMemoryLocationsLength);
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public struct UsedRegisterIterator : IEnumerable, IEnumerator {
readonly UsedRegister[] usedRegisters;
readonly uint length;
int index;
internal UsedRegisterIterator(UsedRegister[] usedRegisters, uint length) {
this.usedRegisters = usedRegisters;
this.length = length;
index = -1;
}
public UsedRegisterIterator GetEnumerator() => this;
public UsedRegister Current => usedRegisters[index];
public bool MoveNext() {
index++;
return (uint)index < length;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
UsedRegister IEnumerator.Current => Current;
object IEnumerator.Current => Current;
bool IEnumerator.MoveNext() => MoveNext();
void IEnumerator.Reset() => throw new NotSupportedException();
public void Dispose() { }
}
public struct UsedMemoryIterator : IEnumerable, IEnumerator {
readonly UsedMemory[] usedMemoryLocations;
readonly uint length;
int index;
internal UsedMemoryIterator(UsedMemory[] usedMemoryLocations, uint length) {
this.usedMemoryLocations = usedMemoryLocations;
this.length = length;
index = -1;
}
public UsedMemoryIterator GetEnumerator() => this;
public UsedMemory Current => usedMemoryLocations[index];
public bool MoveNext() {
index++;
return (uint)index < length;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
UsedMemory IEnumerator.Current => Current;
object IEnumerator.Current => Current;
bool IEnumerator.MoveNext() => MoveNext();
void IEnumerator.Reset() => throw new NotSupportedException();
public void Dispose() { }
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
///
/// true if the instruction isn't available in real mode or virtual 8086 mode
///
public bool ProtectedMode => (flags & (uint)Flags.ProtectedMode) != 0;
///
/// true if this is a privileged instruction
///
public bool Privileged => (flags & (uint)Flags.Privileged) != 0;
///
/// true if this is an instruction that implicitly uses the stack pointer (SP/ESP/RSP), eg. call, push, pop, ret, etc.
/// See also
///
public bool StackInstruction => (flags & (uint)Flags.StackInstruction) != 0;
///
/// true if it's an instruction that saves or restores too many registers (eg. fxrstor, xsave, etc).
/// won't return all read/written registers.
///
public bool SaveRestoreInstruction => (flags & (uint)Flags.SaveRestore) != 0;
///
/// Instruction encoding, eg. legacy, VEX, EVEX, ...
///
public EncodingKind Encoding => (EncodingKind)encoding;
///
/// CPU or CPUID feature flag
///
public CpuidFeature CpuidFeature => (CpuidFeature)cpuidFeature;
///
/// Flow control info
///
public FlowControl FlowControl => (FlowControl)flowControl;
///
/// Operand #0 access
///
public OpAccess Op0Access => (OpAccess)(opMaskFlags & (uint)OpMaskFlags.OpAccessMask);
///
/// Operand #1 access
///
public OpAccess Op1Access => (OpAccess)(((uint)opMaskFlags >> (int)OpMaskFlags.Op1AccessShift) & (uint)OpMaskFlags.OpAccessMask);
///
/// Operand #2 access
///
public OpAccess Op2Access => (OpAccess)(((uint)opMaskFlags >> (int)OpMaskFlags.Op2AccessShift) & (uint)OpMaskFlags.OpAccessMask);
///
/// Operand #3 access
///
public OpAccess Op3Access => (OpAccess)(((uint)opMaskFlags >> (int)OpMaskFlags.Op3AccessShift) & (uint)OpMaskFlags.OpAccessMask);
///
/// Operand #4 access
///
public OpAccess Op4Access => (OpAccess)(((uint)opMaskFlags >> (int)OpMaskFlags.Op4AccessShift) & (uint)OpMaskFlags.OpAccessMask);
///
/// Gets operand access
///
/// Operand number, 0-4
///
public OpAccess GetOpAccess(int operand) {
switch (operand) {
case 0: return Op0Access;
case 1: return Op1Access;
case 2: return Op2Access;
case 3: return Op3Access;
case 4: return Op4Access;
default:
ThrowArgumentOutOfRangeException(nameof(operand));
return 0;
}
}
///
/// All flags that are read by the CPU when executing the instruction
///
public RflagsBits RflagsRead => (RflagsBits)RflagsInfoConstants.flagsRead[rflagsInfo];
///
/// All flags that are written by the CPU, except those flags that are known to be undefined, always set or always cleared. See also
///
public RflagsBits RflagsWritten => (RflagsBits)RflagsInfoConstants.flagsWritten[rflagsInfo];
///
/// All flags that are always cleared by the CPU
///
public RflagsBits RflagsCleared => (RflagsBits)RflagsInfoConstants.flagsCleared[rflagsInfo];
///
/// All flags that are always set by the CPU
///
public RflagsBits RflagsSet => (RflagsBits)RflagsInfoConstants.flagsSet[rflagsInfo];
///
/// All flags that are undefined after executing the instruction
///
public RflagsBits RflagsUndefined => (RflagsBits)RflagsInfoConstants.flagsUndefined[rflagsInfo];
///
/// All flags that are modified by the CPU. This is + + +
///
public RflagsBits RflagsModified => (RflagsBits)RflagsInfoConstants.flagsModified[rflagsInfo];
}
}
#endif