Updated Finnish translation
[binutils.git] / opcodes / microblaze-dis.c
blob3c4eef9db5100042ee81a89d49f5cf0b3366733d
1 /* Disassemble Xilinx microblaze instructions.
3 Copyright 2009 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 "microblaze-opc.h"
28 #include "dis-asm.h"
29 #include <strings.h>
31 #define get_field_rd(instr) get_field (instr, RD_MASK, RD_LOW)
32 #define get_field_r1(instr) get_field (instr, RA_MASK, RA_LOW)
33 #define get_field_r2(instr) get_field (instr, RB_MASK, RB_LOW)
34 #define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW)
35 #define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW)
38 enum microblaze_instr get_insn_microblaze (long, bfd_boolean *,
39 enum microblaze_instr_type *, short *);
40 unsigned long microblaze_get_target_address (long, bfd_boolean, int, long, long,
41 long, bfd_boolean *, bfd_boolean *);
42 enum microblaze_instr microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *imm);
44 static char *
45 get_field (long instr, long mask, unsigned short low)
47 char tmpstr[25];
49 sprintf (tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low));
50 return (strdup (tmpstr));
53 static char *
54 get_field_imm (long instr)
56 char tmpstr[25];
58 sprintf (tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW));
59 return (strdup (tmpstr));
62 static char *
63 get_field_imm5 (long instr)
65 char tmpstr[25];
67 sprintf (tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW));
68 return (strdup (tmpstr));
71 static char *
72 get_field_rfsl (long instr)
74 char tmpstr[25];
76 sprintf (tmpstr, "%s%d", fsl_register_prefix,
77 (short)((instr & RFSL_MASK) >> IMM_LOW));
78 return (strdup (tmpstr));
81 static char *
82 get_field_imm15 (long instr)
84 char tmpstr[25];
86 sprintf (tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW));
87 return (strdup (tmpstr));
90 static char *
91 get_field_special (long instr, struct op_code_struct * op)
93 char tmpstr[25];
94 char spr[6];
96 switch ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask))
98 case REG_MSR_MASK :
99 strcpy (spr, "msr");
100 break;
101 case REG_PC_MASK :
102 strcpy (spr, "pc");
103 break;
104 case REG_EAR_MASK :
105 strcpy (spr, "ear");
106 break;
107 case REG_ESR_MASK :
108 strcpy (spr, "esr");
109 break;
110 case REG_FSR_MASK :
111 strcpy (spr, "fsr");
112 break;
113 case REG_BTR_MASK :
114 strcpy (spr, "btr");
115 break;
116 case REG_EDR_MASK :
117 strcpy (spr, "edr");
118 break;
119 case REG_PID_MASK :
120 strcpy (spr, "pid");
121 break;
122 case REG_ZPR_MASK :
123 strcpy (spr, "zpr");
124 break;
125 case REG_TLBX_MASK :
126 strcpy (spr, "tlbx");
127 break;
128 case REG_TLBLO_MASK :
129 strcpy (spr, "tlblo");
130 break;
131 case REG_TLBHI_MASK :
132 strcpy (spr, "tlbhi");
133 break;
134 case REG_TLBSX_MASK :
135 strcpy (spr, "tlbsx");
136 break;
137 default :
138 if (((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000)
139 == REG_PVR_MASK)
141 sprintf (tmpstr, "%spvr%d", register_prefix,
142 (unsigned short)(((instr & IMM_MASK) >> IMM_LOW)
143 ^ op->immval_mask) ^ REG_PVR_MASK);
144 return (strdup (tmpstr));
146 else
147 strcpy (spr, "pc");
148 break;
151 sprintf (tmpstr, "%s%s", register_prefix, spr);
152 return (strdup (tmpstr));
155 static unsigned long
156 read_insn_microblaze (bfd_vma memaddr,
157 struct disassemble_info *info,
158 struct op_code_struct **opr)
160 unsigned char ibytes[4];
161 int status;
162 struct op_code_struct * op;
163 unsigned long inst;
165 status = info->read_memory_func (memaddr, ibytes, 4, info);
167 if (status != 0)
169 info->memory_error_func (status, memaddr, info);
170 return 0;
173 if (info->endian == BFD_ENDIAN_BIG)
174 inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3];
175 else if (info->endian == BFD_ENDIAN_LITTLE)
176 inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0];
177 else
178 abort ();
180 /* Just a linear search of the table. */
181 for (op = opcodes; op->name != 0; op ++)
182 if (op->bit_sequence == (inst & op->opcode_mask))
183 break;
185 *opr = op;
186 return inst;
191 print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
193 fprintf_ftype fprintf = info->fprintf_func;
194 void * stream = info->stream;
195 unsigned long inst, prev_inst;
196 struct op_code_struct * op, *pop;
197 int immval = 0;
198 bfd_boolean immfound = FALSE;
199 static bfd_vma prev_insn_addr = -1; /* Init the prev insn addr. */
200 static int prev_insn_vma = -1; /* Init the prev insn vma. */
201 int curr_insn_vma = info->buffer_vma;
203 info->bytes_per_chunk = 4;
205 inst = read_insn_microblaze (memaddr, info, &op);
206 if (inst == 0)
207 return -1;
209 if (prev_insn_vma == curr_insn_vma)
211 if (memaddr-(info->bytes_per_chunk) == prev_insn_addr)
213 prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
214 if (prev_inst == 0)
215 return -1;
216 if (pop->instr == imm)
218 immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000;
219 immfound = TRUE;
221 else
223 immval = 0;
224 immfound = FALSE;
229 /* Make curr insn as prev insn. */
230 prev_insn_addr = memaddr;
231 prev_insn_vma = curr_insn_vma;
233 if (op->name == NULL)
234 fprintf (stream, ".short 0x%04x", inst);
235 else
237 fprintf (stream, "%s", op->name);
239 switch (op->inst_type)
241 case INST_TYPE_RD_R1_R2:
242 fprintf (stream, "\t%s, %s, %s", get_field_rd (inst),
243 get_field_r1(inst), get_field_r2 (inst));
244 break;
245 case INST_TYPE_RD_R1_IMM:
246 fprintf (stream, "\t%s, %s, %s", get_field_rd (inst),
247 get_field_r1(inst), get_field_imm (inst));
248 if (info->print_address_func && get_int_field_r1 (inst) == 0
249 && info->symbol_at_address_func)
251 if (immfound)
252 immval |= (get_int_field_imm (inst) & 0x0000ffff);
253 else
255 immval = get_int_field_imm (inst);
256 if (immval & 0x8000)
257 immval |= 0xFFFF0000;
259 if (immval > 0 && info->symbol_at_address_func (immval, info))
261 fprintf (stream, "\t// ");
262 info->print_address_func (immval, info);
265 break;
266 case INST_TYPE_RD_R1_IMM5:
267 fprintf (stream, "\t%s, %s, %s", get_field_rd (inst),
268 get_field_r1(inst), get_field_imm5 (inst));
269 break;
270 case INST_TYPE_RD_RFSL:
271 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_rfsl (inst));
272 break;
273 case INST_TYPE_R1_RFSL:
274 fprintf (stream, "\t%s, %s", get_field_r1 (inst), get_field_rfsl (inst));
275 break;
276 case INST_TYPE_RD_SPECIAL:
277 fprintf (stream, "\t%s, %s", get_field_rd (inst),
278 get_field_special (inst, op));
279 break;
280 case INST_TYPE_SPECIAL_R1:
281 fprintf (stream, "\t%s, %s", get_field_special (inst, op),
282 get_field_r1(inst));
283 break;
284 case INST_TYPE_RD_R1:
285 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_r1 (inst));
286 break;
287 case INST_TYPE_R1_R2:
288 fprintf (stream, "\t%s, %s", get_field_r1 (inst), get_field_r2 (inst));
289 break;
290 case INST_TYPE_R1_IMM:
291 fprintf (stream, "\t%s, %s", get_field_r1 (inst), get_field_imm (inst));
292 /* The non-pc relative instructions are returns, which shouldn't
293 have a label printed. */
294 if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET
295 && info->symbol_at_address_func)
297 if (immfound)
298 immval |= (get_int_field_imm (inst) & 0x0000ffff);
299 else
301 immval = get_int_field_imm (inst);
302 if (immval & 0x8000)
303 immval |= 0xFFFF0000;
305 immval += memaddr;
306 if (immval > 0 && info->symbol_at_address_func (immval, info))
308 fprintf (stream, "\t// ");
309 info->print_address_func (immval, info);
311 else
313 fprintf (stream, "\t\t// ");
314 fprintf (stream, "%x", immval);
317 break;
318 case INST_TYPE_RD_IMM:
319 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_imm (inst));
320 if (info->print_address_func && info->symbol_at_address_func)
322 if (immfound)
323 immval |= (get_int_field_imm (inst) & 0x0000ffff);
324 else
326 immval = get_int_field_imm (inst);
327 if (immval & 0x8000)
328 immval |= 0xFFFF0000;
330 if (op->inst_offset_type == INST_PC_OFFSET)
331 immval += (int) memaddr;
332 if (info->symbol_at_address_func (immval, info))
334 fprintf (stream, "\t// ");
335 info->print_address_func (immval, info);
338 break;
339 case INST_TYPE_IMM:
340 fprintf (stream, "\t%s", get_field_imm (inst));
341 if (info->print_address_func && info->symbol_at_address_func
342 && op->instr != imm)
344 if (immfound)
345 immval |= (get_int_field_imm (inst) & 0x0000ffff);
346 else
348 immval = get_int_field_imm (inst);
349 if (immval & 0x8000)
350 immval |= 0xFFFF0000;
352 if (op->inst_offset_type == INST_PC_OFFSET)
353 immval += (int) memaddr;
354 if (immval > 0 && info->symbol_at_address_func (immval, info))
356 fprintf (stream, "\t// ");
357 info->print_address_func (immval, info);
359 else if (op->inst_offset_type == INST_PC_OFFSET)
361 fprintf (stream, "\t\t// ");
362 fprintf (stream, "%x", immval);
365 break;
366 case INST_TYPE_RD_R2:
367 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
368 break;
369 case INST_TYPE_R2:
370 fprintf (stream, "\t%s", get_field_r2 (inst));
371 break;
372 case INST_TYPE_R1:
373 fprintf (stream, "\t%s", get_field_r1 (inst));
374 break;
375 case INST_TYPE_RD_R1_SPECIAL:
376 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
377 break;
378 case INST_TYPE_RD_IMM15:
379 fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_imm15 (inst));
380 break;
381 /* For tuqula instruction */
382 case INST_TYPE_RD:
383 fprintf (stream, "\t%s", get_field_rd (inst));
384 break;
385 case INST_TYPE_RFSL:
386 fprintf (stream, "\t%s", get_field_rfsl (inst));
387 break;
388 default:
389 /* If the disassembler lags the instruction set. */
390 fprintf (stream, "\tundecoded operands, inst is 0x%04x", inst);
391 break;
395 /* Say how many bytes we consumed. */
396 return 4;
399 enum microblaze_instr
400 get_insn_microblaze (long inst,
401 bfd_boolean *isunsignedimm,
402 enum microblaze_instr_type *insn_type,
403 short *delay_slots)
405 struct op_code_struct * op;
406 *isunsignedimm = FALSE;
408 /* Just a linear search of the table. */
409 for (op = opcodes; op->name != 0; op ++)
410 if (op->bit_sequence == (inst & op->opcode_mask))
411 break;
413 if (op->name == 0)
414 return invalid_inst;
415 else
417 *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM);
418 *insn_type = op->instr_type;
419 *delay_slots = op->delay_slots;
420 return op->instr;
424 enum microblaze_instr
425 microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *imm)
427 enum microblaze_instr op;
428 bfd_boolean t1;
429 enum microblaze_instr_type t2;
430 short t3;
432 op = get_insn_microblaze (insn, &t1, &t2, &t3);
433 *rd = (insn & RD_MASK) >> RD_LOW;
434 *ra = (insn & RA_MASK) >> RA_LOW;
435 *rb = (insn & RB_MASK) >> RB_LOW;
436 t3 = (insn & IMM_MASK) >> IMM_LOW;
437 *imm = (int) t3;
438 return (op);
441 unsigned long
442 microblaze_get_target_address (long inst, bfd_boolean immfound, int immval,
443 long pcval, long r1val, long r2val,
444 bfd_boolean *targetvalid,
445 bfd_boolean *unconditionalbranch)
447 struct op_code_struct * op;
448 long targetaddr = 0;
450 *unconditionalbranch = FALSE;
451 /* Just a linear search of the table. */
452 for (op = opcodes; op->name != 0; op ++)
453 if (op->bit_sequence == (inst & op->opcode_mask))
454 break;
456 if (op->name == 0)
458 *targetvalid = FALSE;
460 else if (op->instr_type == branch_inst)
462 switch (op->inst_type)
464 case INST_TYPE_R2:
465 *unconditionalbranch = TRUE;
466 /* Fall through. */
467 case INST_TYPE_RD_R2:
468 case INST_TYPE_R1_R2:
469 targetaddr = r2val;
470 *targetvalid = TRUE;
471 if (op->inst_offset_type == INST_PC_OFFSET)
472 targetaddr += pcval;
473 break;
474 case INST_TYPE_IMM:
475 *unconditionalbranch = TRUE;
476 /* Fall through. */
477 case INST_TYPE_RD_IMM:
478 case INST_TYPE_R1_IMM:
479 if (immfound)
481 targetaddr = (immval << 16) & 0xffff0000;
482 targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
484 else
486 targetaddr = get_int_field_imm (inst);
487 if (targetaddr & 0x8000)
488 targetaddr |= 0xFFFF0000;
490 if (op->inst_offset_type == INST_PC_OFFSET)
491 targetaddr += pcval;
492 *targetvalid = TRUE;
493 break;
494 default:
495 *targetvalid = FALSE;
496 break;
499 else if (op->instr_type == return_inst)
501 if (immfound)
503 targetaddr = (immval << 16) & 0xffff0000;
504 targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
506 else
508 targetaddr = get_int_field_imm (inst);
509 if (targetaddr & 0x8000)
510 targetaddr |= 0xFFFF0000;
512 targetaddr += r1val;
513 *targetvalid = TRUE;
515 else
516 *targetvalid = FALSE;
517 return targetaddr;