/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * * Rockbox port of InfoNES * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "K6502.h" #include "K6502_rw.h" #include "InfoNES_System.h" #include "InfoNES_pAPU.h" /*-------------------------------------------------------------------*/ /* APU Event resources */ /*-------------------------------------------------------------------*/ struct ApuEvent_t *ApuEventQueue; int cur_event; WORD entertime; /*-------------------------------------------------------------------*/ /* APU Register Write Functions */ /*-------------------------------------------------------------------*/ #define APU_WRITEFUNC(name, evtype) \ void ApuWrite##name(WORD addr, BYTE value) \ { \ ApuEventQueue[cur_event].time = entertime - g_wPassedClocks; \ ApuEventQueue[cur_event].type = APUET_W_##evtype; \ ApuEventQueue[cur_event].data = value; \ cur_event++; \ } APU_WRITEFUNC(C1a, C1A); APU_WRITEFUNC(C1b, C1B); APU_WRITEFUNC(C1c, C1C); APU_WRITEFUNC(C1d, C1D); APU_WRITEFUNC(C2a, C2A); APU_WRITEFUNC(C2b, C2B); APU_WRITEFUNC(C2c, C2C); APU_WRITEFUNC(C2d, C2D); APU_WRITEFUNC(C3a, C3A); APU_WRITEFUNC(C3b, C3B); APU_WRITEFUNC(C3c, C3C); APU_WRITEFUNC(C3d, C3D); APU_WRITEFUNC(C4a, C4A); APU_WRITEFUNC(C4b, C4B); APU_WRITEFUNC(C4c, C4C); APU_WRITEFUNC(C4d, C4D); APU_WRITEFUNC(C5a, C5A); APU_WRITEFUNC(C5b, C5B); APU_WRITEFUNC(C5c, C5C); APU_WRITEFUNC(C5d, C5D); APU_WRITEFUNC(Control, CTRL); ApuWritefunc pAPUSoundRegs[20] = { ApuWriteC1a, ApuWriteC1b, ApuWriteC1c, ApuWriteC1d, ApuWriteC2a, ApuWriteC2b, ApuWriteC2c, ApuWriteC2d, ApuWriteC3a, ApuWriteC3b, ApuWriteC3c, ApuWriteC3d, ApuWriteC4a, ApuWriteC4b, ApuWriteC4c, ApuWriteC4d, ApuWriteC5a, ApuWriteC5b, ApuWriteC5c, ApuWriteC5d, }; /*-------------------------------------------------------------------*/ /* APU resources */ /*-------------------------------------------------------------------*/ BYTE wave_buffers[5][735]; /* 44100 / 60 = 735 samples per sync */ BYTE ApuCtrl; BYTE ApuCtrlNew; /*-------------------------------------------------------------------*/ /* APU Quality resources */ /*-------------------------------------------------------------------*/ int ApuQuality; DWORD ApuPulseMagic; DWORD ApuTriangleMagic; DWORD ApuNoiseMagic; unsigned int ApuSamplesPerSync; unsigned int ApuCyclesPerSample; unsigned int ApuSampleRate; DWORD ApuCycleRate; struct ApuQualityData_t { DWORD pulse_magic; DWORD triangle_magic; DWORD noise_magic; unsigned int samples_per_sync; unsigned int cycles_per_sample; unsigned int sample_rate; DWORD cycle_rate; } ApuQual[] = { { 0xa2567000, 0xa2567000, 0xa2567000, 183, 164, 11025, 1062658 }, { 0x512b3800, 0x512b3800, 0x512b3800, 367, 82, 22050, 531329 }, { 0x289d9c00, 0x289d9c00, 0x289d9c00, 735, 41, 44100, 265664 }, }; /*-------------------------------------------------------------------*/ /* Rectangle Wave #1 resources */ /*-------------------------------------------------------------------*/ BYTE ApuC1a, ApuC1b, ApuC1c, ApuC1d; BYTE* ApuC1Wave; DWORD ApuC1Skip; DWORD ApuC1Index; DWORD ApuC1EnvPhase; BYTE ApuC1EnvVol; BYTE ApuC1Atl; DWORD ApuC1SweepPhase; DWORD ApuC1Freq; /*-------------------------------------------------------------------*/ /* Rectangle Wave #2 resources */ /*-------------------------------------------------------------------*/ BYTE ApuC2a, ApuC2b, ApuC2c, ApuC2d; BYTE* ApuC2Wave; DWORD ApuC2Skip; DWORD ApuC2Index; DWORD ApuC2EnvPhase; BYTE ApuC2EnvVol; BYTE ApuC2Atl; DWORD ApuC2SweepPhase; DWORD ApuC2Freq; /*-------------------------------------------------------------------*/ /* Triangle Wave resources */ /*-------------------------------------------------------------------*/ BYTE ApuC3a, ApuC3b, ApuC3c, ApuC3d; DWORD ApuC3Skip; DWORD ApuC3Index; BYTE ApuC3Atl; DWORD ApuC3Llc; /* Linear Length Counter */ BYTE ApuC3WriteLatency; BYTE ApuC3CounterStarted; /*-------------------------------------------------------------------*/ /* Noise resources */ /*-------------------------------------------------------------------*/ BYTE ApuC4a, ApuC4b, ApuC4c, ApuC4d; DWORD ApuC4Sr; /* Shift register */ DWORD ApuC4Fdc; /* Frequency divide counter */ DWORD ApuC4Skip; DWORD ApuC4Index; BYTE ApuC4Atl; BYTE ApuC4EnvVol; DWORD ApuC4EnvPhase; /*-------------------------------------------------------------------*/ /* DPCM resources */ /*-------------------------------------------------------------------*/ BYTE ApuC5Reg[4]; BYTE ApuC5Enable; BYTE ApuC5Looping; BYTE ApuC5CurByte; BYTE ApuC5DpcmValue; int ApuC5Freq; int ApuC5Phaseacc; WORD ApuC5Address, ApuC5CacheAddr; int ApuC5DmaLength, ApuC5CacheDmaLength; /*-------------------------------------------------------------------*/ /* Wave Data */ /*-------------------------------------------------------------------*/ BYTE pulse_25[0x20] = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; BYTE pulse_50[0x20] = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; BYTE pulse_75[0x20] = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; BYTE pulse_87[0x20] = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, }; BYTE triangle_50[0x20] = { 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff, 0xef, 0xdf, 0xcf, 0xbf, 0xaf, 0x9f, 0x8f, 0x7f, 0x6f, 0x5f, 0x4f, 0x3f, 0x2f, 0x1f, 0x0f, }; BYTE *pulse_waves[4] = { pulse_87, pulse_75, pulse_50, pulse_25, }; /*-------------------------------------------------------------------*/ /* Active Time Left Data */ /*-------------------------------------------------------------------*/ BYTE ApuAtl[0x20] = { 5, 127, 10, 1, 19, 2, 40, 3, 80, 4, 30, 5, 7, 6, 13, 7, 6, 8, 12, 9, 24, 10, 48, 11, 96, 12, 36, 13, 8, 14, 16, 15, }; /*-------------------------------------------------------------------*/ /* Frequency Limit of Rectangle Channels */ /*-------------------------------------------------------------------*/ WORD ApuFreqLimit[8] = { 0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F0 }; /*-------------------------------------------------------------------*/ /* Noise Frequency Lookup Table */ /*-------------------------------------------------------------------*/ DWORD ApuNoiseFreq[ 16 ] = { 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 }; /*-------------------------------------------------------------------*/ /* DMC Transfer Clocks Table */ /*-------------------------------------------------------------------*/ DWORD ApuDpcmCycles[ 16 ] = { 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 85, 72, 54 }; /*===================================================================*/ /* */ /* ApuRenderingWave1() : Rendering Rectangular Wave #1 */ /* */ /*===================================================================*/ /*-------------------------------------------------------------------*/ /* Write registers of rectangular wave #1 */ /*-------------------------------------------------------------------*/ int ApuWriteWave1( int cycles, int event ) { /* APU Reg Write Event */ while ( ( event < cur_event ) && ( ApuEventQueue[event].time < cycles ) ) { if ( ( ApuEventQueue[event].type & APUET_MASK ) == APUET_C1 ) { switch ( ApuEventQueue[event].type & 0x03 ) { case 0: ApuC1a = ApuEventQueue[event].data; ApuC1Wave = pulse_waves[ ApuC1DutyCycle >> 6 ]; break; case 1: ApuC1b = ApuEventQueue[event].data; break; case 2: ApuC1c = ApuEventQueue[event].data; ApuC1Freq = ( ( ( (WORD)ApuC1d & 0x07 ) << 8 ) + ApuC1c ); ApuC1Atl = ApuAtl[ ( ApuC1d & 0xf8 ) >> 3 ]; if ( ApuC1Freq ) { ApuC1Skip = ( ApuPulseMagic << 1 ) / ApuC1Freq; } else { ApuC1Skip = 0; } break; case 3: ApuC1d = ApuEventQueue[event].data; ApuC1Freq = ( ( ( (WORD)ApuC1d & 0x07 ) << 8 ) + ApuC1c ); ApuC1Atl = ApuAtl[ ( ApuC1d & 0xf8 ) >> 3 ]; if ( ApuC1Freq ) { ApuC1Skip = ( ApuPulseMagic << 1 ) / ApuC1Freq; } else { ApuC1Skip = 0; } break; } } else if ( ApuEventQueue[event].type == APUET_W_CTRL ) { ApuCtrlNew = ApuEventQueue[event].data; if( !(ApuEventQueue[event].data&(1<<0)) ) { ApuC1Atl = 0; } } event++; } return event; } /*-------------------------------------------------------------------*/ /* Rendering rectangular wave #1 */ /*-------------------------------------------------------------------*/ void ApuRenderingWave1( void ) { int cycles = 0; int event = 0; unsigned int i; /* note: 41 CPU cycles occur between increments of i */ ApuCtrlNew = ApuCtrl; for ( i = 0; i < ApuSamplesPerSync; i++ ) { /* Write registers */ cycles += ApuCyclesPerSample; event = ApuWriteWave1( cycles, event ); /* Envelope decay at a rate of ( Envelope Delay + 1 ) / 240 secs */ ApuC1EnvPhase -= 4; while ( ApuC1EnvPhase < 0 ) { ApuC1EnvPhase += ApuC1EnvDelay; if ( ApuC1Hold ) { ApuC1EnvVol = ( ApuC1EnvVol + 1 ) & 0x0f; } else if ( ApuC1EnvVol < 0x0f ) { ApuC1EnvVol++; } } /* * TODO: using a table of max frequencies is not technically * clean, but it is fast and (or should be) accurate */ if ( ApuC1Freq < 8 || ( !ApuC1SweepIncDec && ApuC1Freq > ApuC1FreqLimit ) ) { wave_buffers[0][i] = 0; break; } /* Frequency sweeping at a rate of ( Sweep Delay + 1) / 120 secs */ if ( ApuC1SweepOn && ApuC1SweepShifts ) { ApuC1SweepPhase -= 2; /* 120/60 */ while ( ApuC1SweepPhase < 0 ) { ApuC1SweepPhase += ApuC1SweepDelay; if ( ApuC1SweepIncDec ) /* ramp up */ { /* Rectangular #1 */ ApuC1Freq += ~( ApuC1Freq >> ApuC1SweepShifts ); } else { /* ramp down */ ApuC1Freq += ( ApuC1Freq >> ApuC1SweepShifts ); } } if ( ApuC1Freq ) { ApuC1Skip = ( ApuPulseMagic << 1 ) / ApuC1Freq; } } /* Wave Rendering */ if ( ( ApuCtrlNew & 0x01 ) && ( ApuC1Atl || ApuC1Hold ) ) { ApuC1Index += ApuC1Skip; ApuC1Index &= 0x1fffffff; if ( ApuC1Env ) { wave_buffers[0][i] = ApuC1Wave[ApuC1Index >> 24] * ( ApuC1Vol + ApuC1EnvVol ); } else { wave_buffers[0][i] = ApuC1Wave[ApuC1Index >> 24] * ApuC1Vol; } } else { wave_buffers[0][i] = 0; } } if ( ApuC1Atl ) { ApuC1Atl--; } } /*===================================================================*/ /* */ /* ApuRenderingWave2() : Rendering Rectangular Wave #2 */ /* */ /*===================================================================*/ /*-------------------------------------------------------------------*/ /* Write registers of rectangular wave #2 */ /*-------------------------------------------------------------------*/ int ApuWriteWave2( int cycles, int event ) { /* APU Reg Write Event */ while ( ( event < cur_event ) && ( ApuEventQueue[event].time < cycles ) ) { if ( ( ApuEventQueue[event].type & APUET_MASK ) == APUET_C2 ) { switch ( ApuEventQueue[event].type & 0x03 ) { case 0: ApuC2a = ApuEventQueue[event].data; ApuC2Wave = pulse_waves[ ApuC2DutyCycle >> 6 ]; break; case 1: ApuC2b = ApuEventQueue[event].data; break; case 2: ApuC2c = ApuEventQueue[event].data; ApuC2Freq = ( ( ( (WORD)ApuC2d & 0x07 ) << 8 ) + ApuC2c ); ApuC2Atl = ApuAtl[ ( ApuC2d & 0xf8 ) >> 3 ]; if ( ApuC2Freq ) { ApuC2Skip = ( ApuPulseMagic << 1 ) / ApuC2Freq; } else { ApuC2Skip = 0; } break; case 3: ApuC2d = ApuEventQueue[event].data; ApuC2Freq = ( ( ( (WORD)ApuC2d & 0x07 ) << 8 ) + ApuC2c ); ApuC2Atl = ApuAtl[ ( ApuC2d & 0xf8 ) >> 3 ]; if ( ApuC2Freq ) { ApuC2Skip = ( ApuPulseMagic << 1 ) / ApuC2Freq; } else { ApuC2Skip = 0; } break; } } else if ( ApuEventQueue[event].type == APUET_W_CTRL ) { ApuCtrlNew = ApuEventQueue[event].data; if( !(ApuEventQueue[event].data&(1<<1)) ) { ApuC2Atl = 0; } } event++; } return event; } /*-------------------------------------------------------------------*/ /* Rendering rectangular wave #2 */ /*-------------------------------------------------------------------*/ void ApuRenderingWave2( void ) { int cycles = 0; int event = 0; unsigned int i; /* note: 41 CPU cycles occur between increments of i */ ApuCtrlNew = ApuCtrl; for ( i = 0; i < ApuSamplesPerSync; i++ ) { /* Write registers */ cycles += ApuCyclesPerSample; event = ApuWriteWave2( cycles, event ); /* Envelope decay at a rate of ( Envelope Delay + 1 ) / 240 secs */ ApuC2EnvPhase -= 4; while ( ApuC2EnvPhase < 0 ) { ApuC2EnvPhase += ApuC2EnvDelay; if ( ApuC2Hold ) { ApuC2EnvVol = ( ApuC2EnvVol + 1 ) & 0x0f; } else if ( ApuC2EnvVol < 0x0f ) { ApuC2EnvVol++; } } /* * TODO: using a table of max frequencies is not technically * clean, but it is fast and (or should be) accurate */ if ( ApuC2Freq < 8 || ( !ApuC2SweepIncDec && ApuC2Freq > ApuC2FreqLimit ) ) { wave_buffers[1][i] = 0; break; } /* Frequency sweeping at a rate of ( Sweep Delay + 1) / 120 secs */ if ( ApuC2SweepOn && ApuC2SweepShifts ) { ApuC2SweepPhase -= 2; /* 120/60 */ while ( ApuC2SweepPhase < 0) { ApuC2SweepPhase += ApuC2SweepDelay; if ( ApuC2SweepIncDec ) /* ramp up */ { /* Rectangular #2 */ ApuC2Freq -= ~( ApuC2Freq >> ApuC2SweepShifts ); } else { /* ramp down */ ApuC2Freq += ( ApuC2Freq >> ApuC2SweepShifts ); } } if ( ApuC2Freq ) { ApuC2Skip = ( ApuPulseMagic << 1 ) / ApuC2Freq; } } /* Wave Rendering */ if ( ( ApuCtrlNew & 0x02 ) && ( ApuC2Atl || ApuC2Hold ) ) { ApuC2Index += ApuC2Skip; ApuC2Index &= 0x1fffffff; if ( ApuC2Env ) { wave_buffers[1][i] = ApuC2Wave[ApuC2Index >> 24] * ( ApuC2Vol + ApuC2EnvVol ); } else { wave_buffers[1][i] = ApuC2Wave[ApuC2Index >> 24] * ApuC2Vol; } } else { wave_buffers[1][i] = 0; } } if ( ApuC2Atl ) { ApuC2Atl--; } } /*===================================================================*/ /* */ /* ApuRenderingWave3() : Rendering Triangle Wave */ /* */ /*===================================================================*/ /*-------------------------------------------------------------------*/ /* Write registers of triangle wave #3 */ /*-------------------------------------------------------------------*/ int ApuWriteWave3( int cycles, int event ) { /* APU Reg Write Event */ while (( event < cur_event ) && ( ApuEventQueue[event].time < cycles ) ) { if ( ( ApuEventQueue[event].type & APUET_MASK ) == APUET_C3 ) { switch ( ApuEventQueue[event].type & 3 ) { case 0: ApuC3a = ApuEventQueue[event].data; ApuC3Llc = ApuC3LinearLength; break; case 1: ApuC3b = ApuEventQueue[event].data; break; case 2: ApuC3c = ApuEventQueue[event].data; if ( ApuC3Freq ) { ApuC3Skip = ApuTriangleMagic / ApuC3Freq; } else { ApuC3Skip = 0; } break; case 3: ApuC3d = ApuEventQueue[event].data; ApuC3Atl = ApuC3LengthCounter; if ( ApuC3Freq ) { ApuC3Skip = ApuTriangleMagic / ApuC3Freq; } else { ApuC3Skip = 0; } } } else if ( ApuEventQueue[event].type == APUET_W_CTRL ) { ApuCtrlNew = ApuEventQueue[event].data; if( !(ApuEventQueue[event].data&(1<<2)) ) { ApuC3Atl = 0; ApuC3Llc = 0; } } event++; } return event; } /*-------------------------------------------------------------------*/ /* Rendering triangle wave #3 */ /*-------------------------------------------------------------------*/ void ApuRenderingWave3( void ) { int cycles = 0; int event = 0; unsigned int i; /* note: 41 CPU cycles occur between increments of i */ ApuCtrlNew = ApuCtrl; for ( i = 0; i < ApuSamplesPerSync; i++) { /* Write registers */ cycles += ApuCyclesPerSample; event = ApuWriteWave3( cycles, event ); /* Cutting Min Frequency */ if ( ApuC3Freq < 8 ) { wave_buffers[2][i] = 0; break; } /* Counter Control */ if ( ApuC3CounterStarted ) { if ( ApuC3Atl > 0 && !ApuC3Holdnote ) { ApuC3Atl--; } if ( ApuC3Llc > 0 ) { ApuC3Llc--; } } else if ( !ApuC3Holdnote && ApuC3WriteLatency > 0 ) { if ( --ApuC3WriteLatency == 0 ) { ApuC3CounterStarted = 0x01; } } /* Wave Rendering */ if ( ( ApuCtrlNew & 0x04 ) && ( ( ApuC3Atl > 0 || ApuC3Holdnote ) && ApuC3Llc > 0 ) ) { ApuC3Index += ApuC3Skip; ApuC3Index &= 0x1fffffff; wave_buffers[2][i] = triangle_50[ ApuC3Index >> 24 ]; } else { wave_buffers[2][i] = 0; } } } /*===================================================================*/ /* */ /* ApuRenderingWave4() : Rendering Noise */ /* */ /*===================================================================*/ /*-------------------------------------------------------------------*/ /* Write registers of noise channel #4 */ /*-------------------------------------------------------------------*/ int ApuWriteWave4( int cycles, int event ) { /* APU Reg Write Event */ while ( (event < cur_event) && (ApuEventQueue[event].time < cycles) ) { if ( ( ApuEventQueue[event].type & APUET_MASK ) == APUET_C4 ) { switch (ApuEventQueue[event].type & 3) { case 0: ApuC4a = ApuEventQueue[event].data; break; case 1: ApuC4b = ApuEventQueue[event].data; break; case 2: ApuC4c = ApuEventQueue[event].data; if ( ApuC4Small ) { ApuC4Sr = 0x001f; } else { ApuC4Sr = 0x01ff; } /* Frequency */ if ( ApuC4Freq ) { ApuC4Skip = ApuNoiseMagic / ApuC4Freq; } else { ApuC4Skip = 0; } ApuC4Atl = ApuC4LengthCounter; break; case 3: ApuC4d = ApuEventQueue[event].data; /* Frequency */ if ( ApuC4Freq ) { ApuC4Skip = ApuNoiseMagic / ApuC4Freq; } else { ApuC4Skip = 0; } ApuC4Atl = ApuC4LengthCounter; } } else if (ApuEventQueue[event].type == APUET_W_CTRL) { ApuCtrlNew = ApuEventQueue[event].data; if( !(ApuEventQueue[event].data&(1<<3)) ) { ApuC4Atl = 0; } } event++; } return event; } /*-------------------------------------------------------------------*/ /* Rendering noise channel #4 */ /*-------------------------------------------------------------------*/ void ApuRenderingWave4(void) { int cycles = 0; int event = 0; unsigned int i; ApuCtrlNew = ApuCtrl; for ( i = 0; i < ApuSamplesPerSync; i++ ) { /* Write registers */ cycles += ApuCyclesPerSample; event = ApuWriteWave4( cycles, event ); /* Envelope decay at a rate of ( Envelope Delay + 1 ) / 240 secs */ ApuC4EnvPhase -= 4; while ( ApuC4EnvPhase < 0 ) { ApuC4EnvPhase += ApuC4EnvDelay; if ( ApuC4Hold ) { ApuC4EnvVol = ( ApuC4EnvVol + 1 ) & 0x0f; } else if ( ApuC4EnvVol < 0x0f ) { ApuC4EnvVol++; } } /* Wave Rendering */ if ( ApuCtrlNew & 0x08 ) { ApuC4Index += ApuC4Skip; if ( ApuC4Index > 0x1fffffff ) { if ( ApuC4Small ) /* FIXME: may be wrong */ { ApuC4Sr |= ((!(ApuC4Sr & 1)) ^ (!(ApuC4Sr & 4))) << 5; } else { ApuC4Sr |= ((!(ApuC4Sr & 1)) ^ (!(ApuC4Sr & 16))) << 9; } ApuC4Sr >>= 1; } ApuC4Index &= 0x1fffffff; if ( ApuC4Atl && ( ApuC4Sr & 1 ) ) { if ( !ApuC4Env ) { wave_buffers[3][i] = ApuC4Vol; } else { wave_buffers[3][i] = ApuC4EnvVol ^ 0x0f; } } else { wave_buffers[3][i] = 0; } } else { wave_buffers[3][i] = 0; } } if ( ApuC4Atl && !ApuC4Hold ) { ApuC4Atl--; } } /*===================================================================*/ /* */ /* ApuRenderingWave5() : Rendering DPCM channel #5 */ /* */ /*===================================================================*/ /*-------------------------------------------------------------------*/ /* Write registers of DPCM channel #5 */ /*-------------------------------------------------------------------*/ int ApuWriteWave5( int cycles, int event ) { /* APU Reg Write Event */ while ( (event < cur_event) && (ApuEventQueue[event].time < cycles) ) { if ( ( ApuEventQueue[event].type & APUET_MASK ) == APUET_C5 ) { ApuC5Reg[ ApuEventQueue[event].type & 3 ] = ApuEventQueue[event].data; switch (ApuEventQueue[event].type & 3) { case 0: ApuC5Freq = ApuDpcmCycles[ ( ApuEventQueue[event].data & 0x0F ) ] << 16; ApuC5Looping = ApuEventQueue[event].data & 0x40; break; case 1: ApuC5DpcmValue = ( ApuEventQueue[event].data & 0x7F ) >> 1; break; case 2: ApuC5CacheAddr = 0xC000 + (WORD)( ApuEventQueue[event].data << 6 ); break; case 3: ApuC5CacheDmaLength = ( ( ApuEventQueue[event].data << 4 ) + 1 ) << 3; break; } } else if (ApuEventQueue[event].type == APUET_W_CTRL) { ApuCtrlNew = ApuEventQueue[event].data; if( !(ApuEventQueue[event].data&(1<<4)) ) { ApuC5Enable = 0; ApuC5DmaLength = 0; } else { ApuC5Enable = 0xFF; if( !ApuC5DmaLength ) { ApuC5Address = ApuC5CacheAddr; ApuC5DmaLength = ApuC5CacheDmaLength; } } } event++; } return event; } /*-------------------------------------------------------------------*/ /* Rendering DPCM channel #5 */ /*-------------------------------------------------------------------*/ void ApuRenderingWave5(void) { int cycles = 0; int event = 0; unsigned int i; ApuCtrlNew = ApuCtrl; for ( i = 0; i < ApuSamplesPerSync; i++ ) { /* Write registers */ cycles += ApuCyclesPerSample; event = ApuWriteWave5( cycles, event ); if( ApuC5DmaLength ) { ApuC5Phaseacc -= ApuCycleRate; while( ApuC5Phaseacc < 0 ) { ApuC5Phaseacc += ApuC5Freq; if( !( ApuC5DmaLength & 7 ) ) { ApuC5CurByte = K6502_Read( ApuC5Address ); if( 0xFFFF == ApuC5Address ) ApuC5Address = 0x8000; else ApuC5Address++; } if( !(--ApuC5DmaLength) ) { if( ApuC5Looping ) { ApuC5Address = ApuC5CacheAddr; ApuC5DmaLength = ApuC5CacheDmaLength; } else { ApuC5Enable = 0; break; } } // positive delta if( ApuC5CurByte & ( 1 << ((ApuC5DmaLength&7)^7)) ) { if( ApuC5DpcmValue < 0x3F ) ApuC5DpcmValue += 1; } else { // negative delta if( ApuC5DpcmValue > 1 ) ApuC5DpcmValue -= 1; } } } /* Wave Rendering */ if ( ApuCtrlNew & 0x10 ) { wave_buffers[4][i] = ( ApuC5Reg[1]&0x01 ) + ( ApuC5DpcmValue << 1 ); } } } /*===================================================================*/ /* */ /* InfoNES_pApuVsync() : Callback Function per Vsync */ /* */ /*===================================================================*/ void InfoNES_pAPUVsync(void) { ApuRenderingWave1(); ApuRenderingWave2(); ApuRenderingWave3(); ApuRenderingWave4(); ApuRenderingWave5(); ApuCtrl = ApuCtrlNew; InfoNES_SoundOutput(ApuSamplesPerSync, wave_buffers[0], wave_buffers[1], wave_buffers[2], wave_buffers[3], wave_buffers[4]); entertime = g_wPassedClocks; cur_event = 0; } /*===================================================================*/ /* */ /* InfoNES_pApuInit() : Initialize pApu */ /* */ /*===================================================================*/ void InfoNES_pAPUInit(void) { /* Sound Hardware Init */ InfoNES_SoundInit(); ApuQuality = pAPU_QUALITY - 1; // 1: 22050, 2: 44100 [samples/sec] ApuPulseMagic = ApuQual[ ApuQuality ].pulse_magic; ApuTriangleMagic = ApuQual[ ApuQuality ].triangle_magic; ApuNoiseMagic = ApuQual[ ApuQuality ].noise_magic; ApuSamplesPerSync = ApuQual[ ApuQuality ].samples_per_sync; ApuCyclesPerSample = ApuQual[ ApuQuality ].cycles_per_sample; ApuSampleRate = ApuQual[ ApuQuality ].sample_rate; ApuCycleRate = ApuQual[ ApuQuality ].cycle_rate; InfoNES_SoundOpen( ApuSamplesPerSync, ApuSampleRate ); /*-------------------------------------------------------------------*/ /* Initialize Rectangular, Noise Wave's Regs */ /*-------------------------------------------------------------------*/ ApuCtrl = ApuCtrlNew = 0; ApuC1Wave = pulse_50; ApuC2Wave = pulse_50; ApuC1a = ApuC1b = ApuC1c = ApuC1d = 0; ApuC2a = ApuC2b = ApuC2c = ApuC2d = 0; ApuC4a = ApuC4b = ApuC4c = ApuC4d = 0; ApuC1Skip = ApuC2Skip = ApuC4Skip = 0; ApuC1Index = ApuC2Index = ApuC4Index = 0; ApuC1EnvPhase = ApuC2EnvPhase = ApuC4EnvPhase = 0; ApuC1EnvVol = ApuC2EnvVol = ApuC4EnvVol = 0; ApuC1Atl = ApuC2Atl = ApuC4Atl = 0; ApuC1SweepPhase = ApuC2SweepPhase = 0; ApuC1Freq = ApuC2Freq = ApuC4Freq = 0; ApuC4Sr = ApuC4Fdc = 0; /*-------------------------------------------------------------------*/ /* Initialize Triangle Wave's Regs */ /*-------------------------------------------------------------------*/ ApuC3a = ApuC3b = ApuC3c = ApuC3d = 0; ApuC3Atl = ApuC3Llc = 0; ApuC3WriteLatency = 3; /* Magic Number */ ApuC3CounterStarted = 0x00; /*-------------------------------------------------------------------*/ /* Initialize DPCM's Regs */ /*-------------------------------------------------------------------*/ ApuC5Reg[0] = ApuC5Reg[1] = ApuC5Reg[2] = ApuC5Reg[3] = 0; ApuC5Enable = ApuC5Looping = ApuC5CurByte = ApuC5DpcmValue = 0; ApuC5Freq = ApuC5Phaseacc; ApuC5Address = ApuC5CacheAddr = 0; ApuC5DmaLength = ApuC5CacheDmaLength = 0; /*-------------------------------------------------------------------*/ /* Initialize Wave Buffers */ /*-------------------------------------------------------------------*/ InfoNES_MemorySet( (void *)wave_buffers[0], 0, 735 ); InfoNES_MemorySet( (void *)wave_buffers[1], 0, 735 ); InfoNES_MemorySet( (void *)wave_buffers[2], 0, 735 ); InfoNES_MemorySet( (void *)wave_buffers[3], 0, 735 ); InfoNES_MemorySet( (void *)wave_buffers[4], 0, 735 ); entertime = g_wPassedClocks; cur_event = 0; } /*===================================================================*/ /* */ /* InfoNES_pApuDone() : Finalize pApu */ /* */ /*===================================================================*/ void InfoNES_pAPUDone(void) { InfoNES_SoundClose(); } /* * End of InfoNES_pAPU.cpp */