mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-06-07 19:41:38 +12:00
Merge branch 'Sync-Objects' of https://github.com/wheremyfoodat/Virtual3DS into Sync-Objects
This commit is contained in:
commit
eae9bc67dd
50 changed files with 975 additions and 122 deletions
|
@ -9,7 +9,7 @@ CPU::CPU(Memory& mem, Kernel& kernel) : mem(mem), env(mem, kernel, *this) {
|
|||
config.arch_version = Dynarmic::A32::ArchVersion::v6K;
|
||||
config.callbacks = &env;
|
||||
config.coprocessors[15] = cp15;
|
||||
// config.define_unpredictable_behaviour = true;
|
||||
config.define_unpredictable_behaviour = true;
|
||||
config.global_monitor = &exclusiveMonitor;
|
||||
config.processor_id = 0;
|
||||
|
||||
|
@ -18,7 +18,7 @@ CPU::CPU(Memory& mem, Kernel& kernel) : mem(mem), env(mem, kernel, *this) {
|
|||
|
||||
void CPU::reset() {
|
||||
setCPSR(CPSR::UserMode);
|
||||
setFPSCR(FPSCR::ThreadDefault);
|
||||
setFPSCR(FPSCR::MainThreadDefault);
|
||||
env.totalTicks = 0;
|
||||
|
||||
cp15->reset();
|
||||
|
@ -26,6 +26,7 @@ void CPU::reset() {
|
|||
jit->Reset();
|
||||
jit->ClearCache();
|
||||
jit->Regs().fill(0);
|
||||
jit->ExtRegs().fill(0);
|
||||
}
|
||||
|
||||
#endif // CPU_DYNARMIC
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
using namespace Floats;
|
||||
|
||||
GPU::GPU(Memory& mem) : mem(mem) {
|
||||
GPU::GPU(Memory& mem) : mem(mem), renderer(regs) {
|
||||
vram = new u8[vramSize];
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ void GPU::reset() {
|
|||
e.config2 = 0;
|
||||
}
|
||||
|
||||
// TODO: Reset blending, texturing, etc here
|
||||
renderer.reset();
|
||||
}
|
||||
|
||||
void GPU::drawArrays(bool indexed) {
|
||||
|
@ -49,12 +49,13 @@ void GPU::drawArrays() {
|
|||
const u32 primType = (primConfig >> 8) & 3;
|
||||
if (primType != 0 && primType != 1) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType);
|
||||
if (vertexCount > vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize");
|
||||
if (vertexCount > Renderer::vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize");
|
||||
|
||||
if ((primType == 0 && vertexCount % 3) || (primType == 1 && vertexCount < 3)) {
|
||||
Helpers::panic("Invalid vertex count for primitive. Type: %d, vert count: %d\n", primType, vertexCount);
|
||||
}
|
||||
|
||||
Vertex vertices[vertexBufferSize];
|
||||
Vertex vertices[Renderer::vertexBufferSize];
|
||||
|
||||
// Get the configuration for the index buffer, used only for indexed drawing
|
||||
u32 indexBufferConfig = regs[PICAInternalRegs::IndexBufferConfig];
|
||||
|
@ -157,7 +158,7 @@ void GPU::drawArrays() {
|
|||
OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::LineStrip
|
||||
};
|
||||
const auto shape = primTypes[primType];
|
||||
drawVertices(shape, vertices, vertexCount);
|
||||
renderer.drawVertices(shape, vertices, vertexCount);
|
||||
}
|
||||
|
||||
void GPU::fireDMA(u32 dest, u32 source, u32 size) {
|
||||
|
|
|
@ -4,8 +4,13 @@
|
|||
using namespace Floats;
|
||||
|
||||
u32 GPU::readReg(u32 address) {
|
||||
log("Ignoring read from GPU register %08X\n", address);
|
||||
return 0;
|
||||
if (address >= 0x1EF01000 && address < 0x1EF01C00) { // Internal registers
|
||||
const u32 index = (address - 0x1EF01000) / sizeof(u32);
|
||||
return readInternalReg(index);
|
||||
} else {
|
||||
log("Ignoring read to external GPU register %08X.\n", address);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::writeReg(u32 address, u32 value) {
|
||||
|
@ -54,6 +59,31 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
|||
fixedAttribMask = (value >> 16) & 0xfff; // Determines which vertex attributes are fixed for all vertices
|
||||
break;
|
||||
|
||||
case ColourBufferLoc: {
|
||||
u32 loc = (value & 0x0fffffff) << 3;
|
||||
renderer.setColourBufferLoc(loc);
|
||||
break;
|
||||
};
|
||||
|
||||
case ColourBufferFormat: {
|
||||
u32 format = (value >> 16) & 7;
|
||||
renderer.setColourFormat(format);
|
||||
break;
|
||||
}
|
||||
|
||||
case DepthBufferLoc: {
|
||||
u32 loc = (value & 0x0fffffff) << 3;
|
||||
renderer.setDepthBufferLoc(loc);
|
||||
break;
|
||||
}
|
||||
|
||||
case FramebufferSize: {
|
||||
const u32 width = value & 0x7ff;
|
||||
const u32 height = ((value >> 12) & 0x3ff) + 1;
|
||||
renderer.setFBSize(width, height);
|
||||
break;
|
||||
}
|
||||
|
||||
case VertexFloatUniformIndex:
|
||||
shaderUnit.vs.setFloatUniformIndex(value);
|
||||
break;
|
||||
|
|
|
@ -30,6 +30,7 @@ void PICAShader::run() {
|
|||
case ShaderOpcodes::MOVA: mova(instruction); break;
|
||||
case ShaderOpcodes::MUL: mul(instruction); break;
|
||||
case ShaderOpcodes::NOP: break; // Do nothing
|
||||
case ShaderOpcodes::RCP: rcp(instruction); break;
|
||||
case ShaderOpcodes::RSQ: rsq(instruction); break;
|
||||
|
||||
case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F:
|
||||
|
@ -292,6 +293,26 @@ void PICAShader::dp4(u32 instruction) {
|
|||
}
|
||||
}
|
||||
|
||||
void PICAShader::rcp(u32 instruction) {
|
||||
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
||||
const u32 src1 = (instruction >> 12) & 0x7f;
|
||||
const u32 idx = (instruction >> 19) & 3;
|
||||
const u32 dest = (instruction >> 21) & 0x1f;
|
||||
|
||||
if (idx) Helpers::panic("[PICA] RCP: idx != 0");
|
||||
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
||||
|
||||
vec4f& destVector = getDest(dest);
|
||||
f24 res = f24::fromFloat32(1.0f) / srcVec1[0];
|
||||
|
||||
u32 componentMask = operandDescriptor & 0xf;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (componentMask & (1 << i)) {
|
||||
destVector[3 - i] = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PICAShader::rsq(u32 instruction) {
|
||||
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
||||
const u32 src1 = (instruction >> 12) & 0x7f;
|
||||
|
|
29
src/core/fs/archive_ext_save_data.cpp
Normal file
29
src/core/fs/archive_ext_save_data.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "fs/archive_ext_save_data.hpp"
|
||||
#include <memory>
|
||||
|
||||
bool ExtSaveDataArchive::openFile(const FSPath& path) {
|
||||
if (path.type != PathType::Binary) {
|
||||
Helpers::panic("ExtSaveData accessed with a non-binary path in OpenFile. Type: %d", path.type);
|
||||
}
|
||||
|
||||
Helpers::panic("ExtSaveDataArchive::OpenFile: Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
ArchiveBase* ExtSaveDataArchive::openArchive(const FSPath& path) {
|
||||
if (path.type != PathType::Binary || path.size != 12) {
|
||||
Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive");
|
||||
}
|
||||
|
||||
u32 mediaType = mem.read32(path.pointer);
|
||||
u64 saveID = mem.read64(path.pointer + 4);
|
||||
|
||||
Helpers::panic("ExtSaveData: media type = %d\n", mediaType);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
std::optional<u32> ExtSaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) {
|
||||
Helpers::panic("ExtSaveDataArchive::ReadFile: Failed");
|
||||
return std::nullopt;
|
||||
}
|
|
@ -37,7 +37,7 @@ void Kernel::clearEvent() {
|
|||
logSVC("ClearEvent(event handle = %X)\n", handle);
|
||||
|
||||
if (event == nullptr) [[unlikely]] {
|
||||
Helpers::panic("Tried to clear non-existent event");
|
||||
Helpers::panic("Tried to clear non-existent event (handle = %X)", handle);
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
return;
|
||||
}
|
||||
|
@ -154,6 +154,7 @@ void Kernel::waitSynchronizationN() {
|
|||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[1] = waitAll ? handleCount - 1 : 0; // Index of the handle that triggered the exit. STUBBED
|
||||
t.status = ThreadStatus::WaitSyncAll;
|
||||
t.waitAll = waitAll;
|
||||
t.outPointer = outPointer;
|
||||
|
|
|
@ -11,7 +11,7 @@ void Kernel::switchThread(int newThreadIndex) {
|
|||
auto& oldThread = threads[currentThreadIndex];
|
||||
auto& newThread = threads[newThreadIndex];
|
||||
newThread.status = ThreadStatus::Running;
|
||||
printf("Switching from thread %d to %d\n", currentThreadIndex, newThreadIndex);
|
||||
logThread("Switching from thread %d to %d\n", currentThreadIndex, newThreadIndex);
|
||||
|
||||
// Bail early if the new thread is actually the old thread
|
||||
if (currentThreadIndex == newThreadIndex) [[unlikely]] {
|
||||
|
@ -70,8 +70,6 @@ std::optional<int> Kernel::getNextThread() {
|
|||
if (canThreadRun(t)) {
|
||||
return index;
|
||||
}
|
||||
|
||||
// TODO: Check timeouts here
|
||||
}
|
||||
|
||||
// No thread was found
|
||||
|
@ -274,7 +272,7 @@ void Kernel::setThreadPriority() {
|
|||
object->getData<Thread>()->priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
sortThreads();
|
||||
rescheduleThreads();
|
||||
}
|
||||
|
||||
|
|
|
@ -74,10 +74,12 @@ u8 Memory::read8(u32 vaddr) {
|
|||
}
|
||||
else {
|
||||
switch (vaddr) {
|
||||
case ConfigMem::BatteryState: return getBatteryState(true, true, BatteryLevel::FourBars);
|
||||
case ConfigMem::EnvInfo: return envInfo;
|
||||
case ConfigMem::KernelVersionMinor: return u8(kernelVersion & 0xff);
|
||||
case ConfigMem::KernelVersionMajor: return u8(kernelVersion >> 8);
|
||||
case ConfigMem::LedState3D: return 1; // Report the 3D LED as always off (non-zero) for now
|
||||
case ConfigMem::HeadphonesConnectedMaybe: return 0;
|
||||
default: Helpers::panic("Unimplemented 8-bit read, addr: %08X", vaddr);
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +117,7 @@ u32 Memory::read32(u32 vaddr) {
|
|||
return 0; // Set to 0 by PTM
|
||||
|
||||
case ConfigMem::AppMemAlloc: return appResourceLimits.maxCommit;
|
||||
case ConfigMem::SyscoreVer: return 2;
|
||||
case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does
|
||||
default:
|
||||
if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "renderer_gl/renderer_gl.hpp"
|
||||
#include "PICA/float_types.hpp"
|
||||
#include "PICA/gpu.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "opengl.hpp"
|
||||
|
||||
using namespace Floats;
|
||||
|
||||
|
@ -38,7 +37,7 @@ const char* fragmentShader = R"(
|
|||
float alpha = fragColour.a;
|
||||
|
||||
switch (func) {
|
||||
case 0: discard; break; // Never pass alpha test
|
||||
case 0: discard; // Never pass alpha test
|
||||
case 1: break; // Always pass alpha test
|
||||
case 2: // Pass if equal
|
||||
if (alpha != reference)
|
||||
|
@ -106,31 +105,19 @@ const char* displayFragmentShader = R"(
|
|||
}
|
||||
)";
|
||||
|
||||
void GPU::initGraphicsContext() {
|
||||
// Set up texture for top screen
|
||||
fboTexture.create(400, 240, GL_RGBA8);
|
||||
fboTexture.bind();
|
||||
void Renderer::reset() {
|
||||
depthBufferCache.reset();
|
||||
colourBufferCache.reset();
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
// Init the colour/depth buffer settings to some random defaults on reset
|
||||
colourBufferLoc = 0;
|
||||
colourBufferFormat = ColourBuffer::Formats::RGBA8;
|
||||
|
||||
fbo.createWithDrawTexture(fboTexture);
|
||||
fbo.bind(OpenGL::DrawAndReadFramebuffer);
|
||||
|
||||
GLuint rbo;
|
||||
glGenRenderbuffers(1, &rbo);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 400, 240); // use a single renderbuffer object for both a depth AND stencil buffer.
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); // now actually attach it
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
Helpers::panic("Incomplete framebuffer");
|
||||
//glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
OpenGL::setViewport(400, 240);
|
||||
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
OpenGL::clearColor();
|
||||
depthBufferLoc = 0;
|
||||
depthBufferFormat = DepthBuffer::Formats::Depth16;
|
||||
}
|
||||
|
||||
void Renderer::initGraphicsContext() {
|
||||
OpenGL::Shader vert(vertexShader, OpenGL::Vertex);
|
||||
OpenGL::Shader frag(fragmentShader, OpenGL::Fragment);
|
||||
triangleProgram.create({ vert, frag });
|
||||
|
@ -157,19 +144,19 @@ void GPU::initGraphicsContext() {
|
|||
|
||||
dummyVBO.create();
|
||||
dummyVAO.create();
|
||||
reset();
|
||||
}
|
||||
|
||||
void GPU::getGraphicsContext() {
|
||||
void Renderer::getGraphicsContext() {
|
||||
OpenGL::disableScissor();
|
||||
OpenGL::setViewport(400, 240);
|
||||
fbo.bind(OpenGL::DrawAndReadFramebuffer);
|
||||
|
||||
vbo.bind();
|
||||
vao.bind();
|
||||
triangleProgram.use();
|
||||
}
|
||||
|
||||
void GPU::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) {
|
||||
void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) {
|
||||
// Adjust alpha test if necessary
|
||||
const u32 alphaControl = regs[PICAInternalRegs::AlphaTestConfig];
|
||||
if (alphaControl != oldAlphaControl) {
|
||||
|
@ -177,6 +164,9 @@ void GPU::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count)
|
|||
glUniform1ui(alphaControlLoc, alphaControl);
|
||||
}
|
||||
|
||||
OpenGL::Framebuffer poop = getColourFBO();
|
||||
poop.bind(OpenGL::DrawAndReadFramebuffer);
|
||||
|
||||
const u32 depthControl = regs[PICAInternalRegs::DepthAndColorMask];
|
||||
bool depthEnable = depthControl & 1;
|
||||
bool depthWriteEnable = (depthControl >> 12) & 1;
|
||||
|
@ -191,8 +181,8 @@ void GPU::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count)
|
|||
f24 depthOffset = f24::fromRaw(regs[PICAInternalRegs::DepthOffset] & 0xffffff);
|
||||
printf("Depth enable: %d, func: %d, writeEnable: %d\n", depthEnable, depthFunc, depthWriteEnable);
|
||||
|
||||
if (depthScale.toFloat32() != -1.0 || depthOffset.toFloat32() != 0.0)
|
||||
Helpers::panic("TODO: Implement depth scale/offset. Remove the depth *= -1.0 from vertex shader");
|
||||
//if (depthScale.toFloat32() != -1.0 || depthOffset.toFloat32() != 0.0)
|
||||
// Helpers::panic("TODO: Implement depth scale/offset. Remove the depth *= -1.0 from vertex shader");
|
||||
|
||||
// TODO: Actually use this
|
||||
float viewportWidth = f24::fromRaw(regs[PICAInternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0;
|
||||
|
@ -220,21 +210,24 @@ constexpr u32 topScreenBuffer = 0x1f000000;
|
|||
constexpr u32 bottomScreenBuffer = 0x1f05dc00;
|
||||
|
||||
// Quick hack to display top screen for now
|
||||
void GPU::display() {
|
||||
void Renderer::display() {
|
||||
OpenGL::disableDepth();
|
||||
OpenGL::disableScissor();
|
||||
|
||||
OpenGL::bindScreenFramebuffer();
|
||||
fboTexture.bind();
|
||||
colourBufferCache[0].texture.bind();
|
||||
|
||||
displayProgram.use();
|
||||
|
||||
dummyVAO.bind();
|
||||
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0); // Clear screen colour
|
||||
OpenGL::setClearColor(0.0, 0.0, 1.0, 1.0); // Clear screen colour
|
||||
OpenGL::clearColor();
|
||||
OpenGL::setViewport(0, 240, 400, 240); // Actually draw our 3DS screen
|
||||
OpenGL::draw(OpenGL::TriangleStrip, 4);
|
||||
}
|
||||
|
||||
void GPU::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||
void Renderer::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||
return;
|
||||
log("GPU: Clear buffer\nStart: %08X End: %08X\nValue: %08X Control: %08X\n", startAddress, endAddress, value, control);
|
||||
|
||||
const float r = float((value >> 24) & 0xff) / 255.0;
|
||||
|
@ -253,4 +246,17 @@ void GPU::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control)
|
|||
|
||||
OpenGL::setClearColor(r, g, b, a);
|
||||
OpenGL::clearColor();
|
||||
}
|
||||
|
||||
OpenGL::Framebuffer Renderer::getColourFBO() {
|
||||
//We construct a colour buffer object and see if our cache has any matching colour buffers in it
|
||||
// If not, we allocate a texture & FBO for our framebuffer and store it in the cache
|
||||
ColourBuffer sampleBuffer(colourBufferLoc, colourBufferFormat, fbSize.x(), fbSize.y());
|
||||
auto buffer = colourBufferCache.find(sampleBuffer);
|
||||
|
||||
if (buffer.has_value()) {
|
||||
return buffer.value().get().fbo;
|
||||
} else {
|
||||
return colourBufferCache.add(sampleBuffer).fbo;
|
||||
}
|
||||
}
|
30
src/core/services/ac.cpp
Normal file
30
src/core/services/ac.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "services/ac.hpp"
|
||||
|
||||
namespace ACCommands {
|
||||
enum : u32 {
|
||||
SetClientVersion = 0x00400042
|
||||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void ACService::reset() {}
|
||||
|
||||
void ACService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case ACCommands::SetClientVersion: setClientVersion(messagePointer); break;
|
||||
default: Helpers::panic("AC service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
void ACService::setClientVersion(u32 messagePointer) {
|
||||
u32 version = mem.read32(messagePointer + 4);
|
||||
log("AC::SetClientVersion (version = %d)\n", version);
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
43
src/core/services/am.cpp
Normal file
43
src/core/services/am.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "services/am.hpp"
|
||||
|
||||
namespace AMCommands {
|
||||
enum : u32 {
|
||||
ListTitleInfo = 0x10070102
|
||||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void AMService::reset() {}
|
||||
|
||||
void AMService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case AMCommands::ListTitleInfo: listTitleInfo(messagePointer); break;
|
||||
default: Helpers::panic("AM service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
void AMService::listTitleInfo(u32 messagePointer) {
|
||||
log("AM::ListDLCOrLicenseTicketInfos\n"); // Yes this is the actual name
|
||||
u32 ticketCount = mem.read32(messagePointer + 4);
|
||||
u64 titleID = mem.read64(messagePointer + 8);
|
||||
u32 pointer = mem.read32(messagePointer + 24);
|
||||
|
||||
for (u32 i = 0; i < ticketCount; i++) {
|
||||
mem.write64(pointer, titleID); // Title ID
|
||||
mem.write64(pointer + 8, 0); // Ticket ID
|
||||
mem.write16(pointer + 16, 0); // Version
|
||||
mem.write16(pointer + 18, 0); // Padding
|
||||
mem.write32(pointer + 20, 0); // Size
|
||||
|
||||
pointer += 24; // = sizeof(TicketInfo)
|
||||
}
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, ticketCount);
|
||||
}
|
|
@ -6,12 +6,14 @@ namespace APTCommands {
|
|||
GetLockHandle = 0x00010040,
|
||||
Initialize = 0x00020080,
|
||||
Enable = 0x00030040,
|
||||
InquireNotification = 0x000B0040,
|
||||
ReceiveParameter = 0x000D0080,
|
||||
ReplySleepQuery = 0x003E0080,
|
||||
NotifyToWait = 0x00430040,
|
||||
AppletUtility = 0x004B00C2,
|
||||
SetApplicationCpuTimeLimit = 0x004F0080,
|
||||
GetApplicationCpuTimeLimit = 0x00500040,
|
||||
SetScreencapPostPermission = 0x00550040,
|
||||
CheckNew3DSApp = 0x01010000,
|
||||
CheckNew3DS = 0x01020000
|
||||
};
|
||||
|
@ -49,12 +51,14 @@ void APTService::handleSyncRequest(u32 messagePointer) {
|
|||
case APTCommands::CheckNew3DSApp: checkNew3DSApp(messagePointer); break;
|
||||
case APTCommands::Enable: enable(messagePointer); break;
|
||||
case APTCommands::Initialize: initialize(messagePointer); break;
|
||||
case APTCommands::InquireNotification: inquireNotification(messagePointer); break;
|
||||
case APTCommands::GetApplicationCpuTimeLimit: getApplicationCpuTimeLimit(messagePointer); break;
|
||||
case APTCommands::GetLockHandle: getLockHandle(messagePointer); break;
|
||||
case APTCommands::NotifyToWait: notifyToWait(messagePointer); break;
|
||||
case APTCommands::ReceiveParameter: receiveParameter(messagePointer); break;
|
||||
case APTCommands::ReplySleepQuery: replySleepQuery(messagePointer); break;
|
||||
case APTCommands::SetApplicationCpuTimeLimit: setApplicationCpuTimeLimit(messagePointer); break;
|
||||
case APTCommands::SetScreencapPostPermission: setScreencapPostPermission(messagePointer); break;
|
||||
default: Helpers::panic("APT service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +100,17 @@ void APTService::initialize(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0x04000000); // Translation descriptor
|
||||
mem.write32(messagePointer + 12, notificationEvent.value()); // Notification Event Handle
|
||||
mem.write32(messagePointer + 12, resumeEvent.value()); // Resume Event Handle
|
||||
mem.write32(messagePointer + 16, resumeEvent.value()); // Resume Event Handle
|
||||
}
|
||||
|
||||
void APTService::inquireNotification(u32 messagePointer) {
|
||||
log("APT::InquireNotification (STUBBED TO FAIL)\n");
|
||||
|
||||
// Thanks to our silly WaitSynchronization hacks, sometimes games will switch to the APT thread without actually getting a notif
|
||||
// After REing the APT code, I figured that making InquireNotification fail is one way of making games not crash when this happens
|
||||
// We should fix this in the future, when the sync object implementation is less hacky.
|
||||
mem.write32(messagePointer + 4, Result::Failure);
|
||||
mem.write32(messagePointer + 8, static_cast<u32>(NotificationType::None));
|
||||
}
|
||||
|
||||
void APTService::getLockHandle(u32 messagePointer) {
|
||||
|
@ -159,4 +173,13 @@ void APTService::getApplicationCpuTimeLimit(u32 messagePointer) {
|
|||
log("APT::GetApplicationCpuTimeLimit\n");
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, cpuTimeLimit);
|
||||
}
|
||||
|
||||
void APTService::setScreencapPostPermission(u32 messagePointer) {
|
||||
u32 perm = mem.read32(messagePointer + 4);
|
||||
log("APT::SetScreencapPostPermission (perm = %d)\n");
|
||||
|
||||
// Apparently only 1-3 are valid values, but I see 0 used in some games like Pokemon Rumble
|
||||
mem.write32(messagePointer, Result::Success);
|
||||
screencapPostPermission = perm;
|
||||
}
|
28
src/core/services/boss.cpp
Normal file
28
src/core/services/boss.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "services/boss.hpp"
|
||||
|
||||
namespace BOSSCommands {
|
||||
enum : u32 {
|
||||
InitializeSession = 0x00010082
|
||||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void BOSSService::reset() {}
|
||||
|
||||
void BOSSService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case BOSSCommands::InitializeSession: initializeSession(messagePointer); break;
|
||||
default: Helpers::panic("BOSS service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
void BOSSService::initializeSession(u32 messagePointer) {
|
||||
log("BOSS::InitializeSession (stubbed)\n");
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
|
@ -37,7 +37,12 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
|
|||
mem.write8(output, static_cast<u8>(DSPService::SoundOutputMode::Stereo));
|
||||
} else if (size == 1 && blockID == 0xA0002){ // System language
|
||||
mem.write8(output, static_cast<u8>(LanguageCodes::English));
|
||||
} else if (size == 0x20 && blockID == 0x50005) {
|
||||
} else if (size == 4 && blockID == 0xB0000) { // Country info
|
||||
mem.write8(output, 0); // Unknown
|
||||
mem.write8(output + 1, 0); // Unknown
|
||||
mem.write8(output + 2, 2); // Province (Temporarily stubbed to Washington DC like Citra)
|
||||
mem.write8(output + 3, 49); // Country code (Temporarily stubbed to USA like Citra)
|
||||
}else if (size == 0x20 && blockID == 0x50005) {
|
||||
printf("[Unimplemented] Read stereo display settings from NAND\n");
|
||||
} else {
|
||||
Helpers::panic("Unhandled GetConfigInfoBlk2 configuration");
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace DSPCommands {
|
|||
WriteProcessPipe = 0x000D0082,
|
||||
ReadPipeIfPossible = 0x001000C0,
|
||||
LoadComponent = 0x001100C2,
|
||||
FlushDataCache = 0x00130082,
|
||||
RegisterInterruptEvents = 0x00150082,
|
||||
GetSemaphoreHandle = 0x00160000,
|
||||
SetSemaphoreMask = 0x00170040,
|
||||
|
@ -30,6 +31,7 @@ void DSPService::handleSyncRequest(u32 messagePointer) {
|
|||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case DSPCommands::ConvertProcessAddressFromDspDram: convertProcessAddressFromDspDram(messagePointer); break;
|
||||
case DSPCommands::FlushDataCache: flushDataCache(messagePointer); break;
|
||||
case DSPCommands::GetHeadphoneStatus: getHeadphoneStatus(messagePointer); break;
|
||||
case DSPCommands::GetSemaphoreHandle: getSemaphoreHandle(messagePointer); break;
|
||||
case DSPCommands::LoadComponent: loadComponent(messagePointer); break;
|
||||
|
@ -133,4 +135,13 @@ void DSPService::writeProcessPipe(u32 messagePointer) {
|
|||
|
||||
log("DSP::writeProcessPipe (channel = %d, size = %X, buffer = %08X)\n", channel, size, buffer);
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void DSPService::flushDataCache(u32 messagePointer) {
|
||||
u32 address = mem.read32(messagePointer + 4);
|
||||
u32 size = mem.read32(messagePointer + 8);
|
||||
u32 process = mem.read32(messagePointer + 16);
|
||||
|
||||
log("DSP::FlushDataCache (addr = %08X, size = %08X, process = %X)\n", address, size, process);
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
56
src/core/services/frd.cpp
Normal file
56
src/core/services/frd.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include "services/frd.hpp"
|
||||
|
||||
namespace FRDCommands {
|
||||
enum : u32 {
|
||||
SetClientSdkVersion = 0x00320042,
|
||||
GetMyFriendKey = 0x00050000,
|
||||
GetMyPresence = 0x00080000
|
||||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void FRDService::reset() {}
|
||||
|
||||
void FRDService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case FRDCommands::GetMyFriendKey: getMyFriendKey(messagePointer); break;
|
||||
case FRDCommands::GetMyPresence: getMyPresence(messagePointer); break;
|
||||
case FRDCommands::SetClientSdkVersion: setClientSDKVersion(messagePointer); break;
|
||||
default: Helpers::panic("FRD service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
void FRDService::getMyFriendKey(u32 messagePointer) {
|
||||
log("FRD::GetMyFriendKey");
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0); // Principal ID
|
||||
mem.write32(messagePointer + 12, 0); // Padding (?)
|
||||
mem.write32(messagePointer + 16, 0); // Local friend code
|
||||
mem.write32(messagePointer + 20, 0);
|
||||
}
|
||||
|
||||
void FRDService::getMyPresence(u32 messagePointer) {
|
||||
static constexpr u32 presenceSize = 0x12C; // A presence seems to be 12C bytes of data, not sure what it contains
|
||||
log("FRD::GetMyPresence\n");
|
||||
u32 buffer = mem.read32(messagePointer + 0x104); // Buffer to write presence info to.
|
||||
|
||||
for (u32 i = 0; i < presenceSize; i += 4) { // Clear presence info with 0s for now
|
||||
mem.write32(buffer + i, 0);
|
||||
}
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void FRDService::setClientSDKVersion(u32 messagePointer) {
|
||||
u32 version = mem.read32(messagePointer + 4);
|
||||
log("FRD::SetClientSdkVersion (version = %d)\n", version);
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
|
@ -8,6 +8,7 @@ namespace FSCommands {
|
|||
OpenFileDirectly = 0x08030204,
|
||||
OpenArchive = 0x080C00C2,
|
||||
CloseArchive = 0x080E0080,
|
||||
IsSdmcDetected = 0x08170000,
|
||||
InitializeWithSdkVersion = 0x08610042,
|
||||
SetPriority = 0x08620040,
|
||||
GetPriority = 0x08630000
|
||||
|
@ -29,6 +30,7 @@ ArchiveBase* FSService::getArchiveFromID(u32 id) {
|
|||
switch (id) {
|
||||
case ArchiveID::SelfNCCH: return &selfNcch;
|
||||
case ArchiveID::SaveData: return &saveData;
|
||||
case ArchiveID::SharedExtSaveData: return &sharedExtSaveData;
|
||||
case ArchiveID::SDMC: return &sdmc;
|
||||
default:
|
||||
Helpers::panic("Unknown archive. ID: %d\n", id);
|
||||
|
@ -77,6 +79,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
|
|||
case FSCommands::GetPriority: getPriority(messagePointer); break;
|
||||
case FSCommands::Initialize: initialize(messagePointer); break;
|
||||
case FSCommands::InitializeWithSdkVersion: initializeWithSdkVersion(messagePointer); break;
|
||||
case FSCommands::IsSdmcDetected: isSdmcDetected(messagePointer); break;
|
||||
case FSCommands::OpenArchive: openArchive(messagePointer); break;
|
||||
case FSCommands::OpenFile: openFile(messagePointer); break;
|
||||
case FSCommands::OpenFileDirectly: openFileDirectly(messagePointer); break;
|
||||
|
@ -210,4 +213,10 @@ void FSService::setPriority(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
priority = value;
|
||||
}
|
||||
|
||||
void FSService::isSdmcDetected(u32 messagePointer) {
|
||||
log("FS::IsSdmcDetected\n");
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0); // Whether SD is detected. For now we emulate a 3DS without an SD.
|
||||
}
|
|
@ -11,7 +11,8 @@ namespace ServiceCommands {
|
|||
FlushDataCache = 0x00080082,
|
||||
SetLCDForceBlack = 0x000B0040,
|
||||
TriggerCmdReqQueue = 0x000C0000,
|
||||
SetInternalPriorities = 0x001E0080
|
||||
SetInternalPriorities = 0x001E0080,
|
||||
StoreDataCache = 0x001F0082
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -47,6 +48,7 @@ void GPUService::handleSyncRequest(u32 messagePointer) {
|
|||
case ServiceCommands::SetAxiConfigQoSMode: setAxiConfigQoSMode(messagePointer); break;
|
||||
case ServiceCommands::SetInternalPriorities: setInternalPriorities(messagePointer); break;
|
||||
case ServiceCommands::SetLCDForceBlack: setLCDForceBlack(messagePointer); break;
|
||||
case ServiceCommands::StoreDataCache: storeDataCache(messagePointer); break;
|
||||
case ServiceCommands::TriggerCmdReqQueue: [[likely]] triggerCmdReqQueue(messagePointer); break;
|
||||
case ServiceCommands::WriteHwRegs: writeHwRegs(messagePointer); break;
|
||||
case ServiceCommands::WriteHwRegsWithMask: writeHwRegsWithMask(messagePointer); break;
|
||||
|
@ -184,6 +186,15 @@ void GPUService::flushDataCache(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void GPUService::storeDataCache(u32 messagePointer) {
|
||||
u32 address = mem.read32(messagePointer + 4);
|
||||
u32 size = mem.read32(messagePointer + 8);
|
||||
u32 processHandle = handle = mem.read32(messagePointer + 16);
|
||||
log("GSP::GPU::StoreDataCache(address = %08X, size = %X, process = %X\n", address, size, processHandle);
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void GPUService::setLCDForceBlack(u32 messagePointer) {
|
||||
u32 flag = mem.read32(messagePointer + 4);
|
||||
log("GSP::GPU::SetLCDForceBlank(flag = %d)\n", flag);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "services/hid.hpp"
|
||||
#include <bit>
|
||||
|
||||
namespace HIDCommands {
|
||||
enum : u32 {
|
||||
|
@ -49,19 +50,25 @@ void HIDService::enableGyroscopeLow(u32 messagePointer) {
|
|||
|
||||
void HIDService::getGyroscopeLowCalibrateParam(u32 messagePointer) {
|
||||
log("HID::GetGyroscopeLowCalibrateParam\n");
|
||||
constexpr s16 unit = 6700; // Approximately from Citra which took it from hardware
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
// Fill calibration data with 0s since we don't have gyro
|
||||
for (int i = 0; i < 5; i++) {
|
||||
mem.write32(messagePointer + 8 + i * 4, 0);
|
||||
// Fill calibration data (for x/y/z depending on i)
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const u32 pointer = messagePointer + 8 + i * 3 * sizeof(u16); // Pointer to write the calibration info for the current coordinate
|
||||
|
||||
mem.write16(pointer, 0); // Zero point
|
||||
mem.write16(pointer + 1 * sizeof(u16), unit); // Positive unit point
|
||||
mem.write16(pointer + 2 * sizeof(u16), -unit); // Negative unit point
|
||||
}
|
||||
}
|
||||
|
||||
void HIDService::getGyroscopeCoefficient(u32 messagePointer) {
|
||||
log("HID::GetGyroscopeLowRawToDpsCoefficient\n");
|
||||
|
||||
constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
// This should write a coeff value here but we don't have gyro so
|
||||
mem.write32(messagePointer + 8, std::bit_cast<u32, float>(gyroscopeCoeff));
|
||||
}
|
||||
|
||||
void HIDService::getIPCHandles(u32 messagePointer) {
|
||||
|
|
28
src/core/services/nim.cpp
Normal file
28
src/core/services/nim.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "services/nim.hpp"
|
||||
|
||||
namespace NIMCommands {
|
||||
enum : u32 {
|
||||
Initialize = 0x00210000
|
||||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void NIMService::reset() {}
|
||||
|
||||
void NIMService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case NIMCommands::Initialize: initialize(messagePointer); break;
|
||||
default: Helpers::panic("NIM service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
void NIMService::initialize(u32 messagePointer) {
|
||||
log("NIM::Initialize\n");
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
|
@ -2,21 +2,28 @@
|
|||
#include "kernel.hpp"
|
||||
|
||||
ServiceManager::ServiceManager(std::array<u32, 16>& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel)
|
||||
: regs(regs), mem(mem), kernel(kernel), apt(mem, kernel), cecd(mem), cfg(mem), dsp(mem), hid(mem),
|
||||
fs(mem, kernel), gsp_gpu(mem, gpu, currentPID), gsp_lcd(mem), mic(mem), ndm(mem), ptm(mem) {}
|
||||
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), apt(mem, kernel), cecd(mem), cfg(mem),
|
||||
dsp(mem), hid(mem), frd(mem), fs(mem, kernel), gsp_gpu(mem, gpu, currentPID), gsp_lcd(mem), mic(mem),
|
||||
nim(mem), ndm(mem), ptm(mem) {}
|
||||
|
||||
static constexpr int MAX_NOTIFICATION_COUNT = 16;
|
||||
|
||||
// Reset every single service
|
||||
void ServiceManager::reset() {
|
||||
ac.reset();
|
||||
am.reset();
|
||||
apt.reset();
|
||||
boss.reset();
|
||||
cecd.reset();
|
||||
cfg.reset();
|
||||
dsp.reset();
|
||||
hid.reset();
|
||||
frd.reset();
|
||||
fs.reset();
|
||||
gsp_gpu.reset();
|
||||
gsp_lcd.reset();
|
||||
mic.reset();
|
||||
nim.reset();
|
||||
ndm.reset();
|
||||
ptm.reset();
|
||||
|
||||
|
@ -60,6 +67,7 @@ void ServiceManager::handleSyncRequest(u32 messagePointer) {
|
|||
case Commands::ReceiveNotification: receiveNotification(messagePointer); break;
|
||||
case Commands::RegisterClient: registerClient(messagePointer); break;
|
||||
case Commands::GetServiceHandle: getServiceHandle(messagePointer); break;
|
||||
case Commands::Subscribe: subscribe(messagePointer); break;
|
||||
default: Helpers::panic("Unknown \"srv:\" command: %08X", header);
|
||||
}
|
||||
}
|
||||
|
@ -79,12 +87,18 @@ void ServiceManager::getServiceHandle(u32 messagePointer) {
|
|||
std::string service = mem.readString(messagePointer + 4, 8);
|
||||
log("srv::getServiceHandle (Service: %s, nameLength: %d, flags: %d)\n", service.c_str(), nameLength, flags);
|
||||
|
||||
if (service == "APT:S") { // TODO: APT:A, APT:S and APT:U are slightly different
|
||||
if (service == "ac:u") {
|
||||
handle = KernelHandles::AC;
|
||||
} else if (service == "am:app") {
|
||||
handle = KernelHandles::AM;
|
||||
} else if (service == "APT:S") { // TODO: APT:A, APT:S and APT:U are slightly different
|
||||
handle = KernelHandles::APT;
|
||||
} else if (service == "APT:A") {
|
||||
handle = KernelHandles::APT;
|
||||
} else if (service == "APT:U") {
|
||||
handle = KernelHandles::APT;
|
||||
} else if (service == "boss:U") {
|
||||
handle = KernelHandles::BOSS;
|
||||
} else if (service == "cecd:u") {
|
||||
handle = KernelHandles::CECD;
|
||||
} else if (service == "cfg:u") {
|
||||
|
@ -93,7 +107,9 @@ void ServiceManager::getServiceHandle(u32 messagePointer) {
|
|||
handle = KernelHandles::DSP;
|
||||
} else if (service == "hid:USER") {
|
||||
handle = KernelHandles::HID;
|
||||
} else if (service == "fs:USER") {
|
||||
} else if (service == "frd:u") {
|
||||
handle = KernelHandles::FRD;
|
||||
} else if (service == "fs:USER") {
|
||||
handle = KernelHandles::FS;
|
||||
} else if (service == "gsp::Gpu") {
|
||||
handle = KernelHandles::GPU;
|
||||
|
@ -103,6 +119,8 @@ void ServiceManager::getServiceHandle(u32 messagePointer) {
|
|||
handle = KernelHandles::MIC;
|
||||
} else if (service == "ndm:u") {
|
||||
handle = KernelHandles::NDM;
|
||||
} else if (service == "nim:aoc") {
|
||||
handle = KernelHandles::NIM;
|
||||
} else if (service == "ptm:u") {
|
||||
handle = KernelHandles::PTM;
|
||||
} else {
|
||||
|
@ -134,17 +152,29 @@ void ServiceManager::receiveNotification(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 8, 0); // Notification ID
|
||||
}
|
||||
|
||||
void ServiceManager::subscribe(u32 messagePointer) {
|
||||
u32 id = mem.read32(messagePointer + 4);
|
||||
log("srv::Subscribe (id = %d) (stubbed)\n", id);
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
|
||||
switch (handle) {
|
||||
case KernelHandles::AC: ac.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::AM: am.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::APT: apt.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::BOSS: boss.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::CECD: cecd.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::CFG: cfg.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::DSP: dsp.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::FRD: frd.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::FS: fs.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::GPU: [[likely]] gsp_gpu.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::LCD: gsp_lcd.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::MIC: mic.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::NIM: nim.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::NDM: ndm.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::PTM: ptm.handleSyncRequest(messagePointer); break;
|
||||
default: Helpers::panic("Sent IPC message to unknown service %08X\n Command: %08X", handle, mem.read32(messagePointer));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue