private X86Instruction DecodeOperands(Opcode opcode, byte op, string strFormat, InstrClass iclass)
{
if (strFormat == null)
{
return(null);
}
MachineOperand pOperand;
PrimitiveType width = null;
PrimitiveType iWidth = dataWidth;
byte modRm;
List <MachineOperand> ops = new List <MachineOperand>();
int i = 0;
while (i != strFormat.Length)
{
if (strFormat[i] == ',')
{
++i;
}
pOperand = null;
MemoryOperand memOp;
char chFmt = strFormat[i++];
switch (chFmt)
{
case '1':
pOperand = new ImmediateOperand(Constant.Byte(1));
break;
case '3':
pOperand = new ImmediateOperand(Constant.Byte(3));
break;
case 'A': // Absolute memory address.
++i;
if (!rdr.TryReadLeUInt16(out ushort off))
{
return(null);
}
if (!rdr.TryReadLeUInt16(out ushort seg))
{
return(null);
}
var addr = mode.CreateSegmentedAddress(seg, off);
if (addr == null)
{
return(null);
}
pOperand = new X86AddressOperand(addr);
break;
case 'C': // control register encoded in the reg field.
++i;
if (!TryEnsureModRM(out modRm))
{
return(null);
}
var creg = mode.GetControlRegister((modRm >> 3) & 7);
if (creg == null)
{
return(null);
}
pOperand = new RegisterOperand(creg);
break;
case 'D': // debug register encoded in the reg field.
++i;
if (!TryEnsureModRM(out modRm))
{
return(null);
}
var dreg = mode.GetDebugRegister((modRm >> 3) & 7);
if (dreg == null)
{
return(null);
}
pOperand = new RegisterOperand(dreg);
break;
case 'E': // memory or register operand specified by mod & r/m fields.
width = OperandWidth(strFormat, ref i);
++i;
pOperand = DecodeModRM(width, this.currentDecodingContext.SegmentOverride, GpRegFromBits);
if (pOperand == null)
{
return(null);
}
break;
case 'Q': // memory or register MMX operand specified by mod & r/m fields.
width = SseOperandWidth(strFormat, ref i);
pOperand = DecodeModRM(width, this.currentDecodingContext.SegmentOverride, MmxRegFromBits);
if (pOperand == null)
{
return(null);
}
break;
case 'G': // register operand specified by the reg field of the modRM byte.
width = OperandWidth(strFormat, ref i);
++i;
if (!TryEnsureModRM(out modRm))
{
return(null);
}
pOperand = new RegisterOperand(RegFromBitsRexR(modRm >> 3, width, GpRegFromBits));
break;
case 'H': // If VEX encoding, use vvvv register.
if (currentDecodingContext.IsVex)
{
width = SseOperandWidth(strFormat, ref i);
pOperand = new RegisterOperand(XmmRegFromBits(currentDecodingContext.VexRegister, width));
}
else
{
i = strFormat.IndexOf(',', i);
}
break;
case 'N': // MMX register operand specified by the r/m field of the modRM byte.
width = SseOperandWidth(strFormat, ref i);
if (!TryEnsureModRM(out modRm))
{
return(null);
}
pOperand = new RegisterOperand(RegFromBitsRexR(modRm, width, MmxRegFromBits));
break;
case 'P': // MMX register operand specified by the reg field of the modRM byte.
width = SseOperandWidth(strFormat, ref i);
if (!TryEnsureModRM(out modRm))
{
return(null);
}
pOperand = new RegisterOperand(RegFromBitsRexR(modRm >> 3, width, MmxRegFromBits));
break;
case 'I': // Immediate operand.
if (strFormat[i] == 'x')
{
iWidth = width; // Use width of the previous operand.
}
else
{
width = OperandWidth(strFormat, ref i); // Don't use the width of the previous operand.
}
++i;
pOperand = CreateImmediateOperand(width, dataWidth);
if (pOperand == null)
{
return(null);
}
break;
case 'L': // The upper 4 bits of the 8-bit immediate selects a 128-bit XMM register or a 256-bit YMM register, determined
// by operand type.
if (!rdr.TryReadByte(out var lReg))
{
return(null);
}
if (strFormat[i] == 'x')
{
iWidth = width; // Use width of the previous operand.
}
else
{
width = OperandWidth(strFormat, ref i); // Don't use the width of the previous operand.
}
++i;
pOperand = new RegisterOperand(XmmRegFromBits((lReg >> 4) & 0xF, width));
break;
case 'J': // Relative ("near") jump.
width = OperandWidth(strFormat, ref i);
++i;
Constant cOffset;
if (!rdr.TryRead(width, out cOffset))
{
return(null);
}
long jOffset = cOffset.ToInt64();
ulong uAddr = (ulong)((long)rdr.Address.Offset + jOffset);
if (defaultAddressWidth.BitSize == 64) //$REVIEW: not too keen on the switch statement here.
{
pOperand = AddressOperand.Ptr64(uAddr);
}
else if (defaultAddressWidth.BitSize == 32)
{
pOperand = AddressOperand.Ptr32((uint)uAddr);
}
else
{
pOperand = new ImmediateOperand(Constant.Create(defaultDataWidth, uAddr));
}
break;
case 'M': // modRM may only refer to memory.
width = OperandWidth(strFormat, ref i);
++i;
if (!TryEnsureModRM(out modRm))
{
return(null);
}
if ((modRm & 0xC0) == 0xC0)
{
return(null);
}
pOperand = DecodeModRM(dataWidth, this.currentDecodingContext.SegmentOverride, GpRegFromBits) as MemoryOperand;
if (pOperand == null)
{
return(null);
}
break;
case 'O': // Offset of the operand is encoded directly after the opcode.
width = OperandWidth(strFormat, ref i);
++i;
if (!rdr.TryReadLe(addressWidth, out var offset))
{
return(null);
}
pOperand = memOp = new MemoryOperand(width, offset);
memOp.SegOverride = this.currentDecodingContext.SegmentOverride;
break;
case 'R': // register operand specified by the mod field of the modRM byte.
width = OperandWidth(strFormat, ref i);
++i;
if (!TryEnsureModRM(out modRm))
{
return(null);
}
pOperand = new RegisterOperand(RegFromBitsRexR(modRm, width, GpRegFromBits));
break;
case 'S': // Segment register encoded by reg field of modRM byte.
++i; // Skip over the 'w'.
if (!TryEnsureModRM(out modRm))
{
return(null);
}
pOperand = new RegisterOperand(SegFromBits(modRm >> 3));
break;
case 'U': // XMM operand specified by the modRm field of the modRM byte.
width = SseOperandWidth(strFormat, ref i);
if (!TryEnsureModRM(out modRm))
{
return(null);
}
pOperand = new RegisterOperand(RegFromBitsRexR(modRm, width, XmmRegFromBits));
break;
case 'V': // XMM operand specified by the reg field of the modRM byte.
width = SseOperandWidth(strFormat, ref i);
if (!TryEnsureModRM(out modRm))
{
return(null);
}
pOperand = new RegisterOperand(RegFromBitsRexR(modRm >> 3, width, XmmRegFromBits));
break;
case 'W': // memory or XMM operand specified by mod & r/m fields.
width = SseOperandWidth(strFormat, ref i);
pOperand = DecodeModRM(width, this.currentDecodingContext.SegmentOverride, XmmRegFromBits);
break;
case 'a': // Implicit use of accumulator.
pOperand = new RegisterOperand(RegFromBitsRexW(0, OperandWidth(strFormat, ref i)));
++i;
break;
case 'b':
iWidth = PrimitiveType.Byte;
pOperand = null;
break;
case 'c': // Implicit use of CL.
pOperand = new RegisterOperand(Registers.cl);
break;
case 'd': // Implicit use of DX or EDX.
width = OperandWidth(strFormat, ref i);
++i;
pOperand = new RegisterOperand(RegFromBitsRexW(2, width));
break;
case 'r': // Register encoded as last 3 bits of instruction.
iWidth = width = OperandWidth(strFormat, ref i);
++i;
pOperand = new RegisterOperand(RegFromBitsRexB(op, width));
break;
case 's': // Segment encoded as next byte of the format string.
pOperand = new RegisterOperand(SegFromBits(strFormat[i++] - '0'));
break;
case 'F': // Floating-point ST(x)
if (!TryEnsureModRM(out modRm))
{
return(null);
}
pOperand = new FpuOperand(modRm & 0x07);
break;
case 'f': // ST(0)
pOperand = new FpuOperand(0);
break;
default:
throw new ArgumentOutOfRangeException(string.Format("Unknown format specifier '{0}' at position {1} of format string '{2}'.", chFmt, i, strFormat));
}
if (pOperand != null)
{
ops.Add(pOperand);
}
}
return(new X86Instruction(opcode, iclass, iWidth, addressWidth, ops.ToArray())
{
repPrefix = this.currentDecodingContext.F2Prefix ? 2 :
this.currentDecodingContext.F3Prefix ? 3 : 0
});
}