Merge branch 'Sync-Objects' of https://github.com/wheremyfoodat/Virtual3DS into Sync-Objects

This commit is contained in:
wheremyfoodat 2023-01-10 00:37:27 +02:00
commit eae9bc67dd
50 changed files with 975 additions and 122 deletions

View file

@ -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

View file

@ -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) {

View file

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

View file

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

View 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;
}

View file

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

View file

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

View file

@ -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) {

View file

@ -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
View 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
View 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);
}

View file

@ -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;
}

View 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);
}

View file

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

View file

@ -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
View 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);
}

View file

@ -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.
}

View file

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

View file

@ -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
View 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);
}

View file

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