Coverage Report

Created: 2026-08-14 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/contrib/contrib-build/game-music-emu/gme/Sap_Cpu.cpp
Line
Count
Source
1
// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
2
3
#include "Sap_Cpu.h"
4
5
#include <limits.h>
6
#include "blargg_endian.h"
7
8
//#include "nes_cpu_log.h"
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
0
#define FLUSH_TIME()    (void) (s.time = s_time)
22
0
#define CACHE_TIME()    (void) (s_time = s.time)
23
24
#include "sap_cpu_io.h"
25
26
#ifndef CPU_DONE
27
0
  #define CPU_DONE( cpu, time, result_out )   { result_out = -1; }
28
#endif
29
30
#include "blargg_source.h"
31
32
enum {
33
    st_n = 0x80,
34
    st_v = 0x40,
35
    st_r = 0x20,
36
    st_b = 0x10,
37
    st_d = 0x08,
38
    st_i = 0x04,
39
    st_z = 0x02,
40
    st_c = 0x01
41
};
42
43
void Sap_Cpu::reset( void* new_mem )
44
0
{
45
0
  check( state == &state_ );
46
0
  state = &state_;
47
0
  mem = (uint8_t*) new_mem;
48
0
  r.status = st_i;
49
0
  r.sp = 0xFF;
50
0
  r.pc = 0;
51
0
  r.a  = 0;
52
0
  r.x  = 0;
53
0
  r.y  = 0;
54
0
  state_.time = 0;
55
0
  state_.base = 0;
56
0
  irq_time_ = future_sap_time;
57
0
  end_time_ = future_sap_time;
58
59
0
  blargg_verify_byte_order();
60
0
}
61
62
0
#define TIME                    (s_time + s.base)
63
0
#define READ( addr )            CPU_READ( this, (addr), TIME )
64
0
#define WRITE( addr, data )     {CPU_WRITE( this, (addr), (data), TIME );}
65
0
#define READ_LOW( addr )        (mem [int (addr)])
66
0
#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
67
0
#define READ_PROG( addr )       (READ_LOW( addr ))
68
69
0
#define SET_SP( v )     (sp = ((v) + 1) | 0x100)
70
0
#define GET_SP()        ((sp - 1) & 0xFF)
71
0
#define PUSH( v )       ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
72
73
bool Sap_Cpu::run( sap_time_t end_time )
74
0
{
75
0
  bool illegal_encountered = false;
76
0
  set_end_time( end_time );
77
0
  state_t s = this->state_;
78
0
  this->state = &s;
79
0
  int32_t s_time = s.time;
80
0
  uint8_t* const mem = this->mem; // cache
81
82
  // registers
83
0
  uint16_t pc = r.pc;
84
0
  uint8_t a = r.a;
85
0
  uint8_t x = r.x;
86
0
  uint8_t y = r.y;
87
0
  uint16_t sp;
88
0
  SET_SP( r.sp );
89
90
  // status flags
91
0
  #define IS_NEG (nz & 0x8080)
92
93
0
  #define CALC_STATUS( out ) do {\
94
0
    out = status & (st_v | st_d | st_i);\
95
0
    out |= ((nz >> 8) | nz) & st_n;\
96
0
    out |= c >> 8 & st_c;\
97
0
    if ( !(nz & 0xFF) ) out |= st_z;\
98
0
  } while ( 0 )
99
100
0
  #define SET_STATUS( in ) do {\
101
0
    status = in & (st_v | st_d | st_i);\
102
0
    nz = in << 8;\
103
0
    c = nz;\
104
0
    nz |= ~in & st_z;\
105
0
  } while ( 0 )
106
107
0
  uint8_t status;
108
0
  uint16_t c;  // carry set if (c & 0x100) != 0
109
0
  uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
110
0
  {
111
0
    uint8_t temp = r.status;
112
0
    SET_STATUS( temp );
113
0
  }
114
115
0
  goto loop;
116
0
dec_clock_loop:
117
0
  s_time--;
118
0
loop:
119
120
  #ifndef NDEBUG
121
  {
122
    sap_time_t correct = end_time_;
123
    if ( !(status & st_i) && correct > irq_time_ )
124
      correct = irq_time_;
125
    check( s.base == correct );
126
  }
127
  #endif
