Coverage Report

Created: 2026-07-12 08:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/contrib/contrib-build/game-music-emu/gme/Nes_Cpu.cpp
Line
Count
Source
1
// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
2
3
#include "Nes_Cpu.h"
4
5
#include "blargg_endian.h"
6
#include <limits.h>
7
8
#define BLARGG_CPU_X86 1
9
10
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
11
can redistribute it and/or modify it under the terms of the GNU Lesser
12
General Public License as published by the Free Software Foundation; either
13
version 2.1 of the License, or (at your option) any later version. This
14
module is distributed in the hope that it will be useful, but WITHOUT ANY
15
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17
details. You should have received a copy of the GNU Lesser General Public
18
License along with this module; if not, write to the Free Software Foundation,
19
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
20
21
#ifdef BLARGG_ENABLE_OPTIMIZER
22
  #include BLARGG_ENABLE_OPTIMIZER
23
#endif
24
25
158M
#define FLUSH_TIME()    (void) (s.time = s_time)
26
158M
#define CACHE_TIME()    (void) (s_time = s.time)
27
28
#include "nes_cpu_io.h"
29
30
#include "blargg_source.h"
31
32
#ifndef CPU_DONE
33
16.2M
  #define CPU_DONE( cpu, time, result_out )   { result_out = -1; }
34
#endif
35
36
#ifndef CPU_READ_PPU
37
  #define CPU_READ_PPU( cpu, addr, out, time )\
38
2.39M
  {\
39
2.39M
    FLUSH_TIME();\
40
2.39M
    out = CPU_READ( cpu, addr, time );\
41
2.39M
    CACHE_TIME();\
42
2.39M
  }
43
#endif
44
45
#if BLARGG_NONPORTABLE
46
  #define PAGE_OFFSET( addr ) (addr)
47
#else
48
2.84G
  #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
49
#endif
50
51
inline void Nes_Cpu::set_code_page( int i, void const* p )
52
282k
{
53
282k
  state->code_map [i] = (uint8_t const*) p - PAGE_OFFSET( i * page_size );
54
282k
}
55
56
enum {
57
    st_n = 0x80,
58
    st_v = 0x40,
59
    st_r = 0x20,
60
    st_b = 0x10,
61
    st_d = 0x08,
62
    st_i = 0x04,
63
    st_z = 0x02,
64
    st_c = 0x01
65
};
66
67
void Nes_Cpu::reset( void const* unmapped_page )
68
5.32k
{
69
5.32k
  check( state == &state_ );
70
5.32k
  state = &state_;
71
5.32k
  r.status = st_i;
72
5.32k
  r.sp = 0xFF;
73
5.32k
  r.pc = 0;
74
5.32k
  r.a  = 0;
75
5.32k
  r.x  = 0;
76
5.32k
  r.y  = 0;
77
5.32k
  state_.time = 0;
78
5.32k
  state_.base = 0;
79
5.32k
  irq_time_ = future_nes_time;
80
5.32k
  end_time_ = future_nes_time;
81
5.32k
  error_count_ = 0;
82
83
5.32k
  blaarg_static_assert( page_size == 0x800, "NES set to use unhandled page size" ); // assumes this
84
5.32k
  set_code_page( page_count, unmapped_page );
85
5.32k
  map_code( 0x2000, 0xE000, unmapped_page, true );
86
5.32k
  map_code( 0x0000, 0x2000, low_mem, true );
87
88
5.32k
  blargg_verify_byte_order();
89
5.32k
}
90
91
void Nes_Cpu::map_code( nes_addr_t start, unsigned size, void const* data, bool mirror )
92
58.5k
{
93
  // address range must begin and end on page boundaries
94
58.5k
  require( start % page_size == 0 );
95
58.5k
  require( size % page_size == 0 );
96
58.5k
  require( start + size <= 0x10000 );
97
98
58.5k
  unsigned page = start / page_size;
99
335k
  for ( unsigned n = size / page_size; n; --n )
100
276k
  {
101
276k
    set_code_page( page++, data );
102
276k
    if ( !mirror )
103
106k
      data = (char const*) data + page_size;
104
276k
  }
105
58.5k
}
106
107
5.94M
#define TIME    (s_time + s.base)
108
2.39M
#define READ_LIKELY_PPU( addr, out )    {CPU_READ_PPU( this, (addr), out, TIME );}
109
62.6M
#define READ( addr )                    CPU_READ( this, (addr), TIME )
110
84.9M
#define WRITE( addr, data )             {CPU_WRITE( this, (addr), (data), TIME );}
111
6.99G
#define READ_LOW( addr )        (low_mem [int (addr)])
112
6.81G
#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
113
229k
#define READ_PROG( addr )       (s.code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )])
114
115
152M
#define SET_SP( v )     (sp = ((v) + 1) | 0x100)
116
152M
#define GET_SP()        ((sp - 1) & 0xFF)
117
6.89M
#define PUSH( v )       ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
118
119
bool Nes_Cpu::run( nes_time_t end_time )
120
152M
{
121
152M
  set_end_time( end_time );
122
152M
  state_t s = this->state_;
123
152M
  this->state = &s;
124
  // even on x86, using s.time in place of s_time was slower
125
152M
  int16_t s_time = s.time;
126
127
  // registers
128
152M
  uint16_t pc = r.pc;
129
152M
  uint8_t a = r.a;
130
152M
  uint8_t x = r.x;
131
152M
  uint8_t y = r.y;
132
152M
  uint16_t sp;
133
152M
  SET_SP( r.sp );
134
135
  // status flags
136
152M
  #define IS_NEG (nz & 0x8080)
137
138
2.41G
  #define CALC_STATUS( out ) do {\
139
2.41G
    out = status & (st_v | st_d | st_i);\
140
2.41G
    out |= ((nz >> 8) | nz) & st_n;\
141
2.41G
    out |= c >> 8 & st_c;\
142
2.41G
    if ( !(nz & 0xFF) ) out |= st_z;\
143
2.41G
  } while ( 0 )
