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