1/* Adapteva epiphany opcode support.  -*- C -*-
2
3   Copyright 2009, 2011 Free Software Foundation, Inc.
4
5   Contributed by Embecosm on behalf of Adapteva, Inc.
6
7   This file is part of the GNU Binutils and of GDB.
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 3 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
22   MA 02110-1301, USA.  */
23
24/*
25   Each section is delimited with start and end markers.
26
27   <arch>-opc.h additions use: "-- opc.h"
28   <arch>-opc.c additions use: "-- opc.c"
29   <arch>-asm.c additions use: "-- asm.c"
30   <arch>-dis.c additions use: "-- dis.c"
31   <arch>-ibd.h additions use: "-- ibd.h".  */
32
33/* -- opc.h */
34
35/* enumerate relaxation types for gas. */
36typedef enum epiphany_relax_types
37{
38  EPIPHANY_RELAX_NONE=0,
39  EPIPHANY_RELAX_NEED_RELAXING,
40
41  EPIPHANY_RELAX_BRANCH_SHORT,	/* Fits into +127..-128 */
42  EPIPHANY_RELAX_BRANCH_LONG,	/* b/bl/b<cond> +-2*16 */
43
44  EPIPHANY_RELAX_ARITH_SIMM3,	/* add/sub -7..3 */
45  EPIPHANY_RELAX_ARITH_SIMM11,	/* add/sub -2**11-1 .. 2**10-1 */
46
47  EPIPHANY_RELAX_MOV_IMM8,		/* mov r,imm8 */
48  EPIPHANY_RELAX_MOV_IMM16,	/* mov r,imm16 */
49
50  EPIPHANY_RELAX_LDST_IMM3,	/* (ldr|str)* r,[r,disp3] */
51  EPIPHANY_RELAX_LDST_IMM11	/* (ldr|str)* r,[r,disp11] */
52
53} EPIPHANY_RELAX_TYPES;
54
55/* Override disassembly hashing... */
56
57/* Can only depend on instruction having 4 decode bits which gets us to the
58   major groups of 16/32 instructions. */
59#undef CGEN_DIS_HASH_SIZE
60#if 1
61
62/* hash code on the 4 LSBs */
63#define CGEN_DIS_HASH_SIZE 16
64
65#define CGEN_DIS_HASH(buf, value) ((*buf) & 0xf)
66#else
67#define CGEN_DIS_HASH_SIZE 1
68#define CGEN_DIS_HASH(buf, value) 0
69#endif
70
71extern const char * parse_shortregs (CGEN_CPU_DESC cd,
72				     const char ** strp,
73				     CGEN_KEYWORD * keywords,
74				     long * valuep);
75
76extern const char * parse_branch_addr (CGEN_CPU_DESC cd,
77				       const char ** strp,
78				       int opindex,
79				       int opinfo,
80				       enum cgen_parse_operand_result * resultp,
81				       bfd_vma *valuep);
82
83/* Allows reason codes to be output when assembler errors occur.  */
84#define CGEN_VERBOSE_ASSEMBLER_ERRORS
85
86
87/* -- opc.c */
88
89
90
91/* -- asm.c */
92const char *
93parse_shortregs (CGEN_CPU_DESC cd,
94		 const char ** strp,
95		 CGEN_KEYWORD * keywords,
96		 long * regno)
97{
98  const char * errmsg;
99
100  /* Parse register.  */
101  errmsg = cgen_parse_keyword (cd, strp, keywords, regno);
102
103  if (errmsg)
104    return errmsg;
105
106  if (*regno > 7)
107    errmsg = _("register unavailable for short instructions");
108
109  return errmsg;
110}
111
112static const char * parse_simm_not_reg (CGEN_CPU_DESC, const char **, int,
113					long *);
114
115static const char *
116parse_uimm_not_reg (CGEN_CPU_DESC cd,
117		    const char ** strp,
118		    int opindex,
119		    unsigned long * valuep)
120{
121  long * svalp = (void *) valuep;
122  return parse_simm_not_reg (cd, strp, opindex, svalp);
123}
124
125/* Handle simm3/simm11/imm3/imm12.  */
126
127static const char *
128parse_simm_not_reg (CGEN_CPU_DESC cd,
129		   const char ** strp,
130		   int opindex,
131		   long * valuep)
132{
133  const char * errmsg;
134
135  int   sign = 0;
136  int   bits = 0;
137
138  switch (opindex)
139    {
140    case EPIPHANY_OPERAND_SIMM3:
141      sign = 1; bits = 3; break;
142    case EPIPHANY_OPERAND_SIMM11:
143      sign = 1; bits = 11; break;
144    case EPIPHANY_OPERAND_DISP3:
145      sign = 0; bits = 3; break;
146    case EPIPHANY_OPERAND_DISP11:
147      /* Load/store displacement is a sign-magnitude 12 bit value.  */
148      sign = 0; bits = 11; break;
149    }
150
151  /* First try to parse as a register name and reject the operand.  */
152  errmsg = cgen_parse_keyword (cd, strp, & epiphany_cgen_opval_gr_names,valuep);
153  if (!errmsg)
154    return _("register name used as immediate value");
155
156  errmsg = (sign ? cgen_parse_signed_integer (cd, strp, opindex, valuep)
157	    : cgen_parse_unsigned_integer (cd, strp, opindex,
158					  (unsigned long *) valuep));
159  if (errmsg)
160    return errmsg;
161
162  if (sign)
163    errmsg = cgen_validate_signed_integer (*valuep,
164					  -((1L << bits) - 1), (1 << (bits - 1)) - 1);
165  else
166    errmsg = cgen_validate_unsigned_integer (*valuep, 0, (1L << bits) - 1);
167
168  return errmsg;
169}
170
171static const char *
172parse_postindex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
173		 const char ** strp,
174		 int opindex ATTRIBUTE_UNUSED,
175		 unsigned long *valuep)
176{
177  if (**strp == '#')
178    ++*strp;			/* Skip leading hashes.  */
179
180  if (**strp == '-')
181    {
182      *valuep = 1;
183      ++*strp;
184    }
185  else if (**strp == '+')
186    {
187      *valuep = 0;
188      ++*strp;
189    }
190  else
191    *valuep = 0;
192
193  return NULL;
194}
195
196static const char *
197parse_imm8 (CGEN_CPU_DESC cd,
198	    const char ** strp,
199	    int opindex,
200	    bfd_reloc_code_real_type code,
201	    enum cgen_parse_operand_result * result_type,
202	    bfd_vma * valuep)
203{
204  const char * errmsg;
205  enum cgen_parse_operand_result rt;
206  long dummyval;
207
208  if (!result_type)
209    result_type = &rt;
210
211  code = BFD_RELOC_NONE;
212
213  if (!cgen_parse_keyword (cd, strp, &epiphany_cgen_opval_gr_names, &dummyval)
214      || !cgen_parse_keyword (cd, strp, &epiphany_cgen_opval_cr_names,
215			      &dummyval))
216    /* Don't treat "mov ip,ip" as a move-immediate.  */
217    return _("register source in immediate move");
218
219  errmsg = cgen_parse_address (cd, strp, opindex, code, result_type, valuep);
220  if (errmsg)
221    return errmsg;
222
223  if (*result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
224    errmsg = cgen_validate_unsigned_integer (*valuep, 0, 0xff);
225  else
226    errmsg = _("byte relocation unsupported");
227
228  *valuep &= 0xff;
229  return errmsg;
230}
231
232static const char * MISSING_CLOSE_PARENTHESIS = N_("missing `)'");
233
234static const char *
235parse_imm16 (CGEN_CPU_DESC cd,
236	     const char ** strp,
237	     int opindex,
238	     bfd_reloc_code_real_type code ATTRIBUTE_UNUSED,
239	     enum cgen_parse_operand_result * result_type,
240	     bfd_vma * valuep)
241{
242  const char * errmsg;
243  enum cgen_parse_operand_result rt;
244  long dummyval;
245
246  if (!result_type)
247    result_type = &rt;
248
249  if (strncasecmp (*strp, "%high(", 6) == 0)
250    {
251      *strp += 6;
252      errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_EPIPHANY_HIGH,
253				   result_type, valuep);
254      if (**strp != ')')
255	return MISSING_CLOSE_PARENTHESIS;
256      ++*strp;
257      *valuep >>= 16;
258    }
259  else if (strncasecmp (*strp, "%low(", 5) == 0)
260    {
261      *strp += 5;
262      errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_EPIPHANY_LOW,
263				   result_type, valuep);
264      if (**strp != ')')
265	return MISSING_CLOSE_PARENTHESIS;
266      ++*strp;
267    }
268  else if (!cgen_parse_keyword (cd, strp, &epiphany_cgen_opval_gr_names,
269				&dummyval)
270	   || !cgen_parse_keyword (cd, strp, &epiphany_cgen_opval_cr_names,
271				   &dummyval))
272    /* Don't treat "mov ip,ip" as a move-immediate.  */
273    return _("register source in immediate move");
274  else
275    errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_16,
276				 result_type, valuep);
277
278  if (!errmsg && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
279    errmsg = cgen_validate_unsigned_integer (*valuep, 0, 0xffff);
280
281  *valuep &= 0xffff;
282  return errmsg;
283}
284
285const char *
286parse_branch_addr (CGEN_CPU_DESC cd,
287		   const char ** strp,
288		   int opindex,
289		   int opinfo ATTRIBUTE_UNUSED,
290		   enum cgen_parse_operand_result * resultp ATTRIBUTE_UNUSED,
291		   bfd_vma *valuep ATTRIBUTE_UNUSED)
292{
293  const char * errmsg;
294  enum cgen_parse_operand_result result_type;
295  bfd_reloc_code_real_type code = BFD_RELOC_NONE;
296  bfd_vma value;
297
298  switch (opindex)
299    {
300    case EPIPHANY_OPERAND_SIMM24:
301      code = BFD_RELOC_EPIPHANY_SIMM24;
302      break;
303
304    case EPIPHANY_OPERAND_SIMM8:
305      code = BFD_RELOC_EPIPHANY_SIMM8;
306      break;
307
308    default:
309      errmsg = _("ABORT: unknown operand");
310      return errmsg;
311    }
312
313  errmsg = cgen_parse_address (cd, strp, opindex, code,
314			       &result_type, &value);
315  if (errmsg == NULL)
316    {
317      if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
318	{
319	  /* Act as if we had done a PC-relative branch, ala .+num.  */
320	  char buf[20];
321	  const char * bufp = (const char *) buf;
322
323	  sprintf (buf, ".+%ld", (long) value);
324	  errmsg = cgen_parse_address (cd, &bufp, opindex, code, &result_type,
325				       &value);
326	}
327
328      if (result_type == CGEN_PARSE_OPERAND_RESULT_QUEUED)
329	{
330	  /* This will happen for things like (s2-s1) where s2 and s1
331	     are labels.  */
332	  /* Nothing further to be done.  */
333	}
334      else
335	errmsg = _("Not a pc-relative address.");
336    }
337  return errmsg;
338}
339
340/* -- dis.c */
341
342#define CGEN_PRINT_INSN epiphany_print_insn
343
344static int
345epiphany_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
346{
347  bfd_byte buf[CGEN_MAX_INSN_SIZE];
348  int buflen;
349  int status;
350
351  info->bytes_per_chunk = 2;
352  info->bytes_per_line = 4;
353
354  /* Attempt to read the base part of the insn.  */
355  buflen = cd->base_insn_bitsize / 8;
356  status = (*info->read_memory_func) (pc, buf, buflen, info);
357
358  /* Try again with the minimum part, if min < base.  */
359  if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
360    {
361      buflen = cd->min_insn_bitsize / 8;
362      status = (*info->read_memory_func) (pc, buf, buflen, info);
363    }
364
365  if (status != 0)
366    {
367      (*info->memory_error_func) (status, pc, info);
368      return -1;
369    }
370
371  return print_insn (cd, pc, info, buf, buflen);
372}
373
374
375static void
376print_postindex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
377		 void * dis_info,
378		 long value,
379		 unsigned int attrs ATTRIBUTE_UNUSED,
380		 bfd_vma pc ATTRIBUTE_UNUSED,
381		 int length ATTRIBUTE_UNUSED)
382{
383  disassemble_info *info = (disassemble_info *) dis_info;
384  (*info->fprintf_func) (info->stream, value ? "-" : "+");
385}
386
387static void
388print_simm_not_reg (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
389		    void * dis_info,
390		    long value,
391		    unsigned int attrs ATTRIBUTE_UNUSED,
392		    bfd_vma pc ATTRIBUTE_UNUSED,
393		    int length ATTRIBUTE_UNUSED)
394{
395  print_address (cd, dis_info, value, attrs, pc, length);
396}
397
398static void
399print_uimm_not_reg (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
400		    void * dis_info,
401		    unsigned long value,
402		    unsigned int attrs ATTRIBUTE_UNUSED,
403		    bfd_vma pc ATTRIBUTE_UNUSED,
404		    int length ATTRIBUTE_UNUSED)
405{
406  disassemble_info *info = (disassemble_info *)dis_info;
407
408  if (value & 0x800)
409    (*info->fprintf_func) (info->stream, "-");
410
411  value &= 0x7ff;
412  print_address (cd, dis_info, value, attrs, pc, length);
413}
414
415
416/* -- */
417
418