1/* Disassemble Xilinx microblaze instructions.
2
3   Copyright (C) 2009-2017 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 "dis-asm.h"
28#include <strings.h>
29#include "microblaze-opc.h"
30#include "microblaze-dis.h"
31
32#define get_field_rd(instr)        get_field (instr, RD_MASK, RD_LOW)
33#define get_field_r1(instr)        get_field (instr, RA_MASK, RA_LOW)
34#define get_field_r2(instr)        get_field (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
39
40static char *
41get_field (long instr, long mask, unsigned short low)
42{
43  char tmpstr[25];
44
45  sprintf (tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low));
46  return (strdup (tmpstr));
47}
48
49static char *
50get_field_imm (long instr)
51{
52  char tmpstr[25];
53
54  sprintf (tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW));
55  return (strdup (tmpstr));
56}
57
58static char *
59get_field_imm5 (long instr)
60{
61  char tmpstr[25];
62
63  sprintf (tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW));
64  return (strdup (tmpstr));
65}
66
67static char *
68get_field_imm5_mbar (long instr)
69{
70  char tmpstr[25];
71
72  sprintf(tmpstr, "%d", (short)((instr & IMM5_MBAR_MASK) >> IMM_MBAR));
73  return(strdup(tmpstr));
74}
75
76static char *
77get_field_rfsl (long instr)
78{
79  char tmpstr[25];
80
81  sprintf (tmpstr, "%s%d", fsl_register_prefix,
82	   (short)((instr & RFSL_MASK) >> IMM_LOW));
83  return (strdup (tmpstr));
84}
85
86static char *
87get_field_imm15 (long instr)
88{
89  char tmpstr[25];
90
91  sprintf (tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW));
92  return (strdup (tmpstr));
93}
94
95static char *
96get_field_special (long instr, struct op_code_struct * op)
97{
98  char tmpstr[25];
99  char spr[6];
100
101  switch ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask))
102    {
103    case REG_MSR_MASK :
104      strcpy (spr, "msr");
105      break;
106    case REG_PC_MASK :
107      strcpy (spr, "pc");
108      break;
109    case REG_EAR_MASK :
110      strcpy (spr, "ear");
111      break;
112    case REG_ESR_MASK :
113      strcpy (spr, "esr");
114      break;
115    case REG_FSR_MASK :
116      strcpy (spr, "fsr");
117      break;
118    case REG_BTR_MASK :
119      strcpy (spr, "btr");
120      break;
121    case REG_EDR_MASK :
122      strcpy (spr, "edr");
123      break;
124    case REG_PID_MASK :
125      strcpy (spr, "pid");
126      break;
127    case REG_ZPR_MASK :
128      strcpy (spr, "zpr");
129      break;
130    case REG_TLBX_MASK :
131      strcpy (spr, "tlbx");
132      break;
133    case REG_TLBLO_MASK :
134      strcpy (spr, "tlblo");
135      break;
136    case REG_TLBHI_MASK :
137      strcpy (spr, "tlbhi");
138      break;
139    case REG_TLBSX_MASK :
140      strcpy (spr, "tlbsx");
141      break;
142    case REG_SHR_MASK :
143      strcpy (spr, "shr");
144      break;
145    case REG_SLR_MASK :
146      strcpy (spr, "slr");
147      break;
148    default :
149      if (((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000)
150          == REG_PVR_MASK)
151        {
152	  sprintf (tmpstr, "%spvr%d", register_prefix,
153		   (unsigned short)(((instr & IMM_MASK) >> IMM_LOW)
154                                    ^ op->immval_mask) ^ REG_PVR_MASK);
155	  return (strdup (tmpstr));
156        }
157      else
158        strcpy (spr, "pc");
159      break;
160    }
161
162   sprintf (tmpstr, "%s%s", register_prefix, spr);
163   return (strdup (tmpstr));
164}
165
166static unsigned long
167read_insn_microblaze (bfd_vma memaddr,
168		      struct disassemble_info *info,
169		      struct op_code_struct **opr)
170{
171  unsigned char       ibytes[4];
172  int                 status;
173  struct op_code_struct * op;
174  unsigned long inst;
175
176  status = info->read_memory_func (memaddr, ibytes, 4, info);
177
178  if (status != 0)
179    {
180      info->memory_error_func (status, memaddr, info);
181      return 0;
182    }
183
184  if (info->endian == BFD_ENDIAN_BIG)
185    inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3];
186  else if (info->endian == BFD_ENDIAN_LITTLE)
187    inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0];
188  else
189    abort ();
190
191  /* Just a linear search of the table.  */
192  for (op = opcodes; op->name != 0; op ++)
193    if (op->bit_sequence == (inst & op->opcode_mask))
194      break;
195
196  *opr = op;
197  return inst;
198}
199
200
201int
202print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
203{
204  fprintf_ftype       print_func = info->fprintf_func;
205  void *              stream = info->stream;
206  unsigned long       inst, prev_inst;
207  struct op_code_struct * op, *pop;
208  int                 immval = 0;
209  bfd_boolean         immfound = FALSE;
210  static bfd_vma      prev_insn_addr = -1; /* Init the prev insn addr.  */
211  static int          prev_insn_vma = -1;  /* Init the prev insn vma.  */
212  int                 curr_insn_vma = info->buffer_vma;
213
214  info->bytes_per_chunk = 4;
215
216  inst = read_insn_microblaze (memaddr, info, &op);
217  if (inst == 0)
218    return -1;
219
220  if (prev_insn_vma == curr_insn_vma)
221    {
222      if (memaddr-(info->bytes_per_chunk) == prev_insn_addr)
223        {
224          prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
225	  if (prev_inst == 0)
226	    return -1;
227	  if (pop->instr == imm)
228	    {
229	      immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000;
230	      immfound = TRUE;
231	    }
232	  else
233	    {
234	      immval = 0;
235	      immfound = FALSE;
236	    }
237	}
238    }
239
240  /* Make curr insn as prev insn.  */
241  prev_insn_addr = memaddr;
242  prev_insn_vma = curr_insn_vma;
243
244  if (op->name == NULL)
245    print_func (stream, ".short 0x%04x", (unsigned int) inst);
246  else
247    {
248      print_func (stream, "%s", op->name);
249
250      switch (op->inst_type)
251	{
252        case INST_TYPE_RD_R1_R2:
253          print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
254		   get_field_r1(inst), get_field_r2 (inst));
255          break;
256        case INST_TYPE_RD_R1_IMM:
257	  print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
258		   get_field_r1(inst), get_field_imm (inst));
259	  if (info->print_address_func && get_int_field_r1 (inst) == 0
260	      && info->symbol_at_address_func)
261	    {
262	      if (immfound)
263	        immval |= (get_int_field_imm (inst) & 0x0000ffff);
264	      else
265		{
266	          immval = get_int_field_imm (inst);
267	          if (immval & 0x8000)
268		    immval |= 0xFFFF0000;
269	        }
270	      if (immval > 0 && info->symbol_at_address_func (immval, info))
271		{
272	          print_func (stream, "\t// ");
273	          info->print_address_func (immval, info);
274	        }
275	    }
276	  break;
277	case INST_TYPE_RD_R1_IMM5:
278	  print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
279	           get_field_r1(inst), get_field_imm5 (inst));
280	  break;
281	case INST_TYPE_RD_RFSL:
282	  print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_rfsl (inst));
283	  break;
284	case INST_TYPE_R1_RFSL:
285	  print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_rfsl (inst));
286	  break;
287	case INST_TYPE_RD_SPECIAL:
288	  print_func (stream, "\t%s, %s", get_field_rd (inst),
289		   get_field_special (inst, op));
290	  break;
291	case INST_TYPE_SPECIAL_R1:
292	  print_func (stream, "\t%s, %s", get_field_special (inst, op),
293		   get_field_r1(inst));
294	  break;
295	case INST_TYPE_RD_R1:
296	  print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_r1 (inst));
297	  break;
298	case INST_TYPE_R1_R2:
299	  print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_r2 (inst));
300	  break;
301	case INST_TYPE_R1_IMM:
302	  print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_imm (inst));
303	  /* The non-pc relative instructions are returns, which shouldn't
304	     have a label printed.  */
305	  if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET
306	      && info->symbol_at_address_func)
307	    {
308	      if (immfound)
309	        immval |= (get_int_field_imm (inst) & 0x0000ffff);
310	      else
311		{
312	          immval = get_int_field_imm (inst);
313	          if (immval & 0x8000)
314		    immval |= 0xFFFF0000;
315	        }
316	      immval += memaddr;
317	      if (immval > 0 && info->symbol_at_address_func (immval, info))
318		{
319	          print_func (stream, "\t// ");
320	          info->print_address_func (immval, info);
321	        }
322	      else
323		{
324	          print_func (stream, "\t\t// ");
325	          print_func (stream, "%x", immval);
326	        }
327	    }
328	  break;
329        case INST_TYPE_RD_IMM:
330	  print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_imm (inst));
331	  if (info->print_address_func && info->symbol_at_address_func)
332	    {
333	    if (immfound)
334	      immval |= (get_int_field_imm (inst) & 0x0000ffff);
335	    else
336	      {
337	        immval = get_int_field_imm (inst);
338	        if (immval & 0x8000)
339		  immval |= 0xFFFF0000;
340	      }
341	    if (op->inst_offset_type == INST_PC_OFFSET)
342	      immval += (int) memaddr;
343	    if (info->symbol_at_address_func (immval, info))
344	      {
345	        print_func (stream, "\t// ");
346	        info->print_address_func (immval, info);
347	      }
348	    }
349	  break;
350        case INST_TYPE_IMM:
351	  print_func (stream, "\t%s", get_field_imm (inst));
352	  if (info->print_address_func && info->symbol_at_address_func
353	      && op->instr != imm)
354	    {
355	      if (immfound)
356	        immval |= (get_int_field_imm (inst) & 0x0000ffff);
357	      else
358		{
359	          immval = get_int_field_imm (inst);
360	          if (immval & 0x8000)
361		    immval |= 0xFFFF0000;
362	        }
363	      if (op->inst_offset_type == INST_PC_OFFSET)
364	        immval += (int) memaddr;
365	      if (immval > 0 && info->symbol_at_address_func (immval, info))
366		{
367	          print_func (stream, "\t// ");
368	          info->print_address_func (immval, info);
369	        }
370	      else if (op->inst_offset_type == INST_PC_OFFSET)
371		{
372	          print_func (stream, "\t\t// ");
373	          print_func (stream, "%x", immval);
374	        }
375	    }
376	  break;
377        case INST_TYPE_RD_R2:
378	  print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
379	  break;
380	case INST_TYPE_R2:
381	  print_func (stream, "\t%s", get_field_r2 (inst));
382	  break;
383	case INST_TYPE_R1:
384	  print_func (stream, "\t%s", get_field_r1 (inst));
385	  break;
386	case INST_TYPE_R1_R2_SPECIAL:
387	  print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_r2 (inst));
388	  break;
389	case INST_TYPE_RD_IMM15:
390	  print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_imm15 (inst));
391	  break;
392        /* For mbar insn.  */
393        case INST_TYPE_IMM5:
394          print_func (stream, "\t%s", get_field_imm5_mbar (inst));
395          break;
396        /* For mbar 16 or sleep insn.  */
397        case INST_TYPE_NONE:
398          break;
399	/* For tuqula instruction */
400	case INST_TYPE_RD:
401	  print_func (stream, "\t%s", get_field_rd (inst));
402	  break;
403	case INST_TYPE_RFSL:
404	  print_func (stream, "\t%s", get_field_rfsl (inst));
405	  break;
406	default:
407	  /* If the disassembler lags the instruction set.  */
408	  print_func (stream, "\tundecoded operands, inst is 0x%04x", (unsigned int) inst);
409	  break;
410	}
411    }
412
413  /* Say how many bytes we consumed.  */
414  return 4;
415}
416
417enum microblaze_instr
418get_insn_microblaze (long inst,
419  		     bfd_boolean *isunsignedimm,
420  		     enum microblaze_instr_type *insn_type,
421  		     short *delay_slots)
422{
423  struct op_code_struct * op;
424  *isunsignedimm = FALSE;
425
426  /* Just a linear search of the table.  */
427  for (op = opcodes; op->name != 0; op ++)
428    if (op->bit_sequence == (inst & op->opcode_mask))
429      break;
430
431  if (op->name == 0)
432    return invalid_inst;
433  else
434    {
435      *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM);
436      *insn_type = op->instr_type;
437      *delay_slots = op->delay_slots;
438      return op->instr;
439    }
440}
441
442enum microblaze_instr
443microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *immed)
444{
445  enum microblaze_instr op;
446  bfd_boolean t1;
447  enum microblaze_instr_type t2;
448  short t3;
449
450  op = get_insn_microblaze (insn, &t1, &t2, &t3);
451  *rd = (insn & RD_MASK) >> RD_LOW;
452  *ra = (insn & RA_MASK) >> RA_LOW;
453  *rb = (insn & RB_MASK) >> RB_LOW;
454  t3 = (insn & IMM_MASK) >> IMM_LOW;
455  *immed = (int) t3;
456  return (op);
457}
458
459unsigned long
460microblaze_get_target_address (long inst, bfd_boolean immfound, int immval,
461			       long pcval, long r1val, long r2val,
462			       bfd_boolean *targetvalid,
463			       bfd_boolean *unconditionalbranch)
464{
465  struct op_code_struct * op;
466  long targetaddr = 0;
467
468  *unconditionalbranch = FALSE;
469  /* Just a linear search of the table.  */
470  for (op = opcodes; op->name != 0; op ++)
471    if (op->bit_sequence == (inst & op->opcode_mask))
472      break;
473
474  if (op->name == 0)
475    {
476      *targetvalid = FALSE;
477    }
478  else if (op->instr_type == branch_inst)
479    {
480      switch (op->inst_type)
481	{
482        case INST_TYPE_R2:
483          *unconditionalbranch = TRUE;
484        /* Fall through.  */
485        case INST_TYPE_RD_R2:
486        case INST_TYPE_R1_R2:
487          targetaddr = r2val;
488          *targetvalid = TRUE;
489          if (op->inst_offset_type == INST_PC_OFFSET)
490	    targetaddr += pcval;
491          break;
492        case INST_TYPE_IMM:
493          *unconditionalbranch = TRUE;
494        /* Fall through.  */
495        case INST_TYPE_RD_IMM:
496        case INST_TYPE_R1_IMM:
497          if (immfound)
498	    {
499	      targetaddr = (immval << 16) & 0xffff0000;
500	      targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
501	    }
502	  else
503	    {
504	      targetaddr = get_int_field_imm (inst);
505	      if (targetaddr & 0x8000)
506	        targetaddr |= 0xFFFF0000;
507            }
508          if (op->inst_offset_type == INST_PC_OFFSET)
509	    targetaddr += pcval;
510          *targetvalid = TRUE;
511          break;
512	default:
513	  *targetvalid = FALSE;
514	  break;
515        }
516    }
517  else if (op->instr_type == return_inst)
518    {
519      if (immfound)
520	{
521	  targetaddr = (immval << 16) & 0xffff0000;
522	  targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
523	}
524      else
525	{
526	  targetaddr = get_int_field_imm (inst);
527	  if (targetaddr & 0x8000)
528	    targetaddr |= 0xFFFF0000;
529	}
530      targetaddr += r1val;
531      *targetvalid = TRUE;
532    }
533  else
534    *targetvalid = FALSE;
535  return targetaddr;
536}
537