blob: dc4c27e88a1eb95c21d6256bc193a426f1faa0cb [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2007-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <assert.h>
29#include <stdio.h>
30#include <stdarg.h>
31
32#include "v8.h"
Leon Clarkef7060e22010-06-03 12:02:55 +010033
34#if defined(V8_TARGET_ARCH_IA32)
35
Steve Blocka7e24c12009-10-30 11:49:00 +000036#include "disasm.h"
37
38namespace disasm {
39
40enum OperandOrder {
41 UNSET_OP_ORDER = 0,
42 REG_OPER_OP_ORDER,
43 OPER_REG_OP_ORDER
44};
45
46
47//------------------------------------------------------------------
48// Tables
49//------------------------------------------------------------------
50struct ByteMnemonic {
51 int b; // -1 terminates, otherwise must be in range (0..255)
52 const char* mnem;
53 OperandOrder op_order_;
54};
55
56
57static ByteMnemonic two_operands_instr[] = {
58 {0x03, "add", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000059 {0x09, "or", OPER_REG_OP_ORDER},
60 {0x0B, "or", REG_OPER_OP_ORDER},
61 {0x1B, "sbb", REG_OPER_OP_ORDER},
Leon Clarked91b9f72010-01-27 17:25:45 +000062 {0x21, "and", OPER_REG_OP_ORDER},
63 {0x23, "and", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000064 {0x29, "sub", OPER_REG_OP_ORDER},
Leon Clarkee46be812010-01-19 14:06:41 +000065 {0x2A, "subb", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000066 {0x2B, "sub", REG_OPER_OP_ORDER},
Leon Clarkeeab96aa2010-01-27 16:31:12 +000067 {0x31, "xor", OPER_REG_OP_ORDER},
68 {0x33, "xor", REG_OPER_OP_ORDER},
Leon Clarked91b9f72010-01-27 17:25:45 +000069 {0x38, "cmpb", OPER_REG_OP_ORDER},
70 {0x3A, "cmpb", REG_OPER_OP_ORDER},
71 {0x3B, "cmp", REG_OPER_OP_ORDER},
72 {0x84, "test_b", REG_OPER_OP_ORDER},
73 {0x85, "test", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000074 {0x87, "xchg", REG_OPER_OP_ORDER},
75 {0x8A, "mov_b", REG_OPER_OP_ORDER},
76 {0x8B, "mov", REG_OPER_OP_ORDER},
Leon Clarked91b9f72010-01-27 17:25:45 +000077 {0x8D, "lea", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000078 {-1, "", UNSET_OP_ORDER}
79};
80
81
82static ByteMnemonic zero_operands_instr[] = {
83 {0xC3, "ret", UNSET_OP_ORDER},
84 {0xC9, "leave", UNSET_OP_ORDER},
85 {0x90, "nop", UNSET_OP_ORDER},
86 {0xF4, "hlt", UNSET_OP_ORDER},
87 {0xCC, "int3", UNSET_OP_ORDER},
88 {0x60, "pushad", UNSET_OP_ORDER},
89 {0x61, "popad", UNSET_OP_ORDER},
90 {0x9C, "pushfd", UNSET_OP_ORDER},
91 {0x9D, "popfd", UNSET_OP_ORDER},
92 {0x9E, "sahf", UNSET_OP_ORDER},
93 {0x99, "cdq", UNSET_OP_ORDER},
94 {0x9B, "fwait", UNSET_OP_ORDER},
Steve Block6ded16b2010-05-10 14:33:55 +010095 {0xFC, "cld", UNSET_OP_ORDER},
Leon Clarkef7060e22010-06-03 12:02:55 +010096 {0xAB, "stos", UNSET_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000097 {-1, "", UNSET_OP_ORDER}
98};
99
100
101static ByteMnemonic call_jump_instr[] = {
102 {0xE8, "call", UNSET_OP_ORDER},
103 {0xE9, "jmp", UNSET_OP_ORDER},
104 {-1, "", UNSET_OP_ORDER}
105};
106
107
108static ByteMnemonic short_immediate_instr[] = {
109 {0x05, "add", UNSET_OP_ORDER},
110 {0x0D, "or", UNSET_OP_ORDER},
111 {0x15, "adc", UNSET_OP_ORDER},
112 {0x25, "and", UNSET_OP_ORDER},
113 {0x2D, "sub", UNSET_OP_ORDER},
114 {0x35, "xor", UNSET_OP_ORDER},
115 {0x3D, "cmp", UNSET_OP_ORDER},
116 {-1, "", UNSET_OP_ORDER}
117};
118
119
120static const char* jump_conditional_mnem[] = {
121 /*0*/ "jo", "jno", "jc", "jnc",
122 /*4*/ "jz", "jnz", "jna", "ja",
123 /*8*/ "js", "jns", "jpe", "jpo",
124 /*12*/ "jl", "jnl", "jng", "jg"
125};
126
127
128static const char* set_conditional_mnem[] = {
129 /*0*/ "seto", "setno", "setc", "setnc",
130 /*4*/ "setz", "setnz", "setna", "seta",
131 /*8*/ "sets", "setns", "setpe", "setpo",
132 /*12*/ "setl", "setnl", "setng", "setg"
133};
134
135
Steve Block3ce2e202009-11-05 08:53:23 +0000136static const char* conditional_move_mnem[] = {
137 /*0*/ "cmovo", "cmovno", "cmovc", "cmovnc",
138 /*4*/ "cmovz", "cmovnz", "cmovna", "cmova",
139 /*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo",
140 /*12*/ "cmovl", "cmovnl", "cmovng", "cmovg"
141};
142
143
Steve Blocka7e24c12009-10-30 11:49:00 +0000144enum InstructionType {
145 NO_INSTR,
146 ZERO_OPERANDS_INSTR,
147 TWO_OPERANDS_INSTR,
148 JUMP_CONDITIONAL_SHORT_INSTR,
149 REGISTER_INSTR,
150 MOVE_REG_INSTR,
151 CALL_JUMP_INSTR,
152 SHORT_IMMEDIATE_INSTR
153};
154
155
156struct InstructionDesc {
157 const char* mnem;
158 InstructionType type;
159 OperandOrder op_order_;
160};
161
162
163class InstructionTable {
164 public:
165 InstructionTable();
166 const InstructionDesc& Get(byte x) const { return instructions_[x]; }
167
168 private:
169 InstructionDesc instructions_[256];
170 void Clear();
171 void Init();
172 void CopyTable(ByteMnemonic bm[], InstructionType type);
173 void SetTableRange(InstructionType type,
174 byte start,
175 byte end,
176 const char* mnem);
177 void AddJumpConditionalShort();
178};
179
180
181InstructionTable::InstructionTable() {
182 Clear();
183 Init();
184}
185
186
187void InstructionTable::Clear() {
188 for (int i = 0; i < 256; i++) {
189 instructions_[i].mnem = "";
190 instructions_[i].type = NO_INSTR;
191 instructions_[i].op_order_ = UNSET_OP_ORDER;
192 }
193}
194
195
196void InstructionTable::Init() {
197 CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
198 CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
199 CopyTable(call_jump_instr, CALL_JUMP_INSTR);
200 CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
201 AddJumpConditionalShort();
202 SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc");
203 SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec");
204 SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push");
205 SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop");
206 SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop.
207 SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov");
208}
209
210
211void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type) {
212 for (int i = 0; bm[i].b >= 0; i++) {
213 InstructionDesc* id = &instructions_[bm[i].b];
214 id->mnem = bm[i].mnem;
215 id->op_order_ = bm[i].op_order_;
Steve Blockd0582a62009-12-15 09:54:21 +0000216 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000217 id->type = type;
218 }
219}
220
221
222void InstructionTable::SetTableRange(InstructionType type,
223 byte start,
224 byte end,
225 const char* mnem) {
226 for (byte b = start; b <= end; b++) {
227 InstructionDesc* id = &instructions_[b];
Steve Blockd0582a62009-12-15 09:54:21 +0000228 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000229 id->mnem = mnem;
230 id->type = type;
231 }
232}
233
234
235void InstructionTable::AddJumpConditionalShort() {
236 for (byte b = 0x70; b <= 0x7F; b++) {
237 InstructionDesc* id = &instructions_[b];
Steve Blockd0582a62009-12-15 09:54:21 +0000238 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000239 id->mnem = jump_conditional_mnem[b & 0x0F];
240 id->type = JUMP_CONDITIONAL_SHORT_INSTR;
241 }
242}
243
244
245static InstructionTable instruction_table;
246
247
248// The IA32 disassembler implementation.
249class DisassemblerIA32 {
250 public:
251 DisassemblerIA32(const NameConverter& converter,
252 bool abort_on_unimplemented = true)
253 : converter_(converter),
254 tmp_buffer_pos_(0),
255 abort_on_unimplemented_(abort_on_unimplemented) {
256 tmp_buffer_[0] = '\0';
257 }
258
259 virtual ~DisassemblerIA32() {}
260
261 // Writes one disassembled instruction into 'buffer' (0-terminated).
262 // Returns the length of the disassembled machine instruction in bytes.
263 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
264
265 private:
266 const NameConverter& converter_;
267 v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
268 unsigned int tmp_buffer_pos_;
269 bool abort_on_unimplemented_;
270
271
272 enum {
273 eax = 0,
274 ecx = 1,
275 edx = 2,
276 ebx = 3,
277 esp = 4,
278 ebp = 5,
279 esi = 6,
280 edi = 7
281 };
282
283
Steve Blockd0582a62009-12-15 09:54:21 +0000284 enum ShiftOpcodeExtension {
285 kROL = 0,
286 kROR = 1,
287 kRCL = 2,
288 kRCR = 3,
289 kSHL = 4,
290 KSHR = 5,
291 kSAR = 7
292 };
293
294
Steve Blocka7e24c12009-10-30 11:49:00 +0000295 const char* NameOfCPURegister(int reg) const {
296 return converter_.NameOfCPURegister(reg);
297 }
298
299
300 const char* NameOfByteCPURegister(int reg) const {
301 return converter_.NameOfByteCPURegister(reg);
302 }
303
304
305 const char* NameOfXMMRegister(int reg) const {
306 return converter_.NameOfXMMRegister(reg);
307 }
308
309
310 const char* NameOfAddress(byte* addr) const {
311 return converter_.NameOfAddress(addr);
312 }
313
314
315 // Disassembler helper functions.
316 static void get_modrm(byte data, int* mod, int* regop, int* rm) {
317 *mod = (data >> 6) & 3;
318 *regop = (data & 0x38) >> 3;
319 *rm = data & 7;
320 }
321
322
323 static void get_sib(byte data, int* scale, int* index, int* base) {
324 *scale = (data >> 6) & 3;
325 *index = (data >> 3) & 7;
326 *base = data & 7;
327 }
328
329 typedef const char* (DisassemblerIA32::*RegisterNameMapping)(int reg) const;
330
331 int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name);
332 int PrintRightOperand(byte* modrmp);
333 int PrintRightByteOperand(byte* modrmp);
334 int PrintOperands(const char* mnem, OperandOrder op_order, byte* data);
335 int PrintImmediateOp(byte* data);
336 int F7Instruction(byte* data);
337 int D1D3C1Instruction(byte* data);
338 int JumpShort(byte* data);
339 int JumpConditional(byte* data, const char* comment);
340 int JumpConditionalShort(byte* data, const char* comment);
341 int SetCC(byte* data);
Steve Block3ce2e202009-11-05 08:53:23 +0000342 int CMov(byte* data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000343 int FPUInstruction(byte* data);
Steve Blockd0582a62009-12-15 09:54:21 +0000344 int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
345 int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
Steve Blocka7e24c12009-10-30 11:49:00 +0000346 void AppendToBuffer(const char* format, ...);
347
348
349 void UnimplementedInstruction() {
350 if (abort_on_unimplemented_) {
351 UNIMPLEMENTED();
352 } else {
353 AppendToBuffer("'Unimplemented Instruction'");
354 }
355 }
356};
357
358
359void DisassemblerIA32::AppendToBuffer(const char* format, ...) {
360 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
361 va_list args;
362 va_start(args, format);
363 int result = v8::internal::OS::VSNPrintF(buf, format, args);
364 va_end(args);
365 tmp_buffer_pos_ += result;
366}
367
368int DisassemblerIA32::PrintRightOperandHelper(
369 byte* modrmp,
370 RegisterNameMapping register_name) {
371 int mod, regop, rm;
372 get_modrm(*modrmp, &mod, &regop, &rm);
373 switch (mod) {
374 case 0:
375 if (rm == ebp) {
376 int32_t disp = *reinterpret_cast<int32_t*>(modrmp+1);
377 AppendToBuffer("[0x%x]", disp);
378 return 5;
379 } else if (rm == esp) {
380 byte sib = *(modrmp + 1);
381 int scale, index, base;
382 get_sib(sib, &scale, &index, &base);
383 if (index == esp && base == esp && scale == 0 /*times_1*/) {
384 AppendToBuffer("[%s]", (this->*register_name)(rm));
385 return 2;
386 } else if (base == ebp) {
387 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
388 AppendToBuffer("[%s*%d+0x%x]",
389 (this->*register_name)(index),
390 1 << scale,
391 disp);
392 return 6;
393 } else if (index != esp && base != ebp) {
394 // [base+index*scale]
395 AppendToBuffer("[%s+%s*%d]",
396 (this->*register_name)(base),
397 (this->*register_name)(index),
398 1 << scale);
399 return 2;
400 } else {
401 UnimplementedInstruction();
402 return 1;
403 }
404 } else {
405 AppendToBuffer("[%s]", (this->*register_name)(rm));
406 return 1;
407 }
408 break;
409 case 1: // fall through
410 case 2:
411 if (rm == esp) {
412 byte sib = *(modrmp + 1);
413 int scale, index, base;
414 get_sib(sib, &scale, &index, &base);
415 int disp =
416 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 2) : *(modrmp + 2);
417 if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
418 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
419 } else {
420 AppendToBuffer("[%s+%s*%d+0x%x]",
421 (this->*register_name)(base),
422 (this->*register_name)(index),
423 1 << scale,
424 disp);
425 }
426 return mod == 2 ? 6 : 3;
427 } else {
428 // No sib.
429 int disp =
430 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 1) : *(modrmp + 1);
431 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
432 return mod == 2 ? 5 : 2;
433 }
434 break;
435 case 3:
436 AppendToBuffer("%s", (this->*register_name)(rm));
437 return 1;
438 default:
439 UnimplementedInstruction();
440 return 1;
441 }
442 UNREACHABLE();
443}
444
445
446int DisassemblerIA32::PrintRightOperand(byte* modrmp) {
447 return PrintRightOperandHelper(modrmp, &DisassemblerIA32::NameOfCPURegister);
448}
449
450
451int DisassemblerIA32::PrintRightByteOperand(byte* modrmp) {
452 return PrintRightOperandHelper(modrmp,
453 &DisassemblerIA32::NameOfByteCPURegister);
454}
455
456
457// Returns number of bytes used including the current *data.
458// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
459int DisassemblerIA32::PrintOperands(const char* mnem,
460 OperandOrder op_order,
461 byte* data) {
462 byte modrm = *data;
463 int mod, regop, rm;
464 get_modrm(modrm, &mod, &regop, &rm);
465 int advance = 0;
466 switch (op_order) {
467 case REG_OPER_OP_ORDER: {
468 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
469 advance = PrintRightOperand(data);
470 break;
471 }
472 case OPER_REG_OP_ORDER: {
473 AppendToBuffer("%s ", mnem);
474 advance = PrintRightOperand(data);
475 AppendToBuffer(",%s", NameOfCPURegister(regop));
476 break;
477 }
478 default:
479 UNREACHABLE();
480 break;
481 }
482 return advance;
483}
484
485
486// Returns number of bytes used by machine instruction, including *data byte.
487// Writes immediate instructions to 'tmp_buffer_'.
488int DisassemblerIA32::PrintImmediateOp(byte* data) {
489 bool sign_extension_bit = (*data & 0x02) != 0;
490 byte modrm = *(data+1);
491 int mod, regop, rm;
492 get_modrm(modrm, &mod, &regop, &rm);
493 const char* mnem = "Imm???";
494 switch (regop) {
495 case 0: mnem = "add"; break;
496 case 1: mnem = "or"; break;
497 case 2: mnem = "adc"; break;
498 case 4: mnem = "and"; break;
499 case 5: mnem = "sub"; break;
500 case 6: mnem = "xor"; break;
501 case 7: mnem = "cmp"; break;
502 default: UnimplementedInstruction();
503 }
504 AppendToBuffer("%s ", mnem);
505 int count = PrintRightOperand(data+1);
506 if (sign_extension_bit) {
507 AppendToBuffer(",0x%x", *(data + 1 + count));
508 return 1 + count + 1 /*int8*/;
509 } else {
510 AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count));
511 return 1 + count + 4 /*int32_t*/;
512 }
513}
514
515
516// Returns number of bytes used, including *data.
517int DisassemblerIA32::F7Instruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000518 ASSERT_EQ(0xF7, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000519 byte modrm = *(data+1);
520 int mod, regop, rm;
521 get_modrm(modrm, &mod, &regop, &rm);
522 if (mod == 3 && regop != 0) {
523 const char* mnem = NULL;
524 switch (regop) {
525 case 2: mnem = "not"; break;
526 case 3: mnem = "neg"; break;
527 case 4: mnem = "mul"; break;
528 case 7: mnem = "idiv"; break;
529 default: UnimplementedInstruction();
530 }
531 AppendToBuffer("%s %s", mnem, NameOfCPURegister(rm));
532 return 2;
533 } else if (mod == 3 && regop == eax) {
534 int32_t imm = *reinterpret_cast<int32_t*>(data+2);
535 AppendToBuffer("test %s,0x%x", NameOfCPURegister(rm), imm);
536 return 6;
537 } else if (regop == eax) {
538 AppendToBuffer("test ");
539 int count = PrintRightOperand(data+1);
540 int32_t imm = *reinterpret_cast<int32_t*>(data+1+count);
541 AppendToBuffer(",0x%x", imm);
542 return 1+count+4 /*int32_t*/;
543 } else {
544 UnimplementedInstruction();
545 return 2;
546 }
547}
548
549int DisassemblerIA32::D1D3C1Instruction(byte* data) {
550 byte op = *data;
Steve Blockd0582a62009-12-15 09:54:21 +0000551 ASSERT(op == 0xD1 || op == 0xD3 || op == 0xC1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000552 byte modrm = *(data+1);
553 int mod, regop, rm;
554 get_modrm(modrm, &mod, &regop, &rm);
555 int imm8 = -1;
556 int num_bytes = 2;
557 if (mod == 3) {
558 const char* mnem = NULL;
Steve Blockd0582a62009-12-15 09:54:21 +0000559 switch (regop) {
560 case kROL: mnem = "rol"; break;
561 case kROR: mnem = "ror"; break;
562 case kRCL: mnem = "rcl"; break;
563 case kSHL: mnem = "shl"; break;
564 case KSHR: mnem = "shr"; break;
565 case kSAR: mnem = "sar"; break;
566 default: UnimplementedInstruction();
567 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000568 if (op == 0xD1) {
569 imm8 = 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000570 } else if (op == 0xC1) {
571 imm8 = *(data+2);
572 num_bytes = 3;
Steve Blocka7e24c12009-10-30 11:49:00 +0000573 } else if (op == 0xD3) {
Steve Blockd0582a62009-12-15 09:54:21 +0000574 // Shift/rotate by cl.
Steve Blocka7e24c12009-10-30 11:49:00 +0000575 }
Steve Blockd0582a62009-12-15 09:54:21 +0000576 ASSERT_NE(NULL, mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +0000577 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(rm));
578 if (imm8 > 0) {
579 AppendToBuffer("%d", imm8);
580 } else {
581 AppendToBuffer("cl");
582 }
583 } else {
584 UnimplementedInstruction();
585 }
586 return num_bytes;
587}
588
589
590// Returns number of bytes used, including *data.
591int DisassemblerIA32::JumpShort(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000592 ASSERT_EQ(0xEB, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000593 byte b = *(data+1);
594 byte* dest = data + static_cast<int8_t>(b) + 2;
595 AppendToBuffer("jmp %s", NameOfAddress(dest));
596 return 2;
597}
598
599
600// Returns number of bytes used, including *data.
601int DisassemblerIA32::JumpConditional(byte* data, const char* comment) {
Steve Blockd0582a62009-12-15 09:54:21 +0000602 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000603 byte cond = *(data+1) & 0x0F;
604 byte* dest = data + *reinterpret_cast<int32_t*>(data+2) + 6;
605 const char* mnem = jump_conditional_mnem[cond];
606 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
607 if (comment != NULL) {
608 AppendToBuffer(", %s", comment);
609 }
610 return 6; // includes 0x0F
611}
612
613
614// Returns number of bytes used, including *data.
615int DisassemblerIA32::JumpConditionalShort(byte* data, const char* comment) {
616 byte cond = *data & 0x0F;
617 byte b = *(data+1);
618 byte* dest = data + static_cast<int8_t>(b) + 2;
619 const char* mnem = jump_conditional_mnem[cond];
620 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
621 if (comment != NULL) {
622 AppendToBuffer(", %s", comment);
623 }
624 return 2;
625}
626
627
628// Returns number of bytes used, including *data.
629int DisassemblerIA32::SetCC(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000630 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000631 byte cond = *(data+1) & 0x0F;
632 const char* mnem = set_conditional_mnem[cond];
633 AppendToBuffer("%s ", mnem);
634 PrintRightByteOperand(data+2);
Steve Blockd0582a62009-12-15 09:54:21 +0000635 return 3; // Includes 0x0F.
Steve Blocka7e24c12009-10-30 11:49:00 +0000636}
637
638
639// Returns number of bytes used, including *data.
Steve Block3ce2e202009-11-05 08:53:23 +0000640int DisassemblerIA32::CMov(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000641 ASSERT_EQ(0x0F, *data);
Steve Block3ce2e202009-11-05 08:53:23 +0000642 byte cond = *(data + 1) & 0x0F;
643 const char* mnem = conditional_move_mnem[cond];
644 int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2);
645 return 2 + op_size; // includes 0x0F
646}
647
648
649// Returns number of bytes used, including *data.
Steve Blocka7e24c12009-10-30 11:49:00 +0000650int DisassemblerIA32::FPUInstruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000651 byte escape_opcode = *data;
652 ASSERT_EQ(0xD8, escape_opcode & 0xF8);
653 byte modrm_byte = *(data+1);
654
655 if (modrm_byte >= 0xC0) {
656 return RegisterFPUInstruction(escape_opcode, modrm_byte);
657 } else {
658 return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000659 }
Steve Blockd0582a62009-12-15 09:54:21 +0000660}
661
662int DisassemblerIA32::MemoryFPUInstruction(int escape_opcode,
663 int modrm_byte,
664 byte* modrm_start) {
665 const char* mnem = "?";
666 int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
667 switch (escape_opcode) {
668 case 0xD9: switch (regop) {
669 case 0: mnem = "fld_s"; break;
670 case 3: mnem = "fstp_s"; break;
671 case 7: mnem = "fstcw"; break;
672 default: UnimplementedInstruction();
673 }
674 break;
675
676 case 0xDB: switch (regop) {
677 case 0: mnem = "fild_s"; break;
678 case 1: mnem = "fisttp_s"; break;
679 case 2: mnem = "fist_s"; break;
680 case 3: mnem = "fistp_s"; break;
681 default: UnimplementedInstruction();
682 }
683 break;
684
685 case 0xDD: switch (regop) {
686 case 0: mnem = "fld_d"; break;
Andrei Popescu402d9372010-02-26 13:31:12 +0000687 case 2: mnem = "fstp"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000688 case 3: mnem = "fstp_d"; break;
689 default: UnimplementedInstruction();
690 }
691 break;
692
693 case 0xDF: switch (regop) {
694 case 5: mnem = "fild_d"; break;
695 case 7: mnem = "fistp_d"; break;
696 default: UnimplementedInstruction();
697 }
698 break;
699
700 default: UnimplementedInstruction();
701 }
702 AppendToBuffer("%s ", mnem);
703 int count = PrintRightOperand(modrm_start);
704 return count + 1;
705}
706
707int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode,
708 byte modrm_byte) {
709 bool has_register = false; // Is the FPU register encoded in modrm_byte?
710 const char* mnem = "?";
711
712 switch (escape_opcode) {
713 case 0xD8:
714 UnimplementedInstruction();
715 break;
716
717 case 0xD9:
718 switch (modrm_byte & 0xF8) {
719 case 0xC8:
720 mnem = "fxch";
721 has_register = true;
722 break;
723 default:
724 switch (modrm_byte) {
725 case 0xE0: mnem = "fchs"; break;
726 case 0xE1: mnem = "fabs"; break;
727 case 0xE4: mnem = "ftst"; break;
728 case 0xE8: mnem = "fld1"; break;
Andrei Popescu402d9372010-02-26 13:31:12 +0000729 case 0xEB: mnem = "fldpi"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000730 case 0xEE: mnem = "fldz"; break;
731 case 0xF5: mnem = "fprem1"; break;
732 case 0xF7: mnem = "fincstp"; break;
733 case 0xF8: mnem = "fprem"; break;
734 case 0xFE: mnem = "fsin"; break;
735 case 0xFF: mnem = "fcos"; break;
736 default: UnimplementedInstruction();
737 }
738 }
739 break;
740
741 case 0xDA:
742 if (modrm_byte == 0xE9) {
743 mnem = "fucompp";
744 } else {
745 UnimplementedInstruction();
746 }
747 break;
748
749 case 0xDB:
750 if ((modrm_byte & 0xF8) == 0xE8) {
751 mnem = "fucomi";
752 has_register = true;
753 } else if (modrm_byte == 0xE2) {
754 mnem = "fclex";
755 } else {
756 UnimplementedInstruction();
757 }
758 break;
759
760 case 0xDC:
761 has_register = true;
762 switch (modrm_byte & 0xF8) {
763 case 0xC0: mnem = "fadd"; break;
764 case 0xE8: mnem = "fsub"; break;
765 case 0xC8: mnem = "fmul"; break;
766 case 0xF8: mnem = "fdiv"; break;
767 default: UnimplementedInstruction();
768 }
769 break;
770
771 case 0xDD:
772 has_register = true;
773 switch (modrm_byte & 0xF8) {
774 case 0xC0: mnem = "ffree"; break;
775 case 0xD8: mnem = "fstp"; break;
776 default: UnimplementedInstruction();
777 }
778 break;
779
780 case 0xDE:
781 if (modrm_byte == 0xD9) {
782 mnem = "fcompp";
783 } else {
784 has_register = true;
785 switch (modrm_byte & 0xF8) {
786 case 0xC0: mnem = "faddp"; break;
787 case 0xE8: mnem = "fsubp"; break;
788 case 0xC8: mnem = "fmulp"; break;
789 case 0xF8: mnem = "fdivp"; break;
790 default: UnimplementedInstruction();
791 }
792 }
793 break;
794
795 case 0xDF:
796 if (modrm_byte == 0xE0) {
797 mnem = "fnstsw_ax";
798 } else if ((modrm_byte & 0xF8) == 0xE8) {
799 mnem = "fucomip";
800 has_register = true;
801 }
802 break;
803
804 default: UnimplementedInstruction();
805 }
806
807 if (has_register) {
808 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
809 } else {
810 AppendToBuffer("%s", mnem);
811 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000812 return 2;
813}
814
815
816// Mnemonics for instructions 0xF0 byte.
817// Returns NULL if the instruction is not handled here.
818static const char* F0Mnem(byte f0byte) {
819 switch (f0byte) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100820 case 0x18: return "prefetch";
Steve Blocka7e24c12009-10-30 11:49:00 +0000821 case 0xA2: return "cpuid";
822 case 0x31: return "rdtsc";
823 case 0xBE: return "movsx_b";
824 case 0xBF: return "movsx_w";
825 case 0xB6: return "movzx_b";
826 case 0xB7: return "movzx_w";
827 case 0xAF: return "imul";
828 case 0xA5: return "shld";
829 case 0xAD: return "shrd";
830 case 0xAB: return "bts";
831 default: return NULL;
832 }
833}
834
835
836// Disassembled instruction '*instr' and writes it into 'out_buffer'.
837int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
838 byte* instr) {
839 tmp_buffer_pos_ = 0; // starting to write as position 0
840 byte* data = instr;
841 // Check for hints.
842 const char* branch_hint = NULL;
843 // We use these two prefixes only with branch prediction
844 if (*data == 0x3E /*ds*/) {
845 branch_hint = "predicted taken";
846 data++;
847 } else if (*data == 0x2E /*cs*/) {
848 branch_hint = "predicted not taken";
849 data++;
850 }
851 bool processed = true; // Will be set to false if the current instruction
852 // is not in 'instructions' table.
853 const InstructionDesc& idesc = instruction_table.Get(*data);
854 switch (idesc.type) {
855 case ZERO_OPERANDS_INSTR:
856 AppendToBuffer(idesc.mnem);
857 data++;
858 break;
859
860 case TWO_OPERANDS_INSTR:
861 data++;
862 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
863 break;
864
865 case JUMP_CONDITIONAL_SHORT_INSTR:
866 data += JumpConditionalShort(data, branch_hint);
867 break;
868
869 case REGISTER_INSTR:
870 AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
871 data++;
872 break;
873
874 case MOVE_REG_INSTR: {
875 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
876 AppendToBuffer("mov %s,%s",
877 NameOfCPURegister(*data & 0x07),
878 NameOfAddress(addr));
879 data += 5;
880 break;
881 }
882
883 case CALL_JUMP_INSTR: {
884 byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
885 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
886 data += 5;
887 break;
888 }
889
890 case SHORT_IMMEDIATE_INSTR: {
891 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
892 AppendToBuffer("%s eax, %s", idesc.mnem, NameOfAddress(addr));
893 data += 5;
894 break;
895 }
896
897 case NO_INSTR:
898 processed = false;
899 break;
900
901 default:
902 UNIMPLEMENTED(); // This type is not implemented.
903 }
904 //----------------------------
905 if (!processed) {
906 switch (*data) {
907 case 0xC2:
908 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data+1));
909 data += 3;
910 break;
911
912 case 0x69: // fall through
913 case 0x6B:
914 { int mod, regop, rm;
915 get_modrm(*(data+1), &mod, &regop, &rm);
916 int32_t imm =
917 *data == 0x6B ? *(data+2) : *reinterpret_cast<int32_t*>(data+2);
918 AppendToBuffer("imul %s,%s,0x%x",
919 NameOfCPURegister(regop),
920 NameOfCPURegister(rm),
921 imm);
922 data += 2 + (*data == 0x6B ? 1 : 4);
923 }
924 break;
925
926 case 0xF6:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100927 { data++;
928 int mod, regop, rm;
929 get_modrm(*data, &mod, &regop, &rm);
930 if (regop == eax) {
931 AppendToBuffer("test_b ");
932 data += PrintRightOperand(data);
933 int32_t imm = *data;
934 AppendToBuffer(",0x%x", imm);
935 data++;
Steve Blocka7e24c12009-10-30 11:49:00 +0000936 } else {
937 UnimplementedInstruction();
938 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000939 }
940 break;
941
942 case 0x81: // fall through
943 case 0x83: // 0x81 with sign extension bit set
944 data += PrintImmediateOp(data);
945 break;
946
947 case 0x0F:
948 { byte f0byte = *(data+1);
949 const char* f0mnem = F0Mnem(f0byte);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100950 if (f0byte == 0x18) {
951 int mod, regop, rm;
952 get_modrm(*data, &mod, &regop, &rm);
953 const char* suffix[] = {"nta", "1", "2", "3"};
954 AppendToBuffer("%s%s ", f0mnem, suffix[regop & 0x03]);
955 data += PrintRightOperand(data);
956 } else if (f0byte == 0xA2 || f0byte == 0x31) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000957 AppendToBuffer("%s", f0mnem);
958 data += 2;
959 } else if ((f0byte & 0xF0) == 0x80) {
960 data += JumpConditional(data, branch_hint);
961 } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
962 f0byte == 0xB7 || f0byte == 0xAF) {
963 data += 2;
964 data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
965 } else if ((f0byte & 0xF0) == 0x90) {
966 data += SetCC(data);
Steve Block3ce2e202009-11-05 08:53:23 +0000967 } else if ((f0byte & 0xF0) == 0x40) {
968 data += CMov(data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000969 } else {
970 data += 2;
971 if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
972 // shrd, shld, bts
973 AppendToBuffer("%s ", f0mnem);
974 int mod, regop, rm;
975 get_modrm(*data, &mod, &regop, &rm);
976 data += PrintRightOperand(data);
977 if (f0byte == 0xAB) {
978 AppendToBuffer(",%s", NameOfCPURegister(regop));
979 } else {
980 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
981 }
982 } else {
983 UnimplementedInstruction();
984 }
985 }
986 }
987 break;
988
989 case 0x8F:
990 { data++;
991 int mod, regop, rm;
992 get_modrm(*data, &mod, &regop, &rm);
993 if (regop == eax) {
994 AppendToBuffer("pop ");
995 data += PrintRightOperand(data);
996 }
997 }
998 break;
999
1000 case 0xFF:
1001 { data++;
1002 int mod, regop, rm;
1003 get_modrm(*data, &mod, &regop, &rm);
1004 const char* mnem = NULL;
1005 switch (regop) {
1006 case esi: mnem = "push"; break;
1007 case eax: mnem = "inc"; break;
1008 case ecx: mnem = "dec"; break;
1009 case edx: mnem = "call"; break;
1010 case esp: mnem = "jmp"; break;
1011 default: mnem = "???";
1012 }
1013 AppendToBuffer("%s ", mnem);
1014 data += PrintRightOperand(data);
1015 }
1016 break;
1017
1018 case 0xC7: // imm32, fall through
1019 case 0xC6: // imm8
1020 { bool is_byte = *data == 0xC6;
1021 data++;
1022 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
1023 data += PrintRightOperand(data);
1024 int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data);
1025 AppendToBuffer(",0x%x", imm);
1026 data += is_byte ? 1 : 4;
1027 }
1028 break;
1029
1030 case 0x80:
1031 { data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001032 int mod, regop, rm;
1033 get_modrm(*data, &mod, &regop, &rm);
1034 const char* mnem = NULL;
Leon Clarkee46be812010-01-19 14:06:41 +00001035 switch (regop) {
1036 case 5: mnem = "subb"; break;
1037 case 7: mnem = "cmpb"; break;
1038 default: UnimplementedInstruction();
1039 }
1040 AppendToBuffer("%s ", mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +00001041 data += PrintRightOperand(data);
1042 int32_t imm = *data;
1043 AppendToBuffer(",0x%x", imm);
1044 data++;
1045 }
1046 break;
1047
1048 case 0x88: // 8bit, fall through
1049 case 0x89: // 32bit
1050 { bool is_byte = *data == 0x88;
1051 int mod, regop, rm;
1052 data++;
1053 get_modrm(*data, &mod, &regop, &rm);
1054 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
1055 data += PrintRightOperand(data);
1056 AppendToBuffer(",%s", NameOfCPURegister(regop));
1057 }
1058 break;
1059
1060 case 0x66: // prefix
1061 data++;
1062 if (*data == 0x8B) {
1063 data++;
1064 data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
1065 } else if (*data == 0x89) {
1066 data++;
1067 int mod, regop, rm;
1068 get_modrm(*data, &mod, &regop, &rm);
1069 AppendToBuffer("mov_w ");
1070 data += PrintRightOperand(data);
1071 AppendToBuffer(",%s", NameOfCPURegister(regop));
Steve Block3ce2e202009-11-05 08:53:23 +00001072 } else if (*data == 0x0F) {
1073 data++;
Steve Block6ded16b2010-05-10 14:33:55 +01001074 if (*data == 0x38) {
1075 data++;
1076 if (*data == 0x17) {
1077 data++;
1078 int mod, regop, rm;
1079 get_modrm(*data, &mod, &regop, &rm);
1080 AppendToBuffer("ptest %s,%s",
1081 NameOfXMMRegister(regop),
1082 NameOfXMMRegister(rm));
1083 data++;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001084 } else if (*data == 0x2A) {
1085 // movntdqa
1086 data++;
1087 int mod, regop, rm;
1088 get_modrm(*data, &mod, &regop, &rm);
1089 AppendToBuffer("movntdqa %s,", NameOfXMMRegister(regop));
1090 data += PrintRightOperand(data);
Steve Block6ded16b2010-05-10 14:33:55 +01001091 } else {
1092 UnimplementedInstruction();
1093 }
1094 } else if (*data == 0x2E || *data == 0x2F) {
1095 const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd";
Steve Block3ce2e202009-11-05 08:53:23 +00001096 data++;
1097 int mod, regop, rm;
1098 get_modrm(*data, &mod, &regop, &rm);
Steve Block6ded16b2010-05-10 14:33:55 +01001099 if (mod == 0x3) {
1100 AppendToBuffer("%s %s,%s", mnem,
1101 NameOfXMMRegister(regop),
1102 NameOfXMMRegister(rm));
1103 data++;
1104 } else {
1105 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1106 data += PrintRightOperand(data);
1107 }
1108 } else if (*data == 0x50) {
1109 data++;
1110 int mod, regop, rm;
1111 get_modrm(*data, &mod, &regop, &rm);
1112 AppendToBuffer("movmskpd %s,%s",
1113 NameOfCPURegister(regop),
Steve Block3ce2e202009-11-05 08:53:23 +00001114 NameOfXMMRegister(rm));
1115 data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001116 } else if (*data == 0x57) {
1117 data++;
1118 int mod, regop, rm;
1119 get_modrm(*data, &mod, &regop, &rm);
1120 AppendToBuffer("xorpd %s,%s",
1121 NameOfXMMRegister(regop),
1122 NameOfXMMRegister(rm));
1123 data++;
Steve Block6ded16b2010-05-10 14:33:55 +01001124 } else if (*data == 0x6E) {
1125 data++;
1126 int mod, regop, rm;
1127 get_modrm(*data, &mod, &regop, &rm);
1128 AppendToBuffer("movd %s,", NameOfXMMRegister(regop));
1129 data += PrintRightOperand(data);
Leon Clarkee46be812010-01-19 14:06:41 +00001130 } else if (*data == 0x6F) {
1131 data++;
1132 int mod, regop, rm;
1133 get_modrm(*data, &mod, &regop, &rm);
1134 AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop));
1135 data += PrintRightOperand(data);
1136 } else if (*data == 0x7F) {
1137 AppendToBuffer("movdqa ");
1138 data++;
1139 int mod, regop, rm;
1140 get_modrm(*data, &mod, &regop, &rm);
1141 data += PrintRightOperand(data);
1142 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001143 } else if (*data == 0xE7) {
1144 AppendToBuffer("movntdq ");
1145 data++;
1146 int mod, regop, rm;
1147 get_modrm(*data, &mod, &regop, &rm);
1148 data += PrintRightOperand(data);
1149 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Steve Block6ded16b2010-05-10 14:33:55 +01001150 } else if (*data == 0xEF) {
1151 data++;
1152 int mod, regop, rm;
1153 get_modrm(*data, &mod, &regop, &rm);
1154 AppendToBuffer("pxor %s,%s",
1155 NameOfXMMRegister(regop),
1156 NameOfXMMRegister(rm));
1157 data++;
Steve Block3ce2e202009-11-05 08:53:23 +00001158 } else {
1159 UnimplementedInstruction();
1160 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001161 } else {
1162 UnimplementedInstruction();
1163 }
1164 break;
1165
1166 case 0xFE:
1167 { data++;
1168 int mod, regop, rm;
1169 get_modrm(*data, &mod, &regop, &rm);
1170 if (mod == 3 && regop == ecx) {
1171 AppendToBuffer("dec_b %s", NameOfCPURegister(rm));
1172 } else {
1173 UnimplementedInstruction();
1174 }
1175 data++;
1176 }
1177 break;
1178
1179 case 0x68:
1180 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
1181 data += 5;
1182 break;
1183
1184 case 0x6A:
1185 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1186 data += 2;
1187 break;
1188
1189 case 0xA8:
1190 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1191 data += 2;
1192 break;
1193
Leon Clarkee46be812010-01-19 14:06:41 +00001194 case 0x2C:
1195 AppendToBuffer("subb eax,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1196 data += 2;
1197 break;
1198
Steve Blocka7e24c12009-10-30 11:49:00 +00001199 case 0xA9:
1200 AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1));
1201 data += 5;
1202 break;
1203
1204 case 0xD1: // fall through
1205 case 0xD3: // fall through
1206 case 0xC1:
1207 data += D1D3C1Instruction(data);
1208 break;
1209
1210 case 0xD9: // fall through
1211 case 0xDA: // fall through
1212 case 0xDB: // fall through
1213 case 0xDC: // fall through
1214 case 0xDD: // fall through
1215 case 0xDE: // fall through
1216 case 0xDF:
1217 data += FPUInstruction(data);
1218 break;
1219
1220 case 0xEB:
1221 data += JumpShort(data);
1222 break;
1223
1224 case 0xF2:
1225 if (*(data+1) == 0x0F) {
1226 byte b2 = *(data+2);
1227 if (b2 == 0x11) {
1228 AppendToBuffer("movsd ");
1229 data += 3;
1230 int mod, regop, rm;
1231 get_modrm(*data, &mod, &regop, &rm);
1232 data += PrintRightOperand(data);
1233 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1234 } else if (b2 == 0x10) {
1235 data += 3;
1236 int mod, regop, rm;
1237 get_modrm(*data, &mod, &regop, &rm);
1238 AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
1239 data += PrintRightOperand(data);
1240 } else {
1241 const char* mnem = "?";
1242 switch (b2) {
1243 case 0x2A: mnem = "cvtsi2sd"; break;
Steve Block6ded16b2010-05-10 14:33:55 +01001244 case 0x2C: mnem = "cvttsd2si"; break;
1245 case 0x51: mnem = "sqrtsd"; break;
Steve Blocka7e24c12009-10-30 11:49:00 +00001246 case 0x58: mnem = "addsd"; break;
1247 case 0x59: mnem = "mulsd"; break;
1248 case 0x5C: mnem = "subsd"; break;
1249 case 0x5E: mnem = "divsd"; break;
1250 }
1251 data += 3;
1252 int mod, regop, rm;
1253 get_modrm(*data, &mod, &regop, &rm);
1254 if (b2 == 0x2A) {
Steve Block6ded16b2010-05-10 14:33:55 +01001255 if (mod != 0x3) {
1256 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1257 data += PrintRightOperand(data);
1258 } else {
1259 AppendToBuffer("%s %s,%s",
1260 mnem,
1261 NameOfXMMRegister(regop),
1262 NameOfCPURegister(rm));
1263 data++;
1264 }
1265 } else if (b2 == 0x2C) {
1266 if (mod != 0x3) {
1267 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
1268 data += PrintRightOperand(data);
1269 } else {
1270 AppendToBuffer("%s %s,%s",
1271 mnem,
1272 NameOfCPURegister(regop),
1273 NameOfXMMRegister(rm));
1274 data++;
1275 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001276 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01001277 if (mod != 0x3) {
1278 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1279 data += PrintRightOperand(data);
1280 } else {
1281 AppendToBuffer("%s %s,%s",
1282 mnem,
1283 NameOfXMMRegister(regop),
1284 NameOfXMMRegister(rm));
1285 data++;
1286 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001287 }
1288 }
1289 } else {
1290 UnimplementedInstruction();
1291 }
1292 break;
1293
1294 case 0xF3:
Leon Clarkee46be812010-01-19 14:06:41 +00001295 if (*(data+1) == 0x0F) {
1296 if (*(data+2) == 0x2C) {
1297 data += 3;
1298 data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
Steve Block6ded16b2010-05-10 14:33:55 +01001299 } else if (*(data+2) == 0x5A) {
1300 data += 3;
1301 int mod, regop, rm;
1302 get_modrm(*data, &mod, &regop, &rm);
1303 AppendToBuffer("cvtss2sd %s,%s",
1304 NameOfXMMRegister(regop),
1305 NameOfXMMRegister(rm));
1306 data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001307 } else if (*(data+2) == 0x6F) {
1308 data += 3;
1309 int mod, regop, rm;
1310 get_modrm(*data, &mod, &regop, &rm);
1311 AppendToBuffer("movdqu %s,", NameOfXMMRegister(regop));
1312 data += PrintRightOperand(data);
1313 } else if (*(data+2) == 0x7F) {
1314 AppendToBuffer("movdqu ");
1315 data += 3;
1316 int mod, regop, rm;
1317 get_modrm(*data, &mod, &regop, &rm);
1318 data += PrintRightOperand(data);
1319 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1320 } else {
1321 UnimplementedInstruction();
1322 }
1323 } else if (*(data+1) == 0xA5) {
1324 data += 2;
1325 AppendToBuffer("rep_movs");
Steve Block6ded16b2010-05-10 14:33:55 +01001326 } else if (*(data+1) == 0xAB) {
1327 data += 2;
1328 AppendToBuffer("rep_stos");
Steve Blocka7e24c12009-10-30 11:49:00 +00001329 } else {
1330 UnimplementedInstruction();
1331 }
1332 break;
1333
1334 case 0xF7:
1335 data += F7Instruction(data);
1336 break;
1337
1338 default:
1339 UnimplementedInstruction();
1340 }
1341 }
1342
1343 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1344 tmp_buffer_[tmp_buffer_pos_] = '\0';
1345 }
1346
1347 int instr_len = data - instr;
Leon Clarkee46be812010-01-19 14:06:41 +00001348 if (instr_len == 0) {
1349 printf("%02x", *data);
1350 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001351 ASSERT(instr_len > 0); // Ensure progress.
1352
1353 int outp = 0;
1354 // Instruction bytes.
1355 for (byte* bp = instr; bp < data; bp++) {
1356 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1357 "%02x",
1358 *bp);
1359 }
1360 for (int i = 6 - instr_len; i >= 0; i--) {
1361 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1362 " ");
1363 }
1364
1365 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1366 " %s",
1367 tmp_buffer_.start());
1368 return instr_len;
1369}
1370
1371
1372//------------------------------------------------------------------------------
1373
1374
1375static const char* cpu_regs[8] = {
1376 "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
1377};
1378
1379
1380static const char* byte_cpu_regs[8] = {
1381 "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
1382};
1383
1384
1385static const char* xmm_regs[8] = {
1386 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
1387};
1388
1389
1390const char* NameConverter::NameOfAddress(byte* addr) const {
1391 static v8::internal::EmbeddedVector<char, 32> tmp_buffer;
1392 v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr);
1393 return tmp_buffer.start();
1394}
1395
1396
1397const char* NameConverter::NameOfConstant(byte* addr) const {
1398 return NameOfAddress(addr);
1399}
1400
1401
1402const char* NameConverter::NameOfCPURegister(int reg) const {
1403 if (0 <= reg && reg < 8) return cpu_regs[reg];
1404 return "noreg";
1405}
1406
1407
1408const char* NameConverter::NameOfByteCPURegister(int reg) const {
1409 if (0 <= reg && reg < 8) return byte_cpu_regs[reg];
1410 return "noreg";
1411}
1412
1413
1414const char* NameConverter::NameOfXMMRegister(int reg) const {
1415 if (0 <= reg && reg < 8) return xmm_regs[reg];
1416 return "noxmmreg";
1417}
1418
1419
1420const char* NameConverter::NameInCode(byte* addr) const {
1421 // IA32 does not embed debug strings at the moment.
1422 UNREACHABLE();
1423 return "";
1424}
1425
1426
1427//------------------------------------------------------------------------------
1428
1429Disassembler::Disassembler(const NameConverter& converter)
1430 : converter_(converter) {}
1431
1432
1433Disassembler::~Disassembler() {}
1434
1435
1436int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1437 byte* instruction) {
1438 DisassemblerIA32 d(converter_, false /*do not crash if unimplemented*/);
1439 return d.InstructionDecode(buffer, instruction);
1440}
1441
1442
1443// The IA-32 assembler does not currently use constant pools.
1444int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }
1445
1446
1447/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1448 NameConverter converter;
1449 Disassembler d(converter);
1450 for (byte* pc = begin; pc < end;) {
1451 v8::internal::EmbeddedVector<char, 128> buffer;
1452 buffer[0] = '\0';
1453 byte* prev_pc = pc;
1454 pc += d.InstructionDecode(buffer, pc);
1455 fprintf(f, "%p", prev_pc);
1456 fprintf(f, " ");
1457
1458 for (byte* bp = prev_pc; bp < pc; bp++) {
1459 fprintf(f, "%02x", *bp);
1460 }
1461 for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
1462 fprintf(f, " ");
1463 }
1464 fprintf(f, " %s\n", buffer.start());
1465 }
1466}
1467
1468
1469} // namespace disasm
Leon Clarkef7060e22010-06-03 12:02:55 +01001470
1471#endif // V8_TARGET_ARCH_IA32