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

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

unix.superglobalmegacorp.com

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