/src/binutils-gdb/opcodes/s390-dis.c
Line | Count | Source |
1 | | /* s390-dis.c -- Disassemble S390 instructions |
2 | | Copyright (C) 2000-2026 Free Software Foundation, Inc. |
3 | | Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). |
4 | | |
5 | | This file is part of the GNU opcodes library. |
6 | | |
7 | | This library 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 3, or (at your option) |
10 | | any later version. |
11 | | |
12 | | It is distributed in the hope that it will be useful, but WITHOUT |
13 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
14 | | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
15 | | License for more details. |
16 | | |
17 | | You should have received a copy of the GNU General Public License |
18 | | along with this file; see the file COPYING. If not, write to the |
19 | | Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, |
20 | | MA 02110-1301, USA. */ |
21 | | |
22 | | #include "sysdep.h" |
23 | | #include <stdio.h> |
24 | | #include "ansidecl.h" |
25 | | #include "disassemble.h" |
26 | | #include "opintl.h" |
27 | | #include "opcode/s390.h" |
28 | | #include "libiberty.h" |
29 | | #include "dis-asm.h" |
30 | | |
31 | | static int opc_index[256]; |
32 | | static int current_arch_mask = 0; |
33 | | static int option_use_insn_len_bits_p = 0; |
34 | | static int option_print_insn_desc = 0; |
35 | | |
36 | | typedef struct |
37 | | { |
38 | | const char *name; |
39 | | const char *description; |
40 | | } s390_options_t; |
41 | | |
42 | | static const s390_options_t options[] = |
43 | | { |
44 | | { "esa" , N_("Disassemble in ESA architecture mode") }, |
45 | | /* TRANSLATORS: Please do not translate 'z/Architecture' as this is a technical name. */ |
46 | | { "zarch", N_("Disassemble in z/Architecture mode") }, |
47 | | { "insnlength", N_("Print unknown instructions according to " |
48 | | "length from first two bits") }, |
49 | | { "insndesc", N_("Print instruction description as comment") }, |
50 | | }; |
51 | | |
52 | | /* Set up index table for first opcode byte. */ |
53 | | |
54 | | void |
55 | | disassemble_init_s390 (struct disassemble_info *info) |
56 | 170 | { |
57 | 170 | int i; |
58 | 170 | const char *p; |
59 | | |
60 | 170 | memset (opc_index, 0, sizeof (opc_index)); |
61 | | |
62 | | /* Reverse order, such that each opc_index ends up pointing to the |
63 | | first matching entry instead of the last. */ |
64 | 467k | for (i = s390_num_opcodes; i--; ) |
65 | 467k | opc_index[s390_opcodes[i].opcode[0]] = i; |
66 | | |
67 | 170 | current_arch_mask = 1 << S390_OPCODE_ZARCH; |
68 | 170 | option_use_insn_len_bits_p = 0; |
69 | 170 | option_print_insn_desc = 0; |
70 | | |
71 | 170 | for (p = info->disassembler_options; p != NULL; ) |
72 | 0 | { |
73 | 0 | if (startswith (p, "esa")) |
74 | 0 | current_arch_mask = 1 << S390_OPCODE_ESA; |
75 | 0 | else if (startswith (p, "zarch")) |
76 | 0 | current_arch_mask = 1 << S390_OPCODE_ZARCH; |
77 | 0 | else if (startswith (p, "insnlength")) |
78 | 0 | option_use_insn_len_bits_p = 1; |
79 | 0 | else if (startswith (p, "insndesc")) |
80 | 0 | option_print_insn_desc = 1; |
81 | 0 | else |
82 | | /* xgettext:c-format */ |
83 | 0 | opcodes_error_handler (_("unknown S/390 disassembler option: %s"), p); |
84 | |
|
85 | 0 | p = strchr (p, ','); |
86 | 0 | if (p != NULL) |
87 | 0 | p++; |
88 | 0 | } |
89 | 170 | } |
90 | | |
91 | | /* Derive the length of an instruction from its first byte. */ |
92 | | |
93 | | static inline int |
94 | | s390_insn_length (const bfd_byte *buffer) |
95 | 136k | { |
96 | | /* 00xxxxxx -> 2, 01xxxxxx/10xxxxxx -> 4, 11xxxxxx -> 6. */ |
97 | 136k | return ((buffer[0] >> 6) + 3) & ~1U; |
98 | 136k | } |
99 | | |
100 | | /* Match the instruction in BUFFER against the given OPCODE, excluding |
101 | | the first byte. */ |
102 | | |
103 | | static inline int |
104 | | s390_insn_matches_opcode (const bfd_byte *buffer, |
105 | | const struct s390_opcode *opcode) |
106 | 1.17M | { |
107 | 1.17M | return (buffer[1] & opcode->mask[1]) == opcode->opcode[1] |
108 | 779k | && (buffer[2] & opcode->mask[2]) == opcode->opcode[2] |
109 | 374k | && (buffer[3] & opcode->mask[3]) == opcode->opcode[3] |
110 | 328k | && (buffer[4] & opcode->mask[4]) == opcode->opcode[4] |
111 | 219k | && (buffer[5] & opcode->mask[5]) == opcode->opcode[5]; |
112 | 1.17M | } |
113 | | |
114 | | union operand_value |
115 | | { |
116 | | int i; |
117 | | unsigned int u; |
118 | | }; |
119 | | |
120 | | /* Extracts an operand value from an instruction. */ |
121 | | /* We do not perform the shift operation for larl-type address |
122 | | operands here since that would lead to an overflow of the 32 bit |
123 | | integer value. Instead the shift operation is done when printing |
124 | | the operand. */ |
125 | | |
126 | | static inline union operand_value |
127 | | s390_extract_operand (const bfd_byte *insn, |
128 | | const struct s390_operand *operand) |
129 | 265k | { |
130 | 265k | union operand_value ret; |
131 | 265k | unsigned int val; |
132 | 265k | int bits; |
133 | 265k | const bfd_byte *orig_insn = insn; |
134 | | |
135 | | /* Extract fragments of the operand byte for byte. */ |
136 | 265k | insn += operand->shift / 8; |
137 | 265k | bits = (operand->shift & 7) + operand->bits; |
138 | 265k | val = 0; |
139 | 265k | do |
140 | 360k | { |
141 | 360k | val <<= 8; |
142 | 360k | val |= (unsigned int) *insn++; |
143 | 360k | bits -= 8; |
144 | 360k | } |
145 | 360k | while (bits > 0); |
146 | 265k | val >>= -bits; |
147 | 265k | val &= ((1U << (operand->bits - 1)) << 1) - 1; |
148 | | |
149 | | /* Check for special long displacement case. */ |
150 | 265k | if (operand->bits == 20 && operand->shift == 20) |
151 | 394 | val = (val & 0xff) << 12 | (val & 0xfff00) >> 8; |
152 | | |
153 | | /* Sign extend value if the operand is signed or pc relative. Avoid |
154 | | integer overflows. */ |
155 | 265k | if (operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL)) |
156 | 4.15k | { |
157 | 4.15k | unsigned int m = 1U << (operand->bits - 1); |
158 | | |
159 | 4.15k | if (val >= m) |
160 | 1.76k | ret.i = (int) (val - m) - 1 - (int) (m - 1U); |
161 | 2.39k | else |
162 | 2.39k | ret.i = (int) val; |
163 | 4.15k | } |
164 | 261k | else if (operand->flags & S390_OPERAND_LENGTH) |
165 | | /* Length x in an instruction has real length x + 1. */ |
166 | 10.6k | ret.u = val + 1; |
167 | | |
168 | 250k | else if (operand->flags & S390_OPERAND_VR) |
169 | 490 | { |
170 | | /* Extract the extra bits for a vector register operand stored |
171 | | in the RXB field. */ |
172 | 490 | unsigned vr = operand->shift == 32 ? 3 |
173 | 490 | : (unsigned) operand->shift / 4 - 2; |
174 | | |
175 | 490 | ret.u = val | ((orig_insn[4] & (1 << (3 - vr))) << (vr + 1)); |
176 | 490 | } |
177 | 249k | else |
178 | 249k | ret.u = val; |
179 | | |
180 | 265k | return ret; |
181 | 265k | } |
182 | | |
183 | | /* Return remaining operand count. */ |
184 | | |
185 | | static unsigned int |
186 | | operand_count (const unsigned char *opindex_ptr) |
187 | 1.65k | { |
188 | 1.65k | unsigned int count = 0; |
189 | | |
190 | 5.51k | for (; *opindex_ptr != 0; opindex_ptr++) |
191 | 3.86k | { |
192 | | /* Count D(X,B), D(B), and D(L,B) as one operand. Assuming correct |
193 | | instruction operand definitions simply do not count D, X, and L. */ |
194 | 3.86k | if (!(s390_operands[*opindex_ptr].flags & (S390_OPERAND_DISP |
195 | 3.86k | | S390_OPERAND_INDEX |
196 | 3.86k | | S390_OPERAND_LENGTH))) |
197 | 2.23k | count++; |
198 | 3.86k | } |
199 | | |
200 | 1.65k | return count; |
201 | 1.65k | } |
202 | | |
203 | | /* Return true if all remaining instruction operands are optional. */ |
204 | | |
205 | | static bool |
206 | | skip_optargs_p (unsigned int opcode_flags, const unsigned char *opindex_ptr) |
207 | 144k | { |
208 | 144k | if ((opcode_flags & (S390_INSTR_FLAG_OPTPARM | S390_INSTR_FLAG_OPTPARM2))) |
209 | 1.65k | { |
210 | 1.65k | unsigned int opcount = operand_count (opindex_ptr); |
211 | | |
212 | 1.65k | if (opcount == 1) |
213 | 1.33k | return true; |
214 | | |
215 | 320 | if ((opcode_flags & S390_INSTR_FLAG_OPTPARM2) && opcount == 2) |
216 | 41 | return true; |
217 | 320 | } |
218 | | |
219 | 143k | return false; |
220 | 144k | } |
221 | | |
222 | | /* Return true if all remaining instruction operands are optional |
223 | | and their values are zero. */ |
224 | | |
225 | | static bool |
226 | | skip_optargs_zero_p (const bfd_byte *buffer, unsigned int opcode_flags, |
227 | | const unsigned char *opindex_ptr) |
228 | 144k | { |
229 | | /* Test if remaining operands are optional. */ |
230 | 144k | if (!skip_optargs_p (opcode_flags, opindex_ptr)) |
231 | 143k | return false; |
232 | | |
233 | | /* Test if remaining operand values are zero. */ |
234 | 2.87k | for (; *opindex_ptr != 0; opindex_ptr++) |
235 | 2.30k | { |
236 | 2.30k | const struct s390_operand *operand = &s390_operands[*opindex_ptr]; |
237 | 2.30k | union operand_value value = s390_extract_operand (buffer, operand); |
238 | | |
239 | 2.30k | if (value.u != 0) |
240 | 800 | return false; |
241 | 2.30k | } |
242 | | |
243 | 573 | return true; |
244 | 1.37k | } |
245 | | |
246 | | /* Print the S390 instruction in BUFFER, assuming that it matches the |
247 | | given OPCODE. */ |
248 | | |
249 | | static void |
250 | | s390_print_insn_with_opcode (bfd_vma memaddr, |
251 | | struct disassemble_info *info, |
252 | | const bfd_byte *buffer, |
253 | | const struct s390_opcode *opcode) |
254 | 70.6k | { |
255 | 70.6k | const unsigned char *opindex; |
256 | 70.6k | char separator; |
257 | | |
258 | | /* Mnemonic. */ |
259 | 70.6k | info->fprintf_styled_func (info->stream, dis_style_mnemonic, |
260 | 70.6k | "%s", opcode->name); |
261 | | |
262 | | /* Operands. */ |
263 | 70.6k | separator = '\t'; |
264 | 284k | for (opindex = opcode->operands; *opindex != 0; opindex++) |
265 | 214k | { |
266 | 214k | const struct s390_operand *operand = s390_operands + *opindex; |
267 | 214k | union operand_value val = s390_extract_operand (buffer, operand); |
268 | 214k | unsigned long flags = operand->flags; |
269 | | |
270 | | /* Omit index register 0, except for vector index register 0. */ |
271 | 214k | if ((flags & S390_OPERAND_INDEX) && !(flags & S390_OPERAND_VR) |
272 | 17.1k | && val.u == 0) |
273 | 2.54k | continue; |
274 | | /* Omit base register 0, if no or omitted index register 0. */ |
275 | 212k | if ((flags & S390_OPERAND_BASE) && val.u == 0 && separator == '(') |
276 | 3.88k | { |
277 | 3.88k | separator = ','; |
278 | 3.88k | continue; |
279 | 3.88k | } |
280 | | |
281 | | /* Omit optional last operands with a value of zero, except if |
282 | | within an addressing operand sequence D(X,B), D(B), and D(L,B). |
283 | | Index and base register operands with a value of zero are |
284 | | handled separately, as they may not be omitted unconditionally. */ |
285 | 208k | if (!(operand->flags & (S390_OPERAND_BASE |
286 | 208k | | S390_OPERAND_INDEX |
287 | 208k | | S390_OPERAND_LENGTH)) |
288 | 144k | && skip_optargs_zero_p (buffer, opcode->flags, opindex)) |
289 | 573 | break; |
290 | | |
291 | 207k | if (flags & S390_OPERAND_GPR) |
292 | 99.5k | { |
293 | 99.5k | info->fprintf_styled_func (info->stream, dis_style_text, |
294 | 99.5k | "%c", separator); |
295 | 99.5k | if ((flags & (S390_OPERAND_BASE | S390_OPERAND_INDEX)) |
296 | 53.2k | && val.u == 0) |
297 | 3.88k | info->fprintf_styled_func (info->stream, dis_style_register, |
298 | 3.88k | "%u", val.u); |
299 | 95.6k | else |
300 | 95.6k | info->fprintf_styled_func (info->stream, dis_style_register, |
301 | 95.6k | "%%r%u", val.u); |
302 | 99.5k | } |
303 | 108k | else if (flags & S390_OPERAND_FPR) |
304 | 43.1k | { |
305 | 43.1k | info->fprintf_styled_func (info->stream, dis_style_text, |
306 | 43.1k | "%c", separator); |
307 | 43.1k | info->fprintf_styled_func (info->stream, dis_style_register, |
308 | 43.1k | "%%f%u", val.u); |
309 | 43.1k | } |
310 | 65.2k | else if (flags & S390_OPERAND_VR) |
311 | 490 | { |
312 | 490 | info->fprintf_styled_func (info->stream, dis_style_text, |
313 | 490 | "%c", separator); |
314 | 490 | info->fprintf_styled_func (info->stream, dis_style_register, |
315 | 490 | "%%v%u", val.u); |
316 | 490 | } |
317 | 64.7k | else if (flags & S390_OPERAND_AR) |
318 | 778 | { |
319 | 778 | info->fprintf_styled_func (info->stream, dis_style_text, |
320 | 778 | "%c", separator); |
321 | 778 | info->fprintf_styled_func (info->stream, dis_style_register, |
322 | 778 | "%%a%u", val.u); |
323 | 778 | } |
324 | 63.9k | else if (flags & S390_OPERAND_CR) |
325 | 1.07k | { |
326 | 1.07k | info->fprintf_styled_func (info->stream, dis_style_text, |
327 | 1.07k | "%c", separator); |
328 | 1.07k | info->fprintf_styled_func (info->stream, dis_style_register, |
329 | 1.07k | "%%c%u", val.u); |
330 | 1.07k | } |
331 | 62.8k | else if (flags & S390_OPERAND_PCREL) |
332 | 3.44k | { |
333 | 3.44k | bfd_vma target = memaddr + val.i + val.i; |
334 | | |
335 | | /* Provide info for jump visualization. May be evaluated by p_a_f(). */ |
336 | 3.44k | info->target = target; |
337 | | |
338 | 3.44k | info->fprintf_styled_func (info->stream, dis_style_text, |
339 | 3.44k | "%c", separator); |
340 | 3.44k | info->print_address_func (target, info); |
341 | 3.44k | } |
342 | 59.4k | else if (flags & S390_OPERAND_SIGNED) |
343 | 718 | { |
344 | 718 | enum disassembler_style style; |
345 | | |
346 | 718 | info->fprintf_styled_func (info->stream, dis_style_text, |
347 | 718 | "%c", separator); |
348 | 718 | style = ((flags & S390_OPERAND_DISP) |
349 | 718 | ? dis_style_address_offset : dis_style_immediate); |
350 | 718 | info->fprintf_styled_func (info->stream, style, "%i", val.i); |
351 | 718 | } |
352 | 58.7k | else |
353 | 58.7k | { |
354 | 58.7k | enum disassembler_style style; |
355 | | |
356 | 58.7k | if (!(flags & S390_OPERAND_LENGTH)) |
357 | 48.0k | { |
358 | 48.0k | union operand_value insn_opval; |
359 | | |
360 | | /* Mask any constant operand bits set in insn template. */ |
361 | 48.0k | insn_opval = s390_extract_operand (opcode->opcode, operand); |
362 | 48.0k | val.u &= ~insn_opval.u; |
363 | 48.0k | } |
364 | | |
365 | 58.7k | if ((opcode->flags & S390_INSTR_FLAG_OPTPARM) |
366 | 429 | && val.u == 0 |
367 | 103 | && opindex[1] == 0) |
368 | 0 | break; |
369 | | |
370 | 58.7k | info->fprintf_styled_func (info->stream, dis_style_text, |
371 | 58.7k | "%c", separator); |
372 | 58.7k | style = ((flags & S390_OPERAND_DISP) |
373 | 58.7k | ? dis_style_address_offset : dis_style_immediate); |
374 | 58.7k | info->fprintf_styled_func (info->stream, style, "%u", val.u); |
375 | 58.7k | } |
376 | | |
377 | 207k | if (flags & S390_OPERAND_DISP) |
378 | 42.5k | separator = '('; |
379 | 165k | else if (flags & S390_OPERAND_BASE) |
380 | 38.6k | { |
381 | 38.6k | info->fprintf_styled_func (info->stream, dis_style_text, ")"); |
382 | 38.6k | separator = ','; |
383 | 38.6k | } |
384 | 126k | else |
385 | 126k | separator = ','; |
386 | 207k | } |
387 | | |
388 | | /* Optional: instruction name. */ |
389 | 70.6k | if (option_print_insn_desc && opcode->description |
390 | 0 | && opcode->description[0] != '\0') |
391 | 0 | info->fprintf_styled_func (info->stream, dis_style_comment_start, "\t# %s", |
392 | 0 | opcode->description); |
393 | 70.6k | } |
394 | | |
395 | | /* Check whether opcode A's mask is more specific than that of B. */ |
396 | | |
397 | | static int |
398 | | opcode_mask_more_specific (const struct s390_opcode *a, |
399 | | const struct s390_opcode *b) |
400 | 5.06k | { |
401 | 5.06k | return (((int) a->mask[0] + a->mask[1] + a->mask[2] |
402 | 5.06k | + a->mask[3] + a->mask[4] + a->mask[5]) |
403 | 5.06k | > ((int) b->mask[0] + b->mask[1] + b->mask[2] |
404 | 5.06k | + b->mask[3] + b->mask[4] + b->mask[5])); |
405 | 5.06k | } |
406 | | |
407 | | /* Print a S390 instruction. */ |
408 | | |
409 | | int |
410 | | print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info) |
411 | 136k | { |
412 | 136k | bfd_byte buffer[6]; |
413 | 136k | const struct s390_opcode *opcode = NULL; |
414 | 136k | unsigned int value; |
415 | 136k | int status, opsize, bufsize, bytes_to_dump, i; |
416 | | |
417 | | /* The output looks better if we put 6 bytes on a line. */ |
418 | 136k | info->bytes_per_line = 6; |
419 | | |
420 | | /* Set some defaults for the insn info. */ |
421 | 136k | info->insn_info_valid = 0; |
422 | 136k | info->branch_delay_insns = 0; |
423 | 136k | info->data_size = 0; |
424 | 136k | info->insn_type = dis_nonbranch; |
425 | 136k | info->target = 0; |
426 | 136k | info->target2 = 0; |
427 | | |
428 | | /* Every S390 instruction is max 6 bytes long. */ |
429 | 136k | memset (buffer, 0, 6); |
430 | 136k | status = info->read_memory_func (memaddr, buffer, 6, info); |
431 | 136k | if (status != 0) |
432 | 281 | { |
433 | 1.13k | for (bufsize = 0; bufsize < 6; bufsize++) |
434 | 1.13k | if (info->read_memory_func (memaddr, buffer, bufsize + 1, info) != 0) |
435 | 281 | break; |
436 | 281 | if (bufsize <= 0) |
437 | 1 | { |
438 | 1 | info->memory_error_func (status, memaddr, info); |
439 | 1 | return -1; |
440 | 1 | } |
441 | 280 | opsize = s390_insn_length (buffer); |
442 | 280 | status = opsize > bufsize; |
443 | 280 | } |
444 | 136k | else |
445 | 136k | { |
446 | 136k | bufsize = 6; |
447 | 136k | opsize = s390_insn_length (buffer); |
448 | 136k | } |
449 | | |
450 | 136k | if (status == 0) |
451 | 136k | { |
452 | 136k | const struct s390_opcode *op; |
453 | | |
454 | | /* Find the "best match" in the opcode table. */ |
455 | 136k | for (op = s390_opcodes + opc_index[buffer[0]]; |
456 | 1.31M | op != s390_opcodes + s390_num_opcodes |
457 | 1.31M | && op->opcode[0] == buffer[0]; |
458 | 1.17M | op++) |
459 | 1.17M | { |
460 | 1.17M | if ((op->modes & current_arch_mask) |
461 | 1.17M | && s390_insn_matches_opcode (buffer, op) |
462 | 75.7k | && (opcode == NULL |
463 | 5.06k | || opcode_mask_more_specific (op, opcode))) |
464 | 71.8k | opcode = op; |
465 | 1.17M | } |
466 | | |
467 | 136k | if (opcode != NULL) |
468 | 70.6k | { |
469 | | /* Provide info for jump visualization. Must be done before print. */ |
470 | 70.6k | switch (opcode->flags & S390_INSTR_FLAG_CLASS_MASK) |
471 | 70.6k | { |
472 | 73 | case S390_INSTR_FLAGS_CLASS_JUMP: |
473 | 73 | info->insn_type = dis_branch; |
474 | 73 | break; |
475 | 830 | case S390_INSTR_FLAGS_CLASS_CONDJUMP: |
476 | 830 | info->insn_type = dis_condbranch; |
477 | 830 | break; |
478 | 109 | case S390_INSTR_FLAGS_CLASS_JUMPSR: |
479 | 109 | info->insn_type = dis_jsr; |
480 | 109 | break; |
481 | 69.6k | default: |
482 | 69.6k | info->insn_type = dis_nonbranch; |
483 | 70.6k | } |
484 | 70.6k | info->insn_info_valid = 1; |
485 | | |
486 | | /* The instruction is valid. Print it and return its size. */ |
487 | 70.6k | s390_print_insn_with_opcode (memaddr, info, buffer, opcode); |
488 | 70.6k | return opsize; |
489 | 70.6k | } |
490 | 136k | } |
491 | | |
492 | | /* For code sections it makes sense to skip unknown instructions |
493 | | according to their length bits. */ |
494 | 65.9k | if (status == 0 |
495 | 65.8k | && option_use_insn_len_bits_p |
496 | 0 | && info->section != NULL |
497 | 0 | && (info->section->flags & SEC_CODE)) |
498 | 0 | bytes_to_dump = opsize; |
499 | 65.9k | else |
500 | | /* By default unknown instructions are printed as .long's/.short' |
501 | | depending on how many bytes are available. */ |
502 | 65.9k | bytes_to_dump = bufsize >= 4 ? 4 : bufsize; |
503 | | |
504 | 65.9k | if (bytes_to_dump == 0) |
505 | 0 | return 0; |
506 | | |
507 | 65.9k | info->insn_type = dis_noninsn; |
508 | 65.9k | info->insn_info_valid = 1; |
509 | | |
510 | | /* Fall back to hex print. */ |
511 | 65.9k | switch (bytes_to_dump) |
512 | 65.9k | { |
513 | 65.8k | case 4: |
514 | 65.8k | value = (unsigned int) buffer[0]; |
515 | 65.8k | value = (value << 8) + (unsigned int) buffer[1]; |
516 | 65.8k | value = (value << 8) + (unsigned int) buffer[2]; |
517 | 65.8k | value = (value << 8) + (unsigned int) buffer[3]; |
518 | 65.8k | info->fprintf_styled_func (info->stream, dis_style_assembler_directive, |
519 | 65.8k | ".long"); |
520 | 65.8k | info->fprintf_styled_func (info->stream, dis_style_text, |
521 | 65.8k | "\t"); |
522 | 65.8k | info->fprintf_styled_func (info->stream, dis_style_immediate, |
523 | 65.8k | "0x%08x", value); |
524 | 65.8k | return 4; |
525 | 71 | case 2: |
526 | 71 | value = (unsigned int) buffer[0]; |
527 | 71 | value = (value << 8) + (unsigned int) buffer[1]; |
528 | 71 | info->fprintf_styled_func (info->stream, dis_style_assembler_directive, |
529 | 71 | ".short"); |
530 | 71 | info->fprintf_styled_func (info->stream, dis_style_text, |
531 | 71 | "\t"); |
532 | 71 | info->fprintf_styled_func (info->stream, dis_style_immediate, |
533 | 71 | "0x%04x", value); |
534 | 71 | return 2; |
535 | 65 | default: |
536 | 65 | info->fprintf_styled_func (info->stream, dis_style_assembler_directive, |
537 | 65 | ".byte"); |
538 | 65 | info->fprintf_styled_func (info->stream, dis_style_text, |
539 | 65 | "\t"); |
540 | 65 | info->fprintf_styled_func (info->stream, dis_style_immediate, |
541 | 65 | "0x%02x", (unsigned int) buffer[0]); |
542 | 117 | for (i = 1; i < bytes_to_dump; i++) |
543 | 52 | info->fprintf_styled_func (info->stream, dis_style_immediate, |
544 | 52 | "0x%02x", (unsigned int) buffer[i]); |
545 | 65 | return bytes_to_dump; |
546 | 65.9k | } |
547 | 0 | return 0; |
548 | 65.9k | } |
549 | | |
550 | | const disasm_options_and_args_t * |
551 | | disassembler_options_s390 (void) |
552 | 0 | { |
553 | 0 | static disasm_options_and_args_t *opts_and_args; |
554 | |
|
555 | 0 | if (opts_and_args == NULL) |
556 | 0 | { |
557 | 0 | size_t i, num_options = ARRAY_SIZE (options); |
558 | 0 | disasm_options_t *opts; |
559 | |
|
560 | 0 | opts_and_args = XNEW (disasm_options_and_args_t); |
561 | 0 | opts_and_args->args = NULL; |
562 | |
|
563 | 0 | opts = &opts_and_args->options; |
564 | 0 | opts->name = XNEWVEC (const char *, num_options + 1); |
565 | 0 | opts->description = XNEWVEC (const char *, num_options + 1); |
566 | 0 | opts->arg = NULL; |
567 | 0 | for (i = 0; i < num_options; i++) |
568 | 0 | { |
569 | 0 | opts->name[i] = options[i].name; |
570 | 0 | opts->description[i] = _(options[i].description); |
571 | 0 | } |
572 | | /* The array we return must be NULL terminated. */ |
573 | 0 | opts->name[i] = NULL; |
574 | 0 | opts->description[i] = NULL; |
575 | 0 | } |
576 | |
|
577 | 0 | return opts_and_args; |
578 | 0 | } |
579 | | |
580 | | void |
581 | | print_s390_disassembler_options (FILE *stream) |
582 | 0 | { |
583 | 0 | unsigned int i, max_len = 0; |
584 | 0 | fprintf (stream, _("\n\ |
585 | 0 | The following S/390 specific disassembler options are supported for use\n\ |
586 | 0 | with the -M switch (multiple options should be separated by commas):\n")); |
587 | |
|
588 | 0 | for (i = 0; i < ARRAY_SIZE (options); i++) |
589 | 0 | { |
590 | 0 | unsigned int len = strlen (options[i].name); |
591 | 0 | if (max_len < len) |
592 | 0 | max_len = len; |
593 | 0 | } |
594 | |
|
595 | 0 | for (i = 0, max_len++; i < ARRAY_SIZE (options); i++) |
596 | 0 | fprintf (stream, " %s%*c %s\n", |
597 | 0 | options[i].name, |
598 | 0 | (int)(max_len - strlen (options[i].name)), ' ', |
599 | | _(options[i].description)); |
600 | 0 | } |