Coverage Report

Created: 2026-08-31 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/capstonenext/arch/M68K/M68KInstPrinter.c
Line
Count
Source
1
/* Capstone Disassembly Engine */
2
/* M68K Backend by Daniel Collin <daniel@collin.com> 2015-2016 */
3
4
#include <stdio.h>
5
#include <stdlib.h>
6
#include <string.h>
7
8
#include "M68KInstPrinter.h"
9
10
#include "M68KDisassembler.h"
11
12
#include "../../Mapping.h"
13
#include "../../cs_priv.h"
14
#include "../../utils.h"
15
16
#include "../../MCInst.h"
17
#include "../../MCInstrDesc.h"
18
#include "../../MCRegisterInfo.h"
19
#include "../../MathExtras.h"
20
21
#ifndef CAPSTONE_DIET
22
static const char s_spacing[] = " ";
23
24
static const char *const s_reg_names[] = {
25
  "invalid",  "d0", "d1",  "d2",   "d3",   "d4",   "d5",   "d6",
26
  "d7",     "a0", "a1",  "a2",   "a3",   "a4",   "a5",   "a6",
27
  "a7",     "fp0",  "fp1",   "fp2",  "fp3",  "fp4",  "fp5",  "fp6",
28
  "fp7",      "pc", "sr",  "ccr",  "sfc",  "dfc",  "usp",  "vbr",
29
  "cacr",     "caar", "msp",   "isp",  "tc",   "itt0", "itt1", "dtt0",
30
  "dtt1",     "mmusr",  "urp",   "srp",
31
32
  "fpcr",     "fpsr", "fpiar",
33
34
  "tt0",      "tt1",  "crp",   "acc",  "acc0", "acc1", "acc2", "acc3",
35
  "accext01", "accext23", "macsr", "mask",
36
};
37
38
static const char *const s_instruction_names[] = {
39
  "invalid",   "abcd",   "add",      "adda",    "addi",
40
  "addq",      "addx",   "and",      "andi",    "asl",
41
  "asr",       "bhs",  "blo",      "bhi",   "bls",
42
  "bcc",       "bcs",  "bne",      "beq",   "bvc",
43
  "bvs",       "bpl",  "bmi",      "bge",   "blt",
44
  "bgt",       "ble",  "bra",      "bsr",   "bchg",
45
  "bclr",      "bset",   "btst",     "bitrev",    "byterev",
46
  "bfchg",     "bfclr",  "bfexts",   "bfextu",    "bfffo",
47
  "bfins",     "bfset",  "bftst",    "bkpt",    "callm",
48
  "cas",       "cas2",   "chk",      "chk2",    "clr",
49
  "cmp",       "cmpa",   "cmpi",     "cmpm",    "cmp2",
50
  "cinvl",     "cinvp",  "cinva",    "cpushl",    "cpushp",
51
  "cpusha",    "dbt",  "dbf",      "dbhi",    "dbls",
52
  "dbcc",      "dbcs",   "dbne",     "dbeq",    "dbvc",
53
  "dbvs",      "dbpl",   "dbmi",     "dbge",    "dblt",
54
  "dbgt",      "dble",   "dbra",     "divs",    "divsl",
55
  "divu",      "divul",  "eor",      "eori",    "exg",
56
  "ext",       "extb",   "ff1",      "fabs",    "fsabs",
57
  "fdabs",     "facos",  "fadd",     "fsadd",   "fdadd",
58
  "fasin",     "fatan",  "fatanh",   "fbf",   "fbeq",
59
  "fbogt",     "fboge",  "fbolt",    "fbole",   "fbogl",
60
  "fbor",      "fbun",   "fbueq",    "fbugt",   "fbuge",
61
  "fbult",     "fbule",  "fbne",     "fbt",   "fbsf",
62
  "fbseq",     "fbgt",   "fbge",     "fblt",    "fble",
63
  "fbgl",      "fbgle",  "fbngle",   "fbngl",   "fbnle",
64
  "fbnlt",     "fbnge",  "fbngt",    "fbsne",   "fbst",
65
  "fcmp",      "fcos",   "fcosh",    "fdbf",    "fdbeq",
66
  "fdbogt",    "fdboge",   "fdbolt",   "fdbole",    "fdbogl",
67
  "fdbor",     "fdbun",  "fdbueq",   "fdbugt",    "fdbuge",
68
  "fdbult",    "fdbule",   "fdbne",    "fdbt",    "fdbsf",
69
  "fdbseq",    "fdbgt",  "fdbge",    "fdblt",   "fdble",
70
  "fdbgl",     "fdbgle",   "fdbngle",  "fdbngl",    "fdbnle",
71
  "fdbnlt",    "fdbnge",   "fdbngt",   "fdbsne",    "fdbst",
72
  "fdiv",      "fsdiv",  "fddiv",    "fetox",   "fetoxm1",
73
  "fgetexp",   "fgetman",  "fint",     "fintrz",    "flog10",
74
  "flog2",     "flogn",  "flognp1",  "fmod",    "fmove",
75
  "fsmove",    "fdmove",   "fmovecr",  "fmovem",    "fmul",
76
  "fsmul",     "fdmul",  "fneg",     "fsneg",   "fdneg",
77
  "fnop",      "frem",   "frestore", "fsave",   "fscale",
78
  "fsgldiv",   "fsglmul",  "fsin",     "fsincos",   "fsinh",
79
  "fsqrt",     "fssqrt",   "fdsqrt",   "fsf",   "fseq",
80
  "fsogt",     "fsoge",  "fsolt",    "fsole",   "fsogl",
81
  "fsor",      "fsun",   "fsueq",    "fsugt",   "fsuge",
82
  "fsult",     "fsule",  "fsne",     "fst",   "fssf",
83
  "fsseq",     "fsgt",   "fsge",     "fslt",    "fsle",
84
  "fsgl",      "fsgle",  "fsngle",   "fsngl",   "fsnle",
85
  "fsnlt",     "fsnge",  "fsngt",    "fssne",   "fsst",
86
  "fsub",      "fssub",  "fdsub",    "ftan",    "ftanh",
87
  "ftentox",   "ftrapf",   "ftrapeq",  "ftrapogt",  "ftrapoge",
88
  "ftrapolt",  "ftrapole", "ftrapogl", "ftrapor",   "ftrapun",
89
  "ftrapueq",  "ftrapugt", "ftrapuge", "ftrapult",  "ftrapule",
90
  "ftrapne",   "ftrapt",   "ftrapsf",  "ftrapseq",  "ftrapgt",
91
  "ftrapge",   "ftraplt",  "ftraple",  "ftrapgl",   "ftrapgle",
92
  "ftrapngle", "ftrapngl", "ftrapnle", "ftrapnlt",  "ftrapnge",
93
  "ftrapngt",  "ftrapsne", "ftrapst",  "ftst",    "ftwotox",
94
  "halt",      "illegal",  "intouch",  "jmp",   "jsr",
95
  "lea",       "link",   "lpstop",   "lsl",   "lsr",
96
  "mac",       "move",   "movea",    "movec",   "movem",
97
  "movep",     "moveq",  "moves",    "move16",    "mov3q",
98
  "movclr",    "msac",   "muls",     "mulu",    "mvs",
99
  "mvz",       "nbcd",   "neg",      "negx",    "nop",
100
  "not",       "or",   "ori",      "pack",    "pea",
101
  "pflush",    "pflusha",  "pflushan", "pflushn",   "ploadr",
102
  "ploadw",    "plpar",  "plpaw",    "pmove",   "pmovefd",
103
  "ptestr",    "ptestw",   "pulse",    "rems",    "remu",
104
  "reset",     "rol",  "ror",      "roxl",    "roxr",
105
  "rtd",       "rte",  "rtm",      "rtr",   "rts",
106
  "sats",      "sbcd",   "st",       "sf",    "shi",
107
  "sls",       "scc",  "shs",      "scs",   "slo",
108
  "sne",       "seq",  "svc",      "svs",   "spl",
109
  "smi",       "sge",  "slt",      "sgt",   "sle",
110
  "stop",      "strldsr",  "sub",      "suba",    "subi",
111
  "subq",      "subx",   "swap",     "tas",   "trap",
112
  "trapv",     "trapt",  "trapf",    "traphi",    "trapls",
113
  "trapcc",    "traphs",   "trapcs",   "traplo",    "trapne",
114
  "trapeq",    "trapvc",   "trapvs",   "trappl",    "trapmi",
115
  "trapge",    "traplt",   "trapgt",   "traple",    "tst",
116
  "unlk",      "unpk",   "wddata",   "wdebug",    "bgnd",
117
  "tbls",      "tblu",   "tblsn",    "tblun",   "cp0bcbusy",
118
  "cp0ld",     "cp0nop",   "cp0st",    "cp1bcbusy", "cp1ld",
119
  "cp1nop",    "cp1st",  "tpf",      "maaac",   "masac",
120
  "msaac",     "mssac",
121
};
122
#endif
123
124
#ifndef CAPSTONE_DIET
125
static const char *getRegName(m68k_reg reg)
126
328k
{
127
328k
  return s_reg_names[(int)reg];
128
328k
}
129
130
static void printRegbits(SStream *O, bool *need_sep, uint32_t data,
131
       const char *prefix)
