1 /* Disassemble Xilinx microblaze instructions.
2
3 Copyright (C) 2009-2016 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 "dis-asm.h"
28 #include <strings.h>
29 #include "microblaze-opc.h"
30 #include "microblaze-dis.h"
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
38
39
40 static char *
get_field(long instr,long mask,unsigned short low)41 get_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
49 static char *
get_field_imm(long instr)50 get_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
58 static char *
get_field_imm5(long instr)59 get_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
67 static char *
get_field_imm5_mbar(long instr)68 get_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
76 static char *
get_field_rfsl(long instr)77 get_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
86 static char *
get_field_imm15(long instr)87 get_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
95 static char *
get_field_special(long instr,struct op_code_struct * op)96 get_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 case REG_SHR_MASK :
143 strcpy (spr, "shr");
144 break;
145 case REG_SLR_MASK :
146 strcpy (spr, "slr");
147 break;
148 default :
149 if (((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000)
150 == REG_PVR_MASK)
151 {
152 sprintf (tmpstr, "%spvr%d", register_prefix,
153 (unsigned short)(((instr & IMM_MASK) >> IMM_LOW)
154 ^ op->immval_mask) ^ REG_PVR_MASK);
155 return (strdup (tmpstr));
156 }
157 else
158 strcpy (spr, "pc");
159 break;
160 }
161
162 sprintf (tmpstr, "%s%s", register_prefix, spr);
163 return (strdup (tmpstr));
164 }
165
166 static unsigned long
read_insn_microblaze(bfd_vma memaddr,struct disassemble_info * info,struct op_code_struct ** opr)167 read_insn_microblaze (bfd_vma memaddr,
168 struct disassemble_info *info,
169 struct op_code_struct **opr)
170 {
171 unsigned char ibytes[4];
172 int status;
173 struct op_code_struct * op;
174 unsigned long inst;
175
176 status = info->read_memory_func (memaddr, ibytes, 4, info);
177
178 if (status != 0)
179 {
180 info->memory_error_func (status, memaddr, info);
181 return 0;
182 }
183
184 if (info->endian == BFD_ENDIAN_BIG)
185 inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3];
186 else if (info->endian == BFD_ENDIAN_LITTLE)
187 inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0];
188 else
189 abort ();
190
191 /* Just a linear search of the table. */
192 for (op = opcodes; op->name != 0; op ++)
193 if (op->bit_sequence == (inst & op->opcode_mask))
194 break;
195
196 *opr = op;
197 return inst;
198 }
199
200
201 int
print_insn_microblaze(bfd_vma memaddr,struct disassemble_info * info)202 print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
203 {
204 fprintf_ftype print_func = info->fprintf_func;
205 void * stream = info->stream;
206 unsigned long inst, prev_inst;
207 struct op_code_struct * op, *pop;
208 int immval = 0;
209 bfd_boolean immfound = FALSE;
210 static bfd_vma prev_insn_addr = -1; /* Init the prev insn addr. */
211 static int prev_insn_vma = -1; /* Init the prev insn vma. */
212 int curr_insn_vma = info->buffer_vma;
213
214 info->bytes_per_chunk = 4;
215
216 inst = read_insn_microblaze (memaddr, info, &op);
217 if (inst == 0)
218 return -1;
219
220 if (prev_insn_vma == curr_insn_vma)
221 {
222 if (memaddr-(info->bytes_per_chunk) == prev_insn_addr)
223 {
224 prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
225 if (prev_inst == 0)
226 return -1;
227 if (pop->instr == imm)
228 {
229 immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000;
230 immfound = TRUE;
231 }
232 else
233 {
234 immval = 0;
235 immfound = FALSE;
236 }
237 }
238 }
239
240 /* Make curr insn as prev insn. */
241 prev_insn_addr = memaddr;
242 prev_insn_vma = curr_insn_vma;
243
244 if (op->name == NULL)
245 print_func (stream, ".short 0x%04x", (unsigned int) inst);
246 else
247 {
248 print_func (stream, "%s", op->name);
249
250 switch (op->inst_type)
251 {
252 case INST_TYPE_RD_R1_R2:
253 print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
254 get_field_r1(inst), get_field_r2 (inst));
255 break;
256 case INST_TYPE_RD_R1_IMM:
257 print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
258 get_field_r1(inst), get_field_imm (inst));
259 if (info->print_address_func && get_int_field_r1 (inst) == 0
260 && info->symbol_at_address_func)
261 {
262 if (immfound)
263 immval |= (get_int_field_imm (inst) & 0x0000ffff);
264 else
265 {
266 immval = get_int_field_imm (inst);
267 if (immval & 0x8000)
268 immval |= 0xFFFF0000;
269 }
270 if (immval > 0 && info->symbol_at_address_func (immval, info))
271 {
272 print_func (stream, "\t// ");
273 info->print_address_func (immval, info);
274 }
275 }
276 break;
277 case INST_TYPE_RD_R1_IMM5:
278 print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
279 get_field_r1(inst), get_field_imm5 (inst));
280 break;
281 case INST_TYPE_RD_RFSL:
282 print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_rfsl (inst));
283 break;
284 case INST_TYPE_R1_RFSL:
285 print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_rfsl (inst));
286 break;
287 case INST_TYPE_RD_SPECIAL:
288 print_func (stream, "\t%s, %s", get_field_rd (inst),
289 get_field_special (inst, op));
290 break;
291 case INST_TYPE_SPECIAL_R1:
292 print_func (stream, "\t%s, %s", get_field_special (inst, op),
293 get_field_r1(inst));
294 break;
295 case INST_TYPE_RD_R1:
296 print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_r1 (inst));
297 break;
298 case INST_TYPE_R1_R2:
299 print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_r2 (inst));
300 break;
301 case INST_TYPE_R1_IMM:
302 print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_imm (inst));
303 /* The non-pc relative instructions are returns, which shouldn't
304 have a label printed. */
305 if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET
306 && info->symbol_at_address_func)
307 {
308 if (immfound)
309 immval |= (get_int_field_imm (inst) & 0x0000ffff);
310 else
311 {
312 immval = get_int_field_imm (inst);
313 if (immval & 0x8000)
314 immval |= 0xFFFF0000;
315 }
316 immval += memaddr;
317 if (immval > 0 && info->symbol_at_address_func (immval, info))
318 {
319 print_func (stream, "\t// ");
320 info->print_address_func (immval, info);
321 }
322 else
323 {
324 print_func (stream, "\t\t// ");
325 print_func (stream, "%x", immval);
326 }
327 }
328 break;
329 case INST_TYPE_RD_IMM:
330 print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_imm (inst));
331 if (info->print_address_func && info->symbol_at_address_func)
332 {
333 if (immfound)
334 immval |= (get_int_field_imm (inst) & 0x0000ffff);
335 else
336 {
337 immval = get_int_field_imm (inst);
338 if (immval & 0x8000)
339 immval |= 0xFFFF0000;
340 }
341 if (op->inst_offset_type == INST_PC_OFFSET)
342 immval += (int) memaddr;
343 if (info->symbol_at_address_func (immval, info))
344 {
345 print_func (stream, "\t// ");
346 info->print_address_func (immval, info);
347 }
348 }
349 break;
350 case INST_TYPE_IMM:
351 print_func (stream, "\t%s", get_field_imm (inst));
352 if (info->print_address_func && info->symbol_at_address_func
353 && op->instr != imm)
354 {
355 if (immfound)
356 immval |= (get_int_field_imm (inst) & 0x0000ffff);
357 else
358 {
359 immval = get_int_field_imm (inst);
360 if (immval & 0x8000)
361 immval |= 0xFFFF0000;
362 }
363 if (op->inst_offset_type == INST_PC_OFFSET)
364 immval += (int) memaddr;
365 if (immval > 0 && info->symbol_at_address_func (immval, info))
366 {
367 print_func (stream, "\t// ");
368 info->print_address_func (immval, info);
369 }
370 else if (op->inst_offset_type == INST_PC_OFFSET)
371 {
372 print_func (stream, "\t\t// ");
373 print_func (stream, "%x", immval);
374 }
375 }
376 break;
377 case INST_TYPE_RD_R2:
378 print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
379 break;
380 case INST_TYPE_R2:
381 print_func (stream, "\t%s", get_field_r2 (inst));
382 break;
383 case INST_TYPE_R1:
384 print_func (stream, "\t%s", get_field_r1 (inst));
385 break;
386 case INST_TYPE_R1_R2_SPECIAL:
387 print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_r2 (inst));
388 break;
389 case INST_TYPE_RD_IMM15:
390 print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_imm15 (inst));
391 break;
392 /* For mbar insn. */
393 case INST_TYPE_IMM5:
394 print_func (stream, "\t%s", get_field_imm5_mbar (inst));
395 break;
396 /* For mbar 16 or sleep insn. */
397 case INST_TYPE_NONE:
398 break;
399 /* For tuqula instruction */
400 case INST_TYPE_RD:
401 print_func (stream, "\t%s", get_field_rd (inst));
402 break;
403 case INST_TYPE_RFSL:
404 print_func (stream, "\t%s", get_field_rfsl (inst));
405 break;
406 default:
407 /* If the disassembler lags the instruction set. */
408 print_func (stream, "\tundecoded operands, inst is 0x%04x", (unsigned int) inst);
409 break;
410 }
411 }
412
413 /* Say how many bytes we consumed. */
414 return 4;
415 }
416
417 enum microblaze_instr
get_insn_microblaze(long inst,bfd_boolean * isunsignedimm,enum microblaze_instr_type * insn_type,short * delay_slots)418 get_insn_microblaze (long inst,
419 bfd_boolean *isunsignedimm,
420 enum microblaze_instr_type *insn_type,
421 short *delay_slots)
422 {
423 struct op_code_struct * op;
424 *isunsignedimm = FALSE;
425
426 /* Just a linear search of the table. */
427 for (op = opcodes; op->name != 0; op ++)
428 if (op->bit_sequence == (inst & op->opcode_mask))
429 break;
430
431 if (op->name == 0)
432 return invalid_inst;
433 else
434 {
435 *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM);
436 *insn_type = op->instr_type;
437 *delay_slots = op->delay_slots;
438 return op->instr;
439 }
440 }
441
442 enum microblaze_instr
microblaze_decode_insn(long insn,int * rd,int * ra,int * rb,int * immed)443 microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *immed)
444 {
445 enum microblaze_instr op;
446 bfd_boolean t1;
447 enum microblaze_instr_type t2;
448 short t3;
449
450 op = get_insn_microblaze (insn, &t1, &t2, &t3);
451 *rd = (insn & RD_MASK) >> RD_LOW;
452 *ra = (insn & RA_MASK) >> RA_LOW;
453 *rb = (insn & RB_MASK) >> RB_LOW;
454 t3 = (insn & IMM_MASK) >> IMM_LOW;
455 *immed = (int) t3;
456 return (op);
457 }
458
459 unsigned long
microblaze_get_target_address(long inst,bfd_boolean immfound,int immval,long pcval,long r1val,long r2val,bfd_boolean * targetvalid,bfd_boolean * unconditionalbranch)460 microblaze_get_target_address (long inst, bfd_boolean immfound, int immval,
461 long pcval, long r1val, long r2val,
462 bfd_boolean *targetvalid,
463 bfd_boolean *unconditionalbranch)
464 {
465 struct op_code_struct * op;
466 long targetaddr = 0;
467
468 *unconditionalbranch = FALSE;
469 /* Just a linear search of the table. */
470 for (op = opcodes; op->name != 0; op ++)
471 if (op->bit_sequence == (inst & op->opcode_mask))
472 break;
473
474 if (op->name == 0)
475 {
476 *targetvalid = FALSE;
477 }
478 else if (op->instr_type == branch_inst)
479 {
480 switch (op->inst_type)
481 {
482 case INST_TYPE_R2:
483 *unconditionalbranch = TRUE;
484 /* Fall through. */
485 case INST_TYPE_RD_R2:
486 case INST_TYPE_R1_R2:
487 targetaddr = r2val;
488 *targetvalid = TRUE;
489 if (op->inst_offset_type == INST_PC_OFFSET)
490 targetaddr += pcval;
491 break;
492 case INST_TYPE_IMM:
493 *unconditionalbranch = TRUE;
494 /* Fall through. */
495 case INST_TYPE_RD_IMM:
496 case INST_TYPE_R1_IMM:
497 if (immfound)
498 {
499 targetaddr = (immval << 16) & 0xffff0000;
500 targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
501 }
502 else
503 {
504 targetaddr = get_int_field_imm (inst);
505 if (targetaddr & 0x8000)
506 targetaddr |= 0xFFFF0000;
507 }
508 if (op->inst_offset_type == INST_PC_OFFSET)
509 targetaddr += pcval;
510 *targetvalid = TRUE;
511 break;
512 default:
513 *targetvalid = FALSE;
514 break;
515 }
516 }
517 else if (op->instr_type == return_inst)
518 {
519 if (immfound)
520 {
521 targetaddr = (immval << 16) & 0xffff0000;
522 targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
523 }
524 else
525 {
526 targetaddr = get_int_field_imm (inst);
527 if (targetaddr & 0x8000)
528 targetaddr |= 0xFFFF0000;
529 }
530 targetaddr += r1val;
531 *targetvalid = TRUE;
532 }
533 else
534 *targetvalid = FALSE;
535 return targetaddr;
536 }
537