Submit initial patch from FS#12176. Adds support for several new game music formats...
[kugel-rb.git] / apps / codecs / libgme / nes_cpu_run.h
blob5b964d5070551cc9dfd211d7abb1a7e4c7fff468
1 // NES 6502 cpu emulator run function
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,
6 though they NEVER have side-effects, so multiple evaluation is OK.
7 - Output parameters might be a multiple-assignment expression like "a=x",
8 so they must NOT be parenthesized.
9 - Except where noted, time() and related functions will NOT work
10 correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and
11 CACHE_TIME() allow the time changing functions to work.
12 - Macros "returning" void may use a {} statement block. */
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
20 // 0 <= addr <= 0x1FF
21 int READ_LOW( addr_t );
22 void WRITE_LOW( addr_t, int data );
23 // 0 <= READ_LOW() <= 0xFF
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 );
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
37 // The following can be used within macros:
39 // Current time
40 time_t TIME();
42 // Allows use of time functions
43 void FLUSH_TIME();
45 // Must be used before end of macro if FLUSH_TIME() was used earlier
46 void CACHE_TIME();
48 // Configuration (optional; commented behavior if defined)
50 // Emulates dummy reads for indexed instructions
51 #define NES_CPU_DUMMY_READS 1
53 // Optimizes as if map_code( 0, 0x10000 + cpu_padding, FLAT_MEM ) is always in effect
54 #define FLAT_MEM my_mem_array
56 // Expanded just before beginning of code, to help debugger
57 #define CPU_BEGIN void my_run_cpu() {
59 #endif
61 /* Copyright (C) 2003-2008 Shay Green. This module is free software; you
62 can redistribute it and/or modify it under the terms of the GNU Lesser
63 General Public License as published by the Free Software Foundation; either
64 version 2.1 of the License, or (at your option) any later version. This
65 module is distributed in the hope that it will be useful, but WITHOUT ANY
66 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
67 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
68 details. You should have received a copy of the GNU Lesser General Public
69 License along with this module; if not, write to the Free Software Foundation,
70 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
72 // Allows MWCW debugger to step through code properly
73 #ifdef CPU_BEGIN
74 CPU_BEGIN
75 #endif
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;}
82 // Defaults
83 #ifndef CAN_WRITE_FAST
84 #define CAN_WRITE_FAST( addr ) 0
85 #define WRITE_FAST( addr, data )
86 #endif
88 #ifndef CAN_READ_FAST
89 #define CAN_READ_FAST( addr ) 0
90 #define READ_FAST( addr, out )
91 #endif
93 #ifndef READ_PPU
94 #define READ_PPU( addr, out )\
96 FLUSH_TIME();\
97 out = READ_MEM( addr );\
98 CACHE_TIME();\
100 #endif
102 #define READ_STACK READ_LOW
103 #define WRITE_STACK WRITE_LOW
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 )\
111 int const time_offset = 1;\
112 FLUSH_TIME();\
113 READ_MEM( (addr - 0x100) );\
114 CACHE_TIME();\
116 #else
117 #define DUMMY_READ( addr, idx )
118 #endif
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 )])
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)
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 */
140 // Flags with hex value for clarity when used as mask.
141 // Stored in indicated variable during emulation.
142 int const n80 = 0x80; // nz
143 int const v40 = 0x40; // flags
144 int const r20 = 0x20;
145 int const b10 = 0x10;
146 int const d08 = 0x08; // flags
147 int const i04 = 0x04; // flags
148 int const z02 = 0x02; // nz
149 int const c01 = 0x01; // c
151 #define IS_NEG (nz & 0x8080)
153 #define GET_FLAGS( out ) \
155 out = flags & (v40 | d08 | i04);\
156 out += ((nz >> 8) | nz) & n80;\
157 out += c >> 8 & c01;\
158 if ( !BYTE( nz ) )\
159 out += z02;\
162 #define SET_FLAGS( in ) \
164 flags = in & (v40 | d08 | i04);\
165 c = nz = in << 8;\
166 nz += ~in & z02;\
170 int const time_offset = 0;
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
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 );
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
195 int temp = cpu->r.flags;
196 SET_FLAGS( temp );
199 loop:
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 );
208 // Read instruction
209 byte const* instr = CODE_PAGE( pc );
210 int opcode;
212 if ( CODE_OFFSET(~0) == ~0 )
214 opcode = instr [pc];
215 pc++;
216 instr += pc;
218 else
220 instr += CODE_OFFSET( pc );
221 opcode = *instr++;
222 pc++;
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
246 // Update time
247 if ( s_time >= 0 )
248 goto out_of_time;
250 #ifdef CPU_INSTR_HOOK
251 { CPU_INSTR_HOOK( (pc-1), (&instr [-1]), a, x, y, GET_SP(), TIME() ); }
252 #endif
254 s_time += clock_table [opcode];
256 int data;
257 data = *instr;
259 switch ( opcode )
262 // Macros
264 #define GET_MSB() (instr [1])
265 #define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB())
266 #define GET_ADDR() GET_LE16( instr )
268 #define PAGE_PENALTY( lsb ) s_time += (lsb) >> 8;
270 #define INC_DEC( reg, n ) reg = BYTE( nz = reg + n ); goto loop;
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 );\
278 #define IND_X( out ) {\
279 int temp = data + x;\
280 out = 0x100 * READ_LOW( BYTE( temp + 1 ) ) + READ_LOW( BYTE( temp ) );\
283 #define ARITH_ADDR_MODES( op )\
284 case op - 0x04: /* (ind,x) */\
285 IND_X( data )\
286 goto ptr##op;\
287 case op + 0x0C: /* (ind),y */\
288 IND_Y( PAGE_PENALTY, data )\
289 goto ptr##op;\
290 case op + 0x10: /* zp,X */\
291 data = BYTE( data + x );\
292 case op + 0x00: /* zp */\
293 data = READ_LOW( data );\
294 goto imm##op;\
295 case op + 0x14: /* abs,Y */\
296 data += y;\
297 goto ind##op;\
298 case op + 0x18: /* abs,X */\
299 data += x;\
300 ind##op:\
301 PAGE_PENALTY( data );\
302 case op + 0x08: /* abs */\
303 ADD_PAGE( data );\
304 ptr##op:\
305 FLUSH_TIME();\
306 data = READ_MEM( data );\
307 CACHE_TIME();\
308 case op + 0x04: /* imm */\
309 imm##op:
311 // TODO: more efficient way to handle negative branch that wraps PC around
312 #define BRANCH( cond )\
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;\
323 // Often-Used
325 case 0xB5: // LDA zp,x
326 a = nz = READ_LOW( BYTE( data + x ) );
327 pc++;
328 goto loop;
330 case 0xA5: // LDA zp
331 a = nz = READ_LOW( data );
332 pc++;
333 goto loop;
335 case 0xD0: // BNE
336 BRANCH( BYTE( nz ) );
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;
347 case 0x4C: // JMP abs
348 pc = GET_ADDR();
349 goto loop;
351 case 0xE8: // INX
352 INC_DEC( x, 1 )
354 case 0x10: // BPL
355 BRANCH( !IS_NEG )
357 ARITH_ADDR_MODES( 0xC5 ) // CMP
358 nz = a - data;
359 pc++;
360 c = ~nz;
361 nz &= 0xFF;
362 goto loop;
364 case 0x30: // BMI
365 BRANCH( IS_NEG )
367 case 0xF0: // BEQ
368 BRANCH( !BYTE( nz ) );
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;
377 case 0xC8: // INY
378 INC_DEC( y, 1 )
380 case 0xA8: // TAY
381 y = a;
382 nz = a;
383 goto loop;
385 case 0x98: // TYA
386 a = y;
387 nz = y;
388 goto loop;
390 case 0xAD:{// LDA abs
391 int addr = GET_ADDR();
392 pc += 2;
393 READ_PPU( addr, a = nz );
394 goto loop;
397 case 0x60: // RTS
398 pc = 1 + READ_STACK( sp );
399 pc += 0x100 * READ_STACK( SP( 1 ) );
400 sp = SP( 2 );
401 goto loop;
404 int addr;
406 case 0x8D: // STA abs
407 addr = GET_ADDR();
408 pc += 2;
409 if ( CAN_WRITE_FAST( addr ) )
411 WRITE_FAST( addr, a );
412 goto loop;
414 sta_ptr:
415 FLUSH_TIME();
416 WRITE_MEM( addr, a );
417 CACHE_TIME();
418 goto loop;
420 case 0x99: // STA abs,Y
421 addr = y + GET_ADDR();
422 pc += 2;
423 if ( CAN_WRITE_FAST( addr ) )
425 WRITE_FAST( addr, a );
426 goto loop;
428 goto sta_abs_x;
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 ) )
435 WRITE_FAST( addr, a );
436 goto loop;
438 DUMMY_READ( addr, x );
439 sta_abs_x:
440 FLUSH_TIME();
441 WRITE_MEM( addr, a );
442 CACHE_TIME();
443 goto loop;
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;
452 case 0x81: // STA (ind,X)
453 IND_X( addr )
454 pc++;
455 goto sta_ptr;
459 case 0xA9: // LDA #imm
460 pc++;
461 a = data;
462 nz = data;
463 goto loop;
465 // common read instructions
467 int addr;
469 case 0xA1: // LDA (ind,X)
470 IND_X( addr )
471 pc++;
472 goto a_nz_read_addr;
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;
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;
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;
510 // Branch
512 case 0x50: // BVC
513 BRANCH( !(flags & v40) )
515 case 0x70: // BVS
516 BRANCH( flags & v40 )
518 case 0xB0: // BCS
519 BRANCH( c & 0x100 )
521 case 0x90: // BCC
522 BRANCH( !(c & 0x100) )
524 // Load/store
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;
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;
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;
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;
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;
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;
585 int temp;
586 case 0x8C: // STY abs
587 temp = y;
588 goto store_abs;
590 case 0x8E: // STX abs
591 temp = x;
592 store_abs:
594 int addr = GET_ADDR();
595 pc += 2;
596 if ( CAN_WRITE_FAST( addr ) )
598 WRITE_FAST( addr, temp );
599 goto loop;
601 FLUSH_TIME();
602 WRITE_MEM( addr, temp );
603 CACHE_TIME();
604 goto loop;
608 // Compare
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;
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;
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;
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;
648 // Logical
650 ARITH_ADDR_MODES( 0x25 ) // AND
651 nz = (a &= data);
652 pc++;
653 goto loop;
655 ARITH_ADDR_MODES( 0x45 ) // EOR
656 nz = (a ^= data);
657 pc++;
658 goto loop;
660 ARITH_ADDR_MODES( 0x05 ) // ORA
661 nz = (a |= data);
662 pc++;
663 goto loop;
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;
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;
685 // Add/subtract
687 ARITH_ADDR_MODES( 0xE5 ) // SBC
688 case 0xEB: // unofficial equivalent
689 data ^= 0xFF;
690 goto adc_imm;
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;
703 // Shift/rotate
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;
714 case 0x0A: // ASL A
715 nz = a << 1;
716 c = nz;
717 a = BYTE( nz );
718 goto loop;
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;
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;
743 case 0x3E: // ROL abs,X
744 data += x;
745 goto rol_abs;
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;
763 case 0x7E: // ROR abs,X
764 data += x;
765 goto ror_abs;
767 case 0x76: // ROR zp,x
768 data = BYTE( data + x );
769 goto ror_zp;
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;
783 case 0x36: // ROL zp,x
784 data = BYTE( data + x );
785 goto rol_zp;
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;
797 // Increment/decrement
799 case 0xCA: // DEX
800 INC_DEC( x, -1 )
802 case 0x88: // DEY
803 INC_DEC( y, -1 )
805 case 0xF6: // INC zp,x
806 data = BYTE( data + x );
807 case 0xE6: // INC zp
808 nz = 1;
809 goto add_nz_zp;
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;
822 case 0xFE: // INC abs,x
823 data = x + GET_ADDR();
824 goto inc_ptr;
826 case 0xEE: // INC abs
827 data = GET_ADDR();
828 inc_ptr:
829 nz = 1;
830 goto inc_common;
832 case 0xDE: // DEC abs,x
833 data = x + GET_ADDR();
834 goto dec_ptr;
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;
848 // Transfer
850 case 0xAA: // TAX
851 x = nz = a;
852 goto loop;
854 case 0x8A: // TXA
855 a = nz = x;
856 goto loop;
858 case 0x9A: // TXS
859 SET_SP( x ); // verified (no flag change)
860 goto loop;
862 case 0xBA: // TSX
863 x = nz = GET_SP();
864 goto loop;
866 // Stack
868 case 0x48: // PHA
869 sp = SP( -1 );
870 WRITE_STACK( sp, a );
871 goto loop;
873 case 0x68: // PLA
874 a = nz = READ_STACK( sp );
875 sp = SP( 1 );
876 goto loop;
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;
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;
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;
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;
923 case 0x00: // BRK
924 goto handle_brk;
926 // Flags
928 case 0x38: // SEC
929 c = 0x100;
930 goto loop;
932 case 0x18: // CLC
933 c = 0;
934 goto loop;
936 case 0xB8: // CLV
937 flags &= ~v40;
938 goto loop;
940 case 0xD8: // CLD
941 flags &= ~d08;
942 goto loop;
944 case 0xF8: // SED
945 flags |= d08;
946 goto loop;
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 )
958 if ( TIME() < cpu->irq_time )
959 goto loop;
960 goto delayed_cli;
962 s.base = cpu->irq_time;
963 s_time += delta;
964 if ( s_time < 0 )
965 goto loop;
967 if ( delta >= s_time + 1 )
969 // delayed irq until after next instruction
970 s.base += s_time + 1;
971 s_time = -1;
972 goto loop;
975 // TODO: implement
976 delayed_cli:
977 dprintf( "Delayed CLI not emulated\n" );
978 goto loop;
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;
993 dprintf( "Delayed SEI not emulated\n" );
994 goto loop;
997 // Unofficial
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;
1010 // NOP
1011 case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:
1012 goto loop;
1014 case halt_opcode: // HLT - halt processor
1015 if ( pc-- > 0x10000 )
1017 // handle wrap-around (assumes caller has put page of HLT at 0x10000)
1018 pc = WORD( pc );
1019 goto loop;
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;
1025 // Unimplemented
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 );
1032 #ifdef UNIMPL_INSTR
1033 UNIMPL_INSTR();
1034 #endif
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
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;
1046 // Account for extra clock
1047 if ( (opcode >> 4) == 0x0B )
1049 if ( opcode == 0xB3 )
1050 data = READ_LOW( data );
1051 if ( opcode != 0xB7 )
1052 PAGE_PENALTY( data + y );
1054 goto loop;
1056 assert( false ); // catch missing 'goto loop' or accidental 'break'
1058 int result_;
1059 handle_brk:
1060 pc++;
1061 result_ = b10 | 4;
1063 #ifdef CPU_DONE
1064 interrupt:
1065 #endif
1067 s_time += 7;
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) );
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 );
1081 // Update I flag in externally-visible flags
1082 cpu->r.flags = (flags |= i04);
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;
1093 out_of_time:
1094 pc--;
1096 // Optional action that triggers interrupt or changes irq/end time
1097 #ifdef CPU_DONE
1099 CPU_DONE( result_ );
1100 if ( result_ >= 0 )
1101 goto interrupt;
1102 if ( s_time < 0 )
1103 goto loop;
1105 #endif
1106 stop:
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;
1115 int temp;
1116 GET_FLAGS( temp );
1117 cpu->r.flags = temp;
1119 cpu->cpu_state_.base = s.base;
1120 cpu->cpu_state_.time = s_time;
1121 cpu->cpu_state = &cpu->cpu_state_;