128
129
0
  check( (unsigned) GET_SP() < 0x100 );
130
0
  check( (unsigned) a < 0x100 );
131
0
  check( (unsigned) x < 0x100 );
132
0
  check( (unsigned) y < 0x100 );
133
134
0
  uint8_t opcode = mem [pc];
135
0
  pc++;
136
0
  uint8_t const* instr = mem + pc;
137
138
0
  static uint8_t const clock_table [256] =
139
0
  {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
140
0
    0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
141
0
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
142
0
    6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
143
0
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
144
0
    6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
145
0
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
146
0
    6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
147
0
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
148
0
    2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
149
0
    3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
150
0
    2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
151
0
    3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
152
0
    2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
153
0
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
154
0
    2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
155
0
    3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
156
0
  }; // 0x00 was 7
157
158
0
  uint16_t data;
159
0
  data = clock_table [opcode];
160
0
  if ( (s_time += data) >= 0 )
161
0
    goto possibly_out_of_time;
162
0
almost_out_of_time:
163
164
0
  data = *instr;
165
166
  #ifdef NES_CPU_LOG_H
167
    nes_cpu_log( "cpu_log", pc - 1, opcode, instr [0], instr [1] );
168
  #endif
169
170
0
  switch ( opcode )
171
0
  {
172
0
possibly_out_of_time:
173
0
    if ( s_time < (int) data )
174
0
      goto almost_out_of_time;
175
0
    s_time -= data;
176
0
    goto out_of_time;
177
178
// Macros
179
180
0
#define GET_MSB()   (instr [1])
181
0
#define ADD_PAGE()  (pc++, data += 0x100 * GET_MSB())
182
0
#define GET_ADDR()  GET_LE16( instr )
183
184
0
#define NO_PAGE_CROSSING( lsb )
185
0
#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8;
186
187
0
#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
188
189
0
#define IND_Y( cross, out ) {\
190
0
    uint16_t temp = READ_LOW( data ) + y;\
191
0
    out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
192
0
    cross( temp );\
193
0
  }
194
195
0
#define IND_X( out ) {\
196
0
    uint16_t temp = data + x;\
197
0
    out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\
198
0
  }
199
200
0
#define ARITH_ADDR_MODES( op )\
201
0
case op - 0x04: /* (ind,x) */\
202
0
  IND_X( data )\
203
0
  goto ptr##op;\
204
0
case op + 0x0C: /* (ind),y */\
205
0
  IND_Y( HANDLE_PAGE_CROSSING, data )\
206
0
  goto ptr##op;\
207
0
case op + 0x10: /* zp,X */\
208
0
  data = uint8_t (data + x);/*FALLTHRU*/\
209
0
case op + 0x00: /* zp */\
210
0
  data = READ_LOW( data );\
211
0
  goto imm##op;\
212
0
case op + 0x14: /* abs,Y */\
213
0
  data += y;\
214
0
  goto ind##op;\
215
0
case op + 0x18: /* abs,X */\
216
0
  data += x;\
217
0
ind##op:\
218
0
  HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/\
219
0
case op + 0x08: /* abs */\
220
0
  ADD_PAGE();\
221
0
ptr##op:\
222
0
  FLUSH_TIME();\
223
0
  data = READ( data );\
224
0
  CACHE_TIME();/*FALLTHRU*/\
225
0
case op + 0x04: /* imm */\
226
0
imm##op:
227
228
// TODO: more efficient way to handle negative branch that wraps PC around
229
0
#define BRANCH( cond )\
230
0
{\
231
0
  int16_t offset = (int8_t) data;\
232
0
  uint16_t extra_clock = (++pc & 0xFF) + offset;\
233
0
  if ( !(cond) ) goto dec_clock_loop;\
234
0
  pc += offset;\
235
0
  s_time += extra_clock >> 8 & 1;\
236
0
  goto loop;\
237
0
}
238
239
// Often-Used
240
241
0
  case 0xB5: // LDA zp,x
242
0
    a = nz = READ_LOW( uint8_t (data + x) );
243
0
    pc++;
244
0
    goto loop;
245
246
0
  case 0xA5: // LDA zp
247
0
    a = nz = READ_LOW( data );
248
0
    pc++;
249
0
    goto loop;
250
251
0
  case 0xD0: // BNE
