DSP: Eliminate most global state

An unfortunately large single commit that deglobalizes the DSP code.
(which I'm very sorry about).

This would have otherwise been extremely difficult to separate due to
extensive use of the globals in very coupling ways that would result in
more scaffolding to work around than is worth it.

Aside from the video code, I believe only the DSP code is the hairiest
to deal with in terms of globals, so I guess it's best to get this dealt
with right off the bat.

A summary of what this commit does:
  - Turns the DSPInterpreter into its own class
    This is the most involved portion of this change.
    The bulk of the changes are turning non-member functions into member
    functions that would be situated into the Interpreter class.

  - Eliminates all usages to globals within DSPCore.
    This generally involves turning a lot of non-member functions into
    member functions that are either situated within SDSP or DSPCore.

  - Discards DSPDebugInterface (it wasn't hooked up to anything,
    and for the sake of eliminating global state, I'd rather get rid of
    it than think up ways for this class to be integrated with
    everything else.

  - Readjusts the DSP JIT to handle calling out to member functions.
    In most cases, this just means wrapping respective member function
    calles into thunk functions.

Surprisingly, this doesn't even make use of the introduced System class.
It was possible all along to do this without it. We can house everything
within the DSPLLE class, which is quite nice =)
This commit is contained in:
Lioncash 2020-12-21 09:22:06 -05:00
parent 2917af03ec
commit 7d1bd565a6
50 changed files with 3037 additions and 3164 deletions

View File

@ -1096,6 +1096,13 @@ public:
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionP(FunctionPointer func, const void* param1)
{
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(param1)));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionPC(FunctionPointer func, const void* param1, u32 param2)
{
@ -1122,6 +1129,15 @@ public:
ABI_CallFunction(func);
}
// Pass a pointer and register as a parameter.
template <typename FunctionPointer>
void ABI_CallFunctionPR(FunctionPointer func, const void* ptr, X64Reg reg1)
{
MOV(64, R(ABI_PARAM2), R(reg1));
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(ptr)));
ABI_CallFunction(func);
}
// Pass two registers as parameters.
template <typename FunctionPointer>
void ABI_CallFunctionRR(FunctionPointer func, X64Reg reg1, X64Reg reg2)
@ -1130,6 +1146,15 @@ public:
ABI_CallFunction(func);
}
// Pass a pointer and two registers as parameters.
template <typename FunctionPointer>
void ABI_CallFunctionPRR(FunctionPointer func, const void* ptr, X64Reg reg1, X64Reg reg2)
{
MOVTwo(64, ABI_PARAM2, reg1, 0, ABI_PARAM3, reg2);
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(ptr)));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionAC(int bits, FunctionPointer func, const Gen::OpArg& arg1, u32 param2)
{

View File

@ -111,23 +111,18 @@ add_library(core
DSP/DSPDisassembler.cpp
DSP/DSPDisassembler.h
DSP/DSPHWInterface.cpp
DSP/DSPHWInterface.h
DSP/DSPMemoryMap.cpp
DSP/DSPMemoryMap.h
DSP/DSPStacks.cpp
DSP/DSPStacks.h
DSP/DSPTables.cpp
DSP/DSPTables.h
DSP/LabelMap.cpp
DSP/LabelMap.h
DSP/Interpreter/DSPIntArithmetic.cpp
DSP/Interpreter/DSPIntBranch.cpp
DSP/Interpreter/DSPIntCCUtil.cpp
DSP/Interpreter/DSPIntCCUtil.h
DSP/Interpreter/DSPInterpreter.cpp
DSP/Interpreter/DSPInterpreter.h
DSP/Interpreter/DSPIntExtOps.cpp
DSP/Interpreter/DSPIntExtOps.h
DSP/Interpreter/DSPIntLoadStore.cpp
DSP/Interpreter/DSPIntMisc.cpp
DSP/Interpreter/DSPIntMultiplier.cpp
@ -186,8 +181,6 @@ add_library(core
HW/DSPHLE/MailHandler.h
HW/DSPHLE/DSPHLE.cpp
HW/DSPHLE/DSPHLE.h
HW/DSPLLE/DSPDebugInterface.cpp
HW/DSPLLE/DSPDebugInterface.h
HW/DSPLLE/DSPHost.cpp
HW/DSPLLE/DSPSymbols.cpp
HW/DSPLLE/DSPSymbols.h

View File

@ -58,7 +58,6 @@
<ClCompile Include="DSP\DSPTables.cpp" />
<ClCompile Include="DSP\Interpreter\DSPIntArithmetic.cpp" />
<ClCompile Include="DSP\Interpreter\DSPIntBranch.cpp" />
<ClCompile Include="DSP\Interpreter\DSPIntCCUtil.cpp" />
<ClCompile Include="DSP\Interpreter\DSPInterpreter.cpp" />
<ClCompile Include="DSP\Interpreter\DSPIntExtOps.cpp" />
<ClCompile Include="DSP\Interpreter\DSPIntLoadStore.cpp" />
@ -107,7 +106,6 @@
<ClCompile Include="HW\DSPHLE\UCodes\INIT.cpp" />
<ClCompile Include="HW\DSPHLE\UCodes\ROM.cpp" />
<ClCompile Include="HW\DSPHLE\UCodes\Zelda.cpp" />
<ClCompile Include="HW\DSPLLE\DSPDebugInterface.cpp" />
<ClCompile Include="HW\DSPLLE\DSPHost.cpp" />
<ClCompile Include="HW\DSPLLE\DSPLLE.cpp" />
<ClCompile Include="HW\DSPLLE\DSPLLEGlobals.cpp" />
@ -420,13 +418,9 @@
<ClInclude Include="DSP\DSPCore.h" />
<ClInclude Include="DSP\DSPDisassembler.h" />
<ClInclude Include="DSP\DSPHost.h" />
<ClInclude Include="DSP\DSPHWInterface.h" />
<ClInclude Include="DSP\DSPMemoryMap.h" />
<ClInclude Include="DSP\DSPStacks.h" />
<ClInclude Include="DSP\DSPTables.h" />
<ClInclude Include="DSP\Interpreter\DSPIntCCUtil.h" />
<ClInclude Include="DSP\Interpreter\DSPInterpreter.h" />
<ClInclude Include="DSP\Interpreter\DSPIntExtOps.h" />
<ClInclude Include="DSP\Interpreter\DSPIntTables.h" />
<ClInclude Include="DSP\Interpreter\DSPIntUtil.h" />
<ClInclude Include="DSP\Jit\DSPEmitterBase.h" />
@ -466,7 +460,6 @@
<ClInclude Include="HW\DSPHLE\UCodes\INIT.h" />
<ClInclude Include="HW\DSPHLE\UCodes\ROM.h" />
<ClInclude Include="HW\DSPHLE\UCodes\Zelda.h" />
<ClInclude Include="HW\DSPLLE\DSPDebugInterface.h" />
<ClInclude Include="HW\DSPLLE\DSPLLE.h" />
<ClInclude Include="HW\DSPLLE\DSPLLEGlobals.h" />
<ClInclude Include="HW\DSPLLE\DSPSymbols.h" />

View File

@ -233,9 +233,6 @@
<ClCompile Include="DSP\Interpreter\DSPIntBranch.cpp">
<Filter>DSPCore\Interpreter</Filter>
</ClCompile>
<ClCompile Include="DSP\Interpreter\DSPIntCCUtil.cpp">
<Filter>DSPCore\Interpreter</Filter>
</ClCompile>
<ClCompile Include="DSP\Interpreter\DSPInterpreter.cpp">
<Filter>DSPCore\Interpreter</Filter>
</ClCompile>
@ -416,9 +413,6 @@
<ClCompile Include="HW\DSPHLE\MailHandler.cpp">
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE</Filter>
</ClCompile>
<ClCompile Include="HW\DSPLLE\DSPDebugInterface.cpp">
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\LLE</Filter>
</ClCompile>
<ClCompile Include="HW\DSPLLE\DSPHost.cpp">
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\LLE</Filter>
</ClCompile>
@ -1070,9 +1064,6 @@
<ClInclude Include="DSP\Interpreter\DSPInterpreter.h">
<Filter>DSPCore\Interpreter</Filter>
</ClInclude>
<ClInclude Include="DSP\Interpreter\DSPIntExtOps.h">
<Filter>DSPCore\Interpreter</Filter>
</ClInclude>
<ClInclude Include="DSP\Interpreter\DSPIntTables.h">
<Filter>DSPCore\Interpreter</Filter>
</ClInclude>
@ -1199,9 +1190,6 @@
<ClInclude Include="HW\DSPHLE\MailHandler.h">
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE</Filter>
</ClInclude>
<ClInclude Include="HW\DSPLLE\DSPDebugInterface.h">
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\LLE</Filter>
</ClInclude>
<ClInclude Include="DSP\DSPHost.h">
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\LLE</Filter>
</ClInclude>
@ -1388,15 +1376,6 @@
<ClInclude Include="DSPEmulator.h">
<Filter>DSPCore</Filter>
</ClInclude>
<ClInclude Include="DSP\DSPHWInterface.h">
<Filter>DSPCore</Filter>
</ClInclude>
<ClInclude Include="DSP\DSPMemoryMap.h">
<Filter>DSPCore</Filter>
</ClInclude>
<ClInclude Include="DSP\DSPStacks.h">
<Filter>DSPCore</Filter>
</ClInclude>
<ClInclude Include="DSP\DSPTables.h">
<Filter>DSPCore</Filter>
</ClInclude>

View File

@ -9,7 +9,7 @@
#include "Common/Logging/Log.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPTables.h"
namespace DSP::Analyzer
@ -72,7 +72,7 @@ void Reset()
code_flags.fill(0);
}
void AnalyzeRange(u16 start_addr, u16 end_addr)
void AnalyzeRange(const SDSP& dsp, u16 start_addr, u16 end_addr)
{
// First we run an extremely simplified version of a disassembler to find
// where all instructions start.
@ -82,7 +82,7 @@ void AnalyzeRange(u16 start_addr, u16 end_addr)
u16 last_arithmetic = 0;
for (u16 addr = start_addr; addr < end_addr;)
{
UDSPInstruction inst = dsp_imem_read(addr);
const UDSPInstruction inst = dsp.ReadIMEM(addr);
const DSPOPCTemplate* opcode = GetOpTemplate(inst);
if (!opcode)
{
@ -94,7 +94,7 @@ void AnalyzeRange(u16 start_addr, u16 end_addr)
if ((inst & 0xffe0) == 0x0060 || (inst & 0xff00) == 0x1100)
{
// BLOOP, BLOOPI
u16 loop_end = dsp_imem_read(addr + 1);
const u16 loop_end = dsp.ReadIMEM(addr + 1);
code_flags[addr] |= CODE_LOOP_START;
code_flags[loop_end] |= CODE_LOOP_END;
}
@ -139,7 +139,7 @@ void AnalyzeRange(u16 start_addr, u16 end_addr)
found = true;
if (idle_skip_sigs[s][i] == 0xFFFF)
continue;
if (idle_skip_sigs[s][i] != dsp_imem_read(static_cast<u16>(addr + i)))
if (idle_skip_sigs[s][i] != dsp.ReadIMEM(static_cast<u16>(addr + i)))
break;
}
if (found)
@ -153,11 +153,11 @@ void AnalyzeRange(u16 start_addr, u16 end_addr)
}
} // Anonymous namespace
void Analyze()
void Analyze(const SDSP& dsp)
{
Reset();
AnalyzeRange(0x0000, 0x1000); // IRAM
AnalyzeRange(0x8000, 0x9000); // IROM
AnalyzeRange(dsp, 0x0000, 0x1000); // IRAM
AnalyzeRange(dsp, 0x8000, 0x9000); // IROM
}
u8 GetCodeFlags(u16 address)

View File

@ -6,6 +6,11 @@
#include "Common/CommonTypes.h"
namespace DSP
{
struct SDSP;
}
// Basic code analysis.
namespace DSP::Analyzer
{
@ -28,7 +33,7 @@ enum
// all old analysis away. Luckily the entire address space is only 64K code
// words and the actual code space 8K instructions in total, so we can do
// some pretty expensive analysis if necessary.
void Analyze();
void Analyze(const SDSP& dsp);
// Retrieves the flags set during analysis for code in memory.
u8 GetCodeFlags(u16 address);

View File

@ -10,6 +10,7 @@
#include <memory>
#include <type_traits>
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/Hash.h"
@ -18,24 +19,18 @@
#include "Core/DSP/DSPAccelerator.h"
#include "Core/DSP/DSPAnalyzer.h"
#include "Core/DSP/DSPHWInterface.h"
#include "Core/DSP/DSPHost.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"
#include "Core/DSP/Jit/DSPEmitterBase.h"
namespace DSP
{
SDSP g_dsp;
DSPBreakpoints g_dsp_breakpoints;
static State core_state = State::Stopped;
bool g_init_hax = false;
std::unique_ptr<JIT::DSPEmitter> g_dsp_jit;
std::unique_ptr<DSPCaptureLogger> g_dsp_cap;
static Common::Event step_event;
// not needed for game ucodes (it slows down interpreter/dspjit32 + easier to compare int VS
// dspjit64 without it)
//#define PRECISE_BACKLOG
// Returns false if the hash fails and the user hits "Yes"
static bool VerifyRoms()
static bool VerifyRoms(const DSPCore& core)
{
struct DspRomHashes
{
@ -64,8 +59,11 @@ static bool VerifyRoms()
{0x128ea7a2, 0xa4a575f5},
}};
const u32 hash_irom = Common::HashAdler32(reinterpret_cast<u8*>(g_dsp.irom), DSP_IROM_BYTE_SIZE);
const u32 hash_drom = Common::HashAdler32(reinterpret_cast<u8*>(g_dsp.coef), DSP_COEF_BYTE_SIZE);
const auto& state = core.DSPState();
const u32 hash_irom =
Common::HashAdler32(reinterpret_cast<const u8*>(state.irom), DSP_IROM_BYTE_SIZE);
const u32 hash_drom =
Common::HashAdler32(reinterpret_cast<const u8*>(state.coef), DSP_COEF_BYTE_SIZE);
int rom_idx = -1;
for (size_t i = 0; i < known_roms.size(); ++i)
@ -104,151 +102,228 @@ static bool VerifyRoms()
return true;
}
static void DSPCore_FreeMemoryPages()
{
Common::FreeMemoryPages(g_dsp.irom, DSP_IROM_BYTE_SIZE);
Common::FreeMemoryPages(g_dsp.iram, DSP_IRAM_BYTE_SIZE);
Common::FreeMemoryPages(g_dsp.dram, DSP_DRAM_BYTE_SIZE);
Common::FreeMemoryPages(g_dsp.coef, DSP_COEF_BYTE_SIZE);
g_dsp.irom = g_dsp.iram = g_dsp.dram = g_dsp.coef = nullptr;
}
class LLEAccelerator final : public Accelerator
{
public:
explicit LLEAccelerator(DSPCore& core) : m_core{core} {}
protected:
u8 ReadMemory(u32 address) override { return Host::ReadHostMemory(address); }
void WriteMemory(u32 address, u8 value) override { Host::WriteHostMemory(value, address); }
void OnEndException() override { DSPCore_SetException(ExceptionType::AcceleratorOverflow); }
void OnEndException() override { m_core.SetException(ExceptionType::AcceleratorOverflow); }
private:
DSPCore& m_core;
};
bool DSPCore_Init(const DSPInitOptions& opts)
SDSP::SDSP(DSPCore& core) : m_dsp_core{core}
{
g_dsp.step_counter = 0;
g_init_hax = false;
}
g_dsp.accelerator = std::make_unique<LLEAccelerator>();
SDSP::~SDSP() = default;
g_dsp.irom = static_cast<u16*>(Common::AllocateMemoryPages(DSP_IROM_BYTE_SIZE));
g_dsp.iram = static_cast<u16*>(Common::AllocateMemoryPages(DSP_IRAM_BYTE_SIZE));
g_dsp.dram = static_cast<u16*>(Common::AllocateMemoryPages(DSP_DRAM_BYTE_SIZE));
g_dsp.coef = static_cast<u16*>(Common::AllocateMemoryPages(DSP_COEF_BYTE_SIZE));
DSPCore::DSPCore()
: m_dsp{*this}, m_dsp_interpreter{std::make_unique<Interpreter::Interpreter>(*this)}
{
}
memcpy(g_dsp.irom, opts.irom_contents.data(), DSP_IROM_BYTE_SIZE);
memcpy(g_dsp.coef, opts.coef_contents.data(), DSP_COEF_BYTE_SIZE);
DSPCore::~DSPCore() = default;
bool DSPCore::Initialize(const DSPInitOptions& opts)
{
m_dsp.step_counter = 0;
m_init_hax = false;
m_dsp.accelerator = std::make_unique<LLEAccelerator>(*this);
m_dsp.irom = static_cast<u16*>(Common::AllocateMemoryPages(DSP_IROM_BYTE_SIZE));
m_dsp.iram = static_cast<u16*>(Common::AllocateMemoryPages(DSP_IRAM_BYTE_SIZE));
m_dsp.dram = static_cast<u16*>(Common::AllocateMemoryPages(DSP_DRAM_BYTE_SIZE));
m_dsp.coef = static_cast<u16*>(Common::AllocateMemoryPages(DSP_COEF_BYTE_SIZE));
std::memcpy(m_dsp.irom, opts.irom_contents.data(), DSP_IROM_BYTE_SIZE);
std::memcpy(m_dsp.coef, opts.coef_contents.data(), DSP_COEF_BYTE_SIZE);
// Try to load real ROM contents.
if (!VerifyRoms())
if (!VerifyRoms(*this))
{
DSPCore_FreeMemoryPages();
FreeMemoryPages();
return false;
}
memset(&g_dsp.r, 0, sizeof(g_dsp.r));
std::memset(&m_dsp.r, 0, sizeof(m_dsp.r));
std::fill(std::begin(g_dsp.reg_stack_ptrs), std::end(g_dsp.reg_stack_ptrs), 0);
std::fill(std::begin(m_dsp.reg_stack_ptrs), std::end(m_dsp.reg_stack_ptrs), 0);
for (auto& stack : g_dsp.reg_stacks)
for (auto& stack : m_dsp.reg_stacks)
std::fill(std::begin(stack), std::end(stack), 0);
// Fill IRAM with HALT opcodes.
std::fill(g_dsp.iram, g_dsp.iram + DSP_IRAM_SIZE, 0x0021);
std::fill(m_dsp.iram, m_dsp.iram + DSP_IRAM_SIZE, 0x0021);
// Just zero out DRAM.
std::fill(g_dsp.dram, g_dsp.dram + DSP_DRAM_SIZE, 0);
std::fill(m_dsp.dram, m_dsp.dram + DSP_DRAM_SIZE, 0);
// Copied from a real console after the custom UCode has been loaded.
// These are the indexing wrapping registers.
std::fill(std::begin(g_dsp.r.wr), std::end(g_dsp.r.wr), 0xffff);
std::fill(std::begin(m_dsp.r.wr), std::end(m_dsp.r.wr), 0xffff);
g_dsp.r.sr |= SR_INT_ENABLE;
g_dsp.r.sr |= SR_EXT_INT_ENABLE;
m_dsp.r.sr |= SR_INT_ENABLE;
m_dsp.r.sr |= SR_EXT_INT_ENABLE;
g_dsp.cr = 0x804;
gdsp_ifx_init();
m_dsp.cr = 0x804;
m_dsp.InitializeIFX();
// Mostly keep IRAM write protected. We unprotect only when DMA-ing
// in new ucodes.
Common::WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
Common::WriteProtectMemory(m_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
// Initialize JIT, if necessary
if (opts.core_type == DSPInitOptions::CoreType::JIT64)
g_dsp_jit = JIT::CreateDSPEmitter();
m_dsp_jit = JIT::CreateDSPEmitter(*this);
g_dsp_cap.reset(opts.capture_logger);
m_dsp_cap.reset(opts.capture_logger);
core_state = State::Running;
m_core_state = State::Running;
return true;
}
void DSPCore_Shutdown()
void DSPCore::Shutdown()
{
if (core_state == State::Stopped)
if (m_core_state == State::Stopped)
return;
core_state = State::Stopped;
m_core_state = State::Stopped;
g_dsp_jit.reset();
m_dsp_jit.reset();
DSPCore_FreeMemoryPages();
FreeMemoryPages();
g_dsp_cap.reset();
m_dsp_cap.reset();
}
void DSPCore_Reset()
// Delegate to JIT or interpreter as appropriate.
// Handle state changes and stepping.
int DSPCore::RunCycles(int cycles)
{
g_dsp.pc = DSP_RESET_VECTOR;
if (m_dsp_jit)
{
return m_dsp_jit->RunCycles(static_cast<u16>(cycles));
}
std::fill(std::begin(g_dsp.r.wr), std::end(g_dsp.r.wr), 0xffff);
while (cycles > 0)
{
switch (m_core_state)
{
case State::Running:
// Seems to slow things down
#if defined(_DEBUG) || defined(DEBUGFAST)
cycles = m_dsp_interpreter->RunCyclesDebug(cycles);
#else
cycles = m_dsp_interpreter->RunCycles(cycles);
#endif
break;
Analyzer::Analyze();
case State::Stepping:
m_step_event.Wait();
if (m_core_state != State::Stepping)
continue;
m_dsp_interpreter->Step();
cycles--;
Host::UpdateDebugger();
break;
case State::Stopped:
break;
}
}
return cycles;
}
void DSPCore_SetException(ExceptionType exception)
void DSPCore::Step()
{
g_dsp.exceptions |= 1 << static_cast<std::underlying_type_t<ExceptionType>>(exception);
if (m_core_state == State::Stepping)
m_step_event.Set();
}
// Notify that an external interrupt is pending (used by thread mode)
void DSPCore_SetExternalInterrupt(bool val)
void DSPCore::Reset()
{
g_dsp.external_interrupt_waiting = val;
m_dsp.pc = DSP_RESET_VECTOR;
std::fill(std::begin(m_dsp.r.wr), std::end(m_dsp.r.wr), 0xffff);
Analyzer::Analyze(m_dsp);
}
// Coming from the CPU
void DSPCore_CheckExternalInterrupt()
void DSPCore::ClearIRAM()
{
if (!Interpreter::dsp_SR_is_flag_set(SR_EXT_INT_ENABLE))
if (!m_dsp_jit)
return;
m_dsp_jit->ClearIRAM();
}
void DSPCore::SetState(State new_state)
{
m_core_state = new_state;
// kick the event, in case we are waiting
if (new_state == State::Running)
m_step_event.Set();
Host::UpdateDebugger();
}
State DSPCore::GetState() const
{
return m_core_state;
}
void DSPCore::SetException(ExceptionType exception)
{
m_dsp.exceptions |= 1 << static_cast<std::underlying_type_t<ExceptionType>>(exception);
}
void DSPCore::SetExternalInterrupt(bool val)
{
m_dsp.external_interrupt_waiting = val;
}
void DSPCore::CheckExternalInterrupt()
{
if (!m_dsp.IsSRFlagSet(SR_EXT_INT_ENABLE))
return;
// Signal the SPU about new mail
DSPCore_SetException(ExceptionType::ExternalInterrupt);
SetException(ExceptionType::ExternalInterrupt);
g_dsp.cr &= ~CR_EXTERNAL_INT;
m_dsp.cr &= ~CR_EXTERNAL_INT;
}
void DSPCore_CheckExceptions()
void DSPCore::CheckExceptions()
{
// Early out to skip the loop in the common case.
if (g_dsp.exceptions == 0)
if (m_dsp.exceptions == 0)
return;
for (int i = 7; i > 0; i--)
{
// Seems exp int are not masked by sr_int_enable
if (g_dsp.exceptions & (1 << i))
if ((m_dsp.exceptions & (1U << i)) != 0)
{
if (Interpreter::dsp_SR_is_flag_set(SR_INT_ENABLE) ||
if (m_dsp.IsSRFlagSet(SR_INT_ENABLE) ||
i == static_cast<int>(ExceptionType::ExternalInterrupt))
{
// store pc and sr until RTI
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
dsp_reg_store_stack(StackRegister::Data, g_dsp.r.sr);
m_dsp.StoreStack(StackRegister::Call, m_dsp.pc);
m_dsp.StoreStack(StackRegister::Data, m_dsp.r.sr);
g_dsp.pc = i * 2;
g_dsp.exceptions &= ~(1 << i);
m_dsp.pc = static_cast<u16>(i * 2);
m_dsp.exceptions &= ~(1 << i);
if (i == 7)
g_dsp.r.sr &= ~SR_EXT_INT_ENABLE;
m_dsp.r.sr &= ~SR_EXT_INT_ENABLE;
else
g_dsp.r.sr &= ~SR_INT_ENABLE;
m_dsp.r.sr &= ~SR_INT_ENABLE;
break;
}
else
@ -261,68 +336,7 @@ void DSPCore_CheckExceptions()
}
}
// Delegate to JIT or interpreter as appropriate.
// Handle state changes and stepping.
int DSPCore_RunCycles(int cycles)
{
if (g_dsp_jit)
{
return g_dsp_jit->RunCycles(static_cast<u16>(cycles));
}
while (cycles > 0)
{
switch (core_state)
{
case State::Running:
// Seems to slow things down
#if defined(_DEBUG) || defined(DEBUGFAST)
cycles = Interpreter::RunCyclesDebug(cycles);
#else
cycles = Interpreter::RunCycles(cycles);
#endif
break;
case State::Stepping:
step_event.Wait();
if (core_state != State::Stepping)
continue;
Interpreter::Step();
cycles--;
Host::UpdateDebugger();
break;
case State::Stopped:
break;
}
}
return cycles;
}
void DSPCore_SetState(State new_state)
{
core_state = new_state;
// kick the event, in case we are waiting
if (new_state == State::Running)
step_event.Set();
Host::UpdateDebugger();
}
State DSPCore_GetState()
{
return core_state;
}
void DSPCore_Step()
{
if (core_state == State::Stepping)
step_event.Set();
}
u16 DSPCore_ReadRegister(size_t reg)
u16 DSPCore::ReadRegister(size_t reg) const
{
switch (reg)
{
@ -330,56 +344,56 @@ u16 DSPCore_ReadRegister(size_t reg)
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
return g_dsp.r.ar[reg - DSP_REG_AR0];
return m_dsp.r.ar[reg - DSP_REG_AR0];
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
return g_dsp.r.ix[reg - DSP_REG_IX0];
return m_dsp.r.ix[reg - DSP_REG_IX0];
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
return g_dsp.r.wr[reg - DSP_REG_WR0];
return m_dsp.r.wr[reg - DSP_REG_WR0];
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
return g_dsp.r.st[reg - DSP_REG_ST0];
return m_dsp.r.st[reg - DSP_REG_ST0];
case DSP_REG_ACH0:
case DSP_REG_ACH1:
return g_dsp.r.ac[reg - DSP_REG_ACH0].h;
return m_dsp.r.ac[reg - DSP_REG_ACH0].h;
case DSP_REG_CR:
return g_dsp.r.cr;
return m_dsp.r.cr;
case DSP_REG_SR:
return g_dsp.r.sr;
return m_dsp.r.sr;
case DSP_REG_PRODL:
return g_dsp.r.prod.l;
return m_dsp.r.prod.l;
case DSP_REG_PRODM:
return g_dsp.r.prod.m;
return m_dsp.r.prod.m;
case DSP_REG_PRODH:
return g_dsp.r.prod.h;
return m_dsp.r.prod.h;
case DSP_REG_PRODM2:
return g_dsp.r.prod.m2;
return m_dsp.r.prod.m2;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
return g_dsp.r.ax[reg - DSP_REG_AXL0].l;
return m_dsp.r.ax[reg - DSP_REG_AXL0].l;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
return g_dsp.r.ax[reg - DSP_REG_AXH0].h;
return m_dsp.r.ax[reg - DSP_REG_AXH0].h;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
return g_dsp.r.ac[reg - DSP_REG_ACL0].l;
return m_dsp.r.ac[reg - DSP_REG_ACL0].l;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
return g_dsp.r.ac[reg - DSP_REG_ACM0].m;
return m_dsp.r.ac[reg - DSP_REG_ACM0].m;
default:
ASSERT_MSG(DSP_CORE, 0, "cannot happen");
return 0;
}
}
void DSPCore_WriteRegister(size_t reg, u16 val)
void DSPCore::WriteRegister(size_t reg, u16 val)
{
switch (reg)
{
@ -387,64 +401,154 @@ void DSPCore_WriteRegister(size_t reg, u16 val)
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
g_dsp.r.ar[reg - DSP_REG_AR0] = val;
m_dsp.r.ar[reg - DSP_REG_AR0] = val;
break;
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
g_dsp.r.ix[reg - DSP_REG_IX0] = val;
m_dsp.r.ix[reg - DSP_REG_IX0] = val;
break;
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
g_dsp.r.wr[reg - DSP_REG_WR0] = val;
m_dsp.r.wr[reg - DSP_REG_WR0] = val;
break;
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
g_dsp.r.st[reg - DSP_REG_ST0] = val;
m_dsp.r.st[reg - DSP_REG_ST0] = val;
break;
case DSP_REG_ACH0:
case DSP_REG_ACH1:
g_dsp.r.ac[reg - DSP_REG_ACH0].h = val;
m_dsp.r.ac[reg - DSP_REG_ACH0].h = val;
break;
case DSP_REG_CR:
g_dsp.r.cr = val;
m_dsp.r.cr = val;
break;
case DSP_REG_SR:
g_dsp.r.sr = val;
m_dsp.r.sr = val;
break;
case DSP_REG_PRODL:
g_dsp.r.prod.l = val;
m_dsp.r.prod.l = val;
break;
case DSP_REG_PRODM:
g_dsp.r.prod.m = val;
m_dsp.r.prod.m = val;
break;
case DSP_REG_PRODH:
g_dsp.r.prod.h = val;
m_dsp.r.prod.h = val;
break;
case DSP_REG_PRODM2:
g_dsp.r.prod.m2 = val;
m_dsp.r.prod.m2 = val;
break;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
g_dsp.r.ax[reg - DSP_REG_AXL0].l = val;
m_dsp.r.ax[reg - DSP_REG_AXL0].l = val;
break;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
g_dsp.r.ax[reg - DSP_REG_AXH0].h = val;
m_dsp.r.ax[reg - DSP_REG_AXH0].h = val;
break;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
g_dsp.r.ac[reg - DSP_REG_ACL0].l = val;
m_dsp.r.ac[reg - DSP_REG_ACL0].l = val;
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
g_dsp.r.ac[reg - DSP_REG_ACM0].m = val;
m_dsp.r.ac[reg - DSP_REG_ACM0].m = val;
break;
}
}
u32 DSPCore::PeekMailbox(Mailbox mailbox) const
{
return m_dsp.PeekMailbox(mailbox);
}
u16 DSPCore::ReadMailboxLow(Mailbox mailbox)
{
return m_dsp.ReadMailboxLow(mailbox);
}
u16 DSPCore::ReadMailboxHigh(Mailbox mailbox)
{
return m_dsp.ReadMailboxHigh(mailbox);
}
void DSPCore::WriteMailboxLow(Mailbox mailbox, u16 value)
{
m_dsp.WriteMailboxLow(mailbox, value);
}
void DSPCore::WriteMailboxHigh(Mailbox mailbox, u16 value)
{
m_dsp.WriteMailboxHigh(mailbox, value);
}
void DSPCore::LogIFXRead(u16 address, u16 read_value)
{
m_dsp_cap->LogIFXRead(address, read_value);
}
void DSPCore::LogIFXWrite(u16 address, u16 written_value)
{
m_dsp_cap->LogIFXWrite(address, written_value);
}
void DSPCore::LogDMA(u16 control, u32 gc_address, u16 dsp_address, u16 length, const u8* data)
{
m_dsp_cap->LogDMA(control, gc_address, dsp_address, length, data);
}
bool DSPCore::IsJITCreated() const
{
return m_dsp_jit != nullptr;
}
void DSPCore::DoState(PointerWrap& p)
{
p.Do(m_dsp.r);
p.Do(m_dsp.pc);
p.Do(m_dsp.cr);
p.Do(m_dsp.reg_stack_ptrs);
p.Do(m_dsp.exceptions);
p.Do(m_dsp.external_interrupt_waiting);
for (auto& stack : m_dsp.reg_stacks)
{
p.Do(stack);
}
p.Do(m_dsp.step_counter);
p.DoArray(m_dsp.ifx_regs);
m_dsp.accelerator->DoState(p);
p.Do(m_dsp.mbox[0]);
p.Do(m_dsp.mbox[1]);
Common::UnWriteProtectMemory(m_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
p.DoArray(m_dsp.iram, DSP_IRAM_SIZE);
Common::WriteProtectMemory(m_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
// TODO: This uses the wrong endianness (producing bad disassembly)
// and a bogus byte count (producing bad hashes)
if (p.GetMode() == PointerWrap::MODE_READ)
Host::CodeLoaded(*this, reinterpret_cast<const u8*>(m_dsp.iram), DSP_IRAM_BYTE_SIZE);
p.DoArray(m_dsp.dram, DSP_DRAM_SIZE);
p.Do(m_init_hax);
if (m_dsp_jit)
m_dsp_jit->DoState(p);
}
void DSPCore::FreeMemoryPages()
{
Common::FreeMemoryPages(m_dsp.irom, DSP_IROM_BYTE_SIZE);
Common::FreeMemoryPages(m_dsp.iram, DSP_IRAM_BYTE_SIZE);
Common::FreeMemoryPages(m_dsp.dram, DSP_DRAM_BYTE_SIZE);
Common::FreeMemoryPages(m_dsp.coef, DSP_COEF_BYTE_SIZE);
m_dsp.irom = nullptr;
m_dsp.iram = nullptr;
m_dsp.dram = nullptr;
m_dsp.coef = nullptr;
}
} // namespace DSP

View File

@ -11,12 +11,21 @@
#include <memory>
#include <string>
#include "Common/Event.h"
#include "Core/DSP/DSPBreakpoints.h"
#include "Core/DSP/DSPCaptureLogger.h"
class PointerWrap;
namespace DSP
{
class Accelerator;
class DSPCore;
namespace Interpreter
{
class Interpreter;
}
namespace JIT
{
@ -216,6 +225,12 @@ enum class ExceptionType
ExternalInterrupt = 7 // 0x000e external int (message from CPU)
};
enum Mailbox : int
{
MAILBOX_CPU,
MAILBOX_DSP
};
struct DSP_Regs
{
u16 ar[4];
@ -263,22 +278,82 @@ struct DSP_Regs
// should be moved here.
struct SDSP
{
DSP_Regs r;
u16 pc;
#if PROFILE
u16 err_pc;
#endif
explicit SDSP(DSPCore& core);
~SDSP();
SDSP(const SDSP&) = delete;
SDSP& operator=(const SDSP&) = delete;
SDSP(SDSP&&) = delete;
SDSP& operator=(SDSP&&) = delete;
// Initializes the IFX registers.
void InitializeIFX();
// Writes to IFX registers.
void WriteIFX(u32 address, u16 value);
// Reads from IFX registers.
u16 ReadIFX(u16 address);
// Checks the whole value within a mailbox.
u32 PeekMailbox(Mailbox mailbox) const;
// Reads the low part of the value in the specified mailbox.
u16 ReadMailboxLow(Mailbox mailbox);
// Reads the high part of the value in the specified mailbox.
u16 ReadMailboxHigh(Mailbox mailbox);
// Writes to the low part of the mailbox.
void WriteMailboxLow(Mailbox mailbox, u16 value);
// Writes to the high part of the mailbox.
void WriteMailboxHigh(Mailbox mailbox, u16 value);
// Reads from instruction memory.
u16 ReadIMEM(u16 address) const;
// Reads from data memory.
u16 ReadDMEM(u16 address);
// Write to data memory.
void WriteDMEM(u16 address, u16 value);
// Fetches the next instruction and increments the PC.
u16 FetchInstruction();
// Fetches the instruction at the PC address, but doesn't increment the PC.
u16 PeekInstruction() const;
// Skips over the next instruction in memory.
void SkipInstruction();
// Sets the given flags in the SR register.
void SetSRFlag(u16 flag) { r.sr |= flag; }
// Whether or not the given flag is set in the SR register.
bool IsSRFlagSet(u16 flag) const { return (r.sr & flag) != 0; }
// Stores a value into the specified stack
void StoreStack(StackRegister stack_reg, u16 val);
// Pops a value off of the specified stack
u16 PopStack(StackRegister stack_reg);
DSP_Regs r{};
u16 pc = 0;
// This is NOT the same cr as r.cr.
// This register is shared with the main emulation, see DSP.cpp
// The engine has control over 0x0C07 of this reg.
// Bits are defined in a struct in DSP.cpp.
u16 cr;
u16 cr = 0;
u8 reg_stack_ptrs[4];
u8 exceptions; // pending exceptions
volatile bool external_interrupt_waiting;
bool reset_dspjit_codespace;
u8 reg_stack_ptrs[4]{};
u8 exceptions = 0; // pending exceptions
volatile bool external_interrupt_waiting = false;
bool reset_dspjit_codespace = false;
// DSP hardware stacks. They're mapped to a bunch of registers, such that writes
// to them push and reads pop.
@ -286,33 +361,38 @@ struct SDSP
// The real DSP has different depths for the different stacks, but it would
// be strange if any ucode relied on stack overflows since on the DSP, when
// the stack overflows, you're screwed.
u16 reg_stacks[4][DSP_STACK_DEPTH];
u16 reg_stacks[4][DSP_STACK_DEPTH]{};
// For debugging.
u32 iram_crc;
u64 step_counter;
u32 iram_crc = 0;
u64 step_counter = 0;
// Mailbox.
std::atomic<u32> mbox[2];
// Accelerator / DMA / other hardware registers. Not GPRs.
std::array<u16, 256> ifx_regs;
std::array<u16, 256> ifx_regs{};
std::unique_ptr<Accelerator> accelerator;
// When state saving, all of the above can just be memcpy'd into the save state.
// The below needs special handling.
u16* iram;
u16* dram;
u16* irom;
u16* coef;
};
u16* iram = nullptr;
u16* dram = nullptr;
u16* irom = nullptr;
u16* coef = nullptr;
extern SDSP g_dsp;
extern DSPBreakpoints g_dsp_breakpoints;
extern bool g_init_hax;
extern std::unique_ptr<JIT::DSPEmitter> g_dsp_jit;
extern std::unique_ptr<DSPCaptureLogger> g_dsp_cap;
private:
void DoDMA();
const u8* DDMAIn(u16 dsp_addr, u32 addr, u32 size);
const u8* DDMAOut(u16 dsp_addr, u32 addr, u32 size);
const u8* IDMAIn(u16 dsp_addr, u32 addr, u32 size);
const u8* IDMAOut(u16 dsp_addr, u32 addr, u32 size);
u16 ReadIFXImpl(u16 address);
DSPCore& m_dsp_core;
};
struct DSPInitOptions
{
@ -338,20 +418,6 @@ struct DSPInitOptions
DSPInitOptions() : capture_logger(new DefaultDSPCaptureLogger()) {}
};
// Initializes the DSP emulator using the provided options. Takes ownership of
// all the pointers contained in the options structure.
bool DSPCore_Init(const DSPInitOptions& opts);
void DSPCore_Reset();
void DSPCore_Shutdown(); // Frees all allocated memory.
void DSPCore_CheckExternalInterrupt();
void DSPCore_CheckExceptions();
void DSPCore_SetExternalInterrupt(bool val);
// sets a flag in the pending exception register.
void DSPCore_SetException(ExceptionType exception);
enum class State
{
Stopped,
@ -359,14 +425,117 @@ enum class State
Stepping,
};
int DSPCore_RunCycles(int cycles);
class DSPCore
{
public:
DSPCore();
~DSPCore();
// These are meant to be called from the UI thread.
void DSPCore_SetState(State new_state);
State DSPCore_GetState();
DSPCore(const DSPCore&) = delete;
DSPCore& operator=(const DSPCore&) = delete;
void DSPCore_Step();
DSPCore(DSPCore&&) = delete;
DSPCore& operator=(DSPCore&&) = delete;
u16 DSPCore_ReadRegister(size_t reg);
void DSPCore_WriteRegister(size_t reg, u16 val);
// Initializes the DSP emulator using the provided options. Takes ownership of
// all the pointers contained in the options structure.
bool Initialize(const DSPInitOptions& opts);
// Shuts down the DSP core and cleans up any necessary state.
void Shutdown();
// Delegates to JIT or interpreter as appropriate.
// Handle state changes and stepping.
int RunCycles(int cycles);
// Steps the DSP by a single instruction.
void Step();
// Resets DSP state as if the reset exception vector has been taken.
void Reset();
// Clears the DSP instruction RAM.
void ClearIRAM();
// Dictates whether or not the DSP is currently stopped, running or stepping
// through instructions.
void SetState(State new_state);
// Retrieves the current execution state of the DSP.
State GetState() const;
// Indicates that a particular exception has occurred
// and sets a flag in the pending exception register.
void SetException(ExceptionType exception);
// Notify that an external interrupt is pending (used by thread mode)
void SetExternalInterrupt(bool val);
// Coming from the CPU
void CheckExternalInterrupt();
// Checks if any exceptions occurred an updates the DSP state as appropriate.
void CheckExceptions();
// Reads the current value from a particular register.
u16 ReadRegister(size_t reg) const;
// Writes a value to a given register.
void WriteRegister(size_t reg, u16 val);
// Checks the value within a mailbox.
u32 PeekMailbox(Mailbox mailbox) const;
// Reads the low part of the specified mailbox register.
u16 ReadMailboxLow(Mailbox mailbox);
// Reads the high part of the specified mailbox register.
u16 ReadMailboxHigh(Mailbox mailbox);
// Writes to the low part of the mailbox register.
void WriteMailboxLow(Mailbox mailbox, u16 value);
// Writes to the high part of the mailbox register.
void WriteMailboxHigh(Mailbox mailbox, u16 value);
// Logs an IFX register read.
void LogIFXRead(u16 address, u16 read_value);
// Logs an IFX register write.
void LogIFXWrite(u16 address, u16 written_value);
// Logs a DMA operation
void LogDMA(u16 control, u32 gc_address, u16 dsp_address, u16 length, const u8* data);
// Whether or not the JIT has been created.
bool IsJITCreated() const;
// Writes or loads state for savestates.
void DoState(PointerWrap& p);
// Accessors for the DSP breakpoint facilities.
DSPBreakpoints& BreakPoints() { return m_dsp_breakpoints; }
const DSPBreakpoints& BreakPoints() const { return m_dsp_breakpoints; }
SDSP& DSPState() { return m_dsp; }
const SDSP& DSPState() const { return m_dsp; }
Interpreter::Interpreter& GetInterpreter() { return *m_dsp_interpreter; }
const Interpreter::Interpreter& GetInterpreter() const { return *m_dsp_interpreter; }
bool GetInitHax() const { return m_init_hax; }
void SetInitHax(bool value) { m_init_hax = value; }
private:
void FreeMemoryPages();
SDSP m_dsp;
DSPBreakpoints m_dsp_breakpoints;
State m_core_state = State::Stopped;
bool m_init_hax = false;
std::unique_ptr<Interpreter::Interpreter> m_dsp_interpreter;
std::unique_ptr<JIT::DSPEmitter> m_dsp_jit;
std::unique_ptr<DSPCaptureLogger> m_dsp_cap;
Common::Event m_step_event;
};
} // namespace DSP

View File

@ -3,8 +3,6 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/DSP/DSPHWInterface.h"
#include <atomic>
#include <cstddef>
#include <cstring>
@ -23,349 +21,346 @@
namespace DSP
{
static void gdsp_do_dma();
void gdsp_ifx_init()
void SDSP::InitializeIFX()
{
g_dsp.ifx_regs.fill(0);
ifx_regs.fill(0);
g_dsp.mbox[MAILBOX_CPU].store(0);
g_dsp.mbox[MAILBOX_DSP].store(0);
mbox[MAILBOX_CPU].store(0);
mbox[MAILBOX_DSP].store(0);
}
u32 gdsp_mbox_peek(Mailbox mbx)
u32 SDSP::PeekMailbox(Mailbox mailbox) const
{
return g_dsp.mbox[mbx].load();
return mbox[mailbox].load();
}
void gdsp_mbox_write_h(Mailbox mbx, u16 val)
u16 SDSP::ReadMailboxLow(Mailbox mailbox)
{
const u32 old_value = g_dsp.mbox[mbx].load(std::memory_order_acquire);
const u32 new_value = (old_value & 0xffff) | (val << 16);
const u32 value = mbox[mailbox].load(std::memory_order_acquire);
mbox[mailbox].store(value & ~0x80000000, std::memory_order_release);
g_dsp.mbox[mbx].store(new_value & ~0x80000000, std::memory_order_release);
}
void gdsp_mbox_write_l(Mailbox mbx, u16 val)
{
const u32 old_value = g_dsp.mbox[mbx].load(std::memory_order_acquire);
const u32 new_value = (old_value & ~0xffff) | val;
g_dsp.mbox[mbx].store(new_value | 0x80000000, std::memory_order_release);
#if defined(_DEBUG) || defined(DEBUGFAST)
const char* const type = mbx == MAILBOX_DSP ? "DSP" : "CPU";
DEBUG_LOG_FMT(DSP_MAIL, "{}(WM) B:{} M:{:#010x} (pc={:#06x})", type, mbx, gdsp_mbox_peek(mbx),
g_dsp.pc);
#endif
}
u16 gdsp_mbox_read_h(Mailbox mbx)
{
if (g_init_hax && mbx == MAILBOX_DSP)
if (m_dsp_core.GetInitHax() && mailbox == MAILBOX_DSP)
{
return 0x8054;
}
return (u16)(g_dsp.mbox[mbx].load() >> 16); // TODO: mask away the top bit?
}
u16 gdsp_mbox_read_l(Mailbox mbx)
{
const u32 value = g_dsp.mbox[mbx].load(std::memory_order_acquire);
g_dsp.mbox[mbx].store(value & ~0x80000000, std::memory_order_release);
if (g_init_hax && mbx == MAILBOX_DSP)
{
g_init_hax = false;
DSPCore_Reset();
m_dsp_core.SetInitHax(false);
m_dsp_core.Reset();
return 0x4348;
}
#if defined(_DEBUG) || defined(DEBUGFAST)
const char* const type = mbx == MAILBOX_DSP ? "DSP" : "CPU";
DEBUG_LOG_FMT(DSP_MAIL, "{}(RM) B:{} M:0x{:#010x} (pc={:#06x})", type, mbx, gdsp_mbox_peek(mbx),
g_dsp.pc);
const char* const type = mailbox == MAILBOX_DSP ? "DSP" : "CPU";
DEBUG_LOG_FMT(DSP_MAIL, "{}(RM) B:{} M:0x{:#010x} (pc={:#06x})", type, mailbox,
PeekMailbox(mailbox), pc);
#endif
return (u16)value;
return static_cast<u16>(value);
}
void gdsp_ifx_write(u32 addr, u16 val)
u16 SDSP::ReadMailboxHigh(Mailbox mailbox)
{
g_dsp_cap->LogIFXWrite(addr, val);
if (m_dsp_core.GetInitHax() && mailbox == MAILBOX_DSP)
{
return 0x8054;
}
switch (addr & 0xff)
// TODO: mask away the top bit?
return static_cast<u16>(PeekMailbox(mailbox) >> 16);
}
void SDSP::WriteMailboxLow(Mailbox mailbox, u16 value)
{
const u32 old_value = mbox[mailbox].load(std::memory_order_acquire);
const u32 new_value = (old_value & ~0xffff) | value;
mbox[mailbox].store(new_value | 0x80000000, std::memory_order_release);
#if defined(_DEBUG) || defined(DEBUGFAST)
const char* const type = mailbox == MAILBOX_DSP ? "DSP" : "CPU";
DEBUG_LOG_FMT(DSP_MAIL, "{}(WM) B:{} M:{:#010x} (pc={:#06x})", type, mailbox,
PeekMailbox(mailbox), pc);
#endif
}
void SDSP::WriteMailboxHigh(Mailbox mailbox, u16 value)
{
const u32 old_value = mbox[mailbox].load(std::memory_order_acquire);
const u32 new_value = (old_value & 0xffff) | (value << 16);
mbox[mailbox].store(new_value & ~0x80000000, std::memory_order_release);
}
void SDSP::WriteIFX(u32 address, u16 value)
{
m_dsp_core.LogIFXWrite(address, value);
switch (address & 0xff)
{
case DSP_DIRQ:
if ((val & 1) != 0)
if ((value & 1) != 0)
Host::InterruptRequest();
else
WARN_LOG_FMT(DSPLLE, "Unknown Interrupt Request pc={:#06x} ({:#06x})", g_dsp.pc, val);
WARN_LOG_FMT(DSPLLE, "Unknown Interrupt Request pc={:#06x} ({:#06x})", pc, value);
break;
case DSP_DMBH:
gdsp_mbox_write_h(MAILBOX_DSP, val);
WriteMailboxHigh(MAILBOX_DSP, value);
break;
case DSP_DMBL:
gdsp_mbox_write_l(MAILBOX_DSP, val);
WriteMailboxLow(MAILBOX_DSP, value);
break;
case DSP_CMBH:
return gdsp_mbox_write_h(MAILBOX_CPU, val);
WriteMailboxHigh(MAILBOX_CPU, value);
break;
case DSP_CMBL:
return gdsp_mbox_write_l(MAILBOX_CPU, val);
WriteMailboxLow(MAILBOX_CPU, value);
break;
case DSP_DSBL:
g_dsp.ifx_regs[DSP_DSBL] = val;
g_dsp.ifx_regs[DSP_DSCR] |= 4; // Doesn't really matter since we do DMA instantly
if (!g_dsp.ifx_regs[DSP_AMDM])
gdsp_do_dma();
ifx_regs[DSP_DSBL] = value;
ifx_regs[DSP_DSCR] |= 4; // Doesn't really matter since we do DMA instantly
if (!ifx_regs[DSP_AMDM])
DoDMA();
else
NOTICE_LOG_FMT(DSPLLE, "Masked DMA skipped");
g_dsp.ifx_regs[DSP_DSCR] &= ~4;
g_dsp.ifx_regs[DSP_DSBL] = 0;
ifx_regs[DSP_DSCR] &= ~4;
ifx_regs[DSP_DSBL] = 0;
break;
case DSP_GAIN:
if (val != 0)
if (value != 0)
{
DEBUG_LOG_FMT(DSPLLE, "Gain Written: {:#06x}", val);
DEBUG_LOG_FMT(DSPLLE, "Gain Written: {:#06x}", value);
}
[[fallthrough]];
case DSP_DSPA:
case DSP_DSMAH:
case DSP_DSMAL:
case DSP_DSCR:
g_dsp.ifx_regs[addr & 0xFF] = val;
ifx_regs[address & 0xFF] = value;
break;
case DSP_ACSAH:
g_dsp.accelerator->SetStartAddress(val << 16 |
static_cast<u16>(g_dsp.accelerator->GetStartAddress()));
accelerator->SetStartAddress(value << 16 | static_cast<u16>(accelerator->GetStartAddress()));
break;
case DSP_ACSAL:
g_dsp.accelerator->SetStartAddress(
static_cast<u16>(g_dsp.accelerator->GetStartAddress() >> 16) << 16 | val);
accelerator->SetStartAddress(static_cast<u16>(accelerator->GetStartAddress() >> 16) << 16 |
value);
break;
case DSP_ACEAH:
g_dsp.accelerator->SetEndAddress(val << 16 |
static_cast<u16>(g_dsp.accelerator->GetEndAddress()));
accelerator->SetEndAddress(value << 16 | static_cast<u16>(accelerator->GetEndAddress()));
break;
case DSP_ACEAL:
g_dsp.accelerator->SetEndAddress(
static_cast<u16>(g_dsp.accelerator->GetEndAddress() >> 16) << 16 | val);
accelerator->SetEndAddress(static_cast<u16>(accelerator->GetEndAddress() >> 16) << 16 | value);
break;
case DSP_ACCAH:
g_dsp.accelerator->SetCurrentAddress(val << 16 |
static_cast<u16>(g_dsp.accelerator->GetCurrentAddress()));
accelerator->SetCurrentAddress(value << 16 |
static_cast<u16>(accelerator->GetCurrentAddress()));
break;
case DSP_ACCAL:
g_dsp.accelerator->SetCurrentAddress(
static_cast<u16>(g_dsp.accelerator->GetCurrentAddress() >> 16) << 16 | val);
accelerator->SetCurrentAddress(static_cast<u16>(accelerator->GetCurrentAddress() >> 16) << 16 |
value);
break;
case DSP_FORMAT:
g_dsp.accelerator->SetSampleFormat(val);
accelerator->SetSampleFormat(value);
break;
case DSP_YN1:
g_dsp.accelerator->SetYn1(val);
accelerator->SetYn1(value);
break;
case DSP_YN2:
g_dsp.accelerator->SetYn2(val);
accelerator->SetYn2(value);
break;
case DSP_PRED_SCALE:
g_dsp.accelerator->SetPredScale(val);
accelerator->SetPredScale(value);
break;
case DSP_ACDATA1: // Accelerator write (Zelda type) - "UnkZelda"
g_dsp.accelerator->WriteD3(val);
accelerator->WriteD3(value);
break;
default:
if ((addr & 0xff) >= 0xa0)
if ((address & 0xff) >= 0xa0)
{
const u32 index = (addr & 0xFF) - 0xa0;
const u32 index = (address & 0xFF) - 0xa0;
const auto& label = pdlabels[index];
if (label.name && label.description)
{
DEBUG_LOG_FMT(DSPLLE, "{:04x} MW {} ({:04x})", g_dsp.pc, label.name, val);
DEBUG_LOG_FMT(DSPLLE, "{:04x} MW {} ({:04x})", pc, label.name, value);
}
else
{
ERROR_LOG_FMT(DSPLLE, "{:04x} MW {:04x} ({:04x})", g_dsp.pc, addr, val);
ERROR_LOG_FMT(DSPLLE, "{:04x} MW {:04x} ({:04x})", pc, address, value);
}
}
else
{
ERROR_LOG_FMT(DSPLLE, "{:04x} MW {:04x} ({:04x})", g_dsp.pc, addr, val);
ERROR_LOG_FMT(DSPLLE, "{:04x} MW {:04x} ({:04x})", pc, address, value);
}
g_dsp.ifx_regs[addr & 0xFF] = val;
ifx_regs[address & 0xFF] = value;
break;
}
}
static u16 _gdsp_ifx_read(u16 addr)
u16 SDSP::ReadIFXImpl(u16 address)
{
switch (addr & 0xff)
switch (address & 0xff)
{
case DSP_DMBH:
return gdsp_mbox_read_h(MAILBOX_DSP);
return ReadMailboxHigh(MAILBOX_DSP);
case DSP_DMBL:
return gdsp_mbox_read_l(MAILBOX_DSP);
return ReadMailboxLow(MAILBOX_DSP);
case DSP_CMBH:
return gdsp_mbox_read_h(MAILBOX_CPU);
return ReadMailboxHigh(MAILBOX_CPU);
case DSP_CMBL:
return gdsp_mbox_read_l(MAILBOX_CPU);
return ReadMailboxLow(MAILBOX_CPU);
case DSP_DSCR:
return g_dsp.ifx_regs[addr & 0xFF];
return ifx_regs[address & 0xFF];
case DSP_ACSAH:
return static_cast<u16>(g_dsp.accelerator->GetStartAddress() >> 16);
return static_cast<u16>(accelerator->GetStartAddress() >> 16);
case DSP_ACSAL:
return static_cast<u16>(g_dsp.accelerator->GetStartAddress());
return static_cast<u16>(accelerator->GetStartAddress());
case DSP_ACEAH:
return static_cast<u16>(g_dsp.accelerator->GetEndAddress() >> 16);
return static_cast<u16>(accelerator->GetEndAddress() >> 16);
case DSP_ACEAL:
return static_cast<u16>(g_dsp.accelerator->GetEndAddress());
return static_cast<u16>(accelerator->GetEndAddress());
case DSP_ACCAH:
return static_cast<u16>(g_dsp.accelerator->GetCurrentAddress() >> 16);
return static_cast<u16>(accelerator->GetCurrentAddress() >> 16);
case DSP_ACCAL:
return static_cast<u16>(g_dsp.accelerator->GetCurrentAddress());
return static_cast<u16>(accelerator->GetCurrentAddress());
case DSP_FORMAT:
return g_dsp.accelerator->GetSampleFormat();
return accelerator->GetSampleFormat();
case DSP_YN1:
return g_dsp.accelerator->GetYn1();
return accelerator->GetYn1();
case DSP_YN2:
return g_dsp.accelerator->GetYn2();
return accelerator->GetYn2();
case DSP_PRED_SCALE:
return g_dsp.accelerator->GetPredScale();
return accelerator->GetPredScale();
case DSP_ACCELERATOR: // ADPCM Accelerator reads
return g_dsp.accelerator->Read(reinterpret_cast<s16*>(&g_dsp.ifx_regs[DSP_COEF_A1_0]));
return accelerator->Read(reinterpret_cast<s16*>(&ifx_regs[DSP_COEF_A1_0]));
case DSP_ACDATA1: // Accelerator reads (Zelda type) - "UnkZelda"
return g_dsp.accelerator->ReadD3();
return accelerator->ReadD3();
default:
{
const u16 ifx_reg = g_dsp.ifx_regs[addr & 0xFF];
const u16 ifx_reg = ifx_regs[address & 0xFF];
if ((addr & 0xff) >= 0xa0)
if ((address & 0xff) >= 0xa0)
{
const u32 index = (addr & 0xFF) - 0xa0;
const u32 index = (address & 0xFF) - 0xa0;
const auto& label = pdlabels[index];
if (label.name && label.description)
{
DEBUG_LOG_FMT(DSPLLE, "{:04x} MR {} ({:04x})", g_dsp.pc, label.name, ifx_reg);
DEBUG_LOG_FMT(DSPLLE, "{:04x} MR {} ({:04x})", pc, label.name, ifx_reg);
}
else
{
ERROR_LOG_FMT(DSPLLE, "{:04x} MR {:04x} ({:04x})", g_dsp.pc, addr, ifx_reg);
ERROR_LOG_FMT(DSPLLE, "{:04x} MR {:04x} ({:04x})", pc, address, ifx_reg);
}
}
else
{
ERROR_LOG_FMT(DSPLLE, "{:04x} MR {:04x} ({:04x})", g_dsp.pc, addr, ifx_reg);
ERROR_LOG_FMT(DSPLLE, "{:04x} MR {:04x} ({:04x})", pc, address, ifx_reg);
}
return ifx_reg;
}
}
}
u16 gdsp_ifx_read(u16 addr)
u16 SDSP::ReadIFX(u16 address)
{
u16 retval = _gdsp_ifx_read(addr);
g_dsp_cap->LogIFXRead(addr, retval);
const u16 retval = ReadIFXImpl(address);
m_dsp_core.LogIFXRead(address, retval);
return retval;
}
static const u8* gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size)
const u8* SDSP::IDMAIn(u16 dsp_addr, u32 addr, u32 size)
{
Common::UnWriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
Host::DMAToDSP(g_dsp.iram + dsp_addr / 2, addr, size);
Common::WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
Common::UnWriteProtectMemory(iram, DSP_IRAM_BYTE_SIZE, false);
Host::DMAToDSP(iram + dsp_addr / 2, addr, size);
Common::WriteProtectMemory(iram, DSP_IRAM_BYTE_SIZE, false);
Host::CodeLoaded(addr, size);
Host::CodeLoaded(m_dsp_core, addr, size);
NOTICE_LOG_FMT(DSPLLE, "*** Copy new UCode from {:#010x} to {:#06x} (crc: {:#08x})", addr,
dsp_addr, g_dsp.iram_crc);
dsp_addr, iram_crc);
return reinterpret_cast<u8*>(g_dsp.iram) + dsp_addr;
return reinterpret_cast<const u8*>(iram) + dsp_addr;
}
static const u8* gdsp_idma_out(u16 dsp_addr, u32 addr, u32 size)
const u8* SDSP::IDMAOut(u16 dsp_addr, u32 addr, u32 size)
{
ERROR_LOG_FMT(DSPLLE, "*** idma_out IRAM_DSP ({:#06x}) -> RAM ({:#010x}) : size ({:#010x})",
dsp_addr / 2, addr, size);
return nullptr;
}
// TODO: These should eat clock cycles.
static const u8* gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size)
const u8* SDSP::DDMAIn(u16 dsp_addr, u32 addr, u32 size)
{
Host::DMAToDSP(g_dsp.dram + dsp_addr / 2, addr, size);
Host::DMAToDSP(dram + dsp_addr / 2, addr, size);
DEBUG_LOG_FMT(DSPLLE, "*** ddma_in RAM ({:#010x}) -> DRAM_DSP ({:#06x}) : size ({:#010x})", addr,
dsp_addr / 2, size);
return reinterpret_cast<u8*>(g_dsp.dram) + dsp_addr;
return reinterpret_cast<const u8*>(dram) + dsp_addr;
}
static const u8* gdsp_ddma_out(u16 dsp_addr, u32 addr, u32 size)
const u8* SDSP::DDMAOut(u16 dsp_addr, u32 addr, u32 size)
{
Host::DMAFromDSP(g_dsp.dram + dsp_addr / 2, addr, size);
Host::DMAFromDSP(dram + dsp_addr / 2, addr, size);
DEBUG_LOG_FMT(DSPLLE, "*** ddma_out DRAM_DSP ({:#06x}) -> RAM ({:#010x}) : size ({:#010x})",
dsp_addr / 2, addr, size);
return reinterpret_cast<const u8*>(g_dsp.dram) + dsp_addr;
return reinterpret_cast<const u8*>(dram) + dsp_addr;
}
static void gdsp_do_dma()
void SDSP::DoDMA()
{
const u32 addr = (g_dsp.ifx_regs[DSP_DSMAH] << 16) | g_dsp.ifx_regs[DSP_DSMAL];
const u16 ctl = g_dsp.ifx_regs[DSP_DSCR];
const u16 dsp_addr = g_dsp.ifx_regs[DSP_DSPA] * 2;
const u16 len = g_dsp.ifx_regs[DSP_DSBL];
const u32 addr = (ifx_regs[DSP_DSMAH] << 16) | ifx_regs[DSP_DSMAL];
const u16 ctl = ifx_regs[DSP_DSCR];
const u16 dsp_addr = ifx_regs[DSP_DSPA] * 2;
const u16 len = ifx_regs[DSP_DSBL];
if (len > 0x4000)
{
ERROR_LOG_FMT(DSPLLE,
"DMA ERROR: PC: {:04x}, Control: {:04x}, Address: {:08x}, DSP Address: {:04x}, "
"Size: {:04x}",
g_dsp.pc, ctl, addr, dsp_addr, len);
pc, ctl, addr, dsp_addr, len);
std::exit(0);
}
#if defined(_DEBUG) || defined(DEBUGFAST)
DEBUG_LOG_FMT(
DSPLLE, "DMA pc: {:04x}, Control: {:04x}, Address: {:08x}, DSP Address: {:04x}, Size: {:04x}",
g_dsp.pc, ctl, addr, dsp_addr, len);
pc, ctl, addr, dsp_addr, len);
#endif
const u8* copied_data_ptr = nullptr;
switch (ctl & 0x3)
{
case (DSP_CR_DMEM | DSP_CR_TO_CPU):
copied_data_ptr = gdsp_ddma_out(dsp_addr, addr, len);
copied_data_ptr = DDMAOut(dsp_addr, addr, len);
break;
case (DSP_CR_DMEM | DSP_CR_FROM_CPU):
copied_data_ptr = gdsp_ddma_in(dsp_addr, addr, len);
copied_data_ptr = DDMAIn(dsp_addr, addr, len);
break;
case (DSP_CR_IMEM | DSP_CR_TO_CPU):
copied_data_ptr = gdsp_idma_out(dsp_addr, addr, len);
copied_data_ptr = IDMAOut(dsp_addr, addr, len);
break;
case (DSP_CR_IMEM | DSP_CR_FROM_CPU):
copied_data_ptr = gdsp_idma_in(dsp_addr, addr, len);
copied_data_ptr = IDMAIn(dsp_addr, addr, len);
break;
}
if (copied_data_ptr)
g_dsp_cap->LogDMA(ctl, addr, dsp_addr, len, copied_data_ptr);
m_dsp_core.LogDMA(ctl, addr, dsp_addr, len, copied_data_ptr);
}
} // namespace DSP

View File

@ -1,27 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Copyright 2004 Duddie & Tratax
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Common/CommonTypes.h"
namespace DSP
{
enum Mailbox
{
MAILBOX_CPU,
MAILBOX_DSP
};
u32 gdsp_mbox_peek(Mailbox mbx);
void gdsp_mbox_write_h(Mailbox mbx, u16 val);
void gdsp_mbox_write_l(Mailbox mbx, u16 val);
u16 gdsp_mbox_read_h(Mailbox mbx);
u16 gdsp_mbox_read_l(Mailbox mbx);
void gdsp_ifx_init();
void gdsp_ifx_write(u32 addr, u16 val);
u16 gdsp_ifx_read(u16 addr);
} // namespace DSP

View File

@ -13,6 +13,11 @@
// core isn't used, for example in an asm/disasm tool, then most of these
// can be stubbed out.
namespace DSP
{
class DSPCore;
}
namespace DSP::Host
{
u8 ReadHostMemory(u32 addr);
@ -23,7 +28,7 @@ void OSD_AddMessage(std::string str, u32 ms);
bool OnThread();
bool IsWiiHost();
void InterruptRequest();
void CodeLoaded(u32 addr, size_t size);
void CodeLoaded(const u8* ptr, size_t size);
void CodeLoaded(DSPCore& dsp, u32 addr, size_t size);
void CodeLoaded(DSPCore& dsp, const u8* ptr, size_t size);
void UpdateDebugger();
} // namespace DSP::Host

View File

@ -3,86 +3,81 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/DSP/DSPMemoryMap.h"
#include "Common/Logging/Log.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPHWInterface.h"
#include "Core/DSP/DSPTables.h"
namespace DSP
{
u16 dsp_imem_read(u16 addr)
u16 SDSP::ReadIMEM(u16 address) const
{
switch (addr >> 12)
switch (address >> 12)
{
case 0: // 0xxx IRAM
return g_dsp.iram[addr & DSP_IRAM_MASK];
return iram[address & DSP_IRAM_MASK];
case 8: // 8xxx IROM - contains code to receive code for IRAM, and a bunch of mixing loops.
return g_dsp.irom[addr & DSP_IROM_MASK];
return irom[address & DSP_IROM_MASK];
default: // Unmapped/non-existing memory
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Executing from invalid ({:04x}) memory", g_dsp.pc,
addr);
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Executing from invalid ({:04x}) memory", pc, address);
return 0;
}
}
u16 dsp_dmem_read(u16 addr)
u16 SDSP::ReadDMEM(u16 address)
{
switch (addr >> 12)
switch (address >> 12)
{
case 0x0: // 0xxx DRAM
return g_dsp.dram[addr & DSP_DRAM_MASK];
return dram[address & DSP_DRAM_MASK];
case 0x1: // 1xxx COEF
DEBUG_LOG_FMT(DSPLLE, "{:04x} : Coefficient Read @ {:04x}", g_dsp.pc, addr);
return g_dsp.coef[addr & DSP_COEF_MASK];
DEBUG_LOG_FMT(DSPLLE, "{:04x} : Coefficient Read @ {:04x}", pc, address);
return coef[address & DSP_COEF_MASK];
case 0xf: // Fxxx HW regs
return gdsp_ifx_read(addr);
return ReadIFX(address);
default: // Unmapped/non-existing memory
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Read from UNKNOWN ({:04x}) memory", g_dsp.pc, addr);
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Read from UNKNOWN ({:04x}) memory", pc, address);
return 0;
}
}
void dsp_dmem_write(u16 addr, u16 val)
void SDSP::WriteDMEM(u16 address, u16 value)
{
switch (addr >> 12)
switch (address >> 12)
{
case 0x0: // 0xxx DRAM
g_dsp.dram[addr & DSP_DRAM_MASK] = val;
dram[address & DSP_DRAM_MASK] = value;
break;
case 0xf: // Fxxx HW regs
gdsp_ifx_write(addr, val);
WriteIFX(address, value);
break;
default: // Unmapped/non-existing memory
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Write to UNKNOWN ({:04x}) memory", g_dsp.pc, addr);
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Write to UNKNOWN ({:04x}) memory", pc, address);
break;
}
}
u16 dsp_fetch_code()
u16 SDSP::FetchInstruction()
{
u16 opc = dsp_imem_read(g_dsp.pc);
g_dsp.pc++;
const u16 opc = PeekInstruction();
pc++;
return opc;
}
u16 dsp_peek_code()
u16 SDSP::PeekInstruction() const
{
return dsp_imem_read(g_dsp.pc);
return ReadIMEM(pc);
}
void dsp_skip_inst()
void SDSP::SkipInstruction()
{
g_dsp.pc += GetOpTemplate(dsp_peek_code())->size;
pc += GetOpTemplate(PeekInstruction())->size;
}
} // namespace DSP

View File

@ -1,19 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Copyright 2004 Duddie & Tratax
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Common/CommonTypes.h"
namespace DSP
{
u16 dsp_imem_read(u16 addr);
void dsp_dmem_write(u16 addr, u16 val);
u16 dsp_dmem_read(u16 addr);
u16 dsp_fetch_code();
u16 dsp_peek_code();
void dsp_skip_inst();
} // namespace DSP

View File

@ -3,8 +3,6 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/DSP/DSPStacks.h"
#include <cstddef>
#include "Common/CommonTypes.h"
@ -13,34 +11,26 @@
// Stacks. The stacks are outside the DSP RAM, in dedicated hardware.
namespace DSP
{
static void dsp_reg_stack_push(size_t stack_reg)
{
g_dsp.reg_stack_ptrs[stack_reg]++;
g_dsp.reg_stack_ptrs[stack_reg] &= DSP_STACK_MASK;
g_dsp.reg_stacks[stack_reg][g_dsp.reg_stack_ptrs[stack_reg]] = g_dsp.r.st[stack_reg];
}
static void dsp_reg_stack_pop(size_t stack_reg)
{
g_dsp.r.st[stack_reg] = g_dsp.reg_stacks[stack_reg][g_dsp.reg_stack_ptrs[stack_reg]];
g_dsp.reg_stack_ptrs[stack_reg]--;
g_dsp.reg_stack_ptrs[stack_reg] &= DSP_STACK_MASK;
}
void dsp_reg_store_stack(StackRegister stack_reg, u16 val)
void SDSP::StoreStack(StackRegister stack_reg, u16 val)
{
const auto reg_index = static_cast<size_t>(stack_reg);
dsp_reg_stack_push(reg_index);
g_dsp.r.st[reg_index] = val;
reg_stack_ptrs[reg_index]++;
reg_stack_ptrs[reg_index] &= DSP_STACK_MASK;
reg_stacks[reg_index][reg_stack_ptrs[reg_index]] = r.st[reg_index];
r.st[reg_index] = val;
}
u16 dsp_reg_load_stack(StackRegister stack_reg)
u16 SDSP::PopStack(StackRegister stack_reg)
{
const auto reg_index = static_cast<size_t>(stack_reg);
const u16 val = r.st[reg_index];
r.st[reg_index] = reg_stacks[reg_index][reg_stack_ptrs[reg_index]];
reg_stack_ptrs[reg_index]--;
reg_stack_ptrs[reg_index] &= DSP_STACK_MASK;
const u16 val = g_dsp.r.st[reg_index];
dsp_reg_stack_pop(reg_index);
return val;
}
} // namespace DSP

View File

@ -1,16 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Copyright 2004 Duddie & Tratax
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Common/CommonTypes.h"
namespace DSP
{
enum class StackRegister;
void dsp_reg_store_stack(StackRegister stack_reg, u16 val);
u16 dsp_reg_load_stack(StackRegister stack_reg);
} // namespace DSP

View File

@ -477,9 +477,6 @@ const std::array<pdlabel_t, 36> regnames =
}};
// clang-format on
std::array<u16, WRITEBACK_LOG_SIZE> writeBackLog;
std::array<int, WRITEBACK_LOG_SIZE> writeBackLogIdx;
const char* pdname(u16 val)
{
static char tmpstr[12]; // nasty
@ -612,10 +609,5 @@ void InitInstructionTable()
else
ERROR_LOG_FMT(DSPLLE, "opcode table place {} already in use for {}", i, iter->name);
}
writeBackLogIdx.fill(-1);
// Ensure the interpreter tables are all set up, as JITs also rely on them.
Interpreter::InitInstructionTables();
}
} // namespace DSP

