Sunsoft FME-7/5A/5B support (Mapper 69) (partial audio chip support)

This commit is contained in:
Souryo 2016-06-11 20:12:20 -04:00
parent 618c8e0b5e
commit cdf0e8751a
9 changed files with 274 additions and 13 deletions

View File

@ -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" />

View File

@ -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">

View File

@ -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);

View File

@ -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;
}

View File

@ -3,7 +3,7 @@
#include "BaseMapper.h"
#include "CPU.h"
class SunSoft3 : public BaseMapper
class Sunsoft3 : public BaseMapper
{
private:
bool _irqLatch = false;

View File

@ -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; }

127
Core/Sunsoft5bAudio.h Normal file
View File

@ -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;
}
}
};

124
Core/SunsoftFme7.h Normal file
View File

@ -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;
}
}
};

View File

@ -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;