1 #include "burnint.h"
2 #include "burn_y8950.h"
3 
4 // Timer Related
5 
6 #define MAX_TIMER_VALUE ((1 << 30) - 65536)
7 
8 static double dTimeY8950;									// Time elapsed since the emulated machine was started
9 
10 static INT32 nTimerCount[2], nTimerStart[2];
11 
12 // Callbacks
13 static INT32 (*pTimerOverCallback)(INT32, INT32);
14 static double (*pTimerTimeCallback)();
15 
16 static INT32 nCPUClockspeed = 0;
17 static INT32 (*pCPUTotalCycles)() = NULL;
18 static INT32 (*pCPURun)(INT32) = NULL;
19 static void (*pCPURunEnd)() = NULL;
20 
21 // ---------------------------------------------------------------------------
22 // Running time
23 
BurnTimerTimeCallbackDummy()24 static double BurnTimerTimeCallbackDummy()
25 {
26 	return 0.0;
27 }
28 
BurnTimerGetTimeY8950()29 extern "C" double BurnTimerGetTimeY8950()
30 {
31 	return dTimeY8950 + pTimerTimeCallback();
32 }
33 
34 // ---------------------------------------------------------------------------
35 // Update timers
36 
37 static INT32 nTicksTotal, nTicksDone, nTicksExtra;
38 
BurnTimerUpdateY8950(INT32 nCycles)39 INT32 BurnTimerUpdateY8950(INT32 nCycles)
40 {
41 	INT32 nIRQStatus = 0;
42 
43 	nTicksTotal = MAKE_TIMER_TICKS(nCycles, nCPUClockspeed);
44 
45 	while (nTicksDone < nTicksTotal) {
46 		INT32 nTimer, nCyclesSegment, nTicksSegment;
47 
48 		// Determine which timer fires first
49 		if (nTimerCount[0] <= nTimerCount[1]) {
50 			nTicksSegment = nTimerCount[0];
51 		} else {
52 			nTicksSegment = nTimerCount[1];
53 		}
54 		if (nTicksSegment > nTicksTotal) {
55 			nTicksSegment = nTicksTotal;
56 		}
57 
58 		nCyclesSegment = MAKE_CPU_CYLES(nTicksSegment + nTicksExtra, nCPUClockspeed);
59 
60 		pCPURun(nCyclesSegment - pCPUTotalCycles());
61 
62 		nTicksDone = MAKE_TIMER_TICKS(pCPUTotalCycles() + 1, nCPUClockspeed) - 1;
63 
64 		nTimer = 0;
65 		if (nTicksDone >= nTimerCount[0]) {
66 			if (nTimerStart[0] == MAX_TIMER_VALUE) {
67 				nTimerCount[0] = MAX_TIMER_VALUE;
68 			} else {
69 				nTimerCount[0] += nTimerStart[0];
70 			}
71 			nTimer |= 1;
72 		}
73 		if (nTicksDone >= nTimerCount[1]) {
74 			if (nTimerStart[1] == MAX_TIMER_VALUE) {
75 				nTimerCount[1] = MAX_TIMER_VALUE;
76 			} else {
77 				nTimerCount[1] += nTimerStart[1];
78 			}
79 			nTimer |= 2;
80 		}
81 		if (nTimer & 1) {
82 			nIRQStatus |= pTimerOverCallback(0, 0);
83 		}
84 		if (nTimer & 2) {
85 			nIRQStatus |= pTimerOverCallback(0, 1);
86 		}
87 	}
88 
89 	return nIRQStatus;
90 }
91 
BurnTimerEndFrameY8950(INT32 nCycles)92 void BurnTimerEndFrameY8950(INT32 nCycles)
93 {
94 	INT32 nTicks = MAKE_TIMER_TICKS(nCycles, nCPUClockspeed);
95 
96 	BurnTimerUpdateY8950(nCycles);
97 
98 	if (nTimerCount[0] < MAX_TIMER_VALUE) {
99 		nTimerCount[0] -= nTicks;
100 	}
101 	if (nTimerCount[1] < MAX_TIMER_VALUE) {
102 		nTimerCount[1] -= nTicks;
103 	}
104 
105 	nTicksDone -= nTicks;
106 	if (nTicksDone < 0) {
107 		nTicksDone = 0;
108 	}
109 }
110 
BurnTimerUpdateEndY8950()111 void BurnTimerUpdateEndY8950()
112 {
113 	pCPURunEnd();
114 
115 	nTicksTotal = 0;
116 }
117 
BurnOPLTimerCallbackY8950(INT32 c,double period)118 void BurnOPLTimerCallbackY8950(INT32 c, double period)
119 {
120 	pCPURunEnd();
121 
122 	if (period == 0.0) {
123 		nTimerCount[c] = MAX_TIMER_VALUE;
124 		return;
125 	}
126 
127 	nTimerCount[c]  = (INT32)(period * (double)TIMER_TICKS_PER_SECOND);
128 	nTimerCount[c] += MAKE_TIMER_TICKS(pCPUTotalCycles(), nCPUClockspeed);
129 }
130 
BurnTimerScanY8950(INT32 nAction,INT32 * pnMin)131 void BurnTimerScanY8950(INT32 nAction, INT32* pnMin)
132 {
133 	if (pnMin && *pnMin < 0x029521) {
134 		*pnMin = 0x029521;
135 	}
136 
137 	if (nAction & ACB_DRIVER_DATA) {
138 		SCAN_VAR(nTimerCount);
139 		SCAN_VAR(nTimerStart);
140 		SCAN_VAR(dTimeY8950);
141 
142 		SCAN_VAR(nTicksDone);
143 	}
144 }
145 
BurnTimerExitY8950()146 void BurnTimerExitY8950()
147 {
148 	nCPUClockspeed = 0;
149 	pCPUTotalCycles = NULL;
150 	pCPURun = NULL;
151 	pCPURunEnd = NULL;
152 
153 	return;
154 }
155 
BurnTimerResetY8950()156 void BurnTimerResetY8950()
157 {
158 	nTimerCount[0] = nTimerCount[1] = MAX_TIMER_VALUE;
159 	nTimerStart[0] = nTimerStart[1] = MAX_TIMER_VALUE;
160 
161 	dTimeY8950 = 0.0;
162 
163 	nTicksDone = 0;
164 }
165 
BurnTimerInitY8950(INT32 (* pOverCallback)(INT32,INT32),double (* pTimeCallback)())166 INT32 BurnTimerInitY8950(INT32 (*pOverCallback)(INT32, INT32), double (*pTimeCallback)())
167 {
168 	BurnTimerExitY8950();
169 
170 	pTimerOverCallback = pOverCallback;
171 	pTimerTimeCallback = pTimeCallback ? pTimeCallback : BurnTimerTimeCallbackDummy;
172 
173 	BurnTimerResetY8950();
174 
175 	return 0;
176 }
177 
178 
BurnTimerAttachY8950(cpu_core_config * ptr,INT32 nClockspeed)179 INT32 BurnTimerAttachY8950(cpu_core_config *ptr, INT32 nClockspeed)
180 {
181 	nCPUClockspeed = nClockspeed;
182 	pCPUTotalCycles = ptr->totalcycles;
183 	pCPURun = ptr->run;
184 	pCPURunEnd = ptr->runend;
185 
186 	nTicksExtra = MAKE_TIMER_TICKS(1, nCPUClockspeed) - 1;
187 
188 //	bprintf(PRINT_NORMAL, _T("--- timer cpu speed %iHz, one cycle = %i ticks.\n"), nClockspeed, MAKE_TIMER_TICKS(1, BurnTimerCPUClockspeed));
189 
190 	return 0;
191 }
192 
193 // Sound Related
194 
195 #define MAX_Y8950	2
196 
197 void (*BurnY8950Update)(INT16* pSoundBuf, INT32 nSegmentEnd);
198 
199 static INT32 (*BurnY8950StreamCallback)(INT32 nSoundRate);
200 
201 static INT32 nBurnY8950SoundRate;
202 
203 static INT16* pBuffer;
204 static INT16* pY8950Buffer[MAX_Y8950];
205 
206 static INT32 nY8950Position;
207 
208 static UINT32 nSampleSize;
209 static INT32 nFractionalPosition;
210 
211 static INT32 nNumChips = 0;
212 
213 static INT32 bY8950AddSignal;
214 
215 static double Y8950Volumes[1 * MAX_Y8950];
216 static INT32 Y8950RouteDirs[1 * MAX_Y8950];
217 
218 // ----------------------------------------------------------------------------
219 // Dummy functions
220 
Y8950UpdateDummy(INT16 *,INT32)221 static void Y8950UpdateDummy(INT16* , INT32)
222 {
223 	return;
224 }
225 
Y8950StreamCallbackDummy(INT32)226 static int Y8950StreamCallbackDummy(INT32)
227 {
228 	return 0;
229 }
230 
231 // ----------------------------------------------------------------------------
232 // Execute Y8950 for part of a frame
233 
Y8950Render(INT32 nSegmentLength)234 static void Y8950Render(INT32 nSegmentLength)
235 {
236 #if defined FBNEO_DEBUG
237 	if (!DebugSnd_Y8950Initted) bprintf(PRINT_ERROR, _T("Y8950Render called without init\n"));
238 #endif
239 
240 	if (nY8950Position >= nSegmentLength || !pBurnSoundOut) {
241 		return;
242 	}
243 
244 	nSegmentLength -= nY8950Position;
245 
246 	Y8950UpdateOne(0, pBuffer + 0 * 4096 + 4 + nY8950Position, nSegmentLength);
247 
248 	if (nNumChips > 1) {
249 		Y8950UpdateOne(1, pBuffer + 1 * 4096 + 4 + nY8950Position, nSegmentLength);
250 	}
251 
252 	nY8950Position += nSegmentLength;
253 }
254 
255 // ----------------------------------------------------------------------------
256 // Update the sound buffer
257 
Y8950UpdateResample(INT16 * pSoundBuf,INT32 nSegmentEnd)258 static void Y8950UpdateResample(INT16* pSoundBuf, INT32 nSegmentEnd)
259 {
260 #if defined FBNEO_DEBUG
261 	if (!DebugSnd_Y8950Initted) bprintf(PRINT_ERROR, _T("Y8950UpdateResample called without init\n"));
262 #endif
263 
264 	if (!pBurnSoundOut) return;
265 
266 	INT32 nSegmentLength = nSegmentEnd;
267 	INT32 nSamplesNeeded = nSegmentEnd * nBurnY8950SoundRate / nBurnSoundRate + 1;
268 
269 
270 	if (nSamplesNeeded < nY8950Position) {
271 		nSamplesNeeded = nY8950Position;
272 	}
273 
274 	if (nSegmentLength > nBurnSoundLen) {
275 		nSegmentLength = nBurnSoundLen;
276 	}
277 	nSegmentLength <<= 1;
278 
279 	Y8950Render(nSamplesNeeded);
280 
281 	pY8950Buffer[0] = pBuffer + 0 * 4096 + 4;
282 
283 	if (nNumChips > 1) {
284 		pY8950Buffer[1] = pBuffer + 1 * 4096 + 4;
285 	}
286 
287 	for (INT32 i = (nFractionalPosition & 0xFFFF0000) >> 15; i < nSegmentLength; i += 2, nFractionalPosition += nSampleSize) {
288 		INT32 nLeftSample[4] = {0, 0, 0, 0};
289 		INT32 nRightSample[4] = {0, 0, 0, 0};
290 		INT32 nTotalLeftSample, nTotalRightSample;
291 
292 		if ((Y8950RouteDirs[BURN_SND_Y8950_ROUTE] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
293 			nLeftSample[0] += (INT32)(pY8950Buffer[0][(nFractionalPosition >> 16) - 3] * Y8950Volumes[BURN_SND_Y8950_ROUTE]);
294 			nLeftSample[1] += (INT32)(pY8950Buffer[0][(nFractionalPosition >> 16) - 2] * Y8950Volumes[BURN_SND_Y8950_ROUTE]);
295 			nLeftSample[2] += (INT32)(pY8950Buffer[0][(nFractionalPosition >> 16) - 1] * Y8950Volumes[BURN_SND_Y8950_ROUTE]);
296 			nLeftSample[3] += (INT32)(pY8950Buffer[0][(nFractionalPosition >> 16) - 0] * Y8950Volumes[BURN_SND_Y8950_ROUTE]);
297 		}
298 		if ((Y8950RouteDirs[BURN_SND_Y8950_ROUTE] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
299 			nRightSample[0] += (INT32)(pY8950Buffer[0][(nFractionalPosition >> 16) - 3] * Y8950Volumes[BURN_SND_Y8950_ROUTE]);
300 			nRightSample[1] += (INT32)(pY8950Buffer[0][(nFractionalPosition >> 16) - 2] * Y8950Volumes[BURN_SND_Y8950_ROUTE]);
301 			nRightSample[2] += (INT32)(pY8950Buffer[0][(nFractionalPosition >> 16) - 1] * Y8950Volumes[BURN_SND_Y8950_ROUTE]);
302 			nRightSample[3] += (INT32)(pY8950Buffer[0][(nFractionalPosition >> 16) - 0] * Y8950Volumes[BURN_SND_Y8950_ROUTE]);
303 		}
304 
305 		if (nNumChips > 1) {
306 			if ((Y8950RouteDirs[1 + BURN_SND_Y8950_ROUTE] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
307 				nLeftSample[0] += (INT32)(pY8950Buffer[1][(nFractionalPosition >> 16) - 3] * Y8950Volumes[1 + BURN_SND_Y8950_ROUTE]);
308 				nLeftSample[1] += (INT32)(pY8950Buffer[1][(nFractionalPosition >> 16) - 2] * Y8950Volumes[1 + BURN_SND_Y8950_ROUTE]);
309 				nLeftSample[2] += (INT32)(pY8950Buffer[1][(nFractionalPosition >> 16) - 1] * Y8950Volumes[1 + BURN_SND_Y8950_ROUTE]);
310 				nLeftSample[3] += (INT32)(pY8950Buffer[1][(nFractionalPosition >> 16) - 0] * Y8950Volumes[1 + BURN_SND_Y8950_ROUTE]);
311 			}
312 			if ((Y8950RouteDirs[1 + BURN_SND_Y8950_ROUTE] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
313 				nRightSample[0] += (INT32)(pY8950Buffer[1][(nFractionalPosition >> 16) - 3] * Y8950Volumes[1 + BURN_SND_Y8950_ROUTE]);
314 				nRightSample[1] += (INT32)(pY8950Buffer[1][(nFractionalPosition >> 16) - 2] * Y8950Volumes[1 + BURN_SND_Y8950_ROUTE]);
315 				nRightSample[2] += (INT32)(pY8950Buffer[1][(nFractionalPosition >> 16) - 1] * Y8950Volumes[1 + BURN_SND_Y8950_ROUTE]);
316 				nRightSample[3] += (INT32)(pY8950Buffer[1][(nFractionalPosition >> 16) - 0] * Y8950Volumes[1 + BURN_SND_Y8950_ROUTE]);
317 			}
318 		}
319 
320 		nTotalLeftSample = INTERPOLATE4PS_16BIT((nFractionalPosition >> 4) & 0x0fff, nLeftSample[0], nLeftSample[1], nLeftSample[2], nLeftSample[3]);
321 		nTotalRightSample = INTERPOLATE4PS_16BIT((nFractionalPosition >> 4) & 0x0fff, nRightSample[0], nRightSample[1], nRightSample[2], nRightSample[3]);
322 
323 		nTotalLeftSample = BURN_SND_CLIP(nTotalLeftSample);
324 		nTotalRightSample = BURN_SND_CLIP(nTotalRightSample);
325 
326 		if (bY8950AddSignal) {
327 			pSoundBuf[i + 0] += nTotalLeftSample;
328 			pSoundBuf[i + 1] += nTotalRightSample;
329 		} else {
330 			pSoundBuf[i + 0] = nTotalLeftSample;
331 			pSoundBuf[i + 1] = nTotalRightSample;
332 		}
333 	}
334 
335 	if (nSegmentEnd >= nBurnSoundLen) {
336 		INT32 nExtraSamples = nSamplesNeeded - (nFractionalPosition >> 16);
337 
338 		for (INT32 i = -4; i < nExtraSamples; i++) {
339 			pY8950Buffer[0][i] = pY8950Buffer[0][(nFractionalPosition >> 16) + i];
340 
341 			if (nNumChips > 1) {
342 				pY8950Buffer[1][i] = pY8950Buffer[1][(nFractionalPosition >> 16) + i];
343 			}
344 		}
345 
346 		nFractionalPosition &= 0xFFFF;
347 
348 		nY8950Position = nExtraSamples;
349 	}
350 }
351 
Y8950UpdateNormal(INT16 * pSoundBuf,INT32 nSegmentEnd)352 static void Y8950UpdateNormal(INT16* pSoundBuf, INT32 nSegmentEnd)
353 {
354 #if defined FBNEO_DEBUG
355 	if (!DebugSnd_Y8950Initted) bprintf(PRINT_ERROR, _T("Y8950UpdateNormal called without init\n"));
356 #endif
357 
358 	if (!pBurnSoundOut) return;
359 
360 	INT32 nSegmentLength = nSegmentEnd;
361 
362 	if (nSegmentEnd < nY8950Position) {
363 		nSegmentEnd = nY8950Position;
364 	}
365 
366 	if (nSegmentLength > nBurnSoundLen) {
367 		nSegmentLength = nBurnSoundLen;
368 	}
369 
370 	Y8950Render(nSegmentEnd);
371 
372 	pY8950Buffer[0] = pBuffer + 4 + 0 * 4096;
373 
374 	if (nNumChips > 1) {
375 		pY8950Buffer[1] = pBuffer + 4 + 1 * 4096;
376 	}
377 
378 	for (INT32 n = nFractionalPosition; n < nSegmentLength; n++) {
379 		INT32 nLeftSample = 0, nRightSample = 0;
380 
381 		if ((Y8950RouteDirs[BURN_SND_Y8950_ROUTE] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
382 			nLeftSample += (INT32)(pY8950Buffer[0][n] * Y8950Volumes[BURN_SND_Y8950_ROUTE]);
383 		}
384 		if ((Y8950RouteDirs[BURN_SND_Y8950_ROUTE] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
385 			nRightSample += (INT32)(pY8950Buffer[0][n] * Y8950Volumes[BURN_SND_Y8950_ROUTE]);
386 		}
387 
388 		if (nNumChips > 1) {
389 			if ((Y8950RouteDirs[1 + BURN_SND_Y8950_ROUTE] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
390 				nLeftSample += (INT32)(pY8950Buffer[1][n] * Y8950Volumes[1 + BURN_SND_Y8950_ROUTE]);
391 			}
392 			if ((Y8950RouteDirs[1 + BURN_SND_Y8950_ROUTE] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
393 				nRightSample += (INT32)(pY8950Buffer[1][n] * Y8950Volumes[1 + BURN_SND_Y8950_ROUTE]);
394 			}
395 		}
396 
397 		nLeftSample = BURN_SND_CLIP(nLeftSample);
398 		nRightSample = BURN_SND_CLIP(nRightSample);
399 
400 		if (bY8950AddSignal) {
401 			pSoundBuf[(n << 1) + 0] += nLeftSample;
402 			pSoundBuf[(n << 1) + 1] += nRightSample;
403 		} else {
404 			pSoundBuf[(n << 1) + 0] = nLeftSample;
405 			pSoundBuf[(n << 1) + 1] = nRightSample;
406 		}
407 	}
408 
409 	nFractionalPosition = nSegmentLength;
410 
411 	if (nSegmentEnd >= nBurnSoundLen) {
412 		INT32 nExtraSamples = nSegmentEnd - nBurnSoundLen;
413 
414 		for (INT32 i = 0; i < nExtraSamples; i++) {
415 			pY8950Buffer[i] = pY8950Buffer[nBurnSoundLen + i];
416 		}
417 
418 		nFractionalPosition = 0;
419 
420 		nY8950Position = nExtraSamples;
421 
422 	}
423 }
424 
425 // ----------------------------------------------------------------------------
426 // Callbacks for Y8950 core
427 
BurnY8950UpdateRequest(INT32,INT32)428 void BurnY8950UpdateRequest(INT32, INT32)
429 {
430 #if defined FBNEO_DEBUG
431 	if (!DebugSnd_Y8950Initted) bprintf(PRINT_ERROR, _T("BurnY8950UpdateRequest called without init\n"));
432 #endif
433 
434 	Y8950Render(BurnY8950StreamCallback(nBurnY8950SoundRate));
435 }
436 
437 // ----------------------------------------------------------------------------
438 // Initialisation, etc.
439 
BurnY8950Reset()440 void BurnY8950Reset()
441 {
442 #if defined FBNEO_DEBUG
443 	if (!DebugSnd_Y8950Initted) bprintf(PRINT_ERROR, _T("BurnY8950Reset called without init\n"));
444 #endif
445 
446 	BurnTimerResetY8950();
447 
448 	for (INT32 i = 0; i < nNumChips; i++) {
449 		Y8950ResetChip(i);
450 	}
451 }
452 
BurnY8950Exit()453 void BurnY8950Exit()
454 {
455 #if defined FBNEO_DEBUG
456 	if (!DebugSnd_Y8950Initted) bprintf(PRINT_ERROR, _T("BurnY8950Exit called without init\n"));
457 #endif
458 
459 	// Crash prevention.
460 	if (!DebugSnd_Y8950Initted) return;
461 
462 	Y8950Shutdown();
463 
464 	BurnTimerExitY8950();
465 
466 	BurnFree(pBuffer);
467 
468 	nNumChips = 0;
469 	bY8950AddSignal = 0;
470 
471 	DebugSnd_Y8950Initted = 0;
472 }
473 
BurnY8950Init(INT32 num,INT32 nClockFrequency,UINT8 * Y8950ADPCM0ROM,INT32 nY8950ADPCM0Size,UINT8 * Y8950ADPCM1ROM,INT32 nY8950ADPCM1Size,OPL_IRQHANDLER IRQCallback,INT32 (* StreamCallback)(INT32),INT32 bAddSignal)474 INT32 BurnY8950Init(INT32 num, INT32 nClockFrequency, UINT8* Y8950ADPCM0ROM, INT32 nY8950ADPCM0Size, UINT8* Y8950ADPCM1ROM, INT32 nY8950ADPCM1Size, OPL_IRQHANDLER IRQCallback, INT32 (*StreamCallback)(INT32), INT32 bAddSignal)
475 {
476 	BurnTimerInitY8950(&Y8950TimerOver, NULL);
477 
478 	if (nBurnSoundRate <= 0) {
479 		BurnY8950StreamCallback = Y8950StreamCallbackDummy;
480 
481 		BurnY8950Update = Y8950UpdateDummy;
482 
483 		Y8950Init(num, nClockFrequency, 11025);
484 		return 0;
485 	}
486 
487 	BurnY8950StreamCallback = StreamCallback;
488 
489 	if (nFMInterpolation == 3) {
490 		// Set Y8950 core samplerate to match the hardware
491 		nBurnY8950SoundRate = nClockFrequency / 72;
492 		// Bring Y8950 core samplerate within usable range
493 		while (nBurnY8950SoundRate > nBurnSoundRate * 3) {
494 			nBurnY8950SoundRate >>= 1;
495 		}
496 
497 		BurnY8950Update = Y8950UpdateResample;
498 
499 		nSampleSize = (UINT32)nBurnY8950SoundRate * (1 << 16) / nBurnSoundRate;
500 		nFractionalPosition = 0;
501 	} else {
502 		nBurnY8950SoundRate = nBurnSoundRate;
503 
504 		BurnY8950Update = Y8950UpdateNormal;
505 	}
506 
507 	Y8950Init(num, nClockFrequency, nBurnY8950SoundRate);
508 	Y8950SetIRQHandler(0, IRQCallback, 0);
509 	Y8950SetTimerHandler(0, &BurnOPLTimerCallbackY8950, 0);
510 	Y8950SetUpdateHandler(0, &BurnY8950UpdateRequest, 0);
511 	Y8950SetDeltaTMemory(0, Y8950ADPCM0ROM, nY8950ADPCM0Size);
512 	if (num > 1) {
513 //		Y8950SetIRQHandler(1, IRQCallback, 0); // ??
514 		Y8950SetTimerHandler(1, &BurnOPLTimerCallbackY8950, 0);
515 		Y8950SetUpdateHandler(1, &BurnY8950UpdateRequest, 0);
516 		Y8950SetDeltaTMemory(1, Y8950ADPCM1ROM, nY8950ADPCM1Size);
517 	}
518 
519 	pBuffer = (INT16*)BurnMalloc(4096 * num * sizeof(INT16));
520 	memset(pBuffer, 0, 4096 * num * sizeof(INT16));
521 
522 	nY8950Position = 0;
523 
524 	nFractionalPosition = 0;
525 
526 	nNumChips = num;
527 	bY8950AddSignal = bAddSignal;
528 
529 	// default routes
530 	Y8950Volumes[BURN_SND_Y8950_ROUTE] = 1.00;
531 	Y8950RouteDirs[BURN_SND_Y8950_ROUTE] = BURN_SND_ROUTE_BOTH;
532 	if (nNumChips > 1) {
533 		Y8950Volumes[1 + BURN_SND_Y8950_ROUTE] = 1.00;
534 		Y8950RouteDirs[1 + BURN_SND_Y8950_ROUTE] = BURN_SND_ROUTE_BOTH;
535 	}
536 
537 	DebugSnd_Y8950Initted = 1;
538 
539 	return 0;
540 }
541 
BurnY8950SetRoute(INT32 nChip,INT32 nIndex,double nVolume,INT32 nRouteDir)542 void BurnY8950SetRoute(INT32 nChip, INT32 nIndex, double nVolume, INT32 nRouteDir)
543 {
544 #if defined FBNEO_DEBUG
545 	if (!DebugSnd_Y8950Initted) bprintf(PRINT_ERROR, _T("BurnY8950SetRoute called without init\n"));
546 	if (nIndex < 0 || nIndex > 1) bprintf(PRINT_ERROR, _T("BurnY8950SetRoute called with invalid index %i\n"), nIndex);
547 	if (nChip >= nNumChips) bprintf(PRINT_ERROR, _T("BurnY8950SetRoute called with invalid chip %i\n"), nChip);
548 #endif
549 
550 	if (nChip == 0) {
551 		Y8950Volumes[nIndex] = nVolume;
552 		Y8950RouteDirs[nIndex] = nRouteDir;
553 	}
554 
555 	if (nChip == 1) {
556 		Y8950Volumes[1 + nIndex] = nVolume;
557 		Y8950RouteDirs[1 + nIndex] = nRouteDir;
558 	}
559 }
560 
BurnY8950Scan(INT32 nAction,INT32 * pnMin)561 void BurnY8950Scan(INT32 nAction, INT32* pnMin)
562 {
563 	#if defined FBNEO_DEBUG
564 	if (!DebugSnd_Y8950Initted) bprintf(PRINT_ERROR, _T("BurnY8950Scan called without init\n"));
565 #endif
566 
567 	BurnTimerScanY8950(nAction, pnMin);
568 	FMOPLScan(FM_OPL_SAVESTATE_Y8950, 0, nAction, pnMin);
569 
570 	if (nAction & ACB_DRIVER_DATA) {
571 		SCAN_VAR(nY8950Position);
572 	}
573 }
574 
575 #undef MAX_Y8950
576