1 // NES 6502 cpu emulator run function
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
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
);
37 // The following can be used within macros:
42 // Allows use of time functions
45 // Must be used before end of macro if FLUSH_TIME() was used earlier
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() {
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
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;}
83 #ifndef CAN_WRITE_FAST
84 #define CAN_WRITE_FAST( addr ) 0
85 #define WRITE_FAST( addr, data )
89 #define CAN_READ_FAST( addr ) 0
90 #define READ_FAST( addr, out )
94 #define READ_PPU( addr, out )\
97 out = READ_MEM( addr );\
102 #define READ_STACK READ_LOW
103 #define WRITE_STACK WRITE_LOW
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;\
113 READ_MEM( (addr - 0x100) );\
117 #define DUMMY_READ( addr, idx )
122 #define CODE_PAGE( addr ) (FLAT_MEM)
123 #define CODE_OFFSET( addr ) (addr)
125 #define CODE_PAGE( addr ) (s.code_map [NES_CPU_PAGE( addr )])
126 #define CODE_OFFSET( addr ) NES_CPU_OFFSET( addr )
128 #define READ_CODE( addr ) (CODE_PAGE( addr ) [CODE_OFFSET( addr )])
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)
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;\
162 #define SET_FLAGS( in ) \
164 flags = in & (v40 | d08 | i04);\
170 int const time_offset
= 0;
173 struct cpu_state_t s
;
175 s
.base
= cpu
->cpu_state_
.base
;
180 int s_time
= cpu
->cpu_state_
.time
; // helps even on x86
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
;
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 );
209 byte
const* instr
= CODE_PAGE( pc
);
212 if ( CODE_OFFSET(~0) == ~0 )
220 instr
+= CODE_OFFSET( 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
250 #ifdef CPU_INSTR_HOOK
251 { CPU_INSTR_HOOK( (pc
-1), (&instr
[-1]), a
, x
, y
, GET_SP(), TIME() ); }
254 s_time
+= clock_table
[opcode
];
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 ) );\
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) */\
287 case op + 0x0C: /* (ind),y */\
288 IND_Y( PAGE_PENALTY, data )\
290 case op + 0x10: /* zp,X */\
291 data = BYTE( data + x );\
292 case op + 0x00: /* zp */\
293 data = READ_LOW( data );\
295 case op + 0x14: /* abs,Y */\
298 case op + 0x18: /* abs,X */\
301 PAGE_PENALTY( data );\
302 case op + 0x08: /* abs */\
306 data = READ_MEM( data );\
308 case op + 0x04: /* imm */\
311 // TODO: more efficient way to handle negative branch that wraps PC around
312 #define BRANCH( cond )\
315 if ( !(cond) ) goto loop;\
317 int offset = SBYTE( data );\
318 s_time += (BYTE(pc) + offset) >> 8 & 1;\
319 pc = WORD( pc + offset );\
325 case 0xB5: // LDA zp,x
326 a
= nz
= READ_LOW( BYTE( data
+ x
) );
331 a
= nz
= READ_LOW( data
);
336 BRANCH( BYTE( nz
) );
341 WRITE_STACK( SP( -1 ), temp
>> 8 );
343 WRITE_STACK( sp
, temp
);
347 case 0x4C: // JMP abs
357 ARITH_ADDR_MODES( 0xC5 ) // CMP
368 BRANCH( !BYTE( nz
) );
370 case 0x95: // STA zp,x
371 data
= BYTE( data
+ x
);
374 WRITE_LOW( data
, a
);
390 case 0xAD:{// LDA abs
391 int addr
= GET_ADDR();
393 READ_PPU( addr
, a
= nz
);
398 pc
= 1 + READ_STACK( sp
);
399 pc
+= 0x100 * READ_STACK( SP( 1 ) );
406 case 0x8D: // STA abs
409 if ( CAN_WRITE_FAST( addr
) )
411 WRITE_FAST( addr
, a
);
416 WRITE_MEM( addr
, a
);
420 case 0x99: // STA abs,Y
421 addr
= y
+ GET_ADDR();
423 if ( CAN_WRITE_FAST( addr
) )
425 WRITE_FAST( addr
, a
);
430 case 0x9D: // STA abs,X (slightly more common than STA abs)
431 addr
= x
+ GET_ADDR();
433 if ( CAN_WRITE_FAST( addr
) )
435 WRITE_FAST( addr
, a
);
438 DUMMY_READ( addr
, x
);
441 WRITE_MEM( addr
, a
);
445 case 0x91: // STA (ind),Y
446 #define NO_PAGE_PENALTY( lsb )
447 IND_Y( NO_PAGE_PENALTY
, addr
)
449 DUMMY_READ( addr
, y
);
452 case 0x81: // STA (ind,X)
459 case 0xA9: // LDA #imm
465 // common read instructions
469 case 0xA1: // LDA (ind,X)
474 case 0xB1:// LDA (ind),Y
475 addr
= READ_LOW( data
) + y
;
476 PAGE_PENALTY( addr
);
477 addr
+= 0x100 * READ_LOW( BYTE( data
+ 1 ) );
479 READ_FAST( addr
, a
= nz
);
480 if ( CAN_READ_FAST( addr
) )
482 DUMMY_READ( addr
, y
);
485 case 0xB9: // LDA abs,Y
486 PAGE_PENALTY( data
+ y
);
487 addr
= GET_ADDR() + y
;
489 READ_FAST( addr
, a
= nz
);
490 if ( CAN_READ_FAST( addr
) )
494 case 0xBD: // LDA abs,X
495 PAGE_PENALTY( data
+ x
);
496 addr
= GET_ADDR() + x
;
498 READ_FAST( addr
, a
= nz
);
499 if ( CAN_READ_FAST( addr
) )
501 DUMMY_READ( addr
, x
);
504 a
= nz
= READ_MEM( addr
);
513 BRANCH( !(flags
& v40
) )
516 BRANCH( flags
& v40
)
522 BRANCH( !(c
& 0x100) )
526 case 0x94: // STY zp,x
527 data
= BYTE( data
+ x
);
530 WRITE_LOW( data
, y
);
533 case 0x96: // STX zp,y
534 data
= BYTE( data
+ y
);
537 WRITE_LOW( data
, x
);
540 case 0xB6: // LDX zp,y
541 data
= BYTE( data
+ y
);
543 data
= READ_LOW( data
);
544 case 0xA2: // LDX #imm
550 case 0xB4: // LDY zp,x
551 data
= BYTE( data
+ x
);
553 data
= READ_LOW( data
);
554 case 0xA0: // LDY #imm
560 case 0xBC: // LDY abs,X
562 PAGE_PENALTY( data
);
563 case 0xAC:{// LDY abs
564 int addr
= data
+ 0x100 * GET_MSB();
567 y
= nz
= READ_MEM( addr
);
572 case 0xBE: // LDX abs,y
574 PAGE_PENALTY( data
);
575 case 0xAE:{// LDX abs
576 int addr
= data
+ 0x100 * GET_MSB();
579 x
= nz
= READ_MEM( addr
);
586 case 0x8C: // STY abs
590 case 0x8E: // STX abs
594 int addr
= GET_ADDR();
596 if ( CAN_WRITE_FAST( addr
) )
598 WRITE_FAST( addr
, temp
);
602 WRITE_MEM( addr
, temp
);
610 case 0xEC: {// CPX abs
611 int addr
= GET_ADDR();
614 data
= READ_MEM( addr
);
620 data
= READ_LOW( data
);
621 case 0xE0: // CPX #imm
629 case 0xCC:{// CPY abs
630 int addr
= GET_ADDR();
633 data
= READ_MEM( addr
);
639 data
= READ_LOW( data
);
640 case 0xC0: // CPY #imm
650 ARITH_ADDR_MODES( 0x25 ) // AND
655 ARITH_ADDR_MODES( 0x45 ) // EOR
660 ARITH_ADDR_MODES( 0x05 ) // ORA
665 case 0x2C:{// BIT abs
666 int addr
= GET_ADDR();
668 READ_PPU( addr
, nz
);
669 flags
= (flags
& ~v40
) + (nz
& v40
);
672 nz
<<= 8; // result must be zero, even if N bit is set
677 nz
= READ_LOW( data
);
679 flags
= (flags
& ~v40
) + (nz
& v40
);
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
687 ARITH_ADDR_MODES( 0xE5 ) // SBC
688 case 0xEB: // unofficial equivalent
692 ARITH_ADDR_MODES( 0x65 ) // ADC
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
;
720 case 0x2A: { // ROL A
722 int temp
= c
>> 8 & 1;
729 case 0x5E: // LSR abs,X
731 case 0x4E: // LSR abs
733 case 0x6E: // ROR abs
737 int temp
= READ_MEM( data
);
738 nz
= (c
>> 1 & 0x80) + (temp
>> 1);
743 case 0x3E: // ROL abs,X
747 case 0x1E: // ASL abs,X
749 case 0x0E: // ASL abs
751 case 0x2E: // ROL abs
756 nz
+= (c
= READ_MEM( data
) << 1);
759 WRITE_MEM( data
, BYTE( nz
) );
763 case 0x7E: // ROR abs,X
767 case 0x76: // ROR zp,x
768 data
= BYTE( data
+ x
);
771 case 0x56: // LSR zp,x
772 data
= BYTE( data
+ x
);
777 int temp
= READ_LOW( data
);
778 nz
= (c
>> 1 & 0x80) + (temp
>> 1);
783 case 0x36: // ROL zp,x
784 data
= BYTE( data
+ x
);
787 case 0x16: // ASL zp,x
788 data
= BYTE( data
+ x
);
794 nz
+= (c
= READ_LOW( data
) << 1);
797 // Increment/decrement
805 case 0xF6: // INC zp,x
806 data
= BYTE( data
+ x
);
811 case 0xD6: // DEC zp,x
812 data
= BYTE( data
+ x
);
816 nz
+= READ_LOW( data
);
819 WRITE_LOW( data
, nz
);
822 case 0xFE: // INC abs,x
823 data
= x
+ GET_ADDR();
826 case 0xEE: // INC abs
832 case 0xDE: // DEC abs,x
833 data
= x
+ GET_ADDR();
836 case 0xCE: // DEC abs
843 nz
+= READ_MEM( data
);
844 WRITE_MEM( data
, BYTE( nz
) );
859 SET_SP( x
); // verified (no flag change)
870 WRITE_STACK( sp
, a
);
874 a
= nz
= READ_STACK( sp
);
879 pc
= READ_STACK( SP( 1 ) );
880 pc
+= READ_STACK( SP( 2 ) ) * 0x100;
881 int temp
= READ_STACK( sp
);
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
;
890 s
.base
= cpu
->irq_time
;
895 int temp
= READ_STACK( sp
);
897 int changed
= flags
^ temp
;
899 if ( !(changed
& i04
) )
900 goto loop
; // I flag didn't change
910 WRITE_STACK( sp
, temp
| (b10
| r20
) );
914 case 0x6C:{// JMP (ind)
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;
949 if ( !(flags
& i04
) )
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
;
958 if ( TIME() < cpu
->irq_time
)
962 s
.base
= cpu
->irq_time
;
967 if ( delta
>= s_time
+ 1 )
969 // delayed irq until after next instruction
970 s
.base
+= s_time
+ 1;
977 dprintf( "Delayed CLI not emulated\n" );
986 cpu
->r
.flags
= flags
; // update externally-visible I flag
987 int delta
= s
.base
- cpu
->end_time
;
988 s
.base
= cpu
->end_time
;
993 dprintf( "Delayed SEI not emulated\n" );
1000 case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
1001 PAGE_PENALTY( data
+ x
);
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:
1011 case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:
1014 case halt_opcode
: // HLT - halt processor
1015 if ( pc
-- > 0x10000 )
1017 // handle wrap-around (assumes caller has put page of HLT at 0x10000)
1021 case 0x02: case 0x12: case 0x32: case 0x42: case 0x52:
1022 case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2:
1027 case 0xFF: // force 256-entry jump table for optimization purposes
1028 c
|= 1; // compiler doesn't know that this won't affect anything
1030 check( (unsigned) opcode
< 0x100 );
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 )
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
);
1056 assert( false ); // catch missing 'goto loop' or accidental 'break'
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) );
1077 temp
|= r20
+ (result_
& b10
); // B flag set for BRK
1079 WRITE_STACK( sp
, temp
);
1081 // Update I flag in externally-visible flags
1082 cpu
->r
.flags
= (flags
|= i04
);
1085 int delta
= s
.base
- cpu
->end_time
;
1089 s
.base
= cpu
->end_time
;
1096 // Optional action that triggers interrupt or changes irq/end time
1099 CPU_DONE( result_
);
1108 // Flush cached state
1110 cpu
->r
.sp
= GET_SP();
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_
;