blob: dfbb6675a989bc0892c5dd1a74c014d6a3dc57d1 [file] [log] [blame]
sewardj2019a972011-03-07 16:04:07 +00001/* -*- mode: C; c-basic-offset: 3; -*- */
2
3/*---------------------------------------------------------------*/
florian79af5752012-09-20 01:22:10 +00004/*--- begin s390_disasm.c ---*/
sewardj2019a972011-03-07 16:04:07 +00005/*---------------------------------------------------------------*/
6
7/*
8 This file is part of Valgrind, a dynamic binary instrumentation
9 framework.
10
florian61f23c12012-08-06 18:33:21 +000011 Copyright IBM Corp. 2010-2012
sewardj2019a972011-03-07 16:04:07 +000012
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 02110-1301, USA.
27
28 The GNU General Public License is contained in the file COPYING.
29*/
30
31/* Contributed by Florian Krohm */
32
33#include <stdarg.h>
34#include "libvex_basictypes.h"
35#include "main_util.h" // vassert
36#include "main_globals.h" // vex_traceflags
florian79af5752012-09-20 01:22:10 +000037#include "s390_disasm.h"
sewardj2019a972011-03-07 16:04:07 +000038
florian76714fd2012-09-13 20:21:42 +000039/* The format that is used to write out a mnemonic. */
40static const HChar s390_mnm_fmt[] = "%-8s";
sewardj2019a972011-03-07 16:04:07 +000041
42
43/* Return the name of a general purpose register for dis-assembly purposes. */
44static const HChar *
45gpr_operand(UInt archreg)
46{
47 static const HChar names[16][5] = {
48 "%r0", "%r1", "%r2", "%r3",
49 "%r4", "%r5", "%r6", "%r7",
50 "%r8", "%r9", "%r10", "%r11",
51 "%r12", "%r13", "%r14", "%r15",
52 };
53
54 vassert(archreg < 16);
55
56 return names[archreg];
57}
58
59
60/* Return the name of a floating point register for dis-assembly purposes. */
61static const HChar *
62fpr_operand(UInt archreg)
63{
64 static const HChar names[16][5] = {
65 "%f0", "%f1", "%f2", "%f3",
66 "%f4", "%f5", "%f6", "%f7",
67 "%f8", "%f9", "%f10", "%f11",
68 "%f12", "%f13", "%f14", "%f15",
69 };
70
71 vassert(archreg < 16);
72
73 return names[archreg];
74}
75
76
77/* Return the name of an access register for dis-assembly purposes. */
78static const HChar *
79ar_operand(UInt archreg)
80{
81 static const HChar names[16][5] = {
82 "%a0", "%a1", "%a2", "%a3",
83 "%a4", "%a5", "%a6", "%a7",
84 "%a8", "%a9", "%a10", "%a11",
85 "%a12", "%a13", "%a14", "%a15",
86 };
87
88 vassert(archreg < 16);
89
90 return names[archreg];
91}
92
93
94/* Build and return the extended mnemonic for the compare and branch
95 opcodes as introduced by z10. See also the opcodes in file
96 opcodes/s390-opc.txt (from binutils) that have a '$' in their name. */
97static const HChar *
98cab_operand(const HChar *base, UInt mask)
99{
100 HChar *to;
101 const HChar *from;
102
103 static HChar buf[10]; /* Maximum is 6 + 2 */
104
florian55085f82012-11-21 00:36:55 +0000105 static const HChar *suffix[] = {
sewardj2019a972011-03-07 16:04:07 +0000106 "", "h", "l", "ne", "e", "nl", "nh", ""
107 };
108
109 /* strcpy(buf, from); */
110 for (from = base, to = buf; *from; ++from, ++to) {
111 *to = *from;
112 }
113 /* strcat(buf, suffix); */
114 for (from = suffix[mask >> 1]; *from; ++from, ++to) {
115 *to = *from;
116 }
117 *to = '\0';
118
119 return buf;
120}
121
sewardjd7bde722011-04-05 13:19:33 +0000122/* Common function used to construct a mnemonic based on a condition code
123 mask. */
124static const HChar *
125construct_mnemonic(const HChar *prefix, const HChar *suffix, UInt mask)
126{
127 HChar *to;
128 const HChar *from;
129
130 static HChar buf[10];
131
132 static HChar mask_id[16][4] = {
133 "", /* 0 -> unused */
134 "o", "h", "nle", "l", "nhe", "lh", "ne",
135 "e", "nlh", "he", "nl", "le", "nh", "no",
136 "" /* 15 -> unused */
137 };
138
139 /* Guard against buffer overflow */
140 vassert(vex_strlen(prefix) + vex_strlen(suffix) + sizeof mask_id[0] <= sizeof buf);
141
142 /* strcpy(buf, prefix); */
143 for (from = prefix, to = buf; *from; ++from, ++to) {
144 *to = *from;
145 }
146 /* strcat(buf, mask_id); */
147 for (from = mask_id[mask]; *from; ++from, ++to) {
148 *to = *from;
149 }
150 /* strcat(buf, suffix); */
151 for (from = suffix; *from; ++from, ++to) {
152 *to = *from;
153 }
154 *to = '\0';
155
156 return buf;
157}
158
sewardj2019a972011-03-07 16:04:07 +0000159
160/* Return the special mnemonic for the BCR opcode */
161static const HChar *
162bcr_operand(UInt m1)
163{
sewardjd7bde722011-04-05 13:19:33 +0000164 if (m1 == 0) return "nopr";
165 if (m1 == 15) return "br";
sewardj2019a972011-03-07 16:04:07 +0000166
sewardjd7bde722011-04-05 13:19:33 +0000167 return construct_mnemonic("b", "r", m1);
sewardj2019a972011-03-07 16:04:07 +0000168}
169
170
171/* Return the special mnemonic for the BC opcode */
172static const HChar *
173bc_operand(UInt m1)
174{
sewardjd7bde722011-04-05 13:19:33 +0000175 if (m1 == 0) return "nop";
176 if (m1 == 15) return "b";
sewardj2019a972011-03-07 16:04:07 +0000177
sewardjd7bde722011-04-05 13:19:33 +0000178 return construct_mnemonic("b", "", m1);
sewardj2019a972011-03-07 16:04:07 +0000179}
180
181
182/* Return the special mnemonic for the BRC opcode */
183static const HChar *
184brc_operand(UInt m1)
185{
sewardjd7bde722011-04-05 13:19:33 +0000186 if (m1 == 0) return "brc";
187 if (m1 == 15) return "j";
sewardj2019a972011-03-07 16:04:07 +0000188
sewardjd7bde722011-04-05 13:19:33 +0000189 return construct_mnemonic("j", "", m1);
sewardj2019a972011-03-07 16:04:07 +0000190}
191
192
193/* Return the special mnemonic for the BRCL opcode */
194static const HChar *
195brcl_operand(UInt m1)
196{
sewardjd7bde722011-04-05 13:19:33 +0000197 if (m1 == 0) return "brcl";
198 if (m1 == 15) return "jg";
sewardj2019a972011-03-07 16:04:07 +0000199
sewardjd7bde722011-04-05 13:19:33 +0000200 return construct_mnemonic("jg", "", m1);
201}
202
203
204/* Return the special mnemonic for a conditional load/store opcode */
205static const HChar *
206cls_operand(Int kind, UInt mask)
207{
florian55085f82012-11-21 00:36:55 +0000208 const HChar *prefix;
sewardjd7bde722011-04-05 13:19:33 +0000209
210 switch (kind) {
211 case S390_XMNM_LOCR: prefix = "locr"; break;
212 case S390_XMNM_LOCGR: prefix = "locgr"; break;
213 case S390_XMNM_LOC: prefix = "loc"; break;
214 case S390_XMNM_LOCG: prefix = "locg"; break;
215 case S390_XMNM_STOC: prefix = "stoc"; break;
216 case S390_XMNM_STOCG: prefix = "stocg"; break;
217 default:
218 vpanic("cls_operand");
219 }
220
221 return construct_mnemonic(prefix, "", mask);
sewardj2019a972011-03-07 16:04:07 +0000222}
223
224
225/* An operand with a base register, an index register, and a displacement.
226 If the displacement is signed, the rightmost 20 bit of D need to be
227 sign extended */
228static HChar *
229dxb_operand(HChar *p, UInt d, UInt x, UInt b, Bool displacement_is_signed)
230{
231 if (displacement_is_signed) {
232 Int displ = ((Int)d << 12) >> 12; /* sign extend */
233
234 p += vex_sprintf(p, "%d", displ);
235 } else {
236 p += vex_sprintf(p, "%u", d);
237 }
238 if (x != 0) {
239 p += vex_sprintf(p, "(%s", gpr_operand(x));
240 if (b != 0) {
241 p += vex_sprintf(p, ",%s", gpr_operand(b));
242 }
243 p += vex_sprintf(p, ")");
244 } else {
245 if (b != 0) {
246 p += vex_sprintf(p, "(%s)", gpr_operand(b));
247 }
248 }
249
250 return p;
251}
252
253
254/* An operand with base register, unsigned length, and a 12-bit
255 unsigned displacement */
256static HChar *
257udlb_operand(HChar *p, UInt d, UInt length, UInt b)
258{
259 p += vex_sprintf(p, "%u", d);
260 p += vex_sprintf(p, "(%u", length + 1); // actual length is +1
261 if (b != 0) {
262 p += vex_sprintf(p, ",%s", gpr_operand(b));
263 }
264 p += vex_sprintf(p, ")");
265
266 return p;
267}
268
269
270/* The first argument is the command that says how to write the disassembled
271 insn. It is understood that the mnemonic comes first and that arguments
272 are separated by a ','. The command holds the arguments. Each argument is
273 encoded using a 4-bit S390_ARG_xyz value. The first argument is placed
274 in the least significant bits of the command and so on. There are at most
275 5 arguments in an insn and a sentinel (S390_ARG_DONE) is needed to identify
276 the end of the argument list. 6 * 4 = 24 bits are required for the
277 command. */
278void
279s390_disasm(UInt command, ...)
280{
281 va_list args;
282 unsigned argkind;
283 HChar buf[128]; /* holds the disassembled insn */
284 HChar *p;
285 HChar separator;
sewardjd7bde722011-04-05 13:19:33 +0000286 Int mask_suffix = -1;
sewardj2019a972011-03-07 16:04:07 +0000287
288 va_start(args, command);
289
290 p = buf;
291 separator = 0;
292
293 while (42) {
294 argkind = command & 0xF;
295 command >>= 4;
296
297 if (argkind == S390_ARG_DONE) goto done;
298
299 if (argkind == S390_ARG_CABM) separator = 0; /* optional */
300
301 /* Write out the separator */
302 if (separator) *p++ = separator;
303
304 /* argument */
305 switch (argkind) {
306 case S390_ARG_MNM:
307 p += vex_sprintf(p, s390_mnm_fmt, va_arg(args, HChar *));
308 separator = ' ';
309 continue;
310
311 case S390_ARG_XMNM: {
312 UInt mask, kind;
313 const HChar *mnm;
314
315 kind = va_arg(args, UInt);
316
317 separator = ' ';
318 switch (kind) {
319 case S390_XMNM_BC:
320 case S390_XMNM_BCR:
321 mask = va_arg(args, UInt);
322 mnm = kind == S390_XMNM_BCR ? bcr_operand(mask) : bc_operand(mask);
323 p += vex_sprintf(p, s390_mnm_fmt, mnm);
324 /* mask == 0 is a NOP and has no argument */
325 if (mask == 0) goto done;
326 break;
327
328 case S390_XMNM_BRC:
329 case S390_XMNM_BRCL:
330 mask = va_arg(args, UInt);
331 mnm = kind == S390_XMNM_BRC ? brc_operand(mask) : brcl_operand(mask);
332 p += vex_sprintf(p, s390_mnm_fmt, mnm);
333
334 /* mask == 0 has no special mnemonic */
335 if (mask == 0) {
336 p += vex_sprintf(p, " 0");
337 separator = ',';
338 }
339 break;
340
341 case S390_XMNM_CAB:
342 mnm = va_arg(args, HChar *);
343 mask = va_arg(args, UInt);
344 p += vex_sprintf(p, s390_mnm_fmt, cab_operand(mnm, mask));
345 break;
sewardjd7bde722011-04-05 13:19:33 +0000346
347 case S390_XMNM_LOCR:
348 case S390_XMNM_LOCGR:
349 case S390_XMNM_LOC:
350 case S390_XMNM_LOCG:
351 case S390_XMNM_STOC:
352 case S390_XMNM_STOCG:
353 mask = va_arg(args, UInt);
354 mnm = cls_operand(kind, mask);
355 p += vex_sprintf(p, s390_mnm_fmt, mnm);
356 /* There are no special opcodes when mask == 0 or 15. In that case
357 the integer mask is appended as the final operand */
358 if (mask == 0 || mask == 15) mask_suffix = mask;
359 break;
sewardj2019a972011-03-07 16:04:07 +0000360 }
361 }
362 continue;
363
364 case S390_ARG_GPR:
365 p += vex_sprintf(p, "%s", gpr_operand(va_arg(args, UInt)));
366 break;
367
368 case S390_ARG_FPR:
369 p += vex_sprintf(p, "%s", fpr_operand(va_arg(args, UInt)));
370 break;
371
372 case S390_ARG_AR:
373 p += vex_sprintf(p, "%s", ar_operand(va_arg(args, UInt)));
374 break;
375
376 case S390_ARG_UINT:
377 p += vex_sprintf(p, "%u", va_arg(args, UInt));
378 break;
379
380 case S390_ARG_INT:
381 p += vex_sprintf(p, "%d", (Int)(va_arg(args, UInt)));
382 break;
383
384 case S390_ARG_PCREL: {
385 Int offset = (Int)(va_arg(args, UInt));
386
387 /* Convert # halfwords to # bytes */
388 offset <<= 1;
389
390 if (offset < 0) {
391 p += vex_sprintf(p, ".%d", offset);
392 } else {
393 p += vex_sprintf(p, ".+%u", offset);
394 }
395 break;
396 }
397
398 case S390_ARG_SDXB: {
399 UInt dh, dl, x, b;
400
401 dh = va_arg(args, UInt);
402 dl = va_arg(args, UInt);
403 x = va_arg(args, UInt);
404 b = va_arg(args, UInt);
405
406 p = dxb_operand(p, (dh << 12) | dl, x, b, 1 /* signed_displacement */);
407 break;
408 }
409
410 case S390_ARG_UDXB: {
411 UInt d, x, b;
412
413 d = va_arg(args, UInt);
414 x = va_arg(args, UInt);
415 b = va_arg(args, UInt);
416
417 p = dxb_operand(p, d, x, b, 0 /* signed_displacement */);
418 break;
419 }
420
421 case S390_ARG_UDLB: {
422 UInt d, l, b;
423
424 d = va_arg(args, UInt);
425 l = va_arg(args, UInt);
426 b = va_arg(args, UInt);
427
428 p = udlb_operand(p, d, l, b);
429 break;
430 }
431
432 case S390_ARG_CABM: {
433 UInt mask;
434
435 mask = va_arg(args, UInt) & 0xE;
436 if (mask == 0 || mask == 14) {
437 p += vex_sprintf(p, ",%u", mask);
438 }
439 break;
440 }
441 }
442
443 separator = ',';
444 }
445
446 done:
447 va_end(args);
448
sewardjd7bde722011-04-05 13:19:33 +0000449 if (mask_suffix != -1)
450 p += vex_sprintf(p, ",%d", mask_suffix);
sewardj2019a972011-03-07 16:04:07 +0000451 *p = '\0';
452
453 vassert(p < buf + sizeof buf); /* detect buffer overwrite */
454
455 /* Finally, write out the disassembled insn */
456 vex_printf("%s\n", buf);
457}
458
459/*---------------------------------------------------------------*/
florian79af5752012-09-20 01:22:10 +0000460/*--- end s390_disasm.c ---*/
sewardj2019a972011-03-07 16:04:07 +0000461/*---------------------------------------------------------------*/