Coverage Report

Created: 2024-08-21 06:24

/src/capstonenext/arch/MOS65XX/MOS65XXDisassembler.c
Line
Count
Source (jump to first uncovered line)
1
/* Capstone Disassembly Engine */
2
/* MOS65XX Backend by Sebastian Macke <sebastian@macke.de> 2018 */
3
4
#include "capstone/mos65xx.h"
5
#include "MOS65XXDisassembler.h"
6
#include "MOS65XXDisassemblerInternals.h"
7
8
typedef struct OpInfo {
9
  mos65xx_insn ins;
10
  mos65xx_address_mode am;
11
  int operand_bytes;
12
} OpInfo;
13
14
static const struct OpInfo OpInfoTable[]= {
15
16
#include "m6502.inc"
17
#include "m65c02.inc"
18
#include "mw65c02.inc"
19
#include "m65816.inc"
20
21
};
22
23
#ifndef CAPSTONE_DIET
24
static const char* const RegNames[] = {
25
  "invalid", "A", "X", "Y", "P", "SP", "DP", "B", "K" 
26
};
27
28
static const char* const GroupNames[] = {
29
  NULL,
30
  "jump",
31
  "call",
32
  "ret",
33
  "int",
34
  "iret",
35
  "branch_relative"
36
};
37
38
typedef struct InstructionInfo {
39
  const char* name;
40
  mos65xx_group_type group_type;
41
  mos65xx_reg write, read;
42
  bool modifies_status;
43
} InstructionInfo;
44
45
static const struct InstructionInfo InstructionInfoTable[]= {
46
47
#include "instruction_info.inc"
48
49
};
50
#endif
51
52
#ifndef CAPSTONE_DIET
53
static void fillDetails(MCInst *MI, struct OpInfo opinfo, int cpu_type)
54
15.9k
{
55
15.9k
  int i;
56
15.9k
  cs_detail *detail = MI->flat_insn->detail;
57
58
15.9k
  InstructionInfo insinfo = InstructionInfoTable[opinfo.ins];
59
60
15.9k
  detail->mos65xx.am = opinfo.am;
61
15.9k
  detail->mos65xx.modifies_flags = insinfo.modifies_status;
62
15.9k
  detail->groups_count = 0;
63
15.9k
  detail->regs_read_count = 0;
64
15.9k
  detail->regs_write_count = 0;
65
15.9k
  detail->mos65xx.op_count = 0;
66
67
15.9k
  if (insinfo.group_type != MOS65XX_GRP_INVALID) {
68
4.72k
    detail->groups[detail->groups_count] = insinfo.group_type;
69
4.72k
    detail->groups_count++;
70
4.72k
  }
71
72
15.9k
  if (opinfo.am == MOS65XX_AM_REL || opinfo.am == MOS65XX_AM_ZP_REL) {
73
615
    detail->groups[detail->groups_count] = MOS65XX_GRP_BRANCH_RELATIVE;
74
615
    detail->groups_count++; 
75
615
  }
76
77
15.9k
  if (insinfo.read != MOS65XX_REG_INVALID) {
78
4.85k
    detail->regs_read[detail->regs_read_count++] = insinfo.read;
79
11.1k
  } else switch(opinfo.am) {
80
814
    case MOS65XX_AM_ACC:
81
814
      detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_ACC;
82
814
      break;
83
277
    case MOS65XX_AM_ZP_Y:
84
693
    case MOS65XX_AM_ZP_IND_Y:
85
1.14k
    case MOS65XX_AM_ABS_Y:
86
1.14k
    case MOS65XX_AM_ZP_IND_LONG_Y:
87
1.14k
      detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_Y;
88
1.14k
      break;
89
90
829
    case MOS65XX_AM_ZP_X:
91
1.41k
    case MOS65XX_AM_ZP_X_IND:
92
2.05k
    case MOS65XX_AM_ABS_X:
93
2.05k
    case MOS65XX_AM_ABS_X_IND:
94
2.05k
    case MOS65XX_AM_ABS_LONG_X:
95
2.05k
      detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_X;
96
2.05k
      break;
97
98
0
    case MOS65XX_AM_SR:
99
0
      detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_SP;
100
0
      break;
101
0
    case MOS65XX_AM_SR_IND_Y:
102
0
      detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_SP;
103
0
      detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_Y;
104
0
      break;
105
106
7.10k
    default:
107
7.10k
      break;
108
11.1k
  }
109
110
15.9k
  if (insinfo.write != MOS65XX_REG_INVALID) {
111
5.55k
    detail->regs_write[detail->regs_write_count++] = insinfo.write;
112
10.4k
  } else if (opinfo.am == MOS65XX_AM_ACC) {
113
814
    detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_ACC;
114
814
  }
115
116
117
15.9k
  switch(opinfo.ins) {
118
554
    case MOS65XX_INS_ADC:
119
1.07k
    case MOS65XX_INS_SBC:
120
1.90k
    case MOS65XX_INS_ROL:
121
2.68k
    case MOS65XX_INS_ROR:
122
      /* these read carry flag (and decimal for ADC/SBC) */
123
2.68k
      detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_P;
124
2.68k
      break;
125
    /* stack operations */
126
0
    case MOS65XX_INS_JSL:
127
582
    case MOS65XX_INS_JSR:
128
582
    case MOS65XX_INS_PEA:
129
582
    case MOS65XX_INS_PEI:
130
582
    case MOS65XX_INS_PER:
131
1.24k
    case MOS65XX_INS_PHA:
132
1.24k
    case MOS65XX_INS_PHB:
133
1.24k
    case MOS65XX_INS_PHD:
134
1.24k
    case MOS65XX_INS_PHK:
135
1.56k
    case MOS65XX_INS_PHP:
136
1.56k
    case MOS65XX_INS_PHX:
137
1.56k
    case MOS65XX_INS_PHY:
138
1.93k
    case MOS65XX_INS_PLA:
139
1.93k
    case MOS65XX_INS_PLB:
140
1.93k
    case MOS65XX_INS_PLD:
141
2.53k
    case MOS65XX_INS_PLP:
142
2.53k
    case MOS65XX_INS_PLX:
143
2.53k
    case MOS65XX_INS_PLY:
144
3.16k
    case MOS65XX_INS_RTI:
145
3.16k
    case MOS65XX_INS_RTL:
146
3.99k
    case MOS65XX_INS_RTS:
147
3.99k
      detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_SP;
148
3.99k
      detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_SP;
149
3.99k
      break;
150
9.29k
    default:
151
9.29k
      break;
152
15.9k
  }
153
154
15.9k
  if (cpu_type == MOS65XX_CPU_TYPE_65816) {
155
0
    switch (opinfo.am) {
156
0
      case MOS65XX_AM_ZP:
157
0
      case MOS65XX_AM_ZP_X:
158
0
      case MOS65XX_AM_ZP_Y:
159
0
      case MOS65XX_AM_ZP_IND:
160
0
      case MOS65XX_AM_ZP_X_IND:
161
0
      case MOS65XX_AM_ZP_IND_Y:
162
0
      case MOS65XX_AM_ZP_IND_LONG:
163
0
      case MOS65XX_AM_ZP_IND_LONG_Y:
164
0
        detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_DP;
165
0
        break;
166
0
      case MOS65XX_AM_BLOCK:
167
0
        detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_ACC;
168
0
        detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_X;
169
0
        detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_Y;
170
0
        detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_ACC;
171
0
        detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_X;
172
0
        detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_Y;
173
0
        detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_B;
174
0
        break;
175
0
      default:
176
0
        break;
177
0
    }
178
179
0
    switch (opinfo.am) {
180
0
      case MOS65XX_AM_ZP_IND:
181
0
      case MOS65XX_AM_ZP_X_IND:
182
0
      case MOS65XX_AM_ZP_IND_Y:
183
0
      case MOS65XX_AM_ABS:
184
0
      case MOS65XX_AM_ABS_X:
185
0
      case MOS65XX_AM_ABS_Y:
186
0
      case MOS65XX_AM_ABS_X_IND:
187
        /* these depend on the databank to generate a 24-bit address */
188
        /* exceptions: PEA, PEI, and JMP (abs) */
189
0
        if (opinfo.ins == MOS65XX_INS_PEI || opinfo.ins == MOS65XX_INS_PEA) break;
190
0
        detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_B;
191
0
        break;
192
0
      default:
193
0
        break;
194
0
    }
195
0
  }
196
197
15.9k
  if (insinfo.modifies_status) {
198
9.61k
    detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_P;
199
9.61k
  }
200
201
15.9k
  switch(opinfo.am) {
202
5.13k
    case MOS65XX_AM_IMP:
203
5.13k
      break;
204
475
    case MOS65XX_AM_IMM:
205
475
      detail->mos65xx.operands[detail->mos65xx.op_count].type = MOS65XX_OP_IMM;
206
475
      detail->mos65xx.operands[detail->mos65xx.op_count].imm = MI->Operands[0].ImmVal;
207
475
      detail->mos65xx.op_count++;
208
475
      break;
209
814
    case MOS65XX_AM_ACC:
210
814
      detail->mos65xx.operands[detail->mos65xx.op_count].type = MOS65XX_OP_REG;
211
814
      detail->mos65xx.operands[detail->mos65xx.op_count].reg = MOS65XX_REG_ACC;
212
814
      detail->mos65xx.op_count++;
213
814
      break;
214
615
    case MOS65XX_AM_REL: {
215
615
      int value = MI->Operands[0].ImmVal;
216
615
      if (MI->op1_size == 1)
217
615
        value = 2 + (signed char)value;
218
0
      else
219
0
        value = 3 + (signed short)value;
220
615
      detail->mos65xx.operands[detail->mos65xx.op_count].type = MOS65XX_OP_MEM;
221
615
      detail->mos65xx.operands[detail->mos65xx.op_count].mem = (MI->address + value) & 0xffff;
222
615
      detail->mos65xx.op_count++;
223
615
      break;
224
0
    }
225
0
    case MOS65XX_AM_ZP_REL: {
226
0
      int value = 3 + (signed char)MI->Operands[1].ImmVal;
227
      /* BBR0, zp, rel  and BBS0, zp, rel */
228
0
      detail->mos65xx.operands[detail->mos65xx.op_count].type = MOS65XX_OP_MEM;
229
0
      detail->mos65xx.operands[detail->mos65xx.op_count].mem = MI->Operands[0].ImmVal;
230
0
      detail->mos65xx.operands[detail->mos65xx.op_count+1].type = MOS65XX_OP_MEM;
231
0
      detail->mos65xx.operands[detail->mos65xx.op_count+1].mem = (MI->address + value) & 0xffff;
232
0
      detail->mos65xx.op_count+=2;
233
0
      break;
234
0
    }
235
8.93k
    default:
236
17.8k
      for (i = 0; i < MI->size; ++i) {
237
8.93k
        detail->mos65xx.operands[detail->mos65xx.op_count].type = MOS65XX_OP_MEM;
238
8.93k
        detail->mos65xx.operands[detail->mos65xx.op_count].mem = MI->Operands[i].ImmVal;
239
8.93k
        detail->mos65xx.op_count++;
240
8.93k
      }
241
8.93k
      break;
242
15.9k
  }
243
15.9k
}
244
#endif
245
246
void MOS65XX_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo)
247
15.9k
{
248
15.9k
#ifndef CAPSTONE_DIET
249
15.9k
  unsigned int value;
250
15.9k
  unsigned opcode = MCInst_getOpcode(MI);
251
15.9k
  mos65xx_info *info = (mos65xx_info *)PrinterInfo;
252
253
15.9k
  OpInfo opinfo = OpInfoTable[opcode];
254
255
15.9k
  const char *prefix = info->hex_prefix ? info->hex_prefix : "0x";
256
257
15.9k
  SStream_concat0(O, InstructionInfoTable[opinfo.ins].name);
258
15.9k
  switch (opinfo.ins) {
259
    /* special case - bit included as part of the instruction name */
260
0
    case MOS65XX_INS_BBR:
261
0
    case MOS65XX_INS_BBS:
262
0
    case MOS65XX_INS_RMB:
263
0
    case MOS65XX_INS_SMB:
264
0
      SStream_concat(O, "%d", (opcode >> 4) & 0x07);
265
0
      break;
266
15.9k
    default:
267
15.9k
      break;
268
15.9k
  }
269
270
15.9k
  value = MI->Operands[0].ImmVal;
271
272
15.9k
  switch (opinfo.am) {
273
0
    default:
274
0
      break;
275
276
5.13k
    case MOS65XX_AM_IMP:
277
5.13k
      break;
278
279
814
    case MOS65XX_AM_ACC:
280
814
      SStream_concat0(O, " a");
281
814
      break;
282
283
475
    case MOS65XX_AM_IMM:
284
475
      if (MI->imm_size == 1)
285
475
        SStream_concat(O, " #%s%02x", prefix, value);
286
0
      else
287
0
        SStream_concat(O, " #%s%04x", prefix, value);
288
475
      break;
289
290
747
    case MOS65XX_AM_ZP:
291
747
      SStream_concat(O, " %s%02x", prefix, value);
292
747
      break;
293
294
1.03k
    case MOS65XX_AM_ABS:
295
1.03k
      SStream_concat(O, " %s%04x", prefix, value);
296
1.03k
      break;
297
298
0
    case MOS65XX_AM_ABS_LONG_X:
299
0
      SStream_concat(O, " %s%06x, x", prefix, value);
300
0
      break;
301
302
1.76k
    case MOS65XX_AM_INT:
303
1.76k
      SStream_concat(O, " %s%02x", prefix, value);
304
1.76k
      break;
305
306
758
    case MOS65XX_AM_ABS_X:
307
758
      SStream_concat(O, " %s%04x, x", prefix, value);
308
758
      break;
309
310
647
    case MOS65XX_AM_ABS_Y:
311
647
      SStream_concat(O, " %s%04x, y", prefix, value);
312
647
      break;
313
314
0
    case MOS65XX_AM_ABS_LONG:
315
0
      SStream_concat(O, " %s%06x", prefix, value);
316
0
      break;
317
318
1.13k
    case MOS65XX_AM_ZP_X:
319
1.13k
      SStream_concat(O, " %s%02x, x", prefix, value);
320
1.13k
      break;
321
322
329
    case MOS65XX_AM_ZP_Y:
323
329
      SStream_concat(O, " %s%02x, y", prefix, value);
324
329
      break;
325
326
615
    case MOS65XX_AM_REL:
327
615
      if (MI->op1_size == 1)
328
615
        value = 2 + (signed char)value;
329
0
      else
330
0
        value = 3 + (signed short)value;
331
332
615
      SStream_concat(O, " %s%04x", prefix, 
333
615
        (MI->address + value) & 0xffff);
334
615
      break;
335
336
299
    case MOS65XX_AM_ABS_IND:
337
299
      SStream_concat(O, " (%s%04x)", prefix, value);
338
299
      break;
339
340
0
    case MOS65XX_AM_ABS_X_IND:
341
0
      SStream_concat(O, " (%s%04x, x)", prefix, value);
342
0
      break;
343
344
0
    case MOS65XX_AM_ABS_IND_LONG:
345
0
      SStream_concat(O, " [%s%04x]", prefix, value);
346
0
      break;
347
348
0
    case MOS65XX_AM_ZP_IND:
349
0
      SStream_concat(O, " (%s%02x)", prefix, value);
350
0
      break;
351
352
1.25k
    case MOS65XX_AM_ZP_X_IND:
353
1.25k
      SStream_concat(O, " (%s%02x, x)", prefix, value);
354
1.25k
      break;
355
356
971
    case MOS65XX_AM_ZP_IND_Y:
357
971
      SStream_concat(O, " (%s%02x), y", prefix, value);
358
971
      break;
359
360
0
    case MOS65XX_AM_ZP_IND_LONG:
361
0
      SStream_concat(O, " [%s%02x]", prefix, value);
362
0
      break;
363
364
0
    case MOS65XX_AM_ZP_IND_LONG_Y:
365
0
      SStream_concat(O, " [%s%02x], y", prefix, value);
366
0
      break;
367
368
0
    case MOS65XX_AM_SR:
369
0
      SStream_concat(O, " %s%02x, s", prefix, value);
370
0
      break;
371
372
0
    case MOS65XX_AM_SR_IND_Y:
373
0
      SStream_concat(O, " (%s%02x, s), y", prefix, value);
374
0
      break;
375
376
0
    case MOS65XX_AM_BLOCK:
377
0
      SStream_concat(O, " %s%02x, %s%02x",
378
0
        prefix, MI->Operands[0].ImmVal,
379
0
        prefix, MI->Operands[1].ImmVal);
380
0
      break;
381
382
0
    case MOS65XX_AM_ZP_REL:
383
0
      value = 3 + (signed char)MI->Operands[1].ImmVal;
384
      /* BBR0, zp, rel  and BBS0, zp, rel */
385
0
      SStream_concat(O, " %s%02x, %s%04x",
386
0
        prefix, MI->Operands[0].ImmVal,
387
0
        prefix, (MI->address + value) & 0xffff);
388
0
      break;
389
390
15.9k
  }
391
15.9k
#endif
392
15.9k
}
393
394
bool MOS65XX_getInstruction(csh ud, const uint8_t *code, size_t code_len,
395
              MCInst *MI, uint16_t *size, uint64_t address, void *inst_info)
