mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-24 22:59:47 -06:00
Merge pull request #318 from delroth/dsp-pcap
Add support for PCAP logging of DSP DMA/MMIO access
This commit is contained in:
@ -14,6 +14,7 @@ set(SRCS BreakPoints.cpp
|
|||||||
MsgHandler.cpp
|
MsgHandler.cpp
|
||||||
NandPaths.cpp
|
NandPaths.cpp
|
||||||
Network.cpp
|
Network.cpp
|
||||||
|
PcapFile.cpp
|
||||||
SettingsHandler.cpp
|
SettingsHandler.cpp
|
||||||
SDCardUtil.cpp
|
SDCardUtil.cpp
|
||||||
StringUtil.cpp
|
StringUtil.cpp
|
||||||
|
@ -78,6 +78,7 @@
|
|||||||
<ClInclude Include="MsgHandler.h" />
|
<ClInclude Include="MsgHandler.h" />
|
||||||
<ClInclude Include="NandPaths.h" />
|
<ClInclude Include="NandPaths.h" />
|
||||||
<ClInclude Include="Network.h" />
|
<ClInclude Include="Network.h" />
|
||||||
|
<ClInclude Include="PcapFile.h" />
|
||||||
<ClInclude Include="SDCardUtil.h" />
|
<ClInclude Include="SDCardUtil.h" />
|
||||||
<ClInclude Include="SettingsHandler.h" />
|
<ClInclude Include="SettingsHandler.h" />
|
||||||
<ClInclude Include="stdafx.h" />
|
<ClInclude Include="stdafx.h" />
|
||||||
@ -114,6 +115,7 @@
|
|||||||
<ClCompile Include="MsgHandler.cpp" />
|
<ClCompile Include="MsgHandler.cpp" />
|
||||||
<ClCompile Include="NandPaths.cpp" />
|
<ClCompile Include="NandPaths.cpp" />
|
||||||
<ClCompile Include="Network.cpp" />
|
<ClCompile Include="Network.cpp" />
|
||||||
|
<ClCompile Include="PcapFile.cpp" />
|
||||||
<ClCompile Include="SDCardUtil.cpp" />
|
<ClCompile Include="SDCardUtil.cpp" />
|
||||||
<ClCompile Include="SettingsHandler.cpp" />
|
<ClCompile Include="SettingsHandler.cpp" />
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
<ClInclude Include="MsgHandler.h" />
|
<ClInclude Include="MsgHandler.h" />
|
||||||
<ClInclude Include="NandPaths.h" />
|
<ClInclude Include="NandPaths.h" />
|
||||||
<ClInclude Include="Network.h" />
|
<ClInclude Include="Network.h" />
|
||||||
|
<ClInclude Include="PcapFile.h" />
|
||||||
<ClInclude Include="SDCardUtil.h" />
|
<ClInclude Include="SDCardUtil.h" />
|
||||||
<ClInclude Include="SettingsHandler.h" />
|
<ClInclude Include="SettingsHandler.h" />
|
||||||
<ClInclude Include="StdConditionVariable.h" />
|
<ClInclude Include="StdConditionVariable.h" />
|
||||||
@ -86,6 +87,7 @@
|
|||||||
<ClCompile Include="MsgHandler.cpp" />
|
<ClCompile Include="MsgHandler.cpp" />
|
||||||
<ClCompile Include="NandPaths.cpp" />
|
<ClCompile Include="NandPaths.cpp" />
|
||||||
<ClCompile Include="Network.cpp" />
|
<ClCompile Include="Network.cpp" />
|
||||||
|
<ClCompile Include="PcapFile.cpp" />
|
||||||
<ClCompile Include="SDCardUtil.cpp" />
|
<ClCompile Include="SDCardUtil.cpp" />
|
||||||
<ClCompile Include="SettingsHandler.cpp" />
|
<ClCompile Include="SettingsHandler.cpp" />
|
||||||
<ClCompile Include="StringUtil.cpp" />
|
<ClCompile Include="StringUtil.cpp" />
|
||||||
|
66
Source/Core/Common/PcapFile.cpp
Normal file
66
Source/Core/Common/PcapFile.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2014 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/FileUtil.h"
|
||||||
|
#include "Common/PcapFile.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const u32 PCAP_MAGIC = 0xa1b2c3d4;
|
||||||
|
const u16 PCAP_VERSION_MAJOR = 2;
|
||||||
|
const u16 PCAP_VERSION_MINOR = 4;
|
||||||
|
const u32 PCAP_CAPTURE_LENGTH = 65535;
|
||||||
|
|
||||||
|
// TODO(delroth): Make this configurable at PCAP creation time?
|
||||||
|
const u32 PCAP_DATA_LINK_TYPE = 147; // Reserved for internal use.
|
||||||
|
|
||||||
|
// Designed to be directly written into the PCAP file. The PCAP format is
|
||||||
|
// endian independent, so this works just fine.
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct PCAPHeader
|
||||||
|
{
|
||||||
|
u32 magic_number;
|
||||||
|
u16 version_major;
|
||||||
|
u16 version_minor;
|
||||||
|
s32 tz_offset; // Offset in seconds from the GMT timezone.
|
||||||
|
u32 ts_accuracy; // In practice, 0.
|
||||||
|
u32 capture_length; // Size at which we truncate packets.
|
||||||
|
u32 data_link_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PCAPRecordHeader
|
||||||
|
{
|
||||||
|
u32 ts_sec;
|
||||||
|
u32 ts_usec;
|
||||||
|
u32 size_in_file; // Size after eventual truncation.
|
||||||
|
u32 real_size; // Size before eventual truncation.
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void PCAP::AddHeader()
|
||||||
|
{
|
||||||
|
PCAPHeader hdr = {
|
||||||
|
PCAP_MAGIC, PCAP_VERSION_MAJOR, PCAP_VERSION_MINOR,
|
||||||
|
0, 0, PCAP_CAPTURE_LENGTH, PCAP_DATA_LINK_TYPE
|
||||||
|
};
|
||||||
|
m_fp->WriteBytes(&hdr, sizeof (hdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PCAP::AddPacket(const u8* bytes, size_t size)
|
||||||
|
{
|
||||||
|
std::chrono::system_clock::time_point now(std::chrono::system_clock::now());
|
||||||
|
auto ts = now.time_since_epoch();
|
||||||
|
PCAPRecordHeader rec_hdr = {
|
||||||
|
(u32)std::chrono::duration_cast<std::chrono::seconds>(ts).count(),
|
||||||
|
(u32)(std::chrono::duration_cast<std::chrono::microseconds>(ts).count() % 1000000),
|
||||||
|
(u32)size, (u32)size
|
||||||
|
};
|
||||||
|
m_fp->WriteBytes(&rec_hdr, sizeof (rec_hdr));
|
||||||
|
m_fp->WriteBytes(bytes, size);
|
||||||
|
}
|
44
Source/Core/Common/PcapFile.h
Normal file
44
Source/Core/Common/PcapFile.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2014 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
// PCAP is a standard file format for network capture files. This also extends
|
||||||
|
// to any capture of packetized intercommunication data. This file provides a
|
||||||
|
// class called PCAP which is a very light wrapper around the file format,
|
||||||
|
// allowing only creating a new PCAP capture file and appending packets to it.
|
||||||
|
//
|
||||||
|
// Example use:
|
||||||
|
// PCAP pcap(new IOFile("test.pcap", "wb"));
|
||||||
|
// pcap.AddPacket(pkt); // pkt is automatically casted to u8*
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/FileUtil.h"
|
||||||
|
|
||||||
|
class PCAP final : public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Takes ownership of the file object. Assumes the file object is already
|
||||||
|
// opened in write mode.
|
||||||
|
explicit PCAP(File::IOFile* fp) : m_fp(fp)
|
||||||
|
{
|
||||||
|
AddHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void AddPacket(const T& obj)
|
||||||
|
{
|
||||||
|
AddPacket(reinterpret_cast<const u8*>(&obj), sizeof (obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddPacket(const u8* bytes, size_t size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AddHeader();
|
||||||
|
|
||||||
|
std::unique_ptr<File::IOFile> m_fp;
|
||||||
|
};
|
@ -29,6 +29,7 @@ set(SRCS ActionReplay.cpp
|
|||||||
DSP/DSPAssembler.cpp
|
DSP/DSPAssembler.cpp
|
||||||
DSP/DSPDisassembler.cpp
|
DSP/DSPDisassembler.cpp
|
||||||
DSP/DSPAccelerator.cpp
|
DSP/DSPAccelerator.cpp
|
||||||
|
DSP/DSPCaptureLogger.cpp
|
||||||
DSP/DSPIntCCUtil.cpp
|
DSP/DSPIntCCUtil.cpp
|
||||||
DSP/DSPIntExtOps.cpp
|
DSP/DSPIntExtOps.cpp
|
||||||
DSP/DSPHWInterface.cpp
|
DSP/DSPHWInterface.cpp
|
||||||
|
@ -326,6 +326,7 @@ void SConfig::SaveDSPSettings(IniFile& ini)
|
|||||||
dsp->Set("DumpAudio", m_DumpAudio);
|
dsp->Set("DumpAudio", m_DumpAudio);
|
||||||
dsp->Set("Backend", sBackend);
|
dsp->Set("Backend", sBackend);
|
||||||
dsp->Set("Volume", m_Volume);
|
dsp->Set("Volume", m_Volume);
|
||||||
|
dsp->Set("CaptureLog", m_DSPCaptureLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SConfig::SaveFifoPlayerSettings(IniFile& ini)
|
void SConfig::SaveFifoPlayerSettings(IniFile& ini)
|
||||||
@ -552,6 +553,7 @@ void SConfig::LoadDSPSettings(IniFile& ini)
|
|||||||
dsp->Get("Backend", &sBackend, BACKEND_NULLSOUND);
|
dsp->Get("Backend", &sBackend, BACKEND_NULLSOUND);
|
||||||
#endif
|
#endif
|
||||||
dsp->Get("Volume", &m_Volume, 100);
|
dsp->Get("Volume", &m_Volume, 100);
|
||||||
|
dsp->Get("CaptureLog", &m_DSPCaptureLog, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SConfig::LoadFifoPlayerSettings(IniFile& ini)
|
void SConfig::LoadFifoPlayerSettings(IniFile& ini)
|
||||||
|
@ -92,6 +92,7 @@ struct SConfig : NonCopyable
|
|||||||
|
|
||||||
// DSP settings
|
// DSP settings
|
||||||
bool m_DSPEnableJIT;
|
bool m_DSPEnableJIT;
|
||||||
|
bool m_DSPCaptureLog;
|
||||||
bool m_DumpAudio;
|
bool m_DumpAudio;
|
||||||
int m_Volume;
|
int m_Volume;
|
||||||
std::string sBackend;
|
std::string sBackend;
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
<ClCompile Include="DSP\DSPDisassembler.cpp" />
|
<ClCompile Include="DSP\DSPDisassembler.cpp" />
|
||||||
<ClCompile Include="DSP\DSPAccelerator.cpp" />
|
<ClCompile Include="DSP\DSPAccelerator.cpp" />
|
||||||
<ClCompile Include="DSP\DSPAnalyzer.cpp" />
|
<ClCompile Include="DSP\DSPAnalyzer.cpp" />
|
||||||
|
<ClCompile Include="DSP\DSPCaptureLogger.cpp" />
|
||||||
<ClCompile Include="DSP\DSPCodeUtil.cpp" />
|
<ClCompile Include="DSP\DSPCodeUtil.cpp" />
|
||||||
<ClCompile Include="DSP\DSPCore.cpp" />
|
<ClCompile Include="DSP\DSPCore.cpp" />
|
||||||
<ClCompile Include="DSP\DSPEmitter.cpp" />
|
<ClCompile Include="DSP\DSPEmitter.cpp" />
|
||||||
@ -268,6 +269,7 @@
|
|||||||
<ClInclude Include="DSP\DSPAccelerator.h" />
|
<ClInclude Include="DSP\DSPAccelerator.h" />
|
||||||
<ClInclude Include="DSP\DSPAnalyzer.h" />
|
<ClInclude Include="DSP\DSPAnalyzer.h" />
|
||||||
<ClInclude Include="DSP\DSPBreakpoints.h" />
|
<ClInclude Include="DSP\DSPBreakpoints.h" />
|
||||||
|
<ClInclude Include="DSP\DSPCaptureLogger.h" />
|
||||||
<ClInclude Include="DSP\DSPCodeUtil.h" />
|
<ClInclude Include="DSP\DSPCodeUtil.h" />
|
||||||
<ClInclude Include="DSP\DSPCommon.h" />
|
<ClInclude Include="DSP\DSPCommon.h" />
|
||||||
<ClInclude Include="DSP\DSPCore.h" />
|
<ClInclude Include="DSP\DSPCore.h" />
|
||||||
|
@ -515,6 +515,9 @@
|
|||||||
<ClCompile Include="DSP\DSPAnalyzer.cpp">
|
<ClCompile Include="DSP\DSPAnalyzer.cpp">
|
||||||
<Filter>DSPCore</Filter>
|
<Filter>DSPCore</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="DSP\DSPCaptureLogger.cpp">
|
||||||
|
<Filter>DSPCore</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="DSP\DSPCodeUtil.cpp">
|
<ClCompile Include="DSP\DSPCodeUtil.cpp">
|
||||||
<Filter>DSPCore</Filter>
|
<Filter>DSPCore</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -1041,6 +1044,9 @@
|
|||||||
<ClInclude Include="DSP\DSPBreakpoints.h">
|
<ClInclude Include="DSP\DSPBreakpoints.h">
|
||||||
<Filter>DSPCore</Filter>
|
<Filter>DSPCore</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="DSP\DSPCaptureLogger.h">
|
||||||
|
<Filter>DSPCore</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="DSP\DSPCodeUtil.h">
|
<ClInclude Include="DSP\DSPCodeUtil.h">
|
||||||
<Filter>DSPCore</Filter>
|
<Filter>DSPCore</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
79
Source/Core/Core/DSP/DSPCaptureLogger.cpp
Normal file
79
Source/Core/Core/DSP/DSPCaptureLogger.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2014 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/FileUtil.h"
|
||||||
|
#include "Common/PcapFile.h"
|
||||||
|
|
||||||
|
#include "Core/DSP/DSPCaptureLogger.h"
|
||||||
|
|
||||||
|
// Definition of the packet structures stored in PCAP capture files.
|
||||||
|
|
||||||
|
const u8 IFX_ACCESS_PACKET_MAGIC = 0;
|
||||||
|
const u8 DMA_PACKET_MAGIC = 1;
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct IFXAccessPacket
|
||||||
|
{
|
||||||
|
u8 magic; // IFX_ACCESS_PACKET_MAGIC
|
||||||
|
u8 is_read; // 0 for writes, 1 for reads.
|
||||||
|
u16 address;
|
||||||
|
u16 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Followed by the bytes of the DMA.
|
||||||
|
struct DMAPacket
|
||||||
|
{
|
||||||
|
u8 magic; // DMA_PACKET_MAGIC
|
||||||
|
u16 dma_control; // Value of the DMA control register.
|
||||||
|
u32 gc_address; // Address in the GC RAM.
|
||||||
|
u16 dsp_address; // Address in the DSP RAM.
|
||||||
|
u16 length; // Length in bytes.
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
PCAPDSPCaptureLogger::PCAPDSPCaptureLogger(const std::string& pcap_filename)
|
||||||
|
: m_pcap(new PCAP(new File::IOFile(pcap_filename, "wb")))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PCAPDSPCaptureLogger::PCAPDSPCaptureLogger(PCAP* pcap) : m_pcap(pcap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PCAPDSPCaptureLogger::PCAPDSPCaptureLogger(std::unique_ptr<PCAP>&& pcap)
|
||||||
|
: m_pcap(std::move(pcap))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PCAPDSPCaptureLogger::LogIFXAccess(bool read, u16 address, u16 value)
|
||||||
|
{
|
||||||
|
IFXAccessPacket pkt;
|
||||||
|
pkt.magic = IFX_ACCESS_PACKET_MAGIC;
|
||||||
|
pkt.is_read = !!read; // Make sure we actually have 0/1.
|
||||||
|
pkt.address = address;
|
||||||
|
pkt.value = value;
|
||||||
|
|
||||||
|
m_pcap->AddPacket(pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PCAPDSPCaptureLogger::LogDMA(u16 control, u32 gc_address, u16 dsp_address,
|
||||||
|
u16 length, const u8* data)
|
||||||
|
{
|
||||||
|
// The length of a DMA cannot be above 64K, so we use a static buffer for
|
||||||
|
// the construction of the packet.
|
||||||
|
static u8 buffer[0x10000];
|
||||||
|
|
||||||
|
DMAPacket* pkt = reinterpret_cast<DMAPacket*>(&buffer[0]);
|
||||||
|
pkt->magic = DMA_PACKET_MAGIC;
|
||||||
|
pkt->dma_control = control;
|
||||||
|
pkt->gc_address = gc_address;
|
||||||
|
pkt->dsp_address = dsp_address;
|
||||||
|
pkt->length = length;
|
||||||
|
memcpy(&buffer[sizeof (DMAPacket)], data, length);
|
||||||
|
|
||||||
|
m_pcap->AddPacket(buffer, sizeof (DMAPacket) + length);
|
||||||
|
}
|
76
Source/Core/Core/DSP/DSPCaptureLogger.h
Normal file
76
Source/Core/Core/DSP/DSPCaptureLogger.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright 2014 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "Common/Common.h"
|
||||||
|
|
||||||
|
class PCAP;
|
||||||
|
|
||||||
|
// An interface used to capture and log structured data about internal DSP
|
||||||
|
// data transfers.
|
||||||
|
//
|
||||||
|
// Note: these calls are done within the DSP emulator in critical paths and at
|
||||||
|
// a high frequency. Implementations must try and avoid blocking for too long.
|
||||||
|
class DSPCaptureLogger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Accesses (reads or writes) to memory mapped registers (external
|
||||||
|
// interface, also known as IFX). These are always 16 bits accesses.
|
||||||
|
virtual void LogIFXRead(u16 address, u16 read_value) = 0;
|
||||||
|
virtual void LogIFXWrite(u16 address, u16 written_value) = 0;
|
||||||
|
|
||||||
|
// DMAs to/from main memory from/to DSP memory. We let the interpretation
|
||||||
|
// of the "control" field to the layer that analyze the logs and do not
|
||||||
|
// perform our own interpretation of it. Thus there is only one call which
|
||||||
|
// is used for DRAM/IRAM in any direction (to/from DSP).
|
||||||
|
//
|
||||||
|
// Length is expressed in bytes, not DSP words.
|
||||||
|
virtual void LogDMA(u16 control, u32 gc_address, u16 dsp_address,
|
||||||
|
u16 length, const u8* data) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A dummy implementation of a capture logger that does nothing. This is the
|
||||||
|
// default implementation used by the DSP emulator.
|
||||||
|
//
|
||||||
|
// Can also be inherited from if you want to only override part of the methods.
|
||||||
|
class DefaultDSPCaptureLogger : public DSPCaptureLogger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void LogIFXRead(u16 address, u16 read_value) override {}
|
||||||
|
virtual void LogIFXWrite(u16 address, u16 written_value) override {}
|
||||||
|
virtual void LogDMA(u16 control, u32 gc_address, u16 dsp_address,
|
||||||
|
u16 length, const u8* data) override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A capture logger implementation that logs to PCAP files in a custom
|
||||||
|
// packet-based format.
|
||||||
|
class PCAPDSPCaptureLogger final : public DSPCaptureLogger, NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Automatically creates a writeable file (truncate existing file).
|
||||||
|
PCAPDSPCaptureLogger(const std::string& pcap_filename);
|
||||||
|
// Takes ownership of pcap.
|
||||||
|
PCAPDSPCaptureLogger(PCAP* pcap);
|
||||||
|
PCAPDSPCaptureLogger(std::unique_ptr<PCAP>&& pcap);
|
||||||
|
|
||||||
|
virtual void LogIFXRead(u16 address, u16 read_value) override
|
||||||
|
{
|
||||||
|
LogIFXAccess(true, address, read_value);
|
||||||
|
}
|
||||||
|
virtual void LogIFXWrite(u16 address, u16 written_value) override
|
||||||
|
{
|
||||||
|
LogIFXAccess(false, address, written_value);
|
||||||
|
}
|
||||||
|
virtual void LogDMA(u16 control, u32 gc_address, u16 dsp_address,
|
||||||
|
u16 length, const u8* data) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void LogIFXAccess(bool read, u16 address, u16 value);
|
||||||
|
|
||||||
|
std::unique_ptr<PCAP> m_pcap;
|
||||||
|
};
|
@ -42,40 +42,11 @@ DSPCoreState core_state = DSPCORE_STOP;
|
|||||||
u16 cyclesLeft = 0;
|
u16 cyclesLeft = 0;
|
||||||
bool init_hax = false;
|
bool init_hax = false;
|
||||||
DSPEmitter *dspjit = nullptr;
|
DSPEmitter *dspjit = nullptr;
|
||||||
|
std::unique_ptr<DSPCaptureLogger> g_dsp_cap;
|
||||||
Common::Event step_event;
|
Common::Event step_event;
|
||||||
|
|
||||||
static bool LoadRom(const std::string& fname, int size_in_words, u16 *rom)
|
|
||||||
{
|
|
||||||
File::IOFile pFile(fname, "rb");
|
|
||||||
const size_t size_in_bytes = size_in_words * sizeof(u16);
|
|
||||||
if (pFile)
|
|
||||||
{
|
|
||||||
pFile.ReadArray(rom, size_in_words);
|
|
||||||
pFile.Close();
|
|
||||||
|
|
||||||
// Byteswap the rom.
|
|
||||||
for (int i = 0; i < size_in_words; i++)
|
|
||||||
rom[i] = Common::swap16(rom[i]);
|
|
||||||
|
|
||||||
// Always keep ROMs write protected.
|
|
||||||
WriteProtectMemory(rom, size_in_bytes, false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
PanicAlertT(
|
|
||||||
"Failed to load DSP ROM:\t%s\n"
|
|
||||||
"\n"
|
|
||||||
"This file is required to use DSP LLE.\n"
|
|
||||||
"It is not included with Dolphin as it contains copyrighted data.\n"
|
|
||||||
"Use DSPSpy to dump the file from your physical console.\n"
|
|
||||||
"\n"
|
|
||||||
"You may use the DSP HLE engine which does not require ROM dumps.\n"
|
|
||||||
"(Choose it from the \"Audio\" tab of the configuration dialog.)", fname.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns false if the hash fails and the user hits "Yes"
|
// Returns false if the hash fails and the user hits "Yes"
|
||||||
static bool VerifyRoms(const std::string& irom_filename, const std::string& coef_filename)
|
static bool VerifyRoms()
|
||||||
{
|
{
|
||||||
struct DspRomHashes
|
struct DspRomHashes
|
||||||
{
|
{
|
||||||
@ -136,7 +107,7 @@ static void DSPCore_FreeMemoryPages()
|
|||||||
g_dsp.irom = g_dsp.iram = g_dsp.dram = g_dsp.coef = nullptr;
|
g_dsp.irom = g_dsp.iram = g_dsp.dram = g_dsp.coef = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DSPCore_Init(const std::string& irom_filename, const std::string& coef_filename, bool bUsingJIT)
|
bool DSPCore_Init(const DSPInitOptions& opts)
|
||||||
{
|
{
|
||||||
g_dsp.step_counter = 0;
|
g_dsp.step_counter = 0;
|
||||||
cyclesLeft = 0;
|
cyclesLeft = 0;
|
||||||
@ -148,14 +119,11 @@ bool DSPCore_Init(const std::string& irom_filename, const std::string& coef_file
|
|||||||
g_dsp.dram = (u16*)AllocateMemoryPages(DSP_DRAM_BYTE_SIZE);
|
g_dsp.dram = (u16*)AllocateMemoryPages(DSP_DRAM_BYTE_SIZE);
|
||||||
g_dsp.coef = (u16*)AllocateMemoryPages(DSP_COEF_BYTE_SIZE);
|
g_dsp.coef = (u16*)AllocateMemoryPages(DSP_COEF_BYTE_SIZE);
|
||||||
|
|
||||||
// Fill roms with zeros.
|
memcpy(g_dsp.irom, opts.irom_contents.data(), DSP_IROM_BYTE_SIZE);
|
||||||
memset(g_dsp.irom, 0, DSP_IROM_BYTE_SIZE);
|
memcpy(g_dsp.coef, opts.coef_contents.data(), DSP_COEF_BYTE_SIZE);
|
||||||
memset(g_dsp.coef, 0, DSP_COEF_BYTE_SIZE);
|
|
||||||
|
|
||||||
// Try to load real ROM contents.
|
// Try to load real ROM contents.
|
||||||
if (!LoadRom(irom_filename, DSP_IROM_SIZE, g_dsp.irom) ||
|
if (!VerifyRoms())
|
||||||
!LoadRom(coef_filename, DSP_COEF_SIZE, g_dsp.coef) ||
|
|
||||||
!VerifyRoms(irom_filename, coef_filename))
|
|
||||||
{
|
{
|
||||||
DSPCore_FreeMemoryPages();
|
DSPCore_FreeMemoryPages();
|
||||||
return false;
|
return false;
|
||||||
@ -201,9 +169,11 @@ bool DSPCore_Init(const std::string& irom_filename, const std::string& coef_file
|
|||||||
WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
||||||
|
|
||||||
// Initialize JIT, if necessary
|
// Initialize JIT, if necessary
|
||||||
if (bUsingJIT)
|
if (opts.core_type == DSPInitOptions::CORE_JIT)
|
||||||
dspjit = new DSPEmitter();
|
dspjit = new DSPEmitter();
|
||||||
|
|
||||||
|
g_dsp_cap.reset(opts.capture_logger);
|
||||||
|
|
||||||
core_state = DSPCORE_RUNNING;
|
core_state = DSPCORE_RUNNING;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -220,6 +190,8 @@ void DSPCore_Shutdown()
|
|||||||
dspjit = nullptr;
|
dspjit = nullptr;
|
||||||
}
|
}
|
||||||
DSPCore_FreeMemoryPages();
|
DSPCore_FreeMemoryPages();
|
||||||
|
|
||||||
|
g_dsp_cap.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSPCore_Reset()
|
void DSPCore_Reset()
|
||||||
|
@ -25,11 +25,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Common/Thread.h"
|
#include "Common/Thread.h"
|
||||||
|
|
||||||
#include "Core/DSP/DSPBreakpoints.h"
|
#include "Core/DSP/DSPBreakpoints.h"
|
||||||
|
#include "Core/DSP/DSPCaptureLogger.h"
|
||||||
#include "Core/DSP/DSPEmitter.h"
|
#include "Core/DSP/DSPEmitter.h"
|
||||||
|
|
||||||
#define DSP_IRAM_BYTE_SIZE 0x2000
|
#define DSP_IRAM_BYTE_SIZE 0x2000
|
||||||
@ -269,8 +272,39 @@ extern DSPBreakpoints dsp_breakpoints;
|
|||||||
extern DSPEmitter *dspjit;
|
extern DSPEmitter *dspjit;
|
||||||
extern u16 cyclesLeft;
|
extern u16 cyclesLeft;
|
||||||
extern bool init_hax;
|
extern bool init_hax;
|
||||||
|
extern std::unique_ptr<DSPCaptureLogger> g_dsp_cap;
|
||||||
|
|
||||||
bool DSPCore_Init(const std::string& irom_filename, const std::string& coef_filename, bool bUsingJIT);
|
struct DSPInitOptions
|
||||||
|
{
|
||||||
|
// DSP IROM blob, which is where the DSP boots from. Embedded into the DSP.
|
||||||
|
std::array<u16, DSP_IROM_SIZE> irom_contents;
|
||||||
|
|
||||||
|
// DSP DROM blob, which contains resampling coefficients.
|
||||||
|
std::array<u16, DSP_COEF_SIZE> coef_contents;
|
||||||
|
|
||||||
|
// Core used to emulate the DSP.
|
||||||
|
// Default: CORE_JIT.
|
||||||
|
enum CoreType
|
||||||
|
{
|
||||||
|
CORE_INTERPRETER,
|
||||||
|
CORE_JIT,
|
||||||
|
};
|
||||||
|
CoreType core_type;
|
||||||
|
|
||||||
|
// Optional capture logger used to log internal DSP data transfers.
|
||||||
|
// Default: dummy implementation, does nothing.
|
||||||
|
DSPCaptureLogger* capture_logger;
|
||||||
|
|
||||||
|
DSPInitOptions()
|
||||||
|
: core_type(CORE_JIT),
|
||||||
|
capture_logger(new DefaultDSPCaptureLogger())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initializes the DSP emulator using the provided options. Takes ownership of
|
||||||
|
// all the pointers contained in the options structure.
|
||||||
|
bool DSPCore_Init(const DSPInitOptions& opts);
|
||||||
|
|
||||||
void DSPCore_Reset();
|
void DSPCore_Reset();
|
||||||
void DSPCore_Shutdown(); // Frees all allocated memory.
|
void DSPCore_Shutdown(); // Frees all allocated memory.
|
||||||
|
@ -115,6 +115,8 @@ u16 gdsp_mbox_read_l(u8 mbx)
|
|||||||
|
|
||||||
void gdsp_ifx_write(u32 addr, u32 val)
|
void gdsp_ifx_write(u32 addr, u32 val)
|
||||||
{
|
{
|
||||||
|
g_dsp_cap->LogIFXWrite(addr, val);
|
||||||
|
|
||||||
switch (addr & 0xff)
|
switch (addr & 0xff)
|
||||||
{
|
{
|
||||||
case DSP_DIRQ:
|
case DSP_DIRQ:
|
||||||
@ -190,7 +192,7 @@ void gdsp_ifx_write(u32 addr, u32 val)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 gdsp_ifx_read(u16 addr)
|
static u16 _gdsp_ifx_read(u16 addr)
|
||||||
{
|
{
|
||||||
switch (addr & 0xff)
|
switch (addr & 0xff)
|
||||||
{
|
{
|
||||||
@ -235,7 +237,14 @@ u16 gdsp_ifx_read(u16 addr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size)
|
u16 gdsp_ifx_read(u16 addr)
|
||||||
|
{
|
||||||
|
u16 retval = _gdsp_ifx_read(addr);
|
||||||
|
g_dsp_cap->LogIFXRead(addr, retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const u8* gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size)
|
||||||
{
|
{
|
||||||
UnWriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
UnWriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
||||||
|
|
||||||
@ -249,11 +258,15 @@ static void gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size)
|
|||||||
DSPHost::CodeLoaded((const u8*)g_dsp.iram + dsp_addr, size);
|
DSPHost::CodeLoaded((const u8*)g_dsp.iram + dsp_addr, size);
|
||||||
|
|
||||||
NOTICE_LOG(DSPLLE, "*** Copy new UCode from 0x%08x to 0x%04x (crc: %8x)", addr, dsp_addr, g_dsp.iram_crc);
|
NOTICE_LOG(DSPLLE, "*** Copy new UCode from 0x%08x to 0x%04x (crc: %8x)", addr, dsp_addr, g_dsp.iram_crc);
|
||||||
|
|
||||||
|
return dst + dsp_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gdsp_idma_out(u16 dsp_addr, u32 addr, u32 size)
|
static const u8* gdsp_idma_out(u16 dsp_addr, u32 addr, u32 size)
|
||||||
{
|
{
|
||||||
ERROR_LOG(DSPLLE, "*** idma_out IRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
|
ERROR_LOG(DSPLLE, "*** idma_out IRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _M_SSE >= 0x301
|
#if _M_SSE >= 0x301
|
||||||
@ -261,7 +274,7 @@ static const __m128i s_mask = _mm_set_epi32(0x0E0F0C0DL, 0x0A0B0809L, 0x06070405
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: These should eat clock cycles.
|
// TODO: These should eat clock cycles.
|
||||||
static void gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size)
|
static const u8* gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size)
|
||||||
{
|
{
|
||||||
u8* dst = ((u8*)g_dsp.dram);
|
u8* dst = ((u8*)g_dsp.dram);
|
||||||
|
|
||||||
@ -282,9 +295,11 @@ static void gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
INFO_LOG(DSPLLE, "*** ddma_in RAM (0x%08x) -> DRAM_DSP (0x%04x) : size (0x%08x)", addr, dsp_addr / 2, size);
|
INFO_LOG(DSPLLE, "*** ddma_in RAM (0x%08x) -> DRAM_DSP (0x%04x) : size (0x%08x)", addr, dsp_addr / 2, size);
|
||||||
|
|
||||||
|
return dst + dsp_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gdsp_ddma_out(u16 dsp_addr, u32 addr, u32 size)
|
static const u8* gdsp_ddma_out(u16 dsp_addr, u32 addr, u32 size)
|
||||||
{
|
{
|
||||||
const u8* src = ((const u8*)g_dsp.dram);
|
const u8* src = ((const u8*)g_dsp.dram);
|
||||||
|
|
||||||
@ -306,6 +321,8 @@ static void gdsp_ddma_out(u16 dsp_addr, u32 addr, u32 size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
INFO_LOG(DSPLLE, "*** ddma_out DRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
|
INFO_LOG(DSPLLE, "*** ddma_out DRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
|
||||||
|
|
||||||
|
return src + dsp_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gdsp_do_dma()
|
static void gdsp_do_dma()
|
||||||
@ -323,22 +340,28 @@ static void gdsp_do_dma()
|
|||||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||||
DEBUG_LOG(DSPLLE, "DMA pc: %04x, Control: %04x, Address: %08x, DSP Address: %04x, Size: %04x", g_dsp.pc, ctl, addr, dsp_addr, len);
|
DEBUG_LOG(DSPLLE, "DMA pc: %04x, Control: %04x, Address: %08x, DSP Address: %04x, Size: %04x", g_dsp.pc, ctl, addr, dsp_addr, len);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const u8* copied_data_ptr;
|
||||||
switch (ctl & 0x3)
|
switch (ctl & 0x3)
|
||||||
{
|
{
|
||||||
case (DSP_CR_DMEM | DSP_CR_TO_CPU):
|
case (DSP_CR_DMEM | DSP_CR_TO_CPU):
|
||||||
gdsp_ddma_out(dsp_addr, addr, len);
|
copied_data_ptr = gdsp_ddma_out(dsp_addr, addr, len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (DSP_CR_DMEM | DSP_CR_FROM_CPU):
|
case (DSP_CR_DMEM | DSP_CR_FROM_CPU):
|
||||||
gdsp_ddma_in(dsp_addr, addr, len);
|
copied_data_ptr = gdsp_ddma_in(dsp_addr, addr, len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (DSP_CR_IMEM | DSP_CR_TO_CPU):
|
case (DSP_CR_IMEM | DSP_CR_TO_CPU):
|
||||||
gdsp_idma_out(dsp_addr, addr, len);
|
copied_data_ptr = gdsp_idma_out(dsp_addr, addr, len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (DSP_CR_IMEM | DSP_CR_FROM_CPU):
|
case (DSP_CR_IMEM | DSP_CR_FROM_CPU):
|
||||||
gdsp_idma_in(dsp_addr, addr, len);
|
copied_data_ptr = gdsp_idma_in(dsp_addr, addr, len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (copied_data_ptr)
|
||||||
|
g_dsp_cap->LogDMA(ctl, addr, dsp_addr, len, copied_data_ptr);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/Host.h"
|
#include "Core/Host.h"
|
||||||
|
#include "Core/DSP/DSPCaptureLogger.h"
|
||||||
#include "Core/DSP/DSPCore.h"
|
#include "Core/DSP/DSPCore.h"
|
||||||
#include "Core/DSP/DSPDisassembler.h"
|
#include "Core/DSP/DSPDisassembler.h"
|
||||||
#include "Core/DSP/DSPHost.h"
|
#include "Core/DSP/DSPHost.h"
|
||||||
@ -108,11 +109,28 @@ void DSPLLE::dsp_thread(DSPLLE *dsp_lle)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DSPLLE::Initialize(void *hWnd, bool bWii, bool bDSPThread)
|
static bool LoadDSPRom(u16* rom, const std::string& filename, u32 size_in_bytes)
|
||||||
{
|
{
|
||||||
m_bWii = bWii;
|
std::string bytes;
|
||||||
m_bDSPThread = bDSPThread;
|
if (!File::ReadFileToString(filename, bytes))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bytes.size() != size_in_bytes)
|
||||||
|
{
|
||||||
|
ERROR_LOG(DSPLLE, "%s has a wrong size (%u, expected %u)",
|
||||||
|
filename.c_str(), (u32)bytes.size(), size_in_bytes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u16* words = reinterpret_cast<const u16*>(bytes.c_str());
|
||||||
|
for (u32 i = 0; i < size_in_bytes / 2; ++i)
|
||||||
|
rom[i] = Common::swap16(words[i]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool FillDSPInitOptions(DSPInitOptions* opts)
|
||||||
|
{
|
||||||
std::string irom_file = File::GetUserPath(D_GCUSER_IDX) + DSP_IROM;
|
std::string irom_file = File::GetUserPath(D_GCUSER_IDX) + DSP_IROM;
|
||||||
std::string coef_file = File::GetUserPath(D_GCUSER_IDX) + DSP_COEF;
|
std::string coef_file = File::GetUserPath(D_GCUSER_IDX) + DSP_COEF;
|
||||||
|
|
||||||
@ -121,8 +139,32 @@ bool DSPLLE::Initialize(void *hWnd, bool bWii, bool bDSPThread)
|
|||||||
if (!File::Exists(coef_file))
|
if (!File::Exists(coef_file))
|
||||||
coef_file = File::GetSysDirectory() + GC_SYS_DIR DIR_SEP DSP_COEF;
|
coef_file = File::GetSysDirectory() + GC_SYS_DIR DIR_SEP DSP_COEF;
|
||||||
|
|
||||||
bool use_jit = SConfig::GetInstance().m_DSPEnableJIT;
|
if (!LoadDSPRom(opts->irom_contents.data(), irom_file, DSP_IROM_BYTE_SIZE))
|
||||||
if (!DSPCore_Init(irom_file, coef_file, use_jit))
|
return false;
|
||||||
|
if (!LoadDSPRom(opts->coef_contents.data(), coef_file, DSP_COEF_BYTE_SIZE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
opts->core_type = SConfig::GetInstance().m_DSPEnableJIT ?
|
||||||
|
DSPInitOptions::CORE_JIT : DSPInitOptions::CORE_INTERPRETER;
|
||||||
|
|
||||||
|
if (SConfig::GetInstance().m_DSPCaptureLog)
|
||||||
|
{
|
||||||
|
const std::string pcap_path = File::GetUserPath(D_DUMPDSP_IDX) + "dsp.pcap";
|
||||||
|
opts->capture_logger = new PCAPDSPCaptureLogger(pcap_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DSPLLE::Initialize(void *hWnd, bool bWii, bool bDSPThread)
|
||||||
|
{
|
||||||
|
m_bWii = bWii;
|
||||||
|
m_bDSPThread = bDSPThread;
|
||||||
|
|
||||||
|
DSPInitOptions opts;
|
||||||
|
if (!FillDSPInitOptions(&opts))
|
||||||
|
return false;
|
||||||
|
if (!DSPCore_Init(opts))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
g_dsp.cpu_ram = Memory::GetPointer(0);
|
g_dsp.cpu_ram = Memory::GetPointer(0);
|
||||||
|
Reference in New Issue
Block a user