132
14.3k
{
133
14.3k
  unsigned int first;
134
14.3k
  int i;
135
136
112k
  for (i = 0; i < 8; ++i) {
137
97.9k
    if (!(data & (1 << i)))
138
82.9k
      continue;
139
140
15.0k
    first = i;
141
32.0k
    while (i < 7 && (data & (1 << (i + 1))))
142
17.0k
      i++;
143
144
15.0k
    if (*need_sep)
145
10.2k
      SStream_concat1(O, '/');
146
15.0k
    *need_sep = true;
147
148
15.0k
    SStream_concat(O, "%s%" PRIu32, prefix, first);
149
150
15.0k
    if ((unsigned int)i > first)
151
6.93k
      SStream_concat(O, "-%s%" PRIu32, prefix,
152
6.93k
               (unsigned int)i);
153
15.0k
  }
154
14.3k
}
155
156
static void registerBits(SStream *O, const cs_m68k_op *op)
157
10.2k
{
158
10.2k
  unsigned int data = op->register_bits;
159
10.2k
  bool need_sep = false;
160
161
10.2k
  if (!data) {
162
1.00k
    SStream_concat(O, "%s", "#$0");
163
1.00k
    return;
164
1.00k
  }
165
166
9.26k
  printRegbits(O, &need_sep, data & 0xff, "d");
167
9.26k
  printRegbits(O, &need_sep, (data >> 8) & 0xff, "a");
168
9.26k
  printRegbits(O, &need_sep, (data >> 16) & 0xff, "fp");
169
9.26k
}
170
171
static void registerPair(SStream *O, const cs_m68k_op *op)
172
5.93k
{
173
5.93k
  SStream_concat(O, "%s:%s", s_reg_names[op->reg_pair.reg_0],
174
5.93k
           s_reg_names[op->reg_pair.reg_1]);
175
5.93k
}
176
177
static void printRegisterName(SStream *O, const cs_m68k_op *op)
178
251k
{
179
251k
  SStream_concat(O, "%s", getRegName(op->reg));
180
251k
  if (op->flags & M68K_OP_FLAG_REG_LOWER)
181
0
    SStream_concat0(O, "l");
182
251k
  else if (op->flags & M68K_OP_FLAG_REG_UPPER)
183
0
    SStream_concat0(O, "u");
184
251k
}
185
186
static void printScaleFactor(SStream *O, uint8_t scale, int threshold)
187
27.4k
{
188
27.4k
  if (scale > threshold)
189
14.8k
    SStream_concat(O, "%s*%s%" PRId8, s_spacing, s_spacing, scale);
190
27.4k
}
191
192
static void printIndexReg(SStream *O, const cs_m68k_op *op)
193
23.8k
{
194
23.8k
  SStream_concat(O, "%s.%c", getRegName(op->mem.index_reg),
195
23.8k
           op->mem.index_size ? 'l' : 'w');
196
23.8k
}
197
198
static void printBitfield(SStream *O, const cs_m68k_op *op)
199
578k
{
200
578k
  if (!op->mem.bitfield)
201
575k
    return;
202
2.23k
  SStream_concat0(O, "{");
203
2.23k
  if (M68K_BF_IS_REG(op->mem.offset))
204
1.31k
    SStream_concat(O, "d%" PRId8, M68K_BF_REG_NUM(op->mem.offset));
205
921
  else
206
921
    SStream_concat(O, "%" PRId8, op->mem.offset);
207
2.23k
  SStream_concat0(O, ":");
208
2.23k
  if (M68K_BF_IS_REG(op->mem.width))
209
885
    SStream_concat(O, "d%" PRId8, M68K_BF_REG_NUM(op->mem.width));
210
1.35k
  else
211
1.35k
    SStream_concat(O, "%" PRId8, op->mem.width);
212
2.23k
  SStream_concat0(O, "}");
213
2.23k
}
214
215
static bool packed_bcd_to_string(uint64_t packed, char digits[17])
216
0
{
217
0
  unsigned int i;
218
219
0
  for (i = 0; i < 16; ++i) {
220
0
    unsigned int digit =
221
0
      (unsigned int)((packed >> ((15 - i) * 4)) & 0xf);
222
0
    if (digit > 9)
223
0
      return false;
224
0
    digits[i] = (char)('0' + digit);
225
0
  }
226
0
  digits[16] = '\0';
227
0
  return true;
228
0
}
229
230
static bool packed_exponent_to_string(uint16_t packed, char digits[4])
231
0
{
232
0
  unsigned int i;
233
234
0
  for (i = 0; i < 3; ++i) {
235
0
    unsigned int digit =
236
0
      (unsigned int)((packed >> ((2 - i) * 4)) & 0xf);
237
0
    if (digit > 9)
238
0
      return false;
239
0
    digits[i] = (char)('0' + digit);
240
0
  }
241
0
  digits[3] = '\0';
242
0
  return true;
243
0
}
244
245
static void printPackedImmediate(SStream *O, const m68k_op_fp_packed *value)
246
0
{
247
0
  const uint32_t header = value->header;
248
0
  const bool negative = (header & 0x80000000U) != 0;
249
0
  const bool negative_exponent = (header & 0x40000000U) != 0;
250
0
  const uint16_t exponent = (uint16_t)((header >> 16) & 0x0fff);
251
0
  const unsigned int integer_digit = header & 0xf;
252
0
  char exponent_digits[4];
253
0
  char fraction_digits[17];
254
255
  /* SE=1, y=3, and exponent=FFF encode infinity or NaN. */
256
0
  if (((header >> 16) & 0x7fff) == 0x7fff) {
257
0
    SStream_concat(O, "#0e%s%s", negative ? "-" : "",
258
0
             value->fraction == 0 ? "inf" : "nan");
259
0
    return;
260
0
  }
261
262
0
  if (integer_digit == 0 && value->fraction == 0) {
263
0
    SStream_concat(O, "#0e%s0", negative ? "-" : "");
264
0
    return;
265
0
  }
266
267
0
  if (integer_digit > 9 ||
268
0
      !packed_exponent_to_string(exponent, exponent_digits) ||
269
0
      !packed_bcd_to_string(value->fraction, fraction_digits)) {
270
0
    SStream_concat0(O, "#<invalid-packed>");
271
0
    return;
272
0
  }
273
274
0
  SStream_concat(O, "#0e%s%u.%se%c%s", negative ? "-" : "", integer_digit,
275
0
           fraction_digits, negative_exponent ? '-' : '+',
276
0
           exponent_digits);
277
0
}
278
279
/* Issue #681: Windows kernel printf-style functions do not support
280
 * floating-point format specifiers, so the text conversion is unused there. */
