Coverage Report

Created: 2026-06-09 09:09

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
123M
#define FLUSH_TIME()    (void) (s.time = s_time)
26
123M
#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
13.3M
  #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
4.80M
  {\
39
4.80M
    FLUSH_TIME();\
40
4.80M
    out = CPU_READ( cpu, addr, time );\
41
4.80M
    CACHE_TIME();\
42
4.80M
  }
43
#endif
44
45
#if BLARGG_NONPORTABLE
46
  #define PAGE_OFFSET( addr ) (addr)
47
#else
48
2.61G
  #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
236k
{
53
236k
  state->code_map [i] = (uint8_t const*) p - PAGE_OFFSET( i * page_size );
54
236k
}
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
4.45k
{
69
4.45k
  check( state == &state_ );
70
4.45k
  state = &state_;
71
4.45k
  r.status = st_i;
72
4.45k
  r.sp = 0xFF;
73
4.45k
  r.pc = 0;
74
4.45k
  r.a  = 0;
75
4.45k
  r.x  = 0;
76
4.45k
  r.y  = 0;
77
4.45k
  state_.time = 0;
78
4.45k
  state_.base = 0;
79
4.45k
  irq_time_ = future_nes_time;
80
4.45k
  end_time_ = future_nes_time;
81
4.45k
  error_count_ = 0;
82
83
4.45k
  blaarg_static_assert( page_size == 0x800, "NES set to use unhandled page size" ); // assumes this
84
4.45k
  set_code_page( page_count, unmapped_page );
85
4.45k
  map_code( 0x2000, 0xE000, unmapped_page, true );
86
4.45k
  map_code( 0x0000, 0x2000, low_mem, true );
87
88
4.45k
  blargg_verify_byte_order();
89
4.45k
}
90
91
void Nes_Cpu::map_code( nes_addr_t start, unsigned size, void const* data, bool mirror )
92
48.9k
{
93
  // address range must begin and end on page boundaries
94
48.9k
  require( start % page_size == 0 );
95
48.9k
  require( size % page_size == 0 );
96
48.9k
  require( start + size <= 0x10000 );
97
98
48.9k
  unsigned page = start / page_size;
99
280k
  for ( unsigned n = size / page_size; n; --n )
100
231k
  {
101
231k
    set_code_page( page++, data );
102
231k
    if ( !mirror )
103
89.0k
      data = (char const*) data + page_size;
104
231k
  }
105
48.9k
}
106
107
5.94M
#define TIME    (s_time + s.base)
108
4.80M
#define READ_LIKELY_PPU( addr, out )    {CPU_READ_PPU( this, (addr), out, TIME );}
109
70.1M
#define READ( addr )                    CPU_READ( this, (addr), TIME )
110
44.4M
#define WRITE( addr, data )             {CPU_WRITE( this, (addr), (data), TIME );}
111
6.52G
#define READ_LOW( addr )        (low_mem [int (addr)])
112
6.39G
#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
113
7.01k
#define READ_PROG( addr )       (s.code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )])
114
115
123M
#define SET_SP( v )     (sp = ((v) + 1) | 0x100)
116
123M
#define GET_SP()        ((sp - 1) & 0xFF)
117
6.86M
#define PUSH( v )       ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
118
119
bool Nes_Cpu::run( nes_time_t end_time )
120
123M
{
121
123M
  set_end_time( end_time );
122
123M
  state_t s = this->state_;
123
123M
  this->state = &s;
124
  // even on x86, using s.time in place of s_time was slower
125
123M
  int16_t s_time = s.time;
126
127
  // registers
128
123M
  uint16_t pc = r.pc;
129
123M
  uint8_t a = r.a;
130
123M
  uint8_t x = r.x;
131
123M
  uint8_t y = r.y;
132
123M
  uint16_t sp;
133
123M
  SET_SP( r.sp );
134
135
  // status flags
136
123M
  #define IS_NEG (nz & 0x8080)
137
138
2.24G
  #define CALC_STATUS( out ) do {\
139
2.24G
    out = status & (st_v | st_d | st_i);\
140
2.24G
    out |= ((nz >> 8) | nz) & st_n;\
141
2.24G
    out |= c >> 8 & st_c;\
142
2.24G
    if ( !(nz & 0xFF) ) out |= st_z;\
143
2.24G
  } while ( 0 )
