Merge branch 'master' into specialized-shaderz

This commit is contained in:
wheremyfoodat 2024-02-02 22:22:23 +02:00
commit 9dc12e0681
77 changed files with 2403 additions and 305 deletions

View file

@ -1,6 +1,9 @@
#pragma once
#include <vector>
#include "helpers.hpp"
#include "kernel/kernel_types.hpp"
#include "memory.hpp"
#include "result/result.hpp"
@ -65,10 +68,11 @@ namespace Applets {
};
struct Parameter {
u32 senderID;
u32 destID;
u32 signal;
std::vector<u8> data;
u32 senderID; // ID of the parameter sender
u32 destID; // ID of the app to receive parameter
u32 signal; // Signal type (eg request)
u32 object; // Some applets will also respond with shared memory handles for transferring data between the sender and called
std::vector<u8> data; // Misc data
};
class AppletBase {
@ -80,7 +84,7 @@ namespace Applets {
virtual const char* name() = 0;
// Called by APT::StartLibraryApplet and similar
virtual Result::HorizonResult start() = 0;
virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector<u8>& parameters, u32 appID) = 0;
// Transfer parameters from application -> applet
virtual Result::HorizonResult receiveParameter(const Parameter& parameter) = 0;
virtual void reset() = 0;

View file

@ -1,6 +1,7 @@
#pragma once
#include <optional>
#include "applets/error_applet.hpp"
#include "applets/mii_selector.hpp"
#include "applets/software_keyboard.hpp"
#include "helpers.hpp"
@ -11,6 +12,7 @@ namespace Applets {
class AppletManager {
MiiSelectorApplet miiSelector;
SoftwareKeyboardApplet swkbd;
ErrorApplet error;
std::optional<Applets::Parameter> nextParameter = std::nullopt;
public:

View file

@ -0,0 +1,15 @@
#include <string>
#include "applets/applet.hpp"
namespace Applets {
class ErrorApplet final : public AppletBase {
public:
virtual const char* name() override { return "Error/EULA Agreement"; }
virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector<u8>& parameters, u32 appID) override;
virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override;
virtual void reset() override;
ErrorApplet(Memory& memory, std::optional<Parameter>& nextParam) : AppletBase(memory, nextParam) {}
};
} // namespace Applets

View file

@ -1,13 +1,83 @@
#include <string>
#include "applets/applet.hpp"
#include "swap.hpp"
namespace Applets {
struct MiiConfig {
u8 enableCancelButton;
u8 enableGuestMii;
u8 showOnTopScreen;
std::array<u8, 0x5> pad1;
std::array<u16_le, 0x40> title;
std::array<u8, 0x4> pad2;
u8 showGuestMiis;
std::array<u8, 0x3> pad3;
u32 initiallySelectedIndex;
std::array<u8, 0x6> guestMiiWhitelist;
std::array<u8, 0x64> userMiiWhitelist;
std::array<u8, 0x2> pad4;
u32 magicValue;
};
static_assert(sizeof(MiiConfig) == 0x104, "Mii config size is wrong");
// Some members of this struct are not properly aligned so we need pragma pack
#pragma pack(push, 1)
struct MiiData {
u8 version;
u8 miiOptions;
u8 miiPos;
u8 consoleID;
u64_be systemID;
u32_be miiID;
std::array<u8, 0x6> creatorMAC;
u16 padding;
u16_be miiDetails;
std::array<char16_t, 0xA> miiName;
u8 height;
u8 width;
u8 faceStyle;
u8 faceDetails;
u8 hairStyle;
u8 hairDetails;
u32_be eyeDetails;
u32_be eyebrowDetails;
u16_be noseDetails;
u16_be mouthDetails;
u16_be moustacheDetails;
u16_be beardDetails;
u16_be glassesDetails;
u16_be moleDetails;
std::array<char16_t, 0xA> authorName;
};
#pragma pack(pop)
static_assert(sizeof(MiiData) == 0x5C, "MiiData structure has incorrect size");
struct MiiResult {
u32_be returnCode;
u32_be isGuestMiiSelected;
u32_be selectedGuestMiiIndex;
MiiData selectedMiiData;
u16_be unknown1;
u16_be miiChecksum;
std::array<u16_le, 0xC> guestMiiName;
};
static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size");
class MiiSelectorApplet final : public AppletBase {
public:
virtual const char* name() override { return "Mii Selector"; }
virtual Result::HorizonResult start() override;
virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector<u8>& parameters, u32 appID) override;
virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override;
virtual void reset() override;
MiiResult output;
MiiConfig config;
MiiResult getDefaultMii();
MiiSelectorApplet(Memory& memory, std::optional<Parameter>& nextParam) : AppletBase(memory, nextParam) {}
};
} // namespace Applets

View file

@ -1,13 +1,162 @@
#include <array>
#include "applets/applet.hpp"
#include "swap.hpp"
namespace Applets {
// Software keyboard definitions adapted from libctru/Citra
// Keyboard input filtering flags. Allows the caller to specify what input is explicitly not allowed
namespace SoftwareKeyboardFilter {
enum Filter : u32 {
Digits = 1, // Disallow the use of more than a certain number of digits (0 or more)
At = 1 << 1, // Disallow the use of the @ sign.
Percent = 1 << 2, // Disallow the use of the % sign.
Backslash = 1 << 3, // Disallow the use of the \ sign.
Profanity = 1 << 4, // Disallow profanity using Nintendo's profanity filter.
Callback = 1 << 5, // Use a callback in order to check the input.
};
} // namespace SoftwareKeyboardFilter
// Keyboard features.
namespace SoftwareKeyboardFeature {
enum Feature {
Parental = 1, // Parental PIN mode.
DarkenTopScreen = 1 << 1, // Darken the top screen when the keyboard is shown.
PredictiveInput = 1 << 2, // Enable predictive input (necessary for Kanji input in JPN systems).
Multiline = 1 << 3, // Enable multiline input.
FixedWidth = 1 << 4, // Enable fixed-width mode.
AllowHome = 1 << 5, // Allow the usage of the HOME button.
AllowReset = 1 << 6, // Allow the usage of a software-reset combination.
AllowPower = 1 << 7, // Allow the usage of the POWER button.
DefaultQWERTY = 1 << 9, // Default to the QWERTY page when the keyboard is shown.
};
} // namespace SoftwareKeyboardFeature
class SoftwareKeyboardApplet final : public AppletBase {
public:
static constexpr int MAX_BUTTON = 3; // Maximum number of buttons that can be in the keyboard.
static constexpr int MAX_BUTTON_TEXT_LEN = 16; // Maximum button text length, in UTF-16 code units.
static constexpr int MAX_HINT_TEXT_LEN = 64; // Maximum hint text length, in UTF-16 code units.
static constexpr int MAX_CALLBACK_MSG_LEN = 256; // Maximum filter callback error message length, in UTF-16 code units.
// Keyboard types
enum class SoftwareKeyboardType : u32 {
Normal, // Normal keyboard with several pages (QWERTY/accents/symbol/mobile)
QWERTY, // QWERTY keyboard only.
NumPad, // Number pad.
Western, // On JPN systems, a text keyboard without Japanese input capabilities, otherwise same as SWKBD_TYPE_NORMAL.
};
// Keyboard dialog buttons.
enum class SoftwareKeyboardButtonConfig : u32 {
SingleButton, // Ok button
DualButton, // Cancel | Ok buttons
TripleButton, // Cancel | I Forgot | Ok buttons
NoButton, // No button (returned by swkbdInputText in special cases)
};
// Accepted input types.
enum class SoftwareKeyboardValidInput : u32 {
Anything, // All inputs are accepted.
NotEmpty, // Empty inputs are not accepted.
NotEmptyNotBlank, // Empty or blank inputs (consisting solely of whitespace) are not accepted.
NotBlank, // Blank inputs (consisting solely of whitespace) are not accepted, but empty inputs are.
FixedLen, // The input must have a fixed length (specified by maxTextLength in swkbdInit)
};
// Keyboard password modes.
enum class SoftwareKeyboardPasswordMode : u32 {
None, // Characters are not concealed.
Hide, // Characters are concealed immediately.
HideDelay, // Characters are concealed a second after they've been typed.
};
// Keyboard filter callback return values.
enum class SoftwareKeyboardCallbackResult : u32 {
OK, // Specifies that the input is valid.
Close, // Displays an error message, then closes the keyboard.
Continue, // Displays an error message and continues displaying the keyboard.
};
// Keyboard return values.
enum class SoftwareKeyboardResult : s32 {
None = -1, // Dummy/unused.
InvalidInput = -2, // Invalid parameters to swkbd.
OutOfMem = -3, // Out of memory.
D0Click = 0, // The button was clicked in 1-button dialogs.
D1Click0, // The left button was clicked in 2-button dialogs.
D1Click1, // The right button was clicked in 2-button dialogs.
D2Click0, // The left button was clicked in 3-button dialogs.
D2Click1, // The middle button was clicked in 3-button dialogs.
D2Click2, // The right button was clicked in 3-button dialogs.
HomePressed = 10, // The HOME button was pressed.
ResetPressed, // The soft-reset key combination was pressed.
PowerPressed, // The POWER button was pressed.
ParentalOK = 20, // The parental PIN was verified successfully.
ParentalFail, // The parental PIN was incorrect.
BannedInput = 30, // The filter callback returned SoftwareKeyboardCallback::CLOSE.
};
struct SoftwareKeyboardConfig {
enum_le<SoftwareKeyboardType> type;
enum_le<SoftwareKeyboardButtonConfig> numButtonsM1;
enum_le<SoftwareKeyboardValidInput> validInput;
enum_le<SoftwareKeyboardPasswordMode> passwordMode;
s32_le isParentalScreen;
s32_le darkenTopScreen;
u32_le filterFlags;
u32_le saveStateFlags;
u16_le maxTextLength;
u16_le dictWordCount;
u16_le maxDigits;
std::array<std::array<u16_le, MAX_BUTTON_TEXT_LEN + 1>, MAX_BUTTON> buttonText;
std::array<u16_le, 2> numpadKeys;
std::array<u16_le, MAX_HINT_TEXT_LEN + 1> hintText; // Text to display when asking the user for input
bool predictiveInput;
bool multiline;
bool fixedWidth;
bool allowHome;
bool allowReset;
bool allowPower;
bool unknown;
bool defaultQwerty;
std::array<bool, 4> buttonSubmitsText;
u16_le language;
u32_le initialTextOffset; // Offset of the default text in the output SharedMemory
u32_le dictOffset;
u32_le initialStatusOffset;
u32_le initialLearningOffset;
u32_le sharedMemorySize; // Size of the SharedMemory
u32_le version;
enum_le<SoftwareKeyboardResult> returnCode;
u32_le statusOffset;
u32_le learningOffset;
u32_le textOffset; // Offset in the SharedMemory where the output text starts
u16_le textLength; // Length in characters of the output text
enum_le<SoftwareKeyboardCallbackResult> callbackResult;
std::array<u16_le, MAX_CALLBACK_MSG_LEN + 1> callbackMessage;
bool skipAtCheck;
std::array<u8, 0xAB> pad;
};
static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software keyboard config size is wrong");
virtual const char* name() override { return "Software Keyboard"; }
virtual Result::HorizonResult start() override;
virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector<u8>& parameters, u32 appID) override;
virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override;
virtual void reset() override;
SoftwareKeyboardApplet(Memory& memory, std::optional<Parameter>& nextParam) : AppletBase(memory, nextParam) {}
void closeKeyboard(u32 appID);
SoftwareKeyboardConfig config;
};
} // namespace Applets

