|
|
1.1 root 1: /*
2: Hatari - dmaSnd.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
6:
7: STE DMA sound emulation. Does not seem to be very hard at first glance,
8: but since the DMA sound has to be mixed together with the PSG sound and
9: the output frequency of the host computer differs from the DMA sound
10: frequency, the copy function is a little bit complicated.
11: For reducing copy latency, we set a "interrupt" with Int_AddRelativeInterrupt
12: to occur just after a sound frame should be finished. There we update the
13: sound. The update function also triggers the ST interrupts (Timer A and
14: MFP-i7) which are often used in ST programs for setting a new sound frame
15: after the old one has finished.
16:
17: The microwire interface is not emulated (yet).
18:
19: Hardware I/O registers:
20:
21: $FF8900 (word) : DMA sound control register
22: $FF8903 (byte) : Frame Start Hi
23: $FF8905 (byte) : Frame Start Mi
24: $FF8907 (byte) : Frame Start Lo
25: $FF8909 (byte) : Frame Count Hi
26: $FF890B (byte) : Frame Count Mi
27: $FF890D (byte) : Frame Count Lo
28: $FF890F (byte) : Frame End Hi
29: $FF8911 (byte) : Frame End Mi
30: $FF8913 (byte) : Frame End Lo
31: $FF8920 (word) : Sound Mode Control (frequency, mono/stereo)
32: $FF8922 (byte) : Microwire Data Register
33: $FF8924 (byte) : Microwire Mask Register
34: */
1.1.1.3 ! root 35: const char DmaSnd_rcsid[] = "Hatari $Id: dmaSnd.c,v 1.15 2008/01/28 22:20:09 thothy Exp $";
1.1 root 36:
37: #include "main.h"
38: #include "audio.h"
1.1.1.3 ! root 39: #include "configuration.h"
1.1 root 40: #include "dmaSnd.h"
41: #include "int.h"
42: #include "ioMem.h"
43: #include "memorySnapShot.h"
44: #include "mfp.h"
45: #include "sound.h"
1.1.1.3 ! root 46: #include "stMemory.h"
1.1 root 47:
48:
49: Uint16 nDmaSoundControl; /* Sound control register */
50:
1.1.1.2 root 51: static Uint16 nDmaSoundMode; /* Sound mode register */
1.1 root 52: static Uint16 nMicrowireData; /* Microwire Data register */
53: static Uint16 nMicrowireMask; /* Microwire Mask register */
54: static int nMwTransferSteps;
55:
56: static Uint32 nFrameStartAddr; /* Sound frame start */
57: static Uint32 nFrameEndAddr; /* Sound frame end */
58: static double FrameCounter; /* Counter in current sound frame */
59: static int nFrameLen; /* Length of the frame */
60:
1.1.1.3 ! root 61: static const double DmaSndSampleRates[4] =
1.1 root 62: {
63: 6258,
64: 12517,
65: 25033,
66: 50066
67: };
68:
69:
1.1.1.3 ! root 70: static const double DmaSndFalcSampleRates[] =
! 71: {
! 72: 49170,
! 73: 32780,
! 74: 24585,
! 75: 19668,
! 76: 16390,
! 77: 14049,
! 78: 12292,
! 79: 10927,
! 80: 9834,
! 81: 8940,
! 82: 8195,
! 83: 7565,
! 84: 7024,
! 85: 6556,
! 86: 6146,
! 87: };
! 88:
! 89:
1.1 root 90: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 91: /**
! 92: * Reset DMA sound variables.
! 93: */
1.1 root 94: void DmaSnd_Reset(BOOL bCold)
95: {
96: nDmaSoundControl = 0;
97:
98: if (bCold)
99: {
100: nDmaSoundMode = 0;
101: }
102: }
103:
104:
105: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 106: /**
! 107: * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
! 108: */
1.1 root 109: void DmaSnd_MemorySnapShot_Capture(BOOL bSave)
110: {
111: /* Save/Restore details */
112: MemorySnapShot_Store(&nDmaSoundControl, sizeof(nDmaSoundControl));
113: MemorySnapShot_Store(&nDmaSoundMode, sizeof(nDmaSoundMode));
114: MemorySnapShot_Store(&nFrameStartAddr, sizeof(nFrameStartAddr));
115: MemorySnapShot_Store(&nFrameEndAddr, sizeof(nFrameEndAddr));
116: MemorySnapShot_Store(&FrameCounter, sizeof(FrameCounter));
117: MemorySnapShot_Store(&nFrameLen, sizeof(nFrameLen));
118: MemorySnapShot_Store(&nMicrowireData, sizeof(nMicrowireData));
119: MemorySnapShot_Store(&nMicrowireMask, sizeof(nMicrowireMask));
120: MemorySnapShot_Store(&nMwTransferSteps, sizeof(nMwTransferSteps));
121: }
122:
123:
1.1.1.3 ! root 124: static double DmaSnd_DetectSampleRate(void)
! 125: {
! 126: int nFalcClk = IoMem[0xff8935] & 0x0f;
! 127:
! 128: if (ConfigureParams.System.nMachineType == MACHINE_FALCON && nFalcClk != 0)
! 129: {
! 130: return DmaSndFalcSampleRates[nFalcClk-1];
! 131: }
! 132: else
! 133: {
! 134: return DmaSndSampleRates[nDmaSoundMode & 3];
! 135: }
! 136: }
! 137:
! 138:
1.1 root 139: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 140: /**
! 141: * This function is called when a new sound frame is started.
! 142: * It copies the start and end address from the I/O registers, calculates
! 143: * the sample length, etc.
! 144: */
1.1 root 145: static void DmaSnd_StartNewFrame(void)
146: {
147: int nCyclesForFrame;
148:
149: nFrameStartAddr = (IoMem[0xff8903] << 16) | (IoMem[0xff8905] << 8) | (IoMem[0xff8907] & ~1);
150: nFrameEndAddr = (IoMem[0xff890f] << 16) | (IoMem[0xff8911] << 8) | (IoMem[0xff8913] & ~1);
151:
152: FrameCounter = 0;
153: nFrameLen = nFrameEndAddr - nFrameStartAddr;
154:
155: /* To get smooth sound, set an "interrupt" for the end of the frame that
156: * updates the sound mix buffer. */
1.1.1.3 ! root 157: nCyclesForFrame = nFrameLen * (8013000.0 / DmaSnd_DetectSampleRate());
1.1 root 158: if (!(nDmaSoundMode & DMASNDMODE_MONO)) /* Is it stereo? */
159: nCyclesForFrame = nCyclesForFrame / 2;
1.1.1.3 ! root 160: Int_AddRelativeInterrupt(nCyclesForFrame, INT_CPU_CYCLE, INTERRUPT_DMASOUND, 0);
1.1 root 161: }
162:
163:
164: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 165: /**
! 166: * Check if end-of-frame has been reached and raise interrupts if needed.
! 167: * Returns TRUE if DMA sound processing should be stopped now and FALSE
! 168: * if it continues.
! 169: */
1.1 root 170: static inline int DmaSnd_CheckForEndOfFrame(int nFrameCounter)
171: {
172: if (nFrameCounter >= nFrameLen)
173: {
174: /* Raise end-of-frame interrupts (MFP-i7 and Time-A) */
175: MFP_InputOnChannel(MFP_TIMER_GPIP7_BIT, MFP_IERA, &MFP_IPRA);
176: if (MFP_TACR == 0x08) /* Is timer A in Event Count mode? */
177: MFP_TimerA_EventCount_Interrupt();
178:
179: if (nDmaSoundControl & DMASNDCTRL_PLAYLOOP)
180: {
181: DmaSnd_StartNewFrame();
182: }
183: else
184: {
185: nDmaSoundControl &= ~DMASNDCTRL_PLAY;
186: return TRUE;
187: }
188: }
189:
190: return FALSE;
191: }
192:
193:
194: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 195: /**
! 196: * Mix DMA sound sample with the normal PSG sound samples.
! 197: */
1.1 root 198: void DmaSnd_GenerateSamples(int nMixBufIdx, int nSamplesToGenerate)
199: {
200: double FreqRatio;
201: int i;
202: int nBufIdx;
203: Sint8 *pFrameStart;
204:
205: if (!(nDmaSoundControl & DMASNDCTRL_PLAY))
206: return;
207:
208: pFrameStart = (Sint8 *)&STRam[nFrameStartAddr];
1.1.1.3 ! root 209: FreqRatio = DmaSnd_DetectSampleRate() / (double)SoundPlayBackFrequencies[OutputAudioFreqIndex];
1.1 root 210:
1.1.1.3 ! root 211: if (ConfigureParams.System.nMachineType == MACHINE_FALCON
! 212: && (nDmaSoundMode & DMASNDMODE_16BITSTEREO))
1.1 root 213: {
1.1.1.3 ! root 214: /* Stereo 16-bit */
! 215: FreqRatio *= 4.0;
! 216: for (i = 0; i < nSamplesToGenerate; i++)
! 217: {
! 218: nBufIdx = (nMixBufIdx + i) % MIXBUFFER_SIZE;
! 219: MixBuffer[nBufIdx] = ((int)MixBuffer[nBufIdx] + (int)pFrameStart[((int)FrameCounter)&~1]
! 220: + (int)pFrameStart[(((int)FrameCounter)&~1)+2]) / 3;
! 221: FrameCounter += FreqRatio;
! 222: if (DmaSnd_CheckForEndOfFrame(FrameCounter))
! 223: break;
! 224: }
! 225: }
! 226: else if (nDmaSoundMode & DMASNDMODE_MONO) /* 8-bit stereo or mono? */
! 227: {
! 228: /* Mono 8-bit */
1.1 root 229: for (i = 0; i < nSamplesToGenerate; i++)
230: {
231: nBufIdx = (nMixBufIdx + i) % MIXBUFFER_SIZE;
232: MixBuffer[nBufIdx] = ((int)MixBuffer[nBufIdx] + (int)pFrameStart[(int)FrameCounter]) / 2;
233: FrameCounter += FreqRatio;
234: if (DmaSnd_CheckForEndOfFrame(FrameCounter))
235: break;
236: }
237: }
238: else
239: {
1.1.1.3 ! root 240: /* Stereo 8-bit */
1.1 root 241: FreqRatio *= 2.0;
242: for (i = 0; i < nSamplesToGenerate; i++)
243: {
244: nBufIdx = (nMixBufIdx + i) % MIXBUFFER_SIZE;
245: MixBuffer[nBufIdx] = ((int)MixBuffer[nBufIdx] + (int)pFrameStart[((int)FrameCounter)&~1]
246: + (int)pFrameStart[(((int)FrameCounter)&~1)+1]) / 3;
247: FrameCounter += FreqRatio;
248: if (DmaSnd_CheckForEndOfFrame(FrameCounter))
249: break;
250: }
251: }
252: }
253:
254:
255: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 256: /**
! 257: * DMA sound end of frame "interrupt". Used for updating the sound after
! 258: * a frame has been finished.
! 259: */
1.1 root 260: void DmaSnd_InterruptHandler(void)
261: {
262: /* Remove this interrupt from list and re-order */
263: Int_AcknowledgeInterrupt();
264:
265: /* Update sound */
266: Sound_Update();
267:
268: /* Make sure that emulated microwire isn't busy forever */
269: nMwTransferSteps = 0;
270: }
271:
272:
273: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 274: /**
! 275: * Create actual position for frame count registers.
! 276: */
1.1 root 277: static Uint32 DmaSnd_GetFrameCount(void)
278: {
279: Uint32 nActCount;
280:
281: if (nDmaSoundControl & DMASNDCTRL_PLAY)
282: nActCount = nFrameStartAddr + (int)FrameCounter;
283: else
284: nActCount = (IoMem[0xff8903] << 16) | (IoMem[0xff8905] << 8) | IoMem[0xff8907];
285:
286: nActCount &= ~1;
287:
288: return nActCount;
289: }
290:
291:
292: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 293: /**
! 294: * Read word from sound control register (0xff8900).
! 295: */
1.1 root 296: void DmaSnd_SoundControl_ReadWord(void)
297: {
298: IoMem_WriteWord(0xff8900, nDmaSoundControl);
299: }
300:
301:
302: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 303: /**
! 304: * Write word to sound control register (0xff8900).
! 305: */
! 306: /* FIXME: add Falcon specific handler here... */
1.1 root 307: void DmaSnd_SoundControl_WriteWord(void)
308: {
309: Uint16 nNewSndCtrl;
310:
311: nNewSndCtrl = IoMem_ReadWord(0xff8900) & 3;
312:
313: if (!(nDmaSoundControl & DMASNDCTRL_PLAY) && (nNewSndCtrl & DMASNDCTRL_PLAY))
314: {
315: //fprintf(stderr, "Turning on DMA sound emulation.\n");
316: DmaSnd_StartNewFrame();
317: }
318: else if ((nDmaSoundControl & DMASNDCTRL_PLAY) && !(nNewSndCtrl & DMASNDCTRL_PLAY))
319: {
320: //fprintf(stderr, "Turning off DMA sound emulation.\n");
321: }
322:
323: nDmaSoundControl = nNewSndCtrl;
324: }
325:
326:
327: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 328: /**
! 329: * Read word from sound frame count high register (0xff8909).
! 330: */
1.1 root 331: void DmaSnd_FrameCountHigh_ReadByte(void)
332: {
333: IoMem_WriteByte(0xff8909, DmaSnd_GetFrameCount() >> 16);
334: }
335:
336:
337: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 338: /**
! 339: * Read word from sound frame count medium register (0xff890b).
! 340: */
1.1 root 341: void DmaSnd_FrameCountMed_ReadByte(void)
342: {
343: IoMem_WriteByte(0xff890b, DmaSnd_GetFrameCount() >> 8);
344: }
345:
346:
347: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 348: /**
! 349: * Read word from sound frame count low register (0xff890d).
! 350: */
1.1 root 351: void DmaSnd_FrameCountLow_ReadByte(void)
352: {
353: IoMem_WriteByte(0xff890d, DmaSnd_GetFrameCount());
354: }
355:
356:
357: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 358: /**
! 359: * Read word from sound mode register (0xff8920).
! 360: */
1.1 root 361: void DmaSnd_SoundMode_ReadWord(void)
362: {
363: IoMem_WriteWord(0xff8920, nDmaSoundMode);
364: }
365:
366:
367: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 368: /**
! 369: * Write word to sound mode register (0xff8920).
! 370: * Handling framework for Falcon specific bits by Matthias Arndt <[email protected]>
! 371: */
1.1 root 372: void DmaSnd_SoundMode_WriteWord(void)
373: {
1.1.1.3 ! root 374: /* Handle Falcon specialities: STE and TT only have bits 7,1 and 0 in this register */
! 375:
! 376: /* Falcon has meaning in almost all bits of SND_SMC */
! 377: if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
! 378: {
! 379:
! 380: nDmaSoundMode = IoMem_ReadWord(0xff8920);
! 381: /* FIXME: add code here to evaluate Falcon specific settings */
! 382:
! 383:
! 384: } else {
! 385: /* STE or TT - hopefully STFM emulation never gets here :)
! 386: * we maskout the Falcon only bits so we only hit bits that exist on a real STE
! 387: */
! 388: nDmaSoundMode = (IoMem_ReadWord(0xff8920)&0x008F);
! 389: /* we also write the masked value back into the emulated hw registers so we have a correct value there */
! 390: IoMem_WriteWord(0xff8920,nDmaSoundMode);
! 391: }
1.1 root 392: //fprintf(stderr,"New sound mode = $%x\n", nDmaSoundMode);
393: }
394:
395:
396: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 397: /**
! 398: * Read word from microwire data register (0xff8922).
! 399: */
1.1 root 400: void DmaSnd_MicrowireData_ReadWord(void)
401: {
402: /* The Microwire shifts the data register bit by bit until it is zero
403: * within 16 usec. We don't emulate the time, only the shift on read access,
404: * so this is not very accurate yet. */
405: if (nMwTransferSteps > 0)
406: {
407: IoMem_WriteWord(0xff8922, IoMem_ReadWord(0xff8922)<<2);
408: nMwTransferSteps -= 2;
409: }
410: else
411: {
412: IoMem_WriteWord(0xff8922, 0);
413: }
414: }
415:
416:
417: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 418: /**
! 419: * Write word to microwire data register (0xff8922).
! 420: */
1.1 root 421: void DmaSnd_MicrowireData_WriteWord(void)
422: {
423: nMicrowireData = IoMem_ReadWord(0xff8922);
424: nMwTransferSteps = 16; /* To simulate a microwire transfer */
425: }
426:
427:
428: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 429: /**
! 430: * Read word from microwire mask register (0xff8924).
! 431: */
1.1 root 432: void DmaSnd_MicrowireMask_ReadWord(void)
433: {
434: /* Same as with data register, but mask register is rotated, not shifted. */
435: if (nMwTransferSteps > 0)
436: {
437: IoMem_WriteWord(0xff8924, (IoMem_ReadWord(0xff8924)<<2)|(IoMem_ReadWord(0xff8924)>>14));
438: nMwTransferSteps -= 2;
439: }
440: else
441: {
442: IoMem_WriteWord(0xff8924, nMicrowireMask);
443: }
444:
445: }
446:
447:
448: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 449: /**
! 450: * Write word to microwire mask register (0xff8924).
! 451: */
1.1 root 452: void DmaSnd_MicrowireMask_WriteWord(void)
453: {
454: nMicrowireMask = IoMem_ReadWord(0xff8924);
455: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.