DSP: Eliminate most global state

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

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

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

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

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

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

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

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

View File

@ -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();