Coverage Report

Created: 2026-09-03 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/contrib/contrib-build/game-music-emu/gme/Hes_Cpu.cpp
Line
Count
Source
1
// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
2
3
#include "Hes_Cpu.h"
4
5
#include "blargg_endian.h"
6
7
//#include "hes_cpu_log.h"
8
9
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
10
can redistribute it and/or modify it under the terms of the GNU Lesser
11
General Public License as published by the Free Software Foundation; either
12
version 2.1 of the License, or (at your option) any later version. This
13
module is distributed in the hope that it will be useful, but WITHOUT ANY
14
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16
details. You should have received a copy of the GNU Lesser General Public
17
License along with this module; if not, write to the Free Software Foundation,
18
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19
20
// TODO: support T flag, including clearing it at appropriate times?
21
22
// all zero-page should really use whatever is at page 1, but that would
23
// reduce efficiency quite a bit
24
int const ram_addr = 0x2000;
25
26
41.2k
#define FLUSH_TIME()    (void) (s.time = s_time)
27
41.2k
#define CACHE_TIME()    (void) (s_time = s.time)
28
29
#include "hes_cpu_io.h"
30
31
#include "blargg_source.h"
32
33
#if BLARGG_NONPORTABLE
34
  #define PAGE_OFFSET( addr ) (addr)
35
#else
36
303k
  #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
37
#endif
38
39
// status flags
40
enum {
41
    st_n = 0x80,
42
    st_v = 0x40,
43
    st_t = 0x20,
44
    st_b = 0x10,
45
    st_d = 0x08,
46
    st_i = 0x04,
47
    st_z = 0x02,
48
    st_c = 0x01
49
};
50
51
void Hes_Cpu::reset()
52
256
{
53
256
  check( state == &state_ );
54
256
  state = &state_;
55
56
256
  state_.time = 0;
57
256
  state_.base = 0;
58
256
  irq_time_   = future_hes_time;
59
256
  end_time_   = future_hes_time;
60
61
256
  r.status = st_i;
62
256
  r.sp     = 0;
63
256
  r.pc     = 0;
64
256
  r.a      = 0;
65
256
  r.x      = 0;
66
256
  r.y      = 0;
67
68
256
  blargg_verify_byte_order();
69
256
}
70
71
void Hes_Cpu::set_mmr( int reg, int bank )
72
2.30k
{
73
2.30k
  assert( (unsigned) reg <= page_count ); // allow page past end to be set
74
2.30k
  assert( (unsigned) bank < 0x100 );
75
2.30k
  mmr [reg] = bank;
76
2.30k
  uint8_t const* code = CPU_SET_MMR( this, reg, bank );
77
2.30k
  state->code_map [reg] = code - PAGE_OFFSET( reg << page_shift );
78
2.30k
}
79
80
0
#define TIME    (s_time + s.base)
81
82
0
#define READ( addr )            CPU_READ( this, (addr), TIME )
83
0
#define WRITE( addr, data )     {CPU_WRITE( this, (addr), (data), TIME );}
84
260k
#define READ_LOW( addr )        (ram [int (addr)])
85
0
#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
86
0
#define READ_PROG( addr )       (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )])
87
88
41.2k
#define SET_SP( v )     (sp = ((v) + 1) | 0x100)
89
41.2k
#define GET_SP()        ((sp - 1) & 0xFF)
90
0
#define PUSH( v )       ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
91
92
bool Hes_Cpu::run( hes_time_t end_time )
93
41.2k
{
94
41.2k
  bool illegal_encountered = false;
95
41.2k
  set_end_time( end_time );
96
41.2k
  state_t s = this->state_;
97
41.2k
  this->state = &s;
98
  // even on x86, using s.time in place of s_time was slower
99
41.2k
  int32_t s_time = s.time;
100
101
  // registers
102
41.2k
  uint_fast16_t pc = r.pc;
103
41.2k
  uint_fast8_t a = r.a;
104
41.2k
  uint_fast8_t x = r.x;
105
41.2k
  uint_fast8_t y = r.y;
106
41.2k
  uint_fast16_t sp;
107
41.2k
  SET_SP( r.sp );
108
109
41.2k
  #define IS_NEG (nz & 0x8080)
110
111
41.2k
  #define CALC_STATUS( out ) do {\
112
41.2k
    out = status & (st_v | st_d | st_i);\
113
41.2k
    out |= ((nz >> 8) | nz) & st_n;\
114
41.2k
    out |= c >> 8 & st_c;\
115
41.2k
    if ( !(nz & 0xFF) ) out |= st_z;\
116
41.2k
  } while ( 0 )
117
118
41.2k
  #define SET_STATUS( in ) do {\
119
41.2k
    status = in & (st_v | st_d | st_i);\
120
41.2k
    nz = in << 8;\
121
41.2k
    c = nz;\
122
41.2k
    nz |= ~in & st_z;\
123
41.2k
  } while ( 0 )
124
125
41.2k
  uint_fast8_t status;
126
41.2k
  uint_fast16_t c;  // carry set if (c & 0x100) != 0
127
41.2k
  uint_fast16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
128
41.2k
  {
129
41.2k
    uint_fast8_t temp = r.status;
130
41.2k
    SET_STATUS( temp );
131
41.2k
  }
132
133
41.2k
  goto loop;
134
260k
branch_not_taken:
135
260k
  s_time -= 2;
136
301k
loop:
137
138
  #ifndef NDEBUG
139
  {
140
    hes_time_t correct = end_time_;
141
    if ( !(status & st_i) && correct > irq_time_ )
142
      correct = irq_time_;
143
    check( s.base == correct );
144
    /*
145
    static long count;
146
    if ( count == 1844 ) Debugger();
147
    if ( s.base != correct ) debug_printf( "%ld\n", count );
148
    count++;
149
    */
150
  }
151
  #endif
152
153
301k
  check( (unsigned) GET_SP() < 0x100 );
