|
|
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: */ ! 35: char DmaSnd_rcsid[] = "Hatari $Id: dmaSnd.c,v 1.5 2005/09/26 15:20:14 thothy Exp $"; ! 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: Uint16 nDmaSoundMode; /* Sound mode register */ ! 49: ! 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: ! 59: static int DmaSndSampleRates[4] = ! 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.