1 | /*****************************************************************************\ |
2 | Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. |
3 | This file is licensed under the Snes9x License. |
4 | For further information, consult the LICENSE file in the root directory. |
5 | \*****************************************************************************/ |
6 | |
7 | #include <cmath> |
8 | #include "../snes9x.h" |
9 | #include "apu.h" |
10 | #include "../msu1.h" |
11 | #include "../snapshot.h" |
12 | #include "../display.h" |
13 | #include "resampler.h" |
14 | |
15 | #include "bapu/snes/snes.hpp" |
16 | |
17 | static const int APU_DEFAULT_INPUT_RATE = 31950; // ~59.94Hz |
18 | static const int APU_SAMPLE_BLOCK = 48; |
19 | static const int APU_NUMERATOR_NTSC = 15664; |
20 | static const int APU_DENOMINATOR_NTSC = 328125; |
21 | static const int APU_NUMERATOR_PAL = 34176; |
22 | static const int APU_DENOMINATOR_PAL = 709379; |
23 | |
24 | // Max number of samples we'll ever generate before call to port API and |
25 | // moving the samples to the resampler. |
26 | // This is 535 sample frames, which corresponds to 1 video frame + some leeway |
27 | // for use with SoundSync, multiplied by 2, for left and right samples. |
28 | static const int MINIMUM_BUFFER_SIZE = 550 * 2; |
29 | |
30 | namespace SNES { |
31 | #include "bapu/dsp/blargg_endian.h" |
32 | CPU cpu; |
33 | } // namespace SNES |
34 | |
35 | namespace spc { |
36 | static apu_callback callback = NULL; |
37 | static void *callback_data = NULL; |
38 | |
39 | static bool8 sound_in_sync = TRUE; |
40 | static bool8 sound_enabled = FALSE; |
41 | |
42 | static Resampler *resampler = NULL; |
43 | |
44 | static int32 reference_time; |
45 | static uint32 remainder; |
46 | |
47 | static const int timing_hack_numerator = 256; |
48 | static int timing_hack_denominator = 256; |
49 | /* Set these to NTSC for now. Will change to PAL in S9xAPUTimingSetSpeedup |
50 | if necessary on game load. */ |
51 | static uint32 ratio_numerator = APU_NUMERATOR_NTSC; |
52 | static uint32 ratio_denominator = APU_DENOMINATOR_NTSC; |
53 | |
54 | static double dynamic_rate_multiplier = 1.0; |
55 | } // namespace spc |
56 | |
57 | namespace msu { |
58 | // Always 16-bit, Stereo; 1.5x dsp buffer to never overflow |
59 | static Resampler *resampler = NULL; |
60 | static int16 *resample_buffer = NULL; |
61 | static int resample_buffer_size = 0; |
62 | } // namespace msu |
63 | |
64 | static void UpdatePlaybackRate(void); |
65 | static void SPCSnapshotCallback(void); |
66 | static inline int S9xAPUGetClock(int32); |
67 | static inline int S9xAPUGetClockRemainder(int32); |
68 | |
69 | bool8 S9xMixSamples(uint8 *dest, int sample_count) |
70 | { |
71 | int16 *out = (int16 *)dest; |
72 | |
73 | if (Settings.Mute) |
74 | { |
75 | memset(out, 0, sample_count << 1); |
76 | S9xClearSamples(); |
77 | } |
78 | else |
79 | { |
80 | if (spc::resampler->avail() >= sample_count) |
81 | { |
82 | spc::resampler->read((short *)out, sample_count); |
83 | |
84 | if (Settings.MSU1) |
85 | { |
86 | if (msu::resampler->avail() >= sample_count) |
87 | { |
88 | if (msu::resample_buffer_size < sample_count) |
89 | { |
90 | if (msu::resample_buffer) |
91 | delete[] msu::resample_buffer; |
92 | msu::resample_buffer = new int16[sample_count]; |
93 | msu::resample_buffer_size = sample_count; |
94 | } |
95 | msu::resampler->read(msu::resample_buffer, |
96 | sample_count); |
97 | for (int i = 0; i < sample_count; ++i) |
98 | { |
99 | int32 mixed = (int32)out[i] + msu::resample_buffer[i]; |
100 | out[i] = ((int16)mixed != mixed) ? (mixed >> 31) ^ 0x7fff : mixed; |
101 | } |
102 | } |
103 | else // should never occur |
104 | assert(0); |
105 | } |
106 | } |
107 | else |
108 | { |
109 | memset(out, 0, sample_count << 1); |
110 | return false; |
111 | } |
112 | } |
113 | |
114 | if (spc::resampler->space_empty() >= 535 * 2 || !Settings.SoundSync || |
115 | Settings.TurboMode || Settings.Mute) |
116 | spc::sound_in_sync = true; |
117 | else |
118 | spc::sound_in_sync = false; |
119 | |
120 | return true; |
121 | } |
122 | |
123 | int S9xGetSampleCount(void) |
124 | { |
125 | return spc::resampler->avail(); |
126 | } |
127 | |
128 | void S9xLandSamples(void) |
129 | { |
130 | if (spc::callback != NULL) |
131 | spc::callback(spc::callback_data); |
132 | |
133 | if (spc::resampler->space_empty() >= 535 * 2 || !Settings.SoundSync || |
134 | Settings.TurboMode || Settings.Mute) |
135 | spc::sound_in_sync = true; |
136 | else |
137 | spc::sound_in_sync = false; |
138 | } |
139 | |
140 | void S9xClearSamples(void) |
141 | { |
142 | spc::resampler->clear(); |
143 | if (Settings.MSU1) |
144 | msu::resampler->clear(); |
145 | } |
146 | |
147 | bool8 S9xSyncSound(void) |
148 | { |
149 | if (!Settings.SoundSync || spc::sound_in_sync) |
150 | return (TRUE); |
151 | |
152 | S9xLandSamples(); |
153 | |
154 | return (spc::sound_in_sync); |
155 | } |
156 | |
157 | void S9xSetSamplesAvailableCallback(apu_callback callback, void *data) |
158 | { |
159 | spc::callback = callback; |
160 | spc::callback_data = data; |
161 | } |
162 | |
163 | void S9xUpdateDynamicRate(int avail, int buffer_size) |
164 | { |
165 | spc::dynamic_rate_multiplier = 1.0 + (Settings.DynamicRateLimit * (buffer_size - 2 * avail)) / |
166 | (double)(1000 * buffer_size); |
167 | |
168 | UpdatePlaybackRate(); |
169 | } |
170 | |
171 | static void UpdatePlaybackRate(void) |
172 | { |
173 | if (Settings.SoundInputRate == 0) |
174 | Settings.SoundInputRate = APU_DEFAULT_INPUT_RATE; |
175 | |
176 | double time_ratio = (double)Settings.SoundInputRate * spc::timing_hack_numerator / (Settings.SoundPlaybackRate * spc::timing_hack_denominator); |
177 | |
178 | if (Settings.DynamicRateControl) |
179 | { |
180 | time_ratio *= spc::dynamic_rate_multiplier; |
181 | } |
182 | |
183 | spc::resampler->time_ratio(time_ratio); |
184 | |
185 | if (Settings.MSU1) |
186 | { |
187 | time_ratio = (44100.0 / Settings.SoundPlaybackRate) * (Settings.SoundInputRate / 32040.0); |
188 | msu::resampler->time_ratio(time_ratio); |
189 | } |
190 | } |
191 | |
192 | bool8 S9xInitSound(int buffer_ms) |
193 | { |
194 | // The resampler and spc unit use samples (16-bit short) as arguments. |
195 | int buffer_size_samples = MINIMUM_BUFFER_SIZE; |
196 | int requested_buffer_size_samples = Settings.SoundPlaybackRate * buffer_ms * 2 / 1000; |
197 | |
198 | if (requested_buffer_size_samples > buffer_size_samples) |
199 | buffer_size_samples = requested_buffer_size_samples; |
200 | |
201 | if (!spc::resampler) |
202 | { |
203 | spc::resampler = new Resampler(buffer_size_samples); |
204 | if (!spc::resampler) |
205 | return (FALSE); |
206 | } |
207 | else |
208 | spc::resampler->resize(buffer_size_samples); |
209 | |
210 | |
211 | if (!msu::resampler) |
212 | { |
213 | msu::resampler = new Resampler(buffer_size_samples * 3 / 2); |
214 | if (!msu::resampler) |
215 | return (FALSE); |
216 | } |
217 | else |
218 | msu::resampler->resize(buffer_size_samples * 3 / 2); |
219 | |
220 | SNES::dsp.spc_dsp.set_output(spc::resampler); |
221 | S9xMSU1SetOutput(msu::resampler); |
222 | |
223 | UpdatePlaybackRate(); |
224 | |
225 | spc::sound_enabled = S9xOpenSoundDevice(); |
226 | |
227 | return (spc::sound_enabled); |
228 | } |
229 | |
230 | void S9xSetSoundControl(uint8 voice_switch) |
231 | { |
232 | SNES::dsp.spc_dsp.set_stereo_switch(voice_switch << 8 | voice_switch); |
233 | } |
234 | |
235 | void S9xSetSoundMute(bool8 mute) |
236 | { |
237 | Settings.Mute = mute; |
238 | if (!spc::sound_enabled) |
239 | Settings.Mute = TRUE; |
240 | } |
241 | |
242 | void S9xDumpSPCSnapshot(void) |
243 | { |
244 | SNES::dsp.spc_dsp.dump_spc_snapshot(); |
245 | } |
246 | |
247 | static void SPCSnapshotCallback(void) |
248 | { |
249 | S9xSPCDump(S9xGetFilenameInc((".spc" ), SPC_DIR)); |
250 | printf("Dumped key-on triggered spc snapshot.\n" ); |
251 | } |
252 | |
253 | bool8 S9xInitAPU(void) |
254 | { |
255 | spc::resampler = NULL; |
256 | msu::resampler = NULL; |
257 | |
258 | return (TRUE); |
259 | } |
260 | |
261 | void S9xDeinitAPU(void) |
262 | { |
263 | if (spc::resampler) |
264 | { |
265 | delete spc::resampler; |
266 | spc::resampler = NULL; |
267 | } |
268 | |
269 | if (msu::resampler) |
270 | { |
271 | delete msu::resampler; |
272 | msu::resampler = NULL; |
273 | } |
274 | |
275 | S9xMSU1DeInit(); |
276 | } |
277 | |
278 | static inline int S9xAPUGetClock(int32 cpucycles) |
279 | { |
280 | return (spc::ratio_numerator * (cpucycles - spc::reference_time) + spc::remainder) / |
281 | spc::ratio_denominator; |
282 | } |
283 | |
284 | static inline int S9xAPUGetClockRemainder(int32 cpucycles) |
285 | { |
286 | return (spc::ratio_numerator * (cpucycles - spc::reference_time) + spc::remainder) % |
287 | spc::ratio_denominator; |
288 | } |
289 | |
290 | uint8 S9xAPUReadPort(int port) |
291 | { |
292 | S9xAPUExecute(); |
293 | return ((uint8)SNES::smp.port_read(port & 3)); |
294 | } |
295 | |
296 | void S9xAPUWritePort(int port, uint8 byte) |
297 | { |
298 | S9xAPUExecute(); |
299 | SNES::cpu.port_write(port & 3, byte); |
300 | } |
301 | |
302 | void S9xAPUSetReferenceTime(int32 cpucycles) |
303 | { |
304 | spc::reference_time = cpucycles; |
305 | } |
306 | |
307 | void S9xAPUExecute(void) |
308 | { |
309 | SNES::smp.clock -= S9xAPUGetClock(CPU.Cycles); |
310 | SNES::smp.enter(); |
311 | |
312 | spc::remainder = S9xAPUGetClockRemainder(CPU.Cycles); |
313 | |
314 | S9xAPUSetReferenceTime(CPU.Cycles); |
315 | } |
316 | |
317 | void S9xAPUEndScanline(void) |
318 | { |
319 | S9xAPUExecute(); |
320 | SNES::dsp.synchronize(); |
321 | |
322 | if (spc::resampler->space_filled() >= APU_SAMPLE_BLOCK || !spc::sound_in_sync) |
323 | S9xLandSamples(); |
324 | } |
325 | |
326 | void S9xAPUTimingSetSpeedup(int ticks) |
327 | { |
328 | if (ticks != 0) |
329 | printf("APU speedup hack: %d\n" , ticks); |
330 | |
331 | spc::timing_hack_denominator = 256 - ticks; |
332 | |
333 | spc::ratio_numerator = Settings.PAL ? APU_NUMERATOR_PAL : APU_NUMERATOR_NTSC; |
334 | spc::ratio_denominator = Settings.PAL ? APU_DENOMINATOR_PAL : APU_DENOMINATOR_NTSC; |
335 | spc::ratio_denominator = spc::ratio_denominator * spc::timing_hack_denominator / spc::timing_hack_numerator; |
336 | |
337 | UpdatePlaybackRate(); |
338 | } |
339 | |
340 | void S9xResetAPU(void) |
341 | { |
342 | spc::reference_time = 0; |
343 | spc::remainder = 0; |
344 | |
345 | SNES::cpu.reset(); |
346 | SNES::smp.power(); |
347 | SNES::dsp.power(); |
348 | SNES::dsp.spc_dsp.set_spc_snapshot_callback(SPCSnapshotCallback); |
349 | |
350 | S9xClearSamples(); |
351 | } |
352 | |
353 | void S9xSoftResetAPU(void) |
354 | { |
355 | spc::reference_time = 0; |
356 | spc::remainder = 0; |
357 | SNES::cpu.reset(); |
358 | SNES::smp.reset(); |
359 | SNES::dsp.reset(); |
360 | |
361 | S9xClearSamples(); |
362 | } |
363 | |
364 | void S9xAPUSaveState(uint8 *block) |
365 | { |
366 | uint8 *ptr = block; |
367 | |
368 | SNES::smp.save_state(&ptr); |
369 | SNES::dsp.save_state(&ptr); |
370 | |
371 | SNES::set_le32(ptr, spc::reference_time); |
372 | ptr += sizeof(int32); |
373 | SNES::set_le32(ptr, spc::remainder); |
374 | ptr += sizeof(int32); |
375 | SNES::set_le32(ptr, SNES::dsp.clock); |
376 | ptr += sizeof(int32); |
377 | memcpy(ptr, SNES::cpu.registers, 4); |
378 | ptr += sizeof(int32); |
379 | |
380 | memset(ptr, 0, SPC_SAVE_STATE_BLOCK_SIZE - (ptr - block)); |
381 | } |
382 | |
383 | void S9xAPULoadState(uint8 *block) |
384 | { |
385 | uint8 *ptr = block; |
386 | |
387 | SNES::smp.load_state(&ptr); |
388 | SNES::dsp.load_state(&ptr); |
389 | |
390 | spc::reference_time = SNES::get_le32(ptr); |
391 | ptr += sizeof(int32); |
392 | spc::remainder = SNES::get_le32(ptr); |
393 | ptr += sizeof(int32); |
394 | SNES::dsp.clock = SNES::get_le32(ptr); |
395 | ptr += sizeof(int32); |
396 | memcpy(SNES::cpu.registers, ptr, 4); |
397 | } |
398 | |
399 | static void to_var_from_buf(uint8 **buf, void *var, size_t size) |
400 | { |
401 | memcpy(var, *buf, size); |
402 | *buf += size; |
403 | } |
404 | |
405 | #undef IF_0_THEN_256 |
406 | #define IF_0_THEN_256(n) ((uint8)((n)-1) + 1) |
407 | void S9xAPULoadBlarggState(uint8 *oldblock) |
408 | { |
409 | uint8 *ptr = oldblock; |
410 | |
411 | SNES::SPC_State_Copier copier(&ptr, to_var_from_buf); |
412 | |
413 | copier.copy(SNES::smp.apuram, 0x10000); // RAM |
414 | |
415 | uint8 regs_in[0x10]; |
416 | uint8 regs[0x10]; |
417 | uint16 pc, spc_time, dsp_time; |
418 | uint8 a, x, y, psw, sp; |
419 | |
420 | copier.copy(regs, 0x10); // REGS |
421 | copier.copy(regs_in, 0x10); // REGS_IN |
422 | |
423 | // CPU Regs |
424 | pc = copier.copy_int(0, sizeof(uint16)); |
425 | a = copier.copy_int(0, sizeof(uint8)); |
426 | x = copier.copy_int(0, sizeof(uint8)); |
427 | y = copier.copy_int(0, sizeof(uint8)); |
428 | psw = copier.copy_int(0, sizeof(uint8)); |
429 | sp = copier.copy_int(0, sizeof(uint8)); |
430 | copier.extra(); |
431 | |
432 | // times |
433 | spc_time = copier.copy_int(0, sizeof(uint16)); |
434 | dsp_time = copier.copy_int(0, sizeof(uint16)); |
435 | |
436 | int cur_time = S9xAPUGetClock(CPU.Cycles); |
437 | |
438 | // spc_time is absolute, dsp_time is relative |
439 | // smp.clock is relative, dsp.clock relative but counting upwards |
440 | SNES::smp.clock = spc_time - cur_time; |
441 | SNES::dsp.clock = -1 * dsp_time; |
442 | |
443 | // DSP |
444 | SNES::dsp.load_state(&ptr); |
445 | |
446 | // Timers |
447 | uint16 next_time[3]; |
448 | uint8 divider[3], counter[3]; |
449 | for (int i = 0; i < 3; i++) |
450 | { |
451 | next_time[i] = copier.copy_int(0, sizeof(uint16)); |
452 | divider[i] = copier.copy_int(0, sizeof(uint8)); |
453 | counter[i] = copier.copy_int(0, sizeof(uint8)); |
454 | copier.extra(); |
455 | } |
456 | // construct timers out of available parts from blargg smp |
457 | SNES::smp.timer0.enable = regs[1] >> 0 & 1; // regs[1] = CONTROL |
458 | SNES::smp.timer0.target = IF_0_THEN_256(regs[10]); // regs[10+i] = TiTARGET |
459 | // blargg counts time, get ticks through timer frequency |
460 | // (assume tempo = 256) |
461 | SNES::smp.timer0.stage1_ticks = 128 - (next_time[0] - cur_time) / 128; |
462 | SNES::smp.timer0.stage2_ticks = divider[0]; |
463 | SNES::smp.timer0.stage3_ticks = counter[0]; |
464 | |
465 | SNES::smp.timer1.enable = regs[1] >> 1 & 1; |
466 | SNES::smp.timer1.target = IF_0_THEN_256(regs[11]); |
467 | SNES::smp.timer1.stage1_ticks = 128 - (next_time[1] - cur_time) / 128; |
468 | SNES::smp.timer1.stage2_ticks = divider[0]; |
469 | SNES::smp.timer1.stage3_ticks = counter[0]; |
470 | |
471 | SNES::smp.timer2.enable = regs[1] >> 2 & 1; |
472 | SNES::smp.timer2.target = IF_0_THEN_256(regs[12]); |
473 | SNES::smp.timer2.stage1_ticks = 16 - (next_time[2] - cur_time) / 16; |
474 | SNES::smp.timer2.stage2_ticks = divider[0]; |
475 | SNES::smp.timer2.stage3_ticks = counter[0]; |
476 | |
477 | copier.extra(); |
478 | |
479 | SNES::smp.opcode_number = 0; |
480 | SNES::smp.opcode_cycle = 0; |
481 | |
482 | SNES::smp.regs.pc = pc; |
483 | SNES::smp.regs.sp = sp; |
484 | SNES::smp.regs.B.a = a; |
485 | SNES::smp.regs.x = x; |
486 | SNES::smp.regs.B.y = y; |
487 | |
488 | // blargg's psw has same layout as byuu's flags |
489 | SNES::smp.regs.p = psw; |
490 | |
491 | // blargg doesn't explicitly store iplrom_enable |
492 | SNES::smp.status.iplrom_enable = regs[1] & 0x80; |
493 | |
494 | SNES::smp.status.dsp_addr = regs[2]; |
495 | |
496 | SNES::smp.status.ram00f8 = regs_in[8]; |
497 | SNES::smp.status.ram00f9 = regs_in[9]; |
498 | |
499 | // default to 0 - we are on an opcode boundary, shouldn't matter |
500 | SNES::smp.rd = SNES::smp.wr = SNES::smp.dp = SNES::smp.sp = SNES::smp.ya = SNES::smp.bit = 0; |
501 | |
502 | spc::reference_time = SNES::get_le32(ptr); |
503 | ptr += sizeof(int32); |
504 | spc::remainder = SNES::get_le32(ptr); |
505 | |
506 | // blargg stores CPUIx in regs_in |
507 | memcpy(SNES::cpu.registers, regs_in + 4, 4); |
508 | } |
509 | |
510 | bool8 S9xSPCDump(const char *filename) |
511 | { |
512 | FILE *fs; |
513 | uint8 buf[SPC_FILE_SIZE]; |
514 | size_t ignore; |
515 | |
516 | fs = fopen(filename, "wb" ); |
517 | if (!fs) |
518 | return (FALSE); |
519 | |
520 | S9xSetSoundMute(TRUE); |
521 | |
522 | SNES::smp.save_spc(buf); |
523 | |
524 | ignore = fwrite(buf, SPC_FILE_SIZE, 1, fs); |
525 | |
526 | if (ignore == 0) |
527 | { |
528 | fprintf(stderr, "Couldn't write file %s.\n" , filename); |
529 | } |
530 | |
531 | fclose(fs); |
532 | |
533 | S9xSetSoundMute(FALSE); |
534 | |
535 | return (TRUE); |
536 | } |
537 | |