154
301k
  check( (unsigned) a < 0x100 );
155
301k
  check( (unsigned) x < 0x100 );
156
157
301k
  uint8_t const* instr = s.code_map [pc >> page_shift];
158
301k
  uint_fast8_t opcode;
159
160
  // TODO: eliminate this special case
161
  #if BLARGG_NONPORTABLE
162
    opcode = instr [pc];
163
    pc++;
164
    instr += pc;
165
  #else
166
301k
    instr += PAGE_OFFSET( pc );
167
301k
    opcode = *instr++;
168
301k
    pc++;
169
301k
  #endif
170
171
  // TODO: each reference lists slightly different timing values, ugh
172
301k
  static uint8_t const clock_table [256] =
173
301k
  {// 0 1 2  3 4 5 6 7 8 9 A B C D E F
174
301k
    1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,6,// 0
175
301k
    4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1
176
301k
    7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2
177
301k
    4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3
178
301k
    7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4
179
301k
    4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5
180
301k
    7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6
181
301k
    4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7
182
301k
    4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8
183
301k
    4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9
184
301k
    2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A
185
301k
    4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B
186
301k
    2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C
187
301k
    4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D
188
301k
    2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E
189
301k
    4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F
190
301k
  }; // 0x00 was 8
191
192
301k
  uint_fast16_t data;
193
301k
  data = clock_table [opcode];
194
301k
  if ( (s_time += data) >= 0 )
195
0
    goto possibly_out_of_time;
196
301k
almost_out_of_time:
197
198
301k
  data = *instr;
199
200
  #ifdef HES_CPU_LOG_H
201
    log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2],
202
        instr [3], instr [4], instr [5] );
203
    //log_opcode( opcode );
204
  #endif
205
206
301k
  switch ( opcode )