144
145
154M
  #define SET_STATUS( in ) do {\
146
154M
    status = in & (st_v | st_d | st_i);\
147
154M
    nz = in << 8;\
148
154M
    c = nz;\
149
154M
    nz |= ~in & st_z;\
150
154M
  } while ( 0 )
151
152
152M
  uint8_t status;
153
152M
  uint16_t c;  // carry set if (c & 0x100) != 0
154
152M
  uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
155
152M
  {
156
152M
    uint8_t temp = r.status;
157
152M
    SET_STATUS( temp );
158
152M
  }
159
160
152M
  goto loop;
161
1.63M
dec_clock_loop:
162
1.63M
  s_time--;
163
2.84G
loop:
164
165
2.84G
  check( (unsigned) GET_SP() < 0x100 );
166
2.84G
  check( (unsigned) pc < 0x10000 );
167
2.84G
  check( (unsigned) a < 0x100 );
168
2.84G
  check( (unsigned) x < 0x100 );
169
2.84G
  check( (unsigned) y < 0x100 );
170
2.84G
  check( -32768 <= s_time && s_time < 32767 );
171
172
2.84G
  uint8_t const* instr = s.code_map [pc >> page_bits];
173
2.84G
  uint8_t opcode;
174
175
  // TODO: eliminate this special case
176
  #if BLARGG_NONPORTABLE
177
    opcode = instr [pc];
178
    pc++;
179
    instr += pc;
180
  #else
181
2.84G
    instr += PAGE_OFFSET( pc );
182
2.84G
    opcode = *instr++;
183
2.84G
    pc++;
184
2.84G
  #endif
185
186
2.84G
  static uint8_t const clock_table [256] =
187
2.84G
  {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
188
2.84G
    0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
189
2.84G
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
190
2.84G
    6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
191
2.84G
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
192
2.84G
    6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
193
2.84G
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
194
2.84G
    6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
195
2.84G
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
196
2.84G
    2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
197
2.84G
    3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
198
2.84G
    2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
199
2.84G
    3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
200
2.84G
    2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
201
2.84G
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
202
2.84G
    2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
203
2.84G
    3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
204
2.84G
  }; // 0x00 was 7 and 0xF2 was 2
205
206
2.84G
  uint16_t data;
207
208
#if !BLARGG_CPU_X86
209
  if ( s_time >= 0 )
210
    goto out_of_time;
211
  s_time += clock_table [opcode];
212
213
  data = *instr;
214
215
  switch ( opcode )
216
  {
217
#else
218
219
2.84G
  data = clock_table [opcode];
220
2.84G
  if ( (s_time += data) >= 0 )
221
20.1M
    goto possibly_out_of_time;
222
2.82G
almost_out_of_time:
223
224
2.82G
  data = *instr;
225
226
2.82G
  switch ( opcode )
227
2.82G
  {
228
20.1M
possibly_out_of_time:
229
20.1M
    if ( s_time < (int) data )
230
3.90M
      goto almost_out_of_time;
231
16.2M
    s_time -= data;
232
16.2M
    goto out_of_time;
233
0
#endif
234
235
// Macros
236
237
30.5M
#define GET_MSB()   (instr [1])
238
29.8M
#define ADD_PAGE()  (pc++, data += 0x100 * GET_MSB())
239
100M
#define GET_ADDR()  GET_LE16( instr )
240
241
0
#define NO_PAGE_CROSSING( lsb )
242
729k
#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8;
243
244
138k
#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
245
246
6.34M
#define IND_Y( cross, out ) {\
247
6.34M
    uint16_t temp = READ_LOW( data ) + y;\
248
6.34M
    out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
249
6.34M
    cross( temp );\
250
6.34M
  }
251
252
22.7M
#define IND_X( out ) {\
253
22.7M
    uint16_t temp = data + x;\
254
22.7M
    out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\
255
22.7M
  }
256
257
22.6M
#define ARITH_ADDR_MODES( op )\
258
22.6M
case op - 0x04: /* (ind,x) */\
259
22.6M
  IND_X( data )\
260
22.6M
  goto ptr##op;\
261
6.31M
case op + 0x0C: /* (ind),y */\
262
6.31M
  IND_Y( HANDLE_PAGE_CROSSING, data )\
263
6.60M
  goto ptr##op;\
264
6.60M
case op + 0x10: /* zp,X */\
265
91.2M
  data = uint8_t (data + x);/* FALLTHRU */\