144
145
127M
  #define SET_STATUS( in ) do {\
146
127M
    status = in & (st_v | st_d | st_i);\
147
127M
    nz = in << 8;\
148
127M
    c = nz;\
149
127M
    nz |= ~in & st_z;\
150
127M
  } while ( 0 )
151
152
123M
  uint8_t status;
153
123M
  uint16_t c;  // carry set if (c & 0x100) != 0
154
123M
  uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
155
123M
  {
156
123M
    uint8_t temp = r.status;
157
123M
    SET_STATUS( temp );
158
123M
  }
159
160
123M
  goto loop;
161
1.55M
dec_clock_loop:
162
1.55M
  s_time--;
163
2.61G
loop:
164
165
2.61G
  check( (unsigned) GET_SP() < 0x100 );
166
2.61G
  check( (unsigned) pc < 0x10000 );
167
2.61G
  check( (unsigned) a < 0x100 );
168
2.61G
  check( (unsigned) x < 0x100 );
169
2.61G
  check( (unsigned) y < 0x100 );
170
2.61G
  check( -32768 <= s_time && s_time < 32767 );
171
172
2.61G
  uint8_t const* instr = s.code_map [pc >> page_bits];
173
2.61G
  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.61G
    instr += PAGE_OFFSET( pc );
182
2.61G
    opcode = *instr++;
183
2.61G
    pc++;
184
2.61G
  #endif
185
186
2.61G
  static uint8_t const clock_table [256] =
187
2.61G
  {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
188
2.61G
    0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
189
2.61G
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
190
2.61G
    6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
191
2.61G
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
192
2.61G
    6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
193
2.61G
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
194
2.61G
    6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
195
2.61G
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
196
2.61G
    2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
197
2.61G
    3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
198
2.61G
    2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
199
2.61G
    3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
200
2.61G
    2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
201
2.61G
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
202
2.61G
    2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
203
2.61G
    3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
204
2.61G
  }; // 0x00 was 7 and 0xF2 was 2
205
206
2.61G
  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.61G
  data = clock_table [opcode];
220
2.61G
  if ( (s_time += data) >= 0 )
221
14.8M
    goto possibly_out_of_time;
222
2.60G
almost_out_of_time:
223
224
2.60G
  data = *instr;
225
226
2.60G
  switch ( opcode )
227
2.60G
  {
228
14.8M
possibly_out_of_time:
229
14.8M
    if ( s_time < (int) data )
230
1.43M
      goto almost_out_of_time;
231
13.3M
    s_time -= data;
232
13.3M
    goto out_of_time;
233
0
#endif
234
235
// Macros
236
237
35.3M
#define GET_MSB()   (instr [1])
238
34.6M
#define ADD_PAGE()  (pc++, data += 0x100 * GET_MSB())
239
67.9M
#define GET_ADDR()  GET_LE16( instr )
240
241
0
#define NO_PAGE_CROSSING( lsb )
242
730k
#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8;
243
244
133k
#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
245
246
5.99M
#define IND_Y( cross, out ) {\
247
5.99M
    uint16_t temp = READ_LOW( data ) + y;\
248
5.99M
    out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
249
5.99M
    cross( temp );\
250
5.99M
  }
251
252
25.7M
#define IND_X( out ) {\
253
25.7M
    uint16_t temp = data + x;\
254
25.7M
    out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\
255
25.7M
  }
256
257
25.6M
#define ARITH_ADDR_MODES( op )\
258
25.6M
case op - 0x04: /* (ind,x) */\
259
25.6M
  IND_X( data )\
260
25.6M
  goto ptr##op;\
261
5.99M
case op + 0x0C: /* (ind),y */\
262
5.99M
  IND_Y( HANDLE_PAGE_CROSSING, data )\
263
6.51M
  goto ptr##op;\
264
6.51M
case op + 0x10: /* zp,X */\
265
41.7M
  data = uint8_t (data + x);/* FALLTHRU */\