207
301k
  {
208
0
possibly_out_of_time:
209
0
    if ( s_time < (int) data )
210
0
      goto almost_out_of_time;
211
0
    s_time -= data;
212
0
    goto out_of_time;
213
214
// Macros
215
216
260k
#define GET_MSB()           (instr [1])
217
0
#define ADD_PAGE( out )     (pc++, out = data + 0x100 * GET_MSB());
218
0
#define GET_ADDR()          GET_LE16( instr )
219
220
// TODO: is the penalty really always added? the original 6502 was much better
221
//#define PAGE_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8)
222
0
#define PAGE_CROSS_PENALTY( lsb )
223
224
// Branch
225
226
// TODO: more efficient way to handle negative branch that wraps PC around
227
0
#define BRANCH( cond )\
228
0
{\
229
0
  int_fast16_t offset = (int8_t) data;\
230
0
  pc++;\
231
260k
  if ( !(cond) ) goto branch_not_taken;\
232
0
  pc = uint16_t (pc + offset);\
233
0
  goto loop;\
234
0
}
235
236
0
  case 0xF0: // BEQ
237
0
    BRANCH( !((uint8_t) nz) );
238
239
0
  case 0xD0: // BNE
240
0
    BRANCH( (uint8_t) nz );
241
242
0
  case 0x10: // BPL
243
0
    BRANCH( !IS_NEG );
244
245
0
  case 0x90: // BCC
246
0
    BRANCH( !(c & 0x100) )
247
248
0
  case 0x30: // BMI
249
0
    BRANCH( IS_NEG )
250
251
0
  case 0x50: // BVC
252
0
    BRANCH( !(status & st_v) )
253
254
0
  case 0x70: // BVS
255
0
    BRANCH( status & st_v )
256
257
0
  case 0xB0: // BCS
258
0
    BRANCH( c & 0x100 )
259
260
0
  case 0x80: // BRA
261
0
  branch_taken:
262
0
    BRANCH( true );
263
264
301k
  case 0xFF:
265
301k
    if ( pc == idle_addr + 1 )
266
41.2k
      goto idle_done;
267
    // FALLTHRU
268
260k
  case 0x0F: // BBRn
269
260k
  case 0x1F:
270
260k
  case 0x2F:
271
260k
  case 0x3F:
272
260k
  case 0x4F:
273
260k
  case 0x5F:
274
260k
  case 0x6F:
275
260k
  case 0x7F:
276
260k
  case 0x8F: // BBSn
277
260k
  case 0x9F:
278
260k
  case 0xAF:
279
260k
  case 0xBF:
280
260k
  case 0xCF:
281
260k
  case 0xDF:
282
260k
  case 0xEF: {
283
260k
    uint_fast16_t t = 0x101 * READ_LOW( data );
284
260k
    t ^= 0xFF;
285
260k
    pc++;
286
260k
    data = GET_MSB();
287
260k
    BRANCH( t & (1 << (opcode >> 4)) )
288
260k
  }
289
290
0
  case 0x4C: // JMP abs
291
0
    pc = GET_ADDR();
292
0
    goto loop;
293
294
0
  case 0x7C: // JMP (ind+X)
295
0
    data += x; // FALLTHRU
296
0
  case 0x6C:{// JMP (ind)
297
0
    data += 0x100 * GET_MSB();
298
0
    pc = GET_LE16( &READ_PROG( data ) );
299
0
    goto loop;
300
0
  }
301
302
// Subroutine
303
304
0
  case 0x44: // BSR
305
0
    WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
306
0
    sp = (sp - 2) | 0x100;
307
0
    WRITE_LOW( sp, pc );
308
0
    goto branch_taken;
309
310
0
  case 0x20: { // JSR
311
0
    uint_fast16_t temp = pc + 1;
312
0
    pc = GET_ADDR();
313
0
    WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
314
0
    sp = (sp - 2) | 0x100;
315
0
    WRITE_LOW( sp, temp );
316
0
    goto loop;
317
0
  }
318
319
0
  case 0x60: // RTS
320
0
    pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
321
0
    pc += 1 + READ_LOW( sp );
322
0
    sp = (sp - 0xFE) | 0x100;
323
0
    goto loop;
324
325
0
  case 0x00: // BRK
326
0
    goto handle_brk;
327
328
// Common
329
330
0
  case 0xBD:{// LDA abs,X
331
0
    PAGE_CROSS_PENALTY( data + x );
332
0
    uint_fast16_t addr = GET_ADDR() + x;
333
0
    pc += 2;
334
0
    CPU_READ_FAST( this, addr, TIME, nz );
335
0
    a = nz;
336
0
    goto loop;
337
0
  }
338
339
0
  case 0x9D:{// STA abs,X
340
0
    uint_fast16_t addr = GET_ADDR() + x;
341
0
    pc += 2;
342
0
    CPU_WRITE_FAST( this, addr, a, TIME );
343
0
    goto loop;
344
0
  }
345
346
0
  case 0x95: // STA zp,x
347
0
    data = uint8_t (data + x); // FALLTHRU
348
0
  case 0x85: // STA zp
349
0
    pc++;
350
0
    WRITE_LOW( data, a );
351
0
    goto loop;
352
353
0
  case 0xAE:{// LDX abs
354
0
    uint_fast16_t addr = GET_ADDR();
355
0
    pc += 2;
356
0
    CPU_READ_FAST( this, addr, TIME, nz );
357
0
    x = nz;
358
0
    goto loop;
359
0
  }
360
361
0
  case 0xA5: // LDA zp
362
0
    a = nz = READ_LOW( data );
363
0
    pc++;
364
0
    goto loop;
365
366
// Load/store
367
368
0
  {
369
0
    uint_fast16_t addr;
370
0
  case 0x91: // STA (ind),Y
371
0
    addr = 0x100 * READ_LOW( uint8_t (data + 1) );
372
0
    addr += READ_LOW( data ) + y;
373
0
    pc++;
374
0
    goto sta_ptr;
375
376
0
  case 0x81: // STA (ind,X)
377
0
    data = uint8_t (data + x);
378
0
  case 0x92: // STA (ind)
379
0
    addr = 0x100 * READ_LOW( uint8_t (data + 1) );
380
0
    addr += READ_LOW( data );
381
0
    pc++;
382
0
    goto sta_ptr;
383
384
0
  case 0x99: // STA abs,Y
385
0
    data += y;
386
0
  case 0x8D: // STA abs
387
0
    addr = data + 0x100 * GET_MSB();
388
0
    pc += 2;
389
0
  sta_ptr:
390
0
    CPU_WRITE_FAST( this, addr, a, TIME );
391
0
    goto loop;
392
0
  }
393
394
0
  {
395
0
    uint_fast16_t addr;
396
0
  case 0xA1: // LDA (ind,X)
397
0
    data = uint8_t (data + x);
398
0
  case 0xB2: // LDA (ind)
399
0
    addr = 0x100 * READ_LOW( uint8_t (data + 1) );
400
0
    addr += READ_LOW( data );
401
0
    pc++;
402
0
    goto a_nz_read_addr;
403
404
0
  case 0xB1:// LDA (ind),Y
405
0
    addr = READ_LOW( data ) + y;
406
0
    PAGE_CROSS_PENALTY( addr );
407
0
    addr += 0x100 * READ_LOW( (uint8_t) (data + 1) );
408
0
    pc++;
409
0
    goto a_nz_read_addr;
410
411
0
  case 0xB9: // LDA abs,Y
412
0
    data += y;
413
0
    PAGE_CROSS_PENALTY( data );
414
0
  case 0xAD: // LDA abs
415
0
    addr = data + 0x100 * GET_MSB();
416
0
    pc += 2;
417
0
  a_nz_read_addr:
418
0
    CPU_READ_FAST( this, addr, TIME, nz );
419
0
    a = nz;
420
0
    goto loop;
421
0
  }
422
423
0
  case 0xBE:{// LDX abs,y
424
0
    PAGE_CROSS_PENALTY( data + y );
425
0
    uint_fast16_t addr = GET_ADDR() + y;
426
0
    pc += 2;
427
0
    FLUSH_TIME();
428
0
    x = nz = READ( addr );
429
0
    CACHE_TIME();
430
0
    goto loop;
431
0
  }
432
433
0
  case 0xB5: // LDA zp,x
434
0
    a = nz = READ_LOW( uint8_t (data + x) );
435
0
    pc++;
436
0
    goto loop;
437
438
0
  case 0xA9: // LDA #imm
439
0
    pc++;
440
0
    a  = data;
441
0
    nz = data;
442
0
    goto loop;
443
444
// Bit operations
445
446
0
  case 0x3C: // BIT abs,x
447
0
    data += x; // FALLTHRU
448
0
  case 0x2C:{// BIT abs
449
0
    uint_fast16_t addr;
450
0
    ADD_PAGE( addr );
451
0
    FLUSH_TIME();
452
0
    nz = READ( addr );
453
0
    CACHE_TIME();
454
0
    goto bit_common;
455
0
  }
456
0
  case 0x34: // BIT zp,x
457
0
    data = uint8_t (data + x); // FALLTHRU
458
0
  case 0x24: // BIT zp
459
0
    data = READ_LOW( data ); // FALLTHRU
460
0
  case 0x89: // BIT imm
461
0
    nz = data;
462
0
  bit_common:
463
0
    pc++;
464
0
    status &= ~st_v;
465
0
    status |= nz & st_v;
466
0
    if ( nz & a )
467
0
      goto loop; // Z should be clear, and nz must be non-zero if nz & a is
468
0
    nz <<= 8; // set Z flag without affecting N flag
469
0
    goto loop;
470
471
0
  {
472
0
    uint_fast16_t addr;
473
474
0
  case 0xB3: // TST abs,x
475
0
    addr = GET_MSB() + x;
476
0
    goto tst_abs;
477
478
0
  case 0x93: // TST abs
479
0
    addr = GET_MSB();
480
0
  tst_abs:
481
0
    addr += 0x100 * instr [2];
482
0
    pc++;
483
0
    FLUSH_TIME();
484
0
    nz = READ( addr );
485
0
    CACHE_TIME();
486
0
    goto tst_common;
487
0
  }
488
489
0
  case 0xA3: // TST zp,x
490
0
    nz = READ_LOW( uint8_t (GET_MSB() + x) );
491
0
    goto tst_common;
492
493
0
  case 0x83: // TST zp
494
0
    nz = READ_LOW( GET_MSB() );
495
0
  tst_common:
496
0
    pc += 2;
497
0
    status &= ~st_v;
498
0
    status |= nz & st_v;
499
0
    if ( nz & data )
500
0
      goto loop; // Z should be clear, and nz must be non-zero if nz & data is
501
0
    nz <<= 8; // set Z flag without affecting N flag
502
0
    goto loop;
503
504
0
  {
505
0
    uint_fast16_t addr;
506
0
  case 0x0C: // TSB abs
507
0
  case 0x1C: // TRB abs
508
0
    addr = GET_ADDR();
509
0
    pc++;
510
0
    goto txb_addr;
511
512
  // TODO: everyone lists different behaviors for the status flags, ugh
513
0
  case 0x04: // TSB zp
514
0
  case 0x14: // TRB zp
515
0
    addr = data + ram_addr;
516
0
  txb_addr:
517
0
    FLUSH_TIME();
518
0
    nz = a | READ( addr );
519
0
    if ( opcode & 0x10 )
520
0
      nz ^= a; // bits from a will already be set, so this clears them
521
0
    status &= ~st_v;
522
0
    status |= nz & st_v;
523
0
    pc++;
524
0
    WRITE( addr, nz );
525
0
    CACHE_TIME();
526
0
    goto loop;
527
0
  }
528
529
0
  case 0x07: // RMBn
530
0
  case 0x17:
531
0
  case 0x27:
532
0
  case 0x37:
533
0
  case 0x47:
534
0
  case 0x57:
535
0
  case 0x67:
536
0
  case 0x77:
537
0
    pc++;
538
0
    READ_LOW( data ) &= ~(1 << (opcode >> 4));
539
0
    goto loop;
540
541
0
  case 0x87: // SMBn
542
0
  case 0x97:
543
0
  case 0xA7:
544
0
  case 0xB7:
545
0
  case 0xC7:
546
0
  case 0xD7:
547
0
  case 0xE7:
548
0
  case 0xF7:
549
0
    pc++;
550
0
    READ_LOW( data ) |= 1 << ((opcode >> 4) - 8);
551
0
    goto loop;
552
553
// Load/store
554
555
0
  case 0x9E: // STZ abs,x
556
0
    data += x; // FALLTHRU
557
0
  case 0x9C: // STZ abs
558
0
    ADD_PAGE( data );
559
0
    pc++;
560
0
    FLUSH_TIME();
561
0
    WRITE( data, 0 );
562
0
    CACHE_TIME();
563
0
    goto loop;
564
565
0
  case 0x74: // STZ zp,x
566
0
    data = uint8_t (data + x); // FALLTHRU
567
0
  case 0x64: // STZ zp
568
0
    pc++;
569
0
    WRITE_LOW( data, 0 );
570
0
    goto loop;
571
572
0
  case 0x94: // STY zp,x
573
0
    data = uint8_t (data + x); // FALLTHRU
574
0
  case 0x84: // STY zp
575
0
    pc++;
576
0
    WRITE_LOW( data, y );
577
0
    goto loop;
578
579
0
  case 0x96: // STX zp,y
580
0
    data = uint8_t (data + y); // FALLTHRU
581
0
  case 0x86: // STX zp
582
0
    pc++;
583
0
    WRITE_LOW( data, x );
584
0
    goto loop;
585
586
0
  case 0xB6: // LDX zp,y
587
0
    data = uint8_t (data + y); // FALLTHRU
588
0
  case 0xA6: // LDX zp
589
0
    data = READ_LOW( data ); // FALLTHRU
590
0
  case 0xA2: // LDX #imm
591
0
    pc++;
592
0
    x = data;
593
0
    nz = data;
594
0
    goto loop;
595
596
0
  case 0xB4: // LDY zp,x
597
0
    data = uint8_t (data + x); // FALLTHRU
598
0
  case 0xA4: // LDY zp
599
0
    data = READ_LOW( data ); // FALLTHRU
600
0
  case 0xA0: // LDY #imm
601
0
    pc++;
602
0
    y = data;
603
0
    nz = data;
604
0
    goto loop;
605
606
0
  case 0xBC: // LDY abs,X
607
0
    data += x;
608
0
    PAGE_CROSS_PENALTY( data );
609
    // FALLTHRU
610
0
  case 0xAC:{// LDY abs
611
0
    uint_fast16_t addr = data + 0x100 * GET_MSB();
612
0
    pc += 2;
613
0
    FLUSH_TIME();
614
0
    y = nz = READ( addr );
615
0
    CACHE_TIME();
616
0
    goto loop;
617
0
  }
618
619
0
  {
620
0
    uint_fast8_t temp;
621
0
  case 0x8C: // STY abs
622
0
    temp = y;
623
0
    goto store_abs;
624
625
0
  case 0x8E: // STX abs
626
0
    temp = x;
627
0
  store_abs:
628
0
    uint_fast16_t addr = GET_ADDR();
629
0
    pc += 2;
630
0
    FLUSH_TIME();
631
0
    WRITE( addr, temp );
632
0
    CACHE_TIME();
633
0
    goto loop;
634
0
  }
635
636
// Compare
637
638
0
  case 0xEC:{// CPX abs
639
0
    uint_fast16_t addr = GET_ADDR();
640
0
    pc++;
641
0
    FLUSH_TIME();
642
0
    data = READ( addr );
643
0
    CACHE_TIME();
644
0
    goto cpx_data;
645
0
  }
646
647
0
  case 0xE4: // CPX zp
648
0
    data = READ_LOW( data ); // FALLTHRU
649
0
  case 0xE0: // CPX #imm
650
0
  cpx_data:
651
0
    nz = x - data;
652
0
    pc++;
653
0
    c = ~nz;
654
0
    nz &= 0xFF;
655
0
    goto loop;
656
657
0
  case 0xCC:{// CPY abs
658
0
    uint_fast16_t addr = GET_ADDR();
659
0
    pc++;
660
0
    FLUSH_TIME();
661
0
    data = READ( addr );
662
0
    CACHE_TIME();
663
0
    goto cpy_data;
664
0
  }
665
666
0
  case 0xC4: // CPY zp
667
0
    data = READ_LOW( data ); // FALLTHRU
668
0
  case 0xC0: // CPY #imm
669
0
  cpy_data:
670
0
    nz = y - data;
671
0
    pc++;
672
0
    c = ~nz;
673
0
    nz &= 0xFF;
674
0
    goto loop;
675
676
// Logical
677
678
0
#define ARITH_ADDR_MODES( op )\
679
0
  case op - 0x04: /* (ind,x) */\
680
0
    data = uint8_t (data + x);/*FALLTHRU*/\
681
0
  case op + 0x0D: /* (ind) */\
682
0
    data = 0x100 * READ_LOW( uint8_t (data + 1) ) + READ_LOW( data );\
683
0
    goto ptr##op;\
684
0
  case op + 0x0C:{/* (ind),y */\
685
0
    uint_fast16_t temp = READ_LOW( data ) + y;\
686
0
    PAGE_CROSS_PENALTY( temp );\
687
0
    data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
688
0
    goto ptr##op;\
689
0
  }\
690
0
  case op + 0x10: /* zp,X */\
691
0
    data = uint8_t (data + x);/*FALLTHRU*/\
692
0
  case op + 0x00: /* zp */\
693
0
    data = READ_LOW( data );\
694
0
    goto imm##op;\
695
0
  case op + 0x14: /* abs,Y */\
696
0
    data += y;\
697
0
    goto ind##op;\
698
0
  case op + 0x18: /* abs,X */\
699
0
    data += x;\
700
0
    goto ind##op;/*WORKAROUND: Mute a fallthrough warning*/\
701
0
  ind##op:/*FALLTHRU*/\
702
0
    PAGE_CROSS_PENALTY( data );/*FALLTHRU*/\
703
0
  case op + 0x08: /* abs */\
704
0
    ADD_PAGE( data );/*FALLTHRU*/\
705
0
  ptr##op:\
706
0
    FLUSH_TIME();\
707
0
    data = READ( data );\
708
0
    CACHE_TIME();/*FALLTHRU*/\
709
0
  case op + 0x04: /* imm */\
710
0
  imm##op:
711
712
0
  ARITH_ADDR_MODES( 0xC5 ) // CMP
713
0
    nz = a - data;
714
0
    pc++;
715
0
    c = ~nz;
716
0
    nz &= 0xFF;
717
0
    goto loop;
718
719
0
  ARITH_ADDR_MODES( 0x25 ) // AND
720
0
    nz = (a &= data);
721
0
    pc++;
722
0
    goto loop;
723
724
0
  ARITH_ADDR_MODES( 0x45 ) // EOR
725
0
    nz = (a ^= data);
726
0
    pc++;
727
0
    goto loop;
728
729
0
  ARITH_ADDR_MODES( 0x05 ) // ORA
730
0
    nz = (a |= data);
731
0
    pc++;
732
0
    goto loop;
733
734
// Add/subtract
735
736
0
  ARITH_ADDR_MODES( 0xE5 ) // SBC
737
0
    data ^= 0xFF;
738
0
    goto adc_imm;
739
740
0
  ARITH_ADDR_MODES( 0x65 ) // ADC
741
0
    /*FALLTHRU*/
742
0
  adc_imm: {
743
0
    if ( status & st_d )
744
0
      debug_printf( "Decimal mode not supported\n" );
745
0
    int_fast16_t carry = c >> 8 & 1;
746
0
    int_fast16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
747
0
    status &= ~st_v;
748
0
    status |= ov >> 2 & 0x40;
749
0
    c = nz = a + data + carry;
750
0
    pc++;
751
0
    a = (uint8_t) nz;
752
0
    goto loop;
753
0
  }
754
755
// Shift/rotate
756
757
0
  case 0x4A: // LSR A
758
0
    c = 0; // FALLTHRU
759
0
  case 0x6A: // ROR A
760
0
    nz = c >> 1 & 0x80;
761
0
    c = a << 8;
762
0
    nz |= a >> 1;
763
0
    a = nz;
764
0
    goto loop;
765
766
0
  case 0x0A: // ASL A
767
0
    nz = a << 1;
768
0
    c = nz;
769
0
    a = (uint8_t) nz;
770
0
    goto loop;
771
772
0
  case 0x2A: { // ROL A
773
0
    nz = a << 1;
774
0
    int_fast16_t temp = c >> 8 & 1;
775
0
    c = nz;
776
0
    nz |= temp;
777
0
    a = (uint8_t) nz;
778
0
    goto loop;
779
0
  }
780
781
0
  case 0x5E: // LSR abs,X
782
0
    data += x;/*FALLTHRU*/
783
0
  case 0x4E: // LSR abs
784
0
    c = 0;/*FALLTHRU*/
785
0
  case 0x6E: // ROR abs
786
0
  ror_abs: {
787
0
    ADD_PAGE( data );
788
0
    FLUSH_TIME();
789
0
    int temp = READ( data );
790
0
    nz = (c >> 1 & 0x80) | (temp >> 1);
791
0
    c = temp << 8;
792
0
    goto rotate_common;
793
0
  }
794
795
0
  case 0x3E: // ROL abs,X
796
0
    data += x;
797
0
    goto rol_abs;
798
799
0
  case 0x1E: // ASL abs,X
800
0
    data += x;/*FALLTHRU*/
801
0
  case 0x0E: // ASL abs
802
0
    c = 0;/*FALLTHRU*/
803
0
  case 0x2E: // ROL abs
804
0
  rol_abs:
805
0
    ADD_PAGE( data );
806
0
    nz = c >> 8 & 1;
807
0
    FLUSH_TIME();
808
0
    nz |= (c = READ( data ) << 1);
809
0
  rotate_common:
810
0
    pc++;
811
0
    WRITE( data, (uint8_t) nz );
812
0
    CACHE_TIME();
813
0
    goto loop;
814
815
0
  case 0x7E: // ROR abs,X
816
0
    data += x;
817
0
    goto ror_abs;
818
819
0
  case 0x76: // ROR zp,x
820
0
    data = uint8_t (data + x);
821
0
    goto ror_zp;
822
823
0
  case 0x56: // LSR zp,x
824
0
    data = uint8_t (data + x);/*FALLTHRU*/
825
0
  case 0x46: // LSR zp
826
0
    c = 0;/*FALLTHRU*/
827
0
  case 0x66: // ROR zp
828
0
  ror_zp: {
829
0
    int temp = READ_LOW( data );
830
0
    nz = (c >> 1 & 0x80) | (temp >> 1);
831
0
    c = temp << 8;
832
0
    goto write_nz_zp;
833
0
  }
834
835
0
  case 0x36: // ROL zp,x
836
0
    data = uint8_t (data + x);
837
0
    goto rol_zp;
838
839
0
  case 0x16: // ASL zp,x
840
0
    data = uint8_t (data + x);/*FALLTHRU*/
841
0
  case 0x06: // ASL zp
842
0
    c = 0;/*FALLTHRU*/
843
0
  case 0x26: // ROL zp
844
0
  rol_zp:
845
0
    nz = c >> 8 & 1;
846
0
    nz |= (c = READ_LOW( data ) << 1);
847
0
    goto write_nz_zp;
848
849
// Increment/decrement
850
851
0
#define INC_DEC_AXY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
852
853
0
  case 0x1A: // INA
854
0
    INC_DEC_AXY( a, +1 )
855
856
0
  case 0xE8: // INX
857
0
    INC_DEC_AXY( x, +1 )
858
859
0
  case 0xC8: // INY
860
0
    INC_DEC_AXY( y, +1 )
861
862
0
  case 0x3A: // DEA
863
0
    INC_DEC_AXY( a, -1 )
864
865
0
  case 0xCA: // DEX
866
0
    INC_DEC_AXY( x, -1 )
867
868
0
  case 0x88: // DEY
869
0
    INC_DEC_AXY( y, -1 )
870
871
0
  case 0xF6: // INC zp,x
872
0
    data = uint8_t (data + x);/*FALLTHRU*/
873
0
  case 0xE6: // INC zp
874
0
    nz = 1;
875
0
    goto add_nz_zp;
876
877
0
  case 0xD6: // DEC zp,x
878
0
    data = uint8_t (data + x);/*FALLTHRU*/
879
0
  case 0xC6: // DEC zp
880
0
    nz = (uint_fast16_t)-1;
881
0
  add_nz_zp:
882
0
    nz += READ_LOW( data );
883
0
  write_nz_zp:
884
0
    pc++;
885
0
    WRITE_LOW( data, nz );
886
0
    goto loop;
887
888
0
  case 0xFE: // INC abs,x
889
0
    data = x + GET_ADDR();
890
0
    goto inc_ptr;
891
892
0
  case 0xEE: // INC abs
893
0
    data = GET_ADDR();
894
0
  inc_ptr:
895
0
    nz = 1;
896
0
    goto inc_common;
897
898
0
  case 0xDE: // DEC abs,x
899
0
    data = x + GET_ADDR();
900
0
    goto dec_ptr;
901
902
0
  case 0xCE: // DEC abs
903
0
    data = GET_ADDR();
904
0
  dec_ptr:
905
0
    nz = (uint_fast16_t) -1;
906
0
  inc_common:
907
0
    FLUSH_TIME();
908
0
    nz += READ( data );
909
0
    pc += 2;
910
0
    WRITE( data, (uint8_t) nz );
911
0
    CACHE_TIME();
912
0
    goto loop;
913
914
// Transfer
915
916
0
  case 0xA8: // TAY
917
0
    y  = a;
918
0
    nz = a;
919
0
    goto loop;
920
921
0
  case 0x98: // TYA
922
0
    a  = y;
923
0
    nz = y;
924
0
    goto loop;
925
926
0
  case 0xAA: // TAX
927
0
    x  = a;
928
0
    nz = a;
929
0
    goto loop;
930
931
0
  case 0x8A: // TXA
932
0
    a  = x;
933
0
    nz = x;
934
0
    goto loop;
935
936
0
  case 0x9A: // TXS
937
0
    SET_SP( x ); // verified (no flag change)
938
0
    goto loop;
939
940
0
  case 0xBA: // TSX
941
0
    x = nz = GET_SP();
942
0
    goto loop;
943
944
0
  #define SWAP_REGS( r1, r2 ) {\
945
0
    uint_fast8_t t = r1;\
946
0
    r1 = r2;\
947
0
    r2 = t;\
948
0
    goto loop;\
949
0
  }
