Annotation of hatari/src/dmaSnd.c, revision 1.1.1.5

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.5 ! root       35: const char DmaSnd_fileid[] = "Hatari dmaSnd.c : " __DATE__ " " __TIME__;
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.1.4   root       94: void DmaSnd_Reset(bool bCold)
1.1       root       95: {
                     96:        nDmaSoundControl = 0;
                     97: 
                     98:        if (bCold)
                     99:        {
                    100:                nDmaSoundMode = 0;
                    101:        }
1.1.1.4   root      102: 
                    103:        nMwTransferSteps = 0;
1.1       root      104: }
                    105: 
                    106: 
                    107: /*-----------------------------------------------------------------------*/
1.1.1.3   root      108: /**
                    109:  * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
                    110:  */
1.1.1.4   root      111: void DmaSnd_MemorySnapShot_Capture(bool bSave)
1.1       root      112: {
                    113:        /* Save/Restore details */
                    114:        MemorySnapShot_Store(&nDmaSoundControl, sizeof(nDmaSoundControl));
                    115:        MemorySnapShot_Store(&nDmaSoundMode, sizeof(nDmaSoundMode));
                    116:        MemorySnapShot_Store(&nFrameStartAddr, sizeof(nFrameStartAddr));
                    117:        MemorySnapShot_Store(&nFrameEndAddr, sizeof(nFrameEndAddr));
                    118:        MemorySnapShot_Store(&FrameCounter, sizeof(FrameCounter));
                    119:        MemorySnapShot_Store(&nFrameLen, sizeof(nFrameLen));
                    120:        MemorySnapShot_Store(&nMicrowireData, sizeof(nMicrowireData));
                    121:        MemorySnapShot_Store(&nMicrowireMask, sizeof(nMicrowireMask));
                    122:        MemorySnapShot_Store(&nMwTransferSteps, sizeof(nMwTransferSteps));
                    123: }
                    124: 
                    125: 
1.1.1.3   root      126: static double DmaSnd_DetectSampleRate(void)
                    127: {
                    128:        int nFalcClk = IoMem[0xff8935] & 0x0f;
                    129: 
                    130:        if (ConfigureParams.System.nMachineType == MACHINE_FALCON && nFalcClk != 0)
                    131:        {
                    132:                return DmaSndFalcSampleRates[nFalcClk-1];
                    133:        }
                    134:        else
                    135:        {
                    136:                return DmaSndSampleRates[nDmaSoundMode & 3];
                    137:        }
                    138: }
                    139: 
                    140: 
1.1       root      141: /*-----------------------------------------------------------------------*/
1.1.1.3   root      142: /**
                    143:  * This function is called when a new sound frame is started.
                    144:  * It copies the start and end address from the I/O registers, calculates
                    145:  * the sample length, etc.
                    146:  */
1.1       root      147: static void DmaSnd_StartNewFrame(void)
                    148: {
                    149:        int nCyclesForFrame;
1.1.1.4   root      150: 
1.1       root      151:        nFrameStartAddr = (IoMem[0xff8903] << 16) | (IoMem[0xff8905] << 8) | (IoMem[0xff8907] & ~1);
                    152:        nFrameEndAddr = (IoMem[0xff890f] << 16) | (IoMem[0xff8911] << 8) | (IoMem[0xff8913] & ~1);
                    153: 
                    154:        FrameCounter = 0;
                    155:        nFrameLen = nFrameEndAddr - nFrameStartAddr;
                    156: 
                    157:        /* To get smooth sound, set an "interrupt" for the end of the frame that
                    158:         * updates the sound mix buffer. */
1.1.1.3   root      159:        nCyclesForFrame = nFrameLen * (8013000.0 / DmaSnd_DetectSampleRate());
1.1       root      160:        if (!(nDmaSoundMode & DMASNDMODE_MONO))  /* Is it stereo? */
                    161:                nCyclesForFrame = nCyclesForFrame / 2;
1.1.1.4   root      162:        Int_AddRelativeInterrupt(nCyclesForFrame, INT_CPU_CYCLE, INTERRUPT_DMASOUND);
1.1       root      163: }
                    164: 
                    165: 
                    166: /*-----------------------------------------------------------------------*/
