Index: src/x64/disasm-x64.cc |
=================================================================== |
--- src/x64/disasm-x64.cc (revision 2455) |
+++ src/x64/disasm-x64.cc (working copy) |
@@ -34,8 +34,15 @@ |
namespace disasm { |
-enum OperandOrder { |
- UNSET_OP_ORDER = 0, REG_OPER_OP_ORDER, OPER_REG_OP_ORDER |
+enum OperandType { |
+ UNSET_OP_ORDER = 0, |
+ // Operand size decides between 16, 32 and 64 bit operands. |
+ REG_OPER_OP_ORDER = 1, // Register destination, operand source. |
+ OPER_REG_OP_ORDER = 2, // Operand destination, register source. |
+ // Fixed 8-bit operands. |
+ BYTE_SIZE_OPERAND_FLAG = 4, |
+ BYTE_REG_OPER_OP_ORDER = REG_OPER_OP_ORDER | BYTE_SIZE_OPERAND_FLAG, |
+ BYTE_OPER_REG_OP_ORDER = OPER_REG_OP_ORDER | BYTE_SIZE_OPERAND_FLAG |
}; |
//------------------------------------------------------------------ |
@@ -43,28 +50,53 @@ |
//------------------------------------------------------------------ |
struct ByteMnemonic { |
int b; // -1 terminates, otherwise must be in range (0..255) |
- OperandOrder op_order_; |
+ OperandType op_order_; |
const char* mnem; |
}; |
static ByteMnemonic two_operands_instr[] = { |
- { 0x03, REG_OPER_OP_ORDER, "add" }, |
- { 0x21, OPER_REG_OP_ORDER, "and" }, |
- { 0x23, REG_OPER_OP_ORDER, "and" }, |
- { 0x3B, REG_OPER_OP_ORDER, "cmp" }, |
- { 0x8D, REG_OPER_OP_ORDER, "lea" }, |
- { 0x09, OPER_REG_OP_ORDER, "or" }, |
- { 0x0B, REG_OPER_OP_ORDER, "or" }, |
- { 0x1B, REG_OPER_OP_ORDER, "sbb" }, |
- { 0x29, OPER_REG_OP_ORDER, "sub" }, |
- { 0x2B, REG_OPER_OP_ORDER, "sub" }, |
- { 0x85, REG_OPER_OP_ORDER, "test" }, |
- { 0x31, OPER_REG_OP_ORDER, "xor" }, |
- { 0x33, REG_OPER_OP_ORDER, "xor" }, |
- { 0x87, REG_OPER_OP_ORDER, "xchg" }, |
- { 0x8A, REG_OPER_OP_ORDER, "movb" }, |
- { 0x8B, REG_OPER_OP_ORDER, "mov" }, |
+ { 0x00, BYTE_OPER_REG_OP_ORDER, "add" }, |
+ { 0x01, OPER_REG_OP_ORDER, "add" }, |
+ { 0x02, BYTE_REG_OPER_OP_ORDER, "add" }, |
+ { 0x03, REG_OPER_OP_ORDER, "add" }, |
+ { 0x08, BYTE_OPER_REG_OP_ORDER, "or" }, |
+ { 0x09, OPER_REG_OP_ORDER, "or" }, |
+ { 0x0A, BYTE_REG_OPER_OP_ORDER, "or" }, |
+ { 0x0B, REG_OPER_OP_ORDER, "or" }, |
+ { 0x10, BYTE_OPER_REG_OP_ORDER, "adc" }, |
+ { 0x11, OPER_REG_OP_ORDER, "adc" }, |
+ { 0x12, BYTE_REG_OPER_OP_ORDER, "adc" }, |
+ { 0x13, REG_OPER_OP_ORDER, "adc" }, |
+ { 0x18, BYTE_OPER_REG_OP_ORDER, "sbb" }, |
+ { 0x19, OPER_REG_OP_ORDER, "sbb" }, |
+ { 0x1A, BYTE_REG_OPER_OP_ORDER, "sbb" }, |
+ { 0x1B, REG_OPER_OP_ORDER, "sbb" }, |
+ { 0x20, BYTE_OPER_REG_OP_ORDER, "and" }, |
+ { 0x21, OPER_REG_OP_ORDER, "and" }, |
+ { 0x22, BYTE_REG_OPER_OP_ORDER, "and" }, |
+ { 0x23, REG_OPER_OP_ORDER, "and" }, |
+ { 0x28, BYTE_OPER_REG_OP_ORDER, "sub" }, |
+ { 0x29, OPER_REG_OP_ORDER, "sub" }, |
+ { 0x2A, BYTE_REG_OPER_OP_ORDER, "sub" }, |
+ { 0x2B, REG_OPER_OP_ORDER, "sub" }, |
+ { 0x30, BYTE_OPER_REG_OP_ORDER, "xor" }, |
+ { 0x31, OPER_REG_OP_ORDER, "xor" }, |
+ { 0x32, BYTE_REG_OPER_OP_ORDER, "xor" }, |
+ { 0x33, REG_OPER_OP_ORDER, "xor" }, |
+ { 0x38, BYTE_OPER_REG_OP_ORDER, "cmp" }, |
+ { 0x39, OPER_REG_OP_ORDER, "cmp" }, |
+ { 0x3A, BYTE_REG_OPER_OP_ORDER, "cmp" }, |
+ { 0x3B, REG_OPER_OP_ORDER, "cmp" }, |
+ { 0x8D, REG_OPER_OP_ORDER, "lea" }, |
+ { 0x84, BYTE_REG_OPER_OP_ORDER, "test" }, |
+ { 0x85, REG_OPER_OP_ORDER, "test" }, |
+ { 0x86, BYTE_REG_OPER_OP_ORDER, "xchg" }, |
+ { 0x87, REG_OPER_OP_ORDER, "xchg" }, |
+ { 0x88, BYTE_OPER_REG_OP_ORDER, "mov" }, |
+ { 0x89, OPER_REG_OP_ORDER, "mov" }, |
+ { 0x8A, BYTE_REG_OPER_OP_ORDER, "mov" }, |
+ { 0x8B, REG_OPER_OP_ORDER, "mov" }, |
{ -1, UNSET_OP_ORDER, "" } |
}; |
@@ -97,6 +129,7 @@ |
{ 0x05, UNSET_OP_ORDER, "add" }, |
{ 0x0D, UNSET_OP_ORDER, "or" }, |
{ 0x15, UNSET_OP_ORDER, "adc" }, |
+ { 0x1D, UNSET_OP_ORDER, "sbb" }, |
{ 0x25, UNSET_OP_ORDER, "and" }, |
{ 0x2D, UNSET_OP_ORDER, "sub" }, |
{ 0x35, UNSET_OP_ORDER, "xor" }, |
@@ -127,7 +160,8 @@ |
struct InstructionDesc { |
const char* mnem; |
InstructionType type; |
- OperandOrder op_order_; |
+ OperandType op_order_; |
+ bool byte_size_operation; // Fixed 8-bit operation. |
}; |
@@ -143,7 +177,7 @@ |
void Clear(); |
void Init(); |
void CopyTable(ByteMnemonic bm[], InstructionType type); |
- void SetTableRange(InstructionType type, byte start, byte end, |
+ void SetTableRange(InstructionType type, byte start, byte end, bool byte_size, |
const char* mnem); |
void AddJumpConditionalShort(); |
}; |
@@ -157,9 +191,10 @@ |
void InstructionTable::Clear() { |
for (int i = 0; i < 256; i++) { |
- instructions_[i].mnem = ""; |
+ instructions_[i].mnem = "(bad)"; |
instructions_[i].type = NO_INSTR; |
instructions_[i].op_order_ = UNSET_OP_ORDER; |
+ instructions_[i].byte_size_operation = false; |
} |
} |
@@ -170,9 +205,9 @@ |
CopyTable(call_jump_instr, CALL_JUMP_INSTR); |
CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR); |
AddJumpConditionalShort(); |
- SetTableRange(PUSHPOP_INSTR, 0x50, 0x57, "push"); |
- SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, "pop"); |
- SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov"); |
+ SetTableRange(PUSHPOP_INSTR, 0x50, 0x57, false, "push"); |
+ SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, false, "pop"); |
+ SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, false, "mov"); |
} |
@@ -180,20 +215,27 @@ |
for (int i = 0; bm[i].b >= 0; i++) { |
InstructionDesc* id = &instructions_[bm[i].b]; |
id->mnem = bm[i].mnem; |
- id->op_order_ = bm[i].op_order_; |
- assert(id->type == NO_INSTR); // Information already entered |
+ OperandType op_order = bm[i].op_order_; |
+ id->op_order_ = |
+ static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG); |
+ assert(id->type == NO_INSTR); // Information not already entered |
id->type = type; |
+ id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0); |
} |
} |
-void InstructionTable::SetTableRange(InstructionType type, byte start, |
- byte end, const char* mnem) { |
+void InstructionTable::SetTableRange(InstructionType type, |
+ byte start, |
+ byte end, |
+ bool byte_size, |
+ const char* mnem) { |
for (byte b = start; b <= end; b++) { |
InstructionDesc* id = &instructions_[b]; |
assert(id->type == NO_INSTR); // Information already entered |
id->mnem = mnem; |
id->type = type; |
+ id->byte_size_operation = byte_size; |
} |
} |
@@ -228,7 +270,8 @@ |
abort_on_unimplemented_( |
unimplemented_action == ABORT_ON_UNIMPLEMENTED_OPCODE), |
rex_(0), |
- operand_size_(0) { |
+ operand_size_(0), |
+ byte_size_operand_(false) { |
tmp_buffer_[0] = '\0'; |
} |
@@ -240,6 +283,12 @@ |
int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction); |
private: |
+ enum OperandSize { |
+ BYTE_SIZE = 0, |
+ WORD_SIZE = 1, |
+ DOUBLEWORD_SIZE = 2, |
+ QUADWORD_SIZE = 3 |
+ }; |
const NameConverter& converter_; |
v8::internal::EmbeddedVector<char, 128> tmp_buffer_; |
@@ -248,6 +297,8 @@ |
// Prefixes parsed |
byte rex_; |
byte operand_size_; |
+ // Byte size operand override. |
+ bool byte_size_operand_; |
void setOperandSizePrefix(byte prefix) { |
ASSERT_EQ(0x66, prefix); |
@@ -272,12 +323,15 @@ |
bool rex_w() { return (rex_ & 0x08) != 0; } |
- int operand_size() { |
- return rex_w() ? 64 : (operand_size_ != 0) ? 16 : 32; |
+ OperandSize operand_size() { |
+ if (byte_size_operand_) return BYTE_SIZE; |
+ if (rex_w()) return QUADWORD_SIZE; |
+ if (operand_size_ != 0) return WORD_SIZE; |
+ return DOUBLEWORD_SIZE; |
} |
char operand_size_code() { |
- return rex_w() ? 'q' : (operand_size_ != 0) ? 'w' : 'l'; |
+ return "bwlq"[operand_size()]; |
} |
const char* NameOfCPURegister(int reg) const { |
@@ -312,7 +366,7 @@ |
int* base) { |
*scale = (data >> 6) & 3; |
*index = ((data >> 3) & 7) | (rex_x() ? 8 : 0); |
- *base = data & 7 | (rex_b() ? 8 : 0); |
+ *base = (data & 7) | (rex_b() ? 8 : 0); |
} |
typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const; |
@@ -322,11 +376,12 @@ |
int PrintRightOperand(byte* modrmp); |
int PrintRightByteOperand(byte* modrmp); |
int PrintOperands(const char* mnem, |
- OperandOrder op_order, |
+ OperandType op_order, |
byte* data); |
+ int PrintImmediate(byte* data, OperandSize size); |
int PrintImmediateOp(byte* data); |
int F7Instruction(byte* data); |
- int D1D3C1Instruction(byte* data); |
+ int ShiftInstruction(byte* data); |
int JumpShort(byte* data); |
int JumpConditional(byte* data); |
int JumpConditionalShort(byte* data); |
@@ -451,6 +506,36 @@ |
} |
+int DisassemblerX64::PrintImmediate(byte* data, OperandSize size) { |
+ int64_t value; |
+ int count; |
+ switch (size) { |
+ case BYTE_SIZE: |
+ value = *data; |
+ count = 1; |
+ break; |
+ case WORD_SIZE: |
+ value = *reinterpret_cast<int16_t*>(data); |
+ count = 2; |
+ break; |
+ case DOUBLEWORD_SIZE: |
+ value = *reinterpret_cast<uint32_t*>(data); |
+ count = 4; |
+ break; |
+ case QUADWORD_SIZE: |
+ value = *reinterpret_cast<int32_t*>(data); |
+ count = 4; |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ value = 0; // Initialize variables on all paths to satisfy the compiler. |
+ count = 0; |
+ } |
+ AppendToBuffer(V8_PTR_PREFIX"x", value); |
+ return count; |
+} |
+ |
+ |
int DisassemblerX64::PrintRightOperand(byte* modrmp) { |
return PrintRightOperandHelper(modrmp, |
&DisassemblerX64::NameOfCPURegister); |
@@ -466,25 +551,30 @@ |
// Returns number of bytes used including the current *data. |
// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'. |
int DisassemblerX64::PrintOperands(const char* mnem, |
- OperandOrder op_order, |
+ OperandType op_order, |
byte* data) { |
byte modrm = *data; |
int mod, regop, rm; |
get_modrm(modrm, &mod, ®op, &rm); |
int advance = 0; |
+ const char* register_name = |
+ byte_size_operand_ ? NameOfByteCPURegister(regop) |
+ : NameOfCPURegister(regop); |
switch (op_order) { |
case REG_OPER_OP_ORDER: { |
AppendToBuffer("%s%c %s,", |
mnem, |
operand_size_code(), |
- NameOfCPURegister(regop)); |
- advance = PrintRightOperand(data); |
+ register_name); |
+ advance = byte_size_operand_ ? PrintRightByteOperand(data) |
+ : PrintRightOperand(data); |
break; |
} |
case OPER_REG_OP_ORDER: { |
AppendToBuffer("%s%c ", mnem, operand_size_code()); |
- advance = PrintRightOperand(data); |
- AppendToBuffer(",%s", NameOfCPURegister(regop)); |
+ advance = byte_size_operand_ ? PrintRightByteOperand(data) |
+ : PrintRightOperand(data); |
+ AppendToBuffer(",%s", register_name); |
break; |
} |
default: |
@@ -498,7 +588,7 @@ |
// Returns number of bytes used by machine instruction, including *data byte. |
// Writes immediate instructions to 'tmp_buffer_'. |
int DisassemblerX64::PrintImmediateOp(byte* data) { |
- bool sign_extension_bit = (*data & 0x02) != 0; |
+ bool byte_size_immediate = (*data & 0x02) != 0; |
byte modrm = *(data + 1); |
int mod, regop, rm; |
get_modrm(modrm, &mod, ®op, &rm); |
@@ -528,15 +618,12 @@ |
default: |
UnimplementedInstruction(); |
} |
- AppendToBuffer("%s ", mnem); |
+ AppendToBuffer("%s%c ", mnem, operand_size_code()); |
int count = PrintRightOperand(data + 1); |
- if (sign_extension_bit) { |
- AppendToBuffer(",0x%x", *(data + 1 + count)); |
- return 1 + count + 1 /*int8*/; |
- } else { |
- AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count)); |
- return 1 + count + 4 /*int32_t*/; |
- } |
+ AppendToBuffer(",0x"); |
+ OperandSize immediate_size = byte_size_immediate ? BYTE_SIZE : operand_size(); |
+ count += PrintImmediate(data + 1 + count, immediate_size); |
+ return 1 + count; |
} |
@@ -589,79 +676,66 @@ |
} |
-int DisassemblerX64::D1D3C1Instruction(byte* data) { |
- byte op = *data; |
- assert(op == 0xD1 || op == 0xD3 || op == 0xC1); |
+int DisassemblerX64::ShiftInstruction(byte* data) { |
+ byte op = *data & (~1); |
+ if (op != 0xD0 && op != 0xD2 && op != 0xC0) { |
+ UnimplementedInstruction(); |
+ return 1; |
+ } |
byte modrm = *(data + 1); |
int mod, regop, rm; |
get_modrm(modrm, &mod, ®op, &rm); |
ASSERT(regop < 8); |
int imm8 = -1; |
int num_bytes = 2; |
- if (mod == 3) { |
- const char* mnem = NULL; |
- if (op == 0xD1) { |
- imm8 = 1; |
- switch (regop) { |
- case 2: |
- mnem = "rcl"; |
- break; |
- case 7: |
- mnem = "sar"; |
- break; |
- case 4: |
- mnem = "shl"; |
- break; |
- default: |
- UnimplementedInstruction(); |
- } |
- } else if (op == 0xC1) { |
- imm8 = *(data + 2); |
- num_bytes = 3; |
- switch (regop) { |
- case 2: |
- mnem = "rcl"; |
- break; |
- case 4: |
- mnem = "shl"; |
- break; |
- case 5: |
- mnem = "shr"; |
- break; |
- case 7: |
- mnem = "sar"; |
- break; |
- default: |
- UnimplementedInstruction(); |
- } |
- } else if (op == 0xD3) { |
- switch (regop) { |
- case 4: |
- mnem = "shl"; |
- break; |
- case 5: |
- mnem = "shr"; |
- break; |
- case 7: |
- mnem = "sar"; |
- break; |
- default: |
- UnimplementedInstruction(); |
- } |
- } |
- assert(mnem != NULL); |
- AppendToBuffer("%s%c %s,", |
- mnem, |
- operand_size_code(), |
- NameOfCPURegister(rm)); |
- if (imm8 > 0) { |
- AppendToBuffer("%d", imm8); |
- } else { |
- AppendToBuffer("cl"); |
- } |
- } else { |
+ if (mod != 3) { |
UnimplementedInstruction(); |
+ return num_bytes; |
} |
+ const char* mnem = NULL; |
+ switch (regop) { |
+ case 0: |
+ mnem = "rol"; |
+ break; |
+ case 1: |
+ mnem = "ror"; |
+ break; |
+ case 2: |
+ mnem = "rcl"; |
+ break; |
+ case 3: |
+ mnem = "rcr"; |
+ break; |
+ case 4: |
+ mnem = "shl"; |
+ break; |
+ case 5: |
+ mnem = "shr"; |
+ break; |
+ case 7: |
+ mnem = "sar"; |
+ break; |
+ default: |
+ UnimplementedInstruction(); |
+ return num_bytes; |
+ } |
+ assert(mnem != NULL); |
+ if (op == 0xD0) { |
+ imm8 = 1; |
+ } else if (op == 0xC0) { |
+ imm8 = *(data + 2); |
+ num_bytes = 3; |
+ } |
+ AppendToBuffer("%s%c %s,", |
+ mnem, |
+ operand_size_code(), |
+ byte_size_operand_ ? NameOfByteCPURegister(rm) |
+ : NameOfCPURegister(rm)); |
+ if (op == 0xD2) { |
+ AppendToBuffer("cl"); |
+ } else { |
+ AppendToBuffer("%d", imm8); |
+ } |
return num_bytes; |
} |
@@ -716,30 +790,30 @@ |
if (b1 == 0xD9) { |
const char* mnem = NULL; |
switch (b2) { |
- case 0xE8: |
- mnem = "fld1"; |
+ case 0xE0: |
+ mnem = "fchs"; |
break; |
- case 0xEE: |
- mnem = "fldz"; |
- break; |
case 0xE1: |
mnem = "fabs"; |
break; |
- case 0xE0: |
- mnem = "fchs"; |
+ case 0xE4: |
+ mnem = "ftst"; |
break; |
- case 0xF8: |
- mnem = "fprem"; |
- break; |
case 0xF5: |
mnem = "fprem1"; |
break; |
case 0xF7: |
mnem = "fincstp"; |
break; |
- case 0xE4: |
- mnem = "ftst"; |
+ case 0xE8: |
+ mnem = "fld1"; |
break; |
+ case 0xEE: |
+ mnem = "fldz"; |
+ break; |
+ case 0xF8: |
+ mnem = "fprem"; |
+ break; |
} |
if (mnem != NULL) { |
AppendToBuffer("%s", mnem); |
@@ -906,7 +980,8 @@ |
while (true) { |
current = *data; |
if (current == 0x66) { |
- setOperandSizePrefix(current); |
+ // If the sequence is 66 0f, it's not a prefix, but a SSE escape. |
+ if (*(data + 1) == 0x0F) break; |
data++; |
} else if ((current & 0xF0) == 0x40) { |
setRex(current); |
@@ -918,6 +993,7 @@ |
} |
const InstructionDesc& idesc = instruction_table.Get(current); |
+ byte_size_operand_ = idesc.byte_size_operation; |
switch (idesc.type) { |
case ZERO_OPERANDS_INSTR: |
AppendToBuffer(idesc.mnem); |
@@ -949,15 +1025,15 @@ |
case MOVE_REG_INSTR: { |
byte* addr = NULL; |
switch (operand_size()) { |
- case 16: |
+ case WORD_SIZE: |
addr = reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1)); |
data += 3; |
break; |
- case 32: |
+ case DOUBLEWORD_SIZE: |
addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1)); |
data += 5; |
break; |
- case 64: |
+ case QUADWORD_SIZE: |
addr = reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1)); |
data += 9; |
break; |
@@ -1012,8 +1088,8 @@ |
AppendToBuffer("imul %s,%s,0x%x", NameOfCPURegister(regop), |
NameOfCPURegister(rm), imm); |
data += 2 + (*data == 0x6B ? 1 : 4); |
+ break; |
} |
- break; |
case 0xF6: { |
int mod, regop, rm; |
@@ -1024,8 +1100,8 @@ |
UnimplementedInstruction(); |
} |
data += 3; |
+ break; |
} |
- break; |
case 0x81: // fall through |
case 0x83: // 0x81 with sign extension bit set |
@@ -1170,13 +1246,13 @@ |
case 0x95: |
case 0x96: |
case 0x97: { |
- int reg = current & 0x7 | (rex_b() ? 8 : 0); |
+ int reg = (current & 0x7) | (rex_b() ? 8 : 0); |
if (reg == 0) { |
AppendToBuffer("nop"); // Common name for xchg rax,rax. |
} else { |
AppendToBuffer("xchg%c rax, %s", |
operand_size_code(), |
- NameOfByteCPURegister(reg)); |
+ NameOfCPURegister(reg)); |
} |
} |
@@ -1209,18 +1285,40 @@ |
data += 2; |
break; |
- case 0xA9: |
- AppendToBuffer("test%c rax,0x%x", // CHECKME! |
+ case 0xA9: { |
+ int64_t value; |
+ switch (operand_size()) { |
+ case WORD_SIZE: |
+ value = *reinterpret_cast<uint16_t*>(data + 1); |
+ data += 3; |
+ break; |
+ case DOUBLEWORD_SIZE: |
+ value = *reinterpret_cast<uint32_t*>(data + 1); |
+ data += 5; |
+ break; |
+ case QUADWORD_SIZE: |
+ value = *reinterpret_cast<int32_t*>(data + 1); |
+ data += 5; |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ break; |
+ AppendToBuffer("test%c rax,0x%"V8_PTR_PREFIX"ux", |
operand_size_code(), |
- *reinterpret_cast<int32_t*>(data + 1)); |
- data += 5; |
- break; |
- |
+ value); |
+ } |
case 0xD1: // fall through |
case 0xD3: // fall through |
case 0xC1: |
- data += D1D3C1Instruction(data); |
+ data += ShiftInstruction(data); |
break; |
+ case 0xD0: // fall through |
+ case 0xD2: // fall through |
+ case 0xC0: |
+ byte_size_operand_ = true; |
+ data += ShiftInstruction(data); |
+ break; |
case 0xD9: // fall through |
case 0xDA: // fall through |
@@ -1289,11 +1387,17 @@ |
break; |
case 0xF3: |
- if (*(data + 1) == 0x0F && *(data + 2) == 0x2C) { |
- data += 3; |
- data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data); |
+ if (*(data + 1) == 0x0F) { |
+ if (*(data + 2) == 0x2C) { |
+ data += 3; |
+ data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data); |
+ } else { |
+ UnimplementedInstruction(); |
+ data += 1; |
+ } |
} else { |
UnimplementedInstruction(); |
+ data += 1; |
} |
break; |
@@ -1303,6 +1407,7 @@ |
default: |
UnimplementedInstruction(); |
+ data += 1; |
} |
} // !processed |