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