/gme/Snes_Spc.cpp

http://game-music-emu.googlecode.com/ · C++ · 380 lines · 269 code · 69 blank · 42 comment · 27 complexity · 499ff690ed23704cdf075f57fa6dc8dd MD5 · raw file

  1. // SPC emulation support: init, sample buffering, reset, SPC loading
  2. // Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
  3. #include "Snes_Spc.h"
  4. #include <string.h>
  5. /* Copyright (C) 2004-2007 Shay Green. This module is free software; you
  6. can redistribute it and/or modify it under the terms of the GNU Lesser
  7. General Public License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version. This
  9. module is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  12. details. You should have received a copy of the GNU Lesser General Public
  13. License along with this module; if not, write to the Free Software Foundation,
  14. Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
  15. #include "blargg_source.h"
  16. #define RAM (m.ram.ram)
  17. #define REGS (m.smp_regs [0])
  18. #define REGS_IN (m.smp_regs [1])
  19. // (n ? n : 256)
  20. #define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
  21. //// Init
  22. blargg_err_t Snes_Spc::init()
  23. {
  24. memset( &m, 0, sizeof m );
  25. dsp.init( RAM );
  26. m.tempo = tempo_unit;
  27. // Most SPC music doesn't need ROM, and almost all the rest only rely
  28. // on these two bytes
  29. m.rom [0x3E] = 0xFF;
  30. m.rom [0x3F] = 0xC0;
  31. static unsigned char const cycle_table [128] =
  32. {// 01 23 45 67 89 AB CD EF
  33. 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0
  34. 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1
  35. 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2
  36. 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3
  37. 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4
  38. 0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5
  39. 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6
  40. 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7
  41. 0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8
  42. 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9
  43. 0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A
  44. 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B
  45. 0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C
  46. 0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D
  47. 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E
  48. 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F
  49. };
  50. // unpack cycle table
  51. for ( int i = 0; i < 128; i++ )
  52. {
  53. int n = cycle_table [i];
  54. m.cycle_table [i * 2 + 0] = n >> 4;
  55. m.cycle_table [i * 2 + 1] = n & 0x0F;
  56. }
  57. #if SPC_LESS_ACCURATE
  58. memcpy( reg_times, reg_times_, sizeof reg_times );
  59. #endif
  60. reset();
  61. return 0;
  62. }
  63. void Snes_Spc::init_rom( uint8_t const in [rom_size] )
  64. {
  65. memcpy( m.rom, in, sizeof m.rom );
  66. }
  67. void Snes_Spc::set_tempo( int t )
  68. {
  69. m.tempo = t;
  70. int const timer2_shift = 4; // 64 kHz
  71. int const other_shift = 3; // 8 kHz
  72. #if SPC_DISABLE_TEMPO
  73. m.timers [2].prescaler = timer2_shift;
  74. m.timers [1].prescaler = timer2_shift + other_shift;
  75. m.timers [0].prescaler = timer2_shift + other_shift;
  76. #else
  77. if ( !t )
  78. t = 1;
  79. int const timer2_rate = 1 << timer2_shift;
  80. int rate = (timer2_rate * tempo_unit + (t >> 1)) / t;
  81. if ( rate < timer2_rate / 4 )
  82. rate = timer2_rate / 4; // max 4x tempo
  83. m.timers [2].prescaler = rate;
  84. m.timers [1].prescaler = rate << other_shift;
  85. m.timers [0].prescaler = rate << other_shift;
  86. #endif
  87. }
  88. // Timer registers have been loaded. Applies these to the timers. Does not
  89. // reset timer prescalers or dividers.
  90. void Snes_Spc::timers_loaded()
  91. {
  92. int i;
  93. for ( i = 0; i < timer_count; i++ )
  94. {
  95. Timer* t = &m.timers [i];
  96. t->period = IF_0_THEN_256( REGS [r_t0target + i] );
  97. t->enabled = REGS [r_control] >> i & 1;
  98. t->counter = REGS_IN [r_t0out + i] & 0x0F;
  99. }
  100. set_tempo( m.tempo );
  101. }
  102. // Loads registers from unified 16-byte format
  103. void Snes_Spc::load_regs( uint8_t const in [reg_count] )
  104. {
  105. memcpy( REGS, in, reg_count );
  106. memcpy( REGS_IN, REGS, reg_count );
  107. // These always read back as 0
  108. REGS_IN [r_test ] = 0;
  109. REGS_IN [r_control ] = 0;
  110. REGS_IN [r_t0target] = 0;
  111. REGS_IN [r_t1target] = 0;
  112. REGS_IN [r_t2target] = 0;
  113. }
  114. // RAM was just loaded from SPC, with $F0-$FF containing SMP registers
  115. // and timer counts. Copies these to proper registers.
  116. void Snes_Spc::ram_loaded()
  117. {
  118. m.rom_enabled = 0;
  119. load_regs( &RAM [0xF0] );
  120. // Put STOP instruction around memory to catch PC underflow/overflow
  121. memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 );
  122. memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 );
  123. }
  124. // Registers were just loaded. Applies these new values.
  125. void Snes_Spc::regs_loaded()
  126. {
  127. enable_rom( REGS [r_control] & 0x80 );
  128. timers_loaded();
  129. }
  130. void Snes_Spc::reset_time_regs()
  131. {
  132. m.cpu_error = 0;
  133. m.echo_accessed = 0;
  134. m.spc_time = 0;
  135. m.dsp_time = 0;
  136. #if SPC_LESS_ACCURATE
  137. m.dsp_time = clocks_per_sample + 1;
  138. #endif
  139. for ( int i = 0; i < timer_count; i++ )
  140. {
  141. Timer* t = &m.timers [i];
  142. t->next_time = 1;
  143. t->divider = 0;
  144. }
  145. regs_loaded();
  146. m.extra_clocks = 0;
  147. reset_buf();
  148. }
  149. void Snes_Spc::reset_common( int timer_counter_init )
  150. {
  151. int i;
  152. for ( i = 0; i < timer_count; i++ )
  153. REGS_IN [r_t0out + i] = timer_counter_init;
  154. // Run IPL ROM
  155. memset( &m.cpu_regs, 0, sizeof m.cpu_regs );
  156. m.cpu_regs.pc = rom_addr;
  157. REGS [r_test ] = 0x0A;
  158. REGS [r_control] = 0xB0; // ROM enabled, clear ports
  159. for ( i = 0; i < port_count; i++ )
  160. REGS_IN [r_cpuio0 + i] = 0;
  161. reset_time_regs();
  162. }
  163. void Snes_Spc::soft_reset()
  164. {
  165. reset_common( 0 );
  166. dsp.soft_reset();
  167. }
  168. void Snes_Spc::reset()
  169. {
  170. memset( RAM, 0xFF, 0x10000 );
  171. ram_loaded();
  172. reset_common( 0x0F );
  173. dsp.reset();
  174. }
  175. char const Snes_Spc::signature [signature_size + 1] =
  176. "SNES-SPC700 Sound File Data v0.30\x1A\x1A";
  177. blargg_err_t Snes_Spc::load_spc( void const* data, long size )
  178. {
  179. spc_file_t const* const spc = (spc_file_t const*) data;
  180. // be sure compiler didn't insert any padding into fle_t
  181. assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 );
  182. // Check signature and file size
  183. if ( size < signature_size || memcmp( spc, signature, 27 ) )
  184. return "Not an SPC file";
  185. if ( size < spc_min_file_size )
  186. return "Corrupt SPC file";
  187. // CPU registers
  188. m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl;
  189. m.cpu_regs.a = spc->a;
  190. m.cpu_regs.x = spc->x;
  191. m.cpu_regs.y = spc->y;
  192. m.cpu_regs.psw = spc->psw;
  193. m.cpu_regs.sp = spc->sp;
  194. // RAM and registers
  195. memcpy( RAM, spc->ram, 0x10000 );
  196. ram_loaded();
  197. // DSP registers
  198. dsp.load( spc->dsp );
  199. reset_time_regs();
  200. return 0;
  201. }
  202. void Snes_Spc::clear_echo()
  203. {
  204. if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) )
  205. {
  206. int addr = 0x100 * dsp.read( Spc_Dsp::r_esa );
  207. int end = addr + 0x800 * (dsp.read( Spc_Dsp::r_edl ) & 0x0F);
  208. if ( end > 0x10000 )
  209. end = 0x10000;
  210. memset( &RAM [addr], 0xFF, end - addr );
  211. }
  212. }
  213. //// Sample output
  214. void Snes_Spc::reset_buf()
  215. {
  216. // Start with half extra buffer of silence
  217. sample_t* out = m.extra_buf;
  218. while ( out < &m.extra_buf [extra_size / 2] )
  219. *out++ = 0;
  220. m.extra_pos = out;
  221. m.buf_begin = 0;
  222. dsp.set_output( 0, 0 );
  223. }
  224. void Snes_Spc::set_output( sample_t* out, int size )
  225. {
  226. require( (size & 1) == 0 ); // size must be even
  227. m.extra_clocks &= clocks_per_sample - 1;
  228. if ( out )
  229. {
  230. sample_t const* out_end = out + size;
  231. m.buf_begin = out;
  232. m.buf_end = out_end;
  233. // Copy extra to output
  234. sample_t const* in = m.extra_buf;
  235. while ( in < m.extra_pos && out < out_end )
  236. *out++ = *in++;
  237. // Handle output being full already
  238. if ( out >= out_end )
  239. {
  240. // Have DSP write to remaining extra space
  241. out = dsp.extra();
  242. out_end = &dsp.extra() [extra_size];
  243. // Copy any remaining extra samples as if DSP wrote them
  244. while ( in < m.extra_pos )
  245. *out++ = *in++;
  246. assert( out <= out_end );
  247. }
  248. dsp.set_output( out, out_end - out );
  249. }
  250. else
  251. {
  252. reset_buf();
  253. }
  254. }
  255. void Snes_Spc::save_extra()
  256. {
  257. // Get end pointers
  258. sample_t const* main_end = m.buf_end; // end of data written to buf
  259. sample_t const* dsp_end = dsp.out_pos(); // end of data written to dsp.extra()
  260. if ( m.buf_begin <= dsp_end && dsp_end <= main_end )
  261. {
  262. main_end = dsp_end;
  263. dsp_end = dsp.extra(); // nothing in DSP's extra
  264. }
  265. // Copy any extra samples at these ends into extra_buf
  266. sample_t* out = m.extra_buf;
  267. sample_t const* in;
  268. for ( in = m.buf_begin + sample_count(); in < main_end; in++ )
  269. *out++ = *in;
  270. for ( in = dsp.extra(); in < dsp_end ; in++ )
  271. *out++ = *in;
  272. m.extra_pos = out;
  273. assert( out <= &m.extra_buf [extra_size] );
  274. }
  275. blargg_err_t Snes_Spc::play( int count, sample_t* out )
  276. {
  277. require( (count & 1) == 0 ); // must be even
  278. if ( count )
  279. {
  280. set_output( out, count );
  281. end_frame( count * (clocks_per_sample / 2) );
  282. }
  283. const char* err = m.cpu_error;
  284. m.cpu_error = 0;
  285. return err;
  286. }
  287. blargg_err_t Snes_Spc::skip( int count )
  288. {
  289. #if SPC_LESS_ACCURATE
  290. if ( count > 2 * sample_rate * 2 )
  291. {
  292. set_output( 0, 0 );
  293. // Skip a multiple of 4 samples
  294. time_t end = count;
  295. count = (count & 3) + 1 * sample_rate * 2;
  296. end = (end - count) * (clocks_per_sample / 2);
  297. m.skipped_kon = 0;
  298. m.skipped_koff = 0;
  299. // Preserve DSP and timer synchronization
  300. // TODO: verify that this really preserves it
  301. int old_dsp_time = m.dsp_time + m.spc_time;
  302. m.dsp_time = end - m.spc_time + skipping_time;
  303. end_frame( end );
  304. m.dsp_time = m.dsp_time - skipping_time + old_dsp_time;
  305. dsp.write( Spc_Dsp::r_koff, m.skipped_koff & ~m.skipped_kon );
  306. dsp.write( Spc_Dsp::r_kon , m.skipped_kon );
  307. clear_echo();
  308. }
  309. #endif
  310. return play( count, 0 );
  311. }