266
91.2M
case op + 0x00: /* zp */\
267
91.2M
  data = READ_LOW( data );\
268
91.2M
  goto imm##op;\
269
5.04M
case op + 0x14: /* abs,Y */\
270
5.04M
  data += y;\
271
10.6M
  goto ind##op;\
272
10.6M
case op + 0x18: /* abs,X */\
273
15.7M
  data += x;\
274
15.7M
ind##op:\
275
24.9M
  HANDLE_PAGE_CROSSING( data );/* FALLTHRU */\
276
24.9M
case op + 0x08: /* abs */\
277
53.8M
  ADD_PAGE();\
278
53.8M
ptr##op:\
279
53.8M
  FLUSH_TIME();\
280
53.8M
  data = READ( data );\
281
64.6M
  CACHE_TIME();/*FALLTHRU*/\
282
155M
case op + 0x04: /* imm */\
283
155M
imm##op:
284
285
// TODO: more efficient way to handle negative branch that wraps PC around
286
8.51M
#define BRANCH( cond )\
287
8.51M
{\
288
8.51M
  int16_t offset = (int8_t) data;\
289
8.51M
  uint16_t extra_clock = (++pc & 0xFF) + offset;\
290
10.1M
  if ( !(cond) ) goto dec_clock_loop;\
291
8.51M
  pc = uint16_t (pc + offset);\
292
8.51M
  s_time += extra_clock >> 8 & 1;\
293
8.51M
  goto loop;\
294
8.51M
}
295
296
// Often-Used
297
298
581
  case 0xB5: // LDA zp,x
299
581
    a = nz = READ_LOW( uint8_t (data + x) );
300
581
    pc++;
301
581
    goto loop;
302
303
225
  case 0xA5: // LDA zp
304
225
    a = nz = READ_LOW( data );
305
225
    pc++;
306
225
    goto loop;
307
308
2.92M
  case 0xD0: // BNE
309
2.92M
    BRANCH( (uint8_t) nz );
310
311
15.4M
  case 0x20: { // JSR
312
15.4M
    uint16_t temp = pc + 1;
313
15.4M
    pc = GET_ADDR();
314
15.4M
    WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
315
15.4M
    sp = (sp - 2) | 0x100;
316
15.4M
    WRITE_LOW( sp, temp );
317
15.4M
    goto loop;
318
0
  }
319
320
80.5k
  case 0x4C: // JMP abs
321
80.5k
    pc = GET_ADDR();
322
80.5k
    goto loop;
323
324
125k
  case 0xE8: // INX
325
125k
    INC_DEC_XY( x, 1 )
326
327
7.09M
  case 0x10: // BPL
328
7.09M
    BRANCH( !IS_NEG )
329
330
184k
  ARITH_ADDR_MODES( 0xC5 ) // CMP
331
184k
    nz = a - data;
332
184k
    pc++;
333
184k
    c = ~nz;
334
184k
    nz &= 0xFF;
335
184k
    goto loop;
336
337
64.4k
  case 0x30: // BMI
338
64.4k
    BRANCH( IS_NEG )
339
340
1.34k
  case 0xF0: // BEQ
341
1.34k
    BRANCH( !(uint8_t) nz );
342
343
2.36k
  case 0x95: // STA zp,x
344
2.36k
    data = uint8_t (data + x);/*FALLTHRU*/
345
10.6k
  case 0x85: // STA zp
346
10.6k
    pc++;
347
10.6k
    WRITE_LOW( data, a );
348
10.6k
    goto loop;
349
350
3.16k
  case 0xC8: // INY
351
3.16k
    INC_DEC_XY( y, 1 )
352
353
225
  case 0xA8: // TAY
354
225
    y  = a;
355
225
    nz = a;
356
225
    goto loop;
357
358
3.08k
  case 0x98: // TYA
359
3.08k
    a  = y;
360
3.08k
    nz = y;
361
3.08k
    goto loop;
362
363
865
  case 0xAD:{// LDA abs
364
865
    unsigned addr = GET_ADDR();
365
865
    pc += 2;
366
865
    READ_LIKELY_PPU( addr, nz );
367
865
    a = nz;
368
865
    goto loop;
369
2.36k
  }
370
371
12.1M
  case 0x60: // RTS
372
12.1M
    pc = 1 + READ_LOW( sp );
373
12.1M
    pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
374
12.1M
    sp = (sp - 0xFE) | 0x100;
375
12.1M
    goto loop;
