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

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: }

unix.superglobalmegacorp.com

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