950
951
0
  case 0x02: // SXY
952
0
    SWAP_REGS( x, y );
953
954
0
  case 0x22: // SAX
955
0
    SWAP_REGS( a, x );
956
957
0
  case 0x42: // SAY
958
0
    SWAP_REGS( a, y );
959
960
0
  case 0x62: // CLA
961
0
    a = 0;
962
0
    goto loop;
963
964
0
  case 0x82: // CLX
965
0
    x = 0;
966
0
    goto loop;
967
968
0
  case 0xC2: // CLY
969
0
    y = 0;
970
0
    goto loop;
971
972
// Stack
973
974
0
  case 0x48: // PHA
975
0
    PUSH( a );
976
0
    goto loop;
977
978
0
  case 0xDA: // PHX
979
0
    PUSH( x );
980
0
    goto loop;
981
982
0
  case 0x5A: // PHY
983
0
    PUSH( y );
984
0
    goto loop;
985
986
0
  case 0x40:{// RTI
987
0
    uint_fast8_t temp = READ_LOW( sp );
988
0
    pc  = READ_LOW( 0x100 | (sp - 0xFF) );
989
0
    pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
990
0
    sp = (sp - 0xFD) | 0x100;
991
0
    data = status;
992
0
    SET_STATUS( temp );
993
0
    this->r.status = status; // update externally-visible I flag
994
0
    if ( (data ^ status) & st_i )
995
0
    {
996
0
      hes_time_t new_time = end_time_;
997
0
      if ( !(status & st_i) && new_time > irq_time_ )
998
0
        new_time = irq_time_;
999
0
      int32_t delta = s.base - new_time;
1000
0
      s.base = new_time;
1001
0
      s_time += delta;
1002
0
    }
1003
0
    goto loop;
1004
0
  }
