]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - opcodes/s390-dis.c
Add note to translators not to translate z/Architecture
[thirdparty/binutils-gdb.git] / opcodes / s390-dis.c
CommitLineData
a85d7ed0 1/* s390-dis.c -- Disassemble S390 instructions
fd67aa11 2 Copyright (C) 2000-2024 Free Software Foundation, Inc.
a85d7ed0
NC
3 Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
4
9b201bb5 5 This file is part of the GNU opcodes library.
a85d7ed0 6
9b201bb5 7 This library is free software; you can redistribute it and/or modify
a85d7ed0 8 it under the terms of the GNU General Public License as published by
9b201bb5
NC
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
a85d7ed0 11
9b201bb5
NC
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.
a85d7ed0
NC
16
17 You should have received a copy of the GNU General Public License
9b201bb5
NC
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. */
a85d7ed0 21
df7b86aa 22#include "sysdep.h"
a85d7ed0
NC
23#include <stdio.h>
24#include "ansidecl.h"
88c1242d 25#include "disassemble.h"
112b7c50 26#include "opintl.h"
a85d7ed0 27#include "opcode/s390.h"
65b48a81 28#include "libiberty.h"
c5306fed 29#include "dis-asm.h"
a85d7ed0 30
a85d7ed0
NC
31static int opc_index[256];
32static int current_arch_mask = 0;
b2cc3f6f 33static int option_use_insn_len_bits_p = 0;
f96fe7f4 34static int option_print_insn_desc = 0;
a85d7ed0 35
65b48a81
PB
36typedef struct
37{
38 const char *name;
39 const char *description;
40} s390_options_t;
41
42static const s390_options_t options[] =
43{
44 { "esa" , N_("Disassemble in ESA architecture mode") },
0d656dfe 45 /* TRANSLATORS: Please do not translate 'z/Architecture' as this is a technical name. */
65b48a81
PB
46 { "zarch", N_("Disassemble in z/Architecture mode") },
47 { "insnlength", N_("Print unknown instructions according to "
f96fe7f4
JR
48 "length from first two bits") },
49 { "insndesc", N_("Print instruction description as comment") },
65b48a81
PB
50};
51
44f2a95d
KH
52/* Set up index table for first opcode byte. */
53
65b48a81
PB
54void
55disassemble_init_s390 (struct disassemble_info *info)
a85d7ed0 56{
9ace48f3 57 int i;
112b7c50 58 const char *p;
a85d7ed0 59
44f2a95d 60 memset (opc_index, 0, sizeof (opc_index));
9ace48f3
AA
61
62 /* Reverse order, such that each opc_index ends up pointing to the
63 first matching entry instead of the last. */
64 for (i = s390_num_opcodes; i--; )
65 opc_index[s390_opcodes[i].opcode[0]] = i;
112b7c50 66
65b48a81
PB
67 current_arch_mask = 1 << S390_OPCODE_ZARCH;
68 option_use_insn_len_bits_p = 0;
f96fe7f4 69 option_print_insn_desc = 0;
65b48a81 70
112b7c50 71 for (p = info->disassembler_options; p != NULL; )
44f2a95d 72 {
08dedd66 73 if (startswith (p, "esa"))
112b7c50 74 current_arch_mask = 1 << S390_OPCODE_ESA;
08dedd66 75 else if (startswith (p, "zarch"))
112b7c50 76 current_arch_mask = 1 << S390_OPCODE_ZARCH;
08dedd66 77 else if (startswith (p, "insnlength"))
b2cc3f6f 78 option_use_insn_len_bits_p = 1;
f96fe7f4
JR
79 else if (startswith (p, "insndesc"))
80 option_print_insn_desc = 1;
112b7c50 81 else
a6743a54
AM
82 /* xgettext:c-format */
83 opcodes_error_handler (_("unknown S/390 disassembler option: %s"), p);
112b7c50
AK
84
85 p = strchr (p, ',');
86 if (p != NULL)
87 p++;
44f2a95d 88 }
a85d7ed0
NC
89}
90
9ace48f3
AA
91/* Derive the length of an instruction from its first byte. */
92
93static inline int
94s390_insn_length (const bfd_byte *buffer)
95{
96 /* 00xxxxxx -> 2, 01xxxxxx/10xxxxxx -> 4, 11xxxxxx -> 6. */
97 return ((buffer[0] >> 6) + 3) & ~1U;
98}
99
100/* Match the instruction in BUFFER against the given OPCODE, excluding
101 the first byte. */
102
103static inline int
104s390_insn_matches_opcode (const bfd_byte *buffer,
105 const struct s390_opcode *opcode)
106{
107 return (buffer[1] & opcode->mask[1]) == opcode->opcode[1]
108 && (buffer[2] & opcode->mask[2]) == opcode->opcode[2]
109 && (buffer[3] & opcode->mask[3]) == opcode->opcode[3]
110 && (buffer[4] & opcode->mask[4]) == opcode->opcode[4]
111 && (buffer[5] & opcode->mask[5]) == opcode->opcode[5];
112}
113
114union operand_value
115{
116 int i;
117 unsigned int u;
118};
119
a85d7ed0 120/* Extracts an operand value from an instruction. */
7330f9c3
AK
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
9ace48f3 124 the operand. */
a85d7ed0 125
9ace48f3
AA
126static inline union operand_value
127s390_extract_operand (const bfd_byte *insn,
128 const struct s390_operand *operand)
a85d7ed0 129{
9ace48f3 130 union operand_value ret;
a85d7ed0
NC
131 unsigned int val;
132 int bits;
1e2e8c52 133 const bfd_byte *orig_insn = insn;
a85d7ed0 134
44f2a95d
KH
135 /* Extract fragments of the operand byte for byte. */
136 insn += operand->shift / 8;
a85d7ed0
NC
137 bits = (operand->shift & 7) + operand->bits;
138 val = 0;
44f2a95d
KH
139 do
140 {
141 val <<= 8;
142 val |= (unsigned int) *insn++;
143 bits -= 8;
144 }
145 while (bits > 0);
a85d7ed0 146 val >>= -bits;
44f2a95d
KH
147 val &= ((1U << (operand->bits - 1)) << 1) - 1;
148
bac02689
MS
149 /* Check for special long displacement case. */
150 if (operand->bits == 20 && operand->shift == 20)
151 val = (val & 0xff) << 12 | (val & 0xfff00) >> 8;
152
9ace48f3
AA
153 /* Sign extend value if the operand is signed or pc relative. Avoid
154 integer overflows. */
155 if (operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL))
156 {
157 unsigned int m = 1U << (operand->bits - 1);
158
159 if (val >= m)
160 ret.i = (int) (val - m) - 1 - (int) (m - 1U);
161 else
162 ret.i = (int) val;
163 }
164 else if (operand->flags & S390_OPERAND_LENGTH)
165 /* Length x in an instruction has real length x + 1. */
166 ret.u = val + 1;
1e2e8c52
AK
167
168 else if (operand->flags & S390_OPERAND_VR)
169 {
170 /* Extract the extra bits for a vector register operand stored
171 in the RXB field. */
172 unsigned vr = operand->shift == 32 ? 3
173 : (unsigned) operand->shift / 4 - 2;
174
175 ret.u = val | ((orig_insn[4] & (1 << (3 - vr))) << (vr + 1));
176 }
9ace48f3
AA
177 else
178 ret.u = val;
179
180 return ret;
181}
182
183/* Print the S390 instruction in BUFFER, assuming that it matches the
184 given OPCODE. */
185
186static void
187s390_print_insn_with_opcode (bfd_vma memaddr,
188 struct disassemble_info *info,
189 const bfd_byte *buffer,
190 const struct s390_opcode *opcode)
191{
192 const unsigned char *opindex;
193 char separator;
194
195 /* Mnemonic. */
ec54dc91
AB
196 info->fprintf_styled_func (info->stream, dis_style_mnemonic,
197 "%s", opcode->name);
9ace48f3
AA
198
199 /* Operands. */
200 separator = '\t';
201 for (opindex = opcode->operands; *opindex != 0; opindex++)
202 {
203 const struct s390_operand *operand = s390_operands + *opindex;
204 union operand_value val = s390_extract_operand (buffer, operand);
205 unsigned long flags = operand->flags;
206
207 if ((flags & S390_OPERAND_INDEX) && val.u == 0)
208 continue;
209 if ((flags & S390_OPERAND_BASE) &&
210 val.u == 0 && separator == '(')
211 {
212 separator = ',';
213 continue;
214 }
215
1e2e8c52
AK
216 /* For instructions with a last optional operand don't print it
217 if zero. */
a09f2586 218 if ((opcode->flags & (S390_INSTR_FLAG_OPTPARM | S390_INSTR_FLAG_OPTPARM2))
1e2e8c52
AK
219 && val.u == 0
220 && opindex[1] == 0)
221 break;
9ace48f3 222
a09f2586
AK
223 if ((opcode->flags & S390_INSTR_FLAG_OPTPARM2)
224 && val.u == 0 && opindex[1] != 0 && opindex[2] == 0)
225 {
226 union operand_value next_op_val =
227 s390_extract_operand (buffer, s390_operands + opindex[1]);
228 if (next_op_val.u == 0)
229 break;
230 }
231
9ace48f3 232 if (flags & S390_OPERAND_GPR)
ec54dc91
AB
233 {
234 info->fprintf_styled_func (info->stream, dis_style_text,
235 "%c", separator);
236 info->fprintf_styled_func (info->stream, dis_style_register,
237 "%%r%u", val.u);
238 }
9ace48f3 239 else if (flags & S390_OPERAND_FPR)
ec54dc91
AB
240 {
241 info->fprintf_styled_func (info->stream, dis_style_text,
242 "%c", separator);
243 info->fprintf_styled_func (info->stream, dis_style_register,
244 "%%f%u", val.u);
245 }
1e2e8c52 246 else if (flags & S390_OPERAND_VR)
ec54dc91
AB
247 {
248 info->fprintf_styled_func (info->stream, dis_style_text,
249 "%c", separator);
250 info->fprintf_styled_func (info->stream, dis_style_register,
251 "%%v%i", val.u);
252 }
9ace48f3 253 else if (flags & S390_OPERAND_AR)
ec54dc91
AB
254 {
255 info->fprintf_styled_func (info->stream, dis_style_text,
256 "%c", separator);
257 info->fprintf_styled_func (info->stream, dis_style_register,
258 "%%a%u", val.u);
259 }
9ace48f3 260 else if (flags & S390_OPERAND_CR)
ec54dc91
AB
261 {
262 info->fprintf_styled_func (info->stream, dis_style_text,
263 "%c", separator);
264 info->fprintf_styled_func (info->stream, dis_style_register,
265 "%%c%u", val.u);
266 }
9ace48f3 267 else if (flags & S390_OPERAND_PCREL)
1e2e8c52 268 {
c5306fed
JR
269 bfd_vma target = memaddr + val.i + val.i;
270
271 /* Provide info for jump visualization. May be evaluated by p_a_f(). */
272 info->target = target;
273
ec54dc91
AB
274 info->fprintf_styled_func (info->stream, dis_style_text,
275 "%c", separator);
c5306fed 276 info->print_address_func (target, info);
1e2e8c52 277 }
9ace48f3 278 else if (flags & S390_OPERAND_SIGNED)
ec54dc91
AB
279 {
280 enum disassembler_style style;
281
282 info->fprintf_styled_func (info->stream, dis_style_text,
283 "%c", separator);
284 style = ((flags & S390_OPERAND_DISP)
285 ? dis_style_address_offset : dis_style_immediate);
286 info->fprintf_styled_func (info->stream, style, "%i", val.i);
287 }
9ace48f3 288 else
1e2e8c52 289 {
ec54dc91
AB
290 enum disassembler_style style;
291
1e2e8c52
AK
292 if (flags & S390_OPERAND_OR1)
293 val.u &= ~1;
294 if (flags & S390_OPERAND_OR2)
295 val.u &= ~2;
296 if (flags & S390_OPERAND_OR8)
297 val.u &= ~8;
298
299 if ((opcode->flags & S390_INSTR_FLAG_OPTPARM)
300 && val.u == 0
301 && opindex[1] == 0)
302 break;
ec54dc91
AB
303 info->fprintf_styled_func (info->stream, dis_style_text,
304 "%c", separator);
305 style = ((flags & S390_OPERAND_DISP)
306 ? dis_style_address_offset : dis_style_immediate);
307 info->fprintf_styled_func (info->stream, style, "%u", val.u);
1e2e8c52 308 }
9ace48f3
AA
309
310 if (flags & S390_OPERAND_DISP)
311 separator = '(';
312 else if (flags & S390_OPERAND_BASE)
313 {
ec54dc91 314 info->fprintf_styled_func (info->stream, dis_style_text, ")");
9ace48f3
AA
315 separator = ',';
316 }
317 else
318 separator = ',';
319 }
f96fe7f4
JR
320
321 /* Optional: instruction name. */
322 if (option_print_insn_desc && opcode->description
323 && opcode->description[0] != '\0')
324 info->fprintf_styled_func (info->stream, dis_style_comment_start, "\t# %s",
325 opcode->description);
9ace48f3
AA
326}
327
328/* Check whether opcode A's mask is more specific than that of B. */
44f2a95d 329
9ace48f3
AA
330static int
331opcode_mask_more_specific (const struct s390_opcode *a,
332 const struct s390_opcode *b)
333{
334 return (((int) a->mask[0] + a->mask[1] + a->mask[2]
335 + a->mask[3] + a->mask[4] + a->mask[5])
336 > ((int) b->mask[0] + b->mask[1] + b->mask[2]
337 + b->mask[3] + b->mask[4] + b->mask[5]));
a85d7ed0
NC
338}
339
340/* Print a S390 instruction. */
341
342int
47b0e7ad 343print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
a85d7ed0
NC
344{
345 bfd_byte buffer[6];
9ace48f3 346 const struct s390_opcode *opcode = NULL;
a85d7ed0 347 unsigned int value;
b2cc3f6f 348 int status, opsize, bufsize, bytes_to_dump, i;
a85d7ed0 349
a85d7ed0
NC
350 /* The output looks better if we put 6 bytes on a line. */
351 info->bytes_per_line = 6;
352
c5306fed
JR
353 /* Set some defaults for the insn info. */
354 info->insn_info_valid = 0;
355 info->branch_delay_insns = 0;
356 info->data_size = 0;
357 info->insn_type = dis_nonbranch;
358 info->target = 0;
359 info->target2 = 0;
360
a85d7ed0 361 /* Every S390 instruction is max 6 bytes long. */
44f2a95d 362 memset (buffer, 0, 6);
9ace48f3 363 status = info->read_memory_func (memaddr, buffer, 6, info);
44f2a95d
KH
364 if (status != 0)
365 {
366 for (bufsize = 0; bufsize < 6; bufsize++)
9ace48f3 367 if (info->read_memory_func (memaddr, buffer, bufsize + 1, info) != 0)
44f2a95d
KH
368 break;
369 if (bufsize <= 0)
370 {
9ace48f3 371 info->memory_error_func (status, memaddr, info);
44f2a95d
KH
372 return -1;
373 }
9ace48f3 374 opsize = s390_insn_length (buffer);
44f2a95d
KH
375 status = opsize > bufsize;
376 }
377 else
378 {
379 bufsize = 6;
9ace48f3 380 opsize = s390_insn_length (buffer);
44f2a95d
KH
381 }
382
383 if (status == 0)
384 {
02cbf767
AK
385 const struct s390_opcode *op;
386
9ace48f3
AA
387 /* Find the "best match" in the opcode table. */
388 for (op = s390_opcodes + opc_index[buffer[0]];
389 op != s390_opcodes + s390_num_opcodes
390 && op->opcode[0] == buffer[0];
391 op++)
44f2a95d 392 {
9ace48f3
AA
393 if ((op->modes & current_arch_mask)
394 && s390_insn_matches_opcode (buffer, op)
395 && (opcode == NULL
396 || opcode_mask_more_specific (op, opcode)))
397 opcode = op;
44f2a95d 398 }
44f2a95d 399
b2cc3f6f
AK
400 if (opcode != NULL)
401 {
c5306fed
JR
402 /* Provide info for jump visualization. Must be done before print. */
403 switch (opcode->flags & S390_INSTR_FLAG_CLASS_MASK)
404 {
405 case S390_INSTR_FLAGS_CLASS_JUMP:
406 info->insn_type = dis_branch;
407 break;
408 case S390_INSTR_FLAGS_CLASS_CONDJUMP:
409 info->insn_type = dis_condbranch;
410 break;
411 case S390_INSTR_FLAGS_CLASS_JUMPSR:
412 info->insn_type = dis_jsr;
413 break;
414 default:
415 info->insn_type = dis_nonbranch;
416 }
417 info->insn_info_valid = 1;
418
b2cc3f6f
AK
419 /* The instruction is valid. Print it and return its size. */
420 s390_print_insn_with_opcode (memaddr, info, buffer, opcode);
421 return opsize;
422 }
9ace48f3
AA
423 }
424
b2cc3f6f
AK
425 /* For code sections it makes sense to skip unknown instructions
426 according to their length bits. */
427 if (status == 0
428 && option_use_insn_len_bits_p
429 && info->section != NULL
430 && (info->section->flags & SEC_CODE))
431 bytes_to_dump = opsize;
432 else
433 /* By default unknown instructions are printed as .long's/.short'
434 depending on how many bytes are available. */
435 bytes_to_dump = bufsize >= 4 ? 4 : bufsize;
436
437 if (bytes_to_dump == 0)
438 return 0;
439
c5306fed
JR
440 info->insn_type = dis_noninsn;
441 info->insn_info_valid = 1;
442
9ace48f3 443 /* Fall back to hex print. */
b2cc3f6f 444 switch (bytes_to_dump)
44f2a95d 445 {
b2cc3f6f 446 case 4:
44f2a95d
KH
447 value = (unsigned int) buffer[0];
448 value = (value << 8) + (unsigned int) buffer[1];
449 value = (value << 8) + (unsigned int) buffer[2];
450 value = (value << 8) + (unsigned int) buffer[3];
ec54dc91
AB
451 info->fprintf_styled_func (info->stream, dis_style_assembler_directive,
452 ".long");
453 info->fprintf_styled_func (info->stream, dis_style_text,
454 "\t");
455 info->fprintf_styled_func (info->stream, dis_style_immediate,
456 "0x%08x", value);
44f2a95d 457 return 4;
b2cc3f6f 458 case 2:
44f2a95d
KH
459 value = (unsigned int) buffer[0];
460 value = (value << 8) + (unsigned int) buffer[1];
ec54dc91
AB
461 info->fprintf_styled_func (info->stream, dis_style_assembler_directive,
462 ".short");
463 info->fprintf_styled_func (info->stream, dis_style_text,
464 "\t");
465 info->fprintf_styled_func (info->stream, dis_style_immediate,
466 "0x%04x", value);
44f2a95d 467 return 2;
b2cc3f6f 468 default:
ec54dc91
AB
469 info->fprintf_styled_func (info->stream, dis_style_assembler_directive,
470 ".byte");
471 info->fprintf_styled_func (info->stream, dis_style_text,
472 "\t");
473 info->fprintf_styled_func (info->stream, dis_style_immediate,
474 "0x%02x", (unsigned int) buffer[0]);
b2cc3f6f 475 for (i = 1; i < bytes_to_dump; i++)
ec54dc91
AB
476 info->fprintf_styled_func (info->stream, dis_style_immediate,
477 "0x%02x", (unsigned int) buffer[i]);
b2cc3f6f 478 return bytes_to_dump;
a85d7ed0 479 }
b2cc3f6f 480 return 0;
a85d7ed0 481}
112b7c50 482
471b9d15 483const disasm_options_and_args_t *
65b48a81
PB
484disassembler_options_s390 (void)
485{
471b9d15 486 static disasm_options_and_args_t *opts_and_args;
65b48a81 487
471b9d15 488 if (opts_and_args == NULL)
65b48a81
PB
489 {
490 size_t i, num_options = ARRAY_SIZE (options);
471b9d15
MR
491 disasm_options_t *opts;
492
493 opts_and_args = XNEW (disasm_options_and_args_t);
494 opts_and_args->args = NULL;
495
496 opts = &opts_and_args->options;
65b48a81
PB
497 opts->name = XNEWVEC (const char *, num_options + 1);
498 opts->description = XNEWVEC (const char *, num_options + 1);
471b9d15 499 opts->arg = NULL;
65b48a81
PB
500 for (i = 0; i < num_options; i++)
501 {
502 opts->name[i] = options[i].name;
503 opts->description[i] = _(options[i].description);
504 }
505 /* The array we return must be NULL terminated. */
506 opts->name[i] = NULL;
507 opts->description[i] = NULL;
508 }
509
471b9d15 510 return opts_and_args;
65b48a81
PB
511}
512
112b7c50
AK
513void
514print_s390_disassembler_options (FILE *stream)
515{
65b48a81 516 unsigned int i, max_len = 0;
112b7c50
AK
517 fprintf (stream, _("\n\
518The following S/390 specific disassembler options are supported for use\n\
519with the -M switch (multiple options should be separated by commas):\n"));
520
65b48a81
PB
521 for (i = 0; i < ARRAY_SIZE (options); i++)
522 {
523 unsigned int len = strlen (options[i].name);
524 if (max_len < len)
525 max_len = len;
526 }
527
528 for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
529 fprintf (stream, " %s%*c %s\n",
530 options[i].name,
531 (int)(max_len - strlen (options[i].name)), ' ',
532 _(options[i].description));
112b7c50 533}