diff options
Diffstat (limited to 'apps/codecs/libgme/nes_cpu_run.h')
-rw-r--r-- | apps/codecs/libgme/nes_cpu_run.h | 1122 |
1 files changed, 1122 insertions, 0 deletions
diff --git a/apps/codecs/libgme/nes_cpu_run.h b/apps/codecs/libgme/nes_cpu_run.h new file mode 100644 index 0000000000..5b964d5070 --- /dev/null +++ b/apps/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, | ||
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. */ | ||
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 | ||
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 */ | ||
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. | ||
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 | ||
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 | |||
199 | loop: | ||
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 )\ | ||
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: | ||
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_; | ||
1059 | handle_brk: | ||
1060 | pc++; | ||
1061 | result_ = b10 | 4; | ||
1062 | |||
1063 | #ifdef CPU_DONE | ||
1064 | interrupt: | ||
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 | |||
1093 | out_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 | ||
1106 | stop: | ||
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 | } | ||