376
377
0
  {
378
0
    uint16_t addr;
379
380
136k
  case 0x99: // STA abs,Y
381
136k
    addr = y + GET_ADDR();
382
136k
    pc += 2;
383
136k
    if ( addr <= 0x7FF )
384
223
    {
385
223
      WRITE_LOW( addr, a );
386
223
      goto loop;
387
223
    }
388
136k
    goto sta_ptr;
389
390
78.8M
  case 0x8D: // STA abs
391
78.8M
    addr = GET_ADDR();
392
78.8M
    pc += 2;
393
78.8M
    if ( addr <= 0x7FF )
394
2.28M
    {
395
2.28M
      WRITE_LOW( addr, a );
396
2.28M
      goto loop;
397
2.28M
    }
398
76.6M
    goto sta_ptr;
399
400
76.6M
  case 0x9D: // STA abs,X (slightly more common than STA abs)
401
478
    addr = x + GET_ADDR();
402
478
    pc += 2;
403
478
    if ( addr <= 0x7FF )
404
225
    {
405
225
      WRITE_LOW( addr, a );
406
225
      goto loop;
407
225
    }
408
76.8M
  sta_ptr:
409
76.8M
    FLUSH_TIME();
410
76.8M
    WRITE( addr, a );
411
76.8M
    CACHE_TIME();
412
76.8M
    goto loop;
413
414
33.1k
  case 0x91: // STA (ind),Y
415
33.1k
    IND_Y( NO_PAGE_CROSSING, addr )
416
33.1k
    pc++;
417
33.1k
    goto sta_ptr;
418
419
127k
  case 0x81: // STA (ind,X)
420
127k
    IND_X( addr )
421
127k
    pc++;
422
127k
    goto sta_ptr;
423
424
478
  }
425
426
1.32M
  case 0xA9: // LDA #imm
427
1.32M
    pc++;
428
1.32M
    a  = data;
429
1.32M
    nz = data;
430
1.32M
    goto loop;
431
432
  // common read instructions
433
0
  {
434
0
    uint16_t addr;
435
436
3.37k
  case 0xA1: // LDA (ind,X)
437
3.37k
    IND_X( addr )
438
3.37k
    pc++;
439
3.37k
    goto a_nz_read_addr;
440
441
223k
  case 0xB1:// LDA (ind),Y
442
223k
    addr = READ_LOW( data ) + y;
443
223k
    HANDLE_PAGE_CROSSING( addr );
444
223k
    addr += 0x100 * READ_LOW( (uint8_t) (data + 1) );
445
223k
    pc++;
446
223k
    a = nz = READ_PROG( addr );
447
223k
    if ( (addr ^ 0x8000) <= 0x9FFF )
448
223k
      goto loop;
449
0
    goto a_nz_read_addr;
450
451
6.56k
  case 0xB9: // LDA abs,Y
452
6.56k
    HANDLE_PAGE_CROSSING( data + y );
453
6.56k
    addr = GET_ADDR() + y;
454
6.56k
    pc += 2;
455
6.56k
    a = nz = READ_PROG( addr );
456
6.56k
    if ( (addr ^ 0x8000) <= 0x9FFF )
457
6.46k
      goto loop;
458
94
    goto a_nz_read_addr;
459
460
228
  case 0xBD: // LDA abs,X
461
228
    HANDLE_PAGE_CROSSING( data + x );
462
228
    addr = GET_ADDR() + x;
463
228
    pc += 2;
464
228
    a = nz = READ_PROG( addr );
465
228
    if ( (addr ^ 0x8000) <= 0x9FFF )
466
226
      goto loop;
467
3.47k
  a_nz_read_addr:
468
3.47k
    FLUSH_TIME();
469
3.47k
    a = nz = READ( addr );
470
3.47k
    CACHE_TIME();
471
3.47k
    goto loop;
472
473
228
  }
474
475
// Branch
476
477
32.1k
  case 0x50: // BVC
478
32.1k
    BRANCH( !(status & st_v) )
479
480
38.2k
  case 0x70: // BVS
481
38.2k
    BRANCH( status & st_v )
482
483
244
  case 0xB0: // BCS
484
244
    BRANCH( c & 0x100 )
485
486
450
  case 0x90: // BCC
487
450
    BRANCH( !(c & 0x100) )
488
489
// Load/store
490
491
12.6k
  case 0x94: // STY zp,x
492
12.6k
    data = uint8_t (data + x); // FALLTHRU
493
225k
  case 0x84: // STY zp
494
225k
    pc++;
495
225k
    WRITE_LOW( data, y );
496
225k
    goto loop;
497
498
1.58k
  case 0x96: // STX zp,y
499
1.58k
    data = uint8_t (data + y); // FALLTHRU
500
3.48k
  case 0x86: // STX zp
501
3.48k
    pc++;
502
3.48k
    WRITE_LOW( data, x );
503
3.48k
    goto loop;
504
505
27.7k
  case 0xB6: // LDX zp,y
506
27.7k
    data = uint8_t (data + y); // FALLTHRU
507
34.5k
  case 0xA6: // LDX zp
508
34.5k
    data = READ_LOW( data ); // FALLTHRU
509
34.7k
  case 0xA2: // LDX #imm
510
34.7k
    pc++;
511
34.7k
    x = data;
512
34.7k
    nz = data;
513
34.7k
    goto loop;
514
515
449
  case 0xB4: // LDY zp,x
516
449
    data = uint8_t (data + x); // FALLTHRU
517
945
  case 0xA4: // LDY zp
518
945
    data = READ_LOW( data ); // FALLTHRU
519
125k
  case 0xA0: // LDY #imm
520
125k
    pc++;
521
125k
    y = data;
522
125k
    nz = data;
523
125k
    goto loop;
524
525
3.74k
  case 0xBC: // LDY abs,X
526
3.74k
    data += x;
527
3.74k
    HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
528
3.97k
  case 0xAC:{// LDY abs
529
3.97k
    unsigned addr = data + 0x100 * GET_MSB();
530
3.97k
    pc += 2;
531
3.97k
    FLUSH_TIME();
532
3.97k
    y = nz = READ( addr );
533
3.97k
    CACHE_TIME();
534
3.97k
    goto loop;
535
3.74k
  }
