summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/hes_cpu_run.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/hes_cpu_run.h')
-rw-r--r--lib/rbcodec/codecs/libgme/hes_cpu_run.h1344
1 files changed, 1344 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/hes_cpu_run.h b/lib/rbcodec/codecs/libgme/hes_cpu_run.h
new file mode 100644
index 0000000000..bfba2b6109
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/hes_cpu_run.h
@@ -0,0 +1,1344 @@
1// Game_Music_Emu 0.6-pre. 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,
6though they NEVER have side-effects, so multiple evaluation is OK.
7- Output parameters might be a multiple-assignment expression like "a=x",
8so they must NOT be parenthesized.
9- Except where noted, time() and related functions will NOT work
10correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and
11CACHE_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 hes_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
50can redistribute it and/or modify it under the terms of the GNU Lesser
51General Public License as published by the Free Software Foundation; either
52version 2.1 of the License, or (at your option) any later version. This
53module is distributed in the hope that it will be useful, but WITHOUT ANY
54WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
55FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
56details. You should have received a copy of the GNU Lesser General Public
57License along with this module; if not, write to the Free Software Foundation,
58Inc., 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
64int const ram_addr = 0x2000;
65
66void Cpu_reset( struct Hes_Cpu* this )
67{
68 check( this->cpu_state == &this->cpu_state_ );
69 this->cpu_state = &this->cpu_state_;
70
71 this->cpu_state_.time = 0;
72 this->cpu_state_.base = 0;
73 this->irq_time_ = future_time;
74 this->end_time_ = future_time;
75
76 this->r.flags = 0x04;
77 this->r.sp = 0;
78 this->r.pc = 0;
79 this->r.a = 0;
80 this->r.x = 0;
81 this->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 ) ((uint8_t ) (n)) /* (unsigned) n & 0xFF */
112#define SBYTE( n ) ((int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
113#define WORD( n ) ((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.
117int const n80 = 0x80; // nz
118int const v40 = 0x40; // flags
119//int const t20 = 0x20;
120int const b10 = 0x10;
121int const d08 = 0x08; // flags
122int const i04 = 0x04; // flags
123int const z02 = 0x02; // nz
124int 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
144bool illegal_encountered = false;
145{
146 struct 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
168loop:
169
170 #ifndef NDEBUG
171 {
172 hes_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 = (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 = (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 hes_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 {
1239 int in = GET_LE16( instr + 0 );
1240 int out = GET_LE16( instr + 2 );
1241 int count = GET_LE16( instr + 4 );
1242 if ( !count )
1243 count = 0x10000;
1244 pc += 6;
1245 WRITE_STACK( SP( -1 ), y );
1246 WRITE_STACK( SP( -2 ), a );
1247 WRITE_STACK( SP( -3 ), x );
1248 FLUSH_TIME();
1249 do
1250 {
1251 // TODO: reads from $0800-$1400 in I/O page should do I/O
1252 int t = READ_MEM( in );
1253 in = WORD( in + in_inc );
1254 s.time += 6;
1255 if ( in_alt )
1256 in_inc = -in_inc;
1257 WRITE_MEM( out, t );
1258 out = WORD( out + out_inc );
1259 if ( out_alt )
1260 out_inc = -out_inc;
1261 }
1262 while ( --count );
1263 CACHE_TIME();
1264 goto loop;
1265 }
1266 }
1267
1268// Illegal
1269
1270 default:
1271 check( (unsigned) opcode <= 0xFF );
1272 dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 );
1273 illegal_encountered = true;
1274 goto loop;
1275 }
1276 assert( false ); // catch missing 'goto loop' or accidental 'break'
1277
1278 int result_;
1279handle_brk:
1280 pc++;
1281 result_ = 6;
1282
1283interrupt:
1284 {
1285 s_time += 7;
1286
1287 // Save PC and read vector
1288 WRITE_STACK( SP( -1 ), pc >> 8 );
1289 WRITE_STACK( SP( -2 ), pc );
1290 pc = GET_LE16( &READ_CODE( 0xFFF0 ) + result_ );
1291
1292 // Save flags
1293 int temp;
1294 GET_FLAGS( temp );
1295 if ( result_ == 6 )
1296 temp |= b10; // BRK sets B bit
1297 sp = SP( -3 );
1298 WRITE_STACK( sp, temp );
1299
1300 // Update I flag in externally-visible flags
1301 flags &= ~d08;
1302 cpu->r.flags = (flags |= i04);
1303
1304 // Update time
1305 int delta = s.base - cpu->end_time_;
1306 if ( delta >= 0 )
1307 goto loop;
1308 s_time += delta;
1309 s.base = cpu->end_time_;
1310 goto loop;
1311 }
1312
1313idle_done:
1314 s_time = 0;
1315
1316out_of_time:
1317 pc--;
1318
1319 // Optional action that triggers interrupt or changes irq/end time
1320 #ifdef CPU_DONE
1321 {
1322 CPU_DONE( result_ );
1323 if ( result_ >= 0 )
1324 goto interrupt;
1325 if ( s_time < 0 )
1326 goto loop;
1327 }
1328 #endif
1329
1330 // Flush cached state
1331 cpu->r.pc = pc;
1332 cpu->r.sp = GET_SP();
1333 cpu->r.a = a;
1334 cpu->r.x = x;
1335 cpu->r.y = y;
1336
1337 int temp;
1338 GET_FLAGS( temp );
1339 cpu->r.flags = temp;
1340
1341 cpu->cpu_state_.base = s.base;
1342 cpu->cpu_state_.time = s_time;
1343 cpu->cpu_state = &cpu->cpu_state_;
1344}