266
41.7M
case op + 0x00: /* zp */\
267
41.7M
  data = READ_LOW( data );\
268
41.7M
  goto imm##op;\
269
7.44M
case op + 0x14: /* abs,Y */\
270
7.44M
  data += y;\
271
11.7M
  goto ind##op;\
272
11.7M
case op + 0x18: /* abs,X */\
273
19.1M
  data += x;\
274
19.1M
ind##op:\
275
28.3M
  HANDLE_PAGE_CROSSING( data );/* FALLTHRU */\
276
28.3M
case op + 0x08: /* abs */\
277
59.9M
  ADD_PAGE();\
278
59.9M
ptr##op:\
279
59.9M
  FLUSH_TIME();\
280
59.9M
  data = READ( data );\
281
72.3M
  CACHE_TIME();/*FALLTHRU*/\
282
114M
case op + 0x04: /* imm */\
283
114M
imm##op:
284
285
// TODO: more efficient way to handle negative branch that wraps PC around
286
7.14M
#define BRANCH( cond )\
287
7.14M
{\
288
7.14M
  int16_t offset = (int8_t) data;\
289
7.14M
  uint16_t extra_clock = (++pc & 0xFF) + offset;\
290
8.70M
  if ( !(cond) ) goto dec_clock_loop;\
291
7.14M
  pc = uint16_t (pc + offset);\
292
7.14M
  s_time += extra_clock >> 8 & 1;\
293
7.14M
  goto loop;\
294
7.14M
}
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
1.48M
  case 0xD0: // BNE
309
1.48M
    BRANCH( (uint8_t) nz );
310
311
24.2M
  case 0x20: { // JSR
312
24.2M
    uint16_t temp = pc + 1;
313
24.2M
    pc = GET_ADDR();
314
24.2M
    WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
315
24.2M
    sp = (sp - 2) | 0x100;
316
24.2M
    WRITE_LOW( sp, temp );
317
24.2M
    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
124k
  case 0xE8: // INX
325
124k
    INC_DEC_XY( x, 1 )
326
327
7.11M
  case 0x10: // BPL
328
7.11M
    BRANCH( !IS_NEG )
329
330
178k
  ARITH_ADDR_MODES( 0xC5 ) // CMP
331
178k
    nz = a - data;
332
178k
    pc++;
333
178k
    c = ~nz;
334
178k
    nz &= 0xFF;
335
178k
    goto loop;
336
337
44.9k
  case 0x30: // BMI
338
44.9k
    BRANCH( IS_NEG )
339
340
1.12k
  case 0xF0: // BEQ
341
1.12k
    BRANCH( !(uint8_t) nz );
342
343
635
  case 0x95: // STA zp,x
344
635
    data = uint8_t (data + x);/*FALLTHRU*/
345
1.55k
  case 0x85: // STA zp
346
1.55k
    pc++;
347
1.55k
    WRITE_LOW( data, a );
348
1.55k
    goto loop;
349
350
2.72k
  case 0xC8: // INY
351
2.72k
    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
229
  case 0x98: // TYA
359
229
    a  = y;
360
229
    nz = y;
361
229
    goto loop;
362
363
896
  case 0xAD:{// LDA abs
364
896
    unsigned addr = GET_ADDR();
365
896
    pc += 2;
366
896
    READ_LIKELY_PPU( addr, nz );
367
896
    a = nz;
368
896
    goto loop;
369
635
  }
370
371
8.00M
  case 0x60: // RTS
372
8.00M
    pc = 1 + READ_LOW( sp );
373
8.00M
    pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
374
8.00M
    sp = (sp - 0xFE) | 0x100;
375
8.00M
    goto loop;
376
377
0
  {
378
0
    uint16_t addr;
379
380
448
  case 0x99: // STA abs,Y
381
448
    addr = y + GET_ADDR();
382
448
    pc += 2;
383
448
    if ( addr <= 0x7FF )
384
223
    {
385
223
      WRITE_LOW( addr, a );
386
223
      goto loop;
387
223
    }
388
225
    goto sta_ptr;
389
390
35.6M
  case 0x8D: // STA abs
391
35.6M
    addr = GET_ADDR();
392
35.6M
    pc += 2;
393
35.6M
    if ( addr <= 0x7FF )
394
918k
    {
395
918k
      WRITE_LOW( addr, a );
396
918k
      goto loop;
397
918k
    }
398
34.7M
    goto sta_ptr;
399
400
34.7M
  case 0x9D: // STA abs,X (slightly more common than STA abs)
401
471
    addr = x + GET_ADDR();
402
471
    pc += 2;
403
471
    if ( addr <= 0x7FF )
404
225
    {
405
225
      WRITE_LOW( addr, a );
406
225
      goto loop;
407
225
    }
408
34.9M
  sta_ptr:
409
34.9M
    FLUSH_TIME();
410
34.9M
    WRITE( addr, a );
411
34.9M
    CACHE_TIME();
412
34.9M
    goto loop;
413
414
1.09k
  case 0x91: // STA (ind),Y
415
1.09k
    IND_Y( NO_PAGE_CROSSING, addr )
416
1.09k
    pc++;
417
1.09k
    goto sta_ptr;
418
419
124k
  case 0x81: // STA (ind,X)
420
124k
    IND_X( addr )
421
124k
    pc++;
422
124k
    goto sta_ptr;
423
424
471
  }
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
230
  case 0xB1:// LDA (ind),Y
442
230
    addr = READ_LOW( data ) + y;
443
230
    HANDLE_PAGE_CROSSING( addr );
444
230
    addr += 0x100 * READ_LOW( (uint8_t) (data + 1) );
445
230
    pc++;
446
230
    a = nz = READ_PROG( addr );
447
230
    if ( (addr ^ 0x8000) <= 0x9FFF )
448
230
      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
226
  case 0xBD: // LDA abs,X
461
226
    HANDLE_PAGE_CROSSING( data + x );
462
226
    addr = GET_ADDR() + x;
463
226
    pc += 2;
464
226
    a = nz = READ_PROG( addr );
465
226
    if ( (addr ^ 0x8000) <= 0x9FFF )
466
226
      goto loop;
467
3.46k
  a_nz_read_addr:
468
3.46k
    FLUSH_TIME();
469
3.46k
    a = nz = READ( addr );
470
3.46k
    CACHE_TIME();
471
3.46k
    goto loop;
472
473
226
  }