536
537
225
  case 0xBE: // LDX abs,y
538
225
    data += y;
539
225
    HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
540
699k
  case 0xAE:{// LDX abs
541
699k
    unsigned addr = data + 0x100 * GET_MSB();
542
699k
    pc += 2;
543
699k
    FLUSH_TIME();
544
699k
    x = nz = READ( addr );
545
699k
    CACHE_TIME();
546
699k
    goto loop;
547
225
  }
548
549
0
  {
550
0
    uint8_t temp;
551
6.68k
  case 0x8C: // STY abs
552
6.68k
    temp = y;
553
6.68k
    goto store_abs;
554
555
8.87k
  case 0x8E: // STX abs
556
8.87k
    temp = x;
557
15.5k
  store_abs:
558
15.5k
    unsigned addr = GET_ADDR();
559
15.5k
    pc += 2;
560
15.5k
    if ( addr <= 0x7FF )
561
223
    {
562
223
      WRITE_LOW( addr, temp );
563
223
      goto loop;
564
223
    }
565
15.3k
    FLUSH_TIME();
566
15.3k
    WRITE( addr, temp );
567
15.3k
    CACHE_TIME();
568
15.3k
    goto loop;
569
15.5k
  }
570
571
// Compare
572
573
279
  case 0xEC:{// CPX abs
574
279
    unsigned addr = GET_ADDR();
575
279
    pc++;
576
279
    FLUSH_TIME();
577
279
    data = READ( addr );
578
279
    CACHE_TIME();
579
279
    goto cpx_data;
580
15.5k
  }
581
582
300
  case 0xE4: // CPX zp
583
300
    data = READ_LOW( data );/*FALLTHRU*/
584
124k
  case 0xE0: // CPX #imm
585
125k
  cpx_data:
586
125k
    nz = x - data;
587
125k
    pc++;
588
125k
    c = ~nz;
589
125k
    nz &= 0xFF;
590
125k
    goto loop;
591
592
250
  case 0xCC:{// CPY abs
593
250
    unsigned addr = GET_ADDR();
594
250
    pc++;
595
250
    FLUSH_TIME();
596
250
    data = READ( addr );
597
250
    CACHE_TIME();
598
250
    goto cpy_data;
599
124k
  }
600
601
46
  case 0xC4: // CPY zp
602
46
    data = READ_LOW( data );/*FALLTHRU*/
603
281
  case 0xC0: // CPY #imm
604
531
  cpy_data:
605
531
    nz = y - data;
606
531
    pc++;
607
531
    c = ~nz;
608
531
    nz &= 0xFF;
609
531
    goto loop;
610
611
// Logical
612
613
7.52M
  ARITH_ADDR_MODES( 0x25 ) // AND
614
7.52M
    nz = (a &= data);
615
7.52M
    pc++;
616
7.52M
    goto loop;
617
618
2.33M
  ARITH_ADDR_MODES( 0x45 ) // EOR
619
2.33M
    nz = (a ^= data);
620
2.33M
    pc++;
621
2.33M
    goto loop;
622
623
139M
  ARITH_ADDR_MODES( 0x05 ) // ORA
624
139M
    nz = (a |= data);
625
139M
    pc++;
626
139M
    goto loop;
627
628
2.39M
  case 0x2C:{// BIT abs
629
2.39M
    unsigned addr = GET_ADDR();
630
2.39M
    pc += 2;
631
2.39M
    status &= ~st_v;
632
2.39M
    READ_LIKELY_PPU( addr, nz );
633
2.39M
    status |= nz & st_v;
634
2.39M
    if ( a & nz )
635
2.39M
      goto loop;
636
1.34k
    nz <<= 8; // result must be zero, even if N bit is set
637
1.34k
    goto loop;
638
2.39M
  }
639
640
2.72M
  case 0x24: // BIT zp
641
2.72M
    nz = READ_LOW( data );
642
2.72M
    pc++;
643
2.72M
    status &= ~st_v;
644
2.72M
    status |= nz & st_v;
645
2.72M
    if ( a & nz )
646
2.72M
      goto loop;
647
202
    nz <<= 8; // result must be zero, even if N bit is set
648
202
    goto loop;
649
650
// Add/subtract
651
652
24.3M
  ARITH_ADDR_MODES( 0xE5 ) // SBC
653
24.3M
  case 0xEB: // unofficial equivalent
654
4.12M
    data ^= 0xFF;
655
4.12M
    goto adc_imm;
656
657
8.10M
  ARITH_ADDR_MODES( 0x65 ) // ADC
658
8.10M
  adc_imm: {
659
6.26M
    int16_t carry = c >> 8 & 1;
660
6.26M
    int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
661
6.26M
    status &= ~st_v;
662
6.26M
    status |= ov >> 2 & 0x40;
663
6.26M
    c = nz = a + data + carry;
664
6.26M
    pc++;
665
6.26M
    a = (uint8_t) nz;
666
6.26M
    goto loop;
667
8.10M
  }
668
669
// Shift/rotate
670
671
151k
  case 0x4A: // LSR A