1.1.1.3   root      167: /**
                    168:  * Check if end-of-frame has been reached and raise interrupts if needed.
                    169:  * Returns TRUE if DMA sound processing should be stopped now and FALSE
                    170:  * if it continues.
                    171:  */
1.1       root      172: static inline int DmaSnd_CheckForEndOfFrame(int nFrameCounter)
                    173: {
                    174:        if (nFrameCounter >= nFrameLen)
                    175:        {
                    176:                /* Raise end-of-frame interrupts (MFP-i7 and Time-A) */
                    177:            MFP_InputOnChannel(MFP_TIMER_GPIP7_BIT, MFP_IERA, &MFP_IPRA);
                    178:                if (MFP_TACR == 0x08)       /* Is timer A in Event Count mode? */
                    179:                        MFP_TimerA_EventCount_Interrupt();
                    180: 
                    181:                if (nDmaSoundControl & DMASNDCTRL_PLAYLOOP)
                    182:                {
                    183:                        DmaSnd_StartNewFrame();
                    184:                }
                    185:                else
                    186:                {
                    187:                        nDmaSoundControl &= ~DMASNDCTRL_PLAY;
                    188:                        return TRUE;
                    189:                }
                    190:        }
                    191: 
                    192:        return FALSE;
                    193: }
                    194: 
                    195: 
                    196: /*-----------------------------------------------------------------------*/
1.1.1.3   root      197: /**
                    198:  * Mix DMA sound sample with the normal PSG sound samples.
                    199:  */
1.1       root      200: void DmaSnd_GenerateSamples(int nMixBufIdx, int nSamplesToGenerate)
                    201: {
                    202:        double FreqRatio;
                    203:        int i;
                    204:        int nBufIdx;
                    205:        Sint8 *pFrameStart;
                    206: 
                    207:        if (!(nDmaSoundControl & DMASNDCTRL_PLAY))
                    208:                return;
                    209: 
                    210:        pFrameStart = (Sint8 *)&STRam[nFrameStartAddr];
1.1.1.3   root      211:        FreqRatio = DmaSnd_DetectSampleRate() / (double)SoundPlayBackFrequencies[OutputAudioFreqIndex];
1.1       root      212: 
1.1.1.3   root      213:        if (ConfigureParams.System.nMachineType == MACHINE_FALCON
                    214:            && (nDmaSoundMode & DMASNDMODE_16BITSTEREO))
1.1       root      215:        {
1.1.1.3   root      216:                /* Stereo 16-bit */
                    217:                FreqRatio *= 4.0;
                    218:                for (i = 0; i < nSamplesToGenerate; i++)
                    219:                {
                    220:                        nBufIdx = (nMixBufIdx + i) % MIXBUFFER_SIZE;
1.1.1.4   root      221:                        MixBuffer[nBufIdx][0] = ((int)MixBuffer[nBufIdx][0]
                    222:                                                + (int)(*(Sint16*)&pFrameStart[((int)FrameCounter)&~1])) / 2;
                    223:                        MixBuffer[nBufIdx][1] = ((int)MixBuffer[nBufIdx][1]
                    224:                                                + (int)(*(Sint16*)&pFrameStart[(((int)FrameCounter)&~1)+2])) / 2;
1.1.1.3   root      225:                        FrameCounter += FreqRatio;
                    226:                        if (DmaSnd_CheckForEndOfFrame(FrameCounter))
                    227:                                break;
                    228:                }
                    229:        }
                    230:        else if (nDmaSoundMode & DMASNDMODE_MONO)  /* 8-bit stereo or mono? */
                    231:        {
                    232:                /* Mono 8-bit */
1.1       root      233:                for (i = 0; i < nSamplesToGenerate; i++)
                    234:                {
                    235:                        nBufIdx = (nMixBufIdx + i) % MIXBUFFER_SIZE;
1.1.1.4   root      236:                        MixBuffer[nBufIdx][0] = MixBuffer[nBufIdx][1] =
                    237:                                ((int)MixBuffer[nBufIdx][0] + (((int)pFrameStart[(int)FrameCounter]) << 8)) / 2;
1.1       root      238:                        FrameCounter += FreqRatio;
                    239:                        if (DmaSnd_CheckForEndOfFrame(FrameCounter))
                    240:                                break;
                    241:                }
                    242:        }
                    243:        else
                    244:        {
1.1.1.3   root      245:                /* Stereo 8-bit */
1.1       root      246:                FreqRatio *= 2.0;
                    247:                for (i = 0; i < nSamplesToGenerate; i++)
                    248:                {
                    249:                        nBufIdx = (nMixBufIdx + i) % MIXBUFFER_SIZE;
1.1.1.4   root      250:                        MixBuffer[nBufIdx][0] = ((int)MixBuffer[nBufIdx][0]
                    251:                                                + (((int)pFrameStart[((int)FrameCounter)&~1]) << 8)) / 2;
                    252:                        MixBuffer[nBufIdx][1] = ((int)MixBuffer[nBufIdx][1]
                    253:                                                + (((int)pFrameStart[(((int)FrameCounter)&~1)+1]) << 8)) / 2;
1.1       root      254:                        FrameCounter += FreqRatio;
                    255:                        if (DmaSnd_CheckForEndOfFrame(FrameCounter))
                    256:                                break;
                    257:                }
                    258:        }
                    259: }
                    260: 
                    261: 
                    262: /*-----------------------------------------------------------------------*/
