blob: 8d342e087c7b07dfbccaf331c057aba337a063d9 [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"
33#include "disasm.h"
34
35namespace disasm {
36
37enum OperandOrder {
38 UNSET_OP_ORDER = 0,
39 REG_OPER_OP_ORDER,
40 OPER_REG_OP_ORDER
41};
42
43
44//------------------------------------------------------------------
45// Tables
46//------------------------------------------------------------------
47struct ByteMnemonic {
48 int b; // -1 terminates, otherwise must be in range (0..255)
49 const char* mnem;
50 OperandOrder op_order_;
51};
52
53
54static ByteMnemonic two_operands_instr[] = {
55 {0x03, "add", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000056 {0x09, "or", OPER_REG_OP_ORDER},
57 {0x0B, "or", REG_OPER_OP_ORDER},
58 {0x1B, "sbb", REG_OPER_OP_ORDER},
Leon Clarked91b9f72010-01-27 17:25:45 +000059 {0x21, "and", OPER_REG_OP_ORDER},
60 {0x23, "and", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000061 {0x29, "sub", OPER_REG_OP_ORDER},
Leon Clarkee46be812010-01-19 14:06:41 +000062 {0x2A, "subb", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000063 {0x2B, "sub", REG_OPER_OP_ORDER},
Leon Clarkeeab96aa2010-01-27 16:31:12 +000064 {0x31, "xor", OPER_REG_OP_ORDER},
65 {0x33, "xor", REG_OPER_OP_ORDER},
Leon Clarked91b9f72010-01-27 17:25:45 +000066 {0x38, "cmpb", OPER_REG_OP_ORDER},
67 {0x3A, "cmpb", REG_OPER_OP_ORDER},
68 {0x3B, "cmp", REG_OPER_OP_ORDER},
69 {0x84, "test_b", REG_OPER_OP_ORDER},
70 {0x85, "test", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000071 {0x87, "xchg", REG_OPER_OP_ORDER},
72 {0x8A, "mov_b", REG_OPER_OP_ORDER},
73 {0x8B, "mov", REG_OPER_OP_ORDER},
Leon Clarked91b9f72010-01-27 17:25:45 +000074 {0x8D, "lea", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000075 {-1, "", UNSET_OP_ORDER}
76};
77
78
79static ByteMnemonic zero_operands_instr[] = {
80 {0xC3, "ret", UNSET_OP_ORDER},
81 {0xC9, "leave", UNSET_OP_ORDER},
82 {0x90, "nop", UNSET_OP_ORDER},
83 {0xF4, "hlt", UNSET_OP_ORDER},
84 {0xCC, "int3", UNSET_OP_ORDER},
85 {0x60, "pushad", UNSET_OP_ORDER},
86 {0x61, "popad", UNSET_OP_ORDER},
87 {0x9C, "pushfd", UNSET_OP_ORDER},
88 {0x9D, "popfd", UNSET_OP_ORDER},
89 {0x9E, "sahf", UNSET_OP_ORDER},
90 {0x99, "cdq", UNSET_OP_ORDER},
91 {0x9B, "fwait", UNSET_OP_ORDER},
Steve Block6ded16b2010-05-10 14:33:55 +010092 {0xFC, "cld", UNSET_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000093 {-1, "", UNSET_OP_ORDER}
94};
95
96
97static ByteMnemonic call_jump_instr[] = {
98 {0xE8, "call", UNSET_OP_ORDER},
99 {0xE9, "jmp", UNSET_OP_ORDER},
100 {-1, "", UNSET_OP_ORDER}
101};
102
103
104static ByteMnemonic short_immediate_instr[] = {
105 {0x05, "add", UNSET_OP_ORDER},
106 {0x0D, "or", UNSET_OP_ORDER},
107 {0x15, "adc", UNSET_OP_ORDER},
108 {0x25, "and", UNSET_OP_ORDER},
109 {0x2D, "sub", UNSET_OP_ORDER},
110 {0x35, "xor", UNSET_OP_ORDER},
111 {0x3D, "cmp", UNSET_OP_ORDER},
112 {-1, "", UNSET_OP_ORDER}
113};
114
115
116static const char* jump_conditional_mnem[] = {
117 /*0*/ "jo", "jno", "jc", "jnc",
118 /*4*/ "jz", "jnz", "jna", "ja",
119 /*8*/ "js", "jns", "jpe", "jpo",
120 /*12*/ "jl", "jnl", "jng", "jg"
121};
122
123
124static const char* set_conditional_mnem[] = {
125 /*0*/ "seto", "setno", "setc", "setnc",
126 /*4*/ "setz", "setnz", "setna", "seta",
127 /*8*/ "sets", "setns", "setpe", "setpo",
128 /*12*/ "setl", "setnl", "setng", "setg"
129};
130
131
Steve Block3ce2e202009-11-05 08:53:23 +0000132static const char* conditional_move_mnem[] = {
133 /*0*/ "cmovo", "cmovno", "cmovc", "cmovnc",
134 /*4*/ "cmovz", "cmovnz", "cmovna", "cmova",
135 /*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo",
136 /*12*/ "cmovl", "cmovnl", "cmovng", "cmovg"
137};
138
139
Steve Blocka7e24c12009-10-30 11:49:00 +0000140enum InstructionType {
141 NO_INSTR,
142 ZERO_OPERANDS_INSTR,
143 TWO_OPERANDS_INSTR,
144 JUMP_CONDITIONAL_SHORT_INSTR,
145 REGISTER_INSTR,
146 MOVE_REG_INSTR,
147 CALL_JUMP_INSTR,
148 SHORT_IMMEDIATE_INSTR
149};
150
151
152struct InstructionDesc {
153 const char* mnem;
154 InstructionType type;
155 OperandOrder op_order_;
156};
157
158
159class InstructionTable {
160 public:
161 InstructionTable();
162 const InstructionDesc& Get(byte x) const { return instructions_[x]; }
163
164 private:
165 InstructionDesc instructions_[256];
166 void Clear();
167 void Init();
168 void CopyTable(ByteMnemonic bm[], InstructionType type);
169 void SetTableRange(InstructionType type,
170 byte start,
171 byte end,
172 const char* mnem);
173 void AddJumpConditionalShort();
174};
175
176
177InstructionTable::InstructionTable() {
178 Clear();
179 Init();
180}
181
182
183void InstructionTable::Clear() {
184 for (int i = 0; i < 256; i++) {
185 instructions_[i].mnem = "";
186 instructions_[i].type = NO_INSTR;
187 instructions_[i].op_order_ = UNSET_OP_ORDER;
188 }
189}
190
191
192void InstructionTable::Init() {
193 CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
194 CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
195 CopyTable(call_jump_instr, CALL_JUMP_INSTR);
196 CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
197 AddJumpConditionalShort();
198 SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc");
199 SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec");
200 SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push");
201 SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop");
202 SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop.
203 SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov");
204}
205
206
207void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type) {
208 for (int i = 0; bm[i].b >= 0; i++) {
209 InstructionDesc* id = &instructions_[bm[i].b];
210 id->mnem = bm[i].mnem;
211 id->op_order_ = bm[i].op_order_;
Steve Blockd0582a62009-12-15 09:54:21 +0000212 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000213 id->type = type;
214 }
215}
216
217
218void InstructionTable::SetTableRange(InstructionType type,
219 byte start,
220 byte end,
221 const char* mnem) {
222 for (byte b = start; b <= end; b++) {
223 InstructionDesc* id = &instructions_[b];
Steve Blockd0582a62009-12-15 09:54:21 +0000224 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000225 id->mnem = mnem;
226 id->type = type;
227 }
228}
229
230
231void InstructionTable::AddJumpConditionalShort() {
232 for (byte b = 0x70; b <= 0x7F; b++) {
233 InstructionDesc* id = &instructions_[b];
Steve Blockd0582a62009-12-15 09:54:21 +0000234 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000235 id->mnem = jump_conditional_mnem[b & 0x0F];
236 id->type = JUMP_CONDITIONAL_SHORT_INSTR;
237 }
238}
239
240
241static InstructionTable instruction_table;
242
243
244// The IA32 disassembler implementation.
245class DisassemblerIA32 {
246 public:
247 DisassemblerIA32(const NameConverter& converter,
248 bool abort_on_unimplemented = true)
249 : converter_(converter),
250 tmp_buffer_pos_(0),
251 abort_on_unimplemented_(abort_on_unimplemented) {
252 tmp_buffer_[0] = '\0';
253 }
254
255 virtual ~DisassemblerIA32() {}
256
257 // Writes one disassembled instruction into 'buffer' (0-terminated).
258 // Returns the length of the disassembled machine instruction in bytes.
259 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
260
261 private:
262 const NameConverter& converter_;
263 v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
264 unsigned int tmp_buffer_pos_;
265 bool abort_on_unimplemented_;
266
267
268 enum {
269 eax = 0,
270 ecx = 1,
271 edx = 2,
272 ebx = 3,
273 esp = 4,
274 ebp = 5,
275 esi = 6,
276 edi = 7
277 };
278
279
Steve Blockd0582a62009-12-15 09:54:21 +0000280 enum ShiftOpcodeExtension {
281 kROL = 0,
282 kROR = 1,
283 kRCL = 2,
284 kRCR = 3,
285 kSHL = 4,
286 KSHR = 5,
287 kSAR = 7
288 };
289
290
Steve Blocka7e24c12009-10-30 11:49:00 +0000291 const char* NameOfCPURegister(int reg) const {
292 return converter_.NameOfCPURegister(reg);
293 }
294
295
296 const char* NameOfByteCPURegister(int reg) const {
297 return converter_.NameOfByteCPURegister(reg);
298 }
299
300
301 const char* NameOfXMMRegister(int reg) const {
302 return converter_.NameOfXMMRegister(reg);
303 }
304
305
306 const char* NameOfAddress(byte* addr) const {
307 return converter_.NameOfAddress(addr);
308 }
309
310
311 // Disassembler helper functions.
312 static void get_modrm(byte data, int* mod, int* regop, int* rm) {
313 *mod = (data >> 6) & 3;
314 *regop = (data & 0x38) >> 3;
315 *rm = data & 7;
316 }
317
318
319 static void get_sib(byte data, int* scale, int* index, int* base) {
320 *scale = (data >> 6) & 3;
321 *index = (data >> 3) & 7;
322 *base = data & 7;
323 }
324
325 typedef const char* (DisassemblerIA32::*RegisterNameMapping)(int reg) const;
326
327 int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name);
328 int PrintRightOperand(byte* modrmp);
329 int PrintRightByteOperand(byte* modrmp);
330 int PrintOperands(const char* mnem, OperandOrder op_order, byte* data);
331 int PrintImmediateOp(byte* data);
332 int F7Instruction(byte* data);
333 int D1D3C1Instruction(byte* data);
334 int JumpShort(byte* data);
335 int JumpConditional(byte* data, const char* comment);
336 int JumpConditionalShort(byte* data, const char* comment);
337 int SetCC(byte* data);
Steve Block3ce2e202009-11-05 08:53:23 +0000338 int CMov(byte* data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000339 int FPUInstruction(byte* data);
Steve Blockd0582a62009-12-15 09:54:21 +0000340 int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
341 int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
Steve Blocka7e24c12009-10-30 11:49:00 +0000342 void AppendToBuffer(const char* format, ...);
343
344
345 void UnimplementedInstruction() {
346 if (abort_on_unimplemented_) {
347 UNIMPLEMENTED();
348 } else {
349 AppendToBuffer("'Unimplemented Instruction'");
350 }
351 }
352};
353
354
355void DisassemblerIA32::AppendToBuffer(const char* format, ...) {
356 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
357 va_list args;
358 va_start(args, format);
359 int result = v8::internal::OS::VSNPrintF(buf, format, args);
360 va_end(args);
361 tmp_buffer_pos_ += result;
362}
363
364int DisassemblerIA32::PrintRightOperandHelper(
365 byte* modrmp,
366 RegisterNameMapping register_name) {
367 int mod, regop, rm;
368 get_modrm(*modrmp, &mod, &regop, &rm);
369 switch (mod) {
370 case 0:
371 if (rm == ebp) {
372 int32_t disp = *reinterpret_cast<int32_t*>(modrmp+1);
373 AppendToBuffer("[0x%x]", disp);
374 return 5;
375 } else if (rm == esp) {
376 byte sib = *(modrmp + 1);
377 int scale, index, base;
378 get_sib(sib, &scale, &index, &base);
379 if (index == esp && base == esp && scale == 0 /*times_1*/) {
380 AppendToBuffer("[%s]", (this->*register_name)(rm));
381 return 2;
382 } else if (base == ebp) {
383 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
384 AppendToBuffer("[%s*%d+0x%x]",
385 (this->*register_name)(index),
386 1 << scale,
387 disp);
388 return 6;
389 } else if (index != esp && base != ebp) {
390 // [base+index*scale]
391 AppendToBuffer("[%s+%s*%d]",
392 (this->*register_name)(base),
393 (this->*register_name)(index),
394 1 << scale);
395 return 2;
396 } else {
397 UnimplementedInstruction();
398 return 1;
399 }
400 } else {
401 AppendToBuffer("[%s]", (this->*register_name)(rm));
402 return 1;
403 }
404 break;
405 case 1: // fall through
406 case 2:
407 if (rm == esp) {
408 byte sib = *(modrmp + 1);
409 int scale, index, base;
410 get_sib(sib, &scale, &index, &base);
411 int disp =
412 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 2) : *(modrmp + 2);
413 if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
414 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
415 } else {
416 AppendToBuffer("[%s+%s*%d+0x%x]",
417 (this->*register_name)(base),
418 (this->*register_name)(index),
419 1 << scale,
420 disp);
421 }
422 return mod == 2 ? 6 : 3;
423 } else {
424 // No sib.
425 int disp =
426 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 1) : *(modrmp + 1);
427 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
428 return mod == 2 ? 5 : 2;
429 }
430 break;
431 case 3:
432 AppendToBuffer("%s", (this->*register_name)(rm));
433 return 1;
434 default:
435 UnimplementedInstruction();
436 return 1;
437 }
438 UNREACHABLE();
439}
440
441
442int DisassemblerIA32::PrintRightOperand(byte* modrmp) {
443 return PrintRightOperandHelper(modrmp, &DisassemblerIA32::NameOfCPURegister);
444}
445
446
447int DisassemblerIA32::PrintRightByteOperand(byte* modrmp) {
448 return PrintRightOperandHelper(modrmp,
449 &DisassemblerIA32::NameOfByteCPURegister);
450}
451
452
453// Returns number of bytes used including the current *data.
454// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
455int DisassemblerIA32::PrintOperands(const char* mnem,
456 OperandOrder op_order,
457 byte* data) {
458 byte modrm = *data;
459 int mod, regop, rm;
460 get_modrm(modrm, &mod, &regop, &rm);
461 int advance = 0;
462 switch (op_order) {
463 case REG_OPER_OP_ORDER: {
464 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
465 advance = PrintRightOperand(data);
466 break;
467 }
468 case OPER_REG_OP_ORDER: {
469 AppendToBuffer("%s ", mnem);
470 advance = PrintRightOperand(data);
471 AppendToBuffer(",%s", NameOfCPURegister(regop));
472 break;
473 }
474 default:
475 UNREACHABLE();
476 break;
477 }
478 return advance;
479}
480
481
482// Returns number of bytes used by machine instruction, including *data byte.
483// Writes immediate instructions to 'tmp_buffer_'.
484int DisassemblerIA32::PrintImmediateOp(byte* data) {
485 bool sign_extension_bit = (*data & 0x02) != 0;
486 byte modrm = *(data+1);
487 int mod, regop, rm;
488 get_modrm(modrm, &mod, &regop, &rm);
489 const char* mnem = "Imm???";
490 switch (regop) {
491 case 0: mnem = "add"; break;
492 case 1: mnem = "or"; break;
493 case 2: mnem = "adc"; break;
494 case 4: mnem = "and"; break;
495 case 5: mnem = "sub"; break;
496 case 6: mnem = "xor"; break;
497 case 7: mnem = "cmp"; break;
498 default: UnimplementedInstruction();
499 }
500 AppendToBuffer("%s ", mnem);
501 int count = PrintRightOperand(data+1);
502 if (sign_extension_bit) {
503 AppendToBuffer(",0x%x", *(data + 1 + count));
504 return 1 + count + 1 /*int8*/;
505 } else {
506 AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count));
507 return 1 + count + 4 /*int32_t*/;
508 }
509}
510
511
512// Returns number of bytes used, including *data.
513int DisassemblerIA32::F7Instruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000514 ASSERT_EQ(0xF7, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000515 byte modrm = *(data+1);
516 int mod, regop, rm;
517 get_modrm(modrm, &mod, &regop, &rm);
518 if (mod == 3 && regop != 0) {
519 const char* mnem = NULL;
520 switch (regop) {
521 case 2: mnem = "not"; break;
522 case 3: mnem = "neg"; break;
523 case 4: mnem = "mul"; break;
524 case 7: mnem = "idiv"; break;
525 default: UnimplementedInstruction();
526 }
527 AppendToBuffer("%s %s", mnem, NameOfCPURegister(rm));
528 return 2;
529 } else if (mod == 3 && regop == eax) {
530 int32_t imm = *reinterpret_cast<int32_t*>(data+2);
531 AppendToBuffer("test %s,0x%x", NameOfCPURegister(rm), imm);
532 return 6;
533 } else if (regop == eax) {
534 AppendToBuffer("test ");
535 int count = PrintRightOperand(data+1);
536 int32_t imm = *reinterpret_cast<int32_t*>(data+1+count);
537 AppendToBuffer(",0x%x", imm);
538 return 1+count+4 /*int32_t*/;
539 } else {
540 UnimplementedInstruction();
541 return 2;
542 }
543}
544
545int DisassemblerIA32::D1D3C1Instruction(byte* data) {
546 byte op = *data;
Steve Blockd0582a62009-12-15 09:54:21 +0000547 ASSERT(op == 0xD1 || op == 0xD3 || op == 0xC1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000548 byte modrm = *(data+1);
549 int mod, regop, rm;
550 get_modrm(modrm, &mod, &regop, &rm);
551 int imm8 = -1;
552 int num_bytes = 2;
553 if (mod == 3) {
554 const char* mnem = NULL;
Steve Blockd0582a62009-12-15 09:54:21 +0000555 switch (regop) {
556 case kROL: mnem = "rol"; break;
557 case kROR: mnem = "ror"; break;
558 case kRCL: mnem = "rcl"; break;
559 case kSHL: mnem = "shl"; break;
560 case KSHR: mnem = "shr"; break;
561 case kSAR: mnem = "sar"; break;
562 default: UnimplementedInstruction();
563 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000564 if (op == 0xD1) {
565 imm8 = 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000566 } else if (op == 0xC1) {
567 imm8 = *(data+2);
568 num_bytes = 3;
Steve Blocka7e24c12009-10-30 11:49:00 +0000569 } else if (op == 0xD3) {
Steve Blockd0582a62009-12-15 09:54:21 +0000570 // Shift/rotate by cl.
Steve Blocka7e24c12009-10-30 11:49:00 +0000571 }
Steve Blockd0582a62009-12-15 09:54:21 +0000572 ASSERT_NE(NULL, mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +0000573 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(rm));
574 if (imm8 > 0) {
575 AppendToBuffer("%d", imm8);
576 } else {
577 AppendToBuffer("cl");
578 }
579 } else {
580 UnimplementedInstruction();
581 }
582 return num_bytes;
583}
584
585
586// Returns number of bytes used, including *data.
587int DisassemblerIA32::JumpShort(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000588 ASSERT_EQ(0xEB, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000589 byte b = *(data+1);
590 byte* dest = data + static_cast<int8_t>(b) + 2;
591 AppendToBuffer("jmp %s", NameOfAddress(dest));
592 return 2;
593}
594
595
596// Returns number of bytes used, including *data.
597int DisassemblerIA32::JumpConditional(byte* data, const char* comment) {
Steve Blockd0582a62009-12-15 09:54:21 +0000598 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000599 byte cond = *(data+1) & 0x0F;
600 byte* dest = data + *reinterpret_cast<int32_t*>(data+2) + 6;
601 const char* mnem = jump_conditional_mnem[cond];
602 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
603 if (comment != NULL) {
604 AppendToBuffer(", %s", comment);
605 }
606 return 6; // includes 0x0F
607}
608
609
610// Returns number of bytes used, including *data.
611int DisassemblerIA32::JumpConditionalShort(byte* data, const char* comment) {
612 byte cond = *data & 0x0F;
613 byte b = *(data+1);
614 byte* dest = data + static_cast<int8_t>(b) + 2;
615 const char* mnem = jump_conditional_mnem[cond];
616 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
617 if (comment != NULL) {
618 AppendToBuffer(", %s", comment);
619 }
620 return 2;
621}
622
623
624// Returns number of bytes used, including *data.
625int DisassemblerIA32::SetCC(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000626 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000627 byte cond = *(data+1) & 0x0F;
628 const char* mnem = set_conditional_mnem[cond];
629 AppendToBuffer("%s ", mnem);
630 PrintRightByteOperand(data+2);
Steve Blockd0582a62009-12-15 09:54:21 +0000631 return 3; // Includes 0x0F.
Steve Blocka7e24c12009-10-30 11:49:00 +0000632}
633
634
635// Returns number of bytes used, including *data.
Steve Block3ce2e202009-11-05 08:53:23 +0000636int DisassemblerIA32::CMov(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000637 ASSERT_EQ(0x0F, *data);
Steve Block3ce2e202009-11-05 08:53:23 +0000638 byte cond = *(data + 1) & 0x0F;
639 const char* mnem = conditional_move_mnem[cond];
640 int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2);
641 return 2 + op_size; // includes 0x0F
642}
643
644
645// Returns number of bytes used, including *data.
Steve Blocka7e24c12009-10-30 11:49:00 +0000646int DisassemblerIA32::FPUInstruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000647 byte escape_opcode = *data;
648 ASSERT_EQ(0xD8, escape_opcode & 0xF8);
649 byte modrm_byte = *(data+1);
650
651 if (modrm_byte >= 0xC0) {
652 return RegisterFPUInstruction(escape_opcode, modrm_byte);
653 } else {
654 return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000655 }
Steve Blockd0582a62009-12-15 09:54:21 +0000656}
657
658int DisassemblerIA32::MemoryFPUInstruction(int escape_opcode,
659 int modrm_byte,
660 byte* modrm_start) {
661 const char* mnem = "?";
662 int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
663 switch (escape_opcode) {
664 case 0xD9: switch (regop) {
665 case 0: mnem = "fld_s"; break;
666 case 3: mnem = "fstp_s"; break;
667 case 7: mnem = "fstcw"; break;
668 default: UnimplementedInstruction();
669 }
670 break;
671
672 case 0xDB: switch (regop) {
673 case 0: mnem = "fild_s"; break;
674 case 1: mnem = "fisttp_s"; break;
675 case 2: mnem = "fist_s"; break;
676 case 3: mnem = "fistp_s"; break;
677 default: UnimplementedInstruction();
678 }
679 break;
680
681 case 0xDD: switch (regop) {
682 case 0: mnem = "fld_d"; break;
Andrei Popescu402d9372010-02-26 13:31:12 +0000683 case 2: mnem = "fstp"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000684 case 3: mnem = "fstp_d"; break;
685 default: UnimplementedInstruction();
686 }
687 break;
688
689 case 0xDF: switch (regop) {
690 case 5: mnem = "fild_d"; break;
691 case 7: mnem = "fistp_d"; break;
692 default: UnimplementedInstruction();
693 }
694 break;
695
696 default: UnimplementedInstruction();
697 }
698 AppendToBuffer("%s ", mnem);
699 int count = PrintRightOperand(modrm_start);
700 return count + 1;
701}
702
703int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode,
704 byte modrm_byte) {
705 bool has_register = false; // Is the FPU register encoded in modrm_byte?
706 const char* mnem = "?";
707
708 switch (escape_opcode) {
709 case 0xD8:
710 UnimplementedInstruction();
711 break;
712
713 case 0xD9:
714 switch (modrm_byte & 0xF8) {
715 case 0xC8:
716 mnem = "fxch";
717 has_register = true;
718 break;
719 default:
720 switch (modrm_byte) {
721 case 0xE0: mnem = "fchs"; break;
722 case 0xE1: mnem = "fabs"; break;
723 case 0xE4: mnem = "ftst"; break;
724 case 0xE8: mnem = "fld1"; break;
Andrei Popescu402d9372010-02-26 13:31:12 +0000725 case 0xEB: mnem = "fldpi"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000726 case 0xEE: mnem = "fldz"; break;
727 case 0xF5: mnem = "fprem1"; break;
728 case 0xF7: mnem = "fincstp"; break;
729 case 0xF8: mnem = "fprem"; break;
730 case 0xFE: mnem = "fsin"; break;
731 case 0xFF: mnem = "fcos"; break;
732 default: UnimplementedInstruction();
733 }
734 }
735 break;
736
737 case 0xDA:
738 if (modrm_byte == 0xE9) {
739 mnem = "fucompp";
740 } else {
741 UnimplementedInstruction();
742 }
743 break;
744
745 case 0xDB:
746 if ((modrm_byte & 0xF8) == 0xE8) {
747 mnem = "fucomi";
748 has_register = true;
749 } else if (modrm_byte == 0xE2) {
750 mnem = "fclex";
751 } else {
752 UnimplementedInstruction();
753 }
754 break;
755
756 case 0xDC:
757 has_register = true;
758 switch (modrm_byte & 0xF8) {
759 case 0xC0: mnem = "fadd"; break;
760 case 0xE8: mnem = "fsub"; break;
761 case 0xC8: mnem = "fmul"; break;
762 case 0xF8: mnem = "fdiv"; break;
763 default: UnimplementedInstruction();
764 }
765 break;
766
767 case 0xDD:
768 has_register = true;
769 switch (modrm_byte & 0xF8) {
770 case 0xC0: mnem = "ffree"; break;
771 case 0xD8: mnem = "fstp"; break;
772 default: UnimplementedInstruction();
773 }
774 break;
775
776 case 0xDE:
777 if (modrm_byte == 0xD9) {
778 mnem = "fcompp";
779 } else {
780 has_register = true;
781 switch (modrm_byte & 0xF8) {
782 case 0xC0: mnem = "faddp"; break;
783 case 0xE8: mnem = "fsubp"; break;
784 case 0xC8: mnem = "fmulp"; break;
785 case 0xF8: mnem = "fdivp"; break;
786 default: UnimplementedInstruction();
787 }
788 }
789 break;
790
791 case 0xDF:
792 if (modrm_byte == 0xE0) {
793 mnem = "fnstsw_ax";
794 } else if ((modrm_byte & 0xF8) == 0xE8) {
795 mnem = "fucomip";
796 has_register = true;
797 }
798 break;
799
800 default: UnimplementedInstruction();
801 }
802
803 if (has_register) {
804 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
805 } else {
806 AppendToBuffer("%s", mnem);
807 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000808 return 2;
809}
810
811
812// Mnemonics for instructions 0xF0 byte.
813// Returns NULL if the instruction is not handled here.
814static const char* F0Mnem(byte f0byte) {
815 switch (f0byte) {
816 case 0xA2: return "cpuid";
817 case 0x31: return "rdtsc";
818 case 0xBE: return "movsx_b";
819 case 0xBF: return "movsx_w";
820 case 0xB6: return "movzx_b";
821 case 0xB7: return "movzx_w";
822 case 0xAF: return "imul";
823 case 0xA5: return "shld";
824 case 0xAD: return "shrd";
825 case 0xAB: return "bts";
826 default: return NULL;
827 }
828}
829
830
831// Disassembled instruction '*instr' and writes it into 'out_buffer'.
832int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
833 byte* instr) {
834 tmp_buffer_pos_ = 0; // starting to write as position 0
835 byte* data = instr;
836 // Check for hints.
837 const char* branch_hint = NULL;
838 // We use these two prefixes only with branch prediction
839 if (*data == 0x3E /*ds*/) {
840 branch_hint = "predicted taken";
841 data++;
842 } else if (*data == 0x2E /*cs*/) {
843 branch_hint = "predicted not taken";
844 data++;
845 }
846 bool processed = true; // Will be set to false if the current instruction
847 // is not in 'instructions' table.
848 const InstructionDesc& idesc = instruction_table.Get(*data);
849 switch (idesc.type) {
850 case ZERO_OPERANDS_INSTR:
851 AppendToBuffer(idesc.mnem);
852 data++;
853 break;
854
855 case TWO_OPERANDS_INSTR:
856 data++;
857 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
858 break;
859
860 case JUMP_CONDITIONAL_SHORT_INSTR:
861 data += JumpConditionalShort(data, branch_hint);
862 break;
863
864 case REGISTER_INSTR:
865 AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
866 data++;
867 break;
868
869 case MOVE_REG_INSTR: {
870 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
871 AppendToBuffer("mov %s,%s",
872 NameOfCPURegister(*data & 0x07),
873 NameOfAddress(addr));
874 data += 5;
875 break;
876 }
877
878 case CALL_JUMP_INSTR: {
879 byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
880 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
881 data += 5;
882 break;
883 }
884
885 case SHORT_IMMEDIATE_INSTR: {
886 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
887 AppendToBuffer("%s eax, %s", idesc.mnem, NameOfAddress(addr));
888 data += 5;
889 break;
890 }
891
892 case NO_INSTR:
893 processed = false;
894 break;
895
896 default:
897 UNIMPLEMENTED(); // This type is not implemented.
898 }
899 //----------------------------
900 if (!processed) {
901 switch (*data) {
902 case 0xC2:
903 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data+1));
904 data += 3;
905 break;
906
907 case 0x69: // fall through
908 case 0x6B:
909 { int mod, regop, rm;
910 get_modrm(*(data+1), &mod, &regop, &rm);
911 int32_t imm =
912 *data == 0x6B ? *(data+2) : *reinterpret_cast<int32_t*>(data+2);
913 AppendToBuffer("imul %s,%s,0x%x",
914 NameOfCPURegister(regop),
915 NameOfCPURegister(rm),
916 imm);
917 data += 2 + (*data == 0x6B ? 1 : 4);
918 }
919 break;
920
921 case 0xF6:
922 { int mod, regop, rm;
923 get_modrm(*(data+1), &mod, &regop, &rm);
924 if (mod == 3 && regop == eax) {
925 AppendToBuffer("test_b %s,%d", NameOfCPURegister(rm), *(data+2));
926 } else {
927 UnimplementedInstruction();
928 }
929 data += 3;
930 }
931 break;
932
933 case 0x81: // fall through
934 case 0x83: // 0x81 with sign extension bit set
935 data += PrintImmediateOp(data);
936 break;
937
938 case 0x0F:
939 { byte f0byte = *(data+1);
940 const char* f0mnem = F0Mnem(f0byte);
941 if (f0byte == 0xA2 || f0byte == 0x31) {
942 AppendToBuffer("%s", f0mnem);
943 data += 2;
944 } else if ((f0byte & 0xF0) == 0x80) {
945 data += JumpConditional(data, branch_hint);
946 } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
947 f0byte == 0xB7 || f0byte == 0xAF) {
948 data += 2;
949 data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
950 } else if ((f0byte & 0xF0) == 0x90) {
951 data += SetCC(data);
Steve Block3ce2e202009-11-05 08:53:23 +0000952 } else if ((f0byte & 0xF0) == 0x40) {
953 data += CMov(data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000954 } else {
955 data += 2;
956 if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
957 // shrd, shld, bts
958 AppendToBuffer("%s ", f0mnem);
959 int mod, regop, rm;
960 get_modrm(*data, &mod, &regop, &rm);
961 data += PrintRightOperand(data);
962 if (f0byte == 0xAB) {
963 AppendToBuffer(",%s", NameOfCPURegister(regop));
964 } else {
965 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
966 }
967 } else {
968 UnimplementedInstruction();
969 }
970 }
971 }
972 break;
973
974 case 0x8F:
975 { data++;
976 int mod, regop, rm;
977 get_modrm(*data, &mod, &regop, &rm);
978 if (regop == eax) {
979 AppendToBuffer("pop ");
980 data += PrintRightOperand(data);
981 }
982 }
983 break;
984
985 case 0xFF:
986 { data++;
987 int mod, regop, rm;
988 get_modrm(*data, &mod, &regop, &rm);
989 const char* mnem = NULL;
990 switch (regop) {
991 case esi: mnem = "push"; break;
992 case eax: mnem = "inc"; break;
993 case ecx: mnem = "dec"; break;
994 case edx: mnem = "call"; break;
995 case esp: mnem = "jmp"; break;
996 default: mnem = "???";
997 }
998 AppendToBuffer("%s ", mnem);
999 data += PrintRightOperand(data);
1000 }
1001 break;
1002
1003 case 0xC7: // imm32, fall through
1004 case 0xC6: // imm8
1005 { bool is_byte = *data == 0xC6;
1006 data++;
1007 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
1008 data += PrintRightOperand(data);
1009 int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data);
1010 AppendToBuffer(",0x%x", imm);
1011 data += is_byte ? 1 : 4;
1012 }
1013 break;
1014
1015 case 0x80:
1016 { data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001017 int mod, regop, rm;
1018 get_modrm(*data, &mod, &regop, &rm);
1019 const char* mnem = NULL;
Leon Clarkee46be812010-01-19 14:06:41 +00001020 switch (regop) {
1021 case 5: mnem = "subb"; break;
1022 case 7: mnem = "cmpb"; break;
1023 default: UnimplementedInstruction();
1024 }
1025 AppendToBuffer("%s ", mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +00001026 data += PrintRightOperand(data);
1027 int32_t imm = *data;
1028 AppendToBuffer(",0x%x", imm);
1029 data++;
1030 }
1031 break;
1032
1033 case 0x88: // 8bit, fall through
1034 case 0x89: // 32bit
1035 { bool is_byte = *data == 0x88;
1036 int mod, regop, rm;
1037 data++;
1038 get_modrm(*data, &mod, &regop, &rm);
1039 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
1040 data += PrintRightOperand(data);
1041 AppendToBuffer(",%s", NameOfCPURegister(regop));
1042 }
1043 break;
1044
1045 case 0x66: // prefix
1046 data++;
1047 if (*data == 0x8B) {
1048 data++;
1049 data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
1050 } else if (*data == 0x89) {
1051 data++;
1052 int mod, regop, rm;
1053 get_modrm(*data, &mod, &regop, &rm);
1054 AppendToBuffer("mov_w ");
1055 data += PrintRightOperand(data);
1056 AppendToBuffer(",%s", NameOfCPURegister(regop));
Steve Block3ce2e202009-11-05 08:53:23 +00001057 } else if (*data == 0x0F) {
1058 data++;
Steve Block6ded16b2010-05-10 14:33:55 +01001059 if (*data == 0x38) {
1060 data++;
1061 if (*data == 0x17) {
1062 data++;
1063 int mod, regop, rm;
1064 get_modrm(*data, &mod, &regop, &rm);
1065 AppendToBuffer("ptest %s,%s",
1066 NameOfXMMRegister(regop),
1067 NameOfXMMRegister(rm));
1068 data++;
1069 } else {
1070 UnimplementedInstruction();
1071 }
1072 } else if (*data == 0x2E || *data == 0x2F) {
1073 const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd";
Steve Block3ce2e202009-11-05 08:53:23 +00001074 data++;
1075 int mod, regop, rm;
1076 get_modrm(*data, &mod, &regop, &rm);
Steve Block6ded16b2010-05-10 14:33:55 +01001077 if (mod == 0x3) {
1078 AppendToBuffer("%s %s,%s", mnem,
1079 NameOfXMMRegister(regop),
1080 NameOfXMMRegister(rm));
1081 data++;
1082 } else {
1083 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1084 data += PrintRightOperand(data);
1085 }
1086 } else if (*data == 0x50) {
1087 data++;
1088 int mod, regop, rm;
1089 get_modrm(*data, &mod, &regop, &rm);
1090 AppendToBuffer("movmskpd %s,%s",
1091 NameOfCPURegister(regop),
Steve Block3ce2e202009-11-05 08:53:23 +00001092 NameOfXMMRegister(rm));
1093 data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001094 } else if (*data == 0x57) {
1095 data++;
1096 int mod, regop, rm;
1097 get_modrm(*data, &mod, &regop, &rm);
1098 AppendToBuffer("xorpd %s,%s",
1099 NameOfXMMRegister(regop),
1100 NameOfXMMRegister(rm));
1101 data++;
Steve Block6ded16b2010-05-10 14:33:55 +01001102 } else if (*data == 0x6E) {
1103 data++;
1104 int mod, regop, rm;
1105 get_modrm(*data, &mod, &regop, &rm);
1106 AppendToBuffer("movd %s,", NameOfXMMRegister(regop));
1107 data += PrintRightOperand(data);
Leon Clarkee46be812010-01-19 14:06:41 +00001108 } else if (*data == 0x6F) {
1109 data++;
1110 int mod, regop, rm;
1111 get_modrm(*data, &mod, &regop, &rm);
1112 AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop));
1113 data += PrintRightOperand(data);
1114 } else if (*data == 0x7F) {
1115 AppendToBuffer("movdqa ");
1116 data++;
1117 int mod, regop, rm;
1118 get_modrm(*data, &mod, &regop, &rm);
1119 data += PrintRightOperand(data);
1120 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Steve Block6ded16b2010-05-10 14:33:55 +01001121 } else if (*data == 0xEF) {
1122 data++;
1123 int mod, regop, rm;
1124 get_modrm(*data, &mod, &regop, &rm);
1125 AppendToBuffer("pxor %s,%s",
1126 NameOfXMMRegister(regop),
1127 NameOfXMMRegister(rm));
1128 data++;
Steve Block3ce2e202009-11-05 08:53:23 +00001129 } else {
1130 UnimplementedInstruction();
1131 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001132 } else {
1133 UnimplementedInstruction();
1134 }
1135 break;
1136
1137 case 0xFE:
1138 { data++;
1139 int mod, regop, rm;
1140 get_modrm(*data, &mod, &regop, &rm);
1141 if (mod == 3 && regop == ecx) {
1142 AppendToBuffer("dec_b %s", NameOfCPURegister(rm));
1143 } else {
1144 UnimplementedInstruction();
1145 }
1146 data++;
1147 }
1148 break;
1149
1150 case 0x68:
1151 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
1152 data += 5;
1153 break;
1154
1155 case 0x6A:
1156 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1157 data += 2;
1158 break;
1159
1160 case 0xA8:
1161 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1162 data += 2;
1163 break;
1164
Leon Clarkee46be812010-01-19 14:06:41 +00001165 case 0x2C:
1166 AppendToBuffer("subb eax,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1167 data += 2;
1168 break;
1169
Steve Blocka7e24c12009-10-30 11:49:00 +00001170 case 0xA9:
1171 AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1));
1172 data += 5;
1173 break;
1174
1175 case 0xD1: // fall through
1176 case 0xD3: // fall through
1177 case 0xC1:
1178 data += D1D3C1Instruction(data);
1179 break;
1180
1181 case 0xD9: // fall through
1182 case 0xDA: // fall through
1183 case 0xDB: // fall through
1184 case 0xDC: // fall through
1185 case 0xDD: // fall through
1186 case 0xDE: // fall through
1187 case 0xDF:
1188 data += FPUInstruction(data);
1189 break;
1190
1191 case 0xEB:
1192 data += JumpShort(data);
1193 break;
1194
1195 case 0xF2:
1196 if (*(data+1) == 0x0F) {
1197 byte b2 = *(data+2);
1198 if (b2 == 0x11) {
1199 AppendToBuffer("movsd ");
1200 data += 3;
1201 int mod, regop, rm;
1202 get_modrm(*data, &mod, &regop, &rm);
1203 data += PrintRightOperand(data);
1204 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1205 } else if (b2 == 0x10) {
1206 data += 3;
1207 int mod, regop, rm;
1208 get_modrm(*data, &mod, &regop, &rm);
1209 AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
1210 data += PrintRightOperand(data);
1211 } else {
1212 const char* mnem = "?";
1213 switch (b2) {
1214 case 0x2A: mnem = "cvtsi2sd"; break;
Steve Block6ded16b2010-05-10 14:33:55 +01001215 case 0x2C: mnem = "cvttsd2si"; break;
1216 case 0x51: mnem = "sqrtsd"; break;
Steve Blocka7e24c12009-10-30 11:49:00 +00001217 case 0x58: mnem = "addsd"; break;
1218 case 0x59: mnem = "mulsd"; break;
1219 case 0x5C: mnem = "subsd"; break;
1220 case 0x5E: mnem = "divsd"; break;
1221 }
1222 data += 3;
1223 int mod, regop, rm;
1224 get_modrm(*data, &mod, &regop, &rm);
1225 if (b2 == 0x2A) {
Steve Block6ded16b2010-05-10 14:33:55 +01001226 if (mod != 0x3) {
1227 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1228 data += PrintRightOperand(data);
1229 } else {
1230 AppendToBuffer("%s %s,%s",
1231 mnem,
1232 NameOfXMMRegister(regop),
1233 NameOfCPURegister(rm));
1234 data++;
1235 }
1236 } else if (b2 == 0x2C) {
1237 if (mod != 0x3) {
1238 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
1239 data += PrintRightOperand(data);
1240 } else {
1241 AppendToBuffer("%s %s,%s",
1242 mnem,
1243 NameOfCPURegister(regop),
1244 NameOfXMMRegister(rm));
1245 data++;
1246 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001247 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01001248 if (mod != 0x3) {
1249 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1250 data += PrintRightOperand(data);
1251 } else {
1252 AppendToBuffer("%s %s,%s",
1253 mnem,
1254 NameOfXMMRegister(regop),
1255 NameOfXMMRegister(rm));
1256 data++;
1257 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001258 }
1259 }
1260 } else {
1261 UnimplementedInstruction();
1262 }
1263 break;
1264
1265 case 0xF3:
Leon Clarkee46be812010-01-19 14:06:41 +00001266 if (*(data+1) == 0x0F) {
1267 if (*(data+2) == 0x2C) {
1268 data += 3;
1269 data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
Steve Block6ded16b2010-05-10 14:33:55 +01001270 } else if (*(data+2) == 0x5A) {
1271 data += 3;
1272 int mod, regop, rm;
1273 get_modrm(*data, &mod, &regop, &rm);
1274 AppendToBuffer("cvtss2sd %s,%s",
1275 NameOfXMMRegister(regop),
1276 NameOfXMMRegister(rm));
1277 data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001278 } else if (*(data+2) == 0x6F) {
1279 data += 3;
1280 int mod, regop, rm;
1281 get_modrm(*data, &mod, &regop, &rm);
1282 AppendToBuffer("movdqu %s,", NameOfXMMRegister(regop));
1283 data += PrintRightOperand(data);
1284 } else if (*(data+2) == 0x7F) {
1285 AppendToBuffer("movdqu ");
1286 data += 3;
1287 int mod, regop, rm;
1288 get_modrm(*data, &mod, &regop, &rm);
1289 data += PrintRightOperand(data);
1290 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1291 } else {
1292 UnimplementedInstruction();
1293 }
1294 } else if (*(data+1) == 0xA5) {
1295 data += 2;
1296 AppendToBuffer("rep_movs");
Steve Block6ded16b2010-05-10 14:33:55 +01001297 } else if (*(data+1) == 0xAB) {
1298 data += 2;
1299 AppendToBuffer("rep_stos");
Steve Blocka7e24c12009-10-30 11:49:00 +00001300 } else {
1301 UnimplementedInstruction();
1302 }
1303 break;
1304
1305 case 0xF7:
1306 data += F7Instruction(data);
1307 break;
1308
1309 default:
1310 UnimplementedInstruction();
1311 }
1312 }
1313
1314 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1315 tmp_buffer_[tmp_buffer_pos_] = '\0';
1316 }
1317
1318 int instr_len = data - instr;
Leon Clarkee46be812010-01-19 14:06:41 +00001319 if (instr_len == 0) {
1320 printf("%02x", *data);
1321 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001322 ASSERT(instr_len > 0); // Ensure progress.
1323
1324 int outp = 0;
1325 // Instruction bytes.
1326 for (byte* bp = instr; bp < data; bp++) {
1327 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1328 "%02x",
1329 *bp);
1330 }
1331 for (int i = 6 - instr_len; i >= 0; i--) {
1332 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1333 " ");
1334 }
1335
1336 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1337 " %s",
1338 tmp_buffer_.start());
1339 return instr_len;
1340}
1341
1342
1343//------------------------------------------------------------------------------
1344
1345
1346static const char* cpu_regs[8] = {
1347 "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
1348};
1349
1350
1351static const char* byte_cpu_regs[8] = {
1352 "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
1353};
1354
1355
1356static const char* xmm_regs[8] = {
1357 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
1358};
1359
1360
1361const char* NameConverter::NameOfAddress(byte* addr) const {
1362 static v8::internal::EmbeddedVector<char, 32> tmp_buffer;
1363 v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr);
1364 return tmp_buffer.start();
1365}
1366
1367
1368const char* NameConverter::NameOfConstant(byte* addr) const {
1369 return NameOfAddress(addr);
1370}
1371
1372
1373const char* NameConverter::NameOfCPURegister(int reg) const {
1374 if (0 <= reg && reg < 8) return cpu_regs[reg];
1375 return "noreg";
1376}
1377
1378
1379const char* NameConverter::NameOfByteCPURegister(int reg) const {
1380 if (0 <= reg && reg < 8) return byte_cpu_regs[reg];
1381 return "noreg";
1382}
1383
1384
1385const char* NameConverter::NameOfXMMRegister(int reg) const {
1386 if (0 <= reg && reg < 8) return xmm_regs[reg];
1387 return "noxmmreg";
1388}
1389
1390
1391const char* NameConverter::NameInCode(byte* addr) const {
1392 // IA32 does not embed debug strings at the moment.
1393 UNREACHABLE();
1394 return "";
1395}
1396
1397
1398//------------------------------------------------------------------------------
1399
1400Disassembler::Disassembler(const NameConverter& converter)
1401 : converter_(converter) {}
1402
1403
1404Disassembler::~Disassembler() {}
1405
1406
1407int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1408 byte* instruction) {
1409 DisassemblerIA32 d(converter_, false /*do not crash if unimplemented*/);
1410 return d.InstructionDecode(buffer, instruction);
1411}
1412
1413
1414// The IA-32 assembler does not currently use constant pools.
1415int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }
1416
1417
1418/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1419 NameConverter converter;
1420 Disassembler d(converter);
1421 for (byte* pc = begin; pc < end;) {
1422 v8::internal::EmbeddedVector<char, 128> buffer;
1423 buffer[0] = '\0';
1424 byte* prev_pc = pc;
1425 pc += d.InstructionDecode(buffer, pc);
1426 fprintf(f, "%p", prev_pc);
1427 fprintf(f, " ");
1428
1429 for (byte* bp = prev_pc; bp < pc; bp++) {
1430 fprintf(f, "%02x", *bp);
1431 }
1432 for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
1433 fprintf(f, " ");
1434 }
1435 fprintf(f, " %s\n", buffer.start());
1436 }
1437}
1438
1439
1440} // namespace disasm