1 /* s390-dis.c -- Disassemble S390 instructions
2 Copyright (C) 2000-2023 Free Software Foundation, Inc.
3 Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
5 This file is part of the GNU opcodes library.
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)
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.
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. */
25 #include "disassemble.h"
27 #include "opcode/s390.h"
28 #include "libiberty.h"
30 static int opc_index
[256];
31 static int current_arch_mask
= 0;
32 static int option_use_insn_len_bits_p
= 0;
37 const char *description
;
40 static const s390_options_t options
[] =
42 { "esa" , N_("Disassemble in ESA architecture mode") },
43 { "zarch", N_("Disassemble in z/Architecture mode") },
44 { "insnlength", N_("Print unknown instructions according to "
45 "length from first two bits") }
48 /* Set up index table for first opcode byte. */
51 disassemble_init_s390 (struct disassemble_info
*info
)
56 memset (opc_index
, 0, sizeof (opc_index
));
58 /* Reverse order, such that each opc_index ends up pointing to the
59 first matching entry instead of the last. */
60 for (i
= s390_num_opcodes
; i
--; )
61 opc_index
[s390_opcodes
[i
].opcode
[0]] = i
;
63 current_arch_mask
= 1 << S390_OPCODE_ZARCH
;
64 option_use_insn_len_bits_p
= 0;
66 for (p
= info
->disassembler_options
; p
!= NULL
; )
68 if (startswith (p
, "esa"))
69 current_arch_mask
= 1 << S390_OPCODE_ESA
;
70 else if (startswith (p
, "zarch"))
71 current_arch_mask
= 1 << S390_OPCODE_ZARCH
;
72 else if (startswith (p
, "insnlength"))
73 option_use_insn_len_bits_p
= 1;
75 /* xgettext:c-format */
76 opcodes_error_handler (_("unknown S/390 disassembler option: %s"), p
);
84 /* Derive the length of an instruction from its first byte. */
87 s390_insn_length (const bfd_byte
*buffer
)
89 /* 00xxxxxx -> 2, 01xxxxxx/10xxxxxx -> 4, 11xxxxxx -> 6. */
90 return ((buffer
[0] >> 6) + 3) & ~1U;
93 /* Match the instruction in BUFFER against the given OPCODE, excluding
97 s390_insn_matches_opcode (const bfd_byte
*buffer
,
98 const struct s390_opcode
*opcode
)
100 return (buffer
[1] & opcode
->mask
[1]) == opcode
->opcode
[1]
101 && (buffer
[2] & opcode
->mask
[2]) == opcode
->opcode
[2]
102 && (buffer
[3] & opcode
->mask
[3]) == opcode
->opcode
[3]
103 && (buffer
[4] & opcode
->mask
[4]) == opcode
->opcode
[4]
104 && (buffer
[5] & opcode
->mask
[5]) == opcode
->opcode
[5];
113 /* Extracts an operand value from an instruction. */
114 /* We do not perform the shift operation for larl-type address
115 operands here since that would lead to an overflow of the 32 bit
116 integer value. Instead the shift operation is done when printing
119 static inline union operand_value
120 s390_extract_operand (const bfd_byte
*insn
,
121 const struct s390_operand
*operand
)
123 union operand_value ret
;
126 const bfd_byte
*orig_insn
= insn
;
128 /* Extract fragments of the operand byte for byte. */
129 insn
+= operand
->shift
/ 8;
130 bits
= (operand
->shift
& 7) + operand
->bits
;
135 val
|= (unsigned int) *insn
++;
140 val
&= ((1U << (operand
->bits
- 1)) << 1) - 1;
142 /* Check for special long displacement case. */
143 if (operand
->bits
== 20 && operand
->shift
== 20)
144 val
= (val
& 0xff) << 12 | (val
& 0xfff00) >> 8;
146 /* Sign extend value if the operand is signed or pc relative. Avoid
147 integer overflows. */
148 if (operand
->flags
& (S390_OPERAND_SIGNED
| S390_OPERAND_PCREL
))
150 unsigned int m
= 1U << (operand
->bits
- 1);
153 ret
.i
= (int) (val
- m
) - 1 - (int) (m
- 1U);
157 else if (operand
->flags
& S390_OPERAND_LENGTH
)
158 /* Length x in an instruction has real length x + 1. */
161 else if (operand
->flags
& S390_OPERAND_VR
)
163 /* Extract the extra bits for a vector register operand stored
165 unsigned vr
= operand
->shift
== 32 ? 3
166 : (unsigned) operand
->shift
/ 4 - 2;
168 ret
.u
= val
| ((orig_insn
[4] & (1 << (3 - vr
))) << (vr
+ 1));
176 /* Print the S390 instruction in BUFFER, assuming that it matches the
180 s390_print_insn_with_opcode (bfd_vma memaddr
,
181 struct disassemble_info
*info
,
182 const bfd_byte
*buffer
,
183 const struct s390_opcode
*opcode
)
185 const unsigned char *opindex
;
189 info
->fprintf_styled_func (info
->stream
, dis_style_mnemonic
,
194 for (opindex
= opcode
->operands
; *opindex
!= 0; opindex
++)
196 const struct s390_operand
*operand
= s390_operands
+ *opindex
;
197 union operand_value val
= s390_extract_operand (buffer
, operand
);
198 unsigned long flags
= operand
->flags
;
200 if ((flags
& S390_OPERAND_INDEX
) && val
.u
== 0)
202 if ((flags
& S390_OPERAND_BASE
) &&
203 val
.u
== 0 && separator
== '(')
209 /* For instructions with a last optional operand don't print it
211 if ((opcode
->flags
& (S390_INSTR_FLAG_OPTPARM
| S390_INSTR_FLAG_OPTPARM2
))
216 if ((opcode
->flags
& S390_INSTR_FLAG_OPTPARM2
)
217 && val
.u
== 0 && opindex
[1] != 0 && opindex
[2] == 0)
219 union operand_value next_op_val
=
220 s390_extract_operand (buffer
, s390_operands
+ opindex
[1]);
221 if (next_op_val
.u
== 0)
225 if (flags
& S390_OPERAND_GPR
)
227 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
229 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
232 else if (flags
& S390_OPERAND_FPR
)
234 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
236 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
239 else if (flags
& S390_OPERAND_VR
)
241 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
243 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
246 else if (flags
& S390_OPERAND_AR
)
248 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
250 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
253 else if (flags
& S390_OPERAND_CR
)
255 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
257 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
260 else if (flags
& S390_OPERAND_PCREL
)
262 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
264 info
->print_address_func (memaddr
+ val
.i
+ val
.i
, info
);
266 else if (flags
& S390_OPERAND_SIGNED
)
268 enum disassembler_style style
;
270 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
272 style
= ((flags
& S390_OPERAND_DISP
)
273 ? dis_style_address_offset
: dis_style_immediate
);
274 info
->fprintf_styled_func (info
->stream
, style
, "%i", val
.i
);
278 enum disassembler_style style
;
280 if (flags
& S390_OPERAND_OR1
)
282 if (flags
& S390_OPERAND_OR2
)
284 if (flags
& S390_OPERAND_OR8
)
287 if ((opcode
->flags
& S390_INSTR_FLAG_OPTPARM
)
291 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
293 style
= ((flags
& S390_OPERAND_DISP
)
294 ? dis_style_address_offset
: dis_style_immediate
);
295 info
->fprintf_styled_func (info
->stream
, style
, "%u", val
.u
);
298 if (flags
& S390_OPERAND_DISP
)
300 else if (flags
& S390_OPERAND_BASE
)
302 info
->fprintf_styled_func (info
->stream
, dis_style_text
, ")");
310 /* Check whether opcode A's mask is more specific than that of B. */
313 opcode_mask_more_specific (const struct s390_opcode
*a
,
314 const struct s390_opcode
*b
)
316 return (((int) a
->mask
[0] + a
->mask
[1] + a
->mask
[2]
317 + a
->mask
[3] + a
->mask
[4] + a
->mask
[5])
318 > ((int) b
->mask
[0] + b
->mask
[1] + b
->mask
[2]
319 + b
->mask
[3] + b
->mask
[4] + b
->mask
[5]));
322 /* Print a S390 instruction. */
325 print_insn_s390 (bfd_vma memaddr
, struct disassemble_info
*info
)
328 const struct s390_opcode
*opcode
= NULL
;
330 int status
, opsize
, bufsize
, bytes_to_dump
, i
;
332 /* The output looks better if we put 6 bytes on a line. */
333 info
->bytes_per_line
= 6;
335 /* Every S390 instruction is max 6 bytes long. */
336 memset (buffer
, 0, 6);
337 status
= info
->read_memory_func (memaddr
, buffer
, 6, info
);
340 for (bufsize
= 0; bufsize
< 6; bufsize
++)
341 if (info
->read_memory_func (memaddr
, buffer
, bufsize
+ 1, info
) != 0)
345 info
->memory_error_func (status
, memaddr
, info
);
348 opsize
= s390_insn_length (buffer
);
349 status
= opsize
> bufsize
;
354 opsize
= s390_insn_length (buffer
);
359 const struct s390_opcode
*op
;
361 /* Find the "best match" in the opcode table. */
362 for (op
= s390_opcodes
+ opc_index
[buffer
[0]];
363 op
!= s390_opcodes
+ s390_num_opcodes
364 && op
->opcode
[0] == buffer
[0];
367 if ((op
->modes
& current_arch_mask
)
368 && s390_insn_matches_opcode (buffer
, op
)
370 || opcode_mask_more_specific (op
, opcode
)))
376 /* The instruction is valid. Print it and return its size. */
377 s390_print_insn_with_opcode (memaddr
, info
, buffer
, opcode
);
382 /* For code sections it makes sense to skip unknown instructions
383 according to their length bits. */
385 && option_use_insn_len_bits_p
386 && info
->section
!= NULL
387 && (info
->section
->flags
& SEC_CODE
))
388 bytes_to_dump
= opsize
;
390 /* By default unknown instructions are printed as .long's/.short'
391 depending on how many bytes are available. */
392 bytes_to_dump
= bufsize
>= 4 ? 4 : bufsize
;
394 if (bytes_to_dump
== 0)
397 /* Fall back to hex print. */
398 switch (bytes_to_dump
)
401 value
= (unsigned int) buffer
[0];
402 value
= (value
<< 8) + (unsigned int) buffer
[1];
403 value
= (value
<< 8) + (unsigned int) buffer
[2];
404 value
= (value
<< 8) + (unsigned int) buffer
[3];
405 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
407 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
409 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
413 value
= (unsigned int) buffer
[0];
414 value
= (value
<< 8) + (unsigned int) buffer
[1];
415 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
417 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
419 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
423 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
425 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
427 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
428 "0x%02x", (unsigned int) buffer
[0]);
429 for (i
= 1; i
< bytes_to_dump
; i
++)
430 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
431 "0x%02x", (unsigned int) buffer
[i
]);
432 return bytes_to_dump
;
437 const disasm_options_and_args_t
*
438 disassembler_options_s390 (void)
440 static disasm_options_and_args_t
*opts_and_args
;
442 if (opts_and_args
== NULL
)
444 size_t i
, num_options
= ARRAY_SIZE (options
);
445 disasm_options_t
*opts
;
447 opts_and_args
= XNEW (disasm_options_and_args_t
);
448 opts_and_args
->args
= NULL
;
450 opts
= &opts_and_args
->options
;
451 opts
->name
= XNEWVEC (const char *, num_options
+ 1);
452 opts
->description
= XNEWVEC (const char *, num_options
+ 1);
454 for (i
= 0; i
< num_options
; i
++)
456 opts
->name
[i
] = options
[i
].name
;
457 opts
->description
[i
] = _(options
[i
].description
);
459 /* The array we return must be NULL terminated. */
460 opts
->name
[i
] = NULL
;
461 opts
->description
[i
] = NULL
;
464 return opts_and_args
;
468 print_s390_disassembler_options (FILE *stream
)
470 unsigned int i
, max_len
= 0;
471 fprintf (stream
, _("\n\
472 The following S/390 specific disassembler options are supported for use\n\
473 with the -M switch (multiple options should be separated by commas):\n"));
475 for (i
= 0; i
< ARRAY_SIZE (options
); i
++)
477 unsigned int len
= strlen (options
[i
].name
);
482 for (i
= 0, max_len
++; i
< ARRAY_SIZE (options
); i
++)
483 fprintf (stream
, " %s%*c %s\n",
485 (int)(max_len
- strlen (options
[i
].name
)), ' ',
486 _(options
[i
].description
));