252
0
    BRANCH( (uint8_t) nz );
253
254
0
  case 0x20: { // JSR
255
0
    uint16_t temp = pc + 1;
256
0
    pc = GET_ADDR();
257
0
    WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
258
0
    sp = (sp - 2) | 0x100;
259
0
    WRITE_LOW( sp, temp );
260
0
    goto loop;
261
0
  }
262
263
0
  case 0x4C: // JMP abs
264
0
    pc = GET_ADDR();
265
0
    goto loop;
266
267
0
  case 0xE8: // INX
268
0
    INC_DEC_XY( x, 1 )
269
270
0
  case 0x10: // BPL
271
0
    BRANCH( !IS_NEG )
272
273
0
  ARITH_ADDR_MODES( 0xC5 ) // CMP
274
0
    nz = a - data;
275
0
    pc++;
276
0
    c = ~nz;
277
0
    nz &= 0xFF;
278
0
    goto loop;
279
280
0
  case 0x30: // BMI
281
0
    BRANCH( IS_NEG )
282
283
0
  case 0xF0: // BEQ
284
0
    BRANCH( !(uint8_t) nz );
285
286
0
  case 0x95: // STA zp,x
287
0
    data = uint8_t (data + x);/*FALLTHRU*/
288
0
  case 0x85: // STA zp
289
0
    pc++;
290
0
    WRITE_LOW( data, a );
291
0
    goto loop;
292
293
0
  case 0xC8: // INY
294
0
    INC_DEC_XY( y, 1 )
295
296
0
  case 0xA8: // TAY
297
0
    y  = a;
298
0
    nz = a;
299
0
    goto loop;
300
301
0
  case 0x98: // TYA
302
0
    a  = y;
303
0
    nz = y;
304
0
    goto loop;
305
306
0
  case 0xAD:{// LDA abs
307
0
    unsigned addr = GET_ADDR();
308
0
    pc += 2;
309
0
    nz = READ( addr );
310
0
    a = nz;
311
0
    goto loop;
312
0
  }
313
314
0
  case 0x60: // RTS
315
0
    pc = 1 + READ_LOW( sp );
316
0
    pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
317
0
    sp = (sp - 0xFE) | 0x100;
318
0
    goto loop;
319
320
0
  {
321
0
    uint16_t addr;
322
323
0
  case 0x99: // STA abs,Y
324
0
    addr = y + GET_ADDR();
325
0
    pc += 2;
326
0
    if ( addr <= 0x7FF )
327
0
    {
328
0
      WRITE_LOW( addr, a );
329
0
      goto loop;
330
0
    }
331
0
    goto sta_ptr;
332
333
0
  case 0x8D: // STA abs
334
0
    addr = GET_ADDR();
335
0
    pc += 2;
336
0
    if ( addr <= 0x7FF )
337
0
    {
338
0
      WRITE_LOW( addr, a );
339
0
      goto loop;
340
0
    }
341
0
    goto sta_ptr;
342
343
0
  case 0x9D: // STA abs,X (slightly more common than STA abs)
344
0
    addr = x + GET_ADDR();
345
0
    pc += 2;
346
0
    if ( addr <= 0x7FF )
347
0
    {
348
0
      WRITE_LOW( addr, a );
349
0
      goto loop;
350
0
    }
351
0
  sta_ptr:
352
0
    FLUSH_TIME();
353
0
    WRITE( addr, a );
354
0
    CACHE_TIME();
355
0
    goto loop;
356
357
0
  case 0x91: // STA (ind),Y
358
0
    IND_Y( NO_PAGE_CROSSING, addr )
359
0
    pc++;
360
0
    goto sta_ptr;
361
362
0
  case 0x81: // STA (ind,X)
363
0
    IND_X( addr )
364
0
    pc++;
365
0
    goto sta_ptr;
366
367
0
  }
368
369
0
  case 0xA9: // LDA #imm
370
0
    pc++;
371
0
    a  = data;
372
0
    nz = data;
373
0
    goto loop;
374
375
  // common read instructions
