1/* FR30 opcode support.  -*- C -*-
2   Copyright 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/* This file is an addendum to fr30.cpu.  Heavy use of C code isn't
24   appropriate in .cpu files, so it resides here.  This especially applies
25   to assembly/disassembly where parsing/printing can be quite involved.
26   Such things aren't really part of the specification of the cpu, per se,
27   so .cpu files provide the general framework and .opc files handle the
28   nitty-gritty details as necessary.
29
30   Each section is delimited with start and end markers.
31
32   <arch>-opc.h additions use: "-- opc.h"
33   <arch>-opc.c additions use: "-- opc.c"
34   <arch>-asm.c additions use: "-- asm.c"
35   <arch>-dis.c additions use: "-- dis.c"
36   <arch>-ibd.h additions use: "-- ibd.h".  */
37
38/* -- opc.h */
39
40/* ??? This can be improved upon.  */
41#undef  CGEN_DIS_HASH_SIZE
42#define CGEN_DIS_HASH_SIZE 16
43#undef  CGEN_DIS_HASH
44#define CGEN_DIS_HASH(buffer, value) (((unsigned char *) (buffer))[0] >> 4)
45
46/* -- */
47
48/* -- asm.c */
49/* Handle register lists for LDMx and STMx.  */
50
51static int
52parse_register_number (const char **strp)
53{
54  int regno;
55
56  if (**strp < '0' || **strp > '9')
57    return -1; /* Error.  */
58  regno = **strp - '0';
59  ++*strp;
60
61  if (**strp >= '0' && **strp <= '9')
62    {
63      regno = regno * 10 + (**strp - '0');
64      ++*strp;
65    }
66
67  return regno;
68}
69
70static const char *
71parse_register_list (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
72		     const char **strp,
73		     int opindex ATTRIBUTE_UNUSED,
74		     unsigned long *valuep,
75		     int high_low,   /* 0 == high, 1 == low.  */
76		     int load_store) /* 0 == load, 1 == store.  */
77{
78  *valuep = 0;
79  while (**strp && **strp != ')')
80    {
81      int regno;
82
83      if (**strp != 'R' && **strp != 'r')
84	break;
85      ++*strp;
86
87      regno = parse_register_number (strp);
88      if (regno == -1)
89	return _("Register number is not valid");
90      if (regno > 7 && !high_low)
91	return _("Register must be between r0 and r7");
92      if (regno < 8 && high_low)
93	return _("Register must be between r8 and r15");
94
95      if (high_low)
96	regno -= 8;
97
98      if (load_store) /* Mask is reversed for store.  */
99	*valuep |= 0x80 >> regno;
100      else
101	*valuep |= 1 << regno;
102
103      if (**strp == ',')
104	{
105	  if (*(*strp + 1) == ')')
106	    break;
107	  ++*strp;
108	}
109    }
110
111  if (!*strp || **strp != ')')
112    return _("Register list is not valid");
113
114  return NULL;
115}
116
117static const char *
118parse_low_register_list_ld (CGEN_CPU_DESC cd,
119			    const char **strp,
120			    int opindex,
121			    unsigned long *valuep)
122{
123  return parse_register_list (cd, strp, opindex, valuep,
124			      0 /* Low.  */, 0 /* Load.  */);
125}
126
127static const char *
128parse_hi_register_list_ld (CGEN_CPU_DESC cd,
129			   const char **strp,
130			   int opindex,
131			   unsigned long *valuep)
132{
133  return parse_register_list (cd, strp, opindex, valuep,
134			      1 /* High.  */, 0 /* Load.  */);
135}
136
137static const char *
138parse_low_register_list_st (CGEN_CPU_DESC cd,
139			    const char **strp,
140			    int opindex,
141			    unsigned long *valuep)
142{
143  return parse_register_list (cd, strp, opindex, valuep,
144			      0 /* Low.  */, 1 /* Store.  */);
145}
146
147static const char *
148parse_hi_register_list_st (CGEN_CPU_DESC cd,
149			   const char **strp,
150			   int opindex,
151			   unsigned long *valuep)
152{
153  return parse_register_list (cd, strp, opindex, valuep,
154			      1 /* High.  */, 1 /* Store.  */);
155}
156
157/* -- */
158
159/* -- dis.c */
160static void
161print_register_list (void * dis_info,
162		     long value,
163		     long offset,
164		     int load_store) /* 0 == load, 1 == store.  */
165{
166  disassemble_info *info = dis_info;
167  int mask;
168  int reg_index = 0;
169  char * comma = "";
170
171  if (load_store)
172    mask = 0x80;
173  else
174    mask = 1;
175
176  if (value & mask)
177    {
178      (*info->fprintf_func) (info->stream, "r%li", reg_index + offset);
179      comma = ",";
180    }
181
182  for (reg_index = 1; reg_index <= 7; ++reg_index)
183    {
184      if (load_store)
185	mask >>= 1;
186      else
187	mask <<= 1;
188
189      if (value & mask)
190	{
191	  (*info->fprintf_func) (info->stream, "%sr%li", comma, reg_index + offset);
192	  comma = ",";
193	}
194    }
195}
196
197static void
198print_hi_register_list_ld (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
199			   void * dis_info,
200			   long value,
201			   unsigned int attrs ATTRIBUTE_UNUSED,
202			   bfd_vma pc ATTRIBUTE_UNUSED,
203			   int length ATTRIBUTE_UNUSED)
204{
205  print_register_list (dis_info, value, 8, 0 /* Load.  */);
206}
207
208static void
209print_low_register_list_ld (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
210			    void * dis_info,
211			    long value,
212			    unsigned int attrs ATTRIBUTE_UNUSED,
213			    bfd_vma pc ATTRIBUTE_UNUSED,
214			    int length ATTRIBUTE_UNUSED)
215{
216  print_register_list (dis_info, value, 0, 0 /* Load.  */);
217}
218
219static void
220print_hi_register_list_st (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
221			   void * dis_info,
222			   long value,
223			   unsigned int attrs ATTRIBUTE_UNUSED,
224			   bfd_vma pc ATTRIBUTE_UNUSED,
225			   int length ATTRIBUTE_UNUSED)
226{
227  print_register_list (dis_info, value, 8, 1 /* Store.  */);
228}
229
230static void
231print_low_register_list_st (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
232			    void * dis_info,
233			    long value,
234			    unsigned int attrs ATTRIBUTE_UNUSED,
235			    bfd_vma pc ATTRIBUTE_UNUSED,
236			    int length ATTRIBUTE_UNUSED)
237{
238  print_register_list (dis_info, value, 0, 1 /* Store.  */);
239}
240
241static void
242print_m4 (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
243	  void * dis_info,
244	  long value,
245	  unsigned int attrs ATTRIBUTE_UNUSED,
246	  bfd_vma pc ATTRIBUTE_UNUSED,
247	  int length ATTRIBUTE_UNUSED)
248{
249  disassemble_info *info = (disassemble_info *) dis_info;
250
251  (*info->fprintf_func) (info->stream, "%ld", value);
252}
253/* -- */
254