396
16.1k
{
397
16.1k
  int i;
398
16.1k
  unsigned char opcode;
399
16.1k
  unsigned char len;
400
16.1k
  unsigned cpu_offset = 0;
401
16.1k
  int cpu_type = MOS65XX_CPU_TYPE_6502;
402
16.1k
  cs_struct* handle = MI->csh;
403
16.1k
  mos65xx_info *info = (mos65xx_info *)handle->printer_info;
404
16.1k
  OpInfo opinfo;
405
406
16.1k
  if (code_len == 0) {
407
0
    *size = 1;
408
0
    return false;
409
0
  }
410
411
16.1k
  cpu_type = info->cpu_type;
412
16.1k
  cpu_offset = cpu_type * 256;
413
414
16.1k
  opcode = code[0];
415
16.1k
  opinfo = OpInfoTable[cpu_offset + opcode];
416
16.1k
  if (opinfo.ins == MOS65XX_INS_INVALID) {
417
122
    *size = 1;
418
122
    return false;
419
122
  }
420
421
16.0k
  len = opinfo.operand_bytes + 1;
422
423
16.0k
  if (cpu_type == MOS65XX_CPU_TYPE_65816 && opinfo.am == MOS65XX_AM_IMM) {
424
0
    switch(opinfo.ins) {
425
0
      case MOS65XX_INS_CPX:
426
0
      case MOS65XX_INS_CPY:
427
0
      case MOS65XX_INS_LDX:
428
0
      case MOS65XX_INS_LDY:
429
0
        if (info->long_x) ++len;
430
0
        break;
431
0
      case MOS65XX_INS_ADC:
432
0
      case MOS65XX_INS_AND:
433
0
      case MOS65XX_INS_BIT:
434
0
      case MOS65XX_INS_CMP:
435
0
      case MOS65XX_INS_EOR:
436
0
      case MOS65XX_INS_LDA:
437
0
      case MOS65XX_INS_ORA:
438
0
      case MOS65XX_INS_SBC:
439
0
        if (info->long_m) ++len;
440
0
        break;
441
0
      default:
442
0
        break;
443
0
    }
444
0
  }
445
446
16.0k
  if (code_len < len) {
447
55
    *size = 1;
448
55
    return false;
449
55
  }
450
451
15.9k
  MI->address = address;
452
453
15.9k
  MCInst_setOpcode(MI, cpu_offset + opcode);
454
15.9k
  MCInst_setOpcodePub(MI, opinfo.ins);
455
456
15.9k
  *size = len;
457
458
  /* needed to differentiate relative vs relative long */
459
15.9k
  MI->op1_size = len - 1;
460
15.9k
  if (opinfo.ins == MOS65XX_INS_NOP) {
461
869
    for (i = 1; i < len; ++i)
462
0
      MCOperand_CreateImm0(MI, code[i]);
463
869
  }
464
465
15.9k
  switch (opinfo.am) {
466
0
    case MOS65XX_AM_ZP_REL:
467
0
      MCOperand_CreateImm0(MI, code[1]);
468
0
      MCOperand_CreateImm0(MI, code[2]);
469
0
      break;
470
0
    case MOS65XX_AM_BLOCK:
471
0
      MCOperand_CreateImm0(MI, code[2]);
472
0
      MCOperand_CreateImm0(MI, code[1]);
473
0
      break;
474
5.13k
    case MOS65XX_AM_IMP:
475
5.94k
    case MOS65XX_AM_ACC:
476
5.94k
      break;
477
478
475
    case MOS65XX_AM_IMM:
479
475
      MI->has_imm = 1;
480
475
      MI->imm_size = len - 1;
481
      /* 65816 immediate is either 1 or 2 bytes */
482
      /* drop through */
483
10.0k
    default:
484
10.0k
      if (len == 2)
485
7.29k
        MCOperand_CreateImm0(MI, code[1]);
486
2.73k
      else if (len == 3)
487
2.73k
        MCOperand_CreateImm0(MI, (code[2]<<8) | code[1]);
488
0
      else if (len == 4)
489
0
        MCOperand_CreateImm0(MI, (code[3]<<16) | (code[2]<<8) | code[1]);
490
10.0k
      break;
491
15.9k
  }
492
493
15.9k
#ifndef CAPSTONE_DIET
494
15.9k
  if (MI->flat_insn->detail) {
495
15.9k
    fillDetails(MI, opinfo, cpu_type);
496
15.9k
  }
497
15.9k
#endif
498
499
15.9k
  return true;
500
15.9k
}
501
502
const char *MOS65XX_insn_name(csh handle, unsigned int id)
503
15.9k
{
504
#ifdef CAPSTONE_DIET
505
  return NULL;
506
#else
507
15.9k
  if (id >= ARR_SIZE(InstructionInfoTable)) {
508
0
    return NULL;
509
0
  }
510
15.9k
  return InstructionInfoTable[id].name;
511
15.9k
#endif
512
15.9k
}
513
514
const char* MOS65XX_reg_name(csh handle, unsigned int reg)
515
35.5k
{
516
#ifdef CAPSTONE_DIET
517
  return NULL;
518
#else
519
35.5k
  if (reg >= ARR_SIZE(RegNames)) {
520
0
    return NULL;
521
0
  }
522
35.5k
  return RegNames[(int)reg];
523
35.5k
#endif
524
35.5k
}
525
526
void MOS65XX_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id)
527
15.9k
{
528
  /* id is cpu_offset + opcode */
529
15.9k
  if (id < ARR_SIZE(OpInfoTable)) {
530
15.9k
    insn->id = OpInfoTable[id].ins;
531
15.9k
  }
532
15.9k
}
533
534
const char *MOS65XX_group_name(csh handle, unsigned int id)
535
5.33k
{
536
#ifdef CAPSTONE_DIET
537
  return NULL;
538
#else
539
5.33k
  if (id >= ARR_SIZE(GroupNames)) {
540
0
    return NULL;
541
0
  }
542
5.33k
  return GroupNames[(int)id];
543
5.33k
#endif
544
5.33k
}