1/* s390-dis.c -- Disassemble S390 instructions
2   Copyright 2000, 2001, 2002, 2003, 2005, 2007, 2008
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 <stdio.h>
24#include "ansidecl.h"
25#include "sysdep.h"
26#include "dis-asm.h"
27#include "opintl.h"
28#include "opcode/s390.h"
29
30static int init_flag = 0;
31static int opc_index[256];
32static int current_arch_mask = 0;
33
34/* Set up index table for first opcode byte.  */
35
36static void
37init_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
89static inline unsigned int
90s390_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
126int
127print_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
298void
299print_s390_disassembler_options (FILE *stream)
300{
301  fprintf (stream, _("\n\
302The following S/390 specific disassembler options are supported for use\n\
303with 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