1.1.1.3   root      263: /**
                    264:  * DMA sound end of frame "interrupt". Used for updating the sound after
                    265:  * a frame has been finished.
                    266:  */
1.1       root      267: void DmaSnd_InterruptHandler(void)
                    268: {
                    269:        /* Remove this interrupt from list and re-order */
                    270:        Int_AcknowledgeInterrupt();
                    271: 
                    272:        /* Update sound */
                    273:        Sound_Update();
                    274: }
                    275: 
                    276: 
                    277: /*-----------------------------------------------------------------------*/
1.1.1.3   root      278: /**
                    279:  * Create actual position for frame count registers.
                    280:  */
1.1       root      281: static Uint32 DmaSnd_GetFrameCount(void)
                    282: {
                    283:        Uint32 nActCount;
                    284: 
                    285:        if (nDmaSoundControl & DMASNDCTRL_PLAY)
                    286:                nActCount = nFrameStartAddr + (int)FrameCounter;
                    287:        else
                    288:                nActCount = (IoMem[0xff8903] << 16) | (IoMem[0xff8905] << 8) | IoMem[0xff8907];
                    289: 
                    290:        nActCount &= ~1;
                    291: 
                    292:        return nActCount;
                    293: }
                    294: 
                    295: 
                    296: /*-----------------------------------------------------------------------*/
1.1.1.3   root      297: /**
                    298:  * Read word from sound control register (0xff8900).
                    299:  */
1.1       root      300: void DmaSnd_SoundControl_ReadWord(void)
                    301: {
                    302:        IoMem_WriteWord(0xff8900, nDmaSoundControl);
                    303: }
                    304: 
                    305: 
                    306: /*-----------------------------------------------------------------------*/
1.1.1.3   root      307: /**
                    308:  * Write word to sound control register (0xff8900).
1.1.1.4   root      309:  *
                    310:  * FIXME: add Falcon specific handler here...
1.1.1.3   root      311:  */
1.1       root      312: void DmaSnd_SoundControl_WriteWord(void)
                    313: {
                    314:        Uint16 nNewSndCtrl;
                    315: 
                    316:        nNewSndCtrl = IoMem_ReadWord(0xff8900) & 3;
                    317: 
                    318:        if (!(nDmaSoundControl & DMASNDCTRL_PLAY) && (nNewSndCtrl & DMASNDCTRL_PLAY))
                    319:        {
                    320:                //fprintf(stderr, "Turning on DMA sound emulation.\n");
                    321:                DmaSnd_StartNewFrame();
                    322:        }
                    323:        else if ((nDmaSoundControl & DMASNDCTRL_PLAY) && !(nNewSndCtrl & DMASNDCTRL_PLAY))
                    324:        {
                    325:                //fprintf(stderr, "Turning off DMA sound emulation.\n");
                    326:        }
                    327: 
                    328:        nDmaSoundControl = nNewSndCtrl;
                    329: }
                    330: 
                    331: 
                    332: /*-----------------------------------------------------------------------*/
