Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/quicknes/nes_emu/Nes_Cpu.cpp
2 views
1
2
// Nes_Emu 0.7.0. http://www.slack.net/~ant/nes-emu/
3
4
// TODO: remove
5
#if !defined (NDEBUG) && 0
6
#pragma peephole on
7
#pragma global_optimizer on
8
#pragma optimization_level 4
9
#pragma scheduling 604
10
#undef BLARGG_ENABLE_OPTIMIZER
11
#endif
12
13
#include "Nes_Cpu.h"
14
15
#include <string.h>
16
#include <limits.h>
17
#include "blargg_endian.h"
18
19
#include "nes_cpu_io.h"
20
21
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
22
can redistribute it and/or modify it under the terms of the GNU Lesser
23
General Public License as published by the Free Software Foundation; either
24
version 2.1 of the License, or (at your option) any later version. This
25
module is distributed in the hope that it will be useful, but WITHOUT ANY
26
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
27
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
28
more details. You should have received a copy of the GNU Lesser General
29
Public License along with this module; if not, write to the Free Software
30
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
31
32
#include "blargg_source.h"
33
34
#ifdef BLARGG_ENABLE_OPTIMIZER
35
#include BLARGG_ENABLE_OPTIMIZER
36
#endif
37
38
inline void Nes_Cpu::set_code_page( int i, uint8_t const* p )
39
{
40
code_map [i] = p - (unsigned) i * page_size;
41
}
42
43
void Nes_Cpu::reset( void const* unmapped_page )
44
{
45
r.status = 0;
46
r.sp = 0;
47
r.pc = 0;
48
r.a = 0;
49
r.x = 0;
50
r.y = 0;
51
52
error_count_ = 0;
53
clock_count = 0;
54
clock_limit = 0;
55
irq_time_ = LONG_MAX / 2 + 1;
56
end_time_ = LONG_MAX / 2 + 1;
57
58
assert( page_size == 0x800 ); // assumes this
59
set_code_page( 0, low_mem );
60
set_code_page( 1, low_mem );
61
set_code_page( 2, low_mem );
62
set_code_page( 3, low_mem );
63
for ( int i = 4; i < page_count + 1; i++ )
64
set_code_page( i, (uint8_t*) unmapped_page );
65
66
#ifndef NDEBUG
67
blargg_verify_byte_order();
68
#endif
69
}
70
71
void Nes_Cpu::map_code( nes_addr_t start, unsigned size, const void* data )
72
{
73
// address range must begin and end on page boundaries
74
require( start % page_size == 0 );
75
require( size % page_size == 0 );
76
require( start + size <= 0x10000 );
77
78
unsigned first_page = start / page_size;
79
for ( unsigned i = size / page_size; i--; )
80
set_code_page( first_page + i, (uint8_t*) data + i * page_size );
81
}
82
83
// Note: 'addr' is evaulated more than once in the following macros, so it
84
// must not contain side-effects.
85
86
//static void log_read( int opcode ) { LOG_FREQ( "read", 256, opcode ); }
87
88
#define READ_LIKELY_PPU( addr ) (NES_CPU_READ_PPU( this, (addr), (clock_count) ))
89
#define READ( addr ) (NES_CPU_READ( this, (addr), (clock_count) ))
90
#define WRITE( addr, data ) {NES_CPU_WRITE( this, (addr), (data), (clock_count) );}
91
92
#define READ_LOW( addr ) (low_mem [int (addr)])
93
#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
94
95
#define READ_PROG( addr ) (code_map [(addr) >> page_bits] [addr])
96
#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
97
98
#define SET_SP( v ) (sp = ((v) + 1) | 0x100)
99
#define GET_SP() ((sp - 1) & 0xFF)
100
#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
101
102
#ifdef BLARGG_ENABLE_OPTIMIZER
103
#include BLARGG_ENABLE_OPTIMIZER
104
#endif
105
106
int Nes_Cpu::read( nes_addr_t addr )
107
{
108
return READ( addr );
109
}
110
111
void Nes_Cpu::write( nes_addr_t addr, int value )
112
{
113
WRITE( addr, value );
114
}
115
116
void Nes_Cpu::set_tracecb(void (*cb)(unsigned int *data))
117
{
118
tracecb = cb;
119
}
120
121
#ifndef NES_CPU_GLUE_ONLY
122
123
static const unsigned char clock_table [256] = {
124
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
125
7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
126
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
127
6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
128
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
129
6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
130
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
131
6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
132
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
133
2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
134
3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
135
2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
136
3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
137
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
138
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
139
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
140
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
141
};
142
143
Nes_Cpu::result_t Nes_Cpu::run( nes_time_t end )
144
{
145
set_end_time_( end );
146
clock_count = 0;
147
148
volatile result_t result = result_cycles;
149
150
#if !BLARGG_CPU_CISC
151
long clock_count = this->clock_count;
152
uint8_t* const low_mem = this->low_mem;
153
#endif
154
155
// registers
156
unsigned pc = r.pc;
157
int sp;
158
SET_SP( r.sp );
159
int a = r.a;
160
int x = r.x;
161
int y = r.y;
162
163
// status flags
164
165
int const st_n = 0x80;
166
int const st_v = 0x40;
167
int const st_r = 0x20;
168
int const st_b = 0x10;
169
int const st_d = 0x08;
170
int const st_i = 0x04;
171
int const st_z = 0x02;
172
int const st_c = 0x01;
173
174
#define IS_NEG (nz & 0x880)
175
176
#define CALC_STATUS( out ) do { \
177
out = status & (st_v | st_d | st_i); \
178
out |= (c >> 8) & st_c; \
179
if ( IS_NEG ) out |= st_n; \
180
if ( !(nz & 0xFF) ) out |= st_z; \
181
} while ( 0 )
182
183
#define SET_STATUS( in ) do { \
184
status = in & (st_v | st_d | st_i); \
185
c = in << 8; \
186
nz = (in << 4) & 0x800; \
187
nz |= ~in & st_z; \
188
} while ( 0 )
189
190
int status;
191
int c; // carry set if (c & 0x100) != 0
192
int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x880) != 0
193
{
194
int temp = r.status;
195
SET_STATUS( temp );
196
}
197
198
goto loop;
199
dec_clock_loop:
200
clock_count--;
201
loop:
202
203
assert( (unsigned) GET_SP() < 0x100 );
204
assert( (unsigned) a < 0x100 );
205
assert( (unsigned) x < 0x100 );
206
assert( (unsigned) y < 0x100 );
207
208
uint8_t const* page = code_map [pc >> page_bits];
209
unsigned opcode = page [pc];
210
pc++;
211
212
if ( clock_count >= clock_limit )
213
goto stop;
214
215
if (tracecb)
216
{
217
unsigned int scratch[7];
218
scratch[0] = a;
219
scratch[1] = x;
220
scratch[2] = y;
221
scratch[3] = sp;
222
scratch[4] = pc - 1;
223
scratch[5] = status;
224
scratch[6] = opcode;
225
tracecb(scratch);
226
}
227
228
clock_count += clock_table [opcode];
229
unsigned data;
230
data = page [pc];
231
232
switch ( opcode )
233
{
234
235
// Macros
236
237
#define GET_OPERAND( addr ) page [addr]
238
#define GET_OPERAND16( addr ) GET_LE16( &page [addr] )
239
240
//#define GET_OPERAND( addr ) READ_PROG( addr )
241
//#define GET_OPERAND16( addr ) READ_PROG16( addr )
242
243
#define ADD_PAGE (pc++, data += 0x100 * GET_OPERAND( pc ));
244
#define GET_ADDR() GET_OPERAND16( pc )
245
246
#define HANDLE_PAGE_CROSSING( lsb ) clock_count += (lsb) >> 8;
247
248
#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
249
250
#define IND_Y(r,c) { \
251
int temp = READ_LOW( data ) + y; \
252
data = temp + 0x100 * READ_LOW( uint8_t (data + 1) ); \
253
if (c) HANDLE_PAGE_CROSSING( temp ); \
254
if (!(r) || (temp & 0x100)) \
255
READ( data - ( temp & 0x100 ) ); \
256
}
257
258
#define IND_X { \
259
int temp = data + x; \
260
data = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) ); \
261
}
262
263
#define ARITH_ADDR_MODES( op ) \
264
case op - 0x04: /* (ind,x) */ \
265
IND_X \
266
goto ptr##op; \
267
case op + 0x0C: /* (ind),y */ \
268
IND_Y(true,true) \
269
goto ptr##op; \
270
case op + 0x10: /* zp,X */ \
271
data = uint8_t (data + x); \
272
case op + 0x00: /* zp */ \
273
data = READ_LOW( data ); \
274
goto imm##op; \
275
case op + 0x14: /* abs,Y */ \
276
data += y; \
277
goto ind##op; \
278
case op + 0x18: /* abs,X */ \
279
data += x; \
280
ind##op: { \
281
HANDLE_PAGE_CROSSING( data ); \
282
int temp = data; \
283
ADD_PAGE \
284
if ( temp & 0x100 ) \
285
READ( data - 0x100 ); \
286
goto ptr##op; \
287
} \
288
case op + 0x08: /* abs */ \
289
ADD_PAGE \
290
ptr##op: \
291
data = READ( data ); \
292
case op + 0x04: /* imm */ \
293
imm##op: \
294
295
#define ARITH_ADDR_MODES_PTR( op ) \
296
case op - 0x04: /* (ind,x) */ \
297
IND_X \
298
goto imm##op; \
299
case op + 0x0C: \
300
IND_Y(false,false) \
301
goto imm##op; \
302
case op + 0x10: /* zp,X */ \
303
data = uint8_t (data + x); \
304
goto imm##op; \
305
case op + 0x14: /* abs,Y */ \
306
data += y; \
307
goto ind##op; \
308
case op + 0x18: /* abs,X */ \
309
data += x; \
310
ind##op: { \
311
int temp = data; \
312
ADD_PAGE \
313
READ( data - ( temp & 0x100 ) ); \
314
goto imm##op; \
315
} \
316
case op + 0x08: /* abs */ \
317
ADD_PAGE \
318
case op + 0x00: /* zp */ \
319
imm##op: \
320
321
#define BRANCH( cond ) \
322
{ \
323
pc++; \
324
int offset = (BOOST::int8_t) data; \
325
int extra_clock = (pc & 0xFF) + offset; \
326
if ( !(cond) ) goto dec_clock_loop; \
327
pc += offset; \
328
pc = BOOST::uint16_t( pc ); \
329
clock_count += (extra_clock >> 8) & 1; \
330
goto loop; \
331
}
332
333
// Often-Used
334
335
case 0xB5: // LDA zp,x
336
data = uint8_t (data + x);
337
case 0xA5: // LDA zp
338
a = nz = READ_LOW( data );
339
pc++;
340
goto loop;
341
342
case 0xD0: // BNE
343
BRANCH( (uint8_t) nz );
344
345
case 0x20: { // JSR
346
int temp = pc + 1;
347
pc = GET_OPERAND16( pc );
348
WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
349
sp = (sp - 2) | 0x100;
350
WRITE_LOW( sp, temp );
351
goto loop;
352
}
353
354
case 0x4C: // JMP abs
355
pc = GET_OPERAND16( pc );
356
goto loop;
357
358
case 0xE8: INC_DEC_XY( x, 1 ) // INX
359
360
case 0x10: // BPL
361
BRANCH( !IS_NEG )
362
363
ARITH_ADDR_MODES( 0xC5 ) // CMP
364
nz = a - data;
365
pc++;
366
c = ~nz;
367
nz &= 0xFF;
368
goto loop;
369
370
case 0x30: // BMI
371
BRANCH( IS_NEG )
372
373
case 0xF0: // BEQ
374
BRANCH( !(uint8_t) nz );
375
376
case 0x95: // STA zp,x
377
data = uint8_t (data + x);
378
case 0x85: // STA zp
379
pc++;
380
WRITE_LOW( data, a );
381
goto loop;
382
383
case 0xC8: INC_DEC_XY( y, 1 ) // INY
384
385
case 0xA8: // TAY
386
y = a;
387
case 0x98: // TYA
388
a = nz = y;
389
goto loop;
390
391
case 0xAD:{// LDA abs
392
unsigned addr = GET_ADDR();
393
pc += 2;
394
a = nz = READ_LIKELY_PPU( addr );
395
goto loop;
396
}
397
398
case 0x60: // RTS
399
pc = 1 + READ_LOW( sp );
400
pc += READ_LOW( 0x100 | (sp - 0xFF) ) * 0x100;
401
sp = (sp - 0xFE) | 0x100;
402
goto loop;
403
404
case 0x99: // STA abs,Y
405
data += y;
406
goto sta_ind_common;
407
408
case 0x9D: // STA abs,X
409
data += x;
410
sta_ind_common: {
411
int temp = data;
412
ADD_PAGE
413
READ( data - ( temp & 0x100 ) );
414
goto sta_ptr;
415
}
416
case 0x8D: // STA abs
417
ADD_PAGE
418
sta_ptr:
419
pc++;
420
WRITE( data, a );
421
goto loop;
422
423
case 0xA9: // LDA #imm
424
pc++;
425
a = data;
426
nz = data;
427
goto loop;
428
429
#if 0
430
case 0xA1: // LDA (ind,X)
431
IND_X
432
goto lda_ptr;
433
434
case 0xB1: // LDA (ind),Y
435
IND_Y(true,true)
436
goto lda_ptr;
437
438
case 0xB9: // LDA abs,Y
439
data += y;
440
goto lda_ind_common;
441
442
case 0xBD: // LDA abs,X
443
data += x;
444
lda_ind_common: {
445
HANDLE_PAGE_CROSSING( data );
446
int temp = data;
447
ADD_PAGE
448
if ( temp & 0x100 )
449
READ( data - 0x100 );
450
}
451
lda_ptr:
452
a = nz = READ( data );
453
pc++;
454
goto loop;
455
#else
456
// optimization of most commonly used memory read instructions
457
458
case 0xB9:// LDA abs,Y
459
data += y;
460
data -= x;
461
case 0xBD:{// LDA abs,X
462
pc++;
463
unsigned msb = GET_OPERAND( pc );
464
data += x;
465
// indexed common
466
pc++;
467
HANDLE_PAGE_CROSSING( data );
468
int temp = data;
469
data += msb * 0x100;
470
a = nz = READ_PROG( BOOST::uint16_t( data ) );
471
if ( (unsigned) (data - 0x2000) >= 0x6000 )
472
goto loop;
473
if ( temp & 0x100 )
474
READ( data - 0x100 );
475
a = nz = READ( data );
476
goto loop;
477
}
478
479
case 0xB1:{// LDA (ind),Y
480
unsigned msb = READ_LOW( (uint8_t) (data + 1) );
481
data = READ_LOW( data ) + y;
482
// indexed common
483
pc++;
484
HANDLE_PAGE_CROSSING( data );
485
int temp = data;
486
data += msb * 0x100;
487
a = nz = READ_PROG( BOOST::uint16_t( data ) );
488
if ( (unsigned) (data - 0x2000) >= 0x6000 )
489
goto loop;
490
if ( temp & 0x100 )
491
READ( data - 0x100 );
492
a = nz = READ( data );
493
goto loop;
494
}
495
496
case 0xA1: // LDA (ind,X)
497
IND_X
498
a = nz = READ( data );
499
pc++;
500
goto loop;
501
502
#endif
503
504
// Branch
505
506
case 0x50: // BVC
507
BRANCH( !(status & st_v) )
508
509
case 0x70: // BVS
510
BRANCH( status & st_v )
511
512
case 0xB0: // BCS
513
BRANCH( c & 0x100 )
514
515
case 0x90: // BCC
516
BRANCH( !(c & 0x100) )
517
518
// Load/store
519
520
case 0x94: // STY zp,x
521
data = uint8_t (data + x);
522
case 0x84: // STY zp
523
pc++;
524
WRITE_LOW( data, y );
525
goto loop;
526
527
case 0x96: // STX zp,y
528
data = uint8_t (data + y);
529
case 0x86: // STX zp
530
pc++;
531
WRITE_LOW( data, x );
532
goto loop;
533
534
case 0xB6: // LDX zp,y
535
data = uint8_t (data + y);
536
case 0xA6: // LDX zp
537
data = READ_LOW( data );
538
case 0xA2: // LDX #imm
539
pc++;
540
x = data;
541
nz = data;
542
goto loop;
543
544
case 0xB4: // LDY zp,x
545
data = uint8_t (data + x);
546
case 0xA4: // LDY zp
547
data = READ_LOW( data );
548
case 0xA0: // LDY #imm
549
pc++;
550
y = data;
551
nz = data;
552
goto loop;
553
554
case 0x91: // STA (ind),Y
555
IND_Y(false,false)
556
goto sta_ptr;
557
558
case 0x81: // STA (ind,X)
559
IND_X
560
goto sta_ptr;
561
562
case 0xBC: // LDY abs,X
563
data += x;
564
HANDLE_PAGE_CROSSING( data );
565
case 0xAC:{// LDY abs
566
pc++;
567
unsigned addr = data + 0x100 * GET_OPERAND( pc );
568
if ( data & 0x100 )
569
READ( addr - 0x100 );
570
pc++;
571
y = nz = READ( addr );
572
goto loop;
573
}
574
575
case 0xBE: // LDX abs,y
576
data += y;
577
HANDLE_PAGE_CROSSING( data );
578
case 0xAE:{// LDX abs
579
pc++;
580
unsigned addr = data + 0x100 * GET_OPERAND( pc );
581
pc++;
582
if ( data & 0x100 )
583
READ( addr - 0x100 );
584
x = nz = READ( addr );
585
goto loop;
586
}
587
588
{
589
int temp;
590
case 0x8C: // STY abs
591
temp = y;
592
goto store_abs;
593
594
case 0x8E: // STX abs
595
temp = x;
596
store_abs:
597
unsigned addr = GET_ADDR();
598
WRITE( addr, temp );
599
pc += 2;
600
goto loop;
601
}
602
603
// Compare
604
605
case 0xEC:{// CPX abs
606
unsigned addr = GET_ADDR();
607
pc++;
608
data = READ( addr );
609
goto cpx_data;
610
}
611
612
case 0xE4: // CPX zp
613
data = READ_LOW( data );
614
case 0xE0: // CPX #imm
615
cpx_data:
616
nz = x - data;
617
pc++;
618
c = ~nz;
619
nz &= 0xFF;
620
goto loop;
621
622
case 0xCC:{// CPY abs
623
unsigned addr = GET_ADDR();
624
pc++;
625
data = READ( addr );
626
goto cpy_data;
627
}
628
629
case 0xC4: // CPY zp
630
data = READ_LOW( data );
631
case 0xC0: // CPY #imm
632
cpy_data:
633
nz = y - data;
634
pc++;
635
c = ~nz;
636
nz &= 0xFF;
637
goto loop;
638
639
// Logical
640
641
ARITH_ADDR_MODES( 0x25 ) // AND
642
nz = (a &= data);
643
pc++;
644
goto loop;
645
646
ARITH_ADDR_MODES( 0x45 ) // EOR
647
nz = (a ^= data);
648
pc++;
649
goto loop;
650
651
ARITH_ADDR_MODES( 0x05 ) // ORA
652
nz = (a |= data);
653
pc++;
654
goto loop;
655
656
case 0x2C:{// BIT abs
657
unsigned addr = GET_ADDR();
658
pc += 2;
659
status &= ~st_v;
660
nz = READ_LIKELY_PPU( addr );
661
status |= nz & st_v;
662
if ( a & nz )
663
goto loop;
664
// result must be zero, even if N bit is set
665
nz = nz << 4 & 0x800;
666
goto loop;
667
}
668
669
case 0x24: // BIT zp
670
nz = READ_LOW( data );
671
pc++;
672
status &= ~st_v;
673
status |= nz & st_v;
674
if ( a & nz )
675
goto loop;
676
// result must be zero, even if N bit is set
677
nz = nz << 4 & 0x800;
678
goto loop;
679
680
// Add/subtract
681
682
ARITH_ADDR_MODES( 0xE5 ) // SBC
683
case 0xEB: // unofficial equivalent
684
data ^= 0xFF;
685
goto adc_imm;
686
687
ARITH_ADDR_MODES( 0x65 ) // ADC
688
adc_imm: {
689
int carry = (c >> 8) & 1;
690
int ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
691
status &= ~st_v;
692
status |= (ov >> 2) & 0x40;
693
c = nz = a + data + carry;
694
pc++;
695
a = (uint8_t) nz;
696
goto loop;
697
}
698
699
// Shift/rotate
700
701
case 0x4A: // LSR A
702
lsr_a:
703
c = 0;
704
case 0x6A: // ROR A
705
nz = (c >> 1) & 0x80; // could use bit insert macro here
706
c = a << 8;
707
nz |= a >> 1;
708
a = nz;
709
goto loop;
710
711
case 0x0A: // ASL A
712
nz = a << 1;
713
c = nz;
714
a = (uint8_t) nz;
715
goto loop;
716
717
case 0x2A: { // ROL A
718
nz = a << 1;
719
int temp = (c >> 8) & 1;
720
c = nz;
721
nz |= temp;
722
a = (uint8_t) nz;
723
goto loop;
724
}
725
726
case 0x3E: // ROL abs,X
727
data += x;
728
goto rol_abs;
729
730
case 0x1E: // ASL abs,X
731
data += x;
732
case 0x0E: // ASL abs
733
c = 0;
734
case 0x2E: // ROL abs
735
rol_abs: {
736
int temp = data;
737
ADD_PAGE
738
if ( opcode == 0x1E || opcode == 0x3E ) READ( data - ( temp & 0x100 ) );
739
WRITE( data, temp = READ( data ) );
740
nz = (c >> 8) & 1;
741
nz |= (c = temp << 1);
742
}
743
rotate_common:
744
pc++;
745
WRITE( data, (uint8_t) nz );
746
goto loop;
747
748
case 0x7E: // ROR abs,X
749
data += x;
750
goto ror_abs;
751
752
case 0x5E: // LSR abs,X
753
data += x;
754
case 0x4E: // LSR abs
755
c = 0;
756
case 0x6E: // ROR abs
757
ror_abs: {
758
int temp = data;
759
ADD_PAGE
760
if ( opcode == 0x5E || opcode == 0x7E ) READ( data - ( temp & 0x100 ) );
761
WRITE( data, temp = READ( data ) );
762
nz = ((c >> 1) & 0x80) | (temp >> 1);
763
c = temp << 8;
764
goto rotate_common;
765
}
766
767
case 0x76: // ROR zp,x
768
data = uint8_t (data + x);
769
goto ror_zp;
770
771
case 0x56: // LSR zp,x
772
data = uint8_t (data + x);
773
case 0x46: // LSR zp
774
c = 0;
775
case 0x66: // ROR zp
776
ror_zp: {
777
int temp = READ_LOW( data );
778
nz = ((c >> 1) & 0x80) | (temp >> 1);
779
c = temp << 8;
780
goto write_nz_zp;
781
}
782
783
case 0x36: // ROL zp,x
784
data = uint8_t (data + x);
785
goto rol_zp;
786
787
case 0x16: // ASL zp,x
788
data = uint8_t (data + x);
789
case 0x06: // ASL zp
790
c = 0;
791
case 0x26: // ROL zp
792
rol_zp:
793
nz = (c >> 8) & 1;
794
nz |= (c = READ_LOW( data ) << 1);
795
goto write_nz_zp;
796
797
// Increment/decrement
798
799
case 0xCA: INC_DEC_XY( x, -1 ) // DEX
800
801
case 0x88: INC_DEC_XY( y, -1 ) // DEY
802
803
case 0xF6: // INC zp,x
804
data = uint8_t (data + x);
805
case 0xE6: // INC zp
806
nz = 1;
807
goto add_nz_zp;
808
809
case 0xD6: // DEC zp,x
810
data = uint8_t (data + x);
811
case 0xC6: // DEC zp
812
nz = -1;
813
add_nz_zp:
814
nz += READ_LOW( data );
815
write_nz_zp:
816
pc++;
817
WRITE_LOW( data, nz );
818
goto loop;
819
820
case 0xFE: { // INC abs,x
821
int temp = data + x;
822
data = x + GET_ADDR();
823
READ( data - ( temp & 0x100 ) );
824
goto inc_ptr;
825
}
826
827
case 0xEE: // INC abs
828
data = GET_ADDR();
829
inc_ptr:
830
nz = 1;
831
goto inc_common;
832
833
case 0xDE: { // DEC abs,x
834
int temp = data + x;
835
data = x + GET_ADDR();
836
READ( data - ( temp & 0x100 ) );
837
goto dec_ptr;
838
}
839
840
case 0xCE: // DEC abs
841
data = GET_ADDR();
842
dec_ptr:
843
nz = -1;
844
inc_common: {
845
int temp;
846
WRITE( data, temp = READ( data ) );
847
nz += temp;
848
pc += 2;
849
WRITE( data, (uint8_t) nz );
850
goto loop;
851
}
852
853
// Transfer
854
855
case 0xAA: // TAX
856
x = a;
857
case 0x8A: // TXA
858
a = nz = x;
859
goto loop;
860
861
case 0x9A: // TXS
862
SET_SP( x ); // verified (no flag change)
863
goto loop;
864
865
case 0xBA: // TSX
866
x = nz = GET_SP();
867
goto loop;
868
869
// Stack
870
871
case 0x48: // PHA
872
PUSH( a ); // verified
873
goto loop;
874
875
case 0x68: // PLA
876
a = nz = READ_LOW( sp );
877
sp = (sp - 0xFF) | 0x100;
878
goto loop;
879
880
case 0x40: // RTI
881
{
882
int temp = READ_LOW( sp );
883
pc = READ_LOW( 0x100 | (sp - 0xFF) );
884
pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
885
sp = (sp - 0xFD) | 0x100;
886
data = status;
887
SET_STATUS( temp );
888
}
889
if ( !((data ^ status) & st_i) )
890
goto loop; // I flag didn't change
891
i_flag_changed:
892
//dprintf( "%6d %s\n", time(), (status & st_i ? "SEI" : "CLI") );
893
this->r.status = status; // update externally-visible I flag
894
// update clock_limit based on modified I flag
895
clock_limit = end_time_;
896
if ( end_time_ <= irq_time_ )
897
goto loop;
898
if ( status & st_i )
899
goto loop;
900
clock_limit = irq_time_;
901
goto loop;
902
903
case 0x28:{// PLP
904
int temp = READ_LOW( sp );
905
sp = (sp - 0xFF) | 0x100;
906
data = status;
907
SET_STATUS( temp );
908
if ( !((data ^ status) & st_i) )
909
goto loop; // I flag didn't change
910
if ( !(status & st_i) )
911
goto handle_cli;
912
goto handle_sei;
913
}
914
915
case 0x08: { // PHP
916
int temp;
917
CALC_STATUS( temp );
918
PUSH( temp | st_b | st_r );
919
goto loop;
920
}
921
922
case 0x6C: // JMP (ind)
923
data = GET_ADDR();
924
pc = READ( data );
925
pc |= READ( (data & 0xFF00) | ((data + 1) & 0xFF) ) << 8;
926
goto loop;
927
928
case 0x00: { // BRK
929
pc++;
930
WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
931
WRITE_LOW( 0x100 | (sp - 2), pc );
932
int temp;
933
CALC_STATUS( temp );
934
sp = (sp - 3) | 0x100;
935
WRITE_LOW( sp, temp | st_b | st_r );
936
pc = GET_LE16( &code_map [0xFFFE >> page_bits] [0xFFFE] );
937
status |= st_i;
938
goto i_flag_changed;
939
}
940
941
// Flags
942
943
case 0x38: // SEC
944
c = ~0;
945
goto loop;
946
947
case 0x18: // CLC
948
c = 0;
949
goto loop;
950
951
case 0xB8: // CLV
952
status &= ~st_v;
953
goto loop;
954
955
case 0xD8: // CLD
956
status &= ~st_d;
957
goto loop;
958
959
case 0xF8: // SED
960
status |= st_d;
961
goto loop;
962
963
case 0x58: // CLI
964
if ( !(status & st_i) )
965
goto loop;
966
status &= ~st_i;
967
handle_cli:
968
//dprintf( "%6d CLI\n", time() );
969
this->r.status = status; // update externally-visible I flag
970
if ( clock_count < end_time_ )
971
{
972
assert( clock_limit == end_time_ );
973
if ( end_time_ <= irq_time_ )
974
goto loop; // irq is later
975
if ( clock_count >= irq_time_ )
976
irq_time_ = clock_count + 1; // delay IRQ until after next instruction
977
clock_limit = irq_time_;
978
goto loop;
979
}
980
// execution is stopping now, so delayed CLI must be handled by caller
981
result = result_cli;
982
goto end;
983
984
case 0x78: // SEI
985
if ( status & st_i )
986
goto loop;
987
status |= st_i;
988
handle_sei:
989
//dprintf( "%6d SEI\n", time() );
990
this->r.status = status; // update externally-visible I flag
991
clock_limit = end_time_;
992
if ( clock_count < irq_time_ )
993
goto loop;
994
result = result_sei; // IRQ will occur now, even though I flag is set
995
goto end;
996
997
// Unofficial
998
case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: { // SKW
999
data += x;
1000
HANDLE_PAGE_CROSSING( data );
1001
int addr = GET_ADDR() + x;
1002
if ( data & 0x100 )
1003
READ( addr - 0x100 );
1004
READ( addr );
1005
}
1006
case 0x0C: // SKW
1007
pc++;
1008
case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: // SKB
1009
case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
1010
pc++;
1011
case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: // NOP
1012
goto loop;
1013
1014
ARITH_ADDR_MODES_PTR( 0xC7 ) // DCP
1015
WRITE( data, nz = READ( data ) );
1016
nz = uint8_t( nz - 1 );
1017
WRITE( data, nz );
1018
pc++;
1019
nz = a - nz;
1020
c = ~nz;
1021
nz &= 0xFF;
1022
goto loop;
1023
1024
ARITH_ADDR_MODES_PTR( 0xE7 ) // ISC
1025
WRITE( data, nz = READ( data ) );
1026
nz = uint8_t( nz + 1 );
1027
WRITE( data, nz );
1028
data = nz ^ 0xFF;
1029
goto adc_imm;
1030
1031
ARITH_ADDR_MODES_PTR( 0x27 ) { // RLA
1032
WRITE( data, nz = READ( data ) );
1033
int temp = c;
1034
c = nz << 1;
1035
nz = uint8_t( c ) | ( ( temp >> 8 ) & 0x01 );
1036
WRITE( data, nz );
1037
pc++;
1038
nz = a &= nz;
1039
goto loop;
1040
}
1041
1042
ARITH_ADDR_MODES_PTR( 0x67 ) { // RRA
1043
int temp;
1044
WRITE( data, temp = READ( data ) );
1045
nz = ((c >> 1) & 0x80) | (temp >> 1);
1046
WRITE( data, nz );
1047
data = nz;
1048
c = temp << 8;
1049
goto adc_imm;
1050
}
1051
1052
ARITH_ADDR_MODES_PTR( 0x07 ) // SLO
1053
WRITE( data, nz = READ( data ) );
1054
c = nz << 1;
1055
nz = uint8_t( c );
1056
WRITE( data, nz );
1057
nz = (a |= nz);
1058
pc++;
1059
goto loop;
1060
1061
ARITH_ADDR_MODES_PTR( 0x47 ) // SRE
1062
WRITE( data, nz = READ( data ) );
1063
c = nz << 8;
1064
nz >>= 1;
1065
WRITE( data, nz );
1066
nz = a ^= nz;
1067
pc++;
1068
goto loop;
1069
1070
case 0x4B: // ALR
1071
nz = (a &= data);
1072
pc++;
1073
goto lsr_a;
1074
1075
case 0x0B: // ANC
1076
case 0x2B:
1077
nz = a &= data;
1078
c = a << 1;
1079
pc++;
1080
goto loop;
1081
1082
case 0x6B: // ARR
1083
nz = a = uint8_t( ( ( data & a ) >> 1 ) | ( ( c >> 1 ) & 0x80 ) );
1084
c = a << 2;
1085
status = ( status & ~st_v ) | ( ( a ^ a << 1 ) & st_v );
1086
pc++;
1087
goto loop;
1088
1089
case 0xAB: // LXA
1090
a = data;
1091
x = data;
1092
nz = data;
1093
pc++;
1094
goto loop;
1095
1096
case 0xA3: // LAX
1097
IND_X
1098
goto lax_ptr;
1099
1100
case 0xB3:
1101
IND_Y(true,true)
1102
goto lax_ptr;
1103
1104
case 0xB7:
1105
data = uint8_t (data + y);
1106
1107
case 0xA7:
1108
data = READ_LOW( data );
1109
goto lax_imm;
1110
1111
case 0xBF: {
1112
data += y;
1113
HANDLE_PAGE_CROSSING( data );
1114
int temp = data;
1115
ADD_PAGE;
1116
if ( temp & 0x100 )
1117
READ( data - 0x100 );
1118
goto lax_ptr;
1119
}
1120
1121
case 0xAF:
1122
ADD_PAGE
1123
1124
lax_ptr:
1125
data = READ( data );
1126
lax_imm:
1127
nz = x = a = data;
1128
pc++;
1129
goto loop;
1130
1131
case 0x83: // SAX
1132
IND_X
1133
goto sax_imm;
1134
1135
case 0x97:
1136
data = uint8_t (data + y);
1137
goto sax_imm;
1138
1139
case 0x8F:
1140
ADD_PAGE
1141
1142
case 0x87:
1143
sax_imm:
1144
WRITE( data, a & x );
1145
pc++;
1146
goto loop;
1147
1148
case 0xCB: // SBX
1149
data = ( a & x ) - data;
1150
c = ( data <= 0xFF ) ? 0x100 : 0;
1151
nz = x = uint8_t( data );
1152
pc++;
1153
goto loop;
1154
1155
case 0x93: // SHA (ind),Y
1156
IND_Y(false,false)
1157
pc++;
1158
WRITE( data, uint8_t( a & x & ( ( data >> 8 ) + 1 ) ) );
1159
goto loop;
1160
1161
case 0x9F: { // SHA abs,Y
1162
data += y;
1163
int temp = data;
1164
ADD_PAGE
1165
READ( data - ( temp & 0x100 ) );
1166
pc++;
1167
WRITE( data, uint8_t( a & x & ( ( data >> 8 ) + 1 ) ) );
1168
goto loop;
1169
}
1170
1171
case 0x9E: { // SHX abs,Y
1172
data += y;
1173
int temp = data;
1174
ADD_PAGE
1175
READ( data - ( temp & 0x100 ) );
1176
pc++;
1177
if ( !( temp & 0x100 ) )
1178
WRITE( data, uint8_t( x & ( ( data >> 8 ) + 1 ) ) );
1179
goto loop;
1180
}
1181
1182
case 0x9C: { // SHY abs,X
1183
data += x;
1184
int temp = data;
1185
ADD_PAGE
1186
READ( data - ( temp & 0x100 ) );
1187
pc++;
1188
if ( !( temp & 0x100) )
1189
WRITE( data, uint8_t( y & ( ( data >> 8 ) + 1 ) ) );
1190
goto loop;
1191
}
1192
1193
case 0x9B: { // SHS abs,Y
1194
data += y;
1195
int temp = data;
1196
ADD_PAGE
1197
READ( data - ( temp & 0x100 ) );
1198
pc++;
1199
SET_SP( a & x );
1200
WRITE( data, uint8_t( a & x & ( ( data >> 8 ) + 1 ) ) );
1201
goto loop;
1202
}
1203
1204
case 0xBB: { // LAS abs,Y
1205
data += y;
1206
HANDLE_PAGE_CROSSING( data );
1207
int temp = data;
1208
ADD_PAGE
1209
if ( temp & 0x100 )
1210
READ( data - 0x100 );
1211
pc++;
1212
a = GET_SP();
1213
x = a &= READ( data );
1214
SET_SP( a );
1215
goto loop;
1216
}
1217
1218
// Unimplemented
1219
1220
case page_wrap_opcode: // HLT
1221
if ( pc > 0x10000 )
1222
{
1223
// handle wrap-around (assumes caller has put page of HLT at 0x10000)
1224
pc = (pc - 1) & 0xFFFF;
1225
clock_count -= 2;
1226
goto loop;
1227
}
1228
// fall through
1229
default:
1230
// skip over proper number of bytes
1231
static unsigned char const row [8] = { 0x95, 0x95, 0x95, 0xd5, 0x95, 0x95, 0xd5, 0xf5 };
1232
int len = row [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3;
1233
if ( opcode == 0x9C )
1234
len = 3;
1235
pc += len - 1;
1236
error_count_++;
1237
goto loop;
1238
1239
//result = result_badop; // TODO: re-enable
1240
goto stop;
1241
}
1242
1243
// If this fails then the case above is missing an opcode
1244
assert( false );
1245
1246
stop:
1247
pc--;
1248
end:
1249
1250
{
1251
int temp;
1252
CALC_STATUS( temp );
1253
r.status = temp;
1254
}
1255
1256
this->clock_count = clock_count;
1257
r.pc = pc;
1258
r.sp = GET_SP();
1259
r.a = a;
1260
r.x = x;
1261
r.y = y;
1262
irq_time_ = LONG_MAX / 2 + 1;
1263
1264
return result;
1265
}
1266
1267
#endif
1268
1269
1270