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 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