672
151k
    c = 0;/*FALLTHRU*/
673
658k
  case 0x6A: // ROR A
674
658k
    nz = c >> 1 & 0x80;
675
658k
    c = a << 8;
676
658k
    nz |= a >> 1;
677
658k
    a = nz;
678
658k
    goto loop;
679
680
3.19M
  case 0x0A: // ASL A
681
3.19M
    nz = a << 1;
682
3.19M
    c = nz;
683
3.19M
    a = (uint8_t) nz;
684
3.19M
    goto loop;
685
686
1.45M
  case 0x2A: { // ROL A
687
1.45M
    nz = a << 1;
688
1.45M
    int16_t temp = c >> 8 & 1;
689
1.45M
    c = nz;
690
1.45M
    nz |= temp;
691
1.45M
    a = (uint8_t) nz;
692
1.45M
    goto loop;
693
151k
  }
694
695
144k
  case 0x5E: // LSR abs,X
696
144k
    data += x;/*FALLTHRU*/
697
153k
  case 0x4E: // LSR abs
698
153k
    c = 0;/*FALLTHRU*/
699
1.76M
  case 0x6E: // ROR abs
700
1.89M
  ror_abs: {
701
1.89M
    ADD_PAGE();
702
1.89M
    FLUSH_TIME();
703
1.89M
    int temp = READ( data );
704
1.89M
    nz = (c >> 1 & 0x80) | (temp >> 1);
705
1.89M
    c = temp << 8;
706
1.89M
    goto rotate_common;
707
1.76M
  }
708
709
1.06M
  case 0x3E: // ROL abs,X
710
1.06M
    data += x;
711
1.06M
    goto rol_abs;
712
713
1.92M
  case 0x1E: // ASL abs,X
714
1.92M
    data += x;/*FALLTHRU*/
715
1.92M
  case 0x0E: // ASL abs
716
1.92M
    c = 0;/*FALLTHRU*/
717
1.95M
  case 0x2E: // ROL abs
718
3.01M
  rol_abs:
719
3.01M
    ADD_PAGE();
720
3.01M
    nz = c >> 8 & 1;
721
3.01M
    FLUSH_TIME();
722
3.01M
    nz |= (c = READ( data ) << 1);
723
4.91M
  rotate_common:
724
4.91M
    pc++;
725
4.91M
    WRITE( data, (uint8_t) nz );
726
4.91M
    CACHE_TIME();
727
4.91M
    goto loop;
728
729
127k
  case 0x7E: // ROR abs,X
730
127k
    data += x;
731
127k
    goto ror_abs;
732
733
127k
  case 0x76: // ROR zp,x
734
127k
    data = uint8_t (data + x);
735
127k
    goto ror_zp;
736
737
155k
  case 0x56: // LSR zp,x
738
155k
    data = uint8_t (data + x);/*FALLTHRU*/
739
1.58M
  case 0x46: // LSR zp
740
1.58M
    c = 0;/*FALLTHRU*/
741
1.71M
  case 0x66: // ROR zp
742
1.84M
  ror_zp: {
743
1.84M
    int temp = READ_LOW( data );
744
1.84M
    nz = (c >> 1 & 0x80) | (temp >> 1);
745
1.84M
    c = temp << 8;
746
1.84M
    goto write_nz_zp;
747
1.71M
  }
748
749
1.09M
  case 0x36: // ROL zp,x
750
1.09M
    data = uint8_t (data + x);
751
1.09M
    goto rol_zp;
752
753
3.08M
  case 0x16: // ASL zp,x
754
3.08M
    data = uint8_t (data + x);/*FALLTHRU*/
755
3.08M
  case 0x06: // ASL zp
756
3.08M
    c = 0;/*FALLTHRU*/
757
3.15M
  case 0x26: // ROL zp
758
4.25M
  rol_zp:
759
4.25M
    nz = c >> 8 & 1;
760
4.25M
    nz |= (c = READ_LOW( data ) << 1);
761
4.25M
    goto write_nz_zp;
762
763
// Increment/decrement
764
765
6.49k
  case 0xCA: // DEX
766
6.49k
    INC_DEC_XY( x, -1 )
767
768
3.08k
  case 0x88: // DEY
769
3.08k
    INC_DEC_XY( y, -1 )
770
771
225
  case 0xF6: // INC zp,x
772
225
    data = uint8_t (data + x);/*FALLTHRU*/
773
1.80M
  case 0xE6: // INC zp
774
1.80M
    nz = 1;
775
1.80M
    goto add_nz_zp;
776
777
223
  case 0xD6: // DEC zp,x
778
223
    data = uint8_t (data + x);/*FALLTHRU*/
779
449
  case 0xC6: // DEC zp
780
449
    nz = (uint16_t) -1;
781
1.80M
  add_nz_zp:
782
1.80M
    nz += READ_LOW( data );
783
7.90M
  write_nz_zp:
784
7.90M
    pc++;
785
7.90M
    WRITE_LOW( data, nz );
786
7.90M
    goto loop;
787
788
3.11M
  case 0xFE: // INC abs,x
789
3.11M
    data = x + GET_ADDR();
790
3.11M
    goto inc_ptr;
791
792
226
  case 0xEE: // INC abs