474
475
// Branch
476
477
32.1k
  case 0x50: // BVC
478
32.1k
    BRANCH( !(status & st_v) )
479
480
30.2k
  case 0x70: // BVS
481
30.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
9.56k
  case 0x94: // STY zp,x
492
9.56k
    data = uint8_t (data + x); // FALLTHRU
493
9.79k
  case 0x84: // STY zp
494
9.79k
    pc++;
495
9.79k
    WRITE_LOW( data, y );
496
9.79k
    goto loop;
497
498
271
  case 0x96: // STX zp,y
499
271
    data = uint8_t (data + y); // FALLTHRU
500
1.06k
  case 0x86: // STX zp
501
1.06k
    pc++;
502
1.06k
    WRITE_LOW( data, x );
503
1.06k
    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
892
  case 0xBC: // LDY abs,X
526
892
    data += x;
527
892
    HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
528
1.11k
  case 0xAC:{// LDY abs
529
1.11k
    unsigned addr = data + 0x100 * GET_MSB();
530
1.11k
    pc += 2;
531
1.11k
    FLUSH_TIME();
532
1.11k
    y = nz = READ( addr );
533
1.11k
    CACHE_TIME();
534
1.11k
    goto loop;
535
892
  }
536
537
225
  case 0xBE: // LDX abs,y
538
225
    data += y;
539
225
    HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
540
690k
  case 0xAE:{// LDX abs
541
690k
    unsigned addr = data + 0x100 * GET_MSB();
542
690k
    pc += 2;
543
690k
    FLUSH_TIME();
544
690k
    x = nz = READ( addr );
545
690k
    CACHE_TIME();
546
690k
    goto loop;
547
225
  }
