]>
Commit | Line | Data |
---|---|---|
a85d7ed0 NC |
1 | /* s390-dis.c -- Disassemble S390 instructions |
2 | Copyright (C) 2000, 2001 Free Software Foundation, Inc. | |
3 | Contributed by Martin Schwidefsky ([email protected]). | |
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 | } |