1005
1006
0
  #define POP()  READ_LOW( sp ); sp = (sp - 0xFF) | 0x100
1007
1008
0
  case 0x68: // PLA
1009
0
    a = nz = POP();
1010
0
    goto loop;
1011
1012
0
  case 0xFA: // PLX
1013
0
    x = nz = POP();
1014
0
    goto loop;
1015
1016
0
  case 0x7A: // PLY
1017
0
    y = nz = POP();
1018
0
    goto loop;
1019
1020
0
  case 0x28:{// PLP
1021
0
    uint_fast8_t temp = POP();
1022
0
    uint_fast8_t changed = status ^ temp;
1023
0
    SET_STATUS( temp );
1024
0
    if ( !(changed & st_i) )
1025
0
      goto loop; // I flag didn't change
1026
0
    if ( status & st_i )
1027
0
      goto handle_sei;
1028
0
    goto handle_cli;
1029
0
  }
1030
0
  #undef POP
1031
1032
0
  case 0x08: { // PHP
1033
0
    uint_fast8_t temp;
1034
0
    CALC_STATUS( temp );
1035
0
    PUSH( temp | st_b );
1036
0
    goto loop;
1037
0
  }
1038
1039
// Flags
1040
1041
0
  case 0x38: // SEC
1042
0
    c = (uint_fast16_t) ~0;
