diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml
index f47b3a4a..14209baf 100644
--- a/.github/workflows/build-macos.yml
+++ b/.github/workflows/build-macos.yml
@@ -13,6 +13,7 @@ env:
MELONDS_GIT_BRANCH: ${{ github.ref }}
MELONDS_GIT_HASH: ${{ github.sha }}
MELONDS_BUILD_PROVIDER: GitHub Actions
+ MELONDS_VERSION_SUFFIX: " RC"
jobs:
build-macos:
diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml
index 9af151bd..2d985f34 100644
--- a/.github/workflows/build-ubuntu.yml
+++ b/.github/workflows/build-ubuntu.yml
@@ -12,6 +12,7 @@ env:
MELONDS_GIT_BRANCH: ${{ github.ref }}
MELONDS_GIT_HASH: ${{ github.sha }}
MELONDS_BUILD_PROVIDER: GitHub Actions
+ MELONDS_VERSION_SUFFIX: " RC"
jobs:
build-x86_64:
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
index c3350b4d..077fb152 100644
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -13,6 +13,7 @@ env:
MELONDS_GIT_BRANCH: ${{ github.ref }}
MELONDS_GIT_HASH: ${{ github.sha }}
MELONDS_BUILD_PROVIDER: GitHub Actions
+ MELONDS_VERSION_SUFFIX: " RC"
jobs:
build:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 55bf825f..c54b6450 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,7 +16,7 @@ if (USE_VCPKG)
endif()
project(melonDS
- VERSION 0.9.5
+ VERSION 1.0
DESCRIPTION "DS emulator, sorta"
HOMEPAGE_URL "https://melonds.kuribo64.net"
LANGUAGES C CXX)
diff --git a/flake.nix b/flake.nix
index 85f8b88a..97072b15 100644
--- a/flake.nix
+++ b/flake.nix
@@ -21,7 +21,7 @@
melonDS = pkgs.stdenv.mkDerivation {
pname = "melonDS";
- version = "0.9.5-${shortRevision}";
+ version = "1.0-${shortRevision}";
src = ./.;
nativeBuildInputs = with pkgs; [
diff --git a/res/icon/melon_192x192.png b/res/icon/melon_192x192.png
new file mode 100644
index 00000000..b8d4da5d
Binary files /dev/null and b/res/icon/melon_192x192.png differ
diff --git a/res/melon.qrc b/res/melon.qrc
index 3c5824d6..1defcc75 100644
--- a/res/melon.qrc
+++ b/res/melon.qrc
@@ -2,6 +2,6 @@
icon/melon_256x256.png
- melon.svg
+ melon384.png
diff --git a/res/melon.rc.in b/res/melon.rc.in
index eb437b32..299933cb 100644
--- a/res/melon.rc.in
+++ b/res/melon.rc.in
@@ -6,8 +6,8 @@
//include version information in .exe, modify these values to match your needs
1 VERSIONINFO
-FILEVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
-PRODUCTVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
+FILEVERSION ${MELON_RC_VERSION}
+PRODUCTVERSION ${MELON_RC_VERSION}
FILETYPE VFT_APP
{
BLOCK "StringFileInfo"
diff --git a/res/melon384.png b/res/melon384.png
new file mode 100644
index 00000000..d597d2ed
Binary files /dev/null and b/res/melon384.png differ
diff --git a/res/melon512.png b/res/melon512.png
new file mode 100644
index 00000000..e5728f85
Binary files /dev/null and b/res/melon512.png differ
diff --git a/src/ARM.cpp b/src/ARM.cpp
index b7b703da..0d93dc45 100644
--- a/src/ARM.cpp
+++ b/src/ARM.cpp
@@ -109,21 +109,13 @@ const u32 ARM::ConditionTable[16] =
ARM::ARM(u32 num, bool jit, std::optional gdb, melonDS::NDS& nds) :
#ifdef GDBSTUB_ENABLED
- GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0),
- BreakOnStartup(gdb ? (num ? gdb->ARM7BreakOnStartup : gdb->ARM9BreakOnStartup) : false),
+ GdbStub(this),
+ BreakOnStartup(false),
#endif
Num(num), // well uh
NDS(nds)
{
-#ifdef GDBSTUB_ENABLED
- if (gdb
-#ifdef JIT_ENABLED
- && !jit // TODO: Should we support toggling the GdbStub without destroying the ARM?
-#endif
- )
- GdbStub.Init();
- IsSingleStep = false;
-#endif
+ SetGdbArgs(jit ? gdb : std::nullopt);
}
ARM::~ARM()
@@ -148,6 +140,20 @@ ARMv5::~ARMv5()
// DTCM is owned by Memory, not going to delete it
}
+void ARM::SetGdbArgs(std::optional gdb)
+{
+#ifdef GDBSTUB_ENABLED
+ GdbStub.Close();
+ if (gdb)
+ {
+ int port = Num ? gdb->PortARM7 : gdb->PortARM9;
+ GdbStub.Init(port);
+ BreakOnStartup = Num ? gdb->ARM7BreakOnStartup : gdb->ARM9BreakOnStartup;
+ }
+ IsSingleStep = false;
+#endif
+}
+
void ARM::Reset()
{
Cycles = 0;
diff --git a/src/ARM.h b/src/ARM.h
index b652e74d..f18d7650 100644
--- a/src/ARM.h
+++ b/src/ARM.h
@@ -68,6 +68,8 @@ public:
ARM(u32 num, bool jit, std::optional gdb, NDS& nds);
virtual ~ARM(); // destroy shit
+ void SetGdbArgs(std::optional gdb);
+
virtual void Reset();
virtual void DoSavestate(Savestate* file);
@@ -323,6 +325,7 @@ public:
u32 CP15Control;
u32 RNGSeed;
+ u32 TraceProcessID;
u32 DTCMSetting, ITCMSetting;
diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp
index 1ebcce8e..9582f7c8 100644
--- a/src/ARMJIT.cpp
+++ b/src/ARMJIT.cpp
@@ -30,6 +30,7 @@
#include "ARMJIT_Internal.h"
#include "ARMJIT_Memory.h"
#include "ARMJIT_Compiler.h"
+#include "ARMJIT_Global.h"
#include "ARMInterpreter_ALU.h"
#include "ARMInterpreter_LoadStore.h"
@@ -467,6 +468,16 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] =
};
#undef F
+ARMJIT::ARMJIT(melonDS::NDS& nds, std::optional jit) noexcept :
+ NDS(nds),
+ Memory(nds),
+ JITCompiler(nds),
+ MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
+ LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
+ BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
+ FastMemory((jit.has_value() ? jit->FastMemory : false) && ARMJIT_Memory::IsFastMemSupported())
+{}
+
void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
{
auto it = RestoreCandidates.find(block->InstrHash);
@@ -483,6 +494,7 @@ void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
void ARMJIT::SetJITArgs(JITArgs args) noexcept
{
+ args.FastMemory = args.FastMemory && ARMJIT_Memory::IsFastMemSupported();
args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
if (MaxBlockSize != args.MaxBlockSize
@@ -499,36 +511,22 @@ void ARMJIT::SetJITArgs(JITArgs args) noexcept
void ARMJIT::SetMaxBlockSize(int size) noexcept
{
- size = std::clamp(size, 1, 32);
-
- if (size != MaxBlockSize)
- ResetBlockCache();
-
- MaxBlockSize = size;
+ SetJITArgs(JITArgs{static_cast(size), LiteralOptimizations, LiteralOptimizations, FastMemory});
}
void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
{
- if (LiteralOptimizations != enabled)
- ResetBlockCache();
-
- LiteralOptimizations = enabled;
+ SetJITArgs(JITArgs{static_cast(MaxBlockSize), enabled, BranchOptimizations, FastMemory});
}
void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
{
- if (BranchOptimizations != enabled)
- ResetBlockCache();
-
- BranchOptimizations = enabled;
+ SetJITArgs(JITArgs{static_cast(MaxBlockSize), LiteralOptimizations, enabled, FastMemory});
}
void ARMJIT::SetFastMemory(bool enabled) noexcept
{
- if (FastMemory != enabled)
- ResetBlockCache();
-
- FastMemory = enabled;
+ SetJITArgs(JITArgs{static_cast(MaxBlockSize), LiteralOptimizations, BranchOptimizations, enabled});
}
void ARMJIT::CompileBlock(ARM* cpu) noexcept
@@ -918,7 +916,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
AddressRange* region = CodeMemRegions[addressRanges[j] >> 27];
- if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000) / 512]))
+ if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
AddressRange* range = ®ion[(addressRanges[j] & 0x7FFFFFF) / 512];
@@ -971,7 +969,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
range->Blocks.Remove(i);
if (range->Blocks.Length == 0
- && !PageContainsCode(®ion[(localAddr & 0x7FFF000) / 512]))
+ && !PageContainsCode(®ion[(localAddr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
{
Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
}
@@ -1005,7 +1003,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
if (otherRange->Blocks.Length == 0)
{
- if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000) / 512]))
+ if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
otherRange->Code = 0;
diff --git a/src/ARMJIT.h b/src/ARMJIT.h
index a228a4dd..309aa8e8 100644
--- a/src/ARMJIT.h
+++ b/src/ARMJIT.h
@@ -44,15 +44,7 @@ class JitBlock;
class ARMJIT
{
public:
- ARMJIT(melonDS::NDS& nds, std::optional jit) noexcept :
- NDS(nds),
- Memory(nds),
- JITCompiler(nds),
- MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
- LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
- BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
- FastMemory(jit.has_value() ? jit->FastMemory : false)
- {}
+ ARMJIT(melonDS::NDS& nds, std::optional jit) noexcept;
~ARMJIT() noexcept;
void InvalidateByAddr(u32) noexcept;
void CheckAndInvalidateWVRAM(int) noexcept;
@@ -80,6 +72,7 @@ private:
bool LiteralOptimizations = false;
bool BranchOptimizations = false;
bool FastMemory = false;
+
public:
melonDS::NDS& NDS;
TinyVector InvalidLiterals {};
diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp
index f05de448..7aa71126 100644
--- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp
+++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp
@@ -22,17 +22,7 @@
#include "../ARMInterpreter.h"
#include "../ARMJIT.h"
#include "../NDS.h"
-
-#if defined(__SWITCH__)
-#include
-
-extern char __start__;
-#elif defined(_WIN32)
-#include
-#else
-#include
-#include
-#endif
+#include "../ARMJIT_Global.h"
#include
@@ -66,11 +56,6 @@ const int RegisterCache::NativeRegsAvailable = 15;
const BitSet32 CallerSavedPushRegs({W8, W9, W10, W11, W12, W13, W14, W15});
-const int JitMemSize = 16 * 1024 * 1024;
-#ifndef __SWITCH__
-u8 JitMem[JitMemSize];
-#endif
-
void Compiler::MovePC()
{
ADD(MapReg(15), MapReg(15), Thumb ? 2 : 4);
@@ -260,29 +245,13 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart);
JitMemMainSize = JitMemSize;
#else
- #ifdef _WIN32
- SYSTEM_INFO sysInfo;
- GetSystemInfo(&sysInfo);
+ ARMJIT_Global::Init();
- u64 pageSize = (u64)sysInfo.dwPageSize;
- #else
- u64 pageSize = sysconf(_SC_PAGE_SIZE);
- #endif
- u8* pageAligned = (u8*)(((u64)JitMem & ~(pageSize - 1)) + pageSize);
- u64 alignedSize = (((u64)JitMem + sizeof(JitMem)) & ~(pageSize - 1)) - (u64)pageAligned;
+ CodeMemBase = ARMJIT_Global::AllocateCodeMem();
+ nds.JIT.JitEnableWrite();
- #if defined(_WIN32)
- DWORD dummy;
- VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
- #elif defined(__APPLE__)
- pageAligned = (u8*)mmap(NULL, 1024*1024*16, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
- nds.JIT.JitEnableWrite();
- #else
- mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
- #endif
-
- SetCodeBase(pageAligned, pageAligned);
- JitMemMainSize = alignedSize;
+ SetCodeBase(reinterpret_cast(CodeMemBase), reinterpret_cast(CodeMemBase));
+ JitMemMainSize = ARMJIT_Global::CodeMemorySliceSize;
#endif
SetCodePtr(0);
@@ -493,6 +462,9 @@ Compiler::~Compiler()
free(JitRWBase);
}
#endif
+
+ ARMJIT_Global::FreeCodeMem(CodeMemBase);
+ ARMJIT_Global::DeInit();
}
void Compiler::LoadCycles()
diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h
index a7b567f6..44886b13 100644
--- a/src/ARMJIT_A64/ARMJIT_Compiler.h
+++ b/src/ARMJIT_A64/ARMJIT_Compiler.h
@@ -275,6 +275,7 @@ public:
void* JitRWStart;
void* JitRXStart;
#endif
+ void* CodeMemBase;
void* ReadBanked, *WriteBanked;
diff --git a/src/ARMJIT_Global.cpp b/src/ARMJIT_Global.cpp
new file mode 100644
index 00000000..c9432cbf
--- /dev/null
+++ b/src/ARMJIT_Global.cpp
@@ -0,0 +1,126 @@
+#include "ARMJIT_Global.h"
+#include "ARMJIT_Memory.h"
+
+#ifdef _WIN32
+#include
+#else
+#include
+#include
+#endif
+
+#include
+#include
+
+#include
+
+namespace melonDS
+{
+
+namespace ARMJIT_Global
+{
+
+std::mutex globalMutex;
+
+#if defined(__APPLE__) && defined(__aarch64__)
+#define APPLE_AARCH64
+#endif
+
+#ifndef APPLE_AARCH64
+static constexpr size_t NumCodeMemSlices = 4;
+static constexpr size_t CodeMemoryAlignedSize = NumCodeMemSlices * CodeMemorySliceSize;
+
+// I haven't heard of pages larger than 16 KB
+u8 CodeMemory[CodeMemoryAlignedSize + 16*1024];
+
+u32 AvailableCodeMemSlices = (1 << NumCodeMemSlices) - 1;
+
+u8* GetAlignedCodeMemoryStart()
+{
+ return reinterpret_cast((reinterpret_cast(CodeMemory) + (16*1024-1)) & ~static_cast(16*1024-1));
+}
+#endif
+
+int RefCounter = 0;
+
+void* AllocateCodeMem()
+{
+ std::lock_guard guard(globalMutex);
+
+#ifndef APPLE_AARCH64
+ if (AvailableCodeMemSlices)
+ {
+ int slice = __builtin_ctz(AvailableCodeMemSlices);
+ AvailableCodeMemSlices &= ~(1 << slice);
+ //printf("allocating slice %d\n", slice);
+ return &GetAlignedCodeMemoryStart()[slice * CodeMemorySliceSize];
+ }
+#endif
+
+ // allocate
+#ifdef _WIN32
+ return VirtualAlloc(nullptr, CodeMemorySliceSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+#elif defined(APPLE_AARCH64)
+ return mmap(NULL, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
+#else
+ //printf("mmaping...\n");
+ return mmap(nullptr, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+#endif
+}
+
+void FreeCodeMem(void* codeMem)
+{
+ std::lock_guard guard(globalMutex);
+
+#ifndef APPLE_AARCH64
+ for (int i = 0; i < NumCodeMemSlices; i++)
+ {
+ if (codeMem == &GetAlignedCodeMemoryStart()[CodeMemorySliceSize * i])
+ {
+ //printf("freeing slice\n");
+ AvailableCodeMemSlices |= 1 << i;
+ return;
+ }
+ }
+#endif
+
+#ifdef _WIN32
+ VirtualFree(codeMem, CodeMemorySliceSize, MEM_RELEASE|MEM_DECOMMIT);
+#else
+ munmap(codeMem, CodeMemorySliceSize);
+#endif
+}
+
+void Init()
+{
+ std::lock_guard guard(globalMutex);
+
+ RefCounter++;
+ if (RefCounter == 1)
+ {
+ #ifdef _WIN32
+ DWORD dummy;
+ VirtualProtect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PAGE_EXECUTE_READWRITE, &dummy);
+ #elif defined(APPLE_AARCH64)
+ // Apple aarch64 always uses dynamic allocation
+ #else
+ mprotect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
+ #endif
+
+ ARMJIT_Memory::RegisterFaultHandler();
+ }
+}
+
+void DeInit()
+{
+ std::lock_guard guard(globalMutex);
+
+ RefCounter--;
+ if (RefCounter == 0)
+ {
+ ARMJIT_Memory::UnregisterFaultHandler();
+ }
+}
+
+}
+
+}
diff --git a/src/ARMJIT_Global.h b/src/ARMJIT_Global.h
new file mode 100644
index 00000000..299d71a6
--- /dev/null
+++ b/src/ARMJIT_Global.h
@@ -0,0 +1,44 @@
+/*
+ Copyright 2016-2024 melonDS team
+
+ This file is part of melonDS.
+
+ melonDS is free software: you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with melonDS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#ifndef ARMJIT_GLOBAL_H
+#define ARMJIT_GLOBAL_H
+
+#include "types.h"
+
+#include
+
+namespace melonDS
+{
+
+namespace ARMJIT_Global
+{
+
+static constexpr size_t CodeMemorySliceSize = 1024*1024*32;
+
+void Init();
+void DeInit();
+
+void* AllocateCodeMem();
+void FreeCodeMem(void* codeMem);
+
+}
+
+}
+
+#endif
\ No newline at end of file
diff --git a/src/ARMJIT_Internal.h b/src/ARMJIT_Internal.h
index 5b393903..12591d30 100644
--- a/src/ARMJIT_Internal.h
+++ b/src/ARMJIT_Internal.h
@@ -85,9 +85,9 @@ typedef void (*InterpreterFunc)(ARM* cpu);
extern InterpreterFunc InterpretARM[];
extern InterpreterFunc InterpretTHUMB[];
-inline bool PageContainsCode(const AddressRange* range)
+inline bool PageContainsCode(const AddressRange* range, u32 pageSize)
{
- for (int i = 0; i < 8; i++)
+ for (int i = 0; i < pageSize / 512; i++)
{
if (range[i].Blocks.Length > 0)
return true;
diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp
index 51e022d1..e1c1da43 100644
--- a/src/ARMJIT_Memory.cpp
+++ b/src/ARMJIT_Memory.cpp
@@ -39,6 +39,7 @@
#include "ARMJIT_Internal.h"
#include "ARMJIT_Compiler.h"
+#include "ARMJIT_Global.h"
#include "DSi.h"
#include "GPU.h"
@@ -100,6 +101,9 @@
namespace melonDS
{
+static constexpr u64 AddrSpaceSize = 0x100000000;
+static constexpr u64 VirtmemAreaSize = AddrSpaceSize * 2 + MemoryTotalSize;
+
using Platform::Log;
using Platform::LogLevel;
@@ -152,6 +156,15 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx)
#elif defined(_WIN32)
+static LPVOID ExceptionHandlerHandle = nullptr;
+static HMODULE KernelBaseDll = nullptr;
+
+using VirtualAlloc2Type = PVOID WINAPI (*)(HANDLE Process, PVOID BaseAddress, SIZE_T Size, ULONG AllocationType, ULONG PageProtection, MEM_EXTENDED_PARAMETER* ExtendedParameters, ULONG ParameterCount);
+using MapViewOfFile3Type = PVOID WINAPI (*)(HANDLE FileMapping, HANDLE Process, PVOID BaseAddress, ULONG64 Offset, SIZE_T ViewSize, ULONG AllocationType, ULONG PageProtection, MEM_EXTENDED_PARAMETER* ExtendedParameters, ULONG ParameterCount);
+
+static VirtualAlloc2Type virtualAlloc2Ptr;
+static MapViewOfFile3Type mapViewOfFile3Ptr;
+
LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
{
if (exceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
@@ -170,6 +183,7 @@ LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
return EXCEPTION_CONTINUE_EXECUTION;
}
+ Log(LogLevel::Debug, "it all returns to nothing\n");
return EXCEPTION_CONTINUE_SEARCH;
}
@@ -261,18 +275,61 @@ enum
memstate_MappedProtected,
};
-
+#define CHECK_ALIGNED(value) assert(((value) & (PageSize-1)) == 0)
bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept
{
+ CHECK_ALIGNED(addr);
+ CHECK_ALIGNED(offset);
+ CHECK_ALIGNED(size);
+
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
#ifdef __SWITCH__
Result r = (svcMapProcessMemory(dst, envGetOwnProcessHandle(),
(u64)(MemoryBaseCodeMem + offset), size));
return R_SUCCEEDED(r);
#elif defined(_WIN32)
- bool r = MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, offset, size, dst) == dst;
- return r;
+ uintptr_t uintptrDst = reinterpret_cast(dst);
+ for (auto it = VirtmemPlaceholders.begin(); it != VirtmemPlaceholders.end(); it++)
+ {
+ if (uintptrDst >= it->Start && uintptrDst+size <= it->Start+it->Size)
+ {
+ //Log(LogLevel::Debug, "found mapping %llx %llx %llx %llx\n", uintptrDst, size, it->Start, it->Size);
+ // we split this place holder so that we have a fitting place holder for the mapping
+ if (uintptrDst != it->Start || size != it->Size)
+ {
+ if (!VirtualFree(dst, size, MEM_RELEASE|MEM_PRESERVE_PLACEHOLDER))
+ {
+ Log(LogLevel::Debug, "VirtualFree failed with %x\n", GetLastError());
+ return false;
+ }
+ }
+
+ VirtmemPlaceholder splitPlaceholder = *it;
+ VirtmemPlaceholders.erase(it);
+ if (uintptrDst > splitPlaceholder.Start)
+ {
+ //Log(LogLevel::Debug, "splitting on the left %llx\n", uintptrDst - splitPlaceholder.Start);
+ VirtmemPlaceholders.push_back({splitPlaceholder.Start, uintptrDst - splitPlaceholder.Start});
+ }
+ if (uintptrDst+size < splitPlaceholder.Start+splitPlaceholder.Size)
+ {
+ //Log(LogLevel::Debug, "splitting on the right %llx\n", (splitPlaceholder.Start+splitPlaceholder.Size)-(uintptrDst+size));
+ VirtmemPlaceholders.push_back({uintptrDst+size, (splitPlaceholder.Start+splitPlaceholder.Size)-(uintptrDst+size)});
+ }
+
+ if (!mapViewOfFile3Ptr(MemoryFile, nullptr, dst, offset, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0))
+ {
+ Log(LogLevel::Debug, "MapViewOfFile3 failed with %x\n", GetLastError());
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ Log(LogLevel::Debug, "no mapping at all found??? %p %x %p\n", dst, size, MemoryBase);
+ return false;
#else
return mmap(dst, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, offset) != MAP_FAILED;
#endif
@@ -280,21 +337,68 @@ bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexce
bool ARMJIT_Memory::UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept
{
+ CHECK_ALIGNED(addr);
+ CHECK_ALIGNED(offset);
+ CHECK_ALIGNED(size);
+
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
#ifdef __SWITCH__
Result r = svcUnmapProcessMemory(dst, envGetOwnProcessHandle(),
(u64)(MemoryBaseCodeMem + offset), size);
return R_SUCCEEDED(r);
#elif defined(_WIN32)
- return UnmapViewOfFile(dst);
+ if (!UnmapViewOfFileEx(dst, MEM_PRESERVE_PLACEHOLDER))
+ {
+ Log(LogLevel::Debug, "UnmapViewOfFileEx failed %x\n", GetLastError());
+ return false;
+ }
+
+ uintptr_t uintptrDst = reinterpret_cast(dst);
+ uintptr_t coalesceStart = uintptrDst;
+ size_t coalesceSize = size;
+
+ for (auto it = VirtmemPlaceholders.begin(); it != VirtmemPlaceholders.end();)
+ {
+ if (it->Start+it->Size == uintptrDst)
+ {
+ //Log(LogLevel::Debug, "Coalescing to the left\n");
+ coalesceStart = it->Start;
+ coalesceSize += it->Size;
+ it = VirtmemPlaceholders.erase(it);
+ }
+ else if (it->Start == uintptrDst+size)
+ {
+ //Log(LogLevel::Debug, "Coalescing to the right\n");
+ coalesceSize += it->Size;
+ it = VirtmemPlaceholders.erase(it);
+ }
+ else
+ {
+ it++;
+ }
+ }
+
+ if (coalesceStart != uintptrDst || coalesceSize != size)
+ {
+ if (!VirtualFree(reinterpret_cast(coalesceStart), coalesceSize, MEM_RELEASE|MEM_COALESCE_PLACEHOLDERS))
+ return false;
+
+ }
+ VirtmemPlaceholders.push_back({coalesceStart, coalesceSize});
+ //Log(LogLevel::Debug, "Adding coalesced region %llx %llx", coalesceStart, coalesceSize);
+
+ return true;
#else
- return munmap(dst, size) == 0;
+ return mmap(dst, size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0) != MAP_FAILED;
#endif
}
#ifndef __SWITCH__
void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept
{
+ CHECK_ALIGNED(addr);
+ CHECK_ALIGNED(size);
+
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
#if defined(_WIN32)
DWORD winProtection, oldProtection;
@@ -305,6 +409,10 @@ void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int prot
else
winProtection = PAGE_READWRITE;
bool success = VirtualProtect(dst, size, winProtection, &oldProtection);
+ if (!success)
+ {
+ Log(LogLevel::Debug, "VirtualProtect failed with %x\n", GetLastError());
+ }
assert(success);
#else
int posixProt;
@@ -335,14 +443,14 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
else
{
u32 segmentOffset = offset;
- u8 status = statuses[(Addr + offset) >> 12];
- while (statuses[(Addr + offset) >> 12] == status
+ u8 status = statuses[(Addr + offset) >> PageShift];
+ while (statuses[(Addr + offset) >> PageShift] == status
&& offset < Size
&& (!skipDTCM || Addr + offset != dtcmStart))
{
- assert(statuses[(Addr + offset) >> 12] != memstate_Unmapped);
- statuses[(Addr + offset) >> 12] = memstate_Unmapped;
- offset += 0x1000;
+ assert(statuses[(Addr + offset) >> PageShift] != memstate_Unmapped);
+ statuses[(Addr + offset) >> PageShift] = memstate_Unmapped;
+ offset += PageSize;
}
#ifdef __SWITCH__
@@ -358,7 +466,6 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
}
#ifndef __SWITCH__
-#ifndef _WIN32
u32 dtcmEnd = dtcmStart + dtcmSize;
if (Num == 0
&& dtcmEnd >= Addr
@@ -378,7 +485,6 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
}
}
else
-#endif
{
bool succeded = nds.JIT.Memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size);
assert(succeded);
@@ -388,7 +494,7 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noexcept
{
- offset &= ~0xFFF;
+ offset &= ~(PageSize - 1);
//printf("set code protection %d %x %d\n", region, offset, protect);
for (int i = 0; i < Mappings[region].Length; i++)
@@ -406,9 +512,9 @@ void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noex
u8* states = (u8*)(mapping.Num == 0 ? MappingStatus9 : MappingStatus7);
- //printf("%x %d %x %x %x %d\n", effectiveAddr, mapping.Num, mapping.Addr, mapping.LocalOffset, mapping.Size, states[effectiveAddr >> 12]);
- assert(states[effectiveAddr >> 12] == (protect ? memstate_MappedRW : memstate_MappedProtected));
- states[effectiveAddr >> 12] = protect ? memstate_MappedProtected : memstate_MappedRW;
+ //printf("%x %d %x %x %x %d\n", effectiveAddr, mapping.Num, mapping.Addr, mapping.LocalOffset, mapping.Size, states[effectiveAddr >> PageShift]);
+ assert(states[effectiveAddr >> PageShift] == (protect ? memstate_MappedRW : memstate_MappedProtected));
+ states[effectiveAddr >> PageShift] = protect ? memstate_MappedProtected : memstate_MappedRW;
#if defined(__SWITCH__)
bool success;
@@ -418,7 +524,7 @@ void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noex
success = MapIntoRange(effectiveAddr, mapping.Num, OffsetsPerRegion[region] + offset, 0x1000);
assert(success);
#else
- SetCodeProtectionRange(effectiveAddr, 0x1000, mapping.Num, protect ? 1 : 2);
+ SetCodeProtectionRange(effectiveAddr, PageSize, mapping.Num, protect ? 1 : 2);
#endif
}
}
@@ -543,11 +649,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
u32 dtcmSize = ~NDS.ARM9.DTCMMask + 1;
u32 dtcmEnd = dtcmStart + dtcmSize;
#ifndef __SWITCH__
-#ifndef _WIN32
if (num == 0
&& dtcmEnd >= mirrorStart
&& dtcmStart < mirrorStart + mirrorSize)
{
+ if (dtcmSize < PageSize)
+ {
+ // we could technically mask out the DTCM by setting a hole to access permissions
+ // but realistically there isn't much of a point in mapping less than 16kb of DTCM
+ // so it isn't worth more complex support
+ Log(LogLevel::Info, "DTCM size smaller than 16kb skipping mapping entirely");
+ return false;
+ }
+
bool success;
if (dtcmStart > mirrorStart)
{
@@ -562,7 +676,6 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
}
}
else
-#endif
{
bool succeded = MapIntoRange(mirrorStart, num, OffsetsPerRegion[region] + memoryOffset, mirrorSize);
assert(succeded);
@@ -579,22 +692,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
{
if (skipDTCM && mirrorStart + offset == dtcmStart)
{
-#ifdef _WIN32
- SetCodeProtectionRange(dtcmStart, dtcmSize, 0, 0);
-#endif
offset += dtcmSize;
}
else
{
u32 sectionOffset = offset;
- bool hasCode = isExecutable && PageContainsCode(&range[offset / 512]);
+ bool hasCode = isExecutable && PageContainsCode(&range[offset / 512], PageSize);
while (offset < mirrorSize
- && (!isExecutable || PageContainsCode(&range[offset / 512]) == hasCode)
+ && (!isExecutable || PageContainsCode(&range[offset / 512], PageSize) == hasCode)
&& (!skipDTCM || mirrorStart + offset != NDS.ARM9.DTCMBase))
{
- assert(states[(mirrorStart + offset) >> 12] == memstate_Unmapped);
- states[(mirrorStart + offset) >> 12] = hasCode ? memstate_MappedProtected : memstate_MappedRW;
- offset += 0x1000;
+ assert(states[(mirrorStart + offset) >> PageShift] == memstate_Unmapped);
+ states[(mirrorStart + offset) >> PageShift] = hasCode ? memstate_MappedProtected : memstate_MappedRW;
+ offset += PageSize;
}
u32 sectionSize = offset - sectionOffset;
@@ -624,6 +734,86 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
return true;
}
+u32 ARMJIT_Memory::PageSize = 0;
+u32 ARMJIT_Memory::PageShift = 0;
+
+bool ARMJIT_Memory::IsFastMemSupported()
+{
+#ifdef __APPLE__
+ return false;
+#else
+ static bool initialised = false;
+ static bool isSupported = false;
+ if (!initialised)
+ {
+#ifdef _WIN32
+ ARMJIT_Global::Init();
+ isSupported = virtualAlloc2Ptr != nullptr;
+ ARMJIT_Global::DeInit();
+
+ PageSize = RegularPageSize;
+#else
+ PageSize = __sysconf(_SC_PAGESIZE);
+ isSupported = PageSize == RegularPageSize || PageSize == LargePageSize;
+#endif
+ PageShift = __builtin_ctz(PageSize);
+ initialised = true;
+ }
+ return isSupported;
+#endif
+}
+
+void ARMJIT_Memory::RegisterFaultHandler()
+{
+#ifdef _WIN32
+ ExceptionHandlerHandle = AddVectoredExceptionHandler(1, ExceptionHandler);
+
+ KernelBaseDll = LoadLibrary("KernelBase.dll");
+ if (KernelBaseDll)
+ {
+ virtualAlloc2Ptr = reinterpret_cast(GetProcAddress(KernelBaseDll, "VirtualAlloc2"));
+ mapViewOfFile3Ptr = reinterpret_cast(GetProcAddress(KernelBaseDll, "MapViewOfFile3"));
+ }
+
+ if (!virtualAlloc2Ptr)
+ {
+ Log(LogLevel::Error, "Could not load new Windows virtual memory functions, fast memory is disabled.\n");
+ }
+#else
+ struct sigaction sa;
+ sa.sa_handler = nullptr;
+ sa.sa_sigaction = &SigsegvHandler;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGSEGV, &sa, &OldSaSegv);
+#ifdef __APPLE__
+ sigaction(SIGBUS, &sa, &OldSaBus);
+#endif
+#endif
+}
+
+void ARMJIT_Memory::UnregisterFaultHandler()
+{
+#ifdef _WIN32
+ if (ExceptionHandlerHandle)
+ {
+ RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
+ ExceptionHandlerHandle = nullptr;
+ }
+
+ if (KernelBaseDll)
+ {
+ FreeLibrary(KernelBaseDll);
+ KernelBaseDll = nullptr;
+ }
+#else
+ sigaction(SIGSEGV, &OldSaSegv, nullptr);
+#ifdef __APPLE__
+ sigaction(SIGBUS, &OldSaBus, nullptr);
+#endif
+#endif
+}
+
bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
{
if (nds.JIT.JITCompiler.IsJITFault(faultDesc.FaultPC))
@@ -632,7 +822,7 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
u8* memStatus = nds.CurCPU == 0 ? nds.JIT.Memory.MappingStatus9 : nds.JIT.Memory.MappingStatus7;
- if (memStatus[faultDesc.EmulatedFaultAddr >> 12] == memstate_Unmapped)
+ if (memStatus[faultDesc.EmulatedFaultAddr >> PageShift] == memstate_Unmapped)
rewriteToSlowPath = !nds.JIT.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr);
if (rewriteToSlowPath)
@@ -643,10 +833,9 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
return false;
}
-const u64 AddrSpaceSize = 0x100000000;
-
ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
{
+ ARMJIT_Global::Init();
#if defined(__SWITCH__)
MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize);
virtmemLock();
@@ -671,33 +860,27 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
u8* basePtr = MemoryBaseCodeMem;
#elif defined(_WIN32)
- ExceptionHandlerHandle = AddVectoredExceptionHandler(1, ExceptionHandler);
+ if (virtualAlloc2Ptr)
+ {
+ MemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, MemoryTotalSize, nullptr);
- MemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MemoryTotalSize, NULL);
+ MemoryBase = reinterpret_cast(virtualAlloc2Ptr(nullptr, nullptr, VirtmemAreaSize,
+ MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
+ PAGE_NOACCESS,
+ nullptr, 0));
+ // split off placeholder and map base mapping
+ VirtualFree(MemoryBase, MemoryTotalSize, MEM_RELEASE|MEM_PRESERVE_PLACEHOLDER);
+ mapViewOfFile3Ptr(MemoryFile, nullptr, MemoryBase, 0, MemoryTotalSize, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
- MemoryBase = (u8*)VirtualAlloc(NULL, AddrSpaceSize*4, MEM_RESERVE, PAGE_READWRITE);
- VirtualFree(MemoryBase, 0, MEM_RELEASE);
- // this is incredible hacky
- // but someone else is trying to go into our address space!
- // Windows will very likely give them virtual memory starting at the same address
- // as it is giving us now.
- // That's why we don't use this address, but instead 4gb inwards
- // I know this is terrible
- FastMem9Start = MemoryBase + AddrSpaceSize;
- FastMem7Start = MemoryBase + AddrSpaceSize*2;
- MemoryBase = MemoryBase + AddrSpaceSize*3;
-
- MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, MemoryTotalSize, MemoryBase);
+ VirtmemPlaceholders.push_back({reinterpret_cast(MemoryBase)+MemoryTotalSize, AddrSpaceSize*2});
+ }
+ else
+ {
+ // old Windows version
+ MemoryBase = new u8[MemoryTotalSize];
+ }
#else
- // this used to be allocated with three different mmaps
- // The idea was to give the OS more freedom where to position the buffers,
- // but something was bad about this so instead we take this vmem eating monster
- // which seems to work better.
- MemoryBase = (u8*)mmap(NULL, AddrSpaceSize*4, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
- munmap(MemoryBase, AddrSpaceSize*4);
- FastMem9Start = MemoryBase;
- FastMem7Start = MemoryBase + AddrSpaceSize;
- MemoryBase = MemoryBase + AddrSpaceSize*2;
+ MemoryBase = (u8*)mmap(nullptr, VirtmemAreaSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
#if defined(__ANDROID__)
Libandroid = Platform::DynamicLibrary_Load("libandroid.so");
@@ -730,20 +913,10 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
Log(LogLevel::Error, "Failed to allocate memory using ftruncate! (%s)", strerror(errno));
}
- struct sigaction sa;
- sa.sa_handler = nullptr;
- sa.sa_sigaction = &SigsegvHandler;
- sa.sa_flags = SA_SIGINFO;
- sigemptyset(&sa.sa_mask);
- sigaction(SIGSEGV, &sa, &OldSaSegv);
-#ifdef __APPLE__
- sigaction(SIGBUS, &sa, &OldSaBus);
-#endif
-
mmap(MemoryBase, MemoryTotalSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, 0);
-
- u8* basePtr = MemoryBase;
#endif
+ FastMem9Start = MemoryBase+MemoryTotalSize;
+ FastMem7Start = static_cast(FastMem9Start)+AddrSpaceSize;
}
ARMJIT_Memory::~ARMJIT_Memory() noexcept
@@ -764,34 +937,37 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
free(MemoryBase);
MemoryBase = nullptr;
#elif defined(_WIN32)
- if (MemoryBase)
+ if (virtualAlloc2Ptr)
{
- bool viewUnmapped = UnmapViewOfFile(MemoryBase);
- assert(viewUnmapped);
- MemoryBase = nullptr;
- FastMem9Start = nullptr;
- FastMem7Start = nullptr;
- }
+ if (MemoryBase)
+ {
+ bool viewUnmapped = UnmapViewOfFileEx(MemoryBase, MEM_PRESERVE_PLACEHOLDER);
+ assert(viewUnmapped);
+ bool viewCoalesced = VirtualFree(MemoryBase, VirtmemAreaSize, MEM_RELEASE|MEM_COALESCE_PLACEHOLDERS);
+ assert(viewCoalesced);
+ bool freeEverything = VirtualFree(MemoryBase, 0, MEM_RELEASE);
+ assert(freeEverything);
- if (MemoryFile)
- {
- CloseHandle(MemoryFile);
- MemoryFile = INVALID_HANDLE_VALUE;
- }
+ MemoryBase = nullptr;
+ FastMem9Start = nullptr;
+ FastMem7Start = nullptr;
+ printf("unmappinged everything\n");
+ }
- if (ExceptionHandlerHandle)
+ if (MemoryFile)
+ {
+ CloseHandle(MemoryFile);
+ MemoryFile = INVALID_HANDLE_VALUE;
+ }
+ }
+ else
{
- RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
- ExceptionHandlerHandle = nullptr;
+ delete[] MemoryBase;
}
#else
- sigaction(SIGSEGV, &OldSaSegv, nullptr);
-#ifdef __APPLE__
- sigaction(SIGBUS, &OldSaBus, nullptr);
-#endif
if (MemoryBase)
{
- munmap(MemoryBase, MemoryTotalSize);
+ munmap(MemoryBase, VirtmemAreaSize);
MemoryBase = nullptr;
FastMem9Start = nullptr;
FastMem7Start = nullptr;
@@ -812,6 +988,8 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
#endif
#endif
+
+ ARMJIT_Global::DeInit();
}
void ARMJIT_Memory::Reset() noexcept
@@ -834,17 +1012,6 @@ void ARMJIT_Memory::Reset() noexcept
bool ARMJIT_Memory::IsFastmemCompatible(int region) const noexcept
{
-#ifdef _WIN32
- /*
- TODO: with some hacks, the smaller shared WRAM regions
- could be mapped in some occaisons as well
- */
- if (region == memregion_DTCM
- || region == memregion_SharedWRAM
- || region == memregion_NewSharedWRAM_B
- || region == memregion_NewSharedWRAM_C)
- return false;
-#endif
return OffsetsPerRegion[region] != UINT32_MAX;
}
diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h
index 88e647d5..cac9dc62 100644
--- a/src/ARMJIT_Memory.h
+++ b/src/ARMJIT_Memory.h
@@ -23,6 +23,7 @@
#include "MemConstants.h"
#ifdef JIT_ENABLED
+# include
# include "TinyVector.h"
# include "ARM.h"
# if defined(__SWITCH__)
@@ -48,23 +49,22 @@ class Compiler;
class ARMJIT;
#endif
+static constexpr u32 LargePageSize = 0x4000;
+static constexpr u32 RegularPageSize = 0x1000;
+
constexpr u32 RoundUp(u32 size) noexcept
{
-#ifdef _WIN32
- return (size + 0xFFFF) & ~0xFFFF;
-#else
- return size;
-#endif
+ return (size + LargePageSize - 1) & ~(LargePageSize - 1);
}
-const u32 MemBlockMainRAMOffset = 0;
-const u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize);
-const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize);
-const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize);
-const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize);
-const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize);
-const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize);
-const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize);
+static constexpr u32 MemBlockMainRAMOffset = 0;
+static constexpr u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize);
+static constexpr u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize);
+static constexpr u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize);
+static constexpr u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize);
+static constexpr u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize);
+static constexpr u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize);
+static constexpr u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize);
class ARMJIT_Memory
{
@@ -137,6 +137,14 @@ public:
bool IsFastmemCompatible(int region) const noexcept;
void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept;
bool MapAtAddress(u32 addr) noexcept;
+
+ static bool IsFastMemSupported();
+
+ static void RegisterFaultHandler();
+ static void UnregisterFaultHandler();
+
+ static u32 PageSize;
+ static u32 PageShift;
private:
friend class Compiler;
struct Mapping
@@ -162,14 +170,22 @@ private:
void* FastMem9Start;
void* FastMem7Start;
u8* MemoryBase = nullptr;
+
#if defined(__SWITCH__)
VirtmemReservation* FastMem9Reservation, *FastMem7Reservation;
u8* MemoryBaseCodeMem;
#elif defined(_WIN32)
+ struct VirtmemPlaceholder
+ {
+ uintptr_t Start;
+ size_t Size;
+ };
+ std::vector VirtmemPlaceholders;
+
static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo);
HANDLE MemoryFile = INVALID_HANDLE_VALUE;
- LPVOID ExceptionHandlerHandle = nullptr;
#else
+
static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext);
int MemoryFile = -1;
#endif
diff --git a/src/ARMJIT_x64/ARMJIT_Branch.cpp b/src/ARMJIT_x64/ARMJIT_Branch.cpp
index c32e2b73..bd73ae71 100644
--- a/src/ARMJIT_x64/ARMJIT_Branch.cpp
+++ b/src/ARMJIT_x64/ARMJIT_Branch.cpp
@@ -176,9 +176,9 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
else
MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste
if (Num == 0)
- CALL((void*)&ARMv5JumpToTrampoline);
+ ABI_CallFunction(ARMv5JumpToTrampoline);
else
- CALL((void*)&ARMv4JumpToTrampoline);
+ ABI_CallFunction(ARMv4JumpToTrampoline);
PopRegs(restoreCPSR, true);
diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp
index ba6c0fb4..6de4caf6 100644
--- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp
+++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp
@@ -21,19 +21,13 @@
#include "../ARMJIT.h"
#include "../ARMInterpreter.h"
#include "../NDS.h"
+#include "../ARMJIT_Global.h"
#include
#include
#include "../dolphin/CommonFuncs.h"
-#ifdef _WIN32
-#include
-#else
-#include
-#include
-#endif
-
using namespace Gen;
using namespace Common;
@@ -222,46 +216,21 @@ void Compiler::A_Comp_MSR()
MOV(32, R(ABI_PARAM3), R(RCPSR));
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
MOV(64, R(ABI_PARAM1), R(RCPU));
- CALL((void*)&UpdateModeTrampoline);
+ ABI_CallFunction(UpdateModeTrampoline);
PopRegs(true, true);
}
}
}
-/*
- We'll repurpose this .bss memory
-
- */
-u8 CodeMemory[1024 * 1024 * 32];
-
Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
{
- {
- #ifdef _WIN32
- SYSTEM_INFO sysInfo;
- GetSystemInfo(&sysInfo);
+ ARMJIT_Global::Init();
- u64 pageSize = (u64)sysInfo.dwPageSize;
- #else
- u64 pageSize = sysconf(_SC_PAGE_SIZE);
- #endif
+ CodeMemBase = static_cast(ARMJIT_Global::AllocateCodeMem());
+ CodeMemSize = ARMJIT_Global::CodeMemorySliceSize;
- u8* pageAligned = (u8*)(((u64)CodeMemory & ~(pageSize - 1)) + pageSize);
- u64 alignedSize = (((u64)CodeMemory + sizeof(CodeMemory)) & ~(pageSize - 1)) - (u64)pageAligned;
-
- #ifdef _WIN32
- DWORD dummy;
- VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
- #elif defined(__APPLE__)
- pageAligned = (u8*)mmap(NULL, 1024*1024*32, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
- #else
- mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
- #endif
-
- ResetStart = pageAligned;
- CodeMemSize = alignedSize;
- }
+ ResetStart = CodeMemBase;
Reset();
@@ -475,6 +444,13 @@ Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
FarSize = (ResetStart + CodeMemSize) - FarStart;
}
+Compiler::~Compiler()
+{
+ ARMJIT_Global::FreeCodeMem(CodeMemBase);
+
+ ARMJIT_Global::DeInit();
+}
+
void Compiler::LoadCPSR()
{
assert(!CPSRDirty);
@@ -684,7 +660,7 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken)
if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
- JMP((u8*)&ARM_Ret, true);
+ ABI_TailCall(ARM_Ret);
}
}
@@ -846,7 +822,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
- JMP((u8*)ARM_Ret, true);
+ ABI_TailCall(ARM_Ret);
#ifdef JIT_PROFILING_ENABLED
CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr);
diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h
index 3965e882..c714a6ba 100644
--- a/src/ARMJIT_x64/ARMJIT_Compiler.h
+++ b/src/ARMJIT_x64/ARMJIT_Compiler.h
@@ -84,6 +84,7 @@ class Compiler : public Gen::XEmitter
{
public:
explicit Compiler(melonDS::NDS& nds);
+ ~Compiler();
void Reset();
@@ -256,6 +257,7 @@ public:
std::unordered_map LoadStorePatches {};
+ u8* CodeMemBase;
u8* ResetStart {};
u32 CodeMemSize {};
diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp
index 219c7271..71cd0770 100644
--- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp
+++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp
@@ -316,24 +316,24 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
{
switch (size | NDS.ConsoleType)
{
- case 32: CALL((void*)&SlowWrite9); break;
- case 16: CALL((void*)&SlowWrite9); break;
- case 8: CALL((void*)&SlowWrite9); break;
- case 33: CALL((void*)&SlowWrite9); break;
- case 17: CALL((void*)&SlowWrite9); break;
- case 9: CALL((void*)&SlowWrite9); break;
+ case 32: ABI_CallFunction(SlowWrite9); break;
+ case 16: ABI_CallFunction(SlowWrite9); break;
+ case 8: ABI_CallFunction(&SlowWrite9); break;
+ case 33: ABI_CallFunction(&SlowWrite9); break;
+ case 17: ABI_CallFunction(&SlowWrite9); break;
+ case 9: ABI_CallFunction(&SlowWrite9); break;
}
}
else
{
switch (size | NDS.ConsoleType)
{
- case 32: CALL((void*)&SlowRead9); break;
- case 16: CALL((void*)&SlowRead9); break;
- case 8: CALL((void*)&SlowRead9); break;
- case 33: CALL((void*)&SlowRead9); break;
- case 17: CALL((void*)&SlowRead9); break;
- case 9: CALL((void*)&SlowRead9); break;
+ case 32: ABI_CallFunction(&SlowRead9); break;
+ case 16: ABI_CallFunction(&SlowRead9); break;
+ case 8: ABI_CallFunction(&SlowRead9); break;
+ case 33: ABI_CallFunction(&SlowRead9); break;
+ case 17: ABI_CallFunction(&SlowRead9); break;
+ case 9: ABI_CallFunction(&SlowRead9); break;
}
}
}
@@ -347,24 +347,24 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
switch (size | NDS.ConsoleType)
{
- case 32: CALL((void*)&SlowWrite7); break;
- case 16: CALL((void*)&SlowWrite7); break;
- case 8: CALL((void*)&SlowWrite7); break;
- case 33: CALL((void*)&SlowWrite7); break;
- case 17: CALL((void*)&SlowWrite7); break;
- case 9: CALL((void*)&SlowWrite7); break;
+ case 32: ABI_CallFunction(&SlowWrite7); break;
+ case 16: ABI_CallFunction(&SlowWrite7); break;
+ case 8: ABI_CallFunction(&SlowWrite7); break;
+ case 33: ABI_CallFunction(&SlowWrite7); break;
+ case 17: ABI_CallFunction(&SlowWrite7); break;
+ case 9: ABI_CallFunction(&SlowWrite7); break;
}
}
else
{
switch (size | NDS.ConsoleType)
{
- case 32: CALL((void*)&SlowRead7); break;
- case 16: CALL((void*)&SlowRead7); break;
- case 8: CALL((void*)&SlowRead7); break;
- case 33: CALL((void*)&SlowRead7); break;
- case 17: CALL((void*)&SlowRead7); break;
- case 9: CALL((void*)&SlowRead7); break;
+ case 32: ABI_CallFunction(&SlowRead7); break;
+ case 16: ABI_CallFunction(&SlowRead7); break;
+ case 8: ABI_CallFunction(&SlowRead7); break;
+ case 33: ABI_CallFunction(&SlowRead7); break;
+ case 17: ABI_CallFunction(&SlowRead7); break;
+ case 9: ABI_CallFunction(&SlowRead7); break;
}
}
}
@@ -526,10 +526,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
switch (Num * 2 | NDS.ConsoleType)
{
- case 0: CALL((void*)&SlowBlockTransfer9); break;
- case 1: CALL((void*)&SlowBlockTransfer9); break;
- case 2: CALL((void*)&SlowBlockTransfer7); break;
- case 3: CALL((void*)&SlowBlockTransfer7); break;
+ case 0: ABI_CallFunction(&SlowBlockTransfer9); break;
+ case 1: ABI_CallFunction(&SlowBlockTransfer9); break;
+ case 2: ABI_CallFunction(&SlowBlockTransfer7); break;
+ case 3: ABI_CallFunction(&SlowBlockTransfer7); break;
}
PopRegs(false, false);
@@ -630,10 +630,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
switch (Num * 2 | NDS.ConsoleType)
{
- case 0: CALL((void*)&SlowBlockTransfer9); break;
- case 1: CALL((void*)&SlowBlockTransfer9); break;
- case 2: CALL((void*)&SlowBlockTransfer7); break;
- case 3: CALL((void*)&SlowBlockTransfer7); break;
+ case 0: ABI_CallFunction(&SlowBlockTransfer9); break;
+ case 1: ABI_CallFunction(&SlowBlockTransfer9); break;
+ case 2: ABI_CallFunction(&SlowBlockTransfer7); break;
+ case 3: ABI_CallFunction(&SlowBlockTransfer7); break;
}
ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc));
diff --git a/src/Args.h b/src/Args.h
index 2c405e29..4f4fa1f0 100644
--- a/src/Args.h
+++ b/src/Args.h
@@ -85,18 +85,6 @@ struct GDBArgs
/// New fields here should have default values if possible.
struct NDSArgs
{
- /// NDS ROM to install.
- /// Defaults to nullptr, which means no cart.
- /// Should be populated with the desired save data beforehand,
- /// including an SD card if applicable.
- std::unique_ptr NDSROM = nullptr;
-
- /// GBA ROM to install.
- /// Defaults to nullptr, which means no cart.
- /// Should be populated with the desired save data beforehand.
- /// Ignored in DSi mode.
- std::unique_ptr GBAROM = nullptr;
-
/// NDS ARM9 BIOS to install.
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
std::unique_ptr ARM9BIOS = std::make_unique(bios_arm9_bin);
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a4cb6f1e..fa8d475c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -97,8 +97,13 @@ if (ENABLE_JIT)
ARMJIT.cpp
ARMJIT_Memory.cpp
+ ARMJIT_Global.cpp
dolphin/CommonFuncs.cpp)
+
+ if (WIN32)
+ target_link_libraries(core PRIVATE onecore)
+ endif()
if (ARCHITECTURE STREQUAL x86_64)
target_sources(core PRIVATE
diff --git a/src/CP15.cpp b/src/CP15.cpp
index c271e180..e924bff3 100644
--- a/src/CP15.cpp
+++ b/src/CP15.cpp
@@ -44,6 +44,7 @@ void ARMv5::CP15Reset()
CP15Control = 0x2078; // dunno
RNGSeed = 44203;
+ TraceProcessID = 0;
DTCMSetting = 0;
ITCMSetting = 0;
@@ -643,6 +644,10 @@ void ARMv5::CP15Write(u32 id, u32 val)
UpdateITCMSetting();
return;
+ case 0xD01:
+ TraceProcessID = val;
+ return;
+
case 0xF00:
//printf("cache debug index register %08X\n", val);
return;
@@ -760,6 +765,9 @@ u32 ARMv5::CP15Read(u32 id) const
return DTCMSetting;
case 0x911:
return ITCMSetting;
+
+ case 0xD01:
+ return TraceProcessID;
}
if ((id & 0xF00) == 0xF00) // test/debug shit?
diff --git a/src/DSi.cpp b/src/DSi.cpp
index 00ed8da0..f9115cbf 100644
--- a/src/DSi.cpp
+++ b/src/DSi.cpp
@@ -74,8 +74,6 @@ const u32 NDMAModes[] =
DSi(
DSiArgs {
NDSArgs {
- nullptr,
- nullptr,
bios_arm9_bin,
bios_arm7_bin,
Firmware(0),
diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp
index b1d60d04..8a54bb18 100644
--- a/src/DSi_Camera.cpp
+++ b/src/DSi_Camera.cpp
@@ -40,8 +40,8 @@ const u32 DSi_CamModule::kTransferStart = 60000;
DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi)
{
- DSi.RegisterEventFunc(Event_DSi_CamIRQ, 0, MemberEventFunc(DSi_CamModule, IRQ));
- DSi.RegisterEventFunc(Event_DSi_CamTransfer, 0, MemberEventFunc(DSi_CamModule, TransferScanline));
+ DSi.RegisterEventFuncs(Event_DSi_CamIRQ, this, {MakeEventThunk(DSi_CamModule, IRQ)});
+ DSi.RegisterEventFuncs(Event_DSi_CamTransfer, this, {MakeEventThunk(DSi_CamModule, TransferScanline)});
Camera0 = DSi.I2C.GetOuterCamera();
Camera1 = DSi.I2C.GetInnerCamera();
@@ -52,8 +52,8 @@ DSi_CamModule::~DSi_CamModule()
Camera0 = nullptr;
Camera1 = nullptr;
- DSi.UnregisterEventFunc(Event_DSi_CamIRQ, 0);
- DSi.UnregisterEventFunc(Event_DSi_CamTransfer, 0);
+ DSi.UnregisterEventFuncs(Event_DSi_CamIRQ);
+ DSi.UnregisterEventFuncs(Event_DSi_CamTransfer);
}
void DSi_CamModule::Reset()
diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp
index 25abd474..24fa08b3 100644
--- a/src/DSi_DSP.cpp
+++ b/src/DSi_DSP.cpp
@@ -109,7 +109,7 @@ void DSi_DSP::AudioCb(std::array frame)
DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
{
- DSi.RegisterEventFunc(Event_DSi_DSP, 0, MemberEventFunc(DSi_DSP, DSPCatchUpU32));
+ DSi.RegisterEventFuncs(Event_DSi_DSP, this, {MakeEventThunk(DSi_DSP, DSPCatchUpU32)});
TeakraCore = new Teakra::Teakra();
SCFG_RST = false;
@@ -156,7 +156,7 @@ DSi_DSP::~DSi_DSP()
//PDATAWriteFifo = NULL;
TeakraCore = NULL;
- DSi.UnregisterEventFunc(Event_DSi_DSP, 0);
+ DSi.UnregisterEventFuncs(Event_DSi_DSP);
}
void DSi_DSP::Reset()
diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp
index a6b6c566..13eadd6e 100644
--- a/src/DSi_NAND.cpp
+++ b/src/DSi_NAND.cpp
@@ -189,20 +189,18 @@ void NANDImage::SetupFATCrypto(AES_ctx* ctx, u32 ctr)
u8 iv[16];
memcpy(iv, FATIV.data(), sizeof(iv));
- u32 res;
- res = iv[15] + (ctr & 0xFF);
- iv[15] = (res & 0xFF);
- res = iv[14] + ((ctr >> 8) & 0xFF) + (res >> 8);
- iv[14] = (res & 0xFF);
- res = iv[13] + ((ctr >> 16) & 0xFF) + (res >> 8);
- iv[13] = (res & 0xFF);
- res = iv[12] + (ctr >> 24) + (res >> 8);
- iv[12] = (res & 0xFF);
- iv[11] += (res >> 8);
- for (int i = 10; i >= 0; i--)
- {
- if (iv[i+1] == 0) iv[i]++;
- else break;
+ u8 ctr_value[16] = {0};
+ ctr_value[15] = ctr & 0xFF;
+ ctr_value[14] = (ctr >> 8) & 0xFF;
+ ctr_value[13] = (ctr >> 16) & 0xFF;
+ ctr_value[12] = (ctr >> 24) & 0xFF;
+
+ unsigned carry = 0;
+ for (unsigned i = 0; i < 16; i ++) {
+ unsigned j = 15-i;
+ unsigned x = iv[j] + ctr_value[j] + carry;
+ carry = x >= 0x100;
+ iv[j] = x;
}
AES_init_ctx_iv(ctx, FATKey.data(), iv);
diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp
index 9827bdbe..3ec5d3f0 100644
--- a/src/DSi_NWifi.cpp
+++ b/src/DSi_NWifi.cpp
@@ -31,7 +31,7 @@ using Platform::Log;
using Platform::LogLevel;
-const u8 CIS0[256] =
+u8 CIS0[256] =
{
0x01, 0x03, 0xD9, 0x01, 0xFF,
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
@@ -70,7 +70,7 @@ const u8 CIS0[256] =
0x00, 0x00, 0x00
};
-const u8 CIS1[256] =
+u8 CIS1[256] =
{
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
0x21, 0x02, 0x0C, 0x00,
@@ -134,7 +134,7 @@ DSi_NWifi::DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host) :
},
DSi(dsi)
{
- DSi.RegisterEventFunc(Event_DSi_NWifi, 0, MemberEventFunc(DSi_NWifi, MSTimer));
+ DSi.RegisterEventFuncs(Event_DSi_NWifi, this, {MakeEventThunk(DSi_NWifi, MSTimer)});
// this seems to control whether the firmware upload is done
EEPROMReady = 0;
@@ -144,7 +144,7 @@ DSi_NWifi::~DSi_NWifi()
{
DSi.CancelEvent(Event_DSi_NWifi);
- DSi.UnregisterEventFunc(Event_DSi_NWifi, 0);
+ DSi.UnregisterEventFuncs(Event_DSi_NWifi);
}
void DSi_NWifi::Reset()
@@ -201,6 +201,9 @@ void DSi_NWifi::Reset()
break;
}
+ CIS0[9] = ChipID >= 0x0D000000;
+ CIS1[4] = CIS0[9];
+
memset(EEPROM, 0, 0x400);
*(u32*)&EEPROM[0x000] = 0x300;
@@ -227,6 +230,8 @@ void DSi_NWifi::Reset()
BeaconTimer = 0x10A2220ULL;
ConnectionStatus = 0;
+ SendBSSInfo = true;
+
DSi.CancelEvent(Event_DSi_NWifi);
}
@@ -1001,7 +1006,7 @@ void DSi_NWifi::WMI_Command()
}
// checkme
- ScanTimer = scantime*5;
+ ScanTimer = scantime*8;
}
break;
@@ -1036,6 +1041,7 @@ void DSi_NWifi::WMI_Command()
// TODO: store it somewhere
Log(LogLevel::Debug, "WMI: set probed SSID: id=%d, flags=%02X, len=%d, SSID=%s\n", id, flags, len, ssid);
+ SendBSSInfo = flags == 0 || strcmp(ssid, WifiAP::APName) == 0;
}
break;
@@ -1405,6 +1411,11 @@ void DSi_NWifi::SendWMIAck(u8 ep)
void DSi_NWifi::SendWMIBSSInfo(u8 type, u8* data, u32 len)
{
+ if (!SendBSSInfo) {
+ Log(LogLevel::Info, "NWifi: melonAP filtered, not sending WMI BSSINFO event\n");
+ return;
+ }
+
if (!Mailbox[8].CanFit(6+len+2+16))
{
Log(LogLevel::Error, "NWifi: !! not enough space in RX buffer for WMI BSSINFO event\n");
diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h
index 84ac8a49..4140820b 100644
--- a/src/DSi_NWifi.h
+++ b/src/DSi_NWifi.h
@@ -147,6 +147,8 @@ private:
u32 ConnectionStatus;
u8 LANBuffer[2048];
+
+ bool SendBSSInfo;
};
}
diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp
index c600bc76..c1d07682 100644
--- a/src/DSi_SD.cpp
+++ b/src/DSi_SD.cpp
@@ -64,10 +64,9 @@ enum
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional&& sdcard) noexcept : DSi(dsi), Num(0)
{
- DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
- Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
- DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
- Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
+ DSi.RegisterEventFuncs(Event_DSi_SDMMCTransfer, this,
+ {MakeEventThunk(DSi_SDHost, FinishTX),
+ MakeEventThunk(DSi_SDHost, FinishRX)});
Ports[0] = sdcard ? std::make_unique(DSi, this, std::move(*sdcard)) : nullptr;
sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object
@@ -77,10 +76,9 @@ DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optio
// Creates an SDIO host
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
{
- DSi.RegisterEventFunc(Event_DSi_SDIOTransfer ,
- Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
- DSi.RegisterEventFunc(Event_DSi_SDIOTransfer,
- Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
+ DSi.RegisterEventFuncs(Event_DSi_SDIOTransfer, this,
+ {MakeEventThunk(DSi_SDHost, FinishTX),
+ MakeEventThunk(DSi_SDHost, FinishRX)});
Ports[0] = std::make_unique(DSi, this);
Ports[1] = nullptr;
@@ -88,10 +86,7 @@ DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
DSi_SDHost::~DSi_SDHost()
{
- DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
- Transfer_TX);
- DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
- Transfer_RX);
+ DSi.UnregisterEventFuncs(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer);
// unique_ptr's destructor will clean up the ports
}
diff --git a/src/GBACart.cpp b/src/GBACart.cpp
index a62aca6b..c3550207 100644
--- a/src/GBACart.cpp
+++ b/src/GBACart.cpp
@@ -832,6 +832,27 @@ std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen
return cart;
}
+std::unique_ptr LoadAddon(int type, void* userdata)
+{
+ std::unique_ptr cart;
+ switch (type)
+ {
+ case GBAAddon_RAMExpansion:
+ cart = std::make_unique();
+ break;
+ case GBAAddon_RumblePak:
+ cart = std::make_unique(userdata);
+ break;
+
+ default:
+ Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);
+ return nullptr;
+ }
+
+ cart->Reset();
+ return cart;
+}
+
void GBACartSlot::SetCart(std::unique_ptr&& cart) noexcept
{
Cart = std::move(cart);
@@ -864,23 +885,6 @@ void GBACartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept
}
}
-void GBACartSlot::LoadAddon(void* userdata, int type) noexcept
-{
- switch (type)
- {
- case GBAAddon_RAMExpansion:
- Cart = std::make_unique();
- break;
- case GBAAddon_RumblePak:
- Cart = std::make_unique(userdata);
- break;
-
- default:
- Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);
- return;
- }
-}
-
std::unique_ptr GBACartSlot::EjectCart() noexcept
{
return std::move(Cart);
diff --git a/src/GBACart.h b/src/GBACart.h
index 726a234d..e6639813 100644
--- a/src/GBACart.h
+++ b/src/GBACart.h
@@ -241,8 +241,6 @@ public:
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
- void LoadAddon(void* userdata, int type) noexcept;
-
/// @return The cart that was in the cart slot if any,
/// or \c nullptr if the cart slot was empty.
std::unique_ptr EjectCart() noexcept;
@@ -309,6 +307,8 @@ std::unique_ptr ParseROM(const u8* romdata, u32 romlen, const u8* sr
/// or \c nullptr if there was an error.
std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen, std::unique_ptr&& sramdata, u32 sramlen, void* userdata = nullptr);
+std::unique_ptr LoadAddon(int type, void* userdata);
+
}
#endif // GBACART_H
diff --git a/src/GPU.cpp b/src/GPU.cpp
index f24d8ab5..585f0bea 100644
--- a/src/GPU.cpp
+++ b/src/GPU.cpp
@@ -70,10 +70,13 @@ GPU::GPU(melonDS::NDS& nds, std::unique_ptr&& renderer3d, std::uniqu
GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique()),
GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique(*this))
{
- NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank));
- NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline));
- NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame));
- NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO));
+ NDS.RegisterEventFuncs(Event_LCD, this,
+ {
+ MakeEventThunk(GPU, StartHBlank),
+ MakeEventThunk(GPU, StartScanline),
+ MakeEventThunk(GPU, FinishFrame)
+ });
+ NDS.RegisterEventFuncs(Event_DisplayFIFO, this, {MakeEventThunk(GPU, DisplayFIFO)});
InitFramebuffers();
}
@@ -82,10 +85,8 @@ GPU::~GPU() noexcept
{
// All unique_ptr fields are automatically cleaned up
- NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank);
- NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline);
- NDS.UnregisterEventFunc(Event_LCD, LCD_FinishFrame);
- NDS.UnregisterEventFunc(Event_DisplayFIFO, 0);
+ NDS.UnregisterEventFuncs(Event_LCD);
+ NDS.UnregisterEventFuncs(Event_DisplayFIFO);
}
void GPU::ResetVRAMCache() noexcept
diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp
index 6ad2cd3e..01dade2b 100644
--- a/src/GPU2D_Soft.cpp
+++ b/src/GPU2D_Soft.cpp
@@ -914,6 +914,9 @@ void SoftRenderer::DrawBG_3D()
template
void SoftRenderer::DrawBG_Text(u32 line, u32 bgnum)
{
+ // workaround for backgrounds missing on aarch64 with lto build
+ asm volatile ("" : : : "memory");
+
u16 bgcnt = CurUnit->BGCnt[bgnum];
u32 tilesetaddr, tilemapaddr;
diff --git a/src/NDS.cpp b/src/NDS.cpp
index 1023d3c0..7cef3bf0 100644
--- a/src/NDS.cpp
+++ b/src/NDS.cpp
@@ -74,13 +74,11 @@ const s32 kIterationCycleMargin = 8;
//
// timings for GBA slot and wifi are set up at runtime
-NDS* NDS::Current = nullptr;
+thread_local NDS* NDS::Current = nullptr;
NDS::NDS() noexcept :
NDS(
NDSArgs {
- nullptr,
- nullptr,
std::make_unique(bios_arm9_bin),
std::make_unique(bios_arm7_bin),
Firmware(0),
@@ -102,8 +100,8 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept :
SPI(*this, std::move(args.Firmware)),
RTC(*this),
Wifi(*this),
- NDSCartSlot(*this, std::move(args.NDSROM)),
- GBACartSlot(*this, type == 1 ? nullptr : std::move(args.GBAROM)),
+ NDSCartSlot(*this, nullptr),
+ GBACartSlot(*this, nullptr),
AREngine(*this),
ARM9(*this, args.GDB, args.JIT.has_value()),
ARM7(*this, args.GDB, args.JIT.has_value()),
@@ -124,8 +122,8 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept :
DMA(1, 3, *this),
}
{
- RegisterEventFunc(Event_Div, 0, MemberEventFunc(NDS, DivDone));
- RegisterEventFunc(Event_Sqrt, 0, MemberEventFunc(NDS, SqrtDone));
+ RegisterEventFuncs(Event_Div, this, {MakeEventThunk(NDS, DivDone)});
+ RegisterEventFuncs(Event_Sqrt, this, {MakeEventThunk(NDS, SqrtDone)});
MainRAM = JIT.Memory.GetMainRAM();
SharedWRAM = JIT.Memory.GetSharedWRAM();
@@ -134,8 +132,8 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept :
NDS::~NDS() noexcept
{
- UnregisterEventFunc(Event_Div, 0);
- UnregisterEventFunc(Event_Sqrt, 0);
+ UnregisterEventFuncs(Event_Div);
+ UnregisterEventFuncs(Event_Sqrt);
// The destructor for each component is automatically called by the compiler
}
@@ -229,6 +227,15 @@ void NDS::SetJITArgs(std::optional args) noexcept
}
#endif
+#ifdef GDBSTUB_ENABLED
+void NDS::SetGdbArgs(std::optional args) noexcept
+{
+ ARM9.SetGdbArgs(args);
+ ARM7.SetGdbArgs(args);
+ EnableGDBStub = args.has_value();
+}
+#endif
+
void NDS::InitTimings()
{
// TODO, eventually:
@@ -752,11 +759,6 @@ void NDS::SetGBASave(const u8* savedata, u32 savelen)
}
-void NDS::LoadGBAAddon(int type)
-{
- GBACartSlot.LoadAddon(UserData, type);
-}
-
void NDS::LoadBIOS()
{
Reset();
@@ -816,7 +818,7 @@ void NDS::RunSystem(u64 timestamp)
SchedListMask &= ~(1<
u32 NDS::RunFrame()
{
+ Current = this;
+
FrameStartTimestamp = SysTimestamp;
GPU.TotalScanlines = 0;
@@ -1069,18 +1073,26 @@ void NDS::Reschedule(u64 target)
}
}
-void NDS::RegisterEventFunc(u32 id, u32 funcid, EventFunc func)
+void NDS::RegisterEventFuncs(u32 id, void* that, const std::initializer_list& funcs)
{
SchedEvent& evt = SchedList[id];
- evt.Funcs[funcid] = func;
+ evt.That = that;
+ assert(funcs.size() <= MaxEventFunctions);
+ int i = 0;
+ for (EventFunc func : funcs)
+ {
+ evt.Funcs[i++] = func;
+ }
}
-void NDS::UnregisterEventFunc(u32 id, u32 funcid)
+void NDS::UnregisterEventFuncs(u32 id)
{
SchedEvent& evt = SchedList[id];
- evt.Funcs.erase(funcid);
+ evt.That = nullptr;
+ for (int i = 0; i < MaxEventFunctions; i++)
+ evt.Funcs[i] = nullptr;
}
void NDS::ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param)
@@ -1088,7 +1100,7 @@ void NDS::ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param)
if (SchedListMask & (1<> 8;
+ case 0x04000180: return IPCSync9 & 0xFF;
+ case 0x04000181: return IPCSync9 >> 8;
+
case 0x040001A0:
if (!(ExMemCnt[0] & (1<<11)))
return NDSCartSlot.GetSPICnt() & 0xFF;
@@ -3173,6 +3188,17 @@ void NDS::ARM9IOWrite8(u32 addr, u8 val)
KeyCnt[0] = (KeyCnt[0] & 0x00FF) | (val << 8);
return;
+ case 0x04000181:
+ IPCSync7 &= 0xFFF0;
+ IPCSync7 |= (val & 0x0F);
+ IPCSync9 &= 0xB0FF;
+ IPCSync9 |= ((val & 0x4F) << 8);
+ if ((val & 0x20) && (IPCSync7 & 0x4000))
+ {
+ SetIRQ(1, IRQ_IPCSync);
+ }
+ return;
+
case 0x04000188:
NDS::ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24));
return;
@@ -3664,6 +3690,9 @@ u8 NDS::ARM7IORead8(u32 addr)
case 0x04000138: return RTC.Read() & 0xFF;
+ case 0x04000180: return IPCSync7 & 0xFF;
+ case 0x04000181: return IPCSync7 >> 8;
+
case 0x040001A0:
if (ExMemCnt[0] & (1<<11))
return NDSCartSlot.GetSPICnt() & 0xFF;
@@ -3972,6 +4001,17 @@ void NDS::ARM7IOWrite8(u32 addr, u8 val)
case 0x04000138: RTC.Write(val, true); return;
+ case 0x04000181:
+ IPCSync9 &= 0xFFF0;
+ IPCSync9 |= (val & 0x0F);
+ IPCSync7 &= 0xB0FF;
+ IPCSync7 |= ((val & 0x4F) << 8);
+ if ((val & 0x20) && (IPCSync9 & 0x4000))
+ {
+ SetIRQ(0, IRQ_IPCSync);
+ }
+ return;
+
case 0x04000188:
NDS::ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24));
return;
diff --git a/src/NDS.h b/src/NDS.h
index b2bfb385..6e486e28 100644
--- a/src/NDS.h
+++ b/src/NDS.h
@@ -76,11 +76,15 @@ enum
Event_MAX
};
-typedef std::function EventFunc;
-#define MemberEventFunc(cls,func) std::bind(&cls::func,this,std::placeholders::_1)
+static constexpr u32 MaxEventFunctions = 3;
+
+typedef void (*EventFunc)(void* that, u32 param);
+#define MakeEventThunk(class, func) [](void* that, u32 param) { static_cast(that)->func(param); }
+
struct SchedEvent
{
- std::map Funcs;
+ std::array Funcs;
+ void* That;
u64 Timestamp;
u32 FuncID;
u32 Param;
@@ -384,7 +388,6 @@ public: // TODO: Encapsulate the rest of these members
u32 GetGBASaveLength() const { return GBACartSlot.GetSaveMemoryLength(); }
void SetGBASave(const u8* savedata, u32 savelen);
- void LoadGBAAddon(int type);
std::unique_ptr EjectGBACart() { return GBACartSlot.EjectCart(); }
u32 RunFrame();
@@ -402,8 +405,8 @@ public: // TODO: Encapsulate the rest of these members
virtual void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) {}
void MicInputFrame(s16* data, int samples);
- void RegisterEventFunc(u32 id, u32 funcid, EventFunc func);
- void UnregisterEventFunc(u32 id, u32 funcid);
+ void RegisterEventFuncs(u32 id, void* that, const std::initializer_list& funcs);
+ void UnregisterEventFuncs(u32 id);
void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param);
void CancelEvent(u32 id);
@@ -477,6 +480,12 @@ public: // TODO: Encapsulate the rest of these members
void SetJITArgs(std::optional args) noexcept {}
#endif
+#ifdef GDBSTUB_ENABLED
+ void SetGdbArgs(std::optional args) noexcept;
+#else
+ void SetGdbArgs(std::optional args) noexcept {}
+#endif
+
private:
void InitTimings();
u32 SchedListMask;
@@ -536,8 +545,8 @@ public:
NDS& operator=(const NDS&) = delete;
NDS(NDS&&) = delete;
NDS& operator=(NDS&&) = delete;
- // The frontend should set and unset this manually after creating and destroying the NDS object.
- [[deprecated("Temporary workaround until JIT code generation is revised to accommodate multiple NDS objects.")]] static NDS* Current;
+
+ static thread_local NDS* Current;
protected:
explicit NDS(NDSArgs&& args, int type, void* userdata) noexcept;
virtual void DoSavestateExtra(Savestate* file) {}
diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp
index b0eef56a..1fa0fbfe 100644
--- a/src/NDSCart.cpp
+++ b/src/NDSCart.cpp
@@ -109,36 +109,52 @@ void NDSCartSlot::Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept
}
}
-void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept
+void NDSCartSlot::Key1_LoadKeyBuf(bool dsimode) noexcept
{
- if (NDS.IsLoadedARM7BIOSKnownNative())
+ if (NDS.ConsoleType == 1)
{
- u32 expected_bios_length = dsi ? 0x10000 : 0x4000;
- if (biosLength != expected_bios_length)
+ // DSi mode: grab the right key depending on the requested cart mode
+
+ auto& dsi = static_cast(NDS);
+ if (dsimode)
{
- Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got %u bytes\n", expected_bios_length, biosLength);
- }
- else if (bios == nullptr)
- {
- Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got nullptr\n", expected_bios_length);
+ // load from ARM7 BIOS at 0xC6D0
+
+ const u8* bios = dsi.ARM7iBIOS.data();
+ memcpy(Key1_KeyBuf.data(), bios + 0xC6D0, sizeof(Key1_KeyBuf));
+ Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from ARM7i BIOS\n");
}
else
{
- memcpy(Key1_KeyBuf.data(), bios + (dsi ? 0xC6D0 : 0x0030), sizeof(Key1_KeyBuf));
- Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from memory\n");
+ // load from ARM9 BIOS at 0x99A0
+
+ const u8* bios = dsi.ARM9iBIOS.data();
+ memcpy(Key1_KeyBuf.data(), bios + 0x99A0, sizeof(Key1_KeyBuf));
+ Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from ARM9i BIOS\n");
}
}
else
{
- // well
- memset(Key1_KeyBuf.data(), 0, sizeof(Key1_KeyBuf));
- Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n");
+ // DS mode: load from ARM7 BIOS at 0x0030
+
+ if (NDS.IsLoadedARM7BIOSKnownNative())
+ {
+ const u8* bios = NDS.GetARM7BIOS().data();
+ memcpy(Key1_KeyBuf.data(), bios + 0x0030, sizeof(Key1_KeyBuf));
+ Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from ARM7 BIOS\n");
+ }
+ else
+ {
+ // well
+ memset(Key1_KeyBuf.data(), 0, sizeof(Key1_KeyBuf));
+ Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n");
+ }
}
}
-void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept
+void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) noexcept
{
- Key1_LoadKeyBuf(dsi, bios, biosLength);
+ Key1_LoadKeyBuf(dsi);
u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
if (level >= 1) Key1_ApplyKeycode(keycode, mod);
@@ -262,16 +278,15 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, const u8* cmd,
case 0x3C:
CmdEncMode = 1;
- cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, nds.GetARM7BIOS().data(), ARM7BIOSSize);
+ cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2);
DSiMode = false;
return 0;
case 0x3D:
if (IsDSi)
{
- auto& dsi = static_cast(nds);
CmdEncMode = 1;
- cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, &dsi.ARM7iBIOS[0], sizeof(DSi::ARM7iBIOS));
+ cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2);
DSiMode = true;
}
return 0;
@@ -1426,9 +1441,12 @@ void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len)
NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr&& rom) noexcept : NDS(nds)
{
- NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData));
- NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer));
- NDS.RegisterEventFunc(Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone));
+ NDS.RegisterEventFuncs(Event_ROMTransfer, this,
+ {
+ MakeEventThunk(NDSCartSlot, ROMPrepareData),
+ MakeEventThunk(NDSCartSlot, ROMEndTransfer)
+ });
+ NDS.RegisterEventFuncs(Event_ROMSPITransfer, this, {MakeEventThunk(NDSCartSlot, SPITransferDone)});
// All fields are default-constructed because they're listed as such in the class declaration
if (rom)
@@ -1437,9 +1455,8 @@ NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr&& rom) n
NDSCartSlot::~NDSCartSlot() noexcept
{
- NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData);
- NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_End);
- NDS.UnregisterEventFunc(Event_ROMSPITransfer, 0);
+ NDS.UnregisterEventFuncs(Event_ROMTransfer);
+ NDS.UnregisterEventFuncs(Event_ROMSPITransfer);
// Cart is cleaned up automatically because it's a unique_ptr
}
@@ -1552,10 +1569,10 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept
memcpy(out, &cartrom[arm9base], 0x800);
- Key1_InitKeycode(false, gamecode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
+ Key1_InitKeycode(false, gamecode, 2, 2);
Key1_Decrypt((u32*)&out[0]);
- Key1_InitKeycode(false, gamecode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
+ Key1_InitKeycode(false, gamecode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8)
Key1_Decrypt((u32*)&out[i]);
@@ -1714,11 +1731,11 @@ void NDSCartSlot::SetCart(std::unique_ptr&& cart) noexcept
strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8);
- Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
+ Key1_InitKeycode(false, romparams.GameCode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8)
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]);
- Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
+ Key1_InitKeycode(false, romparams.GameCode, 2, 2);
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]);
Log(LogLevel::Debug, "Re-encrypted cart secure area\n");
diff --git a/src/NDSCart.h b/src/NDSCart.h
index 3c4bb4f5..3704f659 100644
--- a/src/NDSCart.h
+++ b/src/NDSCart.h
@@ -445,8 +445,8 @@ private:
void Key1_Encrypt(u32* data) const noexcept;
void Key1_Decrypt(u32* data) const noexcept;
void Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept;
- void Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept;
- void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept;
+ void Key1_LoadKeyBuf(bool dsi) noexcept;
+ void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) noexcept;
void Key2_Encrypt(const u8* data, u32 len) noexcept;
void ROMEndTransfer(u32 param) noexcept;
void ROMPrepareData(u32 param) noexcept;
diff --git a/src/RTC.cpp b/src/RTC.cpp
index 24e00de7..c10672fb 100644
--- a/src/RTC.cpp
+++ b/src/RTC.cpp
@@ -34,7 +34,7 @@ void WriteDateTime(int num, u8 val);
RTC::RTC(melonDS::NDS& nds) : NDS(nds)
{
- NDS.RegisterEventFunc(Event_RTC, 0, MemberEventFunc(RTC, ClockTimer));
+ NDS.RegisterEventFuncs(Event_RTC, this, {MakeEventThunk(RTC, ClockTimer)});
ResetState();
@@ -45,7 +45,7 @@ RTC::RTC(melonDS::NDS& nds) : NDS(nds)
RTC::~RTC()
{
- NDS.UnregisterEventFunc(Event_RTC, 0);
+ NDS.UnregisterEventFuncs(Event_RTC);
}
void RTC::Reset()
diff --git a/src/SPI.cpp b/src/SPI.cpp
index 6ab94c3a..67b63e2a 100644
--- a/src/SPI.cpp
+++ b/src/SPI.cpp
@@ -474,7 +474,7 @@ void TSC::Write(u8 val)
SPIHost::SPIHost(melonDS::NDS& nds, Firmware&& firmware) : NDS(nds)
{
- NDS.RegisterEventFunc(Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone));
+ NDS.RegisterEventFuncs(Event_SPITransfer, this, {MakeEventThunk(SPIHost, TransferDone)});
Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS, std::move(firmware));
Devices[SPIDevice_PowerMan] = new PowerMan(NDS);
@@ -495,7 +495,7 @@ SPIHost::~SPIHost()
Devices[i] = nullptr;
}
- NDS.UnregisterEventFunc(Event_SPITransfer, 0);
+ NDS.UnregisterEventFuncs(Event_SPITransfer);
}
void SPIHost::Reset()
diff --git a/src/SPU.cpp b/src/SPU.cpp
index eb30c0a9..5b245890 100644
--- a/src/SPU.cpp
+++ b/src/SPU.cpp
@@ -202,7 +202,7 @@ SPU::SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpola
AudioLock(Platform::Mutex_Create()),
Degrade10Bit(bitdepth == AudioBitDepth::_10Bit || (nds.ConsoleType == 1 && bitdepth == AudioBitDepth::Auto))
{
- NDS.RegisterEventFunc(Event_SPU, 0, MemberEventFunc(SPU, Mix));
+ NDS.RegisterEventFuncs(Event_SPU, this, {MakeEventThunk(SPU, Mix)});
ApplyBias = true;
Degrade10Bit = false;
@@ -219,7 +219,7 @@ SPU::~SPU()
Platform::Mutex_Free(AudioLock);
AudioLock = nullptr;
- NDS.UnregisterEventFunc(Event_SPU, 0);
+ NDS.UnregisterEventFuncs(Event_SPU);
}
void SPU::Reset()
diff --git a/src/Wifi.cpp b/src/Wifi.cpp
index 3eaa9fd3..77933dfb 100644
--- a/src/Wifi.cpp
+++ b/src/Wifi.cpp
@@ -91,7 +91,7 @@ bool MACIsBroadcast(const u8* a)
Wifi::Wifi(melonDS::NDS& nds) : NDS(nds)
{
- NDS.RegisterEventFunc(Event_Wifi, 0, MemberEventFunc(Wifi, USTimer));
+ NDS.RegisterEventFuncs(Event_Wifi, this, {MakeEventThunk(Wifi, USTimer)});
WifiAP = new class WifiAP(this, NDS.UserData);
}
@@ -100,7 +100,7 @@ Wifi::~Wifi()
{
delete WifiAP; WifiAP = nullptr;
- NDS.UnregisterEventFunc(Event_Wifi, 0);
+ NDS.UnregisterEventFuncs(Event_Wifi);
}
void Wifi::Reset()
diff --git a/src/debug/GdbStub.cpp b/src/debug/GdbStub.cpp
index b055794a..7ce7693b 100644
--- a/src/debug/GdbStub.cpp
+++ b/src/debug/GdbStub.cpp
@@ -51,16 +51,17 @@ static int SocketSetBlocking(int fd, bool block)
namespace Gdb
{
-GdbStub::GdbStub(StubCallbacks* cb, int port)
- : Cb(cb), Port(port)
+GdbStub::GdbStub(StubCallbacks* cb)
+ : Cb(cb), Port(0)
, SockFd(0), ConnFd(0)
, Stat(TgtStatus::None), CurBkpt(0), CurWatchpt(0), StatFlag(false), NoAck(false)
, ServerSA((void*)new struct sockaddr_in())
, ClientSA((void*)new struct sockaddr_in())
{ }
-bool GdbStub::Init()
+bool GdbStub::Init(int port)
{
+ Port = port;
Log(LogLevel::Info, "[GDB] initializing GDB stub for core %d on port %d\n",
Cb->GetCPU(), Port);
diff --git a/src/debug/GdbStub.h b/src/debug/GdbStub.h
index 3e4a0efe..85934327 100644
--- a/src/debug/GdbStub.h
+++ b/src/debug/GdbStub.h
@@ -115,10 +115,10 @@ public:
int kind;
};
- GdbStub(StubCallbacks* cb, int port);
+ GdbStub(StubCallbacks* cb);
~GdbStub();
- bool Init();
+ bool Init(int port);
void Close();
StubState Poll(bool wait = false);
diff --git a/src/dolphin/x64Emitter.h b/src/dolphin/x64Emitter.h
index 36603218..d83a41f8 100644
--- a/src/dolphin/x64Emitter.h
+++ b/src/dolphin/x64Emitter.h
@@ -1019,6 +1019,28 @@ public:
CALL(ptr);
}
}
+ template
+ void ABI_TailCall(FunctionPointer func)
+ {
+ static_assert(std::is_pointer() &&
+ std::is_function>(),
+ "Supplied type must be a function pointer.");
+
+ const u8* ptr = reinterpret_cast(func);
+ const u64 address = reinterpret_cast(ptr);
+ const u64 distance = address - (reinterpret_cast(code) + 5);
+
+ if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL)
+ {
+ // Far call
+ MOV(64, R(RAX), Imm64(address));
+ JMPptr(R(RAX));
+ }
+ else
+ {
+ JMP(ptr, true);
+ }
+ }
template
void ABI_CallFunctionC16(FunctionPointer func, u16 param1)
diff --git a/src/frontend/ScreenLayout.h b/src/frontend/ScreenLayout.h
index 7b69bd40..a813127d 100644
--- a/src/frontend/ScreenLayout.h
+++ b/src/frontend/ScreenLayout.h
@@ -22,8 +22,8 @@
enum ScreenLayoutType
{
screenLayout_Natural, // top screen above bottom screen always
- screenLayout_Horizontal,
screenLayout_Vertical,
+ screenLayout_Horizontal,
screenLayout_Hybrid,
screenLayout_MAX,
};
diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt
index a14fb1c9..67947807 100644
--- a/src/frontend/qt_sdl/CMakeLists.txt
+++ b/src/frontend/qt_sdl/CMakeLists.txt
@@ -195,6 +195,7 @@ if (WIN32)
target_compile_definitions(melonDS PRIVATE WIN32_PORTABLE)
endif()
+ string(REPLACE . , MELON_RC_VERSION ${melonDS_VERSION})
configure_file("${CMAKE_SOURCE_DIR}/res/melon.rc.in" "${CMAKE_BINARY_DIR}/res/melon.rc")
target_sources(melonDS PUBLIC "${CMAKE_BINARY_DIR}/res/melon.rc")
target_include_directories(melonDS PRIVATE "${CMAKE_BINARY_DIR}/res")
diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp
index 02be5b65..73161809 100644
--- a/src/frontend/qt_sdl/Config.cpp
+++ b/src/frontend/qt_sdl/Config.cpp
@@ -632,7 +632,8 @@ void Table::SetString(const std::string& path, const std::string& val)
void Table::SetDouble(const std::string& path, double val)
{
toml::value& tval = ResolvePath(path);
- tval = val;
+ toml::floating_format_info info = {.prec=10};
+ tval = toml::value(val, info);
}
toml::value& Table::ResolvePath(const std::string& path)
@@ -816,7 +817,7 @@ Table GetLocalTable(int instance)
std::string key = "Instance" + std::to_string(instance);
toml::value& tbl = RootTable[key];
- if (tbl.is_uninitialized())
+ if (tbl.is_empty())
RootTable[key] = RootTable["Instance0"];
return Table(tbl, key);
diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h
index 9e6d3ea4..39278c36 100644
--- a/src/frontend/qt_sdl/Config.h
+++ b/src/frontend/qt_sdl/Config.h
@@ -25,7 +25,7 @@
#include
#include
-#include "toml/toml/value.hpp"
+#include "toml/toml11/types.hpp"
namespace Config
{
diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp
index 2ac3f42d..309abe66 100644
--- a/src/frontend/qt_sdl/EmuInstance.cpp
+++ b/src/frontend/qt_sdl/EmuInstance.cpp
@@ -29,7 +29,6 @@
#include
#include
-#include
#include
#ifdef ARCHIVE_SUPPORT_ENABLED
@@ -77,12 +76,16 @@ EmuInstance::EmuInstance(int inst) : deleting(false),
baseROMDir = "";
baseROMName = "";
baseAssetName = "";
+ nextCart = nullptr;
+ changeCart = false;
gbaSave = nullptr;
gbaCartType = -1;
baseGBAROMDir = "";
baseGBAROMName = "";
baseGBAAssetName = "";
+ nextGBACart = nullptr;
+ changeGBACart = false;
cheatFile = nullptr;
cheatsOn = localCfg.GetBool("EnableCheats");
@@ -118,7 +121,7 @@ EmuInstance::EmuInstance(int inst) : deleting(false),
mpAudioMode = globalCfg.GetInt("MP.AudioMode");
nds = nullptr;
- //updateConsole(nullptr, nullptr);
+ //updateConsole();
audioInit();
inputInit();
@@ -161,7 +164,6 @@ EmuInstance::~EmuInstance()
audioDeInit();
inputDeInit();
- NDS::Current = nullptr;
if (nds)
{
saveRTCData();
@@ -242,6 +244,8 @@ void EmuInstance::deleteWindow(int id, bool close)
if (close)
win->close();
+ if (deleting) return;
+
if (numWindows == 0)
{
// if we closed the last window, delete the instance
@@ -683,7 +687,7 @@ std::string EmuInstance::getSavestateName(int slot)
{
std::string ext = ".ml";
ext += (char)('0'+slot);
- return getAssetPath(false, globalCfg.GetString("SavestatePath"), ext);
+ return getAssetPath(false, localCfg.GetString("SavestatePath"), ext);
}
bool EmuInstance::savestateExists(int slot)
@@ -749,7 +753,7 @@ bool EmuInstance::loadState(const std::string& filename)
previousSaveFile = ndsSave->GetPath();
std::string savefile = filename.substr(lastSep(filename)+1);
- savefile = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav", savefile);
+ savefile = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav", savefile);
savefile += instanceFileSuffix();
ndsSave->SetPath(savefile, true);
}
@@ -800,7 +804,7 @@ bool EmuInstance::saveState(const std::string& filename)
if (globalCfg.GetBool("Savestate.RelocSRAM") && ndsSave)
{
std::string savefile = filename.substr(lastSep(filename)+1);
- savefile = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav", savefile);
+ savefile = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav", savefile);
savefile += instanceFileSuffix();
ndsSave->SetPath(savefile, false);
}
@@ -836,7 +840,7 @@ void EmuInstance::loadCheats()
{
unloadCheats();
- std::string filename = getAssetPath(false, globalCfg.GetString("CheatFilePath"), ".mch");
+ std::string filename = getAssetPath(false, localCfg.GetString("CheatFilePath"), ".mch");
// TODO: check for error (malformed cheat file, ...)
cheatFile = std::make_unique(filename);
@@ -855,7 +859,7 @@ std::unique_ptr EmuInstance::loadARM9BIOS() noexcept
{
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
{
- return globalCfg.GetInt("Emu.ConsoleType") == 0 ? std::make_unique(bios_arm9_bin) : nullptr;
+ return std::make_unique(bios_arm9_bin);
}
string path = globalCfg.GetString("DS.BIOS9Path");
@@ -878,7 +882,7 @@ std::unique_ptr EmuInstance::loadARM7BIOS() noexcept
{
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
{
- return globalCfg.GetInt("Emu.ConsoleType") == 0 ? std::make_unique(bios_arm7_bin) : nullptr;
+ return std::make_unique(bios_arm7_bin);
}
string path = globalCfg.GetString("DS.BIOS7Path");
@@ -1002,11 +1006,12 @@ std::optional EmuInstance::loadFirmware(int type) noexcept
{ // If we're using built-in firmware...
if (type == 1)
{
- Log(Error, "DSi firmware: cannot use built-in firmware in DSi mode!\n");
- return std::nullopt;
+ // TODO: support generating a firmware for DSi mode
+ }
+ else
+ {
+ return generateFirmware(type);
}
-
- return generateFirmware(type);
}
//const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath;
string firmwarepath;
@@ -1215,23 +1220,22 @@ void EmuInstance::setDateTime()
time.time().hour(), time.time().minute(), time.time().second());
}
-bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGBAArgs&& _gbaargs) noexcept
+bool EmuInstance::updateConsole() noexcept
{
// update the console type
consoleType = globalCfg.GetInt("Emu.ConsoleType");
// Let's get the cart we want to use;
- // if we wnat to keep the cart, we'll eject it from the existing console first.
+ // if we want to keep the cart, we'll eject it from the existing console first.
std::unique_ptr nextndscart;
- if (std::holds_alternative(_ndsargs))
+ if (!changeCart)
{ // If we want to keep the existing cart (if any)...
nextndscart = nds ? nds->EjectCart() : nullptr;
- _ndsargs = {};
}
- else if (const auto ptr = std::get_if>(&_ndsargs))
+ else
{
- nextndscart = std::move(*ptr);
- _ndsargs = {};
+ nextndscart = std::move(nextCart);
+ changeCart = false;
}
if (auto* cartsd = dynamic_cast(nextndscart.get()))
@@ -1242,14 +1246,14 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
}
std::unique_ptr nextgbacart;
- if (std::holds_alternative(_gbaargs))
+ if (!changeGBACart)
{
nextgbacart = nds ? nds->EjectGBACart() : nullptr;
}
- else if (const auto ptr = std::get_if>(&_gbaargs))
+ else
{
- nextgbacart = std::move(*ptr);
- _gbaargs = {};
+ nextgbacart = std::move(nextGBACart);
+ changeGBACart = false;
}
@@ -1292,8 +1296,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
#endif
NDSArgs ndsargs {
- std::move(nextndscart),
- std::move(nextgbacart),
std::move(arm9bios),
std::move(arm7bios),
std::move(*firmware),
@@ -1307,8 +1309,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
std::optional dsiargs = std::nullopt;
if (consoleType == 1)
{
- ndsargs.GBAROM = nullptr;
-
auto arm7ibios = loadDSiARM7BIOS();
if (!arm7ibios)
return false;
@@ -1339,7 +1339,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
renderLock.lock();
if ((!nds) || (consoleType != nds->ConsoleType))
{
- NDS::Current = nullptr;
if (nds)
{
saveRTCData();
@@ -1351,7 +1350,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
else
nds = new NDS(std::move(ndsargs), this);
- NDS::Current = nds;
nds->Reset();
loadRTCData();
//emuThread->updateVideoRenderer(); // not actually needed?
@@ -1361,10 +1359,8 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
nds->SetARM7BIOS(*args->ARM7BIOS);
nds->SetARM9BIOS(*args->ARM9BIOS);
nds->SetFirmware(std::move(args->Firmware));
- nds->SetNDSCart(std::move(args->NDSROM));
- nds->SetGBACart(std::move(args->GBAROM));
nds->SetJITArgs(args->JIT);
- // TODO GDB stub shit
+ nds->SetGdbArgs(args->GDB);
nds->SPU.SetInterpolation(args->Interpolation);
nds->SPU.SetDegrade10Bit(args->BitDepth);
@@ -1380,18 +1376,26 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
dsi->SetSDCard(std::move(_dsiargs.DSiSDCard));
// We're moving the optional, not the card
// (inserting std::nullopt here is okay, it means no card)
-
- dsi->EjectGBACart();
}
}
+
+ // loads the carts later -- to be sure that everything else is initialized
+ nds->SetNDSCart(std::move(nextndscart));
+ if (consoleType == 1)
+ nds->EjectGBACart();
+ else
+ nds->SetGBACart(std::move(nextgbacart));
+
renderLock.unlock();
+ loadCheats();
+
return true;
}
void EmuInstance::reset()
{
- updateConsole(Keep {}, Keep {});
+ updateConsole();
if (consoleType == 1) ejectGBACart();
@@ -1402,7 +1406,7 @@ void EmuInstance::reset()
if ((cartType != -1) && ndsSave)
{
std::string oldsave = ndsSave->GetPath();
- std::string newsave = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav");
+ std::string newsave = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav");
newsave += instanceFileSuffix();
if (oldsave != newsave)
ndsSave->SetPath(newsave, false);
@@ -1411,7 +1415,7 @@ void EmuInstance::reset()
if ((gbaCartType != -1) && gbaSave)
{
std::string oldsave = gbaSave->GetPath();
- std::string newsave = getAssetPath(true, globalCfg.GetString("SaveFilePath"), ".sav");
+ std::string newsave = getAssetPath(true, localCfg.GetString("SaveFilePath"), ".sav");
newsave += instanceFileSuffix();
if (oldsave != newsave)
gbaSave->SetPath(newsave, false);
@@ -1452,16 +1456,22 @@ void EmuInstance::reset()
}
-bool EmuInstance::bootToMenu()
+bool EmuInstance::bootToMenu(QString& errorstr)
{
// Keep whatever cart is in the console, if any.
- if (!updateConsole(Keep {}, Keep {}))
+ if (!updateConsole())
+ {
// Try to update the console, but keep the existing cart. If that fails...
+ errorstr = "Failed to boot the firmware.";
return false;
+ }
// BIOS and firmware files are loaded, patched, and installed in UpdateConsole
if (nds->NeedsDirectBoot())
+ {
+ errorstr = "This firmware is not bootable.";
return false;
+ }
initFirmwareSaveManager();
nds->Reset();
@@ -1838,7 +1848,7 @@ QString EmuInstance::getSavErrorString(std::string& filepath, bool gba)
return QString::fromStdString(err1);
}
-bool EmuInstance::loadROM(QStringList filepath, bool reset)
+bool EmuInstance::loadROM(QStringList filepath, bool reset, QString& errorstr)
{
unique_ptr filedata = nullptr;
u32 filelen;
@@ -1847,7 +1857,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
if (!loadROMData(filepath, filedata, filelen, basepath, romname))
{
- QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM.");
+ errorstr = "Failed to load the DS ROM.";
return false;
}
@@ -1860,7 +1870,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
u32 savelen = 0;
std::unique_ptr savedata = nullptr;
- std::string savname = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav");
+ std::string savname = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav");
std::string origsav = savname;
savname += instanceFileSuffix();
@@ -1869,7 +1879,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
{
if (!Platform::CheckFileWritable(origsav))
{
- QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(origsav, false));
+ errorstr = getSavErrorString(origsav, false);
return false;
}
@@ -1877,7 +1887,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
}
else if (!Platform::CheckFileWritable(savname))
{
- QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(savname, false));
+ errorstr = getSavErrorString(savname, false);
return false;
}
@@ -1904,15 +1914,18 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
if (!cart)
{
// If we couldn't parse the ROM...
- QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM.");
+ errorstr = "Failed to load the DS ROM.";
return false;
}
if (reset)
{
- if (!updateConsole(std::move(cart), Keep {}))
+ nextCart = std::move(cart);
+ changeCart = true;
+
+ if (!updateConsole())
{
- QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM.");
+ errorstr = "Failed to load the DS ROM.";
return false;
}
@@ -1929,13 +1942,20 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
}
else
{
- assert(nds != nullptr);
- nds->SetNDSCart(std::move(cart));
+ if (emuIsActive())
+ {
+ nds->SetNDSCart(std::move(cart));
+ loadCheats();
+ }
+ else
+ {
+ nextCart = std::move(cart);
+ changeCart = true;
+ }
}
cartType = 0;
ndsSave = std::make_unique(savname);
- loadCheats();
return true; // success
}
@@ -1944,9 +1964,16 @@ void EmuInstance::ejectCart()
{
ndsSave = nullptr;
- unloadCheats();
-
- nds->EjectCart();
+ if (emuIsActive())
+ {
+ nds->EjectCart();
+ unloadCheats();
+ }
+ else
+ {
+ nextCart = nullptr;
+ changeCart = true;
+ }
cartType = -1;
baseROMDir = "";
@@ -1974,11 +2001,11 @@ QString EmuInstance::cartLabel()
}
-bool EmuInstance::loadGBAROM(QStringList filepath)
+bool EmuInstance::loadGBAROM(QStringList filepath, QString& errorstr)
{
if (consoleType == 1)
{
- QMessageBox::critical(mainWindow, "melonDS", "The DSi doesn't have a GBA slot.");
+ errorstr = "The DSi doesn't have a GBA slot.";
return false;
}
@@ -1989,7 +2016,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
if (!loadROMData(filepath, filedata, filelen, basepath, romname))
{
- QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM.");
+ errorstr = "Failed to load the GBA ROM.";
return false;
}
@@ -2002,7 +2029,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
u32 savelen = 0;
std::unique_ptr savedata = nullptr;
- std::string savname = getAssetPath(true, globalCfg.GetString("SaveFilePath"), ".sav");
+ std::string savname = getAssetPath(true, localCfg.GetString("SaveFilePath"), ".sav");
std::string origsav = savname;
savname += instanceFileSuffix();
@@ -2011,7 +2038,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
{
if (!Platform::CheckFileWritable(origsav))
{
- QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(origsav, true));
+ errorstr = getSavErrorString(origsav, true);
return false;
}
@@ -2019,7 +2046,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
}
else if (!Platform::CheckFileWritable(savname))
{
- QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(savname, true));
+ errorstr = getSavErrorString(savname, true);
return false;
}
@@ -2039,24 +2066,47 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen, this);
if (!cart)
{
- QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM.");
+ errorstr = "Failed to load the GBA ROM.";
return false;
}
- nds->SetGBACart(std::move(cart));
gbaCartType = 0;
- gbaSave = std::make_unique(savname);
+ if (emuIsActive())
+ {
+ nds->SetGBACart(std::move(cart));
+ gbaSave = std::make_unique(savname);
+ }
+ else
+ {
+ nextGBACart = std::move(cart);
+ changeGBACart = true;
+ }
+
return true;
}
-void EmuInstance::loadGBAAddon(int type)
+void EmuInstance::loadGBAAddon(int type, QString& errorstr)
{
if (consoleType == 1) return;
+ auto cart = GBACart::LoadAddon(type, this);
+ if (!cart)
+ {
+ errorstr = "Failed to load the GBA addon.";
+ return;
+ }
+
+ if (emuIsActive())
+ {
+ nds->SetGBACart(std::move(cart));
+ }
+ else
+ {
+ nextGBACart = std::move(cart);
+ changeGBACart = true;
+ }
+
gbaSave = nullptr;
-
- nds->LoadGBAAddon(type);
-
gbaCartType = type;
baseGBAROMDir = "";
baseGBAROMName = "";
@@ -2067,7 +2117,15 @@ void EmuInstance::ejectGBACart()
{
gbaSave = nullptr;
- nds->EjectGBACart();
+ if (emuIsActive())
+ {
+ nds->EjectGBACart();
+ }
+ else
+ {
+ nextGBACart = nullptr;
+ changeGBACart = true;
+ }
gbaCartType = -1;
baseGBAROMDir = "";
diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h
index a8c718af..cec62394 100644
--- a/src/frontend/qt_sdl/EmuInstance.h
+++ b/src/frontend/qt_sdl/EmuInstance.h
@@ -120,7 +120,7 @@ public:
// return: empty string = setup OK, non-empty = error message
QString verifySetup();
- bool updateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept;
+ bool updateConsole() noexcept;
void enableCheats(bool enable);
melonDS::ARCodeFile* getCheatFile();
@@ -184,20 +184,22 @@ private:
std::optional loadSDCard(const std::string& key) noexcept;
void setBatteryLevels();
void reset();
- bool bootToMenu();
+ bool bootToMenu(QString& errorstr);
melonDS::u32 decompressROM(const melonDS::u8* inContent, const melonDS::u32 inSize, std::unique_ptr& outContent);
void clearBackupState();
std::pair, std::string> generateDefaultFirmware();
bool parseMacAddress(void* data);
void customizeFirmware(melonDS::Firmware& firmware, bool overridesettings) noexcept;
+
bool loadROMData(const QStringList& filepath, std::unique_ptr& filedata, melonDS::u32& filelen, std::string& basepath, std::string& romname) noexcept;
QString getSavErrorString(std::string& filepath, bool gba);
- bool loadROM(QStringList filepath, bool reset);
+ bool loadROM(QStringList filepath, bool reset, QString& errorstr);
void ejectCart();
bool cartInserted();
QString cartLabel();
- bool loadGBAROM(QStringList filepath);
- void loadGBAAddon(int type);
+
+ bool loadGBAROM(QStringList filepath, QString& errorstr);
+ void loadGBAAddon(int type, QString& errorstr);
void ejectGBACart();
bool gbaCartInserted();
QString gbaAddonName(int addon);
@@ -261,11 +263,15 @@ private:
std::string baseROMDir;
std::string baseROMName;
std::string baseAssetName;
+ bool changeCart;
+ std::unique_ptr nextCart;
int gbaCartType;
std::string baseGBAROMDir;
std::string baseGBAROMName;
std::string baseGBAAssetName;
+ bool changeGBACart;
+ std::unique_ptr nextGBACart;
// HACK
public:
diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp
index 3b222ba0..a4ac9394 100644
--- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp
+++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp
@@ -159,6 +159,8 @@ void EmuInstance::audioMute()
void EmuInstance::micOpen()
{
+ if (micDevice) return;
+
if (micInputType != micInputType_External)
{
micDevice = 0;
@@ -328,7 +330,11 @@ void EmuInstance::micProcess()
micBufferReadPos += len;
}
- if (len < kFrameLen)
+ if (len == 0)
+ {
+ memset(tmp, 0, sizeof(tmp));
+ }
+ else if (len < kFrameLen)
{
for (int i = len; i < kFrameLen; i++)
tmp[i] = tmp[len-1];
diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp
index b37f7118..30b92fd1 100644
--- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp
@@ -82,9 +82,6 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
ui->chkJITBranchOptimisations->setChecked(cfg.GetBool("JIT.BranchOptimisations"));
ui->chkJITLiteralOptimisations->setChecked(cfg.GetBool("JIT.LiteralOptimisations"));
ui->chkJITFastMemory->setChecked(cfg.GetBool("JIT.FastMemory"));
- #ifdef __APPLE__
- ui->chkJITFastMemory->setDisabled(true);
- #endif
ui->spnJITMaximumBlockSize->setValue(cfg.GetInt("JIT.MaxBlockSize"));
#else
ui->chkEnableJIT->setDisabled(true);
@@ -539,11 +536,14 @@ void EmuSettingsDialog::on_btnDSiSDFolderBrowse_clicked()
void EmuSettingsDialog::on_chkEnableJIT_toggled()
{
bool disabled = !ui->chkEnableJIT->isChecked();
+#ifdef JIT_ENABLED
+ bool fastmemSupported = ARMJIT_Memory::IsFastMemSupported();
+#else
+ bool fastmemSupported = false;
+#endif
ui->chkJITBranchOptimisations->setDisabled(disabled);
ui->chkJITLiteralOptimisations->setDisabled(disabled);
- #ifndef __APPLE__
- ui->chkJITFastMemory->setDisabled(disabled);
- #endif
+ ui->chkJITFastMemory->setDisabled(disabled || !fastmemSupported);
ui->spnJITMaximumBlockSize->setDisabled(disabled);
on_cbGdbEnabled_toggled();
diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp
index 07a014c0..825edf40 100644
--- a/src/frontend/qt_sdl/EmuThread.cpp
+++ b/src/frontend/qt_sdl/EmuThread.cpp
@@ -109,7 +109,7 @@ void EmuThread::run()
Config::Table& globalCfg = emuInstance->getGlobalConfig();
u32 mainScreenPos[3];
- //emuInstance->updateConsole(nullptr, nullptr);
+ //emuInstance->updateConsole();
// No carts are inserted when melonDS first boots
mainScreenPos[0] = 0;
@@ -580,7 +580,7 @@ void EmuThread::handleMessages()
case msg_BootROM:
msgResult = 0;
- if (!emuInstance->loadROM(msg.param.value(), true))
+ if (!emuInstance->loadROM(msg.param.value(), true, msgError))
break;
assert(emuInstance->nds != nullptr);
@@ -590,7 +590,7 @@ void EmuThread::handleMessages()
case msg_BootFirmware:
msgResult = 0;
- if (!emuInstance->bootToMenu())
+ if (!emuInstance->bootToMenu(msgError))
break;
assert(emuInstance->nds != nullptr);
@@ -600,7 +600,7 @@ void EmuThread::handleMessages()
case msg_InsertCart:
msgResult = 0;
- if (!emuInstance->loadROM(msg.param.value(), false))
+ if (!emuInstance->loadROM(msg.param.value(), false, msgError))
break;
msgResult = 1;
@@ -612,7 +612,7 @@ void EmuThread::handleMessages()
case msg_InsertGBACart:
msgResult = 0;
- if (!emuInstance->loadGBAROM(msg.param.value()))
+ if (!emuInstance->loadGBAROM(msg.param.value(), msgError))
break;
msgResult = 1;
@@ -620,7 +620,7 @@ void EmuThread::handleMessages()
case msg_InsertGBAAddon:
msgResult = 0;
- emuInstance->loadGBAAddon(msg.param.value());
+ emuInstance->loadGBAAddon(msg.param.value(), msgError);
msgResult = 1;
break;
@@ -756,36 +756,45 @@ bool EmuThread::emuIsActive()
return emuActive;
}
-int EmuThread::bootROM(const QStringList& filename)
+int EmuThread::bootROM(const QStringList& filename, QString& errorstr)
{
sendMessage({.type = msg_BootROM, .param = filename});
waitMessage();
if (!msgResult)
+ {
+ errorstr = msgError;
return msgResult;
+ }
sendMessage(msg_EmuRun);
waitMessage();
+ errorstr = "";
return msgResult;
}
-int EmuThread::bootFirmware()
+int EmuThread::bootFirmware(QString& errorstr)
{
sendMessage(msg_BootFirmware);
waitMessage();
if (!msgResult)
+ {
+ errorstr = msgError;
return msgResult;
+ }
sendMessage(msg_EmuRun);
waitMessage();
+ errorstr = "";
return msgResult;
}
-int EmuThread::insertCart(const QStringList& filename, bool gba)
+int EmuThread::insertCart(const QStringList& filename, bool gba, QString& errorstr)
{
MessageType msgtype = gba ? msg_InsertGBACart : msg_InsertCart;
sendMessage({.type = msgtype, .param = filename});
waitMessage();
+ errorstr = msgResult ? "" : msgError;
return msgResult;
}
@@ -795,10 +804,11 @@ void EmuThread::ejectCart(bool gba)
waitMessage();
}
-int EmuThread::insertGBAAddon(int type)
+int EmuThread::insertGBAAddon(int type, QString& errorstr)
{
sendMessage({.type = msg_InsertGBAAddon, .param = type});
waitMessage();
+ errorstr = msgResult ? "" : msgError;
return msgResult;
}
diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h
index d611bc35..c8a04d8c 100644
--- a/src/frontend/qt_sdl/EmuThread.h
+++ b/src/frontend/qt_sdl/EmuThread.h
@@ -33,9 +33,6 @@
#include "NDSCart.h"
#include "GBACart.h"
-using Keep = std::monostate;
-using UpdateConsoleNDSArgs = std::variant>;
-using UpdateConsoleGBAArgs = std::variant>;
namespace melonDS
{
class NDS;
@@ -114,11 +111,11 @@ public:
void emuFrameStep();
void emuReset();
- int bootROM(const QStringList& filename);
- int bootFirmware();
- int insertCart(const QStringList& filename, bool gba);
+ int bootROM(const QStringList& filename, QString& errorstr);
+ int bootFirmware(QString& errorstr);
+ int insertCart(const QStringList& filename, bool gba, QString& errorstr);
void ejectCart(bool gba);
- int insertGBAAddon(int type);
+ int insertGBAAddon(int type, QString& errorstr);
int saveState(const QString& filename);
int loadState(const QString& filename);
@@ -184,6 +181,7 @@ private:
int emuPauseStack;
int msgResult = 0;
+ QString msgError;
QMutex msgMutex;
QSemaphore msgSemaphore;
diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp
index bd02405e..a2c1bd71 100644
--- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp
@@ -35,9 +35,9 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare
auto& cfg = emuInstance->getGlobalConfig();
- ui->cbMouseHide->setChecked(cfg.GetBool("MouseHide"));
+ ui->cbMouseHide->setChecked(cfg.GetBool("Mouse.Hide"));
ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked());
- ui->spinMouseHideSeconds->setValue(cfg.GetInt("MouseHideSeconds"));
+ ui->spinMouseHideSeconds->setValue(cfg.GetInt("Mouse.HideSeconds"));
ui->cbPauseLostFocus->setChecked(cfg.GetBool("PauseLostFocus"));
ui->spinTargetFPS->setValue(cfg.GetDouble("TargetFPS"));
ui->spinFFW->setValue(cfg.GetDouble("FastForwardFPS"));
@@ -115,8 +115,8 @@ void InterfaceSettingsDialog::done(int r)
{
auto& cfg = emuInstance->getGlobalConfig();
- cfg.SetBool("MouseHide", ui->cbMouseHide->isChecked());
- cfg.SetInt("MouseHideSeconds", ui->spinMouseHideSeconds->value());
+ cfg.SetBool("Mouse.Hide", ui->cbMouseHide->isChecked());
+ cfg.SetInt("Mouse.HideSeconds", ui->spinMouseHideSeconds->value());
cfg.SetBool("PauseLostFocus", ui->cbPauseLostFocus->isChecked());
double val = ui->spinTargetFPS->value();
diff --git a/src/frontend/qt_sdl/LANStartClientDialog.ui b/src/frontend/qt_sdl/LANStartClientDialog.ui
index eeea25e9..e32714f2 100644
--- a/src/frontend/qt_sdl/LANStartClientDialog.ui
+++ b/src/frontend/qt_sdl/LANStartClientDialog.ui
@@ -7,7 +7,7 @@
0
0
547
- 409
+ 407
@@ -48,7 +48,7 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
@@ -63,17 +63,24 @@
-
- QAbstractItemView::NoEditTriggers
+ QAbstractItemView::EditTrigger::NoEditTriggers
+
+
+
+ -
+
+
+ <html><head/><body><p>Warning: LAN requires low network latency to work.</p><p>Do not expect it to work through a VPN or any sort of tunnel.</p></body></html>
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+ QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok
diff --git a/src/frontend/qt_sdl/LANStartHostDialog.ui b/src/frontend/qt_sdl/LANStartHostDialog.ui
index 0d6cd50c..d9adc3a1 100644
--- a/src/frontend/qt_sdl/LANStartHostDialog.ui
+++ b/src/frontend/qt_sdl/LANStartHostDialog.ui
@@ -7,7 +7,7 @@
0
0
389
- 228
+ 202
@@ -21,7 +21,7 @@
- QLayout::SetFixedSize
+ QLayout::SizeConstraint::SetFixedSize
-
@@ -45,15 +45,29 @@
-
+ -
+
+
+ <html><head/><body><p>Warning: LAN requires low network latency to work.</p><p>Do not expect it to work through a VPN or any sort of tunnel.</p></body></html>
+
+
+
+ -
+
+
+
+
+
+
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+ QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok
diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp
index f3a453d1..d0b42b21 100644
--- a/src/frontend/qt_sdl/PathSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp
@@ -45,7 +45,7 @@ PathSettingsDialog::PathSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne
emuInstance = ((MainWindow*)parent)->getEmuInstance();
- auto& cfg = emuInstance->getGlobalConfig();
+ auto& cfg = emuInstance->getLocalConfig();
ui->txtSaveFilePath->setText(cfg.GetQString("SaveFilePath"));
ui->txtSavestatePath->setText(cfg.GetQString("SavestatePath"));
ui->txtCheatFilePath->setText(cfg.GetQString("CheatFilePath"));
@@ -108,7 +108,7 @@ void PathSettingsDialog::done(int r)
QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok)
return;
- auto& cfg = emuInstance->getGlobalConfig();
+ auto& cfg = emuInstance->getLocalConfig();
cfg.SetQString("SaveFilePath", ui->txtSaveFilePath->text());
cfg.SetQString("SavestatePath", ui->txtSavestatePath->text());
cfg.SetQString("CheatFilePath", ui->txtCheatFilePath->text());
diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp
index 7aa74fca..866f2d35 100644
--- a/src/frontend/qt_sdl/Window.cpp
+++ b/src/frontend/qt_sdl/Window.cpp
@@ -826,6 +826,8 @@ void MainWindow::saveEnabled(bool enabled)
void MainWindow::closeEvent(QCloseEvent* event)
{
+ if (!emuInstance) return;
+
if (windowID == 0)
emuInstance->saveEnabledWindows();
else
@@ -862,7 +864,7 @@ void MainWindow::createScreenPanel()
// Check that creating the context hasn't failed
if (panelGL->createContext() == false)
{
- Log(LogLevel::Error, "Failed to create OpenGL context, falling back to Software Renderer.\n");
+ Log(Platform::LogLevel::Error, "Failed to create OpenGL context, falling back to Software Renderer.\n");
hasOGL = false;
globalCfg.SetBool("Screen.UseGL", false);
@@ -991,10 +993,12 @@ void MainWindow::dropEvent(QDropEvent* event)
isNdsRom |= ZstdNdsRomByExtension(filename);
isGbaRom |= ZstdGbaRomByExtension(filename);
+ QString errorstr;
if (isNdsRom)
{
- if (!emuThread->bootROM(file))
+ if (!emuThread->bootROM(file, errorstr))
{
+ QMessageBox::critical(this, "melonDS", errorstr);
return;
}
@@ -1007,8 +1011,9 @@ void MainWindow::dropEvent(QDropEvent* event)
}
else if (isGbaRom)
{
- if (!emuThread->insertCart(file, true))
+ if (!emuThread->insertCart(file, true, errorstr))
{
+ QMessageBox::critical(this, "melonDS", errorstr);
return;
}
@@ -1076,6 +1081,8 @@ bool MainWindow::verifySetup()
bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
{
+ QString errorstr;
+
if (file.isEmpty() && gbafile.isEmpty())
return false;
@@ -1087,8 +1094,11 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
bool gbaloaded = false;
if (!gbafile.isEmpty())
{
- if (!emuThread->insertCart(gbafile, true))
+ if (!emuThread->insertCart(gbafile, true, errorstr))
+ {
+ QMessageBox::critical(this, "melonDS", errorstr);
return false;
+ }
gbaloaded = true;
}
@@ -1098,13 +1108,19 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
{
if (boot)
{
- if (!emuThread->bootROM(file))
+ if (!emuThread->bootROM(file, errorstr))
+ {
+ QMessageBox::critical(this, "melonDS", errorstr);
return false;
+ }
}
else
{
- if (!emuThread->insertCart(file, false))
+ if (!emuThread->insertCart(file, false, errorstr))
+ {
+ QMessageBox::critical(this, "melonDS", errorstr);
return false;
+ }
}
recentFileList.removeAll(file.join("|"));
@@ -1114,8 +1130,11 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
}
else if (boot)
{
- if (!emuThread->bootFirmware())
+ if (!emuThread->bootFirmware(errorstr))
+ {
+ QMessageBox::critical(this, "melonDS", errorstr);
return false;
+ }
}
updateCartInserted(false);
@@ -1313,8 +1332,10 @@ void MainWindow::onOpenFile()
if (file.isEmpty())
return;
- if (!emuThread->bootROM(file))
+ QString errorstr;
+ if (!emuThread->bootROM(file, errorstr))
{
+ QMessageBox::critical(this, "melonDS", errorstr);
return;
}
@@ -1424,8 +1445,10 @@ void MainWindow::onClickRecentFile()
if (file.isEmpty())
return;
- if (!emuThread->bootROM(file))
+ QString errorstr;
+ if (!emuThread->bootROM(file, errorstr))
{
+ QMessageBox::critical(this, "melonDS", errorstr);
return;
}
@@ -1441,9 +1464,10 @@ void MainWindow::onBootFirmware()
if (!verifySetup())
return;
- if (!emuThread->bootFirmware())
+ QString errorstr;
+ if (!emuThread->bootFirmware(errorstr))
{
- QMessageBox::critical(this, "melonDS", "This firmware is not bootable.");
+ QMessageBox::critical(this, "melonDS", errorstr);
return;
}
}
@@ -1454,8 +1478,10 @@ void MainWindow::onInsertCart()
if (file.isEmpty())
return;
- if (!emuThread->insertCart(file, false))
+ QString errorstr;
+ if (!emuThread->insertCart(file, false, errorstr))
{
+ QMessageBox::critical(this, "melonDS", errorstr);
return;
}
@@ -1474,8 +1500,10 @@ void MainWindow::onInsertGBACart()
if (file.isEmpty())
return;
- if (!emuThread->insertCart(file, true))
+ QString errorstr;
+ if (!emuThread->insertCart(file, true, errorstr))
{
+ QMessageBox::critical(this, "melonDS", errorstr);
return;
}
@@ -1487,7 +1515,13 @@ void MainWindow::onInsertGBAAddon()
QAction* act = (QAction*)sender();
int type = act->data().toInt();
- emuThread->insertGBAAddon(type);
+ QString errorstr;
+ if (!emuThread->insertGBAAddon(type, errorstr))
+ {
+ QMessageBox::critical(this, "melonDS", errorstr);
+ return;
+ }
+
updateCartInserted(true);
}
@@ -2003,8 +2037,8 @@ void MainWindow::onUpdateInterfaceSettings()
emuInstance->targetFPS = globalCfg.GetDouble("TargetFPS");
emuInstance->fastForwardFPS = globalCfg.GetDouble("FastForwardFPS");
emuInstance->slowmoFPS = globalCfg.GetDouble("SlowmoFPS");
- panel->setMouseHide(globalCfg.GetBool("MouseHide"),
- globalCfg.GetInt("MouseHideSeconds")*1000);
+ panel->setMouseHide(globalCfg.GetBool("Mouse.Hide"),
+ globalCfg.GetInt("Mouse.HideSeconds")*1000);
}
void MainWindow::onInterfaceSettingsFinished(int res)
diff --git a/src/frontend/qt_sdl/toml/toml.hpp b/src/frontend/qt_sdl/toml/toml.hpp
index 6b0ca1ed..75cc95be 100644
--- a/src/frontend/qt_sdl/toml/toml.hpp
+++ b/src/frontend/qt_sdl/toml/toml.hpp
@@ -1,38 +1,62 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2017 Toru Niina
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
+#ifndef TOML11_TOML_HPP
+#define TOML11_TOML_HPP
-#ifndef TOML_FOR_MODERN_CPP
-#define TOML_FOR_MODERN_CPP
+// The MIT License (MIT)
+//
+// Copyright (c) 2017-now Toru Niina
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
-#define TOML11_VERSION_MAJOR 3
-#define TOML11_VERSION_MINOR 7
-#define TOML11_VERSION_PATCH 1
+// IWYU pragma: begin_exports
+#include "toml11/color.hpp"
+#include "toml11/comments.hpp"
+#include "toml11/compat.hpp"
+#include "toml11/context.hpp"
+#include "toml11/conversion.hpp"
+#include "toml11/datetime.hpp"
+#include "toml11/error_info.hpp"
+#include "toml11/exception.hpp"
+#include "toml11/find.hpp"
+#include "toml11/format.hpp"
+#include "toml11/from.hpp"
+#include "toml11/get.hpp"
+#include "toml11/into.hpp"
+#include "toml11/literal.hpp"
+#include "toml11/location.hpp"
+#include "toml11/ordered_map.hpp"
+#include "toml11/parser.hpp"
+#include "toml11/region.hpp"
+#include "toml11/result.hpp"
+#include "toml11/scanner.hpp"
+#include "toml11/serializer.hpp"
+#include "toml11/skip.hpp"
+#include "toml11/source_location.hpp"
+#include "toml11/spec.hpp"
+#include "toml11/storage.hpp"
+#include "toml11/syntax.hpp"
+#include "toml11/traits.hpp"
+#include "toml11/types.hpp"
+#include "toml11/utility.hpp"
+#include "toml11/value.hpp"
+#include "toml11/value_t.hpp"
+#include "toml11/version.hpp"
+#include "toml11/visit.hpp"
+// IWYU pragma: end_exports
-#include "toml/parser.hpp"
-#include "toml/literal.hpp"
-#include "toml/serializer.hpp"
-#include "toml/get.hpp"
-#include "toml/macros.hpp"
-
-#endif// TOML_FOR_MODERN_CPP
+#endif// TOML11_TOML_HPP
diff --git a/src/frontend/qt_sdl/toml/toml/color.hpp b/src/frontend/qt_sdl/toml/toml/color.hpp
deleted file mode 100644
index 4cb572cb..00000000
--- a/src/frontend/qt_sdl/toml/toml/color.hpp
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef TOML11_COLOR_HPP
-#define TOML11_COLOR_HPP
-#include
-#include
-
-#ifdef TOML11_COLORIZE_ERROR_MESSAGE
-#define TOML11_ERROR_MESSAGE_COLORIZED true
-#else
-#define TOML11_ERROR_MESSAGE_COLORIZED false
-#endif
-
-namespace toml
-{
-
-// put ANSI escape sequence to ostream
-namespace color_ansi
-{
-namespace detail
-{
-inline int colorize_index()
-{
- static const int index = std::ios_base::xalloc();
- return index;
-}
-} // detail
-
-inline std::ostream& colorize(std::ostream& os)
-{
- // by default, it is zero.
- os.iword(detail::colorize_index()) = 1;
- return os;
-}
-inline std::ostream& nocolorize(std::ostream& os)
-{
- os.iword(detail::colorize_index()) = 0;
- return os;
-}
-inline std::ostream& reset (std::ostream& os)
-{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;}
-inline std::ostream& bold (std::ostream& os)
-{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;}
-inline std::ostream& grey (std::ostream& os)
-{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;}
-inline std::ostream& red (std::ostream& os)
-{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;}
-inline std::ostream& green (std::ostream& os)
-{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;}
-inline std::ostream& yellow (std::ostream& os)
-{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;}
-inline std::ostream& blue (std::ostream& os)
-{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;}
-inline std::ostream& magenta(std::ostream& os)
-{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;}
-inline std::ostream& cyan (std::ostream& os)
-{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;}
-inline std::ostream& white (std::ostream& os)
-{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;}
-} // color_ansi
-
-// ANSI escape sequence is the only and default colorization method currently
-namespace color = color_ansi;
-
-} // toml
-#endif// TOML11_COLOR_HPP
diff --git a/src/frontend/qt_sdl/toml/toml/combinator.hpp b/src/frontend/qt_sdl/toml/toml/combinator.hpp
deleted file mode 100644
index 33ecca1e..00000000
--- a/src/frontend/qt_sdl/toml/toml/combinator.hpp
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright Toru Niina 2017.
-// Distributed under the MIT License.
-#ifndef TOML11_COMBINATOR_HPP
-#define TOML11_COMBINATOR_HPP
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-#include "region.hpp"
-#include "result.hpp"
-#include "traits.hpp"
-#include "utility.hpp"
-
-// they scans characters and returns region if it matches to the condition.
-// when they fail, it does not change the location.
-// in lexer.hpp, these are used.
-
-namespace toml
-{
-namespace detail
-{
-
-// to output character as an error message.
-inline std::string show_char(const char c)
-{
- // It suppresses an error that occurs only in Debug mode of MSVC++ on Windows.
- // I'm not completely sure but they check the value of char to be in the
- // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes
- // has negative value (if char has sign). So here it re-interprets c as
- // unsigned char through pointer. In general, converting pointer to a
- // pointer that has different type cause UB, but `(signed|unsigned)?char`
- // are one of the exceptions. Converting pointer only to char and std::byte
- // (c++17) are valid.
- if(std::isgraph(*reinterpret_cast(std::addressof(c))))
- {
- return std::string(1, c);
- }
- else
- {
- std::array buf;
- buf.fill('\0');
- const auto r = std::snprintf(
- buf.data(), buf.size(), "0x%02x", static_cast(c) & 0xFF);
- (void) r; // Unused variable warning
- assert(r == static_cast(buf.size()) - 1);
- return std::string(buf.data());
- }
-}
-
-template
-struct character
-{
- static constexpr char target = C;
-
- static result
- invoke(location& loc)
- {
- if(loc.iter() == loc.end()) {return none();}
- const auto first = loc.iter();
-
- const char c = *(loc.iter());
- if(c != target)
- {
- return none();
- }
- loc.advance(); // update location
-
- return ok(region(loc, first, loc.iter()));
- }
-};
-template
-constexpr char character::target;
-
-// closed interval [Low, Up]. both Low and Up are included.
-template
-struct in_range
-{
- // assuming ascii part of UTF-8...
- static_assert(Low <= Up, "lower bound should be less than upper bound.");
-
- static constexpr char upper = Up;
- static constexpr char lower = Low;
-
- static result
- invoke(location& loc)
- {
- if(loc.iter() == loc.end()) {return none();}
- const auto first = loc.iter();
-
- const char c = *(loc.iter());
- if(c < lower || upper < c)
- {
- return none();
- }
-
- loc.advance();
- return ok(region(loc, first, loc.iter()));
- }
-};
-template constexpr char in_range::upper;
-template constexpr char in_range::lower;
-
-// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char.
-// for detecting invalid characters, like control sequences in toml string.
-template
-struct exclude
-{
- static result
- invoke(location& loc)
- {
- if(loc.iter() == loc.end()) {return none();}
- auto first = loc.iter();
-
- auto rslt = Combinator::invoke(loc);
- if(rslt.is_ok())
- {
- loc.reset(first);
- return none();
- }
- loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
- return ok(region(loc, first, loc.iter()));
- }
-};
-
-// increment `iter`, if matches. otherwise, just return empty string.
-template
-struct maybe
-{
- static result
- invoke(location& loc)
- {
- const auto rslt = Combinator::invoke(loc);
- if(rslt.is_ok())
- {
- return rslt;
- }
- return ok(region(loc));
- }
-};
-
-template
-struct sequence;
-
-template
-struct sequence
-{
- static result
- invoke(location& loc)
- {
- const auto first = loc.iter();
- auto rslt = Head::invoke(loc);
- if(rslt.is_err())
- {
- loc.reset(first);
- return none();
- }
- return sequence::invoke(loc, std::move(rslt.unwrap()), first);
- }
-
- // called from the above function only, recursively.
- template
- static result
- invoke(location& loc, region reg, Iterator first)
- {
- const auto rslt = Head::invoke(loc);
- if(rslt.is_err())
- {
- loc.reset(first);
- return none();
- }
- reg += rslt.unwrap(); // concat regions
- return sequence::invoke(loc, std::move(reg), first);
- }
-};
-
-template
-struct sequence
-{
- // would be called from sequence::invoke only.
- template
- static result
- invoke(location& loc, region reg, Iterator first)
- {
- const auto rslt = Head::invoke(loc);
- if(rslt.is_err())
- {
- loc.reset(first);
- return none();
- }
- reg += rslt.unwrap(); // concat regions
- return ok(reg);
- }
-};
-
-template