1/* M32R opcode support.  -*- C -*-
2
3   Copyright 1998, 1999, 2000, 2001, 2004, 2005, 2007, 2009
4   Free Software Foundation, Inc.
5
6   Contributed by Red Hat Inc; developed under contract from
7   Mitsubishi Electric Corporation.
8
9   This file is part of the GNU Binutils.
10
11   This program is free software; you can redistribute it and/or modify
12   it under the terms of the GNU General Public License as published by
13   the Free Software Foundation; either version 3 of the License, or
14   (at your option) any later version.
15
16   This program is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   GNU General Public License for more details.
20
21   You should have received a copy of the GNU General Public License
22   along with this program; if not, write to the Free Software
23   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
24   MA 02110-1301, USA.  */
25
26
27/* This file is an addendum to m32r.cpu.  Heavy use of C code isn't
28   appropriate in .cpu files, so it resides here.  This especially applies
29   to assembly/disassembly where parsing/printing can be quite involved.
30   Such things aren't really part of the specification of the cpu, per se,
31   so .cpu files provide the general framework and .opc files handle the
32   nitty-gritty details as necessary.
33
34   Each section is delimited with start and end markers.
35
36   <arch>-opc.h additions use: "-- opc.h"
37   <arch>-opc.c additions use: "-- opc.c"
38   <arch>-asm.c additions use: "-- asm.c"
39   <arch>-dis.c additions use: "-- dis.c"
40   <arch>-ibd.h additions use: "-- ibd.h"  */
41
42/* -- opc.h */
43
44#undef  CGEN_DIS_HASH_SIZE
45#define CGEN_DIS_HASH_SIZE 256
46#undef  CGEN_DIS_HASH
47#if 0
48#define X(b) (((unsigned char *) (b))[0] & 0xf0)
49#define CGEN_DIS_HASH(buffer, value) \
50(X (buffer) | \
51 (X (buffer) == 0x40 || X (buffer) == 0xe0 || X (buffer) == 0x60 || X (buffer) == 0x50 ? 0 \
52  : X (buffer) == 0x70 || X (buffer) == 0xf0 ? (((unsigned char *) (buffer))[0] & 0xf) \
53  : X (buffer) == 0x30 ? ((((unsigned char *) (buffer))[1] & 0x70) >> 4) \
54  : ((((unsigned char *) (buffer))[1] & 0xf0) >> 4)))
55#else
56#define CGEN_DIS_HASH(buffer, value) m32r_cgen_dis_hash (buffer, value)
57extern unsigned int m32r_cgen_dis_hash (const char *, CGEN_INSN_INT);
58#endif
59
60/* -- */
61
62/* -- opc.c */
63unsigned int
64m32r_cgen_dis_hash (const char * buf ATTRIBUTE_UNUSED, CGEN_INSN_INT value)
65{
66  unsigned int x;
67
68  if (value & 0xffff0000) /* 32bit instructions.  */
69    value = (value >> 16) & 0xffff;
70
71  x = (value >> 8) & 0xf0;
72  if (x == 0x40 || x == 0xe0 || x == 0x60 || x == 0x50)
73    return x;
74
75  if (x == 0x70 || x == 0xf0)
76    return x | ((value >> 8) & 0x0f);
77
78  if (x == 0x30)
79    return x | ((value & 0x70) >> 4);
80  else
81    return x | ((value & 0xf0) >> 4);
82}
83
84/* -- */
85
86/* -- asm.c */
87static const char * MISSING_CLOSING_PARENTHESIS = N_("missing `)'");
88
89/* Handle '#' prefixes (i.e. skip over them).  */
90
91static const char *
92parse_hash (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
93	    const char **strp,
94	    int opindex ATTRIBUTE_UNUSED,
95	    long *valuep ATTRIBUTE_UNUSED)
96{
97  if (**strp == '#')
98    ++*strp;
99  return NULL;
100}
101
102/* Handle shigh(), high().  */
103
104static const char *
105parse_hi16 (CGEN_CPU_DESC cd,
106	    const char **strp,
107	    int opindex,
108	    unsigned long *valuep)
109{
110  const char *errmsg;
111  enum cgen_parse_operand_result result_type;
112  bfd_vma value;
113
114  if (**strp == '#')
115    ++*strp;
116
117  if (strncasecmp (*strp, "high(", 5) == 0)
118    {
119      *strp += 5;
120      errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_M32R_HI16_ULO,
121				   & result_type, & value);
122      if (**strp != ')')
123	return MISSING_CLOSING_PARENTHESIS;
124      ++*strp;
125      if (errmsg == NULL
126	  && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
127	{
128	  value >>= 16;
129	  value &= 0xffff;
130	}
131      *valuep = value;
132      return errmsg;
133    }
134  else if (strncasecmp (*strp, "shigh(", 6) == 0)
135    {
136      *strp += 6;
137      errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_M32R_HI16_SLO,
138				   & result_type, & value);
139      if (**strp != ')')
140	return MISSING_CLOSING_PARENTHESIS;
141      ++*strp;
142      if (errmsg == NULL
143	  && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
144	{
145	  value += 0x8000;
146	  value >>= 16;
147	  value &= 0xffff;
148	}
149      *valuep = value;
150      return errmsg;
151    }
152
153  return cgen_parse_unsigned_integer (cd, strp, opindex, valuep);
154}
155
156/* Handle low() in a signed context.  Also handle sda().
157   The signedness of the value doesn't matter to low(), but this also
158   handles the case where low() isn't present.  */
159
160static const char *
161parse_slo16 (CGEN_CPU_DESC cd,
162	     const char ** strp,
163	     int opindex,
164	     long * valuep)
165{
166  const char *errmsg;
167  enum cgen_parse_operand_result result_type;
168  bfd_vma value;
169
170  if (**strp == '#')
171    ++*strp;
172
173  if (strncasecmp (*strp, "low(", 4) == 0)
174    {
175      *strp += 4;
176      errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_M32R_LO16,
177				   & result_type, & value);
178      if (**strp != ')')
179	return MISSING_CLOSING_PARENTHESIS;
180      ++*strp;
181      if (errmsg == NULL
182	  && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
183	value = ((value & 0xffff) ^ 0x8000) - 0x8000;
184      *valuep = value;
185      return errmsg;
186    }
187
188  if (strncasecmp (*strp, "sda(", 4) == 0)
189    {
190      *strp += 4;
191      errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_M32R_SDA16,
192				   NULL, & value);
193      if (**strp != ')')
194	return MISSING_CLOSING_PARENTHESIS;
195      ++*strp;
196      *valuep = value;
197      return errmsg;
198    }
199
200  return cgen_parse_signed_integer (cd, strp, opindex, valuep);
201}
202
203/* Handle low() in an unsigned context.
204   The signedness of the value doesn't matter to low(), but this also
205   handles the case where low() isn't present.  */
206
207static const char *
208parse_ulo16 (CGEN_CPU_DESC cd,
209	     const char **strp,
210	     int opindex,
211	     unsigned long *valuep)
212{
213  const char *errmsg;
214  enum cgen_parse_operand_result result_type;
215  bfd_vma value;
216
217  if (**strp == '#')
218    ++*strp;
219
220  if (strncasecmp (*strp, "low(", 4) == 0)
221    {
222      *strp += 4;
223      errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_M32R_LO16,
224				   & result_type, & value);
225      if (**strp != ')')
226	return MISSING_CLOSING_PARENTHESIS;
227      ++*strp;
228      if (errmsg == NULL
229	  && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
230	value &= 0xffff;
231      *valuep = value;
232      return errmsg;
233    }
234
235  return cgen_parse_unsigned_integer (cd, strp, opindex, valuep);
236}
237
238/* -- */
239
240/* -- dis.c */
241
242/* Print signed operands with '#' prefixes.  */
243
244static void
245print_signed_with_hash_prefix (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
246			       void * dis_info,
247			       long value,
248			       unsigned int attrs ATTRIBUTE_UNUSED,
249			       bfd_vma pc ATTRIBUTE_UNUSED,
250			       int length ATTRIBUTE_UNUSED)
251{
252  disassemble_info *info = (disassemble_info *) dis_info;
253
254  (*info->fprintf_func) (info->stream, "#");
255  (*info->fprintf_func) (info->stream, "%ld", value);
256}
257
258/* Print unsigned operands with '#' prefixes.  */
259
260static void
261print_unsigned_with_hash_prefix (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
262				 void * dis_info,
263				 long value,
264				 unsigned int attrs ATTRIBUTE_UNUSED,
265				 bfd_vma pc ATTRIBUTE_UNUSED,
266				 int length ATTRIBUTE_UNUSED)
267{
268  disassemble_info *info = (disassemble_info *) dis_info;
269
270  (*info->fprintf_func) (info->stream, "#");
271  (*info->fprintf_func) (info->stream, "0x%lx", value);
272}
273
274/* Handle '#' prefixes as operands.  */
275
276static void
277print_hash (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
278	    void * dis_info,
279	    long value ATTRIBUTE_UNUSED,
280	    unsigned int attrs ATTRIBUTE_UNUSED,
281	    bfd_vma pc ATTRIBUTE_UNUSED,
282	    int length ATTRIBUTE_UNUSED)
283{
284  disassemble_info *info = (disassemble_info *) dis_info;
285
286  (*info->fprintf_func) (info->stream, "#");
287}
288
289#undef  CGEN_PRINT_INSN
290#define CGEN_PRINT_INSN my_print_insn
291
292static int
293my_print_insn (CGEN_CPU_DESC cd,
294	       bfd_vma pc,
295	       disassemble_info *info)
296{
297  bfd_byte buffer[CGEN_MAX_INSN_SIZE];
298  bfd_byte *buf = buffer;
299  int status;
300  int buflen = (pc & 3) == 0 ? 4 : 2;
301  int big_p = CGEN_CPU_INSN_ENDIAN (cd) == CGEN_ENDIAN_BIG;
302  bfd_byte *x;
303
304  /* Read the base part of the insn.  */
305
306  status = (*info->read_memory_func) (pc - ((!big_p && (pc & 3) != 0) ? 2 : 0),
307				      buf, buflen, info);
308  if (status != 0)
309    {
310      (*info->memory_error_func) (status, pc, info);
311      return -1;
312    }
313
314  /* 32 bit insn?  */
315  x = (big_p ? &buf[0] : &buf[3]);
316  if ((pc & 3) == 0 && (*x & 0x80) != 0)
317    return print_insn (cd, pc, info, buf, buflen);
318
319  /* Print the first insn.  */
320  if ((pc & 3) == 0)
321    {
322      buf += (big_p ? 0 : 2);
323      if (print_insn (cd, pc, info, buf, 2) == 0)
324	(*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
325      buf += (big_p ? 2 : -2);
326    }
327
328  x = (big_p ? &buf[0] : &buf[1]);
329  if (*x & 0x80)
330    {
331      /* Parallel.  */
332      (*info->fprintf_func) (info->stream, " || ");
333      *x &= 0x7f;
334    }
335  else
336    (*info->fprintf_func) (info->stream, " -> ");
337
338  /* The "& 3" is to pass a consistent address.
339     Parallel insns arguably both begin on the word boundary.
340     Also, branch insns are calculated relative to the word boundary.  */
341  if (print_insn (cd, pc & ~ (bfd_vma) 3, info, buf, 2) == 0)
342    (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
343
344  return (pc & 3) ? 2 : 4;
345}
346
347/* -- */
348