xref: /netbsd-src/external/gpl3/gdb/dist/opcodes/s390-dis.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
1 /* s390-dis.c -- Disassemble S390 instructions
2    Copyright 2000, 2001, 2002, 2003, 2005, 2007, 2008, 2012
3    Free Software Foundation, Inc.
4    Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
5 
6    This file is part of the GNU opcodes library.
7 
8    This library 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, or (at your option)
11    any later version.
12 
13    It is distributed in the hope that it will be useful, but WITHOUT
14    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
16    License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this file; see the file COPYING.  If not, write to the
20    Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
21    MA 02110-1301, USA.  */
22 
23 #include "sysdep.h"
24 #include <stdio.h>
25 #include "ansidecl.h"
26 #include "dis-asm.h"
27 #include "opintl.h"
28 #include "opcode/s390.h"
29 
30 static int init_flag = 0;
31 static int opc_index[256];
32 static int current_arch_mask = 0;
33 
34 /* Set up index table for first opcode byte.  */
35 
36 static void
37 init_disasm (struct disassemble_info *info)
38 {
39   const struct s390_opcode *opcode;
40   const struct s390_opcode *opcode_end;
41   const char *p;
42 
43   memset (opc_index, 0, sizeof (opc_index));
44   opcode_end = s390_opcodes + s390_num_opcodes;
45   for (opcode = s390_opcodes; opcode < opcode_end; opcode++)
46     {
47       opc_index[(int) opcode->opcode[0]] = opcode - s390_opcodes;
48       while ((opcode < opcode_end) &&
49 	     (opcode[1].opcode[0] == opcode->opcode[0]))
50 	opcode++;
51     }
52 
53   for (p = info->disassembler_options; p != NULL; )
54     {
55       if (CONST_STRNEQ (p, "esa"))
56 	current_arch_mask = 1 << S390_OPCODE_ESA;
57       else if (CONST_STRNEQ (p, "zarch"))
58 	current_arch_mask = 1 << S390_OPCODE_ZARCH;
59       else
60 	fprintf (stderr, "Unknown S/390 disassembler option: %s\n", p);
61 
62       p = strchr (p, ',');
63       if (p != NULL)
64 	p++;
65     }
66 
67   if (!current_arch_mask)
68     switch (info->mach)
69       {
70       case bfd_mach_s390_31:
71 	current_arch_mask = 1 << S390_OPCODE_ESA;
72 	break;
73       case bfd_mach_s390_64:
74 	current_arch_mask = 1 << S390_OPCODE_ZARCH;
75 	break;
76       default:
77 	abort ();
78       }
79 
80   init_flag = 1;
81 }
82 
83 /* Extracts an operand value from an instruction.  */
84 /* We do not perform the shift operation for larl-type address
85    operands here since that would lead to an overflow of the 32 bit
86    integer value.  Instead the shift operation is done when printing
87    the operand in print_insn_s390.  */
88 
89 static inline unsigned int
90 s390_extract_operand (unsigned char *insn, const struct s390_operand *operand)
91 {
92   unsigned int val;
93   int bits;
94 
95   /* Extract fragments of the operand byte for byte.  */
96   insn += operand->shift / 8;
97   bits = (operand->shift & 7) + operand->bits;
98   val = 0;
99   do
100     {
101       val <<= 8;
102       val |= (unsigned int) *insn++;
103       bits -= 8;
104     }
105   while (bits > 0);
106   val >>= -bits;
107   val &= ((1U << (operand->bits - 1)) << 1) - 1;
108 
109   /* Check for special long displacement case.  */
110   if (operand->bits == 20 && operand->shift == 20)
111     val = (val & 0xff) << 12 | (val & 0xfff00) >> 8;
112 
113   /* Sign extend value if the operand is signed or pc relative.  */
114   if ((operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL))
115       && (val & (1U << (operand->bits - 1))))
116     val |= (-1U << (operand->bits - 1)) << 1;
117 
118   /* Length x in an instructions has real length x + 1.  */
119   if (operand->flags & S390_OPERAND_LENGTH)
120     val++;
121   return val;
122 }
123 
124 /* Print a S390 instruction.  */
125 
126 int
127 print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
128 {
129   bfd_byte buffer[6];
130   const struct s390_opcode *opcode;
131   const struct s390_opcode *opcode_end;
132   unsigned int value;
133   int status, opsize, bufsize;
134   char separator;
135 
136   if (init_flag == 0)
137     init_disasm (info);
138 
139   /* The output looks better if we put 6 bytes on a line.  */
140   info->bytes_per_line = 6;
141 
142   /* Every S390 instruction is max 6 bytes long.  */
143   memset (buffer, 0, 6);
144   status = (*info->read_memory_func) (memaddr, buffer, 6, info);
145   if (status != 0)
146     {
147       for (bufsize = 0; bufsize < 6; bufsize++)
148 	if ((*info->read_memory_func) (memaddr, buffer, bufsize + 1, info) != 0)
149 	  break;
150       if (bufsize <= 0)
151 	{
152 	  (*info->memory_error_func) (status, memaddr, info);
153 	  return -1;
154 	}
155       /* Opsize calculation looks strange but it works
156 	 00xxxxxx -> 2 bytes, 01xxxxxx/10xxxxxx -> 4 bytes,
157 	 11xxxxxx -> 6 bytes.  */
158       opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1;
159       status = opsize > bufsize;
160     }
161   else
162     {
163       bufsize = 6;
164       opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1;
165     }
166 
167   if (status == 0)
168     {
169       const struct s390_opcode *op;
170 
171       /* Find the first match in the opcode table.  */
172       opcode_end = s390_opcodes + s390_num_opcodes;
173       for (opcode = s390_opcodes + opc_index[(int) buffer[0]];
174 	   (opcode < opcode_end) && (buffer[0] == opcode->opcode[0]);
175 	   opcode++)
176 	{
177 	  const struct s390_operand *operand;
178 	  const unsigned char *opindex;
179 
180 	  /* Check architecture.  */
181 	  if (!(opcode->modes & current_arch_mask))
182 	    continue;
183 
184 	  /* Check signature of the opcode.  */
185 	  if ((buffer[1] & opcode->mask[1]) != opcode->opcode[1]
186 	      || (buffer[2] & opcode->mask[2]) != opcode->opcode[2]
187 	      || (buffer[3] & opcode->mask[3]) != opcode->opcode[3]
188 	      || (buffer[4] & opcode->mask[4]) != opcode->opcode[4]
189 	      || (buffer[5] & opcode->mask[5]) != opcode->opcode[5])
190 	    continue;
191 
192 	  /* Advance to an opcode with a more specific mask.  */
193 	  for (op = opcode + 1; op < opcode_end; op++)
194 	    {
195 	      if ((buffer[0] & op->mask[0]) != op->opcode[0])
196 		break;
197 
198 	      if ((buffer[1] & op->mask[1]) != op->opcode[1]
199 		  || (buffer[2] & op->mask[2]) != op->opcode[2]
200 		  || (buffer[3] & op->mask[3]) != op->opcode[3]
201 		  || (buffer[4] & op->mask[4]) != op->opcode[4]
202 		  || (buffer[5] & op->mask[5]) != op->opcode[5])
203 		continue;
204 
205 	      if (((int)opcode->mask[0] + opcode->mask[1] +
206 		   opcode->mask[2] + opcode->mask[3] +
207 		   opcode->mask[4] + opcode->mask[5]) <
208 		  ((int)op->mask[0] + op->mask[1] +
209 		   op->mask[2] + op->mask[3] +
210 		   op->mask[4] + op->mask[5]))
211 		opcode = op;
212 	    }
213 
214 	  /* The instruction is valid.  */
215 	  if (opcode->operands[0] != 0)
216 	    (*info->fprintf_func) (info->stream, "%s\t", opcode->name);
217 	  else
218 	    (*info->fprintf_func) (info->stream, "%s", opcode->name);
219 
220 	  /* Extract the operands.  */
221 	  separator = 0;
222 	  for (opindex = opcode->operands; *opindex != 0; opindex++)
223 	    {
224 	      operand = s390_operands + *opindex;
225 	      value = s390_extract_operand (buffer, operand);
226 
227 	      if ((operand->flags & S390_OPERAND_INDEX) && value == 0)
228 		continue;
229 	      if ((operand->flags & S390_OPERAND_BASE) &&
230 		  value == 0 && separator == '(')
231 		{
232 		  separator = ',';
233 		  continue;
234 		}
235 
236 	      if (separator)
237 		(*info->fprintf_func) (info->stream, "%c", separator);
238 
239 	      if (operand->flags & S390_OPERAND_GPR)
240 		(*info->fprintf_func) (info->stream, "%%r%i", value);
241 	      else if (operand->flags & S390_OPERAND_FPR)
242 		(*info->fprintf_func) (info->stream, "%%f%i", value);
243 	      else if (operand->flags & S390_OPERAND_AR)
244 		(*info->fprintf_func) (info->stream, "%%a%i", value);
245 	      else if (operand->flags & S390_OPERAND_CR)
246 		(*info->fprintf_func) (info->stream, "%%c%i", value);
247 	      else if (operand->flags & S390_OPERAND_PCREL)
248 		(*info->print_address_func) (memaddr + (int)value + (int)value,
249 					     info);
250 	      else if (operand->flags & S390_OPERAND_SIGNED)
251 		(*info->fprintf_func) (info->stream, "%i", (int) value);
252 	      else
253 		(*info->fprintf_func) (info->stream, "%u", value);
254 
255 	      if (operand->flags & S390_OPERAND_DISP)
256 		{
257 		  separator = '(';
258 		}
259 	      else if (operand->flags & S390_OPERAND_BASE)
260 		{
261 		  (*info->fprintf_func) (info->stream, ")");
262 		  separator = ',';
263 		}
264 	      else
265 		separator = ',';
266 	    }
267 
268 	  /* Found instruction, printed it, return its size.  */
269 	  return opsize;
270 	}
271       /* No matching instruction found, fall through to hex print.  */
272     }
273 
274   if (bufsize >= 4)
275     {
276       value = (unsigned int) buffer[0];
277       value = (value << 8) + (unsigned int) buffer[1];
278       value = (value << 8) + (unsigned int) buffer[2];
279       value = (value << 8) + (unsigned int) buffer[3];
280       (*info->fprintf_func) (info->stream, ".long\t0x%08x", value);
281       return 4;
282     }
283   else if (bufsize >= 2)
284     {
285       value = (unsigned int) buffer[0];
286       value = (value << 8) + (unsigned int) buffer[1];
287       (*info->fprintf_func) (info->stream, ".short\t0x%04x", value);
288       return 2;
289     }
290   else
291     {
292       value = (unsigned int) buffer[0];
293       (*info->fprintf_func) (info->stream, ".byte\t0x%02x", value);
294       return 1;
295     }
296 }
297 
298 void
299 print_s390_disassembler_options (FILE *stream)
300 {
301   fprintf (stream, _("\n\
302 The following S/390 specific disassembler options are supported for use\n\
303 with the -M switch (multiple options should be separated by commas):\n"));
304 
305   fprintf (stream, _("  esa         Disassemble in ESA architecture mode\n"));
306   fprintf (stream, _("  zarch       Disassemble in z/Architecture mode\n"));
307 }
308