mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 22:55:40 +12:00
Hook DSP interface to DSP service HLE
Co-Authored-By: PSISP <12768103+psi-rockin@users.noreply.github.com>
This commit is contained in:
parent
0a51a80d91
commit
363c71e66c
6 changed files with 31 additions and 136 deletions
|
@ -12,7 +12,12 @@ namespace Audio {
|
||||||
TeakraDSP(Memory& mem);
|
TeakraDSP(Memory& mem);
|
||||||
|
|
||||||
void reset() override;
|
void reset() override;
|
||||||
void runAudioFrame() override;
|
void runAudioFrame() override {
|
||||||
|
if (running) {
|
||||||
|
teakra.Run(16384);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u8* getDspMemory() override { return teakra.GetDspMemory().data(); }
|
u8* getDspMemory() override { return teakra.GetDspMemory().data(); }
|
||||||
|
|
||||||
u16 recvData(u32 regId) override { return teakra.RecvData(regId); }
|
u16 recvData(u32 regId) override { return teakra.RecvData(regId); }
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include "audio/dsp_core.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "result/result.hpp"
|
#include "result/result.hpp"
|
||||||
|
|
||||||
namespace DSPPipeType {
|
|
||||||
enum : u32 {
|
|
||||||
Debug = 0, DMA = 1, Audio = 2, Binary = 3
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Circular dependencies!
|
// Circular dependencies!
|
||||||
class Kernel;
|
class Kernel;
|
||||||
|
|
||||||
|
@ -19,15 +14,11 @@ class DSPService {
|
||||||
Handle handle = KernelHandles::DSP;
|
Handle handle = KernelHandles::DSP;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
Kernel& kernel;
|
Kernel& kernel;
|
||||||
|
Audio::DSPCore* dsp = nullptr;
|
||||||
MAKE_LOG_FUNCTION(log, dspServiceLogger)
|
MAKE_LOG_FUNCTION(log, dspServiceLogger)
|
||||||
|
|
||||||
enum class DSPState : u32 {
|
|
||||||
Off, On, Slep
|
|
||||||
};
|
|
||||||
|
|
||||||
// Number of DSP pipes
|
// Number of DSP pipes
|
||||||
static constexpr size_t pipeCount = 8;
|
static constexpr size_t pipeCount = 8;
|
||||||
DSPState dspState;
|
|
||||||
|
|
||||||
// DSP service event handles
|
// DSP service event handles
|
||||||
using DSPEvent = std::optional<Handle>;
|
using DSPEvent = std::optional<Handle>;
|
||||||
|
@ -36,10 +27,6 @@ class DSPService {
|
||||||
DSPEvent interrupt0;
|
DSPEvent interrupt0;
|
||||||
DSPEvent interrupt1;
|
DSPEvent interrupt1;
|
||||||
std::array<DSPEvent, pipeCount> pipeEvents;
|
std::array<DSPEvent, pipeCount> pipeEvents;
|
||||||
std::array<std::vector<u8>, pipeCount> pipeData; // The data of each pipe
|
|
||||||
|
|
||||||
void resetAudioPipe();
|
|
||||||
std::vector<u8> readPipe(u32 pipe, u32 size);
|
|
||||||
|
|
||||||
DSPEvent& getEventRef(u32 type, u32 pipe);
|
DSPEvent& getEventRef(u32 type, u32 pipe);
|
||||||
static constexpr size_t maxEventCount = 6;
|
static constexpr size_t maxEventCount = 6;
|
||||||
|
@ -67,6 +54,7 @@ public:
|
||||||
DSPService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
DSPService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||||
void reset();
|
void reset();
|
||||||
void handleSyncRequest(u32 messagePointer);
|
void handleSyncRequest(u32 messagePointer);
|
||||||
|
void setDSPCore(Audio::DSPCore* pointer) { dsp = pointer; }
|
||||||
|
|
||||||
enum class SoundOutputMode : u8 {
|
enum class SoundOutputMode : u8 {
|
||||||
Mono = 0,
|
Mono = 0,
|
||||||
|
|
|
@ -110,4 +110,5 @@ class ServiceManager {
|
||||||
// Input function wrappers
|
// Input function wrappers
|
||||||
HIDService& getHID() { return hid; }
|
HIDService& getHID() { return hid; }
|
||||||
NFCService& getNFC() { return nfc; }
|
NFCService& getNFC() { return nfc; }
|
||||||
|
DSPService& getDSP() { return dsp; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -55,12 +55,6 @@ void TeakraDSP::reset() {
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeakraDSP::runAudioFrame() {
|
|
||||||
if (running) {
|
|
||||||
teakra.Run(16384);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) {
|
void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,13 +31,7 @@ namespace Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSPService::reset() {
|
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;
|
totalEventCount = 0;
|
||||||
dspState = DSPState::Off;
|
|
||||||
|
|
||||||
semaphoreEvent = std::nullopt;
|
semaphoreEvent = std::nullopt;
|
||||||
interrupt0 = 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<u16, 16> 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<u8>& 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) {
|
void DSPService::handleSyncRequest(u32 messagePointer) {
|
||||||
const u32 command = mem.read32(messagePointer);
|
const u32 command = mem.read32(messagePointer);
|
||||||
switch (command) {
|
switch (command) {
|
||||||
|
@ -117,8 +77,16 @@ void DSPService::loadComponent(u32 messagePointer) {
|
||||||
u32 size = mem.read32(messagePointer + 4);
|
u32 size = mem.read32(messagePointer + 4);
|
||||||
u32 programMask = mem.read32(messagePointer + 8);
|
u32 programMask = mem.read32(messagePointer + 8);
|
||||||
u32 dataMask = mem.read32(messagePointer + 12);
|
u32 dataMask = mem.read32(messagePointer + 12);
|
||||||
|
u32 buffer = mem.read32(messagePointer + 20);
|
||||||
|
|
||||||
|
std::vector<u8> 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);
|
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, IPC::responseHeader(0x11, 2, 2));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, 1); // Component loaded
|
mem.write32(messagePointer + 8, 1); // Component loaded
|
||||||
|
@ -128,32 +96,12 @@ void DSPService::loadComponent(u32 messagePointer) {
|
||||||
|
|
||||||
void DSPService::unloadComponent(u32 messagePointer) {
|
void DSPService::unloadComponent(u32 messagePointer) {
|
||||||
log("DSP::UnloadComponent\n");
|
log("DSP::UnloadComponent\n");
|
||||||
|
dsp->unloadComponent();
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x12, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x12, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> 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<u8>& data = pipeData[pipe];
|
|
||||||
size = std::min<u32>(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<u8> out(data.begin(), data.begin() + size);
|
|
||||||
data.erase(data.begin(), data.begin() + size);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSPService::readPipeIfPossible(u32 messagePointer) {
|
void DSPService::readPipeIfPossible(u32 messagePointer) {
|
||||||
u32 channel = mem.read32(messagePointer + 4);
|
u32 channel = mem.read32(messagePointer + 4);
|
||||||
u32 peer = mem.read32(messagePointer + 8);
|
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);
|
log("DSP::ReadPipeIfPossible (channel = %d, peer = %d, size = %04X, buffer = %08X)\n", channel, peer, size, buffer);
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x10, 2, 2));
|
mem.write32(messagePointer, IPC::responseHeader(0x10, 2, 2));
|
||||||
|
|
||||||
std::vector<u8> data = readPipe(channel, size);
|
std::vector<u8> data = dsp->readPipe(channel, peer, size, buffer);
|
||||||
for (uint i = 0; i < data.size(); i++) {
|
for (uint i = 0; i < data.size(); i++) {
|
||||||
mem.write8(buffer + i, data[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");
|
if (registerIndex != 0) Helpers::panic("Unknown register in DSP::RecvData");
|
||||||
|
|
||||||
// Return 0 if the DSP is running, otherwise 1
|
// 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, IPC::responseHeader(0x01, 2, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write16(messagePointer + 8, ret);
|
mem.write16(messagePointer + 8, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSPService::recvDataIsReady(u32 messagePointer) {
|
void DSPService::recvDataIsReady(u32 messagePointer) {
|
||||||
const u32 registerIndex = mem.read32(messagePointer + 4);
|
const u32 registerIndex = mem.read32(messagePointer + 4);
|
||||||
log("DSP::RecvDataIsReady (register = %d)\n", registerIndex);
|
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, IPC::responseHeader(0x02, 2, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
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) {
|
DSPService::DSPEvent& DSPService::getEventRef(u32 type, u32 pipe) {
|
||||||
|
@ -236,7 +185,6 @@ void DSPService::registerInterruptEvents(u32 messagePointer) {
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
|
|
||||||
totalEventCount++;
|
totalEventCount++;
|
||||||
kernel.signalEvent(eventHandle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,6 +215,7 @@ void DSPService::setSemaphore(u32 messagePointer) {
|
||||||
const u16 value = mem.read16(messagePointer + 4);
|
const u16 value = mem.read16(messagePointer + 4);
|
||||||
log("DSP::SetSemaphore(value = %04X)\n", value);
|
log("DSP::SetSemaphore(value = %04X)\n", value);
|
||||||
|
|
||||||
|
dsp->setSemaphore(value);
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x7, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x7, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
@ -275,6 +224,7 @@ void DSPService::setSemaphoreMask(u32 messagePointer) {
|
||||||
const u16 mask = mem.read16(messagePointer + 4);
|
const u16 mask = mem.read16(messagePointer + 4);
|
||||||
log("DSP::SetSemaphoreMask(mask = %04X)\n", mask);
|
log("DSP::SetSemaphoreMask(mask = %04X)\n", mask);
|
||||||
|
|
||||||
|
dsp->setSemaphoreMask(mask);
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x17, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x17, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
@ -285,51 +235,7 @@ void DSPService::writeProcessPipe(u32 messagePointer) {
|
||||||
const u32 buffer = mem.read32(messagePointer + 16);
|
const u32 buffer = mem.read32(messagePointer + 16);
|
||||||
log("DSP::writeProcessPipe (channel = %d, size = %X, buffer = %08X)\n", channel, size, buffer);
|
log("DSP::writeProcessPipe (channel = %d, size = %X, buffer = %08X)\n", channel, size, buffer);
|
||||||
|
|
||||||
enum class StateChange : u8 {
|
dsp->writeProcessPipe(channel, size, buffer);
|
||||||
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<StateChange>(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0xD, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0xD, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@ Emulator::Emulator()
|
||||||
httpServer(this)
|
httpServer(this)
|
||||||
#endif
|
#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
|
#ifdef PANDA3DS_ENABLE_DISCORD_RPC
|
||||||
if (config.discordRpcEnabled) {
|
if (config.discordRpcEnabled) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue