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
17static const int APU_DEFAULT_INPUT_RATE = 31950; // ~59.94Hz
18static const int APU_SAMPLE_BLOCK = 48;
19static const int APU_NUMERATOR_NTSC = 15664;
20static const int APU_DENOMINATOR_NTSC = 328125;
21static const int APU_NUMERATOR_PAL = 34176;
22static 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.
28static const int MINIMUM_BUFFER_SIZE = 550 * 2;
29
30namespace SNES {
31#include "bapu/dsp/blargg_endian.h"
32CPU cpu;
33} // namespace SNES
34
35namespace spc {
36static apu_callback callback = NULL;
37static void *callback_data = NULL;
38
39static bool8 sound_in_sync = TRUE;
40static bool8 sound_enabled = FALSE;
41
42static Resampler *resampler = NULL;
43
44static int32 reference_time;
45static uint32 remainder;
46
47static const int timing_hack_numerator = 256;
48static int timing_hack_denominator = 256;
49/* Set these to NTSC for now. Will change to PAL in S9xAPUTimingSetSpeedup
50 if necessary on game load. */
51static uint32 ratio_numerator = APU_NUMERATOR_NTSC;
52static uint32 ratio_denominator = APU_DENOMINATOR_NTSC;
53
54static double dynamic_rate_multiplier = 1.0;
55} // namespace spc
56
57namespace msu {
58// Always 16-bit, Stereo; 1.5x dsp buffer to never overflow
59static Resampler *resampler = NULL;
60static int16 *resample_buffer = NULL;
61static int resample_buffer_size = 0;
62} // namespace msu
63
64static void UpdatePlaybackRate(void);
65static void SPCSnapshotCallback(void);
66static inline int S9xAPUGetClock(int32);
67static inline int S9xAPUGetClockRemainder(int32);
68
69bool8 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
123int S9xGetSampleCount(void)
124{
125 return spc::resampler->avail();
126}
127
128void 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
140void S9xClearSamples(void)
141{
142 spc::resampler->clear();
143 if (Settings.MSU1)
144 msu::resampler->clear();
145}
146
147bool8 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
157void S9xSetSamplesAvailableCallback(apu_callback callback, void *data)
158{
159 spc::callback = callback;
160 spc::callback_data = data;
161}
162
163void 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
171static 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
192bool8 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
230void S9xSetSoundControl(uint8 voice_switch)
231{
232 SNES::dsp.spc_dsp.set_stereo_switch(voice_switch << 8 | voice_switch);
233}
234
235void S9xSetSoundMute(bool8 mute)
236{
237 Settings.Mute = mute;
238 if (!spc::sound_enabled)
239 Settings.Mute = TRUE;
240}
241
242void S9xDumpSPCSnapshot(void)
243{
244 SNES::dsp.spc_dsp.dump_spc_snapshot();
245}
246
247static void SPCSnapshotCallback(void)
248{
249 S9xSPCDump(S9xGetFilenameInc((".spc"), SPC_DIR));
250 printf("Dumped key-on triggered spc snapshot.\n");
251}
252
253bool8 S9xInitAPU(void)
254{
255 spc::resampler = NULL;
256 msu::resampler = NULL;
257
258 return (TRUE);
259}
260
261void 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
278static inline int S9xAPUGetClock(int32 cpucycles)
279{
280 return (spc::ratio_numerator * (cpucycles - spc::reference_time) + spc::remainder) /
281 spc::ratio_denominator;
282}
283
284static inline int S9xAPUGetClockRemainder(int32 cpucycles)
285{
286 return (spc::ratio_numerator * (cpucycles - spc::reference_time) + spc::remainder) %
287 spc::ratio_denominator;
288}
289
290uint8 S9xAPUReadPort(int port)
291{
292 S9xAPUExecute();
293 return ((uint8)SNES::smp.port_read(port & 3));
294}
295
296void S9xAPUWritePort(int port, uint8 byte)
297{
298 S9xAPUExecute();
299 SNES::cpu.port_write(port & 3, byte);
300}
301
302void S9xAPUSetReferenceTime(int32 cpucycles)
303{
304 spc::reference_time = cpucycles;
305}
306
307void 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
317void 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
326void 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
340void 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
353void 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
364void 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
383void 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
399static 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)
407void 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
510bool8 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