Fix yellow and red. Disable NSF, VGM, SGC and KSS formats for low memory targets...
[kugel-rb.git] / apps / codecs / libgme / hes_cpu.c
blob60ea09990504079aebffe62adb728892b3509002
1 // Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
3 #include "hes_cpu.h"
5 #include "blargg_endian.h"
7 //#include "hes_cpu_log.h"
9 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you
10 can redistribute it and/or modify it under the terms of the GNU Lesser
11 General Public License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version. This
13 module is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16 details. You should have received a copy of the GNU Lesser General Public
17 License along with this module; if not, write to the Free Software Foundation,
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
20 // TODO: support T flag, including clearing it at appropriate times?
22 // all zero-page should really use whatever is at page 1, but that would
23 // reduce efficiency quite a bit
24 int const ram_addr = 0x2000;
26 #define FLUSH_TIME() (void) (s.time = s_time)
27 #define CACHE_TIME() (void) (s_time = s.time)
29 #include "hes_cpu_io.h"
31 #include "blargg_source.h"
33 #ifdef BLARGG_NONPORTABLE
34 #define PAGE_OFFSET( addr ) (addr)
35 #else
36 #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
37 #endif
39 // status flags
40 int const st_n = 0x80;
41 int const st_v = 0x40;
42 int const st_t = 0x20;
43 int const st_b = 0x10;
44 int const st_d = 0x08;
45 int const st_i = 0x04;
46 int const st_z = 0x02;
47 int const st_c = 0x01;
49 void Cpu_init( struct Hes_Cpu* this )
51 this->state = &this->state_;
54 void Cpu_reset( struct Hes_Cpu* this )
56 check( this->state == &state_ );
57 this->state = &this->state_;
59 this->state_.time = 0;
60 this->state_.base = 0;
61 this->irq_time = (hes_time_t)future_hes_time;
62 this->end_time = (hes_time_t)future_hes_time;
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;
71 blargg_verify_byte_order();
74 void Cpu_set_mmr( struct Hes_Emu* this, int reg, int bank )
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 );
83 #define TIME (s_time + s.base)
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 )])
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 ))
95 // even on x86, using short and unsigned char was slower
96 typedef int fint16;
97 typedef unsigned fuint16;
98 typedef unsigned fuint8;
99 typedef blargg_long fint32;
101 bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time )
103 bool illegal_encountered = false;
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 );
109 struct state_t s = cpu->state_;
110 cpu->state = &s;
112 // even on x86, using s.time in place of s_time was slower
113 fint16 s_time = s.time;
115 struct registers_t* r = &cpu->r;
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 );
125 #define IS_NEG (nz & 0x8080)
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 )
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 )
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
145 fuint8 temp = r->status;
146 SET_STATUS( temp );
149 goto loop;
150 branch_not_taken:
151 s_time -= 2;
152 loop:
154 #ifndef NDEBUG
156 hes_time_t correct = end_time_;
157 if ( !(status & st_i) && correct > irq_time_ )
158 correct = irq_time_;
159 check( s.base == correct );
161 static long count;
162 if ( count == 1844 ) Debugger();
163 if ( s.base != correct ) dprintf( "%ld\n", count );
164 count++;
167 #endif
169 check( (unsigned) GET_SP() < 0x100 );
170 check( (unsigned) a < 0x100 );
171 check( (unsigned) x < 0x100 );
173 uint8_t const* instr = s.code_map [pc >> page_shift];
174 fuint8 opcode;
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
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
208 fuint16 data;
209 data = clock_table [opcode];
210 if ( (s_time += data) >= 0 )
211 goto possibly_out_of_time;
212 almost_out_of_time:
214 data = *instr;
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
222 switch ( opcode )
224 possibly_out_of_time:
225 if ( s_time < (int) data )
226 goto almost_out_of_time;
227 s_time -= data;
228 goto out_of_time;
230 // Macros
232 #define GET_MSB() (instr [1])
233 #define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB());
234 #define GET_ADDR() GET_LE16( instr )
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 )
240 // Branch
242 // TODO: more efficient way to handle negative branch that wraps PC around
243 #define BRANCH( cond )\
245 fint16 offset = (int8_t) data;\
246 pc++;\
247 if ( !(cond) ) goto branch_not_taken;\
248 pc = (uint16_t) (pc + offset);\
249 goto loop;\
252 case 0xF0: // BEQ
253 BRANCH( !((uint8_t) nz) );
255 case 0xD0: // BNE
256 BRANCH( (uint8_t) nz );
258 case 0x10: // BPL
259 BRANCH( !IS_NEG );
261 case 0x90: // BCC
262 BRANCH( !(c & 0x100) )
264 case 0x30: // BMI
265 BRANCH( IS_NEG )
267 case 0x50: // BVC
268 BRANCH( !(status & st_v) )
270 case 0x70: // BVS
271 BRANCH( status & st_v )
273 case 0xB0: // BCS
274 BRANCH( c & 0x100 )
276 case 0x80: // BRA
277 branch_taken:
278 BRANCH( true );
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)) )
305 case 0x4C: // JMP abs
306 pc = GET_ADDR();
307 goto loop;
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;
317 // Subroutine
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;
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;
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;
340 case 0x00: // BRK
341 goto handle_brk;
343 // Common
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;
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;
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;
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;
376 case 0xA5: // LDA zp
377 a = nz = READ_LOW( data );
378 pc++;
379 goto loop;
381 // Load/store
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;
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;
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;
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;
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;
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;
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;
448 case 0xB5: // LDA zp,x
449 a = nz = READ_LOW( (uint8_t) (data + x) );
450 pc++;
451 goto loop;
453 case 0xA9: // LDA #imm
454 pc++;
455 a = data;
456 nz = data;
457 goto loop;
459 // Bit operations
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;
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;
487 fuint16 addr;
489 case 0xB3: // TST abs,x
490 addr = GET_MSB() + x;
491 goto tst_abs;
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;
504 case 0xA3: // TST zp,x
505 nz = READ_LOW( (uint8_t) (GET_MSB() + x) );
506 goto tst_common;
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;
520 fuint16 addr;
521 case 0x0C: // TSB abs
522 case 0x1C: // TRB abs
523 addr = GET_ADDR();
524 pc++;
525 goto txb_addr;
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;
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;
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;
568 // Load/store
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;
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;
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;
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;
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;
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;
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;
634 fuint8 temp;
635 case 0x8C: // STY abs
636 temp = y;
637 goto store_abs;
639 case 0x8E: // STX abs
640 temp = x;
641 store_abs:
643 fuint16 addr = GET_ADDR();
644 pc += 2;
645 FLUSH_TIME();
646 WRITE( addr, temp );
647 CACHE_TIME();
648 goto loop;
652 // Compare
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;
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;
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;
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;
692 // Logical
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;\
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:
727 ARITH_ADDR_MODES( 0xC5 ) // CMP
728 nz = a - data;
729 pc++;
730 c = ~nz;
731 nz &= 0xFF;
732 goto loop;
734 ARITH_ADDR_MODES( 0x25 ) // AND
735 nz = (a &= data);
736 pc++;
737 goto loop;
739 ARITH_ADDR_MODES( 0x45 ) // EOR
740 nz = (a ^= data);
741 pc++;
742 goto loop;
744 ARITH_ADDR_MODES( 0x05 ) // ORA
745 nz = (a |= data);
746 pc++;
747 goto loop;
749 // Add/subtract
751 ARITH_ADDR_MODES( 0xE5 ) // SBC
752 data ^= 0xFF;
753 goto adc_imm;
755 ARITH_ADDR_MODES( 0x65 ) // ADC
756 adc_imm: {
757 if ( status & st_d ) {
758 dprintf( "Decimal mode not supported\n" );
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;
770 // Shift/rotate
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;
781 case 0x0A: // ASL A
782 nz = a << 1;
783 c = nz;
784 a = (uint8_t) nz;
785 goto loop;
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;
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;
810 case 0x3E: // ROL abs,X
811 data += x;
812 goto rol_abs;
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;
830 case 0x7E: // ROR abs,X
831 data += x;
832 goto ror_abs;
834 case 0x76: // ROR zp,x
835 data = (uint8_t) (data + x);
836 goto ror_zp;
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;
850 case 0x36: // ROL zp,x
851 data = (uint8_t) (data + x);
852 goto rol_zp;
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;
864 // Increment/decrement
866 #define INC_DEC_AXY( reg, n ) reg = (uint8_t) (nz = reg + n); goto loop;
868 case 0x1A: // INA
869 INC_DEC_AXY( a, +1 )
871 case 0xE8: // INX
872 INC_DEC_AXY( x, +1 )
874 case 0xC8: // INY
875 INC_DEC_AXY( y, +1 )
877 case 0x3A: // DEA
878 INC_DEC_AXY( a, -1 )
880 case 0xCA: // DEX
881 INC_DEC_AXY( x, -1 )
883 case 0x88: // DEY
884 INC_DEC_AXY( y, -1 )
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;
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;
903 case 0xFE: // INC abs,x
904 data = x + GET_ADDR();
905 goto inc_ptr;
907 case 0xEE: // INC abs
908 data = GET_ADDR();
909 inc_ptr:
910 nz = 1;
911 goto inc_common;
913 case 0xDE: // DEC abs,x
914 data = x + GET_ADDR();
915 goto dec_ptr;
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;
929 // Transfer
931 case 0xA8: // TAY
932 y = a;
933 nz = a;
934 goto loop;
936 case 0x98: // TYA
937 a = y;
938 nz = y;
939 goto loop;
941 case 0xAA: // TAX
942 x = a;
943 nz = a;
944 goto loop;
946 case 0x8A: // TXA
947 a = x;
948 nz = x;
949 goto loop;
951 case 0x9A: // TXS
952 SET_SP( x ); // verified (no flag change)
953 goto loop;
955 case 0xBA: // TSX
956 x = nz = GET_SP();
957 goto loop;
959 #define SWAP_REGS( r1, r2 ) {\
960 fuint8 t = r1;\
961 r1 = r2;\
962 r2 = t;\
963 goto loop;\
966 case 0x02: // SXY
967 SWAP_REGS( x, y );
969 case 0x22: // SAX
970 SWAP_REGS( a, x );
972 case 0x42: // SAY
973 SWAP_REGS( a, y );
975 case 0x62: // CLA
976 a = 0;
977 goto loop;
979 case 0x82: // CLX
980 x = 0;
981 goto loop;
983 case 0xC2: // CLY
984 y = 0;
985 goto loop;
987 // Stack
989 case 0x48: // PHA
990 PUSH( a );
991 goto loop;
993 case 0xDA: // PHX
994 PUSH( x );
995 goto loop;
997 case 0x5A: // PHY
998 PUSH( y );
999 goto loop;
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 )
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;
1018 goto loop;
1021 #define POP() READ_LOW( sp ); sp = (sp - 0xFF) | 0x100
1023 case 0x68: // PLA
1024 a = nz = POP();
1025 goto loop;
1027 case 0xFA: // PLX
1028 x = nz = POP();
1029 goto loop;
1031 case 0x7A: // PLY
1032 y = nz = POP();
1033 goto loop;
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;
1045 #undef POP
1047 case 0x08: { // PHP
1048 fuint8 temp;
1049 CALC_STATUS( temp );
1050 PUSH( temp | st_b );
1051 goto loop;
1054 // Flags
1056 case 0x38: // SEC
1057 c = (unsigned) ~0;
1058 goto loop;
1060 case 0x18: // CLC
1061 c = 0;
1062 goto loop;
1064 case 0xB8: // CLV
1065 status &= ~st_v;
1066 goto loop;
1068 case 0xD8: // CLD
1069 status &= ~st_d;
1070 goto loop;
1072 case 0xF8: // SED
1073 status |= st_d;
1074 goto loop;
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 )
1085 if ( TIME < cpu->irq_time )
1086 goto loop;
1087 goto delayed_cli;
1089 s.base = cpu->irq_time;
1090 s_time += delta;
1091 if ( s_time < 0 )
1092 goto loop;
1094 if ( delta >= s_time + 1 )
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;
1102 delayed_cli:
1103 dprintf( "Delayed CLI not supported\n" ); // TODO: implement
1104 goto loop;
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;
1122 // Special
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;
1135 case 0x43:{// TMA
1136 pc++;
1137 byte const* in = cpu->mmr;
1140 if ( data & 1 )
1141 a = *in;
1142 in++;
1144 while ( (data >>= 1) != 0 );
1145 goto loop;
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;
1161 case 0xEA: // NOP
1162 goto loop;
1164 case 0x54: // CSL
1165 dprintf( "CSL not supported\n" );
1166 illegal_encountered = true;
1167 goto loop;
1169 case 0xD4: // CSH
1170 goto loop;
1172 case 0xF4: { // SET
1173 //fuint16 operand = GET_MSB();
1174 dprintf( "SET not handled\n" );
1175 //switch ( data )
1178 illegal_encountered = true;
1179 goto loop;
1182 // Block transfer
1185 fuint16 in_alt;
1186 fint16 in_inc;
1187 fuint16 out_alt;
1188 fint16 out_inc;
1190 case 0xE3: // TIA
1191 in_alt = 0;
1192 goto bxfer_alt;
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;
1202 case 0xD3: // TIN
1203 in_inc = 1;
1204 out_inc = 0;
1205 goto bxfer_no_alt;
1207 case 0xC3: // TDD
1208 in_inc = -1;
1209 out_inc = -1;
1210 goto bxfer_no_alt;
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();
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;
1244 while ( --count );
1245 CACHE_TIME();
1246 goto loop;
1250 // Illegal
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;
1258 assert( false );
1260 int result_;
1261 handle_brk:
1262 pc++;
1263 result_ = 6;
1265 interrupt:
1267 s_time += 7;
1269 WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
1270 WRITE_LOW( 0x100 | (sp - 2), pc );
1271 pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ );
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 );
1280 status &= ~st_d;
1281 status |= st_i;
1282 r->status = status; // update externally-visible I flag
1284 blargg_long delta = s.base - cpu->end_time;
1285 s.base = cpu->end_time;
1286 s_time += delta;
1287 goto loop;
1290 idle_done:
1291 s_time = 0;
1292 out_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;
1302 s.time = s_time;
1304 r->pc = pc;
1305 r->sp = GET_SP();
1306 r->a = a;
1307 r->x = x;
1308 r->y = y;
1311 fuint8 temp;
1312 CALC_STATUS( temp );
1313 r->status = temp;
1316 cpu->state_ = s;
1317 cpu->state = &cpu->state_;
1319 return illegal_encountered;