summaryrefslogtreecommitdiff
path: root/apps/codecs/libgme/hes_cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codecs/libgme/hes_cpu.c')
-rw-r--r--apps/codecs/libgme/hes_cpu.c1321
1 files changed, 1321 insertions, 0 deletions
diff --git a/apps/codecs/libgme/hes_cpu.c b/apps/codecs/libgme/hes_cpu.c
new file mode 100644
index 0000000000..08dfb5e993
--- /dev/null
+++ b/apps/codecs/libgme/hes_cpu.c
@@ -0,0 +1,1321 @@
1// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
2
3#include "hes_cpu.h"
4
5#include "blargg_endian.h"
6
7//#include "hes_cpu_log.h"
8
9/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
10can redistribute it and/or modify it under the terms of the GNU Lesser
11General Public License as published by the Free Software Foundation; either
12version 2.1 of the License, or (at your option) any later version. This
13module is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16details. You should have received a copy of the GNU Lesser General Public
17License along with this module; if not, write to the Free Software Foundation,
18Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19
20// TODO: support T flag, including clearing it at appropriate times?
21
22// all zero-page should really use whatever is at page 1, but that would
23// reduce efficiency quite a bit
24int const ram_addr = 0x2000;
25
26#define FLUSH_TIME() (void) (s.time = s_time)
27#define CACHE_TIME() (void) (s_time = s.time)
28
29#include "hes_cpu_io.h"
30
31#include "blargg_source.h"
32
33#ifdef BLARGG_NONPORTABLE
34 #define PAGE_OFFSET( addr ) (addr)
35#else
36 #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
37#endif
38
39// status flags
40int const st_n = 0x80;
41int const st_v = 0x40;
42int const st_t = 0x20;
43int const st_b = 0x10;
44int const st_d = 0x08;
45int const st_i = 0x04;
46int const st_z = 0x02;
47int const st_c = 0x01;
48
49void Cpu_init( struct Hes_Cpu* this )
50{
51 this->state = &this->state_;
52}
53
54void Cpu_reset( struct Hes_Cpu* this )
55{
56 check( this->state == &state_ );
57 this->state = &this->state_;
58
59 this->state_.time = 0;
60 this->state_.base = 0;
61 this->irq_time = future_hes_time;
62 this->end_time = future_hes_time;
63
64 this->r.status = st_i;
65 this->r.sp = 0;
66 this->r.pc = 0;
67 this->r.a = 0;
68 this->r.x = 0;
69 this->r.y = 0;
70
71 blargg_verify_byte_order();
72}
73
74void Cpu_set_mmr( struct Hes_Emu* this, int reg, int bank )
75{
76 assert( (unsigned) reg <= page_count ); // allow page past end to be set
77 assert( (unsigned) bank < 0x100 );
78 this->cpu.mmr [reg] = bank;
79 uint8_t const* code = CPU_SET_MMR( this, reg, bank );
80 this->cpu.state->code_map [reg] = code - PAGE_OFFSET( reg << page_shift );
81}
82
83#define TIME (s_time + s.base)
84
85#define READ( addr ) CPU_READ( this, (addr), TIME )
86#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );}
87#define READ_LOW( addr ) (cpu->ram [(int) (addr)])
88#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
89#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )])
90
91#define SET_SP( v ) (sp = ((v) + 1) | 0x100)
92#define GET_SP() ((sp - 1) & 0xFF)
93#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
94
95// even on x86, using short and unsigned char was slower
96typedef int fint16;
97typedef unsigned fuint16;
98typedef unsigned fuint8;
99typedef blargg_long fint32;
100
101bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time )
102{
103 bool illegal_encountered = false;
104
105 // Set cpu end time
106 struct Hes_Cpu* cpu = &this->cpu;
107 cpu->state->time += Cpu_update_end_time( cpu, cpu->r.status, (cpu->end_time = end_time), cpu->irq_time );
108
109 struct state_t s = cpu->state_;
110 cpu->state = &s;
111
112 // even on x86, using s.time in place of s_time was slower
113 fint16 s_time = s.time;
114
115 struct registers_t* r = &cpu->r;
116
117 // registers
118 fuint16 pc = r->pc;
119 fuint8 a = r->a;
120 fuint8 x = r->x;
121 fuint8 y = r->y;
122 fuint16 sp;
123 SET_SP( r->sp );
124
125 #define IS_NEG (nz & 0x8080)
126
127 #define CALC_STATUS( out ) do {\
128 out = status & (st_v | st_d | st_i);\
129 out |= ((nz >> 8) | nz) & st_n;\
130 out |= c >> 8 & st_c;\
131 if ( !(nz & 0xFF) ) out |= st_z;\
132 } while ( 0 )
133
134 #define SET_STATUS( in ) do {\
135 status = in & (st_v | st_d | st_i);\
136 nz = in << 8;\
137 c = nz;\
138 nz |= ~in & st_z;\
139 } while ( 0 )
140
141 fuint8 status;
142 fuint16 c; // carry set if (c & 0x100) != 0
143 fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
144 {
145 fuint8 temp = r->status;
146 SET_STATUS( temp );
147 }
148
149 goto loop;
150branch_not_taken:
151 s_time -= 2;
152loop:
153
154 #ifndef NDEBUG
155 {
156 hes_time_t correct = end_time_;
157 if ( !(status & st_i) && correct > irq_time_ )
158 correct = irq_time_;
159 check( s.base == correct );
160 /*
161 static long count;
162 if ( count == 1844 ) Debugger();
163 if ( s.base != correct ) dprintf( "%ld\n", count );
164 count++;
165 */
166 }
167 #endif
168
169 check( (unsigned) GET_SP() < 0x100 );
170 check( (unsigned) a < 0x100 );
171 check( (unsigned) x < 0x100 );
172
173 uint8_t const* instr = s.code_map [pc >> page_shift];
174 fuint8 opcode;
175
176 // TODO: eliminate this special case
177 #ifdef BLARGG_NONPORTABLE
178 opcode = instr [pc];
179 pc++;
180 instr += pc;
181 #else
182 instr += PAGE_OFFSET( pc );
183 opcode = *instr++;
184 pc++;
185 #endif
186
187 // TODO: each reference lists slightly different timing values, ugh
188 static uint8_t const clock_table [256] ICONST_ATTR =
189 {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
190 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,6,// 0
191 4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1
192 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2
193 4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3
194 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4
195 4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5
196 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6
197 4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7
198 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8
199 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9
200 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A
201 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B
202 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C
203 4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D
204 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E
205 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F
206 }; // 0x00 was 8
207
208 fuint16 data;
209 data = clock_table [opcode];
210 if ( (s_time += data) >= 0 )
211 goto possibly_out_of_time;
212almost_out_of_time:
213
214 data = *instr;
215
216 #ifdef HES_CPU_LOG_H
217 log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2],
218 instr [3], instr [4], instr [5] );
219 //log_opcode( opcode );
220 #endif
221
222 switch ( opcode )
223 {
224possibly_out_of_time:
225 if ( s_time < (int) data )
226 goto almost_out_of_time;
227 s_time -= data;
228 goto out_of_time;
229
230// Macros
231
232#define GET_MSB() (instr [1])
233#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB());
234#define GET_ADDR() GET_LE16( instr )
235
236// TODO: is the penalty really always added? the original 6502 was much better
237//#define PAGE_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8)
238#define PAGE_CROSS_PENALTY( lsb )
239
240// Branch
241
242// TODO: more efficient way to handle negative branch that wraps PC around
243#define BRANCH( cond )\
244{\
245 fint16 offset = (int8_t) data;\
246 pc++;\
247 if ( !(cond) ) goto branch_not_taken;\
248 pc = (uint16_t) (pc + offset);\
249 goto loop;\
250}
251
252 case 0xF0: // BEQ
253 BRANCH( !((uint8_t) nz) );
254
255 case 0xD0: // BNE
256 BRANCH( (uint8_t) nz );
257
258 case 0x10: // BPL
259 BRANCH( !IS_NEG );
260
261 case 0x90: // BCC
262 BRANCH( !(c & 0x100) )
263
264 case 0x30: // BMI
265 BRANCH( IS_NEG )
266
267 case 0x50: // BVC
268 BRANCH( !(status & st_v) )
269
270 case 0x70: // BVS
271 BRANCH( status & st_v )
272
273 case 0xB0: // BCS
274 BRANCH( c & 0x100 )
275
276 case 0x80: // BRA
277 branch_taken:
278 BRANCH( true );
279
280 case 0xFF:
281 if ( pc == idle_addr + 1 )
282 goto idle_done;
283 case 0x0F: // BBRn
284 case 0x1F:
285 case 0x2F:
286 case 0x3F:
287 case 0x4F:
288 case 0x5F:
289 case 0x6F:
290 case 0x7F:
291 case 0x8F: // BBSn
292 case 0x9F:
293 case 0xAF:
294 case 0xBF:
295 case 0xCF:
296 case 0xDF:
297 case 0xEF: {
298 fuint16 t = 0x101 * READ_LOW( data );
299 t ^= 0xFF;
300 pc++;
301 data = GET_MSB();
302 BRANCH( t & (1 << (opcode >> 4)) )
303 }
304
305 case 0x4C: // JMP abs
306 pc = GET_ADDR();
307 goto loop;
308
309 case 0x7C: // JMP (ind+X)
310 data += x;
311 case 0x6C:{// JMP (ind)
312 data += 0x100 * GET_MSB();
313 pc = GET_LE16( &READ_PROG( data ) );
314 goto loop;
315 }
316
317// Subroutine
318
319 case 0x44: // BSR
320 WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
321 sp = (sp - 2) | 0x100;
322 WRITE_LOW( sp, pc );
323 goto branch_taken;
324
325 case 0x20: { // JSR
326 fuint16 temp = pc + 1;
327 pc = GET_ADDR();
328 WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
329 sp = (sp - 2) | 0x100;
330 WRITE_LOW( sp, temp );
331 goto loop;
332 }
333
334 case 0x60: // RTS
335 pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
336 pc += 1 + READ_LOW( sp );
337 sp = (sp - 0xFE) | 0x100;
338 goto loop;
339
340 case 0x00: // BRK
341 goto handle_brk;
342
343// Common
344
345 case 0xBD:{// LDA abs,X
346 PAGE_CROSS_PENALTY( data + x );
347 fuint16 addr = GET_ADDR() + x;
348 pc += 2;
349 CPU_READ_FAST( this, addr, TIME, nz );
350 a = nz;
351 goto loop;
352 }
353
354 case 0x9D:{// STA abs,X
355 fuint16 addr = GET_ADDR() + x;
356 pc += 2;
357 CPU_WRITE_FAST( this, addr, a, TIME );
358 goto loop;
359 }
360
361 case 0x95: // STA zp,x
362 data = (uint8_t) (data + x);
363 case 0x85: // STA zp
364 pc++;
365 WRITE_LOW( data, a );
366 goto loop;
367
368 case 0xAE:{// LDX abs
369 fuint16 addr = GET_ADDR();
370 pc += 2;
371 CPU_READ_FAST( this, addr, TIME, nz );
372 x = nz;
373 goto loop;
374 }
375
376 case 0xA5: // LDA zp
377 a = nz = READ_LOW( data );
378 pc++;
379 goto loop;
380
381// Load/store
382
383 {
384 fuint16 addr;
385 case 0x91: // STA (ind),Y
386 addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
387 addr += READ_LOW( data ) + y;
388 pc++;
389 goto sta_ptr;
390
391 case 0x81: // STA (ind,X)
392 data = (uint8_t) (data + x);
393 case 0x92: // STA (ind)
394 addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
395 addr += READ_LOW( data );
396 pc++;
397 goto sta_ptr;
398
399 case 0x99: // STA abs,Y
400 data += y;
401 case 0x8D: // STA abs
402 addr = data + 0x100 * GET_MSB();
403 pc += 2;
404 sta_ptr:
405 CPU_WRITE_FAST( this, addr, a, TIME );
406 goto loop;
407 }
408
409 {
410 fuint16 addr;
411 case 0xA1: // LDA (ind,X)
412 data = (uint8_t) (data + x);
413 case 0xB2: // LDA (ind)
414 addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
415 addr += READ_LOW( data );
416 pc++;
417 goto a_nz_read_addr;
418
419 case 0xB1:// LDA (ind),Y
420 addr = READ_LOW( data ) + y;
421 PAGE_CROSS_PENALTY( addr );
422 addr += 0x100 * READ_LOW( (uint8_t) (data + 1) );
423 pc++;
424 goto a_nz_read_addr;
425
426 case 0xB9: // LDA abs,Y
427 data += y;
428 PAGE_CROSS_PENALTY( data );
429 case 0xAD: // LDA abs
430 addr = data + 0x100 * GET_MSB();
431 pc += 2;
432 a_nz_read_addr:
433 CPU_READ_FAST( this, addr, TIME, nz );
434 a = nz;
435 goto loop;
436 }
437
438 case 0xBE:{// LDX abs,y
439 PAGE_CROSS_PENALTY( data + y );
440 fuint16 addr = GET_ADDR() + y;
441 pc += 2;
442 FLUSH_TIME();
443 x = nz = READ( addr );
444 CACHE_TIME();
445 goto loop;
446 }
447
448 case 0xB5: // LDA zp,x
449 a = nz = READ_LOW( (uint8_t) (data + x) );
450 pc++;
451 goto loop;
452
453 case 0xA9: // LDA #imm
454 pc++;
455 a = data;
456 nz = data;
457 goto loop;
458
459// Bit operations
460
461 case 0x3C: // BIT abs,x
462 data += x;
463 case 0x2C:{// BIT abs
464 fuint16 addr;
465 ADD_PAGE( addr );
466 FLUSH_TIME();
467 nz = READ( addr );
468 CACHE_TIME();
469 goto bit_common;
470 }
471 case 0x34: // BIT zp,x
472 data = (uint8_t) (data + x);
473 case 0x24: // BIT zp
474 data = READ_LOW( data );
475 case 0x89: // BIT imm
476 nz = data;
477 bit_common:
478 pc++;
479 status &= ~st_v;
480 status |= nz & st_v;
481 if ( nz & a )
482 goto loop; // Z should be clear, and nz must be non-zero if nz & a is
483 nz <<= 8; // set Z flag without affecting N flag
484 goto loop;
485
486 {
487 fuint16 addr;
488
489 case 0xB3: // TST abs,x
490 addr = GET_MSB() + x;
491 goto tst_abs;
492
493 case 0x93: // TST abs
494 addr = GET_MSB();
495 tst_abs:
496 addr += 0x100 * instr [2];
497 pc++;
498 FLUSH_TIME();
499 nz = READ( addr );
500 CACHE_TIME();
501 goto tst_common;
502 }
503
504 case 0xA3: // TST zp,x
505 nz = READ_LOW( (uint8_t) (GET_MSB() + x) );
506 goto tst_common;
507
508 case 0x83: // TST zp
509 nz = READ_LOW( GET_MSB() );
510 tst_common:
511 pc += 2;
512 status &= ~st_v;
513 status |= nz & st_v;
514 if ( nz & data )
515 goto loop; // Z should be clear, and nz must be non-zero if nz & data is
516 nz <<= 8; // set Z flag without affecting N flag
517 goto loop;
518
519 {
520 fuint16 addr;
521 case 0x0C: // TSB abs
522 case 0x1C: // TRB abs
523 addr = GET_ADDR();
524 pc++;
525 goto txb_addr;
526
527 // TODO: everyone lists different behaviors for the status flags, ugh
528 case 0x04: // TSB zp
529 case 0x14: // TRB zp
530 addr = data + ram_addr;
531 txb_addr:
532 FLUSH_TIME();
533 nz = a | READ( addr );
534 if ( opcode & 0x10 )
535 nz ^= a; // bits from a will already be set, so this clears them
536 status &= ~st_v;
537 status |= nz & st_v;
538 pc++;
539 WRITE( addr, nz );
540 CACHE_TIME();
541 goto loop;
542 }
543
544 case 0x07: // RMBn
545 case 0x17:
546 case 0x27:
547 case 0x37:
548 case 0x47:
549 case 0x57:
550 case 0x67:
551 case 0x77:
552 pc++;
553 READ_LOW( data ) &= ~(1 << (opcode >> 4));
554 goto loop;
555
556 case 0x87: // SMBn
557 case 0x97:
558 case 0xA7:
559 case 0xB7:
560 case 0xC7:
561 case 0xD7:
562 case 0xE7:
563 case 0xF7:
564 pc++;
565 READ_LOW( data ) |= 1 << ((opcode >> 4) - 8);
566 goto loop;
567
568// Load/store
569
570 case 0x9E: // STZ abs,x
571 data += x;
572 case 0x9C: // STZ abs
573 ADD_PAGE( data );
574 pc++;
575 FLUSH_TIME();
576 WRITE( data, 0 );
577 CACHE_TIME();
578 goto loop;
579
580 case 0x74: // STZ zp,x
581 data = (uint8_t) (data + x);
582 case 0x64: // STZ zp
583 pc++;
584 WRITE_LOW( data, 0 );
585 goto loop;
586
587 case 0x94: // STY zp,x
588 data = (uint8_t) (data + x);
589 case 0x84: // STY zp
590 pc++;
591 WRITE_LOW( data, y );
592 goto loop;
593
594 case 0x96: // STX zp,y
595 data = (uint8_t) (data + y);
596 case 0x86: // STX zp
597 pc++;
598 WRITE_LOW( data, x );
599 goto loop;
600
601 case 0xB6: // LDX zp,y
602 data = (uint8_t) (data + y);
603 case 0xA6: // LDX zp
604 data = READ_LOW( data );
605 case 0xA2: // LDX #imm
606 pc++;
607 x = data;
608 nz = data;
609 goto loop;
610
611 case 0xB4: // LDY zp,x
612 data = (uint8_t) (data + x);
613 case 0xA4: // LDY zp
614 data = READ_LOW( data );
615 case 0xA0: // LDY #imm
616 pc++;
617 y = data;
618 nz = data;
619 goto loop;
620
621 case 0xBC: // LDY abs,X
622 data += x;
623 PAGE_CROSS_PENALTY( data );
624 case 0xAC:{// LDY abs
625 fuint16 addr = data + 0x100 * GET_MSB();
626 pc += 2;
627 FLUSH_TIME();
628 y = nz = READ( addr );
629 CACHE_TIME();
630 goto loop;
631 }
632
633 {
634 fuint8 temp;
635 case 0x8C: // STY abs
636 temp = y;
637 goto store_abs;
638
639 case 0x8E: // STX abs
640 temp = x;
641 store_abs:
642 {
643 fuint16 addr = GET_ADDR();
644 pc += 2;
645 FLUSH_TIME();
646 WRITE( addr, temp );
647 CACHE_TIME();
648 goto loop;
649 }
650 }
651
652// Compare
653
654 case 0xEC:{// CPX abs
655 fuint16 addr = GET_ADDR();
656 pc++;
657 FLUSH_TIME();
658 data = READ( addr );
659 CACHE_TIME();
660 goto cpx_data;
661 }
662
663 case 0xE4: // CPX zp
664 data = READ_LOW( data );
665 case 0xE0: // CPX #imm
666 cpx_data:
667 nz = x - data;
668 pc++;
669 c = ~nz;
670 nz &= 0xFF;
671 goto loop;
672
673 case 0xCC:{// CPY abs
674 fuint16 addr = GET_ADDR();
675 pc++;
676 FLUSH_TIME();
677 data = READ( addr );
678 CACHE_TIME();
679 goto cpy_data;
680 }
681
682 case 0xC4: // CPY zp
683 data = READ_LOW( data );
684 case 0xC0: // CPY #imm
685 cpy_data:
686 nz = y - data;
687 pc++;
688 c = ~nz;
689 nz &= 0xFF;
690 goto loop;
691
692// Logical
693
694#define ARITH_ADDR_MODES( op )\
695 case op - 0x04: /* (ind,x) */\
696 data = (uint8_t) (data + x);\
697 case op + 0x0D: /* (ind) */\
698 data = 0x100 * READ_LOW( (uint8_t) (data + 1) ) + READ_LOW( data );\
699 goto ptr##op;\
700 case op + 0x0C:{/* (ind),y */\
701 fuint16 temp = READ_LOW( data ) + y;\
702 PAGE_CROSS_PENALTY( temp );\
703 data = temp + 0x100 * READ_LOW( (uint8_t) (data + 1) );\
704 goto ptr##op;\
705 }\
706 case op + 0x10: /* zp,X */\
707 data = (uint8_t) (data + x);\
708 case op + 0x00: /* zp */\
709 data = READ_LOW( data );\
710 goto imm##op;\
711 case op + 0x14: /* abs,Y */\
712 data += y;\
713 goto ind##op;\
714 case op + 0x18: /* abs,X */\
715 data += x;\
716 ind##op:\
717 PAGE_CROSS_PENALTY( data );\
718 case op + 0x08: /* abs */\
719 ADD_PAGE( data );\
720 ptr##op:\
721 FLUSH_TIME();\
722 data = READ( data );\
723 CACHE_TIME();\
724 case op + 0x04: /* imm */\
725 imm##op:
726
727 ARITH_ADDR_MODES( 0xC5 ) // CMP
728 nz = a - data;
729 pc++;
730 c = ~nz;
731 nz &= 0xFF;
732 goto loop;
733
734 ARITH_ADDR_MODES( 0x25 ) // AND
735 nz = (a &= data);
736 pc++;
737 goto loop;
738
739 ARITH_ADDR_MODES( 0x45 ) // EOR
740 nz = (a ^= data);
741 pc++;
742 goto loop;
743
744 ARITH_ADDR_MODES( 0x05 ) // ORA
745 nz = (a |= data);
746 pc++;
747 goto loop;
748
749// Add/subtract
750
751 ARITH_ADDR_MODES( 0xE5 ) // SBC
752 data ^= 0xFF;
753 goto adc_imm;
754
755 ARITH_ADDR_MODES( 0x65 ) // ADC
756 adc_imm: {
757 if ( status & st_d ) {
758 dprintf( "Decimal mode not supported\n" );
759 }
760 fint16 carry = c >> 8 & 1;
761 fint16 ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
762 status &= ~st_v;
763 status |= ov >> 2 & 0x40;
764 c = nz = a + data + carry;
765 pc++;
766 a = (uint8_t) nz;
767 goto loop;
768 }
769
770// Shift/rotate
771
772 case 0x4A: // LSR A
773 c = 0;
774 case 0x6A: // ROR A
775 nz = c >> 1 & 0x80;
776 c = a << 8;
777 nz |= a >> 1;
778 a = nz;
779 goto loop;
780
781 case 0x0A: // ASL A
782 nz = a << 1;
783 c = nz;
784 a = (uint8_t) nz;
785 goto loop;
786
787 case 0x2A: { // ROL A
788 nz = a << 1;
789 fint16 temp = c >> 8 & 1;
790 c = nz;
791 nz |= temp;
792 a = (uint8_t) nz;
793 goto loop;
794 }
795
796 case 0x5E: // LSR abs,X
797 data += x;
798 case 0x4E: // LSR abs
799 c = 0;
800 case 0x6E: // ROR abs
801 ror_abs: {
802 ADD_PAGE( data );
803 FLUSH_TIME();
804 int temp = READ( data );
805 nz = (c >> 1 & 0x80) | (temp >> 1);
806 c = temp << 8;
807 goto rotate_common;
808 }
809
810 case 0x3E: // ROL abs,X
811 data += x;
812 goto rol_abs;
813
814 case 0x1E: // ASL abs,X
815 data += x;
816 case 0x0E: // ASL abs
817 c = 0;
818 case 0x2E: // ROL abs
819 rol_abs:
820 ADD_PAGE( data );
821 nz = c >> 8 & 1;
822 FLUSH_TIME();
823 nz |= (c = READ( data ) << 1);
824 rotate_common:
825 pc++;
826 WRITE( data, (uint8_t) nz );
827 CACHE_TIME();
828 goto loop;
829
830 case 0x7E: // ROR abs,X
831 data += x;
832 goto ror_abs;
833
834 case 0x76: // ROR zp,x
835 data = (uint8_t) (data + x);
836 goto ror_zp;
837
838 case 0x56: // LSR zp,x
839 data = (uint8_t) (data + x);
840 case 0x46: // LSR zp
841 c = 0;
842 case 0x66: // ROR zp
843 ror_zp: {
844 int temp = READ_LOW( data );
845 nz = (c >> 1 & 0x80) | (temp >> 1);
846 c = temp << 8;
847 goto write_nz_zp;
848 }
849
850 case 0x36: // ROL zp,x
851 data = (uint8_t) (data + x);
852 goto rol_zp;
853
854 case 0x16: // ASL zp,x
855 data = (uint8_t) (data + x);
856 case 0x06: // ASL zp
857 c = 0;
858 case 0x26: // ROL zp
859 rol_zp:
860 nz = c >> 8 & 1;
861 nz |= (c = READ_LOW( data ) << 1);
862 goto write_nz_zp;
863
864// Increment/decrement
865
866#define INC_DEC_AXY( reg, n ) reg = (uint8_t) (nz = reg + n); goto loop;
867
868 case 0x1A: // INA
869 INC_DEC_AXY( a, +1 )
870
871 case 0xE8: // INX
872 INC_DEC_AXY( x, +1 )
873
874 case 0xC8: // INY
875 INC_DEC_AXY( y, +1 )
876
877 case 0x3A: // DEA
878 INC_DEC_AXY( a, -1 )
879
880 case 0xCA: // DEX
881 INC_DEC_AXY( x, -1 )
882
883 case 0x88: // DEY
884 INC_DEC_AXY( y, -1 )
885
886 case 0xF6: // INC zp,x
887 data = (uint8_t) (data + x);
888 case 0xE6: // INC zp
889 nz = 1;
890 goto add_nz_zp;
891
892 case 0xD6: // DEC zp,x
893 data = (uint8_t) (data + x);
894 case 0xC6: // DEC zp
895 nz = (unsigned) -1;
896 add_nz_zp:
897 nz += READ_LOW( data );
898 write_nz_zp:
899 pc++;
900 WRITE_LOW( data, nz );
901 goto loop;
902
903 case 0xFE: // INC abs,x
904 data = x + GET_ADDR();
905 goto inc_ptr;
906
907 case 0xEE: // INC abs
908 data = GET_ADDR();
909 inc_ptr:
910 nz = 1;
911 goto inc_common;
912
913 case 0xDE: // DEC abs,x
914 data = x + GET_ADDR();
915 goto dec_ptr;
916
917 case 0xCE: // DEC abs
918 data = GET_ADDR();
919 dec_ptr:
920 nz = (unsigned) -1;
921 inc_common:
922 FLUSH_TIME();
923 nz += READ( data );
924 pc += 2;
925 WRITE( data, (uint8_t) nz );
926 CACHE_TIME();
927 goto loop;
928
929// Transfer
930
931 case 0xA8: // TAY
932 y = a;
933 nz = a;
934 goto loop;
935
936 case 0x98: // TYA
937 a = y;
938 nz = y;
939 goto loop;
940
941 case 0xAA: // TAX
942 x = a;
943 nz = a;
944 goto loop;
945
946 case 0x8A: // TXA
947 a = x;
948 nz = x;
949 goto loop;
950
951 case 0x9A: // TXS
952 SET_SP( x ); // verified (no flag change)
953 goto loop;
954
955 case 0xBA: // TSX
956 x = nz = GET_SP();
957 goto loop;
958
959 #define SWAP_REGS( r1, r2 ) {\
960 fuint8 t = r1;\
961 r1 = r2;\
962 r2 = t;\
963 goto loop;\
964 }
965
966 case 0x02: // SXY
967 SWAP_REGS( x, y );
968
969 case 0x22: // SAX
970 SWAP_REGS( a, x );
971
972 case 0x42: // SAY
973 SWAP_REGS( a, y );
974
975 case 0x62: // CLA
976 a = 0;
977 goto loop;
978
979 case 0x82: // CLX
980 x = 0;
981 goto loop;
982
983 case 0xC2: // CLY
984 y = 0;
985 goto loop;
986
987// Stack
988
989 case 0x48: // PHA
990 PUSH( a );
991 goto loop;
992
993 case 0xDA: // PHX
994 PUSH( x );
995 goto loop;
996
997 case 0x5A: // PHY
998 PUSH( y );
999 goto loop;
1000
1001 case 0x40:{// RTI
1002 fuint8 temp = READ_LOW( sp );
1003 pc = READ_LOW( 0x100 | (sp - 0xFF) );
1004 pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
1005 sp = (sp - 0xFD) | 0x100;
1006 data = status;
1007 SET_STATUS( temp );
1008 r->status = status; // update externally-visible I flag
1009 if ( (data ^ status) & st_i )
1010 {
1011 hes_time_t new_time = cpu->end_time;
1012 if ( !(status & st_i) && new_time > cpu->irq_time )
1013 new_time = cpu->irq_time;
1014 blargg_long delta = s.base - new_time;
1015 s.base = new_time;
1016 s_time += delta;
1017 }
1018 goto loop;
1019 }
1020
1021 #define POP() READ_LOW( sp ); sp = (sp - 0xFF) | 0x100
1022
1023 case 0x68: // PLA
1024 a = nz = POP();
1025 goto loop;
1026
1027 case 0xFA: // PLX
1028 x = nz = POP();
1029 goto loop;
1030
1031 case 0x7A: // PLY
1032 y = nz = POP();
1033 goto loop;
1034
1035 case 0x28:{// PLP
1036 fuint8 temp = POP();
1037 fuint8 changed = status ^ temp;
1038 SET_STATUS( temp );
1039 if ( !(changed & st_i) )
1040 goto loop; // I flag didn't change
1041 if ( status & st_i )
1042 goto handle_sei;
1043 goto handle_cli;
1044 }
1045 #undef POP
1046
1047 case 0x08: { // PHP
1048 fuint8 temp;
1049 CALC_STATUS( temp );
1050 PUSH( temp | st_b );
1051 goto loop;
1052 }
1053
1054// Flags
1055
1056 case 0x38: // SEC
1057 c = (unsigned) ~0;
1058 goto loop;
1059
1060 case 0x18: // CLC
1061 c = 0;
1062 goto loop;
1063
1064 case 0xB8: // CLV
1065 status &= ~st_v;
1066 goto loop;
1067
1068 case 0xD8: // CLD
1069 status &= ~st_d;
1070 goto loop;
1071
1072 case 0xF8: // SED
1073 status |= st_d;
1074 goto loop;
1075
1076 case 0x58: // CLI
1077 if ( !(status & st_i) )
1078 goto loop;
1079 status &= ~st_i;
1080 handle_cli: {
1081 r->status = status; // update externally-visible I flag
1082 blargg_long delta = s.base - cpu->irq_time;
1083 if ( delta <= 0 )
1084 {
1085 if ( TIME < cpu->irq_time )
1086 goto loop;
1087 goto delayed_cli;
1088 }
1089 s.base = cpu->irq_time;
1090 s_time += delta;
1091 if ( s_time < 0 )
1092 goto loop;
1093
1094 if ( delta >= s_time + 1 )
1095 {
1096 // delayed irq until after next instruction
1097 s.base += s_time + 1;
1098 s_time = -1;
1099 cpu->irq_time = s.base; // TODO: remove, as only to satisfy debug check in loop
1100 goto loop;
1101 }
1102 delayed_cli:
1103 dprintf( "Delayed CLI not supported\n" ); // TODO: implement
1104 goto loop;
1105 }
1106
1107 case 0x78: // SEI
1108 if ( status & st_i )
1109 goto loop;
1110 status |= st_i;
1111 handle_sei: {
1112 r->status = status; // update externally-visible I flag
1113 blargg_long delta = s.base - cpu->end_time;
1114 s.base = cpu->end_time;
1115 s_time += delta;
1116 if ( s_time < 0 )
1117 goto loop;
1118 dprintf( "Delayed SEI not supported\n" ); // TODO: implement
1119 goto loop;
1120 }
1121
1122// Special
1123
1124 case 0x53:{// TAM
1125 fuint8 const bits = data; // avoid using data across function call
1126 pc++;
1127 int i;
1128 for ( i = 0; i < 8; i++ )
1129 if ( bits & (1 << i) )
1130 /* this->cpu.set_mmr( i, a ); */
1131 Cpu_set_mmr( this, i, a );
1132 goto loop;
1133 }
1134
1135 case 0x43:{// TMA
1136 pc++;
1137 byte const* in = cpu->mmr;
1138 do
1139 {
1140 if ( data & 1 )
1141 a = *in;
1142 in++;
1143 }
1144 while ( (data >>= 1) != 0 );
1145 goto loop;
1146 }
1147
1148 case 0x03: // ST0
1149 case 0x13: // ST1
1150 case 0x23:{// ST2
1151 fuint16 addr = opcode >> 4;
1152 if ( addr )
1153 addr++;
1154 pc++;
1155 FLUSH_TIME();
1156 CPU_WRITE_VDP( this, addr, data, TIME );
1157 CACHE_TIME();
1158 goto loop;
1159 }
1160
1161 case 0xEA: // NOP
1162 goto loop;
1163
1164 case 0x54: // CSL
1165 dprintf( "CSL not supported\n" );
1166 illegal_encountered = true;
1167 goto loop;
1168
1169 case 0xD4: // CSH
1170 goto loop;
1171
1172 case 0xF4: { // SET
1173 //fuint16 operand = GET_MSB();
1174 dprintf( "SET not handled\n" );
1175 //switch ( data )
1176 //{
1177 //}
1178 illegal_encountered = true;
1179 goto loop;
1180 }
1181
1182// Block transfer
1183
1184 {
1185 fuint16 in_alt;
1186 fint16 in_inc;
1187 fuint16 out_alt;
1188 fint16 out_inc;
1189
1190 case 0xE3: // TIA
1191 in_alt = 0;
1192 goto bxfer_alt;
1193
1194 case 0xF3: // TAI
1195 in_alt = 1;
1196 bxfer_alt:
1197 in_inc = in_alt ^ 1;
1198 out_alt = in_inc;
1199 out_inc = in_alt;
1200 goto bxfer;
1201
1202 case 0xD3: // TIN
1203 in_inc = 1;
1204 out_inc = 0;
1205 goto bxfer_no_alt;
1206
1207 case 0xC3: // TDD
1208 in_inc = -1;
1209 out_inc = -1;
1210 goto bxfer_no_alt;
1211
1212 case 0x73: // TII
1213 in_inc = 1;
1214 out_inc = 1;
1215 bxfer_no_alt:
1216 in_alt = 0;
1217 out_alt = 0;
1218 bxfer: {
1219 fuint16 in = GET_LE16( instr + 0 );
1220 fuint16 out = GET_LE16( instr + 2 );
1221 int count = GET_LE16( instr + 4 );
1222 if ( !count )
1223 count = 0x10000;
1224 pc += 6;
1225 WRITE_LOW( 0x100 | (sp - 1), y );
1226 WRITE_LOW( 0x100 | (sp - 2), a );
1227 WRITE_LOW( 0x100 | (sp - 3), x );
1228 FLUSH_TIME();
1229 do
1230 {
1231 // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O
1232 fuint8 t = READ( in );
1233 in += in_inc;
1234 in &= 0xFFFF;
1235 s.time += 6;
1236 if ( in_alt )
1237 in_inc = -in_inc;
1238 WRITE( out, t );
1239 out += out_inc;
1240 out &= 0xFFFF;
1241 if ( out_alt )
1242 out_inc = -out_inc;
1243 }
1244 while ( --count );
1245 CACHE_TIME();
1246 goto loop;
1247 }
1248 }
1249
1250// Illegal
1251
1252 default:
1253 assert( (unsigned) opcode <= 0xFF );
1254 dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 );
1255 illegal_encountered = true;
1256 goto loop;
1257 }
1258 assert( false );
1259
1260 int result_;
1261handle_brk:
1262 pc++;
1263 result_ = 6;
1264
1265interrupt:
1266 {
1267 s_time += 7;
1268
1269 WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
1270 WRITE_LOW( 0x100 | (sp - 2), pc );
1271 pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ );
1272
1273 sp = (sp - 3) | 0x100;
1274 fuint8 temp;
1275 CALC_STATUS( temp );
1276 if ( result_ == 6 )
1277 temp |= st_b;
1278 WRITE_LOW( sp, temp );
1279
1280 status &= ~st_d;
1281 status |= st_i;
1282 r->status = status; // update externally-visible I flag
1283
1284 blargg_long delta = s.base - cpu->end_time;
1285 s.base = cpu->end_time;
1286 s_time += delta;
1287 goto loop;
1288 }
1289
1290idle_done:
1291 s_time = 0;
1292out_of_time:
1293 pc--;
1294 FLUSH_TIME();
1295 CPU_DONE( this, TIME, result_ );
1296 CACHE_TIME();
1297 if ( result_ > 0 )
1298 goto interrupt;
1299 if ( s_time < 0 )
1300 goto loop;
1301
1302 s.time = s_time;
1303
1304 r->pc = pc;
1305 r->sp = GET_SP();
1306 r->a = a;
1307 r->x = x;
1308 r->y = y;
1309
1310 {
1311 fuint8 temp;
1312 CALC_STATUS( temp );
1313 r->status = temp;
1314 }
1315
1316 cpu->state_ = s;
1317 cpu->state = &cpu->state_;
1318
1319 return illegal_encountered;
1320}
1321