Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
RishiRecon
GitHub Repository: RishiRecon/exploits
Path: blob/main/misc/emulator/xnes/snes9x/apu/SPC_CPU.h
28798 views
1
// snes_spc 0.9.0. http://www.slack.net/~ant/
2
3
/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
4
can redistribute it and/or modify it under the terms of the GNU Lesser
5
General Public License as published by the Free Software Foundation; either
6
version 2.1 of the License, or (at your option) any later version. This
7
module is distributed in the hope that it will be useful, but WITHOUT ANY
8
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
10
details. You should have received a copy of the GNU Lesser General Public
11
License along with this module; if not, write to the Free Software Foundation,
12
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
13
14
//// Memory access
15
16
#if SPC_MORE_ACCURACY
17
#define SUSPICIOUS_OPCODE( name ) ((void) 0)
18
#else
19
#define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" )
20
#endif
21
22
#define CPU_READ( time, offset, addr )\
23
cpu_read( addr, time + offset )
24
25
#define CPU_WRITE( time, offset, addr, data )\
26
cpu_write( data, addr, time + offset )
27
28
#if SPC_MORE_ACCURACY
29
#define CPU_READ_TIMER( time, offset, addr, out )\
30
{ out = CPU_READ( time, offset, addr ); }
31
32
#else
33
// timers are by far the most common thing read from dp
34
#define CPU_READ_TIMER( time, offset, addr_, out )\
35
{\
36
rel_time_t adj_time = time + offset;\
37
int dp_addr = addr_;\
38
int ti = dp_addr - (r_t0out + 0xF0);\
39
if ( (unsigned) ti < timer_count )\
40
{\
41
Timer* t = &m.timers [ti];\
42
if ( adj_time >= t->next_time )\
43
t = run_timer_( t, adj_time );\
44
out = t->counter;\
45
t->counter = 0;\
46
}\
47
else\
48
{\
49
out = ram [dp_addr];\
50
int i = dp_addr - 0xF0;\
51
if ( (unsigned) i < 0x10 )\
52
out = cpu_read_smp_reg( i, adj_time );\
53
}\
54
}
55
#endif
56
57
#define TIME_ADJ( n ) (n)
58
59
#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out )
60
#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) )
61
#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) )
62
63
#define DP_ADDR( addr ) (dp + (addr))
64
65
#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out )
66
#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) )
67
#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data )
68
69
#define READ_PROG16( addr ) GET_LE16( ram + (addr) )
70
71
#define SET_PC( n ) (pc = ram + (n))
72
#define GET_PC() (pc - ram)
73
#define READ_PC( pc ) (*(pc))
74
#define READ_PC16( pc ) GET_LE16( pc )
75
76
// TODO: remove non-wrapping versions?
77
#define SPC_NO_SP_WRAPAROUND 0
78
79
#define SET_SP( v ) (sp = ram + 0x101 + (v))
80
#define GET_SP() (sp - 0x101 - ram)
81
82
#if SPC_NO_SP_WRAPAROUND
83
#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))
84
#define PUSH( v ) (void) (*--sp = (uint8_t) (v))
85
#define POP( out ) (void) ((out) = *sp++)
86
87
#else
88
#define PUSH16( data )\
89
{\
90
int addr = (sp -= 2) - ram;\
91
if ( addr > 0x100 )\
92
{\
93
SET_LE16( sp, data );\
94
}\
95
else\
96
{\
97
ram [(uint8_t) addr + 0x100] = (uint8_t) data;\
98
sp [1] = (uint8_t) (data >> 8);\
99
sp += 0x100;\
100
}\
101
}
102
103
#define PUSH( data )\
104
{\
105
*--sp = (uint8_t) (data);\
106
if ( sp - ram == 0x100 )\
107
sp += 0x100;\
108
}
109
110
#define POP( out )\
111
{\
112
out = *sp++;\
113
if ( sp - ram == 0x201 )\
114
{\
115
out = sp [-0x101];\
116
sp -= 0x100;\
117
}\
118
}
119
120
#endif
121
122
#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel )
123
124
unsigned SNES_SPC::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time )
125
{
126
unsigned addr = READ_PC16( pc );
127
unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13);
128
return t << 8 & 0x100;
129
}
130
131
//// Status flag handling
132
133
// Hex value in name to clarify code and bit shifting.
134
// Flag stored in indicated variable during emulation
135
int const n80 = 0x80; // nz
136
int const v40 = 0x40; // psw
137
int const p20 = 0x20; // dp
138
int const b10 = 0x10; // psw
139
int const h08 = 0x08; // psw
140
int const i04 = 0x04; // psw
141
int const z02 = 0x02; // nz
142
int const c01 = 0x01; // c
143
144
int const nz_neg_mask = 0x880; // either bit set indicates N flag set
145
146
#define GET_PSW( out )\
147
{\
148
out = psw & ~(n80 | p20 | z02 | c01);\
149
out |= c >> 8 & c01;\
150
out |= dp >> 3 & p20;\
151
out |= ((nz >> 4) | nz) & n80;\
152
if ( !(uint8_t) nz ) out |= z02;\
153
}
154
155
#define SET_PSW( in )\
156
{\
157
psw = in;\
158
c = in << 8;\
159
dp = in << 3 & 0x100;\
160
nz = (in << 4 & 0x800) | (~in & z02);\
161
}
162
163
SPC_CPU_RUN_FUNC
164
{
165
uint8_t* const ram = RAM;
166
int a = m.cpu_regs.a;
167
int x = m.cpu_regs.x;
168
int y = m.cpu_regs.y;
169
uint8_t const* pc;
170
uint8_t* sp;
171
int psw;
172
int c;
173
int nz;
174
int dp;
175
176
SET_PC( m.cpu_regs.pc );
177
SET_SP( m.cpu_regs.sp );
178
SET_PSW( m.cpu_regs.psw );
179
180
goto loop;
181
182
183
// Main loop
184
185
cbranch_taken_loop:
186
pc += *(BOOST::int8_t const*) pc;
187
inc_pc_loop:
188
pc++;
189
loop:
190
{
191
unsigned opcode;
192
unsigned data;
193
194
check( (unsigned) a < 0x100 );
195
check( (unsigned) x < 0x100 );
196
check( (unsigned) y < 0x100 );
197
198
opcode = *pc;
199
if (allow_time_overflow && rel_time >= 0 )
200
goto stop;
201
if ( (rel_time += m.cycle_table [opcode]) > 0 && !allow_time_overflow)
202
goto out_of_time;
203
204
#ifdef SPC_CPU_OPCODE_HOOK
205
SPC_CPU_OPCODE_HOOK( GET_PC(), opcode );
206
#endif
207
/*
208
//SUB_CASE_COUNTER( 1 );
209
#define PROFILE_TIMER_LOOP( op, addr, len )\
210
if ( opcode == op )\
211
{\
212
int cond = (unsigned) ((addr) - 0xFD) < 3 &&\
213
pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\
214
SUB_CASE_COUNTER( op && cond );\
215
}
216
217
PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 );
218
PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 );
219
PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 );
220
*/
221
222
#ifdef DEBUGGER
223
if (debug_trace)
224
debug_do_trace(a, x, y, pc, sp, psw, c, nz, dp);
225
#endif
226
227
228
// TODO: if PC is at end of memory, this will get wrong operand (very obscure)
229
data = *++pc;
230
switch ( opcode )
231
{
232
233
// Common instructions
234
235
#define BRANCH( cond )\
236
{\
237
pc++;\
238
pc += (BOOST::int8_t) data;\
239
if ( cond )\
240
goto loop;\
241
pc -= (BOOST::int8_t) data;\
242
rel_time -= 2;\
243
goto loop;\
244
}
245
246
case 0xF0: // BEQ
247
BRANCH( !(uint8_t) nz ) // 89% taken
248
249
case 0xD0: // BNE
250
BRANCH( (uint8_t) nz )
251
252
case 0x3F:{// CALL
253
int old_addr = GET_PC() + 2;
254
SET_PC( READ_PC16( pc ) );
255
PUSH16( old_addr );
256
goto loop;
257
}
258
259
case 0x6F:// RET
260
#if SPC_NO_SP_WRAPAROUND
261
{
262
SET_PC( GET_LE16( sp ) );
263
sp += 2;
264
}
265
#else
266
{
267
int addr = sp - ram;
268
SET_PC( GET_LE16( sp ) );
269
sp += 2;
270
if ( addr < 0x1FF )
271
goto loop;
272
273
SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] );
274
sp -= 0x100;
275
}
276
#endif
277
goto loop;
278
279
case 0xE4: // MOV a,dp
280
++pc;
281
// 80% from timer
282
READ_DP_TIMER( 0, data, a = nz );
283
goto loop;
284
285
case 0xFA:{// MOV dp,dp
286
int temp;
287
READ_DP_TIMER( -2, data, temp );
288
data = temp + no_read_before_write ;
289
}
290
// fall through
291
case 0x8F:{// MOV dp,#imm
292
int temp = READ_PC( pc + 1 );
293
pc += 2;
294
295
#if !SPC_MORE_ACCURACY
296
{
297
int i = dp + temp;
298
ram [i] = (uint8_t) data;
299
i -= 0xF0;
300
if ( (unsigned) i < 0x10 ) // 76%
301
{
302
REGS [i] = (uint8_t) data;
303
304
// Registers other than $F2 and $F4-$F7
305
//if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 )
306
if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12%
307
cpu_write_smp_reg( data, rel_time, i );
308
}
309
}
310
#else
311
WRITE_DP( 0, temp, data );
312
#endif
313
goto loop;
314
}
315
316
case 0xC4: // MOV dp,a
317
++pc;
318
#if !SPC_MORE_ACCURACY
319
{
320
int i = dp + data;
321
ram [i] = (uint8_t) a;
322
i -= 0xF0;
323
if ( (unsigned) i < 0x10 ) // 39%
324
{
325
unsigned sel = i - 2;
326
REGS [i] = (uint8_t) a;
327
328
if ( sel == 1 ) // 51% $F3
329
dsp_write( a, rel_time );
330
else if ( sel > 1 ) // 1% not $F2 or $F3
331
cpu_write_smp_reg_( a, rel_time, i );
332
}
333
}
334
#else
335
WRITE_DP( 0, data, a );
336
#endif
337
goto loop;
338
339
#define CASE( n ) case n:
340
341
// Define common address modes based on opcode for immediate mode. Execution
342
// ends with data set to the address of the operand.
343
#define ADDR_MODES_( op )\
344
CASE( op - 0x02 ) /* (X) */\
345
data = x + dp;\
346
pc--;\
347
goto end_##op;\
348
CASE( op + 0x0F ) /* (dp)+Y */\
349
data = READ_PROG16( data + dp ) + y;\
350
goto end_##op;\
351
CASE( op - 0x01 ) /* (dp+X) */\
352
data = READ_PROG16( ((uint8_t) (data + x)) + dp );\
353
goto end_##op;\
354
CASE( op + 0x0E ) /* abs+Y */\
355
data += y;\
356
goto abs_##op;\
357
CASE( op + 0x0D ) /* abs+X */\
358
data += x;\
359
CASE( op - 0x03 ) /* abs */\
360
abs_##op:\
361
data += 0x100 * READ_PC( ++pc );\
362
goto end_##op;\
363
CASE( op + 0x0C ) /* dp+X */\
364
data = (uint8_t) (data + x);
365
366
#define ADDR_MODES_NO_DP( op )\
367
ADDR_MODES_( op )\
368
data += dp;\
369
end_##op:
370
371
#define ADDR_MODES( op )\
372
ADDR_MODES_( op )\
373
CASE( op - 0x04 ) /* dp */\
374
data += dp;\
375
end_##op:
376
377
// 1. 8-bit Data Transmission Commands. Group I
378
379
ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr
380
a = nz = READ( 0, data );
381
goto inc_pc_loop;
382
383
case 0xBF:{// MOV A,(X)+
384
int temp = x + dp;
385
x = (uint8_t) (x + 1);
386
a = nz = READ( -1, temp );
387
goto loop;
388
}
389
390
case 0xE8: // MOV A,imm
391
a = data;
392
nz = data;
393
goto inc_pc_loop;
394
395
case 0xF9: // MOV X,dp+Y
396
data = (uint8_t) (data + y);
397
case 0xF8: // MOV X,dp
398
READ_DP_TIMER( 0, data, x = nz );
399
goto inc_pc_loop;
400
401
case 0xE9: // MOV X,abs
402
data = READ_PC16( pc );
403
++pc;
404
data = READ( 0, data );
405
case 0xCD: // MOV X,imm
406
x = data;
407
nz = data;
408
goto inc_pc_loop;
409
410
case 0xFB: // MOV Y,dp+X
411
data = (uint8_t) (data + x);
412
case 0xEB: // MOV Y,dp
413
// 70% from timer
414
pc++;
415
READ_DP_TIMER( 0, data, y = nz );
416
goto loop;
417
418
case 0xEC:{// MOV Y,abs
419
int temp = READ_PC16( pc );
420
pc += 2;
421
READ_TIMER( 0, temp, y = nz );
422
//y = nz = READ( 0, temp );
423
goto loop;
424
}
425
426
case 0x8D: // MOV Y,imm
427
y = data;
428
nz = data;
429
goto inc_pc_loop;
430
431
// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2
432
433
ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A
434
WRITE( 0, data, a );
435
goto inc_pc_loop;
436
437
{
438
int temp;
439
case 0xCC: // MOV abs,Y
440
temp = y;
441
goto mov_abs_temp;
442
case 0xC9: // MOV abs,X
443
temp = x;
444
mov_abs_temp:
445
WRITE( 0, READ_PC16( pc ), temp );
446
pc += 2;
447
goto loop;
448
}
449
450
case 0xD9: // MOV dp+Y,X
451
data = (uint8_t) (data + y);
452
case 0xD8: // MOV dp,X
453
WRITE( 0, data + dp, x );
454
goto inc_pc_loop;
455
456
case 0xDB: // MOV dp+X,Y
457
data = (uint8_t) (data + x);
458
case 0xCB: // MOV dp,Y
459
WRITE( 0, data + dp, y );
460
goto inc_pc_loop;
461
462
// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3.
463
464
case 0x7D: // MOV A,X
465
a = x;
466
nz = x;
467
goto loop;
468
469
case 0xDD: // MOV A,Y
470
a = y;
471
nz = y;
472
goto loop;
473
474
case 0x5D: // MOV X,A
475
x = a;
476
nz = a;
477
goto loop;
478
479
case 0xFD: // MOV Y,A
480
y = a;
481
nz = a;
482
goto loop;
483
484
case 0x9D: // MOV X,SP
485
x = nz = GET_SP();
486
goto loop;
487
488
case 0xBD: // MOV SP,X
489
SET_SP( x );
490
goto loop;
491
492
//case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2)
493
494
case 0xAF: // MOV (X)+,A
495
WRITE_DP( 0, x, a + no_read_before_write );
496
x++;
497
goto loop;
498
499
// 5. 8-BIT LOGIC OPERATION COMMANDS
500
501
#define LOGICAL_OP( op, func )\
502
ADDR_MODES( op ) /* addr */\
503
data = READ( 0, data );\
504
case op: /* imm */\
505
nz = a func##= data;\
506
goto inc_pc_loop;\
507
{ unsigned addr;\
508
case op + 0x11: /* X,Y */\
509
data = READ_DP( -2, y );\
510
addr = x + dp;\
511
goto addr_##op;\
512
case op + 0x01: /* dp,dp */\
513
data = READ_DP( -3, data );\
514
case op + 0x10:{/*dp,imm*/\
515
uint8_t const* addr2 = pc + 1;\
516
pc += 2;\
517
addr = READ_PC( addr2 ) + dp;\
518
}\
519
addr_##op:\
520
nz = data func READ( -1, addr );\
521
WRITE( 0, addr, nz );\
522
goto loop;\
523
}
524
525
LOGICAL_OP( 0x28, & ); // AND
526
527
LOGICAL_OP( 0x08, | ); // OR
528
529
LOGICAL_OP( 0x48, ^ ); // EOR
530
531
// 4. 8-BIT ARITHMETIC OPERATION COMMANDS
532
533
ADDR_MODES( 0x68 ) // CMP addr
534
data = READ( 0, data );
535
case 0x68: // CMP imm
536
nz = a - data;
537
c = ~nz;
538
nz &= 0xFF;
539
goto inc_pc_loop;
540
541
case 0x79: // CMP (X),(Y)
542
data = READ_DP( -2, y );
543
nz = READ_DP( -1, x ) - data;
544
c = ~nz;
545
nz &= 0xFF;
546
goto loop;
547
548
case 0x69: // CMP dp,dp
549
data = READ_DP( -3, data );
550
case 0x78: // CMP dp,imm
551
nz = READ_DP( -1, READ_PC( ++pc ) ) - data;
552
c = ~nz;
553
nz &= 0xFF;
554
goto inc_pc_loop;
555
556
case 0x3E: // CMP X,dp
557
data += dp;
558
goto cmp_x_addr;
559
case 0x1E: // CMP X,abs
560
data = READ_PC16( pc );
561
pc++;
562
cmp_x_addr:
563
data = READ( 0, data );
564
case 0xC8: // CMP X,imm
565
nz = x - data;
566
c = ~nz;
567
nz &= 0xFF;
568
goto inc_pc_loop;
569
570
case 0x7E: // CMP Y,dp
571
data += dp;
572
goto cmp_y_addr;
573
case 0x5E: // CMP Y,abs
574
data = READ_PC16( pc );
575
pc++;
576
cmp_y_addr:
577
data = READ( 0, data );
578
case 0xAD: // CMP Y,imm
579
nz = y - data;
580
c = ~nz;
581
nz &= 0xFF;
582
goto inc_pc_loop;
583
584
{
585
int addr;
586
case 0xB9: // SBC (x),(y)
587
case 0x99: // ADC (x),(y)
588
pc--; // compensate for inc later
589
data = READ_DP( -2, y );
590
addr = x + dp;
591
goto adc_addr;
592
case 0xA9: // SBC dp,dp
593
case 0x89: // ADC dp,dp
594
data = READ_DP( -3, data );
595
case 0xB8: // SBC dp,imm
596
case 0x98: // ADC dp,imm
597
addr = READ_PC( ++pc ) + dp;
598
adc_addr:
599
nz = READ( -1, addr );
600
goto adc_data;
601
602
// catch ADC and SBC together, then decode later based on operand
603
#undef CASE
604
#define CASE( n ) case n: case (n) + 0x20:
605
ADDR_MODES( 0x88 ) // ADC/SBC addr
606
data = READ( 0, data );
607
case 0xA8: // SBC imm
608
case 0x88: // ADC imm
609
addr = -1; // A
610
nz = a;
611
adc_data: {
612
int flags;
613
if ( opcode >= 0xA0 ) // SBC
614
data ^= 0xFF;
615
616
flags = data ^ nz;
617
nz += data + (c >> 8 & 1);
618
flags ^= nz;
619
620
psw = (psw & ~(v40 | h08)) |
621
(flags >> 1 & h08) |
622
((flags + 0x80) >> 2 & v40);
623
c = nz;
624
if ( addr < 0 )
625
{
626
a = (uint8_t) nz;
627
goto inc_pc_loop;
628
}
629
WRITE( 0, addr, /*(uint8_t)*/ nz );
630
goto inc_pc_loop;
631
}
632
633
}
634
635
// 6. ADDITION & SUBTRACTION COMMANDS
636
637
#define INC_DEC_REG( reg, op )\
638
nz = reg op;\
639
reg = (uint8_t) nz;\
640
goto loop;
641
642
case 0xBC: INC_DEC_REG( a, + 1 ) // INC A
643
case 0x3D: INC_DEC_REG( x, + 1 ) // INC X
644
case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y
645
646
case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A
647
case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X
648
case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y
649
650
case 0x9B: // DEC dp+X
651
case 0xBB: // INC dp+X
652
data = (uint8_t) (data + x);
653
case 0x8B: // DEC dp
654
case 0xAB: // INC dp
655
data += dp;
656
goto inc_abs;
657
case 0x8C: // DEC abs
658
case 0xAC: // INC abs
659
data = READ_PC16( pc );
660
pc++;
661
inc_abs:
662
nz = (opcode >> 4 & 2) - 1;
663
nz += READ( -1, data );
664
WRITE( 0, data, /*(uint8_t)*/ nz );
665
goto inc_pc_loop;
666
667
// 7. SHIFT, ROTATION COMMANDS
668
669
case 0x5C: // LSR A
670
c = 0;
671
case 0x7C:{// ROR A
672
nz = (c >> 1 & 0x80) | (a >> 1);
673
c = a << 8;
674
a = nz;
675
goto loop;
676
}
677
678
case 0x1C: // ASL A
679
c = 0;
680
case 0x3C:{// ROL A
681
int temp = c >> 8 & 1;
682
c = a << 1;
683
nz = c | temp;
684
a = (uint8_t) nz;
685
goto loop;
686
}
687
688
case 0x0B: // ASL dp
689
c = 0;
690
data += dp;
691
goto rol_mem;
692
case 0x1B: // ASL dp+X
693
c = 0;
694
case 0x3B: // ROL dp+X
695
data = (uint8_t) (data + x);
696
case 0x2B: // ROL dp
697
data += dp;
698
goto rol_mem;
699
case 0x0C: // ASL abs
700
c = 0;
701
case 0x2C: // ROL abs
702
data = READ_PC16( pc );
703
pc++;
704
rol_mem:
705
nz = c >> 8 & 1;
706
nz |= (c = READ( -1, data ) << 1);
707
WRITE( 0, data, /*(uint8_t)*/ nz );
708
goto inc_pc_loop;
709
710
case 0x4B: // LSR dp
711
c = 0;
712
data += dp;
713
goto ror_mem;
714
case 0x5B: // LSR dp+X
715
c = 0;
716
case 0x7B: // ROR dp+X
717
data = (uint8_t) (data + x);
718
case 0x6B: // ROR dp
719
data += dp;
720
goto ror_mem;
721
case 0x4C: // LSR abs
722
c = 0;
723
case 0x6C: // ROR abs
724
data = READ_PC16( pc );
725
pc++;
726
ror_mem: {
727
int temp = READ( -1, data );
728
nz = (c >> 1 & 0x80) | (temp >> 1);
729
c = temp << 8;
730
WRITE( 0, data, nz );
731
goto inc_pc_loop;
732
}
733
734
case 0x9F: // XCN
735
nz = a = (a >> 4) | (uint8_t) (a << 4);
736
goto loop;
737
738
// 8. 16-BIT TRANSMISION COMMANDS
739
740
case 0xBA: // MOVW YA,dp
741
a = READ_DP( -2, data );
742
nz = (a & 0x7F) | (a >> 1);
743
y = READ_DP( 0, (uint8_t) (data + 1) );
744
nz |= y;
745
goto inc_pc_loop;
746
747
case 0xDA: // MOVW dp,YA
748
WRITE_DP( -1, data, a );
749
WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write );
750
goto inc_pc_loop;
751
752
// 9. 16-BIT OPERATION COMMANDS
753
754
case 0x3A: // INCW dp
755
case 0x1A:{// DECW dp
756
int temp;
757
// low byte
758
data += dp;
759
temp = READ( -3, data );
760
temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW
761
nz = ((temp >> 1) | temp) & 0x7F;
762
WRITE( -2, data, /*(uint8_t)*/ temp );
763
764
// high byte
765
data = (uint8_t) (data + 1) + dp;
766
temp = (uint8_t) ((temp >> 8) + READ( -1, data ));
767
nz |= temp;
768
WRITE( 0, data, temp );
769
770
goto inc_pc_loop;
771
}
772
773
case 0x7A: // ADDW YA,dp
774
case 0x9A:{// SUBW YA,dp
775
int lo = READ_DP( -2, data );
776
int hi = READ_DP( 0, (uint8_t) (data + 1) );
777
int result;
778
int flags;
779
780
if ( opcode == 0x9A ) // SUBW
781
{
782
lo = (lo ^ 0xFF) + 1;
783
hi ^= 0xFF;
784
}
785
786
lo += a;
787
result = y + hi + (lo >> 8);
788
flags = hi ^ y ^ result;
789
790
psw = (psw & ~(v40 | h08)) |
791
(flags >> 1 & h08) |
792
((flags + 0x80) >> 2 & v40);
793
c = result;
794
a = (uint8_t) lo;
795
result = (uint8_t) result;
796
y = result;
797
nz = (((lo >> 1) | lo) & 0x7F) | result;
798
799
goto inc_pc_loop;
800
}
801
802
case 0x5A: { // CMPW YA,dp
803
int temp = a - READ_DP( -1, data );
804
nz = ((temp >> 1) | temp) & 0x7F;
805
temp = y + (temp >> 8);
806
temp -= READ_DP( 0, (uint8_t) (data + 1) );
807
nz |= temp;
808
c = ~temp;
809
nz &= 0xFF;
810
goto inc_pc_loop;
811
}
812
813
// 10. MULTIPLICATION & DIVISON COMMANDS
814
815
case 0xCF: { // MUL YA
816
unsigned temp = y * a;
817
a = (uint8_t) temp;
818
nz = ((temp >> 1) | temp) & 0x7F;
819
y = temp >> 8;
820
nz |= y;
821
goto loop;
822
}
823
824
case 0x9E: // DIV YA,X
825
{
826
unsigned ya = y * 0x100 + a;
827
828
psw &= ~(h08 | v40);
829
830
if ( y >= x )
831
psw |= v40;
832
833
if ( (y & 15) >= (x & 15) )
834
psw |= h08;
835
836
if ( y < x * 2 )
837
{
838
a = ya / x;
839
y = ya - a * x;
840
}
841
else
842
{
843
a = 255 - (ya - x * 0x200) / (256 - x);
844
y = x + (ya - x * 0x200) % (256 - x);
845
}
846
847
nz = (uint8_t) a;
848
a = (uint8_t) a;
849
850
goto loop;
851
}
852
853
// 11. DECIMAL COMPENSATION COMMANDS
854
855
case 0xDF: // DAA
856
SUSPICIOUS_OPCODE( "DAA" );
857
if ( a > 0x99 || c & 0x100 )
858
{
859
a += 0x60;
860
c = 0x100;
861
}
862
863
if ( (a & 0x0F) > 9 || psw & h08 )
864
a += 0x06;
865
866
nz = a;
867
a = (uint8_t) a;
868
goto loop;
869
870
case 0xBE: // DAS
871
SUSPICIOUS_OPCODE( "DAS" );
872
if ( a > 0x99 || !(c & 0x100) )
873
{
874
a -= 0x60;
875
c = 0;
876
}
877
878
if ( (a & 0x0F) > 9 || !(psw & h08) )
879
a -= 0x06;
880
881
nz = a;
882
a = (uint8_t) a;
883
goto loop;
884
885
// 12. BRANCHING COMMANDS
886
887
case 0x2F: // BRA rel
888
pc += (BOOST::int8_t) data;
889
goto inc_pc_loop;
890
891
case 0x30: // BMI
892
BRANCH( (nz & nz_neg_mask) )
893
894
case 0x10: // BPL
895
BRANCH( !(nz & nz_neg_mask) )
896
897
case 0xB0: // BCS
898
BRANCH( c & 0x100 )
899
900
case 0x90: // BCC
901
BRANCH( !(c & 0x100) )
902
903
case 0x70: // BVS
904
BRANCH( psw & v40 )
905
906
case 0x50: // BVC
907
BRANCH( !(psw & v40) )
908
909
#define CBRANCH( cond )\
910
{\
911
pc++;\
912
if ( cond )\
913
goto cbranch_taken_loop;\
914
rel_time -= 2;\
915
goto inc_pc_loop;\
916
}
917
918
case 0x03: // BBS dp.bit,rel
919
case 0x23:
920
case 0x43:
921
case 0x63:
922
case 0x83:
923
case 0xA3:
924
case 0xC3:
925
case 0xE3:
926
CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 )
927
928
case 0x13: // BBC dp.bit,rel
929
case 0x33:
930
case 0x53:
931
case 0x73:
932
case 0x93:
933
case 0xB3:
934
case 0xD3:
935
case 0xF3:
936
CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) )
937
938
case 0xDE: // CBNE dp+X,rel
939
data = (uint8_t) (data + x);
940
// fall through
941
case 0x2E:{// CBNE dp,rel
942
int temp;
943
// 61% from timer
944
READ_DP_TIMER( -4, data, temp );
945
CBRANCH( temp != a )
946
}
947
948
case 0x6E: { // DBNZ dp,rel
949
unsigned temp = READ_DP( -4, data ) - 1;
950
WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write );
951
CBRANCH( temp )
952
}
953
954
case 0xFE: // DBNZ Y,rel
955
y = (uint8_t) (y - 1);
956
BRANCH( y )
957
958
case 0x1F: // JMP [abs+X]
959
SET_PC( READ_PC16( pc ) + x );
960
// fall through
961
case 0x5F: // JMP abs
962
SET_PC( READ_PC16( pc ) );
963
goto loop;
964
965
// 13. SUB-ROUTINE CALL RETURN COMMANDS
966
967
case 0x0F:{// BRK
968
int temp;
969
int ret_addr = GET_PC();
970
SUSPICIOUS_OPCODE( "BRK" );
971
SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified
972
PUSH16( ret_addr );
973
GET_PSW( temp );
974
psw = (psw | b10) & ~i04;
975
PUSH( temp );
976
goto loop;
977
}
978
979
case 0x4F:{// PCALL offset
980
int ret_addr = GET_PC() + 1;
981
SET_PC( 0xFF00 | data );
982
PUSH16( ret_addr );
983
goto loop;
984
}
985
986
case 0x01: // TCALL n
987
case 0x11:
988
case 0x21:
989
case 0x31:
990
case 0x41:
991
case 0x51:
992
case 0x61:
993
case 0x71:
994
case 0x81:
995
case 0x91:
996
case 0xA1:
997
case 0xB1:
998
case 0xC1:
999
case 0xD1:
1000
case 0xE1:
1001
case 0xF1: {
1002
int ret_addr = GET_PC();
1003
SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) );
1004
PUSH16( ret_addr );
1005
goto loop;
1006
}
1007
1008
// 14. STACK OPERATION COMMANDS
1009
1010
{
1011
int temp;
1012
case 0x7F: // RET1
1013
temp = *sp;
1014
SET_PC( GET_LE16( sp + 1 ) );
1015
sp += 3;
1016
goto set_psw;
1017
case 0x8E: // POP PSW
1018
POP( temp );
1019
set_psw:
1020
SET_PSW( temp );
1021
goto loop;
1022
}
1023
1024
case 0x0D: { // PUSH PSW
1025
int temp;
1026
GET_PSW( temp );
1027
PUSH( temp );
1028
goto loop;
1029
}
1030
1031
case 0x2D: // PUSH A
1032
PUSH( a );
1033
goto loop;
1034
1035
case 0x4D: // PUSH X
1036
PUSH( x );
1037
goto loop;
1038
1039
case 0x6D: // PUSH Y
1040
PUSH( y );
1041
goto loop;
1042
1043
case 0xAE: // POP A
1044
POP( a );
1045
goto loop;
1046
1047
case 0xCE: // POP X
1048
POP( x );
1049
goto loop;
1050
1051
case 0xEE: // POP Y
1052
POP( y );
1053
goto loop;
1054
1055
// 15. BIT OPERATION COMMANDS
1056
1057
case 0x02: // SET1
1058
case 0x22:
1059
case 0x42:
1060
case 0x62:
1061
case 0x82:
1062
case 0xA2:
1063
case 0xC2:
1064
case 0xE2:
1065
case 0x12: // CLR1
1066
case 0x32:
1067
case 0x52:
1068
case 0x72:
1069
case 0x92:
1070
case 0xB2:
1071
case 0xD2:
1072
case 0xF2: {
1073
int bit = 1 << (opcode >> 5);
1074
int mask = ~bit;
1075
if ( opcode & 0x10 )
1076
bit = 0;
1077
data += dp;
1078
WRITE( 0, data, (READ( -1, data ) & mask) | bit );
1079
goto inc_pc_loop;
1080
}
1081
1082
case 0x0E: // TSET1 abs
1083
case 0x4E: // TCLR1 abs
1084
data = READ_PC16( pc );
1085
pc += 2;
1086
{
1087
unsigned temp = READ( -2, data );
1088
nz = (uint8_t) (a - temp);
1089
temp &= ~a;
1090
if ( opcode == 0x0E )
1091
temp |= a;
1092
WRITE( 0, data, temp );
1093
}
1094
goto loop;
1095
1096
case 0x4A: // AND1 C,mem.bit
1097
c &= MEM_BIT( 0 );
1098
pc += 2;
1099
goto loop;
1100
1101
case 0x6A: // AND1 C,/mem.bit
1102
c &= ~MEM_BIT( 0 );
1103
pc += 2;
1104
goto loop;
1105
1106
case 0x0A: // OR1 C,mem.bit
1107
c |= MEM_BIT( -1 );
1108
pc += 2;
1109
goto loop;
1110
1111
case 0x2A: // OR1 C,/mem.bit
1112
c |= ~MEM_BIT( -1 );
1113
pc += 2;
1114
goto loop;
1115
1116
case 0x8A: // EOR1 C,mem.bit
1117
c ^= MEM_BIT( -1 );
1118
pc += 2;
1119
goto loop;
1120
1121
case 0xEA: // NOT1 mem.bit
1122
data = READ_PC16( pc );
1123
pc += 2;
1124
{
1125
unsigned temp = READ( -1, data & 0x1FFF );
1126
temp ^= 1 << (data >> 13);
1127
WRITE( 0, data & 0x1FFF, temp );
1128
}
1129
goto loop;
1130
1131
case 0xCA: // MOV1 mem.bit,C
1132
data = READ_PC16( pc );
1133
pc += 2;
1134
{
1135
unsigned temp = READ( -2, data & 0x1FFF );
1136
unsigned bit = data >> 13;
1137
temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit);
1138
WRITE( 0, data & 0x1FFF, temp + no_read_before_write );
1139
}
1140
goto loop;
1141
1142
case 0xAA: // MOV1 C,mem.bit
1143
c = MEM_BIT( 0 );
1144
pc += 2;
1145
goto loop;
1146
1147
// 16. PROGRAM PSW FLAG OPERATION COMMANDS
1148
1149
case 0x60: // CLRC
1150
c = 0;
1151
goto loop;
1152
1153
case 0x80: // SETC
1154
c = ~0;
1155
goto loop;
1156
1157
case 0xED: // NOTC
1158
c ^= 0x100;
1159
goto loop;
1160
1161
case 0xE0: // CLRV
1162
psw &= ~(v40 | h08);
1163
goto loop;
1164
1165
case 0x20: // CLRP
1166
dp = 0;
1167
goto loop;
1168
1169
case 0x40: // SETP
1170
dp = 0x100;
1171
goto loop;
1172
1173
case 0xA0: // EI
1174
SUSPICIOUS_OPCODE( "EI" );
1175
psw |= i04;
1176
goto loop;
1177
1178
case 0xC0: // DI
1179
SUSPICIOUS_OPCODE( "DI" );
1180
psw &= ~i04;
1181
goto loop;
1182
1183
// 17. OTHER COMMANDS
1184
1185
case 0x00: // NOP
1186
goto loop;
1187
1188
case 0xFF:{// STOP
1189
// handle PC wrap-around
1190
unsigned addr = GET_PC() - 1;
1191
if ( addr >= 0x10000 )
1192
{
1193
addr &= 0xFFFF;
1194
SET_PC( addr );
1195
dprintf( "SPC: PC wrapped around\n" );
1196
goto loop;
1197
}
1198
}
1199
// fall through
1200
case 0xEF: // SLEEP
1201
SUSPICIOUS_OPCODE( "STOP/SLEEP" );
1202
--pc;
1203
rel_time = 0;
1204
m.cpu_error = "SPC emulation error";
1205
goto stop;
1206
} // switch
1207
1208
assert( 0 ); // catch any unhandled instructions
1209
}
1210
out_of_time:
1211
rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode
1212
stop:
1213
1214
// Uncache registers
1215
if ( GET_PC() >= 0x10000 )
1216
dprintf( "SPC: PC wrapped around\n" );
1217
m.cpu_regs.pc = (uint16_t) GET_PC();
1218
m.cpu_regs.sp = ( uint8_t) GET_SP();
1219
m.cpu_regs.a = ( uint8_t) a;
1220
m.cpu_regs.x = ( uint8_t) x;
1221
m.cpu_regs.y = ( uint8_t) y;
1222
{
1223
int temp;
1224
GET_PSW( temp );
1225
m.cpu_regs.psw = (uint8_t) temp;
1226
}
1227
}
1228
SPC_CPU_RUN_FUNC_END
1229
1230