376
0
  {
377
0
    uint16_t addr;
378
379
0
  case 0xA1: // LDA (ind,X)
380
0
    IND_X( addr )
381
0
    pc++;
382
0
    goto a_nz_read_addr;
383
384
0
  case 0xB1:// LDA (ind),Y
385
0
    addr = READ_LOW( data ) + y;
386
0
    HANDLE_PAGE_CROSSING( addr );
387
0
    addr += 0x100 * READ_LOW( (uint8_t) (data + 1) );
388
0
    pc++;
389
0
    a = nz = READ_PROG( addr );
390
0
    if ( (addr ^ 0x8000) <= 0x9FFF )
391
0
      goto loop;
392
0
    goto a_nz_read_addr;
393
394
0
  case 0xB9: // LDA abs,Y
395
0
    HANDLE_PAGE_CROSSING( data + y );
396
0
    addr = GET_ADDR() + y;
397
0
    pc += 2;
398
0
    a = nz = READ_PROG( addr );
399
0
    if ( (addr ^ 0x8000) <= 0x9FFF )
400
0
      goto loop;
401
0
    goto a_nz_read_addr;
402
403
0
  case 0xBD: // LDA abs,X
404
0
    HANDLE_PAGE_CROSSING( data + x );
405
0
    addr = GET_ADDR() + x;
406
0
    pc += 2;
407
0
    a = nz = READ_PROG( addr );
408
0
    if ( (addr ^ 0x8000) <= 0x9FFF )
409
0
      goto loop;
410
0
  a_nz_read_addr:
411
0
    FLUSH_TIME();
412
0
    a = nz = READ( addr );
413
0
    CACHE_TIME();
414
0
    goto loop;
415
416
0
  }
417
418
// Branch
419
420
0
  case 0x50: // BVC
421
0
    BRANCH( !(status & st_v) )
422
423
0
  case 0x70: // BVS
424
0
    BRANCH( status & st_v )
425
426
0
  case 0xB0: // BCS
427
0
    BRANCH( c & 0x100 )
428
429
0
  case 0x90: // BCC
430
0
    BRANCH( !(c & 0x100) )
431
432
// Load/store
433
434
0
  case 0x94: // STY zp,x
435
0
    data = uint8_t (data + x);/*FALLTHRU*/
436
0
  case 0x84: // STY zp
437
0
    pc++;
438
0
    WRITE_LOW( data, y );
439
0
    goto loop;
440
441
0
  case 0x96: // STX zp,y
442
0
    data = uint8_t (data + y);/*FALLTHRU*/
443
0
  case 0x86: // STX zp
444
0
    pc++;
445
0
    WRITE_LOW( data, x );
446
0
    goto loop;
447
448
0
  case 0xB6: // LDX zp,y
449
0
    data = uint8_t (data + y);/*FALLTHRU*/
450
0
  case 0xA6: // LDX zp
451
0
    data = READ_LOW( data );/*FALLTHRU*/
452
0
  case 0xA2: // LDX #imm
453
0
    pc++;
454
0
    x = data;
455
0
    nz = data;
456
0
    goto loop;
457
458
0
  case 0xB4: // LDY zp,x
459
0
    data = uint8_t (data + x);/*FALLTHRU*/
460
0
  case 0xA4: // LDY zp
461
0
    data = READ_LOW( data );/*FALLTHRU*/
462
0
  case 0xA0: // LDY #imm
463
0
    pc++;
464
0
    y = data;
465
0
    nz = data;
466
0
    goto loop;
467
468
0
  case 0xBC: // LDY abs,X
469
0
    data += x;
470
0
    HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
471
0
  case 0xAC:{// LDY abs
472
0
    unsigned addr = data + 0x100 * GET_MSB();
473
0
    pc += 2;
474
0
    FLUSH_TIME();
475
0
    y = nz = READ( addr );
476
0
    CACHE_TIME();
477
0
    goto loop;
478
0
  }
479
480
0
  case 0xBE: // LDX abs,y
481
0
    data += y;
482
0
    HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
483
0
  case 0xAE:{// LDX abs
484
0
    unsigned addr = data + 0x100 * GET_MSB();
485
0
    pc += 2;
486
0
    FLUSH_TIME();
487
0
    x = nz = READ( addr );
488
0
    CACHE_TIME();
489
0
    goto loop;
490
0
  }