1.1.1.3   root      333: /**
                    334:  * Read word from sound frame count high register (0xff8909).
                    335:  */
1.1       root      336: void DmaSnd_FrameCountHigh_ReadByte(void)
                    337: {
                    338:        IoMem_WriteByte(0xff8909, DmaSnd_GetFrameCount() >> 16);
                    339: }
                    340: 
                    341: 
                    342: /*-----------------------------------------------------------------------*/
1.1.1.3   root      343: /**
                    344:  * Read word from sound frame count medium register (0xff890b).
                    345:  */
1.1       root      346: void DmaSnd_FrameCountMed_ReadByte(void)
                    347: {
                    348:        IoMem_WriteByte(0xff890b, DmaSnd_GetFrameCount() >> 8);
                    349: }
                    350: 
                    351: 
                    352: /*-----------------------------------------------------------------------*/
1.1.1.3   root      353: /**
                    354:  * Read word from sound frame count low register (0xff890d).
                    355:  */
1.1       root      356: void DmaSnd_FrameCountLow_ReadByte(void)
                    357: {
                    358:        IoMem_WriteByte(0xff890d, DmaSnd_GetFrameCount());
                    359: }
                    360: 
                    361: 
                    362: /*-----------------------------------------------------------------------*/
1.1.1.3   root      363: /**
                    364:  * Read word from sound mode register (0xff8920).
                    365:  */
1.1       root      366: void DmaSnd_SoundMode_ReadWord(void)
                    367: {
                    368:        IoMem_WriteWord(0xff8920, nDmaSoundMode);
                    369: }
                    370: 
                    371: 
                    372: /*-----------------------------------------------------------------------*/
1.1.1.3   root      373: /**
                    374:  * Write word to sound mode register (0xff8920).
                    375:  * Handling framework for Falcon specific bits by Matthias Arndt <[email protected]>
                    376:  */
1.1       root      377: void DmaSnd_SoundMode_WriteWord(void)
                    378: {
1.1.1.3   root      379:        /* Handle Falcon specialities: STE and TT only have bits 7,1 and 0 in this register */
                    380: 
                    381:        /* Falcon has meaning in almost all bits of SND_SMC */
                    382:        if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
                    383:        {
1.1.1.4   root      384: 
1.1.1.3   root      385:                nDmaSoundMode = IoMem_ReadWord(0xff8920);
                    386:                /* FIXME: add code here to evaluate Falcon specific settings */
1.1.1.4   root      387: 
                    388: 
1.1.1.3   root      389:        } else {
                    390:                /* STE or TT - hopefully STFM emulation never gets here :)
                    391:                 * we maskout the Falcon only bits so we only hit bits that exist on a real STE
                    392:                 */
                    393:                nDmaSoundMode = (IoMem_ReadWord(0xff8920)&0x008F);
                    394:                /* we also write the masked value back into the emulated hw registers so we have a correct value there */
                    395:                IoMem_WriteWord(0xff8920,nDmaSoundMode);
                    396:        }
1.1       root      397:        //fprintf(stderr,"New sound mode = $%x\n", nDmaSoundMode);
                    398: }
                    399: 
                    400: 
                    401: /*-----------------------------------------------------------------------*/
