summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/nes_cpu_run.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/nes_cpu_run.h')
-rw-r--r--lib/rbcodec/codecs/libgme/nes_cpu_run.h1122
1 files changed, 1122 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/nes_cpu_run.h b/lib/rbcodec/codecs/libgme/nes_cpu_run.h
new file mode 100644
index 0000000000..fd1fea9659
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/nes_cpu_run.h
@@ -0,0 +1,1122 @@
1// NES 6502 cpu emulator run function
2
3#if 0
4/* Define these macros in the source file before #including this file.
5- Parameters might be expressions, so they are best evaluated only once,
6though they NEVER have side-effects, so multiple evaluation is OK.
7- Output parameters might be a multiple-assignment expression like "a=x",
8so they must NOT be parenthesized.
9- Except where noted, time() and related functions will NOT work
10correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and
11CACHE_TIME() allow the time changing functions to work.
12- Macros "returning" void may use a {} statement block. */
13
14 // 0 <= addr <= 0xFFFF + page_size
15 // time functions can be used
16 int READ_MEM( addr_t );
17 void WRITE_MEM( addr_t, int data );
18 // 0 <= READ_MEM() <= 0xFF
19
20 // 0 <= addr <= 0x1FF
21 int READ_LOW( addr_t );
22 void WRITE_LOW( addr_t, int data );
23 // 0 <= READ_LOW() <= 0xFF
24
25 // Often-used instructions attempt these before using a normal memory access.
26 // Optional; defaults to READ_MEM() and WRITE_MEM()
27 bool CAN_READ_FAST( addr_t ); // if true, uses result of READ_FAST
28 void READ_FAST( addr_t, int& out ); // ALWAYS called BEFORE CAN_READ_FAST
29 bool CAN_WRITE_FAST( addr_t ); // if true, uses WRITE_FAST instead of WRITE_MEM
30 void WRITE_FAST( addr_t, int data );
31
32 // Used by instructions most often used to access the NES PPU (LDA abs and BIT abs).
33 // Optional; defaults to READ_MEM.
34 void READ_PPU( addr_t, int& out );
35 // 0 <= out <= 0xFF
36
37// The following can be used within macros:
38
39 // Current time
40 time_t TIME();
41
42 // Allows use of time functions
43 void FLUSH_TIME();
44
45 // Must be used before end of macro if FLUSH_TIME() was used earlier
46 void CACHE_TIME();
47
48// Configuration (optional; commented behavior if defined)
49
50 // Emulates dummy reads for indexed instructions
51 #define NES_CPU_DUMMY_READS 1
52
53 // Optimizes as if map_code( 0, 0x10000 + cpu_padding, FLAT_MEM ) is always in effect
54 #define FLAT_MEM my_mem_array
55
56 // Expanded just before beginning of code, to help debugger
57 #define CPU_BEGIN void my_run_cpu() {
58
59#endif
60
61/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
62can redistribute it and/or modify it under the terms of the GNU Lesser
63General Public License as published by the Free Software Foundation; either
64version 2.1 of the License, or (at your option) any later version. This
65module is distributed in the hope that it will be useful, but WITHOUT ANY
66WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
67FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
68details. You should have received a copy of the GNU Lesser General Public
69License along with this module; if not, write to the Free Software Foundation,
70Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
71
72// Allows MWCW debugger to step through code properly
73#ifdef CPU_BEGIN
74 CPU_BEGIN
75#endif
76
77// Time
78#define TIME() (s_time + s.base)
79#define FLUSH_TIME() {s.time = s_time - time_offset;}
80#define CACHE_TIME() {s_time = s.time + time_offset;}
81
82// Defaults
83#ifndef CAN_WRITE_FAST
84 #define CAN_WRITE_FAST( addr ) 0
85 #define WRITE_FAST( addr, data )
86#endif
87
88#ifndef CAN_READ_FAST
89 #define CAN_READ_FAST( addr ) 0
90 #define READ_FAST( addr, out )
91#endif
92
93#ifndef READ_PPU
94 #define READ_PPU( addr, out )\
95 {\
96 FLUSH_TIME();\
97 out = READ_MEM( addr );\
98 CACHE_TIME();\
99 }
100#endif
101
102#define READ_STACK READ_LOW
103#define WRITE_STACK WRITE_LOW
104
105// Dummy reads
106#ifdef NES_CPU_DUMMY_READS
107 // TODO: optimize time handling
108 #define DUMMY_READ( addr, idx ) \
109 if ( (addr & 0xFF) < idx )\
110 {\
111 int const time_offset = 1;\
112 FLUSH_TIME();\
113 READ_MEM( (addr - 0x100) );\
114 CACHE_TIME();\
115 }
116#else
117 #define DUMMY_READ( addr, idx )
118#endif
119
120// Code
121#ifdef FLAT_MEM
122 #define CODE_PAGE( addr ) (FLAT_MEM)
123 #define CODE_OFFSET( addr ) (addr)
124#else
125 #define CODE_PAGE( addr ) (s.code_map [NES_CPU_PAGE( addr )])
126 #define CODE_OFFSET( addr ) NES_CPU_OFFSET( addr )
127#endif
128#define READ_CODE( addr ) (CODE_PAGE( addr ) [CODE_OFFSET( addr )])
129
130// Stack
131#define SET_SP( v ) (sp = ((v) + 1) | 0x100)
132#define GET_SP() ((sp - 1) & 0xFF)
133#define SP( o ) ((sp + (o - (o>0)*0x100)) | 0x100)
134
135// Truncation
136#define BYTE( n ) ((uint8_t ) (n)) /* (unsigned) n & 0xFF */
137#define SBYTE( n ) ((int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
138#define WORD( n ) ((uint16_t) (n)) /* (unsigned) n & 0xFFFF */
139
140// Flags with hex value for clarity when used as mask.
141// Stored in indicated variable during emulation.
142int const n80 = 0x80; // nz
143int const v40 = 0x40; // flags
144int const r20 = 0x20;
145int const b10 = 0x10;
146int const d08 = 0x08; // flags
147int const i04 = 0x04; // flags
148int const z02 = 0x02; // nz
149int const c01 = 0x01; // c
150
151#define IS_NEG (nz & 0x8080)
152
153#define GET_FLAGS( out ) \
154{\
155 out = flags & (v40 | d08 | i04);\
156 out += ((nz >> 8) | nz) & n80;\
157 out += c >> 8 & c01;\
158 if ( !BYTE( nz ) )\
159 out += z02;\
160}
161
162#define SET_FLAGS( in ) \
163{\
164 flags = in & (v40 | d08 | i04);\
165 c = nz = in << 8;\
166 nz += ~in & z02;\
167}
168
169{
170 int const time_offset = 0;
171
172 // Local state
173 struct cpu_state_t s;
174 #ifdef FLAT_MEM
175 s.base = cpu->cpu_state_.base;
176 #else
177 s = cpu->cpu_state_;
178 #endif
179 cpu->cpu_state = &s;
180 int s_time = cpu->cpu_state_.time; // helps even on x86
181
182 // Registers
183 int pc = cpu->r.pc;
184 int a = cpu->r.a;
185 int x = cpu->r.x;
186 int y = cpu->r.y;
187 int sp;
188 SET_SP( cpu->r.sp );
189
190 // Flags
191 int flags;
192 int c; // carry set if (c & 0x100) != 0
193 int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
194 {
195 int temp = cpu->r.flags;
196 SET_FLAGS( temp );
197 }
198
199loop:
200
201 // Check all values
202 check( (unsigned) sp - 0x100 < 0x100 );
203 check( (unsigned) pc < 0x10000 );
204 check( (unsigned) a < 0x100 );
205 check( (unsigned) x < 0x100 );
206 check( (unsigned) y < 0x100 );
207
208 // Read instruction
209 byte const* instr = CODE_PAGE( pc );
210 int opcode;
211
212 if ( CODE_OFFSET(~0) == ~0 )
213 {
214 opcode = instr [pc];
215 pc++;
216 instr += pc;
217 }
218 else
219 {
220 instr += CODE_OFFSET( pc );
221 opcode = *instr++;
222 pc++;
223 }
224
225 // local to function in case it helps optimizer
226 static byte const clock_table [256] =
227 {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
228 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
229 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
230 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
231 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
232 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
233 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
234 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
235 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
236 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
237 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
238 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
239 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
240 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
241 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
242 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
243 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
244 }; // 0x00 was 7 and 0x22 was 2
245
246 // Update time
247 if ( s_time >= 0 )
248 goto out_of_time;
249
250 #ifdef CPU_INSTR_HOOK
251 { CPU_INSTR_HOOK( (pc-1), (&instr [-1]), a, x, y, GET_SP(), TIME() ); }
252 #endif
253
254 s_time += clock_table [opcode];
255
256 int data;
257 data = *instr;
258
259 switch ( opcode )
260 {
261
262// Macros
263
264#define GET_MSB() (instr [1])
265#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB())
266#define GET_ADDR() GET_LE16( instr )
267
268#define PAGE_PENALTY( lsb ) s_time += (lsb) >> 8;
269
270#define INC_DEC( reg, n ) reg = BYTE( nz = reg + n ); goto loop;
271
272#define IND_Y( cross, out ) {\
273 int temp = READ_LOW( data ) + y;\
274 out = temp + 0x100 * READ_LOW( BYTE( data + 1 ) );\
275 cross( temp );\
276 }
277
278#define IND_X( out ) {\
279 int temp = data + x;\
280 out = 0x100 * READ_LOW( BYTE( temp + 1 ) ) + READ_LOW( BYTE( temp ) );\
281 }
282
283#define ARITH_ADDR_MODES( op )\
284case op - 0x04: /* (ind,x) */\
285 IND_X( data )\
286 goto ptr##op;\
287case op + 0x0C: /* (ind),y */\
288 IND_Y( PAGE_PENALTY, data )\
289 goto ptr##op;\
290case op + 0x10: /* zp,X */\
291 data = BYTE( data + x );\
292case op + 0x00: /* zp */\
293 data = READ_LOW( data );\
294 goto imm##op;\
295case op + 0x14: /* abs,Y */\
296 data += y;\
297 goto ind##op;\
298case op + 0x18: /* abs,X */\
299 data += x;\
300ind##op:\
301 PAGE_PENALTY( data );\
302case op + 0x08: /* abs */\
303 ADD_PAGE( data );\
304ptr##op:\
305 FLUSH_TIME();\
306 data = READ_MEM( data );\
307 CACHE_TIME();\
308case op + 0x04: /* imm */\
309imm##op:
310
311// TODO: more efficient way to handle negative branch that wraps PC around
312#define BRANCH( cond )\
313{\
314 ++pc;\
315 if ( !(cond) ) goto loop;\
316 s_time++;\
317 int offset = SBYTE( data );\
318 s_time += (BYTE(pc) + offset) >> 8 & 1;\
319 pc = WORD( pc + offset );\
320 goto loop;\
321}
322
323// Often-Used
324
325 case 0xB5: // LDA zp,x
326 a = nz = READ_LOW( BYTE( data + x ) );
327 pc++;
328 goto loop;
329
330 case 0xA5: // LDA zp
331 a = nz = READ_LOW( data );
332 pc++;
333 goto loop;
334
335 case 0xD0: // BNE
336 BRANCH( BYTE( nz ) );
337
338 case 0x20: { // JSR
339 int temp = pc + 1;
340 pc = GET_ADDR();
341 WRITE_STACK( SP( -1 ), temp >> 8 );
342 sp = SP( -2 );
343 WRITE_STACK( sp, temp );
344 goto loop;
345 }
346
347 case 0x4C: // JMP abs
348 pc = GET_ADDR();
349 goto loop;
350
351 case 0xE8: // INX
352 INC_DEC( x, 1 )
353
354 case 0x10: // BPL
355 BRANCH( !IS_NEG )
356
357 ARITH_ADDR_MODES( 0xC5 ) // CMP
358 nz = a - data;
359 pc++;
360 c = ~nz;
361 nz &= 0xFF;
362 goto loop;
363
364 case 0x30: // BMI
365 BRANCH( IS_NEG )
366
367 case 0xF0: // BEQ
368 BRANCH( !BYTE( nz ) );
369
370 case 0x95: // STA zp,x
371 data = BYTE( data + x );
372 case 0x85: // STA zp
373 pc++;
374 WRITE_LOW( data, a );
375 goto loop;
376
377 case 0xC8: // INY
378 INC_DEC( y, 1 )
379
380 case 0xA8: // TAY
381 y = a;
382 nz = a;
383 goto loop;
384
385 case 0x98: // TYA
386 a = y;
387 nz = y;
388 goto loop;
389
390 case 0xAD:{// LDA abs
391 int addr = GET_ADDR();
392 pc += 2;
393 READ_PPU( addr, a = nz );
394 goto loop;
395 }
396
397 case 0x60: // RTS
398 pc = 1 + READ_STACK( sp );
399 pc += 0x100 * READ_STACK( SP( 1 ) );
400 sp = SP( 2 );
401 goto loop;
402
403 {
404 int addr;
405
406 case 0x8D: // STA abs
407 addr = GET_ADDR();
408 pc += 2;
409 if ( CAN_WRITE_FAST( addr ) )
410 {
411 WRITE_FAST( addr, a );
412 goto loop;
413 }
414 sta_ptr:
415 FLUSH_TIME();
416 WRITE_MEM( addr, a );
417 CACHE_TIME();
418 goto loop;
419
420 case 0x99: // STA abs,Y
421 addr = y + GET_ADDR();
422 pc += 2;
423 if ( CAN_WRITE_FAST( addr ) )
424 {
425 WRITE_FAST( addr, a );
426 goto loop;
427 }
428 goto sta_abs_x;
429
430 case 0x9D: // STA abs,X (slightly more common than STA abs)
431 addr = x + GET_ADDR();
432 pc += 2;
433 if ( CAN_WRITE_FAST( addr ) )
434 {
435 WRITE_FAST( addr, a );
436 goto loop;
437 }
438 DUMMY_READ( addr, x );
439 sta_abs_x:
440 FLUSH_TIME();
441 WRITE_MEM( addr, a );
442 CACHE_TIME();
443 goto loop;
444
445 case 0x91: // STA (ind),Y
446 #define NO_PAGE_PENALTY( lsb )
447 IND_Y( NO_PAGE_PENALTY, addr )
448 pc++;
449 DUMMY_READ( addr, y );
450 goto sta_ptr;
451
452 case 0x81: // STA (ind,X)
453 IND_X( addr )
454 pc++;
455 goto sta_ptr;
456
457 }
458
459 case 0xA9: // LDA #imm
460 pc++;
461 a = data;
462 nz = data;
463 goto loop;
464
465 // common read instructions
466 {
467 int addr;
468
469 case 0xA1: // LDA (ind,X)
470 IND_X( addr )
471 pc++;
472 goto a_nz_read_addr;
473
474 case 0xB1:// LDA (ind),Y
475 addr = READ_LOW( data ) + y;
476 PAGE_PENALTY( addr );
477 addr += 0x100 * READ_LOW( BYTE( data + 1 ) );
478 pc++;
479 READ_FAST( addr, a = nz );
480 if ( CAN_READ_FAST( addr ) )
481 goto loop;
482 DUMMY_READ( addr, y );
483 goto a_nz_read_addr;
484
485 case 0xB9: // LDA abs,Y
486 PAGE_PENALTY( data + y );
487 addr = GET_ADDR() + y;
488 pc += 2;
489 READ_FAST( addr, a = nz );
490 if ( CAN_READ_FAST( addr ) )
491 goto loop;
492 goto a_nz_read_addr;
493
494 case 0xBD: // LDA abs,X
495 PAGE_PENALTY( data + x );
496 addr = GET_ADDR() + x;
497 pc += 2;
498 READ_FAST( addr, a = nz );
499 if ( CAN_READ_FAST( addr ) )
500 goto loop;
501 DUMMY_READ( addr, x );
502 a_nz_read_addr:
503 FLUSH_TIME();
504 a = nz = READ_MEM( addr );
505 CACHE_TIME();
506 goto loop;
507
508 }
509
510// Branch
511
512 case 0x50: // BVC
513 BRANCH( !(flags & v40) )
514
515 case 0x70: // BVS
516 BRANCH( flags & v40 )
517
518 case 0xB0: // BCS
519 BRANCH( c & 0x100 )
520
521 case 0x90: // BCC
522 BRANCH( !(c & 0x100) )
523
524// Load/store
525
526 case 0x94: // STY zp,x
527 data = BYTE( data + x );
528 case 0x84: // STY zp
529 pc++;
530 WRITE_LOW( data, y );
531 goto loop;
532
533 case 0x96: // STX zp,y
534 data = BYTE( data + y );
535 case 0x86: // STX zp
536 pc++;
537 WRITE_LOW( data, x );
538 goto loop;
539
540 case 0xB6: // LDX zp,y
541 data = BYTE( data + y );
542 case 0xA6: // LDX zp
543 data = READ_LOW( data );
544 case 0xA2: // LDX #imm
545 pc++;
546 x = data;
547 nz = data;
548 goto loop;
549
550 case 0xB4: // LDY zp,x
551 data = BYTE( data + x );
552 case 0xA4: // LDY zp
553 data = READ_LOW( data );
554 case 0xA0: // LDY #imm
555 pc++;
556 y = data;
557 nz = data;
558 goto loop;
559
560 case 0xBC: // LDY abs,X
561 data += x;
562 PAGE_PENALTY( data );
563 case 0xAC:{// LDY abs
564 int addr = data + 0x100 * GET_MSB();
565 pc += 2;
566 FLUSH_TIME();
567 y = nz = READ_MEM( addr );
568 CACHE_TIME();
569 goto loop;
570 }
571
572 case 0xBE: // LDX abs,y
573 data += y;
574 PAGE_PENALTY( data );
575 case 0xAE:{// LDX abs
576 int addr = data + 0x100 * GET_MSB();
577 pc += 2;
578 FLUSH_TIME();
579 x = nz = READ_MEM( addr );
580 CACHE_TIME();
581 goto loop;
582 }
583
584 {
585 int temp;
586 case 0x8C: // STY abs
587 temp = y;
588 goto store_abs;
589
590 case 0x8E: // STX abs
591 temp = x;
592 store_abs:
593 {
594 int addr = GET_ADDR();
595 pc += 2;
596 if ( CAN_WRITE_FAST( addr ) )
597 {
598 WRITE_FAST( addr, temp );
599 goto loop;
600 }
601 FLUSH_TIME();
602 WRITE_MEM( addr, temp );
603 CACHE_TIME();
604 goto loop;
605 }
606 }
607
608// Compare
609
610 case 0xEC: {// CPX abs
611 int addr = GET_ADDR();
612 pc++;
613 FLUSH_TIME();
614 data = READ_MEM( addr );
615 CACHE_TIME();
616 goto cpx_data;
617 }
618
619 case 0xE4: // CPX zp
620 data = READ_LOW( data );
621 case 0xE0: // CPX #imm
622 cpx_data:
623 nz = x - data;
624 pc++;
625 c = ~nz;
626 nz &= 0xFF;
627 goto loop;
628
629 case 0xCC:{// CPY abs
630 int addr = GET_ADDR();
631 pc++;
632 FLUSH_TIME();
633 data = READ_MEM( addr );
634 CACHE_TIME();
635 goto cpy_data;
636 }
637
638 case 0xC4: // CPY zp
639 data = READ_LOW( data );
640 case 0xC0: // CPY #imm
641 cpy_data:
642 nz = y - data;
643 pc++;
644 c = ~nz;
645 nz &= 0xFF;
646 goto loop;
647
648// Logical
649
650 ARITH_ADDR_MODES( 0x25 ) // AND
651 nz = (a &= data);
652 pc++;
653 goto loop;
654
655 ARITH_ADDR_MODES( 0x45 ) // EOR
656 nz = (a ^= data);
657 pc++;
658 goto loop;
659
660 ARITH_ADDR_MODES( 0x05 ) // ORA
661 nz = (a |= data);
662 pc++;
663 goto loop;
664
665 case 0x2C:{// BIT abs
666 int addr = GET_ADDR();
667 pc += 2;
668 READ_PPU( addr, nz );
669 flags = (flags & ~v40) + (nz & v40);
670 if ( a & nz )
671 goto loop;
672 nz <<= 8; // result must be zero, even if N bit is set
673 goto loop;
674 }
675
676 case 0x24: // BIT zp
677 nz = READ_LOW( data );
678 pc++;
679 flags = (flags & ~v40) + (nz & v40);
680 if ( a & nz )
681 goto loop; // Z should be clear, and nz must be non-zero if nz & a is
682 nz <<= 8; // set Z flag without affecting N flag
683 goto loop;
684
685// Add/subtract
686
687 ARITH_ADDR_MODES( 0xE5 ) // SBC
688 case 0xEB: // unofficial equivalent
689 data ^= 0xFF;
690 goto adc_imm;
691
692 ARITH_ADDR_MODES( 0x65 ) // ADC
693 adc_imm: {
694 int carry = c >> 8 & 1;
695 int ov = (a ^ 0x80) + carry + SBYTE( data );
696 flags = (flags & ~v40) + (ov >> 2 & v40);
697 c = nz = a + data + carry;
698 pc++;
699 a = BYTE( nz );
700 goto loop;
701 }
702
703// Shift/rotate
704
705 case 0x4A: // LSR A
706 c = 0;
707 case 0x6A: // ROR A
708 nz = c >> 1 & 0x80;
709 c = a << 8;
710 nz += a >> 1;
711 a = nz;
712 goto loop;
713
714 case 0x0A: // ASL A
715 nz = a << 1;
716 c = nz;
717 a = BYTE( nz );
718 goto loop;
719
720 case 0x2A: { // ROL A
721 nz = a << 1;
722 int temp = c >> 8 & 1;
723 c = nz;
724 nz += temp;
725 a = BYTE( nz );
726 goto loop;
727 }
728
729 case 0x5E: // LSR abs,X
730 data += x;
731 case 0x4E: // LSR abs
732 c = 0;
733 case 0x6E: // ROR abs
734 ror_abs: {
735 ADD_PAGE( data );
736 FLUSH_TIME();
737 int temp = READ_MEM( data );
738 nz = (c >> 1 & 0x80) + (temp >> 1);
739 c = temp << 8;
740 goto rotate_common;
741 }
742
743 case 0x3E: // ROL abs,X
744 data += x;
745 goto rol_abs;
746
747 case 0x1E: // ASL abs,X
748 data += x;
749 case 0x0E: // ASL abs
750 c = 0;
751 case 0x2E: // ROL abs
752 rol_abs:
753 ADD_PAGE( data );
754 nz = c >> 8 & 1;
755 FLUSH_TIME();
756 nz += (c = READ_MEM( data ) << 1);
757 rotate_common:
758 pc++;
759 WRITE_MEM( data, BYTE( nz ) );
760 CACHE_TIME();
761 goto loop;
762
763 case 0x7E: // ROR abs,X
764 data += x;
765 goto ror_abs;
766
767 case 0x76: // ROR zp,x
768 data = BYTE( data + x );
769 goto ror_zp;
770
771 case 0x56: // LSR zp,x
772 data = BYTE( data + x );
773 case 0x46: // LSR zp
774 c = 0;
775 case 0x66: // ROR zp
776 ror_zp: {
777 int temp = READ_LOW( data );
778 nz = (c >> 1 & 0x80) + (temp >> 1);
779 c = temp << 8;
780 goto write_nz_zp;
781 }
782
783 case 0x36: // ROL zp,x
784 data = BYTE( data + x );
785 goto rol_zp;
786
787 case 0x16: // ASL zp,x
788 data = BYTE( data + x );
789 case 0x06: // ASL zp
790 c = 0;
791 case 0x26: // ROL zp
792 rol_zp:
793 nz = c >> 8 & 1;
794 nz += (c = READ_LOW( data ) << 1);
795 goto write_nz_zp;
796
797// Increment/decrement
798
799 case 0xCA: // DEX
800 INC_DEC( x, -1 )
801
802 case 0x88: // DEY
803 INC_DEC( y, -1 )
804
805 case 0xF6: // INC zp,x
806 data = BYTE( data + x );
807 case 0xE6: // INC zp
808 nz = 1;
809 goto add_nz_zp;
810
811 case 0xD6: // DEC zp,x
812 data = BYTE( data + x );
813 case 0xC6: // DEC zp
814 nz = -1;
815 add_nz_zp:
816 nz += READ_LOW( data );
817 write_nz_zp:
818 pc++;
819 WRITE_LOW( data, nz );
820 goto loop;
821
822 case 0xFE: // INC abs,x
823 data = x + GET_ADDR();
824 goto inc_ptr;
825
826 case 0xEE: // INC abs
827 data = GET_ADDR();
828 inc_ptr:
829 nz = 1;
830 goto inc_common;
831
832 case 0xDE: // DEC abs,x
833 data = x + GET_ADDR();
834 goto dec_ptr;
835
836 case 0xCE: // DEC abs
837 data = GET_ADDR();
838 dec_ptr:
839 nz = -1;
840 inc_common:
841 FLUSH_TIME();
842 pc += 2;
843 nz += READ_MEM( data );
844 WRITE_MEM( data, BYTE( nz ) );
845 CACHE_TIME();
846 goto loop;
847
848// Transfer
849
850 case 0xAA: // TAX
851 x = nz = a;
852 goto loop;
853
854 case 0x8A: // TXA
855 a = nz = x;
856 goto loop;
857
858 case 0x9A: // TXS
859 SET_SP( x ); // verified (no flag change)
860 goto loop;
861
862 case 0xBA: // TSX
863 x = nz = GET_SP();
864 goto loop;
865
866// Stack
867
868 case 0x48: // PHA
869 sp = SP( -1 );
870 WRITE_STACK( sp, a );
871 goto loop;
872
873 case 0x68: // PLA
874 a = nz = READ_STACK( sp );
875 sp = SP( 1 );
876 goto loop;
877
878 case 0x40:{// RTI
879 pc = READ_STACK( SP( 1 ) );
880 pc += READ_STACK( SP( 2 ) ) * 0x100;
881 int temp = READ_STACK( sp );
882 sp = SP( 3 );
883 data = flags;
884 SET_FLAGS( temp );
885 cpu->r.flags = flags; // update externally-visible I flag
886 int delta = s.base - cpu->irq_time;
887 if ( delta <= 0 ) goto loop; // end_time < irq_time
888 if ( flags & i04 ) goto loop;
889 s_time += delta;
890 s.base = cpu->irq_time;
891 goto loop;
892 }
893
894 case 0x28:{// PLP
895 int temp = READ_STACK( sp );
896 sp = SP( 1 );
897 int changed = flags ^ temp;
898 SET_FLAGS( temp );
899 if ( !(changed & i04) )
900 goto loop; // I flag didn't change
901 if ( flags & i04 )
902 goto handle_sei;
903 goto handle_cli;
904 }
905
906 case 0x08:{// PHP
907 int temp;
908 GET_FLAGS( temp );
909 sp = SP( -1 );
910 WRITE_STACK( sp, temp | (b10 | r20) );
911 goto loop;
912 }
913
914 case 0x6C:{// JMP (ind)
915 data = GET_ADDR();
916 byte const* page = CODE_PAGE( data );
917 pc = page [CODE_OFFSET( data )];
918 data = (data & 0xFF00) + ((data + 1) & 0xFF);
919 pc += page [CODE_OFFSET( data )] * 0x100;
920 goto loop;
921 }
922
923 case 0x00: // BRK
924 goto handle_brk;
925
926// Flags
927
928 case 0x38: // SEC
929 c = 0x100;
930 goto loop;
931
932 case 0x18: // CLC
933 c = 0;
934 goto loop;
935
936 case 0xB8: // CLV
937 flags &= ~v40;
938 goto loop;
939
940 case 0xD8: // CLD
941 flags &= ~d08;
942 goto loop;
943
944 case 0xF8: // SED
945 flags |= d08;
946 goto loop;
947
948 case 0x58: // CLI
949 if ( !(flags & i04) )
950 goto loop;
951 flags &= ~i04;
952 handle_cli: {
953 //dprintf( "CLI at %d\n", TIME );
954 cpu->r.flags = flags; // update externally-visible I flag
955 int delta = s.base - cpu->irq_time;
956 if ( delta <= 0 )
957 {
958 if ( TIME() < cpu->irq_time )
959 goto loop;
960 goto delayed_cli;
961 }
962 s.base = cpu->irq_time;
963 s_time += delta;
964 if ( s_time < 0 )
965 goto loop;
966
967 if ( delta >= s_time + 1 )
968 {
969 // delayed irq until after next instruction
970 s.base += s_time + 1;
971 s_time = -1;
972 goto loop;
973 }
974
975 // TODO: implement
976 delayed_cli:
977 dprintf( "Delayed CLI not emulated\n" );
978 goto loop;
979 }
980
981 case 0x78: // SEI
982 if ( flags & i04 )
983 goto loop;
984 flags |= i04;
985 handle_sei: {
986 cpu->r.flags = flags; // update externally-visible I flag
987 int delta = s.base - cpu->end_time;
988 s.base = cpu->end_time;
989 s_time += delta;
990 if ( s_time < 0 )
991 goto loop;
992
993 dprintf( "Delayed SEI not emulated\n" );
994 goto loop;
995 }
996
997// Unofficial
998
999 // SKW - skip word
1000 case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
1001 PAGE_PENALTY( data + x );
1002 case 0x0C:
1003 pc++;
1004 // SKB - skip byte
1005 case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64:
1006 case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
1007 pc++;
1008 goto loop;
1009
1010 // NOP
1011 case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:
1012 goto loop;
1013
1014 case halt_opcode: // HLT - halt processor
1015 if ( pc-- > 0x10000 )
1016 {
1017 // handle wrap-around (assumes caller has put page of HLT at 0x10000)
1018 pc = WORD( pc );
1019 goto loop;
1020 }
1021 case 0x02: case 0x12: case 0x32: case 0x42: case 0x52:
1022 case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2:
1023 goto stop;
1024
1025// Unimplemented
1026
1027 case 0xFF: // force 256-entry jump table for optimization purposes
1028 c |= 1; // compiler doesn't know that this won't affect anything
1029 default:
1030 check( (unsigned) opcode < 0x100 );
1031
1032 #ifdef UNIMPL_INSTR
1033 UNIMPL_INSTR();
1034 #endif
1035
1036 // At least skip over proper number of bytes instruction uses
1037 static unsigned char const illop_lens [8] = {
1038 0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0
1039 };
1040 int opcode = instr [-1];
1041 int len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3;
1042 if ( opcode == 0x9C )
1043 len = 2;
1044 pc += len;
1045
1046 // Account for extra clock
1047 if ( (opcode >> 4) == 0x0B )
1048 {
1049 if ( opcode == 0xB3 )
1050 data = READ_LOW( data );
1051 if ( opcode != 0xB7 )
1052 PAGE_PENALTY( data + y );
1053 }
1054 goto loop;
1055 }
1056 assert( false ); // catch missing 'goto loop' or accidental 'break'
1057
1058 int result_;
1059handle_brk:
1060 pc++;
1061 result_ = b10 | 4;
1062
1063#ifdef CPU_DONE
1064interrupt:
1065#endif
1066 {
1067 s_time += 7;
1068
1069 // Save PC and read vector
1070 WRITE_STACK( SP( -1 ), pc >> 8 );
1071 WRITE_STACK( SP( -2 ), pc );
1072 pc = GET_LE16( &READ_CODE( 0xFFFA ) + (result_ & 4) );
1073
1074 // Save flags
1075 int temp;
1076 GET_FLAGS( temp );
1077 temp |= r20 + (result_ & b10); // B flag set for BRK
1078 sp = SP( -3 );
1079 WRITE_STACK( sp, temp );
1080
1081 // Update I flag in externally-visible flags
1082 cpu->r.flags = (flags |= i04);
1083
1084 // Update time
1085 int delta = s.base - cpu->end_time;
1086 if ( delta >= 0 )
1087 goto loop;
1088 s_time += delta;
1089 s.base = cpu->end_time;
1090 goto loop;
1091 }
1092
1093out_of_time:
1094 pc--;
1095
1096 // Optional action that triggers interrupt or changes irq/end time
1097 #ifdef CPU_DONE
1098 {
1099 CPU_DONE( result_ );
1100 if ( result_ >= 0 )
1101 goto interrupt;
1102 if ( s_time < 0 )
1103 goto loop;
1104 }
1105 #endif
1106stop:
1107
1108 // Flush cached state
1109 cpu->r.pc = pc;
1110 cpu->r.sp = GET_SP();
1111 cpu->r.a = a;
1112 cpu->r.x = x;
1113 cpu->r.y = y;
1114
1115 int temp;
1116 GET_FLAGS( temp );
1117 cpu->r.flags = temp;
1118
1119 cpu->cpu_state_.base = s.base;
1120 cpu->cpu_state_.time = s_time;
1121 cpu->cpu_state = &cpu->cpu_state_;
1122}