/// <summary> /// Decodes an instruction. /// </summary> /// <param name="code">X86 binary code.</param> /// <param name="startIndex">Index of the first byte to start /// decoding.</param> /// <param name="context">Decoding context.</param> /// <returns>The decoded instruction.</returns> /// <exception cref="InvalidInstructionException">If decoding failed. /// </exception> public Instruction Decode( byte[] code, int startIndex, int count, DecoderContext context) { Instruction instruction = new Instruction(); // Create an instruction reader. InstructionReader reader = new InstructionReader(code, startIndex, count); // Reset the context. context.AddressSize = CpuSize.Use16Bit; context.OperandSize = CpuSize.Use16Bit; context.SegmentOverride = Register.None; // Decode prefixes. Prefixes prefix = DecodeLegacyPrefixes(reader); if ((prefix & Prefixes.OperandSizeOverride) != 0) { context.OperandSize = CpuSize.Use32Bit; } if ((prefix & Prefixes.AddressSizeOverride) != 0) { context.AddressSize = CpuSize.Use32Bit; } if ((prefix & Prefixes.Group2) != 0) { Register seg = Register.None; switch (prefix & Prefixes.Group2) { case Prefixes.ES: seg = Register.ES; break; case Prefixes.CS: seg = Register.CS; break; case Prefixes.SS: seg = Register.SS; break; case Prefixes.DS: seg = Register.DS; break; case Prefixes.FS: seg = Register.FS; break; case Prefixes.GS: seg = Register.GS; break; } context.SegmentOverride = seg; } instruction.Prefix = prefix; // Decode the opcode to retrieve opcode specification. Op spec = DecodeOpcode(reader); instruction.Operation = spec.Operation; // Decode operands. instruction.Operands = new Operand[spec.Operands.Length]; for (int i = 0; i < spec.Operands.Length; i++) { instruction.Operands[i] = DecodeOperand(spec.Operands[i], reader, context); } // Update the encoded length of the instruction. instruction.EncodedLength = reader.Position; //instruction.Location = location; return instruction; }
static Operand DecodeOperand(O spec, InstructionReader reader, DecoderContext context) { int number; CpuSize size; switch (spec) { case O.Imm0: case O.Imm1: case O.Imm2: case O.Imm3: return new ImmediateOperand(spec - O.Imm0, CpuSize.Use8Bit); case O.ES: case O.CS: case O.SS: case O.DS: return CreateRegisterOperand( RegisterType.Segment, spec - O.ES, CpuSize.Use16Bit); case O.AL: case O.CL: case O.DL: case O.BL: return CreateRegisterOperand( RegisterType.General, spec - O.AL, CpuSize.Use8Bit); case O.AH: case O.CH: case O.DH: case O.BH: return CreateRegisterOperand( RegisterType.HighByte, spec - O.AH, CpuSize.Use8Bit); case O.AX: case O.CX: case O.DX: case O.BX: case O.SP: case O.BP: case O.SI: case O.DI: return CreateRegisterOperand( RegisterType.General, spec - O.AX, CpuSize.Use16Bit); case O.eAX: case O.eCX: case O.eDX: case O.eBX: case O.eSP: case O.eBP: case O.eSI: case O.eDI: number = spec - O.eAX; size = (context.OperandSize == CpuSize.Use16Bit) ? CpuSize.Use16Bit : CpuSize.Use32Bit; return CreateRegisterOperand(RegisterType.General, number, size); case O.rAX: case O.rCX: case O.rDX: case O.rBX: case O.rSP: case O.rBP: case O.rSI: case O.rDI: number = spec - O.rAX; return CreateRegisterOperand(RegisterType.General, number, context.OperandSize); case O.ST0: return new RegisterOperand(Register.ST0); case O.Ap: // off:seg encoded in the instruction if (context.OperandSize != CpuSize.Use16Bit) { throw new NotSupportedException(); } else { var off = reader.ReadImmediate(CpuSize.Use16Bit); var seg = reader.ReadImmediate(CpuSize.Use16Bit); return new PointerOperand( new Operand.LocationAware<UInt16>(seg.Location, (UInt16)seg.Value), new Operand.LocationAware<UInt32>(off.Location, (UInt16)off.Value)); } case O.Eb: // r/m; 8-bit return DecodeMemoryOperand(reader, RegisterType.General, CpuSize.Use8Bit, context); case O.Ep: // r/m; contains far ptr if (context.OperandSize != CpuSize.Use16Bit) throw new NotSupportedException(); // TBD: operand size? address size? return DecodeMemoryOperand(reader, RegisterType.General, CpuSize.Use16Bit, context); case O.Ev: // r/m; 16, 32, or 64 bit return DecodeMemoryOperand(reader, RegisterType.General, context.OperandSize, context); case O.Ew: // r/m; 16-bit return DecodeMemoryOperand(reader, RegisterType.General, CpuSize.Use16Bit, context); case O.Fv: // FLAGS/EFLAGS/RFLAGS return new RegisterOperand(Register.FLAGS.Resize(context.OperandSize)); case O.Gb: // general-purpose register; byte return DecodeRegisterOperand(reader, RegisterType.General, CpuSize.Use8Bit, context); case O.Gv: // general-purpose register; 16, 32, or 64 bit return DecodeRegisterOperand(reader, RegisterType.General, context.OperandSize, context); case O.Gw: // general-purpose register; 16-bit return DecodeRegisterOperand(reader, RegisterType.General, CpuSize.Use16Bit, context); case O.Gz: // general-purpose register; 16 or 32 bit return DecodeRegisterOperand( reader, RegisterType.General, context.OperandSize == CpuSize.Use16Bit ? CpuSize.Use16Bit : CpuSize.Use32Bit, context); case O.Ib: // immediate; byte return DecodeImmediateOperand(reader, CpuSize.Use8Bit); case O.Iw: // immediate; 16 bit return DecodeImmediateOperand(reader, CpuSize.Use16Bit); case O.Iv: // immediate, 16, 32, or 64 bit return DecodeImmediateOperand(reader, context.OperandSize); case O.Iz: // immediate, 16 or 32 bit return DecodeImmediateOperand(reader, (context.OperandSize == CpuSize.Use16Bit) ? CpuSize.Use16Bit : CpuSize.Use32Bit); case O.Jb: // immediate encodes relative offset; byte return DecodeRelativeOperand(reader, CpuSize.Use8Bit); case O.Jz: // immediate encodes relative offset; 16 or 32 bit return DecodeRelativeOperand(reader, (context.OperandSize == CpuSize.Use16Bit) ? CpuSize.Use16Bit : CpuSize.Use32Bit); case O.M_: // r/m must refer to memory; contains void pointer return DecodeMemoryOperand(reader, RegisterType.None, CpuSize.Default, context); case O.Ma: // r/m must refer to memory; contains a pair of words or dwords // TODO: handle 32-bit return DecodeMemoryOperand(reader, RegisterType.None, CpuSize.Use32Bit, context); case O.Mp: // r/m must refer to memory; contains far pointer // seg:ptr of 32, 48, or 80 bits. if (context.OperandSize != CpuSize.Use16Bit) throw new NotSupportedException(); return DecodeMemoryOperand(reader, RegisterType.None, CpuSize.Use32Bit, context); case O.Mb: // r/m refers to memory; byte return DecodeMemoryOperand(reader, RegisterType.None, CpuSize.Use8Bit, context); case O.Mw: // r/m refers to memory; word return DecodeMemoryOperand(reader, RegisterType.None, CpuSize.Use16Bit, context); case O.Md: // r/m refers to memory; dword return DecodeMemoryOperand(reader, RegisterType.None, CpuSize.Use32Bit, context); case O.Mq: return DecodeMemoryOperand(reader, RegisterType.None, CpuSize.Use64Bit, context); case O.M14or28b: // r/m refers to memory; 14 or 28 bytes // TODO: we actually need to check the CPU operation mode, // not the operand-size attribute. return DecodeMemoryOperand( reader, RegisterType.None, (context.OperandSize == CpuSize.Use16Bit) ? CpuSize.Use14Bytes : CpuSize.Use28Bytes, context); case O.M32fp: // r/m refers to memory; single-precision float return DecodeMemoryOperand(reader, RegisterType.None, CpuSize.Use32Bit, context); case O.M64fp: // r/m refers to memory; double-precision float return DecodeMemoryOperand(reader, RegisterType.None, CpuSize.Use64Bit, context); case O.M80fp: // r/m refers to memory; extended-precision float return DecodeMemoryOperand(reader, RegisterType.None, CpuSize.Use80Bit, context); case O.Ob: // absolute address (w/o segment); byte // TODO: check 64-bit mode behavior return new MemoryOperand { Size = CpuSize.Use8Bit, Displacement = reader.ReadImmediate(context.AddressSize), // Segment = Register.DS, }; case O.Ov: // absolute address (w/o segment); 16, 32, or 64 bit // TODO: check 64-bit mode behavior return new MemoryOperand { Size = context.OperandSize, Displacement = reader.ReadImmediate(context.AddressSize), // Segment = Register.DS, }; case O.Sw: // REG(modrm) selects segment register return DecodeRegisterOperand(reader, RegisterType.Segment, CpuSize.Use16Bit, context); case O.T80fp: // r/m selects x87 FPU register ST0-ST7 return CreateRegisterOperand(RegisterType.Fpu, reader.GetModRM().RM, CpuSize.Use80Bit); case O.Xb: // memory addressed by DS:rSI; byte return new MemoryOperand { Size = CpuSize.Use8Bit, Segment = Register.DS, Base = Register.SI.Resize(context.AddressSize) }; case O.Xv: // memory addressed by DS:rSI; 16, 32, or 64 bit return new MemoryOperand { Size = context.OperandSize, Segment = Register.DS, Base = Register.SI.Resize(context.AddressSize) }; case O.Xz: // memory addressed by DS:rSI; 16 or 32 bit return new MemoryOperand { Size = CpuSize.Use16Bit, // TODO: handle 32 bit Segment = Register.DS, Base = Register.SI.Resize(context.AddressSize) }; case O.Yb: // memory addressed by ES:rDI; byte return new MemoryOperand { Size = CpuSize.Use8Bit, Segment = Register.ES, Base = Register.DI.Resize(context.AddressSize) }; case O.Yv: // memory addressed by ES:rDI; 16, 32, or 64 bit return new MemoryOperand { Size = context.OperandSize, Segment = Register.ES, Base = Register.DI.Resize(context.AddressSize) }; case O.Yz: // memory addressed by ES:rDI; 16 or 32 bit return new MemoryOperand { Size = CpuSize.Use16Bit, // TODO: handle 32 bit Segment = Register.ES, Base = Register.DI.Resize(context.AddressSize) }; case O._XLAT: return new MemoryOperand { Size = CpuSize.Use8Bit, Segment = context.SegmentOverride, Base = (context.AddressSize == CpuSize.Use16Bit) ? Register.BX : Register.EBX }; default: throw new NotImplementedException( "DecodeOperand() is not implemented for operand type " + spec.ToString()); } }
static RelativeOperand DecodeRelativeOperand(InstructionReader reader, CpuSize size) { return new RelativeOperand(reader.ReadImmediate(size)); }
static ImmediateOperand DecodeImmediateOperand(InstructionReader reader, CpuSize size) { return new ImmediateOperand(reader.ReadImmediate(size), size); }
// Note: we need to take into account OperandSizeOverride and // AddressSizeOverride!!!!!! /// <summary> /// Decodes a memory operand encoded by the ModRM byte, SIB byte, and /// Displacement, or a register operand if MOD=3 and registerType is /// specified. /// </summary> /// <param name="reader">Instruction reader.</param> /// <param name="registerType">Type of the register to return if the /// Mod field of the ModR/M byte is 3. If this parameter is set to /// RegisterType.None, an exception is thrown if Mod=3.</param> /// <param name="operandSize">Size of the returned operand.</param> /// <param name="context">Decoding context.</param> /// <returns>The decoded memory or register operand.</returns> /// <exception cref="InvalidInstructionException">If registerType is /// set to None but the ModR/M byte encodes a register.</exception> static Operand DecodeMemoryOperand( InstructionReader reader, RegisterType registerType, CpuSize operandSize, DecoderContext context) { if (context.CpuMode == CpuMode.X64Mode) throw new NotSupportedException(); //if (operandSize == CpuSize.Default) // throw new ArgumentException("operandSize is not specified."); if (context.AddressSize != CpuSize.Use16Bit) throw new NotSupportedException("32-bit addressing mode is not supported."); ModRM modrm = reader.GetModRM(); int rm = modrm.RM; int mod = modrm.MOD; // Decode a register if MOD = (11). if (mod == 3) { // If the instruction expects a memory operand, throw an exception. if (registerType == RegisterType.None) { throw new InvalidInstructionException( "The instruction expects a memory operand, but the ModR/M byte encodes a register."); } // Treat AH-DH specially. if (registerType == RegisterType.General && operandSize == CpuSize.Use8Bit && rm >= 4) { return new RegisterOperand(new Register( RegisterType.HighByte, rm - 4, CpuSize.Use8Bit)); } else { return new RegisterOperand(new Register(registerType, rm, operandSize)); } } // Take into account segment override prefix if present. Register segment = context.SegmentOverride; // Special treatment for MOD = (00) and RM = (110). // This encodes a 16-bit sign-extended displacement. if (mod == 0 && rm == 6) { return new MemoryOperand { Size = operandSize, Segment = segment, Displacement = reader.ReadImmediate(CpuSize.Use16Bit) }; } /* Decode an indirect memory address XX[+YY][+disp]. */ MemoryOperand mem = new MemoryOperand(); mem.Size = operandSize; mem.Segment = segment; switch (rm) { case 0: /* [BX+SI] */ mem.Base = Register.BX; mem.Index = Register.SI; break; case 1: /* [BX+DI] */ mem.Base = Register.BX; mem.Index = Register.DI; break; case 2: /* [BP+SI] */ mem.Base = Register.BP; mem.Index = Register.SI; break; case 3: /* [BP+DI] */ mem.Base = Register.BP; mem.Index = Register.DI; break; case 4: /* [SI] */ mem.Base = Register.SI; break; case 5: /* [DI] */ mem.Base = Register.DI; break; case 6: /* [BP] */ mem.Base = Register.BP; break; case 7: /* [BX] */ mem.Base = Register.BX; break; } if (mod == 1) /* disp8, sign-extended */ { mem.Displacement = reader.ReadImmediate(CpuSize.Use8Bit); } else if (mod == 2) /* disp16, sign-extended */ { mem.Displacement = reader.ReadImmediate(CpuSize.Use16Bit); } return mem; }
/// <summary> /// Decodes a register operand from the REG field of the ModR/M byte. /// Since the REG field only contains the register number, additional /// parameters are used to determine the register's type and size. /// </summary> /// <param name="reader">Instruction reader.</param> /// <param name="registerType">Type of the register to return.</param> /// <param name="operandSize">Size of the register to return.</param> /// <param name="context">Decoding context.</param> /// <returns>The decoded register operand.</returns> private static RegisterOperand DecodeRegisterOperand( InstructionReader reader, RegisterType registerType, CpuSize operandSize, DecoderContext context) { if (context.CpuMode == CpuMode.X64Mode) throw new NotSupportedException(); if (operandSize == CpuSize.Default) throw new ArgumentException("operandSize is not specified."); int reg = reader.GetModRM().REG; if (registerType == RegisterType.General && operandSize == CpuSize.Use8Bit && reg >= 4) { return new RegisterOperand(new Register( RegisterType.HighByte, reg - 4, CpuSize.Use8Bit)); } return new RegisterOperand(new Register(registerType, reg, operandSize)); }
private Op DecodeFpuOpcode(InstructionReader reader, byte firstByte) { ModRM modrm = reader.GetModRM(); int mod = modrm.MOD; int reg = modrm.REG; int rm = modrm.RM; Op x = Op.Empty; switch (firstByte) { case 0xD8: if (mod == 3) { switch (reg) { case 0: x = new Op(Operation.FADD, O.ST0, O.T80fp); break; case 1: x = new Op(Operation.FMUL, O.ST0, O.T80fp); break; case 2: x = new Op(Operation.FCOM, O.ST0, O.T80fp); break; case 3: x = new Op(Operation.FCOMP, O.ST0, O.T80fp); break; case 4: x = new Op(Operation.FSUB, O.ST0, O.T80fp); break; case 5: x = new Op(Operation.FSUBR, O.ST0, O.T80fp); break; case 6: x = new Op(Operation.FDIV, O.ST0, O.T80fp); break; case 7: x = new Op(Operation.FDIVR, O.ST0, O.T80fp); break; } } else { switch (reg) { case 0: x = new Op(Operation.FADD, O.ST0, O.M32fp); break; case 1: x = new Op(Operation.FMUL, O.ST0, O.M32fp); break; case 2: x = new Op(Operation.FCOM, O.ST0, O.M32fp); break; case 3: x = new Op(Operation.FCOMP, O.ST0, O.M32fp); break; case 4: x = new Op(Operation.FSUB, O.ST0, O.M32fp); break; case 5: x = new Op(Operation.FSUBR, O.ST0, O.M32fp); break; case 6: x = new Op(Operation.FDIV, O.ST0, O.M32fp); break; case 7: x = new Op(Operation.FDIVR, O.ST0, O.M32fp); break; } } break; case 0xD9: if (mod == 3) { switch (reg) { case 0: x = new Op(Operation.FLD, O.ST0, O.T80fp); break; case 1: x = new Op(Operation.FXCH, O.ST0, O.T80fp); break; case 2: switch (rm) { case 0: x = new Op(Operation.FNOP); break; } break; case 3: break; case 4: switch (rm) { case 0: x = new Op(Operation.FCHS); break; case 1: x = new Op(Operation.FABS); break; case 4: x = new Op(Operation.FTST); break; case 5: x = new Op(Operation.FXAM); break; } break; case 5: switch (rm) { case 0: x = new Op(Operation.FLD1); break; case 1: x = new Op(Operation.FLDL2T); break; case 2: x = new Op(Operation.FLDL2E); break; case 3: x = new Op(Operation.FLDPI); break; case 4: x = new Op(Operation.FLDLG2); break; case 5: x = new Op(Operation.FLDLN2); break; case 6: x = new Op(Operation.FLDZ); break; } break; case 6: switch (rm) { case 0: x = new Op(Operation.F2XM1); break; case 1: x = new Op(Operation.FYL2X); break; case 2: x = new Op(Operation.FPTAN); break; case 3: x = new Op(Operation.FPATAN); break; case 4: x = new Op(Operation.FXTRACT); break; case 5: x = new Op(Operation.FPREM1); break; case 6: x = new Op(Operation.FDECSTP); break; case 7: x = new Op(Operation.FINCSTP); break; } break; case 7: switch (rm) { case 0: x = new Op(Operation.FPREM); break; case 1: x = new Op(Operation.FYL2XP1); break; case 2: x = new Op(Operation.FSQRT); break; case 3: x = new Op(Operation.FSINCOS); break; case 4: x = new Op(Operation.FRNDINT); break; case 5: x = new Op(Operation.FSCALE); break; case 6: x = new Op(Operation.FSIN); break; case 7: x = new Op(Operation.FCOS); break; } break; } } else { switch (reg) { case 0: x = new Op(Operation.FLD, O.ST0, O.M32fp); break; case 1: break; case 2: x = new Op(Operation.FST, O.ST0, O.M32fp); break; case 3: x = new Op(Operation.FSTP, O.ST0, O.M32fp); break; case 4: x = new Op(Operation.FLDENV, O.M14or28b); break; case 5: x = new Op(Operation.FLDCW, O.Mw); break; case 6: x = new Op(Operation.FSTENV, O.M14or28b); break; case 7: x = new Op(Operation.FSTCW, O.Mw); break; } } break; case 0xDA: if (mod == 3) { switch (reg) { case 0: x = new Op(Operation.FCMOVB, O.ST0, O.T80fp); break; case 1: x = new Op(Operation.FCMOVE, O.ST0, O.T80fp); break; case 2: x = new Op(Operation.FCMOVBE, O.ST0, O.T80fp); break; case 3: x = new Op(Operation.FCMOVU, O.ST0, O.T80fp); break; case 6: switch (rm) { case 1: x = new Op(Operation.FUCOMPP); break; } break; } } else { switch (reg) { case 0: x = new Op(Operation.FIADD, O.ST0, O.Md); break; case 1: x = new Op(Operation.FIMUL, O.ST0, O.Md); break; case 2: x = new Op(Operation.FICOM, O.ST0, O.Md); break; case 3: x = new Op(Operation.FICOMP, O.ST0, O.Md); break; case 4: x = new Op(Operation.FISUB, O.ST0, O.Md); break; case 5: x = new Op(Operation.FISUBR, O.ST0, O.Md); break; case 6: x = new Op(Operation.FIDIV, O.ST0, O.Md); break; case 7: x = new Op(Operation.FIDIVR, O.ST0, O.Md); break; } } break; case 0xDB: if (mod == 3) { switch (reg) { case 0: x = new Op(Operation.FCMOVNB, O.ST0, O.T80fp); break; case 1: x = new Op(Operation.FCMOVNE, O.ST0, O.T80fp); break; case 2: x = new Op(Operation.FCMOVNBE, O.ST0, O.T80fp); break; case 3: x = new Op(Operation.FCMOVNU, O.ST0, O.T80fp); break; case 4: switch (rm) { case 2: x = new Op(Operation.FCLEX); break; case 3: x = new Op(Operation.FINIT); break; } break; case 5: x = new Op(Operation.FUCOMI, O.ST0, O.T80fp); break; case 6: x = new Op(Operation.FCOMI, O.ST0, O.T80fp); break; case 7: break; } } else { switch (reg) { case 0: x = new Op(Operation.FILD, O.Md); break; case 1: x = new Op(Operation.FISTTP, O.Md); break; case 2: x = new Op(Operation.FIST, O.Md); break; case 3: x = new Op(Operation.FISTP, O.Md); break; case 4: break; case 5: x = new Op(Operation.FLD, O.M80fp); break; case 6: break; case 7: x = new Op(Operation.FSTP, O.M80fp); break; } } break; case 0xDC: if (mod == 3) { switch (reg) { case 0: x = new Op(Operation.FADD,O.T80fp, O.ST0); break; case 1: x = new Op(Operation.FMUL, O.T80fp, O.ST0); break; case 2: break; case 3: break; case 4: x = new Op(Operation.FSUBR, O.T80fp, O.ST0); break; case 5: x = new Op(Operation.FSUB, O.T80fp, O.ST0); break; case 6: x = new Op(Operation.FDIVR, O.T80fp, O.ST0); break; case 7: x = new Op(Operation.FDIV, O.T80fp, O.ST0); break; } } else { switch (reg) { case 0: x = new Op(Operation.FADD, O.ST0, O.M64fp); break; case 1: x = new Op(Operation.FMUL, O.ST0, O.M64fp); break; case 2: x = new Op(Operation.FCOM, O.ST0, O.M64fp); break; case 3: x = new Op(Operation.FCOMP, O.ST0, O.M64fp); break; case 4: x = new Op(Operation.FSUB, O.ST0, O.M64fp); break; case 5: x = new Op(Operation.FSUBR, O.ST0, O.M64fp); break; case 6: x = new Op(Operation.FDIV, O.ST0, O.M64fp); break; case 7: x = new Op(Operation.FDIVR, O.ST0, O.M64fp); break; } } break; case 0xDD: if (mod == 3) { switch (reg) { case 0: x = new Op(Operation.FFREE, O.T80fp); break; case 1: break; case 2: x = new Op(Operation.FST, O.T80fp); break; case 3: x = new Op(Operation.FSTP, O.T80fp); break; case 4: x = new Op(Operation.FUCOM, O.T80fp, O.ST0); break; case 5: x = new Op(Operation.FUCOMP, O.T80fp, O.ST0); break; case 6: break; case 7: break; } } else { switch (reg) { case 0: x = new Op(Operation.FLD, O.M64fp); break; case 1: x = new Op(Operation.FISTTP, O.Mq); break; case 2: x = new Op(Operation.FST, O.M64fp); break; case 3: x = new Op(Operation.FSTP, O.M64fp); break; case 4: x = new Op(Operation.FRSTOR, O.Mb); break; // TODO: should be 98/108 bytes case 5: break; case 6: x = new Op(Operation.FSAVE, O.Mb); break; // TODO: should be 98/108 bytes case 7: x = new Op(Operation.FSTSW, O.Mw); break; } } break; case 0xDE: if (mod == 3) { switch (reg) { case 0: x = new Op(Operation.FADDP, O.T80fp, O.ST0); break; case 1: x = new Op(Operation.FMULP, O.T80fp, O.ST0); break; case 2: break; case 3: switch (rm) { case 1: x = new Op(Operation.FCOMPP); break; } break; case 4: x = new Op(Operation.FSUBRP, O.T80fp, O.ST0); break; case 5: x = new Op(Operation.FSUBP, O.T80fp, O.ST0); break; case 6: x = new Op(Operation.FDIVRP, O.T80fp, O.ST0); break; case 7: x = new Op(Operation.FDIVP, O.T80fp, O.ST0); break; } } else { switch (reg) { case 0: x = new Op(Operation.FIADD, O.ST0, O.Mw); break; case 1: x = new Op(Operation.FIMUL, O.ST0, O.Mw); break; case 2: x = new Op(Operation.FICOM, O.ST0, O.Mw); break; case 3: x = new Op(Operation.FICOMP, O.ST0, O.Mw); break; case 4: x = new Op(Operation.FISUB, O.ST0, O.Mw); break; case 5: x = new Op(Operation.FISUBR, O.ST0, O.Mw); break; case 6: x = new Op(Operation.FIDIV, O.ST0, O.Mw); break; case 7: x = new Op(Operation.FIDIVR, O.ST0, O.Mw); break; } } break; case 0xDF: if (mod == 3) { switch (reg) { case 0: break; case 1: break; case 2: break; case 3: break; case 4: switch (rm) { case 0: x = new Op(Operation.FSTSW, O.AX); break; } break; case 5: x = new Op(Operation.FUCOMIP, O.ST0, O.T80fp); break; case 6: x = new Op(Operation.FCOMIP, O.ST0, O.T80fp); break; case 7: break; } } else { switch (reg) { case 0: x = new Op(Operation.FILD, O.Mw); break; case 1: x = new Op(Operation.FISTTP, O.Mw); break; case 2: x = new Op(Operation.FIST, O.Mw); break; case 3: x = new Op(Operation.FISTP, O.Mw); break; case 4: x = new Op(Operation.FBLD, O.M80fp); break; case 5: x = new Op(Operation.FILD, O.Mq); break; case 6: x = new Op(Operation.FBSTP, O.M80fp); break; case 7: x = new Op(Operation.FISTP, O.Mq); break; } } break; } if (x.IsEmpty) { throw new InvalidInstructionException(string.Format( "Invalid opcode: {0:X2} {1:X2}.", firstByte, modrm.Value)); } return x; }
private Op DecodeOpcode(InstructionReader reader) { // Process the first byte of the opcode. byte c = reader.PeekByte(); reader.ConsumeOpcodeByte(); // Find an opcode entry for this opcode, and return it if it's // well-defined. Op spec = OneByteOpcodeMap[c]; // Check opcode extensions. if (spec.IsExtension) { int reg = reader.GetModRM().REG; int mod = reader.GetModRM().MOD; int rm = reader.GetModRM().RM; Op x = new Op(); switch (spec.Extension) { case OpcodeExtension.Ext1: switch (reg) { case 0: x = new Op(Operation.ADD); break; case 1: x = new Op(Operation.OR); break; case 2: x = new Op(Operation.ADC); break; case 3: x = new Op(Operation.SBB); break; case 4: x = new Op(Operation.AND); break; case 5: x = new Op(Operation.SUB); break; case 6: x = new Op(Operation.XOR); break; case 7: x = new Op(Operation.CMP); break; } break; case OpcodeExtension.Ext1A: switch (reg) { case 0: x = new Op(Operation.POP, O.Ev); break; } break; case OpcodeExtension.Ext2: switch (reg) { case 0: x = new Op(Operation.ROL); break; case 1: x = new Op(Operation.ROR); break; case 2: x = new Op(Operation.RCL); break; case 3: x = new Op(Operation.RCR); break; case 4: x = new Op(Operation.SHL); break; case 5: x = new Op(Operation.SHR); break; case 6: break; case 7: x = new Op(Operation.SAR); break; } break; case OpcodeExtension.Ext3: if (c == 0xF6) { switch (reg) { case 0: x = new Op(Operation.TEST, O.Eb, O.Ib); break; case 1: break; case 2: x = new Op(Operation.NOT, O.Eb); break; case 3: x = new Op(Operation.NEG, O.Eb); break; case 4: x = new Op(Operation.MULB, O.Eb, O.AL); break; case 5: x = new Op(Operation.IMULB, O.Eb, O.AL); break; case 6: x = new Op(Operation.DIVB, O.Eb); break; case 7: x = new Op(Operation.IDIVB, O.Eb); break; } } else if (c == 0xF7) { switch (reg) { case 0: x = new Op(Operation.TEST, O.Ev, O.Iz); break; case 1: break; case 2: x = new Op(Operation.NOT, O.Ev); break; case 3: x = new Op(Operation.NEG, O.Ev); break; case 4: x = new Op(Operation.MULW, O.Ev, O.rAX); break; case 5: x = new Op(Operation.IMULW, O.Ev, O.rAX); break; case 6: x = new Op(Operation.DIVW, O.Ev); break; case 7: x = new Op(Operation.IDIVW, O.Ev); break; } } break; case OpcodeExtension.Ext4: switch (reg) { case 0: x = new Op(Operation.INC, O.Eb); break; case 1: x = new Op(Operation.DEC, O.Eb); break; } break; case OpcodeExtension.Ext5: switch (reg) { case 0: x = new Op(Operation.INC, O.Ev); break; case 1: x = new Op(Operation.DEC, O.Ev); break; case 2: x = new Op(Operation.CALL, O.Ev); break; case 3: x = new Op(Operation.CALLF, O.Ep); break; case 4: x = new Op(Operation.JMP, O.Ev); break; case 5: x = new Op(Operation.JMPF, O.Mp); break; case 6: x = new Op(Operation.PUSH, O.Ev); break; case 7: break; } break; case OpcodeExtension.Ext6: switch (reg) { case 0: x = new Op(Operation.SLDT, O.Rv, O.Mw); break; case 1: x = new Op(Operation.STR, O.Rv, O.Mw); break; case 2: x = new Op(Operation.LLDT, O.Ew); break; case 3: x = new Op(Operation.LTR, O.Ew); break; case 4: x = new Op(Operation.VERR, O.Ew); break; case 5: x = new Op(Operation.VERW, O.Ew); break; case 6: break; case 7: break; } break; case OpcodeExtension.Ext11: if (c == 0xC6) { switch (reg) { case 0: x = new Op(Operation.MOV, O.Eb, O.Ib); break; case 7: if (mod == 3 && rm == 0) x = new Op(Operation.XABORT, O.Ib); break; } } else if (c == 0xC7) { switch (reg) { case 0: x = new Op(Operation.MOV, O.Ev, O.Iz); break; case 7: if (mod == 3 && rm == 0) x = new Op(Operation.XBEGIN, O.Jz); break; } } break; case OpcodeExtension.Fpu: return DecodeFpuOpcode(reader, c); } spec = x.MergeOperandsFrom(spec); } if (spec.Operation == Operation.None) throw new InvalidInstructionException("Invalid opcode."); return spec; }
/// <summary> /// Decodes legacy prefixes of an instruction. There are four groups /// of legacy prefixes; at most one prefix from each group may be /// present. The encoded prefixes may take zero to four bytes. /// </summary> /// <exception cref="InvalidInstructionException"> /// More than one prefix from the same group is present. /// </exception> /// <returns>The legacy prefixes, which may be None if no prefix is /// present.</returns> private Prefixes DecodeLegacyPrefixes(InstructionReader reader) { Prefixes prefix = Prefixes.None; while (true) { Prefixes pfx = Prefixes.None; Prefixes grp = Prefixes.None; byte c = reader.PeekByte(); switch (c) { case 0xF0: pfx = Prefixes.LOCK; grp = Prefixes.Group1; break; case 0xF2: pfx = Prefixes.REPNZ; grp = Prefixes.Group1; break; case 0xF3: pfx = Prefixes.REP; grp = Prefixes.Group1; break; case 0x2E: pfx = Prefixes.CS; grp = Prefixes.Group2; break; case 0x36: pfx = Prefixes.SS; grp = Prefixes.Group2; break; case 0x3E: pfx = Prefixes.DS; grp = Prefixes.Group2; break; case 0x26: pfx = Prefixes.ES; grp = Prefixes.Group2; break; case 0x64: pfx = Prefixes.FS; grp = Prefixes.Group2; break; case 0x65: pfx = Prefixes.GS; grp = Prefixes.Group2; break; case 0x66: pfx = Prefixes.OperandSizeOverride; grp = Prefixes.Group3; break; case 0x67: pfx = Prefixes.AddressSizeOverride; grp = Prefixes.Group4; break; } if (pfx == Prefixes.None) { break; } if ((prefix & grp) != Prefixes.None) { throw new InvalidInstructionException(string.Format( "The instruction contains multiple prefixes from {0}: {1} and {2}.", grp, prefix & grp, pfx)); } // Consume 1 byte. reader.ConsumePrefixByte(); prefix |= pfx; } return prefix; }