Improve objdump's handling of compressed sections.
[binutils-gdb.git] / opcodes / microblaze-dis.c
blob0b5262255fb4c736c27b424c8d633db1beef7003
1 /* Disassemble Xilinx microblaze instructions.
3 Copyright (C) 2009-2023 Free Software Foundation, Inc.
5 This file is part of the GNU opcodes library.
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.
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.
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. */
23 #include "sysdep.h"
24 #define STATIC_TABLE
25 #define DEFINE_TABLE
27 #include "disassemble.h"
28 #include <strings.h>
29 #include "microblaze-opc.h"
30 #include "microblaze-dis.h"
32 #define get_field_rd(buf, instr) get_field (buf, instr, RD_MASK, RD_LOW)
33 #define get_field_r1(buf, instr) get_field (buf, instr, RA_MASK, RA_LOW)
34 #define get_field_r2(buf, instr) get_field (buf, 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)
38 #define NUM_STRBUFS 4
39 #define STRBUF_SIZE 25
41 struct string_buf
43 unsigned int which;
44 char str[NUM_STRBUFS][STRBUF_SIZE];
47 static inline char *
48 strbuf (struct string_buf *buf)
50 #ifdef ENABLE_CHECKING
51 if (buf->which >= NUM_STRBUFS)
52 abort ();
53 #endif
54 return buf->str[buf->which++];
57 static char *
58 get_field (struct string_buf *buf, long instr, long mask, unsigned short low)
60 char *p = strbuf (buf);
62 sprintf (p, "%s%d", register_prefix, (int)((instr & mask) >> low));
63 return p;
66 static char *
67 get_field_imm (struct string_buf *buf, long instr)
69 char *p = strbuf (buf);
71 sprintf (p, "%d", (short)((instr & IMM_MASK) >> IMM_LOW));
72 return p;
75 static char *
76 get_field_imm5 (struct string_buf *buf, long instr)
78 char *p = strbuf (buf);
80 sprintf (p, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW));
81 return p;
84 static char *
85 get_field_imm5_mbar (struct string_buf *buf, long instr)
87 char *p = strbuf (buf);
89 sprintf (p, "%d", (short)((instr & IMM5_MBAR_MASK) >> IMM_MBAR));
90 return p;
93 static char *
94 get_field_immw (struct string_buf *buf, long instr)
96 char *p = strbuf (buf);
98 if (instr & 0x00004000)
99 sprintf (p, "%d", (short)(((instr & IMM5_WIDTH_MASK)
100 >> IMM_WIDTH_LOW))); /* bsefi */
101 else
102 sprintf (p, "%d", (short)(((instr & IMM5_WIDTH_MASK) >>
103 IMM_WIDTH_LOW) - ((instr & IMM5_MASK) >>
104 IMM_LOW) + 1)); /* bsifi */
105 return p;
108 static char *
109 get_field_rfsl (struct string_buf *buf, long instr)
111 char *p = strbuf (buf);
113 sprintf (p, "%s%d", fsl_register_prefix,
114 (short)((instr & RFSL_MASK) >> IMM_LOW));
115 return p;
118 static char *
119 get_field_imm15 (struct string_buf *buf, long instr)
121 char *p = strbuf (buf);
123 sprintf (p, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW));
124 return p;
127 static char *
128 get_field_special (struct string_buf *buf, long instr,
129 const struct op_code_struct *op)
131 char *p = strbuf (buf);
132 char *spr;
134 switch ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask))
136 case REG_MSR_MASK :
137 spr = "msr";
138 break;
139 case REG_PC_MASK :
140 spr = "pc";
141 break;
142 case REG_EAR_MASK :
143 spr = "ear";
144 break;
145 case REG_ESR_MASK :
146 spr = "esr";
147 break;
148 case REG_FSR_MASK :
149 spr = "fsr";
150 break;
151 case REG_BTR_MASK :
152 spr = "btr";
153 break;
154 case REG_EDR_MASK :
155 spr = "edr";
156 break;
157 case REG_PID_MASK :
158 spr = "pid";
159 break;
160 case REG_ZPR_MASK :
161 spr = "zpr";
162 break;
163 case REG_TLBX_MASK :
164 spr = "tlbx";
165 break;
166 case REG_TLBLO_MASK :
167 spr = "tlblo";
168 break;
169 case REG_TLBHI_MASK :
170 spr = "tlbhi";
171 break;
172 case REG_TLBSX_MASK :
173 spr = "tlbsx";
174 break;
175 case REG_SHR_MASK :
176 spr = "shr";
177 break;
178 case REG_SLR_MASK :
179 spr = "slr";
180 break;
181 default :
182 if (((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000)
183 == REG_PVR_MASK)
185 sprintf (p, "%spvr%d", register_prefix,
186 (unsigned short)(((instr & IMM_MASK) >> IMM_LOW)
187 ^ op->immval_mask) ^ REG_PVR_MASK);
188 return p;
190 else
191 spr = "pc";
192 break;
195 sprintf (p, "%s%s", register_prefix, spr);
196 return p;
199 static unsigned long
200 read_insn_microblaze (bfd_vma memaddr,
201 struct disassemble_info *info,
202 const struct op_code_struct **opr)
204 unsigned char ibytes[4];
205 int status;
206 const struct op_code_struct *op;
207 unsigned long inst;
209 status = info->read_memory_func (memaddr, ibytes, 4, info);
211 if (status != 0)
213 info->memory_error_func (status, memaddr, info);
214 return 0;
217 if (info->endian == BFD_ENDIAN_BIG)
218 inst = (((unsigned) ibytes[0] << 24) | (ibytes[1] << 16)
219 | (ibytes[2] << 8) | ibytes[3]);
220 else if (info->endian == BFD_ENDIAN_LITTLE)
221 inst = (((unsigned) ibytes[3] << 24) | (ibytes[2] << 16)
222 | (ibytes[1] << 8) | ibytes[0]);
223 else
224 abort ();
226 /* Just a linear search of the table. */
227 for (op = microblaze_opcodes; op->name != 0; op ++)
228 if (op->bit_sequence == (inst & op->opcode_mask))
229 break;
231 *opr = op;
232 return inst;
237 print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
239 fprintf_ftype print_func = info->fprintf_func;
240 void *stream = info->stream;
241 unsigned long inst, prev_inst;
242 const struct op_code_struct *op, *pop;
243 int immval = 0;
244 bool immfound = false;
245 static bfd_vma prev_insn_addr = -1; /* Init the prev insn addr. */
246 static int prev_insn_vma = -1; /* Init the prev insn vma. */
247 int curr_insn_vma = info->buffer_vma;
248 struct string_buf buf;
250 buf.which = 0;
251 info->bytes_per_chunk = 4;
253 inst = read_insn_microblaze (memaddr, info, &op);
254 if (inst == 0)
255 return -1;
257 if (prev_insn_vma == curr_insn_vma)
259 if (memaddr-(info->bytes_per_chunk) == prev_insn_addr)
261 prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
262 if (prev_inst == 0)
263 return -1;
264 if (pop->instr == imm)
266 immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000;
267 immfound = true;
269 else
271 immval = 0;
272 immfound = false;
277 /* Make curr insn as prev insn. */
278 prev_insn_addr = memaddr;
279 prev_insn_vma = curr_insn_vma;
281 if (op->name == NULL)
282 print_func (stream, ".long 0x%04x", (unsigned int) inst);
283 else
285 print_func (stream, "%s", op->name);
287 switch (op->inst_type)
289 case INST_TYPE_RD_R1_R2:
290 print_func (stream, "\t%s, %s, %s", get_field_rd (&buf, inst),
291 get_field_r1 (&buf, inst), get_field_r2 (&buf, inst));
292 break;
293 case INST_TYPE_RD_R1_IMM:
294 print_func (stream, "\t%s, %s, %s", get_field_rd (&buf, inst),
295 get_field_r1 (&buf, inst), get_field_imm (&buf, inst));
296 if (info->print_address_func && get_int_field_r1 (inst) == 0
297 && info->symbol_at_address_func)
299 if (immfound)
300 immval |= (get_int_field_imm (inst) & 0x0000ffff);
301 else
303 immval = get_int_field_imm (inst);
304 if (immval & 0x8000)
305 immval |= 0xFFFF0000;
307 if (immval > 0 && info->symbol_at_address_func (immval, info))
309 print_func (stream, "\t// ");
310 info->print_address_func (immval, info);
313 break;
314 case INST_TYPE_RD_R1_IMM5:
315 print_func (stream, "\t%s, %s, %s", get_field_rd (&buf, inst),
316 get_field_r1 (&buf, inst), get_field_imm5 (&buf, inst));
317 break;
318 case INST_TYPE_RD_RFSL:
319 print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
320 get_field_rfsl (&buf, inst));
321 break;
322 case INST_TYPE_R1_RFSL:
323 print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst),
324 get_field_rfsl (&buf, inst));
325 break;
326 case INST_TYPE_RD_SPECIAL:
327 print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
328 get_field_special (&buf, inst, op));
329 break;
330 case INST_TYPE_SPECIAL_R1:
331 print_func (stream, "\t%s, %s", get_field_special (&buf, inst, op),
332 get_field_r1 (&buf, inst));
333 break;
334 case INST_TYPE_RD_R1:
335 print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
336 get_field_r1 (&buf, inst));
337 break;
338 case INST_TYPE_R1_R2:
339 print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst),
340 get_field_r2 (&buf, inst));
341 break;
342 case INST_TYPE_R1_IMM:
343 print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst),
344 get_field_imm (&buf, inst));
345 /* The non-pc relative instructions are returns, which shouldn't
346 have a label printed. */
347 if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET
348 && info->symbol_at_address_func)
350 if (immfound)
351 immval |= (get_int_field_imm (inst) & 0x0000ffff);
352 else
354 immval = get_int_field_imm (inst);
355 if (immval & 0x8000)
356 immval |= 0xFFFF0000;
358 immval += memaddr;
359 if (immval > 0 && info->symbol_at_address_func (immval, info))
361 print_func (stream, "\t// ");
362 info->print_address_func (immval, info);
364 else
366 print_func (stream, "\t\t// ");
367 print_func (stream, "%x", immval);
370 break;
371 case INST_TYPE_RD_IMM:
372 print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
373 get_field_imm (&buf, inst));
374 if (info->print_address_func && info->symbol_at_address_func)
376 if (immfound)
377 immval |= (get_int_field_imm (inst) & 0x0000ffff);
378 else
380 immval = get_int_field_imm (inst);
381 if (immval & 0x8000)
382 immval |= 0xFFFF0000;
384 if (op->inst_offset_type == INST_PC_OFFSET)
385 immval += (int) memaddr;
386 if (info->symbol_at_address_func (immval, info))
388 print_func (stream, "\t// ");
389 info->print_address_func (immval, info);
392 break;
393 case INST_TYPE_IMM:
394 print_func (stream, "\t%s", get_field_imm (&buf, inst));
395 if (info->print_address_func && info->symbol_at_address_func
396 && op->instr != imm)
398 if (immfound)
399 immval |= (get_int_field_imm (inst) & 0x0000ffff);
400 else
402 immval = get_int_field_imm (inst);
403 if (immval & 0x8000)
404 immval |= 0xFFFF0000;
406 if (op->inst_offset_type == INST_PC_OFFSET)
407 immval += (int) memaddr;
408 if (immval > 0 && info->symbol_at_address_func (immval, info))
410 print_func (stream, "\t// ");
411 info->print_address_func (immval, info);
413 else if (op->inst_offset_type == INST_PC_OFFSET)
415 print_func (stream, "\t\t// ");
416 print_func (stream, "%x", immval);
419 break;
420 case INST_TYPE_RD_R2:
421 print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
422 get_field_r2 (&buf, inst));
423 break;
424 case INST_TYPE_R2:
425 print_func (stream, "\t%s", get_field_r2 (&buf, inst));
426 break;
427 case INST_TYPE_R1:
428 print_func (stream, "\t%s", get_field_r1 (&buf, inst));
429 break;
430 case INST_TYPE_R1_R2_SPECIAL:
431 print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst),
432 get_field_r2 (&buf, inst));
433 break;
434 case INST_TYPE_RD_IMM15:
435 print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
436 get_field_imm15 (&buf, inst));
437 break;
438 /* For mbar insn. */
439 case INST_TYPE_IMM5:
440 print_func (stream, "\t%s", get_field_imm5_mbar (&buf, inst));
441 break;
442 /* For mbar 16 or sleep insn. */
443 case INST_TYPE_NONE:
444 break;
445 /* For bit field insns. */
446 case INST_TYPE_RD_R1_IMMW_IMMS:
447 print_func (stream, "\t%s, %s, %s, %s",
448 get_field_rd (&buf, inst),
449 get_field_r1 (&buf, inst),
450 get_field_immw (&buf, inst),
451 get_field_imm5 (&buf, inst));
452 break;
453 /* For tuqula instruction */
454 case INST_TYPE_RD:
455 print_func (stream, "\t%s", get_field_rd (&buf, inst));
456 break;
457 case INST_TYPE_RFSL:
458 print_func (stream, "\t%s", get_field_rfsl (&buf, inst));
459 break;
460 default:
461 /* If the disassembler lags the instruction set. */
462 print_func (stream, "\tundecoded operands, inst is 0x%04x",
463 (unsigned int) inst);
464 break;
468 /* Say how many bytes we consumed. */
469 return 4;
472 enum microblaze_instr
473 get_insn_microblaze (long inst,
474 bool *isunsignedimm,
475 enum microblaze_instr_type *insn_type,
476 short *delay_slots)
478 const struct op_code_struct *op;
479 *isunsignedimm = false;
481 /* Just a linear search of the table. */
482 for (op = microblaze_opcodes; op->name != 0; op ++)
483 if (op->bit_sequence == (inst & op->opcode_mask))
484 break;
486 if (op->name == 0)
487 return invalid_inst;
488 else
490 *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM);
491 *insn_type = op->instr_type;
492 *delay_slots = op->delay_slots;
493 return op->instr;
497 enum microblaze_instr
498 microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *immed)
500 enum microblaze_instr op;
501 bool t1;
502 enum microblaze_instr_type t2;
503 short t3;
505 op = get_insn_microblaze (insn, &t1, &t2, &t3);
506 *rd = (insn & RD_MASK) >> RD_LOW;
507 *ra = (insn & RA_MASK) >> RA_LOW;
508 *rb = (insn & RB_MASK) >> RB_LOW;
509 t3 = (insn & IMM_MASK) >> IMM_LOW;
510 *immed = (int) t3;
511 return (op);
514 unsigned long
515 microblaze_get_target_address (long inst, bool immfound, int immval,
516 long pcval, long r1val, long r2val,
517 bool *targetvalid,
518 bool *unconditionalbranch)
520 const struct op_code_struct *op;
521 long targetaddr = 0;
523 *unconditionalbranch = false;
524 /* Just a linear search of the table. */
525 for (op = microblaze_opcodes; op->name != 0; op ++)
526 if (op->bit_sequence == (inst & op->opcode_mask))
527 break;
529 if (op->name == 0)
531 *targetvalid = false;
533 else if (op->instr_type == branch_inst)
535 switch (op->inst_type)
537 case INST_TYPE_R2:
538 *unconditionalbranch = true;
539 /* Fall through. */
540 case INST_TYPE_RD_R2:
541 case INST_TYPE_R1_R2:
542 targetaddr = r2val;
543 *targetvalid = true;
544 if (op->inst_offset_type == INST_PC_OFFSET)
545 targetaddr += pcval;
546 break;
547 case INST_TYPE_IMM:
548 *unconditionalbranch = true;
549 /* Fall through. */
550 case INST_TYPE_RD_IMM:
551 case INST_TYPE_R1_IMM:
552 if (immfound)
554 targetaddr = (immval << 16) & 0xffff0000;
555 targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
557 else
559 targetaddr = get_int_field_imm (inst);
560 if (targetaddr & 0x8000)
561 targetaddr |= 0xFFFF0000;
563 if (op->inst_offset_type == INST_PC_OFFSET)
564 targetaddr += pcval;
565 *targetvalid = true;
566 break;
567 default:
568 *targetvalid = false;
569 break;
572 else if (op->instr_type == return_inst)
574 if (immfound)
576 targetaddr = (immval << 16) & 0xffff0000;
577 targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
579 else
581 targetaddr = get_int_field_imm (inst);
582 if (targetaddr & 0x8000)
583 targetaddr |= 0xFFFF0000;
585 targetaddr += r1val;
586 *targetvalid = true;
588 else
589 *targetvalid = false;
590 return targetaddr;