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