s390-dis.c revision 1.1.1.1
1/* s390-dis.c -- Disassemble S390 instructions
2   Copyright 2000, 2001, 2002 Free Software Foundation, Inc.
3   Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
4
5   This file is part of GDB, GAS and the GNU binutils.
6
7   This program 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 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public 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., 59 Temple Place - Suite 330, Boston, MA
20   02111-1307, USA.  */
21
22#include <stdio.h>
23#include "ansidecl.h"
24#include "sysdep.h"
25#include "dis-asm.h"
26#include "opcode/s390.h"
27
28static int init_flag = 0;
29static int opc_index[256];
30static int current_arch_mask = 0;
31
32static void init_disasm PARAMS ((struct disassemble_info *));
33static unsigned int s390_extract_operand
34  PARAMS ((unsigned char *, const struct s390_operand *));
35
36/* Set up index table for first opcode byte.  */
37
38static void
39init_disasm (info)
40     struct disassemble_info *info;
41{
42  const struct s390_opcode *opcode;
43  const struct s390_opcode *opcode_end;
44
45  memset (opc_index, 0, sizeof (opc_index));
46  opcode_end = s390_opcodes + s390_num_opcodes;
47  for (opcode = s390_opcodes; opcode < opcode_end; opcode++)
48    {
49      opc_index[(int) opcode->opcode[0]] = opcode - s390_opcodes;
50      while ((opcode < opcode_end) &&
51	     (opcode[1].opcode[0] == opcode->opcode[0]))
52	opcode++;
53    }
54  switch (info->mach)
55    {
56    case bfd_mach_s390_31:
57      current_arch_mask = 1 << S390_OPCODE_ESA;
58      break;
59    case bfd_mach_s390_64:
60      current_arch_mask = 1 << S390_OPCODE_ZARCH;
61      break;
62    default:
63      abort ();
64    }
65  init_flag = 1;
66}
67
68/* Extracts an operand value from an instruction.  */
69
70static inline unsigned int
71s390_extract_operand (insn, operand)
72     unsigned char *insn;
73     const struct s390_operand *operand;
74{
75  unsigned int val;
76  int bits;
77
78  /* Extract fragments of the operand byte for byte.  */
79  insn += operand->shift / 8;
80  bits = (operand->shift & 7) + operand->bits;
81  val = 0;
82  do
83    {
84      val <<= 8;
85      val |= (unsigned int) *insn++;
86      bits -= 8;
87    }
88  while (bits > 0);
89  val >>= -bits;
90  val &= ((1U << (operand->bits - 1)) << 1) - 1;
91
92  /* Sign extend value if the operand is signed or pc relative.  */
93  if ((operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL))
94      && (val & (1U << (operand->bits - 1))))
95    val |= (-1U << (operand->bits - 1)) << 1;
96
97  /* Double value if the operand is pc relative.  */
98  if (operand->flags & S390_OPERAND_PCREL)
99    val <<= 1;
100
101  /* Length x in an instructions has real length x+1.  */
102  if (operand->flags & S390_OPERAND_LENGTH)
103    val++;
104  return val;
105}
106
107/* Print a S390 instruction.  */
108
109int
110print_insn_s390 (memaddr, info)
111     bfd_vma memaddr;
112     struct disassemble_info *info;
113{
114  bfd_byte buffer[6];
115  const struct s390_opcode *opcode;
116  const struct s390_opcode *opcode_end;
117  unsigned int value;
118  int status, opsize, bufsize;
119  char separator;
120
121  if (init_flag == 0)
122    init_disasm (info);
123
124  /* The output looks better if we put 6 bytes on a line.  */
125  info->bytes_per_line = 6;
126
127  /* Every S390 instruction is max 6 bytes long.  */
128  memset (buffer, 0, 6);
129  status = (*info->read_memory_func) (memaddr, buffer, 6, info);
130  if (status != 0)
131    {
132      for (bufsize = 0; bufsize < 6; bufsize++)
133	if ((*info->read_memory_func) (memaddr, buffer, bufsize + 1, info) != 0)
134	  break;
135      if (bufsize <= 0)
136	{
137	  (*info->memory_error_func) (status, memaddr, info);
138	  return -1;
139	}
140      /* Opsize calculation looks strange but it works
141	 00xxxxxx -> 2 bytes, 01xxxxxx/10xxxxxx -> 4 bytes,
142	 11xxxxxx -> 6 bytes.  */
143      opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1;
144      status = opsize > bufsize;
145    }
146  else
147    {
148      bufsize = 6;
149      opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1;
150    }
151
152  if (status == 0)
153    {
154      /* Find the first match in the opcode table.  */
155      opcode_end = s390_opcodes + s390_num_opcodes;
156      for (opcode = s390_opcodes + opc_index[(int) buffer[0]];
157	   (opcode < opcode_end) && (buffer[0] == opcode->opcode[0]);
158	   opcode++)
159	{
160	  const struct s390_operand *operand;
161	  const unsigned char *opindex;
162
163	  /* Check architecture.  */
164	  if (!(opcode->modes & current_arch_mask))
165	    continue;
166	  /* Check signature of the opcode.  */
167	  if ((buffer[1] & opcode->mask[1]) != opcode->opcode[1]
168	      || (buffer[2] & opcode->mask[2]) != opcode->opcode[2]
169	      || (buffer[3] & opcode->mask[3]) != opcode->opcode[3]
170	      || (buffer[4] & opcode->mask[4]) != opcode->opcode[4]
171	      || (buffer[5] & opcode->mask[5]) != opcode->opcode[5])
172	    continue;
173
174	  /* The instruction is valid.  */
175	  if (opcode->operands[0] != 0)
176	    (*info->fprintf_func) (info->stream, "%s\t", opcode->name);
177	  else
178	    (*info->fprintf_func) (info->stream, "%s", opcode->name);
179
180	  /* Extract the operands.  */
181	  separator = 0;
182	  for (opindex = opcode->operands; *opindex != 0; opindex++)
183	    {
184	      unsigned int value;
185
186	      operand = s390_operands + *opindex;
187	      value = s390_extract_operand (buffer, operand);
188
189	      if ((operand->flags & S390_OPERAND_INDEX) && value == 0)
190		continue;
191	      if ((operand->flags & S390_OPERAND_BASE) &&
192		  value == 0 && separator == '(')
193		{
194		  separator = ',';
195		  continue;
196		}
197
198	      if (separator)
199		(*info->fprintf_func) (info->stream, "%c", separator);
200
201	      if (operand->flags & S390_OPERAND_GPR)
202		(*info->fprintf_func) (info->stream, "%%r%i", value);
203	      else if (operand->flags & S390_OPERAND_FPR)
204		(*info->fprintf_func) (info->stream, "%%f%i", value);
205	      else if (operand->flags & S390_OPERAND_AR)
206		(*info->fprintf_func) (info->stream, "%%a%i", value);
207	      else if (operand->flags & S390_OPERAND_CR)
208		(*info->fprintf_func) (info->stream, "%%c%i", value);
209	      else if (operand->flags & S390_OPERAND_PCREL)
210		(*info->print_address_func) (memaddr + (int) value, info);
211	      else if (operand->flags & S390_OPERAND_SIGNED)
212		(*info->fprintf_func) (info->stream, "%i", (int) value);
213	      else
214		(*info->fprintf_func) (info->stream, "%i", value);
215
216	      if (operand->flags & S390_OPERAND_DISP)
217		{
218		  separator = '(';
219		}
220	      else if (operand->flags & S390_OPERAND_BASE)
221		{
222		  (*info->fprintf_func) (info->stream, ")");
223		  separator = ',';
224		}
225	      else
226		separator = ',';
227	    }
228
229	  /* Found instruction, printed it, return its size.  */
230	  return opsize;
231	}
232      /* No matching instruction found, fall through to hex print.  */
233    }
234
235  if (bufsize >= 4)
236    {
237      value = (unsigned int) buffer[0];
238      value = (value << 8) + (unsigned int) buffer[1];
239      value = (value << 8) + (unsigned int) buffer[2];
240      value = (value << 8) + (unsigned int) buffer[3];
241      (*info->fprintf_func) (info->stream, ".long\t0x%08x", value);
242      return 4;
243    }
244  else if (bufsize >= 2)
245    {
246      value = (unsigned int) buffer[0];
247      value = (value << 8) + (unsigned int) buffer[1];
248      (*info->fprintf_func) (info->stream, ".short\t0x%04x", value);
249      return 2;
250    }
251  else
252    {
253      value = (unsigned int) buffer[0];
254      (*info->fprintf_func) (info->stream, ".byte\t0x%02x", value);
255      return 1;
256    }
257}
258