OSDN Git Service

6409d5fcfef8ad67688d908dde1f2c1d8b72292a
[pf3gnuchains/pf3gnuchains3x.git] / opcodes / s390-dis.c
1 /* s390-dis.c -- Disassemble S390 instructions
2    Copyright (C) 2000, 2001 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
28 static int init_flag = 0;
29 static int opc_index[256];
30 static int current_arch_mask = 0;
31
32 /* Set up index table for first opcode byte */
33 static void 
34 init_disasm(info)
35     struct disassemble_info *info ATTRIBUTE_UNUSED;
36 {
37   const struct s390_opcode *opcode;
38   const struct s390_opcode *opcode_end;
39
40   memset(opc_index, 0, sizeof(opc_index));
41   opcode_end = s390_opcodes + s390_num_opcodes;
42   for (opcode = s390_opcodes; opcode < opcode_end; opcode++) {
43     opc_index[(int) opcode->opcode[0]] = opcode - s390_opcodes;
44     while ((opcode < opcode_end) && 
45            (opcode[1].opcode[0] == opcode->opcode[0]))
46       opcode++;
47   }
48   switch (info->mach) {
49   case bfd_mach_s390_esa:
50     current_arch_mask = 1 << S390_OPCODE_ESA;
51     break;
52   case bfd_mach_s390_esame:
53     current_arch_mask = 1 << S390_OPCODE_ESAME;
54     break;
55   default:
56     abort();
57   }
58   init_flag = 1;
59 }
60
61 /* Extracts an operand value from an instruction.  */
62
63 static inline unsigned int
64 s390_extract_operand (insn, operand)
65      unsigned char *insn;
66      const struct s390_operand *operand;
67 {
68   unsigned int val;
69   int bits;
70
71   /* extract fragments of the operand byte for byte */
72   insn += operand->shift/8;
73   bits = (operand->shift & 7) + operand->bits;
74   val = 0;
75   do {
76     val <<= 8;
77     val |= (unsigned int) *insn++;
78     bits -= 8;
79   } while (bits > 0);
80   val >>= -bits;
81   val &= ((1U << (operand->bits-1))<<1) - 1;
82
83   /* sign extend value if the operand is signed or pc relative */
84   if ((operand->flags & (S390_OPERAND_SIGNED|S390_OPERAND_PCREL)) &&
85       (val & (1U << (operand->bits-1))))
86     val |= (-1U << (operand->bits-1))<<1;
87  
88   /* double value if the operand is pc relative */
89   if (operand->flags & S390_OPERAND_PCREL)
90     val <<= 1;
91  
92   /* length x in an instructions has real length x+1 */
93   if (operand->flags & S390_OPERAND_LENGTH)
94     val++;
95   return val;
96 }
97
98 /* Print a S390 instruction.  */
99
100 int
101 print_insn_s390 (memaddr, info)
102      bfd_vma memaddr;
103      struct disassemble_info *info;
104 {
105   bfd_byte buffer[6];
106   const struct s390_opcode *opcode;
107   const struct s390_opcode *opcode_end;
108   unsigned int value;
109   int status, opsize, bufsize;
110   char separator;
111
112   if (init_flag == 0)
113     init_disasm(info);
114
115   /* The output looks better if we put 6 bytes on a line.  */
116   info->bytes_per_line = 6;
117
118   /* Every S390 instruction is max 6 bytes long.  */
119   memset(buffer, 0, 6);
120   status = (*info->read_memory_func) (memaddr, buffer, 6, info);
121   if (status != 0) {
122     for (bufsize = 0; bufsize < 6; bufsize++)
123       if ((*info->read_memory_func) (memaddr, buffer, bufsize+1, info) != 0)
124         break;
125     if (bufsize <= 0) {
126       (*info->memory_error_func) (status, memaddr, info);
127       return -1;
128     }
129     /* Opsize calculation looks strange but it works
130        00xxxxxx -> 2 bytes, 01xxxxxx/10xxxxxx -> 4 bytes, 
131        11xxxxxx -> 6 bytes.  */
132     opsize = ((((buffer[0]>>6)+1)>>1)+1)<<1;
133     status = opsize > bufsize;
134   } else {
135     bufsize = 6;
136     opsize = ((((buffer[0]>>6)+1)>>1)+1)<<1;
137   }
138
139   if (status == 0) {
140     /* Find the first match in the opcode table.  */
141     opcode_end = s390_opcodes + s390_num_opcodes;
142     for (opcode = s390_opcodes + opc_index[(int) buffer[0]]; 
143          (opcode < opcode_end) && (buffer[0] == opcode->opcode[0]);
144          opcode++) {
145       const struct s390_operand *operand;
146       const unsigned char *opindex;
147
148       /* check architecture */
149       if (!(opcode->architecture & current_arch_mask))
150         continue;
151       /* check signature of the opcode */
152       if ((buffer[1] & opcode->mask[1]) != opcode->opcode[1] ||
153           (buffer[2] & opcode->mask[2]) != opcode->opcode[2] ||
154           (buffer[3] & opcode->mask[3]) != opcode->opcode[3] ||
155           (buffer[4] & opcode->mask[4]) != opcode->opcode[4] ||
156           (buffer[5] & opcode->mask[5]) != opcode->opcode[5])
157         continue;
158   
159       /* the instruction is valid */
160       if (opcode->operands[0] != 0)
161         (*info->fprintf_func) (info->stream, "%s\t", opcode->name);
162       else
163         (*info->fprintf_func) (info->stream, "%s", opcode->name);
164   
165       /* Extract the operands.  */
166       separator = 0;
167       for (opindex = opcode->operands; *opindex != 0; opindex++) {
168         unsigned int value;
169
170         operand = s390_operands + *opindex;
171         value = s390_extract_operand(buffer, operand);
172
173         if ((operand->flags & S390_OPERAND_INDEX) && value == 0)
174           continue;
175         if ((operand->flags & S390_OPERAND_BASE) && 
176             value == 0 && separator == '(') {
177           separator = ',';
178           continue;
179         }
180
181         if (separator)
182           (*info->fprintf_func) (info->stream, "%c", separator);
183
184         if (operand->flags & S390_OPERAND_GPR)
185           (*info->fprintf_func) (info->stream, "%%r%i", value);
186         else if (operand->flags & S390_OPERAND_FPR)
187           (*info->fprintf_func) (info->stream, "%%f%i", value);
188         else if (operand->flags & S390_OPERAND_AR)
189           (*info->fprintf_func) (info->stream, "%%a%i", value);
190         else if (operand->flags & S390_OPERAND_CR)
191           (*info->fprintf_func) (info->stream, "%%c%i", value);
192         else if (operand->flags & S390_OPERAND_PCREL)
193           (*info->print_address_func) (memaddr + (int) value, info);
194         else if (operand->flags & S390_OPERAND_SIGNED)
195           (*info->fprintf_func) (info->stream, "%i", (int) value);
196         else
197           (*info->fprintf_func) (info->stream, "%i", value);
198
199         if (operand->flags & S390_OPERAND_DISP) {
200           separator = '(';
201         } else if (operand->flags & S390_OPERAND_BASE) {
202           (*info->fprintf_func) (info->stream, ")");
203           separator = ',';
204         } else
205           separator = ',';
206       }
207
208       /* found instruction, printed it, return its size */
209       return opsize;
210     }
211     /* no matching instruction found, fall through to hex print  */
212   }
213
214   if (bufsize >= 4) {
215     value = (unsigned int) buffer[0];
216     value = (value << 8) + (unsigned int) buffer[1];
217     value = (value << 8) + (unsigned int) buffer[2];
218     value = (value << 8) + (unsigned int) buffer[3];
219     (*info->fprintf_func) (info->stream,".long\t0x%08x", value);
220     return 4;
221   } else if (bufsize >= 2) {
222     value = (unsigned int) buffer[0];
223     value = (value << 8) + (unsigned int) buffer[1];
224     (*info->fprintf_func) (info->stream,".short\t0x%04x", value);
225     return 2;
226   } else {
227     value = (unsigned int) buffer[0];
228     (*info->fprintf_func) (info->stream,".byte\t0x%02x", value);
229     return 1;
230   }
231 }