548
549
0
  {
550
0
    uint8_t temp;
551
33.4k
  case 0x8C: // STY abs
552
33.4k
    temp = y;
553
33.4k
    goto store_abs;
554
555
5.80k
  case 0x8E: // STX abs
556
5.80k
    temp = x;
557
39.2k
  store_abs:
558
39.2k
    unsigned addr = GET_ADDR();
559
39.2k
    pc += 2;
560
39.2k
    if ( addr <= 0x7FF )
561
223
    {
562
223
      WRITE_LOW( addr, temp );
563
223
      goto loop;
564
223
    }
565
39.0k
    FLUSH_TIME();
566
39.0k
    WRITE( addr, temp );
567
39.0k
    CACHE_TIME();
568
39.0k
    goto loop;
569
39.2k
  }
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
39.2k
  }
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
249
  case 0xCC:{// CPY abs
593
249
    unsigned addr = GET_ADDR();
594
249
    pc++;
595
249
    FLUSH_TIME();
596
249
    data = READ( addr );
597
249
    CACHE_TIME();
598
249
    goto cpy_data;
599
124k
  }
600
601
44
  case 0xC4: // CPY zp
602
44
    data = READ_LOW( data );/*FALLTHRU*/
603
279
  case 0xC0: // CPY #imm
604
528
  cpy_data:
605
528
    nz = y - data;
606
528
    pc++;
607
528
    c = ~nz;
608
528
    nz &= 0xFF;
609
528
    goto loop;
610
611
// Logical
612
613
13.3M
  ARITH_ADDR_MODES( 0x25 ) // AND
614
13.3M
    nz = (a &= data);
615
13.3M
    pc++;
616
13.3M
    goto loop;
617
618
1.94M
  ARITH_ADDR_MODES( 0x45 ) // EOR
619
1.94M
    nz = (a ^= data);
620
1.94M
    pc++;
621
1.94M
    goto loop;
622
623
91.2M
  ARITH_ADDR_MODES( 0x05 ) // ORA
624
91.2M
    nz = (a |= data);
625
91.2M
    pc++;
626
91.2M
    goto loop;
627
628
4.80M
  case 0x2C:{// BIT abs
629
4.80M
    unsigned addr = GET_ADDR();
630
4.80M
    pc += 2;
631
4.80M
    status &= ~st_v;
632
4.80M
    READ_LIKELY_PPU( addr, nz );
633
4.80M
    status |= nz & st_v;
634
4.80M
    if ( a & nz )
635
4.80M
      goto loop;
636
1.34k
    nz <<= 8; // result must be zero, even if N bit is set
637
1.34k
    goto loop;
638
4.80M
  }
639
640
4.06M
  case 0x24: // BIT zp
641
4.06M
    nz = READ_LOW( data );
642
4.06M
    pc++;
643
4.06M
    status &= ~st_v;
644
4.06M
    status |= nz & st_v;
645
4.06M
    if ( a & nz )
646
4.06M
      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.4M
  ARITH_ADDR_MODES( 0xE5 ) // SBC
653
24.4M
  case 0xEB: // unofficial equivalent
654
4.15M
    data ^= 0xFF;
655
4.15M
    goto adc_imm;
656
657
11.1M
  ARITH_ADDR_MODES( 0x65 ) // ADC
658
11.1M
  adc_imm: {
659
7.30M
    int16_t carry = c >> 8 & 1;
660
7.30M
    int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
661
7.30M
    status &= ~st_v;
662
7.30M
    status |= ov >> 2 & 0x40;
663
7.30M
    c = nz = a + data + carry;
664
7.30M
    pc++;
665
7.30M
    a = (uint8_t) nz;
666
7.30M
    goto loop;
667
11.1M
  }
668
669
// Shift/rotate
670
671
151k
  case 0x4A: // LSR A
672
151k
    c = 0;/*FALLTHRU*/
673
153k
  case 0x6A: // ROR A
674
153k
    nz = c >> 1 & 0x80;
675
153k
    c = a << 8;
676
153k
    nz |= a >> 1;
677
153k
    a = nz;
678
153k
    goto loop;
679
680
3.22M
  case 0x0A: // ASL A
681
3.22M
    nz = a << 1;
682
3.22M
    c = nz;
683
3.22M
    a = (uint8_t) nz;