281
#if !defined(_KERNEL_MODE)
282
/* Round `value >> shift` to nearest, ties to even.  Requires 1 <= shift <= 64. */
283
static uint64_t round_right_to_even(uint64_t value, unsigned int shift)
284
0
{
285
0
  if (shift < 64) {
286
0
    uint64_t quotient = value >> shift;
287
0
    uint64_t remainder = value & ((1ULL << shift) - 1);
288
0
    uint64_t halfway = 1ULL << (shift - 1);
289
0
    if (remainder > halfway ||
290
0
        (remainder == halfway && (quotient & 1)))
291
0
      return quotient + 1;
292
0
    return quotient;
293
0
  }
294
0
  if (value > 0x8000000000000000ULL)
295
0
    return 1;
296
0
  return 0;
297
0
}
298
299
/* Convert only for assembly-text rendering.  The detail operand retains the
300
 * complete external representation in fp_extended. */
301
static double extended_to_double(const m68k_op_fp_extended *value)
302
26
{
303
26
  const uint16_t sign_exp = value->sign_exp;
304
26
  const uint64_t significand = value->significand;
305
26
  const uint64_t sign = ((uint64_t)(sign_exp & 0x8000)) << 48;
306
26
  const unsigned int E = sign_exp & 0x7fff;
307
308
26
  if (E == 0x7fff) {
309
0
    uint64_t fraction = significand & 0x7fffffffffffffffULL;
310
0
    if (fraction == 0)
311
0
      return BitsToDouble(sign | 0x7ff0000000000000ULL);
312
0
    return BitsToDouble(sign | 0x7ff0000000000000ULL |
313
0
            ((fraction >> 11) | 0x0008000000000000ULL));
314
0
  }
315
316
26
  if (E == 0 || significand == 0)
317
0
    return BitsToDouble(sign);
318
319
26
  {
320
26
    unsigned int leading = CountLeadingZeros_64(significand);
321
26
    uint64_t normalized = significand << leading;
322
26
    int64_t e = (int64_t)E - 16383 - (int64_t)leading;
323
324
26
    if (e > 1023)
325
26
      return BitsToDouble(sign | 0x7ff0000000000000ULL);
326
327
0
    if (e >= -1022) {
328
0
      uint64_t rounded = round_right_to_even(normalized, 11);
329
0
      if (rounded == (1ULL << 53)) {
330
0
        rounded >>= 1;
331
0
        ++e;
332
0
        if (e > 1023)
333
0
          return BitsToDouble(
334
0
            sign | 0x7ff0000000000000ULL);
335
0
      }
336
0
      return BitsToDouble(sign |
337
0
              ((uint64_t)(e + 1023) << 52) |
338
0
              (rounded & 0x000fffffffffffffULL));
339
0
    }
340
341
0
    {
342
0
      uint64_t shift = (uint64_t)(-e - 1011);
343
0
      uint64_t fraction =
344
0
        shift > 64 ? 0 :
345
0
               round_right_to_even(
346
0
                 normalized,
347
0
                 (unsigned int)shift);
348
0
      return BitsToDouble(sign | fraction);
349
0
    }
350
0
  }
351
0
}
352
#endif
353
354
static void printImmediate(SStream *O, const cs_m68k *inst,
355
         const cs_m68k_op *op)
