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