diff --git a/APU.cpp b/APU.cpp index 7db2979..599613a 100644 --- a/APU.cpp +++ b/APU.cpp @@ -26,6 +26,7 @@ namespace APU #define DAC1_enabled ((NR12&0xf8)!=0) #define DAC2_enabled ((NR22&0xf8)!=0) #define DAC3_enabled ((NR30&0x80)!=0) + #define DAC4_enabled ((NR42&0xf8)!=0) uint8_t duty_cycles[4][8] = { {1, 1, 1, 1, 1, 1, 1, 0}, @@ -74,6 +75,7 @@ namespace APU uint8_t NR52 = 0; // 0xff26 - Sound on/off // 0xff27-0xff2f - Not used? uint8_t WaveRAM[16]; // 0xff30-0xff3f + uint16_t LFSR = 0; void audioCallback(void * userdata, uint8_t * stream, int len) @@ -127,6 +129,17 @@ namespace APU NR51 = 0; NR52 = 0; CH1.duty_cycle = 0; + LFSR = 0; + for (int i=0; i<16; ++i) WaveRAM[i]=0; + } + + uint8_t getLFSR() + { + LFSR = (LFSR&0x7fff) | ( (LFSR&1)==((LFSR>>1)&1) ? 0x8000 : 0x0000 ); + if (NR43&0x08) LFSR = (LFSR&0xff7f) | ((LFSR>>8)&0x0080); + LFSR=LFSR>>1; + + return LFSR&1; } void triggerCH1() @@ -159,6 +172,17 @@ namespace APU // sweep does several things (check documentation) } + void triggerCH4() + { + CH4.enabled = true; + CH4.length_timer=NR41&0x3f; + uint8_t clock_shift = ((NR43&0xf0)>>4);// if (clock_shift==0) clock_shift = 1; + CH4.period_divider = clock_shift << (NR43&0x07); + // envelope timer is reset + CH4.volume = NR42>>4; + // sweep does several things (check documentation) + } + uint8_t readRegister(uint16_t address) { switch(address) @@ -180,6 +204,11 @@ namespace APU case 0xff1d: return 0x00; break; case 0xff1e: return NR34 & 0x40; break; + case 0xff20: return 0x00; break; + case 0xff21: return NR42; break; + case 0xff22: return NR43; break; + case 0xff23: return NR44 & 0x40; break; + case 0xff24: return NR50; break; case 0xff25: return NR51; break; case 0xff26: return NR52; break; @@ -227,6 +256,11 @@ namespace APU case 0xff1d: NR33 = value; break; case 0xff1e: NR34 = value; CH3.length_enable=(value&0x40); if (value&0x80) triggerCH3(); break; + case 0xff20: NR41 = value; CH1.length_timer=NR11&0x3f; CH1.duty_cycle = NR11>>6; break; + case 0xff21: NR42 = value; break; + case 0xff22: NR43 = value; break; + case 0xff23: NR44 = value; CH4.length_enable=(value&0x40); if (value&0x80) triggerCH4(); break; + case 0xff24: NR50 = value; break; case 0xff25: NR51 = value; break; case 0xff26: if (value&0x80) reset(); NR52 = (value&0x80) | (NR52 & 0x0f); break; @@ -284,6 +318,14 @@ namespace APU CH3.length_timer++; } } + if (CH4.enabled && CH4.length_enable) { + if (CH4.length_timer==63) { + CH4.enabled = false; + CH4.length_timer=0; + } else { + CH4.length_timer++; + } + } } DIVAPU_CH1_freq_sweep++; if (DIVAPU_CH1_freq_sweep==4) { @@ -315,13 +357,23 @@ namespace APU } } } - - // Do the envelope sweep thing + if ( CH4.enabled && (NR42&0x7) ) { // If sweep pace != 0, envelope sweep is enabled + CH4.envelope_sweep_timer++; + if ( CH4.envelope_sweep_timer == (NR42&0x07) ) { // if timer == envelope sweep, increase or decrease volume + CH4.envelope_sweep_timer=0; + if (NR42&0x8) { // bit set increases, reset decreases + if (CH4.volume<0x0f) CH4.volume++; + } else { + if (CH4.volume>0) CH4.volume--; + } + } + } } } uint32_t dots = 0; uint32_t dotsCH3 = 0; + uint32_t dotsCH4 = 0; void update(uint32_t dt) { dots += dt; @@ -350,6 +402,16 @@ namespace APU if (CH3.duty_step==32) CH3.duty_step=0; } } + dotsCH4 += dt; + while (dotsCH4>=16) { + dotsCH4 -= 16; + CH4.period_divider--; + if (CH4.period_divider==0) { + uint8_t clock_shift = ((NR43&0xf0)>>3); if (clock_shift==0) clock_shift = 1; + CH4.period_divider = clock_shift << (NR43&0x07); + CH4.duty_step=getLFSR(); + } + } t_sound += dt; samples_t += dt; @@ -369,7 +431,10 @@ namespace APU sampleCH3 = CH3.volume==0 ? 0 : CH3.volume==1 ? actual_sample : CH3.volume==2 ? actual_sample>>1 : actual_sample>>2; } - uint8_t sample = (sampleCH1+sampleCH2+sampleCH3)&0xff; + uint16_t sampleCH4 = 0; + if (apu_enabled && DAC4_enabled) sampleCH4 = (CH4.duty_step*CH4.volume)<<2; + + uint8_t sample = (sampleCH1+sampleCH2+sampleCH3+sampleCH4)&0xff; sound_buffer[(sound_pos++)&(AUDIO_BUFFER_SIZE-1)] = sample; audio_viewer::addsample(sample);