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