SNES: Randomize DSP register values on power on, but reset the internal state to 0

This commit is contained in:
Sour 2023-03-14 19:46:47 -04:00
parent 83fc79b55e
commit 91b7ebbd60
8 changed files with 51 additions and 25 deletions

View file

@ -12,6 +12,7 @@ unordered_map<string, int64_t>& ExpressionEvaluator::GetSpcTokens()
{ "ps", EvalValues::RegPS },
{ "sp", EvalValues::RegSP },
{ "pc", EvalValues::RegPC },
{ "dspreg", EvalValues::SpcDspReg },
};
return supportedTokens;
@ -27,6 +28,7 @@ int64_t ExpressionEvaluator::GetSpcTokenValue(int64_t token, EvalResultType& res
case EvalValues::RegSP: return s.SP;
case EvalValues::RegPS: return s.PS;
case EvalValues::RegPC: return s.PC;
case EvalValues::SpcDspReg: return s.DspReg;
default: return 0;
}
}

View file

@ -143,6 +143,8 @@ enum EvalValues : int64_t
VerticalBlank,
SpriteOverflow,
SpcDspReg,
PceVramTransferDone,
PceSatbTransferDone,
PceScanlineDetected,

View file

@ -1,18 +1,20 @@
#include "pch.h"
#include "SNES/DSP/Dsp.h"
#include "SNES/Spc.h"
#include "SNES/SnesConsole.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Utilities/Serializer.h"
//Quoted comments are from anomie's DSP document (with modifications by jwdonal)
Dsp::Dsp(Emulator* emu, Spc* spc)
Dsp::Dsp(Emulator* emu, SnesConsole* console, Spc* spc)
{
_emu = emu;
_spc = spc;
memcpy(_state.Regs, _initialValues, 0x80);
memset(_state.Regs, 0, 0x80);
console->InitializeRam(_state.ExternalRegs, 0x80);
for(int i = 0; i < 8; i++) {
_voices[i].Init(i, spc, this, _state.Regs + (i * 0x10), &_emu->GetSettings()->GetSnesConfig());
@ -24,9 +26,22 @@ Dsp::Dsp(Emulator* emu, Spc* spc)
Reset();
}
void Dsp::LoadSpcFileRegs(uint8_t* regs)
{
for(uint8_t i = 0; i < 0x80; i++) {
Write(i, regs[i]);
}
_state.NewKeyOn = ReadReg(DspGlobalRegs::KeyOn);
_state.DirSampleTableAddress = ReadReg(DspGlobalRegs::DirSampleTableAddress);
_state.EchoRingBufferAddress = ReadReg(DspGlobalRegs::EchoRingBufferAddress);
}
void Dsp::Reset()
{
WriteReg(DspGlobalRegs::Flags, 0xE0);
//"FLG will always act as if set to 0xE0 after power on or reset, even if the value read back indicates otherwise"
_state.Regs[(int)DspGlobalRegs::Flags] = 0xE0;
_state.Counter = 0;
_state.EchoHistoryPos = 0;
_state.EchoOffset = 0;
@ -88,6 +103,7 @@ void Dsp::UpdateCounter()
void Dsp::Write(uint8_t reg, uint8_t value)
{
_state.Regs[reg] = value;
_state.ExternalRegs[reg] = value;
switch(reg & 0x0F) {
case (int)DspVoiceRegs::Envelope: _state.EnvRegBuffer = value; break;
case (int)DspVoiceRegs::Out: _state.OutRegBuffer = value; break;
@ -98,7 +114,7 @@ void Dsp::Write(uint8_t reg, uint8_t value)
case (int)DspGlobalRegs::VoiceEnd:
_state.VoiceEndBuffer = 0;
_state.Regs[(int)DspGlobalRegs::VoiceEnd] = 0;
WriteGlobalReg(DspGlobalRegs::VoiceEnd, 0);
break;
}
break;
@ -356,6 +372,7 @@ void Dsp::Exec()
void Dsp::Serialize(Serializer& s)
{
SVArray(_state.Regs, 128);
SVArray(_state.ExternalRegs, 128);
for(int i = 0; i < 8; i++) {
SVI(_voices[i]);

View file

@ -5,24 +5,12 @@
#include "Utilities/ISerializable.h"
class Emulator;
class SnesConsole;
class Spc;
class Dsp final : public ISerializable
{
private:
//KOF ($5C) must be initialized to $00, some games (Chester Cheetah, King of Dragons) do not initialize its value
//This causes missing sound effects in both games.
static constexpr uint8_t _initialValues[0x80] = {
0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80,
0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF,
0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A,
0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF,
0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67,
0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x00,0x4E,0x7B,0xFF,
0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F,
0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF
};
DspState _state = {};
DspVoice _voices[8] = {};
Emulator* _emu = nullptr;
@ -47,8 +35,9 @@ private:
void EchoStep30();
public:
Dsp(Emulator* emu, Spc* spc);
Dsp(Emulator* emu, SnesConsole* console, Spc* spc);
void LoadSpcFileRegs(uint8_t* regs);
void Reset();
DspState& GetState() { return _state; }
@ -60,11 +49,22 @@ public:
bool CheckCounter(int32_t rate);
uint8_t Read(uint8_t reg) { return _state.Regs[reg]; }
uint8_t Read(uint8_t reg) { return _state.ExternalRegs[reg]; }
void Write(uint8_t reg, uint8_t value);
uint8_t ReadReg(DspGlobalRegs reg) { return _state.Regs[(int)reg]; }
void WriteReg(DspGlobalRegs reg, uint8_t value) { _state.Regs[(int)reg] = value; }
void WriteGlobalReg(DspGlobalRegs reg, uint8_t value)
{
_state.ExternalRegs[(int)reg] = value;
_state.Regs[(int)reg] = value;
}
void WriteVoiceReg(uint8_t voiceIndex, DspVoiceRegs reg, uint8_t value)
{
_state.ExternalRegs[voiceIndex * 0x10 + (int)reg] = value;
_state.Regs[voiceIndex * 0x10 + (int)reg] = value;
}
static int16_t Clamp16(int32_t val)
{
@ -75,6 +75,7 @@ public:
}
return val;
}
void Exec();
void Serialize(Serializer& s) override;

View file

@ -3,6 +3,7 @@
struct DspState
{
uint8_t ExternalRegs[128];
uint8_t Regs[128];
int32_t NoiseLfsr = 0x4000;

View file

@ -359,7 +359,7 @@ void DspVoice::Step6()
void DspVoice::Step7()
{
//"The new ENDX.x value may now be read."
_dsp->WriteReg(DspGlobalRegs::VoiceEnd, _shared->VoiceEndBuffer);
_dsp->WriteGlobalReg(DspGlobalRegs::VoiceEnd, _shared->VoiceEndBuffer);
//"The new VxENVX value is prepared, and can be overwritten. Reads will not see it yet."
_shared->EnvRegBuffer = _envOut;
@ -368,13 +368,13 @@ void DspVoice::Step7()
void DspVoice::Step8()
{
//"The new VxOUTX value may now be read."
WriteReg(DspVoiceRegs::Out, _shared->OutRegBuffer);
_dsp->WriteVoiceReg(_voiceIndex, DspVoiceRegs::Out, _shared->OutRegBuffer);
}
void DspVoice::Step9()
{
//"The new VxENVX value may now be read."
WriteReg(DspVoiceRegs::Envelope, _shared->EnvRegBuffer);
_dsp->WriteVoiceReg(_voiceIndex, DspVoiceRegs::Envelope, _shared->EnvRegBuffer);
}
void DspVoice::Serialize(Serializer& s)

View file

@ -31,7 +31,7 @@ Spc::Spc(SnesConsole* console)
_emu->RegisterMemory(MemoryType::SpcRom, _spcBios, Spc::SpcRomSize);
#ifndef DUMMYSPC
_dsp.reset(new Dsp(_emu, this));
_dsp.reset(new Dsp(_emu, console, this));
#endif
_state = {};
@ -579,7 +579,7 @@ void Spc::LoadSpcFile(SpcFileData* data)
{
memcpy(_ram, data->SpcRam, Spc::SpcRamSize);
memcpy(_dsp->GetState().Regs, data->DspRegs, sizeof(data->DspRegs));
_dsp->LoadSpcFileRegs(data->DspRegs);
_state.PC = data->PC;
_state.A = data->A;

View file

@ -267,6 +267,9 @@ namespace Mesen.Interop
public struct DspState
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public byte[] ExternalRegs;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public byte[] Regs;