ruby6502 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }