diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index b359f9bc..febe62da 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -529,10 +529,12 @@ <ClInclude Include="StereoDelayFilter.h" /> <ClInclude Include="StereoPanningFilter.h" /> <ClInclude Include="Sunsoft184.h" /> - <ClInclude Include="SunSoft3.h" /> - <ClInclude Include="SunSoft4.h" /> + <ClInclude Include="Sunsoft3.h" /> + <ClInclude Include="Sunsoft4.h" /> + <ClInclude Include="Sunsoft5bAudio.h" /> <ClInclude Include="Sunsoft89.h" /> <ClInclude Include="Sunsoft93.h" /> + <ClInclude Include="SunsoftFme7.h" /> <ClInclude Include="TaitoTc0190.h" /> <ClInclude Include="TaitoX1005.h" /> <ClInclude Include="TaitoX1017.h" /> diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index bc1eee58..023b30d4 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -403,12 +403,6 @@ <ClInclude Include="Mapper246.h"> <Filter>Nes\Mappers\Unnamed</Filter> </ClInclude> - <ClInclude Include="SunSoft3.h"> - <Filter>Nes\Mappers\Sunsoft</Filter> - </ClInclude> - <ClInclude Include="SunSoft4.h"> - <Filter>Nes\Mappers\Sunsoft</Filter> - </ClInclude> <ClInclude Include="Sunsoft89.h"> <Filter>Nes\Mappers\Sunsoft</Filter> </ClInclude> @@ -604,6 +598,18 @@ <ClInclude Include="Namco163Audio.h"> <Filter>Nes\Mappers\Namco</Filter> </ClInclude> + <ClInclude Include="SunsoftFme7.h"> + <Filter>Nes\Mappers\Sunsoft</Filter> + </ClInclude> + <ClInclude Include="Sunsoft5bAudio.h"> + <Filter>Nes\Mappers\Sunsoft</Filter> + </ClInclude> + <ClInclude Include="Sunsoft3.h"> + <Filter>Nes\Mappers\Sunsoft</Filter> + </ClInclude> + <ClInclude Include="Sunsoft4.h"> + <Filter>Nes\Mappers\Sunsoft</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="stdafx.cpp"> diff --git a/Core/MapperFactory.cpp b/Core/MapperFactory.cpp index 421ded68..41cc63fb 100644 --- a/Core/MapperFactory.cpp +++ b/Core/MapperFactory.cpp @@ -79,6 +79,7 @@ #include "Sunsoft89.h" #include "Sunsoft93.h" #include "Sunsoft184.h" +#include "SunsoftFme7.h" #include "TaitoTc0190.h" #include "TaitoX1005.h" #include "TaitoX1017.h" @@ -147,8 +148,9 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData) case 64: return new Rambo1(); case 65: return new IremH3001(); case 66: return new GxRom(); - case 67: return new SunSoft3(); - case 68: return new SunSoft4(); //incomplete support + case 67: return new Sunsoft3(); + case 68: return new Sunsoft4(); //incomplete support + case 69: return new SunsoftFme7(); case 70: return new Bandai74161_7432(false); case 71: return new BF909x(); case 72: return new JalecoJf17_19(false); diff --git a/Core/SoundMixer.cpp b/Core/SoundMixer.cpp index 7a90ab1b..dee7d51f 100644 --- a/Core/SoundMixer.cpp +++ b/Core/SoundMixer.cpp @@ -153,6 +153,7 @@ int16_t SoundMixer::GetOutputVolume() case AudioChannel::FDS: expansionOutput = (int16_t)(_currentOutput[ExpansionAudioIndex] * _volumes[ExpansionAudioIndex] * 20); break; case AudioChannel::VRC6: expansionOutput = (int16_t)(_currentOutput[ExpansionAudioIndex] * _volumes[ExpansionAudioIndex] * 75); break; case AudioChannel::Namco163: expansionOutput = (int16_t)(_currentOutput[ExpansionAudioIndex] * _volumes[ExpansionAudioIndex] * 20); break; + case AudioChannel::Sunsoft5B: expansionOutput = (int16_t)(_currentOutput[ExpansionAudioIndex] * _volumes[ExpansionAudioIndex] * 20); break; } return squareVolume + tndVolume + expansionOutput; } diff --git a/Core/SunSoft3.h b/Core/Sunsoft3.h similarity index 97% rename from Core/SunSoft3.h rename to Core/Sunsoft3.h index 0239f3f5..ef050c92 100644 --- a/Core/SunSoft3.h +++ b/Core/Sunsoft3.h @@ -3,7 +3,7 @@ #include "BaseMapper.h" #include "CPU.h" -class SunSoft3 : public BaseMapper +class Sunsoft3 : public BaseMapper { private: bool _irqLatch = false; diff --git a/Core/SunSoft4.h b/Core/Sunsoft4.h similarity index 96% rename from Core/SunSoft4.h rename to Core/Sunsoft4.h index c510dd53..e5b43d32 100644 --- a/Core/SunSoft4.h +++ b/Core/Sunsoft4.h @@ -3,7 +3,7 @@ #include "BaseMapper.h" #include "CPU.h" -class SunSoft4 : public BaseMapper +class Sunsoft4 : public BaseMapper { protected: virtual uint16_t GetPRGPageSize() { return 0x4000; } diff --git a/Core/Sunsoft5bAudio.h b/Core/Sunsoft5bAudio.h new file mode 100644 index 00000000..eb64a456 --- /dev/null +++ b/Core/Sunsoft5bAudio.h @@ -0,0 +1,127 @@ +#pragma once +#include "stdafx.h" +#include "Snapshotable.h" +#include "APU.h" + +class Sunsoft5bAudio : public Snapshotable +{ +private: + uint8_t _volumeLut[0x10]; + uint8_t _currentRegister; + uint8_t _registers[0x10]; + int16_t _lastOutput; + uint16_t _timer[3]; + uint8_t _toneStep[3]; + bool _processTick; + + uint16_t GetPeriod(int channel) + { + return _registers[channel * 2] | (_registers[channel * 2 + 1] << 8); + } + + uint16_t GetEnvelopePeriod() + { + return _registers[0x0B] | (_registers[0x0C] << 8); + } + + uint8_t GetNoisePeriod() + { + return _registers[6]; + } + + uint8_t GetVolume(int channel) + { + return _volumeLut[_registers[8 + channel] & 0x0F]; + } + + bool IsEnvelopeEnabled(int channel) + { + return (_registers[8 + channel] & 0x10) == 0x10; + } + + bool IsToneEnabled(int channel) + { + return ((_registers[7] >> channel) & 0x01) == 0x00; + } + + bool IsNoiseEnabled(int channel) + { + return ((_registers[7] >> (channel + 3)) & 0x01) == 0x00; + } + + void UpdateChannel(int channel) + { + _timer[channel]--; + if(_timer[channel] == 0) { + _timer[channel] = GetPeriod(channel) + 1; + _toneStep[channel] = (_toneStep[channel] + 1) & 0x0F; + } + } + + void UpdateOutputLevel() + { + int16_t summedOutput = 0; + for(int i = 0; i < 3; i++) { + if(IsToneEnabled(i) && _toneStep[i] < 0x08) { + summedOutput += GetVolume(i); + } + } + + APU::AddExpansionAudioDelta(AudioChannel::Sunsoft5B, summedOutput - _lastOutput); + _lastOutput = summedOutput; + } + +protected: + void StreamState(bool saving) + { + ArrayInfo<uint16_t> timer{ _timer, 3 }; + ArrayInfo<uint8_t> registers{ _registers, 0x10 }; + ArrayInfo<uint8_t> toneStep{ _toneStep, 3 }; + Stream(timer, registers, toneStep, _currentRegister, _lastOutput, _processTick); + } + +public: + Sunsoft5bAudio() + { + memset(_timer, 0, sizeof(_timer)); + memset(_registers, 0, sizeof(_registers)); + memset(_toneStep, 0, sizeof(_toneStep)); + _currentRegister = 0; + _lastOutput = 0; + _processTick = false; + + double output = 1.0; + _volumeLut[0] = 0; + for(int i = 1; i < 0x10; i++) { + //+1.5 dB 2x for every 1 step in volume + output *= 1.1885022274370184377301224648922; + output *= 1.1885022274370184377301224648922; + + _volumeLut[i] = (uint8_t)output; + } + } + + void ProcessCpuClock() + { + if(_processTick) { + for(int i = 0; i < 3; i++) { + UpdateChannel(i); + } + UpdateOutputLevel(); + } + _processTick = !_processTick; + } + + void WriteRegister(uint16_t addr, uint8_t value) + { + switch(addr & 0xE000) { + case 0xC000: + _currentRegister = value & 0x0F; + break; + + case 0xE000: + _registers[_currentRegister] = value; + break; + } + } +}; \ No newline at end of file diff --git a/Core/SunsoftFme7.h b/Core/SunsoftFme7.h new file mode 100644 index 00000000..f6e4e5bb --- /dev/null +++ b/Core/SunsoftFme7.h @@ -0,0 +1,124 @@ +#pragma once +#include "stdafx.h" +#include "BaseMapper.h" +#include "CPU.h" +#include "Sunsoft5bAudio.h" + +class SunsoftFme7 : public BaseMapper +{ +private: + Sunsoft5bAudio _audio; + uint8_t _command; + uint8_t _workRamValue; + bool _irqEnabled; + bool _irqCounterEnabled; + uint16_t _irqCounter; + +protected: + virtual uint16_t GetPRGPageSize() { return 0x2000; } + virtual uint16_t GetCHRPageSize() { return 0x400; } + virtual uint32_t GetWorkRamSize() { return 0x80000; } + virtual uint32_t GetWorkRamPageSize() { return 0x2000; } + virtual uint32_t GetSaveRamSize() { return 0x80000; } + virtual uint32_t GetSaveRamPageSize() { return 0x2000; } + + void InitMapper() + { + _command = 0; + _workRamValue = 0; + _irqEnabled = false; + _irqCounterEnabled = false; + _irqCounter = 0; + + SelectPRGPage(3, -1); + + UpdateWorkRam(); + } + + void StreamState(bool saving) + { + SnapshotInfo audio{ &_audio }; + Stream(_command, _workRamValue, _irqEnabled, _irqCounterEnabled, _irqCounter, audio); + if(!saving) { + UpdateWorkRam(); + } + } + + void ProcessCpuClock() + { + if(_irqCounterEnabled) { + _irqCounter--; + if(_irqCounter == 0xFFFF) { + if(_irqEnabled) { + CPU::SetIRQSource(IRQSource::External); + } + } + } + + _audio.ProcessCpuClock(); + } + + void UpdateWorkRam() + { + if(_workRamValue & 0x40) { + MemoryAccessType accessType = (_workRamValue & 0x80) ? MemoryAccessType::ReadWrite : MemoryAccessType::NoAccess; + SetCpuMemoryMapping(0x6000, 0x7FFF, _workRamValue & 0x3F, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam, accessType); + } else { + SetCpuMemoryMapping(0x6000, 0x7FFF, _workRamValue & 0x3F, PrgMemoryType::PrgRom); + } + } + + void WriteRegister(uint16_t addr, uint8_t value) + { + switch(addr & 0xE000) { + case 0x8000: + _command = value; + break; + case 0xA000: + switch(_command) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + SelectCHRPage(_command, value); + break; + + case 8: { + _workRamValue = value; + UpdateWorkRam(); + break; + } + + case 9: case 0xA: case 0xB: + SelectPRGPage(_command - 9, value & 0x3F); + break; + + case 0xC: + switch(value & 0x03) { + case 0: SetMirroringType(MirroringType::Vertical); break; + case 1: SetMirroringType(MirroringType::Horizontal); break; + case 2: SetMirroringType(MirroringType::ScreenAOnly); break; + case 3: SetMirroringType(MirroringType::ScreenBOnly); break; + } + break; + + case 0xD: + _irqEnabled = (value & 0x01) == 0x01; + _irqCounterEnabled = (value & 0x80) == 0x80; + CPU::ClearIRQSource(IRQSource::External); + break; + + case 0xE: + _irqCounter = _irqCounter & 0xFF00 | value; + break; + + case 0xF: + _irqCounter = _irqCounter & 0xFF | (value << 8); + break; + } + break; + + case 0xC000: + case 0xE000: + _audio.WriteRegister(addr, value); + break; + } + } +}; \ No newline at end of file diff --git a/GUI.NET/Forms/Config/frmAudioConfig.Designer.cs b/GUI.NET/Forms/Config/frmAudioConfig.Designer.cs index 134767bf..69028f12 100644 --- a/GUI.NET/Forms/Config/frmAudioConfig.Designer.cs +++ b/GUI.NET/Forms/Config/frmAudioConfig.Designer.cs @@ -312,7 +312,6 @@ // trkSunsoft5b // this.trkSunsoft5b.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.trkSunsoft5b.Enabled = false; this.trkSunsoft5b.Location = new System.Drawing.Point(384, 160); this.trkSunsoft5b.Margin = new System.Windows.Forms.Padding(0); this.trkSunsoft5b.Maximum = 100;