* arm.cc (Arm_relocate_functions::got_prel) New function.
[deliverable/binutils-gdb.git] / opcodes / microblaze-dis.c
CommitLineData
7ba29e2a
NC
1/* Disassemble Xilinx microblaze instructions.
2
3 Copyright 2009 Free Software Foundation, Inc.
4
5 This file is part of the GNU opcodes library.
6
7 This library is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11
12 It is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this file; see the file COPYING. If not, write to the
19 Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
20 MA 02110-1301, USA. */
21
22
23#include "sysdep.h"
24#define STATIC_TABLE
25#define DEFINE_TABLE
26
27#include "microblaze-opc.h"
28#include "dis-asm.h"
29#include <strings.h>
30
31#define get_field_rd(instr) get_field (instr, RD_MASK, RD_LOW)
32#define get_field_r1(instr) get_field (instr, RA_MASK, RA_LOW)
33#define get_field_r2(instr) get_field (instr, RB_MASK, RB_LOW)
34#define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW)
35#define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW)
36
fe2d172c
ME
37
38enum microblaze_instr get_insn_microblaze (long, bfd_boolean *,
39 enum microblaze_instr_type *, short *);
40unsigned long microblaze_get_target_address (long, bfd_boolean, int, long, long,
41 long, bfd_boolean *, bfd_boolean *);
42enum microblaze_instr microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *imm);
43
7ba29e2a
NC
44static char *
45get_field (long instr, long mask, unsigned short low)
46{
47 char tmpstr[25];
48
49 sprintf (tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low));
50 return (strdup (tmpstr));
51}
52
53static char *
54get_field_imm (long instr)
55{
56 char tmpstr[25];
57
58 sprintf (tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW));
59 return (strdup (tmpstr));
60}
61
62static char *
63get_field_imm5 (long instr)
64{
65 char tmpstr[25];
66
67 sprintf (tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW));
68 return (strdup (tmpstr));
69}
70
71static char *
72get_field_rfsl (long instr)
73{
74 char tmpstr[25];
75
76 sprintf (tmpstr, "%s%d", fsl_register_prefix,
77 (short)((instr & RFSL_MASK) >> IMM_LOW));
78 return (strdup (tmpstr));
79}
80
81static char *
82get_field_imm15 (long instr)
83{
84 char tmpstr[25];
85
86 sprintf (tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW));
87 return (strdup (tmpstr));
88}
89
90static char *
91get_field_special (long instr, struct op_code_struct * op)
92{
93 char tmpstr[25];
94 char spr[6];
95
96 switch ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask))
97 {
98 case REG_MSR_MASK :
99 strcpy (spr, "msr");
100 break;
101 case REG_PC_MASK :
102 strcpy (spr, "pc");
103 break;
104 case REG_EAR_MASK :
105 strcpy (spr, "ear");
106 break;
107 case REG_ESR_MASK :
108 strcpy (spr, "esr");
109 break;
110 case REG_FSR_MASK :
111 strcpy (spr, "fsr");
112 break;
113 case REG_BTR_MASK :
114 strcpy (spr, "btr");
115 break;
116 case REG_EDR_MASK :
117 strcpy (spr, "edr");
118 break;
119 case REG_PID_MASK :
120 strcpy (spr, "pid");
121 break;
122 case REG_ZPR_MASK :
123 strcpy (spr, "zpr");
124 break;
125 case REG_TLBX_MASK :
126 strcpy (spr, "tlbx");
127 break;
128 case REG_TLBLO_MASK :
129 strcpy (spr, "tlblo");
130 break;
131 case REG_TLBHI_MASK :
132 strcpy (spr, "tlbhi");
133 break;
134 case REG_TLBSX_MASK :
135 strcpy (spr, "tlbsx");
136 break;
137 default :
138 if (((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000)
139 == REG_PVR_MASK)
140 {
141 sprintf (tmpstr, "%spvr%d", register_prefix,
142 (unsigned short)(((instr & IMM_MASK) >> IMM_LOW)
143 ^ op->immval_mask) ^ REG_PVR_MASK);
144 return (strdup (tmpstr));
145 }
146 else
147 strcpy (spr, "pc");
148 break;
149 }
150
151 sprintf (tmpstr, "%s%s", register_prefix, spr);
152 return (strdup (tmpstr));
153}
154
155static unsigned long
156read_insn_microblaze (bfd_vma memaddr,
157 struct disassemble_info *info,
158 struct op_code_struct **opr)
159{
160 unsigned char ibytes[4];
161 int status;
162 struct op_code_struct * op;
163 unsigned long inst;
164
165 status = info->read_memory_func (memaddr, ibytes, 4, info);
166
167 if (status != 0)
168 {
169 info->memory_error_func (status, memaddr, info);
170 return 0;
171 }
172
173 if (info->endian == BFD_ENDIAN_BIG)
174 inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3];
175 else if (info->endian == BFD_ENDIAN_LITTLE)
176 inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0];
177 else
178 abort ();
179
180 /* Just a linear search of the table. */
181 for (op = opcodes; op->name != 0; op ++)
182 if (op->bit_sequence == (inst & op->opcode_mask))
183 break;
184
185 *opr = op;
186 return inst;
187}
188
189
190int
191print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
192{
193 fprintf_ftype fprintf = info->fprintf_func;
194 void * stream = info->stream;
195 unsigned long inst, prev_inst;
196 struct op_code_struct * op, *pop;
197 int immval = 0;
198 bfd_boolean immfound = FALSE;
199 static bfd_vma prev_insn_addr = -1; /* Init the prev insn addr. */
200 static int prev_insn_vma = -1; /* Init the prev insn vma. */
201 int curr_insn_vma = info->buffer_vma;
202
203 info->bytes_per_chunk = 4;
204
205 inst = read_insn_microblaze (memaddr, info, &op);
206 if (inst == 0)
207 return -1;
208
209 if (prev_insn_vma == curr_insn_vma)
210 {
211 if (memaddr-(info->bytes_per_chunk) == prev_insn_addr)
212 {
213 prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
214 if (prev_inst == 0)
215 return -1;
216 if (pop->instr == imm)
217 {
218 immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000;
219 immfound = TRUE;
220 }
221 else
222 {
223 immval = 0;
224 immfound = FALSE;
225 }
226 }
227 }
228
229 /* Make curr insn as prev insn. */
230 prev_insn_addr = memaddr;
231 prev_insn_vma = curr_insn_vma;
232
233 if (op->name == NULL)
234 fprintf (stream, ".short 0x%04x", inst);
235 else
236 {
237 fprintf (stream, "%s", op->name);
238
239 switch (op->inst_type)
240 {
241 case INST_TYPE_RD_R1_R2:
242 fprintf (stream, "\t%s, %s, %s", get_field_rd (inst),
243 get_field_r1(inst), get_field_r2 (inst));
244 break;
245 case INST_TYPE_RD_R1_IMM:
246 fprintf (stream, "\t%s, %s, %s", get_field_rd (inst),
247 get_field_r1(inst), get_field_imm (inst));
248 if (info->print_address_func && get_int_field_r1 (inst) == 0
249 && info->symbol_at_address_func)
250 {
251 if (immfound)
252 immval |= (get_int_field_imm (inst) & 0x0000ffff);
253 else
254 {
255 immval = get_int_field_imm (inst);
256 if (immval & 0x8000)
257 immval |= 0xFFFF0000;
258 }
259 if (immval > 0 && info->symbol_at_address_func (immval, info))
260 {
261 fprintf (stream, "\t// ");
262 info->print_address_func (immval, info);
263 }
264 }
265 break;
266 case INST_TYPE_RD_R1_IMM5:
267 fprintf (stream, "\t%s, %s, %s", get_field_rd (inst),
268 get_field_r1(inst), get_field_imm5 (inst));
269 break;
270 case INST_TYPE_RD_RFSL:
271 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_rfsl (inst));
272 break;
273 case INST_TYPE_R1_RFSL:
274 fprintf (stream, "\t%s, %s", get_field_r1 (inst), get_field_rfsl (inst));
275 break;
276 case INST_TYPE_RD_SPECIAL:
277 fprintf (stream, "\t%s, %s", get_field_rd (inst),
278 get_field_special (inst, op));
279 break;
280 case INST_TYPE_SPECIAL_R1:
281 fprintf (stream, "\t%s, %s", get_field_special (inst, op),
282 get_field_r1(inst));
283 break;
284 case INST_TYPE_RD_R1:
285 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_r1 (inst));
286 break;
287 case INST_TYPE_R1_R2:
288 fprintf (stream, "\t%s, %s", get_field_r1 (inst), get_field_r2 (inst));
289 break;
290 case INST_TYPE_R1_IMM:
291 fprintf (stream, "\t%s, %s", get_field_r1 (inst), get_field_imm (inst));
292 /* The non-pc relative instructions are returns, which shouldn't
293 have a label printed. */
294 if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET
295 && info->symbol_at_address_func)
296 {
297 if (immfound)
298 immval |= (get_int_field_imm (inst) & 0x0000ffff);
299 else
300 {
301 immval = get_int_field_imm (inst);
302 if (immval & 0x8000)
303 immval |= 0xFFFF0000;
304 }
305 immval += memaddr;
306 if (immval > 0 && info->symbol_at_address_func (immval, info))
307 {
308 fprintf (stream, "\t// ");
309 info->print_address_func (immval, info);
310 }
311 else
312 {
313 fprintf (stream, "\t\t// ");
314 fprintf (stream, "%x", immval);
315 }
316 }
317 break;
318 case INST_TYPE_RD_IMM:
319 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_imm (inst));
320 if (info->print_address_func && info->symbol_at_address_func)
321 {
322 if (immfound)
323 immval |= (get_int_field_imm (inst) & 0x0000ffff);
324 else
325 {
326 immval = get_int_field_imm (inst);
327 if (immval & 0x8000)
328 immval |= 0xFFFF0000;
329 }
330 if (op->inst_offset_type == INST_PC_OFFSET)
331 immval += (int) memaddr;
332 if (info->symbol_at_address_func (immval, info))
333 {
334 fprintf (stream, "\t// ");
335 info->print_address_func (immval, info);
336 }
337 }
338 break;
339 case INST_TYPE_IMM:
340 fprintf (stream, "\t%s", get_field_imm (inst));
341 if (info->print_address_func && info->symbol_at_address_func
342 && op->instr != imm)
343 {
344 if (immfound)
345 immval |= (get_int_field_imm (inst) & 0x0000ffff);
346 else
347 {
348 immval = get_int_field_imm (inst);
349 if (immval & 0x8000)
350 immval |= 0xFFFF0000;
351 }
352 if (op->inst_offset_type == INST_PC_OFFSET)
353 immval += (int) memaddr;
354 if (immval > 0 && info->symbol_at_address_func (immval, info))
355 {
356 fprintf (stream, "\t// ");
357 info->print_address_func (immval, info);
358 }
359 else if (op->inst_offset_type == INST_PC_OFFSET)
360 {
361 fprintf (stream, "\t\t// ");
362 fprintf (stream, "%x", immval);
363 }
364 }
365 break;
366 case INST_TYPE_RD_R2:
367 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
368 break;
369 case INST_TYPE_R2:
370 fprintf (stream, "\t%s", get_field_r2 (inst));
371 break;
372 case INST_TYPE_R1:
373 fprintf (stream, "\t%s", get_field_r1 (inst));
374 break;
375 case INST_TYPE_RD_R1_SPECIAL:
376 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
377 break;
378 case INST_TYPE_RD_IMM15:
379 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_imm15 (inst));
380 break;
381 /* For tuqula instruction */
382 case INST_TYPE_RD:
383 fprintf (stream, "\t%s", get_field_rd (inst));
384 break;
385 case INST_TYPE_RFSL:
386 fprintf (stream, "\t%s", get_field_rfsl (inst));
387 break;
388 default:
389 /* If the disassembler lags the instruction set. */
390 fprintf (stream, "\tundecoded operands, inst is 0x%04x", inst);
391 break;
392 }
393 }
394
395 /* Say how many bytes we consumed. */
396 return 4;
397}
fe2d172c
ME
398
399enum microblaze_instr
7ba29e2a
NC
400get_insn_microblaze (long inst,
401 bfd_boolean *isunsignedimm,
402 enum microblaze_instr_type *insn_type,
403 short *delay_slots)
404{
405 struct op_code_struct * op;
406 *isunsignedimm = FALSE;
407
408 /* Just a linear search of the table. */
409 for (op = opcodes; op->name != 0; op ++)
410 if (op->bit_sequence == (inst & op->opcode_mask))
411 break;
412
413 if (op->name == 0)
414 return invalid_inst;
415 else
416 {
417 *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM);
418 *insn_type = op->instr_type;
419 *delay_slots = op->delay_slots;
420 return op->instr;
421 }
422}
423
7ba29e2a
NC
424enum microblaze_instr
425microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *imm)
426{
427 enum microblaze_instr op;
428 bfd_boolean t1;
429 enum microblaze_instr_type t2;
430 short t3;
431
432 op = get_insn_microblaze (insn, &t1, &t2, &t3);
433 *rd = (insn & RD_MASK) >> RD_LOW;
434 *ra = (insn & RA_MASK) >> RA_LOW;
435 *rb = (insn & RB_MASK) >> RB_LOW;
436 t3 = (insn & IMM_MASK) >> IMM_LOW;
437 *imm = (int) t3;
438 return (op);
439}
440
441unsigned long
442microblaze_get_target_address (long inst, bfd_boolean immfound, int immval,
443 long pcval, long r1val, long r2val,
444 bfd_boolean *targetvalid,
445 bfd_boolean *unconditionalbranch)
446{
447 struct op_code_struct * op;
448 long targetaddr = 0;
449
450 *unconditionalbranch = FALSE;
451 /* Just a linear search of the table. */
452 for (op = opcodes; op->name != 0; op ++)
453 if (op->bit_sequence == (inst & op->opcode_mask))
454 break;
455
456 if (op->name == 0)
457 {
458 *targetvalid = FALSE;
459 }
460 else if (op->instr_type == branch_inst)
461 {
462 switch (op->inst_type)
463 {
464 case INST_TYPE_R2:
465 *unconditionalbranch = TRUE;
466 /* Fall through. */
467 case INST_TYPE_RD_R2:
468 case INST_TYPE_R1_R2:
469 targetaddr = r2val;
470 *targetvalid = TRUE;
471 if (op->inst_offset_type == INST_PC_OFFSET)
472 targetaddr += pcval;
473 break;
474 case INST_TYPE_IMM:
475 *unconditionalbranch = TRUE;
476 /* Fall through. */
477 case INST_TYPE_RD_IMM:
478 case INST_TYPE_R1_IMM:
479 if (immfound)
480 {
481 targetaddr = (immval << 16) & 0xffff0000;
482 targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
483 }
484 else
485 {
486 targetaddr = get_int_field_imm (inst);
487 if (targetaddr & 0x8000)
488 targetaddr |= 0xFFFF0000;
489 }
490 if (op->inst_offset_type == INST_PC_OFFSET)
491 targetaddr += pcval;
492 *targetvalid = TRUE;
493 break;
494 default:
495 *targetvalid = FALSE;
496 break;
497 }
498 }
499 else if (op->instr_type == return_inst)
500 {
501 if (immfound)
502 {
503 targetaddr = (immval << 16) & 0xffff0000;
504 targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
505 }
506 else
507 {
508 targetaddr = get_int_field_imm (inst);
509 if (targetaddr & 0x8000)
510 targetaddr |= 0xFFFF0000;
511 }
512 targetaddr += r1val;
513 *targetvalid = TRUE;
514 }
515 else
516 *targetvalid = FALSE;
517 return targetaddr;
518}
This page took 0.052864 seconds and 4 git commands to generate.