356
88.1k
{
357
88.1k
  if (inst->op_size.type == M68K_SIZE_TYPE_FPU) {
358
275
    if (op->type == M68K_OP_FP_PACKED) {
359
0
      printPackedImmediate(O, &op->fp_packed);
360
0
      return;
361
0
    }
362
#if defined(_KERNEL_MODE)
363
    SStream_concat(O, "#<float_point_unsupported>");
364
    return;
365
#else
366
    /* Dispatch on the operand storage type.  Extended immediates
367
     * are converted only for GNU-compatible assembly text. */
368
275
    if (op->type == M68K_OP_FP_SINGLE)
369
103
      SStream_concat(O, "#%f", op->simm);
370
172
    else if (op->type == M68K_OP_FP_DOUBLE)
371
91
      SStream_concat(O, "#%f", op->dimm);
372
81
    else if (op->type == M68K_OP_FP_EXTENDED)
373
26
      SStream_concat(O, "#0e%g",
374
26
               extended_to_double(&op->fp_extended));
375
55
    else
376
55
      SStream_concat(O, "#<unsupported>");
377
275
    return;
378
275
#endif
379
275
  }
380
87.8k
  SStream_concat(O, "#$%" PRIx64, op->imm);
381
87.8k
}
382
383
static void printIndex8BitDisp(SStream *O, uint32_t pc, const cs_m68k_op *op)
384
16.8k
{
385
16.8k
  if (op->address_mode == M68K_AM_PCI_INDEX_8_BIT_DISP) {
386
1.86k
    SStream_concat(O, "$%" PRIx32 "(pc,%s", pc + 2 + op->mem.disp,
387
1.86k
             s_spacing);
388
14.9k
  } else {
389
14.9k
    SStream_concat(O, "%s$%" PRIx16 "(%s,%s",
390
14.9k
             op->mem.disp < 0 ? "-" : "", abs(op->mem.disp),
391
14.9k
             getRegName(op->mem.base_reg), s_spacing);
392
14.9k
  }
393
16.8k
  printIndexReg(O, op);
394
16.8k
  printScaleFactor(O, op->mem.scale, 1);
395
16.8k
  SStream_concat0(O, ")");
396
16.8k
}
397
398
static void printRegAddrMode(SStream *O, uint32_t pc, const cs_m68k_op *op)
399
383k
{
400
383k
  m68k_reg base_reg = op->type == M68K_OP_MEM ? op->mem.base_reg :
401
383k
                  op->reg;
402
403
383k
  switch (op->address_mode) {
404
197k
  case M68K_AM_REG_DIRECT_DATA:
405
197k
    printRegisterName(O, op);
406
197k
    break;
407
26.2k
  case M68K_AM_REG_DIRECT_ADDR:
408
26.2k
    printRegisterName(O, op);
409
26.2k
    break;
410
33.2k
  case M68K_AM_REGI_ADDR:
411
33.2k
    SStream_concat(O, "(a%" PRId32 ")", (base_reg - M68K_REG_A0));
412
33.2k
    break;
413
38.8k
  case M68K_AM_REGI_ADDR_POST_INC:
414
38.8k
    SStream_concat(O, "(a%" PRId32 ")+", (base_reg - M68K_REG_A0));
415
38.8k
    break;
416
58.4k
  case M68K_AM_REGI_ADDR_PRE_DEC:
417
58.4k
    SStream_concat(O, "-(a%" PRId32 ")", (base_reg - M68K_REG_A0));
418
58.4k
    break;
419
25.7k
  case M68K_AM_REGI_ADDR_DISP:
420
25.7k
    SStream_concat(O, "%s$%" PRIx16 "(a%" PRId32 ")",
421
25.7k
             op->mem.disp < 0 ? "-" : "", abs(op->mem.disp),
422
25.7k
             (base_reg - M68K_REG_A0));
423
25.7k
    break;
424
3.52k
  case M68K_AM_PCI_DISP:
425
3.52k
    SStream_concat(O, "$%" PRIx32 "(pc)", pc + 2 + op->mem.disp);
426
3.52k
    break;
427
0
  default:
428
0
    break;
429
383k
  }
430
383k
}
431
432
static void printBaseDisp(SStream *O, uint32_t pc, const cs_m68k_op *op)
433
3.81k
{
434
3.81k
  int is_pc = (op->address_mode == M68K_AM_PCI_INDEX_BASE_DISP);
435
436
3.81k
  if (is_pc) {
437
262
    SStream_concat(O, "$%" PRIx32, pc + 2 + op->mem.in_disp);
438
3.54k
  } else if (op->mem.in_disp != 0) {
439
1.90k
    SStream_concat(O, "%s$%" PRIx32,
440
1.90k
             op->mem.in_disp >= 0 ? "" : "-",
441
1.90k
             abs(op->mem.in_disp));
442
1.90k
  }
443
444
3.81k
  SStream_concat0(O, "(");
445
446
3.81k
  if (is_pc) {
447
262
    SStream_concat0(O, "pc");
448
3.54k
  } else if (op->mem.base_reg != M68K_REG_INVALID) {
449
2.15k
    SStream_concat(O, "a%" PRId32, op->mem.base_reg - M68K_REG_A0);
450
2.15k
  }
451
452
3.81k
  if ((is_pc || op->mem.base_reg != M68K_REG_INVALID) &&
453
2.41k
      op->mem.index_reg != M68K_REG_INVALID)
454
1.64k
    SStream_concat(O, ",%s", s_spacing);
455
456
3.81k
  if (op->mem.index_reg != M68K_REG_INVALID) {
457
2.03k
    printIndexReg(O, op);
458
2.03k
    printScaleFactor(O, op->mem.scale, 0);
459
2.03k
  }
460
461
3.81k
  SStream_concat0(O, ")");
462
3.81k
}
463
464
static void printMemIndirect(SStream *O, uint32_t pc, const cs_m68k_op *op)
465
8.62k
{
466
8.62k
  int is_pc = (op->address_mode == M68K_AM_PC_MEMI_POST_INDEX ||
467
8.44k
         op->address_mode == M68K_AM_PC_MEMI_PRE_INDEX);
468
8.62k
  int is_post = (op->address_mode == M68K_AM_MEMI_POST_INDEX ||
469
4.46k
           op->address_mode == M68K_AM_PC_MEMI_POST_INDEX);
470
8.62k
  int is_pre = (op->address_mode == M68K_AM_MEMI_PRE_INDEX ||
471
4.88k
          op->address_mode == M68K_AM_PC_MEMI_PRE_INDEX);
472
473
8.62k
  SStream_concat0(O, "([");
474
475
8.62k
  if (is_pc) {
476
731
    SStream_concat(O, "$%" PRIx32, pc + 2 + op->mem.in_disp);
477
7.89k
  } else if (op->mem.in_disp != 0) {
478
4.48k
    SStream_concat(O, "%s$%" PRIx32,
479
4.48k
             op->mem.in_disp >= 0 ? "" : "-",
480
4.48k
             abs(op->mem.in_disp));
481
4.48k
  }
482
483
8.62k
  if (op->mem.base_reg != M68K_REG_INVALID) {
484
4.54k
    if (op->mem.in_disp != 0)
485
2.58k
      SStream_concat(O, ",%s%s", s_spacing,
486
2.58k
               getRegName(op->mem.base_reg));
487
1.95k
    else
488
1.95k
      SStream_concat(O, "%s", getRegName(op->mem.base_reg));
489
4.54k
  }
490
491
8.62k
  if (is_post)
492
4.33k
    SStream_concat0(O, "]");
493
494
8.62k
  if (op->mem.index_reg != M68K_REG_INVALID) {
495
5.00k
    SStream_concat(O, ",%s", s_spacing);
496
5.00k
    printIndexReg(O, op);
497
5.00k
  }
498
499
8.62k
  printScaleFactor(O, op->mem.scale, 0);
500
501
8.62k
  if (is_pre)
502
4.28k
    SStream_concat0(O, "]");
503
504
8.62k
  if (op->mem.out_disp != 0) {
505
4.90k
    SStream_concat(O, ",%s%s$%" PRIx32, s_spacing,
506
4.90k
             op->mem.out_disp >= 0 ? "" : "-",
507
4.90k
             abs(op->mem.out_disp));
508
4.90k
  }
509
510
8.62k
  SStream_concat0(O, ")");
511
8.62k
}
512
513
static void printAddressingMode(SStream *O, uint32_t pc, const cs_m68k *inst,
514
        const cs_m68k_op *op)
