ruby6502 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1055 @@
1
+ /* Fake6502 CPU emulator core v1.1 *******************
2
+ * (c)2011 Mike Chambers (miker00lz@gmail.com) *
3
+ *****************************************************
4
+ * v1.1 - Small bugfix in BIT opcode, but it was the *
5
+ * difference between a few games in my NES *
6
+ * emulator working and being broken! *
7
+ * I went through the rest carefully again *
8
+ * after fixing it just to make sure I didn't *
9
+ * have any other typos! (Dec. 17, 2011) *
10
+ * *
11
+ * v1.0 - First release (Nov. 24, 2011) *
12
+ *****************************************************
13
+ * LICENSE: This source code is released into the *
14
+ * public domain, but if you use it please do give *
15
+ * credit. I put a lot of effort into writing this! *
16
+ * *
17
+ *****************************************************
18
+ * Fake6502 is a MOS Technology 6502 CPU emulation *
19
+ * engine in C. It was written as part of a Nintendo *
20
+ * Entertainment System emulator I've been writing. *
21
+ * *
22
+ * A couple important things to know about are two *
23
+ * defines in the code. One is "UNDOCUMENTED" which, *
24
+ * when defined, allows Fake6502 to compile with *
25
+ * full support for the more predictable *
26
+ * undocumented instructions of the 6502. If it is *
27
+ * undefined, undocumented opcodes just act as NOPs. *
28
+ * *
29
+ * The other define is "NES_CPU", which causes the *
30
+ * code to compile without support for binary-coded *
31
+ * decimal (BCD) support for the ADC and SBC *
32
+ * opcodes. The Ricoh 2A03 CPU in the NES does not *
33
+ * support BCD, but is otherwise identical to the *
34
+ * standard MOS 6502. (Note that this define is *
35
+ * enabled in this file if you haven't changed it *
36
+ * yourself. If you're not emulating a NES, you *
37
+ * should comment it out.) *
38
+ * *
39
+ * If you do discover an error in timing accuracy, *
40
+ * or operation in general please e-mail me at the *
41
+ * address above so that I can fix it. Thank you! *
42
+ * *
43
+ *****************************************************
44
+ * Usage: *
45
+ * *
46
+ * Fake6502 requires you to provide two external *
47
+ * functions: *
48
+ * *
49
+ * uint8_t read6502(uint16_t address) *
50
+ * void write6502(uint16_t address, uint8_t value) *
51
+ * *
52
+ * You may optionally pass Fake6502 the pointer to a *
53
+ * function which you want to be called after every *
54
+ * emulated instruction. This function should be a *
55
+ * void with no parameters expected to be passed to *
56
+ * it. *
57
+ * *
58
+ * This can be very useful. For example, in a NES *
59
+ * emulator, you check the number of clock ticks *
60
+ * that have passed so you can know when to handle *
61
+ * APU events. *
62
+ * *
63
+ * To pass Fake6502 this pointer, use the *
64
+ * hookexternal(void *funcptr) function provided. *
65
+ * *
66
+ * To disable the hook later, pass NULL to it. *
67
+ *****************************************************
68
+ * Useful functions in this emulator: *
69
+ * *
70
+ * void reset6502() *
71
+ * - Call this once before you begin execution. *
72
+ * *
73
+ * void exec6502(uint32_t tickcount) *
74
+ * - Execute 6502 code up to the next specified *
75
+ * count of clock ticks. *
76
+ * *
77
+ * void step6502() *
78
+ * - Execute a single instrution. *
79
+ * *
80
+ * void irq6502() *
81
+ * - Trigger a hardware IRQ in the 6502 core. *
82
+ * *
83
+ * void nmi6502() *
84
+ * - Trigger an NMI in the 6502 core. *
85
+ * *
86
+ * void hookexternal(void *funcptr) *
87
+ * - Pass a pointer to a void function taking no *
88
+ * parameters. This will cause Fake6502 to call *
89
+ * that function once after each emulated *
90
+ * instruction. *
91
+ * *
92
+ *****************************************************
93
+ * Useful variables in this emulator: *
94
+ * *
95
+ * uint32_t clockticks6502 *
96
+ * - A running total of the emulated cycle count. *
97
+ * *
98
+ * uint32_t instructions *
99
+ * - A running total of the total emulated *
100
+ * instruction count. This is not related to *
101
+ * clock cycle timing. *
102
+ * *
103
+ *****************************************************/
104
+
105
+ #include <stdio.h>
106
+ #include <stdint.h>
107
+ #include "fake6502.h"
108
+
109
+ //6502 defines
110
+ //#define UNDOCUMENTED //when this is defined, undocumented opcodes are handled.
111
+ //otherwise, they're simply treated as NOPs.
112
+
113
+ //#define NES_CPU //when this is defined, the binary-coded decimal (BCD)
114
+ //status flag is not honored by ADC and SBC. the 2A03
115
+ //CPU in the Nintendo Entertainment System does not
116
+ //support BCD operation.
117
+
118
+ #define FLAG_CARRY 0x01
119
+ #define FLAG_ZERO 0x02
120
+ #define FLAG_INTERRUPT 0x04
121
+ #define FLAG_DECIMAL 0x08
122
+ #define FLAG_BREAK 0x10
123
+ #define FLAG_CONSTANT 0x20
124
+ #define FLAG_OVERFLOW 0x40
125
+ #define FLAG_SIGN 0x80
126
+
127
+ #define BASE_STACK 0x100
128
+
129
+ #define saveaccum(n) a = (uint8_t)((n) & 0x00FF)
130
+
131
+
132
+ //flag modifier macros
133
+ #define setcarry() status |= FLAG_CARRY
134
+ #define clearcarry() status &= (~FLAG_CARRY)
135
+ #define setzero() status |= FLAG_ZERO
136
+ #define clearzero() status &= (~FLAG_ZERO)
137
+ #define setinterrupt() status |= FLAG_INTERRUPT
138
+ #define clearinterrupt() status &= (~FLAG_INTERRUPT)
139
+ #define setdecimal() status |= FLAG_DECIMAL
140
+ #define cleardecimal() status &= (~FLAG_DECIMAL)
141
+ #define setoverflow() status |= FLAG_OVERFLOW
142
+ #define clearoverflow() status &= (~FLAG_OVERFLOW)
143
+ #define setsign() status |= FLAG_SIGN
144
+ #define clearsign() status &= (~FLAG_SIGN)
145
+
146
+
147
+ //flag calculation macros
148
+ #define zerocalc(n) {\
149
+ if ((n) & 0x00FF) clearzero();\
150
+ else setzero();\
151
+ }
152
+
153
+ #define signcalc(n) {\
154
+ if ((n) & 0x0080) setsign();\
155
+ else clearsign();\
156
+ }
157
+
158
+ #define carrycalc(n) {\
159
+ if ((n) & 0xFF00) setcarry();\
160
+ else clearcarry();\
161
+ }
162
+
163
+ #define overflowcalc(n, m, o) { /* n = result, m = accumulator, o = memory */ \
164
+ if (((n) ^ (uint16_t)(m)) & ((n) ^ (o)) & 0x0080) setoverflow();\
165
+ else clearoverflow();\
166
+ }
167
+
168
+
169
+ //6502 CPU registers
170
+ uint16_t pc;
171
+ uint8_t sp, a, x, y, status;
172
+
173
+
174
+ //helper variables
175
+ uint64_t instructions = 0; //keep track of total instructions executed
176
+ uint64_t clockticks6502 = 0, clockgoal6502 = 0;
177
+ uint16_t oldpc, ea, reladdr, value, result;
178
+ uint8_t opcode, oldstatus;
179
+
180
+ //externally supplied functions
181
+ extern uint8_t read6502(uint16_t address);
182
+ extern void write6502(uint16_t address, uint8_t value);
183
+
184
+ //a few general functions used by various other functions
185
+ void push16(uint16_t pushval) {
186
+ write6502(BASE_STACK + sp, (pushval >> 8) & 0xFF);
187
+ write6502(BASE_STACK + ((sp - 1) & 0xFF), pushval & 0xFF);
188
+ sp -= 2;
189
+ }
190
+
191
+ void push8(uint8_t pushval) {
192
+ write6502(BASE_STACK + sp--, pushval);
193
+ }
194
+
195
+ uint16_t pull16() {
196
+ uint16_t temp16;
197
+ temp16 = read6502(BASE_STACK + ((sp + 1) & 0xFF)) | ((uint16_t)read6502(BASE_STACK + ((sp + 2) & 0xFF)) << 8);
198
+ sp += 2;
199
+ return(temp16);
200
+ }
201
+
202
+ uint8_t pull8() {
203
+ return (read6502(BASE_STACK + ++sp));
204
+ }
205
+
206
+ void reset6502() {
207
+ pc = (uint16_t)read6502(0xFFFC) | ((uint16_t)read6502(0xFFFD) << 8);
208
+ a = 0;
209
+ x = 0;
210
+ y = 0;
211
+ sp = 0xFD;
212
+ status = FLAG_CONSTANT;
213
+ instructions = 0;
214
+ clockticks6502 = 0;
215
+ clockgoal6502 = 0;
216
+ }
217
+
218
+
219
+ static void (*addrtable[256])();
220
+ static void (*optable[256])();
221
+ uint8_t penaltyop, penaltyaddr;
222
+
223
+ //addressing mode functions, calculates effective addresses
224
+ static void imp() { //implied
225
+ }
226
+
227
+ static void acc() { //accumulator
228
+ }
229
+
230
+ static void imm() { //immediate
231
+ ea = pc++;
232
+ }
233
+
234
+ static void zp() { //zero-page
235
+ ea = (uint16_t)read6502((uint16_t)pc++);
236
+ }
237
+
238
+ static void zpx() { //zero-page,X
239
+ ea = ((uint16_t)read6502((uint16_t)pc++) + (uint16_t)x) & 0xFF; //zero-page wraparound
240
+ }
241
+
242
+ static void zpy() { //zero-page,Y
243
+ ea = ((uint16_t)read6502((uint16_t)pc++) + (uint16_t)y) & 0xFF; //zero-page wraparound
244
+ }
245
+
246
+ static void rel() { //relative for branch ops (8-bit immediate value, sign-extended)
247
+ reladdr = (uint16_t)read6502(pc++);
248
+ if (reladdr & 0x80) reladdr |= 0xFF00;
249
+ }
250
+
251
+ static void abso() { //absolute
252
+ ea = (uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8);
253
+ pc += 2;
254
+ }
255
+
256
+ static void absx() { //absolute,X
257
+ uint16_t startpage;
258
+ ea = ((uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8));
259
+ startpage = ea & 0xFF00;
260
+ ea += (uint16_t)x;
261
+
262
+ if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes
263
+ penaltyaddr = 1;
264
+ }
265
+
266
+ pc += 2;
267
+ }
268
+
269
+ static void absy() { //absolute,Y
270
+ uint16_t startpage;
271
+ ea = ((uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8));
272
+ startpage = ea & 0xFF00;
273
+ ea += (uint16_t)y;
274
+
275
+ if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes
276
+ penaltyaddr = 1;
277
+ }
278
+
279
+ pc += 2;
280
+ }
281
+
282
+ static void ind() { //indirect
283
+ uint16_t eahelp, eahelp2;
284
+ eahelp = (uint16_t)read6502(pc) | (uint16_t)((uint16_t)read6502(pc+1) << 8);
285
+ eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); //replicate 6502 page-boundary wraparound bug
286
+ ea = (uint16_t)read6502(eahelp) | ((uint16_t)read6502(eahelp2) << 8);
287
+ pc += 2;
288
+ }
289
+
290
+ static void indx() { // (indirect,X)
291
+ uint16_t eahelp;
292
+ eahelp = (uint16_t)(((uint16_t)read6502(pc++) + (uint16_t)x) & 0xFF); //zero-page wraparound for table pointer
293
+ ea = (uint16_t)read6502(eahelp & 0x00FF) | ((uint16_t)read6502((eahelp+1) & 0x00FF) << 8);
294
+ }
295
+
296
+ static void indy() { // (indirect),Y
297
+ uint16_t eahelp, eahelp2, startpage;
298
+ eahelp = (uint16_t)read6502(pc++);
299
+ eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); //zero-page wraparound
300
+ ea = (uint16_t)read6502(eahelp) | ((uint16_t)read6502(eahelp2) << 8);
301
+ startpage = ea & 0xFF00;
302
+ ea += (uint16_t)y;
303
+
304
+ if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes
305
+ penaltyaddr = 1;
306
+ }
307
+ }
308
+
309
+ static uint16_t getvalue() {
310
+ if (addrtable[opcode] == acc) return((uint16_t)a);
311
+ else return((uint16_t)read6502(ea));
312
+ }
313
+
314
+ static uint16_t getvalue16() {
315
+ return((uint16_t)read6502(ea) | ((uint16_t)read6502(ea+1) << 8));
316
+ }
317
+
318
+ static void putvalue(uint16_t saveval) {
319
+ if (addrtable[opcode] == acc) a = (uint8_t)(saveval & 0x00FF);
320
+ else write6502(ea, (saveval & 0x00FF));
321
+ }
322
+
323
+
324
+ //instruction handler functions
325
+
326
+ // Original adc which does not handle BCD mode
327
+ // static void adc() {
328
+ // penaltyop = 1;
329
+ // value = getvalue();
330
+ // result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY);
331
+
332
+ // carrycalc(result);
333
+ // zerocalc(result);
334
+ // overflowcalc(result, a, value);
335
+ // signcalc(result);
336
+
337
+ // #ifndef NES_CPU
338
+ // if (status & FLAG_DECIMAL) {
339
+ // clearcarry();
340
+
341
+ // if ((a & 0x0F) > 0x09) {
342
+ // a += 0x06;
343
+ // }
344
+ // if ((a & 0xF0) > 0x90) {
345
+ // a += 0x60;
346
+ // setcarry();
347
+ // }
348
+
349
+ // clockticks6502++;
350
+ // }
351
+ // #endif
352
+
353
+ // saveaccum(result);
354
+ // }
355
+ // from http://forum.6502.org/viewtopic.php?f=2&t=2052#p37758
356
+ static void adc() {
357
+ penaltyop = 1;
358
+ value = getvalue();
359
+ result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY);
360
+
361
+ zerocalc(result);
362
+ overflowcalc(result, a, value);
363
+ signcalc(result);
364
+
365
+ #ifndef NES_CPU
366
+ if (status & FLAG_DECIMAL) /* detect and apply BCD nybble carries */
367
+ result += ((((result + 0x66) ^ (uint16_t)a ^ value) >> 3) & 0x22) * 3;
368
+ #endif
369
+
370
+ carrycalc(result);
371
+ saveaccum(result);
372
+ }
373
+
374
+ static void and() {
375
+ penaltyop = 1;
376
+ value = getvalue();
377
+ result = (uint16_t)a & value;
378
+
379
+ zerocalc(result);
380
+ signcalc(result);
381
+
382
+ saveaccum(result);
383
+ }
384
+
385
+ static void asl() {
386
+ value = getvalue();
387
+ result = value << 1;
388
+
389
+ carrycalc(result);
390
+ zerocalc(result);
391
+ signcalc(result);
392
+
393
+ putvalue(result);
394
+ }
395
+
396
+ static void bcc() {
397
+ if ((status & FLAG_CARRY) == 0) {
398
+ oldpc = pc;
399
+ pc += reladdr;
400
+ if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
401
+ else clockticks6502++;
402
+ }
403
+ }
404
+
405
+ static void bcs() {
406
+ if ((status & FLAG_CARRY) == FLAG_CARRY) {
407
+ oldpc = pc;
408
+ pc += reladdr;
409
+ if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
410
+ else clockticks6502++;
411
+ }
412
+ }
413
+
414
+ static void beq() {
415
+ if ((status & FLAG_ZERO) == FLAG_ZERO) {
416
+ oldpc = pc;
417
+ pc += reladdr;
418
+ if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
419
+ else clockticks6502++;
420
+ }
421
+ }
422
+
423
+ static void bit() {
424
+ value = getvalue();
425
+ result = (uint16_t)a & value;
426
+
427
+ zerocalc(result);
428
+ status = (status & 0x3F) | (uint8_t)(value & 0xC0);
429
+ }
430
+
431
+ static void bmi() {
432
+ if ((status & FLAG_SIGN) == FLAG_SIGN) {
433
+ oldpc = pc;
434
+ pc += reladdr;
435
+ if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
436
+ else clockticks6502++;
437
+ }
438
+ }
439
+
440
+ static void bne() {
441
+ if ((status & FLAG_ZERO) == 0) {
442
+ oldpc = pc;
443
+ pc += reladdr;
444
+ if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
445
+ else clockticks6502++;
446
+ }
447
+ }
448
+
449
+ static void bpl() {
450
+ if ((status & FLAG_SIGN) == 0) {
451
+ oldpc = pc;
452
+ pc += reladdr;
453
+ if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
454
+ else clockticks6502++;
455
+ }
456
+ }
457
+
458
+ static void brk() {
459
+ pc++;
460
+ push16(pc); //push next instruction address onto stack
461
+ push8(status | FLAG_BREAK); //push CPU status to stack
462
+ setinterrupt(); //set interrupt flag
463
+ pc = (uint16_t)read6502(0xFFFE) | ((uint16_t)read6502(0xFFFF) << 8);
464
+ }
465
+
466
+ static void bvc() {
467
+ if ((status & FLAG_OVERFLOW) == 0) {
468
+ oldpc = pc;
469
+ pc += reladdr;
470
+ if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
471
+ else clockticks6502++;
472
+ }
473
+ }
474
+
475
+ static void bvs() {
476
+ if ((status & FLAG_OVERFLOW) == FLAG_OVERFLOW) {
477
+ oldpc = pc;
478
+ pc += reladdr;
479
+ if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
480
+ else clockticks6502++;
481
+ }
482
+ }
483
+
484
+ static void clc() {
485
+ clearcarry();
486
+ }
487
+
488
+ static void cld() {
489
+ cleardecimal();
490
+ }
491
+
492
+ static void cli() {
493
+ clearinterrupt();
494
+ }
495
+
496
+ static void clv() {
497
+ clearoverflow();
498
+ }
499
+
500
+ static void cmp() {
501
+ penaltyop = 1;
502
+ value = getvalue();
503
+ result = (uint16_t)a - value;
504
+
505
+ if (a >= (uint8_t)(value & 0x00FF)) setcarry();
506
+ else clearcarry();
507
+ if (a == (uint8_t)(value & 0x00FF)) setzero();
508
+ else clearzero();
509
+ signcalc(result);
510
+ }
511
+
512
+ static void cpx() {
513
+ value = getvalue();
514
+ result = (uint16_t)x - value;
515
+
516
+ if (x >= (uint8_t)(value & 0x00FF)) setcarry();
517
+ else clearcarry();
518
+ if (x == (uint8_t)(value & 0x00FF)) setzero();
519
+ else clearzero();
520
+ signcalc(result);
521
+ }
522
+
523
+ static void cpy() {
524
+ value = getvalue();
525
+ result = (uint16_t)y - value;
526
+
527
+ if (y >= (uint8_t)(value & 0x00FF)) setcarry();
528
+ else clearcarry();
529
+ if (y == (uint8_t)(value & 0x00FF)) setzero();
530
+ else clearzero();
531
+ signcalc(result);
532
+ }
533
+
534
+ static void dec() {
535
+ value = getvalue();
536
+ result = value - 1;
537
+
538
+ zerocalc(result);
539
+ signcalc(result);
540
+
541
+ putvalue(result);
542
+ }
543
+
544
+ static void dex() {
545
+ x--;
546
+
547
+ zerocalc(x);
548
+ signcalc(x);
549
+ }
550
+
551
+ static void dey() {
552
+ y--;
553
+
554
+ zerocalc(y);
555
+ signcalc(y);
556
+ }
557
+
558
+ static void eor() {
559
+ penaltyop = 1;
560
+ value = getvalue();
561
+ result = (uint16_t)a ^ value;
562
+
563
+ zerocalc(result);
564
+ signcalc(result);
565
+
566
+ saveaccum(result);
567
+ }
568
+
569
+ static void inc() {
570
+ value = getvalue();
571
+ result = value + 1;
572
+
573
+ zerocalc(result);
574
+ signcalc(result);
575
+
576
+ putvalue(result);
577
+ }
578
+
579
+ static void inx() {
580
+ x++;
581
+
582
+ zerocalc(x);
583
+ signcalc(x);
584
+ }
585
+
586
+ static void iny() {
587
+ y++;
588
+
589
+ zerocalc(y);
590
+ signcalc(y);
591
+ }
592
+
593
+ static void jmp() {
594
+ pc = ea;
595
+ }
596
+
597
+ static void jsr() {
598
+ push16(pc - 1);
599
+ pc = ea;
600
+ }
601
+
602
+ static void lda() {
603
+ penaltyop = 1;
604
+ value = getvalue();
605
+ a = (uint8_t)(value & 0x00FF);
606
+
607
+ zerocalc(a);
608
+ signcalc(a);
609
+ }
610
+
611
+ static void ldx() {
612
+ penaltyop = 1;
613
+ value = getvalue();
614
+ x = (uint8_t)(value & 0x00FF);
615
+
616
+ zerocalc(x);
617
+ signcalc(x);
618
+ }
619
+
620
+ static void ldy() {
621
+ penaltyop = 1;
622
+ value = getvalue();
623
+ y = (uint8_t)(value & 0x00FF);
624
+
625
+ zerocalc(y);
626
+ signcalc(y);
627
+ }
628
+
629
+ static void lsr() {
630
+ value = getvalue();
631
+ result = value >> 1;
632
+
633
+ if (value & 1) setcarry();
634
+ else clearcarry();
635
+ zerocalc(result);
636
+ signcalc(result);
637
+
638
+ putvalue(result);
639
+ }
640
+
641
+ static void nop() {
642
+ switch (opcode) {
643
+ case 0x1C:
644
+ case 0x3C:
645
+ case 0x5C:
646
+ case 0x7C:
647
+ case 0xDC:
648
+ case 0xFC:
649
+ penaltyop = 1;
650
+ break;
651
+ }
652
+ }
653
+
654
+ static void ora() {
655
+ penaltyop = 1;
656
+ value = getvalue();
657
+ result = (uint16_t)a | value;
658
+
659
+ zerocalc(result);
660
+ signcalc(result);
661
+
662
+ saveaccum(result);
663
+ }
664
+
665
+ static void pha() {
666
+ push8(a);
667
+ }
668
+
669
+ static void php() {
670
+ push8(status | FLAG_BREAK);
671
+ }
672
+
673
+ static void pla() {
674
+ a = pull8();
675
+
676
+ zerocalc(a);
677
+ signcalc(a);
678
+ }
679
+
680
+ static void plp() {
681
+ status = pull8() | FLAG_CONSTANT;
682
+ }
683
+
684
+ static void rol() {
685
+ value = getvalue();
686
+ result = (value << 1) | (status & FLAG_CARRY);
687
+
688
+ carrycalc(result);
689
+ zerocalc(result);
690
+ signcalc(result);
691
+
692
+ putvalue(result);
693
+ }
694
+
695
+ static void ror() {
696
+ value = getvalue();
697
+ result = (value >> 1) | ((status & FLAG_CARRY) << 7);
698
+
699
+ if (value & 1) setcarry();
700
+ else clearcarry();
701
+ zerocalc(result);
702
+ signcalc(result);
703
+
704
+ putvalue(result);
705
+ }
706
+
707
+ static void rti() {
708
+ status = pull8();
709
+ value = pull16();
710
+ pc = value;
711
+ }
712
+
713
+ static void rts() {
714
+ value = pull16();
715
+ pc = value + 1;
716
+ }
717
+
718
+ // Original adc which does not handle BCD mode
719
+ // static void sbc() {
720
+ // penaltyop = 1;
721
+ // value = getvalue() ^ 0x00FF;
722
+ // result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY);
723
+
724
+ // carrycalc(result);
725
+ // zerocalc(result);
726
+ // overflowcalc(result, a, value);
727
+ // signcalc(result);
728
+
729
+ // #ifndef NES_CPU
730
+ // if (status & FLAG_DECIMAL) {
731
+ // clearcarry();
732
+
733
+ // a -= 0x66;
734
+ // if ((a & 0x0F) > 0x09) {
735
+ // a += 0x06;
736
+ // }
737
+ // if ((a & 0xF0) > 0x90) {
738
+ // a += 0x60;
739
+ // setcarry();
740
+ // }
741
+
742
+ // clockticks6502++;
743
+ // }
744
+ // #endif
745
+
746
+ // saveaccum(result);
747
+ // }
748
+ // from http://forum.6502.org/viewtopic.php?f=2&t=2052#p37758
749
+ static void sbc() {
750
+ penaltyop = 1;
751
+ value = getvalue() ^ 0x00FF; /* ones complement */
752
+
753
+ #ifndef NES_CPU
754
+ if (status & FLAG_DECIMAL) /* use nines complement for BCD */
755
+ value -= 0x0066;
756
+ #endif
757
+
758
+ result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY);
759
+
760
+ zerocalc(result);
761
+ overflowcalc(result, a, value);
762
+ signcalc(result);
763
+
764
+ #ifndef NES_CPU
765
+ if (status & FLAG_DECIMAL) /* detect and apply BCD nybble carries */
766
+ result += ((((result + 0x66) ^ (uint16_t)a ^ value) >> 3) & 0x22) * 3;
767
+ #endif
768
+
769
+ carrycalc(result);
770
+ saveaccum(result);
771
+ }
772
+
773
+ static void sec() {
774
+ setcarry();
775
+ }
776
+
777
+ static void sed() {
778
+ setdecimal();
779
+ }
780
+
781
+ static void sei() {
782
+ setinterrupt();
783
+ }
784
+
785
+ static void sta() {
786
+ putvalue(a);
787
+ }
788
+
789
+ static void stx() {
790
+ putvalue(x);
791
+ }
792
+
793
+ static void sty() {
794
+ putvalue(y);
795
+ }
796
+
797
+ static void tax() {
798
+ x = a;
799
+
800
+ zerocalc(x);
801
+ signcalc(x);
802
+ }
803
+
804
+ static void tay() {
805
+ y = a;
806
+
807
+ zerocalc(y);
808
+ signcalc(y);
809
+ }
810
+
811
+ static void tsx() {
812
+ x = sp;
813
+
814
+ zerocalc(x);
815
+ signcalc(x);
816
+ }
817
+
818
+ static void txa() {
819
+ a = x;
820
+
821
+ zerocalc(a);
822
+ signcalc(a);
823
+ }
824
+
825
+ static void txs() {
826
+ sp = x;
827
+ }
828
+
829
+ static void tya() {
830
+ a = y;
831
+
832
+ zerocalc(a);
833
+ signcalc(a);
834
+ }
835
+
836
+ //undocumented instructions
837
+ #ifdef UNDOCUMENTED
838
+ static void lax() {
839
+ lda();
840
+ ldx();
841
+ }
842
+
843
+ static void sax() {
844
+ sta();
845
+ stx();
846
+ putvalue(a & x);
847
+ if (penaltyop && penaltyaddr) clockticks6502--;
848
+ }
849
+
850
+ static void dcp() {
851
+ dec();
852
+ cmp();
853
+ if (penaltyop && penaltyaddr) clockticks6502--;
854
+ }
855
+
856
+ static void isb() {
857
+ inc();
858
+ sbc();
859
+ if (penaltyop && penaltyaddr) clockticks6502--;
860
+ }
861
+
862
+ static void slo() {
863
+ asl();
864
+ ora();
865
+ if (penaltyop && penaltyaddr) clockticks6502--;
866
+ }
867
+
868
+ static void rla() {
869
+ rol();
870
+ and();
871
+ if (penaltyop && penaltyaddr) clockticks6502--;
872
+ }
873
+
874
+ static void sre() {
875
+ lsr();
876
+ eor();
877
+ if (penaltyop && penaltyaddr) clockticks6502--;
878
+ }
879
+
880
+ static void rra() {
881
+ ror();
882
+ adc();
883
+ if (penaltyop && penaltyaddr) clockticks6502--;
884
+ }
885
+ #else
886
+ #define lax nop
887
+ #define sax nop
888
+ #define dcp nop
889
+ #define isb nop
890
+ #define slo nop
891
+ #define rla nop
892
+ #define sre nop
893
+ #define rra nop
894
+ #endif
895
+
896
+
897
+ static void (*addrtable[256])() = {
898
+ /* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
899
+ /* 0 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 0 */
900
+ /* 1 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 1 */
901
+ /* 2 */ abso, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 2 */
902
+ /* 3 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 3 */
903
+ /* 4 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 4 */
904
+ /* 5 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 5 */
905
+ /* 6 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, ind, abso, abso, abso, /* 6 */
906
+ /* 7 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 7 */
907
+ /* 8 */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* 8 */
908
+ /* 9 */ rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* 9 */
909
+ /* A */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* A */
910
+ /* B */ rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* B */
911
+ /* C */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* C */
912
+ /* D */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* D */
913
+ /* E */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* E */
914
+ /* F */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx /* F */
915
+ };
916
+
917
+ static void (*optable[256])() = {
918
+ /* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
919
+ /* 0 */ brk, ora, nop, slo, nop, ora, asl, slo, php, ora, asl, nop, nop, ora, asl, slo, /* 0 */
920
+ /* 1 */ bpl, ora, nop, slo, nop, ora, asl, slo, clc, ora, nop, slo, nop, ora, asl, slo, /* 1 */
921
+ /* 2 */ jsr, and, nop, rla, bit, and, rol, rla, plp, and, rol, nop, bit, and, rol, rla, /* 2 */
922
+ /* 3 */ bmi, and, nop, rla, nop, and, rol, rla, sec, and, nop, rla, nop, and, rol, rla, /* 3 */
923
+ /* 4 */ rti, eor, nop, sre, nop, eor, lsr, sre, pha, eor, lsr, nop, jmp, eor, lsr, sre, /* 4 */
924
+ /* 5 */ bvc, eor, nop, sre, nop, eor, lsr, sre, cli, eor, nop, sre, nop, eor, lsr, sre, /* 5 */
925
+ /* 6 */ rts, adc, nop, rra, nop, adc, ror, rra, pla, adc, ror, nop, jmp, adc, ror, rra, /* 6 */
926
+ /* 7 */ bvs, adc, nop, rra, nop, adc, ror, rra, sei, adc, nop, rra, nop, adc, ror, rra, /* 7 */
927
+ /* 8 */ nop, sta, nop, sax, sty, sta, stx, sax, dey, nop, txa, nop, sty, sta, stx, sax, /* 8 */
928
+ /* 9 */ bcc, sta, nop, nop, sty, sta, stx, sax, tya, sta, txs, nop, nop, sta, nop, nop, /* 9 */
929
+ /* A */ ldy, lda, ldx, lax, ldy, lda, ldx, lax, tay, lda, tax, nop, ldy, lda, ldx, lax, /* A */
930
+ /* B */ bcs, lda, nop, lax, ldy, lda, ldx, lax, clv, lda, tsx, lax, ldy, lda, ldx, lax, /* B */
931
+ /* C */ cpy, cmp, nop, dcp, cpy, cmp, dec, dcp, iny, cmp, dex, nop, cpy, cmp, dec, dcp, /* C */
932
+ /* D */ bne, cmp, nop, dcp, nop, cmp, dec, dcp, cld, cmp, nop, dcp, nop, cmp, dec, dcp, /* D */
933
+ /* E */ cpx, sbc, nop, isb, cpx, sbc, inc, isb, inx, sbc, nop, sbc, cpx, sbc, inc, isb, /* E */
934
+ /* F */ beq, sbc, nop, isb, nop, sbc, inc, isb, sed, sbc, nop, isb, nop, sbc, inc, isb /* F */
935
+ };
936
+
937
+ static const uint32_t ticktable[256] = {
938
+ /* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
939
+ /* 0 */ 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, /* 0 */
940
+ /* 1 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 1 */
941
+ /* 2 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, /* 2 */
942
+ /* 3 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 3 */
943
+ /* 4 */ 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, /* 4 */
944
+ /* 5 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 5 */
945
+ /* 6 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, /* 6 */
946
+ /* 7 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 7 */
947
+ /* 8 */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* 8 */
948
+ /* 9 */ 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, /* 9 */
949
+ /* A */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* A */
950
+ /* B */ 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, /* B */
951
+ /* C */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* C */
952
+ /* D */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* D */
953
+ /* E */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* E */
954
+ /* F */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 /* F */
955
+ };
956
+
957
+
958
+ void nmi6502() {
959
+ push16(pc);
960
+ push8(status);
961
+ status |= FLAG_INTERRUPT;
962
+ pc = (uint16_t)read6502(0xFFFA) | ((uint16_t)read6502(0xFFFB) << 8);
963
+ }
964
+
965
+ void irq6502() {
966
+ if (! (status & FLAG_INTERRUPT) ) {
967
+ push16(pc);
968
+ push8(status);
969
+ status |= FLAG_INTERRUPT;
970
+ pc = (uint16_t)read6502(0xFFFE) | ((uint16_t)read6502(0xFFFF) << 8);
971
+ }
972
+ }
973
+
974
+ uint8_t callexternal = 0;
975
+ void (*loopexternal)();
976
+
977
+ void exec6502(uint32_t tickcount) {
978
+ clockgoal6502 += tickcount;
979
+
980
+ while (clockticks6502 < clockgoal6502) {
981
+ opcode = read6502(pc++);
982
+ status |= FLAG_CONSTANT;
983
+
984
+ penaltyop = 0;
985
+ penaltyaddr = 0;
986
+
987
+ (*addrtable[opcode])();
988
+ (*optable[opcode])();
989
+ clockticks6502 += ticktable[opcode];
990
+ if (penaltyop && penaltyaddr) clockticks6502++;
991
+
992
+ instructions++;
993
+
994
+ if (callexternal) (*loopexternal)();
995
+ }
996
+
997
+ }
998
+
999
+ void step6502() {
1000
+ opcode = read6502(pc++);
1001
+ status |= FLAG_CONSTANT;
1002
+
1003
+ penaltyop = 0;
1004
+ penaltyaddr = 0;
1005
+
1006
+ (*addrtable[opcode])();
1007
+ (*optable[opcode])();
1008
+ clockticks6502 += ticktable[opcode];
1009
+ if (penaltyop && penaltyaddr) clockticks6502++;
1010
+ clockgoal6502 = clockticks6502;
1011
+
1012
+ instructions++;
1013
+
1014
+ if (callexternal) (*loopexternal)();
1015
+ }
1016
+
1017
+ void hookexternal(void *funcptr) {
1018
+ if (funcptr != (void *)NULL) {
1019
+ loopexternal = funcptr;
1020
+ callexternal = 1;
1021
+ } else callexternal = 0;
1022
+ }
1023
+
1024
+ // functions added by infiton<kbt.tate@gmail.com> to expose 6502 internals
1025
+ uint16_t getPC() {
1026
+ return pc;
1027
+ }
1028
+
1029
+ uint8_t getSP() {
1030
+ return sp;
1031
+ }
1032
+
1033
+ uint8_t getA() {
1034
+ return a;
1035
+ }
1036
+
1037
+ uint8_t getX() {
1038
+ return x;
1039
+ }
1040
+
1041
+ uint8_t getY() {
1042
+ return y;
1043
+ }
1044
+
1045
+ uint8_t getStatus() {
1046
+ return status;
1047
+ }
1048
+
1049
+ uint32_t getInstructions() {
1050
+ return instructions;
1051
+ }
1052
+
1053
+ uint64_t getTicks() {
1054
+ return clockticks6502;
1055
+ }