From 0a51a80d91553786779a7391316993aa9fe3b76c Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 17 Feb 2024 00:39:55 +0200 Subject: [PATCH 01/14] Add DSP emulation interface Co-Authored-By: PSISP <12768103+psi-rockin@users.noreply.github.com> --- .gitmodules | 3 + CMakeLists.txt | 8 +- include/audio/dsp_core.hpp | 35 ++++++++ include/audio/null_core.hpp | 41 +++++++++ include/audio/teakra_core.hpp | 28 ++++++ include/emulator.hpp | 6 +- include/logger.hpp | 1 + include/memory.hpp | 6 +- src/core/audio/dsp_core.cpp | 21 +++++ src/core/audio/null_core.cpp | 129 ++++++++++++++++++++++++++++ src/core/audio/teakra_core.cpp | 150 +++++++++++++++++++++++++++++++++ src/core/memory.cpp | 1 - src/emulator.cpp | 2 + third_party/teakra | 1 + 14 files changed, 426 insertions(+), 6 deletions(-) create mode 100644 include/audio/dsp_core.hpp create mode 100644 include/audio/null_core.hpp create mode 100644 include/audio/teakra_core.hpp create mode 100644 src/core/audio/dsp_core.cpp create mode 100644 src/core/audio/null_core.cpp create mode 100644 src/core/audio/teakra_core.cpp create mode 160000 third_party/teakra diff --git a/.gitmodules b/.gitmodules index f1e8f469c..7d234ac6d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -55,3 +55,6 @@ [submodule "third_party/libuv"] path = third_party/libuv url = https://github.com/libuv/libuv +[submodule "third_party/teakra"] + path = third_party/teakra + url = https://github.com/wwylele/teakra diff --git a/CMakeLists.txt b/CMakeLists.txt index 456d6513a..2c0ec2554 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ include_directories(third_party/toml11) include_directories(third_party/glm) add_subdirectory(third_party/cmrc) +add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL) set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/third_party/boost") set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/third_party/boost") @@ -191,6 +192,7 @@ set(FS_SOURCE_FILES src/core/fs/archive_self_ncch.cpp src/core/fs/archive_save_d set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp src/core/applets/error_applet.cpp ) +set(AUDIO_SOURCE_FILES src/core/audio/dsp_core.cpp src/core/audio/null_core.cpp src/core/audio/teakra_core.cpp) set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp) # Frontend source files @@ -247,6 +249,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp include/services/nwm_uds.hpp include/fs/archive_system_save_data.hpp include/lua_manager.hpp include/memory_mapped_file.hpp include/hydra_icon.hpp include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp include/applets/error_applet.hpp + include/audio/dsp_core.hpp include/audio/null_core.hpp include/audio/teakra_core.hpp ) cmrc_add_resource_library( @@ -299,6 +302,7 @@ source_group("Source Files\\Core\\Loader" FILES ${LOADER_SOURCE_FILES}) source_group("Source Files\\Core\\Services" FILES ${SERVICE_SOURCE_FILES}) source_group("Source Files\\Core\\Applets" FILES ${APPLET_SOURCE_FILES}) source_group("Source Files\\Core\\PICA" FILES ${PICA_SOURCE_FILES}) +source_group("Source Files\\Core\\Audio" FILES ${AUDIO_SOURCE_FILES}) source_group("Source Files\\Core\\Software Renderer" FILES ${RENDERER_SW_SOURCE_FILES}) source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES}) @@ -397,7 +401,7 @@ endif() source_group("Header Files\\Core" FILES ${HEADER_FILES}) set(ALL_SOURCES ${SOURCE_FILES} ${FRONTEND_SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${LOADER_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${APPLET_SOURCE_FILES} ${RENDERER_SW_SOURCE_FILES} ${PICA_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} - ${HEADER_FILES} ${FRONTEND_HEADER_FILES}) + ${AUDIO_SOURCE_FILES} ${HEADER_FILES} ${FRONTEND_HEADER_FILES}) if(ENABLE_OPENGL) # Add the OpenGL source files to ALL_SOURCES @@ -429,7 +433,7 @@ if(ENABLE_LTO OR ENABLE_USER_BUILD) set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() -target_link_libraries(Alber PRIVATE dynarmic cryptopp glad resources_console_fonts) +target_link_libraries(Alber PRIVATE dynarmic cryptopp glad resources_console_fonts teakra) if(NOT ANDROID) target_link_libraries(Alber PRIVATE SDL2-static) diff --git a/include/audio/dsp_core.hpp b/include/audio/dsp_core.hpp new file mode 100644 index 000000000..07c90a91a --- /dev/null +++ b/include/audio/dsp_core.hpp @@ -0,0 +1,35 @@ +#pragma once +#include +#include +#include + +#include "helpers.hpp" +#include "logger.hpp" +#include "memory.hpp" + +namespace Audio { + class DSPCore { + protected: + Memory& mem; + MAKE_LOG_FUNCTION(log, dspLogger) + + public: + enum class Type { Null, Teakra }; + DSPCore(Memory& mem) : mem(mem) {} + + virtual void reset() = 0; + virtual void runAudioFrame() = 0; + virtual u8* getDspMemory() = 0; + + virtual u16 recvData(u32 regId) = 0; + virtual bool recvDataIsReady(u32 regId) = 0; + virtual void setSemaphore(u16 value) = 0; + virtual void writeProcessPipe(u32 channel, u32 size, u32 buffer) = 0; + virtual std::vector readPipe(u32 channel, u32 peer, u32 size, u32 buffer) = 0; + virtual void loadComponent(std::vector& data, u32 programMask, u32 dataMask) = 0; + virtual void unloadComponent() = 0; + virtual void setSemaphoreMask(u16 value) = 0; + }; + + std::unique_ptr makeDSPCore(DSPCore::Type type, Memory& mem); +} // namespace Audio \ No newline at end of file diff --git a/include/audio/null_core.hpp b/include/audio/null_core.hpp new file mode 100644 index 000000000..8eadfa094 --- /dev/null +++ b/include/audio/null_core.hpp @@ -0,0 +1,41 @@ +#pragma once +#include +#include "audio/dsp_core.hpp" + +namespace Audio { + class NullDSP : public DSPCore { + enum class DSPState : u32 { + Off, + On, + Slep, + }; + + // Number of DSP pipes + static constexpr size_t pipeCount = 8; + DSPState dspState; + + std::array, pipeCount> pipeData; // The data of each pipe + std::array dspRam; + + void resetAudioPipe(); + + public: + NullDSP(Memory& mem) : DSPCore(mem) {} + + void reset() override; + void runAudioFrame() override {} + u8* getDspMemory() override { return dspRam.data(); } + + u16 recvData(u32 regId) override; + bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready + void writeProcessPipe(u32 channel, u32 size, u32 buffer) override; + std::vector readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override; + + // NOPs for null DSP core + void loadComponent(std::vector& data, u32 programMask, u32 dataMask) override {} + void unloadComponent() override {} + void setSemaphore(u16 value) override {} + void setSemaphoreMask(u16 value) override {} + }; + +} // namespace Audio \ No newline at end of file diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp new file mode 100644 index 000000000..7955e8a82 --- /dev/null +++ b/include/audio/teakra_core.hpp @@ -0,0 +1,28 @@ +#pragma once +#include "audio/dsp_core.hpp" +#include "teakra/teakra.h" + +namespace Audio { + class TeakraDSP : public DSPCore { + Teakra::Teakra teakra; + u32 pipeBaseAddr; + bool running; + + public: + TeakraDSP(Memory& mem); + + void reset() override; + void runAudioFrame() override; + u8* getDspMemory() override { return teakra.GetDspMemory().data(); } + + u16 recvData(u32 regId) override { return teakra.RecvData(regId); } + bool recvDataIsReady(u32 regId) override { return teakra.RecvDataIsReady(regId); } + void setSemaphore(u16 value) override { return teakra.SetSemaphore(value); } + void setSemaphoreMask(u16 value) override { teakra.MaskSemaphore(value); } + + void writeProcessPipe(u32 channel, u32 size, u32 buffer) override; + std::vector readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override; + void loadComponent(std::vector& data, u32 programMask, u32 dataMask) override; + void unloadComponent() override; + }; +} // namespace Audio diff --git a/include/emulator.hpp b/include/emulator.hpp index d3377f6c9..207bef461 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -2,10 +2,12 @@ #include #include +#include #include #include #include "PICA/gpu.hpp" +#include "audio/dsp_core.hpp" #include "cheats.hpp" #include "config.hpp" #include "cpu.hpp" @@ -41,9 +43,11 @@ class Emulator { GPU gpu; Memory memory; Kernel kernel; + std::unique_ptr dsp; + Scheduler scheduler; + Crypto::AESEngine aesEngine; Cheats cheats; - Scheduler scheduler; // Variables to keep track of whether the user is controlling the 3DS analog stick with their keyboard // This is done so when a gamepad is connected, we won't automatically override the 3DS analog stick settings with the gamepad's state diff --git a/include/logger.hpp b/include/logger.hpp index e021a685d..4fc521b6c 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -36,6 +36,7 @@ namespace Log { static Logger gpuLogger; static Logger rendererLogger; static Logger shaderJITLogger; + static Logger dspLogger; // Service loggers static Logger acLogger; diff --git a/include/memory.hpp b/include/memory.hpp index 640ae5f00..e2716e2a9 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -100,8 +100,8 @@ namespace KernelMemoryTypes { class Memory { u8* fcram; - u8* dspRam; - u8* vram; // Provided to the memory class by the GPU class + u8* dspRam; // Provided to us by Audio + u8* vram; // Provided to the memory class by the GPU class u64& cpuTicks; // Reference to the CPU tick counter using SharedMemoryBlock = KernelMemoryTypes::SharedMemoryBlock; @@ -281,6 +281,8 @@ class Memory { u32 getUsedUserMem() { return usedUserMemory; } void setVRAM(u8* pointer) { vram = pointer; } + void setDSPMem(u8* pointer) { dspRam = pointer; } + bool allocateMainThreadStack(u32 size); Regions getConsoleRegion(); void copySharedFont(u8* ptr); diff --git a/src/core/audio/dsp_core.cpp b/src/core/audio/dsp_core.cpp new file mode 100644 index 000000000..0d50abe18 --- /dev/null +++ b/src/core/audio/dsp_core.cpp @@ -0,0 +1,21 @@ +#include "audio/dsp_core.hpp" + +#include "audio/null_core.hpp" +#include "audio/teakra_core.hpp" + +std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& mem) { + std::unique_ptr core; + + switch (type) { + case DSPCore::Type::Null: core = std::make_unique(mem); break; + case DSPCore::Type::Teakra: core = std::make_unique(mem); break; + + default: + Helpers::warn("Invalid DSP core selected!"); + core = std::make_unique(mem); + break; + } + + mem.setDSPMem(core->getDspMemory()); + return core; +} diff --git a/src/core/audio/null_core.cpp b/src/core/audio/null_core.cpp new file mode 100644 index 000000000..4d355c1e2 --- /dev/null +++ b/src/core/audio/null_core.cpp @@ -0,0 +1,129 @@ +#include "audio/null_core.hpp" + +namespace Audio { + namespace DSPPipeType { + enum : u32 { + Debug = 0, + DMA = 1, + Audio = 2, + Binary = 3, + }; + } + + void NullDSP::resetAudioPipe() { + // Hardcoded responses for now + // These are DSP DRAM offsets for various variables + // https://www.3dbrew.org/wiki/DSP_Memory_Region + static constexpr std::array responses = { + 0x000F, // Number of responses + 0xBFFF, // Frame counter + 0x9E92, // Source configs + 0x8680, // Source statuses + 0xA792, // ADPCM coefficients + 0x9430, // DSP configs + 0x8400, // DSP status + 0x8540, // Final samples + 0x9492, // Intermediate mix samples + 0x8710, // Compressor + 0x8410, // Debug + 0xA912, // ?? + 0xAA12, // ?? + 0xAAD2, // ?? + 0xAC52, // Surround sound biquad filter 1 + 0xAC5C // Surround sound biquad filter 2 + }; + + std::vector& audioPipe = pipeData[DSPPipeType::Audio]; + audioPipe.resize(responses.size() * sizeof(u16)); + + // Push back every response to the audio pipe + size_t index = 0; + for (auto e : responses) { + audioPipe[index++] = e & 0xff; + audioPipe[index++] = e >> 8; + } + } + + void NullDSP::reset() { + for (auto& e : pipeData) e.clear(); + + // Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted + resetAudioPipe(); + } + + u16 NullDSP::recvData(u32 regId) { + if (regId != 0) { + Helpers::panic("Audio: invalid register in null frontend"); + } + + return dspState == DSPState::On; + } + + void NullDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { + enum class StateChange : u8 { + Initialize = 0, + Shutdown = 1, + Wakeup = 2, + Sleep = 3, + }; + + switch (channel) { + case DSPPipeType::Audio: { + if (size != 4) { + printf("Invalid size written to DSP Audio Pipe\n"); + break; + } + + // Get new state + const u8 state = mem.read8(buffer); + if (state > 3) { + log("WriteProcessPipe::Audio: Unknown state change type"); + } else { + switch (static_cast(state)) { + case StateChange::Initialize: + // TODO: Other initialization stuff here + dspState = DSPState::On; + resetAudioPipe(); + break; + + case StateChange::Shutdown: + dspState = DSPState::Off; + break; + + default: Helpers::panic("Unimplemented DSP audio pipe state change %d", state); + } + } + break; + } + + case DSPPipeType::Binary: + Helpers::warn("Unimplemented write to binary pipe! Size: %d\n", size); + break; + + default: log("Audio::NullDSP: Wrote to unimplemented pipe %d\n", channel); break; + } + } + + std::vector NullDSP::readPipe(u32 pipe, u32 peer, u32 size, u32 buffer) { + if (size & 1) Helpers::panic("Tried to read odd amount of bytes from DSP pipe"); + if (pipe >= pipeCount || size > 0xffff) { + return {}; + } + + if (pipe != DSPPipeType::Audio) { + log("Reading from non-audio pipe! This might be broken, might need to check what pipe is being read from and implement writing to it\n"); + } + + std::vector& data = pipeData[pipe]; + size = std::min(size, data.size()); // Clamp size to the maximum available data size + + if (size == 0) { + return {}; + } + + // Return "size" bytes from the audio pipe and erase them + std::vector out(data.begin(), data.begin() + size); + data.erase(data.begin(), data.begin() + size); + return out; + } +} // namespace Audio diff --git a/src/core/audio/teakra_core.cpp b/src/core/audio/teakra_core.cpp new file mode 100644 index 000000000..aa20a38e9 --- /dev/null +++ b/src/core/audio/teakra_core.cpp @@ -0,0 +1,150 @@ +#include "audio/teakra_core.hpp" + +using namespace Audio; + +struct Dsp1 { + // All sizes are in bytes unless otherwise specified + + u8 signature[0x100]; + u8 magic[4]; + u32 size; + u8 codeMemLayout; + u8 dataMemLayout; + u8 pad[3]; + u8 specialType; + u8 segmentCount; + u8 flags; + u32 specialStart; + u32 specialSize; + u64 zeroBits; + + struct Segment { + u32 offs; // Offset of the segment data + u32 dspAddr; // Start of the segment in 16-bit units + u32 size; + u8 pad[3]; + u8 type; + u8 hash[0x20]; + }; + + Segment segments[10]; +}; + +TeakraDSP::TeakraDSP(Memory& mem) : DSPCore(mem), pipeBaseAddr(0), running(false) { + teakra.Reset(); + + // Set up callbacks for Teakra + Teakra::AHBMCallback ahbm; + + ahbm.read8 = [&](u32 addr) -> u8 { return mem.read8(addr); }; + ahbm.read16 = [&](u32 addr) -> u16 { return mem.read16(addr); }; + ahbm.read32 = [&](u32 addr) -> u32 { return mem.read32(addr); }; + + ahbm.write8 = [&](u32 addr, u8 value) { mem.write8(addr, value); }; + ahbm.write16 = [&](u32 addr, u16 value) { mem.write16(addr, value); }; + ahbm.write32 = [&](u32 addr, u32 value) { mem.write32(addr, value); }; + + teakra.SetAHBMCallback(ahbm); + teakra.SetAudioCallback([=](std::array sample) { + // NOP for now + }); +} + +void TeakraDSP::reset() { + teakra.Reset(); + running = false; +} + +void TeakraDSP::runAudioFrame() { + if (running) { + teakra.Run(16384); + } +} + +void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { + // TODO +} + +std::vector TeakraDSP::readPipe(u32 channel, u32 peer, u32 size, u32 buffer) { + // TODO + return std::vector(); +} + +void TeakraDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMask) { + // TODO: maybe move this to the DSP service + + u8* dspCode = teakra.GetDspMemory().data(); + u8* dspData = dspCode + 0x40000; + + Dsp1 dsp1; + memcpy(&dsp1, data.data(), sizeof(dsp1)); + + // TODO: verify DSP1 signature + + // Load DSP segments to DSP RAM + // TODO: verify hashes + for (unsigned int i = 0; i < dsp1.segmentCount; i++) { + auto& segment = dsp1.segments[i]; + u32 addr = segment.dspAddr << 1; + u8* src = data.data() + segment.offs; + u8* dst = nullptr; + + switch (segment.type) { + case 0: + case 1: dst = dspCode + addr; break; + default: dst = dspData + addr; break; + } + + memcpy(dst, src, segment.size); + } + + bool syncWithDsp = dsp1.flags & 0x1; + bool loadSpecialSegment = (dsp1.flags >> 1) & 0x1; + + // TODO: how does the special segment work? + if (loadSpecialSegment) { + log("LoadComponent: special segment not supported"); + } + + running = true; + + if (syncWithDsp) { + // Wait for the DSP to reply with 1s in all RECV registers + for (int i = 0; i < 3; i++) { + do { + while (!teakra.RecvDataIsReady(i)) { + runAudioFrame(); + } + } while (teakra.RecvData(i) != 1); + } + } + + // Retrieve the pipe base address + while (!teakra.RecvDataIsReady(2)) { + runAudioFrame(); + } + + pipeBaseAddr = teakra.RecvData(2); +} + +void TeakraDSP::unloadComponent() { + if (!running) { + Helpers::panic("Audio: unloadComponent called without a running program"); + } + + // Wait for SEND2 to be ready, then send the shutdown command to the DSP + while (!teakra.SendDataIsEmpty(2)) { + runAudioFrame(); + } + + teakra.SendData(2, 0x8000); + + // Wait for shutdown to be acknowledged + while (!teakra.RecvDataIsReady(2)) { + runAudioFrame(); + } + + // Read the value and discard it, completing shutdown + teakra.RecvData(2); + running = false; +} \ No newline at end of file diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 83248ef51..fdc8c4754 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -15,7 +15,6 @@ using namespace KernelMemoryTypes; Memory::Memory(u64& cpuTicks, const EmulatorConfig& config) : cpuTicks(cpuTicks), config(config) { fcram = new uint8_t[FCRAM_SIZE](); - dspRam = new uint8_t[DSP_RAM_SIZE](); readTable.resize(totalPageCount, 0); writeTable.resize(totalPageCount, 0); diff --git a/src/emulator.cpp b/src/emulator.cpp index c567cbc7a..3b6ee637f 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -24,6 +24,8 @@ Emulator::Emulator() httpServer(this) #endif { + dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Null, memory); + #ifdef PANDA3DS_ENABLE_DISCORD_RPC if (config.discordRpcEnabled) { discordRpc.init(); diff --git a/third_party/teakra b/third_party/teakra new file mode 160000 index 000000000..01db7cdd0 --- /dev/null +++ b/third_party/teakra @@ -0,0 +1 @@ +Subproject commit 01db7cdd00aabcce559a8dddce8798dabb71949b From 363c71e66cf77f4525e3fc2e0dd1764b896d9aa9 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 17 Feb 2024 01:36:10 +0200 Subject: [PATCH 02/14] Hook DSP interface to DSP service HLE Co-Authored-By: PSISP <12768103+psi-rockin@users.noreply.github.com> --- include/audio/teakra_core.hpp | 7 +- include/services/dsp.hpp | 18 +--- include/services/service_manager.hpp | 1 + src/core/audio/teakra_core.cpp | 6 -- src/core/services/dsp.cpp | 132 ++++----------------------- src/emulator.cpp | 3 +- 6 files changed, 31 insertions(+), 136 deletions(-) diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp index 7955e8a82..52be271ef 100644 --- a/include/audio/teakra_core.hpp +++ b/include/audio/teakra_core.hpp @@ -12,7 +12,12 @@ namespace Audio { TeakraDSP(Memory& mem); void reset() override; - void runAudioFrame() override; + void runAudioFrame() override { + if (running) { + teakra.Run(16384); + } + } + u8* getDspMemory() override { return teakra.GetDspMemory().data(); } u16 recvData(u32 regId) override { return teakra.RecvData(regId); } diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index ab9fb106e..e0d529284 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -1,17 +1,12 @@ #pragma once #include #include +#include "audio/dsp_core.hpp" #include "helpers.hpp" #include "logger.hpp" #include "memory.hpp" #include "result/result.hpp" -namespace DSPPipeType { - enum : u32 { - Debug = 0, DMA = 1, Audio = 2, Binary = 3 - }; -} - // Circular dependencies! class Kernel; @@ -19,15 +14,11 @@ class DSPService { Handle handle = KernelHandles::DSP; Memory& mem; Kernel& kernel; + Audio::DSPCore* dsp = nullptr; MAKE_LOG_FUNCTION(log, dspServiceLogger) - enum class DSPState : u32 { - Off, On, Slep - }; - // Number of DSP pipes static constexpr size_t pipeCount = 8; - DSPState dspState; // DSP service event handles using DSPEvent = std::optional; @@ -36,10 +27,6 @@ class DSPService { DSPEvent interrupt0; DSPEvent interrupt1; std::array pipeEvents; - std::array, pipeCount> pipeData; // The data of each pipe - - void resetAudioPipe(); - std::vector readPipe(u32 pipe, u32 size); DSPEvent& getEventRef(u32 type, u32 pipe); static constexpr size_t maxEventCount = 6; @@ -67,6 +54,7 @@ class DSPService { DSPService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} void reset(); void handleSyncRequest(u32 messagePointer); + void setDSPCore(Audio::DSPCore* pointer) { dsp = pointer; } enum class SoundOutputMode : u8 { Mono = 0, diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index 93700498f..b6c803b01 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -110,4 +110,5 @@ class ServiceManager { // Input function wrappers HIDService& getHID() { return hid; } NFCService& getNFC() { return nfc; } + DSPService& getDSP() { return dsp; } }; diff --git a/src/core/audio/teakra_core.cpp b/src/core/audio/teakra_core.cpp index aa20a38e9..259442072 100644 --- a/src/core/audio/teakra_core.cpp +++ b/src/core/audio/teakra_core.cpp @@ -55,12 +55,6 @@ void TeakraDSP::reset() { running = false; } -void TeakraDSP::runAudioFrame() { - if (running) { - teakra.Run(16384); - } -} - void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { // TODO } diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index 69eb9fb36..e44c31e51 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -31,13 +31,7 @@ namespace Result { } void DSPService::reset() { - for (auto& e : pipeData) - e.clear(); - - // Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted - resetAudioPipe(); totalEventCount = 0; - dspState = DSPState::Off; semaphoreEvent = std::nullopt; interrupt0 = std::nullopt; @@ -48,40 +42,6 @@ void DSPService::reset() { } } -void DSPService::resetAudioPipe() { - // Hardcoded responses for now - // These are DSP DRAM offsets for various variables - // https://www.3dbrew.org/wiki/DSP_Memory_Region - static constexpr std::array responses = { - 0x000F, // Number of responses - 0xBFFF, // Frame counter - 0x9E92, // Source configs - 0x8680, // Source statuses - 0xA792, // ADPCM coefficients - 0x9430, // DSP configs - 0x8400, // DSP status - 0x8540, // Final samples - 0x9492, // Intermediate mix samples - 0x8710, // Compressor - 0x8410, // Debug - 0xA912, // ?? - 0xAA12, // ?? - 0xAAD2, // ?? - 0xAC52, // Surround sound biquad filter 1 - 0xAC5C // Surround sound biquad filter 2 - }; - - std::vector& audioPipe = pipeData[DSPPipeType::Audio]; - audioPipe.resize(responses.size() * sizeof(u16)); - - // Push back every response to the audio pipe - size_t index = 0; - for (auto e : responses) { - audioPipe[index++] = e & 0xff; - audioPipe[index++] = e >> 8; - } -} - void DSPService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { @@ -117,8 +77,16 @@ void DSPService::loadComponent(u32 messagePointer) { u32 size = mem.read32(messagePointer + 4); u32 programMask = mem.read32(messagePointer + 8); u32 dataMask = mem.read32(messagePointer + 12); + u32 buffer = mem.read32(messagePointer + 20); + + std::vector data(size); + for (u32 i = 0; i < size; i++) { + data[i] = mem.read8(buffer + i); + } log("DSP::LoadComponent (size = %08X, program mask = %X, data mask = %X\n", size, programMask, dataMask); + dsp->loadComponent(data, programMask, dataMask); + mem.write32(messagePointer, IPC::responseHeader(0x11, 2, 2)); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, 1); // Component loaded @@ -128,32 +96,12 @@ void DSPService::loadComponent(u32 messagePointer) { void DSPService::unloadComponent(u32 messagePointer) { log("DSP::UnloadComponent\n"); + dsp->unloadComponent(); + mem.write32(messagePointer, IPC::responseHeader(0x12, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } -std::vector DSPService::readPipe(u32 pipe, u32 size) { - if (size & 1) Helpers::panic("Tried to read odd amount of bytes from DSP pipe"); - if (pipe >= pipeCount || size > 0xffff) { - return {}; - } - - if (pipe != DSPPipeType::Audio) { - log("Reading from non-audio pipe! This might be broken, might need to check what pipe is being read from and implement writing to it\n"); - } - - std::vector& data = pipeData[pipe]; - size = std::min(size, u32(data.size())); // Clamp size to the maximum available data size - - if (size == 0) - return {}; - - // Return "size" bytes from the audio pipe and erase them - std::vector out(data.begin(), data.begin() + size); - data.erase(data.begin(), data.begin() + size); - return out; -} - void DSPService::readPipeIfPossible(u32 messagePointer) { u32 channel = mem.read32(messagePointer + 4); u32 peer = mem.read32(messagePointer + 8); @@ -162,7 +110,7 @@ void DSPService::readPipeIfPossible(u32 messagePointer) { log("DSP::ReadPipeIfPossible (channel = %d, peer = %d, size = %04X, buffer = %08X)\n", channel, peer, size, buffer); mem.write32(messagePointer, IPC::responseHeader(0x10, 2, 2)); - std::vector data = readPipe(channel, size); + std::vector data = dsp->readPipe(channel, peer, size, buffer); for (uint i = 0; i < data.size(); i++) { mem.write8(buffer + i, data[i]); } @@ -177,21 +125,22 @@ void DSPService::recvData(u32 messagePointer) { if (registerIndex != 0) Helpers::panic("Unknown register in DSP::RecvData"); // Return 0 if the DSP is running, otherwise 1 - const u16 ret = dspState == DSPState::On ? 0 : 1; + const u16 data = dsp->recvData(registerIndex); mem.write32(messagePointer, IPC::responseHeader(0x01, 2, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write16(messagePointer + 8, ret); + mem.write16(messagePointer + 8, data); } void DSPService::recvDataIsReady(u32 messagePointer) { const u32 registerIndex = mem.read32(messagePointer + 4); log("DSP::RecvDataIsReady (register = %d)\n", registerIndex); - if (registerIndex != 0) Helpers::panic("Unknown register in DSP::RecvDataIsReady"); + + bool isReady = dsp->recvDataIsReady(registerIndex); mem.write32(messagePointer, IPC::responseHeader(0x02, 2, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 8, 1); // Always return that the register is ready for now + mem.write32(messagePointer + 8, isReady ? 1 : 0); } DSPService::DSPEvent& DSPService::getEventRef(u32 type, u32 pipe) { @@ -236,7 +185,6 @@ void DSPService::registerInterruptEvents(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); totalEventCount++; - kernel.signalEvent(eventHandle); } } } @@ -267,6 +215,7 @@ void DSPService::setSemaphore(u32 messagePointer) { const u16 value = mem.read16(messagePointer + 4); log("DSP::SetSemaphore(value = %04X)\n", value); + dsp->setSemaphore(value); mem.write32(messagePointer, IPC::responseHeader(0x7, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } @@ -275,6 +224,7 @@ void DSPService::setSemaphoreMask(u32 messagePointer) { const u16 mask = mem.read16(messagePointer + 4); log("DSP::SetSemaphoreMask(mask = %04X)\n", mask); + dsp->setSemaphoreMask(mask); mem.write32(messagePointer, IPC::responseHeader(0x17, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } @@ -285,51 +235,7 @@ void DSPService::writeProcessPipe(u32 messagePointer) { const u32 buffer = mem.read32(messagePointer + 16); log("DSP::writeProcessPipe (channel = %d, size = %X, buffer = %08X)\n", channel, size, buffer); - enum class StateChange : u8 { - Initialize = 0, - Shutdown = 1, - Wakeup = 2, - Sleep = 3, - }; - - switch (channel) { - case DSPPipeType::Audio: { - if (size != 4) { - printf("Invalid size written to DSP Audio Pipe\n"); - break; - } - - // Get new state - const u8 state = mem.read8(buffer); - if (state > 3) { - log("WriteProcessPipe::Audio: Unknown state change type"); - } else { - switch (static_cast(state)) { - case StateChange::Initialize: - // TODO: Other initialization stuff here - dspState = DSPState::On; - resetAudioPipe(); - break; - - case StateChange::Shutdown: - dspState = DSPState::Off; - break; - - default: Helpers::panic("Unimplemented DSP audio pipe state change %d", state); - } - } - break; - } - - case DSPPipeType::Binary: - Helpers::warn("Unimplemented write to binary pipe! Size: %d\n", size); - break; - - default: - log("DSP: Wrote to unimplemented pipe %d\n", channel); - break; - } - + dsp->writeProcessPipe(channel, size, buffer); mem.write32(messagePointer, IPC::responseHeader(0xD, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } diff --git a/src/emulator.cpp b/src/emulator.cpp index 3b6ee637f..79b222aad 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -24,7 +24,8 @@ Emulator::Emulator() httpServer(this) #endif { - dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Null, memory); + dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Teakra, memory); + kernel.getServiceManager().getDSP().setDSPCore(dsp.get()); #ifdef PANDA3DS_ENABLE_DISCORD_RPC if (config.discordRpcEnabled) { From 5dd3c02ffb0d97d40a27fdd7832934fcd678710a Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 17 Feb 2024 03:48:37 +0200 Subject: [PATCH 03/14] Moar Teak LLE Co-Authored-By: PSISP <12768103+psi-rockin@users.noreply.github.com> --- include/audio/dsp_core.hpp | 9 +- include/audio/null_core.hpp | 2 +- include/audio/teakra_core.hpp | 58 ++++++++++- include/scheduler.hpp | 4 +- include/services/dsp.hpp | 4 + src/core/audio/dsp_core.cpp | 8 +- src/core/audio/teakra_core.cpp | 179 +++++++++++++++++++++++++++++++-- src/core/services/dsp.cpp | 24 +++++ src/core/services/gsp_gpu.cpp | 2 +- src/emulator.cpp | 15 ++- 10 files changed, 284 insertions(+), 21 deletions(-) diff --git a/include/audio/dsp_core.hpp b/include/audio/dsp_core.hpp index 07c90a91a..498e111f3 100644 --- a/include/audio/dsp_core.hpp +++ b/include/audio/dsp_core.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -7,15 +8,19 @@ #include "logger.hpp" #include "memory.hpp" +// The DSP core must have access to the DSP service to be able to trigger interrupts properly +class DSPService; + namespace Audio { class DSPCore { protected: Memory& mem; + DSPService& dspService; MAKE_LOG_FUNCTION(log, dspLogger) public: enum class Type { Null, Teakra }; - DSPCore(Memory& mem) : mem(mem) {} + DSPCore(Memory& mem, DSPService& dspService) : mem(mem), dspService(dspService) {} virtual void reset() = 0; virtual void runAudioFrame() = 0; @@ -31,5 +36,5 @@ namespace Audio { virtual void setSemaphoreMask(u16 value) = 0; }; - std::unique_ptr makeDSPCore(DSPCore::Type type, Memory& mem); + std::unique_ptr makeDSPCore(DSPCore::Type type, Memory& mem, DSPService& dspService); } // namespace Audio \ No newline at end of file diff --git a/include/audio/null_core.hpp b/include/audio/null_core.hpp index 8eadfa094..2a01fc07d 100644 --- a/include/audio/null_core.hpp +++ b/include/audio/null_core.hpp @@ -20,7 +20,7 @@ namespace Audio { void resetAudioPipe(); public: - NullDSP(Memory& mem) : DSPCore(mem) {} + NullDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspService) {} void reset() override; void runAudioFrame() override {} diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp index 52be271ef..96c862121 100644 --- a/include/audio/teakra_core.hpp +++ b/include/audio/teakra_core.hpp @@ -1,5 +1,6 @@ #pragma once #include "audio/dsp_core.hpp" +#include "swap.hpp" #include "teakra/teakra.h" namespace Audio { @@ -8,8 +9,63 @@ namespace Audio { u32 pipeBaseAddr; bool running; + // Get a pointer to a data memory address + u8* getDataPointer(u32 address) { return getDspMemory() + Memory::DSP_DATA_MEMORY_OFFSET + address; } + + enum class PipeDirection { + DSPtoCPU = 0, + CPUtoDSP = 1, + }; + + // A lot of Teakra integration code, especially pipe stuff are based on Citra's integration here: + // https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp + struct PipeStatus { + // All addresses and sizes here refer to byte values, NOT 16-bit values. + u16_le address; + u16_le byteSize; + u16_le readPointer; + u16_le writePointer; + u8 slot; + u8 flags; + + static constexpr u16 wrapBit = 0x8000; + static constexpr u16 pointerMask = 0x7FFF; + + bool isFull() const { return (readPointer ^ writePointer) == wrapBit; } + bool isEmpty() const { return (readPointer ^ writePointer) == 0; } + + // isWrapped: Are read and write pointers in different memory passes. + // true: xxxx]----[xxxx (data is wrapping around the end of memory) + // false: ----[xxxx]---- + bool isWrapped() const { return (readPointer ^ writePointer) >= wrapBit; } + }; + static_assert(sizeof(PipeStatus) == 10, "Teakra: Pipe Status size is wrong"); + static constexpr u8 pipeToSlotIndex(u8 pipe, PipeDirection direction) { return (pipe * 2) + u8(direction); } + + PipeStatus getPipeStatus(u8 pipe, PipeDirection direction) { + PipeStatus ret; + const u8 index = pipeToSlotIndex(pipe, direction); + + std::memcpy(&ret, getDataPointer(pipeBaseAddr * 2 + index * sizeof(PipeStatus)), sizeof(PipeStatus)); + return ret; + } + + void updatePipeStatus(const PipeStatus& status) { + u8 slot = status.slot; + u8* statusAddress = getDataPointer(pipeBaseAddr * 2 + slot * sizeof(PipeStatus)); + + if (slot % 2 == 0) { + std::memcpy(statusAddress + 4, &status.readPointer, sizeof(u16)); + } else { + std::memcpy(statusAddress + 6, &status.writePointer, sizeof(u16)); + } + } + + bool signalledData; + bool signalledSemaphore; + public: - TeakraDSP(Memory& mem); + TeakraDSP(Memory& mem, DSPService& dspService); void reset() override; void runAudioFrame() override { diff --git a/include/scheduler.hpp b/include/scheduler.hpp index 5645f47d5..e251c92a6 100644 --- a/include/scheduler.hpp +++ b/include/scheduler.hpp @@ -10,7 +10,8 @@ struct Scheduler { enum class EventType { VBlank = 0, // End of frame event UpdateTimers = 1, // Update kernel timer objects - Panic = 2, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX) + RunDSP = 2, // Make the emulated DSP run for one audio frame + Panic = 3, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX) TotalNumberOfEvents // How many event types do we have in total? }; static constexpr usize totalNumberOfEvents = static_cast(EventType::TotalNumberOfEvents); @@ -49,6 +50,7 @@ struct Scheduler { // Clear any pending events events.clear(); addEvent(Scheduler::EventType::VBlank, arm11Clock / 60); + addEvent(Scheduler::EventType::RunDSP, 16384 * 2); // Add a dummy event to always keep the scheduler non-empty addEvent(EventType::Panic, std::numeric_limits::max()); diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index e0d529284..5b8c0f4f0 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -63,4 +63,8 @@ class DSPService { }; void signalEvents(); + void triggerPipeEvent(int index); + void triggerSemaphoreEvent(); + void triggerInterrupt0(); + void triggerInterrupt1(); }; \ No newline at end of file diff --git a/src/core/audio/dsp_core.cpp b/src/core/audio/dsp_core.cpp index 0d50abe18..f29af59ab 100644 --- a/src/core/audio/dsp_core.cpp +++ b/src/core/audio/dsp_core.cpp @@ -3,16 +3,16 @@ #include "audio/null_core.hpp" #include "audio/teakra_core.hpp" -std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& mem) { +std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& mem, DSPService& dspService) { std::unique_ptr core; switch (type) { - case DSPCore::Type::Null: core = std::make_unique(mem); break; - case DSPCore::Type::Teakra: core = std::make_unique(mem); break; + case DSPCore::Type::Null: core = std::make_unique(mem, dspService); break; + case DSPCore::Type::Teakra: core = std::make_unique(mem, dspService); break; default: Helpers::warn("Invalid DSP core selected!"); - core = std::make_unique(mem); + core = std::make_unique(mem, dspService); break; } diff --git a/src/core/audio/teakra_core.cpp b/src/core/audio/teakra_core.cpp index 259442072..23a3c3ce3 100644 --- a/src/core/audio/teakra_core.cpp +++ b/src/core/audio/teakra_core.cpp @@ -1,10 +1,14 @@ #include "audio/teakra_core.hpp" +#include +#include + +#include "services/dsp.hpp" + using namespace Audio; struct Dsp1 { // All sizes are in bytes unless otherwise specified - u8 signature[0x100]; u8 magic[4]; u32 size; @@ -30,7 +34,7 @@ struct Dsp1 { Segment segments[10]; }; -TeakraDSP::TeakraDSP(Memory& mem) : DSPCore(mem), pipeBaseAddr(0), running(false) { +TeakraDSP::TeakraDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspService), pipeBaseAddr(0), running(false) { teakra.Reset(); // Set up callbacks for Teakra @@ -48,30 +52,188 @@ TeakraDSP::TeakraDSP(Memory& mem) : DSPCore(mem), pipeBaseAddr(0), running(false teakra.SetAudioCallback([=](std::array sample) { // NOP for now }); + + // Set up event handlers + teakra.SetRecvDataHandler(0, [&]() { + if (running) { + dspService.triggerInterrupt0(); + } + }); + + teakra.SetRecvDataHandler(1, [&]() { + if (running) { + dspService.triggerInterrupt1(); + } + }); + + auto processPipeEvent = [&](bool dataEvent) { + if (!running) { + return; + } + + if (dataEvent) { + signalledData = true; + } else { + if ((teakra.GetSemaphore() & 0x8000) == 0) { + return; + } + + signalledSemaphore = true; + } + + if (signalledSemaphore && signalledData) { + signalledSemaphore = signalledData = false; + + u16 slot = teakra.RecvData(2); + u16 side = slot % 2; + u16 pipe = slot / 2; + + if (side != static_cast(PipeDirection::DSPtoCPU)) { + return; + } + + if (pipe == 0) { + Helpers::warn("Pipe event for debug pipe: Should be ignored and the data should be flushed"); + } else { + dspService.triggerPipeEvent(pipe); + } + } + }; + + teakra.SetRecvDataHandler(2, [processPipeEvent]() { processPipeEvent(true); }); + teakra.SetSemaphoreHandler([processPipeEvent]() { processPipeEvent(false); }); } void TeakraDSP::reset() { teakra.Reset(); running = false; + signalledData = signalledSemaphore = false; } +// https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { - // TODO + Helpers::warn("Teakra: Write process pipe"); + size &= 0xffff; + + PipeStatus status = getPipeStatus(channel, PipeDirection::CPUtoDSP); + bool needUpdate = false; // Do we need to update the pipe status and catch up Teakra? + + std::vector data; + data.reserve(size); + + // Read data to write + for (int i = 0; i < size; i++) { + const u8 byte = mem.read8(buffer + i); + data.push_back(byte); + } + u8* dataPointer = data.data(); + + while (size != 0) { + if (status.isFull()) { + Helpers::warn("Teakra: Writing to full pipe"); + } + + // Calculate begin/end/size for write + const u16 writeEnd = status.isWrapped() ? (status.readPointer & PipeStatus::pointerMask) : status.byteSize; + const u16 writeBegin = status.writePointer & PipeStatus::pointerMask; + const u16 writeSize = std::min(u16(size), writeEnd - writeBegin); + + if (writeEnd <= writeBegin) [[unlikely]] { + Helpers::warn("Teakra: Writing to pipe but end <= start"); + } + + // Write data to pipe, increment write and buffer pointers, decrement size + std::memcpy(getDataPointer(status.address * 2 + writeBegin), dataPointer, writeSize); + dataPointer += writeSize; + status.writePointer += writeSize; + size -= writeSize; + + if ((status.writePointer & PipeStatus::pointerMask) > status.byteSize) [[unlikely]] { + Helpers::warn("Teakra: Writing to pipe but write > size"); + } + + if ((status.writePointer & PipeStatus::pointerMask) == status.byteSize) { + status.writePointer &= PipeStatus::wrapBit; + status.writePointer ^= PipeStatus::wrapBit; + } + needUpdate = true; + } + + if (needUpdate) { + updatePipeStatus(status); + while (!teakra.SendDataIsEmpty(2)) { + runAudioFrame(); + } + + teakra.SendData(2, status.slot); + } } std::vector TeakraDSP::readPipe(u32 channel, u32 peer, u32 size, u32 buffer) { - // TODO - return std::vector(); + Helpers::warn("Teakra: Read pipe"); + size &= 0xffff; + + PipeStatus status = getPipeStatus(channel, PipeDirection::DSPtoCPU); + + std::vector pipeData(size); + u8* dataPointer = pipeData.data(); + bool needUpdate = false; // Do we need to update the pipe status and catch up Teakra? + + while (size != 0) { + if (status.isEmpty()) [[unlikely]] { + Helpers::warn("Teakra: Reading from empty pipe"); + return pipeData; + } + + // Read as many bytes as possible + const u16 readEnd = status.isWrapped() ? status.byteSize : (status.writePointer & PipeStatus::pointerMask); + const u16 readBegin = status.readPointer & PipeStatus::pointerMask; + const u16 readSize = std::min(u16(size), readEnd - readBegin); + + // Copy bytes to the output vector, increment the read and vector pointers and decrement the size appropriately + std::memcpy(dataPointer, getDataPointer(status.address * 2 + readBegin), readSize); + dataPointer += readSize; + status.readPointer += readSize; + size -= readSize; + + if ((status.readPointer & PipeStatus::pointerMask) > status.byteSize) [[unlikely]] { + Helpers::warn("Teakra: Reading from pipe but read > size"); + } + + if ((status.readPointer & PipeStatus::pointerMask) == status.byteSize) { + status.readPointer &= PipeStatus::wrapBit; + status.readPointer ^= PipeStatus::wrapBit; + } + + needUpdate = true; + } + + if (needUpdate) { + updatePipeStatus(status); + while (!teakra.SendDataIsEmpty(2)) { + runAudioFrame(); + } + + teakra.SendData(2, status.slot); + } + + return pipeData; } void TeakraDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMask) { // TODO: maybe move this to the DSP service + if (running) { + Helpers::warn("Loading DSP component when already loaded"); + return; + } + + teakra.Reset(); u8* dspCode = teakra.GetDspMemory().data(); u8* dspData = dspCode + 0x40000; Dsp1 dsp1; - memcpy(&dsp1, data.data(), sizeof(dsp1)); + std::memcpy(&dsp1, data.data(), sizeof(dsp1)); // TODO: verify DSP1 signature @@ -89,7 +251,7 @@ void TeakraDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMa default: dst = dspData + addr; break; } - memcpy(dst, src, segment.size); + std::memcpy(dst, src, segment.size); } bool syncWithDsp = dsp1.flags & 0x1; @@ -123,7 +285,8 @@ void TeakraDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMa void TeakraDSP::unloadComponent() { if (!running) { - Helpers::panic("Audio: unloadComponent called without a running program"); + Helpers::warn("Audio: unloadComponent called without a running program"); + return; } // Wait for SEND2 to be ready, then send the shutdown command to the DSP diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index e44c31e51..f12e31f04 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -268,4 +268,28 @@ void DSPService::signalEvents() { if (semaphoreEvent.has_value()) { kernel.signalEvent(semaphoreEvent.value()); } if (interrupt0.has_value()) { kernel.signalEvent(interrupt0.value()); } if (interrupt1.has_value()) { kernel.signalEvent(interrupt1.value()); } +} + +void DSPService::triggerPipeEvent(int index) { + if (index < pipeCount && pipeEvents[index].has_value()) { + kernel.signalEvent(*pipeEvents[index]); + } +} + +void DSPService::triggerSemaphoreEvent() { + if (semaphoreEvent.has_value()) { + kernel.signalEvent(*semaphoreEvent); + } +} + +void DSPService::triggerInterrupt0() { + if (interrupt0.has_value()) { + kernel.signalEvent(*interrupt0); + } +} + +void DSPService::triggerInterrupt1() { + if (interrupt1.has_value()) { + kernel.signalEvent(*interrupt1); + } } \ No newline at end of file diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 8dff6a79d..19a40a182 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -125,7 +125,7 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) { void GPUService::requestInterrupt(GPUInterrupt type) { // HACK: Signal DSP events on GPU interrupt for now until we have the DSP since games need DSP events // Maybe there's a better alternative? - kernel.signalDSPEvents(); + // kernel.signalDSPEvents(); if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet return; diff --git a/src/emulator.cpp b/src/emulator.cpp index 79b222aad..642b1a02a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -24,8 +24,10 @@ Emulator::Emulator() httpServer(this) #endif { - dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Teakra, memory); - kernel.getServiceManager().getDSP().setDSPCore(dsp.get()); + DSPService& dspService = kernel.getServiceManager().getDSP(); + + dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Teakra, memory, dspService); + dspService.setDSPCore(dsp.get()); #ifdef PANDA3DS_ENABLE_DISCORD_RPC if (config.discordRpcEnabled) { @@ -49,6 +51,8 @@ void Emulator::reset(ReloadOption reload) { cpu.reset(); gpu.reset(); memory.reset(); + dsp->reset(); + // Reset scheduler and add a VBlank event scheduler.reset(); @@ -139,13 +143,18 @@ void Emulator::pollScheduler() { ServiceManager& srv = kernel.getServiceManager(); srv.sendGPUInterrupt(GPUInterrupt::VBlank0); srv.sendGPUInterrupt(GPUInterrupt::VBlank1); - + // Queue next VBlank event scheduler.addEvent(Scheduler::EventType::VBlank, time + CPU::ticksPerSec / 60); break; } case Scheduler::EventType::UpdateTimers: kernel.pollTimers(); break; + case Scheduler::EventType::RunDSP: { + scheduler.addEvent(Scheduler::EventType::RunDSP, time + 16384 * 2); + dsp->runAudioFrame(); + break; + } default: { Helpers::panic("Scheduler: Unimplemented event type received: %d\n", static_cast(eventType)); From d3dc9e2f7104d32b217581b7ac1631c14dc418e9 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 17 Feb 2024 23:54:43 +0200 Subject: [PATCH 04/14] Forward interrupts properly in Teakra DSP core --- include/audio/teakra_core.hpp | 5 +++-- src/core/audio/teakra_core.cpp | 28 ++++++++++++++++------------ src/core/services/dsp.cpp | 1 - 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp index 96c862121..99ae88005 100644 --- a/include/audio/teakra_core.hpp +++ b/include/audio/teakra_core.hpp @@ -7,7 +7,8 @@ namespace Audio { class TeakraDSP : public DSPCore { Teakra::Teakra teakra; u32 pipeBaseAddr; - bool running; + bool running; // Is the DSP running? + bool loaded; // Have we finished loading a binary with LoadComponent? // Get a pointer to a data memory address u8* getDataPointer(u32 address) { return getDspMemory() + Memory::DSP_DATA_MEMORY_OFFSET + address; } @@ -17,7 +18,7 @@ namespace Audio { CPUtoDSP = 1, }; - // A lot of Teakra integration code, especially pipe stuff are based on Citra's integration here: + // A lot of Teakra integration code, especially pipe stuff is based on Citra's integration here: // https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp struct PipeStatus { // All addresses and sizes here refer to byte values, NOT 16-bit values. diff --git a/src/core/audio/teakra_core.cpp b/src/core/audio/teakra_core.cpp index 23a3c3ce3..7541185af 100644 --- a/src/core/audio/teakra_core.cpp +++ b/src/core/audio/teakra_core.cpp @@ -35,8 +35,6 @@ struct Dsp1 { }; TeakraDSP::TeakraDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspService), pipeBaseAddr(0), running(false) { - teakra.Reset(); - // Set up callbacks for Teakra Teakra::AHBMCallback ahbm; @@ -53,21 +51,23 @@ TeakraDSP::TeakraDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspServ // NOP for now }); - // Set up event handlers + // Set up event handlers. These handlers forward a hardware interrupt to the DSP service, which is responsible + // For triggering the appropriate DSP kernel events + // Note: It's important not to fire any events if "loaded" is false, ie if we haven't fully loaded a DSP component yet teakra.SetRecvDataHandler(0, [&]() { - if (running) { + if (loaded) { dspService.triggerInterrupt0(); } }); teakra.SetRecvDataHandler(1, [&]() { - if (running) { + if (loaded) { dspService.triggerInterrupt1(); } }); auto processPipeEvent = [&](bool dataEvent) { - if (!running) { + if (!loaded) { return; } @@ -85,7 +85,7 @@ TeakraDSP::TeakraDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspServ signalledSemaphore = signalledData = false; u16 slot = teakra.RecvData(2); - u16 side = slot % 2; + u16 side = slot & 1; u16 pipe = slot / 2; if (side != static_cast(PipeDirection::DSPtoCPU)) { @@ -107,6 +107,7 @@ TeakraDSP::TeakraDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspServ void TeakraDSP::reset() { teakra.Reset(); running = false; + loaded = false; signalledData = signalledSemaphore = false; } @@ -222,12 +223,13 @@ std::vector TeakraDSP::readPipe(u32 channel, u32 peer, u32 size, u32 buffer) void TeakraDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMask) { // TODO: maybe move this to the DSP service - if (running) { + if (loaded) { Helpers::warn("Loading DSP component when already loaded"); return; } teakra.Reset(); + running = true; u8* dspCode = teakra.GetDspMemory().data(); u8* dspData = dspCode + 0x40000; @@ -239,7 +241,7 @@ void TeakraDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMa // Load DSP segments to DSP RAM // TODO: verify hashes - for (unsigned int i = 0; i < dsp1.segmentCount; i++) { + for (uint i = 0; i < dsp1.segmentCount; i++) { auto& segment = dsp1.segments[i]; u32 addr = segment.dspAddr << 1; u8* src = data.data() + segment.offs; @@ -262,8 +264,6 @@ void TeakraDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMa log("LoadComponent: special segment not supported"); } - running = true; - if (syncWithDsp) { // Wait for the DSP to reply with 1s in all RECV registers for (int i = 0; i < 3; i++) { @@ -275,19 +275,23 @@ void TeakraDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMa } } + printf("DSP::LoadComponent: Semaphore value: %X\n", teakra.GetSemaphore()); + // Retrieve the pipe base address while (!teakra.RecvDataIsReady(2)) { runAudioFrame(); } pipeBaseAddr = teakra.RecvData(2); + loaded = true; } void TeakraDSP::unloadComponent() { - if (!running) { + if (!loaded) { Helpers::warn("Audio: unloadComponent called without a running program"); return; } + loaded = false; // Wait for SEND2 to be ready, then send the shutdown command to the DSP while (!teakra.SendDataIsEmpty(2)) { diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index f12e31f04..c768248c8 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -124,7 +124,6 @@ void DSPService::recvData(u32 messagePointer) { log("DSP::RecvData (register = %d)\n", registerIndex); if (registerIndex != 0) Helpers::panic("Unknown register in DSP::RecvData"); - // Return 0 if the DSP is running, otherwise 1 const u16 data = dsp->recvData(registerIndex); mem.write32(messagePointer, IPC::responseHeader(0x01, 2, 0)); From f58354af06225bb6cb923513be2897f9318cbb8e Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 18 Feb 2024 03:54:00 +0200 Subject: [PATCH 05/14] Update DSP semaphore on semaphore event signal Fixes Pokemon X and other DSP-needy games... --- include/audio/teakra_core.hpp | 2 +- include/kernel/kernel.hpp | 13 +++++++++---- include/kernel/kernel_types.hpp | 8 ++++++++ include/services/dsp.hpp | 4 ++++ src/core/audio/teakra_core.cpp | 16 ++++++++++------ src/core/kernel/events.cpp | 19 ++++++++++++++++--- src/core/services/dsp.cpp | 6 +++++- 7 files changed, 53 insertions(+), 15 deletions(-) diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp index 99ae88005..400a9e71f 100644 --- a/include/audio/teakra_core.hpp +++ b/include/audio/teakra_core.hpp @@ -79,7 +79,7 @@ namespace Audio { u16 recvData(u32 regId) override { return teakra.RecvData(regId); } bool recvDataIsReady(u32 regId) override { return teakra.RecvDataIsReady(regId); } - void setSemaphore(u16 value) override { return teakra.SetSemaphore(value); } + void setSemaphore(u16 value) override { teakra.SetSemaphore(value); } void setSemaphoreMask(u16 value) override { teakra.MaskSemaphore(value); } void writeProcessPipe(u32 channel, u32 size, u32 buffer) override; diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index e78a588a9..27db30c65 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -66,15 +66,20 @@ class Kernel { Handle makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission); public: - Handle makeEvent(ResetType resetType); // Needs to be public to be accessible to the APT/HID services - Handle makeMutex(bool locked = false); // Needs to be public to be accessible to the APT/DSP services - Handle makeSemaphore(u32 initialCount, u32 maximumCount); // Needs to be public to be accessible to the service manager port + // Needs to be public to be accessible to the APT/HID services + Handle makeEvent(ResetType resetType, Event::CallbackType callback = Event::CallbackType::None); + // Needs to be public to be accessible to the APT/DSP services + Handle makeMutex(bool locked = false); + // Needs to be public to be accessible to the service manager port + Handle makeSemaphore(u32 initialCount, u32 maximumCount); Handle makeTimer(ResetType resetType); void pollTimers(); // Signals an event, returns true on success or false if the event does not exist bool signalEvent(Handle e); - + // Run the callback for "special" events that have callbacks + void runEventCallback(Event::CallbackType callback); + void clearEvent(Handle e) { KernelObject* object = getObject(e, KernelObjectType::Event); if (object != nullptr) { diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index 01af4bd90..a68ef8d54 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -62,11 +62,19 @@ struct Process { }; struct Event { + // Some events (for now, only the DSP semaphore events) need to execute a callback when signalled + // This enum stores what kind of callback they should execute + enum class CallbackType : u32 { + None, DSPSemaphore, + }; + u64 waitlist; // A bitfield where each bit symbolizes if the thread with thread with the corresponding index is waiting on the event ResetType resetType = ResetType::OneShot; + CallbackType callback = CallbackType::None; bool fired = false; Event(ResetType resetType) : resetType(resetType), waitlist(0) {} + Event(ResetType resetType, CallbackType cb) : resetType(resetType), waitlist(0), callback(cb) {} }; struct Port { diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index 5b8c0f4f0..2c1859235 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -27,6 +27,7 @@ class DSPService { DSPEvent interrupt0; DSPEvent interrupt1; std::array pipeEvents; + u16 semaphoreMask = 0; DSPEvent& getEventRef(u32 type, u32 pipe); static constexpr size_t maxEventCount = 6; @@ -55,6 +56,9 @@ class DSPService { void reset(); void handleSyncRequest(u32 messagePointer); void setDSPCore(Audio::DSPCore* pointer) { dsp = pointer; } + + // Special callback that's ran when the semaphore event is signalled + void onSemaphoreEventSignal() { dsp->setSemaphore(semaphoreMask); } enum class SoundOutputMode : u8 { Mono = 0, diff --git a/src/core/audio/teakra_core.cpp b/src/core/audio/teakra_core.cpp index 7541185af..7c263b1f0 100644 --- a/src/core/audio/teakra_core.cpp +++ b/src/core/audio/teakra_core.cpp @@ -38,16 +38,20 @@ TeakraDSP::TeakraDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspServ // Set up callbacks for Teakra Teakra::AHBMCallback ahbm; - ahbm.read8 = [&](u32 addr) -> u8 { return mem.read8(addr); }; - ahbm.read16 = [&](u32 addr) -> u16 { return mem.read16(addr); }; - ahbm.read32 = [&](u32 addr) -> u32 { return mem.read32(addr); }; + // The AHBM read handlers read from paddrs rather than vaddrs which mem.read8 and the like use + // TODO: When we implement more efficient paddr accesses with a page table or similar, these handlers + // Should be made to properly use it, since this method is hacky and will segfault if given an invalid addr + ahbm.read8 = [&](u32 addr) -> u8 { return mem.getFCRAM()[addr - PhysicalAddrs::FCRAM]; }; + ahbm.read16 = [&](u32 addr) -> u16 { return *(u16*)&mem.getFCRAM()[addr - PhysicalAddrs::FCRAM]; }; + ahbm.read32 = [&](u32 addr) -> u32 { return *(u32*)&mem.getFCRAM()[addr - PhysicalAddrs::FCRAM]; }; - ahbm.write8 = [&](u32 addr, u8 value) { mem.write8(addr, value); }; - ahbm.write16 = [&](u32 addr, u16 value) { mem.write16(addr, value); }; - ahbm.write32 = [&](u32 addr, u32 value) { mem.write32(addr, value); }; + ahbm.write8 = [&](u32 addr, u8 value) { mem.getFCRAM()[addr - PhysicalAddrs::FCRAM] = value; }; + ahbm.write16 = [&](u32 addr, u16 value) { *(u16*)&mem.getFCRAM()[addr - PhysicalAddrs::FCRAM] = value; }; + ahbm.write32 = [&](u32 addr, u32 value) { *(u32*)&mem.getFCRAM()[addr - PhysicalAddrs::FCRAM] = value; }; teakra.SetAHBMCallback(ahbm); teakra.SetAudioCallback([=](std::array sample) { + //printf("%d %d\n", sample[0], sample[1]); // NOP for now }); diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index b2f89fbf5..7c0d30479 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -12,9 +12,9 @@ const char* Kernel::resetTypeToString(u32 type) { } } -Handle Kernel::makeEvent(ResetType resetType) { +Handle Kernel::makeEvent(ResetType resetType, Event::CallbackType callback) { Handle ret = makeObject(KernelObjectType::Event); - objects[ret].data = new Event(resetType); + objects[ret].data = new Event(resetType, callback); return ret; } @@ -42,8 +42,13 @@ bool Kernel::signalEvent(Handle handle) { event->fired = false; } } - + rescheduleThreads(); + // Run the callback for events that require a special callback + if (event->callback != Event::CallbackType::None) [[unlikely]] { + runEventCallback(event->callback); + } + return true; } @@ -230,4 +235,12 @@ void Kernel::waitSynchronizationN() { } else { Helpers::panic("WaitSynchronizationN with waitAll"); } +} + +void Kernel::runEventCallback(Event::CallbackType callback) { + switch (callback) { + case Event::CallbackType::None: break; + case Event::CallbackType::DSPSemaphore: serviceManager.getDSP().onSemaphoreEventSignal(); break; + default: Helpers::panic("Unimplemented special callback for kernel event!"); break; + } } \ No newline at end of file diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index c768248c8..fafa0f165 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -32,6 +32,7 @@ namespace Result { void DSPService::reset() { totalEventCount = 0; + semaphoreMask = 0; semaphoreEvent = std::nullopt; interrupt0 = std::nullopt; @@ -83,6 +84,7 @@ void DSPService::loadComponent(u32 messagePointer) { for (u32 i = 0; i < size; i++) { data[i] = mem.read8(buffer + i); } + printf("Loado compartment: %08X %08X %08X %08X\n", data[0], data[1], data[2], data[3]); log("DSP::LoadComponent (size = %08X, program mask = %X, data mask = %X\n", size, programMask, dataMask); dsp->loadComponent(data, programMask, dataMask); @@ -200,7 +202,7 @@ void DSPService::getSemaphoreEventHandle(u32 messagePointer) { log("DSP::GetSemaphoreEventHandle\n"); if (!semaphoreEvent.has_value()) { - semaphoreEvent = kernel.makeEvent(ResetType::OneShot); + semaphoreEvent = kernel.makeEvent(ResetType::OneShot, Event::CallbackType::DSPSemaphore); } mem.write32(messagePointer, IPC::responseHeader(0x16, 1, 2)); @@ -224,6 +226,8 @@ void DSPService::setSemaphoreMask(u32 messagePointer) { log("DSP::SetSemaphoreMask(mask = %04X)\n", mask); dsp->setSemaphoreMask(mask); + semaphoreMask = mask; + mem.write32(messagePointer, IPC::responseHeader(0x17, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } From 33eb096ef8fe205a675ae975ccd258663aaf2fce Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 18 Feb 2024 22:22:00 +0200 Subject: [PATCH 06/14] Better DSP scheduling --- include/audio/dsp_core.hpp | 14 ++++++++++++-- include/audio/null_core.hpp | 2 +- include/audio/teakra_core.hpp | 16 ++++++++++++---- include/scheduler.hpp | 1 - src/core/audio/dsp_core.cpp | 8 ++++---- src/core/audio/teakra_core.cpp | 25 +++++++++++++------------ src/core/services/gsp_gpu.cpp | 2 +- src/emulator.cpp | 3 +-- 8 files changed, 44 insertions(+), 27 deletions(-) diff --git a/include/audio/dsp_core.hpp b/include/audio/dsp_core.hpp index 498e111f3..cc674f07f 100644 --- a/include/audio/dsp_core.hpp +++ b/include/audio/dsp_core.hpp @@ -7,20 +7,30 @@ #include "helpers.hpp" #include "logger.hpp" #include "memory.hpp" +#include "scheduler.hpp" // The DSP core must have access to the DSP service to be able to trigger interrupts properly class DSPService; namespace Audio { + // There are 160 stereo samples in 1 audio frame, so 320 samples total + static constexpr u64 samplesInFrame = 160; + // 1 frame = 4096 DSP cycles = 8192 ARM11 cycles + static constexpr u64 cyclesPerFrame = samplesInFrame * 8192; + // For LLE DSP cores, we run the DSP for N cycles at a time, every N*2 arm11 cycles since the ARM11 runs twice as fast + static constexpr u64 lleSlice = 16384; + class DSPCore { protected: Memory& mem; + Scheduler& scheduler; DSPService& dspService; + MAKE_LOG_FUNCTION(log, dspLogger) public: enum class Type { Null, Teakra }; - DSPCore(Memory& mem, DSPService& dspService) : mem(mem), dspService(dspService) {} + DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService) : mem(mem), scheduler(scheduler), dspService(dspService) {} virtual void reset() = 0; virtual void runAudioFrame() = 0; @@ -36,5 +46,5 @@ namespace Audio { virtual void setSemaphoreMask(u16 value) = 0; }; - std::unique_ptr makeDSPCore(DSPCore::Type type, Memory& mem, DSPService& dspService); + std::unique_ptr makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService); } // namespace Audio \ No newline at end of file diff --git a/include/audio/null_core.hpp b/include/audio/null_core.hpp index 2a01fc07d..e06d9988f 100644 --- a/include/audio/null_core.hpp +++ b/include/audio/null_core.hpp @@ -20,7 +20,7 @@ namespace Audio { void resetAudioPipe(); public: - NullDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspService) {} + NullDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {} void reset() override; void runAudioFrame() override {} diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp index 400a9e71f..380d5404a 100644 --- a/include/audio/teakra_core.hpp +++ b/include/audio/teakra_core.hpp @@ -65,14 +65,22 @@ namespace Audio { bool signalledData; bool signalledSemaphore; + // Run 1 slice of DSP instructions + void runSlice() { + if (running) { + teakra.Run(Audio::lleSlice); + } + } + public: - TeakraDSP(Memory& mem, DSPService& dspService); + TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService); void reset() override; + + // Run 1 slice of DSP instructions and schedule the next audio frame void runAudioFrame() override { - if (running) { - teakra.Run(16384); - } + runSlice(); + scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2); } u8* getDspMemory() override { return teakra.GetDspMemory().data(); } diff --git a/include/scheduler.hpp b/include/scheduler.hpp index e251c92a6..97c50afc9 100644 --- a/include/scheduler.hpp +++ b/include/scheduler.hpp @@ -50,7 +50,6 @@ struct Scheduler { // Clear any pending events events.clear(); addEvent(Scheduler::EventType::VBlank, arm11Clock / 60); - addEvent(Scheduler::EventType::RunDSP, 16384 * 2); // Add a dummy event to always keep the scheduler non-empty addEvent(EventType::Panic, std::numeric_limits::max()); diff --git a/src/core/audio/dsp_core.cpp b/src/core/audio/dsp_core.cpp index f29af59ab..5f82c6574 100644 --- a/src/core/audio/dsp_core.cpp +++ b/src/core/audio/dsp_core.cpp @@ -3,16 +3,16 @@ #include "audio/null_core.hpp" #include "audio/teakra_core.hpp" -std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& mem, DSPService& dspService) { +std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService) { std::unique_ptr core; switch (type) { - case DSPCore::Type::Null: core = std::make_unique(mem, dspService); break; - case DSPCore::Type::Teakra: core = std::make_unique(mem, dspService); break; + case DSPCore::Type::Null: core = std::make_unique(mem, scheduler, dspService); break; + case DSPCore::Type::Teakra: core = std::make_unique(mem, scheduler, dspService); break; default: Helpers::warn("Invalid DSP core selected!"); - core = std::make_unique(mem, dspService); + core = std::make_unique(mem, scheduler, dspService); break; } diff --git a/src/core/audio/teakra_core.cpp b/src/core/audio/teakra_core.cpp index 7c263b1f0..1f609187a 100644 --- a/src/core/audio/teakra_core.cpp +++ b/src/core/audio/teakra_core.cpp @@ -34,7 +34,8 @@ struct Dsp1 { Segment segments[10]; }; -TeakraDSP::TeakraDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspService), pipeBaseAddr(0), running(false) { +TeakraDSP::TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) + : DSPCore(mem, scheduler, dspService), pipeBaseAddr(0), running(false) { // Set up callbacks for Teakra Teakra::AHBMCallback ahbm; @@ -117,7 +118,6 @@ void TeakraDSP::reset() { // https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { - Helpers::warn("Teakra: Write process pipe"); size &= 0xffff; PipeStatus status = getPipeStatus(channel, PipeDirection::CPUtoDSP); @@ -167,7 +167,7 @@ void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { if (needUpdate) { updatePipeStatus(status); while (!teakra.SendDataIsEmpty(2)) { - runAudioFrame(); + runSlice(); } teakra.SendData(2, status.slot); @@ -175,7 +175,6 @@ void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) { } std::vector TeakraDSP::readPipe(u32 channel, u32 peer, u32 size, u32 buffer) { - Helpers::warn("Teakra: Read pipe"); size &= 0xffff; PipeStatus status = getPipeStatus(channel, PipeDirection::DSPtoCPU); @@ -216,7 +215,7 @@ std::vector TeakraDSP::readPipe(u32 channel, u32 peer, u32 size, u32 buffer) if (needUpdate) { updatePipeStatus(status); while (!teakra.SendDataIsEmpty(2)) { - runAudioFrame(); + runSlice(); } teakra.SendData(2, status.slot); @@ -273,20 +272,20 @@ void TeakraDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMa for (int i = 0; i < 3; i++) { do { while (!teakra.RecvDataIsReady(i)) { - runAudioFrame(); + runSlice(); } } while (teakra.RecvData(i) != 1); } } - printf("DSP::LoadComponent: Semaphore value: %X\n", teakra.GetSemaphore()); - // Retrieve the pipe base address while (!teakra.RecvDataIsReady(2)) { - runAudioFrame(); + runSlice(); } - pipeBaseAddr = teakra.RecvData(2); + + // Schedule next DSP event + scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2); loaded = true; } @@ -296,17 +295,19 @@ void TeakraDSP::unloadComponent() { return; } loaded = false; + // Stop scheduling DSP events + scheduler.removeEvent(Scheduler::EventType::RunDSP); // Wait for SEND2 to be ready, then send the shutdown command to the DSP while (!teakra.SendDataIsEmpty(2)) { - runAudioFrame(); + runSlice(); } teakra.SendData(2, 0x8000); // Wait for shutdown to be acknowledged while (!teakra.RecvDataIsReady(2)) { - runAudioFrame(); + runSlice(); } // Read the value and discard it, completing shutdown diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 19a40a182..98b091b8f 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -125,7 +125,7 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) { void GPUService::requestInterrupt(GPUInterrupt type) { // HACK: Signal DSP events on GPU interrupt for now until we have the DSP since games need DSP events // Maybe there's a better alternative? - // kernel.signalDSPEvents(); + //kernel.signalDSPEvents(); if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet return; diff --git a/src/emulator.cpp b/src/emulator.cpp index 642b1a02a..fcab4acfe 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -26,7 +26,7 @@ Emulator::Emulator() { DSPService& dspService = kernel.getServiceManager().getDSP(); - dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Teakra, memory, dspService); + dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Teakra, memory, scheduler, dspService); dspService.setDSPCore(dsp.get()); #ifdef PANDA3DS_ENABLE_DISCORD_RPC @@ -151,7 +151,6 @@ void Emulator::pollScheduler() { case Scheduler::EventType::UpdateTimers: kernel.pollTimers(); break; case Scheduler::EventType::RunDSP: { - scheduler.addEvent(Scheduler::EventType::RunDSP, time + 16384 * 2); dsp->runAudioFrame(); break; } From 7a5bb2859e33f0ca79feb82c8027fb3145fcab75 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 19 Feb 2024 17:57:21 +0200 Subject: [PATCH 07/14] Make DSP backends properly configurable --- include/audio/dsp_core.hpp | 7 ++++++- include/audio/null_core.hpp | 4 +++- include/audio/teakra_core.hpp | 7 ++++--- include/config.hpp | 2 ++ src/config.cpp | 11 +++++++++++ src/core/audio/dsp_core.cpp | 31 +++++++++++++++++++++++++++++++ src/core/services/dsp.cpp | 1 - src/emulator.cpp | 2 +- 8 files changed, 58 insertions(+), 7 deletions(-) diff --git a/include/audio/dsp_core.hpp b/include/audio/dsp_core.hpp index cc674f07f..3f1768ff9 100644 --- a/include/audio/dsp_core.hpp +++ b/include/audio/dsp_core.hpp @@ -2,15 +2,17 @@ #include #include #include +#include +#include #include #include "helpers.hpp" #include "logger.hpp" -#include "memory.hpp" #include "scheduler.hpp" // The DSP core must have access to the DSP service to be able to trigger interrupts properly class DSPService; +class Memory; namespace Audio { // There are 160 stereo samples in 1 audio frame, so 320 samples total @@ -44,6 +46,9 @@ namespace Audio { virtual void loadComponent(std::vector& data, u32 programMask, u32 dataMask) = 0; virtual void unloadComponent() = 0; virtual void setSemaphoreMask(u16 value) = 0; + + static Audio::DSPCore::Type typeFromString(std::string inString); + static const char* typeToString(Audio::DSPCore::Type type); }; std::unique_ptr makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService); diff --git a/include/audio/null_core.hpp b/include/audio/null_core.hpp index e06d9988f..0c2ddbac4 100644 --- a/include/audio/null_core.hpp +++ b/include/audio/null_core.hpp @@ -1,6 +1,8 @@ #pragma once #include + #include "audio/dsp_core.hpp" +#include "memory.hpp" namespace Audio { class NullDSP : public DSPCore { @@ -27,7 +29,7 @@ namespace Audio { u8* getDspMemory() override { return dspRam.data(); } u16 recvData(u32 regId) override; - bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready + bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready void writeProcessPipe(u32 channel, u32 size, u32 buffer) override; std::vector readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override; diff --git a/include/audio/teakra_core.hpp b/include/audio/teakra_core.hpp index 380d5404a..57db0e4a6 100644 --- a/include/audio/teakra_core.hpp +++ b/include/audio/teakra_core.hpp @@ -1,5 +1,6 @@ #pragma once #include "audio/dsp_core.hpp" +#include "memory.hpp" #include "swap.hpp" #include "teakra/teakra.h" @@ -35,9 +36,9 @@ namespace Audio { bool isFull() const { return (readPointer ^ writePointer) == wrapBit; } bool isEmpty() const { return (readPointer ^ writePointer) == 0; } - // isWrapped: Are read and write pointers in different memory passes. - // true: xxxx]----[xxxx (data is wrapping around the end of memory) - // false: ----[xxxx]---- + // isWrapped: Are read and write pointers in different memory passes. + // true: xxxx]----[xxxx (data is wrapping around the end of memory) + // false: ----[xxxx]---- bool isWrapped() const { return (readPointer ^ writePointer) >= wrapBit; } }; static_assert(sizeof(PipeStatus) == 10, "Teakra: Pipe Status size is wrong"); diff --git a/include/config.hpp b/include/config.hpp index 155f5961d..e5c10f4bd 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include "audio/dsp_core.hpp" #include "renderer.hpp" // Remember to initialize every field here to its default value otherwise bad things will happen @@ -15,6 +16,7 @@ struct EmulatorConfig { bool shaderJitEnabled = shaderJitDefault; bool discordRpcEnabled = false; RendererType rendererType = RendererType::OpenGL; + Audio::DSPCore::Type dspType = Audio::DSPCore::Type::Null; bool sdCardInserted = true; bool sdWriteProtected = false; diff --git a/src/config.cpp b/src/config.cpp index cd4e1f797..12b112dcd 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -62,6 +62,16 @@ void EmulatorConfig::load() { } } + if (data.contains("Audio")) { + auto audioResult = toml::expect(data.at("Audio")); + if (audioResult.is_ok()) { + auto audio = audioResult.unwrap(); + + auto dspCoreName = toml::find_or(audio, "DSPEmulation", "Null"); + dspType = Audio::DSPCore::typeFromString(dspCoreName); + } + } + if (data.contains("Battery")) { auto batteryResult = toml::expect(data.at("Battery")); if (batteryResult.is_ok()) { @@ -109,6 +119,7 @@ void EmulatorConfig::save() { data["General"]["UsePortableBuild"] = usePortableBuild; data["GPU"]["EnableShaderJIT"] = shaderJitEnabled; data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType)); + data["Audio"]["DSPEmulation"] = std::string(Audio::DSPCore::typeToString(dspType)); data["Battery"]["ChargerPlugged"] = chargerPlugged; data["Battery"]["BatteryPercentage"] = batteryPercentage; diff --git a/src/core/audio/dsp_core.cpp b/src/core/audio/dsp_core.cpp index 5f82c6574..e4162e939 100644 --- a/src/core/audio/dsp_core.cpp +++ b/src/core/audio/dsp_core.cpp @@ -3,6 +3,10 @@ #include "audio/null_core.hpp" #include "audio/teakra_core.hpp" +#include +#include +#include + std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService) { std::unique_ptr core; @@ -19,3 +23,30 @@ std::unique_ptr Audio::makeDSPCore(DSPCore::Type type, Memory& m mem.setDSPMem(core->getDspMemory()); return core; } + +Audio::DSPCore::Type Audio::DSPCore::typeFromString(std::string inString) { + // Transform to lower-case to make the setting case-insensitive + std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); }); + + static const std::unordered_map map = { + {"null", Audio::DSPCore::Type::Null}, + {"none", Audio::DSPCore::Type::Null}, + {"lle", Audio::DSPCore::Type::Teakra}, + {"teakra", Audio::DSPCore::Type::Teakra}, + }; + + if (auto search = map.find(inString); search != map.end()) { + return search->second; + } + + printf("Invalid DSP type. Defaulting to null\n"); + return Audio::DSPCore::Type::Null; +} + +const char* Audio::DSPCore::typeToString(Audio::DSPCore::Type type) { + switch (type) { + case Audio::DSPCore::Type::Null: return "null"; + case Audio::DSPCore::Type::Teakra: return "teakra"; + default: return "invalid"; + } +} diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index fafa0f165..f5a6dcaec 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -84,7 +84,6 @@ void DSPService::loadComponent(u32 messagePointer) { for (u32 i = 0; i < size; i++) { data[i] = mem.read8(buffer + i); } - printf("Loado compartment: %08X %08X %08X %08X\n", data[0], data[1], data[2], data[3]); log("DSP::LoadComponent (size = %08X, program mask = %X, data mask = %X\n", size, programMask, dataMask); dsp->loadComponent(data, programMask, dataMask); diff --git a/src/emulator.cpp b/src/emulator.cpp index fcab4acfe..dbbb0c371 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -26,7 +26,7 @@ Emulator::Emulator() { DSPService& dspService = kernel.getServiceManager().getDSP(); - dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Teakra, memory, scheduler, dspService); + dsp = Audio::makeDSPCore(config.dspType, memory, scheduler, dspService); dspService.setDSPCore(dsp.get()); #ifdef PANDA3DS_ENABLE_DISCORD_RPC From f7c6ec3b5778b737f3c865cc602cbad61ebb053d Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 19 Feb 2024 18:50:41 +0200 Subject: [PATCH 08/14] Properly handle DSP interrupts in HLE --- include/audio/null_core.hpp | 8 +++--- include/kernel/kernel.hpp | 1 - include/services/dsp.hpp | 1 - include/services/service_manager.hpp | 2 -- src/core/audio/null_core.cpp | 39 +++++++++++++++++++++++++++- src/core/services/dsp.cpp | 10 ------- src/core/services/gsp_gpu.cpp | 4 --- 7 files changed, 43 insertions(+), 22 deletions(-) diff --git a/include/audio/null_core.hpp b/include/audio/null_core.hpp index 0c2ddbac4..136a76acc 100644 --- a/include/audio/null_core.hpp +++ b/include/audio/null_core.hpp @@ -20,12 +20,14 @@ namespace Audio { std::array dspRam; void resetAudioPipe(); + bool loaded = false; // Have we loaded a component? public: NullDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {} void reset() override; - void runAudioFrame() override {} + void runAudioFrame() override; + u8* getDspMemory() override { return dspRam.data(); } u16 recvData(u32 regId) override; @@ -34,8 +36,8 @@ namespace Audio { std::vector readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override; // NOPs for null DSP core - void loadComponent(std::vector& data, u32 programMask, u32 dataMask) override {} - void unloadComponent() override {} + void loadComponent(std::vector& data, u32 programMask, u32 dataMask) override; + void unloadComponent() override; void setSemaphore(u16 value) override {} void setSemaphoreMask(u16 value) override {} }; diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 27db30c65..fc7fe3f32 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -245,6 +245,5 @@ class Kernel { ServiceManager& getServiceManager() { return serviceManager; } void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); } - void signalDSPEvents() { serviceManager.signalDSPEvents(); } void clearInstructionCache(); }; \ No newline at end of file diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index 2c1859235..bc27377d3 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -66,7 +66,6 @@ class DSPService { Surround = 2 }; - void signalEvents(); void triggerPipeEvent(int index); void triggerSemaphoreEvent(); void triggerInterrupt0(); diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index b6c803b01..8d1cf3817 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -105,8 +105,6 @@ class ServiceManager { void setHIDSharedMem(u8* ptr) { hid.setSharedMem(ptr); } void setCSNDSharedMem(u8* ptr) { csnd.setSharedMemory(ptr); } - void signalDSPEvents() { dsp.signalEvents(); } - // Input function wrappers HIDService& getHID() { return hid; } NFCService& getNFC() { return nfc; } diff --git a/src/core/audio/null_core.cpp b/src/core/audio/null_core.cpp index 4d355c1e2..ec073ae74 100644 --- a/src/core/audio/null_core.cpp +++ b/src/core/audio/null_core.cpp @@ -1,5 +1,7 @@ #include "audio/null_core.hpp" +#include "services/dsp.hpp" + namespace Audio { namespace DSPPipeType { enum : u32 { @@ -45,11 +47,41 @@ namespace Audio { } void NullDSP::reset() { - for (auto& e : pipeData) e.clear(); + loaded = false; + for (auto& e : pipeData) { + e.clear(); + } // Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted resetAudioPipe(); } + + void NullDSP::loadComponent(std::vector& data, u32 programMask, u32 dataMask) { + if (loaded) { + Helpers::warn("Loading DSP component when already loaded"); + } + + loaded = true; + scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::cyclesPerFrame); + } + + void NullDSP::unloadComponent() { + if (!loaded) { + Helpers::warn("Audio: unloadComponent called without a running program"); + } + + loaded = false; + scheduler.removeEvent(Scheduler::EventType::RunDSP); + } + + void NullDSP::runAudioFrame() { + // Signal audio pipe when an audio frame is done + if (dspState == DSPState::On) [[likely]] { + dspService.triggerPipeEvent(DSPPipeType::Audio); + } + + scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::cyclesPerFrame); + } u16 NullDSP::recvData(u32 regId) { if (regId != 0) { @@ -84,6 +116,8 @@ namespace Audio { // TODO: Other initialization stuff here dspState = DSPState::On; resetAudioPipe(); + + dspService.triggerPipeEvent(DSPPipeType::Audio); break; case StateChange::Shutdown: @@ -98,6 +132,9 @@ namespace Audio { case DSPPipeType::Binary: Helpers::warn("Unimplemented write to binary pipe! Size: %d\n", size); + + // This pipe and interrupt are normally used for requests like AAC decode + dspService.triggerPipeEvent(DSPPipeType::Binary); break; default: log("Audio::NullDSP: Wrote to unimplemented pipe %d\n", channel); break; diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index f5a6dcaec..33c1703d1 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -262,16 +262,6 @@ void DSPService::invalidateDCache(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } -void DSPService::signalEvents() { - for (const DSPEvent& e : pipeEvents) { - if (e.has_value()) { kernel.signalEvent(e.value()); } - } - - if (semaphoreEvent.has_value()) { kernel.signalEvent(semaphoreEvent.value()); } - if (interrupt0.has_value()) { kernel.signalEvent(interrupt0.value()); } - if (interrupt1.has_value()) { kernel.signalEvent(interrupt1.value()); } -} - void DSPService::triggerPipeEvent(int index) { if (index < pipeCount && pipeEvents[index].has_value()) { kernel.signalEvent(*pipeEvents[index]); diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 98b091b8f..7dcc11588 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -123,10 +123,6 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) { } void GPUService::requestInterrupt(GPUInterrupt type) { - // HACK: Signal DSP events on GPU interrupt for now until we have the DSP since games need DSP events - // Maybe there's a better alternative? - //kernel.signalDSPEvents(); - if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet return; } From fa4cc35a9ce8c669eff32e4d5e546ad8de9cfd17 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 19 Feb 2024 22:04:50 +0200 Subject: [PATCH 09/14] CMake: Include teakra after xbyak for better support with the JIT branch --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c0ec2554..43391513b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,7 +89,6 @@ include_directories(third_party/toml11) include_directories(third_party/glm) add_subdirectory(third_party/cmrc) -add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL) set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/third_party/boost") set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/third_party/boost") @@ -150,6 +149,7 @@ if(HOST_X64 OR HOST_ARM64) else() message(FATAL_ERROR "Currently unsupported CPU architecture") endif() +add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL) set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp From 505abbe532ee3e0b84cfc7d64ce1605eb2ddcb14 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 20 Feb 2024 21:59:33 +0200 Subject: [PATCH 10/14] Stub NWM_UDS::Initialize to fail --- src/core/services/nwm_uds.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/services/nwm_uds.cpp b/src/core/services/nwm_uds.cpp index 9c0ef95f1..7752e5030 100644 --- a/src/core/services/nwm_uds.cpp +++ b/src/core/services/nwm_uds.cpp @@ -37,7 +37,8 @@ void NwmUdsService::initializeWithVersion(u32 messagePointer) { initialized = true; - mem.write32(messagePointer + 4, Result::Success); + // Stubbed to fail temporarily, since some games will break trying to establish networks otherwise + mem.write32(messagePointer + 4, Result::FailurePlaceholder); mem.write32(messagePointer + 8, 0); mem.write32(messagePointer + 12, eventHandle.value()); } From 1151f4a06a5d853956e8da2aa2c4d20da4bd4fdf Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:46:31 +0530 Subject: [PATCH 11/14] add proper proguard-rules --- src/pandroid/app/proguard-rules.pro | 30 ++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/pandroid/app/proguard-rules.pro b/src/pandroid/app/proguard-rules.pro index 481bb4348..0accae78c 100644 --- a/src/pandroid/app/proguard-rules.pro +++ b/src/pandroid/app/proguard-rules.pro @@ -1,16 +1,24 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# +# Pandroid Proguard Rules # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} +# Keep all JNI and C++ related classes and methods +-keepclasseswithmembernames class * { + native ; +} + +# Keep all classes with native methods +-keepclasseswithmembernames class * { + native ; +} + +# Keep all native libraries and their methods +-keep class * { + native ; +} + +# Keep all classes in the specified package and its subpackages +-keep class com.panda3ds.pandroid.** {*;} # Uncomment this to preserve the line number information for # debugging stack traces. @@ -18,4 +26,4 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile From 23d74816a288a2bf1e6a7bcfb52f790716be03d4 Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:48:07 +0530 Subject: [PATCH 12/14] enable apk optimisations --- src/pandroid/app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pandroid/app/build.gradle.kts b/src/pandroid/app/build.gradle.kts index 201d5db13..b67f94198 100644 --- a/src/pandroid/app/build.gradle.kts +++ b/src/pandroid/app/build.gradle.kts @@ -22,8 +22,8 @@ android { buildTypes { getByName("release") { - isMinifyEnabled = false - isShrinkResources = false + isMinifyEnabled = true + isShrinkResources = true isDebuggable = false signingConfig = signingConfigs.getByName("debug") proguardFiles( From 89c6a4e8f96f9d2523e41acd226481102cc18b44 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 21 Feb 2024 11:17:55 +0200 Subject: [PATCH 13/14] Android: Remove duplicate proguard rule --- src/pandroid/app/proguard-rules.pro | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pandroid/app/proguard-rules.pro b/src/pandroid/app/proguard-rules.pro index 0accae78c..59ace401b 100644 --- a/src/pandroid/app/proguard-rules.pro +++ b/src/pandroid/app/proguard-rules.pro @@ -12,11 +12,6 @@ native ; } -# Keep all native libraries and their methods --keep class * { - native ; -} - # Keep all classes in the specified package and its subpackages -keep class com.panda3ds.pandroid.** {*;} From bf80fa3ce093e77743bb5a55e1be448267d0f9b6 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 21 Feb 2024 11:25:37 +0200 Subject: [PATCH 14/14] derp --- src/pandroid/app/proguard-rules.pro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pandroid/app/proguard-rules.pro b/src/pandroid/app/proguard-rules.pro index 59ace401b..31c24c5af 100644 --- a/src/pandroid/app/proguard-rules.pro +++ b/src/pandroid/app/proguard-rules.pro @@ -7,8 +7,8 @@ native ; } -# Keep all classes with native methods --keepclasseswithmembernames class * { +# Keep all native libraries and their methods +-keep class * { native ; }