491
492
0
  {
493
0
    uint8_t temp;
494
0
  case 0x8C: // STY abs
495
0
    temp = y;
496
0
    goto store_abs;
497
498
0
  case 0x8E: // STX abs
499
0
    temp = x;
500
0
  store_abs:
501
0
    unsigned addr = GET_ADDR();
502
0
    pc += 2;
503
0
    if ( addr <= 0x7FF )
504
0
    {
505
0
      WRITE_LOW( addr, temp );
506
0
      goto loop;
507
0
    }
508
0
    FLUSH_TIME();
509
0
    WRITE( addr, temp );
510
0
    CACHE_TIME();
511
0
    goto loop;
512
0
  }
513
514
// Compare
515
516
0
  case 0xEC:{// CPX abs
517
0
    unsigned addr = GET_ADDR();
518
0
    pc++;
519
0
    FLUSH_TIME();
520
0
    data = READ( addr );
521
0
    CACHE_TIME();
522
0
    goto cpx_data;
523
0
  }
524
525
0
  case 0xE4: // CPX zp
526
0
    data = READ_LOW( data );/*FALLTHRU*/
527
0
  case 0xE0: // CPX #imm
528
0
  cpx_data:
529
0
    nz = x - data;
530
0
    pc++;
531
0
    c = ~nz;
532
0
    nz &= 0xFF;
533
0
    goto loop;
534
535
0
  case 0xCC:{// CPY abs
536
0
    unsigned addr = GET_ADDR();
537
0
    pc++;
538
0
    FLUSH_TIME();
539
0
    data = READ( addr );
540
0
    CACHE_TIME();
541
0
    goto cpy_data;
542
0
  }
543
544
0
  case 0xC4: // CPY zp
545
0
    data = READ_LOW( data ); // FALLTHRU
546
0
  case 0xC0: // CPY #imm
547
0
  cpy_data:
548
0
    nz = y - data;
549
0
    pc++;
550
0
    c = ~nz;
551
0
    nz &= 0xFF;
552
0
    goto loop;
553
554
// Logical
555
556
0
  ARITH_ADDR_MODES( 0x25 ) // AND
557
0
    nz = (a &= data);
558
0
    pc++;
559
0
    goto loop;
560
561
0
  ARITH_ADDR_MODES( 0x45 ) // EOR
562
0
    nz = (a ^= data);
563
0
    pc++;
564
0
    goto loop;
565
566
0
  ARITH_ADDR_MODES( 0x05 ) // ORA
567
0
    nz = (a |= data);
568
0
    pc++;
569
0
    goto loop;
570
571
0
  case 0x2C:{// BIT abs
572
0
    unsigned addr = GET_ADDR();
573
0
    pc += 2;
574
0
    status &= ~st_v;
575
0
    nz = READ( addr );
576
0
    status |= nz & st_v;
577
0
    if ( a & nz )
578
0
      goto loop;
579
0
    nz <<= 8; // result must be zero, even if N bit is set
580
0
    goto loop;
581
0
  }
582
583
0
  case 0x24: // BIT zp
584
0
    nz = READ_LOW( data );
585
0
    pc++;
586
0
    status &= ~st_v;
587
0
    status |= nz & st_v;
588
0
    if ( a & nz )
589
0
      goto loop;
590
0
    nz <<= 8; // result must be zero, even if N bit is set
591
0
    goto loop;
592
593
// Add/subtract
594
595
0
  ARITH_ADDR_MODES( 0xE5 ) // SBC
596
0
  case 0xEB: // unofficial equivalent
597
0
    data ^= 0xFF;
598
0
    goto adc_imm;
599
600
0
  ARITH_ADDR_MODES( 0x65 ) // ADC
601
0
  adc_imm: {
602
0
    check( !(status & st_d) );
603
0
    int16_t carry = c >> 8 & 1;
604
0
    int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
605
0
    status &= ~st_v;
606
0
    status |= ov >> 2 & 0x40;
607
0
    c = nz = a + data + carry;
608
0
    pc++;
609
0
    a = (uint8_t) nz;
610
0
    goto loop;
611
0
  }
612
613
// Shift/rotate
614
615
0
  case 0x4A: // LSR A
616
0
    c = 0;/*FALLTHRU*/
617
0
  case 0x6A: // ROR A
618
0
    nz = c >> 1 & 0x80;
619
0
    c = a << 8;
620
0
    nz |= a >> 1;
621
0
    a = nz;
622
0
    goto loop;