793
226
    data = GET_ADDR();
794
3.11M
  inc_ptr:
795
3.11M
    nz = 1;
796
3.11M
    goto inc_common;
797
798
1.11k
  case 0xDE: // DEC abs,x
799
1.11k
    data = x + GET_ADDR();
800
1.11k
    goto dec_ptr;
801
802
283
  case 0xCE: // DEC abs
803
283
    data = GET_ADDR();
804
1.39k
  dec_ptr:
805
1.39k
    nz = (uint16_t) -1;
806
3.12M
  inc_common:
807
3.12M
    FLUSH_TIME();
808
3.12M
    nz += READ( data );
809
3.12M
    pc += 2;
810
3.12M
    WRITE( data, (uint8_t) nz );
811
3.12M
    CACHE_TIME();
812
3.12M
    goto loop;
813
814
// Transfer
815
816
719
  case 0xAA: // TAX
817
719
    x  = a;
818
719
    nz = a;
819
719
    goto loop;
820
821
3.08k
  case 0x8A: // TXA
822
3.08k
    a  = x;
823
3.08k
    nz = x;
824
3.08k
    goto loop;
825
826
41
  case 0x9A: // TXS
827
41
    SET_SP( x ); // verified (no flag change)
828
41
    goto loop;
829
830
9.25k
  case 0xBA: // TSX
831
9.25k
    x = nz = GET_SP();
832
9.25k
    goto loop;
833
834
// Stack
835
836
24.8k
  case 0x48: // PHA
837
24.8k
    PUSH( a ); // verified
838
24.8k
    goto loop;
839
840
282
  case 0x68: // PLA
841
282
    a = nz = READ_LOW( sp );
842
282
    sp = (sp - 0xFF) | 0x100;
843
282
    goto loop;
844
845
3.93k
  case 0x40:{// RTI
846
3.93k
    uint8_t temp = READ_LOW( sp );
847
3.93k
    pc  = READ_LOW( 0x100 | (sp - 0xFF) );
848
3.93k
    pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
849
3.93k
    sp = (sp - 0xFD) | 0x100;
850
3.93k
    data = status;
851
3.93k
    SET_STATUS( temp );
852
3.93k
    if ( !((data ^ status) & st_i) ) goto loop; // I flag didn't change
853
9
    this->r.status = status; // update externally-visible I flag
854
9
    blargg_long delta = s.base - irq_time_;
855
9
    if ( delta <= 0 ) goto loop;
856
0
    if ( status & st_i ) goto loop;
857
0
    s_time += delta;
858
0
    s.base = irq_time_;
859
0
    goto loop;
860
0
  }
861
862
2.38M
  case 0x28:{// PLP
863
2.38M
    uint8_t temp = READ_LOW( sp );
864
2.38M
    sp = (sp - 0xFF) | 0x100;
865
2.38M
    uint8_t changed = status ^ temp;
866
2.38M
    SET_STATUS( temp );
867
2.38M
    if ( !(changed & st_i) )
868
2.38M
      goto loop; // I flag didn't change
869
116
    if ( status & st_i )
870
57
      goto handle_sei;
871
59
    goto handle_cli;
872
116
  }
873
874
6.87M
  case 0x08: { // PHP
875
6.87M
    uint8_t temp;
876
6.87M
    CALC_STATUS( temp );
877
6.87M
    PUSH( temp | (st_b | st_r) );
878
6.87M
    goto loop;
879
116
  }
880
881
19
  case 0x6C:{// JMP (ind)
882
19
    data = GET_ADDR();
883
19
    check( unsigned (data - 0x2000) >= 0x4000 ); // ensure it's outside I/O space
884
19
    uint8_t const* page = s.code_map [data >> page_bits];
885
19
    pc = page [PAGE_OFFSET( data )];
886
19
    data = (data & 0xFF00) | ((data + 1) & 0xFF);
887
19
    pc |= page [PAGE_OFFSET( data )] << 8;
888
19
    goto loop;
889
116
  }
890
891
2.25G
  case 0x00: // BRK
892
2.25G
    goto handle_brk;
893
894
// Flags
895
896
1.46M
  case 0x38: // SEC
897
1.46M
    c = (uint16_t) ~0;
898
1.46M
    goto loop;
899
900
3.00M
  case 0x18: // CLC
901
3.00M
    c = 0;
902
3.00M
    goto loop;
903
904
364
  case 0xB8: // CLV
905
364
    status &= ~st_v;
906
364
    goto loop;
907
908
225
  case 0xD8: // CLD
909
225
    status &= ~st_d;
910
225
    goto loop;
911
912
72.0k
  case 0xF8: // SED
913
72.0k
    status |= st_d;
914
72.0k
    goto loop;
915
916
6.17M
  case 0x58: // CLI
917
6.17M
    if ( !(status & st_i) )
918
229k
      goto loop;
919
5.94M
    status &= ~st_i;
