Remove path name from test case
[binutils-gdb.git] / opcodes / microblaze-dis.c
1 /* Disassemble Xilinx microblaze instructions.
2
3 Copyright (C) 2009-2023 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 "disassemble.h"
28 #include <strings.h>
29 #include "microblaze-opc.h"
30 #include "microblaze-dis.h"
31
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)
37
38 #define NUM_STRBUFS 4
39 #define STRBUF_SIZE 25
40
41 struct string_buf
42 {
43 unsigned int which;
44 char str[NUM_STRBUFS][STRBUF_SIZE];
45 };
46
47 static inline char *
48 strbuf (struct string_buf *buf)
49 {
50 #ifdef ENABLE_CHECKING
51 if (buf->which >= NUM_STRBUFS)
52 abort ();
53 #endif
54 return buf->str[buf->which++];
55 }
56
57 static char *
58 get_field (struct string_buf *buf, long instr, long mask, unsigned short low)
59 {
60 char *p = strbuf (buf);
61
62 sprintf (p, "%s%d", register_prefix, (int)((instr & mask) >> low));
63 return p;
64 }
65
66 static char *
67 get_field_imm (struct string_buf *buf, long instr)
68 {
69 char *p = strbuf (buf);
70
71 sprintf (p, "%d", (short)((instr & IMM_MASK) >> IMM_LOW));
72 return p;
73 }
74
75 static char *
76 get_field_imm5 (struct string_buf *buf, long instr)
77 {
78 char *p = strbuf (buf);
79
80 sprintf (p, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW));
81 return p;
82 }
83
84 static char *
85 get_field_imm5_mbar (struct string_buf *buf, long instr)
86 {
87 char *p = strbuf (buf);
88
89 sprintf (p, "%d", (short)((instr & IMM5_MBAR_MASK) >> IMM_MBAR));
90 return p;
91 }
92
93 static char *
94 get_field_immw (struct string_buf *buf, long instr)
95 {
96 char *p = strbuf (buf);
97
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;
106 }
107
108 static char *
109 get_field_rfsl (struct string_buf *buf, long instr)
110 {
111 char *p = strbuf (buf);
112
113 sprintf (p, "%s%d", fsl_register_prefix,
114 (short)((instr & RFSL_MASK) >> IMM_LOW));
115 return p;
116 }
117
118 static char *
119 get_field_imm15 (struct string_buf *buf, long instr)
120 {
121 char *p = strbuf (buf);
122
123 sprintf (p, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW));
124 return p;
125 }
126
127 static char *
128 get_field_special (struct string_buf *buf, long instr,
129 const struct op_code_struct *op)
130 {
131 char *p = strbuf (buf);
132 char *spr;
133
134 switch ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask))
135 {
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)
184 {
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;
189 }
190 else
191 spr = "pc";
192 break;
193 }
194
195 sprintf (p, "%s%s", register_prefix, spr);
196 return p;
197 }
198
199 static unsigned long
200 read_insn_microblaze (bfd_vma memaddr,
201 struct disassemble_info *info,
202 const struct op_code_struct **opr)
203 {
204 unsigned char ibytes[4];
205 int status;
206 const struct op_code_struct *op;
207 unsigned long inst;
208
209 status = info->read_memory_func (memaddr, ibytes, 4, info);
210
211 if (status != 0)
212 {
213 info->memory_error_func (status, memaddr, info);
214 return 0;
215 }
216
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 ();
225
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;
230
231 *opr = op;
232 return inst;
233 }
234
235
236 int
237 print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
238 {
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;
249
250 buf.which = 0;
251 info->bytes_per_chunk = 4;
252
253 inst = read_insn_microblaze (memaddr, info, &op);
254 if (inst == 0)
255 return -1;
256
257 if (prev_insn_vma == curr_insn_vma)
258 {
259 if (memaddr-(info->bytes_per_chunk) == prev_insn_addr)
260 {
261 prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
262 if (prev_inst == 0)
263 return -1;
264 if (pop->instr == imm)
265 {
266 immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000;
267 immfound = true;
268 }
269 else
270 {
271 immval = 0;
272 immfound = false;
273 }
274 }
275 }
276
277 /* Make curr insn as prev insn. */
278 prev_insn_addr = memaddr;
279 prev_insn_vma = curr_insn_vma;
280
281 if (op->name == NULL)
282 print_func (stream, ".long 0x%04x", (unsigned int) inst);
283 else
284 {
285 print_func (stream, "%s", op->name);
286
287 switch (op->inst_type)
288 {
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)
298 {
299 if (immfound)
300 immval |= (get_int_field_imm (inst) & 0x0000ffff);
301 else
302 {
303 immval = get_int_field_imm (inst);
304 if (immval & 0x8000)
305 immval |= 0xFFFF0000;
306 }
307 if (immval > 0 && info->symbol_at_address_func (immval, info))
308 {
309 print_func (stream, "\t// ");
310 info->print_address_func (immval, info);
311 }
312 }
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)
349 {
350 if (immfound)
351 immval |= (get_int_field_imm (inst) & 0x0000ffff);
352 else
353 {
354 immval = get_int_field_imm (inst);
355 if (immval & 0x8000)
356 immval |= 0xFFFF0000;
357 }
358 immval += memaddr;
359 if (immval > 0 && info->symbol_at_address_func (immval, info))
360 {
361 print_func (stream, "\t// ");
362 info->print_address_func (immval, info);
363 }
364 else
365 {
366 print_func (stream, "\t\t// ");
367 print_func (stream, "%x", immval);
368 }
369 }
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)
375 {
376 if (immfound)
377 immval |= (get_int_field_imm (inst) & 0x0000ffff);
378 else
379 {
380 immval = get_int_field_imm (inst);
381 if (immval & 0x8000)
382 immval |= 0xFFFF0000;
383 }
384 if (op->inst_offset_type == INST_PC_OFFSET)
385 immval += (int) memaddr;
386 if (info->symbol_at_address_func (immval, info))
387 {
388 print_func (stream, "\t// ");
389 info->print_address_func (immval, info);
390 }
391 }
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)
397 {
398 if (immfound)
399 immval |= (get_int_field_imm (inst) & 0x0000ffff);
400 else
401 {
402 immval = get_int_field_imm (inst);
403 if (immval & 0x8000)
404 immval |= 0xFFFF0000;
405 }
406 if (op->inst_offset_type == INST_PC_OFFSET)
407 immval += (int) memaddr;
408 if (immval > 0 && info->symbol_at_address_func (immval, info))
409 {
410 print_func (stream, "\t// ");
411 info->print_address_func (immval, info);
412 }
413 else if (op->inst_offset_type == INST_PC_OFFSET)
414 {
415 print_func (stream, "\t\t// ");
416 print_func (stream, "%x", immval);
417 }
418 }
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;
465 }
466 }
467
468 /* Say how many bytes we consumed. */
469 return 4;
470 }
471
472 enum microblaze_instr
473 get_insn_microblaze (long inst,
474 bool *isunsignedimm,
475 enum microblaze_instr_type *insn_type,
476 short *delay_slots)
477 {
478 const struct op_code_struct *op;
479 *isunsignedimm = false;
480
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;
485
486 if (op->name == 0)
487 return invalid_inst;
488 else
489 {
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;
494 }
495 }
496
497 enum microblaze_instr
498 microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *immed)
499 {
500 enum microblaze_instr op;
501 bool t1;
502 enum microblaze_instr_type t2;
503 short t3;
504
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);
512 }
513
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)
519 {
520 const struct op_code_struct *op;
521 long targetaddr = 0;
522
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;
528
529 if (op->name == 0)
530 {
531 *targetvalid = false;
532 }
533 else if (op->instr_type == branch_inst)
534 {
535 switch (op->inst_type)
536 {
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)
553 {
554 targetaddr = (immval << 16) & 0xffff0000;
555 targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
556 }
557 else
558 {
559 targetaddr = get_int_field_imm (inst);
560 if (targetaddr & 0x8000)
561 targetaddr |= 0xFFFF0000;
562 }
563 if (op->inst_offset_type == INST_PC_OFFSET)
564 targetaddr += pcval;
565 *targetvalid = true;
566 break;
567 default:
568 *targetvalid = false;
569 break;
570 }
571 }
572 else if (op->instr_type == return_inst)
573 {
574 if (immfound)
575 {
576 targetaddr = (immval << 16) & 0xffff0000;
577 targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
578 }
579 else
580 {
581 targetaddr = get_int_field_imm (inst);
582 if (targetaddr & 0x8000)
583 targetaddr |= 0xFFFF0000;
584 }
585 targetaddr += r1val;
586 *targetvalid = true;
587 }
588 else
589 *targetvalid = false;
590 return targetaddr;
591 }