623
624
0
  case 0x0A: // ASL A
625
0
    nz = a << 1;
626
0
    c = nz;
627
0
    a = (uint8_t) nz;
628
0
    goto loop;
629
630
0
  case 0x2A: { // ROL A
631
0
    nz = a << 1;
632
0
    int16_t temp = c >> 8 & 1;
633
0
    c = nz;
634
0
    nz |= temp;
635
0
    a = (uint8_t) nz;
636
0
    goto loop;
637
0
  }
638
639
0
  case 0x5E: // LSR abs,X
640
0
    data += x;/*FALLTHRU*/
641
0
  case 0x4E: // LSR abs
642
0
    c = 0;/*FALLTHRU*/
643
0
  case 0x6E: // ROR abs
644
0
  ror_abs: {
645
0
    ADD_PAGE();
646
0
    FLUSH_TIME();
647
0
    int temp = READ( data );
648
0
    nz = (c >> 1 & 0x80) | (temp >> 1);
649
0
    c = temp << 8;
650
0
    goto rotate_common;
651
0
  }
652
653
0
  case 0x3E: // ROL abs,X
654
0
    data += x;
655
0
    goto rol_abs;
656
657
0
  case 0x1E: // ASL abs,X
658
0
    data += x;/*FALLTHRU*/
659
0
  case 0x0E: // ASL abs
660
0
    c = 0;/*FALLTHRU*/
661
0
  case 0x2E: // ROL abs
662
0
  rol_abs:
663
0
    ADD_PAGE();
664
0
    nz = c >> 8 & 1;
665
0
    FLUSH_TIME();
666
0
    nz |= (c = READ( data ) << 1);
667
0
  rotate_common:
668
0
    pc++;
669
0
    WRITE( data, (uint8_t) nz );
670
0
    CACHE_TIME();
671
0
    goto loop;
672
673
0
  case 0x7E: // ROR abs,X
674
0
    data += x;
675
0
    goto ror_abs;
676
677
0
  case 0x76: // ROR zp,x
678
0
    data = uint8_t (data + x);
679
0
    goto ror_zp;
680
681
0
  case 0x56: // LSR zp,x
682
0
    data = uint8_t (data + x);/*FALLTHRU*/
683
0
  case 0x46: // LSR zp
684
0
    c = 0;/*FALLTHRU*/
685
0
  case 0x66: // ROR zp
686
0
  ror_zp: {
687
0
    int temp = READ_LOW( data );
688
0
    nz = (c >> 1 & 0x80) | (temp >> 1);
689
0
    c = temp << 8;
690
0
    goto write_nz_zp;
691
0
  }
692
693
0
  case 0x36: // ROL zp,x
694
0
    data = uint8_t (data + x);
695
0
    goto rol_zp;
696
697
0
  case 0x16: // ASL zp,x
698
0
    data = uint8_t (data + x);/*FALLTHRU*/
699
0
  case 0x06: // ASL zp
700
0
    c = 0;/*FALLTHRU*/
701
0
  case 0x26: // ROL zp
702
0
  rol_zp:
703
0
    nz = c >> 8 & 1;
704
0
    nz |= (c = READ_LOW( data ) << 1);
705
0
    goto write_nz_zp;
706
707
// Increment/decrement
708
709
0
  case 0xCA: // DEX
710
0
    INC_DEC_XY( x, -1 )
711
712
0
  case 0x88: // DEY
713
0
    INC_DEC_XY( y, -1 )
714
715
0
  case 0xF6: // INC zp,x
716
0
    data = uint8_t (data + x);/*FALLTHRU*/
717
0
  case 0xE6: // INC zp
718
0
    nz = 1;
719
0
    goto add_nz_zp;
720
721
0
  case 0xD6: // DEC zp,x
722
0
    data = uint8_t (data + x);/*FALLTHRU*/
723
0
  case 0xC6: // DEC zp
724
0
    nz = (uint16_t) -1;
725
0
  add_nz_zp:
726
0
    nz += READ_LOW( data );
727
0
  write_nz_zp:
728
0
    pc++;
729
0
    WRITE_LOW( data, nz );
730
0
    goto loop;
731
732
0
  case 0xFE: // INC abs,x
733
0
    data = x + GET_ADDR();
734
0
    goto inc_ptr;
