Rizin
unix-like reverse engineering framework and cli tools
6502_il.inc
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2022 Florian Märkl <info@florianmaerkl.de>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
8 // clang-format off
9 
10 #include <rz_il.h>
11 
12 typedef enum {
13  _6502_ADDR_KIND_ADDR,
14  _6502_ADDR_KIND_IMMEDIATE,
15  _6502_ADDR_KIND_ACCUMULATOR
16 } _6502AddrKind;
17 
18 typedef struct {
19  _6502AddrKind kind;
21 } _6502ILAddr;
22 
24 
25 // Different common addressing modes, producing a pure value to be used for the actual instruction
26 // They receive the raw 1 or 2-byte value that follows the opcode byte.
27 // https://www.masswerk.at/6502/6502_instruction_set.html#description
28 
32 static void _6502_il_accumulator(_6502ILAddr *out) {
33  out->kind = _6502_ADDR_KIND_ACCUMULATOR;
34  out->addr = VARG("a");
35 }
36 
40 static void _6502_il_immediate(_6502ILAddr *out, ut16 imm) {
41  out->kind = _6502_ADDR_KIND_IMMEDIATE;
42  out->addr = U8(imm);
43 }
44 
49 static void _6502_il_addr_absolute(_6502ILAddr *out, ut16 imm) {
50  out->kind = _6502_ADDR_KIND_ADDR;
51  out->addr = U16(imm);
52 }
53 
58 static void _6502_il_addr_zero_page_reg(_6502ILAddr *out, ut16 imm, const char *reg) {
59  out->kind = _6502_ADDR_KIND_ADDR;
60  out->addr = UNSIGNED(16, ADD(U8(imm), VARG(reg)));
61 }
62 
67 static void _6502_il_addr_reg(_6502ILAddr *out, ut16 imm, const char *reg) {
68  out->kind = _6502_ADDR_KIND_ADDR;
69  out->addr = ADD(U16(imm), UNSIGNED(16, VARG(reg)));
70 }
71 
75 static void _6502_il_addr_indirect_x(_6502ILAddr *out, ut16 imm) {
76  out->kind = _6502_ADDR_KIND_ADDR;
77  RzILOpPure *zp = ADD(U8(imm), VARG("x"));
78  out->addr = APPEND(
79  LOAD(UNSIGNED(16, ADD(DUP(zp), U8(1)))),
80  LOAD(UNSIGNED(16, zp))
81  );
82 }
83 
87 static void _6502_il_addr_indirect_y(_6502ILAddr *out, ut16 imm) {
88  out->kind = _6502_ADDR_KIND_ADDR;
89  RzILOpPure *zp = U8(imm);
90  RzILOpPure *base = APPEND(
91  LOAD(UNSIGNED(16, ADD(DUP(zp), U8(1)))),
92  LOAD(UNSIGNED(16, zp))
93  );
94  out->addr = ADD(base, UNSIGNED(16, VARG("y")));
95 }
96 
98 // Flags and stack handling
99 
100 static RzILOpEffect *update_flags_nz(RzILOpPure *val) {
101  return SEQ2(
102  SETG("Z", IS_ZERO(val)),
103  SETG("N", MSB(DUP(val)))
104  );
105 }
106 
107 static RzILOpBitVector *status_byte(bool b) {
109  ITE(VARG("N"), U8(0xb0), U8(0x30)), // "unused" bit is always set
110  LOGOR(ITE(VARG("V"), U8(0x40), U8(0)),
111  LOGOR(ITE(VARG("D"), U8(0x08), U8(0)),
112  LOGOR(ITE(VARG("I"), U8(0x04), U8(0)),
113  LOGOR(ITE(VARG("Z"), U8(0x02), U8(0)),
114  ITE(VARG("C"), U8(0x01), U8(0)))))));
115  return b ? LOGOR(r, U8(0x20)) : r;
116 }
117 
118 static RzILOpEffect *status_byte_apply(RzILOpBitVector *sb) {
119  return SEQ6(
120  SETG("N", MSB(sb)),
121  SETG("V", INV(IS_ZERO(LOGAND(DUP(sb), U8(0x40))))),
122  SETG("D", INV(IS_ZERO(LOGAND(DUP(sb), U8(0x08))))),
123  SETG("I", INV(IS_ZERO(LOGAND(DUP(sb), U8(0x04))))),
124  SETG("Z", INV(IS_ZERO(LOGAND(DUP(sb), U8(0x02))))),
125  SETG("C", LSB(DUP(sb)))
126  );
127 }
128 
129 static RzILOpEffect *stack_push(RzILOpBitVector *v) {
130  return SEQ2(
131  STORE(APPEND(U8(1), VARG("sp")), v),
132  SETG("sp", SUB(VARG("sp"), U8(1))));
133 }
134 
135 static RzILOpEffect *stack_pop(const char *varname) {
136  return SEQ2(
137  SETG("sp", ADD(VARG("sp"), U8(1))),
138  SETL(varname, LOAD(APPEND(U8(1), VARG("sp")))));
139 }
140 
142 // The ops themselves, taking a value aquired from one of the functions above,
143 // and (if possible) the information whether this is an immediate value or
144 // an address to dereference.
145 
146 static RzILOpBitVector *do_load(_6502ILAddr *addr) {
147  switch (addr->kind) {
148  case _6502_ADDR_KIND_ADDR:
149  return LOAD(addr->addr);
150  case _6502_ADDR_KIND_IMMEDIATE:
151  return addr->addr;
152  case _6502_ADDR_KIND_ACCUMULATOR:
153  return VARG("a");
154  default:
156  return NULL;
157  }
158 }
159 
164 static _6502ILAddr *dup_addr(_6502ILAddr *addr) {
165  if (addr->addr) {
166  addr->addr = DUP(addr->addr);
167  }
168  return addr;
169 }
170 
171 static RzILOpEffect *do_store(_6502ILAddr *addr, RzILOpBitVector *v) {
172  switch (addr->kind) {
173  case _6502_ADDR_KIND_ADDR:
174  return STORE(addr->addr, v);
175  case _6502_ADDR_KIND_ACCUMULATOR:
176  return SETG("a", v);
177  default:
178  // can't store to immediate
180  return NULL;
181  }
182 }
183 
189 static RzILOpEffect *_6502_il_op_ld(const char *reg, _6502ILAddr *addr) {
190  RzILOpBitVector *v = do_load(addr);
191  return SEQ2(
192  SETG(reg, v),
193  update_flags_nz(VARG(reg))
194  );
195 }
196 
202 static RzILOpEffect *_6502_il_op_st(const char *reg, _6502ILAddr *addr) {
203  return do_store(addr, VARG(reg));
204 }
205 
209 static RzILOpEffect *_6502_il_op_and(_6502ILAddr *addr) {
210  RzILOpBitVector *v = do_load(addr);
211  return SEQ2(
212  SETG("a", LOGAND(VARG("a"), v)),
213  update_flags_nz(VARG("a"))
214  );
215 }
216 
220 static RzILOpEffect *_6502_il_op_ora(_6502ILAddr *addr) {
221  RzILOpBitVector *v = do_load(addr);
222  return SEQ2(
223  SETG("a", LOGOR(VARG("a"), v)),
224  update_flags_nz(VARG("a"))
225  );
226 }
227 
231 static RzILOpEffect *_6502_il_op_eor(_6502ILAddr *addr) {
232  RzILOpBitVector *v = do_load(addr);
233  return SEQ2(
234  SETG("a", LOGXOR(VARG("a"), v)),
235  update_flags_nz(VARG("a"))
236  );
237 }
238 
242 static RzILOpEffect *_6502_il_op_adc(_6502ILAddr *addr) {
243  // TODO: bcd if D
244  return SEQ6(
245  SETL("src", do_load(addr)),
246  SETL("res", ADD(
247  ITE(VARG("C"), U8(1), U8(0)),
248  ADD(VARG("a"), VARL("src")))),
249  update_flags_nz(VARL("res")),
250  SETG("C", ITE(VARG("C"), ULE(VARL("res"), VARG("a")), ULT(VARL("res"), VARG("a")))),
251  SETG("V",
252  AND(
253  INV(XOR(MSB(VARG("a")), MSB(VARL("src")))),
254  XOR(MSB(VARG("a")), MSB(VARL("res"))))),
255  SETG("a", VARL("res")));
256 }
257 
261 static RzILOpEffect *_6502_il_op_sbc(_6502ILAddr *addr) {
262  // TODO: bcd if D
263  return SEQ7(
264  SETL("src", do_load(addr)),
265  SETL("res", SUB(
266  SUB(
267  UNSIGNED(9, VARG("a")),
268  UNSIGNED(9, VARL("src"))),
269  ITE(VARG("C"), UN(9, 0), UN(9, 1)))),
270  SETG("C", INV(MSB(VARL("res")))),
271  SETL("res8", UNSIGNED(8, VARL("res"))),
272  update_flags_nz(VARL("res8")),
273  SETG("V",
274  AND(
275  XOR(MSB(VARG("a")), MSB(VARL("res8"))),
276  XOR(MSB(VARG("a")), MSB(VARL("src"))))),
277  SETG("a", VARL("res8")));
278 }
279 
283 static RzILOpEffect *_6502_il_op_asl(_6502ILAddr *addr) {
284  RzILOpEffect *load = SETL("tmp", do_load(addr));
285  return SEQ5(
286  load,
287  SETG("C", MSB(VARL("tmp"))),
288  SETL("tmp", SHIFTL0(VARL("tmp"), UN(3, 1))),
289  do_store(dup_addr(addr), VARL("tmp")),
290  update_flags_nz(VARL("tmp"))
291  );
292 }
293 
297 static RzILOpEffect *_6502_il_op_lsr(_6502ILAddr *addr) {
298  RzILOpEffect *load = SETL("tmp", do_load(addr));
299  return SEQ6(
300  load,
301  SETG("C", LSB(VARL("tmp"))),
302  SETL("tmp", SHIFTR0(VARL("tmp"), UN(3, 1))),
303  do_store(dup_addr(addr), VARL("tmp")),
304  SETG("Z", IS_ZERO(VARL("tmp"))),
305  SETG("N", IL_FALSE)
306  );
307 }
308 
309 typedef enum {
310  _6502_BRANCH_ON_PLUS = 0x10,
311  _6502_BRANCH_ON_MINUS = 0x30,
312  _6502_BRANCH_ON_OVERFLOW_CLEAR = 0x50,
313  _6502_BRANCH_ON_OVERFLOW_SET = 0x70,
314  _6502_BRANCH_ON_CARRY_CLEAR = 0x90,
315  _6502_BRANCH_ON_CARRY_SET = 0xb0,
316  _6502_BRANCH_ON_NOT_EQUAL = 0xd0,
317  _6502_BRANCH_ON_EQUAL = 0xf0
318 } _6502BranchCond;
319 
320 
331 static RzILOpEffect *_6502_il_op_branch(_6502BranchCond cond, ut16 target) {
332  RzILOpBool *c;
333  switch (cond) {
334  case _6502_BRANCH_ON_PLUS:
335  c = INV(VARG("N"));
336  break;
337  case _6502_BRANCH_ON_MINUS:
338  c = VARG("N");
339  break;
340  case _6502_BRANCH_ON_OVERFLOW_CLEAR:
341  c = INV(VARG("V"));
342  break;
343  case _6502_BRANCH_ON_OVERFLOW_SET:
344  c = VARG("V");
345  break;
346  case _6502_BRANCH_ON_CARRY_CLEAR:
347  c = INV(VARG("C"));
348  break;
349  case _6502_BRANCH_ON_CARRY_SET:
350  c = VARG("C");
351  break;
352  case _6502_BRANCH_ON_NOT_EQUAL:
353  c = INV(VARG("Z"));
354  break;
355  case _6502_BRANCH_ON_EQUAL:
356  c = VARG("Z");
357  break;
358  default:
360  return NULL;
361  }
362  return BRANCH(c, JMP(U16(target)), NOP());
363 }
364 
368 static RzILOpEffect *_6502_il_op_jmp(ut16 target, bool indir) {
369  RzILOpBitVector *addr = U16(target);
370  return JMP(indir ? LOADW(16, addr) : addr);
371 }
372 
376 static RzILOpEffect *_6502_il_op_brk(ut16 offset) {
377  offset += 2;
378  return SEQ6(
379  stack_push(U8((ut8)(offset >> 8))),
380  stack_push(U8((ut8)offset)),
381  stack_push(status_byte(true)),
382  SETG("D", IL_FALSE),
383  SETG("I", IL_TRUE),
384  JMP(LOADW(16, U16(0xfffe)))
385  );
386 }
387 
391 static RzILOpEffect *_6502_il_op_jsr(ut16 target, ut64 offset) {
392  offset += 2; // yes, this is **inside** the jsr.
393  return SEQ3(
394  stack_push(U8((ut8)(offset >> 8))),
395  stack_push(U8((ut8)offset)),
396  JMP(U16(target)));
397 }
398 
402 static RzILOpEffect *_6502_il_op_rti() {
403  return SEQ5(
404  stack_pop("sr"),
405  status_byte_apply(VARL("sr")),
406  stack_pop("pcl"),
407  stack_pop("pch"),
408  JMP(APPEND(VARL("pch"), VARL("pcl"))));
409 }
410 
414 static RzILOpEffect *_6502_il_op_rts() {
415  return SEQ3(
416  stack_pop("pcl"),
417  stack_pop("pch"),
418  JMP(ADD(APPEND(VARL("pch"), VARL("pcl")), U16(1))));
419 }
420 
424 static RzILOpEffect *_6502_il_op_bit(_6502ILAddr *addr) {
425  return SEQ4(
426  SETL("tmp", do_load(addr)),
427  SETG("N", MSB(VARL("tmp"))),
428  SETG("V", MSB(UNSIGNED(7, VARL("tmp")))),
429  SETG("Z", IS_ZERO(LOGAND(VARL("tmp"), VARG("a"))))
430  );
431 }
432 
433 typedef enum {
434  _6502_FLAG_OP_SET_I = 0x78,
435  _6502_FLAG_OP_CLEAR_I = 0x58,
436  _6502_FLAG_OP_SET_C = 0x38,
437  _6502_FLAG_OP_CLEAR_C = 0x18,
438  _6502_FLAG_OP_SET_D = 0xf8,
439  _6502_FLAG_OP_CLEAR_D = 0xd8,
440  _6502_FLAG_OP_CLEAR_V = 0xb8
441 } _6502FlagOp;
442 
452 static RzILOpEffect *_6502_il_op_flag(_6502FlagOp op) {
453  switch (op) {
454  case _6502_FLAG_OP_SET_I:
455  return SETG("I", IL_TRUE);
456  case _6502_FLAG_OP_CLEAR_I:
457  return SETG("I", IL_FALSE);
458  case _6502_FLAG_OP_SET_C:
459  return SETG("C", IL_TRUE);
460  case _6502_FLAG_OP_CLEAR_C:
461  return SETG("C", IL_FALSE);
462  case _6502_FLAG_OP_SET_D:
463  return SETG("D", IL_TRUE);
464  case _6502_FLAG_OP_CLEAR_D:
465  return SETG("D", IL_FALSE);
466  case _6502_FLAG_OP_CLEAR_V:
467  return SETG("V", IL_FALSE);
468  default:
470  return NULL;
471  }
472 }
473 
479 static RzILOpEffect *_6502_il_op_cmp(const char *reg, _6502ILAddr *addr) {
480  return SEQ3(
481  SETL("tmp", SUB(UNSIGNED(9, VARG(reg)), UNSIGNED(9, do_load(addr)))),
482  SETG("C", INV(MSB(VARL("tmp")))),
483  update_flags_nz(UNSIGNED(8, VARL("tmp")))
484  );
485 }
486 
493 static RzILOpEffect *_6502_il_op_inc_reg(const char *reg, bool inc) {
494  RzILOpBitVector *v = inc ? ADD(VARG(reg), U8(1)) : SUB(VARG(reg), U8(1));
495  return SEQ2(
496  SETG(reg, v),
497  update_flags_nz(UNSIGNED(8, VARG(reg)))
498  );
499 }
500 
505 static RzILOpEffect *_6502_il_op_inc(_6502ILAddr *addr, bool inc) {
506  RzILOpBitVector *v = inc ? ADD(do_load(addr), U8(1)) : SUB(do_load(addr), U8(1));
507  return SEQ3(
508  SETL("tmp", v),
509  do_store(dup_addr(addr), VARL("tmp")),
510  update_flags_nz(VARL("tmp"))
511  );
512 }
513 
517 static RzILOpEffect *_6502_il_op_pha() {
518  return stack_push(VARG("a"));
519 }
520 
524 static RzILOpEffect *_6502_il_op_php() {
525  return stack_push(status_byte(true));
526 }
527 
531 static RzILOpEffect *_6502_il_op_pla() {
532  return SEQ3(
533  stack_pop("tmp"),
534  SETG("a", VARL("tmp")),
535  update_flags_nz(VARL("tmp")));
536 }
537 
541 static RzILOpEffect *_6502_il_op_plp() {
542  return SEQ2(
543  stack_pop("tmp"),
544  status_byte_apply(VARL("tmp")));
545 }
546 
550 static RzILOpEffect *_6502_il_op_rol(_6502ILAddr *addr) {
551  RzILOpEffect *load = SETL("tmp", do_load(addr));
552  return SEQ5(
553  load,
554  SETL("res", LOGOR(SHIFTL0(VARL("tmp"), UN(3, 1)), ITE(VARG("C"), U8(1), U8(0)))),
555  SETG("C", MSB(VARL("tmp"))),
556  do_store(dup_addr(addr), VARL("res")),
557  update_flags_nz(VARL("res")));
558 }
559 
563 static RzILOpEffect *_6502_il_op_ror(_6502ILAddr *addr) {
564  RzILOpEffect *load = SETL("tmp", do_load(addr));
565  return SEQ5(
566  load,
567  SETL("res", LOGOR(SHIFTR0(VARL("tmp"), UN(3, 1)), ITE(VARG("C"), U8(0x80), U8(0)))),
568  SETG("C", LSB(VARL("tmp"))),
569  do_store(dup_addr(addr), VARL("res")),
570  update_flags_nz(VARL("res")));
571 }
572 
581 static RzILOpEffect *_6502_il_op_transfer(const char *dst, const char *src, bool update_flags) {
582  RzILOpEffect *tf = SETG(dst, VARG(src));
583  return update_flags ? SEQ2(tf, update_flags_nz(VARG(dst))) : tf;
584 }
585 
587 // clang-format on
#define imm
static void update_flags(RzAnalysisOp *op, int flags)
Definition: analysis_v810.c:20
lzma_index * src
Definition: index.h:567
#define BRANCH
ut16 val
Definition: armass64_const.h:6
static SblHeader sb
Definition: bin_mbn.c:26
const lzma_allocator const uint8_t size_t uint8_t * out
Definition: block.h:528
#define NULL
Definition: cris-opc.c:27
#define r
Definition: crypto_rc6.c:12
uint16_t ut16
const char * v
Definition: dsignal.c:12
voidpf uLong offset
Definition: ioapi.h:144
#define reg(n)
uint8_t ut8
Definition: lh5801.h:11
#define AND
Definition: ansidecl.h:254
char * dst
Definition: lz4.h:724
void * load(const char *name, size_t *len)
Definition: pufftest.c:60
#define XOR
Definition: rsp_idec.c:212
#define NOP
Definition: rsp_idec.c:182
#define ADD
Definition: rsp_idec.c:200
#define rz_warn_if_reached()
Definition: rz_assert.h:29
Syntax Macros for RzIL Lifting.
#define LOAD(addr)
#define LOGOR(x, y)
#define IS_ZERO(x)
#define SHIFTR0(v, dist)
#define LOADW(n, addr)
#define U8(val)
#define U16(val)
#define LSB(x)
#define APPEND(high, low)
#define UN(l, val)
#define IL_FALSE
#define ULE(x, y)
#define SEQ3(e0, e1, e2)
#define SEQ5(e0, e1, e2, e3, e4)
#define SETL(name, v)
#define SEQ4(e0, e1, e2, e3)
#define IL_TRUE
#define INV(x)
#define SEQ2(e0, e1)
#define ITE(c, t, f)
#define LOGXOR(x, y)
#define UNSIGNED(n, x)
#define SEQ7(e0, e1, e2, e3, e4, e5, e6)
#define SEQ6(e0, e1, e2, e3, e4, e5)
#define SHIFTL0(v, dist)
#define VARL(name)
#define MSB(x)
#define LOGAND(x, y)
#define STORE(addr, val)
#define VARG(name)
#define ULT(x, y)
#define JMP(tgt)
#define SETG(name, v)
@ DUP
Definition: packet.c:12
#define SUB(ns, call)
#define b(i)
Definition: sha256.c:42
#define c(i)
Definition: sha256.c:43
#define cond(bop, top, mask, flags)
An IL op performing a pure computation, 'a pure.
Definition: dis.c:32
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
static int addr
Definition: z80asm.c:58