/* * Copyright (C) 2017 - 2020 FIX94 * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ #include #include #include #include #include #include "apu.h" #include "audio_fds.h" #include "audio_mmc5.h" #include "audio_vrc6.h" #include "audio_vrc7.h" #include "audio_n163.h" #include "audio_s5b.h" #include "audio.h" #include "mapper.h" #include "mem.h" #include "cpu.h" #define P1_ENABLE (1<<0) #define P2_ENABLE (1<<1) #define TRI_ENABLE (1<<2) #define NOISE_ENABLE (1<<3) #define DMC_ENABLE (1<<4) #define PULSE_CONST_V (1<<4) #define PULSE_HALT_LOOP (1<<5) #define TRI_HALT_LOOP (1<<7) #define DMC_HALT_LOOP (1<<6) #define DMC_IRQ_ENABLE (1<<7) static struct { uint8_t reg[0x18]; uint32_t BufSize; uint32_t BufSizeBytes; uint32_t curBufPos; uint32_t Frequency; uint16_t freq1; uint16_t freq2; uint16_t triFreq; uint16_t noiseFreq; uint16_t noiseShiftReg; uint16_t dmcFreq; uint16_t dmcAddr, dmcLen; uint16_t dmcCurAddr, dmcCurLen; uint8_t p1LengthCtr, p2LengthCtr, noiseLengthCtr; uint8_t triLengthCtr, triLinearCtr, triCurLinearCtr; uint8_t dmcVol, dmcCurVol; uint8_t dmcSampleRemain; uint8_t dmcSampleBuf, dmcCpuBuf; uint8_t irq; bool mode5; uint8_t modePos; uint16_t modeCurCtr; uint16_t p1freqCtr, p2freqCtr, triFreqCtr, noiseFreqCtr, dmcFreqCtr; uint8_t p1Cycle, p2Cycle, triCycle; bool p1haltloop, p2haltloop, trihaltloop, noisehaltloop, dmchaltloop; bool dmcenabled; bool dmcready; bool dmcirqenable; bool trireload; bool noiseMode1; bool enable_irq; envelope_t p1Env, p2Env, noiseEnv; sweep_t p1Sweep, p2Sweep; #if AUDIO_FLOAT float pulseLookupTbl[32]; float tndLookupTbl[204]; float lpVal; float hpVal; float *OutBuf; float lastHPOut; float lastLPOut; float *ampVol; #else int32_t pulseLookupTbl[32]; int32_t tndLookupTbl[204]; int32_t lpVal; int32_t hpVal; int16_t *OutBuf; int32_t lastHPOut; int32_t lastLPOut; int32_t *ampVol; #endif #if FAKE_STEREO uint32_t DelaySize; uint32_t DelaySizeBytes; uint32_t DelayCnt; #if AUDIO_FLOAT float *DelayBuf; #else int16_t *DelayBuf; #endif #endif const uint16_t *dmcPeriod, *noisePeriod; const uint16_t *mode4Ctr, *mode5Ctr; bool mode_change; bool new_mode5; uint8_t vrc7Clock; uint8_t apuClock; uint8_t p1Out; uint8_t p2Out; uint8_t triOut; uint8_t noiseOut; const uint8_t *p1seq; const uint8_t *p2seq; const uint8_t *lengthLookupTbl; const uint8_t *triSeq; bool waitForRefill; } apu; #if AUDIO_FLOAT static float APU_ampVol[7] = { 2.0f, 1.75f, 1.5f, 1.2f, 1.0f, 0.85f, 0.75f }; #else static int32_t APU_ampVol[7] = { 128, 112, 96, 77, 64, 55, 48 }; #endif //used externally const uint8_t lengthLookupTbl[0x20] = { 10,254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30 }; //used externally const uint8_t pulseSeqs[4][8] = { { 0, 1, 0, 0, 0, 0, 0, 0 }, { 0, 1, 1, 0, 0, 0, 0, 0 }, { 0, 1, 1, 1, 1, 0, 0, 0 }, { 1, 0, 0, 1, 1, 1, 1, 1 }, }; static const uint8_t triSeq[32] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }; static const uint16_t noisePeriodNtsc[16] = { 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068, }; static const uint16_t noisePeriodPal[16] = { 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778, }; static const uint16_t dmcPeriodNtsc[16] = { 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54, }; static const uint16_t dmcPeriodPal[16] = { 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50, }; //used externally const uint16_t mode4CtrNtsc[6] = { 7456, 7458, 7457, 1, 1, 7457 }; const uint16_t mode4CtrPal[6] = { 8314, 8312, 8313, 1, 1, 8313 }; static const uint16_t mode5CtrNtsc[6] = { 1, 7457, 7456, 7458, 7458, 7452 }; static const uint16_t mode5CtrPal[6] = { 1, 8313, 8314, 8312, 8314, 8312 }; extern uint8_t interrupt; #define M_2_PI 6.28318530717958647692 extern bool fdsMasterEnable; extern uint32_t vrc7CycleTimer; extern bool nesPAL; uint8_t audioExpansion; void apuInitBufs() { apu.noisePeriod = nesPAL ? noisePeriodPal : noisePeriodNtsc; apu.dmcPeriod = nesPAL ? dmcPeriodPal : dmcPeriodNtsc; apu.mode4Ctr = nesPAL ? mode4CtrPal : mode4CtrNtsc; apu.mode5Ctr = nesPAL ? mode5CtrPal : mode5CtrNtsc; apu.lengthLookupTbl = lengthLookupTbl; apu.triSeq = triSeq; apu.ampVol = APU_ampVol; //effective frequencies for 50.000Hz and 60.000Hz Video out //apu.Frequency = nesPAL ? 831187 : 893415; //effective frequencies for Original PPU Video out //apu.Frequency = nesPAL ? 831303 : 894886; #if AUDIO_LOWERFREQ apu.Frequency = nesPAL ? 51956 : 55930; #else apu.Frequency = nesPAL ? 207825 : 223721; #endif audioExpansion = 0; double dt = 1.0/((double)apu.Frequency); //LP at 22kHz double rc = 1.0/(M_2_PI * 22000.0); #if AUDIO_FLOAT apu.lpVal = dt / (rc + dt); #else //convert to 32bit int for calcs later apu.lpVal = (int32_t)((dt / (rc + dt))*32768.0); #endif //HP at 40Hz rc = 1.0/(M_2_PI * 40.0); #if AUDIO_FLOAT apu.hpVal = rc / (rc + dt); #else //convert to 32bit int for calcs later apu.hpVal = (int32_t)((rc / (rc + dt))*32768.0); #endif //just have something larger than 1 frame //to hold changing data size apu.BufSize = apu.Frequency/30*2; #if AUDIO_FLOAT apu.BufSizeBytes = apu.BufSize*sizeof(float); apu.OutBuf = (float*)malloc(apu.BufSizeBytes); printf("Audio: 32-bit Float Output at %iHz\n", apu.Frequency); #else apu.BufSizeBytes = apu.BufSize*sizeof(int16_t); apu.OutBuf = (int16_t*)malloc(apu.BufSizeBytes); printf("Audio: 16-bit Short Output at %iHz\n", apu.Frequency); #endif //extra delay #if FAKE_STEREO apu.DelaySize = apu.Frequency/125; //8ms apu.DelayCnt = 0; #if AUDIO_FLOAT apu.DelaySizeBytes = apu.DelaySize*sizeof(float); apu.DelayBuf = (float*)malloc(apu.DelaySizeBytes); #else apu.DelaySizeBytes = apu.DelaySize*sizeof(int16_t); apu.DelayBuf = (int16_t*)malloc(apu.DelaySizeBytes); #endif memset(apu.DelayBuf, 0, apu.DelaySizeBytes); printf("Audio: Enabled Fake Stereo Delay of 8ms\n"); #endif /* https://wiki.nesdev.com/w/index.php/APU_Mixer#Lookup_Table */ uint8_t i; #if AUDIO_FLOAT for(i = 0; i < 32; i++) apu.pulseLookupTbl[i] = (95.52 / ((8128.0 / i) + 100)); for(i = 0; i < 204; i++) apu.tndLookupTbl[i] = (163.67 / ((24329.0 / i) + 100)); #else for(i = 0; i < 32; i++) apu.pulseLookupTbl[i] = (int32_t)((95.52 / ((8128.0 / i) + 100))*32768.0); for(i = 0; i < 204; i++) apu.tndLookupTbl[i] = (int32_t)((163.67 / ((24329.0 / i) + 100))*32768.0); #endif } void apuDeinitBufs() { if(apu.OutBuf) free(apu.OutBuf); apu.OutBuf = NULL; #if FAKE_STEREO if(apu.DelayBuf) free(apu.DelayBuf); apu.DelayBuf = NULL; #endif } void apuInit() { memset(apu.reg,0,0x18); memset(apu.OutBuf, 0, apu.BufSizeBytes); apu.curBufPos = 0; apu.freq1 = 0; apu.freq2 = 0; apu.triFreq = 0; apu.noiseFreq = apu.noisePeriod[0]-1, apu.dmcFreq = apu.dmcPeriod[0]-1; apu.noiseShiftReg = 1; apu.p1LengthCtr = 0; apu.p2LengthCtr = 0; apu.noiseLengthCtr = 0; apu.triLengthCtr = 0; apu.triLinearCtr = 0; apu.triCurLinearCtr = 0; //make sure to properly init dmc values apu.dmcAddr = 0xC000, apu.dmcLen = 1, apu.dmcVol = 0; apu.dmcSampleBuf = 0; apu.dmcCpuBuf = 0; apu.dmcCurAddr = 0, apu.dmcCurLen = 0; apu.dmcCurVol = 0; apu.dmcSampleRemain = 0; apu.irq = 0; apu.p1freqCtr = 0; apu.p2freqCtr = 0; apu.triFreqCtr = 0, apu.noiseFreqCtr = 0, apu.dmcFreqCtr = 0; apu.p1Cycle = 0; apu.p2Cycle = 0; apu.triCycle = 0; memset(&apu.p1Env,0,sizeof(envelope_t)); memset(&apu.p2Env,0,sizeof(envelope_t)); memset(&apu.noiseEnv,0,sizeof(envelope_t)); memset(&apu.p1Sweep,0,sizeof(sweep_t)); apu.p1Sweep.chan1 = true; //for negative sweep memset(&apu.p2Sweep,0,sizeof(sweep_t)); apu.p2Sweep.chan1 = false; apu.p1haltloop = false; apu.p2haltloop = false; apu.trihaltloop = false; apu.noisehaltloop = false; apu.dmcenabled = false; apu.dmcready = false; apu.dmcirqenable = false; apu.trireload = false; apu.noiseMode1 = false; //4017 starts out as 0, so enable apu.enable_irq = true; apu.mode_change = false; apu.new_mode5 = false; apu.vrc7Clock = 1; apu.apuClock = 0; apu.mode5 = false; apu.modePos = 5; apu.modeCurCtr = nesPAL ? 8315 : 7459; apu.p1seq = pulseSeqs[0]; apu.p2seq = pulseSeqs[1]; } void apuWriteDMCBuf(uint8_t val) { apu.dmcready = true; apu.dmcCpuBuf = val; apu.dmcCurAddr++; if(!apu.dmcCurLen) { if(apu.dmchaltloop) { apu.dmcCurAddr = apu.dmcAddr; apu.dmcCurLen = apu.dmcLen; } else if(apu.dmcirqenable) { //printf("DMC IRQ\n"); interrupt |= DMC_IRQ; } } } extern bool cpu_odd_cycle; FIXNES_NOINLINE static void apuChangeMode() { if(!cpu_odd_cycle) return; apu.mode_change = false; apu.mode5 = apu.new_mode5; apu.modePos = 5; if(apu.mode5) apu.modeCurCtr = 1; else apu.modeCurCtr = nesPAL ? 8315 : 7459; } void doEnvelopeLogic(envelope_t *env) { if(env->start) { env->start = false; env->divider = env->vol; env->decay = 15; } else { if(env->divider == 0) { env->divider = env->vol; if(env->decay == 0) { if(env->loop) env->decay = 15; } else env->decay--; } else env->divider--; } //too slow on its own? //env->envelope = (env->constant ? env->vol : env->decay); } static void sweepUpdateFreq(sweep_t *sw, uint16_t *freq) { //any freq update causes target freq update sw->targetFreq = *freq; if(sw->targetFreq >= 8) { if(sw->negative) { sw->targetFreq -= (sw->targetFreq >> sw->shift); if(sw->chan1 == true) sw->targetFreq--; } else sw->targetFreq += (sw->targetFreq >> sw->shift); if(sw->targetFreq <= 0x7FF) sw->mute = false; else //larger than freq register sw->mute = true; } else //any input < 8 gets muted sw->mute = true; } static void doSweepLogic(sweep_t *sw, uint16_t *freq) { if(sw->divider == 0) { if(sw->enabled && sw->shift && !sw->mute) { *freq = sw->targetFreq; sweepUpdateFreq(sw, freq); } sw->divider = sw->period; } else sw->divider--; if(sw->start) { sw->divider = sw->period; sw->start = false; } } FIXNES_NOINLINE static void apuClockA() { if(apu.p1LengthCtr) { doSweepLogic(&apu.p1Sweep, &apu.freq1); if(!apu.p1haltloop) apu.p1LengthCtr--; } if(apu.p2LengthCtr) { doSweepLogic(&apu.p2Sweep, &apu.freq2); if(!apu.p2haltloop) apu.p2LengthCtr--; } if(apu.triLengthCtr && !apu.trihaltloop) apu.triLengthCtr--; if(apu.noiseLengthCtr && !apu.noisehaltloop) apu.noiseLengthCtr--; } FIXNES_NOINLINE static void apuClockB() { if(apu.p1LengthCtr) doEnvelopeLogic(&apu.p1Env); if(apu.p2LengthCtr) doEnvelopeLogic(&apu.p2Env); if(apu.noiseLengthCtr) doEnvelopeLogic(&apu.noiseEnv); if(apu.trireload) apu.triCurLinearCtr = apu.triLinearCtr; else if(apu.triCurLinearCtr) apu.triCurLinearCtr--; if(!apu.trihaltloop) apu.trireload = false; } FIXNES_ALWAYSINLINE void apuCycle() { uint8_t aExp = audioExpansion; #if AUDIO_LOWERFREQ if(!(apu.apuClock&31)) #else if(!(apu.apuClock&7)) #endif { if(apu.p1LengthCtr && (apu.reg[0x15] & P1_ENABLE)) { if(!apu.p1Sweep.mute) apu.p1Out = apu.p1seq[apu.p1Cycle] ? (apu.p1Env.constant ? apu.p1Env.vol : apu.p1Env.decay) : 0; } if(apu.p2LengthCtr && (apu.reg[0x15] & P2_ENABLE)) { if(!apu.p2Sweep.mute) apu.p2Out = apu.p2seq[apu.p2Cycle] ? (apu.p2Env.constant ? apu.p2Env.vol : apu.p2Env.decay) : 0; } if(apu.triLengthCtr && apu.triCurLinearCtr && (apu.reg[0x15] & TRI_ENABLE)) { if(apu.triFreq >= 2) apu.triOut = apu.triSeq[apu.triCycle]; } if(apu.noiseLengthCtr && (apu.reg[0x15] & NOISE_ENABLE)) { if(apu.noiseFreq > 0) apu.noiseOut = (apu.noiseShiftReg&1) == 0 ? (apu.noiseEnv.constant ? apu.noiseEnv.vol : apu.noiseEnv.decay) : 0; } #if AUDIO_FLOAT float curIn = apu.pulseLookupTbl[apu.p1Out + apu.p2Out] + apu.tndLookupTbl[(3*apu.triOut) + (2*apu.noiseOut) + apu.dmcVol]; uint8_t ampVolPos = 0; //very rough still if(aExp & EXP_VRC6) { vrc6AudioCycle(); curIn += ((float)vrc6Out)*0.008f; ampVolPos++; } if(aExp & EXP_FDS) { fdsAudioCycle(); curIn += ((float)fdsOut)*0.00617f; ampVolPos++; } if(aExp & EXP_MMC5) { mmc5AudioCycle(); curIn += apu.pulseLookupTbl[mmc5Out]+(mmc5pcm*0.002f); ampVolPos++; } if(aExp & EXP_VRC7) { curIn += (((float)(vrc7Out>>7))/32768.f); ampVolPos++; } if(aExp & EXP_N163) { curIn += ((float)n163Out)*0.0008f; ampVolPos++; } if(aExp & EXP_S5B) { s5BAudioCycle(); curIn += ((float)s5BOut)/32768.f; ampVolPos++; } //amplify input curIn *= apu.ampVol[ampVolPos]; float curLPout = apu.lastLPOut+(apu.lpVal*(curIn-apu.lastLPOut)); float curHPOut = apu.hpVal*(apu.lastHPOut+apu.lastLPOut-curLPout); //set output apu.OutBuf[apu.curBufPos] = curHPOut; apu.lastLPOut = curLPout; apu.lastHPOut = curHPOut; #else int32_t curIn = apu.pulseLookupTbl[apu.p1Out + apu.p2Out] + apu.tndLookupTbl[(3*apu.triOut) + (2*apu.noiseOut) + apu.dmcVol]; uint8_t ampVolPos = 0; //very rough still if(aExp & EXP_VRC6) { vrc6AudioCycle(); curIn += ((int32_t)vrc6Out)*262; ampVolPos++; } if(aExp & EXP_FDS) { fdsAudioCycle(); curIn += ((int32_t)fdsOut)*202; ampVolPos++; } if(aExp & EXP_MMC5) { mmc5AudioCycle(); curIn += apu.pulseLookupTbl[mmc5Out]+(mmc5pcm<<6); ampVolPos++; } if(aExp & EXP_VRC7) { curIn += vrc7Out>>7; ampVolPos++; } if(aExp & EXP_N163) { curIn += n163Out*26; ampVolPos++; } if(aExp & EXP_S5B) { s5BAudioCycle(); curIn += s5BOut; ampVolPos++; } //amplify input curIn *= apu.ampVol[ampVolPos]; int32_t curOut; //gen output curOut = apu.lastLPOut+((apu.lpVal*((curIn>>6)-apu.lastLPOut))>>15); //Set Lowpass Output curIn = (apu.lastHPOut+apu.lastLPOut-curOut); //Set Highpass Input curIn += (curIn>>31)&1; //Add Sign Bit for proper Downshift later apu.lastLPOut = curOut; //Save Lowpass Output curOut = (apu.hpVal*curIn)>>15; //Set Highpass Output apu.lastHPOut = curOut; //Save Highpass Output //Save Clipped Highpass Output apu.OutBuf[apu.curBufPos] = (curOut > 32767)?(32767):((curOut < -32768)?(-32768):curOut); #endif #if FAKE_STEREO //add slight delay for stereo effect apu.OutBuf[apu.curBufPos+1] = apu.DelayBuf[apu.DelayCnt]; apu.DelayBuf[apu.DelayCnt] = apu.OutBuf[apu.curBufPos]; apu.DelayCnt++; apu.DelayCnt%=apu.DelaySize; #else //just copy same output apu.OutBuf[apu.curBufPos+1] = apu.OutBuf[apu.curBufPos]; #endif apu.curBufPos+=2; } apu.apuClock++; if(apu.p1freqCtr == 0) { apu.p1freqCtr = (apu.freq1<<1)+1; apu.p1Cycle = (apu.p1Cycle+1)&7; } else apu.p1freqCtr--; if(apu.p2freqCtr == 0) { apu.p2freqCtr = (apu.freq2<<1)+1; apu.p2Cycle = (apu.p2Cycle+1)&7; } else apu.p2freqCtr--; if(apu.triFreqCtr == 0) { apu.triFreqCtr = apu.triFreq; apu.triCycle = (apu.triCycle+1)&31; } else apu.triFreqCtr--; if(apu.noiseFreqCtr == 0) { apu.noiseFreqCtr = apu.noiseFreq; uint8_t cmpBit = apu.noiseMode1 ? (apu.noiseShiftReg>>6)&1 : (apu.noiseShiftReg>>1)&1; uint8_t cmpRes = (apu.noiseShiftReg&1)^cmpBit; apu.noiseShiftReg >>= 1; apu.noiseShiftReg |= cmpRes<<14; } else apu.noiseFreqCtr--; if(apu.dmcFreqCtr == 0) { apu.dmcFreqCtr = apu.dmcFreq; if(apu.dmcenabled) { if(apu.dmcSampleBuf&1) { if(apu.dmcVol <= 125) apu.dmcVol += 2; } else if(apu.dmcVol >= 2) apu.dmcVol -= 2; apu.dmcSampleBuf>>=1; } if(apu.dmcSampleRemain == 0) { if(apu.dmcready) { apu.dmcSampleBuf = apu.dmcCpuBuf; apu.dmcenabled = true; apu.dmcready = false; } else apu.dmcenabled = false; apu.dmcSampleRemain = 7; } else apu.dmcSampleRemain--; } else apu.dmcFreqCtr--; if(!apu.dmcready && !cpuInDMC_DMA() && apu.dmcCurLen) { cpuDoDMC_DMA(apu.dmcCurAddr); apu.dmcCurLen--; } if(aExp&EXP_VRC7) { if(apu.vrc7Clock == vrc7CycleTimer) { vrc7AudioCycle(); apu.vrc7Clock = 1; } else apu.vrc7Clock++; } if(aExp&EXP_FDS) fdsAudioMasterUpdate(); if(aExp&EXP_MMC5) mmc5AudioLenCycle(); if(apu.mode_change) apuChangeMode(); if(apu.modeCurCtr == 0) { if(apu.modePos == 5) apu.modePos = 0; else apu.modePos++; if(apu.mode5 == false) { apu.modeCurCtr = apu.mode4Ctr[apu.modePos]-1; if(apu.modePos == 3 || apu.modePos == 5) { if(apu.enable_irq) apu.irq = 1; } else { if(apu.modePos == 1) apuClockA(); else if(apu.modePos == 4) { apuClockA(); if(apu.enable_irq) { apu.irq = 1; //actually set for cpu interrupt |= APU_IRQ; } } apuClockB(); } } else { apu.modeCurCtr = apu.mode5Ctr[apu.modePos]-1; if(apu.modePos != 1 && apu.modePos != 5) { if(apu.modePos == 0 || apu.modePos == 3) apuClockA(); apuClockB(); } } } else apu.modeCurCtr--; } void apuSetReg00(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0] = val; apu.p1Env.vol = val&0xF; apu.p1seq = pulseSeqs[val>>6]; apu.p1Env.constant = ((val&PULSE_CONST_V) != 0); apu.p1Env.loop = apu.p1haltloop = ((val&PULSE_HALT_LOOP) != 0); } void apuSetReg01(uint16_t addr, uint8_t val) { (void)addr; apu.reg[1] = val; //printf("P1 sweep %02x\n", val); apu.p1Sweep.enabled = ((val&0x80) != 0); apu.p1Sweep.shift = val&7; apu.p1Sweep.period = (val>>4)&7; apu.p1Sweep.negative = ((val&0x8) != 0); apu.p1Sweep.start = true; //adjust for new sweep regs sweepUpdateFreq(&apu.p1Sweep, &apu.freq1); } void apuSetReg02(uint16_t addr, uint8_t val) { (void)addr; apu.reg[2] = val; //printf("P1 time low %02x\n", val); apu.freq1 = ((apu.freq1&~0xFF) | val); sweepUpdateFreq(&apu.p1Sweep, &apu.freq1); } void apuSetReg03(uint16_t addr, uint8_t val) { (void)addr; apu.reg[3] = val; apu.p1Cycle = 0; if(apu.reg[0x15] & P1_ENABLE) apu.p1LengthCtr = apu.lengthLookupTbl[val>>3]; apu.freq1 = (apu.freq1&0xFF) | ((val&7)<<8); sweepUpdateFreq(&apu.p1Sweep, &apu.freq1); //printf("P1 new freq %04x\n", apu.freq1); apu.p1Env.start = true; } void apuSetReg04(uint16_t addr, uint8_t val) { (void)addr; apu.reg[4] = val; apu.p2Env.vol = val&0xF; apu.p2seq = pulseSeqs[val>>6]; apu.p2Env.constant = ((val&PULSE_CONST_V) != 0); apu.p2Env.loop = apu.p2haltloop = ((val&PULSE_HALT_LOOP) != 0); } void apuSetReg05(uint16_t addr, uint8_t val) { (void)addr; apu.reg[5] = val; //printf("P2 sweep %02x\n", val); apu.p2Sweep.enabled = ((val&0x80) != 0); apu.p2Sweep.shift = val&7; apu.p2Sweep.period = (val>>4)&7; apu.p2Sweep.negative = ((val&0x8) != 0); apu.p2Sweep.start = true; //adjust for new sweep regs sweepUpdateFreq(&apu.p2Sweep, &apu.freq2); } void apuSetReg06(uint16_t addr, uint8_t val) { (void)addr; apu.reg[6] = val; //printf("P2 time low %02x\n", val); apu.freq2 = ((apu.freq2&~0xFF) | val); sweepUpdateFreq(&apu.p2Sweep, &apu.freq2); } void apuSetReg07(uint16_t addr, uint8_t val) { (void)addr; apu.reg[7] = val; apu.p2Cycle = 0; if(apu.reg[0x15] & P2_ENABLE) apu.p2LengthCtr = apu.lengthLookupTbl[val>>3]; apu.freq2 = (apu.freq2&0xFF) | ((val&7)<<8); sweepUpdateFreq(&apu.p2Sweep, &apu.freq2); //printf("P2 new freq %04x\n", apu.freq2); apu.p2Env.start = true; } void apuSetReg08(uint16_t addr, uint8_t val) { (void)addr; apu.reg[8] = val; apu.triLinearCtr = val&0x7F; apu.trihaltloop = ((val&TRI_HALT_LOOP) != 0); } void apuSetReg0A(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0xA] = val; apu.triFreq = ((apu.triFreq&~0xFF) | val); } void apuSetReg0B(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0xB] = val; if(apu.reg[0x15] & TRI_ENABLE) apu.triLengthCtr = apu.lengthLookupTbl[val>>3]; apu.triFreq = (apu.triFreq&0xFF) | ((val&7)<<8); //printf("Tri new freq %04x\n", apu.triFreq); apu.trireload = true; } void apuSetReg0C(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0xC] = val; apu.noiseEnv.vol = val&0xF; apu.noiseEnv.constant = ((val&PULSE_CONST_V) != 0); apu.noiseEnv.loop = apu.noisehaltloop = ((val&PULSE_HALT_LOOP) != 0); } void apuSetReg0E(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0xE] = val; apu.noiseMode1 = ((val&0x80) != 0); apu.noiseFreq = apu.noisePeriod[val&0xF]-1; } void apuSetReg0F(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0xF] = val; if(apu.reg[0x15] & NOISE_ENABLE) apu.noiseLengthCtr = apu.lengthLookupTbl[val>>3]; apu.noiseEnv.start = true; } void apuSetReg10(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0x10] = val; //printf("Set 0x10 %02x\n", val); apu.dmcFreq = apu.dmcPeriod[val&0xF]-1; apu.dmchaltloop = ((val&DMC_HALT_LOOP) != 0); apu.dmcirqenable = ((val&DMC_IRQ_ENABLE) != 0); //printf("%d\n", apu.dmcirqenable); if(!apu.dmcirqenable) interrupt &= ~DMC_IRQ; } void apuSetReg11(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0x11] = val; apu.dmcVol = val&0x7F; } void apuSetReg12(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0x12] = val; apu.dmcAddr = 0xC000+(val*64); } void apuSetReg13(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0x13] = val; //printf("Set 0x13 %02x\n", val); apu.dmcLen = (val*16)+1; } void apuSetReg15(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0x15] = val; //printf("Set 0x15 %02x\n",val); if(!(val & P1_ENABLE)) apu.p1LengthCtr = 0; if(!(val & P2_ENABLE)) apu.p2LengthCtr = 0; if(!(val & TRI_ENABLE)) apu.triLengthCtr = 0; if(!(val & NOISE_ENABLE)) apu.noiseLengthCtr = 0; if(!(val & DMC_ENABLE)) apu.dmcCurLen = 0; else if(apu.dmcCurLen == 0) { apu.dmcCurAddr = apu.dmcAddr; apu.dmcCurLen = apu.dmcLen; } interrupt &= ~DMC_IRQ; } void apuSetReg17(uint16_t addr, uint8_t val) { (void)addr; apu.reg[0x17] = val; apu.enable_irq = ((val&(1<<6)) == 0); if(!apu.enable_irq) { apu.irq = 0; interrupt &= ~APU_IRQ; } apu.new_mode5 = ((val&(1<<7)) != 0); //printf("Set 0x17 %d %d\n", apu.enable_irq, apu.new_mode5); apu.mode_change = true; } uint8_t apuGetReg15(uint16_t addr) { (void)addr; uint8_t intrflags = ((apu.irq<<6) | ((!!(interrupt&DMC_IRQ))<<7)); uint8_t apuretval = ((apu.p1LengthCtr > 0) | ((apu.p2LengthCtr > 0)<<1) | ((apu.triLengthCtr > 0)<<2) | ((apu.noiseLengthCtr > 0)<<3) | ((apu.dmcCurLen > 0)<<4) | intrflags); //printf("Get 0x15 %02x\n",apuretval); interrupt &= ~APU_IRQ; apu.irq = 0; return apuretval; } void apuBoot() { apuSetReg15(0x15,0); apuSetReg17(0x17,0); } void apuReset() { apuSetReg15(0x15,0); interrupt &= ~APU_IRQ; apu.irq = 0; apu.mode_change = true; } uint8_t *apuGetBuf() { return (uint8_t*)apu.OutBuf; } uint32_t apuGetBufSize() { #if AUDIO_FLOAT return apu.curBufPos*sizeof(float); #else return apu.curBufPos*sizeof(int16_t); #endif } uint32_t apuGetFrequency() { return apu.Frequency; } extern bool emuSkipFrame; bool apuUpdate() { #ifdef __LIBRETRO__ audioUpdate(); #else int updateRes = audioUpdate(); //printf("%i\n",updateRes); if(updateRes == 0) { emuSkipFrame = false; apu.waitForRefill = false; return false; } if(apu.waitForRefill && updateRes < 2) { apu.waitForRefill = false; emuSkipFrame = false; } else if(!apu.waitForRefill && updateRes > 2) { emuSkipFrame = true; apu.waitForRefill = true; } #endif apu.curBufPos = 0; return true; }