1/* IP2K opcode support.  -*- C -*-
2   Copyright 2002, 2005, 2011 Free Software Foundation, Inc.
3
4   Contributed 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, write to the Free Software
20   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
21   MA 02110-1301, USA.  */
22
23/*
24   Each section is delimited with start and end markers.
25
26   <arch>-opc.h additions use: "-- opc.h"
27   <arch>-opc.c additions use: "-- opc.c"
28   <arch>-asm.c additions use: "-- asm.c"
29   <arch>-dis.c additions use: "-- dis.c"
30   <arch>-ibd.h additions use: "-- ibd.h".  */
31
32/* -- opc.h */
33
34/* Check applicability of instructions against machines.  */
35#define CGEN_VALIDATE_INSN_SUPPORTED
36
37/* Allows reason codes to be output when assembler errors occur.  */
38#define CGEN_VERBOSE_ASSEMBLER_ERRORS
39
40/* Override disassembly hashing - there are variable bits in the top
41   byte of these instructions.  */
42#define CGEN_DIS_HASH_SIZE 8
43#define CGEN_DIS_HASH(buf, value) \
44  (((* (unsigned char*) (buf)) >> 5) % CGEN_DIS_HASH_SIZE)
45
46#define CGEN_ASM_HASH_SIZE 127
47#define CGEN_ASM_HASH(insn) ip2k_asm_hash (insn)
48
49extern unsigned int ip2k_asm_hash (const char *);
50extern int ip2k_cgen_insn_supported (CGEN_CPU_DESC, const CGEN_INSN *);
51
52/* -- opc.c */
53
54#include "safe-ctype.h"
55
56/* A better hash function for instruction mnemonics.  */
57unsigned int
58ip2k_asm_hash (const char* insn)
59{
60  unsigned int hash;
61  const char* m = insn;
62
63  for (hash = 0; *m && ! ISSPACE (*m); m++)
64    hash = (hash * 23) ^ (0x1F & TOLOWER (*m));
65
66  /* printf ("%s %d\n", insn, (hash % CGEN_ASM_HASH_SIZE)); */
67
68  return hash % CGEN_ASM_HASH_SIZE;
69}
70
71
72/* Special check to ensure that instruction exists for given machine.  */
73
74int
75ip2k_cgen_insn_supported (CGEN_CPU_DESC cd, const CGEN_INSN *insn)
76{
77  int machs = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_MACH);
78
79  /* No mach attribute?  Assume it's supported for all machs.  */
80  if (machs == 0)
81    return 1;
82
83  return (machs & cd->machs) != 0;
84}
85
86
87/* -- asm.c */
88
89static const char *
90parse_fr (CGEN_CPU_DESC cd,
91	  const char **strp,
92	  int opindex,
93	  unsigned long *valuep)
94{
95  const char *errmsg;
96  const char *old_strp;
97  char *afteroffset;
98  enum cgen_parse_operand_result result_type;
99  bfd_vma value;
100  extern CGEN_KEYWORD ip2k_cgen_opval_register_names;
101  bfd_vma tempvalue;
102
103  old_strp = *strp;
104  afteroffset = NULL;
105
106  /* Check here to see if you're about to try parsing a w as the first arg
107     and return an error if you are.  */
108  if ((strncmp (*strp, "w", 1) == 0) || (strncmp (*strp, "W", 1) == 0))
109    {
110      (*strp)++;
111
112      if ((strncmp (*strp, ",", 1) == 0) || ISSPACE (**strp))
113	{
114	  /* We've been passed a w.  Return with an error message so that
115	     cgen will try the next parsing option.  */
116	  errmsg = _("W keyword invalid in FR operand slot.");
117	  return errmsg;
118	}
119      *strp = old_strp;
120    }
121
122  /* Attempt parse as register keyword. */
123  errmsg = cgen_parse_keyword (cd, strp, & ip2k_cgen_opval_register_names,
124			       (long *) valuep);
125  if (*strp != NULL
126      && errmsg == NULL)
127    return errmsg;
128
129  /* Attempt to parse for "(IP)".  */
130  afteroffset = strstr (*strp, "(IP)");
131
132  if (afteroffset == NULL)
133    /* Make sure it's not in lower case.  */
134    afteroffset = strstr (*strp, "(ip)");
135
136  if (afteroffset != NULL)
137    {
138      if (afteroffset != *strp)
139	{
140	  /* Invalid offset present.  */
141	  errmsg = _("offset(IP) is not a valid form");
142	  return errmsg;
143	}
144      else
145	{
146	  *strp += 4;
147	  *valuep = 0;
148	  errmsg = NULL;
149	  return errmsg;
150	}
151    }
152
153  /* Attempt to parse for DP. ex: mov w, offset(DP)
154                                  mov offset(DP),w   */
155
156  /* Try parsing it as an address and see what comes back.  */
157  afteroffset = strstr (*strp, "(DP)");
158
159  if (afteroffset == NULL)
160    /* Maybe it's in lower case.  */
161    afteroffset = strstr (*strp, "(dp)");
162
163  if (afteroffset != NULL)
164    {
165      if (afteroffset == *strp)
166	{
167	  /* No offset present. Use 0 by default.  */
168	  tempvalue = 0;
169	  errmsg = NULL;
170	}
171      else
172	errmsg = cgen_parse_address (cd, strp, opindex,
173				     BFD_RELOC_IP2K_FR_OFFSET,
174				     & result_type, & tempvalue);
175
176      if (errmsg == NULL)
177	{
178	  if (tempvalue <= 127)
179	    {
180	      /* Value is ok.  Fix up the first 2 bits and return.  */
181	      *valuep = 0x0100 | tempvalue;
182	      *strp += 4; /* Skip over the (DP) in *strp.  */
183	      return errmsg;
184	    }
185	  else
186	    {
187	      /* Found something there in front of (DP) but it's out
188		 of range.  */
189	      errmsg = _("(DP) offset out of range.");
190	      return errmsg;
191	    }
192	}
193    }
194
195
196  /* Attempt to parse for SP. ex: mov w, offset(SP)
197                                  mov offset(SP), w.  */
198  afteroffset = strstr (*strp, "(SP)");
199
200  if (afteroffset == NULL)
201    /* Maybe it's in lower case.  */
202    afteroffset = strstr (*strp, "(sp)");
203
204  if (afteroffset != NULL)
205    {
206      if (afteroffset == *strp)
207	{
208	  /* No offset present. Use 0 by default.  */
209	  tempvalue = 0;
210	  errmsg = NULL;
211	}
212      else
213	errmsg = cgen_parse_address (cd, strp, opindex,
214				     BFD_RELOC_IP2K_FR_OFFSET,
215				     & result_type, & tempvalue);
216
217      if (errmsg == NULL)
218	{
219	  if (tempvalue <= 127)
220	    {
221	      /* Value is ok.  Fix up the first 2 bits and return.  */
222	      *valuep = 0x0180 | tempvalue;
223	      *strp += 4; /* Skip over the (SP) in *strp.  */
224	      return errmsg;
225	    }
226	  else
227	    {
228	      /* Found something there in front of (SP) but it's out
229		 of range.  */
230	      errmsg = _("(SP) offset out of range.");
231	      return errmsg;
232	    }
233	}
234    }
235
236  /* Attempt to parse as an address.  */
237  *strp = old_strp;
238  errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_IP2K_FR9,
239			       & result_type, & value);
240  if (errmsg == NULL)
241    {
242      *valuep = value;
243
244      /* If a parenthesis is found, warn about invalid form.  */
245      if (**strp == '(')
246	errmsg = _("illegal use of parentheses");
247
248      /* If a numeric value is specified, ensure that it is between
249	 1 and 255.  */
250      else if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
251	{
252	  if (value < 0x1 || value > 0xff)
253	    errmsg = _("operand out of range (not between 1 and 255)");
254	}
255    }
256  return errmsg;
257}
258
259static const char *
260parse_addr16 (CGEN_CPU_DESC cd,
261	      const char **strp,
262	      int opindex,
263	      unsigned long *valuep)
264{
265  const char *errmsg;
266  enum cgen_parse_operand_result result_type;
267  bfd_reloc_code_real_type code = BFD_RELOC_NONE;
268  bfd_vma value;
269
270  if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16H)
271    code = BFD_RELOC_IP2K_HI8DATA;
272  else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16L)
273    code = BFD_RELOC_IP2K_LO8DATA;
274  else
275    {
276      /* Something is very wrong. opindex has to be one of the above.  */
277      errmsg = _("parse_addr16: invalid opindex.");
278      return errmsg;
279    }
280
281  errmsg = cgen_parse_address (cd, strp, opindex, code,
282			       & result_type, & value);
283  if (errmsg == NULL)
284    {
285      /* We either have a relocation or a number now.  */
286      if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
287	{
288	  /* We got a number back.  */
289	  if (code == BFD_RELOC_IP2K_HI8DATA)
290            value >>= 8;
291	  else
292	    /* code = BFD_RELOC_IP2K_LOW8DATA.  */
293	    value &= 0x00FF;
294	}
295      *valuep = value;
296    }
297
298  return errmsg;
299}
300
301static const char *
302parse_addr16_cjp (CGEN_CPU_DESC cd,
303		  const char **strp,
304		  int opindex,
305		  unsigned long *valuep)
306{
307  const char *errmsg;
308  enum cgen_parse_operand_result result_type;
309  bfd_reloc_code_real_type code = BFD_RELOC_NONE;
310  bfd_vma value;
311
312  if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16CJP)
313    code = BFD_RELOC_IP2K_ADDR16CJP;
314  else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16P)
315    code = BFD_RELOC_IP2K_PAGE3;
316
317  errmsg = cgen_parse_address (cd, strp, opindex, code,
318			       & result_type, & value);
319  if (errmsg == NULL)
320    {
321      if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
322	{
323	  if ((value & 0x1) == 0)  /* If the address is even .... */
324	    {
325	      if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16CJP)
326                *valuep = (value >> 1) & 0x1FFF;  /* Should mask be 1FFF?  */
327	      else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16P)
328                *valuep = (value >> 14) & 0x7;
329	    }
330          else
331 	    errmsg = _("Byte address required. - must be even.");
332	}
333      else if (result_type == CGEN_PARSE_OPERAND_RESULT_QUEUED)
334	{
335	  /* This will happen for things like (s2-s1) where s2 and s1
336	     are labels.  */
337	  *valuep = value;
338	}
339      else
340        errmsg = _("cgen_parse_address returned a symbol. Literal required.");
341    }
342  return errmsg;
343}
344
345static const char *
346parse_lit8 (CGEN_CPU_DESC cd,
347	    const char **strp,
348	    int opindex,
349	    long *valuep)
350{
351  const char *errmsg;
352  enum cgen_parse_operand_result result_type;
353  bfd_reloc_code_real_type code = BFD_RELOC_NONE;
354  bfd_vma value;
355
356  /* Parse %OP relocating operators.  */
357  if (strncmp (*strp, "%bank", 5) == 0)
358    {
359      *strp += 5;
360      code = BFD_RELOC_IP2K_BANK;
361    }
362  else if (strncmp (*strp, "%lo8data", 8) == 0)
363    {
364      *strp += 8;
365      code = BFD_RELOC_IP2K_LO8DATA;
366    }
367  else if (strncmp (*strp, "%hi8data", 8) == 0)
368    {
369      *strp += 8;
370      code = BFD_RELOC_IP2K_HI8DATA;
371    }
372  else if (strncmp (*strp, "%ex8data", 8) == 0)
373    {
374      *strp += 8;
375      code = BFD_RELOC_IP2K_EX8DATA;
376    }
377  else if (strncmp (*strp, "%lo8insn", 8) == 0)
378    {
379      *strp += 8;
380      code = BFD_RELOC_IP2K_LO8INSN;
381    }
382  else if (strncmp (*strp, "%hi8insn", 8) == 0)
383    {
384      *strp += 8;
385      code = BFD_RELOC_IP2K_HI8INSN;
386    }
387
388  /* Parse %op operand.  */
389  if (code != BFD_RELOC_NONE)
390    {
391      errmsg = cgen_parse_address (cd, strp, opindex, code,
392				   & result_type, & value);
393      if ((errmsg == NULL) &&
394	  (result_type != CGEN_PARSE_OPERAND_RESULT_QUEUED))
395	errmsg = _("percent-operator operand is not a symbol");
396
397      *valuep = value;
398    }
399  /* Parse as a number.  */
400  else
401    {
402      errmsg = cgen_parse_signed_integer (cd, strp, opindex, valuep);
403
404      /* Truncate to eight bits to accept both signed and unsigned input.  */
405      if (errmsg == NULL)
406	*valuep &= 0xFF;
407    }
408
409  return errmsg;
410}
411
412static const char *
413parse_bit3 (CGEN_CPU_DESC cd,
414	    const char **strp,
415	    int opindex,
416	    unsigned long *valuep)
417{
418  const char *errmsg;
419  char mode = 0;
420  long count = 0;
421  unsigned long value;
422
423  if (strncmp (*strp, "%bit", 4) == 0)
424    {
425      *strp += 4;
426      mode = 1;
427    }
428  else if (strncmp (*strp, "%msbbit", 7) == 0)
429    {
430      *strp += 7;
431      mode = 1;
432    }
433  else if (strncmp (*strp, "%lsbbit", 7) == 0)
434    {
435      *strp += 7;
436      mode = 2;
437    }
438
439  errmsg = cgen_parse_unsigned_integer (cd, strp, opindex, valuep);
440  if (errmsg)
441    return errmsg;
442
443  if (mode)
444    {
445      value = * valuep;
446      if (value == 0)
447	{
448	  errmsg = _("Attempt to find bit index of 0");
449	  return errmsg;
450	}
451
452      if (mode == 1)
453	{
454	  count = 31;
455	  while ((value & 0x80000000) == 0)
456	    {
457	      count--;
458	      value <<= 1;
459	    }
460	}
461      else if (mode == 2)
462	{
463	  count = 0;
464	  while ((value & 0x00000001) == 0)
465	    {
466	      count++;
467	      value >>= 1;
468	    }
469	}
470
471      *valuep = count;
472    }
473
474  return errmsg;
475}
476
477/* -- dis.c */
478
479static void
480print_fr (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
481	  void * dis_info,
482	  long value,
483	  unsigned int attrs ATTRIBUTE_UNUSED,
484	  bfd_vma pc ATTRIBUTE_UNUSED,
485	  int length ATTRIBUTE_UNUSED)
486{
487  disassemble_info *info = (disassemble_info *) dis_info;
488  const CGEN_KEYWORD_ENTRY *ke;
489  extern CGEN_KEYWORD ip2k_cgen_opval_register_names;
490  long offsettest;
491  long offsetvalue;
492
493  if (value == 0) /* This is (IP).  */
494    {
495      (*info->fprintf_func) (info->stream, "%s", "(IP)");
496      return;
497    }
498
499  offsettest = value >> 7;
500  offsetvalue = value & 0x7F;
501
502  /* Check to see if first two bits are 10 -> (DP).  */
503  if (offsettest == 2)
504    {
505      if (offsetvalue == 0)
506	(*info->fprintf_func) (info->stream, "%s","(DP)");
507      else
508	(*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue, "(DP)");
509      return;
510    }
511
512  /* Check to see if first two bits are 11 -> (SP).  */
513  if (offsettest == 3)
514    {
515      if (offsetvalue == 0)
516	(*info->fprintf_func) (info->stream, "%s", "(SP)");
517      else
518	(*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue,"(SP)");
519      return;
520    }
521
522  /* Attempt to print as a register keyword.  */
523  ke = cgen_keyword_lookup_value (& ip2k_cgen_opval_register_names, value);
524
525  if (ke != NULL)
526    (*info->fprintf_func) (info->stream, "%s", ke->name);
527  else
528    /* Print as an address literal.  */
529    (*info->fprintf_func) (info->stream, "$%02lx", value);
530}
531
532static void
533print_dollarhex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
534		 void * dis_info,
535		 long value,
536		 unsigned int attrs ATTRIBUTE_UNUSED,
537		 bfd_vma pc ATTRIBUTE_UNUSED,
538		 int length ATTRIBUTE_UNUSED)
539{
540  disassemble_info *info = (disassemble_info *) dis_info;
541
542  (*info->fprintf_func) (info->stream, "$%lx", value);
543}
544
545static void
546print_dollarhex8 (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
547		  void * dis_info,
548		  long value,
549		  unsigned int attrs ATTRIBUTE_UNUSED,
550		  bfd_vma pc ATTRIBUTE_UNUSED,
551		  int length ATTRIBUTE_UNUSED)
552{
553  disassemble_info *info = (disassemble_info *) dis_info;
554
555  (*info->fprintf_func) (info->stream, "$%02lx", value);
556}
557
558static void
559print_dollarhex_addr16h (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
560			 void * dis_info,
561			 long value,
562			 unsigned int attrs ATTRIBUTE_UNUSED,
563			 bfd_vma pc ATTRIBUTE_UNUSED,
564			 int length ATTRIBUTE_UNUSED)
565{
566  disassemble_info *info = (disassemble_info *) dis_info;
567
568  /* This is a loadh instruction. Shift the value to the left
569     by 8 bits so that disassembled code will reassemble properly.  */
570  value = ((value << 8) & 0xFF00);
571
572  (*info->fprintf_func) (info->stream, "$%04lx", value);
573}
574
575static void
576print_dollarhex_addr16l (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
577			 void * dis_info,
578			 long value,
579			 unsigned int attrs ATTRIBUTE_UNUSED,
580			 bfd_vma pc ATTRIBUTE_UNUSED,
581			 int length ATTRIBUTE_UNUSED)
582{
583  disassemble_info *info = (disassemble_info *) dis_info;
584
585  (*info->fprintf_func) (info->stream, "$%04lx", value);
586}
587
588static void
589print_dollarhex_p (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
590		   void * dis_info,
591		   long value,
592		   unsigned int attrs ATTRIBUTE_UNUSED,
593		   bfd_vma pc ATTRIBUTE_UNUSED,
594		   int length ATTRIBUTE_UNUSED)
595{
596  disassemble_info *info = (disassemble_info *) dis_info;
597
598  value = ((value << 14) & 0x1C000);
599  ;value = (value  & 0x1FFFF);
600  (*info->fprintf_func) (info->stream, "$%05lx", value);
601}
602
603static void
604print_dollarhex_cj (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
605		    void * dis_info,
606		    long value,
607		    unsigned int attrs ATTRIBUTE_UNUSED,
608		    bfd_vma pc ATTRIBUTE_UNUSED,
609		    int length ATTRIBUTE_UNUSED)
610{
611  disassemble_info *info = (disassemble_info *) dis_info;
612
613  value = ((value << 1) & 0x1FFFF);
614  (*info->fprintf_func) (info->stream, "$%05lx", value);
615}
616
617static void
618print_decimal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
619	       void * dis_info,
620	       long value,
621	       unsigned int attrs ATTRIBUTE_UNUSED,
622	       bfd_vma pc ATTRIBUTE_UNUSED,
623	       int length ATTRIBUTE_UNUSED)
624{
625  disassemble_info *info = (disassemble_info *) dis_info;
626
627  (*info->fprintf_func) (info->stream, "%ld", value);
628}
629
630
631
632/* -- */
633
634