mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-24 14:49:42 -06:00
DSP plugin merge - the two DSP plugins are now gone and all the code has been merged into Dolphin.
This WILL temporarily break the Linux and MacOSX builds but should be easy to fix. Things left to do: * The UI on the new Audio tab for the LLE/HLE choice is ugly * At times the code still look "plugin-y" and needs cleanup * The two plugins should be merged further. DSPHLE should use the emulated memory etc of DSPLLE as much as possible, so that simply saving the DSPLLE state is enough. This would also bring the possibility of savestate compatibility between the two plugins. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6947 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
@ -155,6 +155,7 @@ void SConfig::SaveSettings()
|
||||
ini.Set("Core", "CPUCore", m_LocalCoreStartupParameter.iCPUCore);
|
||||
ini.Set("Core", "CPUThread", m_LocalCoreStartupParameter.bCPUThread);
|
||||
ini.Set("Core", "DSPThread", m_LocalCoreStartupParameter.bDSPThread);
|
||||
ini.Set("Core", "DSPHLE", m_LocalCoreStartupParameter.bDSPHLE);
|
||||
ini.Set("Core", "SkipIdle", m_LocalCoreStartupParameter.bSkipIdle);
|
||||
ini.Set("Core", "LockThreads", m_LocalCoreStartupParameter.bLockThreads);
|
||||
ini.Set("Core", "DefaultGCM", m_LocalCoreStartupParameter.m_strDefaultGCM);
|
||||
@ -184,7 +185,6 @@ void SConfig::SaveSettings()
|
||||
|
||||
// Plugins
|
||||
ini.Set("Core", "GFXPlugin", m_LocalCoreStartupParameter.m_strVideoPlugin);
|
||||
ini.Set("Core", "DSPPlugin", m_LocalCoreStartupParameter.m_strDSPPlugin);
|
||||
|
||||
ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
||||
m_SYSCONF->Save();
|
||||
@ -280,6 +280,7 @@ void SConfig::LoadSettings()
|
||||
ini.Get("Core", "HLE_BS2", &m_LocalCoreStartupParameter.bHLE_BS2, false);
|
||||
ini.Get("Core", "CPUCore", &m_LocalCoreStartupParameter.iCPUCore, 1);
|
||||
ini.Get("Core", "DSPThread", &m_LocalCoreStartupParameter.bDSPThread, false);
|
||||
ini.Get("Core", "DSPHLE", &m_LocalCoreStartupParameter.bDSPHLE, true);
|
||||
ini.Get("Core", "CPUThread", &m_LocalCoreStartupParameter.bCPUThread, true);
|
||||
ini.Get("Core", "SkipIdle", &m_LocalCoreStartupParameter.bSkipIdle, true);
|
||||
ini.Get("Core", "LockThreads", &m_LocalCoreStartupParameter.bLockThreads, false);
|
||||
@ -318,7 +319,6 @@ void SConfig::LoadSettings()
|
||||
|
||||
// Plugins
|
||||
ini.Get("Core", "GFXPlugin", &m_LocalCoreStartupParameter.m_strVideoPlugin, m_DefaultGFXPlugin.c_str());
|
||||
ini.Get("Core", "DSPPlugin", &m_LocalCoreStartupParameter.m_strDSPPlugin, m_DefaultDSPPlugin.c_str());
|
||||
}
|
||||
|
||||
m_SYSCONF = new SysConf();
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "PowerPC/JitCommon/JitBase.h"
|
||||
|
||||
#include "PluginManager.h"
|
||||
#include "PluginDSP.h"
|
||||
#include "ConfigManager.h"
|
||||
|
||||
#include "VolumeHandler.h"
|
||||
@ -369,24 +370,7 @@ void EmuThread()
|
||||
Callback_PeekMessages = VideoInitialize.pPeekMessages;
|
||||
g_pUpdateFPSDisplay = VideoInitialize.pUpdateFPSDisplay;
|
||||
|
||||
// Load and init DSPPlugin
|
||||
DSPInitialize dspInit;
|
||||
dspInit.hWnd = g_pWindowHandle;
|
||||
dspInit.pARAM_Read_U8 = (u8 (__cdecl *)(const u32))DSP::ReadARAM;
|
||||
dspInit.pARAM_Write_U8 = (void (__cdecl *)(const u8, const u32))DSP::WriteARAM;
|
||||
dspInit.pGetARAMPointer = DSP::GetARAMPtr;
|
||||
dspInit.pGetMemoryPointer = Memory::GetPointer;
|
||||
dspInit.pLog = Callback_DSPLog;
|
||||
dspInit.pName = Callback_ISOName;
|
||||
dspInit.pDebuggerBreak = Callback_DebuggerBreak;
|
||||
dspInit.pGenerateDSPInterrupt = Callback_DSPInterrupt;
|
||||
dspInit.pGetAudioStreaming = AudioInterface::Callback_GetStreaming;
|
||||
dspInit.pGetSampleRate = AudioInterface::Callback_GetSampleRate;
|
||||
dspInit.pEmulatorState = (int *)PowerPC::GetStatePtr();
|
||||
dspInit.bWii = _CoreParameter.bWii;
|
||||
dspInit.bOnThread = _CoreParameter.bDSPThread;
|
||||
|
||||
Plugins.GetDSP()->Initialize((void *)&dspInit);
|
||||
DSP::GetPlugin()->Initialize(g_pWindowHandle, _CoreParameter.bWii, _CoreParameter.bDSPThread);
|
||||
|
||||
Pad::Initialize(g_pWindowHandle);
|
||||
|
||||
@ -484,7 +468,7 @@ void EmuThread()
|
||||
|
||||
// Stop audio thread - Actually this does nothing on HLE plugin.
|
||||
// But stops the DSP Interpreter on LLE plugin.
|
||||
Plugins.GetDSP()->DSP_StopSoundStream();
|
||||
DSP::GetPlugin()->DSP_StopSoundStream();
|
||||
|
||||
// We must set up this flag before executing HW::Shutdown()
|
||||
g_bHwInit = false;
|
||||
@ -499,7 +483,6 @@ void EmuThread()
|
||||
|
||||
Pad::Shutdown();
|
||||
Wiimote::Shutdown();
|
||||
Plugins.ShutdownPlugins();
|
||||
|
||||
NOTICE_LOG(CONSOLE, "%s", StopMessage(false, "Plugins shutdown").c_str());
|
||||
|
||||
|
@ -42,7 +42,7 @@ SCoreStartupParameter::SCoreStartupParameter()
|
||||
bJITBranchOff(false), bJITProfiledReJIT(false),
|
||||
bJITILTimeProfiling(false), bJITILOutputIR(false),
|
||||
bEnableFPRF(false),
|
||||
bCPUThread(true), bDSPThread(false),
|
||||
bCPUThread(true), bDSPThread(false), bDSPHLE(true),
|
||||
bSkipIdle(true), bNTSC(false), bNTSCJ(false),
|
||||
bHLE_BS2(true), bUseFastMem(false),
|
||||
bLockThreads(false),
|
||||
@ -72,6 +72,7 @@ void SCoreStartupParameter::LoadDefaults()
|
||||
bCPUThread = false;
|
||||
bSkipIdle = false;
|
||||
bRunCompareServer = false;
|
||||
bDSPHLE = true;
|
||||
bDSPThread = true;
|
||||
bLockThreads = true;
|
||||
bEnableFPRF = false;
|
||||
|
@ -69,6 +69,7 @@ struct SCoreStartupParameter
|
||||
|
||||
bool bCPUThread;
|
||||
bool bDSPThread;
|
||||
bool bDSPHLE;
|
||||
bool bSkipIdle;
|
||||
bool bNTSC;
|
||||
bool bNTSCJ;
|
||||
@ -129,7 +130,6 @@ struct SCoreStartupParameter
|
||||
|
||||
// files
|
||||
std::string m_strVideoPlugin;
|
||||
std::string m_strDSPPlugin;
|
||||
|
||||
std::string m_strFilename;
|
||||
std::string m_strBootROM;
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "../PowerPC/PowerPC.h"
|
||||
#include "../PluginManager.h"
|
||||
#include "../ConfigManager.h"
|
||||
#include "../PluginDSP.h"
|
||||
|
||||
namespace DSP
|
||||
{
|
||||
@ -60,8 +61,8 @@ enum
|
||||
DSP_MAIL_FROM_DSP_LO = 0x5006,
|
||||
DSP_CONTROL = 0x500A,
|
||||
DSP_INTERRUPT_CONTROL = 0x5010,
|
||||
AR_INFO = 0x5012, // These names are a good guess at best
|
||||
AR_MODE = 0x5016, //
|
||||
AR_INFO = 0x5012, // These names are a good guess at best
|
||||
AR_MODE = 0x5016, //
|
||||
AR_REFRESH = 0x501a,
|
||||
AR_DMA_MMADDR_H = 0x5020,
|
||||
AR_DMA_MMADDR_L = 0x5022,
|
||||
@ -71,7 +72,7 @@ enum
|
||||
AR_DMA_CNT_L = 0x502A,
|
||||
AUDIO_DMA_START_HI = 0x5030,
|
||||
AUDIO_DMA_START_LO = 0x5032,
|
||||
AUDIO_DMA_BLOCKS_LENGTH = 0x5034, // Ever used?
|
||||
AUDIO_DMA_BLOCKS_LENGTH = 0x5034, // Ever used?
|
||||
AUDIO_DMA_CONTROL_LEN = 0x5036,
|
||||
AUDIO_DMA_BLOCKS_LEFT = 0x503A,
|
||||
};
|
||||
@ -211,7 +212,7 @@ static ARAM_Info g_ARAM_Info;
|
||||
static u16 g_AR_MODE;
|
||||
static u16 g_AR_REFRESH;
|
||||
|
||||
Common::PluginDSP *dsp_plugin;
|
||||
PluginDSP *dsp_plugin;
|
||||
|
||||
static int dsp_slice = 0;
|
||||
static bool dsp_is_lle = false;
|
||||
@ -229,6 +230,8 @@ void DoState(PointerWrap &p)
|
||||
p.Do(g_ARAM_Info);
|
||||
p.Do(g_AR_MODE);
|
||||
p.Do(g_AR_REFRESH);
|
||||
|
||||
dsp_plugin->DoState(p);
|
||||
}
|
||||
|
||||
|
||||
@ -245,13 +248,15 @@ void GenerateDSPInterrupt_Wrapper(u64 userdata, int cyclesLate)
|
||||
GenerateDSPInterrupt((DSPInterruptType)(userdata&0xFFFF), (bool)((userdata>>16) & 1));
|
||||
}
|
||||
|
||||
void Init()
|
||||
PluginDSP *GetPlugin()
|
||||
{
|
||||
dsp_plugin = CPluginManager::GetInstance().GetDSP();
|
||||
PLUGIN_INFO DSPType;
|
||||
dsp_plugin->GetInfo(DSPType);
|
||||
std::string DSPName(DSPType.Name);
|
||||
dsp_is_lle = (DSPName.find("LLE") != std::string::npos) || (DSPName.find("lle") != std::string::npos);
|
||||
return dsp_plugin;
|
||||
}
|
||||
|
||||
void Init(bool hle)
|
||||
{
|
||||
dsp_plugin = CreateDSPPlugin(hle);
|
||||
dsp_is_lle = dsp_plugin->IsLLE();
|
||||
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
|
||||
{
|
||||
@ -263,7 +268,7 @@ void Init()
|
||||
}
|
||||
else
|
||||
{
|
||||
// On the GC, ARAM is accessible only through this interface (unless you're doing mmu tricks?...)
|
||||
// On the GC, ARAM is accessible only through this interface.
|
||||
g_ARAM.wii_mode = false;
|
||||
g_ARAM.size = ARAM_SIZE;
|
||||
g_ARAM.mask = ARAM_MASK;
|
||||
@ -288,6 +293,8 @@ void Shutdown()
|
||||
FreeMemoryPages(g_ARAM.ptr, g_ARAM.size);
|
||||
g_ARAM.ptr = NULL;
|
||||
|
||||
dsp_plugin->Shutdown();
|
||||
delete dsp_plugin;
|
||||
dsp_plugin = NULL;
|
||||
}
|
||||
|
||||
@ -301,11 +308,11 @@ void Read16(u16& _uReturnValue, const u32 _iAddress)
|
||||
dsp_plugin->DSP_Update(DSP_MAIL_SLICE);
|
||||
dsp_slice -= DSP_MAIL_SLICE;
|
||||
}
|
||||
_uReturnValue = dsp_plugin->DSP_ReadMailboxHigh(true);
|
||||
_uReturnValue = dsp_plugin->DSP_ReadMailBoxHigh(true);
|
||||
break;
|
||||
|
||||
case DSP_MAIL_TO_DSP_LO:
|
||||
_uReturnValue = dsp_plugin->DSP_ReadMailboxLow(true);
|
||||
_uReturnValue = dsp_plugin->DSP_ReadMailBoxLow(true);
|
||||
break;
|
||||
|
||||
case DSP_MAIL_FROM_DSP_HI:
|
||||
@ -313,11 +320,11 @@ void Read16(u16& _uReturnValue, const u32 _iAddress)
|
||||
dsp_plugin->DSP_Update(DSP_MAIL_SLICE);
|
||||
dsp_slice -= DSP_MAIL_SLICE;
|
||||
}
|
||||
_uReturnValue = dsp_plugin->DSP_ReadMailboxHigh(false);
|
||||
_uReturnValue = dsp_plugin->DSP_ReadMailBoxHigh(false);
|
||||
break;
|
||||
|
||||
case DSP_MAIL_FROM_DSP_LO:
|
||||
_uReturnValue = dsp_plugin->DSP_ReadMailboxLow(false);
|
||||
_uReturnValue = dsp_plugin->DSP_ReadMailBoxLow(false);
|
||||
break;
|
||||
|
||||
case DSP_CONTROL:
|
||||
@ -382,11 +389,11 @@ void Write16(const u16 _Value, const u32 _Address)
|
||||
{
|
||||
// DSP
|
||||
case DSP_MAIL_TO_DSP_HI:
|
||||
dsp_plugin->DSP_WriteMailboxHigh(true, _Value);
|
||||
dsp_plugin->DSP_WriteMailBoxHigh(true, _Value);
|
||||
break;
|
||||
|
||||
case DSP_MAIL_TO_DSP_LO:
|
||||
dsp_plugin->DSP_WriteMailboxLow(true, _Value);
|
||||
dsp_plugin->DSP_WriteMailBoxLow(true, _Value);
|
||||
break;
|
||||
|
||||
case DSP_MAIL_FROM_DSP_HI:
|
||||
@ -527,7 +534,7 @@ void Read32(u32& _uReturnValue, const u32 _iAddress)
|
||||
{
|
||||
// DSP
|
||||
case DSP_MAIL_TO_DSP_HI:
|
||||
_uReturnValue = (dsp_plugin->DSP_ReadMailboxHigh(true) << 16) | dsp_plugin->DSP_ReadMailboxLow(true);
|
||||
_uReturnValue = (dsp_plugin->DSP_ReadMailBoxHigh(true) << 16) | dsp_plugin->DSP_ReadMailBoxLow(true);
|
||||
break;
|
||||
|
||||
// AI
|
||||
@ -563,8 +570,8 @@ void Write32(const u32 _iValue, const u32 _iAddress)
|
||||
{
|
||||
// DSP
|
||||
case DSP_MAIL_TO_DSP_HI:
|
||||
dsp_plugin->DSP_WriteMailboxHigh(true, _iValue >> 16);
|
||||
dsp_plugin->DSP_WriteMailboxLow(true, (u16)_iValue);
|
||||
dsp_plugin->DSP_WriteMailBoxHigh(true, _iValue >> 16);
|
||||
dsp_plugin->DSP_WriteMailBoxLow(true, (u16)_iValue);
|
||||
break;
|
||||
|
||||
// AI
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
class PointerWrap;
|
||||
class PluginDSP;
|
||||
|
||||
namespace DSP
|
||||
{
|
||||
@ -38,8 +39,11 @@ enum
|
||||
ARAM_MASK = 0x00FFFFFF,
|
||||
};
|
||||
|
||||
void Init();
|
||||
void Init(bool hle);
|
||||
void Shutdown();
|
||||
|
||||
PluginDSP *GetPlugin();
|
||||
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
void GenerateDSPInterrupt(DSPInterruptType _DSPInterruptType, bool _bSet = true);
|
||||
|
229
Source/Core/Core/Src/HW/DSPHLE/DSPHLE.cpp
Normal file
229
Source/Core/Core/Src/HW/DSPHLE/DSPHLE.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "DSPHLEGlobals.h" // Local
|
||||
|
||||
#include "ChunkFile.h"
|
||||
#include "IniFile.h"
|
||||
#include "HLEMixer.h"
|
||||
#include "DSPHandler.h"
|
||||
#include "Config.h"
|
||||
#include "Setup.h"
|
||||
#include "StringUtil.h"
|
||||
#include "LogManager.h"
|
||||
#include "IniFile.h"
|
||||
#include "DSPHLE.h"
|
||||
#include "../AudioInterface.h"
|
||||
|
||||
DSPHLE::DSPHLE() {
|
||||
g_InitMixer = false;
|
||||
soundStream = NULL;
|
||||
}
|
||||
|
||||
// Mailbox utility
|
||||
struct DSPState
|
||||
{
|
||||
u32 CPUMailbox;
|
||||
u32 DSPMailbox;
|
||||
|
||||
void Reset() {
|
||||
CPUMailbox = 0x00000000;
|
||||
DSPMailbox = 0x00000000;
|
||||
}
|
||||
|
||||
DSPState()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
};
|
||||
DSPState g_dspState;
|
||||
|
||||
void DSPHLE::Initialize(void *hWnd, bool bWii, bool bDSPThread)
|
||||
{
|
||||
this->hWnd = hWnd;
|
||||
this->bWii = bWii;
|
||||
|
||||
g_InitMixer = false;
|
||||
g_dspState.Reset();
|
||||
|
||||
CDSPHandler::CreateInstance(bWii);
|
||||
}
|
||||
|
||||
void DSPHLE::DSP_StopSoundStream()
|
||||
{
|
||||
}
|
||||
|
||||
void DSPHLE::Shutdown()
|
||||
{
|
||||
AudioCommon::ShutdownSoundStream();
|
||||
|
||||
// Delete the UCodes
|
||||
CDSPHandler::Destroy();
|
||||
}
|
||||
|
||||
void DSPHLE::DoState(PointerWrap &p)
|
||||
{
|
||||
p.Do(g_InitMixer);
|
||||
CDSPHandler::GetInstance().GetUCode()->DoState(p);
|
||||
}
|
||||
|
||||
void DSPHLE::EmuStateChange(PLUGIN_EMUSTATE newState)
|
||||
{
|
||||
DSP_ClearAudioBuffer((newState == PLUGIN_EMUSTATE_PLAY) ? false : true);
|
||||
}
|
||||
|
||||
// Mailbox fuctions
|
||||
unsigned short DSPHLE::DSP_ReadMailBoxHigh(bool _CPUMailbox)
|
||||
{
|
||||
if (_CPUMailbox)
|
||||
{
|
||||
return (g_dspState.CPUMailbox >> 16) & 0xFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CDSPHandler::GetInstance().AccessMailHandler().ReadDSPMailboxHigh();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short DSPHLE::DSP_ReadMailBoxLow(bool _CPUMailbox)
|
||||
{
|
||||
if (_CPUMailbox)
|
||||
{
|
||||
return g_dspState.CPUMailbox & 0xFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CDSPHandler::GetInstance().AccessMailHandler().ReadDSPMailboxLow();
|
||||
}
|
||||
}
|
||||
|
||||
void DSPHLE::DSP_WriteMailBoxHigh(bool _CPUMailbox, unsigned short _Value)
|
||||
{
|
||||
if (_CPUMailbox)
|
||||
{
|
||||
g_dspState.CPUMailbox = (g_dspState.CPUMailbox & 0xFFFF) | (_Value << 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("CPU can't write %08x to DSP mailbox", _Value);
|
||||
}
|
||||
}
|
||||
|
||||
void DSPHLE::DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short _Value)
|
||||
{
|
||||
if (_CPUMailbox)
|
||||
{
|
||||
g_dspState.CPUMailbox = (g_dspState.CPUMailbox & 0xFFFF0000) | _Value;
|
||||
CDSPHandler::GetInstance().SendMailToDSP(g_dspState.CPUMailbox);
|
||||
// Mail sent so clear MSB to show that it is progressed
|
||||
g_dspState.CPUMailbox &= 0x7FFFFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("CPU can't write %08x to DSP mailbox", _Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Other DSP fuctions
|
||||
unsigned short DSPHLE::DSP_WriteControlRegister(unsigned short _Value)
|
||||
{
|
||||
UDSPControl Temp(_Value);
|
||||
if (!g_InitMixer)
|
||||
{
|
||||
if (!Temp.DSPHalt && Temp.DSPInit)
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate, BackendSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
std::string frequency = ac_Config.sFrequency;
|
||||
if (frequency == "48,000 Hz")
|
||||
BackendSampleRate = 48000;
|
||||
else
|
||||
BackendSampleRate = 32000;
|
||||
|
||||
soundStream = AudioCommon::InitSoundStream(
|
||||
new HLEMixer(AISampleRate, DACSampleRate, BackendSampleRate), hWnd);
|
||||
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||
// Mixer is initialized
|
||||
g_InitMixer = true;
|
||||
}
|
||||
}
|
||||
return CDSPHandler::GetInstance().WriteControlRegister(_Value);
|
||||
}
|
||||
|
||||
unsigned short DSPHLE::DSP_ReadControlRegister()
|
||||
{
|
||||
return CDSPHandler::GetInstance().ReadControlRegister();
|
||||
}
|
||||
|
||||
void DSPHLE::DSP_Update(int cycles)
|
||||
{
|
||||
// This is called OFTEN - better not do anything expensive!
|
||||
// ~1/6th as many cycles as the period PPC-side.
|
||||
CDSPHandler::GetInstance().Update(cycles / 6);
|
||||
}
|
||||
|
||||
// The reason that we don't disable this entire
|
||||
// function when Other Audio is disabled is that then we can't turn it back on
|
||||
// again once the game has started.
|
||||
void DSPHLE::DSP_SendAIBuffer(unsigned int address, unsigned int num_samples)
|
||||
{
|
||||
if (!soundStream)
|
||||
return;
|
||||
|
||||
CMixer* pMixer = soundStream->GetMixer();
|
||||
|
||||
if (pMixer && address)
|
||||
{
|
||||
short* samples = (short*)HLEMemory_Get_Pointer(address);
|
||||
// Internal sample rate is always 32khz
|
||||
pMixer->PushSamples(samples, num_samples);
|
||||
|
||||
// FIXME: Write the audio to a file
|
||||
//if (log_ai)
|
||||
// g_wave_writer.AddStereoSamples(samples, 8);
|
||||
}
|
||||
|
||||
soundStream->Update();
|
||||
}
|
||||
|
||||
void DSPHLE::DSP_ClearAudioBuffer(bool mute)
|
||||
{
|
||||
if (soundStream)
|
||||
soundStream->Clear(mute);
|
||||
}
|
||||
|
||||
|
||||
#define HLE_CONFIG_FILE "DSP.ini"
|
||||
|
||||
void DSPHLE_LoadConfig()
|
||||
{
|
||||
// first load defaults
|
||||
IniFile file;
|
||||
file.Load((std::string(File::GetUserPath(D_CONFIG_IDX)) + HLE_CONFIG_FILE).c_str());
|
||||
ac_Config.Load(file);
|
||||
}
|
||||
|
||||
void DSPHLE_SaveConfig()
|
||||
{
|
||||
IniFile file;
|
||||
file.Load((std::string(File::GetUserPath(D_CONFIG_IDX)) + HLE_CONFIG_FILE).c_str());
|
||||
ac_Config.Set(file);
|
||||
file.Save((std::string(File::GetUserPath(D_CONFIG_IDX)) + HLE_CONFIG_FILE).c_str());
|
||||
}
|
86
Source/Core/Core/Src/HW/DSPHLE/DSPHLE.h
Normal file
86
Source/Core/Core/Src/HW/DSPHLE/DSPHLE.h
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSPHLE_H
|
||||
#define _DSPHLE_H
|
||||
|
||||
#include "SoundStream.h"
|
||||
#include "DSPHLEGlobals.h" // Local
|
||||
#include "../../PluginDSP.h"
|
||||
|
||||
class DSPHLE : public PluginDSP {
|
||||
public:
|
||||
DSPHLE();
|
||||
~DSPHLE();
|
||||
|
||||
virtual void Initialize(void *hWnd, bool bWii, bool bDSPThread);
|
||||
virtual void Shutdown();
|
||||
virtual bool IsLLE() { return false; }
|
||||
|
||||
/*
|
||||
GUI
|
||||
virtual void Config(void *_hwnd);
|
||||
virtual void About(void *_hwnd);
|
||||
virtual void *Debug(void *Parent, bool Show);
|
||||
*/
|
||||
|
||||
virtual void DoState(PointerWrap &p);
|
||||
virtual void EmuStateChange(PLUGIN_EMUSTATE newState);
|
||||
|
||||
virtual void DSP_WriteMailBoxHigh(bool _CPUMailbox, unsigned short);
|
||||
virtual void DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short);
|
||||
virtual unsigned short DSP_ReadMailBoxHigh(bool _CPUMailbox);
|
||||
virtual unsigned short DSP_ReadMailBoxLow(bool _CPUMailbox);
|
||||
virtual unsigned short DSP_ReadControlRegister();
|
||||
virtual unsigned short DSP_WriteControlRegister(unsigned short);
|
||||
virtual void DSP_SendAIBuffer(unsigned int address, unsigned int num_samples);
|
||||
virtual void DSP_Update(int cycles);
|
||||
virtual void DSP_StopSoundStream();
|
||||
virtual void DSP_ClearAudioBuffer(bool mute);
|
||||
|
||||
private:
|
||||
// Declarations and definitions
|
||||
void *hWnd;
|
||||
bool bWii;
|
||||
|
||||
bool g_InitMixer;
|
||||
SoundStream *soundStream;
|
||||
|
||||
// Mailbox utility
|
||||
struct DSPState
|
||||
{
|
||||
u32 CPUMailbox;
|
||||
u32 DSPMailbox;
|
||||
|
||||
void Reset() {
|
||||
CPUMailbox = 0x00000000;
|
||||
DSPMailbox = 0x00000000;
|
||||
}
|
||||
|
||||
DSPState()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
};
|
||||
DSPState g_dspState;
|
||||
};
|
||||
|
||||
// Hack to be deleted.
|
||||
void DSPHLE_LoadConfig();
|
||||
void DSPHLE_SaveConfig();
|
||||
|
||||
#endif // _DSPHLE_H
|
51
Source/Core/Core/Src/HW/DSPHLE/DSPHLEGlobals.h
Normal file
51
Source/Core/Core/Src/HW/DSPHLE/DSPHLEGlobals.h
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// TODO: Get rid of this file.
|
||||
|
||||
#ifndef _GLOBALS_H
|
||||
#define _GLOBALS_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "StringUtil.h"
|
||||
#include "../Memmap.h"
|
||||
|
||||
inline u8 HLEMemory_Read_U8(u32 _uAddress)
|
||||
{
|
||||
_uAddress &= Memory::RAM_MASK;
|
||||
return Memory::m_pRAM[_uAddress];
|
||||
}
|
||||
|
||||
inline u16 HLEMemory_Read_U16(u32 _uAddress)
|
||||
{
|
||||
_uAddress &= Memory::RAM_MASK;
|
||||
return Common::swap16(*(u16*)&Memory::m_pRAM[_uAddress]);
|
||||
}
|
||||
|
||||
inline u32 HLEMemory_Read_U32(u32 _uAddress)
|
||||
{
|
||||
_uAddress &= Memory::RAM_MASK;
|
||||
return Common::swap32(*(u32*)&Memory::m_pRAM[_uAddress]);
|
||||
}
|
||||
|
||||
inline void* HLEMemory_Get_Pointer(u32 _uAddress)
|
||||
{
|
||||
_uAddress &= Memory::RAM_MASK;
|
||||
return &Memory::m_pRAM[_uAddress];
|
||||
}
|
||||
|
||||
#endif
|
110
Source/Core/Core/Src/HW/DSPHLE/DSPHandler.cpp
Normal file
110
Source/Core/Core/Src/HW/DSPHLE/DSPHandler.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "DSPHandler.h"
|
||||
|
||||
CDSPHandler* CDSPHandler::m_pInstance = NULL;
|
||||
|
||||
CDSPHandler::CDSPHandler(bool bWii)
|
||||
: m_pUCode(NULL),
|
||||
m_lastUCode(NULL),
|
||||
m_bHalt(false),
|
||||
m_bAssertInt(false),
|
||||
m_bWii(bWii)
|
||||
{
|
||||
SetUCode(UCODE_ROM);
|
||||
m_DSPControl.DSPHalt = 1;
|
||||
m_DSPControl.DSPInit = 1;
|
||||
}
|
||||
|
||||
CDSPHandler::~CDSPHandler()
|
||||
{
|
||||
delete m_pUCode;
|
||||
m_pUCode = NULL;
|
||||
}
|
||||
|
||||
void CDSPHandler::Update(int cycles)
|
||||
{
|
||||
if (m_pUCode != NULL)
|
||||
m_pUCode->Update(cycles);
|
||||
}
|
||||
|
||||
unsigned short CDSPHandler::WriteControlRegister(unsigned short _Value)
|
||||
{
|
||||
UDSPControl Temp(_Value);
|
||||
if (Temp.DSPReset)
|
||||
{
|
||||
SetUCode(UCODE_ROM);
|
||||
Temp.DSPReset = 0;
|
||||
}
|
||||
if (Temp.DSPInit == 0)
|
||||
{
|
||||
// copy 128 byte from ARAM 0x000000 to IMEM
|
||||
SetUCode(UCODE_INIT_AUDIO_SYSTEM);
|
||||
Temp.DSPInitCode = 0;
|
||||
}
|
||||
|
||||
m_DSPControl.Hex = Temp.Hex;
|
||||
return m_DSPControl.Hex;
|
||||
}
|
||||
|
||||
unsigned short CDSPHandler::ReadControlRegister()
|
||||
{
|
||||
return m_DSPControl.Hex;
|
||||
}
|
||||
|
||||
void CDSPHandler::SendMailToDSP(u32 _uMail)
|
||||
{
|
||||
if (m_pUCode != NULL) {
|
||||
DEBUG_LOG(DSP_MAIL, "CPU writes 0x%08x", _uMail);
|
||||
m_pUCode->HandleMail(_uMail);
|
||||
}
|
||||
}
|
||||
|
||||
IUCode* CDSPHandler::GetUCode()
|
||||
{
|
||||
return m_pUCode;
|
||||
}
|
||||
|
||||
void CDSPHandler::SetUCode(u32 _crc)
|
||||
{
|
||||
delete m_pUCode;
|
||||
|
||||
m_pUCode = NULL;
|
||||
m_MailHandler.Clear();
|
||||
m_pUCode = UCodeFactory(_crc, m_MailHandler, m_bWii);
|
||||
}
|
||||
|
||||
// TODO do it better?
|
||||
// Assumes that every odd call to this func is by the persistent ucode.
|
||||
// Even callers are deleted.
|
||||
void CDSPHandler::SwapUCode(u32 _crc)
|
||||
{
|
||||
m_MailHandler.Clear();
|
||||
|
||||
if (m_lastUCode == NULL)
|
||||
{
|
||||
m_lastUCode = m_pUCode;
|
||||
m_pUCode = UCodeFactory(_crc, m_MailHandler, m_bWii);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete m_pUCode;
|
||||
m_pUCode = m_lastUCode;
|
||||
m_lastUCode = NULL;
|
||||
}
|
||||
}
|
76
Source/Core/Core/Src/HW/DSPHLE/DSPHandler.h
Normal file
76
Source/Core/Core/Src/HW/DSPHLE/DSPHandler.h
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSPHANDLER_H
|
||||
#define _DSPHANDLER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "AudioCommon.h"
|
||||
#include "MailHandler.h"
|
||||
#include "UCodes/UCodes.h"
|
||||
|
||||
class CDSPHandler : NonCopyable
|
||||
{
|
||||
public:
|
||||
void Update(int cycles);
|
||||
unsigned short WriteControlRegister(unsigned short _Value);
|
||||
unsigned short ReadControlRegister();
|
||||
void SendMailToDSP(u32 _uMail);
|
||||
IUCode* GetUCode();
|
||||
void SetUCode(u32 _crc);
|
||||
void SwapUCode(u32 _crc);
|
||||
|
||||
CMailHandler& AccessMailHandler() { return m_MailHandler; }
|
||||
|
||||
static CDSPHandler& GetInstance()
|
||||
{
|
||||
return *m_pInstance;
|
||||
}
|
||||
|
||||
static void Destroy()
|
||||
{
|
||||
delete m_pInstance;
|
||||
m_pInstance = NULL;
|
||||
}
|
||||
|
||||
static CDSPHandler& CreateInstance(bool bWii)
|
||||
{
|
||||
if (!m_pInstance)
|
||||
m_pInstance = new CDSPHandler(bWii);
|
||||
|
||||
return *m_pInstance;
|
||||
}
|
||||
|
||||
private:
|
||||
CDSPHandler(bool bWii);
|
||||
~CDSPHandler();
|
||||
|
||||
// singleton instance
|
||||
static CDSPHandler* m_pInstance;
|
||||
|
||||
IUCode* m_pUCode;
|
||||
IUCode* m_lastUCode;
|
||||
|
||||
UDSPControl m_DSPControl;
|
||||
CMailHandler m_MailHandler;
|
||||
|
||||
bool m_bHalt;
|
||||
bool m_bAssertInt;
|
||||
bool m_bWii;
|
||||
};
|
||||
|
||||
#endif
|
33
Source/Core/Core/Src/HW/DSPHLE/HLEMixer.cpp
Normal file
33
Source/Core/Core/Src/HW/DSPHLE/HLEMixer.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Config.h" // Local
|
||||
#include "DSPHLEGlobals.h"
|
||||
#include "DSPHandler.h"
|
||||
#include "HLEMixer.h"
|
||||
|
||||
void HLEMixer::Premix(short *samples, unsigned int numSamples)
|
||||
{
|
||||
// if this was called directly from the HLE
|
||||
if (IsHLEReady())
|
||||
{
|
||||
IUCode *pUCode = CDSPHandler::GetInstance().GetUCode();
|
||||
if (pUCode && samples)
|
||||
pUCode->MixAdd(samples, numSamples);
|
||||
}
|
||||
}
|
||||
|
17
Source/Core/Core/Src/HW/DSPHLE/HLEMixer.h
Normal file
17
Source/Core/Core/Src/HW/DSPHLE/HLEMixer.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef HLEMIXER_H
|
||||
#define HLEMIXER_H
|
||||
#include "AudioCommon.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
class HLEMixer : public CMixer
|
||||
{
|
||||
public:
|
||||
HLEMixer(unsigned int AISampleRate = 48000, unsigned int DACSampleRate = 48000, unsigned int BackendSampleRate = 32000)
|
||||
: CMixer(AISampleRate, DACSampleRate, BackendSampleRate) {};
|
||||
|
||||
virtual void Premix(short *samples, unsigned int numSamples);
|
||||
};
|
||||
|
||||
#endif // HLEMIXER_H
|
||||
|
||||
|
114
Source/Core/Core/Src/HW/DSPHLE/MailHandler.cpp
Normal file
114
Source/Core/Core/Src/HW/DSPHLE/MailHandler.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "MailHandler.h"
|
||||
|
||||
CMailHandler::CMailHandler()
|
||||
{}
|
||||
|
||||
CMailHandler::~CMailHandler()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void CMailHandler::PushMail(u32 _Mail)
|
||||
{
|
||||
m_Mails.push(_Mail);
|
||||
DEBUG_LOG(DSP_MAIL, "DSP writes 0x%08x", _Mail);
|
||||
}
|
||||
|
||||
u16 CMailHandler::ReadDSPMailboxHigh()
|
||||
{
|
||||
// check if we have a mail for the core
|
||||
if (!m_Mails.empty())
|
||||
{
|
||||
u16 result = (m_Mails.front() >> 16) & 0xFFFF;
|
||||
return result;
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
u16 CMailHandler::ReadDSPMailboxLow()
|
||||
{
|
||||
// check if we have a mail for the core
|
||||
if (!m_Mails.empty())
|
||||
{
|
||||
u16 result = m_Mails.front() & 0xFFFF;
|
||||
m_Mails.pop();
|
||||
return result;
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void CMailHandler::Clear()
|
||||
{
|
||||
while (!m_Mails.empty())
|
||||
m_Mails.pop();
|
||||
}
|
||||
|
||||
bool CMailHandler::IsEmpty()
|
||||
{
|
||||
return m_Mails.empty();
|
||||
}
|
||||
|
||||
void CMailHandler::Halt(bool _Halt)
|
||||
{
|
||||
if (_Halt)
|
||||
{
|
||||
Clear();
|
||||
m_Mails.push(0x80544348);
|
||||
}
|
||||
}
|
||||
|
||||
void CMailHandler::DoState(PointerWrap &p)
|
||||
{
|
||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
Clear();
|
||||
int sz = 0;
|
||||
p.Do(sz);
|
||||
for (int i = 0; i < sz; i++)
|
||||
{
|
||||
u32 mail = 0;
|
||||
p.Do(mail);
|
||||
m_Mails.push(mail);
|
||||
}
|
||||
}
|
||||
else // WRITE and MEASURE
|
||||
{
|
||||
std::queue<u32> temp;
|
||||
int sz = (int)m_Mails.size();
|
||||
p.Do(sz);
|
||||
for (int i = 0; i < sz; i++)
|
||||
{
|
||||
u32 value = m_Mails.front();
|
||||
m_Mails.pop();
|
||||
p.Do(value);
|
||||
temp.push(value);
|
||||
}
|
||||
if (!m_Mails.empty())
|
||||
PanicAlert("CMailHandler::DoState - WTF?");
|
||||
|
||||
// Restore queue.
|
||||
for (int i = 0; i < sz; i++)
|
||||
{
|
||||
u32 value = temp.front();
|
||||
temp.pop();
|
||||
m_Mails.push(value);
|
||||
}
|
||||
}
|
||||
}
|
58
Source/Core/Core/Src/HW/DSPHLE/MailHandler.h
Normal file
58
Source/Core/Core/Src/HW/DSPHLE/MailHandler.h
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _MAILHANDLER_H
|
||||
#define _MAILHANDLER_H
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "Common.h"
|
||||
#include "ChunkFile.h"
|
||||
|
||||
class CMailHandler
|
||||
{
|
||||
public:
|
||||
CMailHandler();
|
||||
~CMailHandler();
|
||||
|
||||
void PushMail(u32 _Mail);
|
||||
void Clear();
|
||||
void Halt(bool _Halt);
|
||||
void DoState(PointerWrap &p);
|
||||
bool IsEmpty();
|
||||
|
||||
u16 ReadDSPMailboxHigh();
|
||||
u16 ReadDSPMailboxLow();
|
||||
|
||||
u32 GetNextMail()
|
||||
{
|
||||
if (m_Mails.size())
|
||||
return m_Mails.front();
|
||||
else
|
||||
{
|
||||
// WARN_LOG(DSPHLE, "GetNextMail: No mails");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// mail handler
|
||||
std::queue<u32> m_Mails;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
458
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp
Normal file
458
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp
Normal file
@ -0,0 +1,458 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "FileUtil.h" // For IsDirectory()
|
||||
#include "StringUtil.h" // For StringFromFormat()
|
||||
#include <sstream>
|
||||
|
||||
#include "../DSPHLEGlobals.h"
|
||||
#include "Mixer.h"
|
||||
#include "../MailHandler.h"
|
||||
#include "../DSPHandler.h"
|
||||
#include "../../DSP.h"
|
||||
#include "UCodes.h"
|
||||
#include "UCode_AXStructs.h"
|
||||
#include "UCode_AX.h"
|
||||
#include "UCode_AX_Voice.h"
|
||||
|
||||
CUCode_AX::CUCode_AX(CMailHandler& _rMailHandler)
|
||||
: IUCode(_rMailHandler)
|
||||
, m_addressPBs(0xFFFFFFFF)
|
||||
{
|
||||
// we got loaded
|
||||
m_rMailHandler.PushMail(DSP_INIT);
|
||||
|
||||
templbuffer = new int[1024 * 1024];
|
||||
temprbuffer = new int[1024 * 1024];
|
||||
}
|
||||
|
||||
CUCode_AX::~CUCode_AX()
|
||||
{
|
||||
m_rMailHandler.Clear();
|
||||
delete [] templbuffer;
|
||||
delete [] temprbuffer;
|
||||
}
|
||||
|
||||
// Needs A LOT of love!
|
||||
static void ProcessUpdates(AXPB &PB)
|
||||
{
|
||||
// Make the updates we are told to do. When there are multiple updates for a block they
|
||||
// are placed in memory directly following updaddr. They are mostly for initial time
|
||||
// delays, sometimes for the FIR filter or channel volumes. We do all of them at once here.
|
||||
// If we get both an on and an off update we chose on. Perhaps that makes the RE1 music
|
||||
// work better.
|
||||
int numupd = PB.updates.num_updates[0]
|
||||
+ PB.updates.num_updates[1]
|
||||
+ PB.updates.num_updates[2]
|
||||
+ PB.updates.num_updates[3]
|
||||
+ PB.updates.num_updates[4];
|
||||
if (numupd > 64) numupd = 64; // prevent crazy values TODO: LOL WHAT
|
||||
const u32 updaddr = (u32)(PB.updates.data_hi << 16) | PB.updates.data_lo;
|
||||
int on = 0, off = 0;
|
||||
for (int j = 0; j < numupd; j++)
|
||||
{
|
||||
const u16 updpar = HLEMemory_Read_U16(updaddr + j*4);
|
||||
const u16 upddata = HLEMemory_Read_U16(updaddr + j*4 + 2);
|
||||
// some safety checks, I hope it's enough
|
||||
if (updaddr > 0x80000000 && updaddr < 0x817fffff
|
||||
&& updpar < 63 && updpar > 3 // updpar > 3 because we don't want to change
|
||||
// 0-3, those are important
|
||||
//&& (upd0 || upd1 || upd2 || upd3 || upd4) // We should use these in some way to I think
|
||||
// but I don't know how or when
|
||||
)
|
||||
{
|
||||
((u16*)&PB)[updpar] = upddata; // WTF ABOUNDS!
|
||||
}
|
||||
if (updpar == 7 && upddata != 0) on++;
|
||||
if (updpar == 7 && upddata == 0) off++;
|
||||
}
|
||||
// hack: if we get both an on and an off select on rather than off
|
||||
if (on > 0 && off > 0) PB.running = 1;
|
||||
}
|
||||
|
||||
static void VoiceHacks(AXPB &pb)
|
||||
{
|
||||
// get necessary values
|
||||
const u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo;
|
||||
const u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo;
|
||||
// const u32 updaddr = (u32)(pb.updates.data_hi << 16) | pb.updates.data_lo;
|
||||
// const u16 updpar = HLEMemory_Read_U16(updaddr);
|
||||
// const u16 upddata = HLEMemory_Read_U16(updaddr + 2);
|
||||
|
||||
// =======================================================================================
|
||||
/* Fix problems introduced with the SSBM fix. Sometimes when a music stream ended sampleEnd
|
||||
would end up outside of bounds while the block was still playing resulting in noise
|
||||
a strange noise. This should take care of that.
|
||||
*/
|
||||
if ((sampleEnd > (0x017fffff * 2) || loopPos > (0x017fffff * 2))) // ARAM bounds in nibbles
|
||||
{
|
||||
pb.running = 0;
|
||||
|
||||
// also reset all values if it makes any difference
|
||||
pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0;
|
||||
pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0;
|
||||
pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0;
|
||||
|
||||
pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0;
|
||||
pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0;
|
||||
|
||||
pb.audio_addr.looping = 0;
|
||||
pb.adpcm_loop_info.pred_scale = 0;
|
||||
pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
// the fact that no settings are reset (except running) after a SSBM type music stream or another
|
||||
looping block (for example in Battle Stadium DON) has ended could cause loud garbled sound to be
|
||||
played from one or more blocks. Perhaps it was in conjunction with the old sequenced music fix below,
|
||||
I'm not sure. This was an attempt to prevent that anyway by resetting all. But I'm not sure if this
|
||||
is needed anymore. Please try to play SSBM without it and see if it works anyway.
|
||||
*/
|
||||
if (
|
||||
// detect blocks that have recently been running that we should reset
|
||||
pb.running == 0 && pb.audio_addr.looping == 1
|
||||
//pb.running == 0 && pb.adpcm_loop_info.pred_scale
|
||||
|
||||
// this prevents us from ruining sequenced music blocks, may not be needed
|
||||
/*
|
||||
&& !(pb.updates.num_updates[0] || pb.updates.num_updates[1] || pb.updates.num_updates[2]
|
||||
|| pb.updates.num_updates[3] || pb.updates.num_updates[4])
|
||||
*/
|
||||
//&& !(updpar || upddata)
|
||||
|
||||
&& pb.mixer_control == 0 // only use this in SSBM
|
||||
)
|
||||
{
|
||||
// reset the detection values
|
||||
pb.audio_addr.looping = 0;
|
||||
pb.adpcm_loop_info.pred_scale = 0;
|
||||
pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0;
|
||||
|
||||
//pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0;
|
||||
//pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0;
|
||||
//pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0;
|
||||
|
||||
//pb.src.cur_addr_frac = 0; PBs[i].src.ratio_hi = 0; PBs[i].src.ratio_lo = 0;
|
||||
//pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_AX::MixAdd(short* _pBuffer, int _iSize)
|
||||
{
|
||||
if (_iSize > 1024 * 1024)
|
||||
_iSize = 1024 * 1024;
|
||||
|
||||
memset(templbuffer, 0, _iSize * sizeof(int));
|
||||
memset(temprbuffer, 0, _iSize * sizeof(int));
|
||||
|
||||
AXPB PB;
|
||||
|
||||
for (int x = 0; x < numPBaddr; x++)
|
||||
{
|
||||
//u32 blockAddr = m_addressPBs;
|
||||
u32 blockAddr = PBaddr[x];
|
||||
|
||||
if (!blockAddr)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_PBS; i++)
|
||||
{
|
||||
if (!ReadPB(blockAddr, PB))
|
||||
break;
|
||||
|
||||
ProcessUpdates(PB);
|
||||
VoiceHacks(PB);
|
||||
MixAddVoice(PB, templbuffer, temprbuffer, _iSize);
|
||||
|
||||
if (!WritePB(blockAddr, PB))
|
||||
break;
|
||||
|
||||
// next PB, or done
|
||||
blockAddr = (PB.next_pb_hi << 16) | PB.next_pb_lo;
|
||||
if (!blockAddr)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_pBuffer)
|
||||
{
|
||||
for (int i = 0; i < _iSize; i++)
|
||||
{
|
||||
// Clamp into 16-bit. Maybe we should add a volume compressor here.
|
||||
int left = templbuffer[i] + _pBuffer[0];
|
||||
int right = temprbuffer[i] + _pBuffer[1];
|
||||
if (left < -32767) left = -32767;
|
||||
if (left > 32767) left = 32767;
|
||||
if (right < -32767) right = -32767;
|
||||
if (right > 32767) right = 32767;
|
||||
*_pBuffer++ = left;
|
||||
*_pBuffer++ = right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Handle incoming mail
|
||||
void CUCode_AX::HandleMail(u32 _uMail)
|
||||
{
|
||||
if (m_UploadSetupInProgress)
|
||||
{
|
||||
PrepareBootUCode(_uMail);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST)
|
||||
{
|
||||
// We are expected to get a new CmdBlock
|
||||
DEBUG_LOG(DSPHLE, "GetNextCmdBlock (%ibytes)", (u16)_uMail);
|
||||
}
|
||||
else if (_uMail == 0xCDD10000) // Action 0 - AX_ResumeTask();
|
||||
{
|
||||
m_rMailHandler.PushMail(DSP_RESUME);
|
||||
}
|
||||
else if (_uMail == 0xCDD10001) // Action 1 - new ucode upload ( GC: BayBlade S.T.B,...)
|
||||
{
|
||||
DEBUG_LOG(DSPHLE,"DSP IROM - New Ucode!");
|
||||
// TODO find a better way to protect from HLEMixer?
|
||||
soundStream->GetMixer()->SetHLEReady(false);
|
||||
m_UploadSetupInProgress = true;
|
||||
}
|
||||
else if (_uMail == 0xCDD10002) // Action 2 - IROM_Reset(); ( GC: NFS Carbon, FF Crystal Chronicles,...)
|
||||
{
|
||||
DEBUG_LOG(DSPHLE,"DSP IROM - Reset!");
|
||||
CDSPHandler::GetInstance().SetUCode(UCODE_ROM);
|
||||
return;
|
||||
}
|
||||
else if (_uMail == 0xCDD10003) // Action 3 - AX_GetNextCmdBlock();
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(DSPHLE, " >>>> u32 MAIL : AXTask Mail (%08x)", _uMail);
|
||||
AXTask(_uMail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Update with DSP Interrupt
|
||||
void CUCode_AX::Update(int cycles)
|
||||
{
|
||||
if (NeedsResumeMail())
|
||||
{
|
||||
m_rMailHandler.PushMail(DSP_RESUME);
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
}
|
||||
// check if we have to send something
|
||||
else if (!m_rMailHandler.IsEmpty())
|
||||
{
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// AX seems to bootup one task only and waits for resume-callbacks
|
||||
// everytime the DSP has "spare time" it sends a resume-mail to the CPU
|
||||
// and the __DSPHandler calls a AX-Callback which generates a new AXFrame
|
||||
bool CUCode_AX::AXTask(u32& _uMail)
|
||||
{
|
||||
u32 uAddress = _uMail;
|
||||
DEBUG_LOG(DSPHLE, "Begin");
|
||||
DEBUG_LOG(DSPHLE, "=====================================================================");
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXTask - AXCommandList-Addr:", uAddress);
|
||||
|
||||
u32 Addr__AXStudio;
|
||||
u32 Addr__AXOutSBuffer;
|
||||
u32 Addr__AXOutSBuffer_1;
|
||||
u32 Addr__AXOutSBuffer_2;
|
||||
u32 Addr__A;
|
||||
u32 Addr__12;
|
||||
u32 Addr__4_1;
|
||||
u32 Addr__4_2;
|
||||
//u32 Addr__4_3;
|
||||
//u32 Addr__4_4;
|
||||
u32 Addr__5_1;
|
||||
u32 Addr__5_2;
|
||||
u32 Addr__6;
|
||||
u32 Addr__9;
|
||||
|
||||
bool bExecuteList = true;
|
||||
|
||||
numPBaddr = 0;
|
||||
|
||||
while (bExecuteList)
|
||||
{
|
||||
static int last_valid_command = 0;
|
||||
u16 iCommand = HLEMemory_Read_U16(uAddress);
|
||||
uAddress += 2;
|
||||
|
||||
switch (iCommand)
|
||||
{
|
||||
case AXLIST_STUDIOADDR: //00
|
||||
Addr__AXStudio = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST studio address: %08x", uAddress, Addr__AXStudio);
|
||||
break;
|
||||
|
||||
case 0x001: // 2byte x 10
|
||||
{
|
||||
u32 address = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
u16 param1 = HLEMemory_Read_U16(uAddress);
|
||||
uAddress += 2;
|
||||
u16 param2 = HLEMemory_Read_U16(uAddress);
|
||||
uAddress += 2;
|
||||
u16 param3 = HLEMemory_Read_U16(uAddress);
|
||||
uAddress += 2;
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST 1: %08x, %04x, %04x, %04x", uAddress, address, param1, param2, param3);
|
||||
}
|
||||
break;
|
||||
|
||||
//
|
||||
// Somewhere we should be getting a bitmask of AX_SYNC values
|
||||
// that tells us what has been updated
|
||||
// Dunno if important
|
||||
//
|
||||
case AXLIST_PBADDR: //02
|
||||
{
|
||||
PBaddr[numPBaddr] = HLEMemory_Read_U32(uAddress);
|
||||
numPBaddr++;
|
||||
|
||||
m_addressPBs = HLEMemory_Read_U32(uAddress); // left in for now
|
||||
uAddress += 4;
|
||||
soundStream->GetMixer()->SetHLEReady(true);
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST PB address: %08x", uAddress, m_addressPBs);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0003:
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST command 0x0003 ????", uAddress);
|
||||
break;
|
||||
|
||||
case 0x0004: // AUX?
|
||||
Addr__4_1 = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
Addr__4_2 = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST 4_1 4_2 addresses: %08x %08x", uAddress, Addr__4_1, Addr__4_2);
|
||||
break;
|
||||
|
||||
case 0x0005:
|
||||
Addr__5_1 = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
Addr__5_2 = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST 5_1 5_2 addresses: %08x %08x", uAddress, Addr__5_1, Addr__5_2);
|
||||
break;
|
||||
|
||||
case 0x0006:
|
||||
Addr__6 = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST 6 address: %08x", uAddress, Addr__6);
|
||||
break;
|
||||
|
||||
case AXLIST_SBUFFER:
|
||||
Addr__AXOutSBuffer = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST OutSBuffer address: %08x", uAddress, Addr__AXOutSBuffer);
|
||||
break;
|
||||
|
||||
case 0x0009:
|
||||
Addr__9 = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST 6 address: %08x", uAddress, Addr__9);
|
||||
break;
|
||||
|
||||
case AXLIST_COMPRESSORTABLE: // 0xa
|
||||
Addr__A = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST CompressorTable address: %08x", uAddress, Addr__A);
|
||||
break;
|
||||
|
||||
case 0x000e:
|
||||
Addr__AXOutSBuffer_1 = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
|
||||
// Addr__AXOutSBuffer_2 is the address in RAM that we are supposed to mix to.
|
||||
// Although we don't, currently.
|
||||
Addr__AXOutSBuffer_2 = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST sbuf2 addresses: %08x %08x", uAddress, Addr__AXOutSBuffer_1, Addr__AXOutSBuffer_2);
|
||||
break;
|
||||
|
||||
case AXLIST_END:
|
||||
bExecuteList = false;
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST end", uAddress);
|
||||
break;
|
||||
|
||||
case 0x0010: //Super Monkey Ball 2
|
||||
DEBUG_LOG(DSPHLE, "%08x : AXLIST 0x0010", uAddress);
|
||||
//should probably read/skip stuff here
|
||||
uAddress += 8;
|
||||
break;
|
||||
|
||||
case 0x0011:
|
||||
uAddress += 4;
|
||||
break;
|
||||
|
||||
case 0x0012:
|
||||
Addr__12 = HLEMemory_Read_U16(uAddress);
|
||||
uAddress += 2;
|
||||
break;
|
||||
|
||||
case 0x0013:
|
||||
uAddress += 6 * 4; // 6 Addresses.
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
static bool bFirst = true;
|
||||
if (bFirst)
|
||||
{
|
||||
char szTemp[2048];
|
||||
sprintf(szTemp, "Unknown AX-Command 0x%x (address: 0x%08x). Last valid: %02x\n",
|
||||
iCommand, uAddress - 2, last_valid_command);
|
||||
int num = -32;
|
||||
while (num < 64+32)
|
||||
{
|
||||
char szTemp2[128] = "";
|
||||
sprintf(szTemp2, "%s0x%04x\n", num == 0 ? ">>" : " ", HLEMemory_Read_U16(uAddress + num));
|
||||
strcat(szTemp, szTemp2);
|
||||
num += 2;
|
||||
}
|
||||
|
||||
PanicAlert("%s", szTemp);
|
||||
// bFirst = false;
|
||||
}
|
||||
|
||||
// unknown command so stop the execution of this TaskList
|
||||
bExecuteList = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (bExecuteList)
|
||||
last_valid_command = iCommand;
|
||||
}
|
||||
DEBUG_LOG(DSPHLE, "AXTask - done, send resume");
|
||||
DEBUG_LOG(DSPHLE, "=====================================================================");
|
||||
DEBUG_LOG(DSPHLE, "End");
|
||||
|
||||
m_rMailHandler.PushMail(DSP_YIELD);
|
||||
return true;
|
||||
}
|
63
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h
Normal file
63
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_AX
|
||||
#define _UCODE_AX
|
||||
|
||||
#include <iostream>
|
||||
#include "UCode_AXStructs.h"
|
||||
|
||||
enum
|
||||
{
|
||||
NUMBER_OF_PBS = 128
|
||||
};
|
||||
|
||||
class CUCode_AX : public IUCode
|
||||
{
|
||||
public:
|
||||
CUCode_AX(CMailHandler& _rMailHandler);
|
||||
virtual ~CUCode_AX();
|
||||
|
||||
void HandleMail(u32 _uMail);
|
||||
void MixAdd(short* _pBuffer, int _iSize);
|
||||
void Update(int cycles);
|
||||
|
||||
// PBs
|
||||
u8 numPBaddr;
|
||||
u32 PBaddr[8]; //2 needed for MP2
|
||||
u32 m_addressPBs;
|
||||
u32 _CRC;
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
MAIL_AX_ALIST = 0xBABE0000,
|
||||
AXLIST_STUDIOADDR = 0x0000,
|
||||
AXLIST_PBADDR = 0x0002,
|
||||
AXLIST_SBUFFER = 0x0007,
|
||||
AXLIST_COMPRESSORTABLE = 0x000A,
|
||||
AXLIST_END = 0x000F
|
||||
};
|
||||
|
||||
int *templbuffer;
|
||||
int *temprbuffer;
|
||||
|
||||
// ax task message handler
|
||||
bool AXTask(u32& _uMail);
|
||||
};
|
||||
|
||||
#endif // _UCODE_AX
|
365
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h
Normal file
365
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h
Normal file
@ -0,0 +1,365 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_AX_STRUCTS_H
|
||||
#define _UCODE_AX_STRUCTS_H
|
||||
|
||||
struct PBMixer
|
||||
{
|
||||
u16 left;
|
||||
u16 left_delta;
|
||||
u16 right;
|
||||
u16 right_delta;
|
||||
|
||||
u16 unknown3[8];
|
||||
u16 unknown4[6];
|
||||
};
|
||||
|
||||
struct PBMixerWii
|
||||
{
|
||||
// volume mixing values in .15, 0x8000 = ca. 1.0
|
||||
u16 left;
|
||||
u16 left_delta;
|
||||
u16 right;
|
||||
u16 right_delta;
|
||||
|
||||
u16 auxA_left;
|
||||
u16 auxA_left_delta;
|
||||
u16 auxA_right;
|
||||
u16 auxA_right_delta;
|
||||
|
||||
u16 auxB_left;
|
||||
u16 auxB_left_delta;
|
||||
u16 auxB_right;
|
||||
u16 auxB_right_delta;
|
||||
|
||||
// Note: the following elements usage changes a little in DPL2 mode
|
||||
// TODO: implement and comment it in the mixer
|
||||
u16 auxC_left;
|
||||
u16 auxC_left_delta;
|
||||
u16 auxC_right;
|
||||
u16 auxC_right_delta;
|
||||
|
||||
u16 surround;
|
||||
u16 surround_delta;
|
||||
u16 auxA_surround;
|
||||
u16 auxA_surround_delta;
|
||||
u16 auxB_surround;
|
||||
u16 auxB_surround_delta;
|
||||
u16 auxC_surround;
|
||||
u16 auxC_surround_delta;
|
||||
};
|
||||
|
||||
struct PBMixerWM
|
||||
{
|
||||
u16 main0;
|
||||
u16 main0_delta;
|
||||
u16 aux0;
|
||||
u16 aux0_delta;
|
||||
|
||||
u16 main1;
|
||||
u16 main1_delta;
|
||||
u16 aux1;
|
||||
u16 aux1_delta;
|
||||
|
||||
u16 main2;
|
||||
u16 main2_delta;
|
||||
u16 aux2;
|
||||
u16 aux2_delta;
|
||||
|
||||
u16 main3;
|
||||
u16 main3_delta;
|
||||
u16 aux3;
|
||||
u16 aux3_delta;
|
||||
};
|
||||
|
||||
struct PBInitialTimeDelay
|
||||
{
|
||||
u16 on;
|
||||
u16 addrMemHigh;
|
||||
u16 addrMemLow;
|
||||
u16 offsetLeft;
|
||||
u16 offsetRight;
|
||||
u16 targetLeft;
|
||||
u16 targetRight;
|
||||
};
|
||||
|
||||
// Update data - read these each 1ms subframe and use them!
|
||||
// It seems that to provide higher time precisions for MIDI events, some games
|
||||
// use this thing to update the parameter blocks per 1ms sub-block (a block is 5ms).
|
||||
// Using this data should fix games that are missing MIDI notes.
|
||||
struct PBUpdates
|
||||
{
|
||||
u16 num_updates[5];
|
||||
u16 data_hi; // These point to main RAM. Not sure about the structure of the data.
|
||||
u16 data_lo;
|
||||
};
|
||||
|
||||
// The DSP stores the final sample values for each voice after every frame of processing.
|
||||
// The values are then accumulated for all dropped voices, added to the next frame of audio,
|
||||
// and ramped down on a per-sample basis to provide a gentle "roll off."
|
||||
struct PBDpop
|
||||
{
|
||||
s16 unknown[9];
|
||||
};
|
||||
|
||||
struct PBDpopWii
|
||||
{
|
||||
s16 left;
|
||||
s16 auxA_left;
|
||||
s16 auxB_left;
|
||||
s16 auxC_left;
|
||||
|
||||
s16 right;
|
||||
s16 auxA_right;
|
||||
s16 auxB_right;
|
||||
s16 auxC_right;
|
||||
|
||||
s16 surround;
|
||||
s16 auxA_surround;
|
||||
s16 auxB_surround;
|
||||
s16 auxC_surround;
|
||||
};
|
||||
|
||||
struct PBDpopWM
|
||||
{
|
||||
s16 aMain0;
|
||||
s16 aMain1;
|
||||
s16 aMain2;
|
||||
s16 aMain3;
|
||||
|
||||
s16 aAux0;
|
||||
s16 aAux1;
|
||||
s16 aAux2;
|
||||
s16 aAux3;
|
||||
};
|
||||
|
||||
struct PBVolumeEnvelope
|
||||
{
|
||||
u16 cur_volume; // volume at start of frame
|
||||
s16 cur_volume_delta; // signed per sample delta (96 samples per frame)
|
||||
};
|
||||
|
||||
struct PBUnknown2
|
||||
{
|
||||
u16 unknown_reserved[3];
|
||||
};
|
||||
|
||||
struct PBAudioAddr
|
||||
{
|
||||
u16 looping;
|
||||
u16 sample_format;
|
||||
u16 loop_addr_hi; // Start of loop (this will point to a shared "zero" buffer if one-shot mode is active)
|
||||
u16 loop_addr_lo;
|
||||
u16 end_addr_hi; // End of sample (and loop), inclusive
|
||||
u16 end_addr_lo;
|
||||
u16 cur_addr_hi;
|
||||
u16 cur_addr_lo;
|
||||
};
|
||||
|
||||
struct PBADPCMInfo
|
||||
{
|
||||
s16 coefs[16];
|
||||
u16 gain;
|
||||
u16 pred_scale;
|
||||
s16 yn1;
|
||||
s16 yn2;
|
||||
};
|
||||
|
||||
struct PBSampleRateConverter
|
||||
{
|
||||
// ratio = (f32)ratio * 0x10000;
|
||||
// valid range is 1/512 to 4.0000
|
||||
u16 ratio_hi; // integer part of sampling ratio
|
||||
u16 ratio_lo; // fraction part of sampling ratio
|
||||
u16 cur_addr_frac;
|
||||
u16 last_samples[4];
|
||||
};
|
||||
|
||||
struct PBSampleRateConverterWM
|
||||
{
|
||||
u16 currentAddressFrac;
|
||||
u16 last_samples[4];
|
||||
};
|
||||
|
||||
struct PBADPCMLoopInfo
|
||||
{
|
||||
u16 pred_scale;
|
||||
u16 yn1;
|
||||
u16 yn2;
|
||||
};
|
||||
|
||||
struct AXPB
|
||||
{
|
||||
u16 next_pb_hi;
|
||||
u16 next_pb_lo;
|
||||
u16 this_pb_hi;
|
||||
u16 this_pb_lo;
|
||||
|
||||
u16 src_type; // Type of sample rate converter (none, ?, linear)
|
||||
u16 coef_select;
|
||||
u16 mixer_control;
|
||||
|
||||
u16 running; // 1=RUN 0=STOP
|
||||
u16 is_stream; // 1 = stream, 0 = one shot
|
||||
|
||||
PBMixer mixer;
|
||||
PBInitialTimeDelay initial_time_delay;
|
||||
PBUpdates updates;
|
||||
PBDpop dpop;
|
||||
PBVolumeEnvelope vol_env;
|
||||
PBUnknown2 unknown3;
|
||||
PBAudioAddr audio_addr;
|
||||
PBADPCMInfo adpcm;
|
||||
PBSampleRateConverter src;
|
||||
PBADPCMLoopInfo adpcm_loop_info;
|
||||
u16 unknown_maybe_padding[3];
|
||||
};
|
||||
|
||||
struct PBLowPassFilter
|
||||
{
|
||||
u16 enabled;
|
||||
u16 yn1;
|
||||
u16 a0;
|
||||
u16 b0;
|
||||
};
|
||||
|
||||
struct PBBiquadFilter
|
||||
{
|
||||
|
||||
u16 on; // on = 2, off = 0
|
||||
u16 xn1; // History data
|
||||
u16 xn2;
|
||||
u16 yn1;
|
||||
u16 yn2;
|
||||
u16 b0; // Filter coefficients
|
||||
u16 b1;
|
||||
u16 b2;
|
||||
u16 a1;
|
||||
u16 a2;
|
||||
|
||||
};
|
||||
|
||||
union PBInfImpulseResponseWM
|
||||
{
|
||||
PBLowPassFilter lpf;
|
||||
PBBiquadFilter biquad;
|
||||
};
|
||||
|
||||
struct AXPBWii
|
||||
{
|
||||
u16 next_pb_hi;
|
||||
u16 next_pb_lo;
|
||||
u16 this_pb_hi;
|
||||
u16 this_pb_lo;
|
||||
|
||||
u16 src_type; // Type of sample rate converter (none, 4-tap, linear)
|
||||
u16 coef_select; // coef for the 4-tap src
|
||||
u32 mixer_control;
|
||||
|
||||
u16 running; // 1=RUN 0=STOP
|
||||
u16 is_stream; // 1 = stream, 0 = one shot
|
||||
|
||||
PBMixerWii mixer;
|
||||
PBInitialTimeDelay initial_time_delay;
|
||||
PBDpopWii dpop;
|
||||
PBVolumeEnvelope vol_env;
|
||||
PBAudioAddr audio_addr;
|
||||
PBADPCMInfo adpcm;
|
||||
PBSampleRateConverter src;
|
||||
PBADPCMLoopInfo adpcm_loop_info;
|
||||
PBLowPassFilter lpf;
|
||||
PBBiquadFilter biquad;
|
||||
|
||||
// WIIMOTE :D
|
||||
u16 remote;
|
||||
u16 remote_mixer_control;
|
||||
|
||||
PBMixerWM remote_mixer;
|
||||
PBDpopWM remote_dpop;
|
||||
PBSampleRateConverterWM remote_src;
|
||||
PBInfImpulseResponseWM remote_iir;
|
||||
|
||||
u16 pad[12]; // align us, captain! (32B)
|
||||
};
|
||||
|
||||
// Seems like nintendo used an early version of AXWii and forgot to remove the update functionality ;p
|
||||
struct PBUpdatesWiiSports
|
||||
{
|
||||
u16 num_updates[3];
|
||||
u16 data_hi;
|
||||
u16 data_lo;
|
||||
};
|
||||
|
||||
struct AXPBWiiSports
|
||||
{
|
||||
u16 next_pb_hi;
|
||||
u16 next_pb_lo;
|
||||
u16 this_pb_hi;
|
||||
u16 this_pb_lo;
|
||||
|
||||
u16 src_type; // Type of sample rate converter (none, 4-tap, linear)
|
||||
u16 coef_select; // coef for the 4-tap src
|
||||
u32 mixer_control;
|
||||
|
||||
u16 running; // 1=RUN 0=STOP
|
||||
u16 is_stream; // 1 = stream, 0 = one shot
|
||||
|
||||
PBMixerWii mixer;
|
||||
PBInitialTimeDelay initial_time_delay;
|
||||
PBUpdatesWiiSports updates;
|
||||
PBDpopWii dpop;
|
||||
PBVolumeEnvelope vol_env;
|
||||
PBAudioAddr audio_addr;
|
||||
PBADPCMInfo adpcm;
|
||||
PBSampleRateConverter src;
|
||||
PBADPCMLoopInfo adpcm_loop_info;
|
||||
PBLowPassFilter lpf;
|
||||
PBBiquadFilter biquad;
|
||||
|
||||
// WIIMOTE :D
|
||||
u16 remote;
|
||||
u16 remote_mixer_control;
|
||||
|
||||
PBMixerWM remote_mixer;
|
||||
PBDpopWM remote_dpop;
|
||||
PBSampleRateConverterWM remote_src;
|
||||
PBInfImpulseResponseWM remote_iir;
|
||||
|
||||
u16 pad[7]; // align us, captain! (32B)
|
||||
};
|
||||
|
||||
// TODO: All these enums have changed a lot for wii
|
||||
enum {
|
||||
AUDIOFORMAT_ADPCM = 0,
|
||||
AUDIOFORMAT_PCM8 = 0x19,
|
||||
AUDIOFORMAT_PCM16 = 0xA,
|
||||
};
|
||||
|
||||
enum {
|
||||
SRCTYPE_LINEAR = 1,
|
||||
SRCTYPE_NEAREST = 2,
|
||||
MIXCONTROL_RAMPING = 8,
|
||||
};
|
||||
|
||||
// Both may be used at once
|
||||
enum {
|
||||
FILTER_LOWPASS = 1,
|
||||
FILTER_BIQUAD = 2,
|
||||
};
|
||||
|
||||
#endif // _UCODE_AX_STRUCTS_H
|
258
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp
Normal file
258
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "StringUtil.h"
|
||||
|
||||
#include "../MailHandler.h"
|
||||
#include "Mixer.h"
|
||||
#include "../DSPHandler.h"
|
||||
|
||||
#include "UCodes.h"
|
||||
#include "UCode_AXStructs.h"
|
||||
#include "UCode_AX.h" // for some functions in CUCode_AX
|
||||
#include "UCode_AXWii.h"
|
||||
#include "UCode_AX_Voice.h"
|
||||
|
||||
|
||||
CUCode_AXWii::CUCode_AXWii(CMailHandler& _rMailHandler, u32 l_CRC)
|
||||
: IUCode(_rMailHandler)
|
||||
, m_addressPBs(0xFFFFFFFF)
|
||||
, _CRC(l_CRC)
|
||||
{
|
||||
// we got loaded
|
||||
m_rMailHandler.PushMail(DSP_INIT);
|
||||
|
||||
templbuffer = new int[1024 * 1024];
|
||||
temprbuffer = new int[1024 * 1024];
|
||||
|
||||
wiisportsHack = _CRC == 0xfa450138;
|
||||
}
|
||||
|
||||
CUCode_AXWii::~CUCode_AXWii()
|
||||
{
|
||||
m_rMailHandler.Clear();
|
||||
delete [] templbuffer;
|
||||
delete [] temprbuffer;
|
||||
}
|
||||
|
||||
void CUCode_AXWii::HandleMail(u32 _uMail)
|
||||
{
|
||||
if (m_UploadSetupInProgress)
|
||||
{
|
||||
PrepareBootUCode(_uMail);
|
||||
return;
|
||||
}
|
||||
else if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST)
|
||||
{
|
||||
// We are expected to get a new CmdBlock
|
||||
DEBUG_LOG(DSPHLE, "GetNextCmdBlock (%ibytes)", (u16)_uMail);
|
||||
}
|
||||
else switch(_uMail)
|
||||
{
|
||||
case 0xCDD10000: // Action 0 - AX_ResumeTask()
|
||||
m_rMailHandler.PushMail(DSP_RESUME);
|
||||
break;
|
||||
|
||||
case 0xCDD10001: // Action 1 - new ucode upload
|
||||
DEBUG_LOG(DSPHLE,"DSP IROM - New Ucode!");
|
||||
// TODO find a better way to protect from HLEMixer?
|
||||
soundStream->GetMixer()->SetHLEReady(false);
|
||||
m_UploadSetupInProgress = true;
|
||||
break;
|
||||
|
||||
case 0xCDD10002: // Action 2 - IROM_Reset(); ( WII: De Blob, Cursed Mountain,...)
|
||||
DEBUG_LOG(DSPHLE,"DSP IROM - Reset!");
|
||||
CDSPHandler::GetInstance().SetUCode(UCODE_ROM);
|
||||
return;
|
||||
|
||||
case 0xCDD10003: // Action 3 - AX_GetNextCmdBlock()
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_LOG(DSPHLE, " >>>> u32 MAIL : AXTask Mail (%08x)", _uMail);
|
||||
AXTask(_uMail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize)
|
||||
{
|
||||
AXPBWii PB;
|
||||
|
||||
if (_iSize > 1024 * 1024)
|
||||
_iSize = 1024 * 1024;
|
||||
|
||||
memset(templbuffer, 0, _iSize * sizeof(int));
|
||||
memset(temprbuffer, 0, _iSize * sizeof(int));
|
||||
|
||||
u32 blockAddr = m_addressPBs;
|
||||
if (!blockAddr)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_PBS; i++)
|
||||
{
|
||||
if (!ReadPB(blockAddr, PB))
|
||||
break;
|
||||
|
||||
if (wiisportsHack)
|
||||
MixAddVoice(*(AXPBWiiSports*)&PB, templbuffer, temprbuffer, _iSize);
|
||||
else
|
||||
MixAddVoice(PB, templbuffer, temprbuffer, _iSize);
|
||||
|
||||
if (!WritePB(blockAddr, PB))
|
||||
break;
|
||||
|
||||
// next PB, or done
|
||||
blockAddr = (PB.next_pb_hi << 16) | PB.next_pb_lo;
|
||||
if (!blockAddr)
|
||||
break;
|
||||
}
|
||||
|
||||
// We write the sound to _pBuffer
|
||||
if (_pBuffer)
|
||||
{
|
||||
for (int i = 0; i < _iSize; i++)
|
||||
{
|
||||
// Clamp into 16-bit. Maybe we should add a volume compressor here.
|
||||
int left = templbuffer[i] + _pBuffer[0];
|
||||
int right = temprbuffer[i] + _pBuffer[1];
|
||||
if (left < -32767) left = -32767;
|
||||
else if (left > 32767) left = 32767;
|
||||
if (right < -32767) right = -32767;
|
||||
else if (right > 32767) right = 32767;
|
||||
*_pBuffer++ = left;
|
||||
*_pBuffer++ = right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CUCode_AXWii::Update(int cycles)
|
||||
{
|
||||
if (NeedsResumeMail())
|
||||
{
|
||||
m_rMailHandler.PushMail(DSP_RESUME);
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
}
|
||||
// check if we have to send something
|
||||
else if (!m_rMailHandler.IsEmpty())
|
||||
{
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
}
|
||||
}
|
||||
|
||||
// AX seems to bootup one task only and waits for resume-callbacks
|
||||
// everytime the DSP has "spare time" it sends a resume-mail to the CPU
|
||||
// and the __DSPHandler calls a AX-Callback which generates a new AXFrame
|
||||
bool CUCode_AXWii::AXTask(u32& _uMail)
|
||||
{
|
||||
u32 uAddress = _uMail;
|
||||
u32 Addr__AXStudio;
|
||||
u32 Addr__AXOutSBuffer;
|
||||
bool bExecuteList = true;
|
||||
|
||||
/*
|
||||
for (int i=0;i<64;i++) {
|
||||
NOTICE_LOG(DSPHLE,"%x - %08x",uAddress+(i*4),HLEMemory_Read_U32(uAddress+(i*4)));
|
||||
}
|
||||
*/
|
||||
|
||||
while (bExecuteList)
|
||||
{
|
||||
u16 iCommand = HLEMemory_Read_U16(uAddress);
|
||||
uAddress += 2;
|
||||
//NOTICE_LOG(DSPHLE,"AXWII - AXLIST CMD %X",iCommand);
|
||||
|
||||
switch (iCommand)
|
||||
{
|
||||
case 0x0000:
|
||||
Addr__AXStudio = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 4;
|
||||
break;
|
||||
|
||||
case 0x0001:
|
||||
uAddress += 4;
|
||||
break;
|
||||
|
||||
case 0x0003:
|
||||
uAddress += 4;
|
||||
break;
|
||||
|
||||
case 0x0004:
|
||||
// PBs are here now
|
||||
m_addressPBs = HLEMemory_Read_U32(uAddress);
|
||||
soundStream->GetMixer()->SetHLEReady(true);
|
||||
// soundStream->Update();
|
||||
uAddress += 4;
|
||||
break;
|
||||
|
||||
case 0x0005:
|
||||
if (!wiisportsHack)
|
||||
uAddress += 10;
|
||||
break;
|
||||
|
||||
case 0x0006:
|
||||
uAddress += 10;
|
||||
break;
|
||||
|
||||
case 0x0007: // AXLIST_SBUFFER
|
||||
Addr__AXOutSBuffer = HLEMemory_Read_U32(uAddress);
|
||||
uAddress += 10;
|
||||
break;
|
||||
|
||||
case 0x0008:
|
||||
uAddress += 26;
|
||||
break;
|
||||
|
||||
case 0x000a:
|
||||
uAddress += wiisportsHack ? 4 : 8; // AXLIST_COMPRESSORTABLE
|
||||
break;
|
||||
|
||||
case 0x000b:
|
||||
uAddress += wiisportsHack ? 2 : 10;
|
||||
break;
|
||||
|
||||
case 0x000c:
|
||||
uAddress += wiisportsHack ? 8 : 10;
|
||||
break;
|
||||
|
||||
case 0x000d:
|
||||
uAddress += 16;
|
||||
break;
|
||||
|
||||
case 0x000e:
|
||||
if (wiisportsHack)
|
||||
uAddress += 16;
|
||||
else
|
||||
bExecuteList = false;
|
||||
break;
|
||||
|
||||
case 0x000f: // only for Wii Sports uCode
|
||||
bExecuteList = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
INFO_LOG(DSPHLE,"DSPHLE - AXwii - AXLIST - Unknown CMD: %x",iCommand);
|
||||
// unknown command so stop the execution of this TaskList
|
||||
bExecuteList = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_rMailHandler.PushMail(DSP_YIELD); //its here in case there is a CMD fuckup
|
||||
return true;
|
||||
}
|
55
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h
Normal file
55
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_AXWII
|
||||
#define _UCODE_AXWII
|
||||
|
||||
#include "UCode_AXStructs.h"
|
||||
|
||||
#define NUMBER_OF_PBS 128
|
||||
|
||||
class CUCode_AXWii : public IUCode
|
||||
{
|
||||
public:
|
||||
CUCode_AXWii(CMailHandler& _rMailHandler, u32 _CRC);
|
||||
virtual ~CUCode_AXWii();
|
||||
|
||||
void HandleMail(u32 _uMail);
|
||||
void MixAdd(short* _pBuffer, int _iSize);
|
||||
void Update(int cycles);
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
MAIL_AX_ALIST = 0xBABE0000,
|
||||
};
|
||||
|
||||
// PBs
|
||||
u32 m_addressPBs;
|
||||
u32 _CRC;
|
||||
|
||||
bool wiisportsHack;
|
||||
|
||||
int *templbuffer;
|
||||
int *temprbuffer;
|
||||
|
||||
// ax task message handler
|
||||
bool AXTask(u32& _uMail);
|
||||
void SendMail(u32 _uMail);
|
||||
};
|
||||
|
||||
#endif // _UCODE_AXWII
|
93
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_ADPCM.h
Normal file
93
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_ADPCM.h
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_AX_ADPCM_H
|
||||
#define _UCODE_AX_ADPCM_H
|
||||
|
||||
#include "../DSPHLEGlobals.h"
|
||||
#include "../../DSP.h"
|
||||
|
||||
inline s16 ADPCM_Step(PBADPCMInfo &adpcm, u32& samplePos, u32 newSamplePos, u16 frac)
|
||||
{
|
||||
while (samplePos < newSamplePos)
|
||||
{
|
||||
if ((samplePos & 15) == 0)
|
||||
{
|
||||
adpcm.pred_scale = DSP::ReadARAM((samplePos & ~15) >> 1);
|
||||
samplePos += 2;
|
||||
newSamplePos += 2;
|
||||
}
|
||||
|
||||
int scale = 1 << (adpcm.pred_scale & 0xF);
|
||||
int coef_idx = (adpcm.pred_scale >> 4) & 7;
|
||||
|
||||
s32 coef1 = adpcm.coefs[coef_idx * 2 + 0];
|
||||
s32 coef2 = adpcm.coefs[coef_idx * 2 + 1];
|
||||
|
||||
int temp = (samplePos & 1) ?
|
||||
(DSP::ReadARAM(samplePos >> 1) & 0xF) :
|
||||
(DSP::ReadARAM(samplePos >> 1) >> 4);
|
||||
|
||||
if (temp >= 8)
|
||||
temp -= 16;
|
||||
|
||||
// 0x400 = 0.5 in 11-bit fixed point
|
||||
int val = (scale * temp) + ((0x400 + coef1 * adpcm.yn1 + coef2 * adpcm.yn2) >> 11);
|
||||
|
||||
if (val > 0x7FFF)
|
||||
val = 0x7FFF;
|
||||
else if (val < -0x7FFF)
|
||||
val = -0x7FFF;
|
||||
|
||||
adpcm.yn2 = adpcm.yn1;
|
||||
adpcm.yn1 = val;
|
||||
|
||||
samplePos++;
|
||||
}
|
||||
|
||||
return adpcm.yn1;
|
||||
}
|
||||
|
||||
// TODO: WTF is going on here?!?
|
||||
// Volume control (ramping)
|
||||
inline u16 ADPCM_Vol(u16 vol, u16 delta)
|
||||
{
|
||||
int x = vol;
|
||||
if (delta && delta < 0x5000)
|
||||
x += delta * 20 * 8; // unsure what the right step is
|
||||
//x += 1 * 20 * 8;
|
||||
else if (delta && delta > 0x5000)
|
||||
//x -= (0x10000 - delta); // this is to small, it's often 1
|
||||
x -= (0x10000 - delta) * 20 * 16; // if this was 20 * 8 the sounds in Fire Emblem and Paper Mario
|
||||
// did not have time to go to zero before the were closed
|
||||
//x -= 1 * 20 * 16;
|
||||
|
||||
// make lower limits
|
||||
if (x < 0) x = 0;
|
||||
//if (pb.mixer_control < 1000 && x < pb.mixer_control) x = pb.mixer_control; // does this make
|
||||
// any sense?
|
||||
|
||||
// make upper limits
|
||||
//if (mixer_control > 1000 && x > mixer_control) x = mixer_control; // maybe mixer_control also
|
||||
// has a volume target?
|
||||
//if (x >= 0x7fff) x = 0x7fff; // this seems a little high
|
||||
//if (x >= 0x4e20) x = 0x4e20; // add a definitive limit at 20 000
|
||||
if (x >= 0x8000) x = 0x8000; // clamp to 32768;
|
||||
return x; // update volume
|
||||
}
|
||||
|
||||
#endif // _UCODE_AX_ADPCM_H
|
269
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h
Normal file
269
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h
Normal file
@ -0,0 +1,269 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_AX_VOICE_H
|
||||
#define _UCODE_AX_VOICE_H
|
||||
|
||||
#include "UCodes.h"
|
||||
#include "UCode_AX_ADPCM.h"
|
||||
#include "UCode_AX.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
// MRAM -> ARAM for GC
|
||||
inline bool ReadPB(u32 addr, AXPB &PB)
|
||||
{
|
||||
const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr);
|
||||
if (PB_in_mram == NULL)
|
||||
return false;
|
||||
u16* PB_in_aram = (u16*)&PB;
|
||||
|
||||
for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++)
|
||||
{
|
||||
PB_in_aram[p] = Common::swap16(PB_in_mram[p]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// MRAM -> ARAM for Wii
|
||||
inline bool ReadPB(u32 addr, AXPBWii &PB)
|
||||
{
|
||||
const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr);
|
||||
if (PB_in_mram == NULL)
|
||||
return false;
|
||||
u16* PB_in_aram = (u16*)&PB;
|
||||
|
||||
// preswap the mixer_control
|
||||
PB.mixer_control = ((u32)PB_in_mram[7] << 16) | ((u32)PB_in_mram[6] >> 16);
|
||||
|
||||
for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++)
|
||||
{
|
||||
PB_in_aram[p] = Common::swap16(PB_in_mram[p]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ARAM -> MRAM for GC
|
||||
inline bool WritePB(u32 addr, AXPB &PB)
|
||||
{
|
||||
const u16* PB_in_aram = (const u16*)&PB;
|
||||
u16* PB_in_mram = (u16*)Memory::GetPointer(addr);
|
||||
if (PB_in_mram == NULL)
|
||||
return false;
|
||||
|
||||
for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++)
|
||||
{
|
||||
PB_in_mram[p] = Common::swap16(PB_in_aram[p]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ARAM -> MRAM for Wii
|
||||
inline bool WritePB(u32 addr, AXPBWii &PB)
|
||||
{
|
||||
const u16* PB_in_aram = (const u16*)&PB;
|
||||
u16* PB_in_mram = (u16*)Memory::GetPointer(addr);
|
||||
if (PB_in_mram == NULL)
|
||||
return false;
|
||||
|
||||
// preswap the mixer_control
|
||||
*(u32*)&PB_in_mram[6] = (PB.mixer_control << 16) | (PB.mixer_control >> 16);
|
||||
|
||||
for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++)
|
||||
{
|
||||
PB_in_mram[p] = Common::swap16(PB_in_aram[p]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TODO: fix handling of gc/wii PB differences
|
||||
// TODO: generally fix up the mess - looks crazy and kinda wrong
|
||||
template<class ParamBlockType>
|
||||
inline void MixAddVoice(ParamBlockType &pb,
|
||||
int *templbuffer, int *temprbuffer,
|
||||
int _iSize)
|
||||
{
|
||||
if (pb.running)
|
||||
{
|
||||
const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo)
|
||||
* /*ratioFactor:*/(32000.0f / (float)soundStream->GetMixer()->GetSampleRate()));
|
||||
u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo;
|
||||
u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo;
|
||||
|
||||
u32 samplePos = (pb.audio_addr.cur_addr_hi << 16) | pb.audio_addr.cur_addr_lo;
|
||||
u32 frac = pb.src.cur_addr_frac;
|
||||
|
||||
// =======================================================================================
|
||||
// Handle No-SRC streams - No src streams have pb.src_type == 2 and have pb.src.ratio_hi = 0
|
||||
// and pb.src.ratio_lo = 0. We handle that by setting the sampling ratio integer to 1. This
|
||||
// makes samplePos update in the correct way. I'm unsure how we are actually supposed to
|
||||
// detect that this setting. Updates did not fix this automatically.
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Stream settings
|
||||
// src_type = 2 (most other games have src_type = 0)
|
||||
// Affected games:
|
||||
// Baten Kaitos - Eternal Wings (2003)
|
||||
// Baten Kaitos - Origins (2006)?
|
||||
// Soul Calibur 2: The movie music use src_type 2 but it needs no adjustment, perhaps
|
||||
// the sound format plays in to, Baten use ADPCM, SC2 use PCM16
|
||||
//if (pb.src_type == 2 && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0))
|
||||
if (pb.running && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0))
|
||||
{
|
||||
pb.src.ratio_hi = 1;
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// Games that use looping to play non-looping music streams - SSBM has info in all
|
||||
// pb.adpcm_loop_info parameters but has pb.audio_addr.looping = 0. If we treat these streams
|
||||
// like any other looping streams the music works. I'm unsure how we are actually supposed to
|
||||
// detect that these kinds of blocks should be looping. It seems like pb.mixer_control == 0 may
|
||||
// identify these types of blocks. Updates did not write any looping values.
|
||||
if (
|
||||
(pb.adpcm_loop_info.pred_scale || pb.adpcm_loop_info.yn1 || pb.adpcm_loop_info.yn2)
|
||||
&& pb.mixer_control == 0 && pb.adpcm_loop_info.pred_scale <= 0x7F
|
||||
)
|
||||
{
|
||||
pb.audio_addr.looping = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Top Spin 3 Wii
|
||||
if (pb.audio_addr.sample_format > 25)
|
||||
pb.audio_addr.sample_format = 0;
|
||||
|
||||
// =======================================================================================
|
||||
// Walk through _iSize. _iSize = numSamples. If the game goes slow _iSize will be higher to
|
||||
// compensate for that. _iSize can be as low as 100 or as high as 2000 some cases.
|
||||
for (int s = 0; s < _iSize; s++)
|
||||
{
|
||||
int sample = 0;
|
||||
u32 oldFrac = frac;
|
||||
frac += ratio;
|
||||
u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac
|
||||
|
||||
// =======================================================================================
|
||||
// Process sample format
|
||||
switch (pb.audio_addr.sample_format)
|
||||
{
|
||||
case AUDIOFORMAT_PCM8:
|
||||
pb.adpcm.yn2 = ((s8)DSP::ReadARAM(samplePos)) << 8; //current sample
|
||||
pb.adpcm.yn1 = ((s8)DSP::ReadARAM(samplePos + 1)) << 8; //next sample
|
||||
|
||||
if (pb.src_type == SRCTYPE_NEAREST)
|
||||
sample = pb.adpcm.yn2;
|
||||
else // linear interpolation
|
||||
sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16;
|
||||
|
||||
samplePos = newSamplePos;
|
||||
break;
|
||||
|
||||
case AUDIOFORMAT_PCM16:
|
||||
pb.adpcm.yn2 = (s16)(u16)((DSP::ReadARAM(samplePos * 2) << 8) | (DSP::ReadARAM((samplePos * 2 + 1)))); //current sample
|
||||
pb.adpcm.yn1 = (s16)(u16)((DSP::ReadARAM((samplePos + 1) * 2) << 8) | (DSP::ReadARAM(((samplePos + 1) * 2 + 1)))); //next sample
|
||||
|
||||
if (pb.src_type == SRCTYPE_NEAREST)
|
||||
sample = pb.adpcm.yn2;
|
||||
else // linear interpolation
|
||||
sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16;
|
||||
|
||||
samplePos = newSamplePos;
|
||||
break;
|
||||
|
||||
case AUDIOFORMAT_ADPCM:
|
||||
ADPCM_Step(pb.adpcm, samplePos, newSamplePos, frac);
|
||||
|
||||
if (pb.src_type == SRCTYPE_NEAREST)
|
||||
sample = pb.adpcm.yn2;
|
||||
else // linear interpolation
|
||||
sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac) + pb.adpcm.yn2) >> 16; //adpcm moves on frac
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Overall volume control. In addition to this there is also separate volume settings to
|
||||
// different channels (left, right etc).
|
||||
frac &= 0xffff;
|
||||
|
||||
int vol = pb.vol_env.cur_volume >> 9;
|
||||
sample = sample * vol >> 8;
|
||||
|
||||
if (pb.mixer_control & MIXCONTROL_RAMPING)
|
||||
{
|
||||
int x = pb.vol_env.cur_volume;
|
||||
x += pb.vol_env.cur_volume_delta; // I'm not sure about this, can anybody find a game
|
||||
// that use this? Or how does it work?
|
||||
if (x < 0)
|
||||
x = 0;
|
||||
if (x >= 0x7fff)
|
||||
x = 0x7fff;
|
||||
pb.vol_env.cur_volume = x; // maybe not per sample?? :P
|
||||
}
|
||||
|
||||
int leftmix = pb.mixer.left >> 5;
|
||||
int rightmix = pb.mixer.right >> 5;
|
||||
int left = sample * leftmix >> 8;
|
||||
int right = sample * rightmix >> 8;
|
||||
// adpcm has to walk from oldSamplePos to samplePos here
|
||||
templbuffer[s] += left;
|
||||
temprbuffer[s] += right;
|
||||
|
||||
// Control the behavior when we reach the end of the sample
|
||||
if (samplePos >= sampleEnd)
|
||||
{
|
||||
if (pb.audio_addr.looping == 1)
|
||||
{
|
||||
samplePos = loopPos;
|
||||
if ((!pb.is_stream) && (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM))
|
||||
{
|
||||
pb.adpcm.yn1 = pb.adpcm_loop_info.yn1;
|
||||
pb.adpcm.yn2 = pb.adpcm_loop_info.yn2;
|
||||
pb.adpcm.pred_scale = pb.adpcm_loop_info.pred_scale;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pb.running = 0;
|
||||
samplePos = loopPos;
|
||||
//samplePos = samplePos - sampleEnd + loopPos;
|
||||
memset(&pb.dpop, 0, sizeof(pb.dpop));
|
||||
memset(pb.src.last_samples, 0, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // end of the _iSize loop
|
||||
|
||||
// Update volume
|
||||
pb.mixer.left = ADPCM_Vol(pb.mixer.left, pb.mixer.left_delta);
|
||||
pb.mixer.right = ADPCM_Vol(pb.mixer.right, pb.mixer.right_delta);
|
||||
|
||||
pb.src.cur_addr_frac = (u16)frac;
|
||||
pb.audio_addr.cur_addr_hi = samplePos >> 16;
|
||||
pb.audio_addr.cur_addr_lo = (u16)samplePos;
|
||||
|
||||
} // if (pb.running)
|
||||
}
|
||||
|
||||
#endif
|
63
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_CARD.cpp
Normal file
63
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_CARD.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "../DSPHLEGlobals.h"
|
||||
#include "../DSPHandler.h"
|
||||
#include "UCodes.h"
|
||||
#include "UCode_CARD.h"
|
||||
#include "../../DSP.h"
|
||||
|
||||
|
||||
CUCode_CARD::CUCode_CARD(CMailHandler& _rMailHandler)
|
||||
: IUCode(_rMailHandler)
|
||||
{
|
||||
DEBUG_LOG(DSPHLE, "CUCode_CARD - initialized");
|
||||
m_rMailHandler.PushMail(DSP_INIT);
|
||||
}
|
||||
|
||||
|
||||
CUCode_CARD::~CUCode_CARD()
|
||||
{
|
||||
m_rMailHandler.Clear();
|
||||
}
|
||||
|
||||
|
||||
void CUCode_CARD::Update(int cycles)
|
||||
{
|
||||
// check if we have to sent something
|
||||
if (!m_rMailHandler.IsEmpty())
|
||||
{
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_CARD::HandleMail(u32 _uMail)
|
||||
{
|
||||
if (_uMail == 0xFF000000) // unlock card
|
||||
{
|
||||
// m_Mails.push(0x00000001); // ACK (actualy anything != 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(DSPHLE, "CUCode_CARD - unknown cmd: %x", _uMail);
|
||||
}
|
||||
|
||||
m_rMailHandler.PushMail(DSP_DONE);
|
||||
CDSPHandler::GetInstance().SetUCode(UCODE_ROM);
|
||||
}
|
||||
|
||||
|
34
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_CARD.h
Normal file
34
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_CARD.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_CARD_H
|
||||
#define _UCODE_CARD_H
|
||||
|
||||
#include "UCodes.h"
|
||||
|
||||
class CUCode_CARD : public IUCode
|
||||
{
|
||||
public:
|
||||
CUCode_CARD(CMailHandler& _rMailHandler);
|
||||
virtual ~CUCode_CARD();
|
||||
|
||||
void HandleMail(u32 _uMail);
|
||||
void Update(int cycles);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
156
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_GBA.cpp
Normal file
156
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_GBA.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "../DSPHLEGlobals.h"
|
||||
#include "../DSPHandler.h"
|
||||
#include "UCodes.h"
|
||||
#include "UCode_GBA.h"
|
||||
|
||||
#include "../../DSP.h"
|
||||
|
||||
CUCode_GBA::CUCode_GBA(CMailHandler& _rMailHandler)
|
||||
: IUCode(_rMailHandler)
|
||||
{
|
||||
m_rMailHandler.PushMail(DSP_INIT);
|
||||
}
|
||||
|
||||
CUCode_GBA::~CUCode_GBA()
|
||||
{
|
||||
m_rMailHandler.Clear();
|
||||
}
|
||||
|
||||
void CUCode_GBA::Update(int cycles)
|
||||
{
|
||||
// check if we have to send something
|
||||
if (!m_rMailHandler.IsEmpty())
|
||||
{
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_GBA::HandleMail(u32 _uMail)
|
||||
{
|
||||
static bool nextmail_is_mramaddr = false;
|
||||
static bool calc_done = false;
|
||||
|
||||
if (m_UploadSetupInProgress)
|
||||
{
|
||||
PrepareBootUCode(_uMail);
|
||||
}
|
||||
else if ((_uMail >> 16 == 0xabba) && !nextmail_is_mramaddr)
|
||||
{
|
||||
nextmail_is_mramaddr = true;
|
||||
}
|
||||
else if (nextmail_is_mramaddr)
|
||||
{
|
||||
nextmail_is_mramaddr = false;
|
||||
u32 mramaddr = _uMail;
|
||||
|
||||
struct sec_params_t {
|
||||
u16 key[2];
|
||||
u16 unk1[2];
|
||||
u16 unk2[2];
|
||||
u32 length;
|
||||
u32 dest_addr;
|
||||
u32 pad[3];
|
||||
} sec_params;
|
||||
|
||||
// 32 bytes from mram addr to dram @ 0
|
||||
for (int i = 0; i < 8; i++, mramaddr += 4)
|
||||
((u32*)&sec_params)[i] = HLEMemory_Read_U32(mramaddr);
|
||||
|
||||
// This is the main decrypt routine
|
||||
u16 x11 = 0, x12 = 0,
|
||||
x20 = 0, x21 = 0, x22 = 0, x23 = 0;
|
||||
|
||||
x20 = Common::swap16(sec_params.key[0]) ^ 0x6f64;
|
||||
x21 = Common::swap16(sec_params.key[1]) ^ 0x6573;
|
||||
|
||||
s16 unk2 = (s8)sec_params.unk2[0];
|
||||
if (unk2 < 0)
|
||||
{
|
||||
x11 = ((~unk2 + 3) << 1) | (sec_params.unk1[0] << 4);
|
||||
}
|
||||
else if (unk2 == 0)
|
||||
{
|
||||
x11 = (sec_params.unk1[0] << 1) | 0x70;
|
||||
}
|
||||
else // unk2 > 0
|
||||
{
|
||||
x11 = ((unk2 - 1) << 1) | (sec_params.unk1[0] << 4);
|
||||
}
|
||||
|
||||
s32 rounded_sub = ((sec_params.length + 7) & ~7) - 0x200;
|
||||
u16 size = (rounded_sub < 0) ? 0 : rounded_sub >> 3;
|
||||
|
||||
u32 t = (((size << 16) | 0x3f80) & 0x3f80ffff) << 1;
|
||||
s16 t_low = (s8)(t >> 8);
|
||||
t += (t_low & size) << 16;
|
||||
x12 = t >> 16;
|
||||
x11 |= (size & 0x4000) >> 14; // this would be stored in ac0.h if we weren't constrained to 32bit :)
|
||||
t = ((x11 & 0xff) << 16) + ((x12 & 0xff) << 16) + (x12 << 8);
|
||||
|
||||
u16 final11 = 0, final12 = 0;
|
||||
final11 = x11 | ((t >> 8) & 0xff00) | 0x8080;
|
||||
final12 = x12 | 0x8080;
|
||||
|
||||
if ((final12 & 0x200) != 0)
|
||||
{
|
||||
x22 = final11 ^ 0x6f64;
|
||||
x23 = final12 ^ 0x6573;
|
||||
}
|
||||
else
|
||||
{
|
||||
x22 = final11 ^ 0x6177;
|
||||
x23 = final12 ^ 0x614b;
|
||||
}
|
||||
|
||||
// Send the result back to mram
|
||||
*(u32*)HLEMemory_Get_Pointer(sec_params.dest_addr) = Common::swap32((x20 << 16) | x21);
|
||||
*(u32*)HLEMemory_Get_Pointer(sec_params.dest_addr+4) = Common::swap32((x22 << 16) | x23);
|
||||
|
||||
// Done!
|
||||
DEBUG_LOG(DSPHLE, "\n%08x -> key %08x len %08x dest_addr %08x unk1 %08x unk2 %08x"
|
||||
" 22 %04x 23 %04x",
|
||||
mramaddr,
|
||||
*(u32*)sec_params.key, sec_params.length, sec_params.dest_addr,
|
||||
*(u32*)sec_params.unk1, *(u32*)sec_params.unk2,
|
||||
x22, x23);
|
||||
|
||||
calc_done = true;
|
||||
m_rMailHandler.PushMail(DSP_DONE);
|
||||
}
|
||||
else if ((_uMail >> 16 == 0xcdd1) && calc_done)
|
||||
{
|
||||
switch (_uMail & 0xffff)
|
||||
{
|
||||
case 1:
|
||||
m_UploadSetupInProgress = true;
|
||||
break;
|
||||
case 2:
|
||||
CDSPHandler::GetInstance().SetUCode(UCODE_ROM);
|
||||
break;
|
||||
default:
|
||||
DEBUG_LOG(DSPHLE, "CUCode_GBA - unknown 0xcdd1 cmd: %08x", _uMail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(DSPHLE, "CUCode_GBA - unknown cmd: %08x", _uMail);
|
||||
}
|
||||
}
|
29
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_GBA.h
Normal file
29
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_GBA.h
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "UCodes.h"
|
||||
|
||||
struct CUCode_GBA : public IUCode
|
||||
{
|
||||
CUCode_GBA(CMailHandler& _rMailHandler);
|
||||
virtual ~CUCode_GBA();
|
||||
|
||||
void HandleMail(u32 _uMail);
|
||||
void Update(int cycles);
|
||||
};
|
@ -0,0 +1,50 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "../DSPHLEGlobals.h"
|
||||
#include "../DSPHandler.h"
|
||||
#include "UCodes.h"
|
||||
#include "UCode_InitAudioSystem.h"
|
||||
|
||||
CUCode_InitAudioSystem::CUCode_InitAudioSystem(CMailHandler& _rMailHandler)
|
||||
: IUCode(_rMailHandler)
|
||||
{
|
||||
DEBUG_LOG(DSPHLE, "CUCode_InitAudioSystem - initialized");
|
||||
}
|
||||
|
||||
|
||||
CUCode_InitAudioSystem::~CUCode_InitAudioSystem()
|
||||
{}
|
||||
|
||||
|
||||
void CUCode_InitAudioSystem::Init()
|
||||
{}
|
||||
|
||||
|
||||
void CUCode_InitAudioSystem::Update(int cycles)
|
||||
{
|
||||
if (m_rMailHandler.IsEmpty())
|
||||
{
|
||||
m_rMailHandler.PushMail(0x80544348);
|
||||
// HALT
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_InitAudioSystem::HandleMail(u32 _uMail)
|
||||
{}
|
||||
|
||||
|
@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_INITAUDIOSYSTEM
|
||||
#define _UCODE_INITAUDIOSYSTEM
|
||||
|
||||
#include "UCodes.h"
|
||||
|
||||
class CUCode_InitAudioSystem : public IUCode
|
||||
{
|
||||
public:
|
||||
CUCode_InitAudioSystem(CMailHandler& _rMailHandler);
|
||||
virtual ~CUCode_InitAudioSystem();
|
||||
|
||||
void HandleMail(u32 _uMail);
|
||||
void Update(int cycles);
|
||||
void Init();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
126
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_ROM.cpp
Normal file
126
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_ROM.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "../DSPHLEGlobals.h"
|
||||
#include "../DSPHandler.h"
|
||||
#include "UCodes.h"
|
||||
#include "UCode_ROM.h"
|
||||
#include "Hash.h"
|
||||
#include "../../Memmap.h"
|
||||
|
||||
CUCode_Rom::CUCode_Rom(CMailHandler& _rMailHandler)
|
||||
: IUCode(_rMailHandler)
|
||||
, m_CurrentUCode()
|
||||
, m_BootTask_numSteps(0)
|
||||
, m_NextParameter(0)
|
||||
{
|
||||
DEBUG_LOG(DSPHLE, "UCode_Rom - initialized");
|
||||
m_rMailHandler.Clear();
|
||||
m_rMailHandler.PushMail(0x8071FEED);
|
||||
}
|
||||
|
||||
CUCode_Rom::~CUCode_Rom()
|
||||
{}
|
||||
|
||||
void CUCode_Rom::Update(int cycles)
|
||||
{}
|
||||
|
||||
void CUCode_Rom::HandleMail(u32 _uMail)
|
||||
{
|
||||
if (m_NextParameter == 0)
|
||||
{
|
||||
// wait for beginning of UCode
|
||||
if ((_uMail & 0xFFFF0000) != 0x80F30000)
|
||||
{
|
||||
u32 Message = 0xFEEE0000 | (_uMail & 0xFFFF);
|
||||
m_rMailHandler.PushMail(Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NextParameter = _uMail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (m_NextParameter)
|
||||
{
|
||||
case 0x80F3A001:
|
||||
m_CurrentUCode.m_RAMAddress = _uMail;
|
||||
break;
|
||||
|
||||
case 0x80F3A002:
|
||||
m_CurrentUCode.m_Length = _uMail & 0xffff;
|
||||
break;
|
||||
|
||||
case 0x80F3C002:
|
||||
m_CurrentUCode.m_IMEMAddress = _uMail & 0xffff;
|
||||
break;
|
||||
|
||||
case 0x80F3B002:
|
||||
m_CurrentUCode.m_DMEMLength = _uMail & 0xffff;
|
||||
if (m_CurrentUCode.m_DMEMLength) {
|
||||
NOTICE_LOG(DSPHLE,"m_CurrentUCode.m_DMEMLength = 0x%04x.", m_CurrentUCode.m_DMEMLength);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80F3D001:
|
||||
{
|
||||
m_CurrentUCode.m_StartPC = _uMail & 0xffff;
|
||||
BootUCode();
|
||||
return; // Important! BootUCode indirectly does "delete this;". Must exit immediately.
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// THE GODDAMN OVERWRITE WAS HERE. Without the return above, since BootUCode may delete "this", well ...
|
||||
m_NextParameter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_Rom::BootUCode()
|
||||
{
|
||||
u32 ector_crc = HashEctor(
|
||||
(u8*)HLEMemory_Get_Pointer(m_CurrentUCode.m_RAMAddress),
|
||||
m_CurrentUCode.m_Length);
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
char binFile[MAX_PATH];
|
||||
sprintf(binFile, "%sDSP_UC_%08X.bin", File::GetUserPath(D_DUMPDSP_IDX), ector_crc);
|
||||
|
||||
FILE* pFile = fopen(binFile, "wb");
|
||||
if (pFile)
|
||||
{
|
||||
fwrite((u8*)Memory::GetPointer(m_CurrentUCode.m_RAMAddress), m_CurrentUCode.m_Length, 1, pFile);
|
||||
fclose(pFile);
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_LOG(DSPHLE, "CurrentUCode SOURCE Addr: 0x%08x", m_CurrentUCode.m_RAMAddress);
|
||||
DEBUG_LOG(DSPHLE, "CurrentUCode Length: 0x%08x", m_CurrentUCode.m_Length);
|
||||
DEBUG_LOG(DSPHLE, "CurrentUCode DEST Addr: 0x%08x", m_CurrentUCode.m_IMEMAddress);
|
||||
DEBUG_LOG(DSPHLE, "CurrentUCode DMEM Length: 0x%08x", m_CurrentUCode.m_DMEMLength);
|
||||
DEBUG_LOG(DSPHLE, "CurrentUCode init_vector: 0x%08x", m_CurrentUCode.m_StartPC);
|
||||
DEBUG_LOG(DSPHLE, "CurrentUCode CRC: 0x%08x", ector_crc);
|
||||
DEBUG_LOG(DSPHLE, "BootTask - done");
|
||||
|
||||
CDSPHandler::GetInstance().SetUCode(ector_crc);
|
||||
}
|
||||
|
||||
|
50
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_ROM.h
Normal file
50
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_ROM.h
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_ROM
|
||||
#define _UCODE_ROM
|
||||
|
||||
#include "UCodes.h"
|
||||
|
||||
class CUCode_Rom : public IUCode
|
||||
{
|
||||
public:
|
||||
CUCode_Rom(CMailHandler& _rMailHandler);
|
||||
virtual ~CUCode_Rom();
|
||||
|
||||
void HandleMail(u32 _uMail);
|
||||
void Update(int cycles);
|
||||
|
||||
private:
|
||||
struct SUCode
|
||||
{
|
||||
u32 m_RAMAddress;
|
||||
u32 m_Length;
|
||||
u32 m_IMEMAddress;
|
||||
u32 m_DMEMLength;
|
||||
u32 m_StartPC;
|
||||
};
|
||||
SUCode m_CurrentUCode;
|
||||
int m_BootTask_numSteps;
|
||||
|
||||
u32 m_NextParameter;
|
||||
|
||||
void BootUCode();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
625
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda.cpp
Normal file
625
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda.cpp
Normal file
@ -0,0 +1,625 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Games that uses this UCode:
|
||||
// Zelda: The Windwaker, Mario Sunshine, Mario Kart, Twilight Princess,
|
||||
// Super Mario Galaxy
|
||||
|
||||
#include "../DSPHLEGlobals.h"
|
||||
#include "UCodes.h"
|
||||
#include "UCode_Zelda.h"
|
||||
#include "../MailHandler.h"
|
||||
|
||||
#include "Mixer.h"
|
||||
|
||||
#include "WaveFile.h"
|
||||
#include "../DSPHandler.h"
|
||||
#include "../../DSP.h"
|
||||
|
||||
|
||||
CUCode_Zelda::CUCode_Zelda(CMailHandler& _rMailHandler, u32 _CRC)
|
||||
:
|
||||
IUCode(_rMailHandler),
|
||||
m_CRC(_CRC),
|
||||
|
||||
m_bSyncInProgress(false),
|
||||
m_MaxVoice(0),
|
||||
|
||||
m_NumSyncMail(0),
|
||||
|
||||
m_NumVoices(0),
|
||||
|
||||
m_bSyncCmdPending(false),
|
||||
m_CurVoice(0),
|
||||
m_CurBuffer(0),
|
||||
m_NumBuffers(0),
|
||||
|
||||
m_VoicePBsAddr(0),
|
||||
m_UnkTableAddr(0),
|
||||
m_ReverbPBsAddr(0),
|
||||
|
||||
m_RightBuffersAddr(0),
|
||||
m_LeftBuffersAddr(0),
|
||||
m_pos(0),
|
||||
|
||||
m_DMABaseAddr(0),
|
||||
|
||||
m_numSteps(0),
|
||||
m_bListInProgress(false),
|
||||
m_step(0),
|
||||
|
||||
m_readOffset(0),
|
||||
|
||||
m_MailState(WaitForMail),
|
||||
|
||||
m_NumPBs(0),
|
||||
m_PBAddress(0),
|
||||
m_PBAddress2(0)
|
||||
{
|
||||
DEBUG_LOG(DSPHLE, "UCode_Zelda - add boot mails for handshake");
|
||||
|
||||
if (IsLightVersion())
|
||||
{
|
||||
NOTICE_LOG(DSPHLE, "Luigi Stylee!");
|
||||
m_rMailHandler.PushMail(0x88881111);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rMailHandler.PushMail(DSP_INIT);
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
m_rMailHandler.PushMail(0xF3551111); // handshake
|
||||
}
|
||||
|
||||
m_VoiceBuffer = new s32[256 * 1024];
|
||||
m_ResampleBuffer = new s16[256 * 1024];
|
||||
m_LeftBuffer = new s32[256 * 1024];
|
||||
m_RightBuffer = new s32[256 * 1024];
|
||||
|
||||
memset(m_Buffer, 0, sizeof(m_Buffer));
|
||||
memset(m_SyncFlags, 0, sizeof(m_SyncFlags));
|
||||
memset(m_AFCCoefTable, 0, sizeof(m_AFCCoefTable));
|
||||
memset(m_PBMask, 0, sizeof(m_PBMask));
|
||||
}
|
||||
|
||||
CUCode_Zelda::~CUCode_Zelda()
|
||||
{
|
||||
m_rMailHandler.Clear();
|
||||
|
||||
delete [] m_VoiceBuffer;
|
||||
delete [] m_ResampleBuffer;
|
||||
delete [] m_LeftBuffer;
|
||||
delete [] m_RightBuffer;
|
||||
}
|
||||
|
||||
u8 *CUCode_Zelda::GetARAMPointer(u32 address)
|
||||
{
|
||||
if (IsDMAVersion())
|
||||
return (u8 *)(Memory::GetPointer(m_DMABaseAddr)) + address;
|
||||
else
|
||||
return (u8 *)(DSP::GetARAMPtr()) + address;
|
||||
}
|
||||
|
||||
void CUCode_Zelda::Update(int cycles)
|
||||
{
|
||||
if (!IsLightVersion())
|
||||
{
|
||||
if (m_rMailHandler.GetNextMail() == DSP_FRAME_END)
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
}
|
||||
|
||||
if (NeedsResumeMail())
|
||||
{
|
||||
m_rMailHandler.PushMail(DSP_RESUME);
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_Zelda::HandleMail(u32 _uMail)
|
||||
{
|
||||
if (IsLightVersion())
|
||||
HandleMail_LightVersion(_uMail);
|
||||
else if (IsSMSVersion())
|
||||
HandleMail_SMSVersion(_uMail);
|
||||
else
|
||||
HandleMail_NormalVersion(_uMail);
|
||||
}
|
||||
|
||||
void CUCode_Zelda::HandleMail_LightVersion(u32 _uMail)
|
||||
{
|
||||
//ERROR_LOG(DSPHLE, "Light version mail %08X, list in progress: %s, step: %i/%i",
|
||||
// _uMail, m_bListInProgress ? "yes":"no", m_step, m_numSteps);
|
||||
|
||||
if (m_bSyncCmdPending)
|
||||
{
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
m_CurBuffer++;
|
||||
|
||||
if (m_CurBuffer == m_NumBuffers)
|
||||
{
|
||||
soundStream->GetMixer()->SetHLEReady(true);
|
||||
m_bSyncCmdPending = false;
|
||||
DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_bListInProgress)
|
||||
{
|
||||
switch ((_uMail >> 24) & 0x7F)
|
||||
{
|
||||
case 0x00: m_numSteps = 1; break; // dummy
|
||||
case 0x01: m_numSteps = 5; break; // DsetupTable
|
||||
case 0x02: m_numSteps = 3; break; // DsyncFrame
|
||||
|
||||
default:
|
||||
{
|
||||
m_numSteps = 0;
|
||||
PanicAlert("Zelda uCode (light version): unknown/unsupported command %02X", (_uMail >> 24) & 0x7F);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_bListInProgress = true;
|
||||
m_step = 0;
|
||||
}
|
||||
|
||||
if (m_step >= sizeof(m_Buffer) / 4)
|
||||
PanicAlert("m_step out of range");
|
||||
|
||||
((u32*)m_Buffer)[m_step] = _uMail;
|
||||
m_step++;
|
||||
|
||||
if (m_step >= m_numSteps)
|
||||
{
|
||||
ExecuteList();
|
||||
m_bListInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_Zelda::HandleMail_SMSVersion(u32 _uMail)
|
||||
{
|
||||
if (m_bSyncInProgress)
|
||||
{
|
||||
if (m_bSyncCmdPending)
|
||||
{
|
||||
m_SyncFlags[(m_NumSyncMail << 1) ] = _uMail >> 16;
|
||||
m_SyncFlags[(m_NumSyncMail << 1) + 1] = _uMail & 0xFFFF;
|
||||
|
||||
m_NumSyncMail++;
|
||||
if (m_NumSyncMail == 2)
|
||||
{
|
||||
m_NumSyncMail = 0;
|
||||
m_bSyncInProgress = false;
|
||||
|
||||
m_CurBuffer++;
|
||||
|
||||
m_rMailHandler.PushMail(DSP_SYNC);
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
m_rMailHandler.PushMail(0xF355FF00 | m_CurBuffer);
|
||||
|
||||
if (m_CurBuffer == m_NumBuffers)
|
||||
{
|
||||
m_rMailHandler.PushMail(DSP_FRAME_END);
|
||||
// DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
|
||||
soundStream->GetMixer()->SetHLEReady(true);
|
||||
DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync");
|
||||
// soundStream->Update(); //do it in this thread to avoid sync problems
|
||||
|
||||
m_bSyncCmdPending = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bSyncInProgress = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_bListInProgress)
|
||||
{
|
||||
if (m_step >= sizeof(m_Buffer) / 4)
|
||||
PanicAlert("m_step out of range");
|
||||
|
||||
((u32*)m_Buffer)[m_step] = _uMail;
|
||||
m_step++;
|
||||
|
||||
if (m_step >= m_numSteps)
|
||||
{
|
||||
ExecuteList();
|
||||
m_bListInProgress = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Here holds: m_bSyncInProgress == false && m_bListInProgress == false
|
||||
|
||||
if (_uMail == 0)
|
||||
{
|
||||
m_bSyncInProgress = true;
|
||||
m_NumSyncMail = 0;
|
||||
}
|
||||
else if ((_uMail >> 16) == 0)
|
||||
{
|
||||
m_bListInProgress = true;
|
||||
m_numSteps = _uMail;
|
||||
m_step = 0;
|
||||
}
|
||||
else if ((_uMail >> 16) == 0xCDD1) // A 0xCDD1000X mail should come right after we send a DSP_SYNCEND mail
|
||||
{
|
||||
// The low part of the mail tells the operation to perform
|
||||
// Seeing as every possible operation number halts the uCode,
|
||||
// except 3, that thing seems to be intended for debugging
|
||||
switch (_uMail & 0xFFFF)
|
||||
{
|
||||
case 0x0003: // Do nothing
|
||||
return;
|
||||
|
||||
case 0x0000: // Halt
|
||||
case 0x0001: // Dump memory? and halt
|
||||
case 0x0002: // Do something and halt
|
||||
WARN_LOG(DSPHLE, "Zelda uCode(SMS version): received halting operation %04X", _uMail & 0xFFFF);
|
||||
return;
|
||||
|
||||
default: // Invalid (the real ucode would likely crash)
|
||||
WARN_LOG(DSPHLE, "Zelda uCode(SMS version): received invalid operation %04X", _uMail & 0xFFFF);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(DSPHLE, "Zelda uCode (SMS version): unknown mail %08X", _uMail);
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_Zelda::HandleMail_NormalVersion(u32 _uMail)
|
||||
{
|
||||
// WARN_LOG(DSPHLE, "Zelda uCode: Handle mail %08X", _uMail);
|
||||
|
||||
if (m_UploadSetupInProgress) // evaluated first!
|
||||
{
|
||||
PrepareBootUCode(_uMail);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_bSyncInProgress)
|
||||
{
|
||||
if (m_bSyncCmdPending)
|
||||
{
|
||||
u32 n = (_uMail >> 16) & 0xF;
|
||||
m_MaxVoice = (n + 1) << 4;
|
||||
m_SyncFlags[n] = _uMail & 0xFFFF;
|
||||
m_bSyncInProgress = false;
|
||||
|
||||
// Normally, we should mix to the buffers used by the game.
|
||||
// We don't do it currently for a simple reason:
|
||||
// if the game runs fast all the time, then it's OK,
|
||||
// but if it runs slow, sound can become choppy.
|
||||
// This problem won't happen when mixing to the buffer
|
||||
// provided by MixAdd(), because the size of this buffer
|
||||
// is automatically adjusted if the game runs slow.
|
||||
#if 0
|
||||
if (m_SyncFlags[n] & 0x8000)
|
||||
{
|
||||
for (; m_CurVoice < m_MaxVoice; m_CurVoice++)
|
||||
{
|
||||
if (m_CurVoice >= m_NumVoices)
|
||||
break;
|
||||
|
||||
MixVoice(m_CurVoice);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
m_CurVoice = m_MaxVoice;
|
||||
|
||||
if (m_CurVoice >= m_NumVoices)
|
||||
{
|
||||
m_CurBuffer++;
|
||||
|
||||
m_rMailHandler.PushMail(DSP_SYNC);
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
m_rMailHandler.PushMail(0xF355FF00 | m_CurBuffer);
|
||||
|
||||
m_CurVoice = 0;
|
||||
|
||||
if (m_CurBuffer == m_NumBuffers)
|
||||
{
|
||||
if (!IsDMAVersion()) // this is a hack... without it Pikmin 1 Wii/ Zelda TP Wii mail-s stopped
|
||||
m_rMailHandler.PushMail(DSP_FRAME_END);
|
||||
//g_dspInitialize.pGenerateDSPInterrupt();
|
||||
|
||||
soundStream->GetMixer()->SetHLEReady(true);
|
||||
DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync");
|
||||
// soundStream->Update(); //do it in this thread to avoid sync problems
|
||||
|
||||
m_bSyncCmdPending = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bSyncInProgress = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_bListInProgress)
|
||||
{
|
||||
if (m_step >= sizeof(m_Buffer) / 4)
|
||||
PanicAlert("m_step out of range");
|
||||
|
||||
((u32*)m_Buffer)[m_step] = _uMail;
|
||||
m_step++;
|
||||
|
||||
if (m_step >= m_numSteps)
|
||||
{
|
||||
ExecuteList();
|
||||
m_bListInProgress = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Here holds: m_bSyncInProgress == false && m_bListInProgress == false
|
||||
|
||||
// Zelda-only mails:
|
||||
// - 0000XXXX - Begin list
|
||||
// - 00000000, 000X0000 - Sync mails
|
||||
// - CDD1XXXX - comes after DsyncFrame completed, seems to be debugging stuff
|
||||
|
||||
if (_uMail == 0)
|
||||
{
|
||||
m_bSyncInProgress = true;
|
||||
}
|
||||
else if ((_uMail >> 16) == 0)
|
||||
{
|
||||
m_bListInProgress = true;
|
||||
m_numSteps = _uMail;
|
||||
m_step = 0;
|
||||
}
|
||||
else if ((_uMail >> 16) == 0xCDD1) // A 0xCDD1000X mail should come right after we send a DSP_FRAME_END mail
|
||||
{
|
||||
// The low part of the mail tells the operation to perform
|
||||
// Seeing as every possible operation number halts the uCode,
|
||||
// except 3, that thing seems to be intended for debugging
|
||||
switch (_uMail & 0xFFFF)
|
||||
{
|
||||
case 0x0003: // Do nothing - continue normally
|
||||
return;
|
||||
|
||||
case 0x0001: // accepts params to either dma to iram and/or dram (used for hotbooting a new ucode)
|
||||
// TODO find a better way to protect from HLEMixer?
|
||||
soundStream->GetMixer()->SetHLEReady(false);
|
||||
m_UploadSetupInProgress = true;
|
||||
return;
|
||||
|
||||
case 0x0002: // Let IROM play us off
|
||||
CDSPHandler::GetInstance().SetUCode(UCODE_ROM);
|
||||
return;
|
||||
|
||||
case 0x0000: // Halt
|
||||
WARN_LOG(DSPHLE, "Zelda uCode: received halting operation %04X", _uMail & 0xFFFF);
|
||||
return;
|
||||
|
||||
default: // Invalid (the real ucode would likely crash)
|
||||
WARN_LOG(DSPHLE, "Zelda uCode: received invalid operation %04X", _uMail & 0xFFFF);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(DSPHLE, "Zelda uCode: unknown mail %08X", _uMail);
|
||||
}
|
||||
}
|
||||
|
||||
// zelda debug ..803F6418
|
||||
void CUCode_Zelda::ExecuteList()
|
||||
{
|
||||
// begin with the list
|
||||
m_readOffset = 0;
|
||||
|
||||
u32 CmdMail = Read32();
|
||||
u32 Command = (CmdMail >> 24) & 0x7f;
|
||||
u32 Sync;
|
||||
u32 ExtraData = CmdMail & 0xFFFF;
|
||||
|
||||
if (IsLightVersion())
|
||||
Sync = 0x62 + (Command << 1); // seen in DSP_UC_Luigi.txt
|
||||
else
|
||||
Sync = CmdMail >> 16;
|
||||
|
||||
DEBUG_LOG(DSPHLE, "==============================================================================");
|
||||
DEBUG_LOG(DSPHLE, "Zelda UCode - execute dlist (cmd: 0x%04x : sync: 0x%04x)", Command, Sync);
|
||||
|
||||
switch (Command)
|
||||
{
|
||||
// dummy
|
||||
case 0x00: break;
|
||||
|
||||
// DsetupTable ... zelda ww jumps to 0x0095
|
||||
case 0x01:
|
||||
{
|
||||
m_NumVoices = ExtraData;
|
||||
m_VoicePBsAddr = Read32() & 0x7FFFFFFF;
|
||||
m_UnkTableAddr = Read32() & 0x7FFFFFFF;
|
||||
m_AFCCoefTableAddr = Read32() & 0x7FFFFFFF;
|
||||
m_ReverbPBsAddr = Read32() & 0x7FFFFFFF; // WARNING: reverb PBs are very different from voice PBs!
|
||||
|
||||
// Read the other table
|
||||
u16 *TempPtr = (u16*)Memory::GetPointer(m_UnkTableAddr);
|
||||
for (int i = 0; i < 0x280; i++)
|
||||
m_MiscTable[i] = (s16)Common::swap16(TempPtr[i]);
|
||||
|
||||
// Read AFC coef table
|
||||
TempPtr = (u16*)Memory::GetPointer(m_AFCCoefTableAddr);
|
||||
for (int i = 0; i < 32; i++)
|
||||
m_AFCCoefTable[i] = (s16)Common::swap16(TempPtr[i]);
|
||||
|
||||
DEBUG_LOG(DSPHLE, "DsetupTable");
|
||||
DEBUG_LOG(DSPHLE, "Num voice param blocks: %i", m_NumVoices);
|
||||
DEBUG_LOG(DSPHLE, "Voice param blocks address: 0x%08x", m_VoicePBsAddr);
|
||||
|
||||
// This points to some strange data table. Don't know yet what it's for. Reverb coefs?
|
||||
DEBUG_LOG(DSPHLE, "DSPRES_FILTER (size: 0x40): 0x%08x", m_UnkTableAddr);
|
||||
|
||||
// Zelda WW: This points to a 64-byte array of coefficients, which are EXACTLY the same
|
||||
// as the AFC ADPCM coef array in decode.c of the in_cube winamp plugin,
|
||||
// which can play Zelda audio. So, these should definitely be used when decoding AFC.
|
||||
DEBUG_LOG(DSPHLE, "DSPADPCM_FILTER (size: 0x500): 0x%08x", m_AFCCoefTableAddr);
|
||||
DEBUG_LOG(DSPHLE, "Reverb param blocks address: 0x%08x", m_ReverbPBsAddr);
|
||||
}
|
||||
break;
|
||||
|
||||
// SyncFrame ... zelda ww jumps to 0x0243
|
||||
case 0x02:
|
||||
{
|
||||
m_bSyncCmdPending = true;
|
||||
|
||||
m_CurBuffer = 0;
|
||||
m_NumBuffers = (CmdMail >> 16) & 0xFF;
|
||||
|
||||
// Addresses for right & left buffers in main memory
|
||||
// Each buffer is 160 bytes long. The number of (both left & right) buffers
|
||||
// is set by the first mail of the list.
|
||||
m_RightBuffersAddr = Read32() & 0x7FFFFFFF;
|
||||
m_LeftBuffersAddr = Read32() & 0x7FFFFFFF;
|
||||
|
||||
DEBUG_LOG(DSPHLE, "DsyncFrame");
|
||||
// These alternate between three sets of mixing buffers. They are all three fairly near,
|
||||
// but not at, the ADMA read addresses.
|
||||
DEBUG_LOG(DSPHLE, "Right buffer address: 0x%08x", m_RightBuffersAddr);
|
||||
DEBUG_LOG(DSPHLE, "Left buffer address: 0x%08x", m_LeftBuffersAddr);
|
||||
|
||||
if (IsLightVersion())
|
||||
break;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Simply sends the sync messages
|
||||
case 0x03: break;
|
||||
|
||||
/* case 0x04: break; // dunno ... zelda ww jmps to 0x0580
|
||||
case 0x05: break; // dunno ... zelda ww jmps to 0x0592
|
||||
case 0x06: break; // dunno ... zelda ww jmps to 0x0469
|
||||
case 0x07: break; // dunno ... zelda ww jmps to 0x044d
|
||||
case 0x08: break; // Mixer ... zelda ww jmps to 0x0485
|
||||
case 0x09: break; // dunno ... zelda ww jmps to 0x044d
|
||||
*/
|
||||
|
||||
// DsetDolbyDelay ... zelda ww jumps to 0x00b2
|
||||
case 0x0d:
|
||||
{
|
||||
u32 tmp = Read32();
|
||||
DEBUG_LOG(DSPHLE, "DSetDolbyDelay");
|
||||
DEBUG_LOG(DSPHLE, "DOLBY2_DELAY_BUF (size 0x960): 0x%08x", tmp);
|
||||
}
|
||||
break;
|
||||
|
||||
// This opcode, in the SMG ucode, sets the base address for audio data transfers from main memory (using DMA).
|
||||
// In the Zelda ucode, it is dummy, because this ucode uses accelerator for audio data transfers.
|
||||
case 0x0e:
|
||||
{
|
||||
m_DMABaseAddr = Read32() & 0x7FFFFFFF;
|
||||
DEBUG_LOG(DSPHLE, "DsetDMABaseAddr");
|
||||
DEBUG_LOG(DSPHLE, "DMA base address: 0x%08x", m_DMABaseAddr);
|
||||
}
|
||||
break;
|
||||
|
||||
// default ... zelda ww jumps to 0x0043
|
||||
default:
|
||||
PanicAlert("Zelda UCode - unknown cmd: %x (size %i)", Command, m_numSteps);
|
||||
break;
|
||||
}
|
||||
|
||||
// sync, we are ready
|
||||
if (IsLightVersion())
|
||||
{
|
||||
if (m_bSyncCmdPending)
|
||||
m_rMailHandler.PushMail(0x80000000 | m_NumBuffers); // after CMD_2
|
||||
else
|
||||
m_rMailHandler.PushMail(0x80000000 | Sync); // after CMD_0, CMD_1
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rMailHandler.PushMail(DSP_SYNC);
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
m_rMailHandler.PushMail(0xF3550000 | Sync);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CUCode_Zelda::DoState(PointerWrap &p)
|
||||
{
|
||||
// It's bad if we try to save during Mix()
|
||||
m_csMix.Enter();
|
||||
|
||||
p.Do(m_CRC);
|
||||
|
||||
p.Do(m_AFCCoefTable);
|
||||
p.Do(m_MiscTable);
|
||||
|
||||
p.Do(m_bSyncInProgress);
|
||||
p.Do(m_MaxVoice);
|
||||
p.Do(m_SyncFlags);
|
||||
|
||||
p.Do(m_NumSyncMail);
|
||||
|
||||
p.Do(m_NumVoices);
|
||||
|
||||
p.Do(m_bSyncCmdPending);
|
||||
p.Do(m_CurVoice);
|
||||
p.Do(m_CurBuffer);
|
||||
p.Do(m_NumBuffers);
|
||||
|
||||
p.Do(m_VoicePBsAddr);
|
||||
p.Do(m_UnkTableAddr);
|
||||
p.Do(m_AFCCoefTableAddr);
|
||||
p.Do(m_ReverbPBsAddr);
|
||||
|
||||
p.Do(m_RightBuffersAddr);
|
||||
p.Do(m_LeftBuffersAddr);
|
||||
p.Do(m_pos);
|
||||
|
||||
p.Do(m_DMABaseAddr);
|
||||
|
||||
p.Do(m_numSteps);
|
||||
p.Do(m_bListInProgress);
|
||||
p.Do(m_step);
|
||||
p.Do(m_Buffer);
|
||||
|
||||
p.Do(m_readOffset);
|
||||
|
||||
p.Do(m_MailState);
|
||||
p.Do(m_PBMask);
|
||||
|
||||
p.Do(m_NumPBs);
|
||||
p.Do(m_PBAddress);
|
||||
p.Do(m_PBAddress2);
|
||||
|
||||
p.Do(m_UploadSetupInProgress);
|
||||
|
||||
m_rMailHandler.DoState(p);
|
||||
|
||||
m_csMix.Leave();
|
||||
}
|
318
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda.h
Normal file
318
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda.h
Normal file
@ -0,0 +1,318 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_ZELDA_H
|
||||
#define _UCODE_ZELDA_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "UCodes.h"
|
||||
|
||||
// Obviously missing things that must be in here, somewhere among the "unknown":
|
||||
// * Volume
|
||||
// * L/R Pan
|
||||
// * (probably) choice of resampling algorithm (point, linear, cubic)
|
||||
|
||||
union ZeldaVoicePB
|
||||
{
|
||||
struct
|
||||
{
|
||||
// Read-Write part
|
||||
u16 Status; // 0x00 | 1 = play, 0 = stop
|
||||
u16 KeyOff; // 0x01 | writing 1 stops voice?
|
||||
u16 RatioInt; // 0x02 | Position delta (playback speed)
|
||||
u16 Unk03; // 0x03 | unknown
|
||||
u16 NeedsReset; // 0x04 | indicates if some values in PB need to be reset
|
||||
u16 ReachedEnd; // 0x05 | set to 1 when end reached
|
||||
u16 IsBlank; // 0x06 | 0 = normal sound, 1 = samples are always the same
|
||||
u16 Unk07; // 0x07 | unknown, in zelda always 0x0010. Something to do with number of saved samples (0x68)?
|
||||
|
||||
u16 SoundType; // 0x08 | "Sound type": so far in zww: 0x0d00 for music (volume mode 0), 0x4861 for sfx (volume mode 1)
|
||||
u16 volumeLeft1; // 0x09 | Left Volume 1 // There's probably two of each because they should be ramped within each frame.
|
||||
u16 volumeLeft2; // 0x0A | Left Volume 2
|
||||
u16 Unk0B; // 0x0B | unknown
|
||||
|
||||
u16 SoundType2; // 0x0C | "Sound type" 2 (not really sound type)
|
||||
u16 volumeRight1; // 0x0D | Right Volume 1
|
||||
u16 volumeRight2; // 0x0E | Right Volume 2
|
||||
u16 Unk0F; // 0x0F | unknown
|
||||
|
||||
u16 SoundType3; // 0x10 | "Sound type" 3 (not really sound type)
|
||||
u16 volumeUnknown1_1; // 0x11 | Unknown Volume 1
|
||||
u16 volumeUnknown1_2; // 0x12 | Unknown Volume 1
|
||||
u16 Unk13; // 0x13 | unknown
|
||||
|
||||
u16 SoundType4; // 0x14 | "Sound type" 4 (not really sound type)
|
||||
u16 volumeUnknown2_1; // 0x15 | Unknown Volume 2
|
||||
u16 volumeUnknown2_2; // 0x16 | Unknown Volume 2
|
||||
u16 Unk17; // 0x17 | unknown
|
||||
|
||||
u16 Unk18[0x10]; // 0x18 | unknown
|
||||
u16 Unk28; // 0x28 | unknown
|
||||
u16 Unk29; // 0x29 | unknown // multiplied by 0x2a @ 0d21/ZWW
|
||||
u16 Unk2a; // 0x2A | unknown // loaded at 0d2e/ZWW
|
||||
u16 Unk2b; // 0x2B | unknown
|
||||
u16 VolumeMode; // 0x2C | unknown // See 0337/ZWW
|
||||
u16 Unk2D; // 0x2D | unknown
|
||||
u16 Unk2E; // 0x2E | unknown
|
||||
u16 Unk2F; // 0x2F | unknown
|
||||
u16 CurSampleFrac; // 0x30 | Fractional part of the current sample position
|
||||
u16 Unk31; // 0x31 | unknown / unused
|
||||
u16 CurBlock; // 0x32 | current block? used by zelda's AFC decoder. we don't need it.
|
||||
u16 FixedSample; // 0x33 | sample value for "blank" voices
|
||||
u32 RestartPos; // 0x34 | restart pos / "loop start offset"
|
||||
u16 Unk36[2]; // 0x36 | unknown // loaded at 0adc/ZWW in 0x21 decoder
|
||||
u32 CurAddr; // 0x38 | current address
|
||||
u32 RemLength; // 0x3A | remaining length
|
||||
u16 ResamplerOldData[4]; // 0x3C | The resampler stores the last 4 decoded samples here from the previous frame, so that the filter kernel has something to read before the start of the buffer.
|
||||
u16 Unk40[0x10]; // 0x40 | Used as some sort of buffer by IIR
|
||||
u16 Unk50[0x8]; // 0x50 | Used as some sort of buffer by 06ff/ZWW
|
||||
u16 Unk58[0x8]; // 0x58 |
|
||||
u16 Unk60[0x6]; // 0x60 |
|
||||
u16 YN2; // 0x66 | YN2
|
||||
u16 YN1; // 0x67 | YN1
|
||||
u16 Unk68[0x10]; // 0x68 | Saved samples from last decode?
|
||||
u16 FilterState1; // 0x78 | unknown // ZWW: 0c84_FilterBufferInPlace loads and stores. Simply, the filter state.
|
||||
u16 FilterState2; // 0x79 | unknown // ZWW: same as above. these two are active if 0x04a8 != 0.
|
||||
u16 Unk7A; // 0x7A | unknown
|
||||
u16 Unk7B; // 0x7B | unknown
|
||||
u16 Unk7C; // 0x7C | unknown
|
||||
u16 Unk7D; // 0x7D | unknown
|
||||
u16 Unk7E; // 0x7E | unknown
|
||||
u16 Unk7F; // 0x7F | unknown
|
||||
|
||||
// Read-only part
|
||||
u16 Format; // 0x80 | audio format
|
||||
u16 RepeatMode; // 0x81 | 0 = one-shot, non zero = loop
|
||||
u16 LoopYN1; // 0x82 | YN1 reload (when AFC loops)
|
||||
u16 LoopYN2; // 0x83 | YN2 reload (when AFC loops)
|
||||
u16 Unk84; // 0x84 | IIR Filter # coefs?
|
||||
u16 StopOnSilence; // 0x85 | Stop on silence? (Flag for something volume related. Decides the weird stuff at 035a/ZWW, alco 0cd3)
|
||||
u16 Unk86; // 0x86 | unknown
|
||||
u16 Unk87; // 0x87 | unknown
|
||||
u32 LoopStartPos; // 0x88 | loopstart pos
|
||||
u32 Length; // 0x8A | sound length
|
||||
u32 StartAddr; // 0x8C | sound start address
|
||||
u32 UnkAddr; // 0x8E | ???
|
||||
u16 Padding[0x10]; // 0x90 | padding
|
||||
u16 Padding2[0x8]; // 0xa0 | FIR filter coefs of some sort (0xa4 controls the appearance of 0xa5-0xa7 and is almost always 0x7FFF)
|
||||
u16 FilterEnable; // 0xa8 | FilterBufferInPlace enable
|
||||
u16 Padding3[0x7]; // 0xa9 | padding
|
||||
u16 Padding4[0x10]; // 0xb0 | padding
|
||||
};
|
||||
u16 raw[0xc0]; // WARNING-do not use on parts of the 32-bit values - they are swapped!
|
||||
};
|
||||
|
||||
union ZeldaUnkPB
|
||||
{
|
||||
struct
|
||||
{
|
||||
u16 Control; // 0x00 | control
|
||||
u16 Unk01; // 0x01 | unknown
|
||||
u32 SrcAddr; // 0x02 | some address
|
||||
u16 Unk04[0xC]; // 0x04 | unknown
|
||||
};
|
||||
u16 raw[16];
|
||||
};
|
||||
|
||||
namespace {
|
||||
// If this miscompiles, adjust the size of ZeldaVoicePB to 0x180 bytes (0xc0 shorts).
|
||||
CompileTimeAssert<sizeof(ZeldaVoicePB) == 0x180> volatile ensure_zpb_size_correct;
|
||||
} // namespace
|
||||
|
||||
class CUCode_Zelda : public IUCode
|
||||
{
|
||||
public:
|
||||
CUCode_Zelda(CMailHandler& _rMailHandler, u32 _CRC);
|
||||
virtual ~CUCode_Zelda();
|
||||
|
||||
void HandleMail(u32 _uMail);
|
||||
void HandleMail_LightVersion(u32 _uMail);
|
||||
void HandleMail_SMSVersion(u32 _uMail);
|
||||
void HandleMail_NormalVersion(u32 _uMail);
|
||||
|
||||
void Update(int cycles);
|
||||
void MixAdd(short* buffer, int size);
|
||||
|
||||
void CopyPBsFromRAM();
|
||||
void CopyPBsToRAM();
|
||||
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
int *templbuffer;
|
||||
int *temprbuffer;
|
||||
|
||||
// Simple dump ...
|
||||
int DumpAFC(u8* pIn, const int size, const int srate);
|
||||
|
||||
u32 Read32()
|
||||
{
|
||||
u32 res = *(u32*)&m_Buffer[m_readOffset];
|
||||
m_readOffset += 4;
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
// These map CRC to behaviour.
|
||||
|
||||
// DMA version
|
||||
// - sound data transferred using DMA instead of accelerator
|
||||
bool IsDMAVersion() const
|
||||
{
|
||||
switch (m_CRC)
|
||||
{
|
||||
case 0xb7eb9a9c: // Wii Pikmin - PAL
|
||||
case 0xeaeb38cc: // Wii Pikmin 2 - PAL
|
||||
case 0x6c3f6f94: // Wii Zelda TP - PAL
|
||||
case 0xD643001F: // Super Mario Galaxy
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Light version
|
||||
// - slightly different communication protocol (no list begin mail)
|
||||
// - exceptions and interrupts not used
|
||||
bool IsLightVersion() const
|
||||
{
|
||||
switch (m_CRC)
|
||||
{
|
||||
case 0x6ba3b3ea: // IPL - PAL
|
||||
case 0x24b22038: // IPL - NTSC/NTSC-JAP
|
||||
case 0x42f64ac4: // Luigi
|
||||
case 0x4be6a5cb: // AC, Pikmin NTSC
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// SMS version
|
||||
// - sync mails are sent every frame, not every 16 PBs
|
||||
// (named SMS because it's used by Super Mario Sunshine
|
||||
// and I couldn't find a better name)
|
||||
bool IsSMSVersion() const
|
||||
{
|
||||
switch (m_CRC)
|
||||
{
|
||||
case 0x56d36052: // Super Mario Sunshine
|
||||
case 0x267fd05a: // Pikmin PAL
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
u32 m_CRC;
|
||||
|
||||
// These are the only dynamically allocated things allowed in the ucode.
|
||||
s32* m_VoiceBuffer;
|
||||
s16* m_ResampleBuffer;
|
||||
s32* m_LeftBuffer;
|
||||
s32* m_RightBuffer;
|
||||
|
||||
|
||||
// If you add variables, remember to keep DoState() and the constructor up to date.
|
||||
|
||||
s16 m_AFCCoefTable[32];
|
||||
s16 m_MiscTable[0x280];
|
||||
|
||||
bool m_bSyncInProgress;
|
||||
u32 m_MaxVoice;
|
||||
u32 m_SyncFlags[16];
|
||||
|
||||
// Used by SMS version
|
||||
u32 m_NumSyncMail;
|
||||
|
||||
u32 m_NumVoices;
|
||||
|
||||
bool m_bSyncCmdPending;
|
||||
u32 m_CurVoice;
|
||||
u32 m_CurBuffer;
|
||||
u32 m_NumBuffers;
|
||||
|
||||
// Those are set by command 0x1 (DsetupTable)
|
||||
u32 m_VoicePBsAddr;
|
||||
u32 m_UnkTableAddr;
|
||||
u32 m_AFCCoefTableAddr;
|
||||
u32 m_ReverbPBsAddr;
|
||||
|
||||
u32 m_RightBuffersAddr;
|
||||
u32 m_LeftBuffersAddr;
|
||||
//u32 m_unkAddr;
|
||||
u32 m_pos;
|
||||
|
||||
// Only in SMG ucode
|
||||
// Set by command 0xE (DsetDMABaseAddr)
|
||||
u32 m_DMABaseAddr;
|
||||
|
||||
// List, buffer management =====================
|
||||
u32 m_numSteps;
|
||||
bool m_bListInProgress;
|
||||
u32 m_step;
|
||||
u8 m_Buffer[1024];
|
||||
|
||||
u32 m_readOffset;
|
||||
|
||||
enum EMailState
|
||||
{
|
||||
WaitForMail,
|
||||
ReadingFrameSync,
|
||||
ReadingMessage,
|
||||
ReadingSystemMsg
|
||||
};
|
||||
|
||||
EMailState m_MailState;
|
||||
u16 m_PBMask[0x10];
|
||||
|
||||
u32 m_NumPBs;
|
||||
u32 m_PBAddress; // The main param block array
|
||||
u32 m_PBAddress2; // 4 smaller param blocks
|
||||
|
||||
void ExecuteList();
|
||||
|
||||
u8 *GetARAMPointer(u32 address);
|
||||
|
||||
// AFC decoder
|
||||
static void AFCdecodebuffer(const s16 *coef, const char *input, signed short *out, short *histp, short *hist2p, int type);
|
||||
|
||||
void ReadVoicePB(u32 _Addr, ZeldaVoicePB& PB);
|
||||
void WritebackVoicePB(u32 _Addr, ZeldaVoicePB& PB);
|
||||
|
||||
// Voice formats
|
||||
void RenderSynth_Constant(ZeldaVoicePB &PB, s32* _Buffer, int _Size);
|
||||
void RenderSynth_RectWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size);
|
||||
void RenderSynth_SawWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size);
|
||||
void RenderSynth_WaveTable(ZeldaVoicePB &PB, s32* _Buffer, int _Size);
|
||||
|
||||
void RenderVoice_PCM8(ZeldaVoicePB& PB, s16* _Buffer, int _Size);
|
||||
void RenderVoice_PCM16(ZeldaVoicePB& PB, s16* _Buffer, int _Size);
|
||||
|
||||
void RenderVoice_AFC(ZeldaVoicePB& PB, s16* _Buffer, int _Size);
|
||||
void RenderVoice_Raw(ZeldaVoicePB& PB, s16* _Buffer, int _Size);
|
||||
|
||||
void Resample(ZeldaVoicePB &PB, int size, s16 *in, s32 *out, bool do_resample = false);
|
||||
|
||||
int ConvertRatio(int pb_ratio);
|
||||
int SizeForResampling(ZeldaVoicePB &PB, int size, int ratio);
|
||||
|
||||
// Renders a voice and mixes it into LeftBuffer, RightBuffer
|
||||
void RenderAddVoice(ZeldaVoicePB& PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
84
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda_ADPCM.cpp
Normal file
84
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda_ADPCM.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "UCode_Zelda.h"
|
||||
|
||||
void CUCode_Zelda::AFCdecodebuffer(const s16 *coef, const char *src, signed short *out, short *histp, short *hist2p, int type)
|
||||
{
|
||||
// First 2 nibbles are ADPCM scale etc.
|
||||
short delta = 1 << (((*src) >> 4) & 0xf);
|
||||
short idx = (*src) & 0xf;
|
||||
src++;
|
||||
|
||||
short nibbles[16];
|
||||
if (type == 9)
|
||||
{
|
||||
for (int i = 0; i < 16; i += 2)
|
||||
{
|
||||
nibbles[i + 0] = *src >> 4;
|
||||
nibbles[i + 1] = *src & 15;
|
||||
src++;
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (nibbles[i] >= 8)
|
||||
nibbles[i] = nibbles[i] - 16;
|
||||
nibbles[i] <<= 11;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// In Pikmin, Dolphin's engine sound is using AFC type 5, even though such a sound is hard
|
||||
// to compare, it seems like to sound exactly like a real GC
|
||||
// In Super Mario Sunshine, you can get such a sound by talking to/jumping on anyone
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
nibbles[i + 0] = (*src >> 6) & 0x02;
|
||||
nibbles[i + 1] = (*src >> 4) & 0x02;
|
||||
nibbles[i + 2] = (*src >> 2) & 0x02;
|
||||
nibbles[i + 3] = (*src >> 0) & 0x02;
|
||||
src++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (nibbles[i] >= 2)
|
||||
nibbles[i] = nibbles[i] - 4;
|
||||
nibbles[i] <<= 13;
|
||||
}
|
||||
}
|
||||
|
||||
short hist = *histp;
|
||||
short hist2 = *hist2p;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
int sample = delta * nibbles[i] + ((int)hist * coef[idx * 2]) + ((int)hist2 * coef[idx * 2 + 1]);
|
||||
if (type == 9)
|
||||
sample >>= 11;
|
||||
else
|
||||
sample >>= 13;
|
||||
if (sample > 32767)
|
||||
sample = 32767;
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
out[i] = sample;
|
||||
hist2 = hist;
|
||||
hist = (short)sample;
|
||||
}
|
||||
*histp = hist;
|
||||
*hist2p = hist2;
|
||||
}
|
180
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda_Obsolete.txt
Normal file
180
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda_Obsolete.txt
Normal file
@ -0,0 +1,180 @@
|
||||
|
||||
#if 0
|
||||
void CUCode_Zelda::UpdatePB(ZPB& _rPB, int *templbuffer, int *temprbuffer, u32 _Size)
|
||||
{
|
||||
u16* pTest = (u16*)&_rPB;
|
||||
|
||||
// Checks at 0293
|
||||
if (pTest[0x00] == 0)
|
||||
return;
|
||||
|
||||
if (pTest[0x01] != 0)
|
||||
return;
|
||||
|
||||
|
||||
if (pTest[0x06] != 0x00)
|
||||
{
|
||||
// probably pTest[0x06] == 0 -> AFC (and variants)
|
||||
// See 02a4
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (_rPB.type) // or Bytes per Sample
|
||||
{
|
||||
case 0x05:
|
||||
case 0x09:
|
||||
{
|
||||
// initialize "decoder" if the sample is played the first time
|
||||
if (pTest[0x04] != 0)
|
||||
{
|
||||
// This is 0717_ReadOutPBStuff
|
||||
|
||||
// increment 4fb
|
||||
|
||||
// zelda:
|
||||
// perhaps init or "has played before"
|
||||
pTest[0x32] = 0x00;
|
||||
pTest[0x66] = 0x00; // history1
|
||||
pTest[0x67] = 0x00; // history2
|
||||
|
||||
// samplerate? length? num of samples? i dunno...
|
||||
// Likely length...
|
||||
pTest[0x3a] = pTest[0x8a];
|
||||
pTest[0x3b] = pTest[0x8b];
|
||||
|
||||
// Copy ARAM addr from r to rw area.
|
||||
pTest[0x38] = pTest[0x8c];
|
||||
pTest[0x39] = pTest[0x8d];
|
||||
}
|
||||
|
||||
if (pTest[0x01] != 0) // 0747 early out... i dunno if this can happen because we filter it above
|
||||
return;
|
||||
|
||||
u32 ARAMAddr = (pTest[0x38] << 16) | pTest[0x39];
|
||||
u32 NumberOfSamples = (pTest[0x3a] << 16) | pTest[0x3b];
|
||||
|
||||
// round upwards how many samples we need to copy, 0759
|
||||
NumberOfSamples = (NumberOfSamples + 0xf) >> 4; // i think the lower 4 are the fraction
|
||||
u32 frac = NumberOfSamples & 0xF;
|
||||
|
||||
u8 inBuffer[9];
|
||||
short outbuf[16];
|
||||
u32 sampleCount = 0;
|
||||
|
||||
// It must be something like this:
|
||||
|
||||
// The PB contains a small sample buffer of 0x4D decoded samples.
|
||||
// If it's empty or "used", decode to it.
|
||||
// Then, resample from this buffer to the output as you go. When it needs
|
||||
// wrapping, decode more.
|
||||
|
||||
#define USE_RESAMPLE
|
||||
#if !defined(USE_RESAMPLE)
|
||||
for (int s = 0; s < _Size/16; s++)
|
||||
{
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
inBuffer[i] = DSP::ReadARAM(ARAMAddr);
|
||||
ARAMAddr++;
|
||||
}
|
||||
|
||||
AFCdecodebuffer((char*)inBuffer, outbuf, (short*)&pTest[0x66], (short*)&pTest[0x67]);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
templbuffer[sampleCount] += outbuf[i];
|
||||
temprbuffer[sampleCount] += outbuf[i];
|
||||
sampleCount++;
|
||||
}
|
||||
|
||||
NumberOfSamples--;
|
||||
|
||||
if (NumberOfSamples<=0)
|
||||
break;
|
||||
}
|
||||
#else
|
||||
while (NumberOfSamples > 0)
|
||||
{
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
inBuffer[i] = DSP::ReadARAM(ARAMAddr);
|
||||
ARAMAddr++;
|
||||
}
|
||||
|
||||
AFCdecodebuffer(m_AFCCoefTable, (char*)inBuffer, outbuf, (short*)&pTest[0x66], (short*)&pTest[0x67], 9);
|
||||
CResampler Sampler(outbuf, 16, 48000);
|
||||
|
||||
while (Sampler.m_queueSize > 0)
|
||||
{
|
||||
int sample = Sampler.sample_queue.front();
|
||||
Sampler.sample_queue.pop();
|
||||
Sampler.m_queueSize -= 1;
|
||||
|
||||
templbuffer[sampleCount] += sample;
|
||||
temprbuffer[sampleCount] += sample;
|
||||
sampleCount++;
|
||||
|
||||
if (sampleCount > _Size)
|
||||
break;
|
||||
}
|
||||
|
||||
if (sampleCount > _Size)
|
||||
break;
|
||||
|
||||
NumberOfSamples--;
|
||||
}
|
||||
#endif
|
||||
if (NumberOfSamples == 0)
|
||||
{
|
||||
pTest[0x01] = 1; // we are done ??
|
||||
}
|
||||
|
||||
// write back
|
||||
NumberOfSamples = (NumberOfSamples << 4); // missing fraction
|
||||
|
||||
pTest[0x38] = ARAMAddr >> 16;
|
||||
pTest[0x39] = ARAMAddr & 0xFFFF;
|
||||
pTest[0x3a] = NumberOfSamples >> 16;
|
||||
pTest[0x3b] = NumberOfSamples & 0xFFFF;
|
||||
|
||||
|
||||
#if 0
|
||||
NumberOfSamples = (NumberOfSamples + 0xf) >> 4;
|
||||
|
||||
static u8 Buffer[500000];
|
||||
for (int i =0; i<NumberOfSamples*9; i++)
|
||||
{
|
||||
Buffer[i] = DSP::ReadARAM(ARAMAddr+i);
|
||||
}
|
||||
|
||||
// yes, the dumps are really zelda sound ;)
|
||||
DumpAFC(Buffer, NumberOfSamples*9, 0x3d00);
|
||||
|
||||
DumpPB(_rPB);
|
||||
|
||||
// exit(1);
|
||||
#endif
|
||||
|
||||
// i think pTest[0x3a] and pTest[0x3b] got an update after you have decoded some samples...
|
||||
// just decrement them with the number of samples you have played
|
||||
// and incrrease the ARAM Offset in pTest[0x38], pTest[0x39]
|
||||
|
||||
|
||||
// end of block (Zelda 03b2)
|
||||
if (pTest[0x06] == 0)
|
||||
{
|
||||
// 02a4
|
||||
//
|
||||
|
||||
pTest[0x04] = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(DSPHLE, "Zelda Ucode: Unknown PB type %i", _rPB.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
188
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda_Synth.cpp
Normal file
188
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda_Synth.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "../DSPHLEGlobals.h"
|
||||
#include "UCodes.h"
|
||||
#include "UCode_Zelda.h"
|
||||
#include "AudioCommon.h"
|
||||
|
||||
#include "Mixer.h"
|
||||
|
||||
void CUCode_Zelda::RenderSynth_RectWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size)
|
||||
{
|
||||
float _ratioFactor = 32000.0f / (float)soundStream->GetMixer()->GetSampleRate();
|
||||
u32 _ratio = (PB.RatioInt << 16);
|
||||
s64 ratio = (s64)((_ratio * _ratioFactor) * 16);
|
||||
s64 TrueSamplePosition = PB.CurSampleFrac;
|
||||
|
||||
// PB.Format == 0x3 -> Rectangular Wave, 0x0 -> Square Wave
|
||||
unsigned int mask = PB.Format ? 3 : 1;
|
||||
// int shift = PB.Format ? 2 : 1; // Unused?
|
||||
|
||||
u32 pos[2] = {0, 0};
|
||||
int i = 0;
|
||||
|
||||
if (PB.KeyOff != 0)
|
||||
return;
|
||||
|
||||
if (PB.NeedsReset)
|
||||
{
|
||||
PB.RemLength = PB.Length - PB.RestartPos;
|
||||
PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1);
|
||||
PB.ReachedEnd = 0;
|
||||
}
|
||||
|
||||
_lRestart:
|
||||
if (PB.ReachedEnd)
|
||||
{
|
||||
PB.ReachedEnd = 0;
|
||||
|
||||
if (PB.RepeatMode == 0)
|
||||
{
|
||||
PB.KeyOff = 1;
|
||||
PB.RemLength = 0;
|
||||
PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1) + PB.Length;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
PB.RestartPos = PB.LoopStartPos;
|
||||
PB.RemLength = PB.Length - PB.RestartPos;
|
||||
PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1);
|
||||
pos[1] = 0; pos[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while(i < _Size)
|
||||
{
|
||||
s16 sample = ((pos[1] & mask) == mask) ? 0xc000 : 0x4000;
|
||||
|
||||
TrueSamplePosition += (ratio >> 16);
|
||||
|
||||
_Buffer[i++] = (s32)sample;
|
||||
|
||||
(*(u64*)&pos) += ratio;
|
||||
if ((pos[1] + ((PB.CurAddr - PB.StartAddr) >> 1)) >= PB.Length)
|
||||
{
|
||||
PB.ReachedEnd = 1;
|
||||
goto _lRestart;
|
||||
}
|
||||
}
|
||||
|
||||
if (PB.RemLength < pos[1])
|
||||
{
|
||||
PB.RemLength = 0;
|
||||
PB.ReachedEnd = 1;
|
||||
}
|
||||
else
|
||||
PB.RemLength -= pos[1];
|
||||
|
||||
PB.CurSampleFrac = TrueSamplePosition & 0xFFFF;
|
||||
}
|
||||
|
||||
void CUCode_Zelda::RenderSynth_SawWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size)
|
||||
{
|
||||
s32 ratio = (s32)ceil((float)PB.RatioInt / 3);
|
||||
s64 pos = PB.CurSampleFrac;
|
||||
|
||||
for (int i = 0; i < _Size; i++)
|
||||
{
|
||||
pos += ratio;
|
||||
_Buffer[i] = pos & 0xFFFF;
|
||||
}
|
||||
|
||||
PB.CurSampleFrac = pos & 0xFFFF;
|
||||
}
|
||||
|
||||
void CUCode_Zelda::RenderSynth_Constant(ZeldaVoicePB &PB, s32* _Buffer, int _Size)
|
||||
{
|
||||
// TODO: Header, footer
|
||||
for (int i = 0; i < _Size; i++)
|
||||
_Buffer[i] = (s32)PB.RatioInt;
|
||||
}
|
||||
|
||||
// A piece of code from LLE so we can see how the wrap register affects the sound
|
||||
|
||||
inline u16 AddValueToReg(u32 ar, s32 ix)
|
||||
{
|
||||
u32 wr = 0x3f;
|
||||
u32 mx = (wr | 1) << 1;
|
||||
u32 nar = ar + ix;
|
||||
u32 dar = (nar ^ ar ^ ix) & mx;
|
||||
|
||||
if (ix >= 0)
|
||||
{
|
||||
if (dar > wr) //overflow
|
||||
nar -= wr + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((((nar + wr + 1) ^ nar) & dar) <= wr) //underflow or below min for mask
|
||||
nar += wr + 1;
|
||||
}
|
||||
return nar;
|
||||
}
|
||||
|
||||
void CUCode_Zelda::RenderSynth_WaveTable(ZeldaVoicePB &PB, s32* _Buffer, int _Size)
|
||||
{
|
||||
u16 address;
|
||||
switch(PB.Format) {
|
||||
default:
|
||||
case 0x0004:
|
||||
address = 0x140;
|
||||
break;
|
||||
|
||||
case 0x0007:
|
||||
address = 0x100;
|
||||
break;
|
||||
|
||||
case 0x000b:
|
||||
address = 0x180;
|
||||
break;
|
||||
|
||||
case 0x000c:
|
||||
address = 0x1c0;
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Resample this!
|
||||
INFO_LOG(DSPHLE, "Synthesizing the incomplete format 0x%04x", PB.Format);
|
||||
|
||||
u64 ACC0 = PB.CurSampleFrac << 6;
|
||||
|
||||
ACC0 &= 0xffff003fffffULL;
|
||||
|
||||
address = AddValueToReg(address, ((ACC0 >> 16) & 0xffff));
|
||||
ACC0 &= 0xffff0000ffffULL;
|
||||
|
||||
for(int i = 0; i < 0x50; i++)
|
||||
{
|
||||
_Buffer[i] = m_MiscTable[address];
|
||||
|
||||
ACC0 += PB.RatioInt << 5;
|
||||
address = AddValueToReg(address, ((ACC0 >> 16) & 0xffff));
|
||||
|
||||
ACC0 &= 0xffff0000ffffULL;
|
||||
}
|
||||
|
||||
ACC0 += address << 16;
|
||||
PB.CurSampleFrac = (ACC0 >> 6) & 0xffff;
|
||||
}
|
||||
|
||||
|
793
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda_Voice.cpp
Normal file
793
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_Zelda_Voice.cpp
Normal file
@ -0,0 +1,793 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "../DSPHLEGlobals.h"
|
||||
#include "UCodes.h"
|
||||
#include "UCode_Zelda.h"
|
||||
|
||||
// #include "../main.h"
|
||||
#include "AudioCommon.h"
|
||||
#include "Mixer.h"
|
||||
#include "../../Memmap.h"
|
||||
#include "../../DSP.h"
|
||||
|
||||
void CUCode_Zelda::ReadVoicePB(u32 _Addr, ZeldaVoicePB& PB)
|
||||
{
|
||||
u16 *memory = (u16*)Memory::GetPointer(_Addr);
|
||||
|
||||
// Perform byteswap
|
||||
for (int i = 0; i < (0x180 / 2); i++)
|
||||
((u16*)&PB)[i] = Common::swap16(memory[i]);
|
||||
|
||||
// Word swap all 32-bit variables.
|
||||
PB.RestartPos = (PB.RestartPos << 16) | (PB.RestartPos >> 16);
|
||||
PB.CurAddr = (PB.CurAddr << 16) | (PB.CurAddr >> 16);
|
||||
PB.RemLength = (PB.RemLength << 16) | (PB.RemLength >> 16);
|
||||
// Read only part
|
||||
PB.LoopStartPos = (PB.LoopStartPos << 16) | (PB.LoopStartPos >> 16);
|
||||
PB.Length = (PB.Length << 16) | (PB.Length >> 16);
|
||||
PB.StartAddr = (PB.StartAddr << 16) | (PB.StartAddr >> 16);
|
||||
PB.UnkAddr = (PB.UnkAddr << 16) | (PB.UnkAddr >> 16);
|
||||
}
|
||||
|
||||
void CUCode_Zelda::WritebackVoicePB(u32 _Addr, ZeldaVoicePB& PB)
|
||||
{
|
||||
u16 *memory = (u16*)Memory::GetPointer(_Addr);
|
||||
|
||||
// Word swap all 32-bit variables.
|
||||
PB.RestartPos = (PB.RestartPos << 16) | (PB.RestartPos >> 16);
|
||||
PB.CurAddr = (PB.CurAddr << 16) | (PB.CurAddr >> 16);
|
||||
PB.RemLength = (PB.RemLength << 16) | (PB.RemLength >> 16);
|
||||
|
||||
// Perform byteswap
|
||||
// Only the first 0x100 bytes are written back
|
||||
for (int i = 0; i < (0x100 / 2); i++)
|
||||
memory[i] = Common::swap16(((u16*)&PB)[i]);
|
||||
}
|
||||
|
||||
int CUCode_Zelda::ConvertRatio(int pb_ratio)
|
||||
{
|
||||
float _ratioFactor = 32000.0f / (float)soundStream->GetMixer()->GetSampleRate();
|
||||
u32 _ratio = (pb_ratio << 16);
|
||||
return (u64)((_ratio * _ratioFactor) * 16) >> 16;
|
||||
}
|
||||
|
||||
int CUCode_Zelda::SizeForResampling(ZeldaVoicePB &PB, int size, int ratio) {
|
||||
// This is the little calculation at the start of every sample decoder
|
||||
// in the ucode.
|
||||
return (PB.CurSampleFrac + size * ConvertRatio(PB.RatioInt)) >> 16;
|
||||
}
|
||||
|
||||
// Simple resampler, linear interpolation.
|
||||
// Any future state should be stored in PB.raw[0x3c to 0x3f].
|
||||
// In must point 4 samples into a buffer.
|
||||
void CUCode_Zelda::Resample(ZeldaVoicePB &PB, int size, s16 *in, s32 *out, bool do_resample)
|
||||
{
|
||||
if (!do_resample)
|
||||
{
|
||||
memcpy(out, in, size * sizeof(int));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
in[i - 4] = (s16)PB.ResamplerOldData[i];
|
||||
}
|
||||
|
||||
int ratio = ConvertRatio(PB.RatioInt);
|
||||
int in_size = SizeForResampling(PB, size, ratio);
|
||||
|
||||
int position = PB.CurSampleFrac;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
int int_pos = (position >> 16);
|
||||
int frac = ((position & 0xFFFF) >> 1);
|
||||
out[i] = (in[int_pos - 3] * (frac ^ 0x7FFF) + in[int_pos - 2] * frac) >> 15;
|
||||
position += ratio;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
PB.ResamplerOldData[i] = (u16)(s16)in[in_size - 4 + i];
|
||||
}
|
||||
PB.CurSampleFrac = position & 0xFFFF;
|
||||
}
|
||||
|
||||
void UpdateSampleCounters10(ZeldaVoicePB &PB)
|
||||
{
|
||||
PB.RemLength = PB.Length - PB.RestartPos;
|
||||
PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1);
|
||||
PB.ReachedEnd = 0;
|
||||
}
|
||||
|
||||
void CUCode_Zelda::RenderVoice_PCM16(ZeldaVoicePB &PB, s16 *_Buffer, int _Size)
|
||||
{
|
||||
int _RealSize = SizeForResampling(PB, _Size, PB.RatioInt);
|
||||
u32 rem_samples = _RealSize;
|
||||
if (PB.KeyOff)
|
||||
goto clear_buffer;
|
||||
if (PB.NeedsReset)
|
||||
{
|
||||
UpdateSampleCounters10(PB);
|
||||
for (int i = 0; i < 4; i++)
|
||||
PB.ResamplerOldData[i] = 0; // Doesn't belong here, but dunno where to do it.
|
||||
}
|
||||
if (PB.ReachedEnd)
|
||||
{
|
||||
PB.ReachedEnd = 0;
|
||||
reached_end:
|
||||
if (!PB.RepeatMode) {
|
||||
// One shot - play zeros the rest of the buffer.
|
||||
clear_buffer:
|
||||
for (u32 i = 0; i < rem_samples; i++)
|
||||
*_Buffer++ = 0;
|
||||
PB.KeyOff = 1;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
PB.RestartPos = PB.LoopStartPos;
|
||||
UpdateSampleCounters10(PB);
|
||||
}
|
||||
}
|
||||
// SetupAccelerator
|
||||
const s16 *read_ptr = (s16*)GetARAMPointer(PB.CurAddr);
|
||||
if (PB.RemLength < (u32)rem_samples)
|
||||
{
|
||||
// finish-up loop
|
||||
for (u32 i = 0; i < PB.RemLength; i++)
|
||||
*_Buffer++ = Common::swap16(*read_ptr++);
|
||||
rem_samples -= PB.RemLength;
|
||||
goto reached_end;
|
||||
}
|
||||
// main render loop
|
||||
for (u32 i = 0; i < rem_samples; i++)
|
||||
*_Buffer++ = Common::swap16(*read_ptr++);
|
||||
|
||||
PB.RemLength -= rem_samples;
|
||||
if (PB.RemLength == 0)
|
||||
PB.ReachedEnd = 1;
|
||||
PB.CurAddr += rem_samples << 1;
|
||||
}
|
||||
|
||||
void UpdateSampleCounters8(ZeldaVoicePB &PB)
|
||||
{
|
||||
PB.RemLength = PB.Length - PB.RestartPos;
|
||||
PB.CurAddr = PB.StartAddr + PB.RestartPos;
|
||||
PB.ReachedEnd = 0;
|
||||
}
|
||||
|
||||
void CUCode_Zelda::RenderVoice_PCM8(ZeldaVoicePB &PB, s16 *_Buffer, int _Size)
|
||||
{
|
||||
int _RealSize = SizeForResampling(PB, _Size, PB.RatioInt);
|
||||
u32 rem_samples = _RealSize;
|
||||
if (PB.KeyOff)
|
||||
goto clear_buffer;
|
||||
if (PB.NeedsReset)
|
||||
{
|
||||
UpdateSampleCounters8(PB);
|
||||
for (int i = 0; i < 4; i++)
|
||||
PB.ResamplerOldData[i] = 0; // Doesn't belong here, but dunno where to do it.
|
||||
}
|
||||
if (PB.ReachedEnd)
|
||||
{
|
||||
reached_end:
|
||||
PB.ReachedEnd = 0;
|
||||
if (!PB.RepeatMode)
|
||||
{
|
||||
// One shot - play zeros the rest of the buffer.
|
||||
clear_buffer:
|
||||
for (u32 i = 0; i < rem_samples; i++)
|
||||
*_Buffer++ = 0;
|
||||
PB.KeyOff = 1;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
PB.RestartPos = PB.LoopStartPos;
|
||||
UpdateSampleCounters8(PB);
|
||||
}
|
||||
}
|
||||
|
||||
// SetupAccelerator
|
||||
const s8 *read_ptr = (s8*)GetARAMPointer(PB.CurAddr);
|
||||
if (PB.RemLength < (u32)rem_samples)
|
||||
{
|
||||
// finish-up loop
|
||||
for (u32 i = 0; i < PB.RemLength; i++)
|
||||
*_Buffer++ = (s8)(*read_ptr++) << 8;
|
||||
rem_samples -= PB.RemLength;
|
||||
goto reached_end;
|
||||
}
|
||||
// main render loop
|
||||
for (u32 i = 0; i < rem_samples; i++)
|
||||
*_Buffer++ = (s8)(*read_ptr++) << 8;
|
||||
|
||||
PB.RemLength -= rem_samples;
|
||||
if (PB.RemLength == 0)
|
||||
PB.ReachedEnd = 1;
|
||||
PB.CurAddr += rem_samples;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PrintObject(const T &Obj)
|
||||
{
|
||||
char byte[2] = {0};
|
||||
std::stringstream ss;
|
||||
u8 *o = (u8 *)&Obj;
|
||||
for(int i = 0; i < sizeof(T); i++) {
|
||||
if((i > 0) && ((i & 1) == 0))
|
||||
ss << " ";
|
||||
|
||||
sprintf(byte, "%02X", Common::swap16(o[i]));
|
||||
ss << byte;
|
||||
}
|
||||
|
||||
DEBUG_LOG(DSPHLE, "AFC PB: %s", ss.str().c_str());
|
||||
}
|
||||
|
||||
void CUCode_Zelda::RenderVoice_AFC(ZeldaVoicePB &PB, s16 *_Buffer, int _Size)
|
||||
{
|
||||
// TODO: Compare mono, stereo and surround samples
|
||||
#if defined DEBUG || defined DEBUGFAST
|
||||
PrintObject(PB);
|
||||
#endif
|
||||
|
||||
int _RealSize = SizeForResampling(PB, _Size, PB.RatioInt);
|
||||
|
||||
// initialize "decoder" if the sample is played the first time
|
||||
if (PB.NeedsReset != 0)
|
||||
{
|
||||
// This is 0717_ReadOutPBStuff
|
||||
// increment 4fb
|
||||
// zelda:
|
||||
// perhaps init or "has played before"
|
||||
PB.CurBlock = 0x00;
|
||||
PB.YN2 = 0x00; // history1
|
||||
PB.YN1 = 0x00; // history2
|
||||
|
||||
// Length in samples.
|
||||
PB.RemLength = PB.Length;
|
||||
// Copy ARAM addr from r to rw area.
|
||||
PB.CurAddr = PB.StartAddr;
|
||||
PB.ReachedEnd = 0;
|
||||
PB.CurSampleFrac = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
PB.ResamplerOldData[i] = 0;
|
||||
}
|
||||
|
||||
if (PB.KeyOff != 0) // 0747 early out... i dunno if this can happen because we filter it above
|
||||
{
|
||||
for (int i = 0; i < _RealSize; i++)
|
||||
*_Buffer++ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Round upwards how many samples we need to copy, 0759
|
||||
// u32 frac = NumberOfSamples & 0xF;
|
||||
// NumberOfSamples = (NumberOfSamples + 0xf) >> 4; // i think the lower 4 are the fraction
|
||||
|
||||
const u8 *source;
|
||||
u32 ram_mask = 1024 * 1024 * 16 - 1;
|
||||
if (IsDMAVersion()) {
|
||||
source = Memory::GetPointer(m_DMABaseAddr);
|
||||
ram_mask = 1024 * 1024 * 64 - 1;
|
||||
}
|
||||
else
|
||||
source = DSP::GetARAMPtr();
|
||||
|
||||
int sampleCount = 0; // must be above restart.
|
||||
|
||||
restart:
|
||||
if (PB.ReachedEnd)
|
||||
{
|
||||
PB.ReachedEnd = 0;
|
||||
|
||||
if ((PB.RepeatMode == 0) || (!PB.StopOnSilence == 0))
|
||||
{
|
||||
PB.KeyOff = 1;
|
||||
PB.RemLength = 0;
|
||||
PB.CurAddr = PB.StartAddr + PB.RestartPos + PB.Length;
|
||||
|
||||
while (sampleCount < _RealSize)
|
||||
_Buffer[sampleCount++] = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//AFC looping
|
||||
// The loop start pos is incorrect? (Fixed?), so samples will loop a bit wrong.
|
||||
// this fixes the intro music in ZTP.
|
||||
PB.RestartPos = PB.LoopStartPos;
|
||||
PB.RemLength = PB.Length - PB.RestartPos;
|
||||
// see DSP_UC_Zelda.txt line 2817
|
||||
PB.CurAddr = ((((((PB.LoopStartPos >> 4) & 0xffff0000)*PB.Format)<<16)+
|
||||
(((PB.LoopStartPos >> 4) & 0xffff)*PB.Format))+PB.StartAddr) & 0xffffffff;
|
||||
|
||||
// Hmm, this shouldn't be reversed .. or should it? Is it different between versions of the ucode?
|
||||
// -> it has to be reversed in ZTP, otherwise intro music is broken...
|
||||
PB.YN1 = PB.LoopYN2;
|
||||
PB.YN2 = PB.LoopYN1;
|
||||
}
|
||||
}
|
||||
|
||||
short outbuf[16] = {0};
|
||||
u16 prev_yn1 = PB.YN1;
|
||||
u16 prev_yn2 = PB.YN2;
|
||||
u32 prev_addr = PB.CurAddr;
|
||||
|
||||
// Prefill the decode buffer.
|
||||
AFCdecodebuffer(m_AFCCoefTable, (char*)(source + (PB.CurAddr & ram_mask)), outbuf, (short*)&PB.YN2, (short*)&PB.YN1, PB.Format);
|
||||
PB.CurAddr += PB.Format; // 9 or 5
|
||||
|
||||
u32 SamplePosition = PB.Length - PB.RemLength;
|
||||
while (sampleCount < _RealSize)
|
||||
{
|
||||
_Buffer[sampleCount] = outbuf[SamplePosition & 15];
|
||||
sampleCount++;
|
||||
|
||||
SamplePosition++;
|
||||
PB.RemLength--;
|
||||
if (PB.RemLength == 0)
|
||||
{
|
||||
PB.ReachedEnd = 1;
|
||||
goto restart;
|
||||
}
|
||||
|
||||
// Need new samples!
|
||||
if ((SamplePosition & 15) == 0) {
|
||||
prev_yn1 = PB.YN1;
|
||||
prev_yn2 = PB.YN2;
|
||||
prev_addr = PB.CurAddr;
|
||||
|
||||
AFCdecodebuffer(m_AFCCoefTable, (char*)(source + (PB.CurAddr & ram_mask)), outbuf, (short*)&PB.YN2, (short*)&PB.YN1, PB.Format);
|
||||
PB.CurAddr += PB.Format; // 9 or 5
|
||||
}
|
||||
}
|
||||
|
||||
// Here we should back off to the previous addr/yn1/yn2, since we didn't consume the full last block.
|
||||
// We'll re-decode it the next time around.
|
||||
PB.YN2 = prev_yn2;
|
||||
PB.YN1 = prev_yn1;
|
||||
PB.CurAddr = prev_addr;
|
||||
|
||||
PB.NeedsReset = 0;
|
||||
// PB.CurBlock = 0x10 - (PB.LoopStartPos & 0xf);
|
||||
// write back
|
||||
// NumberOfSamples = (NumberOfSamples << 4) | frac; // missing fraction
|
||||
|
||||
// i think pTest[0x3a] and pTest[0x3b] got an update after you have decoded some samples...
|
||||
// just decrement them with the number of samples you have played
|
||||
// and increase the ARAM Offset in pTest[0x38], pTest[0x39]
|
||||
|
||||
// end of block (Zelda 03b2)
|
||||
}
|
||||
|
||||
void Decoder21_ReadAudio(ZeldaVoicePB &PB, int size, s16 *_Buffer);
|
||||
|
||||
// Researching what's actually inside the mysterious 0x21 case
|
||||
// 0x21 seems to really just be reading raw 16-bit audio from RAM (not ARAM).
|
||||
// The rules seem to be quite different, though.
|
||||
// It's used for streaming, not for one-shot or looped sample playback.
|
||||
void CUCode_Zelda::RenderVoice_Raw(ZeldaVoicePB &PB, s16 *_Buffer, int _Size)
|
||||
{
|
||||
// Decoder0x21 starts here.
|
||||
u32 _RealSize = SizeForResampling(PB, _Size, PB.RatioInt);
|
||||
|
||||
// Decoder0x21Core starts here.
|
||||
u32 AX0 = _RealSize;
|
||||
|
||||
// ERROR_LOG(DSPHLE, "0x21 volume mode: %i , stop: %i ", PB.VolumeMode, PB.StopOnSilence);
|
||||
|
||||
// The PB.StopOnSilence check is a hack, we should check the buffers and enter this
|
||||
// only when the buffer is completely 0 (i.e. when the music has finished fading out)
|
||||
if (PB.StopOnSilence || PB.RemLength < (u32)_RealSize)
|
||||
{
|
||||
WARN_LOG(DSPHLE, "Raw: END");
|
||||
// Let's ignore this entire case since it doesn't seem to happen
|
||||
// in Zelda, since Length is set to 0xF0000000
|
||||
// blah
|
||||
// blah
|
||||
// readaudio
|
||||
// blah
|
||||
PB.RemLength = 0;
|
||||
PB.KeyOff = 1;
|
||||
}
|
||||
|
||||
PB.RemLength -= _RealSize;
|
||||
|
||||
u64 ACC0 = (u32)(PB.raw[0x8a ^ 1] << 16); // 0x8a 0ad5, yes it loads a, not b
|
||||
u64 ACC1 = (u32)(PB.raw[0x34 ^ 1] << 16); // 0x34
|
||||
|
||||
// ERROR_LOG(DSPHLE, "%08x %08x", (u32)ACC0, (u32)ACC1);
|
||||
|
||||
ACC0 -= ACC1;
|
||||
|
||||
PB.Unk36[0] = (u16)(ACC0 >> 16);
|
||||
|
||||
// This subtract does really not make much sense at all.
|
||||
ACC0 -= AX0 << 16;
|
||||
|
||||
if ((s64)ACC0 < 0)
|
||||
{
|
||||
// There's something wrong with this looping code.
|
||||
|
||||
ERROR_LOG(DSPHLE, "Raw loop: ReadAudio size = %04x 34:%04x %08x", PB.Unk36[0], PB.raw[0x34 ^ 1], (int)ACC0);
|
||||
Decoder21_ReadAudio(PB, PB.Unk36[0], _Buffer);
|
||||
|
||||
ACC0 = _Size << 16;
|
||||
ACC0 -= PB.Unk36[0] << 16;
|
||||
|
||||
PB.raw[0x34 ^ 1] = 0;
|
||||
|
||||
PB.StartAddr = PB.LoopStartPos;
|
||||
|
||||
Decoder21_ReadAudio(PB, (int)(ACC0 >> 16), _Buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
Decoder21_ReadAudio(PB, _RealSize, _Buffer);
|
||||
}
|
||||
|
||||
void Decoder21_ReadAudio(ZeldaVoicePB &PB, int size, s16 *_Buffer)
|
||||
{
|
||||
// 0af6
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
// 0afa
|
||||
u32 AX1 = (PB.RestartPos >> 16) & 1; // PB.raw[0x34], except that it's part of a dword
|
||||
// 0b00 - Eh, WTF.
|
||||
u32 ACC0 = PB.StartAddr + ((PB.RestartPos >> 16) << 1) - 2*AX1;
|
||||
u32 ACC1 = (size << 16) + 0x20000;
|
||||
// All this trickery, and more, seems to be to align the DMA, which
|
||||
// we really don't care about. So let's skip it. See the #else.
|
||||
|
||||
#else
|
||||
// ERROR_LOG(DSPHLE, "ReadAudio: %08x %08x", PB.StartAddr, PB.raw[0x34 ^ 1]);
|
||||
u32 ACC0 = PB.StartAddr + (PB.raw[0x34 ^ 1] << 1);
|
||||
u32 ACC1 = (size << 16);
|
||||
#endif
|
||||
// ACC0 is the address
|
||||
// ACC1 is the read size
|
||||
|
||||
const u32 ram_mask = 0x1FFFFFF;
|
||||
const u8 *source = Memory::GetPointer(0x80000000);
|
||||
const u16 *src = (u16 *)(source + (ACC0 & ram_mask));
|
||||
|
||||
for (u32 i = 0; i < (ACC1 >> 16); i++) {
|
||||
_Buffer[i] = Common::swap16(src[i]);
|
||||
}
|
||||
|
||||
PB.raw[0x34 ^ 1] += size;
|
||||
}
|
||||
|
||||
|
||||
void CUCode_Zelda::RenderAddVoice(ZeldaVoicePB &PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size)
|
||||
{
|
||||
if (PB.IsBlank)
|
||||
{
|
||||
s32 sample = (s32)(s16)PB.FixedSample;
|
||||
for (int i = 0; i < _Size; i++)
|
||||
m_VoiceBuffer[i] = sample;
|
||||
|
||||
goto ContinueWithBlock; // Yes, a goto. Yes, it's evil, but it makes the flow look much more like the DSP code.
|
||||
}
|
||||
|
||||
// XK: Use this to disable MIDI music (GREAT for testing). Also kills some sound FX.
|
||||
//if(PB.SoundType == 0x0d00) {
|
||||
// PB.NeedsReset = 0;
|
||||
// return;
|
||||
//}
|
||||
|
||||
// The Resample calls actually don't resample yet.
|
||||
|
||||
// ResampleBuffer corresponds to 0x0580 in ZWW ucode.
|
||||
// VoiceBuffer corresponds to 0x0520.
|
||||
|
||||
// First jump table at ZWW: 2a6
|
||||
switch (PB.Format)
|
||||
{
|
||||
case 0x0005: // AFC with extra low bitrate (32:5 compression).
|
||||
case 0x0009: // AFC with normal bitrate (32:9 compression).
|
||||
RenderVoice_AFC(PB, m_ResampleBuffer + 4, _Size);
|
||||
Resample(PB, _Size, m_ResampleBuffer + 4, m_VoiceBuffer, true);
|
||||
break;
|
||||
|
||||
case 0x0008: // PCM8 - normal PCM 8-bit audio. Used in Mario Kart DD + very little in Zelda WW.
|
||||
RenderVoice_PCM8(PB, m_ResampleBuffer + 4, _Size);
|
||||
Resample(PB, _Size, m_ResampleBuffer + 4, m_VoiceBuffer, true);
|
||||
break;
|
||||
|
||||
case 0x0010: // PCM16 - normal PCM 16-bit audio.
|
||||
RenderVoice_PCM16(PB, m_ResampleBuffer + 4, _Size);
|
||||
Resample(PB, _Size, m_ResampleBuffer + 4, m_VoiceBuffer, true);
|
||||
break;
|
||||
|
||||
case 0x0020:
|
||||
// Normally, this shouldn't resample, it should just decode directly
|
||||
// to the output buffer. However, (if we ever see this sound type), we'll
|
||||
// have to resample anyway since we're running at a different sample rate.
|
||||
|
||||
RenderVoice_Raw(PB, m_ResampleBuffer + 4, _Size);
|
||||
Resample(PB, _Size, m_ResampleBuffer + 4, m_VoiceBuffer, true);
|
||||
break;
|
||||
|
||||
case 0x0021:
|
||||
// Raw sound from RAM. Important for Zelda WW. Cutscenes use the music
|
||||
// to let the game know they ended
|
||||
RenderVoice_Raw(PB, m_ResampleBuffer + 4, _Size);
|
||||
Resample(PB, _Size, m_ResampleBuffer + 4, m_VoiceBuffer, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Second jump table
|
||||
// TODO: Cases to find examples of:
|
||||
// -0x0002
|
||||
// -0x0003
|
||||
// -0x0006
|
||||
// -0x000a
|
||||
switch (PB.Format)
|
||||
{
|
||||
// Synthesized sounds
|
||||
case 0x0003: WARN_LOG(DSPHLE, "PB Format 0x03 used!");
|
||||
case 0x0000: // Example: Magic meter filling up in ZWW
|
||||
RenderSynth_RectWave(PB, m_VoiceBuffer, _Size);
|
||||
break;
|
||||
|
||||
case 0x0001: // Example: "Denied" sound when trying to pull out a sword
|
||||
// indoors in ZWW
|
||||
RenderSynth_SawWave(PB, m_VoiceBuffer, _Size);
|
||||
break;
|
||||
|
||||
case 0x0006:
|
||||
WARN_LOG(DSPHLE, "Synthesizing 0x0006 (constant sound)");
|
||||
RenderSynth_Constant(PB, m_VoiceBuffer, _Size);
|
||||
break;
|
||||
|
||||
// These are more "synth" formats - square wave, saw wave etc.
|
||||
case 0x0002:
|
||||
WARN_LOG(DSPHLE, "PB Format 0x02 used!");
|
||||
break;
|
||||
|
||||
case 0x0004: // Example: Big Pikmin onion mothership landing/building a bridge in Pikmin
|
||||
case 0x0007: // Example: "success" SFX in Pikmin 1, Pikmin 2 in a cave, not sure what sound it is.
|
||||
case 0x000b: // Example: SFX in area selection menu in Pikmin
|
||||
case 0x000c: // Example: beam of death/yellow force-field in Temple of the Gods, ZWW
|
||||
RenderSynth_WaveTable(PB, m_VoiceBuffer, _Size);
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO: Implement general decoder here
|
||||
memset(m_VoiceBuffer, 0, _Size * sizeof(s32));
|
||||
ERROR_LOG(DSPHLE, "Unknown MixAddVoice format in zelda %04x", PB.Format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ContinueWithBlock:
|
||||
|
||||
if (PB.FilterEnable)
|
||||
{ // 0x04a8
|
||||
for (int i = 0; i < _Size; i++)
|
||||
{
|
||||
// TODO: Apply filter from ZWW: 0c84_FilterBufferInPlace
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _Size; i++)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Apply volume. There are two different modes.
|
||||
if (PB.VolumeMode != 0)
|
||||
{
|
||||
// Complex volume mode. Let's see what we can do.
|
||||
if (PB.StopOnSilence) {
|
||||
PB.raw[0x2b] = PB.raw[0x2a] >> 1;
|
||||
if (PB.raw[0x2b] == 0)
|
||||
{
|
||||
PB.KeyOff = 1;
|
||||
}
|
||||
}
|
||||
short AX0L = PB.raw[0x28] >> 8;
|
||||
short AX0H = PB.raw[0x28] & 0x7F;
|
||||
short AX1L = AX0L ^ 0x7F;
|
||||
short AX1H = AX0H ^ 0x7F;
|
||||
AX0L = m_MiscTable[0x200 + AX0L];
|
||||
AX0H = m_MiscTable[0x200 + AX0H];
|
||||
AX1L = m_MiscTable[0x200 + AX1L];
|
||||
AX1H = m_MiscTable[0x200 + AX1H];
|
||||
|
||||
short b00[20];
|
||||
b00[0] = AX1L * AX1H >> 16;
|
||||
b00[1] = AX0L * AX1H >> 16;
|
||||
b00[2] = AX0H * AX1L >> 16;
|
||||
b00[3] = AX0L * AX0H >> 16;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
b00[i + 4] = (s16)b00[i] * (s16)PB.raw[0x2a] >> 16;
|
||||
}
|
||||
|
||||
int prod = ((s16)PB.raw[0x2a] * (s16)PB.raw[0x29] * 2) >> 16;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
b00[i + 8] = (s16)b00[i + 4] * prod;
|
||||
}
|
||||
|
||||
// ZWW 0d34
|
||||
|
||||
int diff = (s16)PB.raw[0x2b] - (s16)PB.raw[0x2a];
|
||||
PB.raw[0x2a] = PB.raw[0x2b];
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
b00[i + 0xc] = (unsigned short)b00[i] * diff >> 16;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
b00[i + 0x10] = (s16)b00[i + 0xc] * PB.raw[0x29];
|
||||
}
|
||||
|
||||
for (int count = 0; count < 8; count++)
|
||||
{
|
||||
// The 8 buffers to mix to: 0d00, 0d60, 0f40 0ca0 0e80 0ee0 0c00 0c50
|
||||
// We just mix to the first two and call it stereo :p
|
||||
int value = b00[0x4 + count];
|
||||
//int delta = b00[0xC + count] << 11; // Unused?
|
||||
|
||||
int ramp = value << 16;
|
||||
for (int i = 0; i < _Size; i++)
|
||||
{
|
||||
int unmixed_audio = m_VoiceBuffer[i];
|
||||
switch (count) {
|
||||
case 0: _LeftBuffer[i] += (u64)unmixed_audio * ramp >> 29; break;
|
||||
case 1: _RightBuffer[i] += (u64)unmixed_audio * ramp >> 29; break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ZWW 0355
|
||||
if (PB.StopOnSilence)
|
||||
{
|
||||
int sum = 0;
|
||||
int addr = 0x0a;
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
u16 value = PB.raw[addr];
|
||||
addr--;
|
||||
value >>= 1;
|
||||
PB.raw[addr] = value;
|
||||
sum += value;
|
||||
addr += 5;
|
||||
}
|
||||
if (sum == 0) {
|
||||
PB.KeyOff = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Seems there are 6 temporary output buffers.
|
||||
for (int count = 0; count < 6; count++)
|
||||
{
|
||||
int addr = 0x08;
|
||||
|
||||
// we'll have to keep a map of buffers I guess...
|
||||
u16 dest_buffer_address = PB.raw[addr++];
|
||||
|
||||
bool mix = dest_buffer_address ? true : false;
|
||||
|
||||
u16 vol2 = PB.raw[addr++];
|
||||
u16 vol1 = PB.raw[addr++];
|
||||
|
||||
int delta = (vol2 - vol1) << 11;
|
||||
|
||||
addr--;
|
||||
|
||||
u32 ramp = vol1 << 16;
|
||||
if (mix)
|
||||
{
|
||||
// 0ca9_RampedMultiplyAddBuffer
|
||||
for (int i = 0; i < _Size; i++)
|
||||
{
|
||||
int value = m_VoiceBuffer[i];
|
||||
|
||||
// TODO - add to buffer specified by dest_buffer_address
|
||||
switch (count)
|
||||
{
|
||||
// These really should be 32.
|
||||
case 0: _LeftBuffer[i] += (u64)value * ramp >> 29; break;
|
||||
case 1: _RightBuffer[i] += (u64)value * ramp >> 29; break;
|
||||
}
|
||||
if (((i & 1) == 0) && i < 64) {
|
||||
ramp += delta;
|
||||
}
|
||||
}
|
||||
if (_Size < 32)
|
||||
{
|
||||
ramp += delta * (_Size - 32);
|
||||
}
|
||||
}
|
||||
// Update the PB with the volume actually reached.
|
||||
PB.raw[addr++] = ramp >> 16;
|
||||
|
||||
addr++;
|
||||
}
|
||||
}
|
||||
// 03b2, this is the reason of using PB.NeedsReset. Seems to be necessary for SMG, and maybe other games.
|
||||
if (PB.IsBlank == 0){
|
||||
PB.NeedsReset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// size is in stereo samples.
|
||||
void CUCode_Zelda::MixAdd(short *_Buffer, int _Size)
|
||||
{
|
||||
m_csMix.Enter();
|
||||
// Safety check
|
||||
if (_Size > 256 * 1024 - 8)
|
||||
_Size = 256 * 1024 - 8;
|
||||
|
||||
// Final mix buffers
|
||||
memset(m_LeftBuffer, 0, _Size * sizeof(s32));
|
||||
memset(m_RightBuffer, 0, _Size * sizeof(s32));
|
||||
|
||||
// For each PB...
|
||||
for (u32 i = 0; i < m_NumVoices; i++)
|
||||
{
|
||||
if (!IsLightVersion())
|
||||
{
|
||||
u32 flags = m_SyncFlags[(i >> 4) & 0xF];
|
||||
if (!(flags & 1 << (15 - (i & 0xF))))
|
||||
continue;
|
||||
}
|
||||
|
||||
ZeldaVoicePB pb;
|
||||
ReadVoicePB(m_VoicePBsAddr + (i * 0x180), pb);
|
||||
|
||||
if (pb.Status == 0)
|
||||
continue;
|
||||
if (pb.KeyOff != 0)
|
||||
continue;
|
||||
|
||||
RenderAddVoice(pb, m_LeftBuffer, m_RightBuffer, _Size);
|
||||
WritebackVoicePB(m_VoicePBsAddr + (i * 0x180), pb);
|
||||
}
|
||||
|
||||
// Post processing, final conversion.
|
||||
for (int i = 0; i < _Size; i++)
|
||||
{
|
||||
s32 left = (s32)_Buffer[0] + m_LeftBuffer[i];
|
||||
s32 right = (s32)_Buffer[1] + m_RightBuffer[i];
|
||||
|
||||
if (left < -32768) left = -32768;
|
||||
if (left > 32767) left = 32767;
|
||||
_Buffer[0] = (short)left;
|
||||
|
||||
if (right < -32768) right = -32768;
|
||||
if (right > 32767) right = 32767;
|
||||
_Buffer[1] = (short)right;
|
||||
|
||||
_Buffer += 2;
|
||||
}
|
||||
m_csMix.Leave();
|
||||
}
|
186
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp
Normal file
186
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "../DSPHLEGlobals.h"
|
||||
|
||||
#include "UCodes.h"
|
||||
|
||||
#include "UCode_AX.h"
|
||||
#include "UCode_AXWii.h"
|
||||
#include "UCode_Zelda.h"
|
||||
#include "UCode_ROM.h"
|
||||
#include "UCode_CARD.h"
|
||||
#include "UCode_InitAudioSystem.h"
|
||||
#include "UCode_GBA.h"
|
||||
#include "Hash.h"
|
||||
#include "../DSPHandler.h"
|
||||
|
||||
IUCode* UCodeFactory(u32 _CRC, CMailHandler& _rMailHandler, bool bWii)
|
||||
{
|
||||
switch (_CRC)
|
||||
{
|
||||
case UCODE_ROM:
|
||||
INFO_LOG(DSPHLE, "Switching to ROM ucode");
|
||||
return new CUCode_Rom(_rMailHandler);
|
||||
|
||||
case UCODE_INIT_AUDIO_SYSTEM:
|
||||
INFO_LOG(DSPHLE, "Switching to INIT ucode");
|
||||
return new CUCode_InitAudioSystem(_rMailHandler);
|
||||
|
||||
case 0x65d6cc6f: // CARD
|
||||
INFO_LOG(DSPHLE, "Switching to CARD ucode");
|
||||
return new CUCode_CARD(_rMailHandler);
|
||||
|
||||
case 0xdd7e72d5:
|
||||
INFO_LOG(DSPHLE, "Switching to GBA ucode");
|
||||
return new CUCode_GBA(_rMailHandler);
|
||||
|
||||
case 0x3ad3b7ac: // Naruto3, Paper Mario - The Thousand Year Door
|
||||
case 0x3daf59b9: // Alien Hominid
|
||||
case 0x4e8a8b21: // spdemo, ctaxi, 18 wheeler, disney, monkeyball 1/2,cubivore,puzzlecollection,wario,
|
||||
// capcom vs snk, naruto2, lost kingdoms, star fox, mario party 4, mortal kombat,
|
||||
// smugglers run warzone, smash brothers, sonic mega collection, ZooCube
|
||||
// nddemo, starfox
|
||||
case 0x07f88145: // bustamove, ikaruga, fzero, robotech battle cry, star soldier, soul calibur2,
|
||||
// Zelda:OOT, Tony hawk, viewtiful joe
|
||||
case 0xe2136399: // billy hatcher, dragonballz, mario party 5, TMNT, ava1080
|
||||
INFO_LOG(DSPHLE, "CRC %08x: AX ucode chosen", _CRC);
|
||||
return new CUCode_AX(_rMailHandler);
|
||||
|
||||
case 0x6ba3b3ea: // IPL - PAL
|
||||
case 0x24b22038: // IPL - NTSC/NTSC-JAP
|
||||
case 0x42f64ac4: // Luigi
|
||||
case 0x4be6a5cb: // AC, Pikmin
|
||||
INFO_LOG(DSPHLE, "CRC %08x: JAC (early Zelda) ucode chosen", _CRC);
|
||||
return new CUCode_Zelda(_rMailHandler, _CRC);
|
||||
|
||||
case 0x6CA33A6D: // DK Jungle Beat
|
||||
case 0x86840740: // Zelda WW - US
|
||||
case 0x56d36052: // Mario Sunshine
|
||||
case 0x2fcdf1ec: // Mario Kart, zelda 4 swords
|
||||
case 0x267fd05a: // Pikmin PAL
|
||||
INFO_LOG(DSPHLE, "CRC %08x: Zelda ucode chosen", _CRC);
|
||||
return new CUCode_Zelda(_rMailHandler, _CRC);
|
||||
|
||||
// WII CRCs
|
||||
case 0xb7eb9a9c: // Wii Pikmin - PAL
|
||||
case 0xeaeb38cc: // Wii Pikmin 2 - PAL
|
||||
case 0x6c3f6f94: // Zelda TP - PAL
|
||||
case 0xd643001f: // Mario Galaxy - PAL / WII DK Jungle Beat - PAL
|
||||
INFO_LOG(DSPHLE, "CRC %08x: Zelda Wii ucode chosen\n", _CRC);
|
||||
return new CUCode_Zelda(_rMailHandler, _CRC);
|
||||
|
||||
case 0x2ea36ce6: // Some Wii demos
|
||||
case 0x5ef56da3: // AX demo
|
||||
case 0x347112ba: // raving rabbits
|
||||
case 0xfa450138: // wii sports - PAL
|
||||
case 0xadbc06bd: // Elebits
|
||||
case 0x4cc52064: // Bleach: Versus Crusade
|
||||
case 0xd9c4bf34: // WiiMenu
|
||||
INFO_LOG(DSPHLE, "CRC %08x: Wii - AXWii chosen", _CRC);
|
||||
return new CUCode_AXWii(_rMailHandler, _CRC);
|
||||
|
||||
default:
|
||||
if (bWii)
|
||||
{
|
||||
PanicAlert("DSPHLE: Unknown ucode (CRC = %08x) - forcing AXWii.\n\nTry LLE plugin if this is homebrew.", _CRC);
|
||||
return new CUCode_AXWii(_rMailHandler, _CRC);
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("DSPHLE: Unknown ucode (CRC = %08x) - forcing AX.\n\nTry LLE plugin if this is homebrew.", _CRC);
|
||||
return new CUCode_AX(_rMailHandler);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool IUCode::NeedsResumeMail()
|
||||
{
|
||||
if (m_NeedsResumeMail)
|
||||
{
|
||||
m_NeedsResumeMail = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void IUCode::PrepareBootUCode(u32 mail)
|
||||
{
|
||||
switch (m_NextUCode_steps)
|
||||
{
|
||||
case 0: m_NextUCode.mram_dest_addr = mail; break;
|
||||
case 1: m_NextUCode.mram_size = mail & 0xffff; break;
|
||||
case 2: m_NextUCode.mram_dram_addr = mail & 0xffff; break;
|
||||
case 3: m_NextUCode.iram_mram_addr = mail; break;
|
||||
case 4: m_NextUCode.iram_size = mail & 0xffff; break;
|
||||
case 5: m_NextUCode.iram_dest = mail & 0xffff; break;
|
||||
case 6: m_NextUCode.iram_startpc = mail & 0xffff; break;
|
||||
case 7: m_NextUCode.dram_mram_addr = mail; break;
|
||||
case 8: m_NextUCode.dram_size = mail & 0xffff; break;
|
||||
case 9: m_NextUCode.dram_dest = mail & 0xffff; break;
|
||||
}
|
||||
m_NextUCode_steps++;
|
||||
|
||||
if (m_NextUCode_steps == 10)
|
||||
{
|
||||
m_NextUCode_steps = 0;
|
||||
m_NeedsResumeMail = true;
|
||||
m_UploadSetupInProgress = false;
|
||||
|
||||
u32 ector_crc = HashEctor(
|
||||
(u8*)HLEMemory_Get_Pointer(m_NextUCode.iram_mram_addr),
|
||||
m_NextUCode.iram_size);
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
char binFile[MAX_PATH];
|
||||
sprintf(binFile, "%sDSP_UC_%08X.bin", File::GetUserPath(D_DUMPDSP_IDX), ector_crc);
|
||||
|
||||
FILE* pFile = fopen(binFile, "wb");
|
||||
if (pFile)
|
||||
{
|
||||
fwrite((u8*)Memory::GetPointer(m_NextUCode.iram_mram_addr), m_NextUCode.iram_size, 1, pFile);
|
||||
fclose(pFile);
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_LOG(DSPHLE, "PrepareBootUCode 0x%08x", ector_crc);
|
||||
DEBUG_LOG(DSPHLE, "DRAM -> MRAM: src %04x dst %08x size %04x",
|
||||
m_NextUCode.mram_dram_addr, m_NextUCode.mram_dest_addr,
|
||||
m_NextUCode.mram_size);
|
||||
DEBUG_LOG(DSPHLE, "MRAM -> IRAM: src %08x dst %04x size %04x startpc %04x",
|
||||
m_NextUCode.iram_mram_addr, m_NextUCode.iram_dest,
|
||||
m_NextUCode.iram_size, m_NextUCode.iram_startpc);
|
||||
DEBUG_LOG(DSPHLE, "MRAM -> DRAM: src %08x dst %04x size %04x",
|
||||
m_NextUCode.dram_mram_addr, m_NextUCode.dram_dest,
|
||||
m_NextUCode.dram_size);
|
||||
|
||||
if (m_NextUCode.mram_size)
|
||||
{
|
||||
WARN_LOG(DSPHLE,
|
||||
"Trying to boot new ucode with dram download - not implemented");
|
||||
}
|
||||
if (m_NextUCode.dram_size)
|
||||
{
|
||||
WARN_LOG(DSPHLE,
|
||||
"Trying to boot new ucode with dram upload - not implemented");
|
||||
}
|
||||
|
||||
CDSPHandler::GetInstance().SwapUCode(ector_crc);
|
||||
}
|
||||
}
|
99
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.h
Normal file
99
Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.h
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODES_H
|
||||
#define _UCODES_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "ChunkFile.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#define UCODE_ROM 0x0000000
|
||||
#define UCODE_INIT_AUDIO_SYSTEM 0x0000001
|
||||
|
||||
class CMailHandler;
|
||||
|
||||
class IUCode
|
||||
{
|
||||
public:
|
||||
IUCode(CMailHandler& _rMailHandler)
|
||||
: m_rMailHandler(_rMailHandler)
|
||||
, m_UploadSetupInProgress(false)
|
||||
, m_NextUCode()
|
||||
, m_NextUCode_steps(0)
|
||||
, m_NeedsResumeMail(false)
|
||||
{}
|
||||
|
||||
virtual ~IUCode()
|
||||
{}
|
||||
|
||||
virtual void HandleMail(u32 _uMail) = 0;
|
||||
|
||||
// Cycles are out of the 81/121mhz the DSP runs at.
|
||||
virtual void Update(int cycles) = 0;
|
||||
virtual void MixAdd(short* buffer, int size) {}
|
||||
|
||||
virtual void DoState(PointerWrap &p) {}
|
||||
|
||||
protected:
|
||||
void PrepareBootUCode(u32 mail);
|
||||
|
||||
// Some ucodes (notably zelda) require a resume mail to be
|
||||
// sent if they are be started via PrepareBootUCode.
|
||||
// The HLE can use this to
|
||||
bool NeedsResumeMail();
|
||||
|
||||
CMailHandler& m_rMailHandler;
|
||||
Common::CriticalSection m_csMix;
|
||||
|
||||
enum EDSP_Codes
|
||||
{
|
||||
DSP_INIT = 0xDCD10000,
|
||||
DSP_RESUME = 0xDCD10001,
|
||||
DSP_YIELD = 0xDCD10002,
|
||||
DSP_DONE = 0xDCD10003,
|
||||
DSP_SYNC = 0xDCD10004,
|
||||
DSP_FRAME_END = 0xDCD10005,
|
||||
};
|
||||
|
||||
// UCode is forwarding mails to PrepareBootUCode
|
||||
// UCode only needs to set this to true, IUCode will set to false when done!
|
||||
bool m_UploadSetupInProgress;
|
||||
|
||||
private:
|
||||
struct SUCode
|
||||
{
|
||||
u32 mram_dest_addr;
|
||||
u16 mram_size;
|
||||
u16 mram_dram_addr;
|
||||
u32 iram_mram_addr;
|
||||
u16 iram_size;
|
||||
u16 iram_dest;
|
||||
u16 iram_startpc;
|
||||
u32 dram_mram_addr;
|
||||
u16 dram_size;
|
||||
u16 dram_dest;
|
||||
};
|
||||
SUCode m_NextUCode;
|
||||
int m_NextUCode_steps;
|
||||
|
||||
bool m_NeedsResumeMail;
|
||||
};
|
||||
|
||||
extern IUCode* UCodeFactory(u32 _CRC, CMailHandler& _rMailHandler, bool bWii);
|
||||
|
||||
#endif
|
198
Source/Core/Core/Src/HW/DSPLLE/DSPDebugInterface.cpp
Normal file
198
Source/Core/Core/Src/HW/DSPLLE/DSPDebugInterface.cpp
Normal file
@ -0,0 +1,198 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "DSPDebugInterface.h"
|
||||
|
||||
#include "DSPCore.h"
|
||||
#include "disassemble.h"
|
||||
|
||||
#include "DSPSymbols.h"
|
||||
#include "DSPMemoryMap.h"
|
||||
|
||||
void DSPDebugInterface::disasm(unsigned int address, char *dest, int max_size)
|
||||
{
|
||||
// we'll treat addresses as line numbers.
|
||||
strncpy(dest, DSPSymbols::GetLineText(address), max_size);
|
||||
dest[max_size-1] = 0;
|
||||
}
|
||||
|
||||
void DSPDebugInterface::getRawMemoryString(int memory, unsigned int address, char *dest, int max_size)
|
||||
{
|
||||
if (DSPCore_GetState() == DSPCORE_STOP)
|
||||
{
|
||||
dest[0] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (memory) {
|
||||
case 0: // IMEM
|
||||
switch (address >> 12) {
|
||||
case 0:
|
||||
case 0x8:
|
||||
sprintf(dest, "%04x", dsp_imem_read(address));
|
||||
break;
|
||||
default:
|
||||
sprintf(dest, "--IMEM--");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1: // DMEM
|
||||
switch (address >> 12) {
|
||||
case 0:
|
||||
case 1:
|
||||
sprintf(dest, "%04x (DMEM)", dsp_dmem_read(address));
|
||||
break;
|
||||
case 0xf:
|
||||
sprintf(dest, "%04x (MMIO)", g_dsp.ifx_regs[address & 0xFF]);
|
||||
break;
|
||||
default:
|
||||
sprintf(dest, "--DMEM--");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int DSPDebugInterface::readMemory(unsigned int address)
|
||||
{
|
||||
return 0; //Memory::ReadUnchecked_U32(address);
|
||||
}
|
||||
|
||||
unsigned int DSPDebugInterface::readInstruction(unsigned int address)
|
||||
{
|
||||
return 0; //Memory::Read_Instruction(address);
|
||||
}
|
||||
|
||||
bool DSPDebugInterface::isAlive()
|
||||
{
|
||||
return true; //Core::GetState() != Core::CORE_UNINITIALIZED;
|
||||
}
|
||||
|
||||
bool DSPDebugInterface::isBreakpoint(unsigned int address)
|
||||
{
|
||||
int real_addr = DSPSymbols::Line2Addr(address);
|
||||
if (real_addr >= 0)
|
||||
return dsp_breakpoints.IsAddressBreakPoint(real_addr);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void DSPDebugInterface::setBreakpoint(unsigned int address)
|
||||
{
|
||||
int real_addr = DSPSymbols::Line2Addr(address);
|
||||
if (real_addr >= 0) {
|
||||
if (dsp_breakpoints.Add(real_addr))
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
void DSPDebugInterface::clearBreakpoint(unsigned int address)
|
||||
{
|
||||
int real_addr = DSPSymbols::Line2Addr(address);
|
||||
if (real_addr >= 0) {
|
||||
if (dsp_breakpoints.Remove(real_addr))
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
void DSPDebugInterface::clearAllBreakpoints() {
|
||||
dsp_breakpoints.Clear();
|
||||
}
|
||||
|
||||
void DSPDebugInterface::toggleBreakpoint(unsigned int address)
|
||||
{
|
||||
int real_addr = DSPSymbols::Line2Addr(address);
|
||||
if (real_addr >= 0) {
|
||||
if (dsp_breakpoints.IsAddressBreakPoint(real_addr))
|
||||
dsp_breakpoints.Remove(real_addr);
|
||||
else
|
||||
dsp_breakpoints.Add(real_addr);
|
||||
}
|
||||
}
|
||||
|
||||
bool DSPDebugInterface::isMemCheck(unsigned int address)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void DSPDebugInterface::toggleMemCheck(unsigned int address)
|
||||
{
|
||||
PanicAlert("MemCheck functionality not supported in DSP module.");
|
||||
}
|
||||
|
||||
void DSPDebugInterface::insertBLR(unsigned int address)
|
||||
{
|
||||
PanicAlert("insertBLR functionality not supported in DSP module.");
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// Separate the blocks with colors.
|
||||
// -------------
|
||||
int DSPDebugInterface::getColor(unsigned int address)
|
||||
{
|
||||
static const int colors[6] =
|
||||
{
|
||||
0xd0FFFF, // light cyan
|
||||
0xFFd0d0, // light red
|
||||
0xd8d8FF, // light blue
|
||||
0xFFd0FF, // light purple
|
||||
0xd0FFd0, // light green
|
||||
0xFFFFd0, // light yellow
|
||||
};
|
||||
|
||||
// Scan backwards so we don't miss it. Hm, actually, let's not - it looks pretty good.
|
||||
int addr = -1;
|
||||
for (int i = 0; i < 1; i++)
|
||||
{
|
||||
addr = DSPSymbols::Line2Addr(address - i);
|
||||
if (addr >= 0)
|
||||
break;
|
||||
}
|
||||
if (addr == -1)
|
||||
return 0xFFFFFF;
|
||||
|
||||
Symbol *symbol = DSPSymbols::g_dsp_symbol_db.GetSymbolFromAddr(addr);
|
||||
if (!symbol)
|
||||
return 0xFFFFFF;
|
||||
if (symbol->type != Symbol::SYMBOL_FUNCTION)
|
||||
return 0xEEEEFF;
|
||||
return colors[symbol->index % 6];
|
||||
}
|
||||
// =============
|
||||
|
||||
|
||||
std::string DSPDebugInterface::getDescription(unsigned int address)
|
||||
{
|
||||
return ""; // g_symbolDB.GetDescription(address);
|
||||
}
|
||||
|
||||
unsigned int DSPDebugInterface::getPC()
|
||||
{
|
||||
return DSPSymbols::Addr2Line(g_dsp.pc);
|
||||
}
|
||||
|
||||
void DSPDebugInterface::setPC(unsigned int address)
|
||||
{
|
||||
int new_pc = DSPSymbols::Line2Addr(address);
|
||||
if (new_pc > 0)
|
||||
g_dsp.pc = new_pc;
|
||||
}
|
||||
|
||||
void DSPDebugInterface::runToBreakpoint()
|
||||
{
|
||||
|
||||
}
|
35
Source/Core/Core/Src/HW/DSPLLE/DSPDebugInterface.h
Normal file
35
Source/Core/Core/Src/HW/DSPLLE/DSPDebugInterface.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef _DSPDEBUGINTERFACE_H
|
||||
#define _DSPDEBUGINTERFACE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "DebugInterface.h"
|
||||
#include "Common.h"
|
||||
|
||||
class DSPDebugInterface : public DebugInterface
|
||||
{
|
||||
public:
|
||||
DSPDebugInterface(){}
|
||||
virtual void disasm(unsigned int address, char *dest, int max_size);
|
||||
virtual void getRawMemoryString(int memory, unsigned int address, char *dest, int max_size);
|
||||
virtual int getInstructionSize(int instruction) {return 1;}
|
||||
virtual bool isAlive();
|
||||
virtual bool isBreakpoint(unsigned int address);
|
||||
virtual void setBreakpoint(unsigned int address);
|
||||
virtual void clearBreakpoint(unsigned int address);
|
||||
virtual void clearAllBreakpoints();
|
||||
virtual void toggleBreakpoint(unsigned int address);
|
||||
virtual bool isMemCheck(unsigned int address);
|
||||
virtual void toggleMemCheck(unsigned int address);
|
||||
virtual unsigned int readMemory(unsigned int address);
|
||||
virtual unsigned int readInstruction(unsigned int address);
|
||||
virtual unsigned int getPC();
|
||||
virtual void setPC(unsigned int address);
|
||||
virtual void step() {}
|
||||
virtual void runToBreakpoint();
|
||||
virtual void insertBLR(unsigned int address);
|
||||
virtual int getColor(unsigned int address);
|
||||
virtual std::string getDescription(unsigned int address);
|
||||
};
|
||||
|
||||
#endif // _DSPDEBUGINTERFACE_H
|
122
Source/Core/Core/Src/HW/DSPLLE/DSPHost.cpp
Normal file
122
Source/Core/Core/Src/HW/DSPLLE/DSPHost.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Hash.h"
|
||||
#include "DSPHost.h"
|
||||
#include "DSPSymbols.h"
|
||||
#include "DSPLLETools.h"
|
||||
#include "../DSP.h"
|
||||
#include "../../ConfigManager.h"
|
||||
#include "../../PowerPC/PowerPC.h"
|
||||
|
||||
/*
|
||||
ECTORTODO
|
||||
#if defined(HAVE_WX) && HAVE_WX
|
||||
|
||||
#include "Debugger/DSPDebugWindow.h" // For the DSPDebuggerLLE class
|
||||
extern DSPDebuggerLLE* m_DebuggerFrame;
|
||||
|
||||
#endif
|
||||
*/
|
||||
|
||||
// The user of the DSPCore library must supply a few functions so that the
|
||||
// emulation core can access the environment it runs in. If the emulation
|
||||
// core isn't used, for example in an asm/disasm tool, then most of these
|
||||
// can be stubbed out.
|
||||
|
||||
u8 DSPHost_ReadHostMemory(u32 addr)
|
||||
{
|
||||
return DSP::ReadARAM(addr);
|
||||
}
|
||||
|
||||
void DSPHost_WriteHostMemory(u8 value, u32 addr)
|
||||
{
|
||||
DSP::WriteARAM(value, addr);
|
||||
}
|
||||
|
||||
bool DSPHost_OnThread()
|
||||
{
|
||||
const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
return _CoreParameter.bDSPThread;
|
||||
}
|
||||
|
||||
bool DSPHost_Running()
|
||||
{
|
||||
return !(*PowerPC::GetStatePtr());
|
||||
}
|
||||
|
||||
void DSPHost_InterruptRequest()
|
||||
{
|
||||
// Fire an interrupt on the PPC ASAP.
|
||||
DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP);
|
||||
}
|
||||
|
||||
u32 DSPHost_CodeLoaded(const u8 *ptr, int size)
|
||||
{
|
||||
u32 ector_crc = HashEctor(ptr, size);
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
DumpDSPCode(ptr, size, ector_crc);
|
||||
#endif
|
||||
|
||||
DSPSymbols::Clear();
|
||||
|
||||
// Auto load text file - if none just disassemble.
|
||||
|
||||
NOTICE_LOG(DSPLLE, "ector_crc: %08x", ector_crc);
|
||||
|
||||
DSPSymbols::Clear();
|
||||
bool success = false;
|
||||
switch (ector_crc)
|
||||
{
|
||||
case 0x86840740: success = DSPSymbols::ReadAnnotatedAssembly("../../docs/DSP/DSP_UC_Zelda.txt"); break;
|
||||
case 0x42f64ac4: success = DSPSymbols::ReadAnnotatedAssembly("../../docs/DSP/DSP_UC_Luigi.txt"); break;
|
||||
case 0x07f88145: success = DSPSymbols::ReadAnnotatedAssembly("../../docs/DSP/DSP_UC_AX_07F88145.txt"); break;
|
||||
case 0x3ad3b7ac: success = DSPSymbols::ReadAnnotatedAssembly("../../docs/DSP/DSP_UC_AX_3AD3B7AC.txt"); break;
|
||||
case 0x3daf59b9: success = DSPSymbols::ReadAnnotatedAssembly("../../docs/DSP/DSP_UC_AX_3DAF59B9.txt"); break;
|
||||
case 0x4e8a8b21: success = DSPSymbols::ReadAnnotatedAssembly("../../docs/DSP/DSP_UC_AX_4E8A8B21.txt"); break;
|
||||
case 0xe2136399: success = DSPSymbols::ReadAnnotatedAssembly("../../docs/DSP/DSP_UC_AX_E2136399.txt"); break;
|
||||
case 0xdd7e72d5: success = DSPSymbols::ReadAnnotatedAssembly("../../docs/DSP/DSP_UC_GBA.txt"); break;
|
||||
case 0x347112BA: success = DSPSymbols::ReadAnnotatedAssembly("../../docs/DSP/DSP_UC_AXWii.txt"); break;
|
||||
case 0xD643001F: success = DSPSymbols::ReadAnnotatedAssembly("../../docs/DSP/DSP_UC_SuperMarioGalaxy.txt"); break;
|
||||
default: success = false; break;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
DSPSymbols::AutoDisassembly(0x0, 0x1000);
|
||||
}
|
||||
|
||||
// Always add the ROM.
|
||||
DSPSymbols::AutoDisassembly(0x8000, 0x9000);
|
||||
/* ECTORTODO
|
||||
#if defined(HAVE_WX) && HAVE_WX
|
||||
if (m_DebuggerFrame)
|
||||
m_DebuggerFrame->Refresh();
|
||||
#endif
|
||||
*/
|
||||
return ector_crc;
|
||||
}
|
||||
|
||||
void DSPHost_UpdateDebugger()
|
||||
{
|
||||
/* ECTORTODO
|
||||
#if defined(HAVE_WX) && HAVE_WX
|
||||
if (m_DebuggerFrame)
|
||||
m_DebuggerFrame->Refresh();
|
||||
#endif */
|
||||
}
|
366
Source/Core/Core/Src/HW/DSPLLE/DSPLLE.cpp
Normal file
366
Source/Core/Core/Src/HW/DSPLLE/DSPLLE.cpp
Normal file
@ -0,0 +1,366 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
|
||||
#include "Common.h"
|
||||
#include "CommonPaths.h"
|
||||
#include "Atomic.h"
|
||||
#include "CommonTypes.h"
|
||||
#include "LogManager.h"
|
||||
#include "Thread.h"
|
||||
#include "ChunkFile.h"
|
||||
#include "IniFile.h"
|
||||
|
||||
#include "DSPLLEGlobals.h" // Local
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSPHWInterface.h"
|
||||
#include "disassemble.h"
|
||||
#include "DSPSymbols.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include "AudioCommon.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
#include "DSPTables.h"
|
||||
#include "DSPCore.h"
|
||||
#include "DSPLLE.h"
|
||||
#include "../Memmap.h"
|
||||
#include "../AudioInterface.h"
|
||||
|
||||
|
||||
DSPLLE::DSPLLE() {
|
||||
soundStream = NULL;
|
||||
g_InitMixer = false;
|
||||
bIsRunning = false;
|
||||
cycle_count = 0;
|
||||
}
|
||||
|
||||
DSPLLE::~DSPLLE() {
|
||||
}
|
||||
|
||||
void DSPLLE::DoState(PointerWrap &p)
|
||||
{
|
||||
p.Do(g_InitMixer);
|
||||
|
||||
p.Do(g_dsp.r);
|
||||
p.Do(g_dsp.pc);
|
||||
#if PROFILE
|
||||
p.Do(g_dsp.err_pc);
|
||||
#endif
|
||||
p.Do(g_dsp.cr);
|
||||
p.Do(g_dsp.reg_stack_ptr);
|
||||
p.Do(g_dsp.exceptions);
|
||||
p.Do(g_dsp.external_interrupt_waiting);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
p.Do(g_dsp.reg_stack[i]);
|
||||
}
|
||||
p.Do(g_dsp.iram_crc);
|
||||
p.Do(g_dsp.step_counter);
|
||||
p.Do(g_dsp.ifx_regs);
|
||||
p.Do(g_dsp.mbox[0]);
|
||||
p.Do(g_dsp.mbox[1]);
|
||||
UnWriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
||||
p.DoArray(g_dsp.iram, DSP_IRAM_SIZE);
|
||||
WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
||||
p.DoArray(g_dsp.dram, DSP_DRAM_SIZE);
|
||||
p.Do(cyclesLeft);
|
||||
p.Do(cycle_count);
|
||||
}
|
||||
|
||||
void DSPLLE::EmuStateChange(PLUGIN_EMUSTATE newState)
|
||||
{
|
||||
DSP_ClearAudioBuffer((newState == PLUGIN_EMUSTATE_PLAY) ? false : true);
|
||||
}
|
||||
|
||||
/* ECTORTODO
|
||||
void *DllDebugger(void *_hParent, bool Show)
|
||||
{
|
||||
#if defined(HAVE_WX) && HAVE_WX
|
||||
m_DebuggerFrame = new DSPDebuggerLLE((wxWindow *)_hParent);
|
||||
return (void *)m_DebuggerFrame;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// Regular thread
|
||||
void DSPLLE::dsp_thread(DSPLLE *lpParameter)
|
||||
{
|
||||
DSPLLE *dsp_lle = (DSPLLE *)lpParameter;
|
||||
while (dsp_lle->bIsRunning)
|
||||
{
|
||||
int cycles = (int)dsp_lle->cycle_count;
|
||||
if (cycles > 0) {
|
||||
if (dspjit)
|
||||
DSPCore_RunCycles(cycles);
|
||||
else
|
||||
DSPInterpreter::RunCycles(cycles);
|
||||
|
||||
Common::AtomicAdd(dsp_lle->cycle_count, -cycles);
|
||||
}
|
||||
// yield?
|
||||
}
|
||||
}
|
||||
|
||||
/* ECTORTODO
|
||||
void DSPLLE::DSP_DebugBreak()
|
||||
{
|
||||
#if defined(HAVE_WX) && HAVE_WX
|
||||
// if (m_DebuggerFrame)
|
||||
// m_DebuggerFrame->DebugBreak();
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
|
||||
void DSPLLE::Initialize(void *hWnd, bool bWii, bool bDSPThread)
|
||||
{
|
||||
this->hWnd = hWnd;
|
||||
this->bWii = bWii;
|
||||
this->bDSPThread = bDSPThread;
|
||||
g_InitMixer = false;
|
||||
bool bCanWork = true;
|
||||
char irom_file[MAX_PATH];
|
||||
char coef_file[MAX_PATH];
|
||||
|
||||
snprintf(irom_file, MAX_PATH, "%s%s",
|
||||
File::GetSysDirectory().c_str(), GC_SYS_DIR DIR_SEP DSP_IROM);
|
||||
if (!File::Exists(irom_file))
|
||||
snprintf(irom_file, MAX_PATH, "%s%s",
|
||||
File::GetUserPath(D_GCUSER_IDX), DIR_SEP DSP_IROM);
|
||||
snprintf(coef_file, MAX_PATH, "%s%s",
|
||||
File::GetSysDirectory().c_str(), GC_SYS_DIR DIR_SEP DSP_COEF);
|
||||
if (!File::Exists(coef_file))
|
||||
snprintf(coef_file, MAX_PATH, "%s%s",
|
||||
File::GetUserPath(D_GCUSER_IDX), DIR_SEP DSP_COEF);
|
||||
bCanWork = DSPCore_Init(irom_file, coef_file, AudioCommon::UseJIT());
|
||||
|
||||
g_dsp.cpu_ram = Memory::GetPointer(0);
|
||||
DSPCore_Reset();
|
||||
|
||||
if (!bCanWork)
|
||||
{
|
||||
DSPCore_Shutdown();
|
||||
// No way to shutdown Core from here? Hardcore shutdown!
|
||||
exit(EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
bIsRunning = true;
|
||||
|
||||
InitInstructionTable();
|
||||
|
||||
if (bDSPThread)
|
||||
{
|
||||
// g_hDSPThread = new Common::Thread(dsp_thread, (void *)this);
|
||||
g_hDSPThread = std::thread(dsp_thread, this);
|
||||
}
|
||||
/*
|
||||
ECTORTODO
|
||||
#if defined(HAVE_WX) && HAVE_WX
|
||||
if (m_DebuggerFrame)
|
||||
m_DebuggerFrame->Refresh();
|
||||
#endif
|
||||
*/
|
||||
}
|
||||
|
||||
void DSPLLE::DSP_StopSoundStream()
|
||||
{
|
||||
DSPInterpreter::Stop();
|
||||
bIsRunning = false;
|
||||
if (bDSPThread)
|
||||
{
|
||||
g_hDSPThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void DSPLLE::Shutdown()
|
||||
{
|
||||
AudioCommon::ShutdownSoundStream();
|
||||
DSPCore_Shutdown();
|
||||
}
|
||||
|
||||
u16 DSPLLE::DSP_WriteControlRegister(u16 _uFlag)
|
||||
{
|
||||
UDSPControl Temp(_uFlag);
|
||||
if (!g_InitMixer)
|
||||
{
|
||||
if (!Temp.DSPHalt && Temp.DSPInit)
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
soundStream = AudioCommon::InitSoundStream(new CMixer(AISampleRate, DACSampleRate), hWnd);
|
||||
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||
// Mixer is initialized
|
||||
g_InitMixer = true;
|
||||
}
|
||||
}
|
||||
DSPInterpreter::WriteCR(_uFlag);
|
||||
|
||||
// Check if the CPU has set an external interrupt (CR_EXTERNAL_INT)
|
||||
// and immediately process it, if it has.
|
||||
if (_uFlag & 2)
|
||||
{
|
||||
if (!bDSPThread)
|
||||
{
|
||||
DSPCore_CheckExternalInterrupt();
|
||||
DSPCore_CheckExceptions();
|
||||
}
|
||||
else
|
||||
{
|
||||
DSPCore_SetExternalInterrupt(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return DSPInterpreter::ReadCR();
|
||||
}
|
||||
|
||||
u16 DSPLLE::DSP_ReadControlRegister()
|
||||
{
|
||||
return DSPInterpreter::ReadCR();
|
||||
}
|
||||
|
||||
u16 DSPLLE::DSP_ReadMailBoxHigh(bool _CPUMailbox)
|
||||
{
|
||||
if (_CPUMailbox)
|
||||
return gdsp_mbox_read_h(GDSP_MBOX_CPU);
|
||||
else
|
||||
return gdsp_mbox_read_h(GDSP_MBOX_DSP);
|
||||
}
|
||||
|
||||
u16 DSPLLE::DSP_ReadMailBoxLow(bool _CPUMailbox)
|
||||
{
|
||||
if (_CPUMailbox)
|
||||
return gdsp_mbox_read_l(GDSP_MBOX_CPU);
|
||||
else
|
||||
return gdsp_mbox_read_l(GDSP_MBOX_DSP);
|
||||
}
|
||||
|
||||
void DSPLLE::DSP_WriteMailBoxHigh(bool _CPUMailbox, u16 _uHighMail)
|
||||
{
|
||||
if (_CPUMailbox)
|
||||
{
|
||||
if (gdsp_mbox_peek(GDSP_MBOX_CPU) & 0x80000000)
|
||||
{
|
||||
ERROR_LOG(DSPLLE, "Mailbox isnt empty ... strange");
|
||||
}
|
||||
|
||||
#if PROFILE
|
||||
if ((_uHighMail) == 0xBABE)
|
||||
{
|
||||
ProfilerStart();
|
||||
}
|
||||
#endif
|
||||
|
||||
gdsp_mbox_write_h(GDSP_MBOX_CPU, _uHighMail);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(DSPLLE, "CPU cant write to DSP mailbox");
|
||||
}
|
||||
}
|
||||
|
||||
void DSPLLE::DSP_WriteMailBoxLow(bool _CPUMailbox, u16 _uLowMail)
|
||||
{
|
||||
if (_CPUMailbox)
|
||||
{
|
||||
gdsp_mbox_write_l(GDSP_MBOX_CPU, _uLowMail);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(DSPLLE, "CPU cant write to DSP mailbox");
|
||||
}
|
||||
}
|
||||
|
||||
void DSPLLE::DSP_Update(int cycles)
|
||||
{
|
||||
unsigned int dsp_cycles = cycles / 6; //(jit?20:6);
|
||||
|
||||
// Sound stream update job has been handled by AudioDMA routine, which is more efficient
|
||||
/*
|
||||
// This gets called VERY OFTEN. The soundstream update might be expensive so only do it 200 times per second or something.
|
||||
int cycles_between_ss_update;
|
||||
|
||||
if (g_dspInitialize.bWii)
|
||||
cycles_between_ss_update = 121500000 / 200;
|
||||
else
|
||||
cycles_between_ss_update = 81000000 / 200;
|
||||
|
||||
cycle_count += cycles;
|
||||
if (cycle_count > cycles_between_ss_update)
|
||||
{
|
||||
while (cycle_count > cycles_between_ss_update)
|
||||
cycle_count -= cycles_between_ss_update;
|
||||
soundStream->Update();
|
||||
}
|
||||
*/
|
||||
// If we're not on a thread, run cycles here.
|
||||
if (!bDSPThread)
|
||||
{
|
||||
// ~1/6th as many cycles as the period PPC-side.
|
||||
DSPCore_RunCycles(dsp_cycles);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wait for dsp thread to catch up reasonably. Note: this logic should be thought through.
|
||||
while (cycle_count > dsp_cycles)
|
||||
;
|
||||
Common::AtomicAdd(cycle_count, dsp_cycles);
|
||||
}
|
||||
}
|
||||
|
||||
void DSPLLE::DSP_SendAIBuffer(unsigned int address, unsigned int num_samples)
|
||||
{
|
||||
if (!soundStream)
|
||||
return;
|
||||
|
||||
CMixer *pMixer = soundStream->GetMixer();
|
||||
|
||||
if (pMixer != 0 && address != 0)
|
||||
{
|
||||
const short *samples = (const short *)LLEMemory_Get_Pointer(address);
|
||||
pMixer->PushSamples(samples, num_samples);
|
||||
}
|
||||
|
||||
soundStream->Update();
|
||||
}
|
||||
|
||||
void DSPLLE::DSP_ClearAudioBuffer(bool mute)
|
||||
{
|
||||
if (soundStream)
|
||||
soundStream->Clear(mute);
|
||||
}
|
||||
|
||||
#define LLE_CONFIG_FILE "DSPLLE.ini"
|
||||
|
||||
void DSPLLE_LoadConfig()
|
||||
{
|
||||
// first load defaults
|
||||
IniFile file;
|
||||
file.Load((std::string(File::GetUserPath(D_CONFIG_IDX)) + LLE_CONFIG_FILE).c_str());
|
||||
ac_Config.Load(file);
|
||||
}
|
||||
|
||||
void DSPLLE_SaveConfig()
|
||||
{
|
||||
IniFile file;
|
||||
file.Load((std::string(File::GetUserPath(D_CONFIG_IDX)) + LLE_CONFIG_FILE).c_str());
|
||||
ac_Config.Set(file);
|
||||
file.Save((std::string(File::GetUserPath(D_CONFIG_IDX)) + LLE_CONFIG_FILE).c_str());
|
||||
}
|
76
Source/Core/Core/Src/HW/DSPLLE/DSPLLE.h
Normal file
76
Source/Core/Core/Src/HW/DSPLLE/DSPLLE.h
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSPLLE_H
|
||||
#define _DSPLLE_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "SoundStream.h"
|
||||
#include "DSPLLEGlobals.h" // Local
|
||||
#include "../../PluginDSP.h"
|
||||
|
||||
class DSPLLE : public PluginDSP {
|
||||
public:
|
||||
DSPLLE();
|
||||
~DSPLLE();
|
||||
|
||||
virtual void Initialize(void *hWnd, bool bWii, bool bDSPThread);
|
||||
virtual void Shutdown();
|
||||
|
||||
virtual bool IsLLE() { return true; }
|
||||
|
||||
void SetGlobals(PLUGIN_GLOBALS* _PluginGlobals);
|
||||
|
||||
/*
|
||||
GUI
|
||||
virtual void Config(void *_hwnd);
|
||||
virtual void About(void *_hwnd);
|
||||
virtual void *Debug(void *Parent, bool Show);
|
||||
*/
|
||||
|
||||
virtual void DoState(PointerWrap &p);
|
||||
virtual void EmuStateChange(PLUGIN_EMUSTATE newState);
|
||||
|
||||
virtual void DSP_WriteMailBoxHigh(bool _CPUMailbox, unsigned short);
|
||||
virtual void DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short);
|
||||
virtual unsigned short DSP_ReadMailBoxHigh(bool _CPUMailbox);
|
||||
virtual unsigned short DSP_ReadMailBoxLow(bool _CPUMailbox);
|
||||
virtual unsigned short DSP_ReadControlRegister();
|
||||
virtual unsigned short DSP_WriteControlRegister(unsigned short);
|
||||
virtual void DSP_SendAIBuffer(unsigned int address, unsigned int num_samples);
|
||||
virtual void DSP_Update(int cycles);
|
||||
virtual void DSP_StopSoundStream();
|
||||
virtual void DSP_ClearAudioBuffer(bool mute);
|
||||
|
||||
private:
|
||||
static void dsp_thread(DSPLLE* lpParameter);
|
||||
|
||||
std::thread g_hDSPThread;
|
||||
SoundStream *soundStream;
|
||||
bool g_InitMixer;
|
||||
void *hWnd;
|
||||
bool bWii;
|
||||
bool bDSPThread;
|
||||
bool bIsRunning;
|
||||
volatile u32 cycle_count;
|
||||
};
|
||||
|
||||
// Hack to be deleted.
|
||||
void DSPLLE_LoadConfig();
|
||||
void DSPLLE_SaveConfig();
|
||||
|
||||
#endif // _DSPLLE_H
|
93
Source/Core/Core/Src/HW/DSPLLE/DSPLLEGlobals.cpp
Normal file
93
Source/Core/Core/Src/HW/DSPLLE/DSPLLEGlobals.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <iostream> // I hope this doesn't break anything
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "Common.h" // for Common::swap
|
||||
#include "DSPCore.h"
|
||||
#include "DSPLLEGlobals.h"
|
||||
|
||||
// TODO: Get rid of this file.
|
||||
|
||||
// =======================================================================================
|
||||
// For PB address detection
|
||||
// --------------
|
||||
|
||||
// This will only work on GC, not Wii.
|
||||
u32 RAM_MASK = 0x1FFFFFF;
|
||||
|
||||
u16 LLEMemory_Read_U16(u32 _uAddress)
|
||||
{
|
||||
return Common::swap16(*(u16*)&g_dsp.cpu_ram[_uAddress & RAM_MASK]);
|
||||
}
|
||||
|
||||
u32 LLEMemory_Read_U32(u32 _uAddress)
|
||||
{
|
||||
return Common::swap32(*(u32*)&g_dsp.cpu_ram[_uAddress & RAM_MASK]);
|
||||
}
|
||||
|
||||
void* LLEMemory_Get_Pointer(u32 _uAddress)
|
||||
{
|
||||
return &g_dsp.cpu_ram[_uAddress & RAM_MASK];
|
||||
}
|
||||
|
||||
#if PROFILE
|
||||
|
||||
#define PROFILE_MAP_SIZE 0x10000
|
||||
|
||||
u64 g_profileMap[PROFILE_MAP_SIZE];
|
||||
bool g_profile = false;
|
||||
|
||||
void ProfilerStart()
|
||||
{
|
||||
g_profile = true;
|
||||
}
|
||||
|
||||
void ProfilerAddDelta(int _addr, int _delta)
|
||||
{
|
||||
if (g_profile)
|
||||
{
|
||||
g_profileMap[_addr] += _delta;
|
||||
}
|
||||
}
|
||||
|
||||
void ProfilerInit()
|
||||
{
|
||||
memset(g_profileMap, 0, sizeof(g_profileMap));
|
||||
}
|
||||
|
||||
void ProfilerDump(u64 count)
|
||||
{
|
||||
FILE* pFile = fopen("DSP_Prof.txt", "wt");
|
||||
if (pFile != NULL)
|
||||
{
|
||||
fprintf(pFile, "Number of DSP steps: %llu\n\n", count);
|
||||
for (int i=0; i<PROFILE_MAP_SIZE;i++)
|
||||
{
|
||||
if (g_profileMap[i] > 0)
|
||||
{
|
||||
fprintf(pFile, "0x%04X: %llu\n", i, g_profileMap[i]);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
41
Source/Core/Core/Src/HW/DSPLLE/DSPLLEGlobals.h
Normal file
41
Source/Core/Core/Src/HW/DSPLLE/DSPLLEGlobals.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _GLOBALS_H
|
||||
#define _GLOBALS_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "AudioCommon.h"
|
||||
#include <stdio.h>
|
||||
|
||||
// TODO: Get rid of this file.
|
||||
|
||||
#define PROFILE 0
|
||||
|
||||
u16 LLEMemory_Read_U16(u32 _uAddress); // For PB address detection
|
||||
u32 LLEMemory_Read_U32(u32 _uAddress);
|
||||
void* LLEMemory_Get_Pointer(u32 _uAddress);
|
||||
|
||||
#if PROFILE
|
||||
void ProfilerDump(u64 _count);
|
||||
void ProfilerInit();
|
||||
void ProfilerAddDelta(int _addr, int _delta);
|
||||
void ProfilerStart();
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
90
Source/Core/Core/Src/HW/DSPLLE/DSPLLETools.cpp
Normal file
90
Source/Core/Core/Src/HW/DSPLLE/DSPLLETools.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "DSPLLEGlobals.h"
|
||||
|
||||
#include "FileUtil.h"
|
||||
#include "DSPCore.h"
|
||||
#include "DSPCodeUtil.h"
|
||||
#include "DSPLLETools.h"
|
||||
#include "disassemble.h"
|
||||
#include "DSPInterpreter.h"
|
||||
|
||||
bool DumpDSPCode(const u8 *code_be, int size_in_bytes, u32 crc)
|
||||
{
|
||||
char binFile[MAX_PATH];
|
||||
char txtFile[MAX_PATH];
|
||||
sprintf(binFile, "%sDSP_UC_%08X.bin", File::GetUserPath(D_DUMPDSP_IDX), crc);
|
||||
sprintf(txtFile, "%sDSP_UC_%08X.txt", File::GetUserPath(D_DUMPDSP_IDX), crc);
|
||||
|
||||
FILE* pFile = fopen(binFile, "wb");
|
||||
if (pFile)
|
||||
{
|
||||
fwrite(code_be, size_in_bytes, 1, pFile);
|
||||
fclose(pFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("Cant open file (%s) to dump UCode!!", binFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the binary back in.
|
||||
std::vector<u16> code;
|
||||
LoadBinary(binFile, code);
|
||||
|
||||
AssemblerSettings settings;
|
||||
settings.show_hex = true;
|
||||
settings.show_pc = true;
|
||||
settings.ext_separator = '\'';
|
||||
settings.decode_names = true;
|
||||
settings.decode_registers = true;
|
||||
|
||||
std::string text;
|
||||
DSPDisassembler disasm(settings);
|
||||
|
||||
if (!disasm.Disassemble(0, code, 0x0000, text))
|
||||
return false;
|
||||
|
||||
return File::WriteStringToFile(true, text, txtFile);
|
||||
}
|
||||
|
||||
// TODO make this useful :p
|
||||
bool DumpCWCode(u32 _Address, u32 _Length)
|
||||
{
|
||||
char filename[256];
|
||||
sprintf(filename, "%sDSP_UCode.bin", File::GetUserPath(D_DUMPDSP_IDX));
|
||||
FILE* pFile = fopen(filename, "wb");
|
||||
|
||||
if (pFile != NULL)
|
||||
{
|
||||
for (size_t i = _Address; i < _Address + _Length; i++)
|
||||
{
|
||||
u16 val = g_dsp.iram[i];
|
||||
fprintf(pFile, " cw 0x%04x \n", val);
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
24
Source/Core/Core/Src/HW/DSPLLE/DSPLLETools.h
Normal file
24
Source/Core/Core/Src/HW/DSPLLE/DSPLLETools.h
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSPLLE_TOOLS_H
|
||||
#define _DSPLLE_TOOLS_H
|
||||
|
||||
bool DumpDSPCode(const u8 *code_be, int size_in_bytes, u32 crc);
|
||||
bool DumpCWCode(u32 _Address, u32 _Length);
|
||||
|
||||
#endif //_DSPLLE_TOOLS_H
|
287
Source/Core/Core/Src/HW/DSPLLE/DSPSymbols.cpp
Normal file
287
Source/Core/Core/Src/HW/DSPLLE/DSPSymbols.cpp
Normal file
@ -0,0 +1,287 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <iostream> // I hope this doesn't break anything
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "Common.h"
|
||||
#include "StringUtil.h"
|
||||
|
||||
#include "DSPCore.h"
|
||||
#include "DSPSymbols.h"
|
||||
#include "disassemble.h"
|
||||
|
||||
namespace DSPSymbols {
|
||||
|
||||
DSPSymbolDB g_dsp_symbol_db;
|
||||
|
||||
std::map<u16, int> addr_to_line;
|
||||
std::map<int, u16> line_to_addr;
|
||||
std::map<int, const char *> line_to_symbol;
|
||||
std::vector<std::string> lines;
|
||||
int line_counter = 0;
|
||||
|
||||
int Addr2Line(u16 address) // -1 for not found
|
||||
{
|
||||
std::map<u16, int>::iterator iter = addr_to_line.find(address);
|
||||
if (iter != addr_to_line.end())
|
||||
return iter->second;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Line2Addr(int line) // -1 for not found
|
||||
{
|
||||
std::map<int, u16>::iterator iter = line_to_addr.find(line);
|
||||
if (iter != line_to_addr.end())
|
||||
return iter->second;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *GetLineText(int line)
|
||||
{
|
||||
if (line > 0 && line < (int)lines.size())
|
||||
{
|
||||
return lines[line].c_str();
|
||||
}
|
||||
else
|
||||
return "----";
|
||||
}
|
||||
|
||||
Symbol *DSPSymbolDB::GetSymbolFromAddr(u32 addr)
|
||||
{
|
||||
XFuncMap::iterator it = functions.find(addr);
|
||||
if (it != functions.end())
|
||||
return &it->second;
|
||||
else
|
||||
{
|
||||
for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); ++iter)
|
||||
{
|
||||
if (addr >= iter->second.address && addr < iter->second.address + iter->second.size)
|
||||
return &iter->second;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// lower case only
|
||||
bool IsHexDigit(char c) {
|
||||
switch (c) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsAlpha(char c) {
|
||||
return (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z');
|
||||
}
|
||||
|
||||
void DisasssembleRange(u16 start, u16 end)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool ReadAnnotatedAssembly(const char *filename)
|
||||
{
|
||||
FILE *f = fopen(filename, "r");
|
||||
if (!f) {
|
||||
ERROR_LOG(DSPLLE, "Bah! ReadAnnotatedAssembly couldn't find the file %s", filename);
|
||||
return false;
|
||||
}
|
||||
char line[512];
|
||||
|
||||
int last_addr = 0;
|
||||
|
||||
lines.reserve(3000);
|
||||
|
||||
// Symbol generation
|
||||
int brace_count = 0;
|
||||
bool symbol_in_progress = false;
|
||||
|
||||
int symbol_count = 0;
|
||||
Symbol current_symbol;
|
||||
|
||||
while (fgets(line, 512, f))
|
||||
{
|
||||
// Scan string for the first 4-digit hex string.
|
||||
size_t len = strlen(line);
|
||||
int first_hex = -1;
|
||||
bool hex_found = false;
|
||||
for (unsigned int i = 0; i < strlen(line); i++)
|
||||
{
|
||||
const char c = line[i];
|
||||
if (IsHexDigit(c))
|
||||
{
|
||||
if (first_hex == -1)
|
||||
{
|
||||
first_hex = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove hex notation
|
||||
if ((int)i == first_hex + 3 &&
|
||||
(first_hex == 0 || line[first_hex - 1] != 'x') &&
|
||||
(i >= len - 1 || line[i + 1] == ' '))
|
||||
{
|
||||
hex_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (i - first_hex < 3)
|
||||
{
|
||||
first_hex = -1;
|
||||
}
|
||||
if (IsAlpha(c))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Scan for function starts
|
||||
if (!memcmp(line, "void", 4)) {
|
||||
char temp[256];
|
||||
for (size_t i = 6; i < len; i++) {
|
||||
if (line[i] == '(') {
|
||||
// Yep, got one.
|
||||
memcpy(temp, line + 5, i - 5);
|
||||
temp[i - 5] = 0;
|
||||
|
||||
// Mark symbol so the next hex sets the address
|
||||
current_symbol.name = temp;
|
||||
current_symbol.address = 0xFFFF;
|
||||
current_symbol.index = symbol_count++;
|
||||
symbol_in_progress = true;
|
||||
|
||||
// Reset brace count.
|
||||
brace_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan for braces
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (line[i] == '{')
|
||||
brace_count++;
|
||||
if (line[i] == '}')
|
||||
{
|
||||
brace_count--;
|
||||
if (brace_count == 0 && symbol_in_progress) {
|
||||
// Commit this symbol.
|
||||
current_symbol.size = last_addr - current_symbol.address + 1;
|
||||
g_dsp_symbol_db.AddCompleteSymbol(current_symbol);
|
||||
current_symbol.address = 0xFFFF;
|
||||
symbol_in_progress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hex_found)
|
||||
{
|
||||
int hex = 0;
|
||||
sscanf(line + first_hex, "%04x", &hex);
|
||||
|
||||
// Sanity check
|
||||
if (hex > last_addr + 3 || hex < last_addr - 3) {
|
||||
static int errors = 0;
|
||||
INFO_LOG(DSPLLE, "Got Insane Hex Digit %04x (%04x) from %s", hex, last_addr, line);
|
||||
errors++;
|
||||
if (errors > 10)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if (line_counter >= 200 && line_counter <= 220)
|
||||
// NOTICE_LOG(DSPLLE, "Got Hex Digit %04x from %s, line %i", hex, line, line_counter);
|
||||
if (symbol_in_progress && current_symbol.address == 0xFFFF)
|
||||
current_symbol.address = hex;
|
||||
|
||||
line_to_addr[line_counter] = hex;
|
||||
addr_to_line[hex] = line_counter;
|
||||
last_addr = hex;
|
||||
}
|
||||
}
|
||||
|
||||
lines.push_back(TabsToSpaces(4, line));
|
||||
line_counter++;
|
||||
}
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutoDisassembly(u16 start_addr, u16 end_addr)
|
||||
{
|
||||
AssemblerSettings settings;
|
||||
settings.show_pc = true;
|
||||
settings.show_hex = true;
|
||||
DSPDisassembler disasm(settings);
|
||||
|
||||
u16 addr = start_addr;
|
||||
const u16 *ptr = (start_addr >> 15) ? g_dsp.irom : g_dsp.iram;
|
||||
while (addr < end_addr)
|
||||
{
|
||||
line_to_addr[line_counter] = addr;
|
||||
addr_to_line[addr] = line_counter;
|
||||
|
||||
std::string buf;
|
||||
if (!disasm.DisOpcode(ptr, 0, 2, &addr, buf))
|
||||
{
|
||||
ERROR_LOG(DSPLLE, "disasm failed at %04x", addr);
|
||||
break;
|
||||
}
|
||||
|
||||
//NOTICE_LOG(DSPLLE, "added %04x %i %s", addr, line_counter, buf.c_str());
|
||||
lines.push_back(buf);
|
||||
line_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
addr_to_line.clear();
|
||||
line_to_addr.clear();
|
||||
lines.clear();
|
||||
line_counter = 0;
|
||||
}
|
||||
|
||||
} // namespace DSPSymbols
|
53
Source/Core/Core/Src/HW/DSPLLE/DSPSymbols.h
Normal file
53
Source/Core/Core/Src/HW/DSPLLE/DSPSymbols.h
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSPSYMBOLS_H
|
||||
#define _DSPSYMBOLS_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "SymbolDB.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace DSPSymbols {
|
||||
|
||||
class DSPSymbolDB : public SymbolDB
|
||||
{
|
||||
public:
|
||||
DSPSymbolDB() {}
|
||||
~DSPSymbolDB() {}
|
||||
|
||||
Symbol *GetSymbolFromAddr(u32 addr);
|
||||
|
||||
};
|
||||
|
||||
extern DSPSymbolDB g_dsp_symbol_db;
|
||||
|
||||
bool ReadAnnotatedAssembly(const char *filename);
|
||||
void AutoDisassembly(u16 start_addr, u16 end_addr);
|
||||
|
||||
void Clear();
|
||||
|
||||
int Addr2Line(u16 address);
|
||||
int Line2Addr(int line); // -1 for not found
|
||||
|
||||
const char *GetLineText(int line);
|
||||
|
||||
} // namespace DSPSymbols
|
||||
|
||||
#endif
|
||||
|
@ -53,7 +53,7 @@ namespace HW
|
||||
SerialInterface::Init();
|
||||
ProcessorInterface::Init();
|
||||
Memory::Init();
|
||||
DSP::Init();
|
||||
DSP::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPHLE);
|
||||
DVDInterface::Init();
|
||||
GPFifo::Init();
|
||||
ExpansionInterface::Init();
|
||||
|
@ -71,6 +71,7 @@ IPC_HLE_PERIOD: For the Wiimote this is the call schedule:
|
||||
#include "../CoreTiming.h"
|
||||
#include "../ConfigManager.h"
|
||||
#include "../IPC_HLE/WII_IPC_HLE.h"
|
||||
#include "../PluginDSP.h"
|
||||
#include "Thread.h"
|
||||
#include "Timer.h"
|
||||
|
||||
@ -241,16 +242,11 @@ void PatchEngineCallback(u64 userdata, int cyclesLate)
|
||||
|
||||
void Init()
|
||||
{
|
||||
PLUGIN_INFO DSPType;
|
||||
(*CPluginManager::GetInstance().GetDSP()).GetInfo(DSPType);
|
||||
std::string DSPName(DSPType.Name);
|
||||
bool UsingDSPLLE = (DSPName.find("LLE") != std::string::npos) || (DSPName.find("lle") != std::string::npos);
|
||||
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
|
||||
{
|
||||
CPU_CORE_CLOCK = 729000000u;
|
||||
|
||||
if (!UsingDSPLLE)
|
||||
if (!DSP::GetPlugin()->IsLLE())
|
||||
DSP_PERIOD = (int)(GetTicksPerSecond() * 0.003f);
|
||||
|
||||
// AyuanX: TO BE TWEAKED
|
||||
@ -266,11 +262,11 @@ void Init()
|
||||
{
|
||||
CPU_CORE_CLOCK = 486000000u;
|
||||
|
||||
if (!UsingDSPLLE)
|
||||
if (!DSP::GetPlugin()->IsLLE())
|
||||
DSP_PERIOD = (int)(GetTicksPerSecond() * 0.005f);
|
||||
}
|
||||
|
||||
if (UsingDSPLLE)
|
||||
if (DSP::GetPlugin()->IsLLE())
|
||||
DSP_PERIOD = 12000; // TO BE TWEAKED
|
||||
|
||||
// This is the biggest question mark.
|
||||
|
@ -28,10 +28,12 @@
|
||||
#include "State.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "PluginManager.h"
|
||||
#include "HW/DSP.h"
|
||||
#include "HW/Memmap.h"
|
||||
#include "Host.h"
|
||||
#include "PowerPC/PowerPC.h"
|
||||
#include "CoreTiming.h"
|
||||
#include "PluginDSP.h"
|
||||
|
||||
extern "C" {
|
||||
#include "lua.h"
|
||||
@ -2956,8 +2958,7 @@ DEFINE_LUA_FUNCTION(movie_close, "")
|
||||
|
||||
DEFINE_LUA_FUNCTION(sound_clear, "")
|
||||
{
|
||||
if(CPluginManager::GetInstance().GetDSP())
|
||||
CPluginManager::GetInstance().GetDSP()->DSP_ClearAudioBuffer();
|
||||
DSP::GetPlugin()->DSP_ClearAudioBuffer(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -130,13 +130,13 @@ bool IsUsingPad(int controller)
|
||||
switch (controller)
|
||||
{
|
||||
case 0:
|
||||
return g_numPads & 0x01;
|
||||
return (g_numPads & 0x01) != 0;
|
||||
case 1:
|
||||
return g_numPads & 0x02;
|
||||
return (g_numPads & 0x02) != 0;
|
||||
case 2:
|
||||
return g_numPads & 0x04;
|
||||
return (g_numPads & 0x04) != 0;
|
||||
case 3:
|
||||
return g_numPads & 0x08;
|
||||
return (g_numPads & 0x08) != 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
38
Source/Core/Core/Src/PluginDSP.cpp
Normal file
38
Source/Core/Core/Src/PluginDSP.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "PluginDSP.h"
|
||||
|
||||
#include "HW/DSPLLE/DSPLLE.h"
|
||||
#include "HW/DSPHLE/DSPHLE.h"
|
||||
|
||||
PluginDSP *CreateDSPPlugin(bool HLE)
|
||||
{
|
||||
if (HLE)
|
||||
{
|
||||
DSPHLE_LoadConfig();
|
||||
return new DSPHLE();
|
||||
}
|
||||
else
|
||||
{
|
||||
DSPLLE_LoadConfig();
|
||||
return new DSPLLE();
|
||||
}
|
||||
}
|
||||
|
||||
PluginDSP::PluginDSP() {}
|
||||
PluginDSP::~PluginDSP() {}
|
60
Source/Core/Core/Src/PluginDSP.h
Normal file
60
Source/Core/Core/Src/PluginDSP.h
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _PLUGINDSP_H_
|
||||
#define _PLUGINDSP_H_
|
||||
|
||||
|
||||
#include "Plugin.h" // TODO: Only here for EmuStateChange
|
||||
#include "ChunkFile.h"
|
||||
|
||||
class PluginDSP
|
||||
{
|
||||
public:
|
||||
PluginDSP();
|
||||
~PluginDSP();
|
||||
|
||||
virtual bool IsLLE() = 0;
|
||||
|
||||
virtual void Initialize(void *hWnd, bool bWii, bool bDSPThread) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
/*
|
||||
GUI
|
||||
virtual void Config(void *_hwnd) = 0;
|
||||
virtual void About(void *_hwnd) = 0;
|
||||
virtual void *Debug(void *Parent, bool Show) = 0;
|
||||
*/
|
||||
|
||||
virtual void DoState(PointerWrap &p) = 0;
|
||||
virtual void EmuStateChange(PLUGIN_EMUSTATE newState) = 0;
|
||||
|
||||
virtual void DSP_WriteMailBoxHigh(bool _CPUMailbox, unsigned short) = 0;
|
||||
virtual void DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short) = 0;
|
||||
virtual unsigned short DSP_ReadMailBoxHigh(bool _CPUMailbox) = 0;
|
||||
virtual unsigned short DSP_ReadMailBoxLow(bool _CPUMailbox) = 0;
|
||||
virtual unsigned short DSP_ReadControlRegister() = 0;
|
||||
virtual unsigned short DSP_WriteControlRegister(unsigned short) = 0;
|
||||
virtual void DSP_SendAIBuffer(unsigned int address, unsigned int num_samples) = 0;
|
||||
virtual void DSP_Update(int cycles) = 0;
|
||||
virtual void DSP_StopSoundStream() = 0;
|
||||
virtual void DSP_ClearAudioBuffer(bool mute) = 0;
|
||||
};
|
||||
|
||||
PluginDSP *CreateDSPPlugin(bool LLE);
|
||||
|
||||
#endif // _PLUGINDSP_H_
|
@ -70,7 +70,6 @@ CPluginManager::CPluginManager()
|
||||
|
||||
// Set initial values to NULL.
|
||||
m_video = NULL;
|
||||
m_dsp = NULL;
|
||||
}
|
||||
|
||||
// This will call FreeLibrary() for all plugins
|
||||
@ -79,7 +78,6 @@ CPluginManager::~CPluginManager()
|
||||
INFO_LOG(CONSOLE, "Delete CPluginManager\n");
|
||||
|
||||
delete m_PluginGlobals;
|
||||
delete m_dsp;
|
||||
delete m_video;
|
||||
}
|
||||
|
||||
@ -104,27 +102,9 @@ bool CPluginManager::InitPlugins()
|
||||
}
|
||||
INFO_LOG(CONSOLE, "After GetVideo\n");
|
||||
|
||||
if (!GetDSP()) {
|
||||
PanicAlertT("Can't init DSP Plugin");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// FreeLibrary() after ShutDown() is disabled for some plugins. See the comment in the file description
|
||||
// for an explanation about the current LoadLibrary() and FreeLibrary() behavior.
|
||||
void CPluginManager::ShutdownPlugins()
|
||||
{
|
||||
if (m_dsp)
|
||||
{
|
||||
m_dsp->Shutdown();
|
||||
FreeDSP();
|
||||
NOTICE_LOG(CONSOLE, "%s", Core::StopMessage(false, "Audio shutdown").c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void CPluginManager::ShutdownVideoPlugin()
|
||||
{
|
||||
if (m_video)
|
||||
@ -216,10 +196,6 @@ void *CPluginManager::LoadPlugin(const char *_rFilename)
|
||||
plugin = new Common::PluginVideo(_rFilename);
|
||||
break;
|
||||
|
||||
case PLUGIN_TYPE_DSP:
|
||||
plugin = new Common::PluginDSP(_rFilename);
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlertT("Trying to load unsupported type %d", type);
|
||||
return NULL;
|
||||
@ -286,28 +262,6 @@ void CPluginManager::ScanForPlugins()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Create or return the already created plugin pointers. This will be called
|
||||
often for the DSP from the DSP files.
|
||||
|
||||
We don't need to check if [Plugin]->IsValid() here because it will not be set by LoadPlugin()
|
||||
if it's not valid.
|
||||
*/
|
||||
|
||||
Common::PluginDSP *CPluginManager::GetDSP()
|
||||
{
|
||||
if (m_dsp != NULL)
|
||||
{
|
||||
if (m_dsp->GetFilename() == m_params->m_strDSPPlugin)
|
||||
return m_dsp;
|
||||
else
|
||||
FreeDSP();
|
||||
}
|
||||
// Else load a new plugin
|
||||
m_dsp = (Common::PluginDSP*)LoadPlugin(m_params->m_strDSPPlugin.c_str());
|
||||
return m_dsp;
|
||||
}
|
||||
|
||||
Common::PluginVideo *CPluginManager::GetVideo()
|
||||
{
|
||||
/* We don't need to check if m_video->IsValid() here, because m_video will not be set by LoadPlugin()
|
||||
@ -331,21 +285,11 @@ void CPluginManager::FreeVideo()
|
||||
m_video = NULL;
|
||||
}
|
||||
|
||||
void CPluginManager::FreeDSP()
|
||||
{
|
||||
WARN_LOG(CONSOLE, "%s", Core::StopMessage(false, "Will unload audio DLL").c_str());
|
||||
delete m_dsp;
|
||||
m_dsp = NULL;
|
||||
}
|
||||
|
||||
void CPluginManager::EmuStateChange(PLUGIN_EMUSTATE newState)
|
||||
{
|
||||
GetVideo()->EmuStateChange(newState);
|
||||
GetDSP()->EmuStateChange(newState);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Call DLL functions
|
||||
// ------------
|
||||
|
||||
@ -365,11 +309,6 @@ void CPluginManager::OpenConfig(void* _Parent, const char *_rFilename, PLUGIN_TY
|
||||
GetVideo()->Config(_Parent);
|
||||
break;
|
||||
|
||||
case PLUGIN_TYPE_DSP:
|
||||
if (GetDSP() != NULL)
|
||||
GetDSP()->Config(_Parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlertT("Type %d config not supported in plugin %s", Type, _rFilename);
|
||||
break;
|
||||
@ -379,7 +318,7 @@ void CPluginManager::OpenConfig(void* _Parent, const char *_rFilename, PLUGIN_TY
|
||||
// Open debugging window. Type = Video or DSP. Show = Show or hide window.
|
||||
void *CPluginManager::OpenDebug(void* _Parent, const char *_rFilename, PLUGIN_TYPE Type, bool Show)
|
||||
{
|
||||
if (! File::Exists((File::GetPluginsDirectory() + _rFilename).c_str()))
|
||||
if (!File::Exists((File::GetPluginsDirectory() + _rFilename).c_str()))
|
||||
{
|
||||
PanicAlert("Can't find plugin %s", _rFilename);
|
||||
return NULL;
|
||||
@ -391,10 +330,6 @@ void *CPluginManager::OpenDebug(void* _Parent, const char *_rFilename, PLUGIN_TY
|
||||
return GetVideo()->Debug(_Parent, Show);
|
||||
break;
|
||||
|
||||
case PLUGIN_TYPE_DSP:
|
||||
return GetDSP()->Debug(_Parent, Show);
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlert("Type %d debug not supported in plugin %s", Type, _rFilename);
|
||||
return NULL;
|
||||
|
@ -19,7 +19,6 @@
|
||||
#define __PLUGIN_MANAGER_H_
|
||||
|
||||
#include "Plugin.h"
|
||||
#include "PluginDSP.h"
|
||||
#include "PluginVideo.h"
|
||||
#include "CoreParameter.h"
|
||||
|
||||
@ -49,15 +48,12 @@ public:
|
||||
static void Shutdown();
|
||||
|
||||
Common::PluginVideo *GetVideo();
|
||||
Common::PluginDSP *GetDSP();
|
||||
|
||||
void FreeVideo();
|
||||
void FreeDSP();
|
||||
|
||||
void EmuStateChange(PLUGIN_EMUSTATE newState);
|
||||
|
||||
bool InitPlugins();
|
||||
void ShutdownPlugins();
|
||||
void ShutdownVideoPlugin();
|
||||
void ScanForPlugins();
|
||||
void OpenConfig(void* _Parent, const char *_rFilename, PLUGIN_TYPE Type);
|
||||
@ -71,7 +67,6 @@ private:
|
||||
CPluginInfos m_PluginInfos;
|
||||
PLUGIN_GLOBALS *m_PluginGlobals;
|
||||
Common::PluginVideo *m_video;
|
||||
Common::PluginDSP *m_dsp;
|
||||
|
||||
SCoreStartupParameter * m_params;
|
||||
CPluginManager();
|
||||
|
@ -18,6 +18,7 @@ files = [
|
||||
"MemTools.cpp",
|
||||
"PatchEngine.cpp",
|
||||
"PluginManager.cpp",
|
||||
"PluginDSP.cpp",
|
||||
"LuaInterface.cpp",
|
||||
"State.cpp",
|
||||
"Tracer.cpp",
|
||||
@ -37,6 +38,27 @@ files = [
|
||||
"HW/AudioInterface.cpp",
|
||||
"HW/CPU.cpp",
|
||||
"HW/DSP.cpp",
|
||||
"HW/DSPHLE/UCodes/UCode_AX.cpp",
|
||||
"HW/DSPHLE/UCodes/UCode_AXWii.cpp",
|
||||
"HW/DSPHLE/UCodes/UCode_CARD.cpp",
|
||||
"HW/DSPHLE/UCodes/UCode_InitAudioSystem.cpp",
|
||||
"HW/DSPHLE/UCodes/UCode_ROM.cpp",
|
||||
"HW/DSPHLE/UCodes/UCodes.cpp",
|
||||
"HW/DSPHLE/UCodes/UCode_GBA.cpp",
|
||||
"HW/DSPHLE/UCodes/UCode_Zelda.cpp",
|
||||
"HW/DSPHLE/UCodes/UCode_Zelda_ADPCM.cpp",
|
||||
"HW/DSPHLE/UCodes/UCode_Zelda_Voice.cpp",
|
||||
"HW/DSPHLE/UCodes/UCode_Zelda_Synth.cpp",)
|
||||
"HW/DSPHLE/DSPHandler.cpp",
|
||||
"HW/DSPHLE/HLEMixer.cpp",
|
||||
"HW/DSPHLE/MailHandler.cpp",
|
||||
"HW/DSPHLE/DSPHLE.cpp",
|
||||
"HW/DSPLLE/DSPDebugInterface.cpp",
|
||||
"HW/DSPLLE/DSPHost.cpp",
|
||||
"HW/DSPLLE/DSPSymbols.cpp",
|
||||
"HW/DSPLLE/DSPLLEGlobals.cpp",
|
||||
"HW/DSPLLE/DSPLLE.cpp",
|
||||
"HW/DSPLLE/DSPLLETools.cpp",
|
||||
"HW/DVDInterface.cpp",
|
||||
"HW/EXI.cpp",
|
||||
"HW/EXI_Channel.cpp",
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "CoreTiming.h"
|
||||
#include "OnFrame.h"
|
||||
#include "HW/Wiimote.h"
|
||||
#include "HW/DSP.h"
|
||||
#include "HW/HW.h"
|
||||
#include "PowerPC/PowerPC.h"
|
||||
#include "PowerPC/JitCommon/JitBase.h"
|
||||
@ -89,7 +90,7 @@ void DoState(PointerWrap &p)
|
||||
// Begin with video plugin, so that it gets a chance to clear it's caches and writeback modified things to RAM
|
||||
CPluginManager &pm = CPluginManager::GetInstance();
|
||||
pm.GetVideo()->DoState(p.GetPPtr(), p.GetMode());
|
||||
pm.GetDSP()->DoState(p.GetPPtr(), p.GetMode());
|
||||
|
||||
if (Core::g_CoreStartupParameter.bWii)
|
||||
Wiimote::DoState(p.GetPPtr(), p.GetMode());
|
||||
PowerPC::DoState(p);
|
||||
|
Reference in New Issue
Block a user