735
736
0
  case 0xEE: // INC abs
737
0
    data = GET_ADDR();
738
0
  inc_ptr:
739
0
    nz = 1;
740
0
    goto inc_common;
741
742
0
  case 0xDE: // DEC abs,x
743
0
    data = x + GET_ADDR();
744
0
    goto dec_ptr;
745
746
0
  case 0xCE: // DEC abs
747
0
    data = GET_ADDR();
748
0
  dec_ptr:
749
0
    nz = (uint16_t) -1;
750
0
  inc_common:
751
0
    FLUSH_TIME();
752
0
    nz += READ( data );
753
0
    pc += 2;
754
0
    WRITE( data, (uint8_t) nz );
755
0
    CACHE_TIME();
756
0
    goto loop;
757
758
// Transfer
759
760
0
  case 0xAA: // TAX
761
0
    x  = a;
762
0
    nz = a;
763
0
    goto loop;
764
765
0
  case 0x8A: // TXA
766
0
    a  = x;
767
0
    nz = x;
768
0
    goto loop;
769
770
0
  case 0x9A: // TXS
771
0
    SET_SP( x ); // verified (no flag change)
772
0
    goto loop;
773
774
0
  case 0xBA: // TSX
775
0
    x = nz = GET_SP();
776
0
    goto loop;
777
778
// Stack
779
780
0
  case 0x48: // PHA
781
0
    PUSH( a ); // verified
782
0
    goto loop;
783
784
0
  case 0x68: // PLA
785
0
    a = nz = READ_LOW( sp );
786
0
    sp = (sp - 0xFF) | 0x100;
787
0
    goto loop;
788
789
0
  case 0x40:{// RTI
790
0
    uint8_t temp = READ_LOW( sp );
791
0
    pc  = READ_LOW( 0x100 | (sp - 0xFF) );
792
0
    pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
793
0
    sp = (sp - 0xFD) | 0x100;
794
0
    data = status;
795
0
    SET_STATUS( temp );
796
0
    this->r.status = status; // update externally-visible I flag
797
0
    if ( (data ^ status) & st_i )
798
0
    {
799
0
      sap_time_t new_time = end_time_;
800
0
      if ( !(status & st_i) && new_time > irq_time_ )
801
0
        new_time = irq_time_;
802
0
      int32_t delta = s.base - new_time;
803
0
      s.base = new_time;
804
0
      s_time += delta;
805
0
    }
806
0
    goto loop;
807
0
  }
808
809
0
  case 0x28:{// PLP
810
0
    uint8_t temp = READ_LOW( sp );
811
0
    sp = (sp - 0xFF) | 0x100;
812
0
    uint8_t changed = status ^ temp;
813
0
    SET_STATUS( temp );
814
0
    if ( !(changed & st_i) )
815
0
      goto loop; // I flag didn't change
816
0
    if ( status & st_i )
817
0
      goto handle_sei;
818
0
    goto handle_cli;
819
0
  }
820
821
0
  case 0x08: { // PHP
822
0
    uint8_t temp;
823
0
    CALC_STATUS( temp );
824
0
    PUSH( temp | (st_b | st_r) );
825
0
    goto loop;
826
0
  }
827
828
0
  case 0x6C:{// JMP (ind)
829
0
    data = GET_ADDR();
830
0
    pc = READ_PROG( data );
831
0
    data = (data & 0xFF00) | ((data + 1) & 0xFF);
832
0
    pc |= 0x100 * READ_PROG( data );
833
0
    goto loop;
834
0
  }
835
836
0
  case 0x00: // BRK
837
0
    goto handle_brk;
838
839
// Flags
840
841
0
  case 0x38: // SEC
842
0
    c = (uint16_t) ~0;
843
0
    goto loop;
844
845
0
  case 0x18: // CLC
846
0
    c = 0;
847
0
    goto loop;
848
849
0
  case 0xB8: // CLV
850
0
    status &= ~st_v;
851
0
    goto loop;
852
853
0
  case 0xD8: // CLD
854
0
    status &= ~st_d;
855
0
    goto loop;
856
857
0
  case 0xF8: // SED
858
0
    status |= st_d;
859
0
    goto loop;
860
861
0
  case 0x58: // CLI
862
0
    if ( !(status & st_i) )
863
0
      goto loop;
