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