1 /* radare - LGPL - Copyright 2019-2020 - condret, riq */
2 
3 /* 6502 info taken from http://unusedino.de/ec64/technical/aay/c64/bchrt651.htm
4  *
5  * Mnemonics logic based on:
6  *	http://homepage.ntlworld.com/cyborgsystems/CS_Main/6502/6502.htm
7  * and:
8  *	http://vice-emu.sourceforge.net/
9  */
10 
11 #include <string.h>
12 #include <r_types.h>
13 #include <r_lib.h>
14 #include <r_asm.h>
15 #include <r_anal.h>
16 #include "../../asm/arch/snes/snes_op_table.h"
17 
18 enum {
19 	_6502_FLAGS_C = (1 << 0),
20 	_6502_FLAGS_B = (1 << 1),
21 	_6502_FLAGS_Z = (1 << 2),
22 	_6502_FLAGS_N = (1 << 3),
23 
24 	_6502_FLAGS_NZ = (_6502_FLAGS_Z | _6502_FLAGS_N),
25 	_6502_FLAGS_CNZ = (_6502_FLAGS_C | _6502_FLAGS_Z | _6502_FLAGS_N),
26 	_6502_FLAGS_BNZ = (_6502_FLAGS_B | _6502_FLAGS_Z | _6502_FLAGS_N),
27 };
28 
_6502_anal_update_flags(RAnalOp * op,int flags)29 static void _6502_anal_update_flags(RAnalOp *op, int flags) {
30 	/* FIXME: 9,$b instead of 8,$b to prevent the bug triggered by: A = 0 - 0xff - 1 */
31 	if (flags & _6502_FLAGS_B) {
32 		r_strbuf_append (&op->esil, ",9,$b,C,:=");
33 	}
34 	if (flags & _6502_FLAGS_C) {
35 		r_strbuf_append (&op->esil, ",7,$c,C,:=");
36 	}
37 	if (flags & _6502_FLAGS_Z) {
38 		r_strbuf_append (&op->esil, ",$z,Z,:=");
39 	}
40 	if (flags & _6502_FLAGS_N) {
41 		r_strbuf_append (&op->esil, ",7,$s,N,:=");
42 	}
43 }
44 
45 /* ORA, AND, EOR, ADC, STA, LDA, CMP and SBC share this pattern */
_6502_anal_esil_get_addr_pattern1(RAnalOp * op,const ut8 * data,int len,char * addrbuf,int addrsize)46 static void _6502_anal_esil_get_addr_pattern1(RAnalOp *op, const ut8* data, int len, char* addrbuf, int addrsize) {
47 	if (len < 1) {
48 		return;
49 	}
50 	// turn off bits 5, 6 and 7
51 	switch (data[0] & 0x1f) { // 0x1f = b00111111
52 	case 0x09: // op #$ff
53 		op->cycles = 2;
54 		snprintf (addrbuf, addrsize,"0x%02x", (len > 1)? data[1]: 0);
55 		break;
56 	case 0x05: // op $ff
57 		op->cycles = 3;
58 		snprintf (addrbuf, addrsize,"0x%02x", (len > 1)? data[1]: 0);
59 		break;
60 	case 0x15: // op $ff,x
61 		op->cycles = 4;
62 		snprintf (addrbuf, addrsize,"x,0x%02x,+", (len > 1)? data[1]: 0);
63 		break;
64 	case 0x0d: // op $ffff
65 		op->cycles = 4;
66 		snprintf (addrbuf, addrsize,"0x%04x", (len > 2) ? (data[1] | data[2] << 8): 0);
67 		break;
68 	case 0x1d: // op $ffff,x
69 		// FIXME: Add 1 if page boundary is crossed.
70 		op->cycles = 4;
71 		snprintf (addrbuf, addrsize,"x,0x%04x,+", (len > 2) ? data[1] | data[2] << 8: 0);
72 		break;
73 	case 0x19: // op $ffff,y
74 		// FIXME: Add 1 if page boundary is crossed.
75 		op->cycles = 4;
76 		snprintf (addrbuf, addrsize,"y,0x%04x,+", (len > 2)? data[1] | data[2] << 8: 0);
77 		break;
78 	case 0x01: // op ($ff,x)
79 		op->cycles = 6;
80 		snprintf (addrbuf, addrsize,"x,0x%02x,+,[2]", (len > 1)? data[1]: 0);
81 		break;
82 	case 0x11: // op ($ff),y
83 		// FIXME: Add 1 if page boundary is crossed.
84 		op->cycles = 5;
85 		snprintf (addrbuf, addrsize,"y,0x%02x,[2],+", (len > 1) ? data[1]: 0);
86 		break;
87 	}
88 }
89 
90 /* ASL, ROL, LSR, ROR, STX, LDX, DEC and INC share this pattern */
_6502_anal_esil_get_addr_pattern2(RAnalOp * op,const ut8 * data,int len,char * addrbuf,int addrsize,char reg)91 static void _6502_anal_esil_get_addr_pattern2(RAnalOp *op, const ut8* data, int len, char* addrbuf, int addrsize, char reg) {
92 	// turn off bits 5, 6 and 7
93 	if (len < 1) {
94 		return;
95 	}
96 	switch (data[0] & 0x1f) { // 0x1f = b00111111
97 	case 0x02: // op #$ff
98 		op->cycles = 2;
99 		snprintf (addrbuf, addrsize, "0x%02x", (len>1)? data[1]: 0);
100 		break;
101 	case 0x0a: //op a
102 		op->cycles = 2;
103 		snprintf (addrbuf, addrsize, "a");
104 		break;
105 	case 0x06: // op $ff
106 		op->cycles = 5;
107 		snprintf (addrbuf, addrsize, "0x%02x", (len>1)?data[1]:0);
108 		break;
109 	case 0x16: // op $ff,x
110 		op->cycles = 6;
111 		snprintf (addrbuf, addrsize, "%c,0x%02x,+", reg, (len >1)? data[1]:0);
112 		break;
113 	case 0x0e: // op $ffff
114 		op->cycles = 6;
115 		snprintf (addrbuf, addrsize, "0x%04x", (len>2)? data[1] | data[2] << 8: 0);
116 		break;
117 	case 0x1e: // op $ffff,x
118 		op->cycles = 7;
119 		snprintf (addrbuf, addrsize, "%c,0x%04x,+", reg, (len>2)? data[1] | data[2] << 8: 0);
120 		break;
121 	}
122 }
123 
124 /* BIT, JMP, JMP(), STY, LDY, CPY, and CPX share this pattern */
_6502_anal_esil_get_addr_pattern3(RAnalOp * op,const ut8 * data,int len,char * addrbuf,int addrsize,char reg)125 static void _6502_anal_esil_get_addr_pattern3(RAnalOp *op, const ut8* data, int len, char* addrbuf, int addrsize, char reg) {
126 	// turn off bits 5, 6 and 7
127 	if (len < 1) {
128 		return;
129 	}
130 	switch (data[0] & 0x1f) { // 0x1f = b00111111
131 	case 0x00: // op #$ff
132 		op->cycles = 2;
133 		snprintf (addrbuf, addrsize, "0x%02x", (len > 1) ? data[1]: 0);
134 		break;
135 	case 0x08: //op a
136 		op->cycles = 2;
137 		snprintf (addrbuf, addrsize, "a");
138 		break;
139 	case 0x04: // op $ff
140 		op->cycles = 5;
141 		snprintf (addrbuf, addrsize, "0x%02x", (len > 1)? data[1]: 0);
142 		break;
143 	case 0x14: // op $ff,x
144 		op->cycles = 6;
145 		snprintf (addrbuf, addrsize, "%c,0x%02x,+", reg, (len>1)? data[1]:0);
146 		break;
147 	case 0x0c: // op $ffff
148 		op->cycles = 6;
149 		snprintf (addrbuf, addrsize, "0x%04x", (len>2)? data[1] | data[2] << 8: 0);
150 		break;
151 	case 0x1c: // op $ffff,x
152 		op->cycles = 7;
153 		snprintf (addrbuf, addrsize, "%c,0x%04x,+", reg, (len>2)? data[1] | data[2] << 8: 0);
154 		break;
155 	}
156 }
157 
_6502_anal_esil_ccall(RAnalOp * op,ut8 data0)158 static void _6502_anal_esil_ccall(RAnalOp *op, ut8 data0) {
159 	char *flag;
160 	switch (data0) {
161 	case 0x10: // bpl $ffff
162 		flag = "N,!";
163 		break;
164 	case 0x30: // bmi $ffff
165 		flag = "N";
166 		break;
167 	case 0x50: // bvc $ffff
168 		flag = "V,!";
169 		break;
170 	case 0x70: // bvs $ffff
171 		flag = "V";
172 		break;
173 	case 0x90: // bcc $ffff
174 		flag = "C,!";
175 		break;
176 	case 0xb0: // bcs $ffff
177 		flag = "C";
178 		break;
179 	case 0xd0: // bne $ffff
180 		flag = "Z,!";
181 		break;
182 	case 0xf0: // beq $ffff
183 		flag = "Z";
184 		break;
185 	default:
186 		// FIXME: should not happen
187 		flag = "unk";
188 		break;
189 	}
190 	r_strbuf_setf (&op->esil, "%s,?{,0x%04x,pc,=,}", flag, (ut32)(op->jump & 0xffff));
191 }
192 
193 // inc register
_6502_anal_esil_inc_reg(RAnalOp * op,ut8 data0,char * sign)194 static void _6502_anal_esil_inc_reg(RAnalOp *op, ut8 data0, char* sign) {
195 	char* reg = NULL;
196 
197 	switch(data0) {
198 	case 0xe8: // inx
199 	case 0xca: // dex
200 		reg = "x";
201 		break;
202 	case 0xc8: // iny
203 	case 0x88: // dey
204 		reg = "y";
205 		break;
206 	}
207 	r_strbuf_setf (&op->esil, "%s,%s%s=", reg, sign, sign);
208 	_6502_anal_update_flags (op, _6502_FLAGS_NZ);
209 }
210 
_6502_anal_esil_mov(RAnalOp * op,ut8 data0)211 static void _6502_anal_esil_mov(RAnalOp *op, ut8 data0) {
212 	const char* src="unk";
213 	const char* dst="unk";
214 	switch(data0) {
215 	case 0xaa: // tax
216 		src="a";
217 		dst="x";
218 		break;
219 	case 0x8a: // txa
220 		src="x";
221 		dst="a";
222 		break;
223 	case 0xa8: // tay
224 		src="a";
225 		dst="y";
226 		break;
227 	case 0x98: // tya
228 		src="y";
229 		dst="a";
230 		break;
231 	case 0x9a: // txs
232 		src="x";
233 		dst="sp";
234 		break;
235 	case 0xba: // tsx
236 		src="sp";
237 		dst="x";
238 		break;
239 	default:
240 		// FIXME: should not happen
241 		break;
242 	}
243 	r_strbuf_setf (&op->esil, "%s,%s,=",src,dst);
244 
245 	// don't update NZ on txs
246 	if (data0 != 0x9a) {
247 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
248 	}
249 }
250 
_6502_anal_esil_push(RAnalOp * op,ut8 data0)251 static void _6502_anal_esil_push(RAnalOp *op, ut8 data0) {
252 	// case 0x08: // php
253 	// case 0x48: // pha
254 	char *reg = (data0==0x08) ? "flags" : "a";
255 	// stack is on page one: sp + 0x100
256 	r_strbuf_setf (&op->esil, "%s,sp,0x100,+,=[1],sp,--=", reg);
257 }
258 
_6502_anal_esil_pop(RAnalOp * op,ut8 data0)259 static void _6502_anal_esil_pop(RAnalOp *op, ut8 data0) {
260 	// case 0x28: // plp
261 	// case 0x68: // pla
262 	char *reg = (data0==0x28) ? "flags" : "a";
263 	// stack is on page one: sp + 0x100
264 	r_strbuf_setf (&op->esil, "sp,++=,sp,0x100,+,[1],%s,=", reg);
265 
266 	if (data0 == 0x68) {
267 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
268 	}
269 }
270 
_6502_anal_esil_flags(RAnalOp * op,ut8 data0)271 static void _6502_anal_esil_flags(RAnalOp *op, ut8 data0) {
272 	int enabled=0;
273 	char flag ='u';
274 	switch(data0) {
275 	case 0x78: // sei
276 		enabled = 1;
277 		flag = 'I';
278 		break;
279 	case 0x58: // cli
280 		enabled = 0;
281 		flag = 'I';
282 		break;
283 	case 0x38: // sec
284 		enabled = 1;
285 		flag = 'C';
286 		break;
287 	case 0x18: // clc
288 		enabled = 0;
289 		flag = 'C';
290 		break;
291 	case 0xf8: // sed
292 		enabled = 1;
293 		flag = 'D';
294 		break;
295 	case 0xd8: // cld
296 		enabled = 0;
297 		flag = 'D';
298 		break;
299 	case 0xb8: // clv
300 		enabled = 0;
301 		flag = 'V';
302 		break;
303 		break;
304 	}
305 	r_strbuf_setf (&op->esil, "%d,%c,=", enabled, flag);
306 }
307 
_6502_op(RAnal * anal,RAnalOp * op,ut64 addr,const ut8 * data,int len,RAnalOpMask mask)308 static int _6502_op(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int len, RAnalOpMask mask) {
309 	char addrbuf[64];
310 	const int buffsize = sizeof (addrbuf) - 1;
311 	if (len < 1) {
312 		return -1;
313 	}
314 
315 	op->size = snes_op_get_size (1, 1, &snes_op[data[0]]);	//snes-arch is similar to nes/6502
316 	op->addr = addr;
317 	op->type = R_ANAL_OP_TYPE_UNK;
318 	op->id = data[0];
319 	r_strbuf_init (&op->esil);
320 	switch (data[0]) {
321 	case 0x02:
322 	case 0x03:
323 	case 0x04:
324 	case 0x07:
325 	case 0x0b:
326 	case 0x0c:
327 	case 0x0f:
328 	case 0x12:
329 	case 0x13:
330 	case 0x14:
331 	case 0x17:
332 	case 0x1a:
333 	case 0x1b:
334 	case 0x1c:
335 	case 0x1f:
336 	case 0x22:
337 	case 0x23:
338 	case 0x27:
339 	case 0x2b:
340 	case 0x2f:
341 	case 0x32:
342 	case 0x33:
343 	case 0x34:
344 	case 0x37:
345 	case 0x3a:
346 	case 0x3b:
347 	case 0x3c:
348 	case 0x3f:
349 	case 0x42:
350 	case 0x43:
351 	case 0x44:
352 	case 0x47:
353 	case 0x4b:
354 	case 0x4f:
355 	case 0x52:
356 	case 0x53:
357 	case 0x54:
358 	case 0x57:
359 	case 0x5a:
360 	case 0x5b:
361 	case 0x5c:
362 	case 0x5f:
363 	case 0x62:
364 	case 0x63:
365 	case 0x64:
366 	case 0x67:
367 	case 0x6b:
368 	case 0x6f:
369 	case 0x72:
370 	case 0x73:
371 	case 0x74:
372 	case 0x77:
373 	case 0x7a:
374 	case 0x7b:
375 	case 0x7c:
376 	case 0x7f:
377 	case 0x80:
378 	case 0x82:
379 	case 0x83:
380 	case 0x87:
381 	case 0x89:
382 	case 0x8b:
383 	case 0x8f:
384 	case 0x92:
385 	case 0x93:
386 	case 0x97:
387 	case 0x9b:
388 	case 0x9c:
389 	case 0x9e:
390 	case 0x9f:
391 	case 0xa3:
392 	case 0xa7:
393 	case 0xab:
394 	case 0xaf:
395 	case 0xb2:
396 	case 0xb3:
397 	case 0xb7:
398 	case 0xbb:
399 	case 0xbf:
400 	case 0xc2:
401 	case 0xc3:
402 	case 0xc7:
403 	case 0xcb:
404 	case 0xcf:
405 	case 0xd2:
406 	case 0xd3:
407 	case 0xd4:
408 	case 0xd7:
409 	case 0xda:
410 	case 0xdb:
411 	case 0xdc:
412 	case 0xdf:
413 	case 0xe2:
414 	case 0xe3:
415 	case 0xe7:
416 	case 0xeb:
417 	case 0xef:
418 	case 0xf2:
419 	case 0xf3:
420 	case 0xf4:
421 	case 0xf7:
422 	case 0xfa:
423 	case 0xfb:
424 	case 0xfc:
425 	case 0xff:
426 		// undocumented or not-implemented opcodes for 6502.
427 		// some of them might be implemented in 65816
428 		op->size = 1;
429 		op->type = R_ANAL_OP_TYPE_ILL;
430 		break;
431 
432 	// BRK
433 	case 0x00: // brk
434 		op->cycles = 7;
435 		op->type = R_ANAL_OP_TYPE_SWI;
436 		// override 65816 code which seems to be wrong: size is 1, but pc = pc + 2
437 		op->size = 1;
438 		// PC + 2 to Stack, P to Stack  B=1 D=0 I=1. "B" is not a flag. Only its bit is pushed on the stack
439 		// PC was already incremented by one at this point. Needs to incremented once more
440 		// New PC is Interrupt Vector: $fffe. (FIXME: Confirm this is valid for all 6502)
441 		r_strbuf_set (&op->esil, ",1,I,=,0,D,=,flags,0x10,|,0x100,sp,+,=[1],pc,1,+,0xfe,sp,+,=[2],3,sp,-=,0xfffe,[2],pc,=");
442 		break;
443 
444 	// FLAGS
445 	case 0x78: // sei
446 	case 0x58: // cli
447 	case 0x38: // sec
448 	case 0x18: // clc
449 	case 0xf8: // sed
450 	case 0xd8: // cld
451 	case 0xb8: // clv
452 		op->cycles = 2;
453 		// FIXME: what opcode for this?
454 		op->type = R_ANAL_OP_TYPE_NOP;
455 		_6502_anal_esil_flags (op, data[0]);
456 		break;
457 	// BIT
458 	case 0x24: // bit $ff
459 	case 0x2c: // bit $ffff
460 		op->type = R_ANAL_OP_TYPE_MOV;
461 		_6502_anal_esil_get_addr_pattern3 (op, data, len, addrbuf, buffsize, 0);
462 		r_strbuf_setf (&op->esil, "%s,[1],0x80,&,!,!,N,=,%s,[1],0x40,&,!,!,V,=,a,%s,[1],&,0xff,&,!,Z,=", addrbuf, addrbuf, addrbuf);
463 		break;
464 	// ADC
465 	case 0x69: // adc #$ff
466 	case 0x65: // adc $ff
467 	case 0x75: // adc $ff,x
468 	case 0x6d: // adc $ffff
469 	case 0x7d: // adc $ffff,x
470 	case 0x79: // adc $ffff,y
471 	case 0x61: // adc ($ff,x)
472 	case 0x71: // adc ($ff,y)
473 		// FIXME: update V
474 		// FIXME: support BCD mode
475 		op->type = R_ANAL_OP_TYPE_ADD;
476 		_6502_anal_esil_get_addr_pattern1 (op, data, len, addrbuf, buffsize);
477 		if (data[0] == 0x69) { // immediate mode
478 			r_strbuf_setf (&op->esil, "%s,a,+=,7,$c,C,a,+=,7,$c,|,C,:=", addrbuf);
479 		} else {
480 			r_strbuf_setf (&op->esil, "%s,[1],a,+=,7,$c,C,a,+=,7,$c,|,C,:=", addrbuf);
481 		}
482 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
483 		// fix Z
484 		r_strbuf_append (&op->esil, ",a,a,=,$z,Z,:=");
485 		break;
486 	// SBC
487 	case 0xe9: // sbc #$ff
488 	case 0xe5: // sbc $ff
489 	case 0xf5: // sbc $ff,x
490 	case 0xed: // sbc $ffff
491 	case 0xfd: // sbc $ffff,x
492 	case 0xf9: // sbc $ffff,y
493 	case 0xe1: // sbc ($ff,x)
494 	case 0xf1: // sbc ($ff,y)
495 		// FIXME: update V
496 		// FIXME: support BCD mode
497 		op->type = R_ANAL_OP_TYPE_SUB;
498 		_6502_anal_esil_get_addr_pattern1 (op, data, len, addrbuf, buffsize);
499 		if (data[0] == 0xe9) { // immediate mode
500 			r_strbuf_setf (&op->esil, "C,!,%s,+,a,-=", addrbuf);
501 		} else {
502 			r_strbuf_setf (&op->esil, "C,!,%s,[1],+,a,-=", addrbuf);
503 		}
504 		_6502_anal_update_flags (op, _6502_FLAGS_BNZ);
505 		// fix Z and revert C
506 		r_strbuf_append (&op->esil, ",a,a,=,$z,Z,:=,C,!=");
507 		break;
508 	// ORA
509 	case 0x09: // ora #$ff
510 	case 0x05: // ora $ff
511 	case 0x15: // ora $ff,x
512 	case 0x0d: // ora $ffff
513 	case 0x1d: // ora $ffff,x
514 	case 0x19: // ora $ffff,y
515 	case 0x01: // ora ($ff,x)
516 	case 0x11: // ora ($ff),y
517 		op->type = R_ANAL_OP_TYPE_OR;
518 		_6502_anal_esil_get_addr_pattern1 (op, data, len, addrbuf, buffsize);
519 		if (data[0] == 0x09) { // immediate mode
520 			r_strbuf_setf (&op->esil, "%s,a,|=", addrbuf);
521 		} else {
522 			r_strbuf_setf (&op->esil, "%s,[1],a,|=", addrbuf);
523 		}
524 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
525 		break;
526 	// AND
527 	case 0x29: // and #$ff
528 	case 0x25: // and $ff
529 	case 0x35: // and $ff,x
530 	case 0x2d: // and $ffff
531 	case 0x3d: // and $ffff,x
532 	case 0x39: // and $ffff,y
533 	case 0x21: // and ($ff,x)
534 	case 0x31: // and ($ff),y
535 		op->type = R_ANAL_OP_TYPE_AND;
536 		_6502_anal_esil_get_addr_pattern1 (op, data, len, addrbuf, buffsize);
537 		if (data[0] == 0x29) { // immediate mode
538 			r_strbuf_setf (&op->esil, "%s,a,&=", addrbuf);
539 		} else {
540 			r_strbuf_setf (&op->esil, "%s,[1],a,&=", addrbuf);
541 		}
542 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
543 		break;
544 	// EOR
545 	case 0x49: // eor #$ff
546 	case 0x45: // eor $ff
547 	case 0x55: // eor $ff,x
548 	case 0x4d: // eor $ffff
549 	case 0x5d: // eor $ffff,x
550 	case 0x59: // eor $ffff,y
551 	case 0x41: // eor ($ff,x)
552 	case 0x51: // eor ($ff),y
553 		op->type = R_ANAL_OP_TYPE_XOR;
554 		_6502_anal_esil_get_addr_pattern1 (op, data, len, addrbuf, buffsize);
555 		if (data[0] == 0x49) { // immediate mode
556 			r_strbuf_setf (&op->esil, "%s,a,^=", addrbuf);
557 		} else {
558 			r_strbuf_setf (&op->esil, "%s,[1],a,^=", addrbuf);
559 		}
560 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
561 		break;
562 	// ASL
563 	case 0x0a: // asl a
564 	case 0x06: // asl $ff
565 	case 0x16: // asl $ff,x
566 	case 0x0e: // asl $ffff
567 	case 0x1e: // asl $ffff,x
568 		op->type = R_ANAL_OP_TYPE_SHL;
569 		if (data[0] == 0x0a) {
570 			r_strbuf_set (&op->esil, "1,a,<<=,7,$c,C,:=,a,a,=");
571 		} else  {
572 			_6502_anal_esil_get_addr_pattern2 (op, data, len, addrbuf, buffsize, 'x');
573 			r_strbuf_setf (&op->esil, "1,%s,[1],<<,%s,=[1],7,$c,C,:=", addrbuf, addrbuf);
574 		}
575 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
576 		break;
577 	// LSR
578 	case 0x4a: // lsr a
579 	case 0x46: // lsr $ff
580 	case 0x56: // lsr $ff,x
581 	case 0x4e: // lsr $ffff
582 	case 0x5e: // lsr $ffff,x
583 		op->type = R_ANAL_OP_TYPE_SHR;
584 		if (data[0] == 0x4a) {
585 			r_strbuf_set (&op->esil, "1,a,&,C,=,1,a,>>=");
586 		} else {
587 			_6502_anal_esil_get_addr_pattern2 (op, data, len, addrbuf, buffsize, 'x');
588 			r_strbuf_setf (&op->esil, "1,%s,[1],&,C,=,1,%s,[1],>>,%s,=[1]", addrbuf, addrbuf, addrbuf);
589 		}
590 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
591 		break;
592 	// ROL
593 	case 0x2a: // rol a
594 	case 0x26: // rol $ff
595 	case 0x36: // rol $ff,x
596 	case 0x2e: // rol $ffff
597 	case 0x3e: // rol $ffff,x
598 		op->type = R_ANAL_OP_TYPE_ROL;
599 		if (data[0] == 0x2a) {
600 			r_strbuf_set (&op->esil, "1,a,<<,C,|,a,=,7,$c,C,:=,a,a,=");
601 		} else {
602 			_6502_anal_esil_get_addr_pattern2 (op, data, len, addrbuf, buffsize, 'x');
603 			r_strbuf_setf (&op->esil, "1,%s,[1],<<,C,|,%s,=[1],7,$c,C,:=", addrbuf, addrbuf);
604 		}
605 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
606 		break;
607 	// ROR
608 	case 0x6a: // ror a
609 	case 0x66: // ror $ff
610 	case 0x76: // ror $ff,x
611 	case 0x6e: // ror $ffff
612 	case 0x7e: // ror $ffff,x
613 		// uses N as temporary to hold C value. but in fact,
614 		// it is not temporary since in all ROR ops, N will have the value of C
615 		op->type = R_ANAL_OP_TYPE_ROR;
616 		if (data[0] == 0x6a) {
617 			r_strbuf_set (&op->esil, "C,N,=,1,a,&,C,=,1,a,>>,7,N,<<,|,a,=");
618 		} else {
619 			_6502_anal_esil_get_addr_pattern2 (op, data, len, addrbuf, buffsize, 'x');
620 			r_strbuf_setf (&op->esil, "C,N,=,1,%s,[1],&,C,=,1,%s,[1],>>,7,N,<<,|,%s,=[1]", addrbuf, addrbuf, addrbuf);
621 		}
622 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
623 		break;
624 	// INC
625 	case 0xe6: // inc $ff
626 	case 0xf6: // inc $ff,x
627 	case 0xee: // inc $ffff
628 	case 0xfe: // inc $ffff,x
629 		op->type = R_ANAL_OP_TYPE_STORE;
630 		_6502_anal_esil_get_addr_pattern2 (op, data, len, addrbuf, buffsize, 'x');
631 		r_strbuf_setf (&op->esil, "%s,++=[1]", addrbuf);
632 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
633 		break;
634 	// DEC
635 	case 0xc6: // dec $ff
636 	case 0xd6: // dec $ff,x
637 	case 0xce: // dec $ffff
638 	case 0xde: // dec $ffff,x
639 		op->type = R_ANAL_OP_TYPE_STORE;
640 		_6502_anal_esil_get_addr_pattern2 (op, data, len, addrbuf, buffsize, 'x');
641 		r_strbuf_setf (&op->esil, "%s,--=[1]", addrbuf);
642 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
643 		break;
644 	// INX, INY
645 	case 0xe8: // inx
646 	case 0xc8: // iny
647 		op->cycles = 2;
648 		op->type = R_ANAL_OP_TYPE_STORE;
649 		_6502_anal_esil_inc_reg (op, data[0], "+");
650 		break;
651 	// DEX, DEY
652 	case 0xca: // dex
653 	case 0x88: // dey
654 		op->cycles = 2;
655 		op->type = R_ANAL_OP_TYPE_STORE;
656 		_6502_anal_esil_inc_reg (op, data[0], "-");
657 		break;
658 	// CMP
659 	case 0xc9: // cmp #$ff
660 	case 0xc5: // cmp $ff
661 	case 0xd5: // cmp $ff,x
662 	case 0xcd: // cmp $ffff
663 	case 0xdd: // cmp $ffff,x
664 	case 0xd9: // cmp $ffff,y
665 	case 0xc1: // cmp ($ff,x)
666 	case 0xd1: // cmp ($ff),y
667 		op->type = R_ANAL_OP_TYPE_CMP;
668 		_6502_anal_esil_get_addr_pattern1 (op, data, len, addrbuf, buffsize);
669 		if (data[0] == 0xc9) { // immediate mode
670 			r_strbuf_setf (&op->esil, "%s,a,==", addrbuf);
671 		} else {
672 			r_strbuf_setf (&op->esil, "%s,[1],a,==", addrbuf);
673 		}
674 		_6502_anal_update_flags (op, _6502_FLAGS_BNZ);
675 		// invert C, since C=1 when A-M >= 0
676 		r_strbuf_append (&op->esil, ",C,!,C,=");
677 		break;
678 	// CPX
679 	case 0xe0: // cpx #$ff
680 	case 0xe4: // cpx $ff
681 	case 0xec: // cpx $ffff
682 		op->type = R_ANAL_OP_TYPE_CMP;
683 		_6502_anal_esil_get_addr_pattern3 (op, data, len, addrbuf, buffsize, 0);
684 		if (data[0] == 0xe0) { // immediate mode
685 			r_strbuf_setf (&op->esil, "%s,x,==", addrbuf);
686 		} else {
687 			r_strbuf_setf (&op->esil, "%s,[1],x,==", addrbuf);
688 		}
689 		_6502_anal_update_flags (op, _6502_FLAGS_BNZ);
690 		// invert C, since C=1 when A-M >= 0
691 		r_strbuf_append (&op->esil, ",C,!,C,=");
692 		break;
693 	// CPY
694 	case 0xc0: // cpy #$ff
695 	case 0xc4: // cpy $ff
696 	case 0xcc: // cpy $ffff
697 		op->type = R_ANAL_OP_TYPE_CMP;
698 		_6502_anal_esil_get_addr_pattern3 (op, data, len, addrbuf, buffsize, 0);
699 		if (data[0] == 0xc0) { // immediate mode
700 			r_strbuf_setf (&op->esil, "%s,y,==", addrbuf);
701 		} else {
702 			r_strbuf_setf (&op->esil, "%s,[1],y,==", addrbuf);
703 		}
704 		_6502_anal_update_flags (op, _6502_FLAGS_BNZ);
705 		// invert C, since C=1 when A-M >= 0
706 		r_strbuf_append (&op->esil, ",C,!,C,=");
707 		break;
708 	// BRANCHES
709 	case 0x10: // bpl $ffff
710 	case 0x30: // bmi $ffff
711 	case 0x50: // bvc $ffff
712 	case 0x70: // bvs $ffff
713 	case 0x90: // bcc $ffff
714 	case 0xb0: // bcs $ffff
715 	case 0xd0: // bne $ffff
716 	case 0xf0: // beq $ffff
717 		// FIXME: Add 1 if branch occurs to same page.
718 		// FIXME: Add 2 if branch occurs to different page
719 		op->cycles = 2;
720 		op->failcycles = 3;
721 		op->type = R_ANAL_OP_TYPE_CJMP;
722 		if (len > 1) {
723 			if (data[1] <= 127) {
724 				op->jump = addr + data[1] + op->size;
725 			} else {
726 				op->jump = addr - (256 - data[1]) + op->size;
727 			}
728 		} else {
729 			op->jump = addr;
730 		}
731 		op->fail = addr + op->size;
732 		// FIXME: add a type of conditional
733 		// op->cond = R_ANAL_COND_LE;
734 		_6502_anal_esil_ccall (op, data[0]);
735 		break;
736 	// JSR
737 	case 0x20: // jsr $ffff
738 		op->cycles = 6;
739 		op->type = R_ANAL_OP_TYPE_CALL;
740 		op->jump = (len > 2)? data[1] | data[2] << 8: 0;
741 		op->stackop = R_ANAL_STACK_INC;
742 		op->stackptr = 2;
743 		// JSR pushes the address-1 of the next operation on to the stack before transferring program
744 		// control to the following address
745 		// stack is on page one and sp is an 8-bit reg: operations must be done like: sp + 0x100
746 		r_strbuf_setf (&op->esil, "1,pc,-,0xff,sp,+,=[2],0x%04" PFMT64x ",pc,=,2,sp,-=", op->jump);
747 		break;
748 	// JMP
749 	case 0x4c: // jmp $ffff
750 		op->cycles = 3;
751 		op->type = R_ANAL_OP_TYPE_JMP;
752 		op->jump = (len > 2)? data[1] | data[2] << 8: 0;
753 		r_strbuf_setf (&op->esil, "0x%04" PFMT64x ",pc,=", op->jump);
754 		break;
755 	case 0x6c: // jmp ($ffff)
756 		op->cycles = 5;
757 		op->type = R_ANAL_OP_TYPE_UJMP;
758 		// FIXME: how to read memory?
759 		// op->jump = data[1] | data[2] << 8;
760 		r_strbuf_setf (&op->esil, "0x%04x,[2],pc,=", len > 2? data[1] | data[2] << 8: 0);
761 		break;
762 	// RTS
763 	case 0x60: // rts
764 		op->eob = true;
765 		op->type = R_ANAL_OP_TYPE_RET;
766 		op->cycles = 6;
767 		op->stackop = R_ANAL_STACK_INC;
768 		op->stackptr = -2;
769 		// Operation:  PC from Stack, PC + 1 -> PC
770 		// stack is on page one and sp is an 8-bit reg: operations must be done like: sp + 0x100
771 		r_strbuf_set (&op->esil, "0x101,sp,+,[2],pc,=,pc,++=,2,sp,+=");
772 		break;
773 	// RTI
774 	case 0x40: // rti
775 		op->eob = true;
776 		op->type = R_ANAL_OP_TYPE_RET;
777 		op->cycles = 6;
778 		op->stackop = R_ANAL_STACK_INC;
779 		op->stackptr = -3;
780 		// Operation: P from Stack, PC from Stack
781 		// stack is on page one and sp is an 8-bit reg: operations must be done like: sp + 0x100
782 		r_strbuf_set (&op->esil, "0x101,sp,+,[1],flags,=,0x102,sp,+,[2],pc,=,3,sp,+=");
783 		break;
784 	// NOP
785 	case 0xea: // nop
786 		op->type = R_ANAL_OP_TYPE_NOP;
787 		op->cycles = 2;
788 		break;
789 	// LDA
790 	case 0xa9: // lda #$ff
791 	case 0xa5: // lda $ff
792 	case 0xb5: // lda $ff,x
793 	case 0xad: // lda $ffff
794 	case 0xbd: // lda $ffff,x
795 	case 0xb9: // lda $ffff,y
796 	case 0xa1: // lda ($ff,x)
797 	case 0xb1: // lda ($ff),y
798 		op->type = R_ANAL_OP_TYPE_LOAD;
799 		_6502_anal_esil_get_addr_pattern1 (op, data, len, addrbuf, buffsize);
800 		if (data[0] == 0xa9) { // immediate mode
801 			r_strbuf_setf (&op->esil, "%s,a,=", addrbuf);
802 		} else {
803 			r_strbuf_setf (&op->esil, "%s,[1],a,=", addrbuf);
804 		}
805 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
806 		break;
807 	// LDX
808 	case 0xa2: // ldx #$ff
809 	case 0xa6: // ldx $ff
810 	case 0xb6: // ldx $ff,y
811 	case 0xae: // ldx $ffff
812 	case 0xbe: // ldx $ffff,y
813 		op->type = R_ANAL_OP_TYPE_LOAD;
814 		_6502_anal_esil_get_addr_pattern2 (op, data, len, addrbuf, buffsize, 'y');
815 		if (data[0] == 0xa2) { // immediate mode
816 			r_strbuf_setf (&op->esil, "%s,x,=", addrbuf);
817 		} else {
818 			r_strbuf_setf (&op->esil, "%s,[1],x,=", addrbuf);
819 		}
820 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
821 		break;
822 	// LDY
823 	case 0xa0: // ldy #$ff
824 	case 0xa4: // ldy $ff
825 	case 0xb4: // ldy $ff,x
826 	case 0xac: // ldy $ffff
827 	case 0xbc: // ldy $ffff,x
828 		op->type = R_ANAL_OP_TYPE_LOAD;
829 		_6502_anal_esil_get_addr_pattern3 (op, data, len, addrbuf, buffsize, 'x');
830 		if (data[0] == 0xa0) { // immediate mode
831 			r_strbuf_setf (&op->esil, "%s,y,=", addrbuf);
832 		} else {
833 			r_strbuf_setf (&op->esil, "%s,[1],y,=", addrbuf);
834 		}
835 		_6502_anal_update_flags (op, _6502_FLAGS_NZ);
836 		break;
837 	// STA
838 	case 0x85: // sta $ff
839 	case 0x95: // sta $ff,x
840 	case 0x8d: // sta $ffff
841 	case 0x9d: // sta $ffff,x
842 	case 0x99: // sta $ffff,y
843 	case 0x81: // sta ($ff,x)
844 	case 0x91: // sta ($ff),y
845 		op->type = R_ANAL_OP_TYPE_STORE;
846 		_6502_anal_esil_get_addr_pattern1 (op, data, len, addrbuf, buffsize);
847 		r_strbuf_setf (&op->esil, "a,%s,=[1]", addrbuf);
848 		break;
849 	// STX
850 	case 0x86: // stx $ff
851 	case 0x96: // stx $ff,y
852 	case 0x8e: // stx $ffff
853 		op->type = R_ANAL_OP_TYPE_STORE;
854 		_6502_anal_esil_get_addr_pattern2 (op, data, len, addrbuf, buffsize, 'y');
855 		r_strbuf_setf (&op->esil, "x,%s,=[1]", addrbuf);
856 		break;
857 	// STY
858 	case 0x84: // sty $ff
859 	case 0x94: // sty $ff,x
860 	case 0x8c: // sty $ffff
861 		op->type = R_ANAL_OP_TYPE_STORE;
862 		_6502_anal_esil_get_addr_pattern3 (op, data, len, addrbuf, buffsize, 'x');
863 		r_strbuf_setf (&op->esil, "y,%s,=[1]", addrbuf);
864 		break;
865 	// PHP/PHA
866 	case 0x08: // php
867 	case 0x48: // pha
868 		op->type = R_ANAL_OP_TYPE_PUSH;
869 		op->cycles = 3;
870 		op->stackop = R_ANAL_STACK_INC;
871 		op->stackptr = 1;
872 		_6502_anal_esil_push (op, data[0]);
873 		break;
874 	// PLP,PLA
875 	case 0x28: // plp
876 	case 0x68: // plp
877 		op->type = R_ANAL_OP_TYPE_POP;
878 		op->cycles = 4;
879 		op->stackop = R_ANAL_STACK_INC;
880 		op->stackptr = -1;
881 		_6502_anal_esil_pop (op, data[0]);
882 		break;
883 	// TAX,TYA,...
884 	case 0xaa: // tax
885 	case 0x8a: // txa
886 	case 0xa8: // tay
887 	case 0x98: // tya
888 		op->type = R_ANAL_OP_TYPE_MOV;
889 		op->cycles = 2;
890 		_6502_anal_esil_mov (op, data[0]);
891 		break;
892 	case 0x9a: // txs
893 		op->type = R_ANAL_OP_TYPE_MOV;
894 		op->cycles = 2;
895 		op->stackop = R_ANAL_STACK_SET;
896 		// FIXME: should I get register X a place it here?
897 		// op->stackptr = get_register_x();
898 		_6502_anal_esil_mov (op, data[0]);
899 		break;
900 	case 0xba: // tsx
901 		op->type = R_ANAL_OP_TYPE_MOV;
902 		op->cycles = 2;
903 		op->stackop = R_ANAL_STACK_GET;
904 		_6502_anal_esil_mov (op, data[0]);
905 		break;
906 	}
907 	return op->size;
908 }
909 
set_reg_profile(RAnal * anal)910 static bool set_reg_profile(RAnal *anal) {
911 	char *p =
912 		"=PC	pc\n"
913 		"=SP	sp\n"
914 		"=A0	y\n"
915 		"=A1	y\n"
916 		"gpr	a	.8	0	0\n"
917 		"gpr	x	.8	1	0\n"
918 		"gpr	y	.8	2	0\n"
919 
920 		"gpr	flags	.8	3	0\n"
921 		"gpr	C	.1	.24	0\n"
922 		"gpr	Z	.1	.25	0\n"
923 		"gpr	I	.1	.26	0\n"
924 		"gpr	D	.1	.27	0\n"
925 		// bit 4 (.28) is NOT a real flag.
926 		// "gpr	B	.1	.28	0\n"
927 		// bit 5 (.29) is not used
928 		"gpr	V	.1	.30	0\n"
929 		"gpr	N	.1	.31	0\n"
930 		"gpr	sp	.8	4	0\n"
931 		"gpr	pc	.16	5	0\n";
932 	return r_reg_set_profile_string (anal->reg, p);
933 }
934 
esil_6502_init(RAnalEsil * esil)935 static int esil_6502_init (RAnalEsil *esil) {
936 	if (esil->anal && esil->anal->reg) {		//initial values
937 		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "pc", -1), 0x0000);
938 		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "sp", -1), 0xff);
939 		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "a", -1), 0x00);
940 		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "x", -1), 0x00);
941 		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "y", -1), 0x00);
942 		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "flags", -1), 0x00);
943 	}
944 	return true;
945 }
946 
esil_6502_fini(RAnalEsil * esil)947 static int esil_6502_fini (RAnalEsil *esil) {
948 	return true;
949 }
950 
951 RAnalPlugin r_anal_plugin_6502 = {
952 	.name = "6502",
953 	.desc = "6502/NES analysis plugin",
954 	.license = "LGPL3",
955 	.arch = "6502",
956 	.bits = 8,
957 	.op = &_6502_op,
958 	.set_reg_profile = &set_reg_profile,
959 	.esil = true,
960 	.esil_init = esil_6502_init,
961 	.esil_fini = esil_6502_fini,
962 };
963 
964 #ifndef R2_PLUGIN_INCORE
965 R_API RLibStruct radare_plugin = {
966 	.type = R_LIB_TYPE_ANAL,
967 	.data = &r_anal_plugin_6502,
968 	.version = R2_VERSION
969 };
970 #endif
971