comparison src/console/Hes_Cpu.cxx @ 316:fb513e10174e trunk

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