xref: /netbsd-src/external/gpl3/binutils.old/dist/cpu/or1k.opc (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1/* OpenRISC 1000 opcode support.  -*- C -*-
2   Copyright 2000-2014 Free Software Foundation, Inc.
3
4   Originally ontributed for OR32 by Red Hat Inc;
5
6   This file is part of the GNU Binutils.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, see <http://www.gnu.org/licenses/>. */
20
21/* This file is an addendum to or1k.cpu.  Heavy use of C code isn't
22   appropriate in .cpu files, so it resides here.  This especially applies
23   to assembly/disassembly where parsing/printing can be quite involved.
24   Such things aren't really part of the specification of the cpu, per se,
25   so .cpu files provide the general framework and .opc files handle the
26   nitty-gritty details as necessary.
27
28   Each section is delimited with start and end markers.
29
30   <arch>-opc.h additions use: "-- opc.h"
31   <arch>-opc.c additions use: "-- opc.c"
32   <arch>-asm.c additions use: "-- asm.c"
33   <arch>-dis.c additions use: "-- dis.c"
34   <arch>-ibd.h additions use: "-- ibd.h"  */
35
36/* -- opc.h */
37
38#undef  CGEN_DIS_HASH_SIZE
39#define CGEN_DIS_HASH_SIZE 256
40#undef  CGEN_DIS_HASH
41#define CGEN_DIS_HASH(buffer, value) (((unsigned char *) (buffer))[0] >> 2)
42
43/* Check applicability of instructions against machines.  */
44#define CGEN_VALIDATE_INSN_SUPPORTED
45
46extern int or1k_cgen_insn_supported (CGEN_CPU_DESC, const CGEN_INSN *);
47
48/* -- */
49
50/* -- opc.c */
51
52/* Special check to ensure that instruction exists for given machine.  */
53
54int
55or1k_cgen_insn_supported (CGEN_CPU_DESC cd, const CGEN_INSN *insn)
56{
57  int machs = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_MACH);
58
59  /* No mach attribute?  Assume it's supported for all machs.  */
60  if (machs == 0)
61    return 1;
62
63  return ((machs & cd->machs) != 0);
64}
65
66/* -- */
67
68/* -- asm.c */
69
70static const char * MISSING_CLOSING_PARENTHESIS = N_("missing `)'");
71static const char * INVALID_STORE_RELOC = N_("relocation invalid for store");
72static const char * INVALID_RELOC_TYPE = N_("internal relocation type invalid");
73
74#define CGEN_VERBOSE_ASSEMBLER_ERRORS
75
76static const char *
77parse_disp26 (CGEN_CPU_DESC cd,
78	      const char ** strp,
79	      int opindex,
80	      int opinfo ATTRIBUTE_UNUSED,
81	      enum cgen_parse_operand_result * resultp,
82	      bfd_vma * valuep)
83{
84  const char *str = *strp;
85  const char *errmsg = NULL;
86  bfd_reloc_code_real_type reloc = BFD_RELOC_OR1K_REL_26;
87
88  if (strncasecmp (str, "plta(", 5) == 0)
89    {
90      *strp = str + 5;
91      reloc = BFD_RELOC_OR1K_PLTA26;
92    }
93  else if (strncasecmp (str, "plt(", 4) == 0)
94    {
95      *strp = str + 4;
96      reloc = BFD_RELOC_OR1K_PLT26;
97    }
98
99  errmsg = cgen_parse_address (cd, strp, opindex, reloc, resultp, valuep);
100
101  if (reloc != BFD_RELOC_OR1K_REL_26)
102    {
103      if (**strp != ')')
104	errmsg = MISSING_CLOSING_PARENTHESIS;
105      else
106	++*strp;
107    }
108
109  return errmsg;
110}
111
112static const char *
113parse_disp21 (CGEN_CPU_DESC cd,
114	      const char ** strp,
115	      int opindex,
116	      int opinfo ATTRIBUTE_UNUSED,
117	      enum cgen_parse_operand_result * resultp,
118	      bfd_vma * valuep)
119{
120  const char *str = *strp;
121  const char *errmsg = NULL;
122  bfd_reloc_code_real_type reloc = BFD_RELOC_OR1K_PCREL_PG21;
123
124  if (strncasecmp (str, "got(", 4) == 0)
125    {
126      *strp = str + 4;
127      reloc = BFD_RELOC_OR1K_GOT_PG21;
128    }
129  else if (strncasecmp (str, "tlsgd(", 6) == 0)
130    {
131      *strp = str + 6;
132      reloc = BFD_RELOC_OR1K_TLS_GD_PG21;
133    }
134  else if (strncasecmp (str, "tlsldm(", 7) == 0)
135    {
136      *strp = str + 7;
137      reloc = BFD_RELOC_OR1K_TLS_LDM_PG21;
138    }
139  else if (strncasecmp (str, "gottp(", 6) == 0)
140    {
141      *strp = str + 6;
142      reloc = BFD_RELOC_OR1K_TLS_IE_PG21;
143    }
144
145  errmsg = cgen_parse_address (cd, strp, opindex, reloc, resultp, valuep);
146
147  if (reloc != BFD_RELOC_OR1K_PCREL_PG21)
148    {
149      if (**strp != ')')
150	errmsg = MISSING_CLOSING_PARENTHESIS;
151      else
152	++*strp;
153    }
154
155  return errmsg;
156}
157
158enum or1k_rclass
159{
160  RCLASS_DIRECT   = 0,
161  RCLASS_GOT      = 1,
162  RCLASS_GOTPC    = 2,
163  RCLASS_GOTOFF   = 3,
164  RCLASS_TLSGD    = 4,
165  RCLASS_TLSLDM   = 5,
166  RCLASS_DTPOFF   = 6,
167  RCLASS_GOTTPOFF = 7,
168  RCLASS_TPOFF    = 8,
169};
170
171enum or1k_rtype
172{
173  RTYPE_LO = 0,
174  RTYPE_SLO = 1,
175  RTYPE_PO = 2,
176  RTYPE_SPO = 3,
177  RTYPE_HI = 4,
178  RTYPE_AHI = 5,
179};
180
181#define RCLASS_SHIFT 3
182#define RTYPE_MASK   7
183
184static const bfd_reloc_code_real_type or1k_imm16_relocs[][6] = {
185  { BFD_RELOC_LO16,
186    BFD_RELOC_OR1K_SLO16,
187    BFD_RELOC_OR1K_LO13,
188    BFD_RELOC_OR1K_SLO13,
189    BFD_RELOC_HI16,
190    BFD_RELOC_HI16_S, },
191  { BFD_RELOC_OR1K_GOT16,
192    BFD_RELOC_UNUSED,
193    BFD_RELOC_OR1K_GOT_LO13,
194    BFD_RELOC_UNUSED,
195    BFD_RELOC_UNUSED,
196    BFD_RELOC_UNUSED },
197  { BFD_RELOC_OR1K_GOTPC_LO16,
198    BFD_RELOC_UNUSED,
199    BFD_RELOC_UNUSED,
200    BFD_RELOC_UNUSED,
201    BFD_RELOC_OR1K_GOTPC_HI16,
202    BFD_RELOC_UNUSED },
203  { BFD_RELOC_LO16_GOTOFF,
204    BFD_RELOC_OR1K_GOTOFF_SLO16,
205    BFD_RELOC_UNUSED,
206    BFD_RELOC_UNUSED,
207    BFD_RELOC_HI16_GOTOFF,
208    BFD_RELOC_HI16_S_GOTOFF },
209  { BFD_RELOC_OR1K_TLS_GD_LO16,
210    BFD_RELOC_UNUSED,
211    BFD_RELOC_OR1K_TLS_GD_LO13,
212    BFD_RELOC_UNUSED,
213    BFD_RELOC_OR1K_TLS_GD_HI16,
214    BFD_RELOC_UNUSED },
215  { BFD_RELOC_OR1K_TLS_LDM_LO16,
216    BFD_RELOC_UNUSED,
217    BFD_RELOC_OR1K_TLS_LDM_LO13,
218    BFD_RELOC_UNUSED,
219    BFD_RELOC_OR1K_TLS_LDM_HI16,
220    BFD_RELOC_UNUSED },
221  { BFD_RELOC_OR1K_TLS_LDO_LO16,
222    BFD_RELOC_UNUSED,
223    BFD_RELOC_UNUSED,
224    BFD_RELOC_UNUSED,
225    BFD_RELOC_OR1K_TLS_LDO_HI16,
226    BFD_RELOC_UNUSED },
227  { BFD_RELOC_OR1K_TLS_IE_LO16,
228    BFD_RELOC_UNUSED,
229    BFD_RELOC_OR1K_TLS_IE_LO13,
230    BFD_RELOC_UNUSED,
231    BFD_RELOC_OR1K_TLS_IE_HI16,
232    BFD_RELOC_OR1K_TLS_IE_AHI16 },
233  { BFD_RELOC_OR1K_TLS_LE_LO16,
234    BFD_RELOC_OR1K_TLS_LE_SLO16,
235    BFD_RELOC_UNUSED,
236    BFD_RELOC_UNUSED,
237    BFD_RELOC_OR1K_TLS_LE_HI16,
238    BFD_RELOC_OR1K_TLS_LE_AHI16 },
239};
240
241static int
242parse_reloc (const char **strp)
243{
244    const char *str = *strp;
245    enum or1k_rclass cls = RCLASS_DIRECT;
246    enum or1k_rtype typ;
247
248    if (strncasecmp (str, "got(", 4) == 0)
249      {
250	*strp = str + 4;
251	return (RCLASS_GOT << RCLASS_SHIFT) | RTYPE_LO;
252      }
253    if (strncasecmp (str, "gotpo(", 6) == 0)
254      {
255	*strp = str + 6;
256	return (RCLASS_GOT << RCLASS_SHIFT) | RTYPE_PO;
257      }
258    if (strncasecmp (str, "gottppo(", 8) == 0)
259      {
260	*strp = str + 8;
261	return (RCLASS_GOTTPOFF << RCLASS_SHIFT) | RTYPE_PO;
262      }
263
264    if (strncasecmp (str, "gotpc", 5) == 0)
265      {
266	str += 5;
267	cls = RCLASS_GOTPC;
268      }
269    else if (strncasecmp (str, "gotoff", 6) == 0)
270      {
271	str += 6;
272	cls = RCLASS_GOTOFF;
273      }
274    else if (strncasecmp (str, "tlsgd", 5) == 0)
275      {
276	str += 5;
277	cls = RCLASS_TLSGD;
278      }
279    else if (strncasecmp (str, "tlsldm", 6) == 0)
280      {
281	str += 6;
282	cls = RCLASS_TLSLDM;
283      }
284    else if (strncasecmp (str, "dtpoff", 6) == 0)
285      {
286	str += 6;
287	cls = RCLASS_DTPOFF;
288      }
289    else if (strncasecmp (str, "gottpoff", 8) == 0)
290      {
291	str += 8;
292	cls = RCLASS_GOTTPOFF;
293      }
294    else if (strncasecmp (str, "tpoff", 5) == 0)
295      {
296	str += 5;
297	cls = RCLASS_TPOFF;
298      }
299
300    if (strncasecmp (str, "hi(", 3) == 0)
301      {
302	str += 3;
303	typ = RTYPE_HI;
304      }
305    else if (strncasecmp (str, "lo(", 3) == 0)
306      {
307	str += 3;
308	typ = RTYPE_LO;
309      }
310    else if (strncasecmp (str, "ha(", 3) == 0)
311      {
312	str += 3;
313	typ = RTYPE_AHI;
314      }
315    else if (strncasecmp (str, "po(", 3) == 0 && cls != RCLASS_GOTTPOFF)
316      {
317	str += 3;
318	typ = RTYPE_PO;
319      }
320    else
321      return -1;
322
323    *strp = str;
324    return (cls << RCLASS_SHIFT) | typ;
325}
326
327static const char *
328parse_imm16 (CGEN_CPU_DESC cd, const char **strp, int opindex,
329	     long *valuep, int splitp)
330{
331  const char *errmsg;
332  enum cgen_parse_operand_result result_type;
333  bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
334  enum or1k_rtype reloc_type;
335  int reloc_code;
336  bfd_vma ret;
337
338  if (**strp == '#')
339    ++*strp;
340
341  reloc_code = parse_reloc (strp);
342  reloc_type = reloc_code & RTYPE_MASK;
343  if (reloc_code >= 0)
344    {
345      enum or1k_rclass reloc_class = reloc_code >> RCLASS_SHIFT;
346      if (splitp)
347	{
348	  if ((reloc_type == RTYPE_LO || reloc_type == RTYPE_PO)
349	      && reloc_class != RCLASS_GOT)
350	    /* If split we or up the type to RTYPE_SLO or RTYPE_SPO.  */
351	    reloc_type |= 1;
352	  else
353	    return INVALID_STORE_RELOC;
354	}
355      reloc = or1k_imm16_relocs[reloc_class][reloc_type];
356    }
357
358  if (reloc != BFD_RELOC_UNUSED)
359    {
360      bfd_vma value;
361
362      errmsg = cgen_parse_address (cd, strp, opindex, reloc,
363				   &result_type, &value);
364      if (**strp != ')')
365	errmsg = MISSING_CLOSING_PARENTHESIS;
366      ++*strp;
367
368      ret = value;
369
370      if (errmsg == NULL && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
371	switch (reloc_type)
372	  {
373	  case RTYPE_AHI:
374	    ret += 0x8000;
375	    /* FALLTHRU */
376	  case RTYPE_HI:
377	    ret >>= 16;
378	    /* FALLTHRU */
379	  case RTYPE_LO:
380	  case RTYPE_SLO:
381	    ret &= 0xffff;
382	    ret = (ret ^ 0x8000) - 0x8000;
383	    break;
384	  case RTYPE_PO:
385	  case RTYPE_SPO:
386	    ret &= 0x1fff;
387	    break;
388	  default:
389	    errmsg = INVALID_RELOC_TYPE;
390	  }
391    }
392  else
393    {
394      long value;
395      errmsg = cgen_parse_signed_integer (cd, strp, opindex, &value);
396      ret = value;
397    }
398
399  if (errmsg == NULL)
400    *valuep = ret;
401
402  return errmsg;
403}
404
405static const char *
406parse_simm16 (CGEN_CPU_DESC cd, const char **strp, int opindex, long *valuep)
407{
408  return parse_imm16(cd, strp, opindex, (long *) valuep, 0);
409}
410
411static const char *
412parse_simm16_split (CGEN_CPU_DESC cd, const char **strp, int opindex,
413		    long *valuep)
414{
415  return parse_imm16(cd, strp, opindex, (long *) valuep, 1);
416}
417
418static const char *
419parse_uimm16 (CGEN_CPU_DESC cd, const char **strp, int opindex,
420	      unsigned long *valuep)
421{
422  const char *errmsg = parse_imm16(cd, strp, opindex, (long *) valuep, 0);
423  if (errmsg == NULL)
424    *valuep &= 0xffff;
425  return errmsg;
426}
427
428static const char *
429parse_uimm16_split (CGEN_CPU_DESC cd, const char **strp, int opindex,
430		    unsigned long *valuep)
431{
432  const char *errmsg = parse_imm16(cd, strp, opindex, (long *) valuep, 1);
433  if (errmsg == NULL)
434    *valuep &= 0xffff;
435  return errmsg;
436}
437
438/* Parse register pairs with syntax rA,rB to a flag + rA value.  */
439
440static const char *
441parse_regpair (CGEN_CPU_DESC cd, const char **strp,
442	       int opindex ATTRIBUTE_UNUSED, unsigned long *valuep)
443{
444  long reg1_index;
445  long reg2_index;
446  const char *errmsg;
447
448  /* The first part should just be a register.  */
449  errmsg = cgen_parse_keyword (cd, strp, &or1k_cgen_opval_h_gpr,
450			       &reg1_index);
451
452  /* If that worked skip the comma separator.  */
453  if (errmsg == NULL)
454    {
455      if (**strp == ',')
456	++*strp;
457      else
458	errmsg = "Unexpected character, expected ','";
459    }
460
461  /* If that worked the next part is just another register.  */
462  if (errmsg == NULL)
463    errmsg = cgen_parse_keyword (cd, strp, &or1k_cgen_opval_h_gpr,
464				 &reg2_index);
465
466  /* Validate the register pair is valid and create the output value.  */
467  if (errmsg == NULL)
468    {
469      int regoffset = reg2_index - reg1_index;
470
471      if (regoffset == 1 || regoffset == 2)
472	{
473	  unsigned short offsetmask;
474	  unsigned short value;
475
476	  offsetmask = ((regoffset == 2 ? 1 : 0) << 5);
477	  value = offsetmask | reg1_index;
478
479	  *valuep = value;
480	}
481      else
482	errmsg = "Invalid register pair, offset not 1 or 2.";
483    }
484
485  return errmsg;
486}
487
488/* -- */
489
490/* -- dis.c */
491
492static void
493print_regpair (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
494	       void * dis_info,
495	       long value,
496	       unsigned int attrs ATTRIBUTE_UNUSED,
497	       bfd_vma pc ATTRIBUTE_UNUSED,
498	       int length ATTRIBUTE_UNUSED)
499{
500  disassemble_info *info = dis_info;
501  char reg1_index;
502  char reg2_index;
503
504  reg1_index = value & 0x1f;
505  reg2_index = reg1_index + ((value & (1 << 5)) ? 2 : 1);
506
507  (*info->fprintf_func) (info->stream, "r%d,r%d", reg1_index, reg2_index);
508}
509
510/* -- */
511
512/* -- ibd.h */
513
514/* -- */
515