View File

@ -85,10 +85,6 @@ struct DSPOPCTemplate
// Opcodes
extern const DSPOPCTemplate cw;
constexpr size_t WRITEBACK_LOG_SIZE = 5;
extern std::array<u16, WRITEBACK_LOG_SIZE> writeBackLog;
extern std::array<int, WRITEBACK_LOG_SIZE> writeBackLogIdx;
// Predefined labels
struct pdlabel_t
{
@ -105,9 +101,6 @@ const char* pdregname(int val);
const char* pdregnamelong(int val);
void InitInstructionTable();
void ApplyWriteBackLog();
void ZeroWriteBackLog();
void ZeroWriteBackLogPreserveAcc(u8 acc);
// Used by the assembler and disassembler for info retrieval.
const DSPOPCTemplate* FindOpInfoByOpcode(UDSPInstruction opcode);

File diff suppressed because it is too large Load Diff

View File

@ -5,10 +5,6 @@
// Additional copyrights go to Duddie and Tratax (c) 2004
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/DSPStacks.h"
#include "Core/DSP/Interpreter/DSPIntCCUtil.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"
namespace DSP::Interpreter
@ -20,14 +16,16 @@ namespace DSP::Interpreter
// Call function if condition cc has been met. Push program counter of
// instruction following "call" to $st0. Set program counter to address
// represented by value that follows this "call" instruction.
void call(const UDSPInstruction opc)
void Interpreter::call(const UDSPInstruction opc)
{
auto& state = m_dsp_core.DSPState();
// must be outside the if.
u16 dest = dsp_fetch_code();
const u16 dest = state.FetchInstruction();
if (CheckCondition(opc & 0xf))
{
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
g_dsp.pc = dest;
state.StoreStack(StackRegister::Call, state.pc);
state.pc = dest;
}
}
@ -37,28 +35,29 @@ void call(const UDSPInstruction opc)
// Call function if condition cc has been met. Push program counter of
// instruction following "call" to call stack $st0. Set program counter to
// register $R.
void callr(const UDSPInstruction opc)
void Interpreter::callr(const UDSPInstruction opc)
{
if (CheckCondition(opc & 0xf))
{
u8 reg = (opc >> 5) & 0x7;
u16 addr = dsp_op_read_reg(reg);
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
g_dsp.pc = addr;
}
if (!CheckCondition(opc & 0xf))
return;
auto& state = m_dsp_core.DSPState();
const u8 reg = (opc >> 5) & 0x7;
const u16 addr = OpReadRegister(reg);
state.StoreStack(StackRegister::Call, state.pc);
state.pc = addr;
}
// Generic if implementation
// IFcc
// 0000 0010 0111 cccc
// Execute following opcode if the condition has been met.
void ifcc(const UDSPInstruction opc)
void Interpreter::ifcc(const UDSPInstruction opc)
{
if (!CheckCondition(opc & 0xf))
{
// skip the next opcode - we have to lookup its size.
dsp_skip_inst();
}
if (CheckCondition(opc & 0xf))
return;
// skip the next opcode - we have to lookup its size.
m_dsp_core.DSPState().SkipInstruction();
}
// Generic jmp implementation
@ -67,12 +66,13 @@ void ifcc(const UDSPInstruction opc)
// aaaa aaaa aaaa aaaa
// Jump to addressA if condition cc has been met. Set program counter to
// address represented by value that follows this "jmp" instruction.
void jcc(const UDSPInstruction opc)
void Interpreter::jcc(const UDSPInstruction opc)
{
u16 dest = dsp_fetch_code();
auto& state = m_dsp_core.DSPState();
const u16 dest = state.FetchInstruction();
if (CheckCondition(opc & 0xf))
{
g_dsp.pc = dest;
state.pc = dest;
}
}
@ -80,13 +80,14 @@ void jcc(const UDSPInstruction opc)
// JMPcc $R
// 0001 0111 rrr0 cccc
// Jump to address; set program counter to a value from register $R.
void jmprcc(const UDSPInstruction opc)
void Interpreter::jmprcc(const UDSPInstruction opc)
{
if (CheckCondition(opc & 0xf))
{
u8 reg = (opc >> 5) & 0x7;
g_dsp.pc = dsp_op_read_reg(reg);
}
if (!CheckCondition(opc & 0xf))
return;
auto& state = m_dsp_core.DSPState();
const u8 reg = (opc >> 5) & 0x7;
state.pc = OpReadRegister(reg);
}
// Generic ret implementation
@ -94,12 +95,13 @@ void jmprcc(const UDSPInstruction opc)
// 0000 0010 1101 cccc
// Return from subroutine if condition cc has been met. Pops stored PC
// from call stack $st0 and sets $pc to this location.
void ret(const UDSPInstruction opc)
void Interpreter::ret(const UDSPInstruction opc)
{
if (CheckCondition(opc & 0xf))
{
g_dsp.pc = dsp_reg_load_stack(StackRegister::Call);
}
if (!CheckCondition(opc & 0xf))
return;
auto& state = m_dsp_core.DSPState();
state.pc = state.PopStack(StackRegister::Call);
}
// RTI
@ -107,19 +109,21 @@ void ret(const UDSPInstruction opc)
// Return from exception. Pops stored status register $sr from data stack
// $st1 and program counter PC from call stack $st0 and sets $pc to this
// location.
void rti(const UDSPInstruction opc)
void Interpreter::rti(const UDSPInstruction)
{
g_dsp.r.sr = dsp_reg_load_stack(StackRegister::Data);
g_dsp.pc = dsp_reg_load_stack(StackRegister::Call);
auto& state = m_dsp_core.DSPState();
state.r.sr = state.PopStack(StackRegister::Data);
state.pc = state.PopStack(StackRegister::Call);
}
// HALT
// 0000 0000 0020 0001
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
void halt(const UDSPInstruction opc)
void Interpreter::halt(const UDSPInstruction)
{
g_dsp.cr |= 0x4;
g_dsp.pc--;
auto& state = m_dsp_core.DSPState();
state.cr |= 0x4;
state.pc--;
}
// LOOP handling: Loop stack is used to control execution of repeated blocks of
@ -128,29 +132,31 @@ void halt(const UDSPInstruction opc)
// then PC is modified with value from call stack $st0. Otherwise values from
// call stack $st0 and both loop stacks $st2 and $st3 are popped and execution
// continues at next opcode.
void HandleLoop()
void Interpreter::HandleLoop()
{
auto& state = m_dsp_core.DSPState();
// Handle looping hardware.
const u16 rCallAddress = g_dsp.r.st[0];
const u16 rLoopAddress = g_dsp.r.st[2];
u16& rLoopCounter = g_dsp.r.st[3];
const u16 rCallAddress = state.r.st[0];
const u16 rLoopAddress = state.r.st[2];
u16& rLoopCounter = state.r.st[3];
if (rLoopAddress > 0 && rLoopCounter > 0)
{
// FIXME: why -1? because we just read past it.
if (g_dsp.pc - 1 == rLoopAddress)
if (state.pc - 1 == rLoopAddress)
{
rLoopCounter--;
if (rLoopCounter > 0)
{
g_dsp.pc = rCallAddress;
state.pc = rCallAddress;
}
else
{
// end of loop
dsp_reg_load_stack(StackRegister::Call);
dsp_reg_load_stack(StackRegister::LoopAddress);
dsp_reg_load_stack(StackRegister::LoopCounter);
state.PopStack(StackRegister::Call);
state.PopStack(StackRegister::LoopAddress);
state.PopStack(StackRegister::LoopCounter);
}
}
}
@ -164,21 +170,22 @@ void HandleLoop()
// then looped instruction will not get executed.
// Actually, this instruction simply prepares the loop stacks for the above.
// The looping hardware takes care of the rest.
void loop(const UDSPInstruction opc)
void Interpreter::loop(const UDSPInstruction opc)
{
u16 reg = opc & 0x1f;
u16 cnt = dsp_op_read_reg(reg);
u16 loop_pc = g_dsp.pc;
auto& state = m_dsp_core.DSPState();
const u16 reg = opc & 0x1f;
const u16 cnt = OpReadRegister(reg);
const u16 loop_pc = state.pc;
if (cnt)
if (cnt != 0)
{
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
dsp_reg_store_stack(StackRegister::LoopAddress, loop_pc);
dsp_reg_store_stack(StackRegister::LoopCounter, cnt);
state.StoreStack(StackRegister::Call, state.pc);
state.StoreStack(StackRegister::LoopAddress, loop_pc);
state.StoreStack(StackRegister::LoopCounter, cnt);
}
else
{
dsp_skip_inst();
state.SkipInstruction();
}
}
@ -190,20 +197,21 @@ void loop(const UDSPInstruction opc)
// instruction will not get executed.
// Actually, this instruction simply prepares the loop stacks for the above.
// The looping hardware takes care of the rest.
void loopi(const UDSPInstruction opc)
void Interpreter::loopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
u16 loop_pc = g_dsp.pc;
auto& state = m_dsp_core.DSPState();
const u16 cnt = opc & 0xff;
const u16 loop_pc = state.pc;
if (cnt)
if (cnt != 0)
{
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
dsp_reg_store_stack(StackRegister::LoopAddress, loop_pc);
dsp_reg_store_stack(StackRegister::LoopCounter, cnt);
state.StoreStack(StackRegister::Call, state.pc);
state.StoreStack(StackRegister::LoopAddress, loop_pc);
state.StoreStack(StackRegister::LoopCounter, cnt);
}
else
{
dsp_skip_inst();
state.SkipInstruction();
}
}
@ -216,22 +224,23 @@ void loopi(const UDSPInstruction opc)
// included in loop. Counter is pushed on loop stack $st3, end of block address
// is pushed on loop stack $st2 and repeat address is pushed on call stack $st0.
// Up to 4 nested loops are allowed.
void bloop(const UDSPInstruction opc)
void Interpreter::bloop(const UDSPInstruction opc)
{
u16 reg = opc & 0x1f;
u16 cnt = dsp_op_read_reg(reg);
u16 loop_pc = dsp_fetch_code();
auto& state = m_dsp_core.DSPState();
const u16 reg = opc & 0x1f;
const u16 cnt = OpReadRegister(reg);
const u16 loop_pc = state.FetchInstruction();
if (cnt)
if (cnt != 0)
{
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
dsp_reg_store_stack(StackRegister::LoopAddress, loop_pc);
dsp_reg_store_stack(StackRegister::LoopCounter, cnt);
state.StoreStack(StackRegister::Call, state.pc);
state.StoreStack(StackRegister::LoopAddress, loop_pc);
state.StoreStack(StackRegister::LoopCounter, cnt);
}
else
{
g_dsp.pc = loop_pc;
dsp_skip_inst();
state.pc = loop_pc;
state.SkipInstruction();
}
}
@ -244,21 +253,22 @@ void bloop(const UDSPInstruction opc)
// loop. Counter is pushed on loop stack $st3, end of block address is pushed
// on loop stack $st2 and repeat address is pushed on call stack $st0. Up to 4
// nested loops are allowed.
void bloopi(const UDSPInstruction opc)
void Interpreter::bloopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
u16 loop_pc = dsp_fetch_code();
auto& state = m_dsp_core.DSPState();
const u16 cnt = opc & 0xff;
const u16 loop_pc = state.FetchInstruction();
if (cnt)
if (cnt != 0)
{
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
dsp_reg_store_stack(StackRegister::LoopAddress, loop_pc);
dsp_reg_store_stack(StackRegister::LoopCounter, cnt);
state.StoreStack(StackRegister::Call, state.pc);
state.StoreStack(StackRegister::LoopAddress, loop_pc);
state.StoreStack(StackRegister::LoopCounter, cnt);
}
else
{
g_dsp.pc = loop_pc;
dsp_skip_inst();
state.pc = loop_pc;
state.SkipInstruction();
}
}
} // namespace DSP::Interpreter

