Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
RishiRecon
GitHub Repository: RishiRecon/exploits
Path: blob/main/misc/emulator/xnes/snes9x/apu/SNES_SPC.cpp
28798 views
1
// Core SPC emulation: CPU, timers, SMP registers, memory
2
3
// snes_spc 0.9.0. http://www.slack.net/~ant/
4
5
#include "SNES_SPC.h"
6
7
#include <string.h>
8
9
/* Copyright (C) 2004-2007 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
#include "blargg_source.h"
21
22
#define RAM (m.ram.ram)
23
#define REGS (m.smp_regs [0])
24
#define REGS_IN (m.smp_regs [1])
25
26
// (n ? n : 256)
27
#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
28
29
// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which
30
// do crazy echo buffer accesses.
31
#ifndef SPC_MORE_ACCURACY
32
#define SPC_MORE_ACCURACY 0
33
#endif
34
35
#ifdef BLARGG_ENABLE_OPTIMIZER
36
#include BLARGG_ENABLE_OPTIMIZER
37
#endif
38
39
40
//// Timers
41
42
#if SPC_DISABLE_TEMPO
43
#define TIMER_DIV( t, n ) ((n) >> t->prescaler)
44
#define TIMER_MUL( t, n ) ((n) << t->prescaler)
45
#else
46
#define TIMER_DIV( t, n ) ((n) / t->prescaler)
47
#define TIMER_MUL( t, n ) ((n) * t->prescaler)
48
#endif
49
50
SNES_SPC::Timer* SNES_SPC::run_timer_( Timer* t, rel_time_t time )
51
{
52
int elapsed = TIMER_DIV( t, time - t->next_time ) + 1;
53
t->next_time += TIMER_MUL( t, elapsed );
54
55
if ( t->enabled )
56
{
57
int remain = IF_0_THEN_256( t->period - t->divider );
58
int divider = t->divider + elapsed;
59
int over = elapsed - remain;
60
if ( over >= 0 )
61
{
62
int n = over / t->period;
63
t->counter = (t->counter + 1 + n) & 0x0F;
64
divider = over - n * t->period;
65
}
66
t->divider = (uint8_t) divider;
67
}
68
return t;
69
}
70
71
inline SNES_SPC::Timer* SNES_SPC::run_timer( Timer* t, rel_time_t time )
72
{
73
if ( time >= t->next_time )
74
t = run_timer_( t, time );
75
return t;
76
}
77
78
79
//// ROM
80
81
void SNES_SPC::enable_rom( int enable )
82
{
83
if ( m.rom_enabled != enable )
84
{
85
m.rom_enabled = dsp.rom_enabled = enable;
86
if ( enable )
87
memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram );
88
memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size );
89
// TODO: ROM can still get overwritten when DSP writes to echo buffer
90
}
91
}
92
93
94
//// DSP
95
96
#if SPC_LESS_ACCURATE
97
int const max_reg_time = 29;
98
99
signed char const SNES_SPC::reg_times_ [256] =
100
{
101
-1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22,
102
2, 3, 0, 1,-12, 0, 1, 1, 7, 6, 14, 14, 27, 14, 14, 23,
103
5, 6, 3, 4, -1, 3, 4, 4, 10, 9, 14, 14, 26, -5, 14, 23,
104
8, 9, 6, 7, 2, 6, 7, 7, 13, 12, 14, 14, 27, -4, 14, 24,
105
11, 12, 9, 10, 5, 9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24,
106
14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24,
107
17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25,
108
20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25,
109
110
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
111
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
112
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
113
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
114
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
115
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
116
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
117
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
118
};
119
120
#define RUN_DSP( time, offset ) \
121
int count = (time) - (offset) - m.dsp_time;\
122
if ( count >= 0 )\
123
{\
124
int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\
125
m.dsp_time += clock_count;\
126
dsp.run( clock_count );\
127
}
128
#else
129
#define RUN_DSP( time, offset ) \
130
{\
131
int count = (time) - m.dsp_time;\
132
if ( !SPC_MORE_ACCURACY || count )\
133
{\
134
assert( count > 0 );\
135
m.dsp_time = (time);\
136
dsp.run( count );\
137
}\
138
}
139
#endif
140
141
int SNES_SPC::dsp_read( rel_time_t time )
142
{
143
RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] );
144
145
int result = dsp.read( REGS [r_dspaddr] & 0x7F );
146
147
#ifdef SPC_DSP_READ_HOOK
148
SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );
149
#endif
150
151
return result;
152
}
153
154
inline void SNES_SPC::dsp_write( int data, rel_time_t time )
155
{
156
RUN_DSP( time, reg_times [REGS [r_dspaddr]] )
157
#if SPC_LESS_ACCURATE
158
else if ( m.dsp_time == skipping_time )
159
{
160
int r = REGS [r_dspaddr];
161
if ( r == SPC_DSP::r_kon )
162
m.skipped_kon |= data & ~dsp.read( SPC_DSP::r_koff );
163
164
if ( r == SPC_DSP::r_koff )
165
{
166
m.skipped_koff |= data;
167
m.skipped_kon &= ~data;
168
}
169
}
170
#endif
171
172
#ifdef SPC_DSP_WRITE_HOOK
173
SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );
174
#endif
175
176
if ( REGS [r_dspaddr] <= 0x7F )
177
dsp.write( REGS [r_dspaddr], data );
178
else if ( !SPC_MORE_ACCURACY )
179
dprintf( "SPC wrote to DSP register > $7F\n" );
180
}
181
182
183
//// Memory access extras
184
185
#if SPC_MORE_ACCURACY
186
#define MEM_ACCESS( time, addr ) \
187
{\
188
if ( time >= m.dsp_time )\
189
{\
190
RUN_DSP( time, max_reg_time );\
191
}\
192
}
193
#elif !defined (NDEBUG)
194
// Debug-only check for read/write within echo buffer, since this might result in
195
// inaccurate emulation due to the DSP not being caught up to the present.
196
197
bool SNES_SPC::check_echo_access( int addr )
198
{
199
if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) )
200
{
201
int start = 0x100 * dsp.read( SPC_DSP::r_esa );
202
int size = 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
203
int end = start + (size ? size : 4);
204
if ( start <= addr && addr < end )
205
{
206
if ( !m.echo_accessed )
207
{
208
m.echo_accessed = 1;
209
return true;
210
}
211
}
212
}
213
return false;
214
}
215
216
#define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
217
#else
218
#define MEM_ACCESS( time, addr )
219
#endif
220
221
222
//// CPU write
223
224
#if SPC_MORE_ACCURACY
225
static unsigned char const glitch_probs [3] [256] =
226
{
227
0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B,
228
0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08,
229
0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09,
230
0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01,
231
0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05,
232
0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07,
233
0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07,
234
0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01,
235
0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09,
236
0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08,
237
0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03,
238
0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03,
239
0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07,
240
0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02,
241
0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02,
242
0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01,
243
244
0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07,
245
0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06,
246
0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09,
247
0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03,
248
0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07,
249
0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03,
250
0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06,
251
0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03,
252
0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05,
253
0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04,
254
0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05,
255
0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01,
256
0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05,
257
0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01,
258
0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03,
259
0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01,
260
261
0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A,
262
0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A,
263
0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A,
264
0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09,
265
0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09,
266
0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02,
267
0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07,
268
0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04,
269
0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A,
270
0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07,
271
0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04,
272
0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02,
273
0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06,
274
0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03,
275
0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02,
276
0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03,
277
};
278
#endif
279
280
// divided into multiple functions to keep rarely-used functionality separate
281
// so often-used functionality can be optimized better by compiler
282
283
// If write isn't preceded by read, data has this added to it
284
int const no_read_before_write = 0x2000;
285
286
void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr )
287
{
288
switch ( addr )
289
{
290
case r_t0target:
291
case r_t1target:
292
case r_t2target: {
293
Timer* t = &m.timers [addr - r_t0target];
294
int period = IF_0_THEN_256( data );
295
if ( t->period != period )
296
{
297
t = run_timer( t, time );
298
#if SPC_MORE_ACCURACY
299
// Insane behavior when target is written just after counter is
300
// clocked and counter matches new period and new period isn't 1, 2, 4, or 8
301
if ( t->divider == (period & 0xFF) &&
302
t->next_time == time + TIMER_MUL( t, 1 ) &&
303
((period - 1) | ~0x0F) & period )
304
{
305
//dprintf( "SPC pathological timer target write\n" );
306
307
// If the period is 3, 5, or 9, there's a probability this behavior won't occur,
308
// based on the previous period
309
int prob = 0xFF;
310
int old_period = t->period & 0xFF;
311
if ( period == 3 ) prob = glitch_probs [0] [old_period];
312
if ( period == 5 ) prob = glitch_probs [1] [old_period];
313
if ( period == 9 ) prob = glitch_probs [2] [old_period];
314
315
// The glitch suppresses incrementing of one of the counter bits, based on
316
// the lowest set bit in the new period
317
int b = 1;
318
while ( !(period & b) )
319
b <<= 1;
320
321
if ( (rand() >> 4 & 0xFF) <= prob )
322
t->divider = (t->divider - b) & 0xFF;
323
}
324
#endif
325
t->period = period;
326
}
327
break;
328
}
329
330
case r_t0out:
331
case r_t1out:
332
case r_t2out:
333
if ( !SPC_MORE_ACCURACY )
334
dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out );
335
336
if ( data < no_read_before_write / 2 )
337
run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0;
338
break;
339
340
// Registers that act like RAM
341
case 0x8:
342
case 0x9:
343
REGS_IN [addr] = (uint8_t) data;
344
break;
345
346
case r_test:
347
if ( (uint8_t) data != 0x0A )
348
dprintf( "SPC wrote to test register\n" );
349
break;
350
351
case r_control:
352
// port clears
353
if ( data & 0x10 )
354
{
355
REGS_IN [r_cpuio0] = 0;
356
REGS_IN [r_cpuio1] = 0;
357
}
358
if ( data & 0x20 )
359
{
360
REGS_IN [r_cpuio2] = 0;
361
REGS_IN [r_cpuio3] = 0;
362
}
363
364
// timers
365
{
366
for ( int i = 0; i < timer_count; i++ )
367
{
368
Timer* t = &m.timers [i];
369
int enabled = data >> i & 1;
370
if ( t->enabled != enabled )
371
{
372
t = run_timer( t, time );
373
t->enabled = enabled;
374
if ( enabled )
375
{
376
t->divider = 0;
377
t->counter = 0;
378
}
379
}
380
}
381
}
382
enable_rom( data & 0x80 );
383
break;
384
}
385
}
386
387
void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr )
388
{
389
if ( addr == r_dspdata ) // 99%
390
dsp_write( data, time );
391
else
392
cpu_write_smp_reg_( data, time, addr );
393
}
394
395
void SNES_SPC::cpu_write_high( int data, int i, rel_time_t time )
396
{
397
if ( i < rom_size )
398
{
399
m.hi_ram [i] = (uint8_t) data;
400
if ( m.rom_enabled )
401
RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM
402
}
403
else
404
{
405
assert( *(&(RAM [0]) + i + rom_addr) == (uint8_t) data );
406
*(&(RAM [0]) + i + rom_addr) = cpu_pad_fill; // restore overwritten padding
407
cpu_write( data, i + rom_addr - 0x10000, time );
408
}
409
}
410
411
int const bits_in_int = CHAR_BIT * sizeof (int);
412
413
void SNES_SPC::cpu_write( int data, int addr, rel_time_t time )
414
{
415
MEM_ACCESS( time, addr )
416
417
// RAM
418
RAM [addr] = (uint8_t) data;
419
int reg = addr - 0xF0;
420
if ( reg >= 0 ) // 64%
421
{
422
// $F0-$FF
423
if ( reg < reg_count ) // 87%
424
{
425
REGS [reg] = (uint8_t) data;
426
427
// Ports
428
#ifdef SPC_PORT_WRITE_HOOK
429
if ( (unsigned) (reg - r_cpuio0) < port_count )
430
SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0),
431
(uint8_t) data, &REGS [r_cpuio0] );
432
#endif
433
434
// Registers other than $F2 and $F4-$F7
435
//if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
436
// TODO: this is a bit on the fragile side
437
if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%
438
cpu_write_smp_reg( data, time, reg );
439
}
440
// High mem/address wrap-around
441
else
442
{
443
reg -= rom_addr - 0xF0;
444
if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around
445
cpu_write_high( data, reg, time );
446
}
447
}
448
}
449
450
451
//// CPU read
452
453
inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time )
454
{
455
int result = REGS_IN [reg];
456
reg -= r_dspaddr;
457
// DSP addr and data
458
if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3
459
{
460
result = REGS [r_dspaddr];
461
if ( (unsigned) reg == 1 )
462
result = dsp_read( time ); // 0xF3
463
}
464
return result;
465
}
466
467
int SNES_SPC::cpu_read( int addr, rel_time_t time )
468
{
469
MEM_ACCESS( time, addr )
470
471
// RAM
472
int result = RAM [addr];
473
int reg = addr - 0xF0;
474
if ( reg >= 0 ) // 40%
475
{
476
reg -= 0x10;
477
if ( (unsigned) reg >= 0xFF00 ) // 21%
478
{
479
reg += 0x10 - r_t0out;
480
481
// Timers
482
if ( (unsigned) reg < timer_count ) // 90%
483
{
484
Timer* t = &m.timers [reg];
485
if ( time >= t->next_time )
486
t = run_timer_( t, time );
487
result = t->counter;
488
t->counter = 0;
489
}
490
// Other registers
491
else if ( reg < 0 ) // 10%
492
{
493
result = cpu_read_smp_reg( reg + r_t0out, time );
494
}
495
else // 1%
496
{
497
assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 );
498
result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time );
499
}
500
}
501
}
502
503
return result;
504
}
505
506
507
//// Run
508
509
// Prefix and suffix for CPU emulator function
510
#define SPC_CPU_RUN_FUNC \
511
BOOST::uint8_t* SNES_SPC::run_until_( time_t end_time )\
512
{\
513
rel_time_t rel_time = m.spc_time - end_time;\
514
/*assert( rel_time <= 0 );*/\
515
m.spc_time = end_time;\
516
m.dsp_time += rel_time;\
517
m.timers [0].next_time += rel_time;\
518
m.timers [1].next_time += rel_time;\
519
m.timers [2].next_time += rel_time;
520
521
#define SPC_CPU_RUN_FUNC_END \
522
m.spc_time += rel_time;\
523
m.dsp_time -= rel_time;\
524
m.timers [0].next_time -= rel_time;\
525
m.timers [1].next_time -= rel_time;\
526
m.timers [2].next_time -= rel_time;\
527
/*assert( m.spc_time >= end_time );*/\
528
return &REGS [r_cpuio0];\
529
}
530
531
int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
532
533
void SNES_SPC::end_frame( time_t end_time )
534
{
535
// Catch CPU up to as close to end as possible. If final instruction
536
// would exceed end, does NOT execute it and leaves m.spc_time < end.
537
if ( end_time > m.spc_time )
538
run_until_( end_time );
539
540
m.spc_time -= end_time;
541
m.extra_clocks += end_time;
542
543
// Greatest number of clocks early that emulation can stop early due to
544
// not being able to execute current instruction without going over
545
// allowed time.
546
assert( -cpu_lag_max <= m.spc_time && m.spc_time <= cpu_lag_max );
547
548
// Catch timers up to CPU
549
for ( int i = 0; i < timer_count; i++ )
550
run_timer( &m.timers [i], 0 );
551
552
// Catch DSP up to CPU
553
if ( m.dsp_time < 0 )
554
{
555
RUN_DSP( 0, max_reg_time );
556
}
557
558
// Save any extra samples beyond what should be generated
559
if ( m.buf_begin )
560
save_extra();
561
}
562
563
// Inclusion here allows static memory access functions and better optimization
564
#include "SPC_CPU.h"
565
566