1.1.1.3   root      402: /**
1.1.1.4   root      403:  * Handle the shifting/rotating of the microwire registers
                    404:  * The microwire regs should be done after 16 usec = 32 NOPs = 128 cycles.
                    405:  * That means we have to shift 16 times with a delay of 8 cycles.
1.1.1.3   root      406:  */
1.1.1.4   root      407: void DmaSnd_InterruptHandler_Microwire(void)
1.1       root      408: {
1.1.1.4   root      409:        /* Remove this interrupt from list and re-order */
                    410:        Int_AcknowledgeInterrupt();
                    411: 
                    412:        --nMwTransferSteps;
                    413: 
                    414:        /* Shift data register until it becomes zero. */
                    415:        if (nMwTransferSteps > 1)
1.1       root      416:        {
1.1.1.4   root      417:                IoMem_WriteWord(0xff8922, nMicrowireData<<(16-nMwTransferSteps));
1.1       root      418:        }
                    419:        else
                    420:        {
1.1.1.4   root      421:                /* Paradox XMAS 2004 demo continuesly writes to the data
                    422:                 * register, but still expects to read a zero inbetween,
                    423:                 * so we have to output a zero before we're really done
                    424:                 * with the transfer. */
1.1       root      425:                IoMem_WriteWord(0xff8922, 0);
                    426:        }
1.1.1.4   root      427: 
                    428:        /* Rotate mask register */
                    429:        IoMem_WriteWord(0xff8924, (nMicrowireMask<<(16-nMwTransferSteps))
                    430:                                  |(nMicrowireMask>>nMwTransferSteps));
                    431: 
                    432:        if (nMwTransferSteps > 0)
                    433:        {
                    434:                Int_AddRelativeInterrupt(8, INT_CPU_CYCLE, INTERRUPT_DMASOUND_MICROWIRE);
                    435:        }
                    436: }
                    437: 
                    438: 
                    439: /**
                    440:  * Read word from microwire data register (0xff8922).
                    441:  */
                    442: void DmaSnd_MicrowireData_ReadWord(void)
                    443: {
                    444:        /* Shifting is done in DmaSnd_InterruptHandler_Microwire! */
                    445:        //fprintf(stderr, "MW data read: 0x%x\n", IoMem_ReadWord(0xff8922));
1.1       root      446: }
                    447: 
                    448: 
1.1.1.3   root      449: /**
                    450:  * Write word to microwire data register (0xff8922).
                    451:  */
1.1       root      452: void DmaSnd_MicrowireData_WriteWord(void)
                    453: {
1.1.1.4   root      454:        /* Only update, if no shift is in progress */
                    455:        if (!nMwTransferSteps)
                    456:        {
                    457:                nMicrowireData = IoMem_ReadWord(0xff8922);
                    458:                /* Start shifting events to simulate a microwire transfer */
                    459:                nMwTransferSteps = 16;
                    460:                Int_AddRelativeInterrupt(8, INT_CPU_CYCLE, INTERRUPT_DMASOUND_MICROWIRE);
                    461:        }
                    462: 
                    463:        //fprintf(stderr, "MW data write: 0x%x\n", IoMem_ReadWord(0xff8922));
1.1       root      464: }
                    465: 
                    466: 
1.1.1.3   root      467: /**
                    468:  * Read word from microwire mask register (0xff8924).
                    469:  */
1.1       root      470: void DmaSnd_MicrowireMask_ReadWord(void)
                    471: {
1.1.1.4   root      472:        /* Same as with data register, but mask is rotated, not shifted. */
                    473:        //fprintf(stderr, "MW mask read: 0x%x\n", IoMem_ReadWord(0xff8924));
1.1       root      474: }
                    475: 
                    476: 
1.1.1.3   root      477: /**
                    478:  * Write word to microwire mask register (0xff8924).
                    479:  */
1.1       root      480: void DmaSnd_MicrowireMask_WriteWord(void)
                    481: {
1.1.1.4   root      482:        /* Only update, if no shift is in progress */
                    483:        if (!nMwTransferSteps)
                    484:        {
                    485:                nMicrowireMask = IoMem_ReadWord(0xff8924);
                    486:        }
                    487: 
                    488:        //fprintf(stderr, "MW mask write: 0x%x\n", IoMem_ReadWord(0xff8924));
1.1       root      489: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.