1 /* Disassemble Xilinx microblaze instructions.
3 Copyright (C) 2009-2025 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)
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. */
27 #include "disassemble.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)
39 #define STRBUF_SIZE 25
44 char str
[NUM_STRBUFS
][STRBUF_SIZE
];
48 strbuf (struct string_buf
*buf
)
50 #ifdef ENABLE_CHECKING
51 if (buf
->which
>= NUM_STRBUFS
)
54 return buf
->str
[buf
->which
++];
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
));
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
));
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
));
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
));
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 */
102 sprintf (p
, "%d", (short)(((instr
& IMM5_WIDTH_MASK
) >>
103 IMM_WIDTH_LOW
) - ((instr
& IMM5_MASK
) >>
104 IMM_LOW
) + 1)); /* bsifi */
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
));
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
));
128 get_field_special (struct string_buf
*buf
, long instr
,
129 const struct op_code_struct
*op
)
131 char *p
= strbuf (buf
);
134 switch ((((instr
& IMM_MASK
) >> IMM_LOW
) ^ op
->immval_mask
))
166 case REG_TLBLO_MASK
:
169 case REG_TLBHI_MASK
:
172 case REG_TLBSX_MASK
:
182 if (((((instr
& IMM_MASK
) >> IMM_LOW
) ^ op
->immval_mask
) & 0xE000)
185 sprintf (p
, "%spvr%d", register_prefix
,
186 (unsigned short)(((instr
& IMM_MASK
) >> IMM_LOW
)
187 ^ op
->immval_mask
) ^ REG_PVR_MASK
);
195 sprintf (p
, "%s%s", register_prefix
, spr
);
200 read_insn_microblaze (bfd_vma memaddr
,
201 struct disassemble_info
*info
,
202 const struct op_code_struct
**opr
)
204 unsigned char ibytes
[4];
206 const struct op_code_struct
*op
;
209 status
= info
->read_memory_func (memaddr
, ibytes
, 4, info
);
213 info
->memory_error_func (status
, memaddr
, info
);
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]);
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
))
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
;
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
;
251 info
->bytes_per_chunk
= 4;
253 inst
= read_insn_microblaze (memaddr
, info
, &op
);
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
);
264 if (pop
->instr
== imm
)
266 immval
= (get_int_field_imm (prev_inst
) << 16) & 0xffff0000;
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
);
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
));
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
)
300 immval
|= (get_int_field_imm (inst
) & 0x0000ffff);
303 immval
= get_int_field_imm (inst
);
307 if (immval
> 0 && info
->symbol_at_address_func (immval
, info
))
309 print_func (stream
, "\t// ");
310 info
->print_address_func (immval
, info
);
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
));
318 case INST_TYPE_RD_RFSL
:
319 print_func (stream
, "\t%s, %s", get_field_rd (&buf
, inst
),
320 get_field_rfsl (&buf
, inst
));
322 case INST_TYPE_R1_RFSL
:
323 print_func (stream
, "\t%s, %s", get_field_r1 (&buf
, inst
),
324 get_field_rfsl (&buf
, inst
));
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
));
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
));
334 case INST_TYPE_RD_R1
:
335 print_func (stream
, "\t%s, %s", get_field_rd (&buf
, inst
),
336 get_field_r1 (&buf
, inst
));
338 case INST_TYPE_R1_R2
:
339 print_func (stream
, "\t%s, %s", get_field_r1 (&buf
, inst
),
340 get_field_r2 (&buf
, inst
));
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
)
351 immval
|= (get_int_field_imm (inst
) & 0x0000ffff);
354 immval
= get_int_field_imm (inst
);
359 if (immval
> 0 && info
->symbol_at_address_func (immval
, info
))
361 print_func (stream
, "\t// ");
362 info
->print_address_func (immval
, info
);
366 print_func (stream
, "\t\t// ");
367 print_func (stream
, "%x", immval
);
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
)
377 immval
|= (get_int_field_imm (inst
) & 0x0000ffff);
380 immval
= get_int_field_imm (inst
);
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
);
394 print_func (stream
, "\t%s", get_field_imm (&buf
, inst
));
395 if (info
->print_address_func
&& info
->symbol_at_address_func
399 immval
|= (get_int_field_imm (inst
) & 0x0000ffff);
402 immval
= get_int_field_imm (inst
);
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
);
420 case INST_TYPE_RD_R2
:
421 print_func (stream
, "\t%s, %s", get_field_rd (&buf
, inst
),
422 get_field_r2 (&buf
, inst
));
425 print_func (stream
, "\t%s", get_field_r2 (&buf
, inst
));
428 print_func (stream
, "\t%s", get_field_r1 (&buf
, inst
));
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
));
434 case INST_TYPE_RD_IMM15
:
435 print_func (stream
, "\t%s, %s", get_field_rd (&buf
, inst
),
436 get_field_imm15 (&buf
, inst
));
440 print_func (stream
, "\t%s", get_field_imm5_mbar (&buf
, inst
));
442 /* For mbar 16 or sleep insn. */
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
));
453 /* For tuqula instruction */
455 print_func (stream
, "\t%s", get_field_rd (&buf
, inst
));
458 print_func (stream
, "\t%s", get_field_rfsl (&buf
, inst
));
461 /* If the disassembler lags the instruction set. */
462 print_func (stream
, "\tundecoded operands, inst is 0x%04x",
463 (unsigned int) inst
);
468 /* Say how many bytes we consumed. */
472 enum microblaze_instr
473 get_insn_microblaze (long inst
,
475 enum microblaze_instr_type
*insn_type
,
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
))
490 *isunsignedimm
= (op
->inst_type
== INST_TYPE_RD_R1_UNSIGNED_IMM
);
491 *insn_type
= op
->instr_type
;
492 *delay_slots
= op
->delay_slots
;
497 enum microblaze_instr
498 microblaze_decode_insn (long insn
, int *rd
, int *ra
, int *rb
, int *immed
)
500 enum microblaze_instr op
;
502 enum microblaze_instr_type t2
;
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
;
515 microblaze_get_target_address (long inst
, bool immfound
, int immval
,
516 long pcval
, long r1val
, long r2val
,
518 bool *unconditionalbranch
)
520 const struct op_code_struct
*op
;
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
))
531 *targetvalid
= false;
533 else if (op
->instr_type
== branch_inst
)
535 switch (op
->inst_type
)
538 *unconditionalbranch
= true;
540 case INST_TYPE_RD_R2
:
541 case INST_TYPE_R1_R2
:
544 if (op
->inst_offset_type
== INST_PC_OFFSET
)
548 *unconditionalbranch
= true;
550 case INST_TYPE_RD_IMM
:
551 case INST_TYPE_R1_IMM
:
554 targetaddr
= (immval
<< 16) & (~0xffff);
555 targetaddr
|= (get_int_field_imm (inst
) & 0x0000ffff);
559 targetaddr
= get_int_field_imm (inst
);
560 if (targetaddr
& 0x8000)
561 targetaddr
|= (~0xFFFF);
563 if (op
->inst_offset_type
== INST_PC_OFFSET
)
568 *targetvalid
= false;
572 else if (op
->instr_type
== return_inst
)
576 targetaddr
= (immval
<< 16) & (~0xffff);
577 targetaddr
|= (get_int_field_imm (inst
) & 0x0000ffff);
581 targetaddr
= get_int_field_imm (inst
);
582 if (targetaddr
& 0x8000)
583 targetaddr
|= (~0xFFFF);
589 *targetvalid
= false;