|
|
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.2 ! root 35: const char DmaSnd_rcsid[] = "Hatari $Id: dmaSnd.c,v 1.8 2006/02/12 21:28:22 eerot Exp $";
1.1 root 36:
37: #include "main.h"
38: #include "audio.h"
39: #include "dmaSnd.h"
40: #include "int.h"
41: #include "ioMem.h"
42: #include "memorySnapShot.h"
43: #include "mfp.h"
44: #include "sound.h"
45:
46:
47: Uint16 nDmaSoundControl; /* Sound control register */
48:
1.1.1.2 ! root 49: static Uint16 nDmaSoundMode; /* Sound mode register */
1.1 root 50: static Uint16 nMicrowireData; /* Microwire Data register */
51: static Uint16 nMicrowireMask; /* Microwire Mask register */
52: static int nMwTransferSteps;
53:
54: static Uint32 nFrameStartAddr; /* Sound frame start */
55: static Uint32 nFrameEndAddr; /* Sound frame end */
56: static double FrameCounter; /* Counter in current sound frame */
57: static int nFrameLen; /* Length of the frame */
58:
1.1.1.2 ! root 59: static const int DmaSndSampleRates[4] =
1.1 root 60: {
61: 6258,
62: 12517,
63: 25033,
64: 50066
65: };
66:
67:
68: /*-----------------------------------------------------------------------*/
69: /*
70: Reset DMA sound variables.
71: */
72: void DmaSnd_Reset(BOOL bCold)
73: {
74: nDmaSoundControl = 0;
75:
76: if (bCold)
77: {
78: nDmaSoundMode = 0;
79: }
80: }
81:
82:
83: /*-----------------------------------------------------------------------*/
84: /*
85: Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
86: */
87: void DmaSnd_MemorySnapShot_Capture(BOOL bSave)
88: {
89: /* Save/Restore details */
90: MemorySnapShot_Store(&nDmaSoundControl, sizeof(nDmaSoundControl));
91: MemorySnapShot_Store(&nDmaSoundMode, sizeof(nDmaSoundMode));
92: MemorySnapShot_Store(&nFrameStartAddr, sizeof(nFrameStartAddr));
93: MemorySnapShot_Store(&nFrameEndAddr, sizeof(nFrameEndAddr));
94: MemorySnapShot_Store(&FrameCounter, sizeof(FrameCounter));
95: MemorySnapShot_Store(&nFrameLen, sizeof(nFrameLen));
96: MemorySnapShot_Store(&nMicrowireData, sizeof(nMicrowireData));
97: MemorySnapShot_Store(&nMicrowireMask, sizeof(nMicrowireMask));
98: MemorySnapShot_Store(&nMwTransferSteps, sizeof(nMwTransferSteps));
99: }
100:
101:
102: /*-----------------------------------------------------------------------*/
103: /*
104: This function is called when a new sound frame is started.
105: It copies the start and end address from the I/O registers, calculates
106: the sample length, etc.
107: */
108: static void DmaSnd_StartNewFrame(void)
109: {
110: int nCyclesForFrame;
111:
112: nFrameStartAddr = (IoMem[0xff8903] << 16) | (IoMem[0xff8905] << 8) | (IoMem[0xff8907] & ~1);
113: nFrameEndAddr = (IoMem[0xff890f] << 16) | (IoMem[0xff8911] << 8) | (IoMem[0xff8913] & ~1);
114:
115: FrameCounter = 0;
116: nFrameLen = nFrameEndAddr - nFrameStartAddr;
117:
118: /* To get smooth sound, set an "interrupt" for the end of the frame that
119: * updates the sound mix buffer. */
120: nCyclesForFrame = nFrameLen * (8013000.0 / (double)DmaSndSampleRates[nDmaSoundMode & 3]);
121: if (!(nDmaSoundMode & DMASNDMODE_MONO)) /* Is it stereo? */
122: nCyclesForFrame = nCyclesForFrame / 2;
123: Int_AddRelativeInterrupt(nCyclesForFrame, INTERRUPT_DMASOUND);
124: }
125:
126:
127: /*-----------------------------------------------------------------------*/
128: /*
129: Check if end-of-frame has been reached and raise interrupts if needed.
130: Returns TRUE if DMA sound processing should be stopped now and FALSE
131: if it continues.
132: */
133: static inline int DmaSnd_CheckForEndOfFrame(int nFrameCounter)
134: {
135: if (nFrameCounter >= nFrameLen)
136: {
137: /* Raise end-of-frame interrupts (MFP-i7 and Time-A) */
138: MFP_InputOnChannel(MFP_TIMER_GPIP7_BIT, MFP_IERA, &MFP_IPRA);
139: if (MFP_TACR == 0x08) /* Is timer A in Event Count mode? */
140: MFP_TimerA_EventCount_Interrupt();
141:
142: if (nDmaSoundControl & DMASNDCTRL_PLAYLOOP)
143: {
144: DmaSnd_StartNewFrame();
145: }
146: else
147: {
148: nDmaSoundControl &= ~DMASNDCTRL_PLAY;
149: return TRUE;
150: }
151: }
152:
153: return FALSE;
154: }
155:
156:
157: /*-----------------------------------------------------------------------*/
158: /*
159: Mix DMA sound sample with the normal PSG sound samples.
160: */
161: void DmaSnd_GenerateSamples(int nMixBufIdx, int nSamplesToGenerate)
162: {
163: double FreqRatio;
164: int i;
165: int nBufIdx;
166: Sint8 *pFrameStart;
167:
168: if (!(nDmaSoundControl & DMASNDCTRL_PLAY))
169: return;
170:
171: pFrameStart = (Sint8 *)&STRam[nFrameStartAddr];
172: FreqRatio = (double)DmaSndSampleRates[nDmaSoundMode & 3]
173: / (double)SoundPlayBackFrequencies[OutputAudioFreqIndex];
174:
175: if (nDmaSoundMode & DMASNDMODE_MONO) /* Stereo or mono? */
176: {
177: /* Mono */
178: for (i = 0; i < nSamplesToGenerate; i++)
179: {
180: nBufIdx = (nMixBufIdx + i) % MIXBUFFER_SIZE;
181: MixBuffer[nBufIdx] = ((int)MixBuffer[nBufIdx] + (int)pFrameStart[(int)FrameCounter]) / 2;
182: FrameCounter += FreqRatio;
183: if (DmaSnd_CheckForEndOfFrame(FrameCounter))
184: break;
185: }
186: }
187: else
188: {
189: /* Stereo */
190: FreqRatio *= 2.0;
191: for (i = 0; i < nSamplesToGenerate; i++)
192: {
193: nBufIdx = (nMixBufIdx + i) % MIXBUFFER_SIZE;
194: MixBuffer[nBufIdx] = ((int)MixBuffer[nBufIdx] + (int)pFrameStart[((int)FrameCounter)&~1]
195: + (int)pFrameStart[(((int)FrameCounter)&~1)+1]) / 3;
196: FrameCounter += FreqRatio;
197: if (DmaSnd_CheckForEndOfFrame(FrameCounter))
198: break;
199: }
200: }
201: }
202:
203:
204: /*-----------------------------------------------------------------------*/
205: /*
206: DMA sound end of frame "interrupt". Used for updating the sound after
207: a frame has been finished.
208: */
209: void DmaSnd_InterruptHandler(void)
210: {
211: /* Remove this interrupt from list and re-order */
212: Int_AcknowledgeInterrupt();
213:
214: /* Update sound */
215: Sound_Update();
216:
217: /* Make sure that emulated microwire isn't busy forever */
218: nMwTransferSteps = 0;
219: }
220:
221:
222: /*-----------------------------------------------------------------------*/
223: /*
224: Create actual position for frame count registers.
225: */
226: static Uint32 DmaSnd_GetFrameCount(void)
227: {
228: Uint32 nActCount;
229:
230: if (nDmaSoundControl & DMASNDCTRL_PLAY)
231: nActCount = nFrameStartAddr + (int)FrameCounter;
232: else
233: nActCount = (IoMem[0xff8903] << 16) | (IoMem[0xff8905] << 8) | IoMem[0xff8907];
234:
235: nActCount &= ~1;
236:
237: return nActCount;
238: }
239:
240:
241: /*-----------------------------------------------------------------------*/
242: /*
243: Read word from sound control register (0xff8900).
244: */
245: void DmaSnd_SoundControl_ReadWord(void)
246: {
247: IoMem_WriteWord(0xff8900, nDmaSoundControl);
248: }
249:
250:
251: /*-----------------------------------------------------------------------*/
252: /*
253: Write word to sound control register (0xff8900).
254: */
255: void DmaSnd_SoundControl_WriteWord(void)
256: {
257: Uint16 nNewSndCtrl;
258:
259: nNewSndCtrl = IoMem_ReadWord(0xff8900) & 3;
260:
261: if (!(nDmaSoundControl & DMASNDCTRL_PLAY) && (nNewSndCtrl & DMASNDCTRL_PLAY))
262: {
263: //fprintf(stderr, "Turning on DMA sound emulation.\n");
264: DmaSnd_StartNewFrame();
265: }
266: else if ((nDmaSoundControl & DMASNDCTRL_PLAY) && !(nNewSndCtrl & DMASNDCTRL_PLAY))
267: {
268: //fprintf(stderr, "Turning off DMA sound emulation.\n");
269: }
270:
271: nDmaSoundControl = nNewSndCtrl;
272: }
273:
274:
275: /*-----------------------------------------------------------------------*/
276: /*
277: Read word from sound frame count high register (0xff8909).
278: */
279: void DmaSnd_FrameCountHigh_ReadByte(void)
280: {
281: IoMem_WriteByte(0xff8909, DmaSnd_GetFrameCount() >> 16);
282: }
283:
284:
285: /*-----------------------------------------------------------------------*/
286: /*
287: Read word from sound frame count medium register (0xff890b).
288: */
289: void DmaSnd_FrameCountMed_ReadByte(void)
290: {
291: IoMem_WriteByte(0xff890b, DmaSnd_GetFrameCount() >> 8);
292: }
293:
294:
295: /*-----------------------------------------------------------------------*/
296: /*
297: Read word from sound frame count low register (0xff890d).
298: */
299: void DmaSnd_FrameCountLow_ReadByte(void)
300: {
301: IoMem_WriteByte(0xff890d, DmaSnd_GetFrameCount());
302: }
303:
304:
305: /*-----------------------------------------------------------------------*/
306: /*
307: Read word from sound mode register (0xff8920).
308: */
309: void DmaSnd_SoundMode_ReadWord(void)
310: {
311: IoMem_WriteWord(0xff8920, nDmaSoundMode);
312: }
313:
314:
315: /*-----------------------------------------------------------------------*/
316: /*
317: Write word to sound mode register (0xff8920).
318: */
319: void DmaSnd_SoundMode_WriteWord(void)
320: {
321: nDmaSoundMode = IoMem_ReadWord(0xff8920);
322: //fprintf(stderr,"New sound mode = $%x\n", nDmaSoundMode);
323: }
324:
325:
326: /*-----------------------------------------------------------------------*/
327: /*
328: Read word from microwire data register (0xff8922).
329: */
330: void DmaSnd_MicrowireData_ReadWord(void)
331: {
332: /* The Microwire shifts the data register bit by bit until it is zero
333: * within 16 usec. We don't emulate the time, only the shift on read access,
334: * so this is not very accurate yet. */
335: if (nMwTransferSteps > 0)
336: {
337: IoMem_WriteWord(0xff8922, IoMem_ReadWord(0xff8922)<<2);
338: nMwTransferSteps -= 2;
339: }
340: else
341: {
342: IoMem_WriteWord(0xff8922, 0);
343: }
344: }
345:
346:
347: /*-----------------------------------------------------------------------*/
348: /*
349: Write word to microwire data register (0xff8922).
350: */
351: void DmaSnd_MicrowireData_WriteWord(void)
352: {
353: nMicrowireData = IoMem_ReadWord(0xff8922);
354: nMwTransferSteps = 16; /* To simulate a microwire transfer */
355: }
356:
357:
358: /*-----------------------------------------------------------------------*/
359: /*
360: Read word from microwire mask register (0xff8924).
361: */
362: void DmaSnd_MicrowireMask_ReadWord(void)
363: {
364: /* Same as with data register, but mask register is rotated, not shifted. */
365: if (nMwTransferSteps > 0)
366: {
367: IoMem_WriteWord(0xff8924, (IoMem_ReadWord(0xff8924)<<2)|(IoMem_ReadWord(0xff8924)>>14));
368: nMwTransferSteps -= 2;
369: }
370: else
371: {
372: IoMem_WriteWord(0xff8924, nMicrowireMask);
373: }
374:
375: }
376:
377:
378: /*-----------------------------------------------------------------------*/
379: /*
380: Write word to microwire mask register (0xff8924).
381: */
382: void DmaSnd_MicrowireMask_WriteWord(void)
383: {
384: nMicrowireMask = IoMem_ReadWord(0xff8924);
385: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.