515
578k
{
516
578k
  switch (op->address_mode) {
517
35.5k
  case M68K_AM_NONE:
518
35.5k
    switch (op->type) {
519
4.97k
    case M68K_OP_REG_BITS:
520
4.97k
      registerBits(O, op);
521
4.97k
      break;
522
2.79k
    case M68K_OP_REG_PAIR:
523
2.79k
      registerPair(O, op);
524
2.79k
      break;
525
27.7k
    case M68K_OP_REG:
526
27.7k
      printRegisterName(O, op);
527
27.7k
      break;
528
0
    case M68K_OP_SHIFT:
529
0
      if (op->flags & M68K_OP_FLAG_SHIFT_LEFT)
530
0
        SStream_concat0(O, "<<");
531
0
      else if (op->flags & M68K_OP_FLAG_SHIFT_RIGHT)
532
0
        SStream_concat0(O, ">>");
533
0
      break;
534
0
    default:
535
0
      break;
536
35.5k
    }
537
35.5k
    break;
538
539
197k
  case M68K_AM_REG_DIRECT_DATA:
540
223k
  case M68K_AM_REG_DIRECT_ADDR:
541
256k
  case M68K_AM_REGI_ADDR:
542
295k
  case M68K_AM_REGI_ADDR_POST_INC:
543
354k
  case M68K_AM_REGI_ADDR_PRE_DEC:
544
379k
  case M68K_AM_REGI_ADDR_DISP:
545
383k
  case M68K_AM_PCI_DISP:
546
383k
    printRegAddrMode(O, pc, op);
547
383k
    break;
548
5.07k
  case M68K_AM_ABSOLUTE_DATA_SHORT:
549
5.07k
    SStream_concat(O, "$%" PRIx32 ".w", (uint32_t)op->mem.address);
550
5.07k
    break;
551
3.05k
  case M68K_AM_ABSOLUTE_DATA_LONG:
552
3.05k
    SStream_concat(O, "$%" PRIx64 ".l", (uint64_t)op->mem.address);
553
3.05k
    break;
554
88.1k
  case M68K_AM_IMMEDIATE:
555
88.1k
    printImmediate(O, inst, op);
556
88.1k
    break;
557
1.86k
  case M68K_AM_PCI_INDEX_8_BIT_DISP:
558
16.8k
  case M68K_AM_AREGI_INDEX_8_BIT_DISP:
559
16.8k
    printIndex8BitDisp(O, pc, op);
560
16.8k
    break;
561
262
  case M68K_AM_PCI_INDEX_BASE_DISP:
562
3.81k
  case M68K_AM_AREGI_INDEX_BASE_DISP:
563
3.81k
    printBaseDisp(O, pc, op);
564
3.81k
    break;
565
180
  case M68K_AM_PC_MEMI_POST_INDEX:
566
731
  case M68K_AM_PC_MEMI_PRE_INDEX:
567
4.46k
  case M68K_AM_MEMI_PRE_INDEX:
568
8.62k
  case M68K_AM_MEMI_POST_INDEX:
569
8.62k
    printMemIndirect(O, pc, op);
570
8.62k
    break;
571
33.5k
  case M68K_AM_BRANCH_DISPLACEMENT:
572
33.5k
    SStream_concat(O, "$%" PRIx32, pc + 2 + op->br_disp.disp);
573
33.5k
  default:
574
33.5k
    break;
575
578k
  }
576
577
578k
  printBitfield(O, op);
578
578k
  if (op->flags & M68K_OP_FLAG_MEM_UPDATE)
579
0
    SStream_concat0(O, "&");
580
578k
}
581
582
static void printCAS2(SStream *O, uint32_t pc, const cs_m68k *ext)
583
1.25k
{
584
1.25k
  printAddressingMode(O, pc, ext, &ext->operands[0]);
585
1.25k
  SStream_concat0(O, ",");
586
1.25k
  printAddressingMode(O, pc, ext, &ext->operands[1]);
587
1.25k
  SStream_concat0(O, ",");
588
589
1.25k
  SStream_concat(O, "(%s):(%s)",
590
1.25k
           s_reg_names[ext->operands[2].reg_pair.reg_0],
591
1.25k
           s_reg_names[ext->operands[2].reg_pair.reg_1]);
592
1.25k
}
593
594
static void printCacheOp(SStream *O, uint32_t pc, const cs_m68k *ext)
595
2.26k
{
596
2.26k
  static const char *const cache_names[] = { "nc", "dc", "ic", "bc" };
597
2.26k
  unsigned int sel = (unsigned int)ext->operands[0].imm;
598
2.26k
  int i;
599
600
2.26k
  if (sel < ARR_SIZE(cache_names))
601
2.26k
    SStream_concat0(O, cache_names[sel]);
602
0
  else
603
0
    SStream_concat(O, "#$%" PRIx64, ext->operands[0].imm);
604
605
4.02k
  for (i = 1; i < ext->op_count; ++i) {
606
1.76k
    SStream_concat(O, ",%s", s_spacing);
607
1.76k
    printAddressingMode(O, pc, ext, &ext->operands[i]);
608
1.76k
  }
609
2.26k
}
610
611
static void printPackedMoveDestination(SStream *O, uint32_t pc,
612
               const cs_m68k *ext)
