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