Commit | Line | Data |
---|---|---|
252b5132 | 1 | /* Instruction printing code for the ARM |
dd92f639 | 2 | Copyright (C) 1994, 95, 96, 97, 98, 1999 Free Software Foundation, Inc. |
252b5132 RH |
3 | Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) |
4 | Modification by James G. Smith (jsmith@cygnus.co.uk) | |
5 | ||
6 | This file is part of libopcodes. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
10 | Software Foundation; either version 2 of the License, or (at your option) | |
11 | any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, but WITHOUT | |
14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
16 | more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
21 | ||
22 | #include "dis-asm.h" | |
23 | #define DEFINE_TABLE | |
24 | #include "arm-opc.h" | |
25 | #include "coff/internal.h" | |
26 | #include "libcoff.h" | |
27 | #include "opintl.h" | |
28 | ||
29 | /* FIXME: This shouldn't be done here */ | |
30 | #include "elf-bfd.h" | |
31 | #include "elf/internal.h" | |
32 | #include "elf/arm.h" | |
33 | ||
5876e06d | 34 | static char * arm_conditional[] = |
252b5132 RH |
35 | {"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", |
36 | "hi", "ls", "ge", "lt", "gt", "le", "", "nv"}; | |
37 | ||
5876e06d | 38 | static char * arm_regnames_raw[] = |
252b5132 | 39 | {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
dd92f639 NC |
40 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}; |
41 | ||
5876e06d | 42 | static char * arm_regnames_standard[] = |
dd92f639 NC |
43 | {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
44 | "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc"}; | |
45 | ||
5876e06d | 46 | static char * arm_regnames_apcs[] = |
dd92f639 NC |
47 | {"a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", |
48 | "v5", "v6", "sl", "fp", "ip", "sp", "lr", "pc"}; | |
49 | ||
50 | /* Choose which register name set to use. */ | |
5876e06d | 51 | static char ** arm_regnames = arm_regnames_standard; |
252b5132 | 52 | |
5876e06d | 53 | static char * arm_fp_const[] = |
252b5132 RH |
54 | {"0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0"}; |
55 | ||
5876e06d | 56 | static char * arm_shift[] = |
252b5132 RH |
57 | {"lsl", "lsr", "asr", "ror"}; |
58 | ||
5876e06d NC |
59 | static int print_insn_arm |
60 | PARAMS ((bfd_vma, struct disassemble_info *, long)); | |
252b5132 RH |
61 | |
62 | static void | |
63 | arm_decode_shift (given, func, stream) | |
64 | long given; | |
65 | fprintf_ftype func; | |
5876e06d | 66 | void * stream; |
252b5132 RH |
67 | { |
68 | func (stream, "%s", arm_regnames[given & 0xf]); | |
5876e06d | 69 | |
252b5132 RH |
70 | if ((given & 0xff0) != 0) |
71 | { | |
72 | if ((given & 0x10) == 0) | |
73 | { | |
74 | int amount = (given & 0xf80) >> 7; | |
75 | int shift = (given & 0x60) >> 5; | |
5876e06d | 76 | |
252b5132 RH |
77 | if (amount == 0) |
78 | { | |
79 | if (shift == 3) | |
80 | { | |
81 | func (stream, ", rrx"); | |
82 | return; | |
83 | } | |
5876e06d | 84 | |
252b5132 RH |
85 | amount = 32; |
86 | } | |
5876e06d | 87 | |
252b5132 RH |
88 | func (stream, ", %s #%d", arm_shift[shift], amount); |
89 | } | |
90 | else | |
91 | func (stream, ", %s %s", arm_shift[(given & 0x60) >> 5], | |
92 | arm_regnames[(given & 0xf00) >> 8]); | |
93 | } | |
94 | } | |
95 | ||
96 | /* Print one instruction from PC on INFO->STREAM. | |
97 | Return the size of the instruction (always 4 on ARM). */ | |
98 | ||
99 | static int | |
100 | print_insn_arm (pc, info, given) | |
5876e06d NC |
101 | bfd_vma pc; |
102 | struct disassemble_info * info; | |
103 | long given; | |
252b5132 RH |
104 | { |
105 | struct arm_opcode * insn; | |
106 | void * stream = info->stream; | |
107 | fprintf_ftype func = info->fprintf_func; | |
108 | ||
109 | for (insn = arm_opcodes; insn->assembler; insn++) | |
110 | { | |
111 | if ((given & insn->mask) == insn->value) | |
112 | { | |
113 | char * c; | |
114 | ||
115 | for (c = insn->assembler; *c; c++) | |
116 | { | |
117 | if (*c == '%') | |
118 | { | |
119 | switch (*++c) | |
120 | { | |
121 | case '%': | |
122 | func (stream, "%%"); | |
123 | break; | |
124 | ||
125 | case 'a': | |
126 | if (((given & 0x000f0000) == 0x000f0000) | |
127 | && ((given & 0x02000000) == 0)) | |
128 | { | |
129 | int offset = given & 0xfff; | |
130 | ||
131 | func (stream, "[pc"); | |
132 | ||
133 | if (given & 0x01000000) | |
134 | { | |
135 | if ((given & 0x00800000) == 0) | |
136 | offset = - offset; | |
137 | ||
138 | /* pre-indexed */ | |
139 | func (stream, ", #%x]", offset); | |
140 | ||
141 | offset += pc + 8; | |
142 | ||
143 | /* Cope with the possibility of write-back being used. | |
144 | Probably a very dangerous thing for the programmer | |
145 | to do, but who are we to argue ? */ | |
146 | if (given & 0x00200000) | |
147 | func (stream, "!"); | |
148 | } | |
149 | else | |
150 | { | |
151 | /* post indexed */ | |
152 | func (stream, "], #%x", offset); | |
153 | ||
154 | offset = pc + 8; /* ie ignore the offset */ | |
155 | } | |
156 | ||
157 | func (stream, "\t; "); | |
158 | info->print_address_func (offset, info); | |
159 | } | |
160 | else | |
161 | { | |
162 | func (stream, "[%s", | |
163 | arm_regnames[(given >> 16) & 0xf]); | |
164 | if ((given & 0x01000000) != 0) | |
165 | { | |
166 | if ((given & 0x02000000) == 0) | |
167 | { | |
168 | int offset = given & 0xfff; | |
169 | if (offset) | |
170 | func (stream, ", %s#%d", | |
171 | (((given & 0x00800000) == 0) | |
172 | ? "-" : ""), offset); | |
173 | } | |
174 | else | |
175 | { | |
176 | func (stream, ", %s", | |
177 | (((given & 0x00800000) == 0) | |
178 | ? "-" : "")); | |
179 | arm_decode_shift (given, func, stream); | |
180 | } | |
181 | ||
182 | func (stream, "]%s", | |
183 | ((given & 0x00200000) != 0) ? "!" : ""); | |
184 | } | |
185 | else | |
186 | { | |
187 | if ((given & 0x02000000) == 0) | |
188 | { | |
189 | int offset = given & 0xfff; | |
190 | if (offset) | |
191 | func (stream, "], %s#%d", | |
192 | (((given & 0x00800000) == 0) | |
193 | ? "-" : ""), offset); | |
194 | else | |
195 | func (stream, "]"); | |
196 | } | |
197 | else | |
198 | { | |
199 | func (stream, "], %s", | |
200 | (((given & 0x00800000) == 0) | |
201 | ? "-" : "")); | |
202 | arm_decode_shift (given, func, stream); | |
203 | } | |
204 | } | |
205 | } | |
206 | break; | |
207 | ||
208 | case 's': | |
209 | if ((given & 0x004f0000) == 0x004f0000) | |
210 | { | |
211 | /* PC relative with immediate offset */ | |
212 | int offset = ((given & 0xf00) >> 4) | (given & 0xf); | |
886796f9 | 213 | |
252b5132 RH |
214 | if ((given & 0x00800000) == 0) |
215 | offset = -offset; | |
886796f9 NC |
216 | |
217 | func (stream, "[pc, #%x]\t; ", offset); | |
218 | ||
252b5132 RH |
219 | (*info->print_address_func) |
220 | (offset + pc + 8, info); | |
221 | } | |
222 | else | |
223 | { | |
224 | func (stream, "[%s", | |
225 | arm_regnames[(given >> 16) & 0xf]); | |
226 | if ((given & 0x01000000) != 0) | |
227 | { | |
228 | /* pre-indexed */ | |
229 | if ((given & 0x00400000) == 0x00400000) | |
230 | { | |
231 | /* immediate */ | |
232 | int offset = ((given & 0xf00) >> 4) | (given & 0xf); | |
233 | if (offset) | |
234 | func (stream, ", %s#%d", | |
235 | (((given & 0x00800000) == 0) | |
236 | ? "-" : ""), offset); | |
237 | } | |
238 | else | |
239 | { | |
240 | /* register */ | |
241 | func (stream, ", %s%s", | |
242 | (((given & 0x00800000) == 0) | |
243 | ? "-" : ""), | |
244 | arm_regnames[given & 0xf]); | |
245 | } | |
246 | ||
247 | func (stream, "]%s", | |
248 | ((given & 0x00200000) != 0) ? "!" : ""); | |
249 | } | |
250 | else | |
251 | { | |
252 | /* post-indexed */ | |
253 | if ((given & 0x00400000) == 0x00400000) | |
254 | { | |
255 | /* immediate */ | |
256 | int offset = ((given & 0xf00) >> 4) | (given & 0xf); | |
257 | if (offset) | |
258 | func (stream, "], %s#%d", | |
259 | (((given & 0x00800000) == 0) | |
260 | ? "-" : ""), offset); | |
261 | else | |
262 | func (stream, "]"); | |
263 | } | |
264 | else | |
265 | { | |
266 | /* register */ | |
267 | func (stream, "], %s%s", | |
268 | (((given & 0x00800000) == 0) | |
269 | ? "-" : ""), | |
270 | arm_regnames[given & 0xf]); | |
271 | } | |
272 | } | |
273 | } | |
274 | break; | |
275 | ||
276 | case 'b': | |
277 | (*info->print_address_func) | |
278 | (BDISP (given) * 4 + pc + 8, info); | |
279 | break; | |
280 | ||
281 | case 'c': | |
282 | func (stream, "%s", | |
283 | arm_conditional [(given >> 28) & 0xf]); | |
284 | break; | |
285 | ||
286 | case 'm': | |
287 | { | |
288 | int started = 0; | |
289 | int reg; | |
290 | ||
291 | func (stream, "{"); | |
292 | for (reg = 0; reg < 16; reg++) | |
293 | if ((given & (1 << reg)) != 0) | |
294 | { | |
295 | if (started) | |
296 | func (stream, ", "); | |
297 | started = 1; | |
298 | func (stream, "%s", arm_regnames[reg]); | |
299 | } | |
300 | func (stream, "}"); | |
301 | } | |
302 | break; | |
303 | ||
304 | case 'o': | |
305 | if ((given & 0x02000000) != 0) | |
306 | { | |
307 | int rotate = (given & 0xf00) >> 7; | |
308 | int immed = (given & 0xff); | |
9f20bbfd NC |
309 | immed = (((immed << (32 - rotate)) |
310 | | (immed >> rotate)) & 0xffffffff); | |
311 | func (stream, "#%d\t; 0x%x", immed, immed); | |
252b5132 RH |
312 | } |
313 | else | |
314 | arm_decode_shift (given, func, stream); | |
315 | break; | |
316 | ||
317 | case 'p': | |
318 | if ((given & 0x0000f000) == 0x0000f000) | |
319 | func (stream, "p"); | |
320 | break; | |
321 | ||
322 | case 't': | |
323 | if ((given & 0x01200000) == 0x00200000) | |
324 | func (stream, "t"); | |
325 | break; | |
326 | ||
327 | case 'h': | |
328 | if ((given & 0x00000020) == 0x00000020) | |
329 | func (stream, "h"); | |
330 | else | |
331 | func (stream, "b"); | |
332 | break; | |
333 | ||
334 | case 'A': | |
335 | func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]); | |
336 | if ((given & 0x01000000) != 0) | |
337 | { | |
338 | int offset = given & 0xff; | |
339 | if (offset) | |
340 | func (stream, ", %s#%d]%s", | |
341 | ((given & 0x00800000) == 0 ? "-" : ""), | |
342 | offset * 4, | |
343 | ((given & 0x00200000) != 0 ? "!" : "")); | |
344 | else | |
345 | func (stream, "]"); | |
346 | } | |
347 | else | |
348 | { | |
349 | int offset = given & 0xff; | |
350 | if (offset) | |
351 | func (stream, "], %s#%d", | |
352 | ((given & 0x00800000) == 0 ? "-" : ""), | |
353 | offset * 4); | |
354 | else | |
355 | func (stream, "]"); | |
356 | } | |
357 | break; | |
358 | ||
359 | case 'C': | |
360 | switch (given & 0x00090000) | |
361 | { | |
362 | default: | |
363 | func (stream, "_???"); | |
364 | break; | |
365 | case 0x90000: | |
366 | func (stream, "_all"); | |
367 | break; | |
368 | case 0x10000: | |
369 | func (stream, "_ctl"); | |
370 | break; | |
371 | case 0x80000: | |
372 | func (stream, "_flg"); | |
373 | break; | |
374 | } | |
375 | break; | |
376 | ||
377 | case 'F': | |
378 | switch (given & 0x00408000) | |
379 | { | |
380 | case 0: | |
381 | func (stream, "4"); | |
382 | break; | |
383 | case 0x8000: | |
384 | func (stream, "1"); | |
385 | break; | |
386 | case 0x00400000: | |
387 | func (stream, "2"); | |
388 | break; | |
389 | default: | |
390 | func (stream, "3"); | |
391 | } | |
392 | break; | |
393 | ||
394 | case 'P': | |
395 | switch (given & 0x00080080) | |
396 | { | |
397 | case 0: | |
398 | func (stream, "s"); | |
399 | break; | |
400 | case 0x80: | |
401 | func (stream, "d"); | |
402 | break; | |
403 | case 0x00080000: | |
404 | func (stream, "e"); | |
405 | break; | |
406 | default: | |
407 | func (stream, _("<illegal precision>")); | |
408 | break; | |
409 | } | |
410 | break; | |
411 | case 'Q': | |
412 | switch (given & 0x00408000) | |
413 | { | |
414 | case 0: | |
415 | func (stream, "s"); | |
416 | break; | |
417 | case 0x8000: | |
418 | func (stream, "d"); | |
419 | break; | |
420 | case 0x00400000: | |
421 | func (stream, "e"); | |
422 | break; | |
423 | default: | |
424 | func (stream, "p"); | |
425 | break; | |
426 | } | |
427 | break; | |
428 | case 'R': | |
429 | switch (given & 0x60) | |
430 | { | |
431 | case 0: | |
432 | break; | |
433 | case 0x20: | |
434 | func (stream, "p"); | |
435 | break; | |
436 | case 0x40: | |
437 | func (stream, "m"); | |
438 | break; | |
439 | default: | |
440 | func (stream, "z"); | |
441 | break; | |
442 | } | |
443 | break; | |
444 | ||
445 | case '0': case '1': case '2': case '3': case '4': | |
446 | case '5': case '6': case '7': case '8': case '9': | |
447 | { | |
448 | int bitstart = *c++ - '0'; | |
449 | int bitend = 0; | |
450 | while (*c >= '0' && *c <= '9') | |
451 | bitstart = (bitstart * 10) + *c++ - '0'; | |
452 | ||
453 | switch (*c) | |
454 | { | |
455 | case '-': | |
456 | c++; | |
457 | while (*c >= '0' && *c <= '9') | |
458 | bitend = (bitend * 10) + *c++ - '0'; | |
459 | if (!bitend) | |
460 | abort (); | |
461 | switch (*c) | |
462 | { | |
463 | case 'r': | |
464 | { | |
465 | long reg; | |
466 | reg = given >> bitstart; | |
467 | reg &= (2 << (bitend - bitstart)) - 1; | |
468 | func (stream, "%s", arm_regnames[reg]); | |
469 | } | |
470 | break; | |
471 | case 'd': | |
472 | { | |
473 | long reg; | |
474 | reg = given >> bitstart; | |
475 | reg &= (2 << (bitend - bitstart)) - 1; | |
476 | func (stream, "%d", reg); | |
477 | } | |
478 | break; | |
479 | case 'x': | |
480 | { | |
481 | long reg; | |
482 | reg = given >> bitstart; | |
483 | reg &= (2 << (bitend - bitstart)) - 1; | |
484 | func (stream, "0x%08x", reg); | |
5876e06d NC |
485 | |
486 | /* Some SWI instructions have special meanings. */ | |
487 | if ((given & 0x0fffffff) == 0x0FF00000) | |
488 | func (stream, "\t; IMB"); | |
489 | else if ((given & 0x0fffffff) == 0x0FF00001) | |
490 | func (stream, "\t; IMBRange"); | |
252b5132 RH |
491 | } |
492 | break; | |
493 | case 'f': | |
494 | { | |
495 | long reg; | |
496 | reg = given >> bitstart; | |
497 | reg &= (2 << (bitend - bitstart)) - 1; | |
498 | if (reg > 7) | |
499 | func (stream, "#%s", | |
500 | arm_fp_const[reg & 7]); | |
501 | else | |
502 | func (stream, "f%d", reg); | |
503 | } | |
504 | break; | |
505 | default: | |
506 | abort (); | |
507 | } | |
508 | break; | |
509 | case '`': | |
510 | c++; | |
511 | if ((given & (1 << bitstart)) == 0) | |
512 | func (stream, "%c", *c); | |
513 | break; | |
514 | case '\'': | |
515 | c++; | |
516 | if ((given & (1 << bitstart)) != 0) | |
517 | func (stream, "%c", *c); | |
518 | break; | |
519 | case '?': | |
520 | ++c; | |
521 | if ((given & (1 << bitstart)) != 0) | |
522 | func (stream, "%c", *c++); | |
523 | else | |
524 | func (stream, "%c", *++c); | |
525 | break; | |
526 | default: | |
527 | abort (); | |
528 | } | |
529 | break; | |
530 | ||
531 | default: | |
532 | abort (); | |
533 | } | |
534 | } | |
535 | } | |
536 | else | |
537 | func (stream, "%c", *c); | |
538 | } | |
539 | return 4; | |
540 | } | |
541 | } | |
542 | abort (); | |
543 | } | |
544 | ||
545 | /* Print one instruction from PC on INFO->STREAM. | |
546 | Return the size of the instruction. */ | |
547 | ||
548 | static int | |
549 | print_insn_thumb (pc, info, given) | |
5876e06d NC |
550 | bfd_vma pc; |
551 | struct disassemble_info * info; | |
552 | long given; | |
252b5132 | 553 | { |
5876e06d NC |
554 | struct thumb_opcode * insn; |
555 | void * stream = info->stream; | |
556 | fprintf_ftype func = info->fprintf_func; | |
252b5132 RH |
557 | |
558 | for (insn = thumb_opcodes; insn->assembler; insn++) | |
559 | { | |
560 | if ((given & insn->mask) == insn->value) | |
561 | { | |
5876e06d | 562 | char * c = insn->assembler; |
252b5132 RH |
563 | |
564 | /* Special processing for Thumb 2 instruction BL sequence: */ | |
565 | if (!*c) /* check for empty (not NULL) assembler string */ | |
566 | { | |
567 | info->bytes_per_chunk = 4; | |
568 | info->bytes_per_line = 4; | |
569 | ||
570 | func (stream, "%04x\tbl\t", given & 0xffff); | |
571 | (*info->print_address_func) | |
572 | (BDISP23 (given) * 2 + pc + 4, info); | |
573 | return 4; | |
574 | } | |
575 | else | |
576 | { | |
577 | info->bytes_per_chunk = 2; | |
578 | info->bytes_per_line = 4; | |
579 | ||
580 | given &= 0xffff; | |
581 | func (stream, "%04x\t", given); | |
5876e06d | 582 | |
252b5132 RH |
583 | for (; *c; c++) |
584 | { | |
585 | if (*c == '%') | |
586 | { | |
587 | int domaskpc = 0; | |
588 | int domasklr = 0; | |
5876e06d | 589 | |
252b5132 RH |
590 | switch (*++c) |
591 | { | |
592 | case '%': | |
593 | func (stream, "%%"); | |
594 | break; | |
595 | ||
596 | case 'S': | |
597 | { | |
598 | long reg; | |
599 | reg = (given >> 3) & 0x7; | |
600 | if (given & (1 << 6)) | |
601 | reg += 8; | |
602 | func (stream, "%s", arm_regnames[reg]); | |
603 | } | |
604 | break; | |
605 | ||
606 | case 'D': | |
607 | { | |
608 | long reg; | |
5876e06d | 609 | |
252b5132 RH |
610 | reg = given & 0x7; |
611 | if (given & (1 << 7)) | |
612 | reg += 8; | |
613 | func (stream, "%s", arm_regnames[reg]); | |
614 | } | |
615 | break; | |
616 | ||
617 | case 'T': | |
618 | func (stream, "%s", | |
619 | arm_conditional [(given >> 8) & 0xf]); | |
620 | break; | |
621 | ||
622 | case 'N': | |
623 | if (given & (1 << 8)) | |
624 | domasklr = 1; | |
625 | /* fall through */ | |
626 | case 'O': | |
627 | if (*c == 'O' && (given & (1 << 8))) | |
628 | domaskpc = 1; | |
629 | /* fall through */ | |
630 | case 'M': | |
631 | { | |
632 | int started = 0; | |
633 | int reg; | |
5876e06d | 634 | |
252b5132 RH |
635 | func (stream, "{"); |
636 | /* It would be nice if we could spot | |
637 | ranges, and generate the rS-rE format: */ | |
638 | for (reg = 0; (reg < 8); reg++) | |
639 | if ((given & (1 << reg)) != 0) | |
640 | { | |
641 | if (started) | |
642 | func (stream, ", "); | |
643 | started = 1; | |
644 | func (stream, "%s", arm_regnames[reg]); | |
645 | } | |
646 | ||
647 | if (domasklr) | |
648 | { | |
649 | if (started) | |
650 | func (stream, ", "); | |
651 | started = 1; | |
652 | func (stream, "lr"); | |
653 | } | |
654 | ||
655 | if (domaskpc) | |
656 | { | |
657 | if (started) | |
658 | func (stream, ", "); | |
659 | func (stream, "pc"); | |
660 | } | |
661 | ||
662 | func (stream, "}"); | |
663 | } | |
664 | break; | |
665 | ||
666 | ||
667 | case '0': case '1': case '2': case '3': case '4': | |
668 | case '5': case '6': case '7': case '8': case '9': | |
669 | { | |
670 | int bitstart = *c++ - '0'; | |
671 | int bitend = 0; | |
5876e06d | 672 | |
252b5132 RH |
673 | while (*c >= '0' && *c <= '9') |
674 | bitstart = (bitstart * 10) + *c++ - '0'; | |
675 | ||
676 | switch (*c) | |
677 | { | |
678 | case '-': | |
679 | { | |
680 | long reg; | |
5876e06d | 681 | |
252b5132 RH |
682 | c++; |
683 | while (*c >= '0' && *c <= '9') | |
684 | bitend = (bitend * 10) + *c++ - '0'; | |
685 | if (!bitend) | |
686 | abort (); | |
687 | reg = given >> bitstart; | |
688 | reg &= (2 << (bitend - bitstart)) - 1; | |
689 | switch (*c) | |
690 | { | |
691 | case 'r': | |
692 | func (stream, "%s", arm_regnames[reg]); | |
693 | break; | |
694 | ||
695 | case 'd': | |
696 | func (stream, "%d", reg); | |
697 | break; | |
698 | ||
699 | case 'H': | |
700 | func (stream, "%d", reg << 1); | |
701 | break; | |
702 | ||
703 | case 'W': | |
704 | func (stream, "%d", reg << 2); | |
705 | break; | |
706 | ||
707 | case 'a': | |
708 | /* PC-relative address -- the bottom two | |
709 | bits of the address are dropped before | |
710 | the calculation. */ | |
711 | info->print_address_func | |
712 | (((pc + 4) & ~3) + (reg << 2), info); | |
713 | break; | |
714 | ||
715 | case 'x': | |
716 | func (stream, "0x%04x", reg); | |
717 | break; | |
718 | ||
719 | case 'I': | |
720 | reg = ((reg ^ (1 << bitend)) - (1 << bitend)); | |
721 | func (stream, "%d", reg); | |
722 | break; | |
723 | ||
724 | case 'B': | |
725 | reg = ((reg ^ (1 << bitend)) - (1 << bitend)); | |
726 | (*info->print_address_func) | |
727 | (reg * 2 + pc + 4, info); | |
728 | break; | |
729 | ||
730 | default: | |
5876e06d | 731 | abort (); |
252b5132 RH |
732 | } |
733 | } | |
734 | break; | |
735 | ||
736 | case '\'': | |
737 | c++; | |
738 | if ((given & (1 << bitstart)) != 0) | |
739 | func (stream, "%c", *c); | |
740 | break; | |
741 | ||
742 | case '?': | |
743 | ++c; | |
744 | if ((given & (1 << bitstart)) != 0) | |
745 | func (stream, "%c", *c++); | |
746 | else | |
747 | func (stream, "%c", *++c); | |
748 | break; | |
749 | ||
750 | default: | |
5876e06d | 751 | abort (); |
252b5132 RH |
752 | } |
753 | } | |
754 | break; | |
755 | ||
756 | default: | |
757 | abort (); | |
758 | } | |
759 | } | |
760 | else | |
761 | func (stream, "%c", *c); | |
762 | } | |
763 | } | |
764 | return 2; | |
765 | } | |
766 | } | |
767 | ||
768 | /* no match */ | |
769 | abort (); | |
770 | } | |
771 | ||
dd92f639 NC |
772 | /* Select a different register name set. |
773 | Returns true if the name set selected is the APCS name set. */ | |
774 | int | |
775 | arm_toggle_regnames () | |
776 | { | |
777 | if (arm_regnames == arm_regnames_standard) | |
778 | arm_regnames = arm_regnames_apcs; | |
779 | else | |
780 | arm_regnames = arm_regnames_standard; | |
781 | ||
782 | return arm_regnames == arm_regnames_apcs; | |
783 | } | |
784 | ||
785 | static void | |
786 | parse_disassembler_options (options) | |
787 | char * options; | |
788 | { | |
789 | if (options == NULL) | |
790 | return; | |
791 | ||
792 | if (strncmp (options, "reg-names-", 10) == 0) | |
793 | { | |
794 | options += 10; | |
795 | ||
796 | if (strcmp (options, "std") == 0) | |
797 | arm_regnames = arm_regnames_standard; | |
798 | else if (strcmp (options, "apcs") == 0) | |
799 | arm_regnames = arm_regnames_apcs; | |
800 | else if (strcmp (options, "raw") == 0) | |
801 | arm_regnames = arm_regnames_raw; | |
802 | else | |
803 | fprintf (stderr, "Unrecognised register name set: %s\n", options); | |
804 | } | |
805 | else | |
806 | fprintf (stderr, "Unrecognised disassembler option: %s\n", options); | |
807 | ||
808 | return; | |
809 | } | |
810 | ||
252b5132 RH |
811 | /* NOTE: There are no checks in these routines that the relevant number of data bytes exist */ |
812 | ||
813 | int | |
814 | print_insn_big_arm (pc, info) | |
815 | bfd_vma pc; | |
5876e06d | 816 | struct disassemble_info * info; |
252b5132 RH |
817 | { |
818 | unsigned char b[4]; | |
819 | long given; | |
820 | int status; | |
5876e06d NC |
821 | coff_symbol_type * cs; |
822 | elf_symbol_type * es; | |
252b5132 RH |
823 | int is_thumb; |
824 | ||
dd92f639 NC |
825 | if (info->disassembler_options) |
826 | { | |
827 | parse_disassembler_options (info->disassembler_options); | |
828 | ||
829 | /* To avoid repeated parsing of this option, we remove it here. */ | |
830 | info->disassembler_options = NULL; | |
831 | } | |
832 | ||
252b5132 RH |
833 | is_thumb = false; |
834 | if (info->symbols != NULL) | |
835 | { | |
5876e06d NC |
836 | if (bfd_asymbol_flavour (*info->symbols) == bfd_target_coff_flavour) |
837 | { | |
838 | cs = coffsymbol (*info->symbols); | |
839 | is_thumb = ( cs->native->u.syment.n_sclass == C_THUMBEXT | |
840 | || cs->native->u.syment.n_sclass == C_THUMBSTAT | |
841 | || cs->native->u.syment.n_sclass == C_THUMBLABEL | |
842 | || cs->native->u.syment.n_sclass == C_THUMBEXTFUNC | |
843 | || cs->native->u.syment.n_sclass == C_THUMBSTATFUNC); | |
844 | } | |
845 | else if (bfd_asymbol_flavour (*info->symbols) == bfd_target_elf_flavour) | |
846 | { | |
847 | es = *(elf_symbol_type **)(info->symbols); | |
848 | is_thumb = ELF_ST_TYPE (es->internal_elf_sym.st_info) == | |
849 | STT_ARM_TFUNC; | |
850 | } | |
851 | } | |
252b5132 RH |
852 | |
853 | info->bytes_per_chunk = 4; | |
854 | info->display_endian = BFD_ENDIAN_BIG; | |
855 | ||
856 | /* Always fetch word aligned values. */ | |
857 | ||
858 | status = (*info->read_memory_func) (pc & ~ 0x3, (bfd_byte *) &b[0], 4, info); | |
859 | if (status != 0) | |
860 | { | |
861 | (*info->memory_error_func) (status, pc, info); | |
862 | return -1; | |
863 | } | |
864 | ||
865 | if (is_thumb) | |
866 | { | |
867 | if (pc & 0x2) | |
868 | { | |
869 | given = (b[2] << 8) | b[3]; | |
870 | ||
871 | status = info->read_memory_func ((pc + 4) & ~ 0x3, (bfd_byte *) b, 4, info); | |
872 | if (status != 0) | |
873 | { | |
874 | info->memory_error_func (status, pc + 4, info); | |
875 | return -1; | |
876 | } | |
877 | ||
878 | given |= (b[0] << 24) | (b[1] << 16); | |
879 | } | |
880 | else | |
5876e06d | 881 | given = (b[0] << 8) | b[1] | (b[2] << 24) | (b[3] << 16); |
252b5132 RH |
882 | } |
883 | else | |
5876e06d | 884 | given = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3]); |
252b5132 RH |
885 | |
886 | if (is_thumb) | |
5876e06d | 887 | status = print_insn_thumb (pc, info, given); |
252b5132 | 888 | else |
5876e06d | 889 | status = print_insn_arm (pc, info, given); |
252b5132 RH |
890 | |
891 | return status; | |
892 | } | |
893 | ||
894 | int | |
895 | print_insn_little_arm (pc, info) | |
896 | bfd_vma pc; | |
897 | struct disassemble_info * info; | |
898 | { | |
899 | unsigned char b[4]; | |
900 | long given; | |
901 | int status; | |
5876e06d NC |
902 | coff_symbol_type * cs; |
903 | elf_symbol_type * es; | |
252b5132 RH |
904 | int is_thumb; |
905 | ||
dd92f639 NC |
906 | if (info->disassembler_options) |
907 | { | |
908 | parse_disassembler_options (info->disassembler_options); | |
909 | ||
910 | /* To avoid repeated parsing of this option, we remove it here. */ | |
911 | info->disassembler_options = NULL; | |
912 | } | |
913 | ||
252b5132 | 914 | is_thumb = false; |
5876e06d | 915 | |
252b5132 RH |
916 | if (info->symbols != NULL) |
917 | { | |
5876e06d NC |
918 | if (bfd_asymbol_flavour (*info->symbols) == bfd_target_coff_flavour) |
919 | { | |
920 | cs = coffsymbol (*info->symbols); | |
921 | is_thumb = ( cs->native->u.syment.n_sclass == C_THUMBEXT | |
922 | || cs->native->u.syment.n_sclass == C_THUMBSTAT | |
923 | || cs->native->u.syment.n_sclass == C_THUMBLABEL | |
924 | || cs->native->u.syment.n_sclass == C_THUMBEXTFUNC | |
925 | || cs->native->u.syment.n_sclass == C_THUMBSTATFUNC); | |
926 | } | |
927 | else if (bfd_asymbol_flavour (*info->symbols) == bfd_target_elf_flavour) | |
928 | { | |
929 | es = *(elf_symbol_type **)(info->symbols); | |
930 | is_thumb = ELF_ST_TYPE (es->internal_elf_sym.st_info) == | |
931 | STT_ARM_TFUNC; | |
932 | } | |
933 | } | |
252b5132 | 934 | |
252b5132 RH |
935 | info->bytes_per_chunk = 4; |
936 | info->display_endian = BFD_ENDIAN_LITTLE; | |
937 | ||
938 | status = (*info->read_memory_func) (pc, (bfd_byte *) &b[0], 4, info); | |
939 | if (status != 0 && is_thumb) | |
940 | { | |
941 | info->bytes_per_chunk = 2; | |
942 | ||
943 | status = info->read_memory_func (pc, (bfd_byte *) b, 2, info); | |
944 | b[3] = b[2] = 0; | |
945 | } | |
946 | if (status != 0) | |
947 | { | |
948 | (*info->memory_error_func) (status, pc, info); | |
949 | return -1; | |
950 | } | |
951 | ||
952 | given = (b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24); | |
953 | ||
954 | if (is_thumb) | |
5876e06d | 955 | status = print_insn_thumb (pc, info, given); |
252b5132 | 956 | else |
5876e06d | 957 | status = print_insn_arm (pc, info, given); |
252b5132 RH |
958 | |
959 | return status; | |
960 | } |