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)
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 "microblaze-opc.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 get_field (long instr
, long mask
, unsigned short low
)
42 sprintf (tmpstr
, "%s%d", register_prefix
, (int)((instr
& mask
) >> low
));
43 return (strdup (tmpstr
));
47 get_field_imm (long instr
)
51 sprintf (tmpstr
, "%d", (short)((instr
& IMM_MASK
) >> IMM_LOW
));
52 return (strdup (tmpstr
));
56 get_field_imm5 (long instr
)
60 sprintf (tmpstr
, "%d", (short)((instr
& IMM5_MASK
) >> IMM_LOW
));
61 return (strdup (tmpstr
));
65 get_field_rfsl (long instr
)
69 sprintf (tmpstr
, "%s%d", fsl_register_prefix
,
70 (short)((instr
& RFSL_MASK
) >> IMM_LOW
));
71 return (strdup (tmpstr
));
75 get_field_imm15 (long instr
)
79 sprintf (tmpstr
, "%d", (short)((instr
& IMM15_MASK
) >> IMM_LOW
));
80 return (strdup (tmpstr
));
84 get_field_special (long instr
, struct op_code_struct
* op
)
89 switch ((((instr
& IMM_MASK
) >> IMM_LOW
) ^ op
->immval_mask
))
119 strcpy (spr
, "tlbx");
121 case REG_TLBLO_MASK
:
122 strcpy (spr
, "tlblo");
124 case REG_TLBHI_MASK
:
125 strcpy (spr
, "tlbhi");
127 case REG_TLBSX_MASK
:
128 strcpy (spr
, "tlbsx");
131 if (((((instr
& IMM_MASK
) >> IMM_LOW
) ^ op
->immval_mask
) & 0xE000)
134 sprintf (tmpstr
, "%spvr%d", register_prefix
,
135 (unsigned short)(((instr
& IMM_MASK
) >> IMM_LOW
)
136 ^ op
->immval_mask
) ^ REG_PVR_MASK
);
137 return (strdup (tmpstr
));
144 sprintf (tmpstr
, "%s%s", register_prefix
, spr
);
145 return (strdup (tmpstr
));
149 read_insn_microblaze (bfd_vma memaddr
,
150 struct disassemble_info
*info
,
151 struct op_code_struct
**opr
)
153 unsigned char ibytes
[4];
155 struct op_code_struct
* op
;
158 status
= info
->read_memory_func (memaddr
, ibytes
, 4, info
);
162 info
->memory_error_func (status
, memaddr
, info
);
166 if (info
->endian
== BFD_ENDIAN_BIG
)
167 inst
= (ibytes
[0] << 24) | (ibytes
[1] << 16) | (ibytes
[2] << 8) | ibytes
[3];
168 else if (info
->endian
== BFD_ENDIAN_LITTLE
)
169 inst
= (ibytes
[3] << 24) | (ibytes
[2] << 16) | (ibytes
[1] << 8) | ibytes
[0];
173 /* Just a linear search of the table. */
174 for (op
= opcodes
; op
->name
!= 0; op
++)
175 if (op
->bit_sequence
== (inst
& op
->opcode_mask
))
184 print_insn_microblaze (bfd_vma memaddr
, struct disassemble_info
* info
)
186 fprintf_ftype fprintf
= info
->fprintf_func
;
187 void * stream
= info
->stream
;
188 unsigned long inst
, prev_inst
;
189 struct op_code_struct
* op
, *pop
;
191 bfd_boolean immfound
= FALSE
;
192 static bfd_vma prev_insn_addr
= -1; /* Init the prev insn addr. */
193 static int prev_insn_vma
= -1; /* Init the prev insn vma. */
194 int curr_insn_vma
= info
->buffer_vma
;
196 info
->bytes_per_chunk
= 4;
198 inst
= read_insn_microblaze (memaddr
, info
, &op
);
202 if (prev_insn_vma
== curr_insn_vma
)
204 if (memaddr
-(info
->bytes_per_chunk
) == prev_insn_addr
)
206 prev_inst
= read_insn_microblaze (prev_insn_addr
, info
, &pop
);
209 if (pop
->instr
== imm
)
211 immval
= (get_int_field_imm (prev_inst
) << 16) & 0xffff0000;
222 /* Make curr insn as prev insn. */
223 prev_insn_addr
= memaddr
;
224 prev_insn_vma
= curr_insn_vma
;
226 if (op
->name
== NULL
)
227 fprintf (stream
, ".short 0x%04x", inst
);
230 fprintf (stream
, "%s", op
->name
);
232 switch (op
->inst_type
)
234 case INST_TYPE_RD_R1_R2
:
235 fprintf (stream
, "\t%s, %s, %s", get_field_rd (inst
),
236 get_field_r1(inst
), get_field_r2 (inst
));
238 case INST_TYPE_RD_R1_IMM
:
239 fprintf (stream
, "\t%s, %s, %s", get_field_rd (inst
),
240 get_field_r1(inst
), get_field_imm (inst
));
241 if (info
->print_address_func
&& get_int_field_r1 (inst
) == 0
242 && info
->symbol_at_address_func
)
245 immval
|= (get_int_field_imm (inst
) & 0x0000ffff);
248 immval
= get_int_field_imm (inst
);
250 immval
|= 0xFFFF0000;
252 if (immval
> 0 && info
->symbol_at_address_func (immval
, info
))
254 fprintf (stream
, "\t// ");
255 info
->print_address_func (immval
, info
);
259 case INST_TYPE_RD_R1_IMM5
:
260 fprintf (stream
, "\t%s, %s, %s", get_field_rd (inst
),
261 get_field_r1(inst
), get_field_imm5 (inst
));
263 case INST_TYPE_RD_RFSL
:
264 fprintf (stream
, "\t%s, %s", get_field_rd (inst
), get_field_rfsl (inst
));
266 case INST_TYPE_R1_RFSL
:
267 fprintf (stream
, "\t%s, %s", get_field_r1 (inst
), get_field_rfsl (inst
));
269 case INST_TYPE_RD_SPECIAL
:
270 fprintf (stream
, "\t%s, %s", get_field_rd (inst
),
271 get_field_special (inst
, op
));
273 case INST_TYPE_SPECIAL_R1
:
274 fprintf (stream
, "\t%s, %s", get_field_special (inst
, op
),
277 case INST_TYPE_RD_R1
:
278 fprintf (stream
, "\t%s, %s", get_field_rd (inst
), get_field_r1 (inst
));
280 case INST_TYPE_R1_R2
:
281 fprintf (stream
, "\t%s, %s", get_field_r1 (inst
), get_field_r2 (inst
));
283 case INST_TYPE_R1_IMM
:
284 fprintf (stream
, "\t%s, %s", get_field_r1 (inst
), get_field_imm (inst
));
285 /* The non-pc relative instructions are returns, which shouldn't
286 have a label printed. */
287 if (info
->print_address_func
&& op
->inst_offset_type
== INST_PC_OFFSET
288 && info
->symbol_at_address_func
)
291 immval
|= (get_int_field_imm (inst
) & 0x0000ffff);
294 immval
= get_int_field_imm (inst
);
296 immval
|= 0xFFFF0000;
299 if (immval
> 0 && info
->symbol_at_address_func (immval
, info
))
301 fprintf (stream
, "\t// ");
302 info
->print_address_func (immval
, info
);
306 fprintf (stream
, "\t\t// ");
307 fprintf (stream
, "%x", immval
);
311 case INST_TYPE_RD_IMM
:
312 fprintf (stream
, "\t%s, %s", get_field_rd (inst
), get_field_imm (inst
));
313 if (info
->print_address_func
&& info
->symbol_at_address_func
)
316 immval
|= (get_int_field_imm (inst
) & 0x0000ffff);
319 immval
= get_int_field_imm (inst
);
321 immval
|= 0xFFFF0000;
323 if (op
->inst_offset_type
== INST_PC_OFFSET
)
324 immval
+= (int) memaddr
;
325 if (info
->symbol_at_address_func (immval
, info
))
327 fprintf (stream
, "\t// ");
328 info
->print_address_func (immval
, info
);
333 fprintf (stream
, "\t%s", get_field_imm (inst
));
334 if (info
->print_address_func
&& info
->symbol_at_address_func
338 immval
|= (get_int_field_imm (inst
) & 0x0000ffff);
341 immval
= get_int_field_imm (inst
);
343 immval
|= 0xFFFF0000;
345 if (op
->inst_offset_type
== INST_PC_OFFSET
)
346 immval
+= (int) memaddr
;
347 if (immval
> 0 && info
->symbol_at_address_func (immval
, info
))
349 fprintf (stream
, "\t// ");
350 info
->print_address_func (immval
, info
);
352 else if (op
->inst_offset_type
== INST_PC_OFFSET
)
354 fprintf (stream
, "\t\t// ");
355 fprintf (stream
, "%x", immval
);
359 case INST_TYPE_RD_R2
:
360 fprintf (stream
, "\t%s, %s", get_field_rd (inst
), get_field_r2 (inst
));
363 fprintf (stream
, "\t%s", get_field_r2 (inst
));
366 fprintf (stream
, "\t%s", get_field_r1 (inst
));
368 case INST_TYPE_RD_R1_SPECIAL
:
369 fprintf (stream
, "\t%s, %s", get_field_rd (inst
), get_field_r2 (inst
));
371 case INST_TYPE_RD_IMM15
:
372 fprintf (stream
, "\t%s, %s", get_field_rd (inst
), get_field_imm15 (inst
));
374 /* For tuqula instruction */
376 fprintf (stream
, "\t%s", get_field_rd (inst
));
379 fprintf (stream
, "\t%s", get_field_rfsl (inst
));
382 /* If the disassembler lags the instruction set. */
383 fprintf (stream
, "\tundecoded operands, inst is 0x%04x", inst
);
388 /* Say how many bytes we consumed. */
392 static enum microblaze_instr
393 get_insn_microblaze (long inst
,
394 bfd_boolean
*isunsignedimm
,
395 enum microblaze_instr_type
*insn_type
,
398 struct op_code_struct
* op
;
399 *isunsignedimm
= FALSE
;
401 /* Just a linear search of the table. */
402 for (op
= opcodes
; op
->name
!= 0; op
++)
403 if (op
->bit_sequence
== (inst
& op
->opcode_mask
))
410 *isunsignedimm
= (op
->inst_type
== INST_TYPE_RD_R1_UNSIGNED_IMM
);
411 *insn_type
= op
->instr_type
;
412 *delay_slots
= op
->delay_slots
;
418 get_delay_slots_microblaze (long inst
)
420 bfd_boolean isunsignedimm
;
421 enum microblaze_instr_type insn_type
;
422 enum microblaze_instr op
;
425 op
= get_insn_microblaze (inst
, &isunsignedimm
, &insn_type
, &delay_slots
);
426 if (op
== invalid_inst
)
432 enum microblaze_instr
433 microblaze_decode_insn (long insn
, int *rd
, int *ra
, int *rb
, int *imm
)
435 enum microblaze_instr op
;
437 enum microblaze_instr_type t2
;
440 op
= get_insn_microblaze (insn
, &t1
, &t2
, &t3
);
441 *rd
= (insn
& RD_MASK
) >> RD_LOW
;
442 *ra
= (insn
& RA_MASK
) >> RA_LOW
;
443 *rb
= (insn
& RB_MASK
) >> RB_LOW
;
444 t3
= (insn
& IMM_MASK
) >> IMM_LOW
;
450 microblaze_get_target_address (long inst
, bfd_boolean immfound
, int immval
,
451 long pcval
, long r1val
, long r2val
,
452 bfd_boolean
*targetvalid
,
453 bfd_boolean
*unconditionalbranch
)
455 struct op_code_struct
* op
;
458 *unconditionalbranch
= FALSE
;
459 /* Just a linear search of the table. */
460 for (op
= opcodes
; op
->name
!= 0; op
++)
461 if (op
->bit_sequence
== (inst
& op
->opcode_mask
))
466 *targetvalid
= FALSE
;
468 else if (op
->instr_type
== branch_inst
)
470 switch (op
->inst_type
)
473 *unconditionalbranch
= TRUE
;
475 case INST_TYPE_RD_R2
:
476 case INST_TYPE_R1_R2
:
479 if (op
->inst_offset_type
== INST_PC_OFFSET
)
483 *unconditionalbranch
= TRUE
;
485 case INST_TYPE_RD_IMM
:
486 case INST_TYPE_R1_IMM
:
489 targetaddr
= (immval
<< 16) & 0xffff0000;
490 targetaddr
|= (get_int_field_imm (inst
) & 0x0000ffff);
494 targetaddr
= get_int_field_imm (inst
);
495 if (targetaddr
& 0x8000)
496 targetaddr
|= 0xFFFF0000;
498 if (op
->inst_offset_type
== INST_PC_OFFSET
)
503 *targetvalid
= FALSE
;
507 else if (op
->instr_type
== return_inst
)
511 targetaddr
= (immval
<< 16) & 0xffff0000;
512 targetaddr
|= (get_int_field_imm (inst
) & 0x0000ffff);
516 targetaddr
= get_int_field_imm (inst
);
517 if (targetaddr
& 0x8000)
518 targetaddr
|= 0xFFFF0000;
524 *targetvalid
= FALSE
;