View File

@ -1,183 +0,0 @@
// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
//
// Additional copyrights go to Duddie and Tratax (c) 2004
// HELPER FUNCTIONS
#include "Core/DSP/Interpreter/DSPIntCCUtil.h"
#include "Core/DSP/DSPCore.h"
namespace DSP::Interpreter
{
void Update_SR_Register64(s64 _Value, bool carry, bool overflow)
{
g_dsp.r.sr &= ~SR_CMP_MASK;
// 0x01
if (carry)
{
g_dsp.r.sr |= SR_CARRY;
}
// 0x02 and 0x80
if (overflow)
{
g_dsp.r.sr |= SR_OVERFLOW;
g_dsp.r.sr |= SR_OVERFLOW_STICKY;
}
// 0x04
if (_Value == 0)
{
g_dsp.r.sr |= SR_ARITH_ZERO;
}
// 0x08
if (_Value < 0)
{
g_dsp.r.sr |= SR_SIGN;
}
// 0x10
if (_Value != (s32)_Value)
{
g_dsp.r.sr |= SR_OVER_S32;
}
// 0x20 - Checks if top bits of m are equal
if (((_Value & 0xc0000000) == 0) || ((_Value & 0xc0000000) == 0xc0000000))
{
g_dsp.r.sr |= SR_TOP2BITS;
}
}
void Update_SR_Register16(s16 _Value, bool carry, bool overflow, bool overS32)
{
g_dsp.r.sr &= ~SR_CMP_MASK;
// 0x01
if (carry)
{
g_dsp.r.sr |= SR_CARRY;
}
// 0x02 and 0x80
if (overflow)
{
g_dsp.r.sr |= SR_OVERFLOW;
g_dsp.r.sr |= SR_OVERFLOW_STICKY;
}
// 0x04
if (_Value == 0)
{
g_dsp.r.sr |= SR_ARITH_ZERO;
}
// 0x08
if (_Value < 0)
{
g_dsp.r.sr |= SR_SIGN;
}
// 0x10
if (overS32)
{
g_dsp.r.sr |= SR_OVER_S32;
}
// 0x20 - Checks if top bits of m are equal
if ((((u16)_Value >> 14) == 0) || (((u16)_Value >> 14) == 3))
{
g_dsp.r.sr |= SR_TOP2BITS;
}
}
void Update_SR_LZ(bool value)
{
if (value == true)
g_dsp.r.sr |= SR_LOGIC_ZERO;
else
g_dsp.r.sr &= ~SR_LOGIC_ZERO;
}
static bool IsCarry()
{
return (g_dsp.r.sr & SR_CARRY) != 0;
}
static bool IsOverflow()
{
return (g_dsp.r.sr & SR_OVERFLOW) != 0;
}
static bool IsOverS32()
{
return (g_dsp.r.sr & SR_OVER_S32) != 0;
}
static bool IsLess()
{
return (g_dsp.r.sr & SR_OVERFLOW) != (g_dsp.r.sr & SR_SIGN);
}
static bool IsZero()
{
return (g_dsp.r.sr & SR_ARITH_ZERO) != 0;
}
static bool IsLogicZero()
{
return (g_dsp.r.sr & SR_LOGIC_ZERO) != 0;
}
static bool IsConditionA()
{
return (((g_dsp.r.sr & SR_OVER_S32) || (g_dsp.r.sr & SR_TOP2BITS)) &&
!(g_dsp.r.sr & SR_ARITH_ZERO)) != 0;
}
// see DSPCore.h for flags
bool CheckCondition(u8 _Condition)
{
switch (_Condition & 0xf)
{
case 0xf: // Always true.
return true;
case 0x0: // GE - Greater Equal
return !IsLess();
case 0x1: // L - Less
return IsLess();
case 0x2: // G - Greater
return !IsLess() && !IsZero();
case 0x3: // LE - Less Equal
return IsLess() || IsZero();
case 0x4: // NZ - Not Zero
return !IsZero();
case 0x5: // Z - Zero
return IsZero();
case 0x6: // NC - Not carry
return !IsCarry();
case 0x7: // C - Carry
return IsCarry();
case 0x8: // ? - Not over s32
return !IsOverS32();
case 0x9: // ? - Over s32
return IsOverS32();
case 0xa: // ?
return IsConditionA();
case 0xb: // ?
return !IsConditionA();
case 0xc: // LNZ - Logic Not Zero
return !IsLogicZero();
case 0xd: // LZ - Logic Zero
return IsLogicZero();
case 0xe: // 0 - Overflow
return IsOverflow();
default:
return true;
}
}
} // namespace DSP::Interpreter

View File

@ -6,36 +6,29 @@
#pragma once
// Anything to do with SR and conditions goes here.
#include "Common/CommonTypes.h"
// Anything to do with SR and conditions goes here.
namespace DSP::Interpreter
{
bool CheckCondition(u8 _Condition);
void Update_SR_Register16(s16 _Value, bool carry = false, bool overflow = false,
bool overS32 = false);
void Update_SR_Register64(s64 _Value, bool carry = false, bool overflow = false);
void Update_SR_LZ(bool value);
inline bool isCarry(u64 val, u64 result)
constexpr bool isCarry(u64 val, u64 result)
{
return (val > result);
return val > result;
}
inline bool isCarry2(u64 val, u64 result)
constexpr bool isCarry2(u64 val, u64 result)
{
return (val >= result);
return val >= result;
}
inline bool isOverflow(s64 val1, s64 val2, s64 res)
constexpr bool isOverflow(s64 val1, s64 val2, s64 res)
{
return ((val1 ^ res) & (val2 ^ res)) < 0;
}
inline bool isOverS32(s64 acc)
constexpr bool isOverS32(s64 acc)
{
return (acc != (s32)acc) ? true : false;
return acc != static_cast<s32>(acc);
}
} // namespace DSP::Interpreter

View File

@ -2,15 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/DSP/Interpreter/DSPIntExtOps.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/DSPTables.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
// not needed for game ucodes (it slows down interpreter/dspjit32 + easier to compare int VS
// dspjit64 without it)
//#define PRECISE_BACKLOG
#include "Core/DSP/Interpreter/DSPInterpreter.h"
// Extended opcodes do not exist on their own. These opcodes can only be
// attached to opcodes that allow extending (8 (or 7) lower bits of opcode not used by
@ -22,15 +14,7 @@
// registers will wrap in odd ways, dictated by the corresponding wrapping
// register, WR0-3.
namespace DSP
{
static void WriteToBackLog(int i, int idx, u16 value)
{
writeBackLog[i] = value;
writeBackLogIdx[i] = idx;
}
namespace Interpreter::Ext
namespace DSP::Interpreter
{
static bool IsSameMemArea(u16 a, u16 b)
{
@ -41,46 +25,48 @@ static bool IsSameMemArea(u16 a, u16 b)
// DR $arR
// xxxx xxxx 0000 01rr
// Decrement addressing register $arR.
void dr(const UDSPInstruction opc)
void Interpreter::dr(const UDSPInstruction opc)
{
WriteToBackLog(0, opc & 0x3, dsp_decrement_addr_reg(opc & 0x3));
WriteToBackLog(0, opc & 0x3, DecrementAddressRegister(opc & 0x3));
}
// IR $arR
// xxxx xxxx 0000 10rr
// Increment addressing register $arR.
void ir(const UDSPInstruction opc)
void Interpreter::ir(const UDSPInstruction opc)
{
WriteToBackLog(0, opc & 0x3, dsp_increment_addr_reg(opc & 0x3));
WriteToBackLog(0, opc & 0x3, IncrementAddressRegister(opc & 0x3));
}
// NR $arR
// xxxx xxxx 0000 11rr
// Add corresponding indexing register $ixR to addressing register $arR.
void nr(const UDSPInstruction opc)
void Interpreter::nr(const UDSPInstruction opc)
{
u8 reg = opc & 0x3;
const u8 reg = opc & 0x3;
const auto& state = m_dsp_core.DSPState();
WriteToBackLog(0, reg, dsp_increase_addr_reg(reg, (s16)g_dsp.r.ix[reg]));
WriteToBackLog(0, reg, IncreaseAddressRegister(reg, static_cast<s16>(state.r.ix[reg])));
}
// MV $axD.D, $acS.S
// xxxx xxxx 0001 ddss
// Move value of $acS.S to the $axD.D.
void mv(const UDSPInstruction opc)
void Interpreter::mv(const UDSPInstruction opc)
{
u8 sreg = (opc & 0x3) + DSP_REG_ACL0;
u8 dreg = ((opc >> 2) & 0x3);
const u8 sreg = (opc & 0x3) + DSP_REG_ACL0;
const u8 dreg = ((opc >> 2) & 0x3);
auto& state = m_dsp_core.DSPState();
switch (sreg)
{
case DSP_REG_ACL0:
case DSP_REG_ACL1:
WriteToBackLog(0, dreg + DSP_REG_AXL0, g_dsp.r.ac[sreg - DSP_REG_ACL0].l);
WriteToBackLog(0, dreg + DSP_REG_AXL0, state.r.ac[sreg - DSP_REG_ACL0].l);
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
WriteToBackLog(0, dreg + DSP_REG_AXL0, dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
WriteToBackLog(0, dreg + DSP_REG_AXL0, OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
break;
}
}
@ -89,69 +75,72 @@ void mv(const UDSPInstruction opc)
// xxxx xxxx 001s s0dd
// Store value of $acS.S in the memory pointed by register $arD.
// Post increment register $arD.
void s(const UDSPInstruction opc)
void Interpreter::s(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
const u8 dreg = opc & 0x3;
const u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
auto& state = m_dsp_core.DSPState();
switch (sreg)
{
case DSP_REG_ACL0:
case DSP_REG_ACL1:
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg - DSP_REG_ACL0].l);
state.WriteDMEM(state.r.ar[dreg], state.r.ac[sreg - DSP_REG_ACL0].l);
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
state.WriteDMEM(state.r.ar[dreg], OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
break;
}
WriteToBackLog(0, dreg, dsp_increment_addr_reg(dreg));
WriteToBackLog(0, dreg, IncrementAddressRegister(dreg));
}
// SN @$arD, $acS.S
// xxxx xxxx 001s s1dd
// Store value of register $acS.S in the memory pointed by register $arD.
// Add indexing register $ixD to register $arD.
void sn(const UDSPInstruction opc)
void Interpreter::sn(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
const u8 dreg = opc & 0x3;
const u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
auto& state = m_dsp_core.DSPState();
switch (sreg)
{
case DSP_REG_ACL0:
case DSP_REG_ACL1:
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg - DSP_REG_ACL0].l);
state.WriteDMEM(state.r.ar[dreg], state.r.ac[sreg - DSP_REG_ACL0].l);
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
state.WriteDMEM(state.r.ar[dreg], OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
break;
}
WriteToBackLog(0, dreg, dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]));
WriteToBackLog(0, dreg, IncreaseAddressRegister(dreg, static_cast<s16>(state.r.ix[dreg])));
}
// L $axD.D, @$arS
// xxxx xxxx 01dd d0ss
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
// Post increment register $arS.
void l(const UDSPInstruction opc)
void Interpreter::l(const UDSPInstruction opc)
{
u8 sreg = opc & 0x3;
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
const u8 sreg = opc & 0x3;
const u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
auto& state = m_dsp_core.DSPState();
if ((dreg >= DSP_REG_ACM0) && (g_dsp.r.sr & SR_40_MODE_BIT))
if (dreg >= DSP_REG_ACM0 && IsSRFlagSet(SR_40_MODE_BIT))
{
u16 val = dsp_dmem_read(g_dsp.r.ar[sreg]);
WriteToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) ? 0xFFFF : 0x0000);
const u16 val = state.ReadDMEM(state.r.ar[sreg]);
WriteToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) != 0 ? 0xFFFF : 0x0000);
WriteToBackLog(1, dreg, val);
WriteToBackLog(2, dreg - DSP_REG_ACM0 + DSP_REG_ACL0, 0);
WriteToBackLog(3, sreg, dsp_increment_addr_reg(sreg));
WriteToBackLog(3, sreg, IncrementAddressRegister(sreg));
}
else
{
WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[sreg]));
WriteToBackLog(1, sreg, dsp_increment_addr_reg(sreg));
WriteToBackLog(0, dreg, state.ReadDMEM(state.r.ar[sreg]));
WriteToBackLog(1, sreg, IncrementAddressRegister(sreg));
}
}
@ -159,23 +148,24 @@ void l(const UDSPInstruction opc)
// xxxx xxxx 01dd d0ss
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
// Add indexing register $ixS to register $arS.
void ln(const UDSPInstruction opc)
void Interpreter::ln(const UDSPInstruction opc)
{
u8 sreg = opc & 0x3;
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
const u8 sreg = opc & 0x3;
const u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
auto& state = m_dsp_core.DSPState();
if ((dreg >= DSP_REG_ACM0) && (g_dsp.r.sr & SR_40_MODE_BIT))
if (dreg >= DSP_REG_ACM0 && IsSRFlagSet(SR_40_MODE_BIT))
{
u16 val = dsp_dmem_read(g_dsp.r.ar[sreg]);
WriteToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) ? 0xFFFF : 0x0000);
const u16 val = state.ReadDMEM(state.r.ar[sreg]);
WriteToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) != 0 ? 0xFFFF : 0x0000);
WriteToBackLog(1, dreg, val);
WriteToBackLog(2, dreg - DSP_REG_ACM0 + DSP_REG_ACL0, 0);
WriteToBackLog(3, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
WriteToBackLog(3, sreg, IncreaseAddressRegister(sreg, static_cast<s16>(state.r.ix[sreg])));
}
else
{
WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[sreg]));
WriteToBackLog(1, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
WriteToBackLog(0, dreg, state.ReadDMEM(state.r.ar[sreg]));
WriteToBackLog(1, sreg, IncreaseAddressRegister(sreg, static_cast<s16>(state.r.ix[sreg])));
}
}
@ -184,16 +174,17 @@ void ln(const UDSPInstruction opc)
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Increment both $ar0 and $ar3.
void ls(const UDSPInstruction opc)
void Interpreter::ls(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
const u8 sreg = opc & 0x1;
const u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
auto& state = m_dsp_core.DSPState();
dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
state.WriteDMEM(state.r.ar[3], OpReadRegisterAndSaturate(sreg));
WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
WriteToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
WriteToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
WriteToBackLog(0, dreg, state.ReadDMEM(state.r.ar[0]));
WriteToBackLog(1, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
WriteToBackLog(2, DSP_REG_AR0, IncrementAddressRegister(DSP_REG_AR0));
}
// LSN $axD.D, $acS.m
@ -202,16 +193,18 @@ void ls(const UDSPInstruction opc)
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix0 to addressing
// register $ar0 and increment $ar3.
void lsn(const UDSPInstruction opc)
void Interpreter::lsn(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
const u8 sreg = opc & 0x1;
const u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
auto& state = m_dsp_core.DSPState();
dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
state.WriteDMEM(state.r.ar[3], OpReadRegisterAndSaturate(sreg));
WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
WriteToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
WriteToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
WriteToBackLog(0, dreg, state.ReadDMEM(state.r.ar[0]));
WriteToBackLog(1, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
WriteToBackLog(2, DSP_REG_AR0,
IncreaseAddressRegister(DSP_REG_AR0, static_cast<s16>(state.r.ix[0])));
}
// LSM $axD.D, $acS.m
@ -220,16 +213,18 @@ void lsn(const UDSPInstruction opc)
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix3 to addressing
// register $ar3 and increment $ar0.
void lsm(const UDSPInstruction opc)
void Interpreter::lsm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
const u8 sreg = opc & 0x1;
const u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
auto& state = m_dsp_core.DSPState();
dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
state.WriteDMEM(state.r.ar[3], OpReadRegisterAndSaturate(sreg));
WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
WriteToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
WriteToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
WriteToBackLog(0, dreg, state.ReadDMEM(state.r.ar[0]));
WriteToBackLog(1, DSP_REG_AR3,
IncreaseAddressRegister(DSP_REG_AR3, static_cast<s16>(state.r.ix[3])));
WriteToBackLog(2, DSP_REG_AR0, IncrementAddressRegister(DSP_REG_AR0));
}
// LSMN $axD.D, $acS.m
@ -239,16 +234,19 @@ void lsm(const UDSPInstruction opc)
// register $ar3. Add corresponding indexing register $ix0 to addressing
// register $ar0 and add corresponding indexing register $ix3 to addressing
// register $ar3.
void lsnm(const UDSPInstruction opc)
void Interpreter::lsnm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
const u8 sreg = opc & 0x1;
const u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
auto& state = m_dsp_core.DSPState();
dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
state.WriteDMEM(state.r.ar[3], OpReadRegisterAndSaturate(sreg));
WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
WriteToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
WriteToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
WriteToBackLog(0, dreg, state.ReadDMEM(state.r.ar[0]));
WriteToBackLog(1, DSP_REG_AR3,
IncreaseAddressRegister(DSP_REG_AR3, static_cast<s16>(state.r.ix[3])));
WriteToBackLog(2, DSP_REG_AR0,
IncreaseAddressRegister(DSP_REG_AR0, static_cast<s16>(state.r.ix[0])));
}
// SL $acS.m, $axD.D
@ -256,16 +254,17 @@ void lsnm(const UDSPInstruction opc)
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Increment both $ar0 and $ar3.
void sl(const UDSPInstruction opc)
void Interpreter::sl(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
const u8 sreg = opc & 0x1;
const u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
auto& state = m_dsp_core.DSPState();
dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
state.WriteDMEM(state.r.ar[0], OpReadRegisterAndSaturate(sreg));
WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
WriteToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
WriteToBackLog(0, dreg, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(1, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
WriteToBackLog(2, DSP_REG_AR0, IncrementAddressRegister(DSP_REG_AR0));
}
// SLN $acS.m, $axD.D
@ -274,16 +273,18 @@ void sl(const UDSPInstruction opc)
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
// and increment $ar3.
void sln(const UDSPInstruction opc)
void Interpreter::sln(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
const u8 sreg = opc & 0x1;
const u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
auto& state = m_dsp_core.DSPState();
dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
state.WriteDMEM(state.r.ar[0], OpReadRegisterAndSaturate(sreg));
WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
WriteToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
WriteToBackLog(0, dreg, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(1, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
WriteToBackLog(2, DSP_REG_AR0,
IncreaseAddressRegister(DSP_REG_AR0, static_cast<s16>(state.r.ix[0])));
}
// SLM $acS.m, $axD.D
@ -292,16 +293,18 @@ void sln(const UDSPInstruction opc)
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix3 to addressing register $ar3
// and increment $ar0.
void slm(const UDSPInstruction opc)
void Interpreter::slm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
const u8 sreg = opc & 0x1;
const u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
auto& state = m_dsp_core.DSPState();
dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
state.WriteDMEM(state.r.ar[0], OpReadRegisterAndSaturate(sreg));
WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
WriteToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
WriteToBackLog(0, dreg, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(1, DSP_REG_AR3,
IncreaseAddressRegister(DSP_REG_AR3, static_cast<s16>(state.r.ix[3])));
WriteToBackLog(2, DSP_REG_AR0, IncrementAddressRegister(DSP_REG_AR0));
}
// SLMN $acS.m, $axD.D
@ -310,16 +313,19 @@ void slm(const UDSPInstruction opc)
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
// and add corresponding indexing register $ix3 to addressing register $ar3.
void slnm(const UDSPInstruction opc)
void Interpreter::slnm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
const u8 sreg = opc & 0x1;
const u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
auto& state = m_dsp_core.DSPState();
dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
state.WriteDMEM(state.r.ar[0], OpReadRegisterAndSaturate(sreg));
WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
WriteToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
WriteToBackLog(0, dreg, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(1, DSP_REG_AR3,
IncreaseAddressRegister(DSP_REG_AR3, static_cast<s16>(state.r.ix[3])));
WriteToBackLog(2, DSP_REG_AR0,
IncreaseAddressRegister(DSP_REG_AR0, static_cast<s16>(state.r.ix[0])));
}
// LD $ax0.d, $ax1.r, @$arS
@ -334,228 +340,173 @@ void slnm(const UDSPInstruction opc)
// implemented yet)
// If AR3 points into an invalid memory page, then AX0.L gets the same value as AX0.H. (not
// implemented yet)
void ld(const UDSPInstruction opc)
void Interpreter::ld(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
const u8 dreg = (opc >> 5) & 0x1;
const u8 rreg = (opc >> 4) & 0x1;
const u8 sreg = opc & 0x3;
auto& state = m_dsp_core.DSPState();
WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(state.r.ar[sreg], state.r.ar[3]))
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, state.ReadDMEM(state.r.ar[sreg]));
else
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
WriteToBackLog(2, sreg, IncrementAddressRegister(sreg));
WriteToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
WriteToBackLog(3, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
}
// LDAX $axR, @$arS
// xxxx xxxx 11sr 0011
void ldax(const UDSPInstruction opc)
void Interpreter::ldax(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
const u8 sreg = (opc >> 5) & 0x1;
const u8 rreg = (opc >> 4) & 0x1;
auto& state = m_dsp_core.DSPState();
WriteToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
WriteToBackLog(0, rreg + DSP_REG_AXH0, state.ReadDMEM(state.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(state.r.ar[sreg], state.r.ar[3]))
WriteToBackLog(1, rreg + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[sreg]));
else
WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, rreg + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
WriteToBackLog(2, sreg, IncrementAddressRegister(sreg));
WriteToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
WriteToBackLog(3, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
}
// LDN $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 01ss
void ldn(const UDSPInstruction opc)
void Interpreter::ldn(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
const u8 dreg = (opc >> 5) & 0x1;
const u8 rreg = (opc >> 4) & 0x1;
const u8 sreg = opc & 0x3;
auto& state = m_dsp_core.DSPState();
WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(state.r.ar[sreg], state.r.ar[3]))
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, state.ReadDMEM(state.r.ar[sreg]));
else
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
WriteToBackLog(2, sreg, IncreaseAddressRegister(sreg, static_cast<s16>(state.r.ix[sreg])));
WriteToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
WriteToBackLog(3, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
}
// LDAXN $axR, @$arS
// xxxx xxxx 11sr 0111
void ldaxn(const UDSPInstruction opc)
void Interpreter::ldaxn(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
const u8 sreg = (opc >> 5) & 0x1;
const u8 rreg = (opc >> 4) & 0x1;
auto& state = m_dsp_core.DSPState();
WriteToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
WriteToBackLog(0, rreg + DSP_REG_AXH0, state.ReadDMEM(state.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(state.r.ar[sreg], state.r.ar[3]))
WriteToBackLog(1, rreg + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[sreg]));
else
WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, rreg + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
WriteToBackLog(2, sreg, IncreaseAddressRegister(sreg, static_cast<s16>(state.r.ix[sreg])));
WriteToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
WriteToBackLog(3, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
}
// LDM $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 10ss
void ldm(const UDSPInstruction opc)
void Interpreter::ldm(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
const u8 dreg = (opc >> 5) & 0x1;
const u8 rreg = (opc >> 4) & 0x1;
const u8 sreg = opc & 0x3;
auto& state = m_dsp_core.DSPState();
WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(state.r.ar[sreg], state.r.ar[3]))
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, state.ReadDMEM(state.r.ar[sreg]));
else
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
WriteToBackLog(2, sreg, IncrementAddressRegister(sreg));
WriteToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
WriteToBackLog(3, DSP_REG_AR3,
IncreaseAddressRegister(DSP_REG_AR3, static_cast<s16>(state.r.ix[3])));
}
// LDAXM $axR, @$arS
// xxxx xxxx 11sr 1011
void ldaxm(const UDSPInstruction opc)
void Interpreter::ldaxm(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
const u8 sreg = (opc >> 5) & 0x1;
const u8 rreg = (opc >> 4) & 0x1;
auto& state = m_dsp_core.DSPState();
WriteToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
WriteToBackLog(0, rreg + DSP_REG_AXH0, state.ReadDMEM(state.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(state.r.ar[sreg], state.r.ar[3]))
WriteToBackLog(1, rreg + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[sreg]));
else
WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, rreg + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
WriteToBackLog(2, sreg, IncrementAddressRegister(sreg));
WriteToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
WriteToBackLog(3, DSP_REG_AR3,
IncreaseAddressRegister(DSP_REG_AR3, static_cast<s16>(state.r.ix[3])));
}
// LDNM $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 11ss
void ldnm(const UDSPInstruction opc)
void Interpreter::ldnm(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
const u8 dreg = (opc >> 5) & 0x1;
const u8 rreg = (opc >> 4) & 0x1;
const u8 sreg = opc & 0x3;
auto& state = m_dsp_core.DSPState();
WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(state.r.ar[sreg], state.r.ar[3]))
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, state.ReadDMEM(state.r.ar[sreg]));
else
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
WriteToBackLog(2, sreg, IncreaseAddressRegister(sreg, static_cast<s16>(state.r.ix[sreg])));
WriteToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
WriteToBackLog(3, DSP_REG_AR3,
IncreaseAddressRegister(DSP_REG_AR3, static_cast<s16>(state.r.ix[3])));
}
// LDAXNM $axR, @$arS
// xxxx xxxx 11dr 1111
void ldaxnm(const UDSPInstruction opc)
void Interpreter::ldaxnm(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
const u8 sreg = (opc >> 5) & 0x1;
const u8 rreg = (opc >> 4) & 0x1;
auto& state = m_dsp_core.DSPState();
WriteToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
WriteToBackLog(0, rreg + DSP_REG_AXH0, state.ReadDMEM(state.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(state.r.ar[sreg], state.r.ar[3]))
WriteToBackLog(1, rreg + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[sreg]));
else
WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
WriteToBackLog(1, rreg + DSP_REG_AXL0, state.ReadDMEM(state.r.ar[3]));
WriteToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
WriteToBackLog(2, sreg, IncreaseAddressRegister(sreg, static_cast<s16>(state.r.ix[sreg])));
WriteToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
WriteToBackLog(3, DSP_REG_AR3,
IncreaseAddressRegister(DSP_REG_AR3, static_cast<s16>(state.r.ix[3])));
}
void nop(const UDSPInstruction opc)
void Interpreter::nop_ext(const UDSPInstruction)
{
}
} // namespace Interpreter::Ext
// The ext ops are calculated in parallel with the actual op. That means that
// both the main op and the ext op see the same register state as input. The
// output is simple as long as the main and ext ops don't change the same
// register. If they do the output is the bitwise or of the result of both the
// main and ext ops.
// The ext op are writing their output into the backlog which is
// being applied to the real registers after the main op was executed
void ApplyWriteBackLog()
{
// always make sure to have an extra entry at the end w/ -1 to avoid
// infinitive loops
for (int i = 0; writeBackLogIdx[i] != -1; i++)
{
u16 value = writeBackLog[i];
#ifdef PRECISE_BACKLOG
value |= Interpreter::dsp_op_read_reg(writeBackLogIdx[i]);
#endif
Interpreter::dsp_op_write_reg(writeBackLogIdx[i], value);
// Clear back log
writeBackLogIdx[i] = -1;
}
}
// This function is being called in the main op after all input regs were read
// and before it writes into any regs. This way we can always use bitwise or to
// apply the ext command output, because if the main op didn't change the value
// then 0 | ext output = ext output and if it did then bitwise or is still the
// right thing to do
// Only needed for cases when mainop and extended are modifying the same ACC
// Games are not doing that + in motorola (similar DSP) dox this is forbidden to do.
void ZeroWriteBackLog()
{
#ifdef PRECISE_BACKLOG
// always make sure to have an extra entry at the end w/ -1 to avoid
// infinitive loops
for (int i = 0; writeBackLogIdx[i] != -1; i++)
{
Interpreter::dsp_op_write_reg(writeBackLogIdx[i], 0);
}
#endif
}
void ZeroWriteBackLogPreserveAcc(u8 acc)
{
#ifdef PRECISE_BACKLOG
for (int i = 0; writeBackLogIdx[i] != -1; i++)
{
// acc0
if ((acc == 0) &&
((writeBackLogIdx[i] == DSP_REG_ACL0) || (writeBackLogIdx[i] == DSP_REG_ACM0) ||
(writeBackLogIdx[i] == DSP_REG_ACH0)))
continue;
// acc1
if ((acc == 1) &&
((writeBackLogIdx[i] == DSP_REG_ACL1) || (writeBackLogIdx[i] == DSP_REG_ACM1) ||
(writeBackLogIdx[i] == DSP_REG_ACH1)))
continue;
Interpreter::dsp_op_write_reg(writeBackLogIdx[i], 0);
}
#endif
}
} // namespace DSP
} // namespace DSP::Interpreter

View File

@ -1,41 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Copyright 2005 Duddie
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Core/DSP/DSPCommon.h"
// Extended opcode support.
// Many opcode have the lower 0xFF (some only 0x7f) free - there, an opcode extension
// can be stored.
namespace DSP::Interpreter::Ext
{
void l(UDSPInstruction opc);
void ln(UDSPInstruction opc);
void ls(UDSPInstruction opc);
void lsn(UDSPInstruction opc);
void lsm(UDSPInstruction opc);
void lsnm(UDSPInstruction opc);
void sl(UDSPInstruction opc);
void sln(UDSPInstruction opc);
void slm(UDSPInstruction opc);
void slnm(UDSPInstruction opc);
void s(UDSPInstruction opc);
void sn(UDSPInstruction opc);
void ld(UDSPInstruction opc);
void ldax(UDSPInstruction opc);
void ldn(UDSPInstruction opc);
void ldaxn(UDSPInstruction opc);
void ldm(UDSPInstruction opc);
void ldaxm(UDSPInstruction opc);
void ldnm(UDSPInstruction opc);
void ldaxnm(UDSPInstruction opc);
void mv(UDSPInstruction opc);
void dr(UDSPInstruction opc);
void ir(UDSPInstruction opc);
void nr(UDSPInstruction opc);
void nop(UDSPInstruction opc);
} // namespace DSP::Interpreter::Ext

View File

