opcodes/
[binutils.git] / cpu / m32r.opc
blob30e0956eac4d7206f8da931a9224bc843c3f36da
1 /* M32R opcode support.  -*- C -*-
3    Copyright 1998, 1999, 2000, 2001, 2004, 2005, 2007, 2009
4    Free Software Foundation, Inc.
6    Contributed by Red Hat Inc; developed under contract from
7    Mitsubishi Electric Corporation.
9    This file is part of the GNU Binutils.
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.
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.
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.  */
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.
34    Each section is delimited with start and end markers.
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"  */
42 /* -- opc.h */
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)
57 extern unsigned int m32r_cgen_dis_hash (const char *, CGEN_INSN_INT);
58 #endif
60 /* -- */
62 /* -- opc.c */
63 unsigned int
64 m32r_cgen_dis_hash (const char * buf ATTRIBUTE_UNUSED, CGEN_INSN_INT value)
66   unsigned int x;
68   if (value & 0xffff0000) /* 32bit instructions.  */
69     value = (value >> 16) & 0xffff;
71   x = (value >> 8) & 0xf0;
72   if (x == 0x40 || x == 0xe0 || x == 0x60 || x == 0x50)
73     return x;
75   if (x == 0x70 || x == 0xf0)
76     return x | ((value >> 8) & 0x0f);
78   if (x == 0x30)
79     return x | ((value & 0x70) >> 4);
80   else
81     return x | ((value & 0xf0) >> 4);
84 /* -- */
86 /* -- asm.c */
87 static const char * MISSING_CLOSING_PARENTHESIS = N_("missing `)'");
89 /* Handle '#' prefixes (i.e. skip over them).  */
91 static const char *
92 parse_hash (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
93             const char **strp,
94             int opindex ATTRIBUTE_UNUSED,
95             long *valuep ATTRIBUTE_UNUSED)
97   if (**strp == '#')
98     ++*strp;
99   return NULL;
102 /* Handle shigh(), high().  */
104 static const char *
105 parse_hi16 (CGEN_CPU_DESC cd,
106             const char **strp,
107             int opindex,
108             unsigned long *valuep)
110   const char *errmsg;
111   enum cgen_parse_operand_result result_type;
112   bfd_vma value;
114   if (**strp == '#')
115     ++*strp;
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     }
153   return cgen_parse_unsigned_integer (cd, strp, opindex, valuep);
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.  */
160 static const char *
161 parse_slo16 (CGEN_CPU_DESC cd,
162              const char ** strp,
163              int opindex,
164              long * valuep)
166   const char *errmsg;
167   enum cgen_parse_operand_result result_type;
168   bfd_vma value;
170   if (**strp == '#')
171     ++*strp;
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     }
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     }
200   return cgen_parse_signed_integer (cd, strp, opindex, valuep);
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.  */
207 static const char *
208 parse_ulo16 (CGEN_CPU_DESC cd,
209              const char **strp,
210              int opindex,
211              unsigned long *valuep)
213   const char *errmsg;
214   enum cgen_parse_operand_result result_type;
215   bfd_vma value;
217   if (**strp == '#')
218     ++*strp;
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     }
235   return cgen_parse_unsigned_integer (cd, strp, opindex, valuep);
238 /* -- */
240 /* -- dis.c */
242 /* Print signed operands with '#' prefixes.  */
244 static void
245 print_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)
252   disassemble_info *info = (disassemble_info *) dis_info;
254   (*info->fprintf_func) (info->stream, "#");
255   (*info->fprintf_func) (info->stream, "%ld", value);
258 /* Print unsigned operands with '#' prefixes.  */
260 static void
261 print_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)
268   disassemble_info *info = (disassemble_info *) dis_info;
270   (*info->fprintf_func) (info->stream, "#");
271   (*info->fprintf_func) (info->stream, "0x%lx", value);
274 /* Handle '#' prefixes as operands.  */
276 static void
277 print_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)
284   disassemble_info *info = (disassemble_info *) dis_info;
286   (*info->fprintf_func) (info->stream, "#");
289 #undef  CGEN_PRINT_INSN
290 #define CGEN_PRINT_INSN my_print_insn
292 static int
293 my_print_insn (CGEN_CPU_DESC cd,
294                bfd_vma pc,
295                disassemble_info *info)
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;
304   /* Read the base part of the insn.  */
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     }
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);
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     }
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, " -> ");
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);
344   return (pc & 3) ? 2 : 4;
347 /* -- */