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