Mercurial > spc_convert
view snes_spc/SNES_SPC.cpp @ 9:477c36226481 tip
added old scripts for historical interest.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 21 Oct 2011 07:46:18 -0700 |
parents | e38dacceb958 |
children |
line wrap: on
line source
1 // Core SPC emulation: CPU, timers, SMP registers, memory3 // 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; you10 can redistribute it and/or modify it under the terms of the GNU Lesser11 General Public License as published by the Free Software Foundation; either12 version 2.1 of the License, or (at your option) any later version. This13 module is distributed in the hope that it will be useful, but WITHOUT ANY14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more16 details. You should have received a copy of the GNU Lesser General Public17 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, which30 // do crazy echo buffer accesses.31 #ifndef SPC_MORE_ACCURACY32 #define SPC_MORE_ACCURACY 033 #endif35 #ifdef BLARGG_ENABLE_OPTIMIZER36 #include BLARGG_ENABLE_OPTIMIZER37 #endif40 //// Timers42 #if SPC_DISABLE_TEMPO43 #define TIMER_DIV( t, n ) ((n) >> t->prescaler)44 #define TIMER_MUL( t, n ) ((n) << t->prescaler)45 #else46 #define TIMER_DIV( t, n ) ((n) / t->prescaler)47 #define TIMER_MUL( t, n ) ((n) * t->prescaler)48 #endif50 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 //// ROM81 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 buffer90 }91 }94 //// DSP96 #if SPC_LESS_ACCURATE97 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 #else129 #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 #endif141 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_HOOK148 SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );149 #endif151 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_ACCURATE158 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 #endif172 #ifdef SPC_DSP_WRITE_HOOK173 SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );174 #endif176 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 extras185 #if SPC_MORE_ACCURACY186 #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 in195 // 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 #else218 #define MEM_ACCESS( time, addr )219 #endif222 //// CPU write224 #if SPC_MORE_ACCURACY225 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 #endif280 // divided into multiple functions to keep rarely-used functionality separate281 // so often-used functionality can be optimized better by compiler283 // If write isn't preceded by read, data has this added to it284 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_ACCURACY299 // Insane behavior when target is written just after counter is300 // clocked and counter matches new period and new period isn't 1, 2, 4, or 8301 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 period309 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 on316 // the lowest set bit in the new period317 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 #endif325 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 RAM341 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 clears353 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 // timers365 {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 else392 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 ROM402 }403 else404 {405 assert( RAM [i + rom_addr] == (uint8_t) data );406 RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding407 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 // RAM418 RAM [addr] = (uint8_t) data;419 int reg = addr - 0xF0;420 if ( reg >= 0 ) // 64%421 {422 // $F0-$FF423 if ( reg < reg_count ) // 87%424 {425 REGS [reg] = (uint8_t) data;427 // Ports428 #ifdef SPC_PORT_WRITE_HOOK429 if ( (unsigned) (reg - r_cpuio0) < port_count )430 SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0),431 (uint8_t) data, ®S [r_cpuio0] );432 #endif434 // Registers other than $F2 and $F4-$F7435 //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )436 // TODO: this is a bit on the fragile side437 if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%438 cpu_write_smp_reg( data, time, reg );439 }440 // High mem/address wrap-around441 else442 {443 reg -= rom_addr - 0xF0;444 if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around445 cpu_write_high( data, reg, time );446 }447 }448 }451 //// CPU read453 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 data458 if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3459 {460 result = REGS [r_dspaddr];461 if ( (unsigned) reg == 1 )462 result = dsp_read( time ); // 0xF3463 }464 return result;465 }467 int SNES_SPC::cpu_read( int addr, rel_time_t time )468 {469 MEM_ACCESS( time, addr )471 // RAM472 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 // Timers482 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 registers491 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 //// Run509 // Prefix and suffix for CPU emulator function510 #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 ®S [r_cpuio0];\529 }531 int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks533 void SNES_SPC::end_frame( time_t end_time )534 {535 // Catch CPU up to as close to end as possible. If final instruction536 // 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 to544 // not being able to execute current instruction without going over545 // allowed time.546 assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 );548 // Catch timers up to CPU549 for ( int i = 0; i < timer_count; i++ )550 run_timer( &m.timers [i], 0 );552 // Catch DSP up to CPU553 if ( m.dsp_time < 0 )554 {555 RUN_DSP( 0, max_reg_time );556 }558 // Save any extra samples beyond what should be generated559 if ( m.buf_begin )560 save_extra();561 }563 // Inclusion here allows static memory access functions and better optimization564 #include "SPC_CPU.h"