1 |
/* Print Motorola 68k instructions. |
2 |
Copyright 1986, 87, 89, 91, 92, 93, 94, 95, 96, 97, 1998 |
3 |
Free Software Foundation, Inc. |
4 |
|
5 |
This file is free software; you can redistribute it and/or modify |
6 |
it under the terms of the GNU General Public License as published by |
7 |
the Free Software Foundation; either version 2 of the License, or |
8 |
(at your option) any later version. |
9 |
|
10 |
This program is distributed in the hope that it will be useful, |
11 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 |
GNU General Public License for more details. |
14 |
|
15 |
You should have received a copy of the GNU General Public License |
16 |
along with this program; if not, write to the Free Software |
17 |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
18 |
|
19 |
#include <stdlib.h> |
20 |
#include "dis-asm.h" |
21 |
#include "floatformat.h" |
22 |
#include "opintl.h" |
23 |
|
24 |
#include "m68k.h" |
25 |
|
26 |
/* Local function prototypes */ |
27 |
|
28 |
static int |
29 |
fetch_data PARAMS ((struct disassemble_info *, bfd_byte *)); |
30 |
|
31 |
static void |
32 |
dummy_print_address PARAMS ((bfd_vma, struct disassemble_info *)); |
33 |
|
34 |
static int |
35 |
fetch_arg PARAMS ((unsigned char *, int, int, disassemble_info *)); |
36 |
|
37 |
static void |
38 |
print_base PARAMS ((int, bfd_vma, disassemble_info*)); |
39 |
|
40 |
static unsigned char * |
41 |
print_indexed PARAMS ((int, unsigned char *, bfd_vma, disassemble_info *)); |
42 |
|
43 |
static int |
44 |
print_insn_arg PARAMS ((const char *, unsigned char *, unsigned char *, |
45 |
bfd_vma, disassemble_info *)); |
46 |
|
47 |
CONST char * CONST fpcr_names[] = { |
48 |
"", "fpiar", "fpsr", "fpiar/fpsr", "fpcr", |
49 |
"fpiar/fpcr", "fpsr/fpcr", "fpiar/fpsr/fpcr"}; |
50 |
|
51 |
static char *const reg_names[] = { |
52 |
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", |
53 |
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", |
54 |
"sr", "pc"}; |
55 |
|
56 |
/* Sign-extend an (unsigned char). */ |
57 |
#if __STDC__ == 1 |
58 |
#define COERCE_SIGNED_CHAR(ch) ((signed char)(ch)) |
59 |
#else |
60 |
#define COERCE_SIGNED_CHAR(ch) ((int)(((ch) ^ 0x80) & 0xFF) - 128) |
61 |
#endif |
62 |
|
63 |
/* Get a 1 byte signed integer. */ |
64 |
#define NEXTBYTE(p) (p += 2, FETCH_DATA (info, p), COERCE_SIGNED_CHAR(p[-1])) |
65 |
|
66 |
/* Get a 2 byte signed integer. */ |
67 |
#define COERCE16(x) ((int) (((x) ^ 0x8000) - 0x8000)) |
68 |
#define NEXTWORD(p) \ |
69 |
(p += 2, FETCH_DATA (info, p), \ |
70 |
COERCE16 ((p[-2] << 8) + p[-1])) |
71 |
|
72 |
/* Get a 4 byte signed integer. */ |
73 |
#define COERCE32(x) ((bfd_signed_vma) ((x) ^ 0x80000000) - 0x80000000) |
74 |
#define NEXTLONG(p) \ |
75 |
(p += 4, FETCH_DATA (info, p), \ |
76 |
(COERCE32 ((((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1]))) |
77 |
|
78 |
/* Get a 4 byte unsigned integer. */ |
79 |
#define NEXTULONG(p) \ |
80 |
(p += 4, FETCH_DATA (info, p), \ |
81 |
(unsigned int) ((((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1])) |
82 |
|
83 |
/* Get a single precision float. */ |
84 |
#define NEXTSINGLE(val, p) \ |
85 |
(p += 4, FETCH_DATA (info, p), \ |
86 |
floatformat_to_double (&floatformat_ieee_single_big, (char *) p - 4, &val)) |
87 |
|
88 |
/* Get a double precision float. */ |
89 |
#define NEXTDOUBLE(val, p) \ |
90 |
(p += 8, FETCH_DATA (info, p), \ |
91 |
floatformat_to_double (&floatformat_ieee_double_big, (char *) p - 8, &val)) |
92 |
|
93 |
/* Get an extended precision float. */ |
94 |
#define NEXTEXTEND(val, p) \ |
95 |
(p += 12, FETCH_DATA (info, p), \ |
96 |
floatformat_to_double (&floatformat_m68881_ext, (char *) p - 12, &val)) |
97 |
|
98 |
/* Need a function to convert from packed to double |
99 |
precision. Actually, it's easier to print a |
100 |
packed number than a double anyway, so maybe |
101 |
there should be a special case to handle this... */ |
102 |
#define NEXTPACKED(p) \ |
103 |
(p += 12, FETCH_DATA (info, p), 0.0) |
104 |
|
105 |
|
106 |
/* Maximum length of an instruction. */ |
107 |
#define MAXLEN 22 |
108 |
|
109 |
#include <setjmp.h> |
110 |
|
111 |
struct private |
112 |
{ |
113 |
/* Points to first byte not fetched. */ |
114 |
bfd_byte *max_fetched; |
115 |
bfd_byte the_buffer[MAXLEN]; |
116 |
bfd_vma insn_start; |
117 |
jmp_buf bailout; |
118 |
}; |
119 |
|
120 |
/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) |
121 |
to ADDR (exclusive) are valid. Returns 1 for success, longjmps |
122 |
on error. */ |
123 |
#define FETCH_DATA(info, addr) \ |
124 |
((addr) <= ((struct private *)(info->private_data))->max_fetched \ |
125 |
? 1 : fetch_data ((info), (addr))) |
126 |
|
127 |
static int |
128 |
fetch_data (info, addr) |
129 |
struct disassemble_info *info; |
130 |
bfd_byte *addr; |
131 |
{ |
132 |
int status; |
133 |
struct private *priv = (struct private *)info->private_data; |
134 |
bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer); |
135 |
|
136 |
status = (*info->read_memory_func) (start, |
137 |
priv->max_fetched, |
138 |
addr - priv->max_fetched, |
139 |
info); |
140 |
if (status != 0) |
141 |
{ |
142 |
(*info->memory_error_func) (status, start, info); |
143 |
longjmp (priv->bailout, 1); |
144 |
} |
145 |
else |
146 |
priv->max_fetched = addr; |
147 |
return 1; |
148 |
} |
149 |
|
150 |
/* This function is used to print to the bit-bucket. */ |
151 |
static int |
152 |
#ifdef __STDC__ |
153 |
dummy_printer (FILE * file, const char * format, ...) |
154 |
#else |
155 |
dummy_printer (file) FILE *file; |
156 |
#endif |
157 |
{ return 0; } |
158 |
|
159 |
static void |
160 |
dummy_print_address (vma, info) |
161 |
bfd_vma vma; |
162 |
struct disassemble_info *info; |
163 |
{ |
164 |
} |
165 |
|
166 |
/* Print the m68k instruction at address MEMADDR in debugged memory, |
167 |
on INFO->STREAM. Returns length of the instruction, in bytes. */ |
168 |
|
169 |
int |
170 |
print_insn_m68k (memaddr, info) |
171 |
bfd_vma memaddr; |
172 |
disassemble_info *info; |
173 |
{ |
174 |
register int i; |
175 |
register unsigned char *p; |
176 |
unsigned char *save_p; |
177 |
register const char *d; |
178 |
register unsigned long bestmask; |
179 |
const struct m68k_opcode *best = 0; |
180 |
unsigned int arch_mask; |
181 |
struct private priv; |
182 |
bfd_byte *buffer = priv.the_buffer; |
183 |
fprintf_ftype save_printer = info->fprintf_func; |
184 |
void (*save_print_address) PARAMS((bfd_vma, struct disassemble_info*)) |
185 |
= info->print_address_func; |
186 |
int major_opcode; |
187 |
static int numopcodes[16]; |
188 |
static const struct m68k_opcode **opcodes[16]; |
189 |
|
190 |
if (!opcodes[0]) |
191 |
{ |
192 |
/* Speed up the matching by sorting the opcode table on the upper |
193 |
four bits of the opcode. */ |
194 |
const struct m68k_opcode **opc_pointer[16]; |
195 |
|
196 |
/* First count how many opcodes are in each of the sixteen buckets. */ |
197 |
for (i = 0; i < m68k_numopcodes; i++) |
198 |
numopcodes[(m68k_opcodes[i].opcode >> 28) & 15]++; |
199 |
|
200 |
/* Then create a sorted table of pointers that point into the |
201 |
unsorted table. */ |
202 |
opc_pointer[0] = ((const struct m68k_opcode **) |
203 |
malloc (sizeof (struct m68k_opcode *) |
204 |
* m68k_numopcodes)); |
205 |
opcodes[0] = opc_pointer[0]; |
206 |
for (i = 1; i < 16; i++) |
207 |
{ |
208 |
opc_pointer[i] = opc_pointer[i - 1] + numopcodes[i - 1]; |
209 |
opcodes[i] = opc_pointer[i]; |
210 |
} |
211 |
|
212 |
for (i = 0; i < m68k_numopcodes; i++) |
213 |
*opc_pointer[(m68k_opcodes[i].opcode >> 28) & 15]++ = &m68k_opcodes[i]; |
214 |
|
215 |
} |
216 |
|
217 |
info->private_data = (PTR) &priv; |
218 |
/* Tell objdump to use two bytes per chunk and six bytes per line for |
219 |
displaying raw data. */ |
220 |
info->bytes_per_chunk = 2; |
221 |
info->bytes_per_line = 6; |
222 |
info->display_endian = BFD_ENDIAN_BIG; |
223 |
priv.max_fetched = priv.the_buffer; |
224 |
priv.insn_start = memaddr; |
225 |
if (setjmp (priv.bailout) != 0) |
226 |
/* Error return. */ |
227 |
return -1; |
228 |
|
229 |
switch (info->mach) |
230 |
{ |
231 |
default: |
232 |
case 0: |
233 |
arch_mask = (unsigned int) -1; |
234 |
break; |
235 |
case bfd_mach_m68000: |
236 |
arch_mask = m68000; |
237 |
break; |
238 |
case bfd_mach_m68008: |
239 |
arch_mask = m68008; |
240 |
break; |
241 |
case bfd_mach_m68010: |
242 |
arch_mask = m68010; |
243 |
break; |
244 |
case bfd_mach_m68020: |
245 |
arch_mask = m68020; |
246 |
break; |
247 |
case bfd_mach_m68030: |
248 |
arch_mask = m68030; |
249 |
break; |
250 |
case bfd_mach_m68040: |
251 |
arch_mask = m68040; |
252 |
break; |
253 |
case bfd_mach_m68060: |
254 |
arch_mask = m68060; |
255 |
break; |
256 |
} |
257 |
|
258 |
arch_mask |= m68881 | m68851; |
259 |
|
260 |
bestmask = 0; |
261 |
FETCH_DATA (info, buffer + 2); |
262 |
major_opcode = (buffer[0] >> 4) & 15; |
263 |
for (i = 0; i < numopcodes[major_opcode]; i++) |
264 |
{ |
265 |
const struct m68k_opcode *opc = opcodes[major_opcode][i]; |
266 |
unsigned long opcode = opc->opcode; |
267 |
unsigned long match = opc->match; |
268 |
|
269 |
if (((0xff & buffer[0] & (match >> 24)) == (0xff & (opcode >> 24))) |
270 |
&& ((0xff & buffer[1] & (match >> 16)) == (0xff & (opcode >> 16))) |
271 |
/* Only fetch the next two bytes if we need to. */ |
272 |
&& (((0xffff & match) == 0) |
273 |
|| |
274 |
(FETCH_DATA (info, buffer + 4) |
275 |
&& ((0xff & buffer[2] & (match >> 8)) == (0xff & (opcode >> 8))) |
276 |
&& ((0xff & buffer[3] & match) == (0xff & opcode))) |
277 |
) |
278 |
&& (opc->arch & arch_mask) != 0) |
279 |
{ |
280 |
/* Don't use for printout the variants of divul and divsl |
281 |
that have the same register number in two places. |
282 |
The more general variants will match instead. */ |
283 |
for (d = opc->args; *d; d += 2) |
284 |
if (d[1] == 'D') |
285 |
break; |
286 |
|
287 |
/* Don't use for printout the variants of most floating |
288 |
point coprocessor instructions which use the same |
289 |
register number in two places, as above. */ |
290 |
if (*d == '\0') |
291 |
for (d = opc->args; *d; d += 2) |
292 |
if (d[1] == 't') |
293 |
break; |
294 |
|
295 |
/* Don't match fmovel with more than one register; wait for |
296 |
fmoveml. */ |
297 |
if (*d == '\0') |
298 |
{ |
299 |
for (d = opc->args; *d; d += 2) |
300 |
{ |
301 |
if (d[0] == 's' && d[1] == '8') |
302 |
{ |
303 |
int val; |
304 |
|
305 |
val = fetch_arg (buffer, d[1], 3, info); |
306 |
if ((val & (val - 1)) != 0) |
307 |
break; |
308 |
} |
309 |
} |
310 |
} |
311 |
|
312 |
if (*d == '\0' && match > bestmask) |
313 |
{ |
314 |
best = opc; |
315 |
bestmask = match; |
316 |
} |
317 |
} |
318 |
} |
319 |
|
320 |
if (best == 0) |
321 |
goto invalid; |
322 |
|
323 |
/* Point at first word of argument data, |
324 |
and at descriptor for first argument. */ |
325 |
p = buffer + 2; |
326 |
|
327 |
/* Figure out how long the fixed-size portion of the instruction is. |
328 |
The only place this is stored in the opcode table is |
329 |
in the arguments--look for arguments which specify fields in the 2nd |
330 |
or 3rd words of the instruction. */ |
331 |
for (d = best->args; *d; d += 2) |
332 |
{ |
333 |
/* I don't think it is necessary to be checking d[0] here; I suspect |
334 |
all this could be moved to the case statement below. */ |
335 |
if (d[0] == '#') |
336 |
{ |
337 |
if (d[1] == 'l' && p - buffer < 6) |
338 |
p = buffer + 6; |
339 |
else if (p - buffer < 4 && d[1] != 'C' && d[1] != '8' ) |
340 |
p = buffer + 4; |
341 |
} |
342 |
if ((d[0] == 'L' || d[0] == 'l') && d[1] == 'w' && p - buffer < 4) |
343 |
p = buffer + 4; |
344 |
switch (d[1]) |
345 |
{ |
346 |
case '1': |
347 |
case '2': |
348 |
case '3': |
349 |
case '7': |
350 |
case '8': |
351 |
case '9': |
352 |
case 'i': |
353 |
if (p - buffer < 4) |
354 |
p = buffer + 4; |
355 |
break; |
356 |
case '4': |
357 |
case '5': |
358 |
case '6': |
359 |
if (p - buffer < 6) |
360 |
p = buffer + 6; |
361 |
break; |
362 |
default: |
363 |
break; |
364 |
} |
365 |
} |
366 |
|
367 |
/* pflusha is an exceptions. It takes no arguments but is two words |
368 |
long. Recognize it by looking at the lower 16 bits of the mask. */ |
369 |
if (p - buffer < 4 && (best->match & 0xFFFF) != 0) |
370 |
p = buffer + 4; |
371 |
|
372 |
/* lpstop is another exception. It takes a one word argument but is |
373 |
three words long. */ |
374 |
if (p - buffer < 6 |
375 |
&& (best->match & 0xffff) == 0xffff |
376 |
&& best->args[0] == '#' |
377 |
&& best->args[1] == 'w') |
378 |
{ |
379 |
/* Copy the one word argument into the usual location for a one |
380 |
word argument, to simplify printing it. We can get away with |
381 |
this because we know exactly what the second word is, and we |
382 |
aren't going to print anything based on it. */ |
383 |
p = buffer + 6; |
384 |
FETCH_DATA (info, p); |
385 |
buffer[2] = buffer[4]; |
386 |
buffer[3] = buffer[5]; |
387 |
} |
388 |
|
389 |
FETCH_DATA (info, p); |
390 |
|
391 |
d = best->args; |
392 |
|
393 |
/* We can the operands twice. The first time we don't print anything, |
394 |
but look for errors. */ |
395 |
|
396 |
save_p = p; |
397 |
info->print_address_func = dummy_print_address; |
398 |
info->fprintf_func = (fprintf_ftype)dummy_printer; |
399 |
for ( ; *d; d += 2) |
400 |
{ |
401 |
int eaten = print_insn_arg (d, buffer, p, memaddr + (p - buffer), info); |
402 |
if (eaten >= 0) |
403 |
p += eaten; |
404 |
else if (eaten == -1) |
405 |
goto invalid; |
406 |
else |
407 |
{ |
408 |
(*info->fprintf_func)(info->stream, |
409 |
/* xgettext:c-format */ |
410 |
_("<internal error in opcode table: %s %s>\n"), |
411 |
best->name, |
412 |
best->args); |
413 |
goto invalid; |
414 |
} |
415 |
|
416 |
} |
417 |
p = save_p; |
418 |
info->fprintf_func = save_printer; |
419 |
info->print_address_func = save_print_address; |
420 |
|
421 |
d = best->args; |
422 |
|
423 |
(*info->fprintf_func) (info->stream, "%s", best->name); |
424 |
|
425 |
if (*d) |
426 |
(*info->fprintf_func) (info->stream, "\t"); |
427 |
|
428 |
while (*d) |
429 |
{ |
430 |
p += print_insn_arg (d, buffer, p, memaddr + (p - buffer), info); |
431 |
d += 2; |
432 |
if (*d && *(d - 2) != 'I' && *d != 'k') |
433 |
(*info->fprintf_func) (info->stream, ","); |
434 |
} |
435 |
return p - buffer; |
436 |
|
437 |
invalid: { |
438 |
extern void print_68k_invalid_opcode(unsigned long, struct disassemble_info *); |
439 |
|
440 |
/* Handle undefined instructions. */ |
441 |
info->fprintf_func = save_printer; |
442 |
info->print_address_func = save_print_address; |
443 |
print_68k_invalid_opcode((buffer[0] << 8) | buffer[1], info); |
444 |
return 2; |
445 |
} |
446 |
} |
447 |
|
448 |
/* Returns number of bytes "eaten" by the operand, or |
449 |
return -1 if an invalid operand was found, or -2 if |
450 |
an opcode tabe error was found. */ |
451 |
|
452 |
static int |
453 |
print_insn_arg (d, buffer, p0, addr, info) |
454 |
const char *d; |
455 |
unsigned char *buffer; |
456 |
unsigned char *p0; |
457 |
bfd_vma addr; /* PC for this arg to be relative to */ |
458 |
disassemble_info *info; |
459 |
{ |
460 |
register int val = 0; |
461 |
register int place = d[1]; |
462 |
register unsigned char *p = p0; |
463 |
int regno; |
464 |
register CONST char *regname; |
465 |
register unsigned char *p1; |
466 |
double flval; |
467 |
int flt_p; |
468 |
bfd_signed_vma disp; |
469 |
unsigned int uval; |
470 |
|
471 |
switch (*d) |
472 |
{ |
473 |
case 'c': /* cache identifier */ |
474 |
{ |
475 |
static char *const cacheFieldName[] = { "nc", "dc", "ic", "bc" }; |
476 |
val = fetch_arg (buffer, place, 2, info); |
477 |
(*info->fprintf_func) (info->stream, cacheFieldName[val]); |
478 |
break; |
479 |
} |
480 |
|
481 |
case 'a': /* address register indirect only. Cf. case '+'. */ |
482 |
{ |
483 |
(*info->fprintf_func) |
484 |
(info->stream, |
485 |
"(%s)", |
486 |
reg_names [fetch_arg (buffer, place, 3, info) + 8]); |
487 |
break; |
488 |
} |
489 |
|
490 |
case '_': /* 32-bit absolute address for move16. */ |
491 |
{ |
492 |
uval = NEXTULONG (p); |
493 |
(*info->print_address_func) (uval, info); |
494 |
break; |
495 |
} |
496 |
|
497 |
case 'C': |
498 |
(*info->fprintf_func) (info->stream, "ccr"); |
499 |
break; |
500 |
|
501 |
case 'S': |
502 |
(*info->fprintf_func) (info->stream, "sr"); |
503 |
break; |
504 |
|
505 |
case 'U': |
506 |
(*info->fprintf_func) (info->stream, "usp"); |
507 |
break; |
508 |
|
509 |
case 'J': |
510 |
{ |
511 |
static const struct { char *name; int value; } names[] |
512 |
= {{"sfc", 0x000}, {"dfc", 0x001}, {"cacr", 0x002}, |
513 |
{"tc", 0x003}, {"itt0",0x004}, {"itt1", 0x005}, |
514 |
{"dtt0",0x006}, {"dtt1",0x007}, {"buscr",0x008}, |
515 |
{"usp", 0x800}, {"vbr", 0x801}, {"caar", 0x802}, |
516 |
{"msp", 0x803}, {"isp", 0x804}, |
517 |
|
518 |
/* Should we be calling this psr like we do in case 'Y'? */ |
519 |
{"mmusr",0x805}, |
520 |
|
521 |
{"urp", 0x806}, {"srp", 0x807}, {"pcr", 0x808}}; |
522 |
|
523 |
val = fetch_arg (buffer, place, 12, info); |
524 |
for (regno = sizeof names / sizeof names[0] - 1; regno >= 0; regno--) |
525 |
if (names[regno].value == val) |
526 |
{ |
527 |
(*info->fprintf_func) (info->stream, "%s", names[regno].name); |
528 |
break; |
529 |
} |
530 |
if (regno < 0) |
531 |
(*info->fprintf_func) (info->stream, "$%04x", val); |
532 |
} |
533 |
break; |
534 |
|
535 |
case 'Q': |
536 |
val = fetch_arg (buffer, place, 3, info); |
537 |
/* 0 means 8, except for the bkpt instruction... */ |
538 |
if (val == 0 && d[1] != 's') |
539 |
val = 8; |
540 |
(*info->fprintf_func) (info->stream, "#%d", val); |
541 |
break; |
542 |
|
543 |
case 'M': |
544 |
val = fetch_arg (buffer, place, 8, info); |
545 |
if (val & 0x80) |
546 |
val = val - 0x100; |
547 |
(*info->fprintf_func) (info->stream, "#$%02x", val); |
548 |
break; |
549 |
|
550 |
case 'T': |
551 |
val = fetch_arg (buffer, place, 4, info); |
552 |
(*info->fprintf_func) (info->stream, "#$%08x", val); |
553 |
break; |
554 |
|
555 |
case 'D': |
556 |
(*info->fprintf_func) (info->stream, "%s", |
557 |
reg_names[fetch_arg (buffer, place, 3, info)]); |
558 |
break; |
559 |
|
560 |
case 'A': |
561 |
(*info->fprintf_func) |
562 |
(info->stream, "%s", |
563 |
reg_names[fetch_arg (buffer, place, 3, info) + 010]); |
564 |
break; |
565 |
|
566 |
case 'R': |
567 |
(*info->fprintf_func) |
568 |
(info->stream, "%s", |
569 |
reg_names[fetch_arg (buffer, place, 4, info)]); |
570 |
break; |
571 |
|
572 |
case 'r': |
573 |
regno = fetch_arg (buffer, place, 4, info); |
574 |
(*info->fprintf_func) (info->stream, "(%s)", reg_names[regno]); |
575 |
break; |
576 |
|
577 |
case 'F': |
578 |
(*info->fprintf_func) |
579 |
(info->stream, "fp%d", |
580 |
fetch_arg (buffer, place, 3, info)); |
581 |
break; |
582 |
|
583 |
case 'O': |
584 |
val = fetch_arg (buffer, place, 6, info); |
585 |
if (val & 0x20) |
586 |
(*info->fprintf_func) (info->stream, "%s", reg_names [val & 7]); |
587 |
else |
588 |
(*info->fprintf_func) (info->stream, "%d", val); |
589 |
break; |
590 |
|
591 |
case '+': |
592 |
(*info->fprintf_func) |
593 |
(info->stream, "(%s)+", |
594 |
reg_names[fetch_arg (buffer, place, 3, info) + 8]); |
595 |
break; |
596 |
|
597 |
case '-': |
598 |
(*info->fprintf_func) |
599 |
(info->stream, "-(%s)", |
600 |
reg_names[fetch_arg (buffer, place, 3, info) + 8]); |
601 |
break; |
602 |
|
603 |
case 'k': |
604 |
if (place == 'k') |
605 |
(*info->fprintf_func) |
606 |
(info->stream, "{%s}", |
607 |
reg_names[fetch_arg (buffer, place, 3, info)]); |
608 |
else if (place == 'C') |
609 |
{ |
610 |
val = fetch_arg (buffer, place, 7, info); |
611 |
if ( val > 63 ) /* This is a signed constant. */ |
612 |
val -= 128; |
613 |
(*info->fprintf_func) (info->stream, "{#%d}", val); |
614 |
} |
615 |
else |
616 |
return -2; |
617 |
break; |
618 |
|
619 |
case '#': |
620 |
case '^': |
621 |
p1 = buffer + (*d == '#' ? 2 : 4); |
622 |
if (place == 's') |
623 |
val = fetch_arg (buffer, place, 4, info); |
624 |
else if (place == 'C') |
625 |
val = fetch_arg (buffer, place, 7, info); |
626 |
else if (place == '8') |
627 |
val = fetch_arg (buffer, place, 3, info); |
628 |
else if (place == '3') |
629 |
val = fetch_arg (buffer, place, 8, info); |
630 |
else if (place == 'b') { |
631 |
val = NEXTBYTE (p1); |
632 |
(*info->fprintf_func) (info->stream, "#$%02x", val & 0xff); |
633 |
break; |
634 |
} |
635 |
else if (place == 'w' || place == 'W') { |
636 |
val = NEXTWORD (p1); |
637 |
(*info->fprintf_func) (info->stream, "#$%04x", val & 0xffff); |
638 |
break; |
639 |
} |
640 |
else if (place == 'l') { |
641 |
val = NEXTLONG (p1); |
642 |
(*info->fprintf_func) (info->stream, "#$%08x", val); |
643 |
break; |
644 |
} |
645 |
else |
646 |
return -2; |
647 |
(*info->fprintf_func) (info->stream, "#%d", val); |
648 |
break; |
649 |
|
650 |
case 'B': |
651 |
if (place == 'b') |
652 |
disp = NEXTBYTE (p); |
653 |
else if (place == 'B') |
654 |
disp = COERCE_SIGNED_CHAR(buffer[1]); |
655 |
else if (place == 'w' || place == 'W') |
656 |
disp = NEXTWORD (p); |
657 |
else if (place == 'l' || place == 'L' || place == 'C') |
658 |
disp = NEXTLONG (p); |
659 |
else if (place == 'g') |
660 |
{ |
661 |
disp = NEXTBYTE (buffer); |
662 |
if (disp == 0) |
663 |
disp = NEXTWORD (p); |
664 |
else if (disp == -1) |
665 |
disp = NEXTLONG (p); |
666 |
} |
667 |
else if (place == 'c') |
668 |
{ |
669 |
if (buffer[1] & 0x40) /* If bit six is one, long offset */ |
670 |
disp = NEXTLONG (p); |
671 |
else |
672 |
disp = NEXTWORD (p); |
673 |
} |
674 |
else |
675 |
return -2; |
676 |
|
677 |
(*info->print_address_func) (addr + disp, info); |
678 |
break; |
679 |
|
680 |
case 'd': |
681 |
val = NEXTWORD (p); |
682 |
(*info->fprintf_func) |
683 |
(info->stream, "($%04x,%s)", |
684 |
val, reg_names[fetch_arg (buffer, place, 3, info) + 8]); |
685 |
break; |
686 |
|
687 |
case 's': |
688 |
(*info->fprintf_func) (info->stream, "%s", |
689 |
fpcr_names[fetch_arg (buffer, place, 3, info)]); |
690 |
break; |
691 |
|
692 |
case 'I': |
693 |
/* Get coprocessor ID... */ |
694 |
val = fetch_arg (buffer, 'd', 3, info); |
695 |
|
696 |
if (val != 1) /* Unusual coprocessor ID? */ |
697 |
(*info->fprintf_func) (info->stream, "(cpid=%d) ", val); |
698 |
break; |
699 |
|
700 |
case '*': |
701 |
case '~': |
702 |
case '%': |
703 |
case ';': |
704 |
case '@': |
705 |
case '!': |
706 |
case '$': |
707 |
case '?': |
708 |
case '/': |
709 |
case '&': |
710 |
case '|': |
711 |
case '<': |
712 |
case '>': |
713 |
case 'm': |
714 |
case 'n': |
715 |
case 'o': |
716 |
case 'p': |
717 |
case 'q': |
718 |
case 'v': |
719 |
|
720 |
if (place == 'd') |
721 |
{ |
722 |
val = fetch_arg (buffer, 'x', 6, info); |
723 |
val = ((val & 7) << 3) + ((val >> 3) & 7); |
724 |
} |
725 |
else |
726 |
val = fetch_arg (buffer, 's', 6, info); |
727 |
|
728 |
/* Get register number assuming address register. */ |
729 |
regno = (val & 7) + 8; |
730 |
regname = reg_names[regno]; |
731 |
switch (val >> 3) |
732 |
{ |
733 |
case 0: |
734 |
(*info->fprintf_func) (info->stream, "%s", reg_names[val]); |
735 |
break; |
736 |
|
737 |
case 1: |
738 |
(*info->fprintf_func) (info->stream, "%s", regname); |
739 |
break; |
740 |
|
741 |
case 2: |
742 |
(*info->fprintf_func) (info->stream, "(%s)", regname); |
743 |
break; |
744 |
|
745 |
case 3: |
746 |
(*info->fprintf_func) (info->stream, "(%s)+", regname); |
747 |
break; |
748 |
|
749 |
case 4: |
750 |
(*info->fprintf_func) (info->stream, "-(%s)", regname); |
751 |
break; |
752 |
|
753 |
case 5: |
754 |
val = NEXTWORD (p); |
755 |
(*info->fprintf_func) (info->stream, "($%04x,%s)", val, regname); |
756 |
break; |
757 |
|
758 |
case 6: |
759 |
p = print_indexed (regno, p, addr, info); |
760 |
break; |
761 |
|
762 |
case 7: |
763 |
switch (val & 7) |
764 |
{ |
765 |
case 0: |
766 |
val = NEXTWORD (p); |
767 |
(*info->print_address_func) (val, info); |
768 |
break; |
769 |
|
770 |
case 1: |
771 |
uval = NEXTULONG (p); |
772 |
(*info->print_address_func) (uval, info); |
773 |
break; |
774 |
|
775 |
case 2: |
776 |
val = NEXTWORD (p); |
777 |
(*info->fprintf_func) (info->stream, "("); |
778 |
(*info->print_address_func) (addr + val, info); |
779 |
(*info->fprintf_func) (info->stream, ",pc)"); |
780 |
break; |
781 |
|
782 |
case 3: |
783 |
p = print_indexed (-1, p, addr, info); |
784 |
break; |
785 |
|
786 |
case 4: |
787 |
switch( place ) |
788 |
{ |
789 |
case 'b': |
790 |
val = NEXTBYTE (p); |
791 |
(*info->fprintf_func) (info->stream, "#$%02x", val & 0xff); |
792 |
goto imm_printed; |
793 |
|
794 |
case 'w': |
795 |
val = NEXTWORD (p); |
796 |
(*info->fprintf_func) (info->stream, "#$%04x", val & 0xffff); |
797 |
goto imm_printed; |
798 |
|
799 |
case 'l': |
800 |
val = NEXTLONG (p); |
801 |
(*info->fprintf_func) (info->stream, "#$%08x", val); |
802 |
goto imm_printed; |
803 |
|
804 |
case 'f': |
805 |
NEXTSINGLE(flval, p); |
806 |
break; |
807 |
|
808 |
case 'F': |
809 |
NEXTDOUBLE(flval, p); |
810 |
break; |
811 |
|
812 |
case 'x': |
813 |
NEXTEXTEND(flval, p); |
814 |
break; |
815 |
|
816 |
case 'p': |
817 |
flval = NEXTPACKED(p); |
818 |
break; |
819 |
|
820 |
default: |
821 |
return -1; |
822 |
} |
823 |
(*info->fprintf_func) (info->stream, "#%g", flval); |
824 |
imm_printed: |
825 |
break; |
826 |
|
827 |
default: |
828 |
return -1; |
829 |
} |
830 |
} |
831 |
break; |
832 |
|
833 |
case 'L': |
834 |
case 'l': |
835 |
if (place == 'w') |
836 |
{ |
837 |
char doneany; |
838 |
p1 = buffer + 2; |
839 |
val = NEXTWORD (p1); |
840 |
/* Move the pointer ahead if this point is farther ahead |
841 |
than the last. */ |
842 |
p = p1 > p ? p1 : p; |
843 |
if (val == 0) |
844 |
{ |
845 |
(*info->fprintf_func) (info->stream, "#0"); |
846 |
break; |
847 |
} |
848 |
if (*d == 'l') |
849 |
{ |
850 |
register int newval = 0; |
851 |
for (regno = 0; regno < 16; ++regno) |
852 |
if (val & (0x8000 >> regno)) |
853 |
newval |= 1 << regno; |
854 |
val = newval; |
855 |
} |
856 |
val &= 0xffff; |
857 |
doneany = 0; |
858 |
for (regno = 0; regno < 16; ++regno) |
859 |
if (val & (1 << regno)) |
860 |
{ |
861 |
int first_regno; |
862 |
if (doneany) |
863 |
(*info->fprintf_func) (info->stream, "/"); |
864 |
doneany = 1; |
865 |
(*info->fprintf_func) (info->stream, "%s", reg_names[regno]); |
866 |
first_regno = regno; |
867 |
while (val & (1 << (regno + 1))) |
868 |
++regno; |
869 |
if (regno > first_regno) |
870 |
(*info->fprintf_func) (info->stream, "-%s", |
871 |
reg_names[regno]); |
872 |
} |
873 |
} |
874 |
else if (place == '3') |
875 |
{ |
876 |
/* `fmovem' insn. */ |
877 |
char doneany; |
878 |
val = fetch_arg (buffer, place, 8, info); |
879 |
if (val == 0) |
880 |
{ |
881 |
(*info->fprintf_func) (info->stream, "#0"); |
882 |
break; |
883 |
} |
884 |
if (*d == 'l') |
885 |
{ |
886 |
register int newval = 0; |
887 |
for (regno = 0; regno < 8; ++regno) |
888 |
if (val & (0x80 >> regno)) |
889 |
newval |= 1 << regno; |
890 |
val = newval; |
891 |
} |
892 |
val &= 0xff; |
893 |
doneany = 0; |
894 |
for (regno = 0; regno < 8; ++regno) |
895 |
if (val & (1 << regno)) |
896 |
{ |
897 |
int first_regno; |
898 |
if (doneany) |
899 |
(*info->fprintf_func) (info->stream, "/"); |
900 |
doneany = 1; |
901 |
(*info->fprintf_func) (info->stream, "fp%d", regno); |
902 |
first_regno = regno; |
903 |
while (val & (1 << (regno + 1))) |
904 |
++regno; |
905 |
if (regno > first_regno) |
906 |
(*info->fprintf_func) (info->stream, "-fp%d", regno); |
907 |
} |
908 |
} |
909 |
else if (place == '8') |
910 |
{ |
911 |
/* fmoveml for FP status registers */ |
912 |
(*info->fprintf_func) (info->stream, "%s", |
913 |
fpcr_names[fetch_arg (buffer, place, 3, |
914 |
info)]); |
915 |
} |
916 |
else |
917 |
return -2; |
918 |
break; |
919 |
|
920 |
case 'X': |
921 |
place = '8'; |
922 |
case 'Y': |
923 |
case 'Z': |
924 |
case 'W': |
925 |
case '0': |
926 |
case '1': |
927 |
case '2': |
928 |
case '3': |
929 |
{ |
930 |
int val = fetch_arg (buffer, place, 5, info); |
931 |
char *name = 0; |
932 |
switch (val) |
933 |
{ |
934 |
case 2: name = "tt0"; break; |
935 |
case 3: name = "tt1"; break; |
936 |
case 0x10: name = "tc"; break; |
937 |
case 0x11: name = "drp"; break; |
938 |
case 0x12: name = "srp"; break; |
939 |
case 0x13: name = "crp"; break; |
940 |
case 0x14: name = "cal"; break; |
941 |
case 0x15: name = "val"; break; |
942 |
case 0x16: name = "scc"; break; |
943 |
case 0x17: name = "ac"; break; |
944 |
case 0x18: name = "psr"; break; |
945 |
case 0x19: name = "pcsr"; break; |
946 |
case 0x1c: |
947 |
case 0x1d: |
948 |
{ |
949 |
int break_reg = ((buffer[3] >> 2) & 7); |
950 |
(*info->fprintf_func) |
951 |
(info->stream, val == 0x1c ? "bad%d" : "bac%d", |
952 |
break_reg); |
953 |
} |
954 |
break; |
955 |
default: |
956 |
(*info->fprintf_func) (info->stream, "<mmu register %d>", val); |
957 |
} |
958 |
if (name) |
959 |
(*info->fprintf_func) (info->stream, "%s", name); |
960 |
} |
961 |
break; |
962 |
|
963 |
case 'f': |
964 |
{ |
965 |
int fc = fetch_arg (buffer, place, 5, info); |
966 |
if (fc == 1) |
967 |
(*info->fprintf_func) (info->stream, "dfc"); |
968 |
else if (fc == 0) |
969 |
(*info->fprintf_func) (info->stream, "sfc"); |
970 |
else |
971 |
/* xgettext:c-format */ |
972 |
(*info->fprintf_func) (info->stream, _("<function code %d>"), fc); |
973 |
} |
974 |
break; |
975 |
|
976 |
case 'V': |
977 |
(*info->fprintf_func) (info->stream, "val"); |
978 |
break; |
979 |
|
980 |
case 't': |
981 |
{ |
982 |
int level = fetch_arg (buffer, place, 3, info); |
983 |
(*info->fprintf_func) (info->stream, "%d", level); |
984 |
} |
985 |
break; |
986 |
|
987 |
default: |
988 |
return -2; |
989 |
} |
990 |
|
991 |
return p - p0; |
992 |
} |
993 |
|
994 |
/* Fetch BITS bits from a position in the instruction specified by CODE. |
995 |
CODE is a "place to put an argument", or 'x' for a destination |
996 |
that is a general address (mode and register). |
997 |
BUFFER contains the instruction. */ |
998 |
|
999 |
static int |
1000 |
fetch_arg (buffer, code, bits, info) |
1001 |
unsigned char *buffer; |
1002 |
int code; |
1003 |
int bits; |
1004 |
disassemble_info *info; |
1005 |
{ |
1006 |
register int val = 0; |
1007 |
switch (code) |
1008 |
{ |
1009 |
case 's': |
1010 |
val = buffer[1]; |
1011 |
break; |
1012 |
|
1013 |
case 'd': /* Destination, for register or quick. */ |
1014 |
val = (buffer[0] << 8) + buffer[1]; |
1015 |
val >>= 9; |
1016 |
break; |
1017 |
|
1018 |
case 'x': /* Destination, for general arg */ |
1019 |
val = (buffer[0] << 8) + buffer[1]; |
1020 |
val >>= 6; |
1021 |
break; |
1022 |
|
1023 |
case 'k': |
1024 |
FETCH_DATA (info, buffer + 3); |
1025 |
val = (buffer[3] >> 4); |
1026 |
break; |
1027 |
|
1028 |
case 'C': |
1029 |
FETCH_DATA (info, buffer + 3); |
1030 |
val = buffer[3]; |
1031 |
break; |
1032 |
|
1033 |
case '1': |
1034 |
FETCH_DATA (info, buffer + 3); |
1035 |
val = (buffer[2] << 8) + buffer[3]; |
1036 |
val >>= 12; |
1037 |
break; |
1038 |
|
1039 |
case '2': |
1040 |
FETCH_DATA (info, buffer + 3); |
1041 |
val = (buffer[2] << 8) + buffer[3]; |
1042 |
val >>= 6; |
1043 |
break; |
1044 |
|
1045 |
case '3': |
1046 |
case 'j': |
1047 |
FETCH_DATA (info, buffer + 3); |
1048 |
val = (buffer[2] << 8) + buffer[3]; |
1049 |
break; |
1050 |
|
1051 |
case '4': |
1052 |
FETCH_DATA (info, buffer + 5); |
1053 |
val = (buffer[4] << 8) + buffer[5]; |
1054 |
val >>= 12; |
1055 |
break; |
1056 |
|
1057 |
case '5': |
1058 |
FETCH_DATA (info, buffer + 5); |
1059 |
val = (buffer[4] << 8) + buffer[5]; |
1060 |
val >>= 6; |
1061 |
break; |
1062 |
|
1063 |
case '6': |
1064 |
FETCH_DATA (info, buffer + 5); |
1065 |
val = (buffer[4] << 8) + buffer[5]; |
1066 |
break; |
1067 |
|
1068 |
case '7': |
1069 |
FETCH_DATA (info, buffer + 3); |
1070 |
val = (buffer[2] << 8) + buffer[3]; |
1071 |
val >>= 7; |
1072 |
break; |
1073 |
|
1074 |
case '8': |
1075 |
FETCH_DATA (info, buffer + 3); |
1076 |
val = (buffer[2] << 8) + buffer[3]; |
1077 |
val >>= 10; |
1078 |
break; |
1079 |
|
1080 |
case '9': |
1081 |
FETCH_DATA (info, buffer + 3); |
1082 |
val = (buffer[2] << 8) + buffer[3]; |
1083 |
val >>= 5; |
1084 |
break; |
1085 |
|
1086 |
case 'e': |
1087 |
val = (buffer[1] >> 6); |
1088 |
break; |
1089 |
|
1090 |
default: |
1091 |
abort (); |
1092 |
} |
1093 |
|
1094 |
switch (bits) |
1095 |
{ |
1096 |
case 2: |
1097 |
return val & 3; |
1098 |
case 3: |
1099 |
return val & 7; |
1100 |
case 4: |
1101 |
return val & 017; |
1102 |
case 5: |
1103 |
return val & 037; |
1104 |
case 6: |
1105 |
return val & 077; |
1106 |
case 7: |
1107 |
return val & 0177; |
1108 |
case 8: |
1109 |
return val & 0377; |
1110 |
case 12: |
1111 |
return val & 07777; |
1112 |
default: |
1113 |
abort (); |
1114 |
} |
1115 |
} |
1116 |
|
1117 |
/* Print an indexed argument. The base register is BASEREG (-1 for pc). |
1118 |
P points to extension word, in buffer. |
1119 |
ADDR is the nominal core address of that extension word. */ |
1120 |
|
1121 |
static unsigned char * |
1122 |
print_indexed (basereg, p, addr, info) |
1123 |
int basereg; |
1124 |
unsigned char *p; |
1125 |
bfd_vma addr; |
1126 |
disassemble_info *info; |
1127 |
{ |
1128 |
register int word; |
1129 |
static char *const scales[] = {"", "*2", "*4", "*8"}; |
1130 |
bfd_vma base_disp; |
1131 |
bfd_vma outer_disp; |
1132 |
char buf[40]; |
1133 |
char vmabuf[50]; |
1134 |
|
1135 |
word = NEXTWORD (p); |
1136 |
|
1137 |
/* Generate the text for the index register. |
1138 |
Where this will be output is not yet determined. */ |
1139 |
sprintf (buf, "%s.%c%s", |
1140 |
reg_names[(word >> 12) & 0xf], |
1141 |
(word & 0x800) ? 'l' : 'w', |
1142 |
scales[(word >> 9) & 3]); |
1143 |
|
1144 |
/* Handle the 68000 style of indexing. */ |
1145 |
|
1146 |
if ((word & 0x100) == 0) |
1147 |
{ |
1148 |
base_disp = word & 0xff; |
1149 |
if ((base_disp & 0x80) != 0) |
1150 |
base_disp -= 0x100; |
1151 |
if (basereg == -1) |
1152 |
base_disp += addr; |
1153 |
(*info->fprintf_func) (info->stream, "(", buf); |
1154 |
print_base (basereg, base_disp, info); |
1155 |
(*info->fprintf_func) (info->stream, ",%s)", buf); |
1156 |
return p; |
1157 |
} |
1158 |
|
1159 |
/* Handle the generalized kind. */ |
1160 |
/* First, compute the displacement to add to the base register. */ |
1161 |
|
1162 |
if (word & 0200) |
1163 |
{ |
1164 |
if (basereg == -1) |
1165 |
basereg = -3; |
1166 |
else |
1167 |
basereg = -2; |
1168 |
} |
1169 |
if (word & 0100) |
1170 |
buf[0] = '\0'; |
1171 |
base_disp = 0; |
1172 |
switch ((word >> 4) & 3) |
1173 |
{ |
1174 |
case 2: |
1175 |
base_disp = NEXTWORD (p); |
1176 |
break; |
1177 |
case 3: |
1178 |
base_disp = NEXTLONG (p); |
1179 |
} |
1180 |
if (basereg == -1) |
1181 |
base_disp += addr; |
1182 |
|
1183 |
/* Handle single-level case (not indirect) */ |
1184 |
|
1185 |
if ((word & 7) == 0) |
1186 |
{ |
1187 |
(*info->fprintf_func) (info->stream, "("); |
1188 |
print_base (basereg, base_disp, info); |
1189 |
if (buf[0] != '\0') |
1190 |
(*info->fprintf_func) (info->stream, ",%s", buf); |
1191 |
(*info->fprintf_func) (info->stream, ")"); |
1192 |
return p; |
1193 |
} |
1194 |
|
1195 |
/* Two level. Compute displacement to add after indirection. */ |
1196 |
|
1197 |
outer_disp = 0; |
1198 |
switch (word & 3) |
1199 |
{ |
1200 |
case 2: |
1201 |
outer_disp = NEXTWORD (p); |
1202 |
break; |
1203 |
case 3: |
1204 |
outer_disp = NEXTLONG (p); |
1205 |
} |
1206 |
|
1207 |
(*info->fprintf_func) (info->stream, "(["); |
1208 |
print_base (basereg, base_disp, info); |
1209 |
if ((word & 4) == 0 && buf[0] != '\0') |
1210 |
{ |
1211 |
(*info->fprintf_func) (info->stream, ",%s", buf); |
1212 |
buf[0] = '\0'; |
1213 |
} |
1214 |
if (outer_disp) |
1215 |
(*info->fprintf_func) (info->stream, "],$%08x", (uint32)outer_disp); |
1216 |
else |
1217 |
(*info->fprintf_func) (info->stream, "]"); |
1218 |
if (buf[0] != '\0') |
1219 |
(*info->fprintf_func) (info->stream, ",%s", buf); |
1220 |
(*info->fprintf_func) (info->stream, ")"); |
1221 |
|
1222 |
return p; |
1223 |
} |
1224 |
|
1225 |
/* Print a base register REGNO and displacement DISP, on INFO->STREAM. |
1226 |
REGNO = -1 for pc, -2 for none (suppressed). */ |
1227 |
|
1228 |
static void |
1229 |
print_base (regno, disp, info) |
1230 |
int regno; |
1231 |
bfd_vma disp; |
1232 |
disassemble_info *info; |
1233 |
{ |
1234 |
if (regno == -1) { |
1235 |
(*info->print_address_func) (disp, info); |
1236 |
(*info->fprintf_func) (info->stream, ",pc"); |
1237 |
} |
1238 |
else { |
1239 |
if (regno == -3) { |
1240 |
(*info->print_address_func) (disp, info); |
1241 |
(*info->fprintf_func) (info->stream, ",zpc"); |
1242 |
} |
1243 |
else if (regno == -2) |
1244 |
(*info->print_address_func) (disp, info); |
1245 |
else |
1246 |
(*info->fprintf_func) (info->stream, "$%08x,%s", (uint32)disp, reg_names[regno]); |
1247 |
} |
1248 |
} |