920
5.94M
  handle_cli: {
921
    //debug_printf( "CLI at %d\n", TIME );
922
5.94M
    this->r.status = status; // update externally-visible I flag
923
5.94M
    blargg_long delta = s.base - irq_time_;
924
5.94M
    if ( delta <= 0 )
925
5.94M
    {
926
5.94M
      if ( TIME < irq_time_ )
927
5.94M
        goto loop;
928
0
      goto delayed_cli;
929
5.94M
    }
930
0
    s.base = irq_time_;
931
0
    s_time += delta;
932
0
    if ( s_time < 0 )
933
0
      goto loop;
934
935
0
    if ( delta >= s_time + 1 )
936
0
    {
937
0
      s.base += s_time + 1;
938
0
      s_time = -1;
939
0
      goto loop;
940
0
    }
941
942
    // TODO: implement
943
0
  delayed_cli:
944
0
    debug_printf( "Delayed CLI not emulated\n" );
945
0
    goto loop;
946
0
  }
947
948
28.8k
  case 0x78: // SEI
949
28.8k
    if ( status & st_i )
950
28.8k
      goto loop;
951
0
    status |= st_i;
952
57
  handle_sei: {
953
57
    this->r.status = status; // update externally-visible I flag
954
57
    blargg_long delta = s.base - end_time_;
955
57
    s.base = end_time_;
956
57
    s_time += delta;
957
57
    if ( s_time < 0 )
958
57
      goto loop;
959
960
0
    debug_printf( "Delayed SEI not emulated\n" );
961
0
    goto loop;
962
57
  }
963
964
// Unofficial
965
966
  // SKW - Skip word
967
11.6M
  case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
968
11.6M
    HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/
969
27.8M
  case 0x0C:
970
27.8M
    pc++;/*FALLTHRU*/
971
  // SKB - Skip byte
972
46.8M
  case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64:
973
47.0M
  case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
974
47.0M
    pc++;
975
47.0M
    goto loop;
976
977
  // NOP
978
9.44M
  case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:
979
9.44M
    goto loop;
980
981
110M
  case bad_opcode: // HLT
982
110M
    pc--;
983
135M
  case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
984
135M
  case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2:
985
135M
    goto stop;
986
987
// Unimplemented
988
989
4.17M
  case 0xFF: // force 256-entry jump table for optimization purposes
990
4.17M
    c |= 1;/*FALLTHRU*/
991
56.6M
  default:
992
56.6M
    check( (unsigned) opcode <= 0xFF );
993
    // skip over proper number of bytes
994
56.6M
    static unsigned char const illop_lens [8] = {
995
56.6M
      0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0
996
56.6M
    };
997
56.6M
    uint8_t opcode = instr [-1];
998
56.6M
    int16_t len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3;
999
56.6M
    if ( opcode == 0x9C )
1000
1.77k
      len = 2;
1001
56.6M
    pc += len;
1002
56.6M
    error_count_++;
1003
1004
56.6M
    if ( (opcode >> 4) == 0x0B )
1005
730k
    {
1006
730k
      if ( opcode == 0xB3 )
1007
225
        data = READ_LOW( data );
1008
730k
      if ( opcode != 0xB7 )
1009
729k
        HANDLE_PAGE_CROSSING( data + y );
1010
730k
    }
1011
56.6M
    goto loop;
1012
2.82G
  }
1013
2.82G
  assert( false );
1014
1015
0
  int result_;
1016
2.25G
handle_brk:
1017
2.25G
  pc++;
1018
2.25G
  result_ = 4;
1019
1020
2.25G
interrupt:
1021
2.25G
  {
1022
2.25G
    s_time += 7;
1023
1024
2.25G
    WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
1025
2.25G
    WRITE_LOW( 0x100 | (sp - 2), pc );
1026
2.25G
    pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ );
1027
1028
2.25G
    sp = (sp - 3) | 0x100;
1029
2.25G
    uint8_t temp;
1030
2.25G
    CALC_STATUS( temp );
1031
2.25G
    temp |= st_r;
1032
2.25G
    if ( result_ )
1033
2.25G
      temp |= st_b; // TODO: incorrectly sets B flag for IRQ
1034
2.25G
    WRITE_LOW( sp, temp );
1035
1036
2.25G
    this->r.status = status |= st_i;
1037
2.25G
    blargg_long delta = s.base - end_time_;
1038
2.25G
    if ( delta >= 0 ) goto loop;
1039
0
    s_time += delta;
1040
0
    s.base = end_time_;
1041
0
    goto loop;
1042
2.25G
  }
1043
1044
16.2M
out_of_time:
1045
16.2M
  pc--;
1046
16.2M
  FLUSH_TIME();
1047
16.2M
  CPU_DONE( this, TIME, result_ );
1048
16.2M
  CACHE_TIME();
1049
16.2M
  if ( result_ >= 0 )
1050
0
    goto interrupt;
1051
16.2M
  if ( s_time < 0 )
1052
0
    goto loop;
1053
1054
152M
stop:
1055
1056
152M
  s.time = s_time;
1057
1058
152M
  r.pc = pc;
1059
152M
  r.sp = GET_SP();
1060
152M
  r.a = a;
1061
152M
  r.x = x;
1062
152M
  r.y = y;
1063
1064
152M
  {
1065
152M
    uint8_t temp;
1066
152M
    CALC_STATUS( temp );
1067
152M
    r.status = temp;
1068
152M
  }
1069
1070
152M
  this->state_ = s;
1071
152M
  this->state = &this->state_;
1072
1073
152M
  return s_time < 0;
1074
16.2M
}
1075