1043
0
    goto loop;
1044
1045
0
  case 0x18: // CLC
1046
0
    c = 0;
1047
0
    goto loop;
1048
1049
0
  case 0xB8: // CLV
1050
0
    status &= ~st_v;
1051
0
    goto loop;
1052
1053
0
  case 0xD8: // CLD
1054
0
    status &= ~st_d;
1055
0
    goto loop;
1056
1057
0
  case 0xF8: // SED
1058
0
    status |= st_d;
1059
0
    goto loop;
1060
1061
0
  case 0x58: // CLI
1062
0
    if ( !(status & st_i) )
1063
0
      goto loop;
1064
0
    status &= ~st_i;
1065
0
  handle_cli: {
1066
0
    this->r.status = status; // update externally-visible I flag
1067
0
    int32_t delta = s.base - irq_time_;
1068
0
    if ( delta <= 0 )
1069
0
    {
1070
0
      if ( TIME < irq_time_ )
1071
0
        goto loop;
1072
0
      goto delayed_cli;
1073
0
    }
1074
0
    s.base = irq_time_;
1075
0
    s_time += delta;
1076
0
    if ( s_time < 0 )
1077
0
      goto loop;
1078
1079
0
    if ( delta >= s_time + 1 )
1080
0
    {
1081
      // delayed irq until after next instruction
1082
0
      s.base += s_time + 1;
1083
0
      s_time = -1;
1084
0
      irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop
1085
0
      goto loop;
1086
0
    }
1087
0
  delayed_cli:
1088
0
    debug_printf( "Delayed CLI not supported\n" ); // TODO: implement
1089
0
    goto loop;
1090
0
  }