View file

@ -24,6 +24,7 @@ class Cheats {
Cheats(Memory& mem, HIDService& hid);
u32 addCheat(const Cheat& cheat);
u32 addCheat(const u8* data, size_t size);
void removeCheat(u32 id);
void enableCheat(u32 id);
void disableCheat(u32 id);
@ -32,6 +33,7 @@ class Cheats {
void clear();
bool haveCheats() const { return cheatsLoaded; }
static constexpr u32 badCheatHandle = 0xFFFFFFFF;
private:
ActionReplay ar; // An ActionReplay cheat machine for executing CTRPF codes

View file

@ -9,15 +9,18 @@
#include "helpers.hpp"
#include "kernel.hpp"
#include "memory.hpp"
#include "scheduler.hpp"
class Emulator;
class CPU;
class MyEnvironment final : public Dynarmic::A32::UserCallbacks {
public:
u64 ticksLeft = 0;
u64 totalTicks = 0;
Memory& mem;
Kernel& kernel;
public:
u64 ticksLeft = 0;
u64 totalTicks = 0;
Memory& mem;
Kernel& kernel;
Scheduler& scheduler;
u64 getCyclesForInstruction(bool isThumb, u32 instruction);
@ -76,54 +79,56 @@ public:
std::terminate();
}
void CallSVC(u32 swi) override {
kernel.serviceSVC(swi);
}
void CallSVC(u32 swi) override {
kernel.serviceSVC(swi);
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
switch (exception) {
case Dynarmic::A32::Exception::UnpredictableInstruction:
Helpers::panic("Unpredictable instruction at pc = %08X", pc);
break;
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
switch (exception) {
case Dynarmic::A32::Exception::UnpredictableInstruction:
Helpers::panic("Unpredictable instruction at pc = %08X", pc);
break;
default: Helpers::panic("Fired exception oops");
}
}
default: Helpers::panic("Fired exception oops");
}
}
void AddTicks(u64 ticks) override {
totalTicks += ticks;
void AddTicks(u64 ticks) override {
scheduler.currentTimestamp += ticks;
if (ticks > ticksLeft) {
ticksLeft = 0;
return;
}
ticksLeft -= ticks;
}
if (ticks > ticksLeft) {
ticksLeft = 0;
return;
}
ticksLeft -= ticks;
}
u64 GetTicksRemaining() override {
return ticksLeft;
}
u64 GetTicksRemaining() override {
return ticksLeft;
}
u64 GetTicksForCode(bool isThumb, u32 vaddr, u32 instruction) override {
return getCyclesForInstruction(isThumb, instruction);
}
u64 GetTicksForCode(bool isThumb, u32 vaddr, u32 instruction) override {
return getCyclesForInstruction(isThumb, instruction);
}
MyEnvironment(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
MyEnvironment(Memory& mem, Kernel& kernel, Scheduler& scheduler) : mem(mem), kernel(kernel), scheduler(scheduler) {}
};
class CPU {
std::unique_ptr<Dynarmic::A32::Jit> jit;
std::shared_ptr<CP15> cp15;
std::unique_ptr<Dynarmic::A32::Jit> jit;
std::shared_ptr<CP15> cp15;
// Make exclusive monitor with only 1 CPU core
Dynarmic::ExclusiveMonitor exclusiveMonitor{1};
MyEnvironment env;
Memory& mem;
// Make exclusive monitor with only 1 CPU core
Dynarmic::ExclusiveMonitor exclusiveMonitor{1};
MyEnvironment env;
Memory& mem;
Scheduler& scheduler;
Emulator& emu;
public:
static constexpr u64 ticksPerSec = 268111856;
public:
static constexpr u64 ticksPerSec = Scheduler::arm11Clock;
CPU(Memory& mem, Kernel& kernel);
CPU(Memory& mem, Kernel& kernel, Emulator& emu);
void reset();
void setReg(int index, u32 value) {
@ -162,29 +167,20 @@ public:
}
u64 getTicks() {
return env.totalTicks;
return scheduler.currentTimestamp;
}
// Get reference to tick count. Memory needs access to this
u64& getTicksRef() {
return env.totalTicks;
return scheduler.currentTimestamp;
}
void clearCache() { jit->ClearCache(); }
void runFrame() {
env.ticksLeft = ticksPerSec / 60;
execute:
const auto exitReason = jit->Run();
if (static_cast<u32>(exitReason) != 0) [[unlikely]] {
// Cache invalidation needs to exit the JIT so it returns a CacheInvalidation HaltReason. In our case, we just go back to executing
// The goto might be terrible but it does guarantee that this does not recursively call run and crash, instead getting optimized to a jump
if (Dynarmic::Has(exitReason, Dynarmic::HaltReason::CacheInvalidation)) {
goto execute;
} else {
Helpers::panic("Exit reason: %d\nPC: %08X", static_cast<u32>(exitReason), getReg(15));
}
}
Scheduler& getScheduler() {
return scheduler;
}
void addTicks(u64 ticks) { env.AddTicks(ticks); }
void clearCache() { jit->ClearCache(); }
void runFrame();
};

View file

@ -15,6 +15,7 @@
#include "io_file.hpp"
#include "lua_manager.hpp"
#include "memory.hpp"
#include "scheduler.hpp"
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
#include "http_server.hpp"
@ -42,6 +43,7 @@ class Emulator {
Kernel kernel;
Crypto::AESEngine aesEngine;
Cheats cheats;
Scheduler scheduler;
// Variables to keep track of whether the user is controlling the 3DS analog stick with their keyboard
// This is done so when a gamepad is connected, we won't automatically override the 3DS analog stick settings with the gamepad's state
@ -85,6 +87,8 @@ class Emulator {
// change ROMs. If Reload is selected, the emulator will reload its selected ROM. This is useful for eg a "reset" button that keeps the current
// ROM and just resets the emu
enum class ReloadOption { NoReload, Reload };
// Used in CPU::runFrame
bool frameDone = false;
Emulator();
~Emulator();
@ -94,6 +98,8 @@ class Emulator {
void reset(ReloadOption reload);
void run(void* frontend = nullptr);
void runFrame();
// Poll the scheduler for events
void pollScheduler();
void resume(); // Resume the emulator
void pause(); // Pause the emulator
@ -121,6 +127,7 @@ class Emulator {
Cheats& getCheats() { return cheats; }
ServiceManager& getServiceManager() { return kernel.getServiceManager(); }
LuaManager& getLua() { return lua; }
Scheduler& getScheduler() { return scheduler; }
RendererType getRendererType() const { return config.rendererType; }
Renderer* getRenderer() { return gpu.getRenderer(); }
@ -128,6 +135,8 @@ class Emulator {
std::filesystem::path getConfigPath();
std::filesystem::path getAndroidAppPath();
// Get the root path for the emulator's app data
std::filesystem::path getAppDataRoot();
std::span<u8> getSMDH();
};

View file

@ -5,8 +5,10 @@
using Result::HorizonResult;
class SDMCArchive : public ArchiveBase {
public:
SDMCArchive(Memory& mem) : ArchiveBase(mem) {}
bool isWriteOnly = false; // There's 2 variants of the SDMC archive: Regular one (Read/Write) and write-only
public:
SDMCArchive(Memory& mem, bool writeOnly = false) : ArchiveBase(mem), isWriteOnly(writeOnly) {}
u64 getFreeBytes() override { return 1_GB; }
std::string name() override { return "SDMC"; }

View file

@ -53,6 +53,7 @@ namespace KernelHandles {
GSPSharedMemHandle = MaxServiceHandle + 1, // Handle for the GSP shared memory
FontSharedMemHandle,
CSNDSharedMemHandle,
APTCaptureSharedMemHandle, // Shared memory for display capture info,
HIDSharedMemHandle,
MinSharedMemHandle = GSPSharedMemHandle,

View file

@ -70,6 +70,7 @@ 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
Handle makeTimer(ResetType resetType);
void pollTimers();
// Signals an event, returns true on success or false if the event does not exist
bool signalEvent(Handle e);
@ -94,7 +95,7 @@ public:
void releaseMutex(Mutex* moo);
void cancelTimer(Timer* timer);
void signalTimer(Handle timerHandle, Timer* timer);
void updateTimer(Handle timerHandle, Timer* timer);
u64 getWakeupTick(s64 ns);
// Wake up the thread with the highest priority out of all threads in the waitlist
// Returns the index of the woken up thread
@ -137,6 +138,7 @@ public:
void duplicateHandle();
void exitThread();
void mapMemoryBlock();
void unmapMemoryBlock();
void queryMemory();
void getCurrentProcessorNumber();
void getProcessID();

View file

@ -83,56 +83,53 @@ struct Port {
};
struct Session {
Handle portHandle; // The port this session is subscribed to
Session(Handle portHandle) : portHandle(portHandle) {}
Handle portHandle; // The port this session is subscribed to
Session(Handle portHandle) : portHandle(portHandle) {}
};
enum class ThreadStatus {
Running, // Currently running
Ready, // Ready to run
WaitArbiter, // Waiting on an address arbiter
WaitSleep, // Waiting due to a SleepThread SVC
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
Dead // Run to completion, or forcefully terminated
Running, // Currently running
Ready, // Ready to run
WaitArbiter, // Waiting on an address arbiter
WaitSleep, // Waiting due to a SleepThread SVC
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
Dead // Run to completion, or forcefully terminated
};
struct Thread {
u32 initialSP; // Initial r13 value
u32 entrypoint; // Initial r15 value
u32 priority;
u32 arg;
ProcessorID processorID;
ThreadStatus status;
Handle handle; // OS handle for this thread
int index; // Index of the thread. 0 for the first thread, 1 for the second, and so on
u32 initialSP; // Initial r13 value
u32 entrypoint; // Initial r15 value
u32 priority;
u32 arg;
ProcessorID processorID;
ThreadStatus status;
Handle handle; // OS handle for this thread
int index; // Index of the thread. 0 for the first thread, 1 for the second, and so on
// The waiting address for threads that are waiting on an AddressArbiter
u32 waitingAddress;
// The waiting address for threads that are waiting on an AddressArbiter
u32 waitingAddress;
// The nanoseconds until a thread wakes up from being asleep or from timing out while waiting on an arbiter
u64 waitingNanoseconds;
// The tick this thread went to sleep on
u64 sleepTick;
// For WaitSynchronization(N): A vector of objects this thread is waiting for
std::vector<Handle> waitList;
// For WaitSynchronizationN: Shows whether the object should wait for all objects in the wait list or just one
bool waitAll;
// For WaitSynchronizationN: The "out" pointer
u32 outPointer;
// For WaitSynchronization(N): A vector of objects this thread is waiting for
std::vector<Handle> waitList;
// For WaitSynchronizationN: Shows whether the object should wait for all objects in the wait list or just one
bool waitAll;
// For WaitSynchronizationN: The "out" pointer
u32 outPointer;
u64 wakeupTick;
// Thread context used for switching between threads
std::array<u32, 16> gprs;
std::array<u32, 32> fprs; // Stored as u32 because dynarmic does it
u32 cpsr;
u32 fpscr;
u32 tlsBase; // Base pointer for thread-local storage
// Thread context used for switching between threads
std::array<u32, 16> gprs;
std::array<u32, 32> fprs; // Stored as u32 because dynarmic does it
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;
// A list of threads waiting for this thread to terminate. Yes, threads are sync objects too.
u64 threadsWaitingForTermination;
};
static const char* kernelObjectTypeToString(KernelObjectType t) {
@ -177,13 +174,12 @@ struct Timer {
u64 waitlist; // Refer to the getWaitlist function below for documentation
ResetType resetType = ResetType::OneShot;
u64 startTick; // CPU tick the timer started
u64 currentDelay; // Number of ns until the timer fires next time
u64 fireTick; // CPU tick the timer will be fired
u64 interval; // Number of ns until the timer fires for the second and future times
bool fired; // Has this timer been signalled?
bool running; // Is this timer running or stopped?
Timer(ResetType type) : resetType(type), startTick(0), currentDelay(0), interval(0), waitlist(0), fired(false), running(false) {}
Timer(ResetType type) : resetType(type), fireTick(0), interval(0), waitlist(0), fired(false), running(false) {}
};
struct MemoryBlock {

View file

@ -2,6 +2,10 @@
#include <cstdarg>
#include <fstream>
#ifdef __ANDROID__
#include <android/log.h>
#endif
namespace Log {
// Our logger class
template <bool enabled>
@ -12,7 +16,11 @@ namespace Log {
std::va_list args;
va_start(args, fmt);
#ifdef __ANDROID__
__android_log_vprint(ANDROID_LOG_DEFAULT, "Panda3DS", fmt, args);
#else
std::vprintf(fmt, args);
#endif
va_end(args);
}
};
@ -81,4 +89,4 @@ namespace Log {
#else
#define MAKE_LOG_FUNCTION(functionName, logger) MAKE_LOG_FUNCTION_USER(functionName, logger)
#endif
}
}

View file

@ -112,11 +112,12 @@ class Memory {
// This tracks our OS' memory allocations
std::vector<KernelMemoryTypes::MemoryInfo> memoryInfo;
std::array<SharedMemoryBlock, 4> sharedMemBlocks = {
std::array<SharedMemoryBlock, 5> sharedMemBlocks = {
SharedMemoryBlock(0, 0, KernelHandles::FontSharedMemHandle), // Shared memory for the system font (size is 0 because we read the size from the cmrc filesystem
SharedMemoryBlock(0, 0x1000, KernelHandles::GSPSharedMemHandle), // GSP shared memory
SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle), // HID shared memory
SharedMemoryBlock(0, 0x3000, KernelHandles::CSNDSharedMemHandle), // CSND shared memory
SharedMemoryBlock(0, 0xE7000, KernelHandles::APTCaptureSharedMemHandle), // APT Capture Buffer memory
};
public:

View file

@ -0,0 +1,26 @@
#pragma once
#include <QAction>
#include <QWidget>
#include <filesystem>
#include <memory>
#include "emulator.hpp"
class QListWidget;
class CheatsWindow final : public QWidget {
Q_OBJECT
public:
CheatsWindow(Emulator* emu, const std::filesystem::path& path, QWidget* parent = nullptr);
~CheatsWindow() = default;
private:
void addEntry();
void removeClicked();
QListWidget* cheatList;
std::filesystem::path cheatPath;
Emulator* emu;
};

View file

@ -5,6 +5,7 @@
#include <QtWidgets>
#include <atomic>
#include <filesystem>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
@ -12,16 +13,38 @@
#include "emulator.hpp"
#include "panda_qt/about_window.hpp"
#include "panda_qt/config_window.hpp"
#include "panda_qt/cheats_window.hpp"
#include "panda_qt/screen.hpp"
#include "panda_qt/text_editor.hpp"
#include "services/hid.hpp"
struct CheatMessage {
u32 handle;
std::vector<uint8_t> cheat;
std::function<void(u32)> callback;
};
class MainWindow : public QMainWindow {
Q_OBJECT
private:
// Types of messages we might send from the GUI thread to the emulator thread
enum class MessageType { LoadROM, Reset, Pause, Resume, TogglePause, DumpRomFS, PressKey, ReleaseKey, LoadLuaScript };
enum class MessageType {
LoadROM,
Reset,
Pause,
Resume,
TogglePause,
DumpRomFS,
PressKey,
ReleaseKey,
SetCirclePadX,
SetCirclePadY,
LoadLuaScript,
EditCheat,
PressTouchscreen,
ReleaseTouchscreen,
};
// Tagged union representing our message queue messages
struct EmulatorMessage {
@ -36,9 +59,22 @@ class MainWindow : public QMainWindow {
u32 key;
} key;
struct {
s16 value;
} circlepad;
struct {
std::string* str;
} string;
struct {
CheatMessage* c;
} cheat;
struct {
u16 x;
u16 y;
} touchscreen;
};
};
@ -54,6 +90,7 @@ class MainWindow : public QMainWindow {
ScreenWidget screen;
AboutWindow* aboutWindow;
ConfigWindow* configWindow;
CheatsWindow* cheatsEditor;
TextEditorWindow* luaEditor;
QMenuBar* menuBar = nullptr;
@ -63,6 +100,7 @@ class MainWindow : public QMainWindow {
void selectROM();
void dumpRomFS();
void openLuaEditor();
void openCheatsEditor();
void showAboutMenu();
void sendMessage(const EmulatorMessage& message);
void dispatchMessage(const EmulatorMessage& message);
@ -77,5 +115,9 @@ class MainWindow : public QMainWindow {
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void loadLuaScript(const std::string& code);
void editCheat(u32 handle, const std::vector<uint8_t>& cheat, const std::function<void(u32)>& callback);
};

89
include/scheduler.hpp Normal file
View file

@ -0,0 +1,89 @@
#pragma once
#include <boost/container/flat_map.hpp>
#include <boost/container/static_vector.hpp>
#include <limits>
#include "helpers.hpp"
#include "logger.hpp"
struct Scheduler {
enum class EventType {
VBlank = 0, // End of frame event
UpdateTimers = 1, // Update kernel timer objects
Panic = 2, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX)
TotalNumberOfEvents // How many event types do we have in total?
};
static constexpr usize totalNumberOfEvents = static_cast<usize>(EventType::TotalNumberOfEvents);
static constexpr u64 arm11Clock = 268111856;
template <typename Key, typename Val, usize size>
using EventMap = boost::container::flat_multimap<Key, Val, std::less<Key>, boost::container::static_vector<std::pair<Key, Val>, size>>;
EventMap<u64, EventType, totalNumberOfEvents> events;
u64 currentTimestamp = 0;
u64 nextTimestamp = 0;
// Set nextTimestamp to the timestamp of the next event
void updateNextTimestamp() { nextTimestamp = events.cbegin()->first; }
void addEvent(EventType type, u64 timestamp) {
events.emplace(timestamp, type);
updateNextTimestamp();
}
void removeEvent(EventType type) {
for (auto it = events.begin(); it != events.end(); it++) {
// Find first event of type "type" and remove it.
// Our scheduler shouldn't have duplicate events, so it's safe to exit when an event is found
if (it->second == type) {
events.erase(it);
updateNextTimestamp();
break;
}
}
};
void reset() {
currentTimestamp = 0;
// Clear any pending events
events.clear();
addEvent(Scheduler::EventType::VBlank, arm11Clock / 60);
// Add a dummy event to always keep the scheduler non-empty
addEvent(EventType::Panic, std::numeric_limits<u64>::max());
}
private:
static constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / arm11Clock;
public:
// Function for converting time units to cycles for various kernel functions
// Thank you Citra
static constexpr s64 nsToCycles(float ns) { return s64(arm11Clock * (0.000000001f) * ns); }
static constexpr s64 nsToCycles(int ns) { return arm11Clock * s64(ns) / 1000000000; }
static constexpr s64 nsToCycles(s64 ns) {
if (ns / 1000000000 > static_cast<s64>(MAX_VALUE_TO_MULTIPLY)) {
return std::numeric_limits<s64>::max();
}
if (ns > static_cast<s64>(MAX_VALUE_TO_MULTIPLY)) {
return arm11Clock * (ns / 1000000000);
}
return (arm11Clock * ns) / 1000000000;
}
static constexpr s64 nsToCycles(u64 ns) {
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
return std::numeric_limits<s64>::max();
}
if (ns > MAX_VALUE_TO_MULTIPLY) {
return arm11Clock * (s64(ns) / 1000000000);
}
return (arm11Clock * s64(ns)) / 1000000000;
}
};

View file

@ -14,6 +14,7 @@ class BOSSService {
void cancelTask(u32 messagePointer);
void initializeSession(u32 messagePointer);
void getErrorCode(u32 messagePointer);
void getNewArrivalFlag(u32 messagePointer);
void getNsDataIdList(u32 messagePointer, u32 commandWord);
void getOptoutFlag(u32 messagePointer);
void getStorageEntryInfo(u32 messagePointer); // Unknown what this is, name taken from Citra

View file

@ -12,22 +12,45 @@
class Kernel;
class CAMService {
using Event = std::optional<Handle>;
struct Port {
Event bufferErrorInterruptEvent = std::nullopt;
Event receiveEvent = std::nullopt;
u16 transferBytes;
void reset() {
bufferErrorInterruptEvent = std::nullopt;
receiveEvent = std::nullopt;
transferBytes = 256;
}
};
Handle handle = KernelHandles::CAM;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, camLogger)
using Event = std::optional<Handle>;
static constexpr size_t portCount = 4; // PORT_NONE, PORT_CAM1, PORT_CAM2, PORT_BOTH
std::array<Event, portCount> bufferErrorInterruptEvents;
static constexpr size_t portCount = 2;
std::array<Port, portCount> ports;
// Service commands
void driverInitialize(u32 messagePointer);
void driverFinalize(u32 messagePointer);
void getMaxBytes(u32 messagePointer);
void getMaxLines(u32 messagePointer);
void getBufferErrorInterruptEvent(u32 messagePointer);
void getSuitableY2RCoefficients(u32 messagePointer);
void getTransferBytes(u32 messagePointer);
void setContrast(u32 messagePointer);
void setFrameRate(u32 messagePointer);
void setReceiving(u32 messagePointer);
void setSize(u32 messagePointer);
void setTransferBytes(u32 messagePointer);
void setTransferLines(u32 messagePointer);
void setTrimming(u32 messagePointer);
void setTrimmingParamsCenter(u32 messagePointer);
void startCapture(u32 messagePointer);
public:
CAMService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}

View file

@ -26,6 +26,7 @@ class FSService {
SelfNCCHArchive selfNcch;
SaveDataArchive saveData;
SDMCArchive sdmc;
SDMCArchive sdmcWriteOnly;
NCCHArchive ncch;
// UserSaveData archives
@ -61,6 +62,7 @@ class FSService {
void getFreeBytes(u32 messagePointer);
void getFormatInfo(u32 messagePointer);
void getPriority(u32 messagePointer);
void getSdmcArchiveResource(u32 messagePointer);
void getThisSaveDataSecureValue(u32 messagePointer);
void theGameboyVCFunction(u32 messagePointer);
void initialize(u32 messagePointer);
@ -81,9 +83,9 @@ class FSService {
public:
FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config)
: mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem), selfNcch(mem),
ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), userSaveData2(mem, ArchiveID::UserSaveData2), kernel(kernel), config(config),
systemSaveData(mem) {}
: mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem),
sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1),
userSaveData2(mem, ArchiveID::UserSaveData2), kernel(kernel), config(config), systemSaveData(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);

View file

@ -60,12 +60,22 @@ class GPUService {
};
static_assert(sizeof(FramebufferUpdate) == 64, "GSP::GPU::FramebufferUpdate has the wrong size");
// Used for saving and restoring GPU state via ImportDisplayCaptureInfo
struct CaptureInfo {
u32 leftFramebuffer; // Left framebuffer VA
u32 rightFramebuffer; // Right framebuffer VA (Top screen only)
u32 format;
u32 stride;
};
static_assert(sizeof(CaptureInfo) == 16, "GSP::GPU::CaptureInfo has the wrong size");
// Service commands
void acquireRight(u32 messagePointer);
void flushDataCache(u32 messagePointer);
void importDisplayCaptureInfo(u32 messagePointer);
void registerInterruptRelayQueue(u32 messagePointer);
void releaseRight(u32 messagePointer);
void restoreVramSysArea(u32 messagePointer);
void saveVramSysArea(u32 messagePointer);
void setAxiConfigQoSMode(u32 messagePointer);
void setBufferSwap(u32 messagePointer);
@ -86,6 +96,15 @@ class GPUService {
void setBufferSwapImpl(u32 screen_id, const FramebufferInfo& info);
// Get the framebuffer info in shared memory for a given screen
FramebufferUpdate* getFramebufferInfo(int screen) {
// TODO: Offset depends on GSP thread being triggered
return reinterpret_cast<FramebufferUpdate*>(&sharedMem[0x200 + screen * sizeof(FramebufferUpdate)]);
}
FramebufferUpdate* getTopFramebufferInfo() { return getFramebufferInfo(0); }
FramebufferUpdate* getBottomFramebufferInfo() { return getFramebufferInfo(1); }
public:
GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu),
kernel(kernel), currentPID(currentPID) {}

View file

@ -17,6 +17,7 @@ class PTMService {
void getAdapterState(u32 messagePointer);
void getBatteryChargeState(u32 messagePointer);
void getBatteryLevel(u32 messagePointer);
void getPedometerState(u32 messagePointer);
void getStepHistory(u32 messagePointer);
void getStepHistoryAll(u32 messagePointer);
void getTotalStepCount(u32 messagePointer);

View file

@ -98,6 +98,7 @@ class Y2RService {
void setSendingY(u32 messagePointer);
void setSendingU(u32 messagePointer);
void setSendingV(u32 messagePointer);
void setSendingYUV(u32 messagePointer);
void setSpacialDithering(u32 messagePointer);
void setStandardCoeff(u32 messagePointer);
void setTemporalDithering(u32 messagePointer);