]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - opcodes/s390-dis.c
Add note to translators not to translate z/Architecture
[thirdparty/binutils-gdb.git] / opcodes / s390-dis.c
1 /* s390-dis.c -- Disassemble S390 instructions
2 Copyright (C) 2000-2024 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 {
57 int i;
58 const char *p;
59
60 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 for (i = s390_num_opcodes; i--; )
65 opc_index[s390_opcodes[i].opcode[0]] = i;
66
67 current_arch_mask = 1 << S390_OPCODE_ZARCH;
68 option_use_insn_len_bits_p = 0;
69 option_print_insn_desc = 0;
70
71 for (p = info->disassembler_options; p != NULL; )
72 {
73 if (startswith (p, "esa"))
74 current_arch_mask = 1 << S390_OPCODE_ESA;
75 else if (startswith (p, "zarch"))
76 current_arch_mask = 1 << S390_OPCODE_ZARCH;
77 else if (startswith (p, "insnlength"))
78 option_use_insn_len_bits_p = 1;
79 else if (startswith (p, "insndesc"))
80 option_print_insn_desc = 1;
81 else
82 /* xgettext:c-format */
83 opcodes_error_handler (_("unknown S/390 disassembler option: %s"), p);
84
85 p = strchr (p, ',');
86 if (p != NULL)
87 p++;
88 }
89 }
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 {
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
103 static inline int
104 s390_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
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 {
130 union operand_value ret;
131 unsigned int val;
132 int bits;
133 const bfd_byte *orig_insn = insn;
134
135 /* Extract fragments of the operand byte for byte. */
136 insn += operand->shift / 8;
137 bits = (operand->shift & 7) + operand->bits;
138 val = 0;
139 do
140 {
141 val <<= 8;
142 val |= (unsigned int) *insn++;
143 bits -= 8;
144 }
145 while (bits > 0);
146 val >>= -bits;
147 val &= ((1U << (operand->bits - 1)) << 1) - 1;
148
149 /* Check for special long displacement case. */
150 if (operand->bits == 20 && operand->shift == 20)
151 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 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;
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 }
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
186 static void
187 s390_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. */
196 info->fprintf_styled_func (info->stream, dis_style_mnemonic,
197 "%s", opcode->name);
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
216 /* For instructions with a last optional operand don't print it
217 if zero. */
218 if ((opcode->flags & (S390_INSTR_FLAG_OPTPARM | S390_INSTR_FLAG_OPTPARM2))
219 && val.u == 0
220 && opindex[1] == 0)
221 break;
222
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
232 if (flags & S390_OPERAND_GPR)
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 }
239 else if (flags & S390_OPERAND_FPR)
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 }
246 else if (flags & S390_OPERAND_VR)
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 }
253 else if (flags & S390_OPERAND_AR)
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 }
260 else if (flags & S390_OPERAND_CR)
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 }
267 else if (flags & S390_OPERAND_PCREL)
268 {
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
274 info->fprintf_styled_func (info->stream, dis_style_text,
275 "%c", separator);
276 info->print_address_func (target, info);
277 }
278 else if (flags & S390_OPERAND_SIGNED)
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 }
288 else
289 {
290 enum disassembler_style style;
291
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;
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);
308 }
309
310 if (flags & S390_OPERAND_DISP)
311 separator = '(';
312 else if (flags & S390_OPERAND_BASE)
313 {
314 info->fprintf_styled_func (info->stream, dis_style_text, ")");
315 separator = ',';
316 }
317 else
318 separator = ',';
319 }
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);
326 }
327
328 /* Check whether opcode A's mask is more specific than that of B. */
329
330 static int
331 opcode_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]));
338 }
339
340 /* Print a S390 instruction. */
341
342 int
343 print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
344 {
345 bfd_byte buffer[6];
346 const struct s390_opcode *opcode = NULL;
347 unsigned int value;
348 int status, opsize, bufsize, bytes_to_dump, i;
349
350 /* The output looks better if we put 6 bytes on a line. */
351 info->bytes_per_line = 6;
352
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
361 /* Every S390 instruction is max 6 bytes long. */
362 memset (buffer, 0, 6);
363 status = info->read_memory_func (memaddr, buffer, 6, info);
364 if (status != 0)
365 {
366 for (bufsize = 0; bufsize < 6; bufsize++)
367 if (info->read_memory_func (memaddr, buffer, bufsize + 1, info) != 0)
368 break;
369 if (bufsize <= 0)
370 {
371 info->memory_error_func (status, memaddr, info);
372 return -1;
373 }
374 opsize = s390_insn_length (buffer);
375 status = opsize > bufsize;
376 }
377 else
378 {
379 bufsize = 6;
380 opsize = s390_insn_length (buffer);
381 }
382
383 if (status == 0)
384 {
385 const struct s390_opcode *op;
386
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++)
392 {
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;
398 }
399
400 if (opcode != NULL)
401 {
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
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 }
423 }
424
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
440 info->insn_type = dis_noninsn;
441 info->insn_info_valid = 1;
442
443 /* Fall back to hex print. */
444 switch (bytes_to_dump)
445 {
446 case 4:
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];
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);
457 return 4;
458 case 2:
459 value = (unsigned int) buffer[0];
460 value = (value << 8) + (unsigned int) buffer[1];
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);
467 return 2;
468 default:
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]);
475 for (i = 1; i < bytes_to_dump; i++)
476 info->fprintf_styled_func (info->stream, dis_style_immediate,
477 "0x%02x", (unsigned int) buffer[i]);
478 return bytes_to_dump;
479 }
480 return 0;
481 }
482
483 const disasm_options_and_args_t *
484 disassembler_options_s390 (void)
485 {
486 static disasm_options_and_args_t *opts_and_args;
487
488 if (opts_and_args == NULL)
489 {
490 size_t i, num_options = ARRAY_SIZE (options);
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;
497 opts->name = XNEWVEC (const char *, num_options + 1);
498 opts->description = XNEWVEC (const char *, num_options + 1);
499 opts->arg = NULL;
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
510 return opts_and_args;
511 }
512
513 void
514 print_s390_disassembler_options (FILE *stream)
515 {
516 unsigned int i, max_len = 0;
517 fprintf (stream, _("\n\
518 The following S/390 specific disassembler options are supported for use\n\
519 with the -M switch (multiple options should be separated by commas):\n"));
520
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));
533 }