684
3.22M
    goto loop;
685
686
1.48M
  case 0x2A: { // ROL A
687
1.48M
    nz = a << 1;
688
1.48M
    int16_t temp = c >> 8 & 1;
689
1.48M
    c = nz;
690
1.48M
    nz |= temp;
691
1.48M
    a = (uint8_t) nz;
692
1.48M
    goto loop;
693
151k
  }
694
695
141k
  case 0x5E: // LSR abs,X
696
141k
    data += x;/*FALLTHRU*/
697
144k
  case 0x4E: // LSR abs
698
144k
    c = 0;/*FALLTHRU*/
699
371k
  case 0x6E: // ROR abs
700
496k
  ror_abs: {
701
496k
    ADD_PAGE();
702
496k
    FLUSH_TIME();
703
496k
    int temp = READ( data );
704
496k
    nz = (c >> 1 & 0x80) | (temp >> 1);
705
496k
    c = temp << 8;
706
496k
    goto rotate_common;
707
371k
  }
708
709
2.00M
  case 0x3E: // ROL abs,X
710
2.00M
    data += x;
711
2.00M
    goto rol_abs;
712
713
3.81M
  case 0x1E: // ASL abs,X
714
3.81M
    data += x;/*FALLTHRU*/
715
3.81M
  case 0x0E: // ASL abs
716
3.81M
    c = 0;/*FALLTHRU*/
717
3.84M
  case 0x2E: // ROL abs
718
5.85M
  rol_abs:
719
5.85M
    ADD_PAGE();
720
5.85M
    nz = c >> 8 & 1;
721
5.85M
    FLUSH_TIME();
722
5.85M
    nz |= (c = READ( data ) << 1);
723
6.34M
  rotate_common:
724
6.34M
    pc++;
725
6.34M
    WRITE( data, (uint8_t) nz );
726
6.34M
    CACHE_TIME();
727
6.34M
    goto loop;
728
729
124k
  case 0x7E: // ROR abs,X
730
124k
    data += x;
731
124k
    goto ror_abs;
732
733
124k
  case 0x76: // ROR zp,x
734
124k
    data = uint8_t (data + x);
735
124k
    goto ror_zp;
736
737
150k
  case 0x56: // LSR zp,x
738
150k
    data = uint8_t (data + x);/*FALLTHRU*/
739
1.58M
  case 0x46: // LSR zp
740
1.58M
    c = 0;/*FALLTHRU*/
741
1.70M
  case 0x66: // ROR zp
742
1.83M
  ror_zp: {
743
1.83M
    int temp = READ_LOW( data );
744
1.83M
    nz = (c >> 1 & 0x80) | (temp >> 1);
745
1.83M
    c = temp << 8;
746
1.83M
    goto write_nz_zp;
747
1.70M
  }
748
749
2.05M
  case 0x36: // ROL zp,x
750
2.05M
    data = uint8_t (data + x);
751
2.05M
    goto rol_zp;
752
753
3.10M
  case 0x16: // ASL zp,x
754
3.10M
    data = uint8_t (data + x);/*FALLTHRU*/
755
3.10M
  case 0x06: // ASL zp
756
3.10M
    c = 0;/*FALLTHRU*/
757
3.11M
  case 0x26: // ROL zp
758
5.17M
  rol_zp:
759
5.17M
    nz = c >> 8 & 1;
760
5.17M
    nz |= (c = READ_LOW( data ) << 1);
761
5.17M
    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
226
  case 0x88: // DEY
769
226
    INC_DEC_XY( y, -1 )
770
771
225
  case 0xF6: // INC zp,x
772
225
    data = uint8_t (data + x);/*FALLTHRU*/
773
384k
  case 0xE6: // INC zp
774
384k
    nz = 1;
775
384k
    goto add_nz_zp;
776
777
223
  case 0xD6: // DEC zp,x
778
223
    data = uint8_t (data + x);/*FALLTHRU*/
779
447
  case 0xC6: // DEC zp
780
447
    nz = (uint16_t) -1;
781
385k
  add_nz_zp:
782
385k
    nz += READ_LOW( data );
783
7.39M
  write_nz_zp:
784
7.39M
    pc++;
785
7.39M
    WRITE_LOW( data, nz );
786
7.39M
    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
225
  case 0xEE: // INC abs
793
225
    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
702
  case 0xAA: // TAX
817
702
    x  = a;
818
702
    nz = a;
819
702
    goto loop;
820
821
226
  case 0x8A: // TXA
822
226
    a  = x;
823
226
    nz = x;
824
226
    goto loop;
825
826
38
  case 0x9A: // TXS
827
38
    SET_SP( x ); // verified (no flag change)
828
38
    goto loop;
829
830
543
  case 0xBA: // TSX
831
543
    x = nz = GET_SP();
832
543
    goto loop;
833
834
// Stack
835
836
24.6k
  case 0x48: // PHA
837
24.6k
    PUSH( a ); // verified
838
24.6k
    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
2.59k
  case 0x40:{// RTI
846
2.59k
    uint8_t temp = READ_LOW( sp );
847
2.59k
    pc  = READ_LOW( 0x100 | (sp - 0xFF) );
848
2.59k
    pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
849
2.59k
    sp = (sp - 0xFD) | 0x100;
850
2.59k
    data = status;
851
2.59k
    SET_STATUS( temp );
852
2.59k
    if ( !((data ^ status) & st_i) ) goto loop; // I flag didn't change
853
5
    this->r.status = status; // update externally-visible I flag
854
5
    blargg_long delta = s.base - irq_time_;
855
5
    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
3.60M
  case 0x28:{// PLP
863
3.60M
    uint8_t temp = READ_LOW( sp );
864
3.60M
    sp = (sp - 0xFF) | 0x100;
865
3.60M
    uint8_t changed = status ^ temp;
866
3.60M
    SET_STATUS( temp );
867
3.60M
    if ( !(changed & st_i) )
868
3.60M
      goto loop; // I flag didn't change
869
102
    if ( status & st_i )
870
51
      goto handle_sei;
871
51
    goto handle_cli;
872
102
  }
873
874
6.84M
  case 0x08: { // PHP
875
6.84M
    uint8_t temp;
876
6.84M
    CALC_STATUS( temp );
877
6.84M
    PUSH( temp | (st_b | st_r) );
878
6.84M
    goto loop;
879
102
  }
880
881
17
  case 0x6C:{// JMP (ind)
882
17
    data = GET_ADDR();
883
17
    check( unsigned (data - 0x2000) >= 0x4000 ); // ensure it's outside I/O space
884
17
    uint8_t const* page = s.code_map [data >> page_bits];
885
17
    pc = page [PAGE_OFFSET( data )];
886
17
    data = (data & 0xFF00) | ((data + 1) & 0xFF);
887
17
    pc |= page [PAGE_OFFSET( data )] << 8;
888
17
    goto loop;
889
102
  }
890
891
2.10G
  case 0x00: // BRK
892
2.10G
    goto handle_brk;
893
894
// Flags
895
896
2.92M
  case 0x38: // SEC
897
2.92M
    c = (uint16_t) ~0;
898
2.92M
    goto loop;
899
900
4.48M
  case 0x18: // CLC
901
4.48M
    c = 0;
902
4.48M
    goto loop;
903
904
364
  case 0xB8: // CLV
905
364
    status &= ~st_v;
906
364
    goto loop;
907
908
223
  case 0xD8: // CLD
909
223
    status &= ~st_d;
910
223
    goto loop;
911
912
551
  case 0xF8: // SED
913
551
    status |= st_d;
914
551
    goto loop;
915
916
6.06M
  case 0x58: // CLI
917
6.06M
    if ( !(status & st_i) )
918
125k
      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
151k
  case 0x78: // SEI
949
151k
    if ( status & st_i )
950
151k
      goto loop;
951
0
    status |= st_i;
952
51
  handle_sei: {
953
51
    this->r.status = status; // update externally-visible I flag
954
51
    blargg_long delta = s.base - end_time_;
955
51
    s.base = end_time_;
956
51
    s_time += delta;
957
51
    if ( s_time < 0 )
958
51
      goto loop;
959
960
0
    debug_printf( "Delayed SEI not emulated\n" );
961
0
    goto loop;
962
51
  }
963
964
// Unofficial
965
966
  // SKW - Skip word
967
12.5M
  case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
968
12.5M
    HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/
969
27.3M
  case 0x0C:
970
27.3M
    pc++;/*FALLTHRU*/
971
  // SKB - Skip byte
972
49.5M
  case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64:
973
49.6M
  case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
974
49.6M
    pc++;
975
49.6M
    goto loop;
976
977
  // NOP
978
15.1M
  case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:
979
15.1M
    goto loop;
980
981
78.4M
  case bad_opcode: // HLT
982
78.4M
    pc--;
983
110M
  case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
984
110M
  case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2:
985
110M
    goto stop;
986
987
// Unimplemented
988
989
4.18M
  case 0xFF: // force 256-entry jump table for optimization purposes
990
4.18M
    c |= 1;/*FALLTHRU*/
991
68.7M
  default:
992
68.7M
    check( (unsigned) opcode <= 0xFF );
993
    // skip over proper number of bytes
994
68.7M
    static unsigned char const illop_lens [8] = {
995
68.7M
      0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0
996
68.7M
    };
997
68.7M
    uint8_t opcode = instr [-1];
998
68.7M
    int16_t len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3;
999
68.7M
    if ( opcode == 0x9C )
1000
448
      len = 2;
1001
68.7M
    pc += len;
1002
68.7M
    error_count_++;
1003
1004
68.7M
    if ( (opcode >> 4) == 0x0B )
1005
730k
    {
1006
730k
      if ( opcode == 0xB3 )
1007
223
        data = READ_LOW( data );
1008
730k
      if ( opcode != 0xB7 )
1009
730k
        HANDLE_PAGE_CROSSING( data + y );
1010
730k
    }
1011
68.7M
    goto loop;
1012
2.60G
  }
