OSDN Git Service

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