Mercurial > spc_convert
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, ®S [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 ®S [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"