1 // Game_Music_Emu 0.6.0. http://www.slack.net/~ant/
2
3 #include "Sap_Cpu.h"
4
5 #include <limits.h>
6 #include "blargg_endian.h"
7
8 //#include "nes_cpu_log.h"
9
10 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you
11 can redistribute it and/or modify it under the terms of the GNU Lesser
12 General Public License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version. This
14 module is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17 details. You should have received a copy of the GNU Lesser General Public
18 License along with this module; if not, write to the Free Software Foundation,
19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
20
21 #define FLUSH_TIME() (void) (s.time = s_time)
22 #define CACHE_TIME() (void) (s_time = s.time)
23
24 #include "sap_cpu_io.h"
25
26 #ifndef CPU_DONE
27 #define CPU_DONE( cpu, time, result_out ) { result_out = -1; }
28 #endif
29
30 #include "blargg_source.h"
31
32 int const st_n = 0x80;
33 int const st_v = 0x40;
34 int const st_r = 0x20;
35 int const st_b = 0x10;
36 int const st_d = 0x08;
37 int const st_i = 0x04;
38 int const st_z = 0x02;
39 int const st_c = 0x01;
40
reset(void * new_mem)41 void Sap_Cpu::reset( void* new_mem )
42 {
43 check( state == &state_ );
44 state = &state_;
45 mem = (uint8_t*) new_mem;
46 r.status = st_i;
47 r.sp = 0xFF;
48 r.pc = 0;
49 r.a = 0;
50 r.x = 0;
51 r.y = 0;
52 state_.time = 0;
53 state_.base = 0;
54 irq_time_ = future_sap_time;
55 end_time_ = future_sap_time;
56
57 blargg_verify_byte_order();
58 }
59
60 #define TIME (s_time + s.base)
61 #define READ( addr ) CPU_READ( this, (addr), TIME )
62 #define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );}
63 #define READ_LOW( addr ) (mem [int (addr)])
64 #define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
65 #define READ_PROG( addr ) (READ_LOW( addr ))
66
67 #define SET_SP( v ) (sp = ((v) + 1) | 0x100)
68 #define GET_SP() ((sp - 1) & 0xFF)
69 #define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
70
71 // even on x86, using short and unsigned char was slower
72 typedef int fint16;
73 typedef unsigned fuint16;
74 typedef unsigned fuint8;
75 typedef blargg_long fint32;
76
run(sap_time_t end_time)77 bool Sap_Cpu::run( sap_time_t end_time )
78 {
79 bool illegal_encountered = false;
80 set_end_time( end_time );
81 state_t s = this->state_;
82 this->state = &s;
83 fint32 s_time = s.time;
84 uint8_t* const mem = this->mem; // cache
85
86 // registers
87 fuint16 pc = r.pc;
88 fuint8 a = r.a;
89 fuint8 x = r.x;
90 fuint8 y = r.y;
91 fuint16 sp;
92 SET_SP( r.sp );
93
94 // status flags
95 #define IS_NEG (nz & 0x8080)
96
97 #define CALC_STATUS( out ) do {\
98 out = status & (st_v | st_d | st_i);\
99 out |= ((nz >> 8) | nz) & st_n;\
100 out |= c >> 8 & st_c;\
101 if ( !(nz & 0xFF) ) out |= st_z;\
102 } while ( 0 )
103
104 #define SET_STATUS( in ) do {\
105 status = in & (st_v | st_d | st_i);\
106 nz = in << 8;\
107 c = nz;\
108 nz |= ~in & st_z;\
109 } while ( 0 )
110
111 fuint8 status;
112 fuint16 c; // carry set if (c & 0x100) != 0
113 fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
114 {
115 fuint8 temp = r.status;
116 SET_STATUS( temp );
117 }
118
119 goto loop;
120 dec_clock_loop:
121 s_time--;
122 loop:
123
124 #ifndef NDEBUG
125 {
126 sap_time_t correct = end_time_;
127 if ( !(status & st_i) && correct > irq_time_ )
128 correct = irq_time_;
129 check( s.base == correct );
130 }
131 #endif
132
133 check( (unsigned) GET_SP() < 0x100 );
134 check( (unsigned) a < 0x100 );
135 check( (unsigned) x < 0x100 );
136 check( (unsigned) y < 0x100 );
137
138 fuint8 opcode = mem [pc];
139 pc++;
140 uint8_t const* instr = mem + pc;
141
142 static uint8_t const clock_table [256] =
143 {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
144 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
145 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
146 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
147 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
148 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
149 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
150 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
151 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
152 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
153 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
154 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
155 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
156 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
157 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
158 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
159 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
160 }; // 0x00 was 7
161
162 fuint16 data;
163 data = clock_table [opcode];
164 if ( (s_time += data) >= 0 )
165 goto possibly_out_of_time;
166 almost_out_of_time:
167
168 data = *instr;
169
170 #ifdef NES_CPU_LOG_H
171 nes_cpu_log( "cpu_log", pc - 1, opcode, instr [0], instr [1] );
172 #endif
173
174 switch ( opcode )
175 {
176 possibly_out_of_time:
177 if ( s_time < (int) data )
178 goto almost_out_of_time;
179 s_time -= data;
180 goto out_of_time;
181
182 // Macros
183
184 #define GET_MSB() (instr [1])
185 #define ADD_PAGE() (pc++, data += 0x100 * GET_MSB())
186 #define GET_ADDR() GET_LE16( instr )
187
188 #define NO_PAGE_CROSSING( lsb )
189 #define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8;
190
191 #define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
192
193 #define IND_Y( cross, out ) {\
194 fuint16 temp = READ_LOW( data ) + y;\
195 out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
196 cross( temp );\
197 }
198
199 #define IND_X( out ) {\
200 fuint16 temp = data + x;\
201 out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\
202 }
203
204 #define ARITH_ADDR_MODES( op )\
205 case op - 0x04: /* (ind,x) */\
206 IND_X( data )\
207 goto ptr##op;\
208 case op + 0x0C: /* (ind),y */\
209 IND_Y( HANDLE_PAGE_CROSSING, data )\
210 goto ptr##op;\
211 case op + 0x10: /* zp,X */\
212 data = uint8_t (data + x);\
213 case op + 0x00: /* zp */\
214 data = READ_LOW( data );\
215 goto imm##op;\
216 case op + 0x14: /* abs,Y */\
217 data += y;\
218 goto ind##op;\
219 case op + 0x18: /* abs,X */\
220 data += x;\
221 ind##op:\
222 HANDLE_PAGE_CROSSING( data );\
223 case op + 0x08: /* abs */\
224 ADD_PAGE();\
225 ptr##op:\
226 FLUSH_TIME();\
227 data = READ( data );\
228 CACHE_TIME();\
229 case op + 0x04: /* imm */\
230 imm##op:
231
232 // TODO: more efficient way to handle negative branch that wraps PC around
233 #define BRANCH( cond )\
234 {\
235 fint16 offset = (BOOST::int8_t) data;\
236 fuint16 extra_clock = (++pc & 0xFF) + offset;\
237 if ( !(cond) ) goto dec_clock_loop;\
238 pc += offset;\
239 s_time += extra_clock >> 8 & 1;\
240 goto loop;\
241 }
242
243 // Often-Used
244
245 case 0xB5: // LDA zp,x
246 a = nz = READ_LOW( uint8_t (data + x) );
247 pc++;
248 goto loop;
249
250 case 0xA5: // LDA zp
251 a = nz = READ_LOW( data );
252 pc++;
253 goto loop;
254
255 case 0xD0: // BNE
256 BRANCH( (uint8_t) nz );
257
258 case 0x20: { // JSR
259 fuint16 temp = pc + 1;
260 pc = GET_ADDR();
261 WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
262 sp = (sp - 2) | 0x100;
263 WRITE_LOW( sp, temp );
264 goto loop;
265 }
266
267 case 0x4C: // JMP abs
268 pc = GET_ADDR();
269 goto loop;
270
271 case 0xE8: // INX
272 INC_DEC_XY( x, 1 )
273
274 case 0x10: // BPL
275 BRANCH( !IS_NEG )
276
277 ARITH_ADDR_MODES( 0xC5 ) // CMP
278 nz = a - data;
279 pc++;
280 c = ~nz;
281 nz &= 0xFF;
282 goto loop;
283
284 case 0x30: // BMI
285 BRANCH( IS_NEG )
286
287 case 0xF0: // BEQ
288 BRANCH( !(uint8_t) nz );
289
290 case 0x95: // STA zp,x
291 data = uint8_t (data + x);
292 case 0x85: // STA zp
293 pc++;
294 WRITE_LOW( data, a );
295 goto loop;
296
297 case 0xC8: // INY
298 INC_DEC_XY( y, 1 )
299
300 case 0xA8: // TAY
301 y = a;
302 nz = a;
303 goto loop;
304
305 case 0x98: // TYA
306 a = y;
307 nz = y;
308 goto loop;
309
310 case 0xAD:{// LDA abs
311 unsigned addr = GET_ADDR();
312 pc += 2;
313 nz = READ( addr );
314 a = nz;
315 goto loop;
316 }
317
318 case 0x60: // RTS
319 pc = 1 + READ_LOW( sp );
320 pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
321 sp = (sp - 0xFE) | 0x100;
322 goto loop;
323
324 {
325 fuint16 addr;
326
327 case 0x99: // STA abs,Y
328 addr = y + GET_ADDR();
329 pc += 2;
330 if ( addr <= 0x7FF )
331 {
332 WRITE_LOW( addr, a );
333 goto loop;
334 }
335 goto sta_ptr;
336
337 case 0x8D: // STA abs
338 addr = GET_ADDR();
339 pc += 2;
340 if ( addr <= 0x7FF )
341 {
342 WRITE_LOW( addr, a );
343 goto loop;
344 }
345 goto sta_ptr;
346
347 case 0x9D: // STA abs,X (slightly more common than STA abs)
348 addr = x + GET_ADDR();
349 pc += 2;
350 if ( addr <= 0x7FF )
351 {
352 WRITE_LOW( addr, a );
353 goto loop;
354 }
355 sta_ptr:
356 FLUSH_TIME();
357 WRITE( addr, a );
358 CACHE_TIME();
359 goto loop;
360
361 case 0x91: // STA (ind),Y
362 IND_Y( NO_PAGE_CROSSING, addr )
363 pc++;
364 goto sta_ptr;
365
366 case 0x81: // STA (ind,X)
367 IND_X( addr )
368 pc++;
369 goto sta_ptr;
370
371 }
372
373 case 0xA9: // LDA #imm
374 pc++;
375 a = data;
376 nz = data;
377 goto loop;
378
379 // common read instructions
380 {
381 fuint16 addr;
382
383 case 0xA1: // LDA (ind,X)
384 IND_X( addr )
385 pc++;
386 goto a_nz_read_addr;
387
388 case 0xB1:// LDA (ind),Y
389 addr = READ_LOW( data ) + y;
390 HANDLE_PAGE_CROSSING( addr );
391 addr += 0x100 * READ_LOW( (uint8_t) (data + 1) );
392 pc++;
393 a = nz = READ_PROG( addr );
394 if ( (addr ^ 0x8000) <= 0x9FFF )
395 goto loop;
396 goto a_nz_read_addr;
397
398 case 0xB9: // LDA abs,Y
399 HANDLE_PAGE_CROSSING( data + y );
400 addr = GET_ADDR() + y;
401 pc += 2;
402 a = nz = READ_PROG( addr );
403 if ( (addr ^ 0x8000) <= 0x9FFF )
404 goto loop;
405 goto a_nz_read_addr;
406
407 case 0xBD: // LDA abs,X
408 HANDLE_PAGE_CROSSING( data + x );
409 addr = GET_ADDR() + x;
410 pc += 2;
411 a = nz = READ_PROG( addr );
412 if ( (addr ^ 0x8000) <= 0x9FFF )
413 goto loop;
414 a_nz_read_addr:
415 FLUSH_TIME();
416 a = nz = READ( addr );
417 CACHE_TIME();
418 goto loop;
419
420 }
421
422 // Branch
423
424 case 0x50: // BVC
425 BRANCH( !(status & st_v) )
426
427 case 0x70: // BVS
428 BRANCH( status & st_v )
429
430 case 0xB0: // BCS
431 BRANCH( c & 0x100 )
432
433 case 0x90: // BCC
434 BRANCH( !(c & 0x100) )
435
436 // Load/store
437
438 case 0x94: // STY zp,x
439 data = uint8_t (data + x);
440 case 0x84: // STY zp
441 pc++;
442 WRITE_LOW( data, y );
443 goto loop;
444
445 case 0x96: // STX zp,y
446 data = uint8_t (data + y);
447 case 0x86: // STX zp
448 pc++;
449 WRITE_LOW( data, x );
450 goto loop;
451
452 case 0xB6: // LDX zp,y
453 data = uint8_t (data + y);
454 case 0xA6: // LDX zp
455 data = READ_LOW( data );
456 case 0xA2: // LDX #imm
457 pc++;
458 x = data;
459 nz = data;
460 goto loop;
461
462 case 0xB4: // LDY zp,x
463 data = uint8_t (data + x);
464 case 0xA4: // LDY zp
465 data = READ_LOW( data );
466 case 0xA0: // LDY #imm
467 pc++;
468 y = data;
469 nz = data;
470 goto loop;
471
472 case 0xBC: // LDY abs,X
473 data += x;
474 HANDLE_PAGE_CROSSING( data );
475 case 0xAC:{// LDY abs
476 unsigned addr = data + 0x100 * GET_MSB();
477 pc += 2;
478 FLUSH_TIME();
479 y = nz = READ( addr );
480 CACHE_TIME();
481 goto loop;
482 }
483
484 case 0xBE: // LDX abs,y
485 data += y;
486 HANDLE_PAGE_CROSSING( data );
487 case 0xAE:{// LDX abs
488 unsigned addr = data + 0x100 * GET_MSB();
489 pc += 2;
490 FLUSH_TIME();
491 x = nz = READ( addr );
492 CACHE_TIME();
493 goto loop;
494 }
495
496 {
497 fuint8 temp;
498 case 0x8C: // STY abs
499 temp = y;
500 goto store_abs;
501
502 case 0x8E: // STX abs
503 temp = x;
504 store_abs:
505 unsigned addr = GET_ADDR();
506 pc += 2;
507 if ( addr <= 0x7FF )
508 {
509 WRITE_LOW( addr, temp );
510 goto loop;
511 }
512 FLUSH_TIME();
513 WRITE( addr, temp );
514 CACHE_TIME();
515 goto loop;
516 }
517
518 // Compare
519
520 case 0xEC:{// CPX abs
521 unsigned addr = GET_ADDR();
522 pc++;
523 FLUSH_TIME();
524 data = READ( addr );
525 CACHE_TIME();
526 goto cpx_data;
527 }
528
529 case 0xE4: // CPX zp
530 data = READ_LOW( data );
531 case 0xE0: // CPX #imm
532 cpx_data:
533 nz = x - data;
534 pc++;
535 c = ~nz;
536 nz &= 0xFF;
537 goto loop;
538
539 case 0xCC:{// CPY abs
540 unsigned addr = GET_ADDR();
541 pc++;
542 FLUSH_TIME();
543 data = READ( addr );
544 CACHE_TIME();
545 goto cpy_data;
546 }
547
548 case 0xC4: // CPY zp
549 data = READ_LOW( data );
550 case 0xC0: // CPY #imm
551 cpy_data:
552 nz = y - data;
553 pc++;
554 c = ~nz;
555 nz &= 0xFF;
556 goto loop;
557
558 // Logical
559
560 ARITH_ADDR_MODES( 0x25 ) // AND
561 nz = (a &= data);
562 pc++;
563 goto loop;
564
565 ARITH_ADDR_MODES( 0x45 ) // EOR
566 nz = (a ^= data);
567 pc++;
568 goto loop;
569
570 ARITH_ADDR_MODES( 0x05 ) // ORA
571 nz = (a |= data);
572 pc++;
573 goto loop;
574
575 case 0x2C:{// BIT abs
576 unsigned addr = GET_ADDR();
577 pc += 2;
578 status &= ~st_v;
579 nz = READ( addr );
580 status |= nz & st_v;
581 if ( a & nz )
582 goto loop;
583 nz <<= 8; // result must be zero, even if N bit is set
584 goto loop;
585 }
586
587 case 0x24: // BIT zp
588 nz = READ_LOW( data );
589 pc++;
590 status &= ~st_v;
591 status |= nz & st_v;
592 if ( a & nz )
593 goto loop;
594 nz <<= 8; // result must be zero, even if N bit is set
595 goto loop;
596
597 // Add/subtract
598
599 ARITH_ADDR_MODES( 0xE5 ) // SBC
600 case 0xEB: // unofficial equivalent
601 data ^= 0xFF;
602 goto adc_imm;
603
604 ARITH_ADDR_MODES( 0x65 ) // ADC
605 adc_imm: {
606 check( !(status & st_d) );
607 fint16 carry = c >> 8 & 1;
608 fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
609 status &= ~st_v;
610 status |= ov >> 2 & 0x40;
611 c = nz = a + data + carry;
612 pc++;
613 a = (uint8_t) nz;
614 goto loop;
615 }
616
617 // Shift/rotate
618
619 case 0x4A: // LSR A
620 c = 0;
621 case 0x6A: // ROR A
622 nz = c >> 1 & 0x80;
623 c = a << 8;
624 nz |= a >> 1;
625 a = nz;
626 goto loop;
627
628 case 0x0A: // ASL A
629 nz = a << 1;
630 c = nz;
631 a = (uint8_t) nz;
632 goto loop;
633
634 case 0x2A: { // ROL A
635 nz = a << 1;
636 fint16 temp = c >> 8 & 1;
637 c = nz;
638 nz |= temp;
639 a = (uint8_t) nz;
640 goto loop;
641 }
642
643 case 0x5E: // LSR abs,X
644 data += x;
645 case 0x4E: // LSR abs
646 c = 0;
647 case 0x6E: // ROR abs
648 ror_abs: {
649 ADD_PAGE();
650 FLUSH_TIME();
651 int temp = READ( data );
652 nz = (c >> 1 & 0x80) | (temp >> 1);
653 c = temp << 8;
654 goto rotate_common;
655 }
656
657 case 0x3E: // ROL abs,X
658 data += x;
659 goto rol_abs;
660
661 case 0x1E: // ASL abs,X
662 data += x;
663 case 0x0E: // ASL abs
664 c = 0;
665 case 0x2E: // ROL abs
666 rol_abs:
667 ADD_PAGE();
668 nz = c >> 8 & 1;
669 FLUSH_TIME();
670 nz |= (c = READ( data ) << 1);
671 rotate_common:
672 pc++;
673 WRITE( data, (uint8_t) nz );
674 CACHE_TIME();
675 goto loop;
676
677 case 0x7E: // ROR abs,X
678 data += x;
679 goto ror_abs;
680
681 case 0x76: // ROR zp,x
682 data = uint8_t (data + x);
683 goto ror_zp;
684
685 case 0x56: // LSR zp,x
686 data = uint8_t (data + x);
687 case 0x46: // LSR zp
688 c = 0;
689 case 0x66: // ROR zp
690 ror_zp: {
691 int temp = READ_LOW( data );
692 nz = (c >> 1 & 0x80) | (temp >> 1);
693 c = temp << 8;
694 goto write_nz_zp;
695 }
696
697 case 0x36: // ROL zp,x
698 data = uint8_t (data + x);
699 goto rol_zp;
700
701 case 0x16: // ASL zp,x
702 data = uint8_t (data + x);
703 case 0x06: // ASL zp
704 c = 0;
705 case 0x26: // ROL zp
706 rol_zp:
707 nz = c >> 8 & 1;
708 nz |= (c = READ_LOW( data ) << 1);
709 goto write_nz_zp;
710
711 // Increment/decrement
712
713 case 0xCA: // DEX
714 INC_DEC_XY( x, -1 )
715
716 case 0x88: // DEY
717 INC_DEC_XY( y, -1 )
718
719 case 0xF6: // INC zp,x
720 data = uint8_t (data + x);
721 case 0xE6: // INC zp
722 nz = 1;
723 goto add_nz_zp;
724
725 case 0xD6: // DEC zp,x
726 data = uint8_t (data + x);
727 case 0xC6: // DEC zp
728 nz = (unsigned) -1;
729 add_nz_zp:
730 nz += READ_LOW( data );
731 write_nz_zp:
732 pc++;
733 WRITE_LOW( data, nz );
734 goto loop;
735
736 case 0xFE: // INC abs,x
737 data = x + GET_ADDR();
738 goto inc_ptr;
739
740 case 0xEE: // INC abs
741 data = GET_ADDR();
742 inc_ptr:
743 nz = 1;
744 goto inc_common;
745
746 case 0xDE: // DEC abs,x
747 data = x + GET_ADDR();
748 goto dec_ptr;
749
750 case 0xCE: // DEC abs
751 data = GET_ADDR();
752 dec_ptr:
753 nz = (unsigned) -1;
754 inc_common:
755 FLUSH_TIME();
756 nz += READ( data );
757 pc += 2;
758 WRITE( data, (uint8_t) nz );
759 CACHE_TIME();
760 goto loop;
761
762 // Transfer
763
764 case 0xAA: // TAX
765 x = a;
766 nz = a;
767 goto loop;
768
769 case 0x8A: // TXA
770 a = x;
771 nz = x;
772 goto loop;
773
774 case 0x9A: // TXS
775 SET_SP( x ); // verified (no flag change)
776 goto loop;
777
778 case 0xBA: // TSX
779 x = nz = GET_SP();
780 goto loop;
781
782 // Stack
783
784 case 0x48: // PHA
785 PUSH( a ); // verified
786 goto loop;
787
788 case 0x68: // PLA
789 a = nz = READ_LOW( sp );
790 sp = (sp - 0xFF) | 0x100;
791 goto loop;
792
793 case 0x40:{// RTI
794 fuint8 temp = READ_LOW( sp );
795 pc = READ_LOW( 0x100 | (sp - 0xFF) );
796 pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
797 sp = (sp - 0xFD) | 0x100;
798 data = status;
799 SET_STATUS( temp );
800 this->r.status = status; // update externally-visible I flag
801 if ( (data ^ status) & st_i )
802 {
803 sap_time_t new_time = end_time_;
804 if ( !(status & st_i) && new_time > irq_time_ )
805 new_time = irq_time_;
806 blargg_long delta = s.base - new_time;
807 s.base = new_time;
808 s_time += delta;
809 }
810 goto loop;
811 }
812
813 case 0x28:{// PLP
814 fuint8 temp = READ_LOW( sp );
815 sp = (sp - 0xFF) | 0x100;
816 fuint8 changed = status ^ temp;
817 SET_STATUS( temp );
818 if ( !(changed & st_i) )
819 goto loop; // I flag didn't change
820 if ( status & st_i )
821 goto handle_sei;
822 goto handle_cli;
823 }
824
825 case 0x08: { // PHP
826 fuint8 temp;
827 CALC_STATUS( temp );
828 PUSH( temp | (st_b | st_r) );
829 goto loop;
830 }
831
832 case 0x6C:{// JMP (ind)
833 data = GET_ADDR();
834 pc = READ_PROG( data );
835 data = (data & 0xFF00) | ((data + 1) & 0xFF);
836 pc |= 0x100 * READ_PROG( data );
837 goto loop;
838 }
839
840 case 0x00: // BRK
841 goto handle_brk;
842
843 // Flags
844
845 case 0x38: // SEC
846 c = (unsigned) ~0;
847 goto loop;
848
849 case 0x18: // CLC
850 c = 0;
851 goto loop;
852
853 case 0xB8: // CLV
854 status &= ~st_v;
855 goto loop;
856
857 case 0xD8: // CLD
858 status &= ~st_d;
859 goto loop;
860
861 case 0xF8: // SED
862 status |= st_d;
863 goto loop;
864
865 case 0x58: // CLI
866 if ( !(status & st_i) )
867 goto loop;
868 status &= ~st_i;
869 handle_cli: {
870 this->r.status = status; // update externally-visible I flag
871 blargg_long delta = s.base - irq_time_;
872 if ( delta <= 0 )
873 {
874 if ( TIME < irq_time_ )
875 goto loop;
876 goto delayed_cli;
877 }
878 s.base = irq_time_;
879 s_time += delta;
880 if ( s_time < 0 )
881 goto loop;
882
883 if ( delta >= s_time + 1 )
884 {
885 // delayed irq until after next instruction
886 s.base += s_time + 1;
887 s_time = -1;
888 irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop
889 goto loop;
890 }
891 delayed_cli:
892 debug_printf( "Delayed CLI not emulated\n" );
893 goto loop;
894 }
895
896 case 0x78: // SEI
897 if ( status & st_i )
898 goto loop;
899 status |= st_i;
900 handle_sei: {
901 this->r.status = status; // update externally-visible I flag
902 blargg_long delta = s.base - end_time_;
903 s.base = end_time_;
904 s_time += delta;
905 if ( s_time < 0 )
906 goto loop;
907 debug_printf( "Delayed SEI not emulated\n" );
908 goto loop;
909 }
910
911 // Unofficial
912
913 // SKW - Skip word
914 case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
915 HANDLE_PAGE_CROSSING( data + x );
916 case 0x0C:
917 pc++;
918 // SKB - Skip byte
919 case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64:
920 case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
921 pc++;
922 goto loop;
923
924 // NOP
925 case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:
926 goto loop;
927
928 // Unimplemented
929
930 // halt
931 //case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
932 //case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2:
933
934 default:
935 assert( (unsigned) opcode <= 0xFF );
936 illegal_encountered = true;
937 pc--;
938 goto stop;
939 }
940 assert( false );
941
942 int result_;
943 handle_brk:
944 if ( (pc - 1) >= idle_addr )
945 goto idle_done;
946 pc++;
947 result_ = 4;
948 debug_printf( "BRK executed\n" );
949
950 interrupt:
951 {
952 s_time += 7;
953
954 WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
955 WRITE_LOW( 0x100 | (sp - 2), pc );
956 pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ );
957
958 sp = (sp - 3) | 0x100;
959 fuint8 temp;
960 CALC_STATUS( temp );
961 temp |= st_r;
962 if ( result_ )
963 temp |= st_b; // TODO: incorrectly sets B flag for IRQ
964 WRITE_LOW( sp, temp );
965
966 status &= ~st_d;
967 status |= st_i;
968 this->r.status = status; // update externally-visible I flag
969
970 blargg_long delta = s.base - end_time_;
971 s.base = end_time_;
972 s_time += delta;
973 goto loop;
974 }
975
976 idle_done:
977 //s_time = 0;
978 pc--;
979 goto stop;
980 out_of_time:
981 pc--;
982 FLUSH_TIME();
983 CPU_DONE( this, TIME, result_ );
984 CACHE_TIME();
985 if ( result_ >= 0 )
986 goto interrupt;
987 if ( s_time < 0 )
988 goto loop;
989
990 stop:
991
992 s.time = s_time;
993
994 r.pc = pc;
995 r.sp = GET_SP();
996 r.a = a;
997 r.x = x;
998 r.y = y;
999
1000 {
1001 fuint8 temp;
1002 CALC_STATUS( temp );
1003 r.status = temp;
1004 }
1005
1006 this->state_ = s;
1007 this->state = &this->state_;
1008
1009 return illegal_encountered;
1010 }
1011
1012