Merge pull request #9348 from lioncash/dsp-deglobal

DSP: Eliminate most global state
This commit is contained in:
Léo Lam 2020-12-28 15:48:11 +01:00 committed by GitHub
commit 3f68aceaca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 3198 additions and 3248 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 SDSP& dsp)
{
struct DspRomHashes
{
@ -64,8 +59,10 @@ 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 u32 hash_irom =
Common::HashAdler32(reinterpret_cast<const u8*>(dsp.irom), DSP_IROM_BYTE_SIZE);
const u32 hash_drom =
Common::HashAdler32(reinterpret_cast<const u8*>(dsp.coef), DSP_COEF_BYTE_SIZE);
int rom_idx = -1;
for (size_t i = 0; i < known_roms.size(); ++i)
@ -104,151 +101,141 @@ 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(SDSP& dsp) : m_dsp{dsp} {}
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_dsp.SetException(ExceptionType::AcceleratorOverflow); }
private:
SDSP& m_dsp;
};
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));
bool SDSP::Initialize(const DSPInitOptions& opts)
{
step_counter = 0;
accelerator = std::make_unique<LLEAccelerator>(*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);
irom = static_cast<u16*>(Common::AllocateMemoryPages(DSP_IROM_BYTE_SIZE));
iram = static_cast<u16*>(Common::AllocateMemoryPages(DSP_IRAM_BYTE_SIZE));
dram = static_cast<u16*>(Common::AllocateMemoryPages(DSP_DRAM_BYTE_SIZE));
coef = static_cast<u16*>(Common::AllocateMemoryPages(DSP_COEF_BYTE_SIZE));
std::memcpy(irom, opts.irom_contents.data(), DSP_IROM_BYTE_SIZE);
std::memcpy(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(&r, 0, sizeof(r));
std::fill(std::begin(g_dsp.reg_stack_ptrs), std::end(g_dsp.reg_stack_ptrs), 0);
std::fill(std::begin(reg_stack_ptrs), std::end(reg_stack_ptrs), 0);
for (auto& stack : g_dsp.reg_stacks)
for (auto& stack : 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(iram, iram + DSP_IRAM_SIZE, 0x0021);
// Just zero out DRAM.
std::fill(g_dsp.dram, g_dsp.dram + DSP_DRAM_SIZE, 0);
std::fill(dram, 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(r.wr), std::end(r.wr), 0xffff);
g_dsp.r.sr |= SR_INT_ENABLE;
g_dsp.r.sr |= SR_EXT_INT_ENABLE;
r.sr |= SR_INT_ENABLE;
r.sr |= SR_EXT_INT_ENABLE;
g_dsp.cr = 0x804;
gdsp_ifx_init();
cr = 0x804;
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);
// Initialize JIT, if necessary
if (opts.core_type == DSPInitOptions::CoreType::JIT64)
g_dsp_jit = JIT::CreateDSPEmitter();
g_dsp_cap.reset(opts.capture_logger);
core_state = State::Running;
Common::WriteProtectMemory(iram, DSP_IRAM_BYTE_SIZE, false);
return true;
}
void DSPCore_Shutdown()
void SDSP::Reset()
{
if (core_state == State::Stopped)
return;
core_state = State::Stopped;
g_dsp_jit.reset();
DSPCore_FreeMemoryPages();
g_dsp_cap.reset();
pc = DSP_RESET_VECTOR;
std::fill(std::begin(r.wr), std::end(r.wr), 0xffff);
}
void DSPCore_Reset()
void SDSP::Shutdown()
{
g_dsp.pc = DSP_RESET_VECTOR;
std::fill(std::begin(g_dsp.r.wr), std::end(g_dsp.r.wr), 0xffff);
Analyzer::Analyze();
FreeMemoryPages();
}
void DSPCore_SetException(ExceptionType exception)
void SDSP::FreeMemoryPages()
{
g_dsp.exceptions |= 1 << static_cast<std::underlying_type_t<ExceptionType>>(exception);
Common::FreeMemoryPages(irom, DSP_IROM_BYTE_SIZE);
Common::FreeMemoryPages(iram, DSP_IRAM_BYTE_SIZE);
Common::FreeMemoryPages(dram, DSP_DRAM_BYTE_SIZE);
Common::FreeMemoryPages(coef, DSP_COEF_BYTE_SIZE);
irom = nullptr;
iram = nullptr;
dram = nullptr;
coef = nullptr;
}
// Notify that an external interrupt is pending (used by thread mode)
void DSPCore_SetExternalInterrupt(bool val)
void SDSP::SetException(ExceptionType exception)
{
g_dsp.external_interrupt_waiting = val;
exceptions |= 1 << static_cast<std::underlying_type_t<ExceptionType>>(exception);
}
// Coming from the CPU
void DSPCore_CheckExternalInterrupt()
void SDSP::SetExternalInterrupt(bool val)
{
if (!Interpreter::dsp_SR_is_flag_set(SR_EXT_INT_ENABLE))
external_interrupt_waiting = val;
}
void SDSP::CheckExternalInterrupt()
{
if (!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;
cr &= ~CR_EXTERNAL_INT;
}
void DSPCore_CheckExceptions()
void SDSP::CheckExceptions()
{
// Early out to skip the loop in the common case.
if (g_dsp.exceptions == 0)
if (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 ((exceptions & (1U << i)) != 0)
{
if (Interpreter::dsp_SR_is_flag_set(SR_INT_ENABLE) ||
i == static_cast<int>(ExceptionType::ExternalInterrupt))
if (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);
StoreStack(StackRegister::Call, pc);
StoreStack(StackRegister::Data, r.sr);
g_dsp.pc = i * 2;
g_dsp.exceptions &= ~(1 << i);
pc = static_cast<u16>(i * 2);
exceptions &= ~(1 << i);
if (i == 7)
g_dsp.r.sr &= ~SR_EXT_INT_ENABLE;
r.sr &= ~SR_EXT_INT_ENABLE;
else
g_dsp.r.sr &= ~SR_INT_ENABLE;
r.sr &= ~SR_INT_ENABLE;
break;
}
else
@ -261,34 +248,225 @@ void DSPCore_CheckExceptions()
}
}
u16 SDSP::ReadRegister(size_t reg) const
{
switch (reg)
{
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
return r.ar[reg - DSP_REG_AR0];
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
return r.ix[reg - DSP_REG_IX0];
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
return r.wr[reg - DSP_REG_WR0];
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
return r.st[reg - DSP_REG_ST0];
case DSP_REG_ACH0:
case DSP_REG_ACH1:
return r.ac[reg - DSP_REG_ACH0].h;
case DSP_REG_CR:
return r.cr;
case DSP_REG_SR:
return r.sr;
case DSP_REG_PRODL:
return r.prod.l;
case DSP_REG_PRODM:
return r.prod.m;
case DSP_REG_PRODH:
return r.prod.h;
case DSP_REG_PRODM2:
return r.prod.m2;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
return r.ax[reg - DSP_REG_AXL0].l;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
return r.ax[reg - DSP_REG_AXH0].h;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
return r.ac[reg - DSP_REG_ACL0].l;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
return r.ac[reg - DSP_REG_ACM0].m;
default:
ASSERT_MSG(DSP_CORE, 0, "cannot happen");
return 0;
}
}
void SDSP::WriteRegister(size_t reg, u16 val)
{
switch (reg)
{
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
r.ar[reg - DSP_REG_AR0] = val;
break;
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
r.ix[reg - DSP_REG_IX0] = val;
break;
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
r.wr[reg - DSP_REG_WR0] = val;
break;
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
r.st[reg - DSP_REG_ST0] = val;
break;
case DSP_REG_ACH0:
case DSP_REG_ACH1:
r.ac[reg - DSP_REG_ACH0].h = val;
break;
case DSP_REG_CR:
r.cr = val;
break;
case DSP_REG_SR:
r.sr = val;
break;
case DSP_REG_PRODL:
r.prod.l = val;
break;
case DSP_REG_PRODM:
r.prod.m = val;
break;
case DSP_REG_PRODH:
r.prod.h = val;
break;
case DSP_REG_PRODM2:
r.prod.m2 = val;
break;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
r.ax[reg - DSP_REG_AXL0].l = val;
break;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
r.ax[reg - DSP_REG_AXH0].h = val;
break;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
r.ac[reg - DSP_REG_ACL0].l = val;
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
r.ac[reg - DSP_REG_ACM0].m = val;
break;
}
}
void SDSP::DoState(PointerWrap& p)
{
p.Do(r);
p.Do(pc);
p.Do(cr);
p.Do(reg_stack_ptrs);
p.Do(exceptions);
p.Do(external_interrupt_waiting);
for (auto& stack : reg_stacks)
{
p.Do(stack);
}
p.Do(step_counter);
p.DoArray(ifx_regs);
accelerator->DoState(p);
p.Do(mbox[0]);
p.Do(mbox[1]);
Common::UnWriteProtectMemory(iram, DSP_IRAM_BYTE_SIZE, false);
p.DoArray(iram, DSP_IRAM_SIZE);
Common::WriteProtectMemory(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(m_dsp_core, reinterpret_cast<const u8*>(iram), DSP_IRAM_BYTE_SIZE);
p.DoArray(dram, DSP_DRAM_SIZE);
}
DSPCore::DSPCore()
: m_dsp{*this}, m_dsp_interpreter{std::make_unique<Interpreter::Interpreter>(*this)}
{
}
DSPCore::~DSPCore() = default;
bool DSPCore::Initialize(const DSPInitOptions& opts)
{
if (!m_dsp.Initialize(opts))
return false;
m_init_hax = false;
// Initialize JIT, if necessary
if (opts.core_type == DSPInitOptions::CoreType::JIT64)
m_dsp_jit = JIT::CreateDSPEmitter(*this);
m_dsp_cap.reset(opts.capture_logger);
m_core_state = State::Running;
return true;
}
void DSPCore::Shutdown()
{
if (m_core_state == State::Stopped)
return;
m_core_state = State::Stopped;
m_dsp_jit.reset();
m_dsp.Shutdown();
m_dsp_cap.reset();
}
// Delegate to JIT or interpreter as appropriate.
// Handle state changes and stepping.
int DSPCore_RunCycles(int cycles)
int DSPCore::RunCycles(int cycles)
{
if (g_dsp_jit)
if (m_dsp_jit)
{
return g_dsp_jit->RunCycles(static_cast<u16>(cycles));
return m_dsp_jit->RunCycles(static_cast<u16>(cycles));
}
while (cycles > 0)
{
switch (core_state)
switch (m_core_state)
{
case State::Running:
// Seems to slow things down
#if defined(_DEBUG) || defined(DEBUGFAST)
cycles = Interpreter::RunCyclesDebug(cycles);
cycles = m_dsp_interpreter->RunCyclesDebug(cycles);
#else
cycles = Interpreter::RunCycles(cycles);
cycles = m_dsp_interpreter->RunCycles(cycles);
#endif
break;
case State::Stepping:
step_event.Wait();
if (core_state != State::Stepping)
m_step_event.Wait();
if (m_core_state != State::Stepping)
continue;
Interpreter::Step();
m_dsp_interpreter->Step();
cycles--;
Host::UpdateDebugger();
@ -300,151 +478,123 @@ int DSPCore_RunCycles(int cycles)
return cycles;
}
void DSPCore_SetState(State new_state)
void DSPCore::Step()
{
core_state = new_state;
if (m_core_state == State::Stepping)
m_step_event.Set();
}
void DSPCore::Reset()
{
m_dsp.Reset();
Analyzer::Analyze(m_dsp);
}
void DSPCore::ClearIRAM()
{
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)
step_event.Set();
m_step_event.Set();
Host::UpdateDebugger();
}
State DSPCore_GetState()
State DSPCore::GetState() const
{
return core_state;
return m_core_state;
}
void DSPCore_Step()
void DSPCore::SetException(ExceptionType exception)
{
if (core_state == State::Stepping)
step_event.Set();
m_dsp.SetException(exception);
}
u16 DSPCore_ReadRegister(size_t reg)
void DSPCore::SetExternalInterrupt(bool val)
{
switch (reg)
{
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_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
return g_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;
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_CORE, 0, "cannot happen");
return 0;
}
m_dsp.SetExternalInterrupt(val);
}
void DSPCore_WriteRegister(size_t reg, u16 val)
void DSPCore::CheckExternalInterrupt()
{
switch (reg)
{
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_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
g_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;
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;
}
m_dsp.CheckExternalInterrupt();
}
void DSPCore::CheckExceptions()
{
m_dsp.CheckExceptions();
}
u16 DSPCore::ReadRegister(size_t reg) const
{
return m_dsp.ReadRegister(reg);
}
void DSPCore::WriteRegister(size_t reg, u16 val)
{
m_dsp.WriteRegister(reg, val);
}
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)
{
m_dsp.DoState(p);
p.Do(m_init_hax);
if (m_dsp_jit)
m_dsp_jit->DoState(p);
}
} // 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];
@ -259,61 +274,6 @@ struct DSP_Regs
} ac[2];
};
// All the state of the DSP should be in this struct. Any DSP state that is not filled on init
// should be moved here.
struct SDSP
{
DSP_Regs r;
u16 pc;
#if PROFILE
u16 err_pc;
#endif
// 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;
u8 reg_stack_ptrs[4];
u8 exceptions; // pending exceptions
volatile bool external_interrupt_waiting;
bool reset_dspjit_codespace;
// DSP hardware stacks. They're mapped to a bunch of registers, such that writes
// to them push and reads pop.
// Let's make stack depth 32 for now, which is way more than what's needed.
// 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];
// For debugging.
u32 iram_crc;
u64 step_counter;
// Mailbox.
std::atomic<u32> mbox[2];
// Accelerator / DMA / other hardware registers. Not GPRs.
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;
};
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;
struct DSPInitOptions
{
// DSP IROM blob, which is where the DSP boots from. Embedded into the DSP.
@ -338,19 +298,158 @@ 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);
// All the state of the DSP should be in this struct. Any DSP state that is not filled on init
// should be moved here.
struct SDSP
{
explicit SDSP(DSPCore& core);
~SDSP();
void DSPCore_Reset();
void DSPCore_Shutdown(); // Frees all allocated memory.
SDSP(const SDSP&) = delete;
SDSP& operator=(const SDSP&) = delete;
void DSPCore_CheckExternalInterrupt();
void DSPCore_CheckExceptions();
void DSPCore_SetExternalInterrupt(bool val);
SDSP(SDSP&&) = delete;
SDSP& operator=(SDSP&&) = delete;
// sets a flag in the pending exception register.
void DSPCore_SetException(ExceptionType exception);
// Initializes overall state.
bool Initialize(const DSPInitOptions& opts);
// Shuts down any necessary DSP state.
void Shutdown();
// Resets DSP state as if the reset exception vector has been taken.
void Reset();
// 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; }
// Indicates that a particular exception has occurred
// and sets a flag in the pending exception register.
void SetException(ExceptionType exception);
// Checks if any exceptions occurred an updates the DSP state as appropriate.
void CheckExceptions();
// Notify that an external interrupt is pending (used by thread mode)
void SetExternalInterrupt(bool val);
// Coming from the CPU
void CheckExternalInterrupt();
// 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);
// 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);
// Saves and loads any necessary state.
void DoState(PointerWrap& p);
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 = 0;
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.
// Let's make stack depth 32 for now, which is way more than what's needed.
// 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]{};
// For debugging.
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::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 = nullptr;
u16* dram = nullptr;
u16* irom = nullptr;
u16* coef = nullptr;
private:
void FreeMemoryPages();
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;
};
enum class State
{
@ -359,14 +458,115 @@ 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:
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()