mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-07-20 05:51:44 +12:00
Merge branch 'Sync-Objects' of https://github.com/wheremyfoodat/Virtual3DS into Sync-Objects
This commit is contained in:
commit
8cfb038226
49 changed files with 1256 additions and 138 deletions
|
@ -81,6 +81,7 @@ namespace PICAInternalRegs {
|
|||
CmdBufTrigger1 = 0x23D,
|
||||
|
||||
PrimitiveConfig = 0x25E,
|
||||
PrimitiveRestart = 0x25F,
|
||||
|
||||
// Vertex shader registers
|
||||
VertexShaderAttrNum = 0x242,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
// Status register definitions
|
||||
namespace CPSR {
|
||||
enum : u32 {
|
||||
enum : std::uint32_t {
|
||||
// Privilege modes
|
||||
UserMode = 16,
|
||||
FIQMode = 17,
|
||||
|
@ -26,7 +27,7 @@ namespace CPSR {
|
|||
|
||||
namespace FPSCR {
|
||||
// FPSCR Flags
|
||||
enum : u32 {
|
||||
enum : std::uint32_t {
|
||||
Sign = 1U << 31U, // Negative condition flag
|
||||
Zero = 1 << 30, // Zero condition flag
|
||||
Carry = 1 << 29, // Carry condition flag
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <io.h> // For _chsize_s
|
||||
#else
|
||||
#include <unistd.h> // For ftruncate
|
||||
#endif
|
||||
|
||||
class IOFile {
|
||||
FILE* handle = nullptr;
|
||||
static inline std::filesystem::path appData = ""; // Directory for holding app data. AppData on Windows
|
||||
|
@ -112,5 +118,19 @@ public:
|
|||
appData = dir;
|
||||
}
|
||||
|
||||
// Sets the size of the file to "size" and returns whether it succeeded or not
|
||||
bool setSize(std::uint64_t size) {
|
||||
if (!isOpen()) return false;
|
||||
bool success;
|
||||
|
||||
#ifdef WIN32
|
||||
success = _chsize_s(_fileno(handle), size) == 0;
|
||||
#else
|
||||
success = ftruncate(fileno(handle), size) == 0;
|
||||
#endif
|
||||
fflush(handle);
|
||||
return success;
|
||||
}
|
||||
|
||||
static std::filesystem::path getAppData() { return IOFile::appData; }
|
||||
};
|
9
include/ipc.hpp
Normal file
9
include/ipc.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace IPC {
|
||||
constexpr std::uint32_t responseHeader(std::uint32_t commandID, std::uint32_t normalResponses, std::uint32_t translateResponses) {
|
||||
// TODO: Maybe validate the response count stuff fits in 6 bits
|
||||
return (commandID << 16) | (normalResponses << 6) | translateResponses;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ namespace ConfigMem {
|
|||
NetworkState = 0x1FF81067,
|
||||
LedState3D = 0x1FF81084,
|
||||
BatteryState = 0x1FF81085,
|
||||
Unknown1086 = 0x1FF81086,
|
||||
HeadphonesConnectedMaybe = 0x1FF810C0 // TODO: What is actually stored here?
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -19,7 +20,16 @@ class Kernel {
|
|||
|
||||
// The handle number for the next kernel object to be created
|
||||
u32 handleCounter;
|
||||
std::array<Thread, appResourceLimits.maxThreads> threads;
|
||||
// A list of our OS threads, the max number of which depends on the resource limit (hardcoded 32 per process on retail it seems).
|
||||
// We have an extra thread for when no thread is capable of running. This thread is called the "idle thread" in our code
|
||||
// This thread is set up in setupIdleThread and just yields in a loop to see if any other thread has woken up
|
||||
std::array<Thread, appResourceLimits.maxThreads + 1> threads;
|
||||
static constexpr int idleThreadIndex = appResourceLimits.maxThreads;
|
||||
// Our waitlist system uses a bitfield of 64 bits to show which threads are waiting on an object.
|
||||
// That means we can have a maximum of 63 threads + 1 idle thread. This assert should never trigger because the max thread # is 32
|
||||
// But we have it here for safety purposes
|
||||
static_assert(appResourceLimits.maxThreads <= 63, "The waitlist system is built on the premise that <= 63 threads max can be active");
|
||||
|
||||
std::vector<KernelObject> objects;
|
||||
std::vector<Handle> portHandles;
|
||||
|
||||
|
@ -52,6 +62,8 @@ public:
|
|||
Handle makeMutex(bool locked = false); // Needs to be public to be accessible to the APT/DSP services
|
||||
Handle makeSemaphore(u32 initialCount, u32 maximumCount); // Needs to be public to be accessible to the service manager port
|
||||
|
||||
// Signals an event, returns true on success or false if the event does not exist
|
||||
bool signalEvent(Handle e);
|
||||
private:
|
||||
void signalArbiter(u32 waitingAddress, s32 threadCount);
|
||||
void sleepThread(s64 ns);
|
||||
|
@ -63,6 +75,7 @@ private:
|
|||
void rescheduleThreads();
|
||||
bool canThreadRun(const Thread& t);
|
||||
bool shouldWaitOnObject(KernelObject* object);
|
||||
void releaseMutex(Mutex* moo);
|
||||
|
||||
std::optional<Handle> getPortHandle(const char* name);
|
||||
void deleteObjectData(KernelObject& object);
|
||||
|
@ -71,7 +84,9 @@ private:
|
|||
s32 getCurrentResourceValue(const KernelObject* limit, u32 resourceName);
|
||||
u32 getMaxForResource(const KernelObject* limit, u32 resourceName);
|
||||
u32 getTLSPointer();
|
||||
void setupIdleThread();
|
||||
|
||||
void acquireSyncObject(KernelObject* object, const Thread& thread);
|
||||
bool isWaitable(const KernelObject* object);
|
||||
|
||||
// Functions for the err:f port
|
||||
|
@ -90,11 +105,8 @@ private:
|
|||
|
||||
// SVC implementations
|
||||
void arbitrateAddress();
|
||||
void clearEvent();
|
||||
void createAddressArbiter();
|
||||
void createEvent();
|
||||
void createMemoryBlock();
|
||||
void createMutex();
|
||||
void createThread();
|
||||
void controlMemory();
|
||||
void duplicateHandle();
|
||||
|
@ -109,11 +121,14 @@ private:
|
|||
void getSystemTick();
|
||||
void getThreadID();
|
||||
void getThreadPriority();
|
||||
void releaseMutex();
|
||||
void sendSyncRequest();
|
||||
void setThreadPriority();
|
||||
void signalEvent();
|
||||
void svcClearEvent();
|
||||
void svcCloseHandle();
|
||||
void svcCreateEvent();
|
||||
void svcCreateMutex();
|
||||
void svcReleaseMutex();
|
||||
void svcSignalEvent();
|
||||
void svcSleepThread();
|
||||
void connectToPort();
|
||||
void outputDebugString();
|
||||
|
@ -127,6 +142,7 @@ private:
|
|||
void writeFile(u32 messagePointer, Handle file);
|
||||
void getFileSize(u32 messagePointer, Handle file);
|
||||
void openLinkFile(u32 messagePointer, Handle file);
|
||||
void setFileSize(u32 messagePointer, Handle file);
|
||||
void setFilePriority(u32 messagePointer, Handle file);
|
||||
|
||||
// Directory operations
|
||||
|
|
|
@ -23,6 +23,12 @@ namespace SVCResult {
|
|||
|
||||
BadThreadPriority = 0xE0E01BFD,
|
||||
PortNameTooLong = 0xE0E0181E,
|
||||
|
||||
// Returned when a thread stops waiting due to timing out
|
||||
Timeout = 0x9401BFE,
|
||||
|
||||
// Returned when a thread releases a mutex it does not own
|
||||
InvalidMutexRelease = 0xD8E0041F
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -72,10 +78,11 @@ struct Process {
|
|||
};
|
||||
|
||||
struct Event {
|
||||
u64 waitlist; // A bitfield where each bit symbolizes if the thread with thread with the corresponding index is waiting on the event
|
||||
ResetType resetType = ResetType::OneShot;
|
||||
bool fired = false;
|
||||
|
||||
Event(ResetType resetType) : resetType(resetType) {}
|
||||
Event(ResetType resetType) : resetType(resetType), waitlist(0) {}
|
||||
};
|
||||
|
||||
struct Port {
|
||||
|
@ -101,7 +108,8 @@ enum class ThreadStatus {
|
|||
Ready, // Ready to run
|
||||
WaitArbiter, // Waiting on an address arbiter
|
||||
WaitSleep, // Waiting due to a SleepThread SVC
|
||||
WaitSync1, // Waiting for AT LEAST one sync object in its wait list to be ready
|
||||
WaitSync1, // Waiting for the single object in the wait list to be ready
|
||||
WaitSyncAny, // Wait for one object of the many that might be in the wait list to be ready
|
||||
WaitSyncAll, // Waiting for ALL sync objects in its wait list to be ready
|
||||
WaitIPC, // Waiting for the reply from an IPC request
|
||||
Dormant, // Created but not yet made ready
|
||||
|
@ -138,6 +146,9 @@ struct Thread {
|
|||
u32 cpsr;
|
||||
u32 fpscr;
|
||||
u32 tlsBase; // Base pointer for thread-local storage
|
||||
|
||||
// A list of threads waiting for this thread to terminate. Yes, threads are sync objects too.
|
||||
u64 threadsWaitingForTermination;
|
||||
};
|
||||
|
||||
static const char* kernelObjectTypeToString(KernelObjectType t) {
|
||||
|
@ -161,17 +172,20 @@ static const char* kernelObjectTypeToString(KernelObjectType t) {
|
|||
}
|
||||
|
||||
struct Mutex {
|
||||
u64 waitlist; // Refer to the getWaitlist function below for documentation
|
||||
Handle ownerThread = 0; // Index of the thread that holds the mutex if it's locked
|
||||
u32 lockCount; // Number of times this mutex has been locked by its daddy. 0 = not locked
|
||||
bool locked;
|
||||
|
||||
Mutex(bool lock = false) : locked(lock) {}
|
||||
Mutex(bool lock = false) : locked(lock), waitlist(0), lockCount(lock ? 1 : 0) {}
|
||||
};
|
||||
|
||||
struct Semaphore {
|
||||
u32 initialCount;
|
||||
u32 maximumCount;
|
||||
u64 waitlist; // Refer to the getWaitlist function below for documentation
|
||||
s32 availableCount;
|
||||
s32 maximumCount;
|
||||
|
||||
Semaphore(u32 initialCount, u32 maximumCount) : initialCount(initialCount), maximumCount(maximumCount) {}
|
||||
Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {}
|
||||
};
|
||||
|
||||
struct MemoryBlock {
|
||||
|
@ -202,7 +216,26 @@ struct KernelObject {
|
|||
return static_cast<T*>(data);
|
||||
}
|
||||
|
||||
const char* getTypeName() {
|
||||
const char* getTypeName() const {
|
||||
return kernelObjectTypeToString(type);
|
||||
}
|
||||
|
||||
// Retrieves a reference to the waitlist for a specified object
|
||||
// We return a reference because this function is only called in the kernel threading internals
|
||||
// We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits.
|
||||
// As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient.
|
||||
// Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object
|
||||
// For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object
|
||||
u64& getWaitlist() {
|
||||
// This code is actually kinda trash but eh good enough
|
||||
switch (type) {
|
||||
case KernelObjectType::Event: return getData<Event>()->waitlist;
|
||||
case KernelObjectType::Mutex: return getData<Mutex>()->waitlist;
|
||||
case KernelObjectType::Semaphore: return getData<Mutex>()->waitlist;
|
||||
case KernelObjectType::Thread: return getData<Thread>()->threadsWaitingForTermination;
|
||||
// This should be unreachable once we fully implement sync objects
|
||||
default: [[unlikely]]
|
||||
Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName());
|
||||
}
|
||||
}
|
||||
};
|
|
@ -111,6 +111,7 @@ class Memory {
|
|||
SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle) // HID shared memory
|
||||
};
|
||||
|
||||
public:
|
||||
static constexpr u32 pageShift = 12;
|
||||
static constexpr u32 pageSize = 1 << pageShift;
|
||||
static constexpr u32 pageMask = pageSize - 1;
|
||||
|
@ -125,6 +126,7 @@ class Memory {
|
|||
static constexpr u32 DSP_CODE_MEMORY_OFFSET = 0_KB;
|
||||
static constexpr u32 DSP_DATA_MEMORY_OFFSET = 256_KB;
|
||||
|
||||
private:
|
||||
std::bitset<FCRAM_PAGE_COUNT> usedFCRAMPages;
|
||||
std::optional<u32> findPaddr(u32 size);
|
||||
u64 timeSince3DSEpoch();
|
||||
|
|
|
@ -39,7 +39,7 @@ class Renderer {
|
|||
|
||||
SurfaceCache<DepthBuffer, 10> depthBufferCache;
|
||||
SurfaceCache<ColourBuffer, 10> colourBufferCache;
|
||||
SurfaceCache<Texture, 16> textureCache;
|
||||
SurfaceCache<Texture, 256> textureCache;
|
||||
|
||||
OpenGL::uvec2 fbSize; // The size of the framebuffer (ie both the colour and depth buffer)'
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ class APTService {
|
|||
void enable(u32 messagePointer);
|
||||
void getSharedFont(u32 messagePointer);
|
||||
void getWirelessRebootInfo(u32 messagePointer);
|
||||
void glanceParameter(u32 messagePointer);
|
||||
void initialize(u32 messagePointer);
|
||||
void inquireNotification(u32 messagePointer);
|
||||
void notifyToWait(u32 messagePointer);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include "helpers.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
|
@ -41,18 +43,38 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// Circular dependencies!
|
||||
class Kernel;
|
||||
|
||||
class DSPService {
|
||||
Handle handle = KernelHandles::DSP;
|
||||
Memory& mem;
|
||||
Kernel& kernel;
|
||||
MAKE_LOG_FUNCTION(log, dspServiceLogger)
|
||||
|
||||
// Number of DSP pipes
|
||||
static constexpr size_t pipeCount = 8;
|
||||
DSPPipe audioPipe;
|
||||
|
||||
// DSP service event handles
|
||||
using DSPEvent = std::optional<Handle>;
|
||||
|
||||
DSPEvent semaphoreEvent;
|
||||
DSPEvent interrupt0;
|
||||
DSPEvent interrupt1;
|
||||
std::array<DSPEvent, pipeCount> pipeEvents;
|
||||
|
||||
DSPEvent& getEventRef(u32 type, u32 pipe);
|
||||
static constexpr size_t maxEventCount = 6;
|
||||
|
||||
// Total number of DSP service events registered with registerInterruptEvents
|
||||
size_t totalEventCount;
|
||||
|
||||
// Service functions
|
||||
void convertProcessAddressFromDspDram(u32 messagePointer); // Nice function name
|
||||
void flushDataCache(u32 messagePointer);
|
||||
void getHeadphoneStatus(u32 messagePointer);
|
||||
void getSemaphoreHandle(u32 messagePointer);
|
||||
void getSemaphoreEventHandle(u32 messagePointer);
|
||||
void invalidateDCache(u32 messagePointer);
|
||||
void loadComponent(u32 messagePointer);
|
||||
void readPipeIfPossible(u32 messagePointer);
|
||||
|
@ -62,7 +84,7 @@ class DSPService {
|
|||
void writeProcessPipe(u32 messagePointer);
|
||||
|
||||
public:
|
||||
DSPService(Memory& mem) : mem(mem) {}
|
||||
DSPService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ class FSService {
|
|||
// Service commands
|
||||
void createFile(u32 messagePointer);
|
||||
void closeArchive(u32 messagePointer);
|
||||
void controlArchive(u32 messagePointer);
|
||||
void deleteFile(u32 messagePointer);
|
||||
void formatSaveData(u32 messagePointer);
|
||||
void getFormatInfo(u32 messagePointer);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include "PICA/gpu.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
|
@ -16,16 +17,21 @@ enum class GPUInterrupt : u8 {
|
|||
DMA = 6
|
||||
};
|
||||
|
||||
// More circular dependencies
|
||||
class Kernel;
|
||||
|
||||
class GPUService {
|
||||
Handle handle = KernelHandles::GPU;
|
||||
Memory& mem;
|
||||
GPU& gpu;
|
||||
Kernel& kernel;
|
||||
u32& currentPID; // Process ID of the current process
|
||||
u8* sharedMem; // Pointer to GSP shared memory
|
||||
|
||||
// At any point in time only 1 process has privileges to use rendering functions
|
||||
// This is the PID of that process
|
||||
u32 privilegedProcess;
|
||||
std::optional<Handle> interruptEvent;
|
||||
|
||||
MAKE_LOG_FUNCTION(log, gspGPULogger)
|
||||
void processCommandBuffer();
|
||||
|
@ -51,7 +57,8 @@ class GPUService {
|
|||
void flushCacheRegions(u32* cmd);
|
||||
|
||||
public:
|
||||
GPUService(Memory& mem, GPU& gpu, u32& currentPID) : mem(mem), gpu(gpu), currentPID(currentPID) {}
|
||||
GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu),
|
||||
kernel(kernel), currentPID(currentPID) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
void requestInterrupt(GPUInterrupt type);
|
||||
|
|
|
@ -10,6 +10,7 @@ class PTMService {
|
|||
MAKE_LOG_FUNCTION(log, ptmLogger)
|
||||
|
||||
// Service commands
|
||||
void configureNew3DSCPU(u32 messagePointer);
|
||||
void getStepHistory(u32 messagePointer);
|
||||
void getTotalStepCount(u32 messagePointer);
|
||||
|
||||
|
|
|
@ -1,23 +1,30 @@
|
|||
#pragma once
|
||||
#include <optional>
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
// Circular dependencies go br
|
||||
class Kernel;
|
||||
|
||||
class Y2RService {
|
||||
Handle handle = KernelHandles::Y2R;
|
||||
Memory& mem;
|
||||
Kernel& kernel;
|
||||
MAKE_LOG_FUNCTION(log, y2rLogger)
|
||||
|
||||
std::optional<Handle> transferEndEvent;
|
||||
bool transferEndInterruptEnabled;
|
||||
|
||||
// Service commands
|
||||
void driverInitialize(u32 messagePointer);
|
||||
void pingProcess(u32 messagePointer);
|
||||
void setTransferEndInterrupt(u32 messagePointer);
|
||||
void getTransferEndEvent(u32 messagePointer);
|
||||
|
||||
public:
|
||||
Y2RService(Memory& mem) : mem(mem) {}
|
||||
Y2RService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue