dolphin/Source/Core/DSPCore/Src/DSPInterpreter.cpp
Shawn Hoffman baf82a0849 hackfix dsp reset: fixes zelda collector's edition and some other games which reset and hang (...are there any others?)
the problem was that the streaming audio interrupts were still being triggered, causing the game to try and jump to an invalid interrupt handler. The code for dsp lle looks like a hack :( (but it works)

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6163 8ced0084-cf51-0410-be5f-012b33b47a6e
2010-09-02 01:36:45 +00:00

247 lines
5.3 KiB
C++

/*====================================================================
filename: gdsp_interpreter.cpp
project: GCemu
created: 2004-6-18
mail: duddie@walla.com
Copyright (c) 2005 Duddie & Tratax
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; either version 2
of the License, or (at your option) any later version.
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 for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
====================================================================*/
#include "DSPTables.h"
#include "DSPHost.h"
#include "DSPCore.h"
#include "DSPAnalyzer.h"
#include "DSPHWInterface.h"
#include "DSPIntUtil.h"
namespace DSPInterpreter {
volatile u32 gdsp_running;
// NOTE: These have nothing to do with g_dsp.r[DSP_REG_CR].
// Hm, should instructions that change CR use this? Probably not (but they
// should call UpdateCachedCR())
void WriteCR(u16 val)
{
// reset
if (val & 1)
{
DSPCore_Reset();
val &= ~1;
}
// init - can reset and init be done at the same time?
else if (val == 4)
{
// this looks like a hack! OSInitAudioSystem ucode
// should send this mail - not dsp core itself
gdsp_mbox_write_h(GDSP_MBOX_DSP, 0x8054);
gdsp_mbox_write_l(GDSP_MBOX_DSP, 0x4348);
val |= 0x800;
}
// update cr
g_dsp.cr = val;
}
// Hm, should instructions that read CR use this? (Probably not).
u16 ReadCR()
{
if (g_dsp.pc & 0x8000)
{
g_dsp.cr |= 0x800;
}
else
{
g_dsp.cr &= ~0x800;
}
return g_dsp.cr;
}
void Step()
{
DSPCore_CheckExceptions();
g_dsp.step_counter++;
#if PROFILE
g_dsp.err_pc = g_dsp.pc;
ProfilerAddDelta(g_dsp.err_pc, 1);
if (g_dsp.step_counter == 1)
{
ProfilerInit();
}
if ((g_dsp.step_counter & 0xFFFFF) == 0)
{
ProfilerDump(g_dsp.step_counter);
}
#endif
u16 opc = dsp_fetch_code();
ExecuteInstruction(UDSPInstruction(opc));
HandleLoop();
}
// Used by thread mode.
void Run()
{
int checkInterrupt = 0;
gdsp_running = true;
while (!(g_dsp.cr & CR_HALT) && gdsp_running)
{
if (jit)
jit->RunForCycles(1);
else {
// Automatically let the other threads work if we're idle skipping
if(DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
Common::YieldCPU();
Step();
// Turns out the less you check for external interrupts, the more
// sound you hear, and it becomes slower
checkInterrupt++;
if(checkInterrupt == 500) { // <-- Arbitrary number. TODO: tweak
DSPCore_CheckExternalInterrupt();
checkInterrupt = 0;
}
}
}
gdsp_running = false;
}
// This one has basic idle skipping, and checks breakpoints.
int RunCyclesDebug(int cycles)
{
// First, let's run a few cycles with no idle skipping so that things can progress a bit.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
return 0;
if (dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
{
DSPCore_SetState(DSPCORE_STEPPING);
return cycles;
}
Step();
cycles--;
if (cycles < 0)
return 0;
}
DSPCore_CheckExternalInterrupt();
while (true)
{
// Next, let's run a few cycles with idle skipping, so that we can skip
// idle loops.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
return 0;
if (dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
{
DSPCore_SetState(DSPCORE_STEPPING);
return cycles;
}
// Idle skipping.
if (DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
// Now, lets run some more without idle skipping.
for (int i = 0; i < 200; i++)
{
if (dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
{
DSPCore_SetState(DSPCORE_STEPPING);
return cycles;
}
Step();
cycles--;
if (cycles < 0)
return 0;
// We don't bother directly supporting pause - if the main emu pauses,
// it just won't call this function anymore.
}
}
}
// Used by non-thread mode. Meant to be efficient.
int RunCycles(int cycles)
{
// First, let's run a few cycles with no idle skipping so that things can
// progress a bit.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
DSPCore_CheckExternalInterrupt();
while (true)
{
// Next, let's run a few cycles with idle skipping, so that we can skip
// idle loops.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
return 0;
// Idle skipping.
if (DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
// Now, lets run some more without idle skipping.
for (int i = 0; i < 200; i++)
{
Step();
cycles--;
if (cycles < 0)
return 0;
// We don't bother directly supporting pause - if the main emu pauses,
// it just won't call this function anymore.
}
}
}
void Stop()
{
gdsp_running = false;
}
} // namespace