613
23
{
614
23
  const cs_m68k_op *k_factor = &ext->operands[2];
615
616
23
  printAddressingMode(O, pc, ext, &ext->operands[0]);
617
23
  SStream_concat(O, ",%s", s_spacing);
618
23
  printAddressingMode(O, pc, ext, &ext->operands[1]);
619
23
  SStream_concat0(O, "{");
620
23
  if (k_factor->type == M68K_OP_IMM)
621
8
    SStream_concat(O, "#%" PRId64, (int64_t)k_factor->imm);
622
15
  else
623
15
    printAddressingMode(O, pc, ext, k_factor);
624
23
  SStream_concat0(O, "}");
625
23
}
626
627
#endif
628
629
static void printOpSize(SStream *O, const cs_m68k *ext)
630
321k
{
631
321k
  switch (ext->op_size.type) {
632
0
  case M68K_SIZE_TYPE_INVALID:
633
0
    break;
634
319k
  case M68K_SIZE_TYPE_CPU:
635
319k
    switch (ext->op_size.cpu_size) {
636
109k
    case M68K_CPU_SIZE_BYTE:
637
109k
      SStream_concat0(O, ".b");
638
109k
      break;
639
89.1k
    case M68K_CPU_SIZE_WORD:
640
89.1k
      SStream_concat0(O, ".w");
641
89.1k
      break;
642
77.5k
    case M68K_CPU_SIZE_LONG:
643
77.5k
      SStream_concat0(O, ".l");
644
77.5k
      break;
645
42.9k
    case M68K_CPU_SIZE_NONE:
646
42.9k
      break;
647
319k
    }
648
319k
    break;
649
319k
  case M68K_SIZE_TYPE_FPU:
650
2.31k
    switch (ext->op_size.fpu_size) {
651
795
    case M68K_FPU_SIZE_SINGLE:
652
795
      SStream_concat0(O, ".s");
653
795
      break;
654
358
    case M68K_FPU_SIZE_DOUBLE:
655
358
      SStream_concat0(O, ".d");
656
358
      break;
657
1.13k
    case M68K_FPU_SIZE_EXTENDED:
658
1.13k
      SStream_concat0(O, ".x");
659
1.13k
      break;
660
25
    case M68K_FPU_SIZE_PACKED:
661
25
      SStream_concat0(O, ".p");
662
25
      break;
663
0
    case M68K_FPU_SIZE_NONE:
664
0
      break;
665
2.31k
    }
666
2.31k
    break;
667
321k
  }
668
321k
}
669
670
void M68K_printInst(MCInst *MI, SStream *O, void *PrinterInfo)
671
398k
{
672
398k
#ifndef CAPSTONE_DIET
673
398k
  m68k_info *info = (m68k_info *)PrinterInfo;
674
398k
  cs_m68k *ext = &info->extension;
675
398k
  cs_detail *detail = NULL;
676
398k
  int i = 0;
677
678
398k
  if (detail_is_set(MI)) {
679
398k
    detail = get_detail(MI);
680
398k
    int regs_read_count = MIN((int)ARR_SIZE(detail->regs_read),
681
398k
            info->regs_read_count);
682
398k
    int regs_write_count = MIN((int)ARR_SIZE(detail->regs_write),
683
398k
             info->regs_write_count);
684
398k
    int groups_count =
685
398k
      MIN((int)ARR_SIZE(detail->groups), info->groups_count);
686
687
398k
    memcpy(&detail->m68k, ext, sizeof(cs_m68k));
688
398k
    memcpy(&detail->regs_read, &info->regs_read,
689
398k
           regs_read_count * sizeof(info->regs_read[0]));
690
398k
    detail->regs_read_count = regs_read_count;
691
692
398k
    memcpy(&detail->regs_write, &info->regs_write,
693
398k
           regs_write_count * sizeof(info->regs_write[0]));
694
398k
    detail->regs_write_count = regs_write_count;
695
696
398k
    memcpy(&detail->groups, &info->groups, groups_count);
697
398k
    detail->groups_count = groups_count;
698
398k
  }
699
700
398k
  if (MI->Opcode == M68K_INS_INVALID) {
701
77.0k
    if (ext->op_count)
702
77.0k
      SStream_concat(O, "dc.w $%" PRIx32,
703
77.0k
               (uint32_t)ext->operands[0].imm);
704
0
    else
705
0
      SStream_concat(O, "dc.w $<unknown>");
706
77.0k
    return;
707
77.0k
  }
708
709
321k
  SStream_concat0(O, (char *)s_instruction_names[MI->Opcode]);
710
321k
  printOpSize(O, ext);
711
321k
  SStream_concat0(O, " ");
712
713
321k
  if (MI->Opcode == M68K_INS_CAS2) {
714
1.25k
    printCAS2(O, info->pc, ext);
715
1.25k
    return;
716
1.25k
  }
717
718
320k
  if (MI->Opcode >= M68K_INS_CINVL && MI->Opcode <= M68K_INS_CPUSHA) {
719
2.26k
    printCacheOp(O, info->pc, ext);
720
2.26k
    return;
721
2.26k
  }
722
723
318k
  if (MI->Opcode == M68K_INS_FMOVE &&
724
1.27k
      ext->op_size.type == M68K_SIZE_TYPE_FPU &&
725
374
      ext->op_size.fpu_size == M68K_FPU_SIZE_PACKED &&
726
25
      ext->op_count == 3) {
727
23
    printPackedMoveDestination(O, info->pc, ext);
728
23
    return;
729
23
  }
730
731
891k
  for (i = 0; i < ext->op_count; ++i) {
732
573k
    printAddressingMode(O, info->pc, ext, &ext->operands[i]);
733
573k
    if ((i + 1) != ext->op_count)
734
257k
      SStream_concat(O, ",%s", s_spacing);
735
573k
  }
736
318k
#endif
737
318k
}
738
739
const char *M68K_reg_name(csh handle, unsigned int reg)
740
871k
{
741
#ifdef CAPSTONE_DIET
742
  return NULL;
743
#else
744
871k
  if (reg >= ARR_SIZE(s_reg_names)) {
745
0
    return NULL;
746
0
  }
747
871k
  return s_reg_names[(int)reg];
748
871k
#endif
749
871k
}
750
751
void M68K_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id)
752
716k
{
753
716k
  insn->id = id; // These id's matches for 68k
754
716k
}
755
756
const char *M68K_insn_name(csh handle, unsigned int id)
757
398k
{
758
#ifdef CAPSTONE_DIET
759
  return NULL;
760
#else
761
398k
  if (id >= ARR_SIZE(s_instruction_names)) {
762
0
    return NULL;
763
0
  }
764
398k
  return s_instruction_names[id];
765
398k
#endif
766
398k
}
767
768
#ifndef CAPSTONE_DIET
769
static const name_map group_name_maps[] = {
770
  { M68K_GRP_INVALID, NULL },
771
  { M68K_GRP_JUMP, "jump" },
772
  { M68K_GRP_RET, "ret" },
773
  { M68K_GRP_IRET, "iret" },
774
  { M68K_GRP_BRANCH_RELATIVE, "branch_relative" },
775
};
776
#endif
777
778
const char *M68K_group_name(csh handle, unsigned int id)
779
129k
{
780
129k
#ifndef CAPSTONE_DIET
781
129k
  return id2name(group_name_maps, ARR_SIZE(group_name_maps), id);
782
#else
783
  return NULL;
784
#endif
785
129k
}
786
787
#ifndef CAPSTONE_DIET
788
void M68K_reg_access(const cs_insn *insn, cs_regs regs_read,
789
         uint8_t *regs_read_count, cs_regs regs_write,
790
         uint8_t *regs_write_count)
791
0
{
792
0
  uint8_t read_count, write_count;
793
794
0
  read_count = insn->detail->regs_read_count;
795
0
  write_count = insn->detail->regs_write_count;
796
797
  // implicit registers
798
0
  memcpy(regs_read, insn->detail->regs_read,
799
0
         read_count * sizeof(insn->detail->regs_read[0]));
800
0
  memcpy(regs_write, insn->detail->regs_write,
801
0
         write_count * sizeof(insn->detail->regs_write[0]));
802
803
0
  *regs_read_count = read_count;
804
0
  *regs_write_count = write_count;
805
0
}
806
#endif