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