mirror of
https://github.com/libretro/MesenCE.git
synced 2026-11-10 03:02:05 -10:00
Resetting the emulator would cause the EPSM to not function for a while. This was because the EPSM implementation was not reset-aware. While the console clock it's comparing against would be reset to 0, its own internal clock would not, and so it would have to wait for the console clock to catch back up. Thanks to Sour for finding the cause and suggesting the fix.
115 lines
2.6 KiB
C++
115 lines
2.6 KiB
C++
#include "pch.h"
|
|
#include "NES/NesConsole.h"
|
|
#include "Shared/Emulator.h"
|
|
#include "NES/INesMemoryHandler.h"
|
|
#include "NES/Epsm.h"
|
|
#include "Utilities/Serializer.h"
|
|
|
|
Epsm::Epsm(Emulator* emu, NesConsole* console, vector<uint8_t>& adpcmRom) : _opn(console, adpcmRom)
|
|
{
|
|
_console = console;
|
|
_emu = emu;
|
|
_masterClockRate = _console->GetMasterClockRate();
|
|
_emu->GetSoundMixer()->RegisterAudioProvider(this);
|
|
}
|
|
|
|
Epsm::~Epsm()
|
|
{
|
|
_emu->GetSoundMixer()->UnregisterAudioProvider(this);
|
|
}
|
|
|
|
void Epsm::Reset()
|
|
{
|
|
_clockCounter = 0;
|
|
}
|
|
|
|
void Epsm::Write(uint8_t dataBus, uint8_t outPins)
|
|
{
|
|
//4016 writes
|
|
//The EPSM uses the value of the data bus + the OUT1 pin
|
|
//The OUT1 can be delayed by a cycle, but not the data bus
|
|
bool isHigh = (outPins & 0x02);
|
|
bool wasHigh = (_prevOutPins & 0x02);
|
|
|
|
if(isHigh && !wasHigh) {
|
|
//rising edge
|
|
_data = (dataBus & 0xF0) | (_data & 0x0F);
|
|
_addr = ((dataBus & 0x04) >> 1) | ((dataBus & 0x08) >> 3);
|
|
} else if(!isHigh && wasHigh) {
|
|
//falling edge
|
|
_data = (_data & 0xF0) | ((dataBus & 0xF0) >> 4);
|
|
_opn.Write(_addr, _data);
|
|
}
|
|
|
|
_prevOutPins = outPins;
|
|
}
|
|
|
|
void Epsm::WriteRam(uint16_t addr, uint8_t value)
|
|
{
|
|
//401C-401F writes
|
|
_opn.Write(addr, value);
|
|
}
|
|
|
|
void Epsm::OnRegionChanged()
|
|
{
|
|
uint64_t masterClockRate = _console->GetMasterClockRate();
|
|
if(_masterClockRate != masterClockRate) {
|
|
//Reset alignment between cpu & epsm clocks if the region changes (e.g pal to ntsc, etc.)
|
|
_masterClockRate = masterClockRate;
|
|
_clockCounter = GetTargetClock();
|
|
}
|
|
}
|
|
|
|
uint64_t Epsm::GetTargetClock()
|
|
{
|
|
uint64_t masterClock = _console->GetMasterClock();
|
|
double clockRatio = (double)OpnInterface::ClockRate / _masterClockRate;
|
|
return masterClock * clockRatio;
|
|
}
|
|
|
|
void Epsm::Exec()
|
|
{
|
|
constexpr int clocksPerSample = 144;
|
|
|
|
uint64_t targetClock = GetTargetClock();
|
|
|
|
while(_clockCounter < targetClock) {
|
|
_clockCounter++;
|
|
_opn.Exec();
|
|
|
|
if(++_sampleClockCounter == clocksPerSample) {
|
|
_opn.GenerateSamples(_samples);
|
|
_sampleClockCounter = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Epsm::MixAudio(int16_t* out, uint32_t sampleCount, uint32_t sampleRate)
|
|
{
|
|
_resampler.SetVolume(_console->GetNesConfig().EpsmVolume / 100.0);
|
|
_resampler.SetSampleRates(_opn.GetSampleRate(), sampleRate);
|
|
_resampler.Resample<true>(_samples.data(), (uint32_t)_samples.size() / 2, out, sampleCount, true);
|
|
_samples.clear();
|
|
}
|
|
|
|
void Epsm::GetMemoryRanges(MemoryRanges& ranges)
|
|
{
|
|
ranges.AddHandler(MemoryOperation::Write, 0x401C, 0x401F);
|
|
}
|
|
|
|
uint8_t Epsm::ReadRam(uint16_t addr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void Epsm::Serialize(Serializer& s)
|
|
{
|
|
SV(_clockCounter);
|
|
SV(_prevOutPins);
|
|
SV(_addr);
|
|
SV(_data);
|
|
SV(_masterClockRate);
|
|
SV(_sampleClockCounter);
|
|
|
|
SV(_opn);
|
|
} |