1013
2.60G
  assert( false );
1014
1015
0
  int result_;
1016
2.10G
handle_brk:
1017
2.10G
  pc++;
1018
2.10G
  result_ = 4;
1019
1020
2.10G
interrupt:
1021
2.10G
  {
1022
2.10G
    s_time += 7;
1023
1024
2.10G
    WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
1025
2.10G
    WRITE_LOW( 0x100 | (sp - 2), pc );
1026
2.10G
    pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ );
1027
1028
2.10G
    sp = (sp - 3) | 0x100;
1029
2.10G
    uint8_t temp;
1030
2.10G
    CALC_STATUS( temp );
1031
2.10G
    temp |= st_r;
1032
2.10G
    if ( result_ )
1033
2.10G
      temp |= st_b; // TODO: incorrectly sets B flag for IRQ
1034
2.10G
    WRITE_LOW( sp, temp );
1035
1036
2.10G
    this->r.status = status |= st_i;
1037
2.10G
    blargg_long delta = s.base - end_time_;
1038
2.10G
    if ( delta >= 0 ) goto loop;
1039
0
    s_time += delta;
1040
0
    s.base = end_time_;
1041
0
    goto loop;
1042
2.10G
  }
1043
1044
13.3M
out_of_time:
1045
13.3M
  pc--;
1046
13.3M
  FLUSH_TIME();
1047
13.3M
  CPU_DONE( this, TIME, result_ );
1048
13.3M
  CACHE_TIME();
1049
13.3M
  if ( result_ >= 0 )
1050
0
    goto interrupt;
1051
13.3M
  if ( s_time < 0 )
1052
0
    goto loop;
1053
1054
123M
stop:
1055
1056
123M
  s.time = s_time;
1057
1058
123M
  r.pc = pc;
1059
123M
  r.sp = GET_SP();
1060
123M
  r.a = a;
1061
123M
  r.x = x;
1062
123M
  r.y = y;
1063
1064
123M
  {
1065
123M
    uint8_t temp;
1066
123M
    CALC_STATUS( temp );
1067
123M
    r.status = temp;
1068
123M
  }
1069
1070
123M
  this->state_ = s;
1071
123M
  this->state = &this->state_;
1072
1073
123M
  return s_time < 0;
1074
13.3M
}
1075