Mercurial > spc_convert
comparison snes_spc/SPC_DSP.h @ 0:e38dacceb958
initial import
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 21 Oct 2011 05:53:11 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:e38dacceb958 |
---|---|
1 // Highly accurate SNES SPC-700 DSP emulator | |
2 | |
3 // snes_spc 0.9.0 | |
4 #ifndef SPC_DSP_H | |
5 #define SPC_DSP_H | |
6 | |
7 #include "blargg_common.h" | |
8 | |
9 extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); } | |
10 | |
11 class SPC_DSP { | |
12 public: | |
13 typedef BOOST::uint8_t uint8_t; | |
14 | |
15 // Setup | |
16 | |
17 // Initializes DSP and has it use the 64K RAM provided | |
18 void init( void* ram_64k ); | |
19 | |
20 // Sets destination for output samples. If out is NULL or out_size is 0, | |
21 // doesn't generate any. | |
22 typedef short sample_t; | |
23 void set_output( sample_t* out, int out_size ); | |
24 | |
25 // Number of samples written to output since it was last set, always | |
26 // a multiple of 2. Undefined if more samples were generated than | |
27 // output buffer could hold. | |
28 int sample_count() const; | |
29 | |
30 // Emulation | |
31 | |
32 // Resets DSP to power-on state | |
33 void reset(); | |
34 | |
35 // Emulates pressing reset switch on SNES | |
36 void soft_reset(); | |
37 | |
38 // Reads/writes DSP registers. For accuracy, you must first call run() | |
39 // to catch the DSP up to present. | |
40 int read ( int addr ) const; | |
41 void write( int addr, int data ); | |
42 | |
43 // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks | |
44 // a pair of samples is be generated. | |
45 void run( int clock_count ); | |
46 | |
47 // Sound control | |
48 | |
49 // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). | |
50 // Reduces emulation accuracy. | |
51 enum { voice_count = 8 }; | |
52 void mute_voices( int mask ); | |
53 | |
54 // State | |
55 | |
56 // Resets DSP and uses supplied values to initialize registers | |
57 enum { register_count = 128 }; | |
58 void load( uint8_t const regs [register_count] ); | |
59 | |
60 // Saves/loads exact emulator state | |
61 enum { state_size = 640 }; // maximum space needed when saving | |
62 typedef dsp_copy_func_t copy_func_t; | |
63 void copy_state( unsigned char** io, copy_func_t ); | |
64 | |
65 // Returns non-zero if new key-on events occurred since last call | |
66 bool check_kon(); | |
67 | |
68 // DSP register addresses | |
69 | |
70 // Global registers | |
71 enum { | |
72 r_mvoll = 0x0C, r_mvolr = 0x1C, | |
73 r_evoll = 0x2C, r_evolr = 0x3C, | |
74 r_kon = 0x4C, r_koff = 0x5C, | |
75 r_flg = 0x6C, r_endx = 0x7C, | |
76 r_efb = 0x0D, r_pmon = 0x2D, | |
77 r_non = 0x3D, r_eon = 0x4D, | |
78 r_dir = 0x5D, r_esa = 0x6D, | |
79 r_edl = 0x7D, | |
80 r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F | |
81 }; | |
82 | |
83 // Voice registers | |
84 enum { | |
85 v_voll = 0x00, v_volr = 0x01, | |
86 v_pitchl = 0x02, v_pitchh = 0x03, | |
87 v_srcn = 0x04, v_adsr0 = 0x05, | |
88 v_adsr1 = 0x06, v_gain = 0x07, | |
89 v_envx = 0x08, v_outx = 0x09 | |
90 }; | |
91 | |
92 public: | |
93 enum { extra_size = 16 }; | |
94 sample_t* extra() { return m.extra; } | |
95 sample_t const* out_pos() const { return m.out; } | |
96 void disable_surround( bool ) { } // not supported | |
97 public: | |
98 BLARGG_DISABLE_NOTHROW | |
99 | |
100 typedef BOOST::int8_t int8_t; | |
101 typedef BOOST::int16_t int16_t; | |
102 | |
103 enum { echo_hist_size = 8 }; | |
104 | |
105 enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; | |
106 enum { brr_buf_size = 12 }; | |
107 struct voice_t | |
108 { | |
109 int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) | |
110 int buf_pos; // place in buffer where next samples will be decoded | |
111 int interp_pos; // relative fractional position in sample (0x1000 = 1.0) | |
112 int brr_addr; // address of current BRR block | |
113 int brr_offset; // current decoding offset in BRR block | |
114 uint8_t* regs; // pointer to voice's DSP registers | |
115 int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc. | |
116 int kon_delay; // KON delay/current setup phase | |
117 env_mode_t env_mode; | |
118 int env; // current envelope level | |
119 int hidden_env; // used by GAIN mode 7, very obscure quirk | |
120 uint8_t t_envx_out; | |
121 }; | |
122 private: | |
123 enum { brr_block_size = 9 }; | |
124 | |
125 struct state_t | |
126 { | |
127 uint8_t regs [register_count]; | |
128 | |
129 // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) | |
130 int echo_hist [echo_hist_size * 2] [2]; | |
131 int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] | |
132 | |
133 int every_other_sample; // toggles every sample | |
134 int kon; // KON value when last checked | |
135 int noise; | |
136 int counter; | |
137 int echo_offset; // offset from ESA in echo buffer | |
138 int echo_length; // number of bytes that echo_offset will stop at | |
139 int phase; // next clock cycle to run (0-31) | |
140 bool kon_check; // set when a new KON occurs | |
141 | |
142 // Hidden registers also written to when main register is written to | |
143 int new_kon; | |
144 uint8_t endx_buf; | |
145 uint8_t envx_buf; | |
146 uint8_t outx_buf; | |
147 | |
148 // Temporary state between clocks | |
149 | |
150 // read once per sample | |
151 int t_pmon; | |
152 int t_non; | |
153 int t_eon; | |
154 int t_dir; | |
155 int t_koff; | |
156 | |
157 // read a few clocks ahead then used | |
158 int t_brr_next_addr; | |
159 int t_adsr0; | |
160 int t_brr_header; | |
161 int t_brr_byte; | |
162 int t_srcn; | |
163 int t_esa; | |
164 int t_echo_enabled; | |
165 | |
166 // internal state that is recalculated every sample | |
167 int t_dir_addr; | |
168 int t_pitch; | |
169 int t_output; | |
170 int t_looped; | |
171 int t_echo_ptr; | |
172 | |
173 // left/right sums | |
174 int t_main_out [2]; | |
175 int t_echo_out [2]; | |
176 int t_echo_in [2]; | |
177 | |
178 voice_t voices [voice_count]; | |
179 | |
180 // non-emulation state | |
181 uint8_t* ram; // 64K shared RAM between DSP and SMP | |
182 int mute_mask; | |
183 sample_t* out; | |
184 sample_t* out_end; | |
185 sample_t* out_begin; | |
186 sample_t extra [extra_size]; | |
187 }; | |
188 state_t m; | |
189 | |
190 void init_counter(); | |
191 void run_counters(); | |
192 unsigned read_counter( int rate ); | |
193 | |
194 int interpolate( voice_t const* v ); | |
195 void run_envelope( voice_t* const v ); | |
196 void decode_brr( voice_t* v ); | |
197 | |
198 void misc_27(); | |
199 void misc_28(); | |
200 void misc_29(); | |
201 void misc_30(); | |
202 | |
203 void voice_output( voice_t const* v, int ch ); | |
204 void voice_V1( voice_t* const ); | |
205 void voice_V2( voice_t* const ); | |
206 void voice_V3( voice_t* const ); | |
207 void voice_V3a( voice_t* const ); | |
208 void voice_V3b( voice_t* const ); | |
209 void voice_V3c( voice_t* const ); | |
210 void voice_V4( voice_t* const ); | |
211 void voice_V5( voice_t* const ); | |
212 void voice_V6( voice_t* const ); | |
213 void voice_V7( voice_t* const ); | |
214 void voice_V8( voice_t* const ); | |
215 void voice_V9( voice_t* const ); | |
216 void voice_V7_V4_V1( voice_t* const ); | |
217 void voice_V8_V5_V2( voice_t* const ); | |
218 void voice_V9_V6_V3( voice_t* const ); | |
219 | |
220 void echo_read( int ch ); | |
221 int echo_output( int ch ); | |
222 void echo_write( int ch ); | |
223 void echo_22(); | |
224 void echo_23(); | |
225 void echo_24(); | |
226 void echo_25(); | |
227 void echo_26(); | |
228 void echo_27(); | |
229 void echo_28(); | |
230 void echo_29(); | |
231 void echo_30(); | |
232 | |
233 void soft_reset_common(); | |
234 }; | |
235 | |
236 #include <assert.h> | |
237 | |
238 inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; } | |
239 | |
240 inline int SPC_DSP::read( int addr ) const | |
241 { | |
242 assert( (unsigned) addr < register_count ); | |
243 return m.regs [addr]; | |
244 } | |
245 | |
246 inline void SPC_DSP::write( int addr, int data ) | |
247 { | |
248 assert( (unsigned) addr < register_count ); | |
249 | |
250 m.regs [addr] = (uint8_t) data; | |
251 switch ( addr & 0x0F ) | |
252 { | |
253 case v_envx: | |
254 m.envx_buf = (uint8_t) data; | |
255 break; | |
256 | |
257 case v_outx: | |
258 m.outx_buf = (uint8_t) data; | |
259 break; | |
260 | |
261 case 0x0C: | |
262 if ( addr == r_kon ) | |
263 m.new_kon = (uint8_t) data; | |
264 | |
265 if ( addr == r_endx ) // always cleared, regardless of data written | |
266 { | |
267 m.endx_buf = 0; | |
268 m.regs [r_endx] = 0; | |
269 } | |
270 break; | |
271 } | |
272 } | |
273 | |
274 inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; } | |
275 | |
276 inline bool SPC_DSP::check_kon() | |
277 { | |
278 bool old = m.kon_check; | |
279 m.kon_check = 0; | |
280 return old; | |
281 } | |
282 | |
283 #if !SPC_NO_COPY_STATE_FUNCS | |
284 | |
285 class SPC_State_Copier { | |
286 SPC_DSP::copy_func_t func; | |
287 unsigned char** buf; | |
288 public: | |
289 SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; } | |
290 void copy( void* state, size_t size ); | |
291 int copy_int( int state, int size ); | |
292 void skip( int count ); | |
293 void extra(); | |
294 }; | |
295 | |
296 #define SPC_COPY( type, state )\ | |
297 {\ | |
298 state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\ | |
299 assert( (BOOST::type) state == state );\ | |
300 } | |
301 | |
302 #endif | |
303 | |
304 #endif |