blob: 1f358674646739dbefbbe023b42e607888a5a304 [file] [log] [blame]
The Android Open Source Project52d4c302009-03-03 19:29:09 -08001// Copyright 2006 The Android Open Source Project
2
3#include <stdio.h>
4#include <string.h>
5#include "armdis.h"
6#include "opcode.h"
7
Jack Veenstra166d76c2009-05-09 11:33:44 -07008static const char *cond_names[] = {
The Android Open Source Project52d4c302009-03-03 19:29:09 -08009 "eq",
10 "ne",
11 "cs",
12 "cc",
13 "mi",
14 "pl",
15 "vs",
16 "vc",
17 "hi",
18 "ls",
19 "ge",
20 "lt",
21 "gt",
22 "le",
23 "",
24 "RESERVED"
25};
26
27// Indexed by the shift type (bits 6-5)
28static const char *shift_names[] = {
29 "LSL",
30 "LSR",
31 "ASR",
32 "ROR"
33};
34
Jack Veenstra166d76c2009-05-09 11:33:44 -070035static const char* cond_to_str(int cond) {
The Android Open Source Project52d4c302009-03-03 19:29:09 -080036 return cond_names[cond];
37}
38
39char *Arm::disasm(uint32_t addr, uint32_t insn, char *result)
40{
41 static char buf[80];
42 char *ptr;
43
44 ptr = result ? result : buf;
45 Opcode opcode = decode(insn);
46 switch (opcode) {
47 case OP_INVALID:
48 sprintf(ptr, "Invalid");
49 return ptr;
50 case OP_UNDEFINED:
51 sprintf(ptr, "Undefined");
52 return ptr;
53 case OP_ADC:
54 case OP_ADD:
55 case OP_AND:
56 case OP_BIC:
57 case OP_CMN:
58 case OP_CMP:
59 case OP_EOR:
60 case OP_MOV:
61 case OP_MVN:
62 case OP_ORR:
63 case OP_RSB:
64 case OP_RSC:
65 case OP_SBC:
66 case OP_SUB:
67 case OP_TEQ:
68 case OP_TST:
69 return disasm_alu(opcode, insn, ptr);
70 case OP_B:
71 case OP_BL:
72 return disasm_branch(addr, opcode, insn, ptr);
73 case OP_BKPT:
74 return disasm_bkpt(insn, ptr);
75 case OP_BLX:
76 // not supported yet
77 break;
78 case OP_BX:
79 return disasm_bx(insn, ptr);
80 case OP_CDP:
81 sprintf(ptr, "cdp");
82 return ptr;
83 case OP_CLZ:
84 return disasm_clz(insn, ptr);
85 case OP_LDC:
86 sprintf(ptr, "ldc");
87 return ptr;
88 case OP_LDM:
89 case OP_STM:
90 return disasm_memblock(opcode, insn, ptr);
91 case OP_LDR:
92 case OP_LDRB:
93 case OP_LDRBT:
94 case OP_LDRT:
95 case OP_STR:
96 case OP_STRB:
97 case OP_STRBT:
98 case OP_STRT:
99 return disasm_mem(insn, ptr);
100 case OP_LDRH:
101 case OP_LDRSB:
102 case OP_LDRSH:
103 case OP_STRH:
104 return disasm_memhalf(insn, ptr);
105 case OP_MCR:
106 case OP_MRC:
107 return disasm_mcr(opcode, insn, ptr);
108 case OP_MLA:
109 return disasm_mla(opcode, insn, ptr);
110 case OP_MRS:
111 return disasm_mrs(insn, ptr);
112 case OP_MSR:
113 return disasm_msr(insn, ptr);
114 case OP_MUL:
115 return disasm_mul(opcode, insn, ptr);
116 case OP_PLD:
117 return disasm_pld(insn, ptr);
118 case OP_STC:
119 sprintf(ptr, "stc");
120 return ptr;
121 case OP_SWI:
122 return disasm_swi(insn, ptr);
123 case OP_SWP:
124 case OP_SWPB:
125 return disasm_swp(opcode, insn, ptr);
126 case OP_UMLAL:
127 case OP_UMULL:
128 case OP_SMLAL:
129 case OP_SMULL:
130 return disasm_umlal(opcode, insn, ptr);
131 default:
132 sprintf(ptr, "Error");
133 return ptr;
134 }
135 return NULL;
136}
137
138char *Arm::disasm_alu(Opcode opcode, uint32_t insn, char *ptr)
139{
140 static const uint8_t kNoOperand1 = 1;
141 static const uint8_t kNoDest = 2;
142 static const uint8_t kNoSbit = 4;
143
144 char rn_str[20];
145 char rd_str[20];
146 uint8_t flags = 0;
147 uint8_t cond = (insn >> 28) & 0xf;
148 uint8_t is_immed = (insn >> 25) & 0x1;
149 uint8_t bit_s = (insn >> 20) & 1;
150 uint8_t rn = (insn >> 16) & 0xf;
151 uint8_t rd = (insn >> 12) & 0xf;
152 uint8_t immed = insn & 0xff;
153
154 const char *opname = opcode_names[opcode];
155 switch (opcode) {
156 case OP_CMN:
157 case OP_CMP:
158 case OP_TEQ:
159 case OP_TST:
160 flags = kNoDest | kNoSbit;
161 break;
162 case OP_MOV:
163 case OP_MVN:
164 flags = kNoOperand1;
165 break;
166 default:
167 break;
168 }
169
170 // The "mov" instruction ignores the first operand (rn).
171 rn_str[0] = 0;
172 if ((flags & kNoOperand1) == 0) {
173 sprintf(rn_str, "r%d, ", rn);
174 }
175
176 // The following instructions do not write the result register (rd):
177 // tst, teq, cmp, cmn.
178 rd_str[0] = 0;
179 if ((flags & kNoDest) == 0) {
180 sprintf(rd_str, "r%d, ", rd);
181 }
182
Jack Veenstra166d76c2009-05-09 11:33:44 -0700183 const char *sbit_str = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800184 if (bit_s && !(flags & kNoSbit))
185 sbit_str = "s";
186
187 if (is_immed) {
188 sprintf(ptr, "%s%s%s\t%s%s#%u ; 0x%x",
189 opname, cond_to_str(cond), sbit_str, rd_str, rn_str, immed, immed);
190 return ptr;
191 }
192
193 uint8_t shift_is_reg = (insn >> 4) & 1;
194 uint8_t rotate = (insn >> 8) & 0xf;
195 uint8_t rm = insn & 0xf;
196 uint8_t shift_type = (insn >> 5) & 0x3;
197 uint8_t rs = (insn >> 8) & 0xf;
198 uint8_t shift_amount = (insn >> 7) & 0x1f;
199 uint32_t rotated_val = immed;
200 uint8_t rotate2 = rotate << 1;
201 rotated_val = (rotated_val >> rotate2) | (rotated_val << (32 - rotate2));
202
203 if (!shift_is_reg && shift_type == 0 && shift_amount == 0) {
204 sprintf(ptr, "%s%s%s\t%s%sr%d",
205 opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm);
206 return ptr;
207 }
208
209 const char *shift_name = shift_names[shift_type];
210 if (shift_is_reg) {
211 sprintf(ptr, "%s%s%s\t%s%sr%d, %s r%d",
212 opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm,
213 shift_name, rs);
214 return ptr;
215 }
216 if (shift_amount == 0) {
217 if (shift_type == 3) {
218 sprintf(ptr, "%s%s%s\t%s%sr%d, RRX",
219 opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm);
220 return ptr;
221 }
222 shift_amount = 32;
223 }
224 sprintf(ptr, "%s%s%s\t%s%sr%d, %s #%u",
225 opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm,
226 shift_name, shift_amount);
227 return ptr;
228}
229
230char *Arm::disasm_branch(uint32_t addr, Opcode opcode, uint32_t insn, char *ptr)
231{
232 uint8_t cond = (insn >> 28) & 0xf;
233 uint32_t offset = insn & 0xffffff;
234 // Sign-extend the 24-bit offset
235 if ((offset >> 23) & 1)
236 offset |= 0xff000000;
237
238 // Pre-compute the left-shift and the prefetch offset
239 offset <<= 2;
240 offset += 8;
241 addr += offset;
242 const char *opname = opcode_names[opcode];
243 sprintf(ptr, "%s%s\t0x%x", opname, cond_to_str(cond), addr);
244 return ptr;
245}
246
247char *Arm::disasm_bx(uint32_t insn, char *ptr)
248{
249 uint8_t cond = (insn >> 28) & 0xf;
250 uint8_t rn = insn & 0xf;
251 sprintf(ptr, "bx%s\tr%d", cond_to_str(cond), rn);
252 return ptr;
253}
254
255char *Arm::disasm_bkpt(uint32_t insn, char *ptr)
256{
257 uint32_t immed = (((insn >> 8) & 0xfff) << 4) | (insn & 0xf);
258 sprintf(ptr, "bkpt\t#%d", immed);
259 return ptr;
260}
261
262char *Arm::disasm_clz(uint32_t insn, char *ptr)
263{
264 uint8_t cond = (insn >> 28) & 0xf;
265 uint8_t rd = (insn >> 12) & 0xf;
266 uint8_t rm = insn & 0xf;
267 sprintf(ptr, "clz%s\tr%d, r%d", cond_to_str(cond), rd, rm);
268 return ptr;
269}
270
271char *Arm::disasm_memblock(Opcode opcode, uint32_t insn, char *ptr)
272{
273 char tmp_reg[10], tmp_list[80];
274
275 uint8_t cond = (insn >> 28) & 0xf;
276 uint8_t write_back = (insn >> 21) & 0x1;
277 uint8_t bit_s = (insn >> 22) & 0x1;
278 uint8_t is_up = (insn >> 23) & 0x1;
279 uint8_t is_pre = (insn >> 24) & 0x1;
280 uint8_t rn = (insn >> 16) & 0xf;
281 uint16_t reg_list = insn & 0xffff;
282
283 const char *opname = opcode_names[opcode];
284
Jack Veenstra166d76c2009-05-09 11:33:44 -0700285 const char *bang = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800286 if (write_back)
287 bang = "!";
288
Jack Veenstra166d76c2009-05-09 11:33:44 -0700289 const char *carret = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800290 if (bit_s)
291 carret = "^";
292
Jack Veenstra166d76c2009-05-09 11:33:44 -0700293 const char *comma = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800294 tmp_list[0] = 0;
295 for (int ii = 0; ii < 16; ++ii) {
296 if (reg_list & (1 << ii)) {
297 sprintf(tmp_reg, "%sr%d", comma, ii);
298 strcat(tmp_list, tmp_reg);
299 comma = ",";
300 }
301 }
302
Jack Veenstra166d76c2009-05-09 11:33:44 -0700303 const char *addr_mode = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800304 if (is_pre) {
305 if (is_up) {
306 addr_mode = "ib";
307 } else {
308 addr_mode = "db";
309 }
310 } else {
311 if (is_up) {
312 addr_mode = "ia";
313 } else {
314 addr_mode = "da";
315 }
316 }
317
318 sprintf(ptr, "%s%s%s\tr%d%s, {%s}%s",
319 opname, cond_to_str(cond), addr_mode, rn, bang, tmp_list, carret);
320 return ptr;
321}
322
323char *Arm::disasm_mem(uint32_t insn, char *ptr)
324{
325 uint8_t cond = (insn >> 28) & 0xf;
326 uint8_t is_reg = (insn >> 25) & 0x1;
327 uint8_t is_load = (insn >> 20) & 0x1;
328 uint8_t write_back = (insn >> 21) & 0x1;
329 uint8_t is_byte = (insn >> 22) & 0x1;
330 uint8_t is_up = (insn >> 23) & 0x1;
331 uint8_t is_pre = (insn >> 24) & 0x1;
332 uint8_t rn = (insn >> 16) & 0xf;
333 uint8_t rd = (insn >> 12) & 0xf;
334 uint16_t offset = insn & 0xfff;
335
Jack Veenstra166d76c2009-05-09 11:33:44 -0700336 const char *opname = "ldr";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800337 if (!is_load)
338 opname = "str";
339
Jack Veenstra166d76c2009-05-09 11:33:44 -0700340 const char *bang = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800341 if (write_back)
342 bang = "!";
343
Jack Veenstra166d76c2009-05-09 11:33:44 -0700344 const char *minus = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800345 if (is_up == 0)
346 minus = "-";
347
Jack Veenstra166d76c2009-05-09 11:33:44 -0700348 const char *byte = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800349 if (is_byte)
350 byte = "b";
351
352 if (is_reg == 0) {
353 if (is_pre) {
354 if (offset == 0) {
355 sprintf(ptr, "%s%s%s\tr%d, [r%d]",
356 opname, cond_to_str(cond), byte, rd, rn);
357 } else {
358 sprintf(ptr, "%s%s%s\tr%d, [r%d, #%s%u]%s",
359 opname, cond_to_str(cond), byte, rd, rn, minus, offset, bang);
360 }
361 } else {
Jack Veenstra166d76c2009-05-09 11:33:44 -0700362 const char *transfer = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800363 if (write_back)
364 transfer = "t";
365 sprintf(ptr, "%s%s%s%s\tr%d, [r%d], #%s%u",
366 opname, cond_to_str(cond), byte, transfer, rd, rn, minus, offset);
367 }
368 return ptr;
369 }
370
371 uint8_t rm = insn & 0xf;
372 uint8_t shift_type = (insn >> 5) & 0x3;
373 uint8_t shift_amount = (insn >> 7) & 0x1f;
374
375 const char *shift_name = shift_names[shift_type];
376
377 if (is_pre) {
378 if (shift_amount == 0) {
379 if (shift_type == 0) {
380 sprintf(ptr, "%s%s%s\tr%d, [r%d, %sr%d]%s",
381 opname, cond_to_str(cond), byte, rd, rn, minus, rm, bang);
382 return ptr;
383 }
384 if (shift_type == 3) {
385 sprintf(ptr, "%s%s%s\tr%d, [r%d, %sr%d, RRX]%s",
386 opname, cond_to_str(cond), byte, rd, rn, minus, rm, bang);
387 return ptr;
388 }
389 shift_amount = 32;
390 }
391 sprintf(ptr, "%s%s%s\tr%d, [r%d, %sr%d, %s #%u]%s",
392 opname, cond_to_str(cond), byte, rd, rn, minus, rm,
393 shift_name, shift_amount, bang);
394 return ptr;
395 }
396
Jack Veenstra166d76c2009-05-09 11:33:44 -0700397 const char *transfer = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800398 if (write_back)
399 transfer = "t";
400
401 if (shift_amount == 0) {
402 if (shift_type == 0) {
403 sprintf(ptr, "%s%s%s%s\tr%d, [r%d], %sr%d",
404 opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm);
405 return ptr;
406 }
407 if (shift_type == 3) {
408 sprintf(ptr, "%s%s%s%s\tr%d, [r%d], %sr%d, RRX",
409 opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm);
410 return ptr;
411 }
412 shift_amount = 32;
413 }
414
415 sprintf(ptr, "%s%s%s%s\tr%d, [r%d], %sr%d, %s #%u",
416 opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm,
417 shift_name, shift_amount);
418 return ptr;
419}
420
421char *Arm::disasm_memhalf(uint32_t insn, char *ptr)
422{
423 uint8_t cond = (insn >> 28) & 0xf;
424 uint8_t is_load = (insn >> 20) & 0x1;
425 uint8_t write_back = (insn >> 21) & 0x1;
426 uint8_t is_immed = (insn >> 22) & 0x1;
427 uint8_t is_up = (insn >> 23) & 0x1;
428 uint8_t is_pre = (insn >> 24) & 0x1;
429 uint8_t rn = (insn >> 16) & 0xf;
430 uint8_t rd = (insn >> 12) & 0xf;
431 uint8_t bits_65 = (insn >> 5) & 0x3;
432 uint8_t rm = insn & 0xf;
433 uint8_t offset = (((insn >> 8) & 0xf) << 4) | (insn & 0xf);
434
Jack Veenstra166d76c2009-05-09 11:33:44 -0700435 const char *opname = "ldr";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800436 if (is_load == 0)
437 opname = "str";
438
Jack Veenstra166d76c2009-05-09 11:33:44 -0700439 const char *width = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800440 if (bits_65 == 1)
441 width = "h";
442 else if (bits_65 == 2)
443 width = "sb";
444 else
445 width = "sh";
446
Jack Veenstra166d76c2009-05-09 11:33:44 -0700447 const char *bang = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800448 if (write_back)
449 bang = "!";
Jack Veenstra166d76c2009-05-09 11:33:44 -0700450 const char *minus = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800451 if (is_up == 0)
452 minus = "-";
453
454 if (is_immed) {
455 if (is_pre) {
456 if (offset == 0) {
457 sprintf(ptr, "%s%sh\tr%d, [r%d]", opname, cond_to_str(cond), rd, rn);
458 } else {
459 sprintf(ptr, "%s%sh\tr%d, [r%d, #%s%u]%s",
460 opname, cond_to_str(cond), rd, rn, minus, offset, bang);
461 }
462 } else {
463 sprintf(ptr, "%s%sh\tr%d, [r%d], #%s%u",
464 opname, cond_to_str(cond), rd, rn, minus, offset);
465 }
466 return ptr;
467 }
468
469 if (is_pre) {
470 sprintf(ptr, "%s%sh\tr%d, [r%d, %sr%d]%s",
471 opname, cond_to_str(cond), rd, rn, minus, rm, bang);
472 } else {
473 sprintf(ptr, "%s%sh\tr%d, [r%d], %sr%d",
474 opname, cond_to_str(cond), rd, rn, minus, rm);
475 }
476 return ptr;
477}
478
479char *Arm::disasm_mcr(Opcode opcode, uint32_t insn, char *ptr)
480{
481 uint8_t cond = (insn >> 28) & 0xf;
482 uint8_t crn = (insn >> 16) & 0xf;
483 uint8_t crd = (insn >> 12) & 0xf;
484 uint8_t cpnum = (insn >> 8) & 0xf;
485 uint8_t opcode2 = (insn >> 5) & 0x7;
486 uint8_t crm = insn & 0xf;
487
488 const char *opname = opcode_names[opcode];
489 sprintf(ptr, "%s%s\t%d, 0, r%d, cr%d, cr%d, {%d}",
490 opname, cond_to_str(cond), cpnum, crd, crn, crm, opcode2);
491 return ptr;
492}
493
494char *Arm::disasm_mla(Opcode opcode, uint32_t insn, char *ptr)
495{
496 uint8_t cond = (insn >> 28) & 0xf;
497 uint8_t rd = (insn >> 16) & 0xf;
498 uint8_t rn = (insn >> 12) & 0xf;
499 uint8_t rs = (insn >> 8) & 0xf;
500 uint8_t rm = insn & 0xf;
501 uint8_t bit_s = (insn >> 20) & 1;
502
503 const char *opname = opcode_names[opcode];
504 sprintf(ptr, "%s%s%s\tr%d, r%d, r%d, r%d",
505 opname, cond_to_str(cond), bit_s ? "s" : "", rd, rm, rs, rn);
506 return ptr;
507}
508
509char *Arm::disasm_umlal(Opcode opcode, uint32_t insn, char *ptr)
510{
511 uint8_t cond = (insn >> 28) & 0xf;
512 uint8_t rdhi = (insn >> 16) & 0xf;
513 uint8_t rdlo = (insn >> 12) & 0xf;
514 uint8_t rs = (insn >> 8) & 0xf;
515 uint8_t rm = insn & 0xf;
516 uint8_t bit_s = (insn >> 20) & 1;
517
518 const char *opname = opcode_names[opcode];
519 sprintf(ptr, "%s%s%s\tr%d, r%d, r%d, r%d",
520 opname, cond_to_str(cond), bit_s ? "s" : "", rdlo, rdhi, rm, rs);
521 return ptr;
522}
523
524char *Arm::disasm_mul(Opcode opcode, uint32_t insn, char *ptr)
525{
526 uint8_t cond = (insn >> 28) & 0xf;
527 uint8_t rd = (insn >> 16) & 0xf;
528 uint8_t rs = (insn >> 8) & 0xf;
529 uint8_t rm = insn & 0xf;
530 uint8_t bit_s = (insn >> 20) & 1;
531
532 const char *opname = opcode_names[opcode];
533 sprintf(ptr, "%s%s%s\tr%d, r%d, r%d",
534 opname, cond_to_str(cond), bit_s ? "s" : "", rd, rm, rs);
535 return ptr;
536}
537
538char *Arm::disasm_mrs(uint32_t insn, char *ptr)
539{
540 uint8_t cond = (insn >> 28) & 0xf;
541 uint8_t rd = (insn >> 12) & 0xf;
542 uint8_t ps = (insn >> 22) & 1;
543
544 sprintf(ptr, "mrs%s\tr%d, %s", cond_to_str(cond), rd, ps ? "spsr" : "cpsr");
545 return ptr;
546}
547
548char *Arm::disasm_msr(uint32_t insn, char *ptr)
549{
550 char flags[8];
551 int flag_index = 0;
552 uint8_t cond = (insn >> 28) & 0xf;
553 uint8_t is_immed = (insn >> 25) & 0x1;
554 uint8_t pd = (insn >> 22) & 1;
555 uint8_t mask = (insn >> 16) & 0xf;
556
557 if (mask & 1)
558 flags[flag_index++] = 'c';
559 if (mask & 2)
560 flags[flag_index++] = 'x';
561 if (mask & 4)
562 flags[flag_index++] = 's';
563 if (mask & 8)
564 flags[flag_index++] = 'f';
565 flags[flag_index] = 0;
566
567 if (is_immed) {
568 uint32_t immed = insn & 0xff;
569 uint8_t rotate = (insn >> 8) & 0xf;
570 uint8_t rotate2 = rotate << 1;
571 uint32_t rotated_val = (immed >> rotate2) | (immed << (32 - rotate2));
572 sprintf(ptr, "msr%s\t%s_%s, #0x%x",
573 cond_to_str(cond), pd ? "spsr" : "cpsr", flags, rotated_val);
574 return ptr;
575 }
576
577 uint8_t rm = insn & 0xf;
578
579 sprintf(ptr, "msr%s\t%s_%s, r%d",
580 cond_to_str(cond), pd ? "spsr" : "cpsr", flags, rm);
581 return ptr;
582}
583
584char *Arm::disasm_pld(uint32_t insn, char *ptr)
585{
586 uint8_t is_reg = (insn >> 25) & 0x1;
587 uint8_t is_up = (insn >> 23) & 0x1;
588 uint8_t rn = (insn >> 16) & 0xf;
589
Jack Veenstra166d76c2009-05-09 11:33:44 -0700590 const char *minus = "";
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800591 if (is_up == 0)
592 minus = "-";
593
594 if (is_reg) {
595 uint8_t rm = insn & 0xf;
596 sprintf(ptr, "pld\t[r%d, %sr%d]", rn, minus, rm);
597 return ptr;
598 }
599
600 uint16_t offset = insn & 0xfff;
601 if (offset == 0) {
602 sprintf(ptr, "pld\t[r%d]", rn);
603 } else {
604 sprintf(ptr, "pld\t[r%d, #%s%u]", rn, minus, offset);
605 }
606 return ptr;
607}
608
609char *Arm::disasm_swi(uint32_t insn, char *ptr)
610{
611 uint8_t cond = (insn >> 28) & 0xf;
612 uint32_t sysnum = insn & 0x00ffffff;
613
614 sprintf(ptr, "swi%s 0x%x", cond_to_str(cond), sysnum);
615 return ptr;
616}
617
618char *Arm::disasm_swp(Opcode opcode, uint32_t insn, char *ptr)
619{
620 uint8_t cond = (insn >> 28) & 0xf;
621 uint8_t rn = (insn >> 16) & 0xf;
622 uint8_t rd = (insn >> 12) & 0xf;
623 uint8_t rm = insn & 0xf;
624
625 const char *opname = opcode_names[opcode];
626 sprintf(ptr, "%s%s\tr%d, r%d, [r%d]", opname, cond_to_str(cond), rd, rm, rn);
627 return ptr;
628}
629
630Opcode Arm::decode(uint32_t insn) {
631 uint32_t bits27_26 = (insn >> 26) & 0x3;
632 switch (bits27_26) {
633 case 0x0:
634 return decode00(insn);
635 case 0x1:
636 return decode01(insn);
637 case 0x2:
638 return decode10(insn);
639 case 0x3:
640 return decode11(insn);
641 }
642 return OP_INVALID;
643}
644
645Opcode Arm::decode00(uint32_t insn) {
646 uint8_t bit25 = (insn >> 25) & 0x1;
647 uint8_t bit4 = (insn >> 4) & 0x1;
648 if (bit25 == 0 && bit4 == 1) {
649 if ((insn & 0x0ffffff0) == 0x012fff10) {
650 // Bx instruction
651 return OP_BX;
652 }
653 if ((insn & 0x0ff000f0) == 0x01600010) {
654 // Clz instruction
655 return OP_CLZ;
656 }
657 if ((insn & 0xfff000f0) == 0xe1200070) {
658 // Bkpt instruction
659 return OP_BKPT;
660 }
661 uint32_t bits7_4 = (insn >> 4) & 0xf;
662 if (bits7_4 == 0x9) {
663 if ((insn & 0x0ff00ff0) == 0x01000090) {
664 // Swp instruction
665 uint8_t bit22 = (insn >> 22) & 0x1;
666 if (bit22)
667 return OP_SWPB;
668 return OP_SWP;
669 }
670 // One of the multiply instructions
671 return decode_mul(insn);
672 }
673
674 uint8_t bit7 = (insn >> 7) & 0x1;
675 if (bit7 == 1) {
676 // One of the load/store halfword/byte instructions
677 return decode_ldrh(insn);
678 }
679 }
680
681 // One of the data processing instructions
682 return decode_alu(insn);
683}
684
685Opcode Arm::decode01(uint32_t insn) {
686 uint8_t is_reg = (insn >> 25) & 0x1;
687 uint8_t bit4 = (insn >> 4) & 0x1;
688 if (is_reg == 1 && bit4 == 1)
689 return OP_UNDEFINED;
690 uint8_t is_load = (insn >> 20) & 0x1;
691 uint8_t is_byte = (insn >> 22) & 0x1;
692 if ((insn & 0xfd70f000) == 0xf550f000) {
693 // Pre-load
694 return OP_PLD;
695 }
696 if (is_load) {
697 if (is_byte) {
698 // Load byte
699 return OP_LDRB;
700 }
701 // Load word
702 return OP_LDR;
703 }
704 if (is_byte) {
705 // Store byte
706 return OP_STRB;
707 }
708 // Store word
709 return OP_STR;
710}
711
712Opcode Arm::decode10(uint32_t insn) {
713 uint8_t bit25 = (insn >> 25) & 0x1;
714 if (bit25 == 0) {
715 // LDM/STM
716 uint8_t is_load = (insn >> 20) & 0x1;
717 if (is_load)
718 return OP_LDM;
719 return OP_STM;
720 }
721 // Branch or Branch with link
722 uint8_t is_link = (insn >> 24) & 1;
723 uint32_t offset = insn & 0xffffff;
724
725 // Sign-extend the 24-bit offset
726 if ((offset >> 23) & 1)
727 offset |= 0xff000000;
728
729 // Pre-compute the left-shift and the prefetch offset
730 offset <<= 2;
731 offset += 8;
732 if (is_link == 0)
733 return OP_B;
734 return OP_BL;
735}
736
737Opcode Arm::decode11(uint32_t insn) {
738 uint8_t bit25 = (insn >> 25) & 0x1;
739 if (bit25 == 0) {
740 // LDC, SDC
741 uint8_t is_load = (insn >> 20) & 0x1;
742 if (is_load) {
743 // LDC
744 return OP_LDC;
745 }
746 // STC
747 return OP_STC;
748 }
749
750 uint8_t bit24 = (insn >> 24) & 0x1;
751 if (bit24 == 0x1) {
752 // SWI
753 return OP_SWI;
754 }
755
756 uint8_t bit4 = (insn >> 4) & 0x1;
757 uint8_t cpnum = (insn >> 8) & 0xf;
758
759 if (cpnum == 15) {
760 // Special case for coprocessor 15
761 uint8_t opcode = (insn >> 21) & 0x7;
762 if (bit4 == 0 || opcode != 0) {
763 // This is an unexpected bit pattern. Create an undefined
764 // instruction in case this is ever executed.
765 return OP_UNDEFINED;
766 }
767
768 // MRC, MCR
769 uint8_t is_mrc = (insn >> 20) & 0x1;
770 if (is_mrc)
771 return OP_MRC;
772 return OP_MCR;
773 }
774
775 if (bit4 == 0) {
776 // CDP
777 return OP_CDP;
778 }
779 // MRC, MCR
780 uint8_t is_mrc = (insn >> 20) & 0x1;
781 if (is_mrc)
782 return OP_MRC;
783 return OP_MCR;
784}
785
786Opcode Arm::decode_mul(uint32_t insn) {
787 uint8_t bit24 = (insn >> 24) & 0x1;
788 if (bit24 != 0) {
789 // This is an unexpected bit pattern. Create an undefined
790 // instruction in case this is ever executed.
791 return OP_UNDEFINED;
792 }
793 uint8_t bit23 = (insn >> 23) & 0x1;
794 uint8_t bit22_U = (insn >> 22) & 0x1;
795 uint8_t bit21_A = (insn >> 21) & 0x1;
796 if (bit23 == 0) {
797 // 32-bit multiply
798 if (bit22_U != 0) {
799 // This is an unexpected bit pattern. Create an undefined
800 // instruction in case this is ever executed.
801 return OP_UNDEFINED;
802 }
803 if (bit21_A == 0)
804 return OP_MUL;
805 return OP_MLA;
806 }
807 // 64-bit multiply
808 if (bit22_U == 0) {
809 // Unsigned multiply long
810 if (bit21_A == 0)
811 return OP_UMULL;
812 return OP_UMLAL;
813 }
814 // Signed multiply long
815 if (bit21_A == 0)
816 return OP_SMULL;
817 return OP_SMLAL;
818}
819
820Opcode Arm::decode_ldrh(uint32_t insn) {
821 uint8_t is_load = (insn >> 20) & 0x1;
822 uint8_t bits_65 = (insn >> 5) & 0x3;
823 if (is_load) {
824 if (bits_65 == 0x1) {
825 // Load unsigned halfword
826 return OP_LDRH;
827 } else if (bits_65 == 0x2) {
828 // Load signed byte
829 return OP_LDRSB;
830 }
831 // Signed halfword
832 if (bits_65 != 0x3) {
833 // This is an unexpected bit pattern. Create an undefined
834 // instruction in case this is ever executed.
835 return OP_UNDEFINED;
836 }
837 // Load signed halfword
838 return OP_LDRSH;
839 }
840 // Store halfword
841 if (bits_65 != 0x1) {
842 // This is an unexpected bit pattern. Create an undefined
843 // instruction in case this is ever executed.
844 return OP_UNDEFINED;
845 }
846 // Store halfword
847 return OP_STRH;
848}
849
850Opcode Arm::decode_alu(uint32_t insn) {
851 uint8_t is_immed = (insn >> 25) & 0x1;
852 uint8_t opcode = (insn >> 21) & 0xf;
853 uint8_t bit_s = (insn >> 20) & 1;
854 uint8_t shift_is_reg = (insn >> 4) & 1;
855 uint8_t bit7 = (insn >> 7) & 1;
856 if (!is_immed && shift_is_reg && (bit7 != 0)) {
857 // This is an unexpected bit pattern. Create an undefined
858 // instruction in case this is ever executed.
859 return OP_UNDEFINED;
860 }
861 switch (opcode) {
862 case 0x0:
863 return OP_AND;
864 case 0x1:
865 return OP_EOR;
866 case 0x2:
867 return OP_SUB;
868 case 0x3:
869 return OP_RSB;
870 case 0x4:
871 return OP_ADD;
872 case 0x5:
873 return OP_ADC;
874 case 0x6:
875 return OP_SBC;
876 case 0x7:
877 return OP_RSC;
878 case 0x8:
879 if (bit_s)
880 return OP_TST;
881 return OP_MRS;
882 case 0x9:
883 if (bit_s)
884 return OP_TEQ;
885 return OP_MSR;
886 case 0xa:
887 if (bit_s)
888 return OP_CMP;
889 return OP_MRS;
890 case 0xb:
891 if (bit_s)
892 return OP_CMN;
893 return OP_MSR;
894 case 0xc:
895 return OP_ORR;
896 case 0xd:
897 return OP_MOV;
898 case 0xe:
899 return OP_BIC;
900 case 0xf:
901 return OP_MVN;
902 }
903 // Unreachable
904 return OP_INVALID;
905}