diff snes_spc/SNES_SPC.cpp @ 0:e38dacceb958

initial import
author Robert McIntyre <rlm@mit.edu>
date Fri, 21 Oct 2011 05:53:11 -0700
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/snes_spc/SNES_SPC.cpp	Fri Oct 21 05:53:11 2011 -0700
     1.3 @@ -0,0 +1,564 @@
     1.4 +// Core SPC emulation: CPU, timers, SMP registers, memory
     1.5 +
     1.6 +// snes_spc 0.9.0. http://www.slack.net/~ant/
     1.7 +
     1.8 +#include "SNES_SPC.h"
     1.9 +
    1.10 +#include <string.h>
    1.11 +
    1.12 +/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
    1.13 +can redistribute it and/or modify it under the terms of the GNU Lesser
    1.14 +General Public License as published by the Free Software Foundation; either
    1.15 +version 2.1 of the License, or (at your option) any later version. This
    1.16 +module is distributed in the hope that it will be useful, but WITHOUT ANY
    1.17 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    1.18 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
    1.19 +details. You should have received a copy of the GNU Lesser General Public
    1.20 +License along with this module; if not, write to the Free Software Foundation,
    1.21 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
    1.22 +
    1.23 +#include "blargg_source.h"
    1.24 +
    1.25 +#define RAM         (m.ram.ram)
    1.26 +#define REGS        (m.smp_regs [0])
    1.27 +#define REGS_IN     (m.smp_regs [1])
    1.28 +
    1.29 +// (n ? n : 256)
    1.30 +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
    1.31 +
    1.32 +// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which
    1.33 +// do crazy echo buffer accesses.
    1.34 +#ifndef SPC_MORE_ACCURACY
    1.35 +	#define SPC_MORE_ACCURACY 0
    1.36 +#endif
    1.37 +
    1.38 +#ifdef BLARGG_ENABLE_OPTIMIZER
    1.39 +	#include BLARGG_ENABLE_OPTIMIZER
    1.40 +#endif
    1.41 +
    1.42 +
    1.43 +//// Timers
    1.44 +
    1.45 +#if SPC_DISABLE_TEMPO
    1.46 +	#define TIMER_DIV( t, n ) ((n) >> t->prescaler)
    1.47 +	#define TIMER_MUL( t, n ) ((n) << t->prescaler)
    1.48 +#else
    1.49 +	#define TIMER_DIV( t, n ) ((n) / t->prescaler)
    1.50 +	#define TIMER_MUL( t, n ) ((n) * t->prescaler)
    1.51 +#endif
    1.52 +
    1.53 +SNES_SPC::Timer* SNES_SPC::run_timer_( Timer* t, rel_time_t time )
    1.54 +{
    1.55 +	int elapsed = TIMER_DIV( t, time - t->next_time ) + 1;
    1.56 +	t->next_time += TIMER_MUL( t, elapsed );
    1.57 +	
    1.58 +	if ( t->enabled )
    1.59 +	{
    1.60 +		int remain = IF_0_THEN_256( t->period - t->divider );
    1.61 +		int divider = t->divider + elapsed;
    1.62 +		int over = elapsed - remain;
    1.63 +		if ( over >= 0 )
    1.64 +		{
    1.65 +			int n = over / t->period;
    1.66 +			t->counter = (t->counter + 1 + n) & 0x0F;
    1.67 +			divider = over - n * t->period;
    1.68 +		}
    1.69 +		t->divider = (uint8_t) divider;
    1.70 +	}
    1.71 +	return t;
    1.72 +}
    1.73 +
    1.74 +inline SNES_SPC::Timer* SNES_SPC::run_timer( Timer* t, rel_time_t time )
    1.75 +{
    1.76 +	if ( time >= t->next_time )
    1.77 +		t = run_timer_( t, time );
    1.78 +	return t;
    1.79 +}
    1.80 +
    1.81 +
    1.82 +//// ROM
    1.83 +
    1.84 +void SNES_SPC::enable_rom( int enable )
    1.85 +{
    1.86 +	if ( m.rom_enabled != enable )
    1.87 +	{
    1.88 +		m.rom_enabled = enable;
    1.89 +		if ( enable )
    1.90 +			memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram );
    1.91 +		memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size );
    1.92 +		// TODO: ROM can still get overwritten when DSP writes to echo buffer
    1.93 +	}
    1.94 +}
    1.95 +
    1.96 +
    1.97 +//// DSP
    1.98 +
    1.99 +#if SPC_LESS_ACCURATE
   1.100 +	int const max_reg_time = 29;
   1.101 +	
   1.102 +	signed char const SNES_SPC::reg_times_ [256] =
   1.103 +	{
   1.104 +		 -1,  0,-11,-10,-15,-11, -2, -2,  4,  3, 14, 14, 26, 26, 14, 22,
   1.105 +		  2,  3,  0,  1,-12,  0,  1,  1,  7,  6, 14, 14, 27, 14, 14, 23,
   1.106 +		  5,  6,  3,  4, -1,  3,  4,  4, 10,  9, 14, 14, 26, -5, 14, 23,
   1.107 +		  8,  9,  6,  7,  2,  6,  7,  7, 13, 12, 14, 14, 27, -4, 14, 24,
   1.108 +		 11, 12,  9, 10,  5,  9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24,
   1.109 +		 14, 15, 12, 13,  8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24,
   1.110 +		 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25,
   1.111 +		 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25,
   1.112 +		 
   1.113 +		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
   1.114 +		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
   1.115 +		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
   1.116 +		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
   1.117 +		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
   1.118 +		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
   1.119 +		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
   1.120 +		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
   1.121 +	};
   1.122 +	
   1.123 +	#define RUN_DSP( time, offset ) \
   1.124 +		int count = (time) - (offset) - m.dsp_time;\
   1.125 +		if ( count >= 0 )\
   1.126 +		{\
   1.127 +			int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\
   1.128 +			m.dsp_time += clock_count;\
   1.129 +			dsp.run( clock_count );\
   1.130 +		}
   1.131 +#else
   1.132 +	#define RUN_DSP( time, offset ) \
   1.133 +		{\
   1.134 +			int count = (time) - m.dsp_time;\
   1.135 +			if ( !SPC_MORE_ACCURACY || count )\
   1.136 +			{\
   1.137 +				assert( count > 0 );\
   1.138 +				m.dsp_time = (time);\
   1.139 +				dsp.run( count );\
   1.140 +			}\
   1.141 +		}
   1.142 +#endif
   1.143 +
   1.144 +int SNES_SPC::dsp_read( rel_time_t time )
   1.145 +{
   1.146 +	RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] );
   1.147 +	
   1.148 +	int result = dsp.read( REGS [r_dspaddr] & 0x7F );
   1.149 +	
   1.150 +	#ifdef SPC_DSP_READ_HOOK
   1.151 +		SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );
   1.152 +	#endif
   1.153 +	
   1.154 +	return result;
   1.155 +}
   1.156 +
   1.157 +inline void SNES_SPC::dsp_write( int data, rel_time_t time )
   1.158 +{
   1.159 +	RUN_DSP( time, reg_times [REGS [r_dspaddr]] )
   1.160 +	#if SPC_LESS_ACCURATE
   1.161 +		else if ( m.dsp_time == skipping_time )
   1.162 +		{
   1.163 +			int r = REGS [r_dspaddr];
   1.164 +			if ( r == SPC_DSP::r_kon )
   1.165 +				m.skipped_kon |= data & ~dsp.read( SPC_DSP::r_koff );
   1.166 +			
   1.167 +			if ( r == SPC_DSP::r_koff )
   1.168 +			{
   1.169 +				m.skipped_koff |= data;
   1.170 +				m.skipped_kon &= ~data;
   1.171 +			}
   1.172 +		}
   1.173 +	#endif
   1.174 +	
   1.175 +	#ifdef SPC_DSP_WRITE_HOOK
   1.176 +		SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );
   1.177 +	#endif
   1.178 +	
   1.179 +	if ( REGS [r_dspaddr] <= 0x7F )
   1.180 +		dsp.write( REGS [r_dspaddr], data );
   1.181 +	else if ( !SPC_MORE_ACCURACY )
   1.182 +		dprintf( "SPC wrote to DSP register > $7F\n" );
   1.183 +}
   1.184 +
   1.185 +
   1.186 +//// Memory access extras
   1.187 +
   1.188 +#if SPC_MORE_ACCURACY
   1.189 +	#define MEM_ACCESS( time, addr ) \
   1.190 +	{\
   1.191 +		if ( time >= m.dsp_time )\
   1.192 +		{\
   1.193 +			RUN_DSP( time, max_reg_time );\
   1.194 +		}\
   1.195 +	}
   1.196 +#elif !defined (NDEBUG)
   1.197 +	// Debug-only check for read/write within echo buffer, since this might result in
   1.198 +	// inaccurate emulation due to the DSP not being caught up to the present.
   1.199 +	
   1.200 +	bool SNES_SPC::check_echo_access( int addr )
   1.201 +	{
   1.202 +		if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) )
   1.203 +		{
   1.204 +			int start = 0x100 * dsp.read( SPC_DSP::r_esa );
   1.205 +			int size  = 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
   1.206 +			int end   = start + (size ? size : 4);
   1.207 +			if ( start <= addr && addr < end )
   1.208 +			{
   1.209 +				if ( !m.echo_accessed )
   1.210 +				{
   1.211 +					m.echo_accessed = 1;
   1.212 +					return true;
   1.213 +				}
   1.214 +			}
   1.215 +		}
   1.216 +		return false;
   1.217 +	}
   1.218 +	
   1.219 +	#define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
   1.220 +#else
   1.221 +	#define MEM_ACCESS( time, addr )
   1.222 +#endif
   1.223 +
   1.224 +
   1.225 +//// CPU write
   1.226 +
   1.227 +#if SPC_MORE_ACCURACY
   1.228 +static unsigned char const glitch_probs [3] [256] =
   1.229 +{
   1.230 +	0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B,
   1.231 +	0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08,
   1.232 +	0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09,
   1.233 +	0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01,
   1.234 +	0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05,
   1.235 +	0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07,
   1.236 +	0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07,
   1.237 +	0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01,
   1.238 +	0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09,
   1.239 +	0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08,
   1.240 +	0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03,
   1.241 +	0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03,
   1.242 +	0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07,
   1.243 +	0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02,
   1.244 +	0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02,
   1.245 +	0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01,
   1.246 +	
   1.247 +	0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07,
   1.248 +	0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06,
   1.249 +	0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09,
   1.250 +	0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03,
   1.251 +	0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07,
   1.252 +	0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03,
   1.253 +	0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06,
   1.254 +	0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03,
   1.255 +	0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05,
   1.256 +	0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04,
   1.257 +	0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05,
   1.258 +	0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01,
   1.259 +	0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05,
   1.260 +	0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01,
   1.261 +	0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03,
   1.262 +	0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01,
   1.263 +	
   1.264 +	0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A,
   1.265 +	0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A,
   1.266 +	0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A,
   1.267 +	0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09,
   1.268 +	0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09,
   1.269 +	0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02,
   1.270 +	0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07,
   1.271 +	0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04,
   1.272 +	0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A,
   1.273 +	0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07,
   1.274 +	0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04,
   1.275 +	0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02,
   1.276 +	0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06,
   1.277 +	0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03,
   1.278 +	0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02,
   1.279 +	0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03,
   1.280 +};
   1.281 +#endif
   1.282 +
   1.283 +// divided into multiple functions to keep rarely-used functionality separate
   1.284 +// so often-used functionality can be optimized better by compiler
   1.285 +
   1.286 +// If write isn't preceded by read, data has this added to it
   1.287 +int const no_read_before_write = 0x2000;
   1.288 +
   1.289 +void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr )
   1.290 +{
   1.291 +	switch ( addr )
   1.292 +	{
   1.293 +	case r_t0target:
   1.294 +	case r_t1target:
   1.295 +	case r_t2target: {
   1.296 +		Timer* t = &m.timers [addr - r_t0target];
   1.297 +		int period = IF_0_THEN_256( data );
   1.298 +		if ( t->period != period )
   1.299 +		{
   1.300 +			t = run_timer( t, time );
   1.301 +			#if SPC_MORE_ACCURACY
   1.302 +				// Insane behavior when target is written just after counter is
   1.303 +				// clocked and counter matches new period and new period isn't 1, 2, 4, or 8
   1.304 +				if ( t->divider == (period & 0xFF) &&
   1.305 +						t->next_time == time + TIMER_MUL( t, 1 ) &&
   1.306 +						((period - 1) | ~0x0F) & period )
   1.307 +				{
   1.308 +					//dprintf( "SPC pathological timer target write\n" );
   1.309 +					
   1.310 +					// If the period is 3, 5, or 9, there's a probability this behavior won't occur,
   1.311 +					// based on the previous period
   1.312 +					int prob = 0xFF;
   1.313 +					int old_period = t->period & 0xFF;
   1.314 +					if ( period == 3 ) prob = glitch_probs [0] [old_period];
   1.315 +					if ( period == 5 ) prob = glitch_probs [1] [old_period];
   1.316 +					if ( period == 9 ) prob = glitch_probs [2] [old_period];
   1.317 +					
   1.318 +					// The glitch suppresses incrementing of one of the counter bits, based on
   1.319 +					// the lowest set bit in the new period
   1.320 +					int b = 1;
   1.321 +					while ( !(period & b) )
   1.322 +						b <<= 1;
   1.323 +					
   1.324 +					if ( (rand() >> 4 & 0xFF) <= prob )
   1.325 +						t->divider = (t->divider - b) & 0xFF;
   1.326 +				}
   1.327 +			#endif
   1.328 +			t->period = period;
   1.329 +		}
   1.330 +		break;
   1.331 +	}
   1.332 +	
   1.333 +	case r_t0out:
   1.334 +	case r_t1out:
   1.335 +	case r_t2out:
   1.336 +		if ( !SPC_MORE_ACCURACY )
   1.337 +			dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out );
   1.338 +		
   1.339 +		if ( data < no_read_before_write  / 2 )
   1.340 +			run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0;
   1.341 +		break;
   1.342 +	
   1.343 +	// Registers that act like RAM
   1.344 +	case 0x8:
   1.345 +	case 0x9:
   1.346 +		REGS_IN [addr] = (uint8_t) data;
   1.347 +		break;
   1.348 +	
   1.349 +	case r_test:
   1.350 +		if ( (uint8_t) data != 0x0A )
   1.351 +			dprintf( "SPC wrote to test register\n" );
   1.352 +		break;
   1.353 +	
   1.354 +	case r_control:
   1.355 +		// port clears
   1.356 +		if ( data & 0x10 )
   1.357 +		{
   1.358 +			REGS_IN [r_cpuio0] = 0;
   1.359 +			REGS_IN [r_cpuio1] = 0;
   1.360 +		}
   1.361 +		if ( data & 0x20 )
   1.362 +		{
   1.363 +			REGS_IN [r_cpuio2] = 0;
   1.364 +			REGS_IN [r_cpuio3] = 0;
   1.365 +		}
   1.366 +		
   1.367 +		// timers
   1.368 +		{
   1.369 +			for ( int i = 0; i < timer_count; i++ )
   1.370 +			{
   1.371 +				Timer* t = &m.timers [i];
   1.372 +				int enabled = data >> i & 1;
   1.373 +				if ( t->enabled != enabled )
   1.374 +				{
   1.375 +					t = run_timer( t, time );
   1.376 +					t->enabled = enabled;
   1.377 +					if ( enabled )
   1.378 +					{
   1.379 +						t->divider = 0;
   1.380 +						t->counter = 0;
   1.381 +					}
   1.382 +				}
   1.383 +			}
   1.384 +		}
   1.385 +		enable_rom( data & 0x80 );
   1.386 +		break;
   1.387 +	}
   1.388 +}
   1.389 +
   1.390 +void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr )
   1.391 +{
   1.392 +	if ( addr == r_dspdata ) // 99%
   1.393 +		dsp_write( data, time );
   1.394 +	else
   1.395 +		cpu_write_smp_reg_( data, time, addr );
   1.396 +}
   1.397 +
   1.398 +void SNES_SPC::cpu_write_high( int data, int i, rel_time_t time )
   1.399 +{
   1.400 +	if ( i < rom_size )
   1.401 +	{
   1.402 +		m.hi_ram [i] = (uint8_t) data;
   1.403 +		if ( m.rom_enabled )
   1.404 +			RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM
   1.405 +	}
   1.406 +	else
   1.407 +	{
   1.408 +		assert( RAM [i + rom_addr] == (uint8_t) data );
   1.409 +		RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding
   1.410 +		cpu_write( data, i + rom_addr - 0x10000, time );
   1.411 +	}
   1.412 +}
   1.413 +
   1.414 +int const bits_in_int = CHAR_BIT * sizeof (int);
   1.415 +
   1.416 +void SNES_SPC::cpu_write( int data, int addr, rel_time_t time )
   1.417 +{
   1.418 +	MEM_ACCESS( time, addr )
   1.419 +	
   1.420 +	// RAM
   1.421 +	RAM [addr] = (uint8_t) data;
   1.422 +	int reg = addr - 0xF0;
   1.423 +	if ( reg >= 0 ) // 64%
   1.424 +	{
   1.425 +		// $F0-$FF
   1.426 +		if ( reg < reg_count ) // 87%
   1.427 +		{
   1.428 +			REGS [reg] = (uint8_t) data;
   1.429 +			
   1.430 +			// Ports
   1.431 +			#ifdef SPC_PORT_WRITE_HOOK
   1.432 +				if ( (unsigned) (reg - r_cpuio0) < port_count )
   1.433 +					SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0),
   1.434 +							(uint8_t) data, &REGS [r_cpuio0] );
   1.435 +			#endif
   1.436 +			
   1.437 +			// Registers other than $F2 and $F4-$F7
   1.438 +			//if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
   1.439 +			// TODO: this is a bit on the fragile side
   1.440 +			if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%
   1.441 +				cpu_write_smp_reg( data, time, reg );
   1.442 +		}
   1.443 +		// High mem/address wrap-around
   1.444 +		else
   1.445 +		{
   1.446 +			reg -= rom_addr - 0xF0;
   1.447 +			if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around
   1.448 +				cpu_write_high( data, reg, time );
   1.449 +		}
   1.450 +	}
   1.451 +}
   1.452 +
   1.453 +
   1.454 +//// CPU read
   1.455 +
   1.456 +inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time )
   1.457 +{
   1.458 +	int result = REGS_IN [reg];
   1.459 +	reg -= r_dspaddr;
   1.460 +	// DSP addr and data
   1.461 +	if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3
   1.462 +	{
   1.463 +		result = REGS [r_dspaddr];
   1.464 +		if ( (unsigned) reg == 1 )
   1.465 +			result = dsp_read( time ); // 0xF3
   1.466 +	}
   1.467 +	return result;
   1.468 +}
   1.469 +
   1.470 +int SNES_SPC::cpu_read( int addr, rel_time_t time )
   1.471 +{
   1.472 +	MEM_ACCESS( time, addr )
   1.473 +	
   1.474 +	// RAM
   1.475 +	int result = RAM [addr];
   1.476 +	int reg = addr - 0xF0;
   1.477 +	if ( reg >= 0 ) // 40%
   1.478 +	{
   1.479 +		reg -= 0x10;
   1.480 +		if ( (unsigned) reg >= 0xFF00 ) // 21%
   1.481 +		{
   1.482 +			reg += 0x10 - r_t0out;
   1.483 +			
   1.484 +			// Timers
   1.485 +			if ( (unsigned) reg < timer_count ) // 90%
   1.486 +			{
   1.487 +				Timer* t = &m.timers [reg];
   1.488 +				if ( time >= t->next_time )
   1.489 +					t = run_timer_( t, time );
   1.490 +				result = t->counter;
   1.491 +				t->counter = 0;
   1.492 +			}
   1.493 +			// Other registers
   1.494 +			else if ( reg < 0 ) // 10%
   1.495 +			{
   1.496 +				result = cpu_read_smp_reg( reg + r_t0out, time );
   1.497 +			}
   1.498 +			else // 1%
   1.499 +			{
   1.500 +				assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 );
   1.501 +				result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time );
   1.502 +			}
   1.503 +		}
   1.504 +	}
   1.505 +	
   1.506 +	return result;
   1.507 +}
   1.508 +
   1.509 +
   1.510 +//// Run
   1.511 +
   1.512 +// Prefix and suffix for CPU emulator function
   1.513 +#define SPC_CPU_RUN_FUNC \
   1.514 +BOOST::uint8_t* SNES_SPC::run_until_( time_t end_time )\
   1.515 +{\
   1.516 +	rel_time_t rel_time = m.spc_time - end_time;\
   1.517 +	assert( rel_time <= 0 );\
   1.518 +	m.spc_time = end_time;\
   1.519 +	m.dsp_time += rel_time;\
   1.520 +	m.timers [0].next_time += rel_time;\
   1.521 +	m.timers [1].next_time += rel_time;\
   1.522 +	m.timers [2].next_time += rel_time;
   1.523 +
   1.524 +#define SPC_CPU_RUN_FUNC_END \
   1.525 +	m.spc_time += rel_time;\
   1.526 +	m.dsp_time -= rel_time;\
   1.527 +	m.timers [0].next_time -= rel_time;\
   1.528 +	m.timers [1].next_time -= rel_time;\
   1.529 +	m.timers [2].next_time -= rel_time;\
   1.530 +	assert( m.spc_time <= end_time );\
   1.531 +	return &REGS [r_cpuio0];\
   1.532 +}
   1.533 +
   1.534 +int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
   1.535 +
   1.536 +void SNES_SPC::end_frame( time_t end_time )
   1.537 +{
   1.538 +	// Catch CPU up to as close to end as possible. If final instruction
   1.539 +	// would exceed end, does NOT execute it and leaves m.spc_time < end.
   1.540 +	if ( end_time > m.spc_time )
   1.541 +		run_until_( end_time );
   1.542 +	
   1.543 +	m.spc_time     -= end_time;
   1.544 +	m.extra_clocks += end_time;
   1.545 +	
   1.546 +	// Greatest number of clocks early that emulation can stop early due to
   1.547 +	// not being able to execute current instruction without going over
   1.548 +	// allowed time.
   1.549 +	assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 );
   1.550 +	
   1.551 +	// Catch timers up to CPU
   1.552 +	for ( int i = 0; i < timer_count; i++ )
   1.553 +		run_timer( &m.timers [i], 0 );
   1.554 +	
   1.555 +	// Catch DSP up to CPU
   1.556 +	if ( m.dsp_time < 0 )
   1.557 +	{
   1.558 +		RUN_DSP( 0, max_reg_time );
   1.559 +	}
   1.560 +	
   1.561 +	// Save any extra samples beyond what should be generated
   1.562 +	if ( m.buf_begin )
   1.563 +		save_extra();
   1.564 +}
   1.565 +
   1.566 +// Inclusion here allows static memory access functions and better optimization
   1.567 +#include "SPC_CPU.h"