view snes_spc/SNES_SPC.cpp @ 3:95cdedd01422

allow user to select number of seconds to convert
author Robert McIntyre <rlm@mit.edu>
date Fri, 21 Oct 2011 06:44:35 -0700
parents e38dacceb958
children
line wrap: on
line source
1 // Core SPC emulation: CPU, timers, SMP registers, memory
3 // snes_spc 0.9.0. http://www.slack.net/~ant/
5 #include "SNES_SPC.h"
7 #include <string.h>
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 */
20 #include "blargg_source.h"
22 #define RAM (m.ram.ram)
23 #define REGS (m.smp_regs [0])
24 #define REGS_IN (m.smp_regs [1])
26 // (n ? n : 256)
27 #define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
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
35 #ifdef BLARGG_ENABLE_OPTIMIZER
36 #include BLARGG_ENABLE_OPTIMIZER
37 #endif
40 //// Timers
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
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 );
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 }
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 }
79 //// ROM
81 void SNES_SPC::enable_rom( int enable )
82 {
83 if ( m.rom_enabled != enable )
84 {
85 m.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 }
94 //// DSP
96 #if SPC_LESS_ACCURATE
97 int const max_reg_time = 29;
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,
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 };
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
141 int SNES_SPC::dsp_read( rel_time_t time )
142 {
143 RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] );
145 int result = dsp.read( REGS [r_dspaddr] & 0x7F );
147 #ifdef SPC_DSP_READ_HOOK
148 SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );
149 #endif
151 return result;
152 }
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 );
164 if ( r == SPC_DSP::r_koff )
165 {
166 m.skipped_koff |= data;
167 m.skipped_kon &= ~data;
168 }
169 }
170 #endif
172 #ifdef SPC_DSP_WRITE_HOOK
173 SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );
174 #endif
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 }
183 //// Memory access extras
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.
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 }
216 #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
217 #else
218 #define MEM_ACCESS( time, addr )
219 #endif
222 //// CPU write
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,
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,
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
280 // divided into multiple functions to keep rarely-used functionality separate
281 // so often-used functionality can be optimized better by compiler
283 // If write isn't preceded by read, data has this added to it
284 int const no_read_before_write = 0x2000;
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" );
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];
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;
321 if ( (rand() >> 4 & 0xFF) <= prob )
322 t->divider = (t->divider - b) & 0xFF;
323 }
324 #endif
325 t->period = period;
326 }
327 break;
328 }
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 );
336 if ( data < no_read_before_write / 2 )
337 run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0;
338 break;
340 // Registers that act like RAM
341 case 0x8:
342 case 0x9:
343 REGS_IN [addr] = (uint8_t) data;
344 break;
346 case r_test:
347 if ( (uint8_t) data != 0x0A )
348 dprintf( "SPC wrote to test register\n" );
349 break;
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 }
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 }
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 }
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 [i + rom_addr] == (uint8_t) data );
406 RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding
407 cpu_write( data, i + rom_addr - 0x10000, time );
408 }
409 }
411 int const bits_in_int = CHAR_BIT * sizeof (int);
413 void SNES_SPC::cpu_write( int data, int addr, rel_time_t time )
414 {
415 MEM_ACCESS( time, addr )
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;
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
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 }
451 //// CPU read
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 }
467 int SNES_SPC::cpu_read( int addr, rel_time_t time )
468 {
469 MEM_ACCESS( time, addr )
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;
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 }
503 return result;
504 }
507 //// Run
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;
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 }
531 int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
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 );
540 m.spc_time -= end_time;
541 m.extra_clocks += end_time;
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 <= 0 );
548 // Catch timers up to CPU
549 for ( int i = 0; i < timer_count; i++ )
550 run_timer( &m.timers [i], 0 );
552 // Catch DSP up to CPU
553 if ( m.dsp_time < 0 )
554 {
555 RUN_DSP( 0, max_reg_time );
556 }
558 // Save any extra samples beyond what should be generated
559 if ( m.buf_begin )
560 save_extra();
561 }
563 // Inclusion here allows static memory access functions and better optimization
564 #include "SPC_CPU.h"