1*3d8817e4Smiod /* Disassemble V850 instructions.
2*3d8817e4Smiod Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2005
3*3d8817e4Smiod Free Software Foundation, Inc.
4*3d8817e4Smiod
5*3d8817e4Smiod This program is free software; you can redistribute it and/or modify
6*3d8817e4Smiod it under the terms of the GNU General Public License as published by
7*3d8817e4Smiod the Free Software Foundation; either version 2 of the License, or
8*3d8817e4Smiod (at your option) any later version.
9*3d8817e4Smiod
10*3d8817e4Smiod This program is distributed in the hope that it will be useful,
11*3d8817e4Smiod but WITHOUT ANY WARRANTY; without even the implied warranty of
12*3d8817e4Smiod MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13*3d8817e4Smiod GNU General Public License for more details.
14*3d8817e4Smiod
15*3d8817e4Smiod You should have received a copy of the GNU General Public License
16*3d8817e4Smiod along with this program; if not, write to the Free Software
17*3d8817e4Smiod Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
18*3d8817e4Smiod MA 02110-1301, USA. */
19*3d8817e4Smiod
20*3d8817e4Smiod
21*3d8817e4Smiod #include <stdio.h>
22*3d8817e4Smiod
23*3d8817e4Smiod #include "sysdep.h"
24*3d8817e4Smiod #include "opcode/v850.h"
25*3d8817e4Smiod #include "dis-asm.h"
26*3d8817e4Smiod #include "opintl.h"
27*3d8817e4Smiod
28*3d8817e4Smiod static const char *const v850_reg_names[] =
29*3d8817e4Smiod { "r0", "r1", "r2", "sp", "gp", "r5", "r6", "r7",
30*3d8817e4Smiod "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
31*3d8817e4Smiod "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
32*3d8817e4Smiod "r24", "r25", "r26", "r27", "r28", "r29", "ep", "lp" };
33*3d8817e4Smiod
34*3d8817e4Smiod static const char *const v850_sreg_names[] =
35*3d8817e4Smiod { "eipc", "eipsw", "fepc", "fepsw", "ecr", "psw", "sr6", "sr7",
36*3d8817e4Smiod "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15",
37*3d8817e4Smiod "ctpc", "ctpsw", "dbpc", "dbpsw", "ctbp", "sr21", "sr22", "sr23",
38*3d8817e4Smiod "sr24", "sr25", "sr26", "sr27", "sr28", "sr29", "sr30", "sr31",
39*3d8817e4Smiod "sr16", "sr17", "sr18", "sr19", "sr20", "sr21", "sr22", "sr23",
40*3d8817e4Smiod "sr24", "sr25", "sr26", "sr27", "sr28", "sr29", "sr30", "sr31" };
41*3d8817e4Smiod
42*3d8817e4Smiod static const char *const v850_cc_names[] =
43*3d8817e4Smiod { "v", "c/l", "z", "nh", "s/n", "t", "lt", "le",
44*3d8817e4Smiod "nv", "nc/nl", "nz", "h", "ns/p", "sa", "ge", "gt" };
45*3d8817e4Smiod
46*3d8817e4Smiod static int
disassemble(bfd_vma memaddr,struct disassemble_info * info,unsigned long insn)47*3d8817e4Smiod disassemble (bfd_vma memaddr,
48*3d8817e4Smiod struct disassemble_info * info,
49*3d8817e4Smiod unsigned long insn)
50*3d8817e4Smiod {
51*3d8817e4Smiod struct v850_opcode * op = (struct v850_opcode *) v850_opcodes;
52*3d8817e4Smiod const struct v850_operand * operand;
53*3d8817e4Smiod int match = 0;
54*3d8817e4Smiod int short_op = ((insn & 0x0600) != 0x0600);
55*3d8817e4Smiod int bytes_read;
56*3d8817e4Smiod int target_processor;
57*3d8817e4Smiod
58*3d8817e4Smiod /* Special case: 32 bit MOV. */
59*3d8817e4Smiod if ((insn & 0xffe0) == 0x0620)
60*3d8817e4Smiod short_op = 1;
61*3d8817e4Smiod
62*3d8817e4Smiod bytes_read = short_op ? 2 : 4;
63*3d8817e4Smiod
64*3d8817e4Smiod /* If this is a two byte insn, then mask off the high bits. */
65*3d8817e4Smiod if (short_op)
66*3d8817e4Smiod insn &= 0xffff;
67*3d8817e4Smiod
68*3d8817e4Smiod switch (info->mach)
69*3d8817e4Smiod {
70*3d8817e4Smiod case 0:
71*3d8817e4Smiod default:
72*3d8817e4Smiod target_processor = PROCESSOR_V850;
73*3d8817e4Smiod break;
74*3d8817e4Smiod
75*3d8817e4Smiod case bfd_mach_v850e:
76*3d8817e4Smiod target_processor = PROCESSOR_V850E;
77*3d8817e4Smiod break;
78*3d8817e4Smiod
79*3d8817e4Smiod case bfd_mach_v850e1:
80*3d8817e4Smiod target_processor = PROCESSOR_V850E1;
81*3d8817e4Smiod break;
82*3d8817e4Smiod }
83*3d8817e4Smiod
84*3d8817e4Smiod /* Find the opcode. */
85*3d8817e4Smiod while (op->name)
86*3d8817e4Smiod {
87*3d8817e4Smiod if ((op->mask & insn) == op->opcode
88*3d8817e4Smiod && (op->processors & target_processor))
89*3d8817e4Smiod {
90*3d8817e4Smiod const unsigned char *opindex_ptr;
91*3d8817e4Smiod unsigned int opnum;
92*3d8817e4Smiod unsigned int memop;
93*3d8817e4Smiod
94*3d8817e4Smiod match = 1;
95*3d8817e4Smiod (*info->fprintf_func) (info->stream, "%s\t", op->name);
96*3d8817e4Smiod
97*3d8817e4Smiod memop = op->memop;
98*3d8817e4Smiod /* Now print the operands.
99*3d8817e4Smiod
100*3d8817e4Smiod MEMOP is the operand number at which a memory
101*3d8817e4Smiod address specification starts, or zero if this
102*3d8817e4Smiod instruction has no memory addresses.
103*3d8817e4Smiod
104*3d8817e4Smiod A memory address is always two arguments.
105*3d8817e4Smiod
106*3d8817e4Smiod This information allows us to determine when to
107*3d8817e4Smiod insert commas into the output stream as well as
108*3d8817e4Smiod when to insert disp[reg] expressions onto the
109*3d8817e4Smiod output stream. */
110*3d8817e4Smiod
111*3d8817e4Smiod for (opindex_ptr = op->operands, opnum = 1;
112*3d8817e4Smiod *opindex_ptr != 0;
113*3d8817e4Smiod opindex_ptr++, opnum++)
114*3d8817e4Smiod {
115*3d8817e4Smiod long value;
116*3d8817e4Smiod int flag;
117*3d8817e4Smiod int status;
118*3d8817e4Smiod bfd_byte buffer[4];
119*3d8817e4Smiod
120*3d8817e4Smiod operand = &v850_operands[*opindex_ptr];
121*3d8817e4Smiod
122*3d8817e4Smiod if (operand->extract)
123*3d8817e4Smiod value = (operand->extract) (insn, 0);
124*3d8817e4Smiod else
125*3d8817e4Smiod {
126*3d8817e4Smiod if (operand->bits == -1)
127*3d8817e4Smiod value = (insn & operand->shift);
128*3d8817e4Smiod else
129*3d8817e4Smiod value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
130*3d8817e4Smiod
131*3d8817e4Smiod if (operand->flags & V850_OPERAND_SIGNED)
132*3d8817e4Smiod value = ((long)(value << (32 - operand->bits))
133*3d8817e4Smiod >> (32 - operand->bits));
134*3d8817e4Smiod }
135*3d8817e4Smiod
136*3d8817e4Smiod /* The first operand is always output without any
137*3d8817e4Smiod special handling.
138*3d8817e4Smiod
139*3d8817e4Smiod For the following arguments:
140*3d8817e4Smiod
141*3d8817e4Smiod If memop && opnum == memop + 1, then we need '[' since
142*3d8817e4Smiod we're about to output the register used in a memory
143*3d8817e4Smiod reference.
144*3d8817e4Smiod
145*3d8817e4Smiod If memop && opnum == memop + 2, then we need ']' since
146*3d8817e4Smiod we just finished the register in a memory reference. We
147*3d8817e4Smiod also need a ',' before this operand.
148*3d8817e4Smiod
149*3d8817e4Smiod Else we just need a comma.
150*3d8817e4Smiod
151*3d8817e4Smiod We may need to output a trailing ']' if the last operand
152*3d8817e4Smiod in an instruction is the register for a memory address.
153*3d8817e4Smiod
154*3d8817e4Smiod The exception (and there's always an exception) is the
155*3d8817e4Smiod "jmp" insn which needs square brackets around it's only
156*3d8817e4Smiod register argument. */
157*3d8817e4Smiod
158*3d8817e4Smiod if (memop && opnum == memop + 1)
159*3d8817e4Smiod info->fprintf_func (info->stream, "[");
160*3d8817e4Smiod else if (memop && opnum == memop + 2)
161*3d8817e4Smiod info->fprintf_func (info->stream, "],");
162*3d8817e4Smiod else if (memop == 1 && opnum == 1
163*3d8817e4Smiod && (operand->flags & V850_OPERAND_REG))
164*3d8817e4Smiod info->fprintf_func (info->stream, "[");
165*3d8817e4Smiod else if (opnum > 1)
166*3d8817e4Smiod info->fprintf_func (info->stream, ", ");
167*3d8817e4Smiod
168*3d8817e4Smiod /* Extract the flags, ignorng ones which
169*3d8817e4Smiod do not effect disassembly output. */
170*3d8817e4Smiod flag = operand->flags;
171*3d8817e4Smiod flag &= ~ V850_OPERAND_SIGNED;
172*3d8817e4Smiod flag &= ~ V850_OPERAND_RELAX;
173*3d8817e4Smiod flag &= - flag;
174*3d8817e4Smiod
175*3d8817e4Smiod switch (flag)
176*3d8817e4Smiod {
177*3d8817e4Smiod case V850_OPERAND_REG:
178*3d8817e4Smiod info->fprintf_func (info->stream, "%s", v850_reg_names[value]);
179*3d8817e4Smiod break;
180*3d8817e4Smiod case V850_OPERAND_SRG:
181*3d8817e4Smiod info->fprintf_func (info->stream, "%s", v850_sreg_names[value]);
182*3d8817e4Smiod break;
183*3d8817e4Smiod case V850_OPERAND_CC:
184*3d8817e4Smiod info->fprintf_func (info->stream, "%s", v850_cc_names[value]);
185*3d8817e4Smiod break;
186*3d8817e4Smiod case V850_OPERAND_EP:
187*3d8817e4Smiod info->fprintf_func (info->stream, "ep");
188*3d8817e4Smiod break;
189*3d8817e4Smiod default:
190*3d8817e4Smiod info->fprintf_func (info->stream, "%ld", value);
191*3d8817e4Smiod break;
192*3d8817e4Smiod case V850_OPERAND_DISP:
193*3d8817e4Smiod {
194*3d8817e4Smiod bfd_vma addr = value + memaddr;
195*3d8817e4Smiod
196*3d8817e4Smiod /* On the v850 the top 8 bits of an address are used by an
197*3d8817e4Smiod overlay manager. Thus it may happen that when we are
198*3d8817e4Smiod looking for a symbol to match against an address with
199*3d8817e4Smiod some of its top bits set, the search fails to turn up an
200*3d8817e4Smiod exact match. In this case we try to find an exact match
201*3d8817e4Smiod against a symbol in the lower address space, and if we
202*3d8817e4Smiod find one, we use that address. We only do this for
203*3d8817e4Smiod JARL instructions however, as we do not want to
204*3d8817e4Smiod misinterpret branch instructions. */
205*3d8817e4Smiod if (operand->bits == 22)
206*3d8817e4Smiod {
207*3d8817e4Smiod if ( ! info->symbol_at_address_func (addr, info)
208*3d8817e4Smiod && ((addr & 0xFF000000) != 0)
209*3d8817e4Smiod && info->symbol_at_address_func (addr & 0x00FFFFFF, info))
210*3d8817e4Smiod addr &= 0x00FFFFFF;
211*3d8817e4Smiod }
212*3d8817e4Smiod info->print_address_func (addr, info);
213*3d8817e4Smiod break;
214*3d8817e4Smiod }
215*3d8817e4Smiod
216*3d8817e4Smiod case V850E_PUSH_POP:
217*3d8817e4Smiod {
218*3d8817e4Smiod static int list12_regs[32] = { 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 29, 28, 23, 22, 21, 20, 27, 26, 25, 24 };
219*3d8817e4Smiod static int list18_h_regs[32] = { 19, 18, 17, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 30, 31, 29, 28, 23, 22, 21, 20, 27, 26, 25, 24 };
220*3d8817e4Smiod static int list18_l_regs[32] = { 3, 2, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 14, 15, 13, 12, 7, 6, 5, 4, 11, 10, 9, 8 };
221*3d8817e4Smiod int *regs;
222*3d8817e4Smiod int i;
223*3d8817e4Smiod unsigned long int mask = 0;
224*3d8817e4Smiod int pc = 0;
225*3d8817e4Smiod int sr = 0;
226*3d8817e4Smiod
227*3d8817e4Smiod switch (operand->shift)
228*3d8817e4Smiod {
229*3d8817e4Smiod case 0xffe00001: regs = list12_regs; break;
230*3d8817e4Smiod case 0xfff8000f: regs = list18_h_regs; break;
231*3d8817e4Smiod case 0xfff8001f:
232*3d8817e4Smiod regs = list18_l_regs;
233*3d8817e4Smiod value &= ~0x10; /* Do not include magic bit. */
234*3d8817e4Smiod break;
235*3d8817e4Smiod default:
236*3d8817e4Smiod /* xgettext:c-format */
237*3d8817e4Smiod fprintf (stderr, _("unknown operand shift: %x\n"),
238*3d8817e4Smiod operand->shift);
239*3d8817e4Smiod abort ();
240*3d8817e4Smiod }
241*3d8817e4Smiod
242*3d8817e4Smiod for (i = 0; i < 32; i++)
243*3d8817e4Smiod {
244*3d8817e4Smiod if (value & (1 << i))
245*3d8817e4Smiod {
246*3d8817e4Smiod switch (regs[ i ])
247*3d8817e4Smiod {
248*3d8817e4Smiod default: mask |= (1 << regs[ i ]); break;
249*3d8817e4Smiod /* xgettext:c-format */
250*3d8817e4Smiod case 0:
251*3d8817e4Smiod fprintf (stderr, _("unknown pop reg: %d\n"), i );
252*3d8817e4Smiod abort ();
253*3d8817e4Smiod case -1: pc = 1; break;
254*3d8817e4Smiod case -2: sr = 1; break;
255*3d8817e4Smiod }
256*3d8817e4Smiod }
257*3d8817e4Smiod }
258*3d8817e4Smiod
259*3d8817e4Smiod info->fprintf_func (info->stream, "{");
260*3d8817e4Smiod
261*3d8817e4Smiod if (mask || pc || sr)
262*3d8817e4Smiod {
263*3d8817e4Smiod if (mask)
264*3d8817e4Smiod {
265*3d8817e4Smiod unsigned int bit;
266*3d8817e4Smiod int shown_one = 0;
267*3d8817e4Smiod
268*3d8817e4Smiod for (bit = 0; bit < 32; bit++)
269*3d8817e4Smiod if (mask & (1 << bit))
270*3d8817e4Smiod {
271*3d8817e4Smiod unsigned long int first = bit;
272*3d8817e4Smiod unsigned long int last;
273*3d8817e4Smiod
274*3d8817e4Smiod if (shown_one)
275*3d8817e4Smiod info->fprintf_func (info->stream, ", ");
276*3d8817e4Smiod else
277*3d8817e4Smiod shown_one = 1;
278*3d8817e4Smiod
279*3d8817e4Smiod info->fprintf_func (info->stream,
280*3d8817e4Smiod v850_reg_names[first]);
281*3d8817e4Smiod
282*3d8817e4Smiod for (bit++; bit < 32; bit++)
283*3d8817e4Smiod if ((mask & (1 << bit)) == 0)
284*3d8817e4Smiod break;
285*3d8817e4Smiod
286*3d8817e4Smiod last = bit;
287*3d8817e4Smiod
288*3d8817e4Smiod if (last > first + 1)
289*3d8817e4Smiod info->fprintf_func (info->stream, " - %s",
290*3d8817e4Smiod v850_reg_names[last - 1]);
291*3d8817e4Smiod }
292*3d8817e4Smiod }
293*3d8817e4Smiod
294*3d8817e4Smiod if (pc)
295*3d8817e4Smiod info->fprintf_func (info->stream, "%sPC", mask ? ", " : "");
296*3d8817e4Smiod if (sr)
297*3d8817e4Smiod info->fprintf_func (info->stream, "%sSR", (mask || pc) ? ", " : "");
298*3d8817e4Smiod }
299*3d8817e4Smiod
300*3d8817e4Smiod info->fprintf_func (info->stream, "}");
301*3d8817e4Smiod }
302*3d8817e4Smiod break;
303*3d8817e4Smiod
304*3d8817e4Smiod case V850E_IMMEDIATE16:
305*3d8817e4Smiod status = info->read_memory_func (memaddr + bytes_read,
306*3d8817e4Smiod buffer, 2, info);
307*3d8817e4Smiod if (status == 0)
308*3d8817e4Smiod {
309*3d8817e4Smiod bytes_read += 2;
310*3d8817e4Smiod value = bfd_getl16 (buffer);
311*3d8817e4Smiod
312*3d8817e4Smiod /* If this is a DISPOSE instruction with ff
313*3d8817e4Smiod set to 0x10, then shift value up by 16. */
314*3d8817e4Smiod if ((insn & 0x001fffc0) == 0x00130780)
315*3d8817e4Smiod value <<= 16;
316*3d8817e4Smiod
317*3d8817e4Smiod info->fprintf_func (info->stream, "0x%lx", value);
318*3d8817e4Smiod }
319*3d8817e4Smiod else
320*3d8817e4Smiod info->memory_error_func (status, memaddr + bytes_read,
321*3d8817e4Smiod info);
322*3d8817e4Smiod break;
323*3d8817e4Smiod
324*3d8817e4Smiod case V850E_IMMEDIATE32:
325*3d8817e4Smiod status = info->read_memory_func (memaddr + bytes_read,
326*3d8817e4Smiod buffer, 4, info);
327*3d8817e4Smiod if (status == 0)
328*3d8817e4Smiod {
329*3d8817e4Smiod bytes_read += 4;
330*3d8817e4Smiod value = bfd_getl32 (buffer);
331*3d8817e4Smiod info->fprintf_func (info->stream, "0x%lx", value);
332*3d8817e4Smiod }
333*3d8817e4Smiod else
334*3d8817e4Smiod info->memory_error_func (status, memaddr + bytes_read,
335*3d8817e4Smiod info);
336*3d8817e4Smiod break;
337*3d8817e4Smiod }
338*3d8817e4Smiod
339*3d8817e4Smiod /* Handle jmp correctly. */
340*3d8817e4Smiod if (memop == 1 && opnum == 1
341*3d8817e4Smiod && ((operand->flags & V850_OPERAND_REG) != 0))
342*3d8817e4Smiod (*info->fprintf_func) (info->stream, "]");
343*3d8817e4Smiod }
344*3d8817e4Smiod
345*3d8817e4Smiod /* Close any square bracket we left open. */
346*3d8817e4Smiod if (memop && opnum == memop + 2)
347*3d8817e4Smiod (*info->fprintf_func) (info->stream, "]");
348*3d8817e4Smiod
349*3d8817e4Smiod /* All done. */
350*3d8817e4Smiod break;
351*3d8817e4Smiod }
352*3d8817e4Smiod op++;
353*3d8817e4Smiod }
354*3d8817e4Smiod
355*3d8817e4Smiod if (!match)
356*3d8817e4Smiod {
357*3d8817e4Smiod if (short_op)
358*3d8817e4Smiod info->fprintf_func (info->stream, ".short\t0x%04lx", insn);
359*3d8817e4Smiod else
360*3d8817e4Smiod info->fprintf_func (info->stream, ".long\t0x%08lx", insn);
361*3d8817e4Smiod }
362*3d8817e4Smiod
363*3d8817e4Smiod return bytes_read;
364*3d8817e4Smiod }
365*3d8817e4Smiod
366*3d8817e4Smiod int
print_insn_v850(bfd_vma memaddr,struct disassemble_info * info)367*3d8817e4Smiod print_insn_v850 (bfd_vma memaddr, struct disassemble_info * info)
368*3d8817e4Smiod {
369*3d8817e4Smiod int status;
370*3d8817e4Smiod bfd_byte buffer[4];
371*3d8817e4Smiod unsigned long insn = 0;
372*3d8817e4Smiod
373*3d8817e4Smiod /* First figure out how big the opcode is. */
374*3d8817e4Smiod status = info->read_memory_func (memaddr, buffer, 2, info);
375*3d8817e4Smiod if (status == 0)
376*3d8817e4Smiod {
377*3d8817e4Smiod insn = bfd_getl16 (buffer);
378*3d8817e4Smiod
379*3d8817e4Smiod if ( (insn & 0x0600) == 0x0600
380*3d8817e4Smiod && (insn & 0xffe0) != 0x0620)
381*3d8817e4Smiod {
382*3d8817e4Smiod /* If this is a 4 byte insn, read 4 bytes of stuff. */
383*3d8817e4Smiod status = info->read_memory_func (memaddr, buffer, 4, info);
384*3d8817e4Smiod
385*3d8817e4Smiod if (status == 0)
386*3d8817e4Smiod insn = bfd_getl32 (buffer);
387*3d8817e4Smiod }
388*3d8817e4Smiod }
389*3d8817e4Smiod
390*3d8817e4Smiod if (status != 0)
391*3d8817e4Smiod {
392*3d8817e4Smiod info->memory_error_func (status, memaddr, info);
393*3d8817e4Smiod return -1;
394*3d8817e4Smiod }
395*3d8817e4Smiod
396*3d8817e4Smiod /* Make sure we tell our caller how many bytes we consumed. */
397*3d8817e4Smiod return disassemble (memaddr, info, insn);
398*3d8817e4Smiod }
399