864
0
    status &= ~st_i;
865
0
  handle_cli: {
866
0
    this->r.status = status; // update externally-visible I flag
867
0
    int32_t delta = s.base - irq_time_;
868
0
    if ( delta <= 0 )
869
0
    {
870
0
      if ( TIME < irq_time_ )
871
0
        goto loop;
872
0
      goto delayed_cli;
873
0
    }
874
0
    s.base = irq_time_;
875
0
    s_time += delta;
876
0
    if ( s_time < 0 )
877
0
      goto loop;
878
879
0
    if ( delta >= s_time + 1 )
880
0
    {
881
      // delayed irq until after next instruction
882
0
      s.base += s_time + 1;
883
0
      s_time = -1;
884
0
      irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop
885
0
      goto loop;
886
0
    }
887
0
  delayed_cli:
888
0
    debug_printf( "Delayed CLI not emulated\n" );
889
0
    goto loop;
890
0
  }
891
892
0
  case 0x78: // SEI
893
0
    if ( status & st_i )
894
0
      goto loop;
895
0
    status |= st_i;
896
0
  handle_sei: {
897
0
    this->r.status = status; // update externally-visible I flag
898
0
    int32_t delta = s.base - end_time_;
899
0
    s.base = end_time_;
900
0
    s_time += delta;
901
0
    if ( s_time < 0 )
902
0
      goto loop;
903
0
    debug_printf( "Delayed SEI not emulated\n" );
904
0
    goto loop;
905
0
  }
906
907
// Unofficial
908
909
  // SKW - Skip word
910
0
  case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
911
0
    HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/
912
0
  case 0x0C:
913
0
    pc++;/*FALLTHRU*/
914
  // SKB - Skip byte
915
0
  case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64:
916
0
  case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
917
0
    pc++;
918
0
    goto loop;
919
920
  // NOP
921
0
  case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:
922
0
    goto loop;
923
924
// Unimplemented
925
926
  // halt
927
  //case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
928
  //case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2:
929
930
0
  default:
931
0
    illegal_encountered = true;
932
0
    pc--;
933
0
    goto stop;
934
0
  }
935
0
  assert( false );
936
937
0
  int result_;
938
0
handle_brk:
939
0
  if ( (pc - 1) >= idle_addr )
940
0
    goto idle_done;
941
0
  pc++;
942
0
  result_ = 4;
943
0
  debug_printf( "BRK executed\n" );
944
945
0
interrupt:
946
0
  {
947
0
    s_time += 7;
948
949
0
    WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
950
0
    WRITE_LOW( 0x100 | (sp - 2), pc );
951
0
    pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ );
952
953
0
    sp = (sp - 3) | 0x100;
954
0
    uint8_t temp;
955
0
    CALC_STATUS( temp );
956
0
    temp |= st_r;
957
0
    if ( result_ )
958
0
      temp |= st_b; // TODO: incorrectly sets B flag for IRQ
959
0
    WRITE_LOW( sp, temp );
960
961
0
    status &= ~st_d;
962
0
    status |= st_i;
963
0
    this->r.status = status; // update externally-visible I flag
964
965
0
    int32_t delta = s.base - end_time_;
966
0
    s.base = end_time_;
967
0
    s_time += delta;
968
0
    goto loop;
969
0
  }
970
971
0
idle_done:
972
  //s_time = 0;
973
0
  pc--;
974
0
  goto stop;
975
0
out_of_time:
976
0
  pc--;
977
0
  FLUSH_TIME();
978
0
  CPU_DONE( this, TIME, result_ );
979
0
  CACHE_TIME();
980
0
  if ( result_ >= 0 )
981
0
    goto interrupt;
982
0
  if ( s_time < 0 )
983
0
    goto loop;
984
985
0
stop:
986
987
0
  s.time = s_time;
988
989
0
  r.pc = pc;
990
0
  r.sp = GET_SP();
991
0
  r.a = a;
992
0
  r.x = x;
993
0
  r.y = y;
994
995
0
  {
996
0
    uint8_t temp;
997
0
    CALC_STATUS( temp );
998
0
    r.status = temp;
999
0
  }
1000
1001
0
  this->state_ = s;
1002
0
  this->state = &this->state_;
1003
1004
0
  return illegal_encountered;
1005
0
}
1006