From 5ccbe95a55a1b7f6d3e0f59d62641a6d0958f788 Mon Sep 17 00:00:00 2001 From: Souryo Date: Thu, 4 May 2017 18:17:46 -0400 Subject: [PATCH] FDS: Fixed sound emulation (fixes Bio Miracle) --- Core/BaseFdsChannel.h | 47 +++++++++++++++++++--------------- Core/FDS.cpp | 7 +++-- Core/FdsAudio.h | 59 +++++++++++++++++++++++++------------------ Core/ModChannel.h | 48 ++++++++++++++++++++++++----------- 4 files changed, 96 insertions(+), 65 deletions(-) diff --git a/Core/BaseFdsChannel.h b/Core/BaseFdsChannel.h index 7caf8438..07edd2ab 100644 --- a/Core/BaseFdsChannel.h +++ b/Core/BaseFdsChannel.h @@ -29,47 +29,47 @@ public: virtual void WriteReg(uint16_t addr, uint8_t value) { - switch(addr & 0x03) { - case 0: + switch(addr) { + case 0x4080: _speed = value & 0x3F; _volumeIncrease = (value & 0x40) == 0x40; _envelopeOff = (value & 0x80) == 0x80; //"Writing to this register immediately resets the clock timer that ticks the volume envelope (delaying the next tick slightly)." - _timer = 8 * (_speed + 1) * _masterSpeed; + ResetTimer(); + + if(_envelopeOff) { + //Envelope is off, gain = speed + _gain = _speed; + } break; - case 2: + case 0x4082: _frequency = (_frequency & 0x0F00) | value; break; - case 3: + case 0x4083: _frequency = (_frequency & 0xFF) | ((value & 0x0F) << 8); break; } } - void TickEnvelope(bool envelopesDisabled) + bool TickEnvelope() { - if(_envelopeOff) { - _gain = _speed; - } else if(!envelopesDisabled && _speed > 0) { - if(_timer > 0) { - _timer--; - } else { - _timer = 8 * (_speed + 1) * _masterSpeed; + if(!_envelopeOff && _masterSpeed > 0) { + _timer--; + if(_timer == 0) { + ResetTimer(); - if(_volumeIncrease) { - if(_gain < 32) { - _gain++; - } - } else { - if(_gain > 0) { - _gain--; - } + if(_volumeIncrease && _gain < 32) { + _gain++; + } else if(!_volumeIncrease && _gain > 0) { + _gain--; } + return true; } } + return false; } uint8_t GetGain() @@ -81,4 +81,9 @@ public: { return _frequency; } + + void ResetTimer() + { + _timer = 8 * (_speed + 1) * _masterSpeed; + } }; \ No newline at end of file diff --git a/Core/FDS.cpp b/Core/FDS.cpp index 30f4f3a7..151ca954 100644 --- a/Core/FDS.cpp +++ b/Core/FDS.cpp @@ -285,7 +285,9 @@ bool FDS::IsDiskInserted() uint8_t FDS::ReadRegister(uint16_t addr) { uint8_t value = 0; - if(_diskRegEnabled && addr < 0x4040 || _soundRegEnabled && addr >= 0x4040) { + if(_soundRegEnabled && addr >= 0x4040) { + return _audio->ReadRegister(addr); + } else if(_diskRegEnabled && addr <= 0x4033) { switch(addr) { case 0x4030: value |= CPU::HasIRQSource(IRQSource::External) ? 0x01 : 0x00; @@ -313,9 +315,6 @@ uint8_t FDS::ReadRegister(uint16_t addr) case 0x4033: //Always return good battery return 0x80 & _extConWriteReg; - - default: - return _audio->ReadRegister(addr); } } diff --git a/Core/FdsAudio.h b/Core/FdsAudio.h index ca8a60b6..3b1ea6ca 100644 --- a/Core/FdsAudio.h +++ b/Core/FdsAudio.h @@ -47,41 +47,46 @@ protected: void ClockAudio() override { - //"The envelopes are not ticked while the waveform is halted." - _volume.TickEnvelope(_disableEnvelopes || _haltWaveform); - _mod.TickEnvelope(_disableEnvelopes || _haltWaveform); - - if(_mod.IsEnabled()) { - if(_mod.TickModulator()) { - //Modulator was ticked, update wave pitch - _wavePitch = _mod.GetWavePitch(_volume.GetFrequency()); + int frequency = _volume.GetFrequency(); + if(!_haltWaveform && !_disableEnvelopes) { + _volume.TickEnvelope(); + if(_mod.TickEnvelope()) { + _mod.UpdateOutput(frequency); } - } else { - _wavePitch = 0; } + if(_mod.TickModulator()) { + //Modulator was ticked, update wave pitch + _mod.UpdateOutput(frequency); + } + if(_haltWaveform) { - //"The high bit of this register halts the waveform and resets its phase to 0. Note that if halted it will output the constant value at $4040" - //"writes to the volume register $4080 or master volume $4089 will affect the output." _wavePosition = 0; - } - - int32_t freq = _volume.GetFrequency() + _wavePitch; - if(freq > 0 && !_waveWriteEnabled) { - _waveOverflowCounter += freq; - if(_waveOverflowCounter < freq) { - //Overflow, tick - uint32_t level = std::min((int)_volume.GetGain(), 32) * WaveVolumeTable[_masterVolume]; - - uint8_t outputLevel = (_waveTable[_wavePosition] * level) / 1152; - APU::AddExpansionAudioDelta(AudioChannel::FDS, outputLevel - _lastOutput); - _lastOutput = outputLevel; + UpdateOutput(); + } else { + UpdateOutput(); - _wavePosition = (_wavePosition + 1) & 0x3F; + if(frequency + _mod.GetOutput() > 0 && !_waveWriteEnabled) { + _waveOverflowCounter += frequency + _mod.GetOutput(); + if(_waveOverflowCounter < frequency + _mod.GetOutput()) { + _wavePosition = (_wavePosition + 1) & 0x3F; + } } } } + void UpdateOutput() + { + uint32_t level = std::min((int)_volume.GetGain(), 32) * WaveVolumeTable[_masterVolume]; + uint8_t outputLevel = (_waveTable[_wavePosition] * level) / 1152; + + + if(_lastOutput != outputLevel) { + APU::AddExpansionAudioDelta(AudioChannel::FDS, outputLevel - _lastOutput); + _lastOutput = outputLevel; + } + } + public: uint8_t ReadRegister(uint16_t addr) { @@ -113,6 +118,10 @@ public: case 0x4083: _disableEnvelopes = (value & 0x40) == 0x40; _haltWaveform = (value & 0x80) == 0x80; + if(_disableEnvelopes) { + _volume.ResetTimer(); + _mod.ResetTimer(); + } _volume.WriteReg(addr, value); break; diff --git a/Core/ModChannel.h b/Core/ModChannel.h index 1a8ca084..7ff2c07f 100644 --- a/Core/ModChannel.h +++ b/Core/ModChannel.h @@ -14,6 +14,7 @@ private: uint8_t _modTable[64]; uint8_t _modTablePosition = 0; uint16_t _overflowCounter = 0; + int32_t _output = 0; protected: void StreamState(bool saving) override @@ -21,17 +22,28 @@ protected: BaseFdsChannel::StreamState(saving); ArrayInfo modTable = { _modTable, 64 }; - Stream(_counter, _modulationDisabled, _modTablePosition, _overflowCounter, modTable); + Stream(_counter, _modulationDisabled, _modTablePosition, _overflowCounter, modTable, _output); } public: virtual void WriteReg(uint16_t addr, uint8_t value) override { - switch(addr & 0x03) { - case 1: UpdateCounter(value & 0x7F); break; - case 3: _modulationDisabled = (value & 0x80) == 0x80; break; + switch(addr) { + case 0x4084: + case 0x4086: + BaseFdsChannel::WriteReg(addr, value); + break; + case 0x4085: + UpdateCounter(_counter & 0x7F); + break; + case 0x4087: + BaseFdsChannel::WriteReg(addr, value); + _modulationDisabled = (value & 0x80) == 0x80; + if(_modulationDisabled) { + _overflowCounter = 0; + } + break; } - BaseFdsChannel::WriteReg(addr, value); } void WriteModTable(uint8_t value) @@ -61,22 +73,23 @@ public: bool TickModulator() { - _overflowCounter += _frequency; + if(IsEnabled()) { + _overflowCounter += _frequency; - if(_overflowCounter < _frequency) { - //Overflowed, tick the modulator - int32_t offset = _modLut[_modTable[_modTablePosition]]; - UpdateCounter(offset == ModReset ? 0 : _counter + offset); + if(_overflowCounter < _frequency) { + //Overflowed, tick the modulator + int32_t offset = _modLut[_modTable[_modTablePosition]]; + UpdateCounter(offset == ModReset ? 0 : _counter + offset); - _modTablePosition = (_modTablePosition + 1) & 0x3F; + _modTablePosition = (_modTablePosition + 1) & 0x3F; - return true; + return true; + } } - return false; } - int32_t GetWavePitch(uint16_t volumePitch) + void UpdateOutput(uint16_t volumePitch) { //Code from NesDev Wiki @@ -108,6 +121,11 @@ public: } // final mod result is in temp - return temp; + _output = temp; + } + + int32_t GetOutput() + { + return IsEnabled() ? _output : 0; } };