@ -4,8 +4,7 @@
//
// Additional copyrights go to Duddie and Tratax (c) 2004
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
#include "Common/CommonTypes.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"
namespace DSP::Interpreter
@ -16,15 +15,16 @@ namespace DSP::Interpreter
// CR[0-7] | M. That is, the upper 8 bits of the address are the
// bottom 8 bits from CR, and the lower 8 bits are from the 8-bit immediate.
// Note: pc+=2 in duddie's doc seems wrong
void srs(const UDSPInstruction opc)
void Interpreter::srs(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
auto& state = m_dsp_core.DSPState();
const auto reg = static_cast<u8>(((opc >> 8) & 0x7) + 0x18);
const auto addr = static_cast<u16>((state.r.cr << 8) | (opc & 0xFF));
if (reg >= DSP_REG_ACM0)
dsp_dmem_write(addr, dsp_op_read_reg_and_saturate(reg - DSP_REG_ACM0));
state.WriteDMEM(addr, OpReadRegisterAndSaturate(reg - DSP_REG_ACM0));
else
dsp_dmem_write(addr, dsp_op_read_reg(reg));
state.WriteDMEM(addr, OpReadRegister(reg));
}
// LRS $(0x18+D), @M
@ -32,40 +32,45 @@ void srs(const UDSPInstruction opc)
// Move value from data memory pointed by address CR[0-7] | M to register
// $(0x18+D). That is, the upper 8 bits of the address are the bottom 8 bits
// from CR, and the lower 8 bits are from the 8-bit immediate.
void lrs(const UDSPInstruction opc)
void Interpreter::lrs(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
dsp_op_write_reg(reg, dsp_dmem_read(addr));
dsp_conditional_extend_accum(reg);
auto& state = m_dsp_core.DSPState();
const auto reg = static_cast<u8>(((opc >> 8) & 0x7) + 0x18);
const auto addr = static_cast<u16>((state.r.cr << 8) | (opc & 0xFF));
OpWriteRegister(reg, state.ReadDMEM(addr));
ConditionalExtendAccum(reg);
}
// LR $D, @M
// 0000 0000 110d dddd
// mmmm mmmm mmmm mmmm
// Move value from data memory pointed by address M to register $D.
void lr(const UDSPInstruction opc)
void Interpreter::lr(const UDSPInstruction opc)
{
u8 reg = opc & 0x1F;
u16 addr = dsp_fetch_code();
u16 val = dsp_dmem_read(addr);
dsp_op_write_reg(reg, val);
dsp_conditional_extend_accum(reg);
auto& state = m_dsp_core.DSPState();
const u8 reg = opc & 0x1F;
const u16 addr = state.FetchInstruction();
const u16 val = state.ReadDMEM(addr);
OpWriteRegister(reg, val);
ConditionalExtendAccum(reg);
}
// SR @M, $S
// 0000 0000 111s ssss
// mmmm mmmm mmmm mmmm
// Store value from register $S to a memory pointed by address M.
void sr(const UDSPInstruction opc)
void Interpreter::sr(const UDSPInstruction opc)
{
u8 reg = opc & 0x1F;
u16 addr = dsp_fetch_code();
auto& state = m_dsp_core.DSPState();
const u8 reg = opc & 0x1F;
const u16 addr = state.FetchInstruction();
if (reg >= DSP_REG_ACM0)
dsp_dmem_write(addr, dsp_op_read_reg_and_saturate(reg - DSP_REG_ACM0));
state.WriteDMEM(addr, OpReadRegisterAndSaturate(reg - DSP_REG_ACM0));
else
dsp_dmem_write(addr, dsp_op_read_reg(reg));
state.WriteDMEM(addr, OpReadRegister(reg));
}
// SI @M, #I
@ -73,176 +78,189 @@ void sr(const UDSPInstruction opc)
// iiii iiii iiii iiii
// Store 16-bit immediate value I to a memory location pointed by address
// M (M is 8-bit value sign extended).
void si(const UDSPInstruction opc)
void Interpreter::si(const UDSPInstruction opc)
{
u16 addr = (s8)opc;
u16 imm = dsp_fetch_code();
dsp_dmem_write(addr, imm);
auto& state = m_dsp_core.DSPState();
const u16 addr = static_cast<u16>(static_cast<s8>(opc));
const u16 imm = state.FetchInstruction();
state.WriteDMEM(addr, imm);
}
// LRR $D, @$S
// 0001 1000 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
void lrr(const UDSPInstruction opc)
void Interpreter::lrr(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
const u8 sreg = (opc >> 5) & 0x3;
const u8 dreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
const u16 val = state.ReadDMEM(OpReadRegister(sreg));
OpWriteRegister(dreg, val);
ConditionalExtendAccum(dreg);
}
// LRRD $D, @$S
// 0001 1000 1ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Decrement register $S.
void lrrd(const UDSPInstruction opc)
void Interpreter::lrrd(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
const u8 sreg = (opc >> 5) & 0x3;
const u8 dreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[sreg] = dsp_decrement_addr_reg(sreg);
const u16 val = state.ReadDMEM(OpReadRegister(sreg));
OpWriteRegister(dreg, val);
ConditionalExtendAccum(dreg);
state.r.ar[sreg] = DecrementAddressRegister(sreg);
}
// LRRI $D, @$S
// 0001 1001 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Increment register $S.
void lrri(const UDSPInstruction opc)
void Interpreter::lrri(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
const u8 sreg = (opc >> 5) & 0x3;
const u8 dreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[sreg] = dsp_increment_addr_reg(sreg);
const u16 val = state.ReadDMEM(OpReadRegister(sreg));
OpWriteRegister(dreg, val);
ConditionalExtendAccum(dreg);
state.r.ar[sreg] = IncrementAddressRegister(sreg);
}
// LRRN $D, @$S
// 0001 1001 1ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Add indexing register $(0x4+S) to register $S.
void lrrn(const UDSPInstruction opc)
void Interpreter::lrrn(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
const u8 sreg = (opc >> 5) & 0x3;
const u8 dreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[sreg] = dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]);
const u16 val = state.ReadDMEM(OpReadRegister(sreg));
OpWriteRegister(dreg, val);
ConditionalExtendAccum(dreg);
state.r.ar[sreg] = IncreaseAddressRegister(sreg, static_cast<s16>(state.r.ix[sreg]));
}
// SRR @$D, $S
// 0001 1010 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D.
void srr(const UDSPInstruction opc)
void Interpreter::srr(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
const u8 dreg = (opc >> 5) & 0x3;
const u8 sreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();
if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
state.WriteDMEM(state.r.ar[dreg], OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));
}
// SRRD @$D, $S
// 0001 1010 1dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Decrement register $D.
void srrd(const UDSPInstruction opc)
void Interpreter::srrd(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
const u8 dreg = (opc >> 5) & 0x3;
const u8 sreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();
if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
state.WriteDMEM(state.r.ar[dreg], OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));
g_dsp.r.ar[dreg] = dsp_decrement_addr_reg(dreg);
state.r.ar[dreg] = DecrementAddressRegister(dreg);
}
// SRRI @$D, $S
// 0001 1011 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Increment register $D.
void srri(const UDSPInstruction opc)
void Interpreter::srri(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
const u8 dreg = (opc >> 5) & 0x3;
const u8 sreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();
if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
state.WriteDMEM(state.r.ar[dreg], OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));
g_dsp.r.ar[dreg] = dsp_increment_addr_reg(dreg);
state.r.ar[dreg] = IncrementAddressRegister(dreg);
}
// SRRN @$D, $S
// 0001 1011 1dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Add DSP_REG_IX0 register to register $D.
void srrn(const UDSPInstruction opc)
void Interpreter::srrn(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
const u8 dreg = (opc >> 5) & 0x3;
const u8 sreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();
if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
state.WriteDMEM(state.r.ar[dreg], OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));
g_dsp.r.ar[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]);
state.r.ar[dreg] = IncreaseAddressRegister(dreg, static_cast<s16>(state.r.ix[dreg]));
}
// ILRR $acD.m, @$arS
// 0000 001d 0001 00ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m.
void ilrr(const UDSPInstruction opc)
void Interpreter::ilrr(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
const u16 reg = opc & 0x3;
const u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
auto& state = m_dsp_core.DSPState();
g_dsp.r.ac[dreg - DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
state.r.ac[dreg - DSP_REG_ACM0].m = state.ReadIMEM(state.r.ar[reg]);
ConditionalExtendAccum(dreg);
}
// ILRRD $acD.m, @$arS
// 0000 001d 0001 01ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Decrement addressing register $arS.
void ilrrd(const UDSPInstruction opc)
void Interpreter::ilrrd(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
const u16 reg = opc & 0x3;
const u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
auto& state = m_dsp_core.DSPState();
g_dsp.r.ac[dreg - DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[reg] = dsp_decrement_addr_reg(reg);
state.r.ac[dreg - DSP_REG_ACM0].m = state.ReadIMEM(state.r.ar[reg]);
ConditionalExtendAccum(dreg);
state.r.ar[reg] = DecrementAddressRegister(reg);
}
// ILRRI $acD.m, @$S
// 0000 001d 0001 10ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Increment addressing register $arS.
void ilrri(const UDSPInstruction opc)
void Interpreter::ilrri(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
const u16 reg = opc & 0x3;
const u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
auto& state = m_dsp_core.DSPState();
g_dsp.r.ac[dreg - DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[reg] = dsp_increment_addr_reg(reg);
state.r.ac[dreg - DSP_REG_ACM0].m = state.ReadIMEM(state.r.ar[reg]);
ConditionalExtendAccum(dreg);
state.r.ar[reg] = IncrementAddressRegister(reg);
}
// ILRRN $acD.m, @$arS
@ -250,13 +268,14 @@ void ilrri(const UDSPInstruction opc)
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Add corresponding indexing
// register $ixS to addressing register $arS.
void ilrrn(const UDSPInstruction opc)
void Interpreter::ilrrn(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
const u16 reg = opc & 0x3;
const u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
auto& state = m_dsp_core.DSPState();
g_dsp.r.ac[dreg - DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[reg] = dsp_increase_addr_reg(reg, (s16)g_dsp.r.ix[reg]);
state.r.ac[dreg - DSP_REG_ACM0].m = state.ReadIMEM(state.r.ar[reg]);
ConditionalExtendAccum(dreg);
state.r.ar[reg] = IncreaseAddressRegister(reg, static_cast<s16>(state.r.ix[reg]));
}
} // namespace DSP::Interpreter

View File

@ -5,7 +5,6 @@
// Additional copyrights go to Duddie and Tratax (c) 2004
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/DSPTables.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"
@ -15,17 +14,17 @@ namespace DSP::Interpreter
// MRR $D, $S
// 0001 11dd ddds ssss
// Move value from register $S to register $D.
void mrr(const UDSPInstruction opc)
void Interpreter::mrr(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1f;
u8 dreg = (opc >> 5) & 0x1f;
const u8 sreg = opc & 0x1f;
const u8 dreg = (opc >> 5) & 0x1f;
if (sreg >= DSP_REG_ACM0)
dsp_op_write_reg(dreg, dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
OpWriteRegister(dreg, OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
else
dsp_op_write_reg(dreg, dsp_op_read_reg(sreg));
OpWriteRegister(dreg, OpReadRegister(sreg));
dsp_conditional_extend_accum(dreg);
ConditionalExtendAccum(dreg);
}
// LRI $D, #I
@ -37,23 +36,26 @@ void mrr(const UDSPInstruction opc)
// register, has a different behaviour in S40 mode if loaded to AC0.M: The
// value gets sign extended to the whole accumulator! This does not happen in
// S16 mode.
void lri(const UDSPInstruction opc)
void Interpreter::lri(const UDSPInstruction opc)
{
u8 reg = opc & 0x1F;
u16 imm = dsp_fetch_code();
dsp_op_write_reg(reg, imm);
dsp_conditional_extend_accum(reg);
auto& state = m_dsp_core.DSPState();
const u8 reg = opc & 0x1F;
const u16 imm = state.FetchInstruction();
OpWriteRegister(reg, imm);
ConditionalExtendAccum(reg);
}
// LRIS $(0x18+D), #I
// 0000 1ddd iiii iiii
// Load immediate value I (8-bit sign extended) to accumulator register.
void lris(const UDSPInstruction opc)
void Interpreter::lris(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;
u16 imm = (s8)opc;
dsp_op_write_reg(reg, imm);
dsp_conditional_extend_accum(reg);
const u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;
const u16 imm = static_cast<u16>(static_cast<s8>(opc));
OpWriteRegister(reg, imm);
ConditionalExtendAccum(reg);
}
//----
@ -63,7 +65,7 @@ void lris(const UDSPInstruction opc)
// No operation, but can be extended with extended opcode.
// This opcode is supposed to do nothing - it's used if you want to use
// an opcode extension but not do anything. At least according to duddie.
void nx(const UDSPInstruction opc)
void Interpreter::nx(const UDSPInstruction)
{
ZeroWriteBackLog();
}
@ -73,38 +75,48 @@ void nx(const UDSPInstruction opc)
// DAR $arD
// 0000 0000 0000 01dd
// Decrement address register $arD.
void dar(const UDSPInstruction opc)
void Interpreter::dar(const UDSPInstruction opc)
{
g_dsp.r.ar[opc & 0x3] = dsp_decrement_addr_reg(opc & 0x3);
auto& state = m_dsp_core.DSPState();
const u16 index = opc & 3;
state.r.ar[index] = DecrementAddressRegister(index);
}
// IAR $arD
// 0000 0000 0000 10dd
// Increment address register $arD.
void iar(const UDSPInstruction opc)
void Interpreter::iar(const UDSPInstruction opc)
{
g_dsp.r.ar[opc & 0x3] = dsp_increment_addr_reg(opc & 0x3);
auto& state = m_dsp_core.DSPState();
const u16 index = opc & 3;
state.r.ar[index] = IncrementAddressRegister(index);
}
// SUBARN $arD
// 0000 0000 0000 11dd
// Subtract indexing register $ixD from an addressing register $arD.
// used only in IPL-NTSC ucode
void subarn(const UDSPInstruction opc)
void Interpreter::subarn(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
g_dsp.r.ar[dreg] = dsp_decrease_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]);
auto& state = m_dsp_core.DSPState();
const u8 dreg = opc & 0x3;
state.r.ar[dreg] = DecreaseAddressRegister(dreg, static_cast<s16>(state.r.ix[dreg]));
}
// ADDARN $arD, $ixS
// 0000 0000 0001 ssdd
// Adds indexing register $ixS to an addressing register $arD.
// It is critical for the Zelda ucode that this one wraps correctly.
void addarn(const UDSPInstruction opc)
void Interpreter::addarn(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = (opc >> 2) & 0x3;
g_dsp.r.ar[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[sreg]);
auto& state = m_dsp_core.DSPState();
const u8 dreg = opc & 0x3;
const u8 sreg = (opc >> 2) & 0x3;
state.r.ar[dreg] = IncreaseAddressRegister(dreg, static_cast<s16>(state.r.ix[sreg]));
}
//----
@ -113,45 +125,51 @@ void addarn(const UDSPInstruction opc)
// 0001 0010 aaaa aiii
// bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
void sbclr(const UDSPInstruction opc)
void Interpreter::sbclr(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
g_dsp.r.sr &= ~(1 << bit);
auto& state = m_dsp_core.DSPState();
const u8 bit = (opc & 0x7) + 6;
state.r.sr &= ~(1U << bit);
}
// SBSET #I
// 0001 0011 aaaa aiii
// Set bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
void sbset(const UDSPInstruction opc)
void Interpreter::sbset(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
g_dsp.r.sr |= (1 << bit);
auto& state = m_dsp_core.DSPState();
const u8 bit = (opc & 0x7) + 6;
state.r.sr |= (1U << bit);
}
// This is a bunch of flag setters, flipping bits in SR.
void srbith(const UDSPInstruction opc)
void Interpreter::srbith(const UDSPInstruction opc)
{
auto& state = m_dsp_core.DSPState();
ZeroWriteBackLog();
switch ((opc >> 8) & 0x7)
{
case 2: // M2
g_dsp.r.sr &= ~SR_MUL_MODIFY;
state.r.sr &= ~SR_MUL_MODIFY;
break;
case 3: // M0
g_dsp.r.sr |= SR_MUL_MODIFY;
state.r.sr |= SR_MUL_MODIFY;
break;
case 4: // CLR15
g_dsp.r.sr &= ~SR_MUL_UNSIGNED;
state.r.sr &= ~SR_MUL_UNSIGNED;
break;
case 5: // SET15
g_dsp.r.sr |= SR_MUL_UNSIGNED;
state.r.sr |= SR_MUL_UNSIGNED;
break;
case 6: // SET16 (CLR40)
g_dsp.r.sr &= ~SR_40_MODE_BIT;
state.r.sr &= ~SR_40_MODE_BIT;
break;
case 7: // SET40
g_dsp.r.sr |= SR_40_MODE_BIT;
state.r.sr |= SR_40_MODE_BIT;
break;
default:
break;

View File

@ -13,62 +13,6 @@
namespace DSP::Interpreter
{
namespace
{
// Only MULX family instructions have unsigned/mixed support.
s64 dsp_get_multiply_prod(u16 a, u16 b, u8 sign)
{
s64 prod;
if ((sign == 1) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) // unsigned
prod = (u32)(a * b);
else if ((sign == 2) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) // mixed
prod = a * (s16)b;
else
prod = (s16)a * (s16)b; // signed
// Conditionally multiply by 2.
if ((g_dsp.r.sr & SR_MUL_MODIFY) == 0)
prod <<= 1;
return prod;
}
s64 dsp_multiply(u16 a, u16 b, u8 sign = 0)
{
s64 prod = dsp_get_multiply_prod(a, b, sign);
return prod;
}
s64 dsp_multiply_add(u16 a, u16 b, u8 sign = 0)
{
s64 prod = dsp_get_long_prod() + dsp_get_multiply_prod(a, b, sign);
return prod;
}
s64 dsp_multiply_sub(u16 a, u16 b, u8 sign = 0)
{
s64 prod = dsp_get_long_prod() - dsp_get_multiply_prod(a, b, sign);
return prod;
}
s64 dsp_multiply_mulx(u8 axh0, u8 axh1, u16 val1, u16 val2)
{
s64 result;
if ((axh0 == 0) && (axh1 == 0))
result = dsp_multiply(val1, val2, 1); // unsigned support ON if both ax?.l regs are used
else if ((axh0 == 0) && (axh1 == 1))
result = dsp_multiply(val1, val2, 2); // mixed support ON (u16)axl.0 * (s16)axh.1
else if ((axh0 == 1) && (axh1 == 0))
result = dsp_multiply(val2, val1, 2); // mixed support ON (u16)axl.1 * (s16)axh.0
else
result = dsp_multiply(val1, val2, 0); // unsigned support OFF if both ax?.h regs are used
return result;
}
} // Anonymous namespace
// CLRP
// 1000 0100 xxxx xxxx
// Clears product register $prod.
@ -78,14 +22,15 @@ s64 dsp_multiply_mulx(u8 axh0, u8 axh1, u16 val1, u16 val2)
//
// It's not ok, to just zero all of them, correct values should be set because of
// direct use of prod regs by AX/AXWII (look @that part of ucode).
void clrp(const UDSPInstruction opc)
void Interpreter::clrp(const UDSPInstruction)
{
ZeroWriteBackLog();
g_dsp.r.prod.l = 0x0000;
g_dsp.r.prod.m = 0xfff0;
g_dsp.r.prod.h = 0x00ff;
g_dsp.r.prod.m2 = 0x0010;
auto& state = m_dsp_core.DSPState();
state.r.prod.l = 0x0000;
state.r.prod.m = 0xfff0;
state.r.prod.h = 0x00ff;
state.r.prod.m2 = 0x0010;
}
// TSTPROD
@ -93,10 +38,10 @@ void clrp(const UDSPInstruction opc)
// Test prod regs value.
//
// flags out: --xx xx0x
void tstprod(const UDSPInstruction opc)
void Interpreter::tstprod(const UDSPInstruction)
{
s64 prod = dsp_get_long_prod();
Update_SR_Register64(prod);
const s64 prod = GetLongProduct();
UpdateSR64(prod);
ZeroWriteBackLog();
}
@ -107,16 +52,15 @@ void tstprod(const UDSPInstruction opc)
// Moves multiply product from $prod register to accumulator $acD register.
//
// flags out: --xx xx0x
void movp(const UDSPInstruction opc)
void Interpreter::movp(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x1;
s64 acc = dsp_get_long_prod();
const u8 dreg = (opc >> 8) & 0x1;
const s64 acc = GetLongProduct();
ZeroWriteBackLog();
dsp_set_long_acc(dreg, acc);
Update_SR_Register64(acc);
SetLongAcc(dreg, acc);
UpdateSR64(acc);
}
// MOVNP $acD
@ -125,16 +69,15 @@ void movp(const UDSPInstruction opc)
// $acD register.
//
// flags out: --xx xx0x
void movnp(const UDSPInstruction opc)
void Interpreter::movnp(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x1;
s64 acc = -dsp_get_long_prod();
const u8 dreg = (opc >> 8) & 0x1;
const s64 acc = -GetLongProduct();
ZeroWriteBackLog();
dsp_set_long_acc(dreg, acc);
Update_SR_Register64(acc);
SetLongAcc(dreg, acc);
UpdateSR64(acc);
}
// MOVPZ $acD
@ -143,16 +86,15 @@ void movnp(const UDSPInstruction opc)
// register and sets (rounds) $acD.l to 0
//
// flags out: --xx xx0x
void movpz(const UDSPInstruction opc)
void Interpreter::movpz(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x01;
s64 acc = dsp_get_long_prod_round_prodl();
const u8 dreg = (opc >> 8) & 0x01;
const s64 acc = GetLongProductRounded();
ZeroWriteBackLog();
dsp_set_long_acc(dreg, acc);
Update_SR_Register64(acc);
SetLongAcc(dreg, acc);
UpdateSR64(acc);
}
// ADDPAXZ $acD, $axS
@ -162,21 +104,21 @@ void movpz(const UDSPInstruction opc)
//
// TODO: ugly code and still small error here (+/- 1 in .m - randomly)
// flags out: --xx xx0x
void addpaxz(const UDSPInstruction opc)
void Interpreter::addpaxz(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
const u8 dreg = (opc >> 8) & 0x1;
const u8 sreg = (opc >> 9) & 0x1;
s64 oldprod = dsp_get_long_prod();
s64 prod = dsp_get_long_prod_round_prodl();
s64 ax = dsp_get_long_acx(sreg);
const s64 oldprod = GetLongProduct();
const s64 prod = GetLongProductRounded();
const s64 ax = GetLongACX(sreg);
s64 res = prod + (ax & ~0xffff);
ZeroWriteBackLog();
dsp_set_long_acc(dreg, res);
res = dsp_get_long_acc(dreg);
Update_SR_Register64(res, isCarry(oldprod, res), false);
SetLongAcc(dreg, res);
res = GetLongAcc(dreg);
UpdateSR64(res, isCarry(oldprod, res), false);
}
//----
@ -184,13 +126,14 @@ void addpaxz(const UDSPInstruction opc)
// MULAXH
// 1000 0011 xxxx xxxx
// Multiply $ax0.h by $ax0.h
void mulaxh(const UDSPInstruction opc)
void Interpreter::mulaxh(const UDSPInstruction)
{
s64 prod = dsp_multiply(dsp_get_ax_h(0), dsp_get_ax_h(0));
const s16 value = GetAXHigh(0);
const s64 prod = Multiply(value, value);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
SetLongProduct(prod);
}
//----
@ -199,17 +142,16 @@ void mulaxh(const UDSPInstruction opc)
// 1001 s000 xxxx xxxx
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed).
void mul(const UDSPInstruction opc)
void Interpreter::mul(const UDSPInstruction opc)
{
u8 sreg = (opc >> 11) & 0x1;
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axh, axl);
const u8 sreg = (opc >> 11) & 0x1;
const u16 axl = GetAXLow(sreg);
const u16 axh = GetAXHigh(sreg);
const s64 prod = Multiply(axh, axl);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
SetLongProduct(prod);
}
// MULAC $axS.l, $axS.h, $acR
@ -219,21 +161,21 @@ void mul(const UDSPInstruction opc)
// accumulator $axS (treat them both as signed).
//
// flags out: --xx xx0x
void mulac(const UDSPInstruction opc)
void Interpreter::mulac(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 11) & 0x1;
const u8 rreg = (opc >> 8) & 0x1;
const u8 sreg = (opc >> 11) & 0x1;
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axl, axh);
const s64 acc = GetLongAcc(rreg) + GetLongProduct();
const u16 axl = GetAXLow(sreg);
const u16 axh = GetAXHigh(sreg);
const s64 prod = Multiply(axl, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
SetLongProduct(prod);
SetLongAcc(rreg, acc);
UpdateSR64(GetLongAcc(rreg));
}
// MULMV $axS.l, $axS.h, $acR
@ -243,21 +185,21 @@ void mulac(const UDSPInstruction opc)
// accumulator $axS (treat them both as signed).
//
// flags out: --xx xx0x
void mulmv(const UDSPInstruction opc)
void Interpreter::mulmv(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 sreg = ((opc >> 11) & 0x1);
const u8 rreg = (opc >> 8) & 0x1;
const u8 sreg = ((opc >> 11) & 0x1);
s64 acc = dsp_get_long_prod();
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axl, axh);
const s64 acc = GetLongProduct();
const u16 axl = GetAXLow(sreg);
const u16 axh = GetAXHigh(sreg);
const s64 prod = Multiply(axl, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
SetLongProduct(prod);
SetLongAcc(rreg, acc);
UpdateSR64(GetLongAcc(rreg));
}
// MULMVZ $axS.l, $axS.h, $acR
@ -268,21 +210,21 @@ void mulmv(const UDSPInstruction opc)
// them both as signed).
//
// flags out: --xx xx0x
void mulmvz(const UDSPInstruction opc)
void Interpreter::mulmvz(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 11) & 0x1;
const u8 rreg = (opc >> 8) & 0x1;
const u8 sreg = (opc >> 11) & 0x1;
s64 acc = dsp_get_long_prod_round_prodl();
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axl, axh);
const s64 acc = GetLongProductRounded();
const u16 axl = GetAXLow(sreg);
const u16 axh = GetAXHigh(sreg);
const s64 prod = Multiply(axl, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
SetLongProduct(prod);
SetLongAcc(rreg, acc);
UpdateSR64(GetLongAcc(rreg));
}
//----
@ -291,18 +233,18 @@ void mulmvz(const UDSPInstruction opc)
// 101s t000 xxxx xxxx
// Multiply one part $ax0 by one part $ax1.
// Part is selected by S and T bits. Zero selects low part, one selects high part.
void mulx(const UDSPInstruction opc)
void Interpreter::mulx(const UDSPInstruction opc)
{
u8 treg = ((opc >> 11) & 0x1);
u8 sreg = ((opc >> 12) & 0x1);
const u8 treg = ((opc >> 11) & 0x1);
const u8 sreg = ((opc >> 12) & 0x1);
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
const u16 val1 = (sreg == 0) ? GetAXLow(0) : GetAXHigh(0);
const u16 val2 = (treg == 0) ? GetAXLow(1) : GetAXHigh(1);
const s64 prod = MultiplyMulX(sreg, treg, val1, val2);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
SetLongProduct(prod);
}
// MULXAC $ax0.S, $ax1.T, $acR
@ -312,22 +254,22 @@ void mulx(const UDSPInstruction opc)
// T bits. Zero selects low part, one selects high part.
//
// flags out: --xx xx0x
void mulxac(const UDSPInstruction opc)
void Interpreter::mulxac(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
const u8 rreg = (opc >> 8) & 0x1;
const u8 treg = (opc >> 11) & 0x1;
const u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
const s64 acc = GetLongAcc(rreg) + GetLongProduct();
const u16 val1 = (sreg == 0) ? GetAXLow(0) : GetAXHigh(0);
const u16 val2 = (treg == 0) ? GetAXLow(1) : GetAXHigh(1);
const s64 prod = MultiplyMulX(sreg, treg, val1, val2);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
SetLongProduct(prod);
SetLongAcc(rreg, acc);
UpdateSR64(GetLongAcc(rreg));
}
// MULXMV $ax0.S, $ax1.T, $acR
@ -337,22 +279,22 @@ void mulxac(const UDSPInstruction opc)
// T bits. Zero selects low part, one selects high part.
//
// flags out: --xx xx0x
void mulxmv(const UDSPInstruction opc)
void Interpreter::mulxmv(const UDSPInstruction opc)
{
u8 rreg = ((opc >> 8) & 0x1);
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
const u8 rreg = ((opc >> 8) & 0x1);
const u8 treg = (opc >> 11) & 0x1;
const u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod();
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
const s64 acc = GetLongProduct();
const u16 val1 = (sreg == 0) ? GetAXLow(0) : GetAXHigh(0);
const u16 val2 = (treg == 0) ? GetAXLow(1) : GetAXHigh(1);
const s64 prod = MultiplyMulX(sreg, treg, val1, val2);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
SetLongProduct(prod);
SetLongAcc(rreg, acc);
UpdateSR64(GetLongAcc(rreg));
}
// MULXMVZ $ax0.S, $ax1.T, $acR
@ -363,22 +305,22 @@ void mulxmv(const UDSPInstruction opc)
// one selects high part.
//
// flags out: --xx xx0x
void mulxmvz(const UDSPInstruction opc)
void Interpreter::mulxmvz(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
const u8 rreg = (opc >> 8) & 0x1;
const u8 treg = (opc >> 11) & 0x1;
const u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod_round_prodl();
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
const s64 acc = GetLongProductRounded();
const u16 val1 = (sreg == 0) ? GetAXLow(0) : GetAXHigh(0);
const u16 val2 = (treg == 0) ? GetAXLow(1) : GetAXHigh(1);
const s64 prod = MultiplyMulX(sreg, treg, val1, val2);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
SetLongProduct(prod);
SetLongAcc(rreg, acc);
UpdateSR64(GetLongAcc(rreg));
}
//----
@ -387,18 +329,18 @@ void mulxmvz(const UDSPInstruction opc)
// 110s t000 xxxx xxxx
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
// secondary accumulator $axS (treat them both as signed).
void mulc(const UDSPInstruction opc)
void Interpreter::mulc(const UDSPInstruction opc)
{
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
const u8 treg = (opc >> 11) & 0x1;
const u8 sreg = (opc >> 12) & 0x1;
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
const u16 accm = GetAccMid(sreg);
const u16 axh = GetAXHigh(treg);
const s64 prod = Multiply(accm, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
SetLongProduct(prod);
}
// MULCAC $acS.m, $axT.h, $acR
@ -408,22 +350,22 @@ void mulc(const UDSPInstruction opc)
// register before multiplication to accumulator $acR.
//
// flags out: --xx xx0x
void mulcac(const UDSPInstruction opc)
void Interpreter::mulcac(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
const u8 rreg = (opc >> 8) & 0x1;
const u8 treg = (opc >> 11) & 0x1;
const u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
const s64 acc = GetLongAcc(rreg) + GetLongProduct();
const u16 accm = GetAccMid(sreg);
const u16 axh = GetAXHigh(treg);
const s64 prod = Multiply(accm, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
SetLongProduct(prod);
SetLongAcc(rreg, acc);
UpdateSR64(GetLongAcc(rreg));
}
// MULCMV $acS.m, $axT.h, $acR
@ -434,22 +376,22 @@ void mulcac(const UDSPInstruction opc)
// possible mistake in duddie's doc axT.h rather than axS.h
//
// flags out: --xx xx0x
void mulcmv(const UDSPInstruction opc)
void Interpreter::mulcmv(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
const u8 rreg = (opc >> 8) & 0x1;
const u8 treg = (opc >> 11) & 0x1;
const u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod();
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
const s64 acc = GetLongProduct();
const u16 accm = GetAccMid(sreg);
const u16 axh = GetAXHigh(treg);
const s64 prod = Multiply(accm, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
SetLongProduct(prod);
SetLongAcc(rreg, acc);
UpdateSR64(GetLongAcc(rreg));
}
// MULCMVZ $acS.m, $axT.h, $acR
@ -461,22 +403,22 @@ void mulcmv(const UDSPInstruction opc)
// accumulator $acR.l to zero.
//
// flags out: --xx xx0x
void mulcmvz(const UDSPInstruction opc)
void Interpreter::mulcmvz(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
const u8 rreg = (opc >> 8) & 0x1;
const u8 treg = (opc >> 11) & 0x1;
const u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod_round_prodl();
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
const s64 acc = GetLongProductRounded();
const u16 accm = GetAccMid(sreg);
const u16 axh = GetAXHigh(treg);
const s64 prod = Multiply(accm, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
SetLongProduct(prod);
SetLongAcc(rreg, acc);
UpdateSR64(GetLongAcc(rreg));
}
//----
@ -486,18 +428,18 @@ void mulcmvz(const UDSPInstruction opc)
// Multiply one part of secondary accumulator $ax0 (selected by S) by
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
// signed) and add result to product register.
void maddx(const UDSPInstruction opc)
void Interpreter::maddx(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
const u8 treg = (opc >> 8) & 0x1;
const u8 sreg = (opc >> 9) & 0x1;
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_add(val1, val2);
const u16 val1 = (sreg == 0) ? GetAXLow(0) : GetAXHigh(0);
const u16 val2 = (treg == 0) ? GetAXLow(1) : GetAXHigh(1);
const s64 prod = MultiplyAdd(val1, val2);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
SetLongProduct(prod);
}
// MSUBX $(0x18+S*2), $(0x19+T*2)
@ -505,18 +447,18 @@ void maddx(const UDSPInstruction opc)
// Multiply one part of secondary accumulator $ax0 (selected by S) by
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
// signed) and subtract result from product register.
void msubx(const UDSPInstruction opc)
void Interpreter::msubx(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
const u8 treg = (opc >> 8) & 0x1;
const u8 sreg = (opc >> 9) & 0x1;
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_sub(val1, val2);
const u16 val1 = (sreg == 0) ? GetAXLow(0) : GetAXHigh(0);
const u16 val2 = (treg == 0) ? GetAXLow(1) : GetAXHigh(1);
const s64 prod = MultiplySub(val1, val2);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
SetLongProduct(prod);
}
// MADDC $acS.m, $axT.h
@ -524,18 +466,18 @@ void msubx(const UDSPInstruction opc)
// Multiply middle part of accumulator $acS.m by high part of secondary
// accumulator $axT.h (treat them both as signed) and add result to product
// register.
void maddc(const UDSPInstruction opc)
void Interpreter::maddc(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
const u8 treg = (opc >> 8) & 0x1;
const u8 sreg = (opc >> 9) & 0x1;
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply_add(accm, axh);
const u16 accm = GetAccMid(sreg);
const u16 axh = GetAXHigh(treg);
const s64 prod = MultiplyAdd(accm, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
SetLongProduct(prod);
}
// MSUBC $acS.m, $axT.h
@ -543,18 +485,18 @@ void maddc(const UDSPInstruction opc)
// Multiply middle part of accumulator $acS.m by high part of secondary
// accumulator $axT.h (treat them both as signed) and subtract result from
// product register.
void msubc(const UDSPInstruction opc)
void Interpreter::msubc(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
const u8 treg = (opc >> 8) & 0x1;
const u8 sreg = (opc >> 9) & 0x1;
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply_sub(accm, axh);
const u16 accm = GetAccMid(sreg);
const u16 axh = GetAXHigh(treg);
const s64 prod = MultiplySub(accm, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
SetLongProduct(prod);
}
// MADD $axS.l, $axS.h
@ -562,17 +504,16 @@ void msubc(const UDSPInstruction opc)
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed) and add
// result to product register.
void madd(const UDSPInstruction opc)
void Interpreter::madd(const UDSPInstruction opc)
{
u8 sreg = (opc >> 8) & 0x1;
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply_add(axl, axh);
const u8 sreg = (opc >> 8) & 0x1;
const u16 axl = GetAXLow(sreg);
const u16 axh = GetAXHigh(sreg);
const s64 prod = MultiplyAdd(axl, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
SetLongProduct(prod);
}
// MSUB $axS.l, $axS.h
@ -580,16 +521,15 @@ void madd(const UDSPInstruction opc)
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed) and
// subtract result from product register.
void msub(const UDSPInstruction opc)
void Interpreter::msub(const UDSPInstruction opc)
{
u8 sreg = (opc >> 8) & 0x1;
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply_sub(axl, axh);
const u8 sreg = (opc >> 8) & 0x1;
const u16 axl = GetAXLow(sreg);
const u16 axh = GetAXHigh(sreg);
const s64 prod = MultiplySub(axl, axh);
ZeroWriteBackLog();
dsp_set_long_prod(prod);
SetLongProduct(prod);
}
} // namespace DSP::Interpreter

View File

@ -8,7 +8,6 @@
#include "Common/CommonTypes.h"
#include "Core/DSP/DSPTables.h"
#include "Core/DSP/Interpreter/DSPIntExtOps.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"
namespace DSP::Interpreter
@ -23,216 +22,216 @@ struct InterpreterOpInfo
// clang-format off
constexpr std::array<InterpreterOpInfo, 124> s_opcodes
{{
{0x0000, 0xfffc, nop},
{0x0000, 0xfffc, &Interpreter::nop},
{0x0004, 0xfffc, dar},
{0x0008, 0xfffc, iar},
{0x000c, 0xfffc, subarn},
{0x0010, 0xfff0, addarn},
{0x0004, 0xfffc, &Interpreter::dar},
{0x0008, 0xfffc, &Interpreter::iar},
{0x000c, 0xfffc, &Interpreter::subarn},
{0x0010, 0xfff0, &Interpreter::addarn},
{0x0021, 0xffff, halt},
{0x0021, 0xffff, &Interpreter::halt},
{0x02d0, 0xfff0, ret},
{0x02d0, 0xfff0, &Interpreter::ret},
{0x02ff, 0xffff, rti},
{0x02ff, 0xffff, &Interpreter::rti},
{0x02b0, 0xfff0, call},
{0x02b0, 0xfff0, &Interpreter::call},
{0x0270, 0xfff0, ifcc},
{0x0270, 0xfff0, &Interpreter::ifcc},
{0x0290, 0xfff0, jcc},
{0x0290, 0xfff0, &Interpreter::jcc},
{0x1700, 0xff10, jmprcc},
{0x1700, 0xff10, &Interpreter::jmprcc},
{0x1710, 0xff10, callr},
{0x1710, 0xff10, &Interpreter::callr},
{0x1200, 0xff00, sbclr},
{0x1300, 0xff00, sbset},
{0x1200, 0xff00, &Interpreter::sbclr},
{0x1300, 0xff00, &Interpreter::sbset},
{0x1400, 0xfec0, lsl},
{0x1440, 0xfec0, lsr},
{0x1480, 0xfec0, asl},
{0x14c0, 0xfec0, asr},
{0x1400, 0xfec0, &Interpreter::lsl},
{0x1440, 0xfec0, &Interpreter::lsr},
{0x1480, 0xfec0, &Interpreter::asl},
{0x14c0, 0xfec0, &Interpreter::asr},
// these two were discovered by ector
{0x02ca, 0xffff, lsrn},
{0x02cb, 0xffff, asrn},
{0x02ca, 0xffff, &Interpreter::lsrn},
{0x02cb, 0xffff, &Interpreter::asrn},
{0x0080, 0xffe0, lri},
{0x00c0, 0xffe0, lr},
{0x00e0, 0xffe0, sr},
{0x0080, 0xffe0, &Interpreter::lri},
{0x00c0, 0xffe0, &Interpreter::lr},
{0x00e0, 0xffe0, &Interpreter::sr},
{0x1c00, 0xfc00, mrr},
{0x1c00, 0xfc00, &Interpreter::mrr},
{0x1600, 0xff00, si},
{0x1600, 0xff00, &Interpreter::si},
{0x0400, 0xfe00, addis},
{0x0600, 0xfe00, cmpis},
{0x0800, 0xf800, lris},
{0x0400, 0xfe00, &Interpreter::addis},
{0x0600, 0xfe00, &Interpreter::cmpis},
{0x0800, 0xf800, &Interpreter::lris},
{0x0200, 0xfeff, addi},
{0x0220, 0xfeff, xori},
{0x0240, 0xfeff, andi},
{0x0260, 0xfeff, ori},
{0x0280, 0xfeff, cmpi},
{0x0200, 0xfeff, &Interpreter::addi},
{0x0220, 0xfeff, &Interpreter::xori},
{0x0240, 0xfeff, &Interpreter::andi},
{0x0260, 0xfeff, &Interpreter::ori},
{0x0280, 0xfeff, &Interpreter::cmpi},
{0x02a0, 0xfeff, andf},
{0x02c0, 0xfeff, andcf},
{0x02a0, 0xfeff, &Interpreter::andf},
{0x02c0, 0xfeff, &Interpreter::andcf},
{0x0210, 0xfefc, ilrr},
{0x0214, 0xfefc, ilrrd},
{0x0218, 0xfefc, ilrri},
{0x021c, 0xfefc, ilrrn},
{0x0210, 0xfefc, &Interpreter::ilrr},
{0x0214, 0xfefc, &Interpreter::ilrrd},
{0x0218, 0xfefc, &Interpreter::ilrri},
{0x021c, 0xfefc, &Interpreter::ilrrn},
// LOOPS
{0x0040, 0xffe0, loop},
{0x0060, 0xffe0, bloop},
{0x1000, 0xff00, loopi},
{0x1100, 0xff00, bloopi},
{0x0040, 0xffe0, &Interpreter::loop},
{0x0060, 0xffe0, &Interpreter::bloop},
{0x1000, 0xff00, &Interpreter::loopi},
{0x1100, 0xff00, &Interpreter::bloopi},
// load and store value pointed by indexing reg and increment; LRR/SRR variants
{0x1800, 0xff80, lrr},
{0x1880, 0xff80, lrrd},
{0x1900, 0xff80, lrri},
{0x1980, 0xff80, lrrn},
{0x1800, 0xff80, &Interpreter::lrr},
{0x1880, 0xff80, &Interpreter::lrrd},
{0x1900, 0xff80, &Interpreter::lrri},
{0x1980, 0xff80, &Interpreter::lrrn},
{0x1a00, 0xff80, srr},
{0x1a80, 0xff80, srrd},
{0x1b00, 0xff80, srri},
{0x1b80, 0xff80, srrn},
{0x1a00, 0xff80, &Interpreter::srr},
{0x1a80, 0xff80, &Interpreter::srrd},
{0x1b00, 0xff80, &Interpreter::srri},
{0x1b80, 0xff80, &Interpreter::srrn},
// 2
{0x2000, 0xf800, lrs},
{0x2800, 0xf800, srs},
{0x2000, 0xf800, &Interpreter::lrs},
{0x2800, 0xf800, &Interpreter::srs},
// opcodes that can be extended
// 3 - main opcode defined by 9 bits, extension defined by last 7 bits!!
{0x3000, 0xfc80, xorr},
{0x3400, 0xfc80, andr},
{0x3800, 0xfc80, orr},
{0x3c00, 0xfe80, andc},
{0x3e00, 0xfe80, orc},
{0x3080, 0xfe80, xorc},
{0x3280, 0xfe80, notc},
{0x3480, 0xfc80, lsrnrx},
{0x3880, 0xfc80, asrnrx},
{0x3c80, 0xfe80, lsrnr},
{0x3e80, 0xfe80, asrnr},
{0x3000, 0xfc80, &Interpreter::xorr},
{0x3400, 0xfc80, &Interpreter::andr},
{0x3800, 0xfc80, &Interpreter::orr},
{0x3c00, 0xfe80, &Interpreter::andc},
{0x3e00, 0xfe80, &Interpreter::orc},
{0x3080, 0xfe80, &Interpreter::xorc},
{0x3280, 0xfe80, &Interpreter::notc},
{0x3480, 0xfc80, &Interpreter::lsrnrx},
{0x3880, 0xfc80, &Interpreter::asrnrx},
{0x3c80, 0xfe80, &Interpreter::lsrnr},
{0x3e80, 0xfe80, &Interpreter::asrnr},
// 4
{0x4000, 0xf800, addr},
{0x4800, 0xfc00, addax},
{0x4c00, 0xfe00, add},
{0x4e00, 0xfe00, addp},
{0x4000, 0xf800, &Interpreter::addr},
{0x4800, 0xfc00, &Interpreter::addax},
{0x4c00, 0xfe00, &Interpreter::add},
{0x4e00, 0xfe00, &Interpreter::addp},
// 5
{0x5000, 0xf800, subr},
{0x5800, 0xfc00, subax},
{0x5c00, 0xfe00, sub},
{0x5e00, 0xfe00, subp},
{0x5000, 0xf800, &Interpreter::subr},
{0x5800, 0xfc00, &Interpreter::subax},
{0x5c00, 0xfe00, &Interpreter::sub},
{0x5e00, 0xfe00, &Interpreter::subp},
// 6
{0x6000, 0xf800, movr},
{0x6800, 0xfc00, movax},
{0x6c00, 0xfe00, mov},
{0x6e00, 0xfe00, movp},
{0x6000, 0xf800, &Interpreter::movr},
{0x6800, 0xfc00, &Interpreter::movax},
{0x6c00, 0xfe00, &Interpreter::mov},
{0x6e00, 0xfe00, &Interpreter::movp},
// 7
{0x7000, 0xfc00, addaxl},
{0x7400, 0xfe00, incm},
{0x7600, 0xfe00, inc},
{0x7800, 0xfe00, decm},
{0x7a00, 0xfe00, dec},
{0x7c00, 0xfe00, neg},
{0x7e00, 0xfe00, movnp},
{0x7000, 0xfc00, &Interpreter::addaxl},
{0x7400, 0xfe00, &Interpreter::incm},
{0x7600, 0xfe00, &Interpreter::inc},
{0x7800, 0xfe00, &Interpreter::decm},
{0x7a00, 0xfe00, &Interpreter::dec},
{0x7c00, 0xfe00, &Interpreter::neg},
{0x7e00, 0xfe00, &Interpreter::movnp},
// 8
{0x8000, 0xf700, nx},
{0x8100, 0xf700, clr},
{0x8200, 0xff00, cmp},
{0x8300, 0xff00, mulaxh},
{0x8400, 0xff00, clrp},
{0x8500, 0xff00, tstprod},
{0x8600, 0xfe00, tstaxh},
{0x8a00, 0xff00, srbith},
{0x8b00, 0xff00, srbith},
{0x8c00, 0xff00, srbith},
{0x8d00, 0xff00, srbith},
{0x8e00, 0xff00, srbith},
{0x8f00, 0xff00, srbith},
{0x8000, 0xf700, &Interpreter::nx},
{0x8100, 0xf700, &Interpreter::clr},
{0x8200, 0xff00, &Interpreter::cmp},
{0x8300, 0xff00, &Interpreter::mulaxh},
{0x8400, 0xff00, &Interpreter::clrp},
{0x8500, 0xff00, &Interpreter::tstprod},
{0x8600, 0xfe00, &Interpreter::tstaxh},
{0x8a00, 0xff00, &Interpreter::srbith},
{0x8b00, 0xff00, &Interpreter::srbith},
{0x8c00, 0xff00, &Interpreter::srbith},
{0x8d00, 0xff00, &Interpreter::srbith},
{0x8e00, 0xff00, &Interpreter::srbith},
{0x8f00, 0xff00, &Interpreter::srbith},
// 9
{0x9000, 0xf700, mul},
{0x9100, 0xf700, asr16},
{0x9200, 0xf600, mulmvz},
{0x9400, 0xf600, mulac},
{0x9600, 0xf600, mulmv},
{0x9000, 0xf700, &Interpreter::mul},
{0x9100, 0xf700, &Interpreter::asr16},
{0x9200, 0xf600, &Interpreter::mulmvz},
{0x9400, 0xf600, &Interpreter::mulac},
{0x9600, 0xf600, &Interpreter::mulmv},
// A-B
{0xa000, 0xe700, mulx},
{0xa100, 0xf700, abs},
{0xa200, 0xe600, mulxmvz},
{0xa400, 0xe600, mulxac},
{0xa600, 0xe600, mulxmv},
{0xb100, 0xf700, tst},
{0xa000, 0xe700, &Interpreter::mulx},
{0xa100, 0xf700, &Interpreter::abs},
{0xa200, 0xe600, &Interpreter::mulxmvz},
{0xa400, 0xe600, &Interpreter::mulxac},
{0xa600, 0xe600, &Interpreter::mulxmv},
{0xb100, 0xf700, &Interpreter::tst},
// C-D
{0xc000, 0xe700, mulc},
{0xc100, 0xe700, cmpar},
{0xc200, 0xe600, mulcmvz},
{0xc400, 0xe600, mulcac},
{0xc600, 0xe600, mulcmv},
{0xc000, 0xe700, &Interpreter::mulc},
{0xc100, 0xe700, &Interpreter::cmpar},
{0xc200, 0xe600, &Interpreter::mulcmvz},
{0xc400, 0xe600, &Interpreter::mulcac},
{0xc600, 0xe600, &Interpreter::mulcmv},
// E
{0xe000, 0xfc00, maddx},
{0xe400, 0xfc00, msubx},
{0xe800, 0xfc00, maddc},
{0xec00, 0xfc00, msubc},
{0xe000, 0xfc00, &Interpreter::maddx},
{0xe400, 0xfc00, &Interpreter::msubx},
{0xe800, 0xfc00, &Interpreter::maddc},
{0xec00, 0xfc00, &Interpreter::msubc},
// F
{0xf000, 0xfe00, lsl16},
{0xf200, 0xfe00, madd},
{0xf400, 0xfe00, lsr16},
{0xf600, 0xfe00, msub},
{0xf800, 0xfc00, addpaxz},
{0xfc00, 0xfe00, clrl},
{0xfe00, 0xfe00, movpz},
{0xf000, 0xfe00, &Interpreter::lsl16},
{0xf200, 0xfe00, &Interpreter::madd},
{0xf400, 0xfe00, &Interpreter::lsr16},
{0xf600, 0xfe00, &Interpreter::msub},
{0xf800, 0xfc00, &Interpreter::addpaxz},
{0xfc00, 0xfe00, &Interpreter::clrl},
{0xfe00, 0xfe00, &Interpreter::movpz},
}};
constexpr std::array<InterpreterOpInfo, 25> s_opcodes_ext
{{
{0x0000, 0x00fc, Ext::nop},
{0x0000, 0x00fc, &Interpreter::nop_ext},
{0x0004, 0x00fc, Ext::dr},
{0x0008, 0x00fc, Ext::ir},
{0x000c, 0x00fc, Ext::nr},
{0x0010, 0x00f0, Ext::mv},
{0x0004, 0x00fc, &Interpreter::dr},
{0x0008, 0x00fc, &Interpreter::ir},
{0x000c, 0x00fc, &Interpreter::nr},
{0x0010, 0x00f0, &Interpreter::mv},
{0x0020, 0x00e4, Ext::s},
{0x0024, 0x00e4, Ext::sn},
{0x0020, 0x00e4, &Interpreter::s},
{0x0024, 0x00e4, &Interpreter::sn},
{0x0040, 0x00c4, Ext::l},
{0x0044, 0x00c4, Ext::ln},
{0x0040, 0x00c4, &Interpreter::l},
{0x0044, 0x00c4, &Interpreter::ln},
{0x0080, 0x00ce, Ext::ls},
{0x0082, 0x00ce, Ext::sl},
{0x0084, 0x00ce, Ext::lsn},
{0x0086, 0x00ce, Ext::sln},
{0x0088, 0x00ce, Ext::lsm},
{0x008a, 0x00ce, Ext::slm},
{0x008c, 0x00ce, Ext::lsnm},
{0x008e, 0x00ce, Ext::slnm},
{0x0080, 0x00ce, &Interpreter::ls},
{0x0082, 0x00ce, &Interpreter::sl},
{0x0084, 0x00ce, &Interpreter::lsn},
{0x0086, 0x00ce, &Interpreter::sln},
{0x0088, 0x00ce, &Interpreter::lsm},
{0x008a, 0x00ce, &Interpreter::slm},
{0x008c, 0x00ce, &Interpreter::lsnm},
{0x008e, 0x00ce, &Interpreter::slnm},
{0x00c3, 0x00cf, Ext::ldax},
{0x00c7, 0x00cf, Ext::ldaxn},
{0x00cb, 0x00cf, Ext::ldaxm},
{0x00cf, 0x00cf, Ext::ldaxnm},
{0x00c3, 0x00cf, &Interpreter::ldax},
{0x00c7, 0x00cf, &Interpreter::ldaxn},
{0x00cb, 0x00cf, &Interpreter::ldaxm},
{0x00cf, 0x00cf, &Interpreter::ldaxnm},
{0x00c0, 0x00cc, Ext::ld},
{0x00c4, 0x00cc, Ext::ldn},
{0x00c8, 0x00cc, Ext::ldm},
{0x00cc, 0x00cc, Ext::ldnm},
{0x00c0, 0x00cc, &Interpreter::ld},
{0x00c4, 0x00cc, &Interpreter::ldn},
{0x00c8, 0x00cc, &Interpreter::ldm},
{0x00cc, 0x00cc, &Interpreter::ldnm},
}};
// clang-format on
@ -266,7 +265,7 @@ void InitInstructionTables()
// ext op table
for (size_t i = 0; i < s_ext_op_table.size(); i++)
{
s_ext_op_table[i] = nop;
s_ext_op_table[i] = &Interpreter::nop;
const auto iter = FindByOpcode(static_cast<UDSPInstruction>(i), s_opcodes_ext);
if (iter == s_opcodes_ext.cend())
@ -278,7 +277,7 @@ void InitInstructionTables()
// op table
for (size_t i = 0; i < s_op_table.size(); i++)
{
s_op_table[i] = nop;
s_op_table[i] = &Interpreter::nop;
const auto iter = FindByOpcode(static_cast<UDSPInstruction>(i), s_opcodes);
if (iter == s_opcodes.cend())

View File

@ -8,7 +8,9 @@
namespace DSP::Interpreter
{
using InterpreterFunction = void (*)(UDSPInstruction);
class Interpreter;
using InterpreterFunction = void (Interpreter::*)(UDSPInstruction);
InterpreterFunction GetOp(UDSPInstruction inst);
InterpreterFunction GetExtOp(UDSPInstruction inst);

View File

@ -5,376 +5,27 @@
#pragma once
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPStacks.h"
namespace DSP::Interpreter
{
// ---------------------------------------------------------------------------------------
// --- SR
// ---------------------------------------------------------------------------------------
static inline void dsp_SR_set_flag(int flag)
{
g_dsp.r.sr |= flag;
}
static inline bool dsp_SR_is_flag_set(int flag)
{
return (g_dsp.r.sr & flag) != 0;
}
// ---------------------------------------------------------------------------------------
// --- AR increments, decrements
// ---------------------------------------------------------------------------------------
static inline u16 dsp_increase_addr_reg(u16 reg, s16 _ix)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];
s32 ix = _ix;
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;
}
static inline u16 dsp_decrease_addr_reg(u16 reg, s16 _ix)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];
s32 ix = _ix;
u32 mx = (wr | 1) << 1;
u32 nar = ar - ix;
u32 dar = (nar ^ ar ^ ~ix) & mx;
if ((u32)ix > 0xFFFF8000) //(ix < 0 && ix != -0x8000)
{
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;
}
static inline u16 dsp_increment_addr_reg(u16 reg)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];
u32 nar = ar + 1;
if ((nar ^ ar) > ((wr | 1) << 1))
nar -= wr + 1;
return nar;
}
static inline u16 dsp_decrement_addr_reg(u16 reg)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];
u32 nar = ar + wr;
if (((nar ^ ar) & ((wr | 1) << 1)) > wr)
nar -= wr + 1;
return nar;
}
// ---------------------------------------------------------------------------------------
// --- reg
// ---------------------------------------------------------------------------------------
static inline u16 dsp_op_read_reg(int _reg)
{
int reg = _reg & 0x1f;
switch (reg)
{
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
return dsp_reg_load_stack(static_cast<StackRegister>(reg - DSP_REG_ST0));
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
return g_dsp.r.ar[reg - DSP_REG_AR0];
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
return g_dsp.r.ix[reg - DSP_REG_IX0];
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
return g_dsp.r.wr[reg - DSP_REG_WR0];
case DSP_REG_ACH0:
case DSP_REG_ACH1:
return g_dsp.r.ac[reg - DSP_REG_ACH0].h;
case DSP_REG_CR:
return g_dsp.r.cr;
case DSP_REG_SR:
return g_dsp.r.sr;
case DSP_REG_PRODL:
return g_dsp.r.prod.l;
case DSP_REG_PRODM:
return g_dsp.r.prod.m;
case DSP_REG_PRODH:
return g_dsp.r.prod.h;
case DSP_REG_PRODM2:
return g_dsp.r.prod.m2;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
return g_dsp.r.ax[reg - DSP_REG_AXL0].l;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
return g_dsp.r.ax[reg - DSP_REG_AXH0].h;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
return g_dsp.r.ac[reg - DSP_REG_ACL0].l;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
return g_dsp.r.ac[reg - DSP_REG_ACM0].m;
default:
ASSERT_MSG(DSP_INT, 0, "cannot happen");
return 0;
}
}
static inline void dsp_op_write_reg(int _reg, u16 val)
{
int reg = _reg & 0x1f;
switch (reg)
{
// 8-bit sign extended registers. Should look at prod.h too...
case DSP_REG_ACH0:
case DSP_REG_ACH1:
// sign extend from the bottom 8 bits.
g_dsp.r.ac[reg - DSP_REG_ACH0].h = (u16)(s16)(s8)(u8)val;
break;
// Stack registers.
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
dsp_reg_store_stack(static_cast<StackRegister>(reg - DSP_REG_ST0), val);
break;
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
g_dsp.r.ar[reg - DSP_REG_AR0] = val;
break;
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
g_dsp.r.ix[reg - DSP_REG_IX0] = val;
break;
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
g_dsp.r.wr[reg - DSP_REG_WR0] = val;
break;
case DSP_REG_CR:
g_dsp.r.cr = val;
break;
case DSP_REG_SR:
g_dsp.r.sr = val;
break;
case DSP_REG_PRODL:
g_dsp.r.prod.l = val;
break;
case DSP_REG_PRODM:
g_dsp.r.prod.m = val;
break;
case DSP_REG_PRODH:
g_dsp.r.prod.h = val;
break;
case DSP_REG_PRODM2:
g_dsp.r.prod.m2 = val;
break;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
g_dsp.r.ax[reg - DSP_REG_AXL0].l = val;
break;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
g_dsp.r.ax[reg - DSP_REG_AXH0].h = val;
break;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
g_dsp.r.ac[reg - DSP_REG_ACL0].l = val;
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
g_dsp.r.ac[reg - DSP_REG_ACM0].m = val;
break;
}
}
static inline void dsp_conditional_extend_accum(int reg)
{
switch (reg)
{
case DSP_REG_ACM0:
case DSP_REG_ACM1:
if (g_dsp.r.sr & SR_40_MODE_BIT)
{
// Sign extend into whole accum.
u16 val = g_dsp.r.ac[reg - DSP_REG_ACM0].m;
g_dsp.r.ac[reg - DSP_REG_ACM0].h = (val & 0x8000) ? 0xFFFF : 0x0000;
g_dsp.r.ac[reg - DSP_REG_ACM0].l = 0;
}
}
}
// ---------------------------------------------------------------------------------------
// --- prod (40-bit)
// ---------------------------------------------------------------------------------------
static inline s64 dsp_get_long_prod()
{
s64 val = (s8)(u8)g_dsp.r.prod.h;
val <<= 32;
s64 low_prod = g_dsp.r.prod.m;
low_prod += g_dsp.r.prod.m2;
low_prod <<= 16;
low_prod |= g_dsp.r.prod.l;
val += low_prod;
return val;
}
static inline s64 dsp_get_long_prod_round_prodl()
{
s64 prod = dsp_get_long_prod();
if (prod & 0x10000)
prod = (prod + 0x8000) & ~0xffff;
else
prod = (prod + 0x7fff) & ~0xffff;
return prod;
}
// For accurate emulation, this is wrong - but the real prod registers behave
// in completely bizarre ways. Not needed to emulate them correctly for game ucodes.
inline void dsp_set_long_prod(s64 val)
{
g_dsp.r.prod.val = val & 0x000000FFFFFFFFFFULL;
}
// ---------------------------------------------------------------------------------------
// --- ACC - main accumulators (40-bit)
// ---------------------------------------------------------------------------------------
inline s64 dsp_get_long_acc(int reg)
{
return ((s64)(g_dsp.r.ac[reg].val << 24) >> 24);
}
inline void dsp_set_long_acc(int _reg, s64 val)
{
g_dsp.r.ac[_reg].val = (u64)val;
}
inline s64 dsp_convert_long_acc(s64 val) // s64 -> s40
// s64 -> s40
inline s64 dsp_convert_long_acc(s64 val)
{
return ((val << 24) >> 24);
}
inline s64 dsp_round_long_acc(s64 val)
{
if (val & 0x10000)
if ((val & 0x10000) != 0)
val = (val + 0x8000) & ~0xffff;
else
val = (val + 0x7fff) & ~0xffff;
return val;
}
inline s16 dsp_get_acc_l(int _reg)
{
return (s16)g_dsp.r.ac[_reg].l;
}
inline s16 dsp_get_acc_m(int _reg)
{
return (s16)g_dsp.r.ac[_reg].m;
}
inline s16 dsp_get_acc_h(int _reg)
{
return (s16)g_dsp.r.ac[_reg].h;
}
inline u16 dsp_op_read_reg_and_saturate(u8 _reg)
{
if (g_dsp.r.sr & SR_40_MODE_BIT)
{
s64 acc = dsp_get_long_acc(_reg);
if (acc != (s32)acc)
{
if (acc > 0)
return 0x7fff;
else
return 0x8000;
}
else
{
return g_dsp.r.ac[_reg].m;
}
}
else
{
return g_dsp.r.ac[_reg].m;
}
}
// ---------------------------------------------------------------------------------------
// --- AX - extra accumulators (32-bit)
// ---------------------------------------------------------------------------------------
inline s32 dsp_get_long_acx(int _reg)
{
return (s32)(((u32)g_dsp.r.ax[_reg].h << 16) | g_dsp.r.ax[_reg].l);
}
inline s16 dsp_get_ax_l(int _reg)
{
return (s16)g_dsp.r.ax[_reg].l;
}
inline s16 dsp_get_ax_h(int _reg)
{
return (s16)g_dsp.r.ax[_reg].h;
}
} // namespace DSP::Interpreter

View File

@ -5,115 +5,68 @@
#include "Core/DSP/Interpreter/DSPInterpreter.h"
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Core/DSP/DSPAnalyzer.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/DSPTables.h"
#include "Core/DSP/Interpreter/DSPIntTables.h"
namespace DSP::Interpreter
{
namespace
Interpreter::Interpreter(DSPCore& dsp) : m_dsp_core{dsp}
{
void ExecuteInstruction(const UDSPInstruction inst)
InitInstructionTables();
}
Interpreter::~Interpreter() = default;
void Interpreter::ExecuteInstruction(const UDSPInstruction inst)
{
const DSPOPCTemplate* opcode_template = GetOpTemplate(inst);
if (opcode_template->extended)
{
GetExtOp(inst)(inst);
(this->*GetExtOp(inst))(inst);
}
GetOp(inst)(inst);
(this->*GetOp(inst))(inst);
if (opcode_template->extended)
{
ApplyWriteBackLog();
}
}
} // Anonymous namespace
// NOTE: These have nothing to do with g_dsp.r.cr !
void WriteCR(u16 val)
void Interpreter::Step()
{
// reset
if (val & 1)
{
INFO_LOG_FMT(DSPLLE, "DSP_CONTROL RESET");
DSPCore_Reset();
val &= ~1;
}
// init
else if (val == 4)
{
// HAX!
// OSInitAudioSystem ucode should send this mail - not DSP core itself
INFO_LOG_FMT(DSPLLE, "DSP_CONTROL INIT");
g_init_hax = true;
val |= 0x800;
}
m_dsp_core.CheckExceptions();
m_dsp_core.DSPState().step_counter++;
// update cr
g_dsp.cr = val;
}
const u16 opc = m_dsp_core.DSPState().FetchInstruction();
ExecuteInstruction(UDSPInstruction{opc});
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));
if (Analyzer::GetCodeFlags(static_cast<u16>(g_dsp.pc - 1u)) & Analyzer::CODE_LOOP_END)
const auto pc = m_dsp_core.DSPState().pc;
if ((Analyzer::GetCodeFlags(static_cast<u16>(pc - 1)) & Analyzer::CODE_LOOP_END) != 0)
HandleLoop();
}
// Used by thread mode.
int RunCyclesThread(int cycles)
int Interpreter::RunCyclesThread(int cycles)
{
auto& state = m_dsp_core.DSPState();
while (true)
{
if (g_dsp.cr & CR_HALT)
if ((state.cr & CR_HALT) != 0)
return 0;
if (g_dsp.external_interrupt_waiting)
if (state.external_interrupt_waiting)
{
DSPCore_CheckExternalInterrupt();
DSPCore_SetExternalInterrupt(false);
m_dsp_core.CheckExternalInterrupt();
m_dsp_core.SetExternalInterrupt(false);
}
Step();
@ -124,16 +77,19 @@ int RunCyclesThread(int cycles)
}
// This one has basic idle skipping, and checks breakpoints.
int RunCyclesDebug(int cycles)
int Interpreter::RunCyclesDebug(int cycles)
{
auto& state = m_dsp_core.DSPState();
// 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)
if ((state.cr & CR_HALT) != 0)
return 0;
if (g_dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
if (m_dsp_core.BreakPoints().IsAddressBreakPoint(state.pc))
{
DSPCore_SetState(State::Stepping);
m_dsp_core.SetState(State::Stepping);
return cycles;
}
Step();
@ -148,16 +104,19 @@ int RunCyclesDebug(int cycles)
// idle loops.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
if ((state.cr & CR_HALT) != 0)
return 0;
if (g_dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
if (m_dsp_core.BreakPoints().IsAddressBreakPoint(state.pc))
{
DSPCore_SetState(State::Stepping);
m_dsp_core.SetState(State::Stepping);
return cycles;
}
// Idle skipping.
if (Analyzer::GetCodeFlags(g_dsp.pc) & Analyzer::CODE_IDLE_SKIP)
if ((Analyzer::GetCodeFlags(state.pc) & Analyzer::CODE_IDLE_SKIP) != 0)
return 0;
Step();
cycles--;
if (cycles < 0)
@ -167,9 +126,9 @@ int RunCyclesDebug(int cycles)
// Now, lets run some more without idle skipping.
for (int i = 0; i < 200; i++)
{
if (g_dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
if (m_dsp_core.BreakPoints().IsAddressBreakPoint(state.pc))
{
DSPCore_SetState(State::Stepping);
m_dsp_core.SetState(State::Stepping);
return cycles;
}
Step();
@ -183,16 +142,20 @@ int RunCyclesDebug(int cycles)
}
// Used by non-thread mode. Meant to be efficient.
int RunCycles(int cycles)
int Interpreter::RunCycles(int cycles)
{
auto& state = m_dsp_core.DSPState();
// 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)
if ((state.cr & CR_HALT) != 0)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
@ -203,13 +166,16 @@ int RunCycles(int cycles)
// idle loops.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
if ((state.cr & CR_HALT) != 0)
return 0;
// Idle skipping.
if (Analyzer::GetCodeFlags(g_dsp.pc) & Analyzer::CODE_IDLE_SKIP)
if ((Analyzer::GetCodeFlags(state.pc) & Analyzer::CODE_IDLE_SKIP) != 0)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
@ -227,7 +193,656 @@ int RunCycles(int cycles)
}
}
void nop(const UDSPInstruction opc)
// NOTE: These have nothing to do with SDSP::r::cr!
void Interpreter::WriteCR(u16 val)
{
// reset
if ((val & 1) != 0)
{
INFO_LOG_FMT(DSPLLE, "DSP_CONTROL RESET");
m_dsp_core.Reset();
val &= ~1;
}
// init
else if (val == 4)
{
// HAX!
// OSInitAudioSystem ucode should send this mail - not DSP core itself
INFO_LOG_FMT(DSPLLE, "DSP_CONTROL INIT");
m_dsp_core.SetInitHax(true);
val |= 0x800;
}
// update cr
m_dsp_core.DSPState().cr = val;
}
u16 Interpreter::ReadCR()
{
auto& state = m_dsp_core.DSPState();
if ((state.pc & 0x8000) != 0)
{
state.cr |= 0x800;
}
else
{
state.cr &= ~0x800;
}
return state.cr;
}
void Interpreter::SetSRFlag(u16 flag)
{
m_dsp_core.DSPState().SetSRFlag(flag);
}
bool Interpreter::IsSRFlagSet(u16 flag) const
{
return m_dsp_core.DSPState().IsSRFlagSet(flag);
}
bool Interpreter::CheckCondition(u8 condition) const
{
const auto IsCarry = [this] { return IsSRFlagSet(SR_CARRY); };
const auto IsOverflow = [this] { return IsSRFlagSet(SR_OVERFLOW); };
const auto IsOverS32 = [this] { return IsSRFlagSet(SR_OVER_S32); };
const auto IsLess = [this] {
const auto& state = m_dsp_core.DSPState();
return (state.r.sr & SR_OVERFLOW) != (state.r.sr & SR_SIGN);
};
const auto IsZero = [this] { return IsSRFlagSet(SR_ARITH_ZERO); };
const auto IsLogicZero = [this] { return IsSRFlagSet(SR_LOGIC_ZERO); };
const auto IsConditionA = [this] {
return (IsSRFlagSet(SR_OVER_S32) || IsSRFlagSet(SR_TOP2BITS)) && !IsSRFlagSet(SR_ARITH_ZERO);
};
switch (condition & 0xf)
{
case 0xf: // Always true.
return true;
case 0x0: // GE - Greater Equal
return !IsLess();
case 0x1: // L - Less
return IsLess();
case 0x2: // G - Greater
return !IsLess() && !IsZero();
case 0x3: // LE - Less Equal
return IsLess() || IsZero();
case 0x4: // NZ - Not Zero
return !IsZero();
case 0x5: // Z - Zero
return IsZero();
case 0x6: // NC - Not carry
return !IsCarry();
case 0x7: // C - Carry
return IsCarry();
case 0x8: // ? - Not over s32
return !IsOverS32();
case 0x9: // ? - Over s32
return IsOverS32();
case 0xa: // ?
return IsConditionA();
case 0xb: // ?
return !IsConditionA();
case 0xc: // LNZ - Logic Not Zero
return !IsLogicZero();
case 0xd: // LZ - Logic Zero
return IsLogicZero();
case 0xe: // 0 - Overflow
return IsOverflow();
default:
return true;
}
}
u16 Interpreter::IncrementAddressRegister(u16 reg) const
{
auto& state = m_dsp_core.DSPState();
const u32 ar = state.r.ar[reg];
const u32 wr = state.r.wr[reg];
u32 nar = ar + 1;
if ((nar ^ ar) > ((wr | 1) << 1))
nar -= wr + 1;
return static_cast<u16>(nar);
}
u16 Interpreter::DecrementAddressRegister(u16 reg) const
{
const auto& state = m_dsp_core.DSPState();
const u32 ar = state.r.ar[reg];
const u32 wr = state.r.wr[reg];
u32 nar = ar + wr;
if (((nar ^ ar) & ((wr | 1) << 1)) > wr)
nar -= wr + 1;
return static_cast<u16>(nar);
}
u16 Interpreter::IncreaseAddressRegister(u16 reg, s16 ix_) const
{
const auto& state = m_dsp_core.DSPState();
const u32 ar = state.r.ar[reg];
const u32 wr = state.r.wr[reg];
const s32 ix = ix_;
const u32 mx = (wr | 1) << 1;
u32 nar = ar + ix;
const u32 dar = (nar ^ ar ^ ix) & mx;
if (ix >= 0)
{
if (dar > wr) // Overflow
nar -= wr + 1;
}
else
{
// Underflow or below min for mask
if ((((nar + wr + 1) ^ nar) & dar) <= wr)
nar += wr + 1;
}
return static_cast<u16>(nar);
}
u16 Interpreter::DecreaseAddressRegister(u16 reg, s16 ix_) const
{
const auto& state = m_dsp_core.DSPState();
const u32 ar = state.r.ar[reg];
const u32 wr = state.r.wr[reg];
const s32 ix = ix_;
const u32 mx = (wr | 1) << 1;
u32 nar = ar - ix;
const u32 dar = (nar ^ ar ^ ~ix) & mx;
// (ix < 0 && ix != -0x8000)
if (static_cast<u32>(ix) > 0xFFFF8000)
{
if (dar > wr) // overflow
nar -= wr + 1;
}
else
{
// Underflow or below min for mask
if ((((nar + wr + 1) ^ nar) & dar) <= wr)
nar += wr + 1;
}
return static_cast<u16>(nar);
}
s32 Interpreter::GetLongACX(s32 reg) const
{
const auto& state = m_dsp_core.DSPState();
return static_cast<s32>((static_cast<u32>(state.r.ax[reg].h) << 16) | state.r.ax[reg].l);
}
s16 Interpreter::GetAXLow(s32 reg) const
{
return static_cast<s16>(m_dsp_core.DSPState().r.ax[reg].l);
}
s16 Interpreter::GetAXHigh(s32 reg) const
{
return static_cast<s16>(m_dsp_core.DSPState().r.ax[reg].h);
}
s64 Interpreter::GetLongAcc(s32 reg) const
{
const auto& state = m_dsp_core.DSPState();
return static_cast<s64>(state.r.ac[reg].val << 24) >> 24;
}
void Interpreter::SetLongAcc(s32 reg, s64 value)
{
auto& state = m_dsp_core.DSPState();
state.r.ac[reg].val = static_cast<u64>(value);
}
s16 Interpreter::GetAccLow(s32 reg) const
{
return static_cast<s16>(m_dsp_core.DSPState().r.ac[reg].l);
}
s16 Interpreter::GetAccMid(s32 reg) const
{
return static_cast<s16>(m_dsp_core.DSPState().r.ac[reg].m);
}
s16 Interpreter::GetAccHigh(s32 reg) const
{
return static_cast<s16>(m_dsp_core.DSPState().r.ac[reg].h);
}
s64 Interpreter::GetLongProduct() const
{
const auto& state = m_dsp_core.DSPState();
s64 val = static_cast<s8>(static_cast<u8>(state.r.prod.h));
val <<= 32;
s64 low_prod = state.r.prod.m;
low_prod += state.r.prod.m2;
low_prod <<= 16;
low_prod |= state.r.prod.l;
val += low_prod;
return val;
}
s64 Interpreter::GetLongProductRounded() const
{
const s64 prod = GetLongProduct();
if ((prod & 0x10000) != 0)
return (prod + 0x8000) & ~0xffff;
else
return (prod + 0x7fff) & ~0xffff;
}
void Interpreter::SetLongProduct(s64 value)
{
// For accurate emulation, this is wrong - but the real prod registers behave
// in completely bizarre ways. Not needed to emulate them correctly for game ucodes.
m_dsp_core.DSPState().r.prod.val = static_cast<u64>(value & 0x000000FFFFFFFFFFULL);
}
s64 Interpreter::GetMultiplyProduct(u16 a, u16 b, u8 sign) const
{
s64 prod;
// Unsigned
if (sign == 1 && IsSRFlagSet(SR_MUL_UNSIGNED))
prod = static_cast<u32>(a * b);
else if (sign == 2 && IsSRFlagSet(SR_MUL_UNSIGNED)) // mixed
prod = a * static_cast<s16>(b);
else // Signed
prod = static_cast<s16>(a) * static_cast<s16>(b);
// Conditionally multiply by 2.
if (!IsSRFlagSet(SR_MUL_MODIFY))
prod <<= 1;
return prod;
}
s64 Interpreter::Multiply(u16 a, u16 b, u8 sign) const
{
return GetMultiplyProduct(a, b, sign);
}
s64 Interpreter::MultiplyAdd(u16 a, u16 b, u8 sign) const
{
return GetLongProduct() + GetMultiplyProduct(a, b, sign);
}
s64 Interpreter::MultiplySub(u16 a, u16 b, u8 sign) const
{
return GetLongProduct() - GetMultiplyProduct(a, b, sign);
}
s64 Interpreter::MultiplyMulX(u8 axh0, u8 axh1, u16 val1, u16 val2) const
{
s64 result;
if (axh0 == 0 && axh1 == 0)
result = Multiply(val1, val2, 1); // Unsigned support ON if both ax?.l regs are used
else if (axh0 == 0 && axh1 == 1)
result = Multiply(val1, val2, 2); // Mixed support ON (u16)axl.0 * (s16)axh.1
else if (axh0 == 1 && axh1 == 0)
result = Multiply(val2, val1, 2); // Mixed support ON (u16)axl.1 * (s16)axh.0
else
result = Multiply(val1, val2, 0); // Unsigned support OFF if both ax?.h regs are used
return result;
}
void Interpreter::UpdateSR16(s16 value, bool carry, bool overflow, bool over_s32)
{
auto& state = m_dsp_core.DSPState();
state.r.sr &= ~SR_CMP_MASK;
// 0x01
if (carry)
{
state.r.sr |= SR_CARRY;
}
// 0x02 and 0x80
if (overflow)
{
state.r.sr |= SR_OVERFLOW;
state.r.sr |= SR_OVERFLOW_STICKY;
}
// 0x04
if (value == 0)
{
state.r.sr |= SR_ARITH_ZERO;
}
// 0x08
if (value < 0)
{
state.r.sr |= SR_SIGN;
}
// 0x10
if (over_s32)
{
state.r.sr |= SR_OVER_S32;
}
// 0x20 - Checks if top bits of m are equal
if (((static_cast<u16>(value) >> 14) == 0) || ((static_cast<u16>(value) >> 14) == 3))
{
state.r.sr |= SR_TOP2BITS;
}
}
void Interpreter::UpdateSR64(s64 value, bool carry, bool overflow)
{
auto& state = m_dsp_core.DSPState();
state.r.sr &= ~SR_CMP_MASK;
// 0x01
if (carry)
{
state.r.sr |= SR_CARRY;
}
// 0x02 and 0x80
if (overflow)
{
state.r.sr |= SR_OVERFLOW;
state.r.sr |= SR_OVERFLOW_STICKY;
}
// 0x04
if (value == 0)
{
state.r.sr |= SR_ARITH_ZERO;
}
// 0x08
if (value < 0)
{
state.r.sr |= SR_SIGN;
}
// 0x10
if (value != static_cast<s32>(value))
{
state.r.sr |= SR_OVER_S32;
}
// 0x20 - Checks if top bits of m are equal
if (((value & 0xc0000000) == 0) || ((value & 0xc0000000) == 0xc0000000))
{
state.r.sr |= SR_TOP2BITS;
}
}
void Interpreter::UpdateSRLogicZero(bool value)
{
auto& state = m_dsp_core.DSPState();
if (value)
state.r.sr |= SR_LOGIC_ZERO;
else
state.r.sr &= ~SR_LOGIC_ZERO;
}
u16 Interpreter::OpReadRegister(int reg_)
{
const int reg = reg_ & 0x1f;
auto& state = m_dsp_core.DSPState();
switch (reg)
{
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
return state.PopStack(static_cast<StackRegister>(reg - DSP_REG_ST0));
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
return state.r.ar[reg - DSP_REG_AR0];
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
return state.r.ix[reg - DSP_REG_IX0];
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
return state.r.wr[reg - DSP_REG_WR0];
case DSP_REG_ACH0:
case DSP_REG_ACH1:
return state.r.ac[reg - DSP_REG_ACH0].h;
case DSP_REG_CR:
return state.r.cr;
case DSP_REG_SR:
return state.r.sr;
case DSP_REG_PRODL:
return state.r.prod.l;
case DSP_REG_PRODM:
return state.r.prod.m;
case DSP_REG_PRODH:
return state.r.prod.h;
case DSP_REG_PRODM2:
return state.r.prod.m2;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
return state.r.ax[reg - DSP_REG_AXL0].l;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
return state.r.ax[reg - DSP_REG_AXH0].h;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
return state.r.ac[reg - DSP_REG_ACL0].l;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
return state.r.ac[reg - DSP_REG_ACM0].m;
default:
ASSERT_MSG(DSP_INT, 0, "cannot happen");
return 0;
}
}
u16 Interpreter::OpReadRegisterAndSaturate(int reg) const
{
if (IsSRFlagSet(SR_40_MODE_BIT))
{
const s64 acc = GetLongAcc(reg);
if (acc != static_cast<s32>(acc))
{
if (acc > 0)
return 0x7fff;
else
return 0x8000;
}
return m_dsp_core.DSPState().r.ac[reg].m;
}
return m_dsp_core.DSPState().r.ac[reg].m;
}
void Interpreter::OpWriteRegister(int reg_, u16 val)
{
const int reg = reg_ & 0x1f;
auto& state = m_dsp_core.DSPState();
switch (reg)
{
// 8-bit sign extended registers. Should look at prod.h too...
case DSP_REG_ACH0:
case DSP_REG_ACH1:
// sign extend from the bottom 8 bits.
state.r.ac[reg - DSP_REG_ACH0].h = (u16)(s16)(s8)(u8)val;
break;
// Stack registers.
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
state.StoreStack(static_cast<StackRegister>(reg - DSP_REG_ST0), val);
break;
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
state.r.ar[reg - DSP_REG_AR0] = val;
break;
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
state.r.ix[reg - DSP_REG_IX0] = val;
break;
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
state.r.wr[reg - DSP_REG_WR0] = val;
break;
case DSP_REG_CR:
state.r.cr = val;
break;
case DSP_REG_SR:
state.r.sr = val;
break;
case DSP_REG_PRODL:
state.r.prod.l = val;
break;
case DSP_REG_PRODM:
state.r.prod.m = val;
break;
case DSP_REG_PRODH:
state.r.prod.h = val;
break;
case DSP_REG_PRODM2:
state.r.prod.m2 = val;
break;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
state.r.ax[reg - DSP_REG_AXL0].l = val;
break;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
state.r.ax[reg - DSP_REG_AXH0].h = val;
break;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
state.r.ac[reg - DSP_REG_ACL0].l = val;
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
state.r.ac[reg - DSP_REG_ACM0].m = val;
break;
}
}
void Interpreter::ConditionalExtendAccum(int reg)
{
if (reg != DSP_REG_ACM0 && reg != DSP_REG_ACM1)
return;
if (!IsSRFlagSet(SR_40_MODE_BIT))
return;
// Sign extend into whole accum.
auto& state = m_dsp_core.DSPState();
const u16 val = state.r.ac[reg - DSP_REG_ACM0].m;
state.r.ac[reg - DSP_REG_ACM0].h = (val & 0x8000) != 0 ? 0xFFFF : 0x0000;
state.r.ac[reg - DSP_REG_ACM0].l = 0;
}
// The ext op are writing their output into the backlog which is
// being applied to the real registers after the main op was executed
void Interpreter::ApplyWriteBackLog()
{
// Always make sure to have an extra entry at the end w/ -1 to avoid
// infinitive loops
for (int i = 0; m_write_back_log_idx[i] != -1; i++)
{
u16 value = m_write_back_log[i];
#ifdef PRECISE_BACKLOG
value |= OpReadRegister(m_write_back_log_idx[i]);
#endif
OpWriteRegister(m_write_back_log_idx[i], value);
// Clear back log
m_write_back_log_idx[i] = -1;
}
}
void Interpreter::WriteToBackLog(int i, int idx, u16 value)
{
m_write_back_log[i] = value;
m_write_back_log_idx[i] = idx;
}
// This function is being called in the main op after all input regs were read
// and before it writes into any regs. This way we can always use bitwise or to
// apply the ext command output, because if the main op didn't change the value
// then 0 | ext output = ext output and if it did then bitwise or is still the
// right thing to do
// Only needed for cases when mainop and extended are modifying the same ACC
// Games are not doing that + in motorola (similar DSP) dox this is forbidden to do.
void Interpreter::ZeroWriteBackLog()
{
#ifdef PRECISE_BACKLOG
// always make sure to have an extra entry at the end w/ -1 to avoid
// infinitive loops
for (int i = 0; m_write_back_log_idx[i] != -1; i++)
{
OpWriteRegister(m_write_back_log_idx[i], 0);
}
#endif
}
void Interpreter::ZeroWriteBackLogPreserveAcc([[maybe_unused]] u8 acc)
{
#ifdef PRECISE_BACKLOG
for (int i = 0; m_write_back_log_idx[i] != -1; i++)
{
// acc0
if (acc == 0 &&
((m_write_back_log_idx[i] == DSP_REG_ACL0) || (m_write_back_log_idx[i] == DSP_REG_ACM0) ||
(m_write_back_log_idx[i] == DSP_REG_ACH0)))
{
continue;
}
// acc1
if (acc == 1 &&
((m_write_back_log_idx[i] == DSP_REG_ACL1) || (m_write_back_log_idx[i] == DSP_REG_ACM1) ||
(m_write_back_log_idx[i] == DSP_REG_ACH1)))
{
continue;
}
OpWriteRegister(m_write_back_log_idx[i], 0);
}
#endif
}
void Interpreter::nop(const UDSPInstruction opc)
{
// The real nop is 0. Anything else is bad.
if (opc == 0)

View File

@ -4,144 +4,248 @@
#pragma once
#include <array>
#include <cstddef>
#include "Core/DSP/DSPCommon.h"
#include "Core/DSP/DSPCore.h"
namespace DSP::Interpreter
{
void Step();
class Interpreter
{
public:
explicit Interpreter(DSPCore& dsp);
~Interpreter();
// See: DspIntBranch.cpp
void HandleLoop();
Interpreter(const Interpreter&) = delete;
Interpreter& operator=(const Interpreter&) = delete;
// If these simply return the same number of cycles as was passed into them,
// chances are that the DSP is halted.
// The difference between them is that the debug one obeys breakpoints.
int RunCyclesThread(int cycles);
int RunCycles(int cycles);
int RunCyclesDebug(int cycles);
Interpreter(Interpreter&&) = delete;
Interpreter& operator=(Interpreter&&) = delete;
void WriteCR(u16 val);
u16 ReadCR();
void Step();
// All the opcode functions.
void abs(UDSPInstruction opc);
void add(UDSPInstruction opc);
void addarn(UDSPInstruction opc);
void addax(UDSPInstruction opc);
void addaxl(UDSPInstruction opc);
void addi(UDSPInstruction opc);
void addis(UDSPInstruction opc);
void addp(UDSPInstruction opc);
void addpaxz(UDSPInstruction opc);
void addr(UDSPInstruction opc);
void andc(UDSPInstruction opc);
void andcf(UDSPInstruction opc);
void andf(UDSPInstruction opc);
void andi(UDSPInstruction opc);
void andr(UDSPInstruction opc);
void asl(UDSPInstruction opc);
void asr(UDSPInstruction opc);
void asr16(UDSPInstruction opc);
void asrn(UDSPInstruction opc);
void asrnr(UDSPInstruction opc);
void asrnrx(UDSPInstruction opc);
void bloop(UDSPInstruction opc);
void bloopi(UDSPInstruction opc);
void call(UDSPInstruction opc);
void callr(UDSPInstruction opc);
void clr(UDSPInstruction opc);
void clrl(UDSPInstruction opc);
void clrp(UDSPInstruction opc);
void cmp(UDSPInstruction opc);
void cmpar(UDSPInstruction opc);
void cmpi(UDSPInstruction opc);
void cmpis(UDSPInstruction opc);
void dar(UDSPInstruction opc);
void dec(UDSPInstruction opc);
void decm(UDSPInstruction opc);
void halt(UDSPInstruction opc);
void iar(UDSPInstruction opc);
void ifcc(UDSPInstruction opc);
void ilrr(UDSPInstruction opc);
void ilrrd(UDSPInstruction opc);
void ilrri(UDSPInstruction opc);
void ilrrn(UDSPInstruction opc);
void inc(UDSPInstruction opc);
void incm(UDSPInstruction opc);
void jcc(UDSPInstruction opc);
void jmprcc(UDSPInstruction opc);
void loop(UDSPInstruction opc);
void loopi(UDSPInstruction opc);
void lr(UDSPInstruction opc);
void lri(UDSPInstruction opc);
void lris(UDSPInstruction opc);
void lrr(UDSPInstruction opc);
void lrrd(UDSPInstruction opc);
void lrri(UDSPInstruction opc);
void lrrn(UDSPInstruction opc);
void lrs(UDSPInstruction opc);
void lsl(UDSPInstruction opc);
void lsl16(UDSPInstruction opc);
void lsr(UDSPInstruction opc);
void lsr16(UDSPInstruction opc);
void lsrn(UDSPInstruction opc);
void lsrnr(UDSPInstruction opc);
void lsrnrx(UDSPInstruction opc);
void madd(UDSPInstruction opc);
void maddc(UDSPInstruction opc);
void maddx(UDSPInstruction opc);
void mov(UDSPInstruction opc);
void movax(UDSPInstruction opc);
void movnp(UDSPInstruction opc);
void movp(UDSPInstruction opc);
void movpz(UDSPInstruction opc);
void movr(UDSPInstruction opc);
void mrr(UDSPInstruction opc);
void msub(UDSPInstruction opc);
void msubc(UDSPInstruction opc);
void msubx(UDSPInstruction opc);
void mul(UDSPInstruction opc);
void mulac(UDSPInstruction opc);
void mulaxh(UDSPInstruction opc);
void mulc(UDSPInstruction opc);
void mulcac(UDSPInstruction opc);
void mulcmv(UDSPInstruction opc);
void mulcmvz(UDSPInstruction opc);
void mulmv(UDSPInstruction opc);
void mulmvz(UDSPInstruction opc);
void mulx(UDSPInstruction opc);
void mulxac(UDSPInstruction opc);
void mulxmv(UDSPInstruction opc);
void mulxmvz(UDSPInstruction opc);
void neg(UDSPInstruction opc);
void nop(UDSPInstruction opc);
void notc(UDSPInstruction opc);
void nx(UDSPInstruction opc);
void orc(UDSPInstruction opc);
void ori(UDSPInstruction opc);
void orr(UDSPInstruction opc);
void ret(UDSPInstruction opc);
void rti(UDSPInstruction opc);
void sbclr(UDSPInstruction opc);
void sbset(UDSPInstruction opc);
void si(UDSPInstruction opc);
void sr(UDSPInstruction opc);
void srbith(UDSPInstruction opc);
void srr(UDSPInstruction opc);
void srrd(UDSPInstruction opc);
void srri(UDSPInstruction opc);
void srrn(UDSPInstruction opc);
void srs(UDSPInstruction opc);
void sub(UDSPInstruction opc);
void subarn(UDSPInstruction opc);
void subax(UDSPInstruction opc);
void subp(UDSPInstruction opc);
void subr(UDSPInstruction opc);
void tst(UDSPInstruction opc);
void tstaxh(UDSPInstruction opc);
void tstprod(UDSPInstruction opc);
void xorc(UDSPInstruction opc);
void xori(UDSPInstruction opc);
void xorr(UDSPInstruction opc);
// If these simply return the same number of cycles as was passed into them,
// chances are that the DSP is halted.
// The difference between them is that the debug one obeys breakpoints.
int RunCyclesThread(int cycles);
int RunCycles(int cycles);
int RunCyclesDebug(int cycles);
void WriteCR(u16 val);
u16 ReadCR();
void SetSRFlag(u16 flag);
bool IsSRFlagSet(u16 flag) const;
void ApplyWriteBackLog();
// All the opcode functions.
void abs(UDSPInstruction opc);
void add(UDSPInstruction opc);
void addarn(UDSPInstruction opc);
void addax(UDSPInstruction opc);
void addaxl(UDSPInstruction opc);
void addi(UDSPInstruction opc);
void addis(UDSPInstruction opc);
void addp(UDSPInstruction opc);
void addpaxz(UDSPInstruction opc);
void addr(UDSPInstruction opc);
void andc(UDSPInstruction opc);
void andcf(UDSPInstruction opc);
void andf(UDSPInstruction opc);
void andi(UDSPInstruction opc);
void andr(UDSPInstruction opc);
void asl(UDSPInstruction opc);
void asr(UDSPInstruction opc);
void asr16(UDSPInstruction opc);
void asrn(UDSPInstruction opc);
void asrnr(UDSPInstruction opc);
void asrnrx(UDSPInstruction opc);
void bloop(UDSPInstruction opc);
void bloopi(UDSPInstruction opc);
void call(UDSPInstruction opc);
void callr(UDSPInstruction opc);
void clr(UDSPInstruction opc);
void clrl(UDSPInstruction opc);
void clrp(UDSPInstruction opc);
void cmp(UDSPInstruction opc);
void cmpar(UDSPInstruction opc);
void cmpi(UDSPInstruction opc);
void cmpis(UDSPInstruction opc);
void dar(UDSPInstruction opc);
void dec(UDSPInstruction opc);
void decm(UDSPInstruction opc);
void halt(UDSPInstruction opc);
void iar(UDSPInstruction opc);
void ifcc(UDSPInstruction opc);
void ilrr(UDSPInstruction opc);
void ilrrd(UDSPInstruction opc);
void ilrri(UDSPInstruction opc);
void ilrrn(UDSPInstruction opc);
void inc(UDSPInstruction opc);
void incm(UDSPInstruction opc);
void jcc(UDSPInstruction opc);
void jmprcc(UDSPInstruction opc);
void loop(UDSPInstruction opc);
void loopi(UDSPInstruction opc);
void lr(UDSPInstruction opc);
void lri(UDSPInstruction opc);
void lris(UDSPInstruction opc);
void lrr(UDSPInstruction opc);
void lrrd(UDSPInstruction opc);
void lrri(UDSPInstruction opc);
void lrrn(UDSPInstruction opc);
void lrs(UDSPInstruction opc);
void lsl(UDSPInstruction opc);
void lsl16(UDSPInstruction opc);
void lsr(UDSPInstruction opc);
void lsr16(UDSPInstruction opc);
void lsrn(UDSPInstruction opc);
void lsrnr(UDSPInstruction opc);
void lsrnrx(UDSPInstruction opc);
void madd(UDSPInstruction opc);
void maddc(UDSPInstruction opc);
void maddx(UDSPInstruction opc);
void mov(UDSPInstruction opc);
void movax(UDSPInstruction opc);
void movnp(UDSPInstruction opc);
void movp(UDSPInstruction opc);
void movpz(UDSPInstruction opc);
void movr(UDSPInstruction opc);
void mrr(UDSPInstruction opc);
void msub(UDSPInstruction opc);
void msubc(UDSPInstruction opc);
void msubx(UDSPInstruction opc);
void mul(UDSPInstruction opc);
void mulac(UDSPInstruction opc);
void mulaxh(UDSPInstruction opc);
void mulc(UDSPInstruction opc);
void mulcac(UDSPInstruction opc);
void mulcmv(UDSPInstruction opc);
void mulcmvz(UDSPInstruction opc);
void mulmv(UDSPInstruction opc);
void mulmvz(UDSPInstruction opc);
void mulx(UDSPInstruction opc);
void mulxac(UDSPInstruction opc);
void mulxmv(UDSPInstruction opc);
void mulxmvz(UDSPInstruction opc);
void neg(UDSPInstruction opc);
void nop(UDSPInstruction opc);
void notc(UDSPInstruction opc);
void nx(UDSPInstruction opc);
void orc(UDSPInstruction opc);
void ori(UDSPInstruction opc);
void orr(UDSPInstruction opc);
void ret(UDSPInstruction opc);
void rti(UDSPInstruction opc);
void sbclr(UDSPInstruction opc);
void sbset(UDSPInstruction opc);
void si(UDSPInstruction opc);
void sr(UDSPInstruction opc);
void srbith(UDSPInstruction opc);
void srr(UDSPInstruction opc);
void srrd(UDSPInstruction opc);
void srri(UDSPInstruction opc);
void srrn(UDSPInstruction opc);
void srs(UDSPInstruction opc);
void sub(UDSPInstruction opc);
void subarn(UDSPInstruction opc);
void subax(UDSPInstruction opc);
void subp(UDSPInstruction opc);
void subr(UDSPInstruction opc);
void tst(UDSPInstruction opc);
void tstaxh(UDSPInstruction opc);
void tstprod(UDSPInstruction opc);
void xorc(UDSPInstruction opc);
void xori(UDSPInstruction opc);
void xorr(UDSPInstruction opc);
// Extended ops
void l(UDSPInstruction opc);
void ln(UDSPInstruction opc);
void ls(UDSPInstruction opc);
void lsn(UDSPInstruction opc);
void lsm(UDSPInstruction opc);
void lsnm(UDSPInstruction opc);
void sl(UDSPInstruction opc);
void sln(UDSPInstruction opc);
void slm(UDSPInstruction opc);
void slnm(UDSPInstruction opc);
void s(UDSPInstruction opc);
void sn(UDSPInstruction opc);
void ld(UDSPInstruction opc);
void ldax(UDSPInstruction opc);
void ldn(UDSPInstruction opc);
void ldaxn(UDSPInstruction opc);
void ldm(UDSPInstruction opc);
void ldaxm(UDSPInstruction opc);
void ldnm(UDSPInstruction opc);
void ldaxnm(UDSPInstruction opc);
void mv(UDSPInstruction opc);
void dr(UDSPInstruction opc);
void ir(UDSPInstruction opc);
void nr(UDSPInstruction opc);
void nop_ext(UDSPInstruction opc);
private:
void ExecuteInstruction(UDSPInstruction inst);
bool CheckCondition(u8 condition) const;
// See: DspIntBranch.cpp
void HandleLoop();
u16 IncrementAddressRegister(u16 reg) const;
u16 DecrementAddressRegister(u16 reg) const;
u16 IncreaseAddressRegister(u16 reg, s16 ix_) const;
u16 DecreaseAddressRegister(u16 reg, s16 ix_) const;
s32 GetLongACX(s32 reg) const;
s16 GetAXLow(s32 reg) const;
s16 GetAXHigh(s32 reg) const;
s64 GetLongAcc(s32 reg) const;
void SetLongAcc(s32 reg, s64 value);
s16 GetAccLow(s32 reg) const;
s16 GetAccMid(s32 reg) const;
s16 GetAccHigh(s32 reg) const;
s64 GetLongProduct() const;
s64 GetLongProductRounded() const;
void SetLongProduct(s64 value);
s64 GetMultiplyProduct(u16 a, u16 b, u8 sign = 0) const;
s64 Multiply(u16 a, u16 b, u8 sign = 0) const;
s64 MultiplyAdd(u16 a, u16 b, u8 sign = 0) const;
s64 MultiplySub(u16 a, u16 b, u8 sign = 0) const;
s64 MultiplyMulX(u8 axh0, u8 axh1, u16 val1, u16 val2) const;
void UpdateSR16(s16 value, bool carry = false, bool overflow = false, bool over_s32 = false);
void UpdateSR64(s64 value, bool carry = false, bool overflow = false);
void UpdateSRLogicZero(bool value);
u16 OpReadRegister(int reg_);
u16 OpReadRegisterAndSaturate(int reg) const;
void OpWriteRegister(int reg_, u16 val);
void ConditionalExtendAccum(int reg);
// The ext ops are calculated in parallel with the actual op. That means that
// both the main op and the ext op see the same register state as input. The
// output is simple as long as the main and ext ops don't change the same
// register. If they do the output is the bitwise OR of the result of both the
// main and ext ops.
void WriteToBackLog(int i, int idx, u16 value);
void ZeroWriteBackLog();
void ZeroWriteBackLogPreserveAcc(u8 acc);
DSPCore& m_dsp_core;
static constexpr size_t WRITEBACK_LOG_SIZE = 5;
std::array<u16, WRITEBACK_LOG_SIZE> m_write_back_log{};
std::array<int, WRITEBACK_LOG_SIZE> m_write_back_log_idx{-1, -1, -1, -1, -1};
};
} // namespace DSP::Interpreter

View File

@ -12,10 +12,10 @@ namespace DSP::JIT
{
DSPEmitter::~DSPEmitter() = default;
std::unique_ptr<DSPEmitter> CreateDSPEmitter()
std::unique_ptr<DSPEmitter> CreateDSPEmitter([[maybe_unused]] DSPCore& dsp)
{
#if defined(_M_X86) || defined(_M_X86_64)
return std::make_unique<x64::DSPEmitter>();
return std::make_unique<x64::DSPEmitter>(dsp);
#else
return std::make_unique<DSPEmitterNull>();
#endif

View File

@ -10,6 +10,11 @@
class PointerWrap;
namespace DSP
{
class DSPCore;
}
namespace DSP::JIT
{
class DSPEmitter
@ -31,5 +36,5 @@ public:
void DoState(PointerWrap&) override {}
};
std::unique_ptr<DSPEmitter> CreateDSPEmitter();
std::unique_ptr<DSPEmitter> CreateDSPEmitter(DSPCore& dsp);
} // namespace DSP::JIT

View File

@ -17,9 +17,9 @@
#include "Core/DSP/DSPAnalyzer.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPHost.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/DSPTables.h"
#include "Core/DSP/Interpreter/DSPIntTables.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"
#include "Core/DSP/Jit/x64/DSPJitTables.h"
using namespace Gen;
@ -30,9 +30,9 @@ constexpr size_t COMPILED_CODE_SIZE = 2097152;
constexpr size_t MAX_BLOCK_SIZE = 250;
constexpr u16 DSP_IDLE_SKIP_CYCLES = 0x1000;
DSPEmitter::DSPEmitter()
DSPEmitter::DSPEmitter(DSPCore& dsp)
: m_compile_status_register{SR_INT_ENABLE | SR_EXT_INT_ENABLE}, m_blocks(MAX_BLOCKS),
m_block_size(MAX_BLOCKS), m_block_links(MAX_BLOCKS)
m_block_size(MAX_BLOCKS), m_block_links(MAX_BLOCKS), m_dsp_core{dsp}
{
x64::InitInstructionTables();
AllocCodeSpace(COMPILED_CODE_SIZE);
@ -51,18 +51,18 @@ DSPEmitter::~DSPEmitter()
u16 DSPEmitter::RunCycles(u16 cycles)
{
if (g_dsp.external_interrupt_waiting)
if (m_dsp_core.DSPState().external_interrupt_waiting)
{
DSPCore_CheckExternalInterrupt();
DSPCore_CheckExceptions();
DSPCore_SetExternalInterrupt(false);
m_dsp_core.CheckExternalInterrupt();
m_dsp_core.CheckExceptions();
m_dsp_core.SetExternalInterrupt(false);
}
m_cycles_left = cycles;
auto exec_addr = (DSPCompiledCode)m_enter_dispatcher;
exec_addr();
if (g_dsp.reset_dspjit_codespace)
if (m_dsp_core.DSPState().reset_dspjit_codespace)
ClearIRAMandDSPJITCodespaceReset();
return m_cycles_left;
@ -82,7 +82,7 @@ void DSPEmitter::ClearIRAM()
m_block_size[i] = 0;
m_unresolved_jumps[i].clear();
}
g_dsp.reset_dspjit_codespace = true;
m_dsp_core.DSPState().reset_dspjit_codespace = true;
}
void DSPEmitter::ClearIRAMandDSPJITCodespaceReset()
@ -98,7 +98,12 @@ void DSPEmitter::ClearIRAMandDSPJITCodespaceReset()
m_block_size[i] = 0;
m_unresolved_jumps[i].clear();
}
g_dsp.reset_dspjit_codespace = false;
m_dsp_core.DSPState().reset_dspjit_codespace = false;
}
static void CheckExceptionsThunk(DSPCore& dsp)
{
dsp.CheckExceptions();
}
// Must go out of block if exception is detected
@ -112,7 +117,7 @@ void DSPEmitter::checkExceptions(u32 retval)
DSPJitRegCache c(m_gpr);
m_gpr.SaveRegs();
ABI_CallFunction(DSPCore_CheckExceptions);
ABI_CallFunctionP(CheckExceptionsThunk, &m_dsp_core);
MOV(32, R(EAX), Imm32(retval));
JMP(m_return_dispatcher, true);
m_gpr.LoadRegs(false);
@ -128,6 +133,11 @@ bool DSPEmitter::FlagsNeeded() const
return !(flags & Analyzer::CODE_START_OF_INST) || (flags & Analyzer::CODE_UPDATE_SR);
}
static void FallbackThunk(Interpreter::Interpreter& interpreter, UDSPInstruction inst)
{
(interpreter.*Interpreter::GetOp(inst))(inst);
}
void DSPEmitter::FallBackToInterpreter(UDSPInstruction inst)
{
const DSPOPCTemplate* const op_template = GetOpTemplate(inst);
@ -146,10 +156,20 @@ void DSPEmitter::FallBackToInterpreter(UDSPInstruction inst)
m_gpr.PushRegs();
ASSERT_MSG(DSPLLE, interpreter_function != nullptr, "No function for %04x", inst);
ABI_CallFunctionC16(interpreter_function, inst);
ABI_CallFunctionPC(FallbackThunk, &m_dsp_core.GetInterpreter(), inst);
m_gpr.PopRegs();
}
static void FallbackExtThunk(Interpreter::Interpreter& interpreter, UDSPInstruction inst)
{
(interpreter.*Interpreter::GetExtOp(inst))(inst);
}
static void ApplyWriteBackLogThunk(Interpreter::Interpreter& interpreter)
{
interpreter.ApplyWriteBackLog();
}
void DSPEmitter::EmitInstruction(UDSPInstruction inst)
{
const DSPOPCTemplate* const op_template = GetOpTemplate(inst);
@ -168,10 +188,8 @@ void DSPEmitter::EmitInstruction(UDSPInstruction inst)
else
{
// Fall back to interpreter
const auto interpreter_function = Interpreter::GetExtOp(inst);
m_gpr.PushRegs();
ABI_CallFunctionC16(interpreter_function, inst);
ABI_CallFunctionPC(FallbackExtThunk, &m_dsp_core.GetInterpreter(), inst);
m_gpr.PopRegs();
INFO_LOG_FMT(DSPLLE, "Instruction not JITed(ext part): {:04x}", inst);
ext_is_jit = false;
@ -198,7 +216,7 @@ void DSPEmitter::EmitInstruction(UDSPInstruction inst)
// need to call the online cleanup function because
// the writeBackLog gets populated at runtime
m_gpr.PushRegs();
ABI_CallFunction(ApplyWriteBackLog);
ABI_CallFunctionP(ApplyWriteBackLogThunk, &m_dsp_core.GetInterpreter());
m_gpr.PopRegs();
}
else
@ -229,7 +247,7 @@ void DSPEmitter::Compile(u16 start_addr)
if (Analyzer::GetCodeFlags(m_compile_pc) & Analyzer::CODE_CHECK_INT)
checkExceptions(m_block_size[start_addr]);
UDSPInstruction inst = dsp_imem_read(m_compile_pc);
const UDSPInstruction inst = m_dsp_core.DSPState().ReadIMEM(m_compile_pc);
const DSPOPCTemplate* opcode = GetOpTemplate(inst);
EmitInstruction(inst);
@ -377,7 +395,7 @@ void DSPEmitter::Compile(u16 start_addr)
void DSPEmitter::CompileCurrent(DSPEmitter& emitter)
{
emitter.Compile(g_dsp.pc);
emitter.Compile(emitter.m_dsp_core.DSPState().pc);
bool retry = true;
@ -414,7 +432,7 @@ void DSPEmitter::CompileDispatcher()
BitSet32 registers_used = ABI_ALL_CALLEE_SAVED & BitSet32(0xffff);
ABI_PushRegistersAndAdjustStack(registers_used, 8);
MOV(64, R(R15), ImmPtr(&g_dsp));
MOV(64, R(R15), ImmPtr(&m_dsp_core.DSPState()));
const u8* dispatcherLoop = GetCodePtr();

View File

@ -28,7 +28,7 @@ namespace JIT::x64
class DSPEmitter final : public JIT::DSPEmitter, public Gen::X64CodeBlock
{
public:
DSPEmitter();
explicit DSPEmitter(DSPCore& dsp);
~DSPEmitter() override;
u16 RunCycles(u16 cycles) override;
@ -197,6 +197,9 @@ private:
// within the class itself to allow access to member variables.
static void CompileCurrent(DSPEmitter& emitter);
static u16 ReadIFXRegisterHelper(DSPEmitter& emitter, u16 address);
static void WriteIFXRegisterHelper(DSPEmitter& emitter, u16 address, u16 value);
void EmitInstruction(UDSPInstruction inst);
void ClearIRAMandDSPJITCodespaceReset();
@ -321,6 +324,8 @@ private:
const u8* m_enter_dispatcher;
const u8* m_return_dispatcher;
const u8* m_stub_entry_point;
DSPCore& m_dsp_core;
};
} // namespace JIT::x64

View File

@ -7,7 +7,6 @@
#include "Common/CommonTypes.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/Jit/x64/DSPEmitter.h"
using namespace Gen;
@ -65,9 +64,9 @@ void DSPEmitter::andcf(const UDSPInstruction opc)
{
if (FlagsNeeded())
{
u8 reg = (opc >> 8) & 0x1;
const u8 reg = (opc >> 8) & 0x1;
// u16 imm = dsp_fetch_code();
u16 imm = dsp_imem_read(m_compile_pc + 1);
const u16 imm = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
// u16 val = dsp_get_acc_m(reg);
get_acc_m(reg);
// Update_SR_LZ(((val & imm) == imm) ? true : false);
@ -100,9 +99,9 @@ void DSPEmitter::andf(const UDSPInstruction opc)
{
if (FlagsNeeded())
{
u8 reg = (opc >> 8) & 0x1;
const u8 reg = (opc >> 8) & 0x1;
// u16 imm = dsp_fetch_code();
u16 imm = dsp_imem_read(m_compile_pc + 1);
const u16 imm = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
// u16 val = dsp_get_acc_m(reg);
get_acc_m(reg);
// Update_SR_LZ(((val & imm) == 0) ? true : false);
@ -226,14 +225,14 @@ void DSPEmitter::cmpi(const UDSPInstruction opc)
{
if (FlagsNeeded())
{
u8 reg = (opc >> 8) & 0x1;
X64Reg tmp1 = m_gpr.GetFreeXReg();
const u8 reg = (opc >> 8) & 0x1;
const X64Reg tmp1 = m_gpr.GetFreeXReg();
// s64 val = dsp_get_long_acc(reg);
get_long_acc(reg, tmp1);
MOV(64, R(RAX), R(tmp1));
// s64 imm = (s64)(s16)dsp_fetch_code() << 16; // Immediate is considered to be at M level in
// the 40-bit accumulator.
u16 imm = dsp_imem_read(m_compile_pc + 1);
const u16 imm = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
MOV(64, R(RDX), Imm64((s64)(s16)imm << 16));
// s64 res = dsp_convert_long_acc(val - imm);
SUB(64, R(RAX), R(RDX));
@ -451,9 +450,9 @@ void DSPEmitter::notc(const UDSPInstruction opc)
// flags out: --xx xx00
void DSPEmitter::xori(const UDSPInstruction opc)
{
u8 reg = (opc >> 8) & 0x1;
const u8 reg = (opc >> 8) & 0x1;
// u16 imm = dsp_fetch_code();
u16 imm = dsp_imem_read(m_compile_pc + 1);
const u16 imm = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
// g_dsp.r.acm[reg] ^= imm;
get_acc_m(reg, RAX);
XOR(16, R(RAX), Imm16(imm));
@ -474,9 +473,9 @@ void DSPEmitter::xori(const UDSPInstruction opc)
// flags out: --xx xx00
void DSPEmitter::andi(const UDSPInstruction opc)
{
u8 reg = (opc >> 8) & 0x1;
const u8 reg = (opc >> 8) & 0x1;
// u16 imm = dsp_fetch_code();
u16 imm = dsp_imem_read(m_compile_pc + 1);
const u16 imm = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
// g_dsp.r.acm[reg] &= imm;
get_acc_m(reg, RAX);
AND(16, R(RAX), Imm16(imm));
@ -497,9 +496,9 @@ void DSPEmitter::andi(const UDSPInstruction opc)
// flags out: --xx xx00
void DSPEmitter::ori(const UDSPInstruction opc)
{
u8 reg = (opc >> 8) & 0x1;
const u8 reg = (opc >> 8) & 0x1;
// u16 imm = dsp_fetch_code();
u16 imm = dsp_imem_read(m_compile_pc + 1);
const u16 imm = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
// g_dsp.r.acm[reg] |= imm;
get_acc_m(reg, RAX);
OR(16, R(RAX), Imm16(imm));
@ -699,7 +698,7 @@ void DSPEmitter::addi(const UDSPInstruction opc)
get_long_acc(areg, tmp1);
MOV(64, R(RAX), R(tmp1));
// s64 imm = (s16)dsp_fetch_code();
s16 imm = dsp_imem_read(m_compile_pc + 1);
const s16 imm = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
// imm <<= 16;
MOV(64, R(RDX), Imm32(imm << 16));
// s64 res = acc + imm;

View File

@ -6,7 +6,6 @@
#include "Core/DSP/DSPAnalyzer.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/DSPTables.h"
#include "Core/DSP/Jit/x64/DSPEmitter.h"
@ -126,7 +125,7 @@ void DSPEmitter::WriteBlockLink(u16 dest)
void DSPEmitter::r_jcc(const UDSPInstruction opc)
{
u16 dest = dsp_imem_read(m_compile_pc + 1);
const u16 dest = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
const DSPOPCTemplate* opcode = GetOpTemplate(opc);
// If the block is unconditional, attempt to link block
@ -172,7 +171,7 @@ void DSPEmitter::r_call(const UDSPInstruction opc)
{
MOV(16, R(DX), Imm16(m_compile_pc + 2));
dsp_reg_store_stack(StackRegister::Call);
u16 dest = dsp_imem_read(m_compile_pc + 1);
const u16 dest = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
const DSPOPCTemplate* opcode = GetOpTemplate(opc);
// If the block is unconditional, attempt to link block
@ -228,8 +227,9 @@ void DSPEmitter::r_ifcc(const UDSPInstruction opc)
// NOTE: Cannot use FallBackToInterpreter(opc) here because of the need to write branch exit
void DSPEmitter::ifcc(const UDSPInstruction opc)
{
const auto& state = m_dsp_core.DSPState();
const u16 address = m_compile_pc + 1;
const DSPOPCTemplate* const op_template = GetOpTemplate(dsp_imem_read(address));
const DSPOPCTemplate* const op_template = GetOpTemplate(state.ReadIMEM(address));
MOV(16, M_SDSP_pc(), Imm16(address + op_template->size));
ReJitConditional(opc, &DSPEmitter::r_ifcc);
@ -347,7 +347,8 @@ void DSPEmitter::loop(const UDSPInstruction opc)
SetJumpTarget(cnt);
// dsp_skip_inst();
MOV(16, M_SDSP_pc(), Imm16(loop_pc + GetOpTemplate(dsp_imem_read(loop_pc))->size));
const auto& state = m_dsp_core.DSPState();
MOV(16, M_SDSP_pc(), Imm16(loop_pc + GetOpTemplate(state.ReadIMEM(loop_pc))->size));
WriteBranchExit();
m_gpr.FlushRegs(c, false);
SetJumpTarget(exit);
@ -380,7 +381,8 @@ void DSPEmitter::loopi(const UDSPInstruction opc)
else
{
// dsp_skip_inst();
MOV(16, M_SDSP_pc(), Imm16(loop_pc + GetOpTemplate(dsp_imem_read(loop_pc))->size));
const auto& state = m_dsp_core.DSPState();
MOV(16, M_SDSP_pc(), Imm16(loop_pc + GetOpTemplate(state.ReadIMEM(loop_pc))->size));
WriteBranchExit();
}
}
@ -396,11 +398,11 @@ void DSPEmitter::loopi(const UDSPInstruction opc)
// Up to 4 nested loops are allowed.
void DSPEmitter::bloop(const UDSPInstruction opc)
{
u16 reg = opc & 0x1f;
const u16 reg = opc & 0x1f;
// u16 cnt = g_dsp.r[reg];
// todo: check if we can use normal variant here
dsp_op_read_reg_dont_saturate(reg, RDX, RegisterExtension::Zero);
u16 loop_pc = dsp_imem_read(m_compile_pc + 1);
const u16 loop_pc = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
TEST(16, R(EDX), R(EDX));
DSPJitRegCache c(m_gpr);
@ -417,7 +419,8 @@ void DSPEmitter::bloop(const UDSPInstruction opc)
SetJumpTarget(cnt);
// g_dsp.pc = loop_pc;
// dsp_skip_inst();
MOV(16, M_SDSP_pc(), Imm16(loop_pc + GetOpTemplate(dsp_imem_read(loop_pc))->size));
const auto& state = m_dsp_core.DSPState();
MOV(16, M_SDSP_pc(), Imm16(loop_pc + GetOpTemplate(state.ReadIMEM(loop_pc))->size));
WriteBranchExit();
m_gpr.FlushRegs(c, false);
SetJumpTarget(exit);
@ -434,11 +437,12 @@ void DSPEmitter::bloop(const UDSPInstruction opc)
// nested loops are allowed.
void DSPEmitter::bloopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
const auto& state = m_dsp_core.DSPState();
const u16 cnt = opc & 0xff;
// u16 loop_pc = dsp_fetch_code();
u16 loop_pc = dsp_imem_read(m_compile_pc + 1);
const u16 loop_pc = state.ReadIMEM(m_compile_pc + 1);
if (cnt)
if (cnt != 0)
{
MOV(16, R(RDX), Imm16(m_compile_pc + 2));
dsp_reg_store_stack(StackRegister::Call);
@ -453,7 +457,7 @@ void DSPEmitter::bloopi(const UDSPInstruction opc)
{
// g_dsp.pc = loop_pc;
// dsp_skip_inst();
MOV(16, M_SDSP_pc(), Imm16(loop_pc + GetOpTemplate(dsp_imem_read(loop_pc))->size));
MOV(16, M_SDSP_pc(), Imm16(loop_pc + GetOpTemplate(state.ReadIMEM(loop_pc))->size));
WriteBranchExit();
}
}

View File

@ -7,7 +7,6 @@
#include "Common/CommonTypes.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/Jit/x64/DSPEmitter.h"
using namespace Gen;
@ -65,8 +64,8 @@ void DSPEmitter::lrs(const UDSPInstruction opc)
// Move value from data memory pointed by address M to register $D.
void DSPEmitter::lr(const UDSPInstruction opc)
{
int reg = opc & 0x1F;
u16 address = dsp_imem_read(m_compile_pc + 1);
const int reg = opc & 0x1F;
const u16 address = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
dmem_read_imm(address);
dsp_op_write_reg(reg, EAX);
dsp_conditional_extend_accum(reg);
@ -78,10 +77,10 @@ void DSPEmitter::lr(const UDSPInstruction opc)
// Store value from register $S to a memory pointed by address M.
void DSPEmitter::sr(const UDSPInstruction opc)
{
u8 reg = opc & 0x1F;
u16 address = dsp_imem_read(m_compile_pc + 1);
const u8 reg = opc & 0x1F;
const u16 address = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
X64Reg tmp1 = m_gpr.GetFreeXReg();
const X64Reg tmp1 = m_gpr.GetFreeXReg();
dsp_op_read_reg(reg, tmp1);
dmem_write_imm(address, tmp1);
@ -96,10 +95,10 @@ void DSPEmitter::sr(const UDSPInstruction opc)
// M (M is 8-bit value sign extended).
void DSPEmitter::si(const UDSPInstruction opc)
{
u16 address = (s8)opc;
u16 imm = dsp_imem_read(m_compile_pc + 1);
const u16 address = static_cast<s8>(opc);
const u16 imm = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
X64Reg tmp1 = m_gpr.GetFreeXReg();
const X64Reg tmp1 = m_gpr.GetFreeXReg();
MOV(32, R(tmp1), Imm32((u32)imm));
dmem_write_imm(address, tmp1);

View File

@ -5,7 +5,6 @@
#include "Common/CommonTypes.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/Jit/x64/DSPEmitter.h"
using namespace Gen;
@ -36,8 +35,8 @@ void DSPEmitter::mrr(const UDSPInstruction opc)
// S16 mode.
void DSPEmitter::lri(const UDSPInstruction opc)
{
u8 reg = opc & 0x1F;
u16 imm = dsp_imem_read(m_compile_pc + 1);
const u8 reg = opc & 0x1F;
const u16 imm = m_dsp_core.DSPState().ReadIMEM(m_compile_pc + 1);
dsp_op_write_reg_imm(reg, imm);
dsp_conditional_extend_accum_imm(reg, imm);
}

View File

@ -11,7 +11,6 @@
#include "Common/Logging/Log.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/Jit/x64/DSPEmitter.h"
using namespace Gen;

View File

@ -6,13 +6,22 @@
#include "Common/Logging/Log.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPHWInterface.h"
#include "Core/DSP/Jit/x64/DSPEmitter.h"
using namespace Gen;
namespace DSP::JIT::x64
{
u16 DSPEmitter::ReadIFXRegisterHelper(DSPEmitter& emitter, u16 address)
{
return emitter.m_dsp_core.DSPState().ReadIFX(address);
}
void DSPEmitter::WriteIFXRegisterHelper(DSPEmitter& emitter, u16 address, u16 value)
{
emitter.m_dsp_core.DSPState().WriteIFX(address, value);
}
// clobbers:
// EAX = (s8)g_dsp.reg_stack_ptrs[reg_index]
// expects:
@ -32,7 +41,7 @@ void DSPEmitter::dsp_reg_stack_push(StackRegister stack_reg)
// g_dsp.reg_stack[reg_index][g_dsp.reg_stack_ptrs[reg_index]] = g_dsp.r[DSP_REG_ST0 + reg_index];
MOV(16, R(tmp1), M_SDSP_r_st(reg_index));
MOVZX(64, 8, RAX, R(AL));
MOV(64, R(tmp2), ImmPtr(g_dsp.reg_stacks[reg_index]));
MOV(64, R(tmp2), ImmPtr(m_dsp_core.DSPState().reg_stacks[reg_index]));
MOV(16, MComplex(tmp2, EAX, SCALE_2, 0), R(tmp1));
m_gpr.PutXReg(tmp1);
m_gpr.PutXReg(tmp2);
@ -50,7 +59,7 @@ void DSPEmitter::dsp_reg_stack_pop(StackRegister stack_reg)
X64Reg tmp1 = m_gpr.GetFreeXReg();
X64Reg tmp2 = m_gpr.GetFreeXReg();
MOVZX(64, 8, RAX, R(AL));
MOV(64, R(tmp2), ImmPtr(g_dsp.reg_stacks[reg_index]));
MOV(64, R(tmp2), ImmPtr(m_dsp_core.DSPState().reg_stacks[reg_index]));
MOV(16, R(tmp1), MComplex(tmp2, EAX, SCALE_2, 0));
MOV(16, M_SDSP_r_st(reg_index), R(tmp1));
m_gpr.PutXReg(tmp1);
@ -520,7 +529,7 @@ void DSPEmitter::dmem_write(X64Reg value)
// g_dsp.dram[addr & DSP_DRAM_MASK] = val;
AND(16, R(EAX), Imm16(DSP_DRAM_MASK));
MOV(64, R(ECX), ImmPtr(g_dsp.dram));
MOV(64, R(ECX), ImmPtr(m_dsp_core.DSPState().dram));
MOV(16, MComplex(ECX, EAX, SCALE_2, 0), R(value));
FixupBranch end = J(true);
@ -530,7 +539,7 @@ void DSPEmitter::dmem_write(X64Reg value)
X64Reg abisafereg = m_gpr.MakeABICallSafe(value);
MOVZX(32, 16, abisafereg, R(abisafereg));
m_gpr.PushRegs();
ABI_CallFunctionRR(gdsp_ifx_write, EAX, abisafereg);
ABI_CallFunctionPRR(WriteIFXRegisterHelper, this, EAX, abisafereg);
m_gpr.PopRegs();
m_gpr.FlushRegs(c);
SetJumpTarget(end);
@ -541,7 +550,7 @@ void DSPEmitter::dmem_write_imm(u16 address, X64Reg value)
switch (address >> 12)
{
case 0x0: // 0xxx DRAM
MOV(64, R(RDX), ImmPtr(g_dsp.dram));
MOV(64, R(RDX), ImmPtr(m_dsp_core.DSPState().dram));
MOV(16, MDisp(RDX, (address & DSP_DRAM_MASK) * 2), R(value));
break;
@ -550,12 +559,13 @@ void DSPEmitter::dmem_write_imm(u16 address, X64Reg value)
MOV(16, R(EAX), Imm16(address));
X64Reg abisafereg = m_gpr.MakeABICallSafe(value);
m_gpr.PushRegs();
ABI_CallFunctionRR(gdsp_ifx_write, EAX, abisafereg);
ABI_CallFunctionPRR(WriteIFXRegisterHelper, this, EAX, abisafereg);
m_gpr.PopRegs();
break;
}
default: // Unmapped/non-existing memory
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Write to UNKNOWN ({:04x}) memory", g_dsp.pc, address);
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Write to UNKNOWN ({:04x}) memory",
m_dsp_core.DSPState().pc, address);
break;
}
}
@ -570,7 +580,7 @@ void DSPEmitter::imem_read(X64Reg address)
FixupBranch irom = J_CC(CC_A);
// return g_dsp.iram[addr & DSP_IRAM_MASK];
AND(16, R(address), Imm16(DSP_IRAM_MASK));
MOV(64, R(ECX), ImmPtr(g_dsp.iram));
MOV(64, R(ECX), ImmPtr(m_dsp_core.DSPState().iram));
MOV(16, R(EAX), MComplex(ECX, address, SCALE_2, 0));
FixupBranch end = J();
@ -578,7 +588,7 @@ void DSPEmitter::imem_read(X64Reg address)
// else if (addr == 0x8)
// return g_dsp.irom[addr & DSP_IROM_MASK];
AND(16, R(address), Imm16(DSP_IROM_MASK));
MOV(64, R(ECX), ImmPtr(g_dsp.irom));
MOV(64, R(ECX), ImmPtr(m_dsp_core.DSPState().irom));
MOV(16, R(EAX), MComplex(ECX, address, SCALE_2, 0));
SetJumpTarget(end);
@ -594,7 +604,7 @@ void DSPEmitter::dmem_read(X64Reg address)
FixupBranch dram = J_CC(CC_A);
// return g_dsp.dram[addr & DSP_DRAM_MASK];
AND(32, R(address), Imm32(DSP_DRAM_MASK));
MOV(64, R(ECX), ImmPtr(g_dsp.dram));
MOV(64, R(ECX), ImmPtr(m_dsp_core.DSPState().dram));
MOV(16, R(EAX), MComplex(ECX, address, SCALE_2, 0));
FixupBranch end = J(true);
@ -604,7 +614,7 @@ void DSPEmitter::dmem_read(X64Reg address)
FixupBranch ifx = J_CC(CC_A);
// return g_dsp.coef[addr & DSP_COEF_MASK];
AND(32, R(address), Imm32(DSP_COEF_MASK));
MOV(64, R(ECX), ImmPtr(g_dsp.coef));
MOV(64, R(ECX), ImmPtr(m_dsp_core.DSPState().coef));
MOV(16, R(EAX), MComplex(ECX, address, SCALE_2, 0));
FixupBranch end2 = J(true);
@ -614,7 +624,7 @@ void DSPEmitter::dmem_read(X64Reg address)
DSPJitRegCache c(m_gpr);
X64Reg abisafereg = m_gpr.MakeABICallSafe(address);
m_gpr.PushRegs();
ABI_CallFunctionR(gdsp_ifx_read, abisafereg);
ABI_CallFunctionPR(ReadIFXRegisterHelper, this, abisafereg);
m_gpr.PopRegs();
m_gpr.FlushRegs(c);
SetJumpTarget(end);
@ -626,24 +636,25 @@ void DSPEmitter::dmem_read_imm(u16 address)
switch (address >> 12)
{
case 0x0: // 0xxx DRAM
MOV(64, R(RDX), ImmPtr(g_dsp.dram));
MOV(64, R(RDX), ImmPtr(m_dsp_core.DSPState().dram));
MOV(16, R(EAX), MDisp(RDX, (address & DSP_DRAM_MASK) * 2));
break;
case 0x1: // 1xxx COEF
MOV(64, R(RDX), ImmPtr(g_dsp.coef));
MOV(64, R(RDX), ImmPtr(m_dsp_core.DSPState().coef));
MOV(16, R(EAX), MDisp(RDX, (address & DSP_COEF_MASK) * 2));
break;
case 0xf: // Fxxx HW regs
{
m_gpr.PushRegs();
ABI_CallFunctionC16(gdsp_ifx_read, address);
ABI_CallFunctionPC(ReadIFXRegisterHelper, this, address);
m_gpr.PopRegs();
break;
}
default: // Unmapped/non-existing memory
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Read from UNKNOWN ({:04x}) memory", g_dsp.pc, address);
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Read from UNKNOWN ({:04x}) memory",
m_dsp_core.DSPState().pc, address);
}
}

View File

@ -1,323 +0,0 @@
// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/HW/DSPLLE/DSPDebugInterface.h"
#include <array>
#include <cstddef>
#include <string>
#include <fmt/format.h>
#include "Common/MsgHandler.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/HW/DSPLLE/DSPSymbols.h"
namespace DSP::LLE
{
void DSPPatches::Patch(std::size_t index)
{
PanicAlertFmt("Patch functionality not supported in DSP module.");
}
DSPDebugInterface::DSPDebugInterface() = default;
DSPDebugInterface::~DSPDebugInterface() = default;
std::size_t DSPDebugInterface::SetWatch(u32 address, std::string name)
{
return m_watches.SetWatch(address, std::move(name));
}
const Common::Debug::Watch& DSPDebugInterface::GetWatch(std::size_t index) const
{
return m_watches.GetWatch(index);
}
const std::vector<Common::Debug::Watch>& DSPDebugInterface::GetWatches() const
{
return m_watches.GetWatches();
}
void DSPDebugInterface::UnsetWatch(u32 address)
{
m_watches.UnsetWatch(address);
}
void DSPDebugInterface::UpdateWatch(std::size_t index, u32 address, std::string name)
{
return m_watches.UpdateWatch(index, address, std::move(name));
}
void DSPDebugInterface::UpdateWatchAddress(std::size_t index, u32 address)
{
return m_watches.UpdateWatchAddress(index, address);
}
void DSPDebugInterface::UpdateWatchName(std::size_t index, std::string name)
{
return m_watches.UpdateWatchName(index, std::move(name));
}
void DSPDebugInterface::EnableWatch(std::size_t index)
{
m_watches.EnableWatch(index);
}
void DSPDebugInterface::DisableWatch(std::size_t index)
{
m_watches.DisableWatch(index);
}
bool DSPDebugInterface::HasEnabledWatch(u32 address) const
{
return m_watches.HasEnabledWatch(address);
}
void DSPDebugInterface::RemoveWatch(std::size_t index)
{
return m_watches.RemoveWatch(index);
}
void DSPDebugInterface::LoadWatchesFromStrings(const std::vector<std::string>& watches)
{
m_watches.LoadFromStrings(watches);
}
std::vector<std::string> DSPDebugInterface::SaveWatchesToStrings() const
{
return m_watches.SaveToStrings();
}
void DSPDebugInterface::ClearWatches()
{
m_watches.Clear();
}
void DSPDebugInterface::SetPatch(u32 address, u32 value)
{
m_patches.SetPatch(address, value);
}
void DSPDebugInterface::SetPatch(u32 address, std::vector<u8> value)
{
m_patches.SetPatch(address, std::move(value));
}
const std::vector<Common::Debug::MemoryPatch>& DSPDebugInterface::GetPatches() const
{
return m_patches.GetPatches();
}
void DSPDebugInterface::UnsetPatch(u32 address)
{
m_patches.UnsetPatch(address);
}
void DSPDebugInterface::EnablePatch(std::size_t index)
{
m_patches.EnablePatch(index);
}
void DSPDebugInterface::DisablePatch(std::size_t index)
{
m_patches.DisablePatch(index);
}
void DSPDebugInterface::RemovePatch(std::size_t index)
{
m_patches.RemovePatch(index);
}
bool DSPDebugInterface::HasEnabledPatch(u32 address) const
{
return m_patches.HasEnabledPatch(address);
}
void DSPDebugInterface::ClearPatches()
{
m_patches.ClearPatches();
}
Common::Debug::Threads DSPDebugInterface::GetThreads() const
{
return {};
}
std::string DSPDebugInterface::Disassemble(u32 address) const
{
// we'll treat addresses as line numbers.
return Symbols::GetLineText(address);
}
std::string DSPDebugInterface::GetRawMemoryString(int memory, u32 address) const
{
if (DSPCore_GetState() == State::Stopped)
return "";
switch (memory)
{
case 0: // IMEM
switch (address >> 12)
{
case 0:
case 0x8:
return fmt::format("{:04x}", dsp_imem_read(address));
default:
return "--IMEM--";
}
case 1: // DMEM
switch (address >> 12)
{
case 0:
case 1:
return fmt::format("{:04x} (DMEM)", dsp_dmem_read(address));
case 0xf:
return fmt::format("{:04x} (MMIO)", g_dsp.ifx_regs[address & 0xFF]);
default:
return "--DMEM--";
}
}
return "";
}
u32 DSPDebugInterface::ReadMemory(u32 address) const
{
return 0;
}
u32 DSPDebugInterface::ReadInstruction(u32 address) const
{
return 0;
}
bool DSPDebugInterface::IsAlive() const
{
return true;
}
bool DSPDebugInterface::IsBreakpoint(u32 address) const
{
int real_addr = Symbols::Line2Addr(address);
if (real_addr >= 0)
return g_dsp_breakpoints.IsAddressBreakPoint(real_addr);
return false;
}
void DSPDebugInterface::SetBreakpoint(u32 address)
{
int real_addr = Symbols::Line2Addr(address);
if (real_addr >= 0)
{
g_dsp_breakpoints.Add(real_addr);
}
}
void DSPDebugInterface::ClearBreakpoint(u32 address)
{
int real_addr = Symbols::Line2Addr(address);
if (real_addr >= 0)
{
g_dsp_breakpoints.Remove(real_addr);
}
}
void DSPDebugInterface::ClearAllBreakpoints()
{
g_dsp_breakpoints.Clear();
}
void DSPDebugInterface::ToggleBreakpoint(u32 address)
{
int real_addr = Symbols::Line2Addr(address);
if (real_addr >= 0)
{
if (g_dsp_breakpoints.IsAddressBreakPoint(real_addr))
g_dsp_breakpoints.Remove(real_addr);
else
g_dsp_breakpoints.Add(real_addr);
}
}
bool DSPDebugInterface::IsMemCheck(u32 address, size_t size) const
{
return false;
}
void DSPDebugInterface::ClearAllMemChecks()
{
PanicAlertFmt("MemCheck functionality not supported in DSP module.");
}
void DSPDebugInterface::ToggleMemCheck(u32 address, bool read, bool write, bool log)
{
PanicAlertFmt("MemCheck functionality not supported in DSP module.");
}
// =======================================================
// Separate the blocks with colors.
// -------------
u32 DSPDebugInterface::GetColor(u32 address) const
{
// 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 = Symbols::Line2Addr(address - i);
if (addr >= 0)
break;
}
if (addr == -1)
return 0xFFFFFF;
Common::Symbol* symbol = Symbols::g_dsp_symbol_db.GetSymbolFromAddr(addr);
if (!symbol)
return 0xFFFFFF;
if (symbol->type != Common::Symbol::Type::Function)
return 0xEEEEFF;
static constexpr std::array<u32, 6> colors{
0xd0FFFF, // light cyan
0xFFd0d0, // light red
0xd8d8FF, // light blue
0xFFd0FF, // light purple
0xd0FFd0, // light green
0xFFFFd0, // light yellow
};
return colors[symbol->index % colors.size()];
}
// =============
std::string DSPDebugInterface::GetDescription(u32 address) const
{
return ""; // g_symbolDB.GetDescription(address);
}
u32 DSPDebugInterface::GetPC() const
{
return Symbols::Addr2Line(DSP::g_dsp.pc);
}
void DSPDebugInterface::SetPC(u32 address)
{
int new_pc = Symbols::Line2Addr(address);
if (new_pc > 0)
g_dsp.pc = new_pc;
}
void DSPDebugInterface::RunToBreakpoint()
{
}
void DSPDebugInterface::Clear()
{
ClearPatches();
ClearWatches();
}
} // namespace DSP::LLE

View File

@ -1,85 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <string>
#include "Common/CommonTypes.h"
#include "Common/Debug/MemoryPatches.h"
#include "Common/Debug/Watches.h"
#include "Common/DebugInterface.h"
namespace DSP::LLE
{
class DSPPatches : public Common::Debug::MemoryPatches
{
private:
void Patch(std::size_t index) override;
};
class DSPDebugInterface final : public Common::DebugInterface
{
public:
DSPDebugInterface();
~DSPDebugInterface() override;
// Watches
std::size_t SetWatch(u32 address, std::string name = "") override;
const Common::Debug::Watch& GetWatch(std::size_t index) const override;
const std::vector<Common::Debug::Watch>& GetWatches() const override;
void UnsetWatch(u32 address) override;
void UpdateWatch(std::size_t index, u32 address, std::string name) override;
void UpdateWatchAddress(std::size_t index, u32 address) override;
void UpdateWatchName(std::size_t index, std::string name) override;
void EnableWatch(std::size_t index) override;
void DisableWatch(std::size_t index) override;
bool HasEnabledWatch(u32 address) const override;
void RemoveWatch(std::size_t index) override;
void LoadWatchesFromStrings(const std::vector<std::string>& watches) override;
std::vector<std::string> SaveWatchesToStrings() const override;
void ClearWatches() override;
// Memory Patches
void SetPatch(u32 address, u32 value) override;
void SetPatch(u32 address, std::vector<u8> value) override;
const std::vector<Common::Debug::MemoryPatch>& GetPatches() const override;
void UnsetPatch(u32 address) override;
void EnablePatch(std::size_t index) override;
void DisablePatch(std::size_t index) override;
void RemovePatch(std::size_t index) override;
bool HasEnabledPatch(u32 address) const override;
void ClearPatches() override;
// Threads
Common::Debug::Threads GetThreads() const override;
std::string Disassemble(u32 address) const override;
std::string GetRawMemoryString(int memory, u32 address) const override;
bool IsAlive() const override;
bool IsBreakpoint(u32 address) const override;
void SetBreakpoint(u32 address) override;
void ClearBreakpoint(u32 address) override;
void ClearAllBreakpoints() override;
void ToggleBreakpoint(u32 address) override;
void ClearAllMemChecks() override;
bool IsMemCheck(u32 address, size_t size) const override;
void ToggleMemCheck(u32 address, bool read = true, bool write = true, bool log = true) override;
u32 ReadMemory(u32 address) const override;
u32 ReadInstruction(u32 address) const override;
u32 GetPC() const override;
void SetPC(u32 address) override;
void Step() override {}
void RunToBreakpoint() override;
u32 GetColor(u32 address) const override;
std::string GetDescription(u32 address) const override;
void Clear() override;
private:
Common::Debug::Watches m_watches;
DSPPatches m_patches;
};
} // namespace DSP::LLE

View File

@ -68,31 +68,33 @@ void InterruptRequest()
DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP);
}
void CodeLoaded(u32 addr, size_t size)
void CodeLoaded(DSPCore& dsp, u32 addr, size_t size)
{
CodeLoaded(Memory::GetPointer(addr), size);
CodeLoaded(dsp, Memory::GetPointer(addr), size);
}
void CodeLoaded(const u8* ptr, size_t size)
void CodeLoaded(DSPCore& dsp, const u8* ptr, size_t size)
{
g_dsp.iram_crc = Common::HashEctor(ptr, size);
auto& state = dsp.DSPState();
const u32 iram_crc = Common::HashEctor(ptr, size);
state.iram_crc = iram_crc;
if (SConfig::GetInstance().m_DumpUCode)
{
DSP::DumpDSPCode(ptr, size, g_dsp.iram_crc);
DSP::DumpDSPCode(ptr, size, iram_crc);
}
NOTICE_LOG_FMT(DSPLLE, "g_dsp.iram_crc: {:08x}", g_dsp.iram_crc);
NOTICE_LOG_FMT(DSPLLE, "g_dsp.iram_crc: {:08x}", iram_crc);
Symbols::Clear();
Symbols::AutoDisassembly(0x0, 0x1000);
Symbols::AutoDisassembly(0x8000, 0x9000);
Symbols::AutoDisassembly(state, 0x0, 0x1000);
Symbols::AutoDisassembly(state, 0x8000, 0x9000);
UpdateDebugger();
if (g_dsp_jit)
g_dsp_jit->ClearIRAM();
dsp.ClearIRAM();
Analyzer::Analyze();
Analyzer::Analyze(state);
}
void UpdateDebugger()

View File

@ -21,7 +21,6 @@
#include "Core/DSP/DSPAccelerator.h"
#include "Core/DSP/DSPCaptureLogger.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPHWInterface.h"
#include "Core/DSP/DSPHost.h"
#include "Core/DSP/DSPTables.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"
@ -32,15 +31,11 @@
namespace DSP::LLE
{
static Common::Event s_dsp_event;
static Common::Event s_ppc_event;
static bool s_request_disable_thread;
DSPLLE::DSPLLE() = default;
DSPLLE::~DSPLLE()
{
DSPCore_Shutdown();
m_dsp_core.Shutdown();
DSP_StopSoundStream();
}
@ -55,39 +50,8 @@ void DSPLLE::DoState(PointerWrap& p)
p.SetMode(PointerWrap::MODE_VERIFY);
return;
}
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_ptrs);
p.Do(g_dsp.exceptions);
p.Do(g_dsp.external_interrupt_waiting);
for (auto& stack : g_dsp.reg_stacks)
{
p.Do(stack);
}
p.Do(g_dsp.step_counter);
p.DoArray(g_dsp.ifx_regs);
g_dsp.accelerator->DoState(p);
p.Do(g_dsp.mbox[0]);
p.Do(g_dsp.mbox[1]);
Common::UnWriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
p.DoArray(g_dsp.iram, DSP_IRAM_SIZE);
Common::WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
// TODO: This uses the wrong endianness (producing bad disassembly)
// and a bogus byte count (producing bad hashes)
if (p.GetMode() == PointerWrap::MODE_READ)
Host::CodeLoaded(reinterpret_cast<const u8*>(g_dsp.iram), DSP_IRAM_BYTE_SIZE);
p.DoArray(g_dsp.dram, DSP_DRAM_SIZE);
p.Do(g_init_hax);
m_dsp_core.DoState(p);
p.Do(m_cycle_count);
if (g_dsp_jit)
g_dsp_jit->DoState(p);
}
// Regular thread
@ -103,21 +67,21 @@ void DSPLLE::DSPThread(DSPLLE* dsp_lle)
std::unique_lock dsp_thread_lock(dsp_lle->m_dsp_thread_mutex, std::try_to_lock);
if (dsp_thread_lock)
{
if (g_dsp_jit)
if (dsp_lle->m_dsp_core.IsJITCreated())
{
DSPCore_RunCycles(cycles);
dsp_lle->m_dsp_core.RunCycles(cycles);
}
else
{
DSP::Interpreter::RunCyclesThread(cycles);
dsp_lle->m_dsp_core.GetInterpreter().RunCyclesThread(cycles);
}
dsp_lle->m_cycle_count.store(0);
continue;
}
}
s_ppc_event.Set();
s_dsp_event.Wait();
dsp_lle->m_ppc_event.Set();
dsp_lle->m_dsp_event.Wait();
}
}
@ -173,22 +137,22 @@ static bool FillDSPInitOptions(DSPInitOptions* opts)
bool DSPLLE::Initialize(bool wii, bool dsp_thread)
{
s_request_disable_thread = false;
m_request_disable_thread = false;
DSPInitOptions opts;
if (!FillDSPInitOptions(&opts))
return false;
if (!DSPCore_Init(opts))
if (!m_dsp_core.Initialize(opts))
return false;
// needs to be after DSPCore_Init for the dspjit ptr
if (Core::WantsDeterminism() || !g_dsp_jit)
if (Core::WantsDeterminism() || !m_dsp_core.IsJITCreated())
dsp_thread = false;
m_wii = wii;
m_is_dsp_on_thread = dsp_thread;
DSPCore_Reset();
m_dsp_core.Reset();
InitInstructionTable();
@ -204,77 +168,70 @@ bool DSPLLE::Initialize(bool wii, bool dsp_thread)
void DSPLLE::DSP_StopSoundStream()
{
if (m_is_dsp_on_thread)
{
m_is_running.Clear();
s_ppc_event.Set();
s_dsp_event.Set();
m_dsp_thread.join();
}
if (!m_is_dsp_on_thread)
return;
m_is_running.Clear();
m_ppc_event.Set();
m_dsp_event.Set();
m_dsp_thread.join();
}
void DSPLLE::Shutdown()
{
DSPCore_Shutdown();
m_dsp_core.Shutdown();
}
u16 DSPLLE::DSP_WriteControlRegister(u16 value)
{
DSP::Interpreter::WriteCR(value);
m_dsp_core.GetInterpreter().WriteCR(value);
if (value & 2)
if ((value & 2) != 0)
{
if (!m_is_dsp_on_thread)
{
DSPCore_CheckExternalInterrupt();
DSPCore_CheckExceptions();
}
else
if (m_is_dsp_on_thread)
{
// External interrupt pending: this is the zelda ucode.
// Disable the DSP thread because there is no performance gain.
s_request_disable_thread = true;
m_request_disable_thread = true;
DSPCore_SetExternalInterrupt(true);
m_dsp_core.SetExternalInterrupt(true);
}
else
{
m_dsp_core.CheckExternalInterrupt();
m_dsp_core.CheckExceptions();
}
}
return DSP::Interpreter::ReadCR();
return DSP_ReadControlRegister();
}
u16 DSPLLE::DSP_ReadControlRegister()
{
return DSP::Interpreter::ReadCR();
return m_dsp_core.GetInterpreter().ReadCR();
}
u16 DSPLLE::DSP_ReadMailBoxHigh(bool cpu_mailbox)
{
return gdsp_mbox_read_h(cpu_mailbox ? MAILBOX_CPU : MAILBOX_DSP);
return m_dsp_core.ReadMailboxHigh(cpu_mailbox ? MAILBOX_CPU : MAILBOX_DSP);
}
u16 DSPLLE::DSP_ReadMailBoxLow(bool cpu_mailbox)
{
return gdsp_mbox_read_l(cpu_mailbox ? MAILBOX_CPU : MAILBOX_DSP);
return m_dsp_core.ReadMailboxLow(cpu_mailbox ? MAILBOX_CPU : MAILBOX_DSP);
}
void DSPLLE::DSP_WriteMailBoxHigh(bool cpu_mailbox, u16 value)
{
if (cpu_mailbox)
{
if (gdsp_mbox_peek(MAILBOX_CPU) & 0x80000000)
if ((m_dsp_core.PeekMailbox(MAILBOX_CPU) & 0x80000000) != 0)
{
// the DSP didn't read the previous value
WARN_LOG_FMT(DSPLLE, "Mailbox isn't empty ... strange");
}
#if PROFILE
if (value == 0xBABE)
{
ProfilerStart();
}
#endif
gdsp_mbox_write_h(MAILBOX_CPU, value);
m_dsp_core.WriteMailboxHigh(MAILBOX_CPU, value);
}
else
{
@ -286,7 +243,7 @@ void DSPLLE::DSP_WriteMailBoxLow(bool cpu_mailbox, u16 value)
{
if (cpu_mailbox)
{
gdsp_mbox_write_l(MAILBOX_CPU, value);
m_dsp_core.WriteMailboxLow(MAILBOX_CPU, value);
}
else
{
@ -296,18 +253,18 @@ void DSPLLE::DSP_WriteMailBoxLow(bool cpu_mailbox, u16 value)
void DSPLLE::DSP_Update(int cycles)
{
int dsp_cycles = cycles / 6;
const int dsp_cycles = cycles / 6;
if (dsp_cycles <= 0)
return;
if (m_is_dsp_on_thread)
{
if (s_request_disable_thread || Core::WantsDeterminism())
if (m_request_disable_thread || Core::WantsDeterminism())
{
DSP_StopSoundStream();
m_is_dsp_on_thread = false;
s_request_disable_thread = false;
m_request_disable_thread = false;
SConfig::GetInstance().bDSPThread = false;
}
}
@ -316,14 +273,14 @@ void DSPLLE::DSP_Update(int cycles)
if (!m_is_dsp_on_thread)
{
// ~1/6th as many cycles as the period PPC-side.
DSPCore_RunCycles(dsp_cycles);
m_dsp_core.RunCycles(dsp_cycles);
}
else
{
// Wait for DSP thread to complete its cycle. Note: this logic should be thought through.
s_ppc_event.Wait();
m_ppc_event.Wait();
m_cycle_count.fetch_add(dsp_cycles);
s_dsp_event.Set();
m_dsp_event.Set();
}
}
@ -345,8 +302,8 @@ void DSPLLE::PauseAndLock(bool do_lock, bool unpause_on_unlock)
if (m_is_dsp_on_thread)
{
// Signal the DSP thread so it can perform any outstanding work now (if any)
s_ppc_event.Wait();
s_dsp_event.Set();
m_ppc_event.Wait();
m_dsp_event.Set();
}
}
}

View File

@ -10,6 +10,7 @@
#include "Common/CommonTypes.h"
#include "Common/Flag.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSPEmulator.h"
class PointerWrap;
@ -41,10 +42,15 @@ public:
private:
static void DSPThread(DSPLLE* dsp_lle);
DSPCore m_dsp_core;
std::thread m_dsp_thread;
std::mutex m_dsp_thread_mutex;
bool m_is_dsp_on_thread = false;
Common::Flag m_is_running;
std::atomic<u32> m_cycle_count{};
Common::Event m_dsp_event;
Common::Event m_ppc_event;
bool m_request_disable_thread = false;
};
} // namespace DSP::LLE

View File

@ -69,7 +69,7 @@ Common::Symbol* DSPSymbolDB::GetSymbolFromAddr(u32 addr)
return nullptr;
}
void AutoDisassembly(u16 start_addr, u16 end_addr)
void AutoDisassembly(const SDSP& dsp, u16 start_addr, u16 end_addr)
{
AssemblerSettings settings;
settings.show_pc = true;
@ -77,7 +77,7 @@ void AutoDisassembly(u16 start_addr, u16 end_addr)
DSPDisassembler disasm(settings);
u16 addr = start_addr;
const u16* ptr = (start_addr >> 15) ? g_dsp.irom : g_dsp.iram;
const u16* ptr = (start_addr >> 15) != 0 ? dsp.irom : dsp.iram;
while (addr < end_addr)
{
line_to_addr[line_counter] = addr;

View File

@ -9,6 +9,11 @@
#include "Common/CommonTypes.h"
#include "Common/SymbolDB.h"
namespace DSP
{
struct SDSP;
}
namespace DSP::Symbols
{
class DSPSymbolDB : public Common::SymbolDB
@ -21,7 +26,7 @@ public:
extern DSPSymbolDB g_dsp_symbol_db;
void AutoDisassembly(u16 start_addr, u16 end_addr);
void AutoDisassembly(const SDSP& dsp, u16 start_addr, u16 end_addr);
void Clear();

View File

@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system
constexpr u32 STATE_VERSION = 125; // Last changed in PR 8867
constexpr u32 STATE_VERSION = 126; // Last changed in PR 9348
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,

View File

@ -39,10 +39,10 @@ bool DSP::Host::IsWiiHost()
{
return false;
}
void DSP::Host::CodeLoaded(u32 addr, size_t size)
void DSP::Host::CodeLoaded(DSPCore& dsp, u32 addr, size_t size)
{
}
void DSP::Host::CodeLoaded(const u8* ptr, size_t size)
void DSP::Host::CodeLoaded(DSPCore& dsp, const u8* ptr, size_t size)
{
}
void DSP::Host::InterruptRequest()