1091
1092
0
  case 0x78: // SEI
1093
0
    if ( status & st_i )
1094
0
      goto loop;
1095
0
    status |= st_i;
1096
0
  handle_sei: {
1097
0
    this->r.status = status; // update externally-visible I flag
1098
0
    int32_t delta = s.base - end_time_;
1099
0
    s.base = end_time_;
1100
0
    s_time += delta;
1101
0
    if ( s_time < 0 )
1102
0
      goto loop;
1103
0
    debug_printf( "Delayed SEI not supported\n" ); // TODO: implement
1104
0
    goto loop;
1105
0
  }
1106
1107
// Special
1108
1109
0
  case 0x53:{// TAM
1110
0
    uint_fast8_t const bits = data; // avoid using data across function call
1111
0
    pc++;
1112
0
    for ( int i = 0; i < 8; i++ )
1113
0
      if ( bits & (1 << i) )
1114
0
        set_mmr( i, a );
1115
0
    goto loop;
1116
0
  }
1117
1118
0
  case 0x43:{// TMA
1119
0
    pc++;
1120
0
    byte const* in = mmr;
1121
0
    do
1122
0
    {
1123
0
      if ( data & 1 )
1124
0
        a = *in;
1125
0
      in++;
1126
0
    }
1127
0
    while ( (data >>= 1) != 0 );
1128
0
    goto loop;
1129
0
  }
1130
1131
0
  case 0x03: // ST0
1132
0
  case 0x13: // ST1
