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

This commit is contained in:
wheremyfoodat 2023-04-23 21:32:10 +03:00
commit 8cfb038226
49 changed files with 1256 additions and 138 deletions

View file

@ -111,6 +111,14 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
}
break;
// Restart immediate mode primitive drawing
case PrimitiveRestart:
if (value & 1) {
immediateModeAttrIndex = 0;
immediateModeVertIndex = 0;
}
break;
case FixedAttribData0: case FixedAttribData1: case FixedAttribData2:
fixedAttrBuff[fixedAttribCount++] = value;
@ -141,10 +149,35 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
immediateModeAttrIndex = 0;
immediateModeVertices[immediateModeVertIndex++] = v;
// Get primitive type
const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig];
const u32 primType = (primConfig >> 8) & 3;
// If we've reached 3 verts, issue a draw call
// Handle rendering depending on the primitive type
if (immediateModeVertIndex == 3) {
renderer.drawVertices(OpenGL::Triangle, &immediateModeVertices[0], 3);
immediateModeVertIndex = 0;
switch (primType) {
// Triangle or geometry primitive. Draw a triangle and discard all vertices
case 0: case 3:
immediateModeVertIndex = 0;
break;
// Triangle strip. Draw triangle, discard first vertex and keep the last 2
case 1:
immediateModeVertIndex = 2;
immediateModeVertices[0] = immediateModeVertices[1];
immediateModeVertices[1] = immediateModeVertices[2];
break;
// Triangle fan. Draw triangle, keep first vertex and last vertex, discard second vertex
case 2:
immediateModeVertIndex = 2;
immediateModeVertices[1] = immediateModeVertices[2];
break;
}
}
}
} else { // Writing to fixed attributes 13 and 14 probably does nothing, but we'll see

View file

@ -25,6 +25,7 @@ Handle Kernel::makeArbiter() {
// Result CreateAddressArbiter(Handle* arbiter)
void Kernel::createAddressArbiter() {
logSVC("CreateAddressArbiter\n");
regs[0] = SVCResult::Success;
regs[1] = makeArbiter();
}

View file

@ -1,5 +1,7 @@
#include "kernel.hpp"
#include "cpu.hpp"
#include <bit>
#include <utility>
const char* Kernel::resetTypeToString(u32 type) {
switch (type) {
@ -16,8 +18,55 @@ Handle Kernel::makeEvent(ResetType resetType) {
return ret;
}
bool Kernel::signalEvent(Handle handle) {
KernelObject* object = getObject(handle, KernelObjectType::Event);
if (object == nullptr) [[unlikely]] {
Helpers::panic("Tried to signal non-existent event");
return false;
}
Event* event = object->getData<Event>();
event->fired = true;
// One shot events go back to being not fired once they are signaled
if (event->resetType == ResetType::Pulse) {
event->fired = false;
}
// Wake up every single thread in the waitlist using a bit scanning algorithm
while (event->waitlist != 0) {
const uint index = std::countr_zero(event->waitlist); // Get one of the set bits to see which thread is waiting
event->waitlist ^= (1ull << index); // Remove thread from waitlist by toggling its bit
// Get the thread we'll be signalling
Thread& t = threads[index];
switch (t.status) {
case ThreadStatus::WaitSync1:
t.status = ThreadStatus::Ready;
break;
case ThreadStatus::WaitSyncAny:
t.status = ThreadStatus::Ready;
// Get the index of the event in the object's waitlist, write it to r1
for (size_t i = 0; i < t.waitList.size(); i++) {
if (t.waitList[i] == handle) {
t.gprs[1] = i;
break;
}
}
break;
case ThreadStatus::WaitSyncAll:
Helpers::panic("SignalEvent: Thread on WaitSyncAll");
break;
}
}
return true;
}
// Result CreateEvent(Handle* event, ResetType resetType)
void Kernel::createEvent() {
void Kernel::svcCreateEvent() {
const u32 outPointer = regs[0];
const u32 resetType = regs[1];
@ -31,7 +80,7 @@ void Kernel::createEvent() {
}
// Result ClearEvent(Handle event)
void Kernel::clearEvent() {
void Kernel::svcClearEvent() {
const Handle handle = regs[0];
const auto event = getObject(handle, KernelObjectType::Event);
logSVC("ClearEvent(event handle = %X)\n", handle);
@ -47,38 +96,15 @@ void Kernel::clearEvent() {
}
// Result SignalEvent(Handle event)
void Kernel::signalEvent() {
void Kernel::svcSignalEvent() {
const Handle handle = regs[0];
const auto event = getObject(handle, KernelObjectType::Event);
logSVC("SignalEvent(event handle = %X)\n", handle);
if (event == nullptr) [[unlikely]] {
logThread("Signalled non-existent event: %X\n", handle);
if (!signalEvent(handle)) {
Helpers::panic("Signalled non-existent event: %X\n", handle);
regs[0] = SVCResult::BadHandle;
} else {
regs[0] = SVCResult::Success;
//regs[0] = SVCResult::BadHandle;
return;
}
regs[0] = SVCResult::Success;
auto eventData = event->getData<Event>();
eventData->fired = true;
switch (eventData->resetType) {
case ResetType::OneShot:
for (int i = 0; i < threadCount; i++) {
Thread& t = threads[i];
if (t.status == ThreadStatus::WaitSync1 && t.waitList[0] == handle) {
t.status = ThreadStatus::Ready;
break;
} else if (t.status == ThreadStatus::WaitSyncAll) {
Helpers::panic("Trying to SignalEvent when a thread is waiting on multiple objects");
}
}
break;
default:
Helpers::panic("Signaled event of unimplemented type: %d", eventData->resetType);
}
}
@ -101,8 +127,15 @@ void Kernel::waitSynchronization1() {
}
if (!shouldWaitOnObject(object)) {
acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready
regs[0] = SVCResult::Success;
} else {
// Timeout is 0, don't bother waiting, instantly timeout
if (ns == 0) {
regs[0] = SVCResult::Timeout;
return;
}
regs[0] = SVCResult::Success;
auto& t = threads[currentThreadIndex];
@ -111,6 +144,10 @@ void Kernel::waitSynchronization1() {
t.sleepTick = cpu.getTicks();
t.waitingNanoseconds = ns;
t.waitList[0] = handle;
// Add the current thread to the object's wait list
object->getWaitlist() |= (1ull << currentThreadIndex);
switchToNextThread();
}
}
@ -120,7 +157,7 @@ void Kernel::waitSynchronizationN() {
// TODO: Are these arguments even correct?
s32 ns1 = regs[0];
u32 handles = regs[1];
u32 handleCount = regs[2];
s32 handleCount = regs[2];
bool waitAll = regs[3] != 0;
u32 ns2 = regs[4];
s32 outPointer = regs[5]; // "out" pointer - shows which object got bonked if we're waiting on multiple objects
@ -128,36 +165,77 @@ void Kernel::waitSynchronizationN() {
logSVC("WaitSynchronizationN (handle pointer: %08X, count: %d, timeout = %lld)\n", handles, handleCount, ns);
if (waitAll && handleCount > 1)
Helpers::panic("Trying to wait on more than 1 object");
if (handleCount < 0)
Helpers::panic("WaitSyncN: Invalid handle count");
auto& t = threads[currentThreadIndex];
t.waitList.resize(handleCount);
for (uint i = 0; i < handleCount; i++) {
using WaitObject = std::pair<Handle, KernelObject*>;
std::vector<WaitObject> waitObjects(handleCount);
// We don't actually need to wait if waitAll == true unless one of the objects is not ready
bool allReady = true; // Default initialize to true, set to fault if one of the objects is not ready
// Tracks whether at least one object is ready, + the index of the first ready object
// This is used when waitAll == false, because if one object is already available then we can skip the sleeping
bool oneObjectReady = false;
s32 firstReadyObjectIndex = 0;
for (s32 i = 0; i < handleCount; i++) {
Handle handle = mem.read32(handles);
handles += sizeof(Handle);
t.waitList[i] = handle;
auto object = getObject(handle);
// Panic if one of the objects is not even an object
if (object == nullptr) [[unlikely]] {
Helpers::panic("WaitSynchronizationN: Bad object handle %X\n", handle);
regs[0] = SVCResult::BadHandle;
return;
}
// Panic if one of the objects is not a valid sync object
if (!isWaitable(object)) [[unlikely]] {
//Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle);
Helpers::panic("Tried to wait on a non waitable object in WaitSyncN. Type: %s, handle: %X\n",
object->getTypeName(), handle);
}
if (shouldWaitOnObject(object)) {
allReady = false; // Derp, not all objects are ready :(
} else { /// At least one object is ready to be acquired ahead of time. If it's the first one, write it down
if (!oneObjectReady) {
oneObjectReady = true;
firstReadyObjectIndex = i;
}
}
waitObjects[i] = {handle, object};
}
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;
t.waitingNanoseconds = ns;
t.sleepTick = cpu.getTicks();
switchToNextThread();
auto& t = threads[currentThreadIndex];
// We only need to wait on one object. Easy...?!
if (!waitAll) {
// If there's ready objects, acquire the first one and return
if (oneObjectReady) {
regs[0] = SVCResult::Success;
regs[1] = firstReadyObjectIndex; // Return index of the acquired object
acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object
return;
}
regs[0] = SVCResult::Success; // If the thread times out, this should be adjusted to SVCResult::Timeout
regs[1] = handleCount - 1; // When the thread exits, this will be adjusted to mirror which handle woke us up
t.waitList.resize(handleCount);
t.status = ThreadStatus::WaitSyncAny;
t.outPointer = outPointer;
t.waitingNanoseconds = ns;
t.sleepTick = cpu.getTicks();
for (s32 i = 0; i < handleCount; i++) {
t.waitList[i] = waitObjects[i].first; // Add object to this thread's waitlist
waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist
}
switchToNextThread();
} else {
Helpers::panic("WaitSynchronizatioN with waitAll");
}
}

View file

@ -5,6 +5,7 @@ namespace FileOps {
Read = 0x080200C2,
Write = 0x08030102,
GetSize = 0x08040000,
SetSize = 0x08050080,
Close = 0x08080000,
SetPriority = 0x080A0040,
OpenLinkFile = 0x080C0000
@ -25,6 +26,7 @@ void Kernel::handleFileOperation(u32 messagePointer, Handle file) {
case FileOps::GetSize: getFileSize(messagePointer, file); break;
case FileOps::OpenLinkFile: openLinkFile(messagePointer, file); break;
case FileOps::Read: readFile(messagePointer, file); break;
case FileOps::SetSize: setFileSize(messagePointer, file); break;
case FileOps::SetPriority: setFilePriority(messagePointer, file); break;
case FileOps::Write: writeFile(messagePointer, file); break;
default: Helpers::panic("Unknown file operation: %08X", cmd);
@ -132,6 +134,34 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) {
}
}
void Kernel::setFileSize(u32 messagePointer, Handle fileHandle) {
logFileIO("Setting size of file %X\n", fileHandle);
const auto p = getObject(fileHandle, KernelObjectType::File);
if (p == nullptr) [[unlikely]] {
Helpers::panic("Called SetFileSize on non-existent file");
}
FileSession* file = p->getData<FileSession>();
if (!file->isOpen) {
Helpers::panic("Tried to get size of closed file");
}
if (file->fd) {
const u64 newSize = mem.read64(messagePointer + 4);
IOFile f(file->fd);
bool success = f.setSize(newSize);
if (success) {
mem.write32(messagePointer + 4, Result::Success);
} else {
Helpers::panic("FileOp::SetFileSize failed");
}
} else {
Helpers::panic("Tried to set file size of file without file descriptor");
}
}
void Kernel::getFileSize(u32 messagePointer, Handle fileHandle) {
logFileIO("Getting size of file %X\n", fileHandle);

View file

@ -0,0 +1,70 @@
#include <cstring>
#include "arm_defs.hpp"
#include "kernel.hpp"
/*
This file sets up an idle thread that's meant to run when no other OS thread can run.
It simply idles and constantly yields to check if there's any other thread that can run
The code for our idle thread looks like this
idle_thread_main:
mov r0, #4096 @ Loop counter
.loop:
nop; nop; nop; nop @ NOP 4 times to waste some cycles
subs r0, #1 @ Decrement counter by 1, go back to looping if loop counter != 0
bne .loop
// Sleep for 0 seconds with the SleepThread SVC, which just yields execution
mov r0, #0
mov r1, #0
svc SleepThread
b idle_thread_main
*/
static constexpr u8 idleThreadCode[] = {
0x01, 0x0A, 0xA0, 0xE3, // mov r0, #4096
0x00, 0xF0, 0x20, 0xE3, 0x00, 0xF0, 0x20, 0xE3, 0x00, 0xF0, 0x20, 0xE3, 0x00, 0xF0, 0x20, 0xE3, // nop (4 times)
0x01, 0x00, 0x50, 0xE2, // subs r0, #1
0xF9, 0xFF, 0xFF, 0x1A, // bne loop
0x00, 0x00, 0xA0, 0xE3, // mov r0, #0
0x00, 0x10, 0xA0, 0xE3, // mov r1, #0
0x0A, 0x00, 0x00, 0xEF, // svc SleepThread
0xF4, 0xFF, 0xFF, 0xEA // b idle_thread_main
};
// Set up an idle thread to run when no thread is able to run
void Kernel::setupIdleThread() {
Thread& t = threads[idleThreadIndex];
constexpr u32 codeAddress = 0xBFC00000;
// Reserve some memory for the idle thread's code. We map this memory to vaddr BFC00000 which is not userland-accessible
// We only allocate 4KB (1 page) because our idle code is pretty small
const u32 fcramIndex = mem.allocateSysMemory(Memory::pageSize);
auto vaddr = mem.allocateMemory(codeAddress, fcramIndex, Memory::pageSize, true, true, false, true, false, true);
if (!vaddr.has_value() || vaddr.value() != codeAddress) {
Helpers::panic("Failed to setup idle thread");
}
// Copy idle thread code to the allocated FCRAM
std::memcpy(&mem.getFCRAM()[fcramIndex], idleThreadCode, sizeof(idleThreadCode));
t.entrypoint = codeAddress;
t.tlsBase = 0;
t.gprs[13] = 0; // Set SP & LR to 0 just in case. The idle thread should never access memory, but let's be safe
t.gprs[14] = 0;
t.gprs[15] = codeAddress;
t.cpsr = CPSR::UserMode;
t.fpscr = FPSCR::ThreadDefault;
// Our idle thread should have as low of a priority as possible, because, well, it's an idle thread.
// We handle this by giving it a priority of 0xff, which is lower than is actually allowed for user threads
// (High priority value = low priority)
t.priority = 0xff;
t.status = ThreadStatus::Ready;
// Add idle thread to the list of thread indices
threadIndices.push_back(idleThreadIndex);
sortThreads();
}

View file

@ -15,6 +15,7 @@ Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu)
t.index = i;
t.tlsBase = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize;
t.status = ThreadStatus::Dead;
t.waitList.clear();
t.waitList.reserve(10); // Reserve some space for the wait list to avoid further memory allocs later
// The state below isn't necessary to initialize but we do it anyways out of caution
t.outPointer = 0;
@ -33,11 +34,11 @@ void Kernel::serviceSVC(u32 svc) {
case 0x0A: svcSleepThread(); break;
case 0x0B: getThreadPriority(); break;
case 0x0C: setThreadPriority(); break;
case 0x13: createMutex(); break;
case 0x14: releaseMutex(); break;
case 0x17: createEvent(); break;
case 0x18: signalEvent(); break;
case 0x19: clearEvent(); break;
case 0x13: svcCreateMutex(); break;
case 0x14: svcReleaseMutex(); break;
case 0x17: svcCreateEvent(); break;
case 0x18: svcSignalEvent(); break;
case 0x19: svcClearEvent(); break;
case 0x1E: createMemoryBlock(); break;
case 0x1F: mapMemoryBlock(); break;
case 0x21: createAddressArbiter(); break;
@ -111,6 +112,7 @@ void Kernel::reset() {
for (auto& t : threads) {
t.status = ThreadStatus::Dead;
t.waitList.clear();
t.threadsWaitingForTermination = 0; // No threads are waiting for this thread to terminate cause it's dead
}
for (auto& object : objects) {
@ -130,6 +132,7 @@ void Kernel::reset() {
// which is thankfully not used. Maybe we should prevent this
mainThread = makeThread(0, VirtualAddrs::StackTop, 0x30, -2, 0, ThreadStatus::Running);
currentThreadIndex = 0;
setupIdleThread();
// Create some of the OS ports
srvHandle = makePort("srv:"); // Service manager port

View file

@ -47,12 +47,14 @@ void Kernel::sortThreads() {
bool Kernel::canThreadRun(const Thread& t) {
if (t.status == ThreadStatus::Ready) {
return true;
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 || t.status == ThreadStatus::WaitSyncAll) {
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1
|| t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll) {
const u64 elapsedTicks = cpu.getTicks() - t.sleepTick;
constexpr double ticksPerSec = double(CPU::ticksPerSec);
constexpr double nsPerTick = ticksPerSec / 1000000000.0;
// TODO: Set r0 to the correct error code on timeout for WaitSync{1/Any/All}
const s64 elapsedNs = s64(double(elapsedTicks) * nsPerTick);
return elapsedNs >= t.waitingNanoseconds;
}
@ -83,6 +85,7 @@ void Kernel::switchToNextThread() {
if (!newThreadIndex.has_value()) {
log("Kernel tried to switch to the next thread but none found. Switching to random thread\n");
assert(aliveThreadCount != 0);
Helpers::panic("rpog");
int index;
do {
@ -147,6 +150,7 @@ Handle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u
t.status = status;
t.handle = ret;
t.waitingAddress = 0;
t.threadsWaitingForTermination = 0; // Thread just spawned, no other threads waiting for it to terminate
t.cpsr = CPSR::UserMode | (isThumb ? CPSR::Thumb : 0);
t.fpscr = FPSCR::ThreadDefault;
@ -161,14 +165,28 @@ Handle Kernel::makeMutex(bool locked) {
Handle ret = makeObject(KernelObjectType::Mutex);
objects[ret].data = new Mutex(locked);
// If the mutex is initially locked, store the index of the thread that owns it
// If the mutex is initially locked, store the index of the thread that owns it and set lock count to 1
if (locked) {
objects[ret].getData<Mutex>()->ownerThread = currentThreadIndex;
Mutex* moo = objects[ret].getData<Mutex>();
moo->ownerThread = currentThreadIndex;
}
return ret;
}
void Kernel::releaseMutex(Mutex* moo) {
// TODO: Assert lockCount > 0 before release, maybe. The SVC should be safe at least.
moo->lockCount--; // Decrement lock count
// If the lock count reached 0 then the thread no longer owns the mootex and it can be given to a new one
if (moo->lockCount == 0) {
moo->locked = false;
if (moo->waitlist != 0) {
Helpers::panic("Mutex got freed while it's got more threads waiting for it. Must make a new thread claim it.");
}
}
}
Handle Kernel::makeSemaphore(u32 initialCount, u32 maximumCount) {
Handle ret = makeObject(KernelObjectType::Semaphore);
objects[ret].data = new Semaphore(initialCount, maximumCount);
@ -184,14 +202,45 @@ void Kernel::sleepThreadOnArbiter(u32 waitingAddress) {
switchToNextThread();
}
// Acquires an object that is **ready to be acquired** without waiting on it
void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) {
switch (object->type) {
case KernelObjectType::Event: {
Event* e = object->getData<Event>();
if (e->resetType == ResetType::OneShot) { // One-shot events automatically get cleared after waking up a thread
e->fired = false;
}
break;
}
case KernelObjectType::Mutex: {
Mutex* moo = object->getData<Mutex>();
moo->locked = true; // Set locked to true, whether it's false or not because who cares
// Increment lock count by 1. If a thread acquires a mootex multiple times, it needs to release it until count == 0
// For the mootex to be free.
moo->lockCount++;
moo->ownerThread = thread.index;
break;
}
case KernelObjectType::Thread:
break;
default: Helpers::panic("Acquiring unimplemented sync object %s", object->getTypeName());
}
}
// Make a thread sleep for a certain amount of nanoseconds at minimum
void Kernel::sleepThread(s64 ns) {
if (ns < 0) {
Helpers::panic("Sleeping a thread for a negative amount of ns");
} else if (ns == 0) { // Used when we want to force a thread switch
int curr = currentThreadIndex;
switchToNextThread(); // Mark thread as ready after switching, to avoid switching to the same thread
threads[curr].status = ThreadStatus::Ready;
std::optional<int> newThreadIndex = getNextThread();
// If there's no other thread waiting, don't bother yielding
if (newThreadIndex.has_value()) {
threads[currentThreadIndex].status = ThreadStatus::Ready;
switchThread(newThreadIndex.value());
}
} else { // If we're sleeping for > 0 ns
Thread& t = threads[currentThreadIndex];
t.status = ThreadStatus::WaitSleep;
@ -226,7 +275,7 @@ void Kernel::createThread() {
// void SleepThread(s64 nanoseconds)
void Kernel::svcSleepThread() {
const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32));
logSVC("SleepThread(ns = %lld)\n", ns);
//logSVC("SleepThread(ns = %lld)\n", ns);
regs[0] = SVCResult::Success;
sleepThread(ns);
@ -310,10 +359,14 @@ void Kernel::exitThread() {
t.status = ThreadStatus::Dead;
aliveThreadCount--;
// Check if any threads are sleeping, waiting for this thread to terminate, and wake them up
if (t.threadsWaitingForTermination != 0)
Helpers::panic("TODO: Implement threads sleeping until another thread terminates");
switchToNextThread();
}
void Kernel::createMutex() {
void Kernel::svcCreateMutex() {
bool locked = regs[1] != 0;
logSVC("CreateMutex (locked = %s)\n", locked ? "yes" : "no");
@ -321,10 +374,25 @@ void Kernel::createMutex() {
regs[1] = makeMutex(locked);
}
void Kernel::releaseMutex() {
void Kernel::svcReleaseMutex() {
const Handle handle = regs[0];
logSVC("ReleaseMutex (handle = %x)\n", handle);
logSVC("ReleaseMutex (handle = %x) (STUBBED)\n", handle);
const auto object = getObject(handle, KernelObjectType::Mutex);
if (object == nullptr) [[unlikely]] {
Helpers::panic("Tried to release non-existent mutex");
regs[0] = SVCResult::BadHandle;
return;
}
Mutex* moo = object->getData<Mutex>();
// A thread can't release a mutex it does not own
if (!moo->locked || moo->ownerThread != currentThreadIndex) {
regs[0] = SVCResult::InvalidMutexRelease;
return;
}
releaseMutex(moo);
regs[0] = SVCResult::Success;
}
@ -344,8 +412,19 @@ bool Kernel::shouldWaitOnObject(KernelObject* object) {
case KernelObjectType::Event: // We should wait on an event only if it has not been signalled
return !object->getData<Event>()->fired;
case KernelObjectType::Mutex: {
Mutex* moo = object->getData<Mutex>(); // mooooooooooo
return moo->locked && moo->ownerThread != currentThreadIndex; // If the current thread owns the moo then no reason to wait
}
case KernelObjectType::Thread: // Waiting on a thread waits until it's dead. If it's dead then no need to wait
return object->getData<Thread>()->status != ThreadStatus::Dead;
case KernelObjectType::Semaphore: // Wait if the semaphore count <= 0
return object->getData<Semaphore>()->availableCount <= 0;
default:
logThread("Not sure whether to wait on object (type: %s)", object->getTypeName());
Helpers::panic("Not sure whether to wait on object (type: %s)", object->getTypeName());
return true;
}
}

View file

@ -76,6 +76,7 @@ u8 Memory::read8(u32 vaddr) {
case ConfigMem::LedState3D: return 1; // Report the 3D LED as always off (non-zero) for now
case ConfigMem::NetworkState: return 2; // Report that we've got an internet connection
case ConfigMem::HeadphonesConnectedMaybe: return 0;
case ConfigMem::Unknown1086: return 1; // It's unknown what this is but some games want it to be 1
default: Helpers::panic("Unimplemented 8-bit read, addr: %08X", vaddr);
}
}

View file

@ -231,13 +231,6 @@ void Renderer::setupBlending() {
GL_SRC_ALPHA_SATURATE, GL_ONE
};
// Temporarily here until we add constant color/alpha
const auto panicIfUnimplementedFunc = [](const u32 func) {
auto x = blendingFuncs[func];
if (x == GL_CONSTANT_COLOR || x == GL_ONE_MINUS_CONSTANT_COLOR || x == GL_ALPHA || x == GL_ONE_MINUS_CONSTANT_ALPHA) [[unlikely]]
Helpers::panic("Unimplemented blending function!");
};
if (!blendingEnabled) {
OpenGL::disableBlend();
} else {
@ -254,11 +247,12 @@ void Renderer::setupBlending() {
const u32 alphaSourceFunc = (blendControl >> 24) & 0xf;
const u32 alphaDestFunc = (blendControl >> 28) & 0xf;
// Panic if one of the blending funcs is unimplemented
panicIfUnimplementedFunc(rgbSourceFunc);
panicIfUnimplementedFunc(rgbDestFunc);
panicIfUnimplementedFunc(alphaSourceFunc);
panicIfUnimplementedFunc(alphaDestFunc);
const u32 constantColor = regs[PICAInternalRegs::BlendColour];
const u32 r = constantColor & 0xff;
const u32 g = (constantColor >> 8) & 0xff;
const u32 b = (constantColor >> 16) & 0xff;
const u32 a = (constantColor >> 24) & 0xff;
OpenGL::setBlendColor(float(r) / 255.f, float(g) / 255.f, float(b) / 255.f, float(a) / 255.f);
// Translate equations and funcs to their GL equivalents and set them
glBlendEquationSeparate(blendingEquations[rgbEquation], blendingEquations[alphaEquation]);

View file

@ -1,4 +1,5 @@
#include "services/ac.hpp"
#include "ipc.hpp"
namespace ACCommands {
enum : u32 {
@ -26,5 +27,6 @@ void ACService::setClientVersion(u32 messagePointer) {
u32 version = mem.read32(messagePointer + 4);
log("AC::SetClientVersion (version = %d)\n", version);
mem.write32(messagePointer, IPC::responseHeader(0x40, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,4 +1,5 @@
#include "services/am.hpp"
#include "ipc.hpp"
namespace AMCommands {
enum : u32 {
@ -40,11 +41,14 @@ void AMService::listTitleInfo(u32 messagePointer) {
pointer += 24; // = sizeof(TicketInfo)
}
mem.write32(messagePointer, IPC::responseHeader(0x1007, 2, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, ticketCount);
}
void AMService::getDLCTitleInfo(u32 messagePointer) {
log("AM::GetDLCTitleInfo (stubbed to fail)\n");
mem.write32(messagePointer, IPC::responseHeader(0x1005, 1, 4));
mem.write32(messagePointer + 4, -1);
}

View file

@ -1,4 +1,5 @@
#include "services/apt.hpp"
#include "ipc.hpp"
#include "kernel.hpp"
namespace APTCommands {
@ -8,6 +9,7 @@ namespace APTCommands {
Enable = 0x00030040,
InquireNotification = 0x000B0040,
ReceiveParameter = 0x000D0080,
GlanceParameter = 0x000E0080,
ReplySleepQuery = 0x003E0080,
NotifyToWait = 0x00430040,
GetSharedFont = 0x00440000,
@ -25,7 +27,31 @@ namespace APTCommands {
namespace Result {
enum : u32 {
Success = 0,
Failure = 0xFFFFFFFF
};
}
// https://www.3dbrew.org/wiki/NS_and_APT_Services#Command
namespace APTTransitions {
enum : u32 {
None = 0,
Wakeup = 1,
Request = 2,
Response = 3,
Exit = 4,
Message = 5,
HomeButtonSingle = 6,
HomeButtonDouble = 7,
DSPSleep = 8,
DSPWakeup = 9,
WakeupByExit = 10,
WakuepByPause = 11,
WakeupByCancel = 12,
WakeupByCancelAll = 13,
WakeupByPowerButton = 14,
WakeupToJumpHome = 15,
RequestForApplet = 16,
WakeupToLaunchApp = 17,
ProcessDed = 0x41
};
}
@ -52,6 +78,7 @@ void APTService::handleSyncRequest(u32 messagePointer) {
case APTCommands::GetApplicationCpuTimeLimit: getApplicationCpuTimeLimit(messagePointer); break;
case APTCommands::GetLockHandle: getLockHandle(messagePointer); break;
case APTCommands::GetWirelessRebootInfo: getWirelessRebootInfo(messagePointer); break;
case APTCommands::GlanceParameter: glanceParameter(messagePointer); break;
case APTCommands::NotifyToWait: notifyToWait(messagePointer); break;
case APTCommands::ReceiveParameter: [[likely]] receiveParameter(messagePointer); break;
case APTCommands::ReplySleepQuery: replySleepQuery(messagePointer); break;
@ -70,11 +97,13 @@ void APTService::appletUtility(u32 messagePointer) {
log("APT::AppletUtility(utility = %d, input size = %x, output size = %x, inputPointer = %08X)\n", utility, inputSize,
outputSize, inputPointer);
mem.write32(messagePointer, IPC::responseHeader(0x4B, 2, 2));
mem.write32(messagePointer + 4, Result::Success);
}
void APTService::checkNew3DS(u32 messagePointer) {
log("APT::CheckNew3DS\n");
mem.write32(messagePointer, IPC::responseHeader(0x102, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, (model == ConsoleModel::New3DS) ? 1 : 0); // u8, Status (0 = Old 3DS, 1 = New 3DS)
}
@ -82,20 +111,28 @@ void APTService::checkNew3DS(u32 messagePointer) {
// TODO: Figure out the slight way this differs from APT::CheckNew3DS
void APTService::checkNew3DSApp(u32 messagePointer) {
log("APT::CheckNew3DSApp\n");
checkNew3DS(messagePointer);
mem.write32(messagePointer, IPC::responseHeader(0x101, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, (model == ConsoleModel::New3DS) ? 1 : 0); // u8, Status (0 = Old 3DS, 1 = New 3DS)
}
void APTService::enable(u32 messagePointer) {
log("APT::Enable\n");
mem.write32(messagePointer, IPC::responseHeader(0x3, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void APTService::initialize(u32 messagePointer) {
log("APT::Initialize\n");
notificationEvent = kernel.makeEvent(ResetType::OneShot);
resumeEvent = kernel.makeEvent(ResetType::OneShot);
if (!notificationEvent.has_value() || !resumeEvent.has_value()) {
notificationEvent = kernel.makeEvent(ResetType::OneShot);
resumeEvent = kernel.makeEvent(ResetType::OneShot);
kernel.signalEvent(resumeEvent.value()); // Seems to be signalled on startup
}
mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 3));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0x04000000); // Translation descriptor
mem.write32(messagePointer + 12, notificationEvent.value()); // Notification Event Handle
@ -103,12 +140,10 @@ void APTService::initialize(u32 messagePointer) {
}
void APTService::inquireNotification(u32 messagePointer) {
log("APT::InquireNotification (STUBBED TO FAIL)\n");
log("APT::InquireNotification (STUBBED TO RETURN NONE)\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, IPC::responseHeader(0xB, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, static_cast<u32>(NotificationType::None));
}
@ -120,7 +155,8 @@ void APTService::getLockHandle(u32 messagePointer) {
lockHandle = kernel.makeMutex();
}
mem.write32(messagePointer + 4, Result::Failure); // Result code
mem.write32(messagePointer, IPC::responseHeader(0x1, 3, 2));
mem.write32(messagePointer + 4, Result::Success); // Result code
mem.write32(messagePointer + 8, 0); // AppletAttr
mem.write32(messagePointer + 12, 0); // APT State (bit0 = Power Button State, bit1 = Order To Close State)
mem.write32(messagePointer + 16, 0); // Translation descriptor
@ -130,6 +166,7 @@ void APTService::getLockHandle(u32 messagePointer) {
// This apparently does nothing on the original kernel either?
void APTService::notifyToWait(u32 messagePointer) {
log("APT::NotifyToWait\n");
mem.write32(messagePointer, IPC::responseHeader(0x43, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -140,11 +177,30 @@ void APTService::receiveParameter(u32 messagePointer) {
if (size > 0x1000) Helpers::panic("APT::ReceiveParameter with size > 0x1000");
// TODO: Properly implement this. We currently stub it in the same way as 3dmoo
// TODO: Properly implement this. We currently stub somewhat like 3dmoo
mem.write32(messagePointer, IPC::responseHeader(0xD, 4, 4));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Sender App ID
mem.write32(messagePointer + 12, 1); // Signal type (1 = app just started, 0xB = returning to app, 0xC = exiting app)
mem.write32(messagePointer + 16, 0x10);
mem.write32(messagePointer + 12, APTTransitions::Wakeup); // Command
mem.write32(messagePointer + 16, 0);
mem.write32(messagePointer + 20, 0x10);
mem.write32(messagePointer + 24, 0);
mem.write32(messagePointer + 28, 0);
}
void APTService::glanceParameter(u32 messagePointer) {
const u32 app = mem.read32(messagePointer + 4);
const u32 size = mem.read32(messagePointer + 8);
log("APT::GlanceParameter(app ID = %X, size = %04X) (STUBBED)\n", app, size);
if (size > 0x1000) Helpers::panic("APT::GlanceParameter with size > 0x1000");
// TODO: Properly implement this. We currently stub it similar
mem.write32(messagePointer, IPC::responseHeader(0xE, 4, 4));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Sender App ID
mem.write32(messagePointer + 12, APTTransitions::Wakeup); // Command
mem.write32(messagePointer + 16, 0);
mem.write32(messagePointer + 20, 0);
mem.write32(messagePointer + 24, 0);
mem.write32(messagePointer + 28, 0);
@ -152,6 +208,7 @@ void APTService::receiveParameter(u32 messagePointer) {
void APTService::replySleepQuery(u32 messagePointer) {
log("APT::ReplySleepQuery (Stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x3E, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -163,6 +220,7 @@ void APTService::setApplicationCpuTimeLimit(u32 messagePointer) {
if (percentage < 5 || percentage > 89 || fixed != 1) {
Helpers::panic("Invalid parameters passed to APT::SetApplicationCpuTimeLimit");
} else {
mem.write32(messagePointer, IPC::responseHeader(0x4F, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
cpuTimeLimit = percentage;
}
@ -170,6 +228,7 @@ void APTService::setApplicationCpuTimeLimit(u32 messagePointer) {
void APTService::getApplicationCpuTimeLimit(u32 messagePointer) {
log("APT::GetApplicationCpuTimeLimit\n");
mem.write32(messagePointer, IPC::responseHeader(0x50, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, cpuTimeLimit);
}
@ -178,8 +237,9 @@ void APTService::setScreencapPostPermission(u32 messagePointer) {
u32 perm = mem.read32(messagePointer + 4);
log("APT::SetScreencapPostPermission (perm = %d)\n");
mem.write32(messagePointer, IPC::responseHeader(0x55, 1, 0));
// Apparently only 1-3 are valid values, but I see 0 used in some games like Pokemon Rumble
mem.write32(messagePointer, Result::Success);
mem.write32(messagePointer + 4, Result::Success);
screencapPostPermission = perm;
}
@ -187,6 +247,7 @@ void APTService::getSharedFont(u32 messagePointer) {
log("APT::GetSharedFont\n");
constexpr u32 fontVaddr = 0x18000000;
mem.write32(messagePointer, IPC::responseHeader(0x44, 2, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, fontVaddr);
mem.write32(messagePointer + 16, KernelHandles::FontSharedMemHandle);
@ -197,6 +258,7 @@ void APTService::getSharedFont(u32 messagePointer) {
void APTService::theSmashBrosFunction(u32 messagePointer) {
log("APT: Called the elusive Smash Bros function\n");
mem.write32(messagePointer, IPC::responseHeader(0x103, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, (model == ConsoleModel::New3DS) ? 2 : 1);
}
@ -208,6 +270,7 @@ void APTService::getWirelessRebootInfo(u32 messagePointer) {
if (size > 0x10)
Helpers::panic("APT::GetWirelessInfo with size > 0x10 bytes");
mem.write32(messagePointer, IPC::responseHeader(0x45, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
for (u32 i = 0; i < size; i++) {
mem.write8(messagePointer + 0x104 + i, 0); // Temporarily stub this until we add SetWirelessRebootInfo

View file

@ -1,4 +1,5 @@
#include "services/boss.hpp"
#include "ipc.hpp"
namespace BOSSCommands {
enum : u32 {
@ -40,22 +41,26 @@ void BOSSService::handleSyncRequest(u32 messagePointer) {
void BOSSService::initializeSession(u32 messagePointer) {
log("BOSS::InitializeSession (stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void BOSSService::getOptoutFlag(u32 messagePointer) {
log("BOSS::getOptoutFlag\n");
mem.write32(messagePointer, IPC::responseHeader(0xA, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, optoutFlag);
}
void BOSSService::getTaskIdList(u32 messagePointer) {
log("BOSS::GetTaskIdList (stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0xE, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void BOSSService::getStorageEntryInfo(u32 messagePointer) {
log("BOSS::GetStorageEntryInfo (undocumented)\n");
mem.write32(messagePointer, IPC::responseHeader(0x30, 3, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // u32, unknown meaning
mem.write16(messagePointer + 12, 0); // s16, unknown meaning
@ -67,21 +72,25 @@ void BOSSService::receiveProperty(u32 messagePointer) {
const u32 ptr = mem.read32(messagePointer + 16);
log("BOSS::ReceiveProperty(stubbed) (id = %d, size = %08X, ptr = %08X)\n", id, size, ptr);
mem.write32(messagePointer, IPC::responseHeader(0x16, 2, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Read size
}
void BOSSService::unregisterTask(u32 messagePointer) {
log("BOSS::UnregisterTask (stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x0C, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
}
void BOSSService::registerStorageEntry(u32 messagePointer) {
log("BOSS::RegisterStorageEntry (stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x2F, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void BOSSService::unregisterStorage(u32 messagePointer) {
log("BOSS::UnregisterStorage (stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x3, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,4 +1,5 @@
#include "services/cam.hpp"
#include "ipc.hpp"
namespace CAMCommands {
enum : u32 {
@ -26,6 +27,7 @@ void CAMService::handleSyncRequest(u32 messagePointer) {
void CAMService::driverInitialize(u32 messagePointer) {
log("CAM::DriverInitialize\n");
mem.write32(messagePointer, IPC::responseHeader(0x39, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -55,6 +57,7 @@ void CAMService::getMaxLines(u32 messagePointer) {
}
}
mem.write32(messagePointer, IPC::responseHeader(0xA, 2, 0));
mem.write32(messagePointer + 4, result);
mem.write16(messagePointer + 8, lines);
}

View file

@ -1,4 +1,5 @@
#include "services/cecd.hpp"
#include "ipc.hpp"
namespace CECDCommands {
enum : u32 {
@ -26,6 +27,8 @@ void CECDService::getEventHandle(u32 messagePointer) {
log("CECD::GetEventHandle (stubbed)\n");
Helpers::panic("TODO: Actually implement CECD::GetEventHandle");
mem.write32(messagePointer, IPC::responseHeader(0xF, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
// TODO: Translation descriptor here?
mem.write32(messagePointer + 12, 0x66666666);
}

View file

@ -1,5 +1,6 @@
#include "services/cfg.hpp"
#include "services/dsp.hpp"
#include "ipc.hpp"
namespace CFGCommands {
enum : u32 {
@ -101,12 +102,14 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
Helpers::panic("Unhandled GetConfigInfoBlk2 configuration. Size = %d, block = %X", size, blockID);
}
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
}
void CFGService::secureInfoGetRegion(u32 messagePointer) {
log("CFG::SecureInfoGetRegion\n");
mem.write32(messagePointer, IPC::responseHeader(0x2, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, static_cast<u32>(Regions::USA)); // TODO: Detect the game region and report it
}
@ -115,6 +118,7 @@ void CFGService::genUniqueConsoleHash(u32 messagePointer) {
log("CFG::GenUniqueConsoleHash (semi-stubbed)\n");
const u32 salt = mem.read32(messagePointer + 4) & 0x000FFFFF;
mem.write32(messagePointer, IPC::responseHeader(0x3, 3, 0));
mem.write32(messagePointer + 4, Result::Success);
// We need to implement hash generation & the SHA-256 digest properly later on. We have cryptopp so the hashing isn't too hard to do
// Let's stub it for now
@ -128,6 +132,7 @@ void CFGService::getRegionCanadaUSA(u32 messagePointer) {
log("CFG::GetRegionCanadaUSA\n");
const u8 ret = (country == CountryCodes::US || country == CountryCodes::CA) ? 1 : 0;
mem.write32(messagePointer, IPC::responseHeader(0x4, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, ret);
}

View file

@ -1,4 +1,6 @@
#include "services/dsp.hpp"
#include "ipc.hpp"
#include "kernel.hpp"
namespace DSPCommands {
enum : u32 {
@ -10,7 +12,7 @@ namespace DSPCommands {
FlushDataCache = 0x00130082,
InvalidateDataCache = 0x00140082,
RegisterInterruptEvents = 0x00150082,
GetSemaphoreHandle = 0x00160000,
GetSemaphoreEventHandle = 0x00160000,
SetSemaphoreMask = 0x00170040,
GetHeadphoneStatus = 0x001F0000
};
@ -26,6 +28,15 @@ namespace Result {
void DSPService::reset() {
audioPipe.reset();
totalEventCount = 0;
semaphoreEvent = std::nullopt;
interrupt0 = std::nullopt;
interrupt1 = std::nullopt;
for (DSPEvent& e : pipeEvents) {
e = std::nullopt;
}
}
void DSPService::handleSyncRequest(u32 messagePointer) {
@ -35,7 +46,7 @@ void DSPService::handleSyncRequest(u32 messagePointer) {
case DSPCommands::FlushDataCache: flushDataCache(messagePointer); break;
case DSPCommands::InvalidateDataCache: invalidateDCache(messagePointer); break;
case DSPCommands::GetHeadphoneStatus: getHeadphoneStatus(messagePointer); break;
case DSPCommands::GetSemaphoreHandle: getSemaphoreHandle(messagePointer); break;
case DSPCommands::GetSemaphoreEventHandle: getSemaphoreEventHandle(messagePointer); break;
case DSPCommands::LoadComponent: loadComponent(messagePointer); break;
case DSPCommands::ReadPipeIfPossible: readPipeIfPossible(messagePointer); break;
case DSPCommands::RegisterInterruptEvents: registerInterruptEvents(messagePointer); break;
@ -49,8 +60,9 @@ void DSPService::handleSyncRequest(u32 messagePointer) {
void DSPService::convertProcessAddressFromDspDram(u32 messagePointer) {
const u32 address = mem.read32(messagePointer + 4);
log("DSP::ConvertProcessAddressFromDspDram (address = %08X)\n", address);
const u32 converted = (address << 1) + 0x1FF40000;
mem.write32(messagePointer, IPC::responseHeader(0xC, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, converted); // Converted address
}
@ -61,6 +73,7 @@ void DSPService::loadComponent(u32 messagePointer) {
u32 dataMask = mem.read32(messagePointer + 12);
log("DSP::LoadComponent (size = %08X, program mask = %X, data mask = %X\n", size, programMask, dataMask);
mem.write32(messagePointer, IPC::responseHeader(0x11, 2, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 1); // Component loaded
mem.write32(messagePointer + 12, (size << 4) | 0xA);
@ -89,37 +102,80 @@ void DSPService::readPipeIfPossible(u32 messagePointer) {
mem.write16(buffer + i, pipe.readUnchecked());
}
mem.write32(messagePointer, IPC::responseHeader(0x10, 2, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write16(messagePointer + 8, i); // Number of bytes read
}
void DSPService::registerInterruptEvents(u32 messagePointer) {
u32 interrupt = mem.read32(messagePointer + 4);
u32 channel = mem.read32(messagePointer + 8);
u32 event = mem.read32(messagePointer + 16);
DSPService::DSPEvent& DSPService::getEventRef(u32 type, u32 pipe) {
switch (type) {
case 0: return interrupt0;
case 1: return interrupt1;
log("DSP::RegisterInterruptEvents (interrupt = %d, channel = %d, event = %d)\n", interrupt, channel, event);
mem.write32(messagePointer + 4, Result::Success);
case 2:
if (pipe >= pipeCount)
Helpers::panic("Tried to access the event of an invalid pipe");
return pipeEvents[pipe];
default:
Helpers::panic("Unknown type for DSP::getEventRef");
}
}
void DSPService::registerInterruptEvents(u32 messagePointer) {
const u32 interrupt = mem.read32(messagePointer + 4);
const u32 channel = mem.read32(messagePointer + 8);
const u32 eventHandle = mem.read32(messagePointer + 16);
log("DSP::RegisterInterruptEvents (interrupt = %d, channel = %d, event = %d)\n", interrupt, channel, eventHandle);
// The event handle being 0 means we're removing an event
if (eventHandle == 0) {
Helpers::panic("DSP::DSP::RegisterinterruptEvents Trying to remove a registered interrupt");
} else {
const KernelObject* object = kernel.getObject(eventHandle, KernelObjectType::Event);
if (!object) {
Helpers::panic("DSP::DSP::RegisterInterruptEvents with invalid event handle");
}
if (totalEventCount >= maxEventCount)
Helpers::panic("DSP::RegisterInterruptEvents overflowed total number of allowed events");
else {
getEventRef(interrupt, channel) = eventHandle;
mem.write32(messagePointer, IPC::responseHeader(0x15, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
totalEventCount++;
kernel.signalEvent(eventHandle);
}
}
}
void DSPService::getHeadphoneStatus(u32 messagePointer) {
log("DSP::GetHeadphoneStatus\n");
mem.write32(messagePointer, IPC::responseHeader(0x1F, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, Result::HeadphonesInserted); // This should be toggleable for shits and giggles
}
void DSPService::getSemaphoreHandle(u32 messagePointer) {
log("DSP::GetSemaphoreHandle\n");
void DSPService::getSemaphoreEventHandle(u32 messagePointer) {
log("DSP::GetSemaphoreEventHandle\n");
if (!semaphoreEvent.has_value()) {
semaphoreEvent = kernel.makeEvent(ResetType::OneShot);
}
mem.write32(messagePointer, IPC::responseHeader(0x16, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 12, 0xF9991234); // Semaphore handle (stubbed with random, obvious number)
// TODO: Translation descriptor here?
mem.write32(messagePointer + 12, semaphoreEvent.value()); // Semaphore event handle
}
void DSPService::setSemaphore(u32 messagePointer) {
const u16 value = mem.read16(messagePointer + 4);
log("DSP::SetSemaphore(value = %04X)\n", value);
mem.write32(messagePointer, IPC::responseHeader(0x7, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -127,6 +183,7 @@ void DSPService::setSemaphoreMask(u32 messagePointer) {
const u16 mask = mem.read16(messagePointer + 4);
log("DSP::SetSemaphoreMask(mask = %04X)\n", mask);
mem.write32(messagePointer, IPC::responseHeader(0x17, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -136,6 +193,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);
mem.write32(messagePointer, IPC::responseHeader(0xD, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -145,6 +203,7 @@ void DSPService::flushDataCache(u32 messagePointer) {
const Handle process = mem.read32(messagePointer + 16);
log("DSP::FlushDataCache (addr = %08X, size = %08X, process = %X)\n", address, size, process);
mem.write32(messagePointer, IPC::responseHeader(0x13, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -154,5 +213,6 @@ void DSPService::invalidateDCache(u32 messagePointer) {
const Handle process = mem.read32(messagePointer + 16);
log("DSP::InvalidateDataCache (addr = %08X, size = %08X, process = %X)\n", address, size, process);
mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,5 +1,6 @@
#include "services/frd.hpp"
#include <string>
#include "services/frd.hpp"
#include "ipc.hpp"
namespace FRDCommands {
enum : u32 {
@ -43,6 +44,7 @@ void FRDService::attachToEventNotification(u32 messagePointer) {
void FRDService::getMyFriendKey(u32 messagePointer) {
log("FRD::GetMyFriendKey\n");
mem.write32(messagePointer, IPC::responseHeader(0x5, 5, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Principal ID
mem.write32(messagePointer + 12, 0); // Padding (?)
@ -56,6 +58,7 @@ void FRDService::getFriendKeyList(u32 messagePointer) {
const u32 count = mem.read32(messagePointer + 8); // From what I understand this is a cap on the number of keys to receive?
constexpr u32 friendCount = 0; // And this should be the number of friends whose keys were actually received?
mem.write32(messagePointer, IPC::responseHeader(0x11, 2, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, friendCount);
@ -74,6 +77,7 @@ void FRDService::getMyPresence(u32 messagePointer) {
mem.write32(buffer + i, 0);
}
mem.write32(messagePointer, IPC::responseHeader(0x8, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
}
@ -97,10 +101,13 @@ void FRDService::setClientSDKVersion(u32 messagePointer) {
u32 version = mem.read32(messagePointer + 4);
log("FRD::SetClientSdkVersion (version = %d)\n", version);
mem.write32(messagePointer, IPC::responseHeader(0x32, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void FRDService::setNotificationMask(u32 messagePointer) {
log("FRD::SetNotificationMask (Not documented)\n");
mem.write32(messagePointer, IPC::responseHeader(0x21, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,6 +1,7 @@
#include "services/fs.hpp"
#include "kernel/kernel.hpp"
#include "io_file.hpp"
#include "ipc.hpp"
#ifdef CreateFile // windows.h defines CreateFile & DeleteFile because of course it does.
#undef CreateFile
@ -16,6 +17,7 @@ namespace FSCommands {
CreateFile = 0x08080202,
OpenDirectory = 0x080B0102,
OpenArchive = 0x080C00C2,
ControlArchive = 0x080D0144,
CloseArchive = 0x080E0080,
IsSdmcDetected = 0x08170000,
GetFormatInfo = 0x084500C2,
@ -151,6 +153,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
const u32 command = mem.read32(messagePointer);
switch (command) {
case FSCommands::CreateFile: createFile(messagePointer); break;
case FSCommands::ControlArchive: controlArchive(messagePointer); break;
case FSCommands::CloseArchive: closeArchive(messagePointer); break;
case FSCommands::DeleteFile: deleteFile(messagePointer); break;
case FSCommands::FormatSaveData: formatSaveData(messagePointer); break;
@ -170,6 +173,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
void FSService::initialize(u32 messagePointer) {
log("FS::Initialize\n");
mem.write32(messagePointer, IPC::responseHeader(0x801, 1, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
}
@ -178,7 +182,8 @@ void FSService::initializeWithSdkVersion(u32 messagePointer) {
const auto version = mem.read32(messagePointer + 4);
log("FS::InitializeWithSDKVersion(version = %d)\n", version);
initialize(messagePointer);
mem.write32(messagePointer, IPC::responseHeader(0x861, 1, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
}
void FSService::closeArchive(u32 messagePointer) {
@ -186,6 +191,8 @@ void FSService::closeArchive(u32 messagePointer) {
const auto object = kernel.getObject(handle, KernelObjectType::Archive);
log("FSService::CloseArchive(handle = %X)\n", handle);
mem.write32(messagePointer, IPC::responseHeader(0x80E, 1, 0));
if (object == nullptr) {
log("FSService::CloseArchive: Tried to close invalid archive %X\n", handle);
mem.write32(messagePointer + 4, ResultCode::Failure);
@ -205,6 +212,7 @@ void FSService::openArchive(u32 messagePointer) {
log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType);
std::optional<Handle> handle = openArchiveHandle(archiveID, archivePath);
mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0));
if (handle.has_value()) {
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write64(messagePointer + 8, handle.value());
@ -215,7 +223,7 @@ void FSService::openArchive(u32 messagePointer) {
}
void FSService::openFile(u32 messagePointer) {
const u32 archiveHandle = mem.read64(messagePointer + 8);
const Handle archiveHandle = mem.read64(messagePointer + 8);
const u32 filePathType = mem.read32(messagePointer + 16);
const u32 filePathSize = mem.read32(messagePointer + 20);
const u32 openFlags = mem.read32(messagePointer + 24);
@ -238,6 +246,7 @@ void FSService::openFile(u32 messagePointer) {
const FilePerms perms(openFlags);
std::optional<Handle> handle = openFileHandle(archive, filePath, archivePath, perms);
mem.write32(messagePointer, IPC::responseHeader(0x802, 1, 2));
if (!handle.has_value()) {
printf("OpenFile failed\n");
mem.write32(messagePointer + 4, ResultCode::FileNotFound);
@ -266,6 +275,7 @@ void FSService::openDirectory(u32 messagePointer) {
const auto dirPath = readPath(pathType, pathPointer, pathSize);
auto dir = openDirectoryHandle(archive, dirPath);
mem.write32(messagePointer, IPC::responseHeader(0x80B, 1, 2));
if (dir.isOk()) {
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 12, dir.unwrap());
@ -302,6 +312,7 @@ void FSService::openFileDirectly(u32 messagePointer) {
}
std::optional<Handle> handle = openFileHandle(archive, filePath, archivePath, perms);
mem.write32(messagePointer, IPC::responseHeader(0x803, 1, 2));
if (!handle.has_value()) {
Helpers::panic("OpenFileDirectly: Failed to open file with given path");
} else {
@ -331,11 +342,12 @@ void FSService::createFile(u32 messagePointer) {
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
FSResult res = archive->createFile(filePath, size);
mem.write32(messagePointer, IPC::responseHeader(0x808, 1, 0));
mem.write32(messagePointer + 4, static_cast<u32>(res));
}
void FSService::deleteFile(u32 messagePointer) {
const u32 archiveHandle = mem.read64(messagePointer + 8);
const Handle archiveHandle = mem.read64(messagePointer + 8);
const u32 filePathType = mem.read32(messagePointer + 16);
const u32 filePathSize = mem.read32(messagePointer + 20);
const u32 filePathPointer = mem.read32(messagePointer + 28);
@ -352,6 +364,7 @@ void FSService::deleteFile(u32 messagePointer) {
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
FSResult res = archive->deleteFile(filePath);
mem.write32(messagePointer, IPC::responseHeader(0x804, 1, 0));
mem.write32(messagePointer + 4, static_cast<u32>(res));
}
@ -370,6 +383,7 @@ void FSService::getFormatInfo(u32 messagePointer) {
}
ArchiveBase::FormatInfo info = archive->getFormatInfo(path);
mem.write32(messagePointer, IPC::responseHeader(0x845, 5, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 8, info.size);
mem.write32(messagePointer + 12, info.numOfDirectories);
@ -400,11 +414,42 @@ void FSService::formatSaveData(u32 messagePointer) {
const bool duplicateData = mem.read8(messagePointer + 36) != 0;
printf("Stubbed FS::FormatSaveData. File num: %d, directory num: %d\n", fileNum, directoryNum);
mem.write32(messagePointer, IPC::responseHeader(0x84C, 1, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
}
void FSService::controlArchive(u32 messagePointer) {
const Handle archiveHandle = mem.read64(messagePointer + 4);
const u32 action = mem.read32(messagePointer + 12);
const u32 inputSize = mem.read32(messagePointer + 16);
const u32 outputSize = mem.read32(messagePointer + 20);
const u32 input = mem.read32(messagePointer + 28);
const u32 output = mem.read32(messagePointer + 36);
log("FS::ControlArchive (action = %X, handle = %X)\n", action, archiveHandle);
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
mem.write32(messagePointer, IPC::responseHeader(0x80D, 1, 0));
if (archiveObject == nullptr) [[unlikely]] {
log("FS::ControlArchive: Invalid archive handle %d\n", archiveHandle);
mem.write32(messagePointer + 4, ResultCode::Failure);
return;
}
switch (action) {
case 0: // Commit save data changes. Shouldn't need us to do anything
mem.write32(messagePointer + 4, ResultCode::Success);
break;
default:
Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action);
break;
}
}
void FSService::getPriority(u32 messagePointer) {
log("FS::GetPriority\n");
mem.write32(messagePointer, IPC::responseHeader(0x863, 2, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 8, priority);
}
@ -412,13 +457,15 @@ void FSService::getPriority(u32 messagePointer) {
void FSService::setPriority(u32 messagePointer) {
const u32 value = mem.read32(messagePointer + 4);
log("FS::SetPriority (priority = %d)\n", value);
mem.write32(messagePointer, IPC::responseHeader(0x862, 1, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
priority = value;
}
void FSService::isSdmcDetected(u32 messagePointer) {
log("FS::IsSdmcDetected\n");
mem.write32(messagePointer, IPC::responseHeader(0x817, 2, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 8, 0); // Whether SD is detected. For now we emulate a 3DS without an SD.
}

View file

@ -1,4 +1,6 @@
#include "services/gsp_gpu.hpp"
#include "ipc.hpp"
#include "kernel.hpp"
// Commands used with SendSyncRequest targetted to the GSP::GPU service
namespace ServiceCommands {
@ -37,6 +39,7 @@ namespace Result {
void GPUService::reset() {
privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle
interruptEvent = std::nullopt;
sharedMem = nullptr;
}
@ -72,6 +75,7 @@ void GPUService::acquireRight(u32 messagePointer) {
privilegedProcess = pid;
}
mem.write32(messagePointer, IPC::responseHeader(0x16, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -88,7 +92,15 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) {
const u32 eventHandle = mem.read32(messagePointer + 12);
log("GSP::GPU::RegisterInterruptRelayQueue (flags = %X, event handle = %X)\n", flags, eventHandle);
mem.write32(messagePointer + 4, Result::SuccessRegisterIRQ);
const auto event = kernel.getObject(eventHandle, KernelObjectType::Event);
if (event == nullptr) { // Check if interrupt event is invalid
Helpers::panic("Invalid event passed to GSP::GPU::RegisterInterruptRelayQueue");
} else {
interruptEvent = eventHandle;
}
mem.write32(messagePointer, IPC::responseHeader(0x13, 2, 2));
mem.write32(messagePointer + 4, Result::SuccessRegisterIRQ); // First init returns a unique result
mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index
mem.write32(messagePointer + 12, 0); // Translation descriptor
mem.write32(messagePointer + 16, KernelHandles::GSPSharedMemHandle);
@ -106,6 +118,11 @@ void GPUService::requestInterrupt(GPUInterrupt type) {
sharedMem[2] = 0; // Set error code to 0
sharedMem[0xC + flagIndex] = static_cast<u8>(type); // Write interrupt type to queue
// Signal interrupt event
if (interruptEvent.has_value()) {
kernel.signalEvent(interruptEvent.value());
}
}
void GPUService::writeHwRegs(u32 messagePointer) {
@ -134,6 +151,8 @@ void GPUService::writeHwRegs(u32 messagePointer) {
dataPointer += 4;
ioAddr += 4;
}
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -176,6 +195,7 @@ void GPUService::writeHwRegsWithMask(u32 messagePointer) {
ioAddr += 4;
}
mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -185,6 +205,7 @@ void GPUService::flushDataCache(u32 messagePointer) {
u32 processHandle = handle = mem.read32(messagePointer + 16);
log("GSP::GPU::FlushDataCache(address = %08X, size = %X, process = %X\n", address, size, processHandle);
mem.write32(messagePointer, IPC::responseHeader(0x8, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -194,6 +215,7 @@ void GPUService::storeDataCache(u32 messagePointer) {
u32 processHandle = handle = mem.read32(messagePointer + 16);
log("GSP::GPU::StoreDataCache(address = %08X, size = %X, process = %X\n", address, size, processHandle);
mem.write32(messagePointer, IPC::responseHeader(0x1F, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -205,23 +227,27 @@ void GPUService::setLCDForceBlack(u32 messagePointer) {
printf("Filled both LCDs with black\n");
}
mem.write32(messagePointer, IPC::responseHeader(0xB, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void GPUService::triggerCmdReqQueue(u32 messagePointer) {
processCommandBuffer();
mem.write32(messagePointer, IPC::responseHeader(0xC, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
// Seems to be completely undocumented, probably not very important or useful
void GPUService::setAxiConfigQoSMode(u32 messagePointer) {
log("GSP::GPU::SetAxiConfigQoSMode\n");
mem.write32(messagePointer, IPC::responseHeader(0x10, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
// Seems to also be completely undocumented
void GPUService::setInternalPriorities(u32 messagePointer) {
log("GSP::GPU::SetInternalPriorities\n");
mem.write32(messagePointer, IPC::responseHeader(0x1E, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,4 +1,5 @@
#include "services/gsp_lcd.hpp"
#include "ipc.hpp"
namespace LCDCommands {
enum : u32 {

View file

@ -1,4 +1,5 @@
#include "services/hid.hpp"
#include "ipc.hpp"
#include <bit>
namespace HIDCommands {
@ -38,20 +39,25 @@ void HIDService::handleSyncRequest(u32 messagePointer) {
void HIDService::enableAccelerometer(u32 messagePointer) {
log("HID::EnableAccelerometer\n");
mem.write32(messagePointer + 4, Result::Success);
accelerometerEnabled = true;
mem.write32(messagePointer, IPC::responseHeader(0x11, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void HIDService::enableGyroscopeLow(u32 messagePointer) {
log("HID::EnableGyroscopeLow\n");
mem.write32(messagePointer + 4, Result::Success);
gyroEnabled = true;
mem.write32(messagePointer, IPC::responseHeader(0x13, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void HIDService::getGyroscopeLowCalibrateParam(u32 messagePointer) {
log("HID::GetGyroscopeLowCalibrateParam\n");
constexpr s16 unit = 6700; // Approximately from Citra which took it from hardware
mem.write32(messagePointer, IPC::responseHeader(0x16, 6, 0));
mem.write32(messagePointer + 4, Result::Success);
// Fill calibration data (for x/y/z depending on i)
for (int i = 0; i < 3; i++) {
@ -67,12 +73,14 @@ void HIDService::getGyroscopeCoefficient(u32 messagePointer) {
log("HID::GetGyroscopeLowRawToDpsCoefficient\n");
constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
mem.write32(messagePointer, IPC::responseHeader(0x15, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, std::bit_cast<u32, float>(gyroscopeCoeff));
}
void HIDService::getIPCHandles(u32 messagePointer) {
log("HID::GetIPCHandles\n");
mem.write32(messagePointer, IPC::responseHeader(0xA, 1, 7));
mem.write32(messagePointer + 4, Result::Success); // Result code
mem.write32(messagePointer + 8, 0x14000000); // Translation descriptor
mem.write32(messagePointer + 12, KernelHandles::HIDSharedMemHandle); // Shared memory handle

View file

@ -1,4 +1,5 @@
#include "services/ldr_ro.hpp"
#include "ipc.hpp"
namespace LDRCommands {
enum : u32 {
@ -31,6 +32,7 @@ void LDRService::initialize(u32 messagePointer) {
const Handle process = mem.read32(messagePointer + 20);
log("LDR_RO::Initialize (buffer = %08X, size = %08X, vaddr = %08X, process = %X)\n", crsPointer, size, mapVaddr, process);
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -40,5 +42,6 @@ void LDRService::loadCRR(u32 messagePointer) {
const Handle process = mem.read32(messagePointer + 20);
log("LDR_RO::LoadCRR (buffer = %08X, size = %08X, process = %X)\n", crrPointer, size, process);
mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,4 +1,5 @@
#include "services/mic.hpp"
#include "ipc.hpp"
namespace MICCommands {
enum : u32 {
@ -41,11 +42,13 @@ void MICService::mapSharedMem(u32 messagePointer) {
u32 handle = mem.read32(messagePointer + 12);
log("MIC::MapSharedMem (size = %08X, handle = %X) (stubbed)\n", size, handle);
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void MICService::getGain(u32 messagePointer) {
log("MIC::GetGain\n");
mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, gain);
}
@ -54,6 +57,7 @@ void MICService::setGain(u32 messagePointer) {
gain = mem.read8(messagePointer + 4);
log("MIC::SetGain (value = %d)\n", gain);
mem.write32(messagePointer, IPC::responseHeader(0x8, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -62,6 +66,7 @@ void MICService::setPower(u32 messagePointer) {
log("MIC::SetPower (value = %d)\n", val);
micEnabled = val != 0;
mem.write32(messagePointer, IPC::responseHeader(0xA, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -70,6 +75,7 @@ void MICService::setClamp(u32 messagePointer) {
log("MIC::SetClamp (value = %d)\n", val);
shouldClamp = val != 0;
mem.write32(messagePointer, IPC::responseHeader(0xD, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -84,5 +90,6 @@ void MICService::startSampling(u32 messagePointer) {
encoding, sampleRate, offset, dataSize, loop ? "yes" : "no"
);
mem.write32(messagePointer, IPC::responseHeader(0x3, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,4 +1,5 @@
#include "services/ndm.hpp"
#include "ipc.hpp"
namespace NDMCommands {
enum : u32 {
@ -32,25 +33,30 @@ void NDMService::handleSyncRequest(u32 messagePointer) {
void NDMService::overrideDefaultDaemons(u32 messagePointer) {
log("NDM::OverrideDefaultDaemons(stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void NDMService::resumeDaemons(u32 messagePointer) {
log("NDM::resumeDaemons(stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x7, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void NDMService::suspendDaemons(u32 messagePointer) {
log("NDM::SuspendDaemons(stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void NDMService::resumeScheduler(u32 messagePointer) {
log("NDM::ResumeScheduler(stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x9, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void NDMService::suspendScheduler(u32 messagePointer) {
log("NDM::SuspendScheduler(stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x8, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,4 +1,5 @@
#include "services/nim.hpp"
#include "ipc.hpp"
namespace NIMCommands {
enum : u32 {
@ -24,5 +25,6 @@ void NIMService::handleSyncRequest(u32 messagePointer) {
void NIMService::initialize(u32 messagePointer) {
log("NIM::Initialize\n");
mem.write32(messagePointer, IPC::responseHeader(0x21, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,9 +1,11 @@
#include "services/ptm.hpp"
#include "ipc.hpp"
namespace PTMCommands {
enum : u32 {
GetStepHistory = 0x000B00C2,
GetTotalStepCount = 0x000C0000
GetTotalStepCount = 0x000C0000,
ConfigureNew3DSCPU = 0x08180040
};
}
@ -18,7 +20,8 @@ void PTMService::reset() {}
void PTMService::handleSyncRequest(u32 messagePointer) {
const u32 command = mem.read32(messagePointer);
switch (command) {
case PTMCommands::GetStepHistory: getStepHistory(messagePointer); break;
case PTMCommands::ConfigureNew3DSCPU: configureNew3DSCPU(messagePointer); break;
case PTMCommands::GetStepHistory: getStepHistory(messagePointer); break;
case PTMCommands::GetTotalStepCount: getTotalStepCount(messagePointer); break;
default: Helpers::panic("PTM service requested. Command: %08X\n", command);
}
@ -26,11 +29,19 @@ void PTMService::handleSyncRequest(u32 messagePointer) {
void PTMService::getStepHistory(u32 messagePointer) {
log("PTM::GetStepHistory [stubbed]\n");
mem.write32(messagePointer, IPC::responseHeader(0xB, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
}
void PTMService::getTotalStepCount(u32 messagePointer) {
log("PTM::GetTotalStepCount\n");
mem.write32(messagePointer, IPC::responseHeader(0xC, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 3); // We walk a lot
}
void PTMService::configureNew3DSCPU(u32 messagePointer) {
log("PTM::ConfigureNew3DSCPU [stubbed]\n");
mem.write32(messagePointer, IPC::responseHeader(0x818, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,11 +1,12 @@
#include "services/service_manager.hpp"
#include <map>
#include "ipc.hpp"
#include "kernel.hpp"
ServiceManager::ServiceManager(std::array<u32, 16>& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel)
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), apt(mem, kernel), cam(mem), cecd(mem), cfg(mem),
dsp(mem), hid(mem), frd(mem), fs(mem, kernel), gsp_gpu(mem, gpu, currentPID), gsp_lcd(mem), ldr(mem), mic(mem),
nim(mem), ndm(mem), ptm(mem), y2r(mem) {}
dsp(mem, kernel), hid(mem), frd(mem), fs(mem, kernel), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem),
mic(mem), nim(mem), ndm(mem), ptm(mem), y2r(mem, kernel) {}
static constexpr int MAX_NOTIFICATION_COUNT = 16;
@ -79,6 +80,7 @@ void ServiceManager::handleSyncRequest(u32 messagePointer) {
// https://www.3dbrew.org/wiki/SRV:RegisterClient
void ServiceManager::registerClient(u32 messagePointer) {
log("srv::registerClient (Stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -102,7 +104,8 @@ static std::map<std::string, Handle> serviceMap = {
{ "mic:u", KernelHandles::MIC },
{ "ndm:u", KernelHandles::NDM },
{ "nim:aoc", KernelHandles::NIM },
{ "ptm:u", KernelHandles::PTM },
{ "ptm:u", KernelHandles::PTM }, // TODO: ptm:u and ptm:sysm have very different command sets
{ "ptm:sysm", KernelHandles::PTM },
{ "y2r:u", KernelHandles::Y2R }
};
@ -121,6 +124,7 @@ void ServiceManager::getServiceHandle(u32 messagePointer) {
else
Helpers::panic("srv: GetServiceHandle with unknown service %s", service.c_str());
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 12, handle);
}
@ -133,6 +137,7 @@ void ServiceManager::enableNotification(u32 messagePointer) {
notificationSemaphore = kernel.makeSemaphore(0, MAX_NOTIFICATION_COUNT);
}
mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 2));
mem.write32(messagePointer + 4, Result::Success); // Result code
mem.write32(messagePointer + 8, 0); // Translation descriptor
// Handle to semaphore signaled on process notification
@ -142,6 +147,7 @@ void ServiceManager::enableNotification(u32 messagePointer) {
void ServiceManager::receiveNotification(u32 messagePointer) {
log("srv::ReceiveNotification() (STUBBED)\n");
mem.write32(messagePointer, IPC::responseHeader(0xB, 2, 0));
mem.write32(messagePointer + 4, Result::Success); // Result code
mem.write32(messagePointer + 8, 0); // Notification ID
}
@ -150,6 +156,7 @@ void ServiceManager::subscribe(u32 messagePointer) {
u32 id = mem.read32(messagePointer + 4);
log("srv::Subscribe (id = %d) (stubbed)\n", id);
mem.write32(messagePointer, IPC::responseHeader(0x9, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,8 +1,11 @@
#include "services/y2r.hpp"
#include "ipc.hpp"
#include "kernel.hpp"
namespace Y2RCommands {
enum : u32 {
SetTransferEndInterrupt = 0x000D0040,
GetTransferEndEvent = 0x000F0000,
PingProcess = 0x002A0000,
DriverInitialize = 0x002B0000
};
@ -16,12 +19,14 @@ namespace Result {
void Y2RService::reset() {
transferEndInterruptEnabled = false;
transferEndEvent = std::nullopt;
}
void Y2RService::handleSyncRequest(u32 messagePointer) {
const u32 command = mem.read32(messagePointer);
switch (command) {
case Y2RCommands::DriverInitialize: driverInitialize(messagePointer); break;
case Y2RCommands::GetTransferEndEvent: getTransferEndEvent(messagePointer); break;
case Y2RCommands::PingProcess: pingProcess(messagePointer); break;
case Y2RCommands::SetTransferEndInterrupt: setTransferEndInterrupt(messagePointer); break;
default: Helpers::panic("Y2R service requested. Command: %08X\n", command);
@ -30,19 +35,32 @@ void Y2RService::handleSyncRequest(u32 messagePointer) {
void Y2RService::pingProcess(u32 messagePointer) {
log("Y2R::PingProcess\n");
mem.write32(messagePointer, IPC::responseHeader(0x2A, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Connected number
}
void Y2RService::driverInitialize(u32 messagePointer) {
log("Y2R::DriverInitialize\n");
mem.write32(messagePointer, IPC::responseHeader(0x2B, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void Y2RService::getTransferEndEvent(u32 messagePointer) {
log("Y2R::GetTransferEndEvent\n");
if (!transferEndEvent.has_value())
transferEndEvent = kernel.makeEvent(ResetType::OneShot);
mem.write32(messagePointer, IPC::responseHeader(0xF, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 12, transferEndEvent.value());
}
void Y2RService::setTransferEndInterrupt(u32 messagePointer) {
const bool enable = mem.read32(messagePointer + 4) != 0;
log("Y2R::SetTransferEndInterrupt (enabled: %s)\n", enable ? "yes" : "no");
mem.write32(messagePointer, IPC::responseHeader(0xD, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
transferEndInterruptEnabled = enable;
}