mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-12 09:09:12 +01:00
293b5cb4c1
to allow both system-side (also Windows) and per-user installations. The IPL ROM is another candidate for this, but I don't have an image to test with. Perhaps a more general solution would be to just phase out the Sys directory. As used for data which would have been available in ROM or flash on the real hardware, it really contains two classes of files that are both read-only during emulation: - Settings and font files, which can be freely distributed - ROM files which must be obtained from the user's own GC or Wii Since the two could be freely put together on Windows without any problems and with the users of that platform being resistant to change, it may be easiest to just treat Sys as another directory to be copied from the application bundle into User/Sys at startup on non-Windows. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6738 8ced0084-cf51-0410-be5f-012b33b47a6e
419 lines
8.9 KiB
C++
419 lines
8.9 KiB
C++
// 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 "Globals.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"
|
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
#include "DSPConfigDlgLLE.h"
|
|
DSPConfigDialogLLE* m_ConfigFrame = NULL;
|
|
#include "Debugger/DSPDebugWindow.h"
|
|
#endif
|
|
|
|
PLUGIN_GLOBALS* globals = NULL;
|
|
DSPInitialize g_dspInitialize;
|
|
Common::Thread *g_hDSPThread = NULL;
|
|
SoundStream *soundStream = NULL;
|
|
bool g_InitMixer = false;
|
|
|
|
bool bIsRunning = false;
|
|
volatile u32 cycle_count = 0;
|
|
|
|
// Standard crap to make wxWidgets happy
|
|
#ifdef _WIN32
|
|
HINSTANCE g_hInstance;
|
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
class wxDLLApp : public wxApp
|
|
{
|
|
bool OnInit()
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_APP_NO_MAIN(wxDLLApp)
|
|
WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst);
|
|
#endif
|
|
|
|
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, // DLL module handle
|
|
DWORD dwReason, // reason called
|
|
LPVOID lpvReserved) // reserved
|
|
{
|
|
switch (dwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
{
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
wxSetInstance((HINSTANCE)hinstDLL);
|
|
wxInitialize();
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
wxUninitialize();
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
g_hInstance = hinstDLL;
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
void GetDllInfo(PLUGIN_INFO* _PluginInfo)
|
|
{
|
|
_PluginInfo->Version = 0x0100;
|
|
_PluginInfo->Type = PLUGIN_TYPE_DSP;
|
|
|
|
#ifdef DEBUGFAST
|
|
sprintf(_PluginInfo->Name, "Dolphin DSP-LLE Plugin (DebugFast)");
|
|
#else
|
|
#ifndef _DEBUG
|
|
sprintf(_PluginInfo->Name, "Dolphin DSP-LLE Plugin");
|
|
#else
|
|
sprintf(_PluginInfo->Name, "Dolphin DSP-LLE Plugin (Debug)");
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void SetDllGlobals(PLUGIN_GLOBALS* _pPluginGlobals)
|
|
{
|
|
globals = _pPluginGlobals;
|
|
LogManager::SetInstance((LogManager *)globals->logManager);
|
|
}
|
|
|
|
void DllConfig(void *_hParent)
|
|
{
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
m_ConfigFrame = new DSPConfigDialogLLE((wxWindow *)_hParent);
|
|
|
|
// add backends
|
|
std::vector<std::string> backends = AudioCommon::GetSoundBackends();
|
|
|
|
for (std::vector<std::string>::const_iterator iter = backends.begin();
|
|
iter != backends.end(); ++iter)
|
|
{
|
|
m_ConfigFrame->AddBackend((*iter).c_str());
|
|
}
|
|
|
|
m_ConfigFrame->ShowModal();
|
|
|
|
m_ConfigFrame->Destroy();
|
|
#endif
|
|
}
|
|
|
|
void DoState(unsigned char **ptr, int mode)
|
|
{
|
|
PointerWrap p(ptr, mode);
|
|
p.Do(g_InitMixer);
|
|
|
|
// Enable this when the HLE is fixed to save/load the same amount of data,
|
|
// no matter how bogus, so that one can switch LLE->HLE. The other way is unlikely to work very well.
|
|
#if 0
|
|
p.Do(g_dsp.r);
|
|
p.Do(g_dsp.pc);
|
|
p.Do(g_dsp.err_pc);
|
|
p.Do(g_dsp.cr);
|
|
p.Do(g_dsp.reg_stack_ptr);
|
|
p.Do(g_dsp.exceptions);
|
|
p.Do(g_dsp.exceptions_in_progress);
|
|
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]);
|
|
p.DoArray(g_dsp.iram, DSP_IRAM_BYTE_SIZE);
|
|
p.DoArray(g_dsp.dram, DSP_DRAM_BYTE_SIZE);
|
|
#endif
|
|
}
|
|
|
|
void EmuStateChange(PLUGIN_EMUSTATE newState)
|
|
{
|
|
DSP_ClearAudioBuffer((newState == PLUGIN_EMUSTATE_PLAY) ? false : true);
|
|
}
|
|
|
|
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
|
|
THREAD_RETURN dsp_thread(void* lpParameter)
|
|
{
|
|
while (bIsRunning)
|
|
{
|
|
int cycles = (int)cycle_count;
|
|
if (cycles > 0) {
|
|
if (jit)
|
|
DSPCore_RunCycles(cycles);
|
|
else
|
|
DSPInterpreter::RunCycles(cycles);
|
|
|
|
Common::AtomicAdd(cycle_count, -cycles);
|
|
}
|
|
// yield?
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void DSP_DebugBreak()
|
|
{
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
// if (m_DebuggerFrame)
|
|
// m_DebuggerFrame->DebugBreak();
|
|
#endif
|
|
}
|
|
|
|
void Initialize(void *init)
|
|
{
|
|
g_InitMixer = false;
|
|
bool bCanWork = true;
|
|
char irom_file[MAX_PATH];
|
|
char coef_file[MAX_PATH];
|
|
g_dspInitialize = *(DSPInitialize*)init;
|
|
|
|
g_Config.Load();
|
|
|
|
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 = g_dspInitialize.pGetMemoryPointer(0);
|
|
DSPCore_Reset();
|
|
|
|
if (!bCanWork)
|
|
{
|
|
PanicAlert("DSPLLE: Failed to initialize plugin, exiting");
|
|
DSPCore_Shutdown();
|
|
return;
|
|
}
|
|
|
|
bIsRunning = true;
|
|
|
|
InitInstructionTable();
|
|
|
|
if (g_dspInitialize.bOnThread)
|
|
{
|
|
g_hDSPThread = new Common::Thread(dsp_thread, NULL);
|
|
}
|
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
if (m_DebuggerFrame)
|
|
m_DebuggerFrame->Refresh();
|
|
#endif
|
|
}
|
|
|
|
void DSP_StopSoundStream()
|
|
{
|
|
DSPInterpreter::Stop();
|
|
bIsRunning = false;
|
|
if (g_dspInitialize.bOnThread)
|
|
{
|
|
delete g_hDSPThread;
|
|
g_hDSPThread = NULL;
|
|
}
|
|
}
|
|
|
|
void Shutdown()
|
|
{
|
|
AudioCommon::ShutdownSoundStream();
|
|
DSPCore_Shutdown();
|
|
}
|
|
|
|
u16 DSP_WriteControlRegister(u16 _uFlag)
|
|
{
|
|
UDSPControl Temp(_uFlag);
|
|
if (!g_InitMixer)
|
|
{
|
|
if (!Temp.DSPHalt && Temp.DSPInit)
|
|
{
|
|
unsigned int AISampleRate, DACSampleRate;
|
|
g_dspInitialize.pGetSampleRate(AISampleRate, DACSampleRate);
|
|
soundStream = AudioCommon::InitSoundStream(new CMixer(AISampleRate, DACSampleRate));
|
|
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)
|
|
{
|
|
DSPCore_CheckExternalInterrupt();
|
|
DSPCore_CheckExceptions();
|
|
}
|
|
|
|
return DSPInterpreter::ReadCR();
|
|
}
|
|
|
|
u16 DSP_ReadControlRegister()
|
|
{
|
|
return DSPInterpreter::ReadCR();
|
|
}
|
|
|
|
u16 DSP_ReadMailboxHigh(bool _CPUMailbox)
|
|
{
|
|
if (_CPUMailbox)
|
|
return gdsp_mbox_read_h(GDSP_MBOX_CPU);
|
|
else
|
|
return gdsp_mbox_read_h(GDSP_MBOX_DSP);
|
|
}
|
|
|
|
u16 DSP_ReadMailboxLow(bool _CPUMailbox)
|
|
{
|
|
if (_CPUMailbox)
|
|
return gdsp_mbox_read_l(GDSP_MBOX_CPU);
|
|
else
|
|
return gdsp_mbox_read_l(GDSP_MBOX_DSP);
|
|
}
|
|
|
|
void 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 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 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 (!g_dspInitialize.bOnThread)
|
|
{
|
|
// ~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 DSP_SendAIBuffer(unsigned int address, unsigned int num_samples)
|
|
{
|
|
if (!soundStream)
|
|
return;
|
|
|
|
CMixer *pMixer = soundStream->GetMixer();
|
|
|
|
if (pMixer != 0 && address != 0)
|
|
{
|
|
short *samples = (short *)Memory_Get_Pointer(address);
|
|
pMixer->PushSamples(samples, num_samples);
|
|
}
|
|
|
|
soundStream->Update();
|
|
}
|
|
|
|
void DSP_ClearAudioBuffer(bool mute)
|
|
{
|
|
if (soundStream)
|
|
soundStream->Clear(mute);
|
|
}
|
|
|