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_OR1K_GOT_AHI16 },
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    else if (strncasecmp (str, "got", 3) == 0)
300      {
301          str += 3;
302          cls = RCLASS_GOT;
303      }
304
305    if (strncasecmp (str, "hi(", 3) == 0)
306      {
307          str += 3;
308          typ = RTYPE_HI;
309      }
310    else if (strncasecmp (str, "lo(", 3) == 0)
311      {
312          str += 3;
313          typ = RTYPE_LO;
314      }
315    else if (strncasecmp (str, "ha(", 3) == 0)
316      {
317          str += 3;
318          typ = RTYPE_AHI;
319      }
320    else if (strncasecmp (str, "po(", 3) == 0 && cls != RCLASS_GOTTPOFF)
321      {
322          str += 3;
323          typ = RTYPE_PO;
324      }
325    else
326      return -1;
327
328    *strp = str;
329    return (cls << RCLASS_SHIFT) | typ;
330}
331
332static const char *
333parse_imm16 (CGEN_CPU_DESC cd, const char **strp, int opindex,
334               long *valuep, int splitp)
335{
336  const char *errmsg;
337  enum cgen_parse_operand_result result_type;
338  bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
339  enum or1k_rtype reloc_type;
340  int reloc_code;
341  bfd_vma ret;
342
343  if (**strp == '#')
344    ++*strp;
345
346  reloc_code = parse_reloc (strp);
347  reloc_type = reloc_code & RTYPE_MASK;
348  if (reloc_code >= 0)
349    {
350      enum or1k_rclass reloc_class = reloc_code >> RCLASS_SHIFT;
351      if (splitp)
352          {
353            if ((reloc_type == RTYPE_LO || reloc_type == RTYPE_PO)
354                && reloc_class != RCLASS_GOT)
355              /* If split we or up the type to RTYPE_SLO or RTYPE_SPO.  */
356              reloc_type |= 1;
357            else
358              return INVALID_STORE_RELOC;
359          }
360      reloc = or1k_imm16_relocs[reloc_class][reloc_type];
361    }
362
363  if (reloc != BFD_RELOC_UNUSED)
364    {
365      bfd_vma value;
366
367      errmsg = cgen_parse_address (cd, strp, opindex, reloc,
368                                           &result_type, &value);
369      if (**strp != ')')
370          errmsg = MISSING_CLOSING_PARENTHESIS;
371      ++*strp;
372
373      ret = value;
374
375      if (errmsg == NULL && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
376          switch (reloc_type)
377            {
378            case RTYPE_AHI:
379              ret += 0x8000;
380              /* FALLTHRU */
381            case RTYPE_HI:
382              ret >>= 16;
383              /* FALLTHRU */
384            case RTYPE_LO:
385            case RTYPE_SLO:
386              ret &= 0xffff;
387              ret = (ret ^ 0x8000) - 0x8000;
388              break;
389            case RTYPE_PO:
390            case RTYPE_SPO:
391              ret &= 0x1fff;
392              break;
393            default:
394              errmsg = INVALID_RELOC_TYPE;
395            }
396    }
397  else
398    {
399      long value;
400      errmsg = cgen_parse_signed_integer (cd, strp, opindex, &value);
401      ret = value;
402    }
403
404  if (errmsg == NULL)
405    *valuep = ret;
406
407  return errmsg;
408}
409
410static const char *
411parse_simm16 (CGEN_CPU_DESC cd, const char **strp, int opindex, long *valuep)
412{
413  return parse_imm16(cd, strp, opindex, (long *) valuep, 0);
414}
415
416static const char *
417parse_simm16_split (CGEN_CPU_DESC cd, const char **strp, int opindex,
418                        long *valuep)
419{
420  return parse_imm16(cd, strp, opindex, (long *) valuep, 1);
421}
422
423static const char *
424parse_uimm16 (CGEN_CPU_DESC cd, const char **strp, int opindex,
425                unsigned long *valuep)
426{
427  const char *errmsg = parse_imm16(cd, strp, opindex, (long *) valuep, 0);
428  if (errmsg == NULL)
429    *valuep &= 0xffff;
430  return errmsg;
431}
432
433static const char *
434parse_uimm16_split (CGEN_CPU_DESC cd, const char **strp, int opindex,
435                        unsigned long *valuep)
436{
437  const char *errmsg = parse_imm16(cd, strp, opindex, (long *) valuep, 1);
438  if (errmsg == NULL)
439    *valuep &= 0xffff;
440  return errmsg;
441}
442
443/* Parse register pairs with syntax rA,rB to a flag + rA value.  */
444
445static const char *
446parse_regpair (CGEN_CPU_DESC cd, const char **strp,
447                 int opindex ATTRIBUTE_UNUSED, unsigned long *valuep)
448{
449  long reg1_index;
450  long reg2_index;
451  const char *errmsg;
452
453  /* The first part should just be a register.  */
454  errmsg = cgen_parse_keyword (cd, strp, &or1k_cgen_opval_h_gpr,
455                                     &reg1_index);
456
457  /* If that worked skip the comma separator.  */
458  if (errmsg == NULL)
459    {
460      if (**strp == ',')
461          ++*strp;
462      else
463          errmsg = "Unexpected character, expected ','";
464    }
465
466  /* If that worked the next part is just another register.  */
467  if (errmsg == NULL)
468    errmsg = cgen_parse_keyword (cd, strp, &or1k_cgen_opval_h_gpr,
469                                         &reg2_index);
470
471  /* Validate the register pair is valid and create the output value.  */
472  if (errmsg == NULL)
473    {
474      int regoffset = reg2_index - reg1_index;
475
476      if (regoffset == 1 || regoffset == 2)
477          {
478            unsigned short offsetmask;
479            unsigned short value;
480
481            offsetmask = ((regoffset == 2 ? 1 : 0) << 5);
482            value = offsetmask | reg1_index;
483
484            *valuep = value;
485          }
486      else
487          errmsg = "Invalid register pair, offset not 1 or 2.";
488    }
489
490  return errmsg;
491}
492
493/* -- */
494
495/* -- dis.c */
496
497static void
498print_regpair (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
499                 void * dis_info,
500                 long value,
501                 unsigned int attrs ATTRIBUTE_UNUSED,
502                 bfd_vma pc ATTRIBUTE_UNUSED,
503                 int length ATTRIBUTE_UNUSED)
504{
505  disassemble_info *info = dis_info;
506  char reg1_index;
507  char reg2_index;
508
509  reg1_index = value & 0x1f;
510  reg2_index = reg1_index + ((value & (1 << 5)) ? 2 : 1);
511
512  (*info->fprintf_func) (info->stream, "r%d,r%d", reg1_index, reg2_index);
513}
514
515/* -- */
516
517/* -- ibd.h */
518
519/* -- */
520