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