Mercurial > spc_convert
view fast_dsp/SPC_DSP.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 source
1 // snes_spc 0.9.0. http://www.slack.net/~ant/3 #include "SPC_DSP.h"5 #include "blargg_endian.h"6 #include <string.h>8 /* Copyright (C) 2007 Shay Green. This module is free software; you9 can redistribute it and/or modify it under the terms of the GNU Lesser10 General Public License as published by the Free Software Foundation; either11 version 2.1 of the License, or (at your option) any later version. This12 module is distributed in the hope that it will be useful, but WITHOUT ANY13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS14 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more15 details. You should have received a copy of the GNU Lesser General Public16 License along with this module; if not, write to the Free Software Foundation,17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */19 #include "blargg_source.h"21 #ifdef BLARGG_ENABLE_OPTIMIZER22 #include BLARGG_ENABLE_OPTIMIZER23 #endif25 #if INT_MAX < 0x7FFFFFFF26 #error "Requires that int type have at least 32 bits"27 #endif30 // TODO: add to blargg_endian.h31 #define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr ))32 #define GET_LE16A( addr ) GET_LE16( addr )33 #define SET_LE16A( addr, data ) SET_LE16( addr, data )35 static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] =36 {37 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80,38 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF,39 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A,40 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF,41 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67,42 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF,43 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F,44 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF45 };47 // if ( io < -32768 ) io = -32768;48 // if ( io > 32767 ) io = 32767;49 #define CLAMP16( io )\50 {\51 if ( (int16_t) io != io )\52 io = (io >> 31) ^ 0x7FFF;\53 }55 // Access global DSP register56 #define REG(n) m.regs [r_##n]58 // Access voice DSP register59 #define VREG(r,n) r [v_##n]61 #define WRITE_SAMPLES( l, r, out ) \62 {\63 out [0] = l;\64 out [1] = r;\65 out += 2;\66 if ( out >= m.out_end )\67 {\68 check( out == m.out_end );\69 check( m.out_end != &m.extra [extra_size] || \70 (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\71 out = m.extra;\72 m.out_end = &m.extra [extra_size];\73 }\74 }\76 void SPC_DSP::set_output( sample_t* out, int size )77 {78 require( (size & 1) == 0 ); // must be even79 if ( !out )80 {81 out = m.extra;82 size = extra_size;83 }84 m.out_begin = out;85 m.out = out;86 m.out_end = out + size;87 }89 // Volume registers and efb are signed! Easy to forget int8_t cast.90 // Prefixes are to avoid accidental use of locals with same names.92 // Interleved gauss table (to improve cache coherency)93 // interleved_gauss [i] = gauss [(i & 1) * 256 + 255 - (i >> 1 & 0xFF)]94 static short const interleved_gauss [512] =95 {96 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303,97 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299,98 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292,99 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282,100 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269,101 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253,102 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234,103 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213,104 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190,105 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164,106 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136,107 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106,108 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074,109 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040,110 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005,111 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969,112 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932,113 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894,114 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855,115 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816,116 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777,117 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737,118 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698,119 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659,120 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620,121 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582,122 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545,123 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508,124 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473,125 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439,126 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405,127 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374,128 };131 //// Counters133 #define RATE( rate, div )\134 (rate >= div ? rate / div * 8 - 1 : rate - 1)136 static unsigned const counter_mask [32] =137 {138 RATE( 2,2), RATE(2048,4), RATE(1536,3),139 RATE(1280,5), RATE(1024,4), RATE( 768,3),140 RATE( 640,5), RATE( 512,4), RATE( 384,3),141 RATE( 320,5), RATE( 256,4), RATE( 192,3),142 RATE( 160,5), RATE( 128,4), RATE( 96,3),143 RATE( 80,5), RATE( 64,4), RATE( 48,3),144 RATE( 40,5), RATE( 32,4), RATE( 24,3),145 RATE( 20,5), RATE( 16,4), RATE( 12,3),146 RATE( 10,5), RATE( 8,4), RATE( 6,3),147 RATE( 5,5), RATE( 4,4), RATE( 3,3),148 RATE( 2,4),149 RATE( 1,4)150 };151 #undef RATE153 inline void SPC_DSP::init_counter()154 {155 // counters start out with this synchronization156 m.counters [0] = 1;157 m.counters [1] = 0;158 m.counters [2] = -0x20u;159 m.counters [3] = 0x0B;161 int n = 2;162 for ( int i = 1; i < 32; i++ )163 {164 m.counter_select [i] = &m.counters [n];165 if ( !--n )166 n = 3;167 }168 m.counter_select [ 0] = &m.counters [0];169 m.counter_select [30] = &m.counters [2];170 }172 inline void SPC_DSP::run_counter( int i )173 {174 int n = m.counters [i];175 if ( !(n-- & 7) )176 n -= 6 - i;177 m.counters [i] = n;178 }180 #define READ_COUNTER( rate )\181 (*m.counter_select [rate] & counter_mask [rate])184 //// Emulation186 void SPC_DSP::run( int clock_count )187 {188 int new_phase = m.phase + clock_count;189 int count = new_phase >> 5;190 m.phase = new_phase & 31;191 if ( !count )192 return;194 uint8_t* const ram = m.ram;195 uint8_t const* const dir = &ram [REG(dir) * 0x100];196 int const slow_gaussian = (REG(pmon) >> 1) | REG(non);197 int const noise_rate = REG(flg) & 0x1F;199 // Global volume200 int mvoll = (int8_t) REG(mvoll);201 int mvolr = (int8_t) REG(mvolr);202 if ( mvoll * mvolr < m.surround_threshold )203 mvoll = -mvoll; // eliminate surround205 do206 {207 // KON/KOFF reading208 if ( (m.every_other_sample ^= 1) != 0 )209 {210 m.new_kon &= ~m.kon;211 m.kon = m.new_kon;212 m.t_koff = REG(koff);213 }215 run_counter( 1 );216 run_counter( 2 );217 run_counter( 3 );219 // Noise220 if ( !READ_COUNTER( noise_rate ) )221 {222 int feedback = (m.noise << 13) ^ (m.noise << 14);223 m.noise = (feedback & 0x4000) ^ (m.noise >> 1);224 }226 // Voices227 int pmon_input = 0;228 int main_out_l = 0;229 int main_out_r = 0;230 int echo_out_l = 0;231 int echo_out_r = 0;232 voice_t* v = m.voices;233 uint8_t* v_regs = m.regs;234 int vbit = 1;235 do236 {237 #define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] )239 int brr_header = ram [v->brr_addr];240 int kon_delay = v->kon_delay;242 // Pitch243 int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF;244 if ( REG(pmon) & vbit )245 pitch += ((pmon_input >> 5) * pitch) >> 10;247 // KON phases248 if ( --kon_delay >= 0 )249 {250 v->kon_delay = kon_delay;252 // Get ready to start BRR decoding on next sample253 if ( kon_delay == 4 )254 {255 v->brr_addr = SAMPLE_PTR( 0 );256 v->brr_offset = 1;257 v->buf_pos = v->buf;258 brr_header = 0; // header is ignored on this sample259 }261 // Envelope is never run during KON262 v->env = 0;263 v->hidden_env = 0;265 // Disable BRR decoding until last three samples266 v->interp_pos = (kon_delay & 3 ? 0x4000 : 0);268 // Pitch is never added during KON269 pitch = 0;270 }272 int env = v->env;274 // Gaussian interpolation275 {276 int output = 0;277 VREG(v_regs,envx) = (uint8_t) (env >> 4);278 if ( env )279 {280 // Make pointers into gaussian based on fractional position between samples281 int offset = (unsigned) v->interp_pos >> 3 & 0x1FE;282 short const* fwd = interleved_gauss + offset;283 short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian285 int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12];287 if ( !(slow_gaussian & vbit) ) // 99%288 {289 // Faster approximation when exact sample value isn't necessary for pitch mod290 output = (fwd [0] * in [0] +291 fwd [1] * in [1] +292 rev [1] * in [2] +293 rev [0] * in [3]) >> 11;294 output = (output * env) >> 11;295 }296 else297 {298 output = (int16_t) (m.noise * 2);299 if ( !(REG(non) & vbit) )300 {301 output = (fwd [0] * in [0]) >> 11;302 output += (fwd [1] * in [1]) >> 11;303 output += (rev [1] * in [2]) >> 11;304 output = (int16_t) output;305 output += (rev [0] * in [3]) >> 11;307 CLAMP16( output );308 output &= ~1;309 }310 output = (output * env) >> 11 & ~1;311 }313 // Output314 int l = output * v->volume [0];315 int r = output * v->volume [1];317 main_out_l += l;318 main_out_r += r;320 if ( REG(eon) & vbit )321 {322 echo_out_l += l;323 echo_out_r += r;324 }325 }327 pmon_input = output;328 VREG(v_regs,outx) = (uint8_t) (output >> 8);329 }331 // Soft reset or end of sample332 if ( REG(flg) & 0x80 || (brr_header & 3) == 1 )333 {334 v->env_mode = env_release;335 env = 0;336 }338 if ( m.every_other_sample )339 {340 // KOFF341 if ( m.t_koff & vbit )342 v->env_mode = env_release;344 // KON345 if ( m.kon & vbit )346 {347 v->kon_delay = 5;348 v->env_mode = env_attack;349 REG(endx) &= ~vbit;350 }351 }353 // Envelope354 if ( !v->kon_delay )355 {356 if ( v->env_mode == env_release ) // 97%357 {358 env -= 0x8;359 v->env = env;360 if ( env <= 0 )361 {362 v->env = 0;363 goto skip_brr; // no BRR decoding for you!364 }365 }366 else // 3%367 {368 int rate;369 int const adsr0 = VREG(v_regs,adsr0);370 int env_data = VREG(v_regs,adsr1);371 if ( adsr0 >= 0x80 ) // 97% ADSR372 {373 if ( v->env_mode > env_decay ) // 89%374 {375 env--;376 env -= env >> 8;377 rate = env_data & 0x1F;379 // optimized handling380 v->hidden_env = env;381 if ( READ_COUNTER( rate ) )382 goto exit_env;383 v->env = env;384 goto exit_env;385 }386 else if ( v->env_mode == env_decay )387 {388 env--;389 env -= env >> 8;390 rate = (adsr0 >> 3 & 0x0E) + 0x10;391 }392 else // env_attack393 {394 rate = (adsr0 & 0x0F) * 2 + 1;395 env += rate < 31 ? 0x20 : 0x400;396 }397 }398 else // GAIN399 {400 int mode;401 env_data = VREG(v_regs,gain);402 mode = env_data >> 5;403 if ( mode < 4 ) // direct404 {405 env = env_data * 0x10;406 rate = 31;407 }408 else409 {410 rate = env_data & 0x1F;411 if ( mode == 4 ) // 4: linear decrease412 {413 env -= 0x20;414 }415 else if ( mode < 6 ) // 5: exponential decrease416 {417 env--;418 env -= env >> 8;419 }420 else // 6,7: linear increase421 {422 env += 0x20;423 if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 )424 env += 0x8 - 0x20; // 7: two-slope linear increase425 }426 }427 }429 // Sustain level430 if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay )431 v->env_mode = env_sustain;433 v->hidden_env = env;435 // unsigned cast because linear decrease going negative also triggers this436 if ( (unsigned) env > 0x7FF )437 {438 env = (env < 0 ? 0 : 0x7FF);439 if ( v->env_mode == env_attack )440 v->env_mode = env_decay;441 }443 if ( !READ_COUNTER( rate ) )444 v->env = env; // nothing else is controlled by the counter445 }446 }447 exit_env:449 {450 // Apply pitch451 int old_pos = v->interp_pos;452 int interp_pos = (old_pos & 0x3FFF) + pitch;453 if ( interp_pos > 0x7FFF )454 interp_pos = 0x7FFF;455 v->interp_pos = interp_pos;457 // BRR decode if necessary458 if ( old_pos >= 0x4000 )459 {460 // Arrange the four input nybbles in 0xABCD order for easy decoding461 int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 +462 ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF];464 // Advance read position465 int const brr_block_size = 9;466 int brr_offset = v->brr_offset;467 if ( (brr_offset += 2) >= brr_block_size )468 {469 // Next BRR block470 int brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF;471 assert( brr_offset == brr_block_size );472 if ( brr_header & 1 )473 {474 brr_addr = SAMPLE_PTR( 1 );475 if ( !v->kon_delay )476 REG(endx) |= vbit;477 }478 v->brr_addr = brr_addr;479 brr_offset = 1;480 }481 v->brr_offset = brr_offset;483 // Decode485 // 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11486 static unsigned char const shifts [16 * 2] = {487 13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16,488 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11489 };490 int const scale = brr_header >> 4;491 int const right_shift = shifts [scale];492 int const left_shift = shifts [scale + 16];494 // Write to next four samples in circular buffer495 int* pos = v->buf_pos;496 int* end;498 // Decode four samples499 for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 )500 {501 // Extract upper nybble and scale appropriately502 int s = ((int16_t) nybbles >> right_shift) << left_shift;504 // Apply IIR filter (8 is the most commonly used)505 int const filter = brr_header & 0x0C;506 int const p1 = pos [brr_buf_size - 1];507 int const p2 = pos [brr_buf_size - 2] >> 1;508 if ( filter >= 8 )509 {510 s += p1;511 s -= p2;512 if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875513 {514 s += p2 >> 4;515 s += (p1 * -3) >> 6;516 }517 else // s += p1 * 0.8984375 - p2 * 0.40625518 {519 s += (p1 * -13) >> 7;520 s += (p2 * 3) >> 4;521 }522 }523 else if ( filter ) // s += p1 * 0.46875524 {525 s += p1 >> 1;526 s += (-p1) >> 5;527 }529 // Adjust and write sample530 CLAMP16( s );531 s = (int16_t) (s * 2);532 pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around533 }535 if ( pos >= &v->buf [brr_buf_size] )536 pos = v->buf;537 v->buf_pos = pos;538 }539 }540 skip_brr:541 // Next voice542 vbit <<= 1;543 v_regs += 0x10;544 v++;545 }546 while ( vbit < 0x100 );548 // Echo position549 int echo_offset = m.echo_offset;550 uint8_t* const echo_ptr = &ram [(REG(esa) * 0x100 + echo_offset) & 0xFFFF];551 if ( !echo_offset )552 m.echo_length = (REG(edl) & 0x0F) * 0x800;553 echo_offset += 4;554 if ( echo_offset >= m.echo_length )555 echo_offset = 0;556 m.echo_offset = echo_offset;558 // FIR559 int echo_in_l = GET_LE16SA( echo_ptr + 0 );560 int echo_in_r = GET_LE16SA( echo_ptr + 2 );562 int (*echo_hist_pos) [2] = m.echo_hist_pos;563 if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] )564 echo_hist_pos = m.echo_hist;565 m.echo_hist_pos = echo_hist_pos;567 echo_hist_pos [0] [0] = echo_hist_pos [8] [0] = echo_in_l;568 echo_hist_pos [0] [1] = echo_hist_pos [8] [1] = echo_in_r;570 #define CALC_FIR_( i, in ) ((in) * (int8_t) REG(fir + i * 0x10))571 echo_in_l = CALC_FIR_( 7, echo_in_l );572 echo_in_r = CALC_FIR_( 7, echo_in_r );574 #define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] )575 #define DO_FIR( i )\576 echo_in_l += CALC_FIR( i, 0 );\577 echo_in_r += CALC_FIR( i, 1 );578 DO_FIR( 0 );579 DO_FIR( 1 );580 DO_FIR( 2 );581 #if defined (__MWERKS__) && __MWERKS__ < 0x3200582 __eieio(); // keeps compiler from stupidly "caching" things in memory583 #endif584 DO_FIR( 3 );585 DO_FIR( 4 );586 DO_FIR( 5 );587 DO_FIR( 6 );589 // Echo out590 if ( !(REG(flg) & 0x20) )591 {592 int l = (echo_out_l >> 7) + ((echo_in_l * (int8_t) REG(efb)) >> 14);593 int r = (echo_out_r >> 7) + ((echo_in_r * (int8_t) REG(efb)) >> 14);595 // just to help pass more validation tests596 #if SPC_MORE_ACCURACY597 l &= ~1;598 r &= ~1;599 #endif601 CLAMP16( l );602 CLAMP16( r );604 SET_LE16A( echo_ptr + 0, l );605 SET_LE16A( echo_ptr + 2, r );606 }608 // Sound out609 int l = (main_out_l * mvoll + echo_in_l * (int8_t) REG(evoll)) >> 14;610 int r = (main_out_r * mvolr + echo_in_r * (int8_t) REG(evolr)) >> 14;612 CLAMP16( l );613 CLAMP16( r );615 if ( (REG(flg) & 0x40) )616 {617 l = 0;618 r = 0;619 }621 sample_t* out = m.out;622 WRITE_SAMPLES( l, r, out );623 m.out = out;624 }625 while ( --count );626 }629 //// Setup631 void SPC_DSP::mute_voices( int mask )632 {633 m.mute_mask = mask;634 for ( int i = 0; i < voice_count; i++ )635 {636 m.voices [i].enabled = (mask >> i & 1) - 1;637 update_voice_vol( i * 0x10 );638 }639 }641 void SPC_DSP::init( void* ram_64k )642 {643 m.ram = (uint8_t*) ram_64k;644 mute_voices( 0 );645 disable_surround( false );646 set_output( 0, 0 );647 reset();649 #ifndef NDEBUG650 // be sure this sign-extends651 assert( (int16_t) 0x8000 == -0x8000 );653 // be sure right shift preserves sign654 assert( (-1 >> 1) == -1 );656 // check clamp macro657 int i;658 i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF );659 i = -0x8001; CLAMP16( i ); assert( i == -0x8000 );661 blargg_verify_byte_order();662 #endif663 }665 void SPC_DSP::soft_reset_common()666 {667 require( m.ram ); // init() must have been called already669 m.noise = 0x4000;670 m.echo_hist_pos = m.echo_hist;671 m.every_other_sample = 1;672 m.echo_offset = 0;673 m.phase = 0;675 init_counter();676 }678 void SPC_DSP::soft_reset()679 {680 REG(flg) = 0xE0;681 soft_reset_common();682 }684 void SPC_DSP::load( uint8_t const regs [register_count] )685 {686 memcpy( m.regs, regs, sizeof m.regs );687 memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count );689 // Internal state690 int i;691 for ( i = voice_count; --i >= 0; )692 {693 voice_t& v = m.voices [i];694 v.brr_offset = 1;695 v.buf_pos = v.buf;696 }697 m.new_kon = REG(kon);699 mute_voices( m.mute_mask );700 soft_reset_common();701 }703 void SPC_DSP::reset() { load( initial_regs ); }