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