1133
0
  case 0x23:{// ST2
1134
0
    uint_fast16_t addr = opcode >> 4;
1135
0
    if ( addr )
1136
0
      addr++;
1137
0
    pc++;
1138
0
    FLUSH_TIME();
1139
0
    CPU_WRITE_VDP( this, addr, data, TIME );
1140
0
    CACHE_TIME();
1141
0
    goto loop;
1142
0
  }
1143
1144
0
  case 0xEA: // NOP
1145
0
    goto loop;
1146
1147
0
  case 0x54: // CSL
1148
0
    debug_printf( "CSL not supported\n" );
1149
0
    illegal_encountered = true;
1150
0
    goto loop;
1151
1152
0
  case 0xD4: // CSH
1153
0
    goto loop;
1154
1155
0
  case 0xF4: { // SET
1156
    //fuint16 operand = GET_MSB();
1157
0
    debug_printf( "SET not handled\n" );
1158
    //switch ( data )
1159
    //{
1160
    //}
1161
0
    illegal_encountered = true;
1162
0
    goto loop;
1163
0
  }
1164
1165
// Block transfer
1166
1167
0
  {
1168
0
    uint_fast16_t in_alt;
1169
0
    int_fast16_t in_inc;
1170
0
    uint_fast16_t out_alt;
1171
0
    int_fast16_t out_inc;
1172
1173
0
  case 0xE3: // TIA
1174
0
    in_alt  = 0;
1175
0
    goto bxfer_alt;
1176
1177
0
  case 0xF3: // TAI
1178
0
    in_alt  = 1;
1179
0
  bxfer_alt:
1180
0
    in_inc  = in_alt ^ 1;
1181
0
    out_alt = in_inc;
1182
0
    out_inc = in_alt;
1183
0
    goto bxfer;
1184
1185
0
  case 0xD3: // TIN
1186
0
    in_inc  = 1;
1187
0
    out_inc = 0;
1188
0
    goto bxfer_no_alt;
1189
1190
0
  case 0xC3: // TDD
1191
0
    in_inc  = -1;
1192
0
    out_inc = -1;
1193
0
    goto bxfer_no_alt;
1194
1195
0
  case 0x73: // TII
1196
0
    in_inc  = 1;
1197
0
    out_inc = 1;
1198
0
  bxfer_no_alt:
1199
0
    in_alt  = 0;
1200
0
    out_alt = 0;
1201
0
  bxfer:
1202
0
    uint_fast16_t in    = GET_LE16( instr + 0 );
1203
0
    uint_fast16_t out   = GET_LE16( instr + 2 );
1204
0
    int     count = GET_LE16( instr + 4 );
1205
0
    if ( !count )
1206
0
      count = 0x10000;
1207
0
    pc += 6;
1208
0
    WRITE_LOW( 0x100 | (sp - 1), y );
1209
0
    WRITE_LOW( 0x100 | (sp - 2), a );
1210
0
    WRITE_LOW( 0x100 | (sp - 3), x );
1211
0
    FLUSH_TIME();
1212
0
    do
1213
0
    {
1214
      // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O
1215
0
      uint_fast8_t t = READ( in );
1216
0
      in += in_inc;
1217
0
      in &= 0xFFFF;
1218
0
      s.time += 6;
1219
0
      if ( in_alt )
1220
0
        in_inc = -in_inc;
1221
0
      WRITE( out, t );
1222
0
      out += out_inc;
1223
0
      out &= 0xFFFF;
1224
0
      if ( out_alt )
1225
0
        out_inc = -out_inc;
1226
0
    }
1227
0
    while ( --count );
1228
0
    CACHE_TIME();
1229
0
    goto loop;
1230
0
  }
1231
1232
// Illegal
1233
1234
0
  default:
1235
0
    debug_printf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 );
1236
0
    illegal_encountered = true;
1237
0
    goto loop;
1238
301k
  }
1239
301k
  assert( false );
1240
1241
0
  int result_;
1242
0
handle_brk:
1243
0
  pc++;
1244
0
  result_ = 6;
1245
1246
0
interrupt:
1247
0
  {
1248
0
    s_time += 7;
1249
1250
0
    WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
1251
0
    WRITE_LOW( 0x100 | (sp - 2), pc );
1252
0
    pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ );
1253
1254
0
    sp = (sp - 3) | 0x100;
1255
0
    uint_fast8_t temp;
1256
0
    CALC_STATUS( temp );
1257
0
    if ( result_ == 6 )
1258
0
      temp |= st_b;
1259
0
    WRITE_LOW( sp, temp );
1260
1261
0
    status &= ~st_d;
1262
0
    status |= st_i;
1263
0
    this->r.status = status; // update externally-visible I flag
1264
1265
0
    int32_t delta = s.base - end_time_;
1266
0
    s.base = end_time_;
1267
0
    s_time += delta;
1268
0
    goto loop;
1269
0
  }
1270
1271
41.2k
idle_done:
1272
41.2k
  s_time = 0;
1273
41.2k
out_of_time:
1274
41.2k
  pc--;
1275
41.2k
  FLUSH_TIME();
1276
41.2k
  CPU_DONE( this, TIME, result_ );
1277
41.2k
  CACHE_TIME();
1278
41.2k
  if ( result_ > 0 )
1279
0
    goto interrupt;
1280
41.2k
  if ( s_time < 0 )
1281
0
    goto loop;
1282
1283
41.2k
  s.time = s_time;
1284
1285
41.2k
  r.pc = pc;
1286
41.2k
  r.sp = GET_SP();
1287
41.2k
  r.a = a;
1288
41.2k
  r.x = x;
1289
41.2k
  r.y = y;
1290
1291
41.2k
  {
1292
41.2k
    uint_fast8_t temp;
1293
41.2k
    CALC_STATUS( temp );
1294
41.2k
    r.status = temp;
1295
41.2k
  }
1296
1297
41.2k
  this->state_ = s;
1298
41.2k
  this->state = &this->state_;
1299
1300
41.2k
  return illegal_encountered;
1301
41.2k
}