1 // Based on MAME driver by Nicola Salmoria
2
3 #include "burnint.h"
4 #include "sn76496.h"
5 #include <stddef.h>
6
7 #define MAX_SN76496_CHIPS 8
8
9 #define MAX_OUTPUT 0x7fff
10
11 #define STEP 0x10000
12
13 struct SN76496
14 {
15 INT32 Register[8]; /* registers */
16 INT32 LastRegister; /* last register written */
17 INT32 Volume[4]; /* volume of voice 0-2 and noise */
18 INT32 RNG; /* noise generator */
19 INT32 NoiseMode; /* active noise mode */
20 INT32 Period[4];
21 INT32 Count[4];
22 INT32 Output[4];
23 INT32 StereoMask; /* the stereo output mask */
24
25 // Init-time stuff
26 INT32 VolTable[16]; /* volume table */
27 INT32 FeedbackMask; /* mask for feedback */
28 INT32 WhitenoiseTaps; /* mask for white noise taps */
29 INT32 WhitenoiseInvert; /* white noise invert flag */
30 INT32 bSignalAdd;
31 double nVolume;
32 INT32 nOutputDir;
33 UINT32 UpdateStep;
34 };
35
36 static INT32 NumChips = 0;
37 static struct SN76496 *Chips[MAX_SN76496_CHIPS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
38
SN76496Update(INT32 Num,INT16 * pSoundBuf,INT32 Length)39 void SN76496Update(INT32 Num, INT16* pSoundBuf, INT32 Length)
40 {
41 #if defined FBA_DEBUG
42 if (!DebugSnd_SN76496Initted) bprintf(PRINT_ERROR, _T("SN76496Update called without init\n"));
43 if (Num > NumChips) bprintf(PRINT_ERROR, _T("SN76496Update called with invalid chip %x\n"), Num);
44 #endif
45
46 if (Num >= MAX_SN76496_CHIPS) return;
47
48 INT32 i;
49 struct SN76496 *R = Chips[Num];
50
51 #if 0
52 // this hack breaks sounds in time pilot '84 (tp84)
53 /* If the volume is 0, increase the counter */
54 for (i = 0;i < 4;i++)
55 {
56 if (R->Volume[i] == 0)
57 {
58 /* note that I do count += length, NOT count = length + 1. You might think */
59 /* it's the same since the volume is 0, but doing the latter could cause */
60 /* interferencies when the program is rapidly modulating the volume. */
61 if (R->Count[i] <= Length*STEP) R->Count[i] += Length*STEP;
62 }
63 }
64 #endif
65
66 while (Length > 0)
67 {
68 INT32 Vol[4];
69 UINT32 Out, Out2 = 0;
70 INT32 Left;
71
72
73 /* vol[] keeps track of how long each square wave stays */
74 /* in the 1 position during the sample period. */
75 Vol[0] = Vol[1] = Vol[2] = Vol[3] = 0;
76
77 for (i = 0;i < 3;i++)
78 {
79 if (R->Output[i]) Vol[i] += R->Count[i];
80 R->Count[i] -= STEP;
81 /* Period[i] is the half period of the square wave. Here, in each */
82 /* loop I add Period[i] twice, so that at the end of the loop the */
83 /* square wave is in the same status (0 or 1) it was at the start. */
84 /* vol[i] is also incremented by Period[i], since the wave has been 1 */
85 /* exactly half of the time, regardless of the initial position. */
86 /* If we exit the loop in the middle, Output[i] has to be inverted */
87 /* and vol[i] incremented only if the exit status of the square */
88 /* wave is 1. */
89 while (R->Count[i] <= 0)
90 {
91 R->Count[i] += R->Period[i];
92 if (R->Count[i] > 0)
93 {
94 R->Output[i] ^= 1;
95 if (R->Output[i]) Vol[i] += R->Period[i];
96 break;
97 }
98 R->Count[i] += R->Period[i];
99 Vol[i] += R->Period[i];
100 }
101 if (R->Output[i]) Vol[i] -= R->Count[i];
102 }
103
104 Left = STEP;
105 do
106 {
107 INT32 NextEvent;
108
109
110 if (R->Count[3] < Left) NextEvent = R->Count[3];
111 else NextEvent = Left;
112
113 if (R->Output[3]) Vol[3] += R->Count[3];
114 R->Count[3] -= NextEvent;
115 if (R->Count[3] <= 0)
116 {
117 if (R->NoiseMode == 1) /* White Noise Mode */
118 {
119 if (((R->RNG & R->WhitenoiseTaps) != R->WhitenoiseTaps) && ((R->RNG & R->WhitenoiseTaps) != 0)) /* crappy xor! */
120 {
121 R->RNG >>= 1;
122 R->RNG |= R->FeedbackMask;
123 }
124 else
125 {
126 R->RNG >>= 1;
127 }
128 R->Output[3] = R->WhitenoiseInvert ? !(R->RNG & 1) : R->RNG & 1;
129 }
130 else /* Periodic noise mode */
131 {
132 if (R->RNG & 1)
133 {
134 R->RNG >>= 1;
135 R->RNG |= R->FeedbackMask;
136 }
137 else
138 {
139 R->RNG >>= 1;
140 }
141 R->Output[3] = R->RNG & 1;
142 }
143 R->Count[3] += R->Period[3];
144 if (R->Output[3]) Vol[3] += R->Period[3];
145 }
146 if (R->Output[3]) Vol[3] -= R->Count[3];
147
148 Left -= NextEvent;
149 } while (Left > 0);
150
151 if (R->StereoMask != 0xFF)
152 {
153 Out = ((R->StereoMask&0x10) ? Vol[0] * R->Volume[0]:0)
154 + ((R->StereoMask&0x20) ? Vol[1] * R->Volume[1]:0)
155 + ((R->StereoMask&0x40) ? Vol[2] * R->Volume[2]:0)
156 + ((R->StereoMask&0x80) ? Vol[3] * R->Volume[3]:0);
157
158 Out2 = ((R->StereoMask&0x1) ? Vol[0] * R->Volume[0]:0)
159 + ((R->StereoMask&0x2) ? Vol[1] * R->Volume[1]:0)
160 + ((R->StereoMask&0x4) ? Vol[2] * R->Volume[2]:0)
161 + ((R->StereoMask&0x8) ? Vol[3] * R->Volume[3]:0);
162 if (Out2 > MAX_OUTPUT * STEP) Out2 = MAX_OUTPUT * STEP;
163
164 Out2 /= STEP;
165 }
166 else
167 {
168 Out = Vol[0] * R->Volume[0] + Vol[1] * R->Volume[1] +
169 Vol[2] * R->Volume[2] + Vol[3] * R->Volume[3];
170 }
171
172 if (Out > MAX_OUTPUT * STEP) Out = MAX_OUTPUT * STEP;
173
174 Out /= STEP;
175
176 INT32 nLeftSample = 0, nRightSample = 0;
177 if ((R->nOutputDir & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
178 nLeftSample += (INT32)(Out * R->nVolume);
179 }
180 if ((R->nOutputDir & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
181 if (R->StereoMask != 0xFF)
182 nRightSample += (INT32)(Out2 * R->nVolume);
183 else
184 nRightSample += (INT32)(Out * R->nVolume);
185 }
186
187 if (R->bSignalAdd) {
188 pSoundBuf[0] = BURN_SND_CLIP(pSoundBuf[0] + nLeftSample);
189 pSoundBuf[1] = BURN_SND_CLIP(pSoundBuf[1] + nRightSample);
190 } else {
191 pSoundBuf[0] = BURN_SND_CLIP(nLeftSample);
192 pSoundBuf[1] = BURN_SND_CLIP(nRightSample);
193 }
194
195 pSoundBuf += 2;
196 Length--;
197 }
198 }
199
SN76496UpdateToBuffer(INT32 Num,INT16 * pSoundBuf,INT32 Length)200 void SN76496UpdateToBuffer(INT32 Num, INT16* pSoundBuf, INT32 Length)
201 {
202 #if defined FBA_DEBUG
203 if (!DebugSnd_SN76496Initted) bprintf(PRINT_ERROR, _T("SN76496Update called without init\n"));
204 if (Num > NumChips) bprintf(PRINT_ERROR, _T("SN76496Update called with invalid chip %x\n"), Num);
205 #endif
206
207 INT32 i;
208
209 if (Num >= MAX_SN76496_CHIPS) return;
210
211 struct SN76496 *R = Chips[Num];
212
213 /* If the volume is 0, increase the counter */
214 for (i = 0;i < 4;i++)
215 {
216 if (R->Volume[i] == 0)
217 {
218 /* note that I do count += length, NOT count = length + 1. You might think */
219 /* it's the same since the volume is 0, but doing the latter could cause */
220 /* interferencies when the program is rapidly modulating the volume. */
221 if (R->Count[i] <= Length*STEP) R->Count[i] += Length*STEP;
222 }
223 }
224
225 while (Length > 0)
226 {
227 INT32 Vol[4];
228 UINT32 Out;
229 INT32 Left;
230
231
232 /* vol[] keeps track of how long each square wave stays */
233 /* in the 1 position during the sample period. */
234 Vol[0] = Vol[1] = Vol[2] = Vol[3] = 0;
235
236 for (i = 0;i < 3;i++)
237 {
238 if (R->Output[i]) Vol[i] += R->Count[i];
239 R->Count[i] -= STEP;
240 /* Period[i] is the half period of the square wave. Here, in each */
241 /* loop I add Period[i] twice, so that at the end of the loop the */
242 /* square wave is in the same status (0 or 1) it was at the start. */
243 /* vol[i] is also incremented by Period[i], since the wave has been 1 */
244 /* exactly half of the time, regardless of the initial position. */
245 /* If we exit the loop in the middle, Output[i] has to be inverted */
246 /* and vol[i] incremented only if the exit status of the square */
247 /* wave is 1. */
248 while (R->Count[i] <= 0)
249 {
250 R->Count[i] += R->Period[i];
251 if (R->Count[i] > 0)
252 {
253 R->Output[i] ^= 1;
254 if (R->Output[i]) Vol[i] += R->Period[i];
255 break;
256 }
257 R->Count[i] += R->Period[i];
258 Vol[i] += R->Period[i];
259 }
260 if (R->Output[i]) Vol[i] -= R->Count[i];
261 }
262
263 Left = STEP;
264 do
265 {
266 INT32 NextEvent;
267
268
269 if (R->Count[3] < Left) NextEvent = R->Count[3];
270 else NextEvent = Left;
271
272 if (R->Output[3]) Vol[3] += R->Count[3];
273 R->Count[3] -= NextEvent;
274 if (R->Count[3] <= 0)
275 {
276 if (R->NoiseMode == 1) /* White Noise Mode */
277 {
278 if (((R->RNG & R->WhitenoiseTaps) != R->WhitenoiseTaps) && ((R->RNG & R->WhitenoiseTaps) != 0)) /* crappy xor! */
279 {
280 R->RNG >>= 1;
281 R->RNG |= R->FeedbackMask;
282 }
283 else
284 {
285 R->RNG >>= 1;
286 }
287 R->Output[3] = R->WhitenoiseInvert ? !(R->RNG & 1) : R->RNG & 1;
288 }
289 else /* Periodic noise mode */
290 {
291 if (R->RNG & 1)
292 {
293 R->RNG >>= 1;
294 R->RNG |= R->FeedbackMask;
295 }
296 else
297 {
298 R->RNG >>= 1;
299 }
300 R->Output[3] = R->RNG & 1;
301 }
302 R->Count[3] += R->Period[3];
303 if (R->Output[3]) Vol[3] += R->Period[3];
304 }
305 if (R->Output[3]) Vol[3] -= R->Count[3];
306
307 Left -= NextEvent;
308 } while (Left > 0);
309
310 Out = Vol[0] * R->Volume[0] + Vol[1] * R->Volume[1] +
311 Vol[2] * R->Volume[2] + Vol[3] * R->Volume[3];
312
313 if (Out > MAX_OUTPUT * STEP) Out = MAX_OUTPUT * STEP;
314
315 pSoundBuf[0] = BURN_SND_CLIP(((INT32)((Out / STEP) * R->nVolume)));
316 pSoundBuf++;
317 Length--;
318 }
319 }
320
SN76496StereoWrite(INT32 Num,INT32 Data)321 void SN76496StereoWrite(INT32 Num, INT32 Data)
322 {
323 #if defined FBA_DEBUG
324 if (!DebugSnd_SN76496Initted) bprintf(PRINT_ERROR, _T("SN76496StereoWrite called without init\n"));
325 if (Num > NumChips) bprintf(PRINT_ERROR, _T("SN76496StereoWrite called with invalid chip %x\n"), Num);
326 #endif
327
328 if (Num >= MAX_SN76496_CHIPS) return;
329
330 struct SN76496 *R = Chips[Num];
331
332 R->StereoMask = Data;
333 }
334
SN76496Write(INT32 Num,INT32 Data)335 void SN76496Write(INT32 Num, INT32 Data)
336 {
337 #if defined FBA_DEBUG
338 if (!DebugSnd_SN76496Initted) bprintf(PRINT_ERROR, _T("SN76496Write called without init\n"));
339 if (Num > NumChips) bprintf(PRINT_ERROR, _T("SN76496Write called with invalid chip %x\n"), Num);
340 #endif
341
342 INT32 n, r, c;
343
344 if (Num >= MAX_SN76496_CHIPS) return;
345
346 struct SN76496 *R = Chips[Num];
347
348 if (Data & 0x80) {
349 r = (Data & 0x70) >> 4;
350 R->LastRegister = r;
351 R->Register[r] = (R->Register[r] & 0x3f0) | (Data & 0x0f);
352 } else {
353 r = R->LastRegister;
354 }
355
356 c = r / 2;
357
358 switch (r)
359 {
360 case 0: /* tone 0 : frequency */
361 case 2: /* tone 1 : frequency */
362 case 4: /* tone 2 : frequency */
363 if ((Data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x0f) | ((Data & 0x3f) << 4);
364 R->Period[c] = R->UpdateStep * R->Register[r];
365 if (R->Period[c] == 0) R->Period[c] = R->UpdateStep;
366 if (r == 4)
367 {
368 /* update noise shift frequency */
369 if ((R->Register[6] & 0x03) == 0x03)
370 R->Period[3] = 2 * R->Period[2];
371 }
372 break;
373 case 1: /* tone 0 : volume */
374 case 3: /* tone 1 : volume */
375 case 5: /* tone 2 : volume */
376 case 7: /* noise : volume */
377 R->Volume[c] = R->VolTable[Data & 0x0f];
378 if ((Data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x3f0) | (Data & 0x0f);
379 break;
380 case 6: /* noise : frequency, mode */
381 {
382 if ((Data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x3f0) | (Data & 0x0f);
383 n = R->Register[6];
384 R->NoiseMode = (n & 4) ? 1 : 0;
385 /* N/512,N/1024,N/2048,Tone #3 output */
386 R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+(n&3)));
387 /* Reset noise shifter */
388 R->RNG = R->FeedbackMask; /* this is correct according to the smspower document */
389 //R->RNG = 0xF35; /* this is not, but sounds better in do run run */
390 R->Output[3] = R->RNG & 1;
391 }
392 break;
393 }
394 }
395
SN76496SetGain(struct SN76496 * R,INT32 Gain)396 static void SN76496SetGain(struct SN76496 *R,INT32 Gain)
397 {
398 INT32 i;
399 double Out;
400
401 Gain &= 0xff;
402
403 /* increase max output basing on gain (0.2 dB per step) */
404 Out = MAX_OUTPUT / 4;
405 while (Gain-- > 0)
406 Out *= 1.023292992; /* = (10 ^ (0.2/20)) */
407
408 /* build volume table (2dB per step) */
409 for (i = 0;i < 15;i++)
410 {
411 /* limit volume to avoid clipping */
412 if (Out > MAX_OUTPUT / 4) R->VolTable[i] = MAX_OUTPUT / 4;
413 else R->VolTable[i] = (INT32)Out;
414
415 Out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
416 }
417 R->VolTable[15] = 0;
418 }
419
SN76496Reset()420 void SN76496Reset()
421 {
422 #if defined FBA_DEBUG
423 if (!DebugSnd_SN76496Initted) bprintf(PRINT_ERROR, _T("SN76496Reset called without init\n"));
424 #endif
425
426 INT32 i;
427
428 for (INT32 Num = 0; Num < NumChips; Num++) {
429 struct SN76496 *R = Chips[Num];
430
431 for (i = 0; i < 4; i++) R->Volume[i] = 0;
432
433 R->LastRegister = 0;
434 for (i = 0; i < 8; i += 2) {
435 R->Register[i + 0] = 0x00;
436 R->Register[i + 1] = 0x0f;
437 }
438
439 for (i = 0; i < 4; i++) {
440 R->Output[i] = 0;
441 R->Period[i] = R->Count[i] = R->UpdateStep;
442 }
443
444 R->FeedbackMask = 0x4000;
445 R->WhitenoiseTaps = 0x03;
446 R->WhitenoiseInvert = 1;
447 R->StereoMask = 0xFF;
448
449 R->RNG = R->FeedbackMask;
450 R->Output[3] = R->RNG & 1;
451 }
452 }
453
SN76496Init(struct SN76496 * R,INT32 Clock)454 static void SN76496Init(struct SN76496 *R, INT32 Clock)
455 {
456 R->UpdateStep = (UINT32)(((double)STEP * nBurnSoundRate * 16) / Clock);
457
458 SN76496Reset();
459 }
460
GenericStart(INT32 Num,INT32 Clock,INT32 FeedbackMask,INT32 NoiseTaps,INT32 NoiseInvert,INT32 SignalAdd)461 static void GenericStart(INT32 Num, INT32 Clock, INT32 FeedbackMask, INT32 NoiseTaps, INT32 NoiseInvert, INT32 SignalAdd)
462 {
463 DebugSnd_SN76496Initted = 1;
464
465 if (Num >= MAX_SN76496_CHIPS) return;
466
467 NumChips = Num + 1;
468
469 Chips[Num] = (struct SN76496*)BurnMalloc(sizeof(struct SN76496));
470 memset(Chips[Num], 0, sizeof(struct SN76496));
471
472 SN76496Init(Chips[Num], Clock);
473 SN76496SetGain(Chips[Num], 0);
474
475 Chips[Num]->FeedbackMask = FeedbackMask;
476 Chips[Num]->WhitenoiseTaps = NoiseTaps;
477 Chips[Num]->WhitenoiseInvert = NoiseInvert;
478 Chips[Num]->bSignalAdd = SignalAdd;
479 Chips[Num]->nVolume = 1.00;
480 Chips[Num]->nOutputDir = BURN_SND_ROUTE_BOTH;
481 }
482
SN76489Init(INT32 Num,INT32 Clock,INT32 SignalAdd)483 void SN76489Init(INT32 Num, INT32 Clock, INT32 SignalAdd)
484 {
485 return GenericStart(Num, Clock, 0x4000, 0x03, 1, SignalAdd);
486 }
487
SN76489AInit(INT32 Num,INT32 Clock,INT32 SignalAdd)488 void SN76489AInit(INT32 Num, INT32 Clock, INT32 SignalAdd)
489 {
490 return GenericStart(Num, Clock, 0x8000, 0x06, 0, SignalAdd);
491 }
492
SN76494Init(INT32 Num,INT32 Clock,INT32 SignalAdd)493 void SN76494Init(INT32 Num, INT32 Clock, INT32 SignalAdd)
494 {
495 return GenericStart(Num, Clock, 0x8000, 0x06, 0, SignalAdd);
496 }
497
SN76496Init(INT32 Num,INT32 Clock,INT32 SignalAdd)498 void SN76496Init(INT32 Num, INT32 Clock, INT32 SignalAdd)
499 {
500 return GenericStart(Num, Clock, 0x8000, 0x06, 0, SignalAdd);
501 }
502
SN76496SetRoute(INT32 Num,double nVolume,INT32 nRouteDir)503 void SN76496SetRoute(INT32 Num, double nVolume, INT32 nRouteDir)
504 {
505 #if defined FBA_DEBUG
506 if (!DebugSnd_SN76496Initted) bprintf(PRINT_ERROR, _T("SN76496SetRoute called without init\n"));
507 if (Num > NumChips) bprintf(PRINT_ERROR, _T("SN76496SetRoute called with invalid chip %i\n"), Num);
508 #endif
509
510 if (Num >= MAX_SN76496_CHIPS) return;
511
512 struct SN76496 *R = Chips[Num];
513
514 R->nVolume = nVolume;
515 R->nOutputDir = nRouteDir;
516 }
517
SN76496Exit()518 void SN76496Exit()
519 {
520 #if defined FBA_DEBUG
521 if (!DebugSnd_SN76496Initted) bprintf(PRINT_ERROR, _T("SN76496Exit called without init\n"));
522 #endif
523
524 for (INT32 i = 0; i < NumChips; i++) {
525 BurnFree(Chips[i]);
526 Chips[i] = NULL;
527 }
528
529 NumChips = 0;
530
531 DebugSnd_SN76496Initted = 0;
532 }
533
SN76496Scan(INT32 nAction,INT32 * pnMin)534 void SN76496Scan(INT32 nAction, INT32 *pnMin)
535 {
536 #if defined FBA_DEBUG
537 if (!DebugSnd_SN76496Initted) bprintf(PRINT_ERROR, _T("SN76496Scan called without init\n"));
538 #endif
539
540 if (pnMin != NULL) {
541 *pnMin = 0x029719;
542 }
543
544 if (nAction & ACB_DRIVER_DATA) {
545 for (INT32 i = 0; i < NumChips; i++) {
546 ScanVar(Chips[i], STRUCT_SIZE_HELPER(struct SN76496, StereoMask), "SN76496/SN76489 Chip");
547 }
548 }
549 }
550
551 #undef MAX_SN76496_CHIPS
552 #undef MAX_OUTPUT
553 #undef STEP
554