Merge remote-tracking branch 'upstream/master' into line-check-correction

This commit is contained in:
Jaklyy 2023-12-26 13:47:29 -05:00
commit 546e8c4ad8
110 changed files with 9734 additions and 8071 deletions

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.16)
cmake_policy(VERSION 3.15)
if (POLICY CMP0076)
@ -9,6 +9,11 @@ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
option(USE_VCPKG "Use vcpkg for dependency packages" OFF)
if (USE_VCPKG)
include(ConfigureVcpkg)
endif()
project(melonDS
VERSION 0.9.5
DESCRIPTION "DS emulator, sorta"
@ -73,8 +78,11 @@ if (ENABLE_LTO)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og")
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og")
endif()
string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")

View File

@ -0,0 +1,90 @@
include(FetchContent)
set(_DEFAULT_VCPKG_ROOT "${CMAKE_SOURCE_DIR}/vcpkg")
set(VCPKG_ROOT "${_DEFAULT_VCPKG_ROOT}" CACHE STRING "The path to the vcpkg repository")
if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}")
FetchContent_Declare(vcpkg
GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
GIT_TAG 2023.12.12
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
FetchContent_MakeAvailable(vcpkg)
endif()
set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets")
option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON)
if (CMAKE_OSX_ARCHITECTURES MATCHES ";")
message(FATAL_ERROR "macOS universal builds are not supported. Build them individually and combine afterwards instead.")
endif()
if (USE_RECOMMENDED_TRIPLETS)
execute_process(
COMMAND uname -m
OUTPUT_VARIABLE _HOST_PROCESSOR
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(_CAN_TARGET_AS_HOST OFF)
if (APPLE)
if (NOT CMAKE_OSX_ARCHITECTURES)
if (_HOST_PROCESSOR STREQUAL arm64)
set(CMAKE_OSX_ARCHITECTURES arm64)
else()
set(CMAKE_OSX_ARCHITECTURES x86_64)
endif()
endif()
if (CMAKE_OSX_ARCHITECTURES STREQUAL arm64)
set(_WANTED_TRIPLET arm64-osx-11-release)
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
else()
set(_WANTED_TRIPLET x64-osx-1015-release)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
endif()
elseif(WIN32)
# TODO Windows arm64 if possible
set(_CAN_TARGET_AS_HOST ON)
set(_WANTED_TRIPLET x64-mingw-static)
endif()
# Don't override it if the user set something else
if (NOT VCPKG_TARGET_TRIPLET)
set(VCPKG_TARGET_TRIPLET "${_WANTED_TRIPLET}")
else()
set(_WANTED_TRIPLET "${VCPKG_TARGET_TRIPLET}")
endif()
if (APPLE)
if (_HOST_PROCESSOR MATCHES arm64)
if (_WANTED_TRIPLET MATCHES "^arm64-osx-")
set(_CAN_TARGET_AS_HOST ON)
elseif (_WANTED_TRIPLET STREQUAL "x64-osx-1015-release")
# Use the default triplet for when building for arm64
# because we're probably making a universal build
set(VCPKG_HOST_TRIPLET arm64-osx-11-release)
endif()
else()
if (_WANTED_TRIPLET MATCHES "^x64-osx-")
set(_CAN_TARGET_AS_HOST ON)
elseif (_WANTED_TRIPLET STREQUAL "arm64-osx-11-release")
set(VCPKG_HOST_TRIPLET x64-osx-1015-release)
endif()
endif()
endif()
# If host and target triplet differ, vcpkg seems to always assume that the host can't run the target's binaries.
# In cases like cross compiling from ARM -> Intel macOS, or target being an older version of the host OS, we *can* do that so the packages built targeting the host are redundant.
if (_CAN_TARGET_AS_HOST AND NOT VCPKG_HOST_TRIPLET)
option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" ON)
else()
option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" OFF)
endif()
if (VCPKG_TARGET_AS_HOST)
set(VCPKG_HOST_TRIPLET "${VCPKG_TARGET_TRIPLET}" CACHE STRING "Host triplet to use for vcpkg")
endif()
endif()
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")

View File

@ -0,0 +1,12 @@
set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
set(VCPKG_CMAKE_SYSTEM_VERSION 11.0)
set(VCPKG_OSX_ARCHITECTURES arm64)
set(VCPKG_BUILD_TYPE release)
set(VCPKG_OSX_DEPLOYMENT_TARGET 11.0)
set(VCPKG_C_FLAGS -mmacosx-version-min=11.0)
set(VCPKG_CXX_FLAGS -mmacosx-version-min=11.0)

View File

@ -0,0 +1,12 @@
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
set(VCPKG_CMAKE_SYSTEM_VERSION 10.15)
set(VCPKG_OSX_ARCHITECTURES x86_64)
set(VCPKG_BUILD_TYPE release)
set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15)
set(VCPKG_C_FLAGS -mmacosx-version-min=10.15)
set(VCPKG_CXX_FLAGS -mmacosx-version-min=10.15)

View File

@ -517,6 +517,9 @@ swi_get_crc16:
mov const_0x1E, #0x1E
adr crc_table_ptr, crc_table
bic crc_value, crc_value, #0xFF000000
bic crc_value, crc_value, #0x00FF0000
movs crc_length, crc_length, lsr #1
beq 1f

Binary file not shown.

Binary file not shown.

View File

@ -40,16 +40,16 @@ AREngine::AREngine(melonDS::NDS& nds) : NDS(nds)
case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \
case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F)
void AREngine::RunCheat(ARCode& arcode)
void AREngine::RunCheat(const ARCode& arcode)
{
u32* code = &arcode.Code[0];
const u32* code = &arcode.Code[0];
u32 offset = 0;
u32 datareg = 0;
u32 cond = 1;
u32 condstack = 0;
u32* loopstart = code;
const u32* loopstart = code;
u32 loopcount = 0;
u32 loopcond = 1;
u32 loopcondstack = 0;

View File

@ -33,7 +33,7 @@ public:
void SetCodeFile(ARCodeFile* file) { CodeFile = file; }
void RunCheats();
void RunCheat(ARCode& arcode);
void RunCheat(const ARCode& arcode);
private:
melonDS::NDS& NDS;
ARCodeFile* CodeFile; // AR code file - frontend is responsible for managing this

View File

@ -17,6 +17,7 @@
*/
#include <stdio.h>
#include <assert.h>
#include <algorithm>
#include "NDS.h"
#include "DSi.h"
@ -106,17 +107,17 @@ const u32 ARM::ConditionTable[16] =
0x0000 // NE
};
ARM::ARM(u32 num, melonDS::NDS& nds) :
ARM::ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, melonDS::NDS& nds) :
#ifdef GDBSTUB_ENABLED
GdbStub(this, Platform::GetConfigInt(num ? Platform::GdbPortARM7 : Platform::GdbPortARM9)),
GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0),
#endif
Num(num), // well uh
NDS(nds)
{
#ifdef GDBSTUB_ENABLED
if (Platform::GetConfigBool(Platform::GdbEnabled)
if (gdb
#ifdef JIT_ENABLED
&& !Platform::GetConfigBool(Platform::JIT_Enable)
&& !jit // TODO: Should we support toggling the GdbStub without destroying the ARM?
#endif
)
GdbStub.Init();
@ -129,14 +130,14 @@ ARM::~ARM()
// dorp
}
ARMv5::ARMv5(melonDS::NDS& nds) : ARM(0, nds)
ARMv5::ARMv5(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit) : ARM(0, jit, gdb, nds)
{
DTCM = NDS.JIT.Memory.GetARM9DTCM();
PU_Map = PU_PrivMap;
}
ARMv4::ARMv4(melonDS::NDS& nds) : ARM(1, nds)
ARMv4::ARMv4(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit) : ARM(1, jit, gdb, nds)
{
//
}
@ -187,8 +188,6 @@ void ARM::Reset()
#ifdef GDBSTUB_ENABLED
IsSingleStep = false;
BreakReq = false;
BreakOnStartup = Platform::GetConfigBool(
Num ? Platform::GdbARM7BreakOnStartup : Platform::GdbARM9BreakOnStartup);
#endif
// zorp
@ -224,7 +223,7 @@ void ARM::DoSavestate(Savestate* file)
file->VarArray(R_UND, 3*sizeof(u32));
file->Var32(&CurInstr);
#ifdef JIT_ENABLED
if (file->Saving && NDS.EnableJIT)
if (file->Saving && NDS.IsJITEnabled())
{
// hack, the JIT doesn't really pipeline
// but we still want JIT save states to be

View File

@ -20,9 +20,11 @@
#define ARM_H
#include <algorithm>
#include <optional>
#include "types.h"
#include "MemRegion.h"
#include "MemConstants.h"
#ifdef GDBSTUB_ENABLED
#include "debug/GdbStub.h"
@ -41,10 +43,7 @@ enum
RWFlags_ForceUser = (1<<21),
};
const u32 ITCMPhysicalSize = 0x8000;
const u32 DTCMPhysicalSize = 0x4000;
struct GDBArgs;
class ARMJIT;
class GPU;
class ARMJIT_Memory;
@ -57,7 +56,7 @@ class ARM
#endif
{
public:
ARM(u32 num, NDS& nds);
ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, NDS& nds);
virtual ~ARM(); // destroy shit
virtual void Reset();
@ -81,7 +80,7 @@ public:
virtual void ExecuteJIT() = 0;
#endif
bool CheckCondition(u32 code)
bool CheckCondition(u32 code) const
{
if (code == 0xE) return true;
if (ConditionTable[code] & (1 << (CPSR>>28))) return true;
@ -110,7 +109,7 @@ public:
if (v) CPSR |= 0x10000000;
}
inline bool ModeIs(u32 mode)
inline bool ModeIs(u32 mode) const
{
u32 cm = CPSR & 0x1f;
mode &= 0x1f;
@ -202,6 +201,7 @@ protected:
bool IsSingleStep;
bool BreakReq;
bool BreakOnStartup;
u16 Port;
public:
int GetCPU() const override { return Num ? 7 : 9; }
@ -225,7 +225,7 @@ protected:
class ARMv5 : public ARM
{
public:
ARMv5(melonDS::NDS& nds);
ARMv5(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit);
~ARMv5();
void Reset() override;
@ -315,7 +315,7 @@ public:
void ICacheInvalidateAll();
void CP15Write(u32 id, u32 val);
u32 CP15Read(u32 id);
u32 CP15Read(u32 id) const;
u32 CP15Control;
@ -377,7 +377,7 @@ protected:
class ARMv4 : public ARM
{
public:
ARMv4(melonDS::NDS& nds);
ARMv4(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit);
void FillPipeline() override;

View File

@ -63,12 +63,12 @@ const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] =
0,
ITCMPhysicalSize,
0,
sizeof(NDS::ARM9BIOS),
ARM9BIOSSize,
MainRAMMaxSize,
SharedWRAMSize,
0,
0x100000,
sizeof(NDS::ARM7BIOS),
ARM7BIOSSize,
ARM7WRAMSize,
0,
0,
@ -237,16 +237,6 @@ ARMJIT::~ARMJIT() noexcept
void ARMJIT::Reset() noexcept
{
MaxBlockSize = Platform::GetConfigInt(Platform::JIT_MaxBlockSize);
LiteralOptimizations = Platform::GetConfigBool(Platform::JIT_LiteralOptimizations);
BranchOptimizations = Platform::GetConfigBool(Platform::JIT_BranchOptimizations);
FastMemory = Platform::GetConfigBool(Platform::JIT_FastMemory);
if (MaxBlockSize < 1)
MaxBlockSize = 1;
if (MaxBlockSize > 32)
MaxBlockSize = 32;
JitEnableWrite();
ResetBlockCache();
@ -491,6 +481,56 @@ void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
}
}
void ARMJIT::SetJITArgs(JITArgs args) noexcept
{
args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
if (MaxBlockSize != args.MaxBlockSize
|| LiteralOptimizations != args.LiteralOptimizations
|| BranchOptimizations != args.BranchOptimizations
|| FastMemory != args.FastMemory)
ResetBlockCache();
MaxBlockSize = args.MaxBlockSize;
LiteralOptimizations = args.LiteralOptimizations;
BranchOptimizations = args.BranchOptimizations;
FastMemory = args.FastMemory;
}
void ARMJIT::SetMaxBlockSize(int size) noexcept
{
size = std::clamp(size, 1, 32);
if (size != MaxBlockSize)
ResetBlockCache();
MaxBlockSize = size;
}
void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
{
if (LiteralOptimizations != enabled)
ResetBlockCache();
LiteralOptimizations = enabled;
}
void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
{
if (BranchOptimizations != enabled)
ResetBlockCache();
BranchOptimizations = enabled;
}
void ARMJIT::SetFastMemory(bool enabled) noexcept
{
if (FastMemory != enabled)
ResetBlockCache();
FastMemory = enabled;
}
void ARMJIT::CompileBlock(ARM* cpu) noexcept
{
bool thumb = cpu->CPSR & 0x20;

View File

@ -19,10 +19,15 @@
#ifndef ARMJIT_H
#define ARMJIT_H
#include <algorithm>
#include <optional>
#include <memory>
#include "types.h"
#include "MemConstants.h"
#include "Args.h"
#include "ARMJIT_Memory.h"
#ifdef JIT_ENABLED
#include "JitBlock.h"
#if defined(__APPLE__) && defined(__aarch64__)
@ -30,7 +35,6 @@
#endif
#include "ARMJIT_Compiler.h"
#include "MemConstants.h"
namespace melonDS
{
@ -40,18 +44,25 @@ class JitBlock;
class ARMJIT
{
public:
ARMJIT(melonDS::NDS& nds) noexcept : NDS(nds), Memory(nds), JITCompiler(nds) {};
~ARMJIT() noexcept NOOP_IF_NO_JIT;
void InvalidateByAddr(u32) noexcept NOOP_IF_NO_JIT;
void CheckAndInvalidateWVRAM(int) noexcept NOOP_IF_NO_JIT;
void CheckAndInvalidateITCM() noexcept NOOP_IF_NO_JIT;
void Reset() noexcept NOOP_IF_NO_JIT;
void JitEnableWrite() noexcept NOOP_IF_NO_JIT;
void JitEnableExecute() noexcept NOOP_IF_NO_JIT;
void CompileBlock(ARM* cpu) noexcept NOOP_IF_NO_JIT;
void ResetBlockCache() noexcept NOOP_IF_NO_JIT;
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> 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() noexcept;
void InvalidateByAddr(u32) noexcept;
void CheckAndInvalidateWVRAM(int) noexcept;
void CheckAndInvalidateITCM() noexcept;
void Reset() noexcept;
void JitEnableWrite() noexcept;
void JitEnableExecute() noexcept;
void CompileBlock(ARM* cpu) noexcept;
void ResetBlockCache() noexcept;
#ifdef JIT_ENABLED
template <u32 num, int region>
void CheckAndInvalidate(u32 addr) noexcept
{
@ -62,23 +73,31 @@ public:
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept;
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept;
u32 LocaliseCodeAddress(u32 num, u32 addr) const noexcept;
#else
template <u32, int>
void CheckAndInvalidate(u32) noexcept {}
#endif
ARMJIT_Memory Memory;
private:
int MaxBlockSize {};
bool LiteralOptimizations = false;
bool BranchOptimizations = false;
bool FastMemory = false;
public:
melonDS::NDS& NDS;
TinyVector<u32> InvalidLiterals {};
friend class ARMJIT_Memory;
void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept;
void RetireJitBlock(JitBlock* block) noexcept;
int GetMaxBlockSize() const noexcept { return MaxBlockSize; }
bool LiteralOptimizationsEnabled() const noexcept { return LiteralOptimizations; }
bool BranchOptimizationsEnabled() const noexcept { return BranchOptimizations; }
bool FastMemoryEnabled() const noexcept { return FastMemory; }
void SetJITArgs(JITArgs args) noexcept;
void SetMaxBlockSize(int size) noexcept;
void SetLiteralOptimizations(bool enabled) noexcept;
void SetBranchOptimizations(bool enabled) noexcept;
void SetFastMemory(bool enabled) noexcept;
Compiler JITCompiler;
std::unordered_map<u32, JitBlock*> JitBlocks9 {};
std::unordered_map<u32, JitBlock*> JitBlocks7 {};
@ -162,5 +181,33 @@ public:
// Defined in assembly
extern "C" void ARM_Dispatch(melonDS::ARM* cpu, melonDS::JitBlockEntry entry);
#else
namespace melonDS
{
class ARM;
// This version is a stub; the methods all do nothing,
// but there's still a Memory member.
class ARMJIT
{
public:
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs>) noexcept : Memory(nds) {}
~ARMJIT() noexcept {}
void InvalidateByAddr(u32) noexcept {}
void CheckAndInvalidateWVRAM(int) noexcept {}
void CheckAndInvalidateITCM() noexcept {}
void Reset() noexcept {}
void JitEnableWrite() noexcept {}
void JitEnableExecute() noexcept {}
void CompileBlock(ARM*) noexcept {}
void ResetBlockCache() noexcept {}
template <u32, int>
void CheckAndInvalidate(u32 addr) noexcept {}
ARMJIT_Memory Memory;
};
}
#endif // JIT_ENABLED
#endif // ARMJIT_H
#endif

View File

@ -276,7 +276,7 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
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);
JitEnableWrite();
nds.JIT.JitEnableWrite();
#else
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif

View File

@ -19,6 +19,8 @@
#ifndef ARMJIT_A64_COMPILER_H
#define ARMJIT_A64_COMPILER_H
#if defined(JIT_ENABLED) && defined(__aarch64__)
#include "../ARM.h"
#include "../dolphin/Arm64Emitter.h"
@ -96,12 +98,8 @@ class Compiler : public Arm64Gen::ARM64XEmitter
public:
typedef void (Compiler::*CompileFunc)();
#ifdef JIT_ENABLED
explicit Compiler(melonDS::NDS& nds);
#else
explicit Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) {}
#endif
~Compiler();
~Compiler() override;
void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);
@ -116,7 +114,7 @@ public:
bool CanCompile(bool thumb, u16 kind);
bool FlagsNZNeeded()
bool FlagsNZNeeded() const
{
return CurInstr.SetFlags & 0xC;
}
@ -236,7 +234,7 @@ public:
return (u8*)entry - GetRXBase();
}
bool IsJITFault(u8* pc);
bool IsJITFault(const u8* pc);
u8* RewriteMemAccess(u8* pc);
void SwapCodeRegion()
@ -291,3 +289,5 @@ public:
}
#endif
#endif

View File

@ -28,7 +28,7 @@ using namespace Arm64Gen;
namespace melonDS
{
bool Compiler::IsJITFault(u8* pc)
bool Compiler::IsJITFault(const u8* pc)
{
return (u64)pc >= (u64)GetRXBase() && (u64)pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize);
}
@ -112,7 +112,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (size == 16)
addressMask = ~1;
if (NDS.JIT.LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@ -147,7 +147,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
MOV(W0, rnMapped);
}
bool addrIsStatic = NDS.JIT.LiteralOptimizations
bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
&& RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@ -189,7 +189,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
? NDS.JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
if (NDS.JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
{
ptrdiff_t memopStart = GetCodeOffset();
LoadStorePatch patch;
@ -453,7 +453,7 @@ void Compiler::T_Comp_LoadPCRel()
u32 offset = ((CurInstr.Instr & 0xFF) << 2);
u32 addr = (R15 & ~0x2) + offset;
if (!NDS.JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}
@ -498,7 +498,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
bool compileFastPath = NDS.JIT.FastMemory
bool compileFastPath = NDS.JIT.FastMemoryEnabled()
&& store && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
{

View File

@ -20,10 +20,6 @@
#define ARMJIT_COMPILER_H
#ifdef JIT_ENABLED
#define NOOP_IF_NO_JIT
#else
#define NOOP_IF_NO_JIT {}
#endif
#if defined(__x86_64__)
#include "ARMJIT_x64/ARMJIT_Compiler.h"
@ -34,3 +30,5 @@
#endif
#endif
#endif

View File

@ -85,7 +85,7 @@ typedef void (*InterpreterFunc)(ARM* cpu);
extern InterpreterFunc InterpretARM[];
extern InterpreterFunc InterpretTHUMB[];
inline bool PageContainsCode(AddressRange* range)
inline bool PageContainsCode(const AddressRange* range)
{
for (int i = 0; i < 8; i++)
{

View File

@ -473,8 +473,10 @@ void ARMJIT_Memory::RemapDTCM(u32 newBase, u32 newSize) noexcept
void ARMJIT_Memory::RemapNWRAM(int num) noexcept
{
auto* dsi = dynamic_cast<DSi*>(&NDS);
assert(dsi != nullptr);
if (NDS.ConsoleType == 0)
return;
auto* dsi = static_cast<DSi*>(&NDS);
for (int i = 0; i < Mappings[memregion_SharedWRAM].Length;)
{
Mapping& mapping = Mappings[memregion_SharedWRAM][i];
@ -1071,15 +1073,19 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept
}
else
{
auto& dsi = static_cast<DSi&>(NDS); // ONLY use this if ConsoleType == 1!
if (NDS.ConsoleType == 1 && addr >= 0xFFFF0000 && !(dsi.SCFG_BIOS & (1<<1)))
if (NDS.ConsoleType == 1)
{
if ((addr >= 0xFFFF8000) && (dsi.SCFG_BIOS & (1<<0)))
return memregion_Other;
auto& dsi = static_cast<DSi&>(NDS);
if (addr >= 0xFFFF0000 && !(dsi.SCFG_BIOS & (1<<1)))
{
if ((addr >= 0xFFFF8000) && (dsi.SCFG_BIOS & (1<<0)))
return memregion_Other;
return memregion_BIOS9DSi;
return memregion_BIOS9DSi;
}
}
else if ((addr & 0xFFFFF000) == 0xFFFF0000)
if ((addr & 0xFFFFF000) == 0xFFFF0000)
{
return memregion_BIOS9;
}
@ -1091,6 +1097,7 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept
case 0x03000000:
if (NDS.ConsoleType == 1)
{
auto& dsi = static_cast<DSi&>(NDS);
if (addr >= dsi.NWRAMStart[0][0] && addr < dsi.NWRAMEnd[0][0])
return memregion_NewSharedWRAM_A;
if (addr >= dsi.NWRAMStart[0][1] && addr < dsi.NWRAMEnd[0][1])
@ -1116,15 +1123,19 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept
int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept
{
auto& dsi = static_cast<DSi&>(NDS);
if (NDS.ConsoleType == 1 && addr < 0x00010000 && !(dsi.SCFG_BIOS & (1<<9)))
if (NDS.ConsoleType == 1)
{
if (addr >= 0x00008000 && dsi.SCFG_BIOS & (1<<8))
return memregion_Other;
auto& dsi = static_cast<DSi&>(NDS);
if (addr < 0x00010000 && !(dsi.SCFG_BIOS & (1<<9)))
{
if (addr >= 0x00008000 && dsi.SCFG_BIOS & (1<<8))
return memregion_Other;
return memregion_BIOS7DSi;
return memregion_BIOS7DSi;
}
}
else if (addr < 0x00004000)
if (addr < 0x00004000)
{
return memregion_BIOS7;
}
@ -1138,6 +1149,7 @@ int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept
case 0x03000000:
if (NDS.ConsoleType == 1)
{
auto& dsi = static_cast<DSi&>(NDS);
if (addr >= dsi.NWRAMStart[1][0] && addr < dsi.NWRAMEnd[1][0])
return memregion_NewSharedWRAM_A;
if (addr >= dsi.NWRAMStart[1][1] && addr < dsi.NWRAMEnd[1][1])

View File

@ -20,33 +20,33 @@
#define ARMJIT_MEMORY
#include "types.h"
#include "TinyVector.h"
#include "ARM.h"
#include "MemConstants.h"
#if defined(__SWITCH__)
#include <switch.h>
#elif defined(_WIN32)
#ifdef JIT_ENABLED
# include "TinyVector.h"
# include "ARM.h"
# if defined(__SWITCH__)
# include <switch.h>
# elif defined(_WIN32)
#include <windows.h>
# else
# include <sys/mman.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <signal.h>
# endif
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#endif
#ifndef JIT_ENABLED
#include <array>
#include "NDS.h"
# include <array>
#endif
namespace melonDS
{
#ifdef JIT_ENABLED
namespace Platform { struct DynamicLibrary; }
class Compiler;
class ARMJIT;
#endif
constexpr u32 RoundUp(u32 size) noexcept
{

View File

@ -99,7 +99,7 @@ public:
LiteralsLoaded &= ~(1 << reg);
}
bool IsLiteral(int reg)
bool IsLiteral(int reg) const
{
return LiteralsLoaded & (1 << reg);
}

View File

@ -651,7 +651,7 @@ const Compiler::CompileFunc T_Comp[ARMInstrInfo::tk_Count] = {
};
#undef F
bool Compiler::CanCompile(bool thumb, u16 kind)
bool Compiler::CanCompile(bool thumb, u16 kind) const
{
return (thumb ? T_Comp[kind] : A_Comp[kind]) != NULL;
}
@ -667,7 +667,7 @@ void Compiler::Reset()
LoadStorePatches.clear();
}
bool Compiler::IsJITFault(u8* addr)
bool Compiler::IsJITFault(const u8* addr)
{
return (u64)addr >= (u64)ResetStart && (u64)addr < (u64)ResetStart + CodeMemSize;
}

View File

@ -19,6 +19,8 @@
#ifndef ARMJIT_X64_COMPILER_H
#define ARMJIT_X64_COMPILER_H
#if defined(JIT_ENABLED) && defined(__x86_64__)
#include "../dolphin/x64Emitter.h"
#include "../ARMJIT_Internal.h"
@ -81,11 +83,7 @@ struct Op2
class Compiler : public Gen::XEmitter
{
public:
#ifdef JIT_ENABLED
explicit Compiler(melonDS::NDS& nds);
#else
explicit Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) {}
#endif
void Reset();
@ -94,7 +92,7 @@ public:
void LoadReg(int reg, Gen::X64Reg nativeReg);
void SaveReg(int reg, Gen::X64Reg nativeReg);
bool CanCompile(bool thumb, u16 kind);
bool CanCompile(bool thumb, u16 kind) const;
typedef void (Compiler::*CompileFunc)();
@ -236,7 +234,7 @@ public:
SetCodePtr(FarCode);
}
bool IsJITFault(u8* addr);
bool IsJITFault(const u8* addr);
u8* RewriteMemAccess(u8* pc);
@ -284,5 +282,6 @@ public:
};
}
#endif
#endif

View File

@ -119,7 +119,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
if (size == 16)
addressMask = ~1;
if (NDS.JIT.LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@ -136,7 +136,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
Comp_AddCycles_CDI();
}
bool addrIsStatic = NDS.JIT.LiteralOptimizations
bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
&& RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@ -200,7 +200,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
if (NDS.JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
{
if (rdMapped.IsImm())
{
@ -431,7 +431,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
else
Comp_AddCycles_CD();
bool compileFastPath = NDS.JIT.FastMemory
bool compileFastPath = NDS.JIT.FastMemoryEnabled()
&& !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
// we need to make sure that the stack stays aligned to 16 bytes
@ -809,7 +809,7 @@ void Compiler::T_Comp_LoadPCRel()
{
u32 offset = (CurInstr.Instr & 0xFF) << 2;
u32 addr = (R15 & ~0x2) + offset;
if (!NDS.JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}

148
src/Args.h Normal file
View File

@ -0,0 +1,148 @@
/*
Copyright 2016-2023 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 MELONDS_ARGS_H
#define MELONDS_ARGS_H
#include <array>
#include <optional>
#include <memory>
#include "NDSCart.h"
#include "GBACart.h"
#include "types.h"
#include "MemConstants.h"
#include "DSi_NAND.h"
#include "FATStorage.h"
#include "FreeBIOS.h"
#include "GPU3D_Soft.h"
#include "SPI_Firmware.h"
#include "SPU.h"
namespace melonDS
{
namespace NDSCart { class CartCommon; }
namespace GBACart { class CartCommon; }
template<size_t N>
constexpr std::array<u8, N> BrokenBIOS = []() constexpr {
std::array<u8, N> broken {};
for (int i = 0; i < 16; i++)
{
broken[i*4+0] = 0xE7;
broken[i*4+1] = 0xFF;
broken[i*4+2] = 0xDE;
broken[i*4+3] = 0xFF;
}
return broken;
}();
/// Arguments that configure the JIT.
/// Ignored in builds that don't have the JIT included.
struct JITArgs
{
unsigned MaxBlockSize = 32;
bool LiteralOptimizations = true;
bool BranchOptimizations = true;
/// Ignored in builds that have fast memory excluded
/// (even if the JIT is otherwise available).
/// Enabled by default, but frontends should disable this when debugging
/// so the constants segfaults don't hinder debugging.
bool FastMemory = true;
};
struct GDBArgs
{
u16 PortARM7 = 0;
u16 PortARM9 = 0;
bool ARM7BreakOnStartup = false;
bool ARM9BreakOnStartup = false;
};
/// Arguments to pass into the NDS constructor.
/// 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<NDSCart::CartCommon> 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<GBACart::CartCommon> GBAROM = nullptr;
/// NDS ARM9 BIOS to install.
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
std::array<u8, ARM9BIOSSize> ARM9BIOS = bios_arm9_bin;
/// NDS ARM7 BIOS to install.
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
std::array<u8, ARM7BIOSSize> ARM7BIOS = bios_arm7_bin;
/// Firmware image to install.
/// Defaults to generated NDS firmware.
/// Generated firmware is not compatible with DSi mode.
melonDS::Firmware Firmware {0};
/// How the JIT should be configured when initializing.
/// Defaults to enabled, with default settings.
/// To disable the JIT, set this to std::nullopt.
/// Ignored in builds that don't have the JIT included.
std::optional<JITArgs> JIT = JITArgs();
AudioBitDepth BitDepth = AudioBitDepth::Auto;
AudioInterpolation Interpolation = AudioInterpolation::None;
/// How the GDB stub should be handled.
/// Defaults to disabled.
/// Ignored in builds that don't have the GDB stub included.
std::optional<GDBArgs> GDB = std::nullopt;
/// The 3D renderer to initialize the DS with.
/// Defaults to the software renderer.
/// Can be changed later at any time.
std::unique_ptr<melonDS::Renderer3D> Renderer3D = std::make_unique<SoftRenderer>();
};
/// Arguments to pass into the DSi constructor.
/// New fields here should have default values if possible.
/// Contains no virtual methods, so there's no vtable.
struct DSiArgs final : public NDSArgs
{
std::array<u8, DSiBIOSSize> ARM9iBIOS = BrokenBIOS<DSiBIOSSize>;
std::array<u8, DSiBIOSSize> ARM7iBIOS = BrokenBIOS<DSiBIOSSize>;
/// NAND image to install.
/// Required, there is no default value.
DSi_NAND::NANDImage NANDImage;
/// SD card to install.
/// Defaults to std::nullopt, which means no SD card.
std::optional<FATStorage> DSiSDCard;
bool FullBIOSBoot = false;
};
}
#endif //MELONDS_ARGS_H

View File

@ -38,6 +38,7 @@ add_library(core STATIC
melonDLDI.h
NDS.cpp
NDSCart.cpp
NDSCartR4.cpp
Platform.h
ROMList.h
ROMList.cpp
@ -49,6 +50,8 @@ add_library(core STATIC
SPI_Firmware.cpp
SPU.cpp
types.h
Utils.cpp
Utils.h
version.h
Wifi.cpp
WifiAP.cpp

View File

@ -668,7 +668,7 @@ void ARMv5::CP15Write(u32 id, u32 val)
Log(LogLevel::Debug, "unknown CP15 write op %03X %08X\n", id, val);
}
u32 ARMv5::CP15Read(u32 id)
u32 ARMv5::CP15Read(u32 id) const
{
//printf("CP15 read op %03X %08X\n", id, NDS::ARM9->R[15]);

View File

@ -17,8 +17,10 @@
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <inttypes.h>
#include "Args.h"
#include "NDS.h"
#include "DSi.h"
#include "ARM.h"
@ -68,8 +70,8 @@ const u32 NDMAModes[] =
0xFF, // wifi / GBA cart slot (TODO)
};
DSi::DSi() noexcept :
NDS(1),
DSi::DSi(DSiArgs&& args) noexcept :
NDS(std::move(args), 1),
NDMAs {
DSi_NDMA(0, 0, *this),
DSi_NDMA(0, 1, *this),
@ -80,9 +82,11 @@ DSi::DSi() noexcept :
DSi_NDMA(1, 2, *this),
DSi_NDMA(1, 3, *this),
},
ARM7iBIOS(args.ARM7iBIOS),
ARM9iBIOS(args.ARM9iBIOS),
DSP(*this),
SDMMC(*this, 0),
SDIO(*this, 1),
SDMMC(*this, std::move(args.NANDImage), std::move(args.DSiSDCard)),
SDIO(*this),
I2C(*this),
CamModule(*this),
AES(*this)
@ -90,7 +94,7 @@ DSi::DSi() noexcept :
// Memory is owned by ARMJIT_Memory, don't free it
NWRAM_A = JIT.Memory.GetNWRAM_A();
NWRAM_B = JIT.Memory.GetNWRAM_B();
NWRAM_C = NDS::JIT.Memory.GetNWRAM_C();
NWRAM_C = JIT.Memory.GetNWRAM_C();
}
DSi::~DSi() noexcept
@ -108,6 +112,8 @@ void DSi::Reset()
//ARM9.CP15Write(0x100, ARM9.CP15Read(0x100) | 0x00050000);
NDS::Reset();
// The SOUNDBIAS register does nothing on DSi
SPU.SetApplyBias(false);
KeyInput &= ~(1 << (16+6));
MapSharedWRAM(3);
@ -118,9 +124,6 @@ void DSi::Reset()
CamModule.Reset();
DSP.Reset();
SDMMC.CloseHandles();
SDIO.CloseHandles();
LoadNAND();
SDMMC.Reset();
@ -128,7 +131,7 @@ void DSi::Reset()
AES.Reset();
if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
if (FullBIOSBoot)
{
SCFG_BIOS = 0x0000;
}
@ -162,25 +165,23 @@ void DSi::Stop(Platform::StopReason reason)
CamModule.Stop();
}
bool DSi::LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen)
void DSi::SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart)
{
if (NDS::LoadCart(romdata, romlen, savedata, savelen))
{
SetCartInserted(true);
return true;
}
return false;
NDS::SetNDSCart(std::move(cart));
SetCartInserted(NDSCartSlot.GetCart() != nullptr);
}
void DSi::EjectCart()
std::unique_ptr<NDSCart::CartCommon> DSi::EjectCart()
{
NDS::EjectCart();
auto oldcart = NDS::EjectCart();
SetCartInserted(false);
return oldcart;
}
void DSi::CamInputFrame(int cam, u32* data, int width, int height, bool rgb)
void DSi::CamInputFrame(int cam, const u32* data, int width, int height, bool rgb)
{
switch (cam)
{
@ -276,7 +277,7 @@ void DSi::SetCartInserted(bool inserted)
SCFG_MC |= 1;
}
void DSi::DecryptModcryptArea(u32 offset, u32 size, u8* iv)
void DSi::DecryptModcryptArea(u32 offset, u32 size, const u8* iv)
{
AES_ctx ctx;
u8 key[16];
@ -509,9 +510,9 @@ void DSi::SetupDirectBoot()
ARM9Write32(0x02FFE000+i, tmp);
}
if (NANDImage && *NANDImage)
if (DSi_NAND::NANDImage* image = SDMMC.GetNAND(); image && *image)
{ // If a NAND image is installed, and it's valid...
if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*NANDImage))
if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*image))
{
DSi_NAND::DSiFirmwareSystemSettings userdata {};
nand.ReadUserData(userdata);
@ -531,7 +532,7 @@ void DSi::SetupDirectBoot()
}
}
Firmware::WifiBoard nwifiver = SPI.GetFirmware()->GetHeader().WifiBoard;
Firmware::WifiBoard nwifiver = SPI.GetFirmware().GetHeader().WifiBoard;
ARM9Write8(0x020005E0, static_cast<u8>(nwifiver));
// TODO: these should be taken from the wifi firmware in NAND
@ -674,9 +675,6 @@ void DSi::SoftReset()
// the DSP most likely gets reset
DSP.Reset();
SDMMC.CloseHandles();
SDIO.CloseHandles();
LoadNAND();
SDMMC.Reset();
@ -684,7 +682,7 @@ void DSi::SoftReset()
AES.Reset();
if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
if (FullBIOSBoot)
{
SCFG_BIOS = 0x0000;
}
@ -709,21 +707,22 @@ void DSi::SoftReset()
bool DSi::LoadNAND()
{
if (!NANDImage)
DSi_NAND::NANDImage* image = SDMMC.GetNAND();
if (!(image && *image))
{
Log(LogLevel::Error, "No NAND image loaded\n");
return false;
}
Log(LogLevel::Info, "Loading DSi NAND\n");
DSi_NAND::NANDMount nandmount(*NANDImage);
DSi_NAND::NANDMount nandmount(*SDMMC.GetNAND());
if (!nandmount)
{
Log(LogLevel::Error, "Failed to load DSi NAND\n");
return false;
}
FileHandle* nand = NANDImage->GetFile();
FileHandle* nand = image->GetFile();
// Make sure NWRAM is accessible.
// The Bits are set to the startup values in Reset() and we might
@ -745,7 +744,7 @@ bool DSi::LoadNAND()
memset(NWRAMMask, 0, sizeof(NWRAMMask));
u32 bootparams[8];
if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
if (FullBIOSBoot)
{
// TODO: figure out default MBK mapping
// MBK1..5: disable mappings
@ -879,11 +878,11 @@ bool DSi::LoadNAND()
}
}
const DSi_NAND::DSiKey& emmccid = NANDImage->GetEMMCID();
const DSi_NAND::DSiKey& emmccid = image->GetEMMCID();
Log(LogLevel::Debug, "eMMC CID: %08llX%08llX\n", *(const u64*)&emmccid[0], *(const u64*)&emmccid[8]);
Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", NANDImage->GetConsoleID());
Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", image->GetConsoleID());
if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
if (FullBIOSBoot)
{
// point CPUs to boot ROM reset vectors
ARM9.JumpTo(0xFFFF0000);
@ -958,21 +957,21 @@ void DSi::StallNDMAs()
}
bool DSi::DMAsInMode(u32 cpu, u32 mode)
bool DSi::DMAsInMode(u32 cpu, u32 mode) const
{
if (NDS::DMAsInMode(cpu, mode)) return true;
return NDMAsInMode(cpu, NDMAModes[mode]);
}
bool DSi::DMAsRunning(u32 cpu)
bool DSi::DMAsRunning(u32 cpu) const
{
if (NDS::DMAsRunning(cpu)) return true;
return NDMAsRunning(cpu);
}
bool DSi::NDMAsInMode(u32 cpu, u32 mode)
bool DSi::NDMAsInMode(u32 cpu, u32 mode) const
{
cpu <<= 2;
if (NDMAs[cpu+0].IsInMode(mode)) return true;
@ -982,7 +981,7 @@ bool DSi::NDMAsInMode(u32 cpu, u32 mode)
return false;
}
bool DSi::NDMAsRunning(u32 cpu)
bool DSi::NDMAsRunning(u32 cpu) const
{
cpu <<= 2;
if (NDMAs[cpu+0].IsRunning()) return true;
@ -1728,12 +1727,12 @@ bool DSi::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
return false;
}
region->Mem = ARM9BIOS;
region->Mem = &ARM9BIOS[0];
region->Mask = 0xFFF;
}
else
{
region->Mem = ARM9iBIOS;
region->Mem = &ARM9iBIOS[0];
region->Mask = 0xFFFF;
}
return true;
@ -2678,14 +2677,14 @@ u8 DSi::ARM7IORead8(u32 addr)
case 0x04004500: return I2C.ReadData();
case 0x04004501: return I2C.ReadCnt();
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFF;
case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 8) & 0xFF;
case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFF;
case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 24) & 0xFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFF;
case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 40) & 0xFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 48) & 0xFF;
case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 56;
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFF;
case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 8) & 0xFF;
case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 16) & 0xFF;
case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 24) & 0xFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFF;
case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 40) & 0xFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 48) & 0xFF;
case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 56;
case 0x04004D08: return 0;
case 0x4004700: return DSP.ReadSNDExCnt() & 0xFF;
@ -2726,10 +2725,10 @@ u16 DSi::ARM7IORead16(u32 addr)
CASE_READ16_32BIT(0x0400405C, MBK[1][7])
CASE_READ16_32BIT(0x04004060, MBK[1][8])
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFF;
case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFFFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 48;
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFF;
case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 16) & 0xFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFFFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 48;
case 0x04004D08: return 0;
case 0x4004700: return DSP.ReadSNDExCnt();
@ -2806,8 +2805,8 @@ u32 DSi::ARM7IORead32(u32 addr)
case 0x04004400: return AES.ReadCnt();
case 0x0400440C: return AES.ReadOutputFIFO();
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFFFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 32;
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFFFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 32;
case 0x04004D08: return 0;
case 0x4004700:

View File

@ -33,6 +33,7 @@ class DSi_I2CHost;
class DSi_CamModule;
class DSi_AES;
class DSi_DSP;
class DSiArgs;
namespace DSi_NAND
{
@ -48,9 +49,8 @@ public:
u16 SCFG_Clock9;
u32 SCFG_EXT[2];
u8 ARM9iBIOS[0x10000];
u8 ARM7iBIOS[0x10000];
std::unique_ptr<DSi_NAND::NANDImage> NANDImage;
std::array<u8, DSiBIOSSize> ARM9iBIOS;
std::array<u8, DSiBIOSSize> ARM7iBIOS;
DSi_SDHost SDMMC;
DSi_SDHost SDIO;
@ -87,8 +87,8 @@ public:
void RunNDMAs(u32 cpu);
void StallNDMAs();
bool NDMAsInMode(u32 cpu, u32 mode);
bool NDMAsRunning(u32 cpu);
bool NDMAsInMode(u32 cpu, u32 mode) const;
bool NDMAsRunning(u32 cpu) const;
void CheckNDMAs(u32 cpu, u32 mode);
void StopNDMAs(u32 cpu, u32 mode);
@ -130,22 +130,32 @@ public:
void ARM7IOWrite32(u32 addr, u32 val) override;
public:
DSi() noexcept;
DSi(DSiArgs&& args) noexcept;
~DSi() noexcept override;
DSi(const DSi&) = delete;
DSi& operator=(const DSi&) = delete;
DSi(DSi&&) = delete;
DSi& operator=(DSi&&) = delete;
bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) override;
void EjectCart() override;
bool NeedsDirectBoot() override
void SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart) override;
std::unique_ptr<NDSCart::CartCommon> EjectCart() override;
bool NeedsDirectBoot() const override
{
// for now, DSi mode requires original BIOS/NAND
return false;
}
void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) override;
bool DMAsInMode(u32 cpu, u32 mode) override;
bool DMAsRunning(u32 cpu) override;
[[nodiscard]] const DSi_NAND::NANDImage& GetNAND() const noexcept { return *SDMMC.GetNAND(); }
[[nodiscard]] DSi_NAND::NANDImage& GetNAND() noexcept { return *SDMMC.GetNAND(); }
void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { SDMMC.SetNAND(std::move(nand)); }
u64 GetConsoleID() const noexcept { return SDMMC.GetNAND()->GetConsoleID(); }
[[nodiscard]] const FATStorage* GetSDCard() const noexcept { return SDMMC.GetSDCard(); }
void SetSDCard(FATStorage&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); }
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); }
void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) override;
bool DMAsInMode(u32 cpu, u32 mode) const override;
bool DMAsRunning(u32 cpu) const override;
void StopDMAs(u32 cpu, u32 mode) override;
void CheckDMAs(u32 cpu, u32 mode) override;
u16 SCFG_Clock7;
@ -162,10 +172,13 @@ public:
u8 GPIO_IE;
u8 GPIO_WiFi;
bool GetFullBIOSBoot() const noexcept { return FullBIOSBoot; }
void SetFullBIOSBoot(bool full) noexcept { FullBIOSBoot = full; }
private:
bool FullBIOSBoot;
void Set_SCFG_Clock9(u16 val);
void Set_SCFG_MC(u32 val);
void DecryptModcryptArea(u32 offset, u32 size, u8* iv);
void DecryptModcryptArea(u32 offset, u32 size, const u8* iv);
void ApplyNewRAMSize(u32 size);
};

View File

@ -78,7 +78,7 @@ void DSi_AES::Reset()
OutputMACDue = false;
// initialize keys
u64 consoleid = DSi.NANDImage->GetConsoleID();
u64 consoleid = DSi.SDMMC.GetNAND()->GetConsoleID();
// slot 0: modcrypt
*(u32*)&KeyX[0][0] = 0x746E694E;
@ -235,7 +235,7 @@ void DSi_AES::ProcessBlock_CTR()
}
u32 DSi_AES::ReadCnt()
u32 DSi_AES::ReadCnt() const
{
u32 ret = Cnt;

View File

@ -54,7 +54,7 @@ public:
void Reset();
void DoSavestate(Savestate* file);
u32 ReadCnt();
u32 ReadCnt() const;
void WriteCnt(u32 val);
void WriteBlkCnt(u32 val);

View File

@ -438,7 +438,7 @@ void DSi_Camera::Stop()
Platform::Camera_Stop(Num);
}
bool DSi_Camera::IsActivated()
bool DSi_Camera::IsActivated() const
{
if (StandbyCnt & (1<<14)) return false; // standby
if (!(MiscCnt & (1<<9))) return false; // data transfer not enabled
@ -477,7 +477,7 @@ void DSi_Camera::StartTransfer()
Platform::Camera_CaptureFrame(Num, FrameBuffer, 640, 480, true);
}
bool DSi_Camera::TransferDone()
bool DSi_Camera::TransferDone() const
{
return TransferY >= FrameHeight;
}
@ -590,7 +590,7 @@ void DSi_Camera::Write(u8 val, bool last)
else DataPos++;
}
u16 DSi_Camera::I2C_ReadReg(u16 addr)
u16 DSi_Camera::I2C_ReadReg(u16 addr) const
{
switch (addr)
{
@ -695,7 +695,7 @@ void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
// TODO: not sure at all what is the accessible range
// or if there is any overlap in the address range
u8 DSi_Camera::MCU_Read(u16 addr)
u8 DSi_Camera::MCU_Read(u16 addr) const
{
addr &= 0x7FFF;
@ -724,7 +724,7 @@ void DSi_Camera::MCU_Write(u16 addr, u8 val)
}
void DSi_Camera::InputFrame(u32* data, int width, int height, bool rgb)
void DSi_Camera::InputFrame(const u32* data, int width, int height, bool rgb)
{
// TODO: double-buffering?

View File

@ -38,10 +38,10 @@ public:
void Reset() override;
void Stop();
bool IsActivated();
bool IsActivated() const;
void StartTransfer();
bool TransferDone();
bool TransferDone() const;
// lengths in words
int TransferScanline(u32* buffer, int maxlen);
@ -50,7 +50,7 @@ public:
u8 Read(bool last) override;
void Write(u8 val, bool last) override;
void InputFrame(u32* data, int width, int height, bool rgb);
void InputFrame(const u32* data, int width, int height, bool rgb);
u32 Num;
@ -59,7 +59,7 @@ private:
u32 RegAddr;
u16 RegData;
u16 I2C_ReadReg(u16 addr);
u16 I2C_ReadReg(u16 addr) const;
void I2C_WriteReg(u16 addr, u16 val);
u16 PLLDiv;
@ -72,7 +72,7 @@ private:
u16 MCUAddr;
u8 MCURegs[0x8000];
u8 MCU_Read(u16 addr);
u8 MCU_Read(u16 addr) const;
void MCU_Write(u16 addr, u8 val);
u16 FrameWidth, FrameHeight;
@ -91,7 +91,9 @@ public:
void Stop();
void DoSavestate(Savestate* file);
const DSi_Camera* GetOuterCamera() const { return Camera0; }
DSi_Camera* GetOuterCamera() { return Camera0; }
const DSi_Camera* GetInnerCamera() const { return Camera1; }
DSi_Camera* GetInnerCamera() { return Camera1; }
void IRQ(u32 param);

View File

@ -34,7 +34,7 @@ const u32 DSi_DSP::DataMemoryOffset = 0x20000; // from Teakra memory_interface.h
// NOTE: ^ IS IN DSP WORDS, NOT IN BYTES!
u16 DSi_DSP::GetPSTS()
u16 DSi_DSP::GetPSTS() const
{
u16 r = DSP_PSTS & (1<<9); // this is the only sticky bit
//r &= ~((1<<2)|(1<<7)); // we support instant resets and wrfifo xfers
@ -182,7 +182,7 @@ void DSi_DSP::Reset()
SNDExCnt = 0;
}
bool DSi_DSP::IsRstReleased()
bool DSi_DSP::IsRstReleased() const
{
return SCFG_RST;
}
@ -193,12 +193,12 @@ void DSi_DSP::SetRstLine(bool release)
DSPTimestamp = DSi.ARM9Timestamp; // only start now!
}
inline bool DSi_DSP::IsDSPCoreEnabled()
inline bool DSi_DSP::IsDSPCoreEnabled() const
{
return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0)));
}
inline bool DSi_DSP::IsDSPIOEnabled()
inline bool DSi_DSP::IsDSPIOEnabled() const
{
return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST;
}

View File

@ -41,7 +41,7 @@ public:
void DSPCatchUpU32(u32 _);
// SCFG_RST bit0
bool IsRstReleased();
bool IsRstReleased() const;
void SetRstLine(bool release);
// DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT)
@ -54,7 +54,7 @@ public:
u32 Read32(u32 addr);
void Write32(u32 addr, u32 val);
u16 ReadSNDExCnt() { return SNDExCnt; }
u16 ReadSNDExCnt() const { return SNDExCnt; }
void WriteSNDExCnt(u16 val, u16 mask);
// NOTE: checks SCFG_CLK9
@ -93,10 +93,10 @@ private:
static const u32 DataMemoryOffset;
u16 GetPSTS();
u16 GetPSTS() const;
inline bool IsDSPCoreEnabled();
inline bool IsDSPIOEnabled();
inline bool IsDSPCoreEnabled() const;
inline bool IsDSPIOEnabled() const;
bool DSPCatchUp();

View File

@ -117,20 +117,20 @@ void DSi_BPTWL::DoSavestate(Savestate* file)
}
// TODO: Needs more investigation on the other bits
inline bool DSi_BPTWL::GetIRQMode()
inline bool DSi_BPTWL::GetIRQMode() const
{
return Registers[0x12] & 0x01;
}
u8 DSi_BPTWL::GetBootFlag() { return Registers[0x70]; }
u8 DSi_BPTWL::GetBootFlag() const { return Registers[0x70]; }
bool DSi_BPTWL::GetBatteryCharging() { return Registers[0x20] >> 7; }
bool DSi_BPTWL::GetBatteryCharging() const { return Registers[0x20] >> 7; }
void DSi_BPTWL::SetBatteryCharging(bool charging)
{
Registers[0x20] = (((charging ? 0x8 : 0x0) << 4) | (Registers[0x20] & 0x0F));
}
u8 DSi_BPTWL::GetBatteryLevel() { return Registers[0x20] & 0xF; }
u8 DSi_BPTWL::GetBatteryLevel() const { return Registers[0x20] & 0xF; }
void DSi_BPTWL::SetBatteryLevel(u8 batteryLevel)
{
Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F));
@ -143,13 +143,13 @@ void DSi_BPTWL::SetBatteryLevel(u8 batteryLevel)
}
u8 DSi_BPTWL::GetVolumeLevel() { return Registers[0x40]; }
u8 DSi_BPTWL::GetVolumeLevel() const { return Registers[0x40]; }
void DSi_BPTWL::SetVolumeLevel(u8 volume)
{
Registers[0x40] = volume & 0x1F;
}
u8 DSi_BPTWL::GetBacklightLevel() { return Registers[0x41]; }
u8 DSi_BPTWL::GetBacklightLevel() const { return Registers[0x41]; }
void DSi_BPTWL::SetBacklightLevel(u8 backlight)
{
Registers[0x41] = backlight > 4 ? 4 : backlight;
@ -246,7 +246,7 @@ void DSi_BPTWL::SetVolumeSwitchReleased(u32 key)
VolumeSwitchRepeatTime = 0.0;
}
inline bool DSi_BPTWL::CheckVolumeSwitchKeysValid()
inline bool DSi_BPTWL::CheckVolumeSwitchKeysValid() const
{
bool up = VolumeSwitchKeysDown & (1 << volumeKey_Up);
bool down = VolumeSwitchKeysDown & (1 << volumeKey_Down);

View File

@ -86,20 +86,20 @@ public:
void Reset() override;
void DoSavestate(Savestate* file) override;
u8 GetBootFlag();
u8 GetBootFlag() const;
bool GetBatteryCharging();
bool GetBatteryCharging() const;
void SetBatteryCharging(bool charging);
u8 GetBatteryLevel();
u8 GetBatteryLevel() const;
void SetBatteryLevel(u8 batteryLevel);
// 0-31
u8 GetVolumeLevel();
u8 GetVolumeLevel() const;
void SetVolumeLevel(u8 volume);
// 0-4
u8 GetBacklightLevel();
u8 GetBacklightLevel() const;
void SetBacklightLevel(u8 backlight);
void DoHardwareReset(bool direct);
@ -144,10 +144,10 @@ private:
u8 Registers[0x100];
u32 CurPos;
bool GetIRQMode();
bool GetIRQMode() const;
void ResetButtonState();
bool CheckVolumeSwitchKeysValid();
bool CheckVolumeSwitchKeysValid() const;
};

View File

@ -122,7 +122,8 @@ NANDImage::NANDImage(NANDImage&& other) noexcept :
ConsoleID(other.ConsoleID),
FATIV(other.FATIV),
FATKey(other.FATKey),
ESKey(other.ESKey)
ESKey(other.ESKey),
Length(other.Length)
{
other.CurFile = nullptr;
}
@ -131,12 +132,16 @@ NANDImage& NANDImage::operator=(NANDImage&& other) noexcept
{
if (this != &other)
{
if (CurFile)
CloseFile(CurFile);
CurFile = other.CurFile;
eMMC_CID = other.eMMC_CID;
ConsoleID = other.ConsoleID;
FATIV = other.FATIV;
FATKey = other.FATKey;
ESKey = other.ESKey;
Length = other.Length;
other.CurFile = nullptr;
}
@ -362,7 +367,7 @@ bool NANDImage::ESEncrypt(u8* data, u32 len) const
return true;
}
bool NANDImage::ESDecrypt(u8* data, u32 len)
bool NANDImage::ESDecrypt(u8* data, u32 len) const
{
AES_ctx ctx;
u8 iv[16];

View File

@ -71,7 +71,7 @@ private:
u32 ReadFATBlock(u64 addr, u32 len, u8* buf);
u32 WriteFATBlock(u64 addr, u32 len, const u8* buf);
bool ESEncrypt(u8* data, u32 len) const;
bool ESDecrypt(u8* data, u32 len);
bool ESDecrypt(u8* data, u32 len) const;
Platform::FileHandle* CurFile = nullptr;
DSiKey eMMC_CID;
u64 ConsoleID;

View File

@ -44,12 +44,12 @@ public:
void Run9();
void Run7();
bool IsInMode(u32 mode)
bool IsInMode(u32 mode) const
{
return ((mode == StartMode) && (Cnt & 0x80000000));
}
bool IsRunning() { return Running!=0; }
bool IsRunning() const { return Running!=0; }
void StartIfNeeded(u32 mode)
{

View File

@ -165,13 +165,13 @@ void DSi_NWifi::Reset()
for (int i = 0; i < 9; i++)
Mailbox[i].Clear();
const Firmware* fw = DSi.SPI.GetFirmware();
const Firmware& fw = DSi.SPI.GetFirmware();
MacAddress mac = fw->GetHeader().MacAddr;
MacAddress mac = fw.GetHeader().MacAddr;
Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Firmware::WifiBoard type = fw->GetHeader().WifiBoard;
Firmware::WifiBoard type = fw.GetHeader().WifiBoard;
switch (type)
{
case Firmware::WifiBoard::W015: // AR6002

View File

@ -18,6 +18,7 @@
#include <stdio.h>
#include <string.h>
#include "Args.h"
#include "DSi.h"
#include "DSi_SD.h"
#include "DSi_NAND.h"
@ -26,6 +27,10 @@
namespace melonDS
{
using std::holds_alternative;
using std::unique_ptr;
using std::get_if;
using std::get;
using namespace Platform;
// observed IRQ behavior during transfers
@ -57,36 +62,38 @@ enum
};
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, u32 num) : DSi(dsi)
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard) noexcept : DSi(dsi), Num(0)
{
Num = num;
DSi.RegisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
DSi.RegisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
Ports[0] = nullptr;
Ports[0] = sdcard ? std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard)) : nullptr;
sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object
Ports[1] = std::make_unique<DSi_MMCStorage>(DSi, this, std::move(nand));
}
// 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));
Ports[0] = std::make_unique<DSi_NWifi>(DSi, this);
Ports[1] = nullptr;
}
DSi_SDHost::~DSi_SDHost()
{
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
Transfer_TX);
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
Transfer_RX);
}
void DSi_SDHost::CloseHandles()
{
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
Ports[0] = nullptr;
Ports[1] = nullptr;
// unique_ptr's destructor will clean up the ports
}
void DSi_SDHost::Reset()
@ -129,48 +136,70 @@ void DSi_SDHost::Reset()
TXReq = false;
CloseHandles();
if (Ports[0]) Ports[0]->Reset();
if (Ports[1]) Ports[1]->Reset();
}
if (Num == 0)
FATStorage* DSi_SDHost::GetSDCard() noexcept
{
if (Num != 0) return nullptr;
return static_cast<DSi_MMCStorage*>(Ports[0].get())->GetSDCard();
}
const FATStorage* DSi_SDHost::GetSDCard() const noexcept
{
if (Num != 0) return nullptr;
return static_cast<const DSi_MMCStorage*>(Ports[0].get())->GetSDCard();
}
DSi_NAND::NANDImage* DSi_SDHost::GetNAND() noexcept
{
if (Num != 0) return nullptr;
return static_cast<DSi_MMCStorage*>(Ports[1].get())->GetNAND();
}
const DSi_NAND::NANDImage* DSi_SDHost::GetNAND() const noexcept
{
if (Num != 0) return nullptr;
return static_cast<const DSi_MMCStorage*>(Ports[1].get())->GetNAND();
}
void DSi_SDHost::SetSDCard(FATStorage&& sdcard) noexcept
{
if (Num != 0) return;
static_cast<DSi_MMCStorage*>(Ports[0].get())->SetSDCard(std::move(sdcard));
}
void DSi_SDHost::SetSDCard(std::optional<FATStorage>&& sdcard) noexcept
{
if (Num != 0) return;
if (sdcard)
{
DSi_MMCStorage* sd;
DSi_MMCStorage* mmc;
if (Platform::GetConfigBool(Platform::DSiSD_Enable))
if (!Ports[0])
{
std::string folderpath;
if (Platform::GetConfigBool(Platform::DSiSD_FolderSync))
folderpath = Platform::GetConfigString(Platform::DSiSD_FolderPath);
else
folderpath = "";
sd = new DSi_MMCStorage(this,
false,
Platform::GetConfigString(Platform::DSiSD_ImagePath),
(u64)Platform::GetConfigInt(Platform::DSiSD_ImageSize) * 1024 * 1024,
Platform::GetConfigBool(Platform::DSiSD_ReadOnly),
folderpath);
u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
sd->SetCID(sd_cid);
Ports[0] = std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard));
}
else
sd = nullptr;
mmc = new DSi_MMCStorage(this, *DSi.NANDImage);
mmc->SetCID(DSi.NANDImage->GetEMMCID().data());
Ports[0] = sd;
Ports[1] = mmc;
{
static_cast<DSi_MMCStorage*>(Ports[0].get())->SetSDCard(std::move(*sdcard));
}
}
else
{
DSi_NWifi* nwifi = new DSi_NWifi(DSi, this);
Ports[0] = nwifi;
Ports[0] = nullptr;
}
if (Ports[0]) Ports[0]->Reset();
if (Ports[1]) Ports[1]->Reset();
sdcard = std::nullopt;
// a moved-from optional isn't empty, it contains a moved-from object
}
void DSi_SDHost::SetNAND(DSi_NAND::NANDImage&& nand) noexcept
{
if (Num != 0) return;
static_cast<DSi_MMCStorage*>(Ports[1].get())->SetNAND(std::move(nand));
}
void DSi_SDHost::DoSavestate(Savestate* file)
@ -261,7 +290,7 @@ void DSi_SDHost::SetCardIRQ()
if (!(CardIRQCtl & (1<<0))) return;
u16 oldflags = CardIRQStatus & ~CardIRQMask;
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev->IRQ) CardIRQStatus |= (1<<0);
else CardIRQStatus &= ~(1<<0);
@ -307,7 +336,7 @@ void DSi_SDHost::FinishRX(u32 param)
SetIRQ(24);
}
u32 DSi_SDHost::DataRX(u8* data, u32 len)
u32 DSi_SDHost::DataRX(const u8* data, u32 len)
{
if (len != BlockLen16) { Log(LogLevel::Warn, "!! BAD BLOCKLEN\n"); len = BlockLen16; }
@ -332,7 +361,7 @@ u32 DSi_SDHost::DataRX(u8* data, u32 len)
void DSi_SDHost::FinishTX(u32 param)
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (BlockCountInternal == 0)
{
@ -411,7 +440,7 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len)
return len;
}
u32 DSi_SDHost::GetTransferrableLen(u32 len)
u32 DSi_SDHost::GetTransferrableLen(u32 len) const
{
if (len > BlockLen16) len = BlockLen16; // checkme
return len;
@ -419,7 +448,7 @@ u32 DSi_SDHost::GetTransferrableLen(u32 len)
void DSi_SDHost::CheckRX()
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
CheckSwapFIFO();
@ -459,7 +488,7 @@ void DSi_SDHost::CheckTX()
return;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev) dev->ContinueTransfer();
}
@ -550,7 +579,6 @@ u16 DSi_SDHost::ReadFIFO16()
return 0;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u16 ret = DataFIFO[f].Read();
if (DataFIFO[f].IsEmpty())
@ -571,7 +599,6 @@ u32 DSi_SDHost::ReadFIFO32()
return 0;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 ret = DataFIFO32.Read();
if (DataFIFO32.IsEmpty())
@ -593,7 +620,7 @@ void DSi_SDHost::Write(u32 addr, u16 val)
Command = val;
u8 cmd = Command & 0x3F;
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev)
{
// CHECKME
@ -707,7 +734,6 @@ void DSi_SDHost::Write(u32 addr, u16 val)
void DSi_SDHost::WriteFIFO16(u16 val)
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 f = CurFIFO;
if (DataFIFO[f].IsFull())
{
@ -780,34 +806,23 @@ void DSi_SDHost::CheckSwapFIFO()
#define MMC_DESC (Internal?"NAND":"SDcard")
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand)
: DSi_SDDevice(host), Internal(true), NAND(&nand), SD(nullptr)
DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept
: DSi_SDDevice(host), DSi(dsi), Storage(std::move(nand))
{
ReadOnly = false;
SetCID(get<DSi_NAND::NANDImage>(Storage).GetEMMCID().data());
}
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir)
: DSi_SDDevice(host)
DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept
: DSi_SDDevice(host), DSi(dsi), Storage(std::move(sdcard))
{
Internal = internal;
NAND = nullptr;
SD = new FATStorage(filename, size, readonly, sourcedir);
SD->Open();
ReadOnly = readonly;
ReadOnly = get<FATStorage>(Storage).IsReadOnly();
SetCID(DSiSDCardCID);
}
DSi_MMCStorage::~DSi_MMCStorage()
{
if (SD)
{
SD->Close();
delete SD;
}
// Do not close the NANDImage, it's not owned by this object
}
// The FATStorage or NANDImage is owned by this object;
// std::variant's destructor will clean it up.
DSi_MMCStorage::~DSi_MMCStorage() = default;
void DSi_MMCStorage::Reset()
{
@ -836,7 +851,7 @@ void DSi_MMCStorage::Reset()
void DSi_MMCStorage::DoSavestate(Savestate* file)
{
file->Section(Internal ? "NAND" : "SDCR");
file->Section(holds_alternative<DSi_NAND::NANDImage>(Storage) ? "NAND" : "SDCR");
file->VarArray(CID, 16);
file->VarArray(CSD, 16);
@ -871,7 +886,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
case 1: // SEND_OP_COND
// CHECKME!!
// also TODO: it's different for the SD card
if (Internal)
if (std::holds_alternative<DSi_NAND::NANDImage>(Storage))
{
param &= ~(1<<30);
OCR &= 0xBF000000;
@ -895,7 +910,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
return;
case 3: // get/set RCA
if (Internal)
if (holds_alternative<DSi_NAND::NANDImage>(Storage))
{
RCA = param >> 16;
Host->SendResponse(CSR|0x10000, true); // huh??
@ -930,7 +945,8 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
case 12: // stop operation
SetState(0x04);
if (NAND) FileFlush(NAND->GetFile());
if (auto* nand = get_if<DSi_NAND::NANDImage>(&Storage))
FileFlush(nand->GetFile());
RWCommand = 0;
Host->SendResponse(CSR, true);
return;
@ -1011,7 +1027,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
// DSi boot2 sets this to 0x40100000 (hardcoded)
// then has two codepaths depending on whether bit30 did get set
// is it settable at all on the MMC? probably not.
if (Internal) param &= ~(1<<30);
if (holds_alternative<DSi_NAND::NANDImage>(Storage)) param &= ~(1<<30);
OCR &= 0xBF000000;
OCR |= (param & 0x40FFFFFF);
Host->SendResponse(OCR, true);
@ -1057,14 +1073,14 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr)
len = Host->GetTransferrableLen(len);
u8 data[0x200];
if (SD)
if (auto* sd = std::get_if<FATStorage>(&Storage))
{
SD->ReadSectors((u32)(addr >> 9), 1, data);
sd->ReadSectors((u32)(addr >> 9), 1, data);
}
else if (NAND)
else if (auto* nand = std::get_if<DSi_NAND::NANDImage>(&Storage))
{
FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start);
FileRead(&data[addr & 0x1FF], 1, len, NAND->GetFile());
FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
FileRead(&data[addr & 0x1FF], 1, len, nand->GetFile());
}
return Host->DataRX(&data[addr & 0x1FF], len);
@ -1078,23 +1094,23 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr)
u8 data[0x200];
if (len < 0x200)
{
if (SD)
if (auto* sd = get_if<FATStorage>(&Storage))
{
SD->ReadSectors((u32)(addr >> 9), 1, data);
sd->ReadSectors((u32)(addr >> 9), 1, data);
}
}
if ((len = Host->DataTX(&data[addr & 0x1FF], len)))
{
if (!ReadOnly)
{
if (SD)
if (auto* sd = get_if<FATStorage>(&Storage))
{
SD->WriteSectors((u32)(addr >> 9), 1, data);
sd->WriteSectors((u32)(addr >> 9), 1, data);
}
else if (NAND)
else if (auto* nand = get_if<DSi_NAND::NANDImage>(&Storage))
{
FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start);
FileWrite(&data[addr & 0x1FF], 1, len, NAND->GetFile());
FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
FileWrite(&data[addr & 0x1FF], 1, len, nand->GetFile());
}
}
}

View File

@ -20,28 +20,30 @@
#define DSI_SD_H
#include <cstring>
#include <variant>
#include "FIFO.h"
#include "FATStorage.h"
#include "DSi_NAND.h"
#include "Savestate.h"
namespace melonDS
{
namespace DSi_NAND
{
class NANDImage;
}
class DSi_SDDevice;
class DSi;
using Nothing = std::monostate;
using DSiStorage = std::variant<std::monostate, FATStorage, DSi_NAND::NANDImage>;
class DSi_SDHost
{
public:
DSi_SDHost(melonDS::DSi& dsi, u32 num);
/// Creates an SDMMC host.
DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard = std::nullopt) noexcept;
/// Creates an SDIO host
explicit DSi_SDHost(melonDS::DSi& dsi) noexcept;
~DSi_SDHost();
void CloseHandles();
void Reset();
void DoSavestate(Savestate* file);
@ -49,9 +51,9 @@ public:
void FinishRX(u32 param);
void FinishTX(u32 param);
void SendResponse(u32 val, bool last);
u32 DataRX(u8* data, u32 len);
u32 DataRX(const u8* data, u32 len);
u32 DataTX(u8* data, u32 len);
u32 GetTransferrableLen(u32 len);
u32 GetTransferrableLen(u32 len) const;
void CheckRX();
void CheckTX();
@ -59,6 +61,15 @@ public:
void SetCardIRQ();
[[nodiscard]] FATStorage* GetSDCard() noexcept;
[[nodiscard]] const FATStorage* GetSDCard() const noexcept;
[[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept;
[[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept;
void SetSDCard(FATStorage&& sdcard) noexcept;
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept;
void SetNAND(DSi_NAND::NANDImage&& nand) noexcept;
u16 Read(u32 addr);
void Write(u32 addr, u16 val);
u16 ReadFIFO16();
@ -96,7 +107,7 @@ private:
u32 Param;
u16 ResponseBuffer[8];
DSi_SDDevice* Ports[2];
std::array<std::unique_ptr<DSi_SDDevice>, 2> Ports {};
u32 CurFIFO; // FIFO accessible for read/write
FIFO<u16, 0x100> DataFIFO[2];
@ -134,25 +145,53 @@ protected:
class DSi_MMCStorage : public DSi_SDDevice
{
public:
DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand);
DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir);
~DSi_MMCStorage();
DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept;
DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept;
~DSi_MMCStorage() override;
void Reset();
[[nodiscard]] FATStorage* GetSDCard() noexcept { return std::get_if<FATStorage>(&Storage); }
[[nodiscard]] const FATStorage* GetSDCard() const noexcept { return std::get_if<FATStorage>(&Storage); }
[[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept { return std::get_if<DSi_NAND::NANDImage>(&Storage); }
[[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept { return std::get_if<DSi_NAND::NANDImage>(&Storage); }
void DoSavestate(Savestate* file);
void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { Storage = std::move(nand); }
void SetSDCard(FATStorage&& sdcard) noexcept { Storage = std::move(sdcard); }
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept
{
if (sdcard)
{ // If we're setting a new SD card...
Storage = std::move(*sdcard);
sdcard = std::nullopt;
}
else
{
Storage = Nothing();
}
}
void SetStorage(DSiStorage&& storage) noexcept
{
Storage = std::move(storage);
storage = Nothing();
// not sure if a moved-from variant is empty or contains a moved-from object;
// better to be safe than sorry
}
void Reset() override;
void DoSavestate(Savestate* file) override;
void SetCID(const u8* cid) { memcpy(CID, cid, sizeof(CID)); }
void SendCMD(u8 cmd, u32 param);
void SendCMD(u8 cmd, u32 param) override;
void SendACMD(u8 cmd, u32 param);
void ContinueTransfer();
void ContinueTransfer() override;
private:
bool Internal;
DSi_NAND::NANDImage* NAND;
FATStorage* SD;
static constexpr u8 DSiSDCardCID[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
melonDS::DSi& DSi;
DSiStorage Storage;
u8 CID[16];
u8 CSD[16];

View File

@ -121,7 +121,7 @@ void DSi_TSC::SetTouchCoords(u16 x, u16 y)
}
}
void DSi_TSC::MicInputFrame(s16* data, int samples)
void DSi_TSC::MicInputFrame(const s16* data, int samples)
{
if (TSCMode == 0x00) return TSC::MicInputFrame(data, samples);

View File

@ -40,7 +40,7 @@ public:
void SetMode(u8 mode);
void SetTouchCoords(u16 x, u16 y) override;
void MicInputFrame(s16* data, int samples) override;
void MicInputFrame(const s16* data, int samples) override;
void Write(u8 val) override;
void Release() override;

View File

@ -29,39 +29,73 @@ namespace melonDS
{
namespace fs = std::filesystem;
using namespace Platform;
using std::string;
FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir)
FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional<string>& sourcedir) :
FATStorage(FATStorageArgs { filename, size, readonly, sourcedir })
{
ReadOnly = readonly;
Load(filename, size, sourcedir);
}
File = nullptr;
FATStorage::FATStorage(const FATStorageArgs& args) noexcept :
FATStorage(args.Filename, args.Size, args.ReadOnly, args.SourceDir)
{
}
FATStorage::FATStorage(FATStorageArgs&& args) noexcept :
FilePath(std::move(args.Filename)),
FileSize(args.Size),
ReadOnly(args.ReadOnly),
SourceDir(std::move(args.SourceDir))
{
Load(FilePath, FileSize, SourceDir);
File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
}
FATStorage::FATStorage(FATStorage&& other) noexcept
{
FilePath = std::move(other.FilePath);
IndexPath = std::move(other.IndexPath);
SourceDir = std::move(other.SourceDir);
ReadOnly = other.ReadOnly;
File = other.File;
FileSize = other.FileSize;
DirIndex = std::move(other.DirIndex);
FileIndex = std::move(other.FileIndex);
other.File = nullptr;
}
FATStorage& FATStorage::operator=(FATStorage&& other) noexcept
{
if (this != &other)
{
if (File)
CloseFile(File);
FilePath = std::move(other.FilePath);
IndexPath = std::move(other.IndexPath);
SourceDir = std::move(other.SourceDir);
ReadOnly = other.ReadOnly;
File = other.File;
FileSize = other.FileSize;
DirIndex = std::move(other.DirIndex);
FileIndex = std::move(other.FileIndex);
other.File = nullptr;
}
return *this;
}
FATStorage::~FATStorage()
{
if (!ReadOnly) Save();
}
bool FATStorage::Open()
{
File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
if (!File)
{
return false;
}
return true;
}
void FATStorage::Close()
{
if (File) CloseFile(File);
File = nullptr;
}
bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
{
if (!File) return false;
@ -104,18 +138,65 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
return nwrite==len;
}
u32 FATStorage::ReadFile(const std::string& path, u32 start, u32 len, u8* data)
{
if (!File) return false;
if (FF_File) return false;
u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data)
FF_File = File;
FF_FileSize = FileSize;
ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9));
FRESULT res;
FATFS fs;
res = f_mount(&fs, "0:", 1);
if (res != FR_OK)
{
ff_disk_close();
FF_File = nullptr;
return false;
}
std::string prefixedPath("0:/");
prefixedPath += path;
FF_FIL file;
res = f_open(&file, prefixedPath.c_str(), FA_READ);
if (res != FR_OK)
{
f_unmount("0:");
ff_disk_close();
FF_File = nullptr;
return false;
}
u32 nread;
f_lseek(&file, start);
f_read(&file, data, len, &nread);
f_close(&file);
f_unmount("0:");
ff_disk_close();
FF_File = nullptr;
return nread;
}
u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data) const
{
return ReadSectorsInternal(File, FileSize, start, num, data);
}
u32 FATStorage::WriteSectors(u32 start, u32 num, u8* data)
u32 FATStorage::WriteSectors(u32 start, u32 num, const u8* data)
{
if (ReadOnly) return 0;
return WriteSectorsInternal(File, FileSize, start, num, data);
}
u64 FATStorage::GetSectorCount() const
{
return FileSize / 0x200;
}
FileHandle* FATStorage::FF_File;
u64 FATStorage::FF_FileSize;
@ -907,7 +988,7 @@ bool FATStorage::ImportDirectory(const std::string& sourcedir)
return true;
}
u64 FATStorage::GetDirectorySize(fs::path sourcedir)
u64 FATStorage::GetDirectorySize(fs::path sourcedir) const
{
u64 ret = 0;
u32 csize = 0x1000; // this is an estimate
@ -930,19 +1011,15 @@ u64 FATStorage::GetDirectorySize(fs::path sourcedir)
return ret;
}
bool FATStorage::Load(const std::string& filename, u64 size, const std::string& sourcedir)
bool FATStorage::Load(const std::string& filename, u64 size, const std::optional<string>& sourcedir)
{
FilePath = filename;
FileSize = size;
SourceDir = sourcedir;
bool hasdir = !sourcedir.empty();
if (hasdir)
bool hasdir = sourcedir && !sourcedir->empty();
if (sourcedir)
{
if (!fs::is_directory(fs::u8path(sourcedir)))
if (!fs::is_directory(fs::u8path(*sourcedir)))
{
hasdir = false;
SourceDir = "";
SourceDir = std::nullopt;
}
}
@ -1005,7 +1082,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
{
if (hasdir)
{
FileSize = GetDirectorySize(fs::u8path(sourcedir));
FileSize = GetDirectorySize(fs::u8path(*sourcedir));
FileSize += 0x8000000ULL; // 128MB leeway
// make it a power of two
@ -1054,7 +1131,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
if (res == FR_OK)
{
if (hasdir)
ImportDirectory(sourcedir);
ImportDirectory(*sourcedir);
}
f_unmount("0:");
@ -1068,9 +1145,9 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
bool FATStorage::Save()
{
if (SourceDir.empty())
{
return true;
if (!SourceDir)
{ // If we're not syncing the SD card image to a host directory...
return true; // Not an error.
}
FF_File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
@ -1094,7 +1171,7 @@ bool FATStorage::Save()
return false;
}
ExportChanges(SourceDir);
ExportChanges(*SourceDir);
SaveIndex();

View File

@ -22,6 +22,7 @@
#include <stdio.h>
#include <string>
#include <map>
#include <optional>
#include <filesystem>
#include "Platform.h"
@ -30,24 +31,44 @@
namespace melonDS
{
/// Contains information necessary to load an SD card image.
/// The intended use case is for loading homebrew NDS ROMs;
/// you won't know that a ROM is homebrew until you parse it,
/// so if you load the SD card before the ROM
/// then you might end up discarding it.
struct FATStorageArgs
{
std::string Filename;
u64 Size;
bool ReadOnly;
std::optional<std::string> SourceDir;
};
class FATStorage
{
public:
FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir);
FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional<std::string>& sourcedir = std::nullopt);
explicit FATStorage(const FATStorageArgs& args) noexcept;
explicit FATStorage(FATStorageArgs&& args) noexcept;
FATStorage(FATStorage&& other) noexcept;
FATStorage(const FATStorage& other) = delete;
FATStorage& operator=(const FATStorage& other) = delete;
FATStorage& operator=(FATStorage&& other) noexcept;
~FATStorage();
bool Open();
void Close();
bool InjectFile(const std::string& path, u8* data, u32 len);
u32 ReadFile(const std::string& path, u32 start, u32 len, u8* data);
u32 ReadSectors(u32 start, u32 num, u8* data);
u32 WriteSectors(u32 start, u32 num, u8* data);
u32 ReadSectors(u32 start, u32 num, u8* data) const;
u32 WriteSectors(u32 start, u32 num, const u8* data);
[[nodiscard]] bool IsReadOnly() const noexcept { return ReadOnly; }
u64 GetSectorCount() const;
private:
std::string FilePath;
std::string IndexPath;
std::string SourceDir;
std::optional<std::string> SourceDir;
bool ReadOnly;
Platform::FileHandle* File;
@ -74,9 +95,9 @@ private:
void CleanupDirectory(const std::string& sourcedir, const std::string& path, int level);
bool ImportFile(const std::string& path, std::filesystem::path in);
bool ImportDirectory(const std::string& sourcedir);
u64 GetDirectorySize(std::filesystem::path sourcedir);
u64 GetDirectorySize(std::filesystem::path sourcedir) const;
bool Load(const std::string& filename, u64 size, const std::string& sourcedir);
bool Load(const std::string& filename, u64 size, const std::optional<std::string>& sourcedir);
bool Save();
typedef struct

View File

@ -74,12 +74,12 @@ public:
return ret;
}
T Peek()
T Peek() const
{
return Entries[ReadPos];
}
T Peek(u32 offset)
T Peek(u32 offset) const
{
u32 pos = ReadPos + offset;
if (pos >= NumEntries)
@ -88,11 +88,11 @@ public:
return Entries[pos];
}
u32 Level() { return NumOccupied; }
bool IsEmpty() { return NumOccupied == 0; }
bool IsFull() { return NumOccupied >= NumEntries; }
u32 Level() const { return NumOccupied; }
bool IsEmpty() const { return NumOccupied == 0; }
bool IsFull() const { return NumOccupied >= NumEntries; }
bool CanFit(u32 num) { return ((NumOccupied + num) <= NumEntries); }
bool CanFit(u32 num) const { return ((NumOccupied + num) <= NumEntries); }
private:
T Entries[NumEntries] = {0};
@ -164,12 +164,12 @@ public:
return ret;
}
T Peek()
T Peek() const
{
return Entries[ReadPos];
}
T Peek(u32 offset)
T Peek(u32 offset) const
{
u32 pos = ReadPos + offset;
if (pos >= NumEntries)
@ -178,11 +178,11 @@ public:
return Entries[pos];
}
u32 Level() { return NumOccupied; }
bool IsEmpty() { return NumOccupied == 0; }
bool IsFull() { return NumOccupied >= NumEntries; }
u32 Level() const { return NumOccupied; }
bool IsEmpty() const { return NumOccupied == 0; }
bool IsFull() const { return NumOccupied >= NumEntries; }
bool CanFit(u32 num) { return ((NumOccupied + num) <= NumEntries); }
bool CanFit(u32 num) const { return ((NumOccupied + num) <= NumEntries); }
private:
u32 NumEntries;

File diff suppressed because it is too large Load Diff

View File

@ -28,10 +28,13 @@
#ifndef FREEBIOS_H
#define FREEBIOS_H
#include <array>
#include "MemConstants.h"
namespace melonDS
{
extern unsigned char bios_arm7_bin[16384];
extern unsigned char bios_arm9_bin[4096];
extern std::array<u8, ARM7BIOSSize> bios_arm7_bin;
extern std::array<u8, ARM9BIOSSize> bios_arm9_bin;
}
#endif // FREEBIOS_H

File diff suppressed because it is too large Load Diff

View File

@ -1,257 +1,289 @@
/*
Copyright 2016-2023 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 GBACART_H
#define GBACART_H
#include <memory>
#include "types.h"
#include "Savestate.h"
namespace melonDS::GBACart
{
enum CartType
{
Default = 0x001,
Game = 0x101,
GameSolarSensor = 0x102,
RAMExpansion = 0x201,
};
// CartCommon -- base code shared by all cart types
class CartCommon
{
public:
CartCommon();
virtual ~CartCommon();
virtual u32 Type() const = 0;
virtual u32 Checksum() const { return 0; }
virtual void Reset();
virtual void DoSavestate(Savestate* file);
virtual void SetupSave(u32 type);
virtual void LoadSave(const u8* savedata, u32 savelen);
virtual int SetInput(int num, bool pressed);
virtual u16 ROMRead(u32 addr) const;
virtual void ROMWrite(u32 addr, u16 val);
virtual u8 SRAMRead(u32 addr);
virtual void SRAMWrite(u32 addr, u8 val);
[[nodiscard]] virtual const u8* GetROM() const { return nullptr; }
[[nodiscard]] virtual u32 GetROMLength() const { return 0; }
virtual u8* GetSaveMemory() const;
virtual u32 GetSaveMemoryLength() const;
};
// CartGame -- regular retail game cart (ROM, SRAM)
class CartGame : public CartCommon
{
public:
CartGame(u8* rom, u32 len);
virtual ~CartGame() override;
virtual u32 Type() const override { return CartType::Game; }
virtual u32 Checksum() const override;
virtual void Reset() override;
virtual void DoSavestate(Savestate* file) override;
virtual void SetupSave(u32 type) override;
virtual void LoadSave(const u8* savedata, u32 savelen) override;
virtual u16 ROMRead(u32 addr) const override;
virtual void ROMWrite(u32 addr, u16 val) override;
virtual u8 SRAMRead(u32 addr) override;
virtual void SRAMWrite(u32 addr, u8 val) override;
[[nodiscard]] const u8* GetROM() const override { return ROM; }
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
virtual u8* GetSaveMemory() const override;
virtual u32 GetSaveMemoryLength() const override;
protected:
virtual void ProcessGPIO();
u8 SRAMRead_EEPROM(u32 addr);
void SRAMWrite_EEPROM(u32 addr, u8 val);
u8 SRAMRead_FLASH(u32 addr);
void SRAMWrite_FLASH(u32 addr, u8 val);
u8 SRAMRead_SRAM(u32 addr);
void SRAMWrite_SRAM(u32 addr, u8 val);
u8* ROM;
u32 ROMLength;
struct
{
u16 data;
u16 direction;
u16 control;
} GPIO;
enum SaveType
{
S_NULL,
S_EEPROM4K,
S_EEPROM64K,
S_SRAM256K,
S_FLASH512K,
S_FLASH1M
};
// from DeSmuME
struct
{
u8 state;
u8 cmd;
u8 device;
u8 manufacturer;
u8 bank;
} SRAMFlashState;
u8* SRAM;
u32 SRAMLength;
SaveType SRAMType;
};
// CartGameSolarSensor -- Boktai game cart
class CartGameSolarSensor : public CartGame
{
public:
CartGameSolarSensor(u8* rom, u32 len);
virtual ~CartGameSolarSensor() override;
virtual u32 Type() const override { return CartType::GameSolarSensor; }
virtual void Reset() override;
virtual void DoSavestate(Savestate* file) override;
virtual int SetInput(int num, bool pressed) override;
private:
virtual void ProcessGPIO() override;
static const int kLuxLevels[11];
bool LightEdge;
u8 LightCounter;
u8 LightSample;
u8 LightLevel;
};
// CartRAMExpansion -- RAM expansion cart (DS browser, ...)
class CartRAMExpansion : public CartCommon
{
public:
CartRAMExpansion();
~CartRAMExpansion() override;
virtual u32 Type() const override { return CartType::RAMExpansion; }
void Reset() override;
void DoSavestate(Savestate* file) override;
u16 ROMRead(u32 addr) const override;
void ROMWrite(u32 addr, u16 val) override;
private:
u8 RAM[0x800000];
u16 RAMEnable;
};
// possible inputs for GBA carts that might accept user input
enum
{
Input_SolarSensorDown = 0,
Input_SolarSensorUp,
};
class GBACartSlot
{
public:
GBACartSlot() noexcept = default;
~GBACartSlot() noexcept = default;
void Reset() noexcept;
void DoSavestate(Savestate* file) noexcept;
/// Applies the GBACartData to the emulator state and unloads an existing ROM if any.
/// Upon successful insertion, \c cart will be nullptr and the global GBACart state
/// (\c CartROM, CartInserted, etc.) will be updated.
bool InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept;
bool LoadROM(const u8* romdata, u32 romlen) noexcept;
void LoadSave(const u8* savedata, u32 savelen) noexcept;
void LoadAddon(int type) noexcept;
void EjectCart() noexcept;
// TODO: make more flexible, support nonbinary inputs
int SetInput(int num, bool pressed) noexcept;
void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; }
u16 ROMRead(u32 addr) const noexcept;
void ROMWrite(u32 addr, u16 val) noexcept;
u8 SRAMRead(u32 addr) noexcept;
void SRAMWrite(u32 addr, u8 val) noexcept;
/// This function is intended to allow frontends to save and load SRAM
/// without using melonDS APIs.
/// Modifying the emulated SRAM for any other reason is strongly discouraged.
/// The returned pointer may be invalidated if the emulator is reset,
/// or when a new game is loaded.
/// Consequently, don't store the returned pointer for any longer than necessary.
/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr.
[[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
[[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
/// @returns The length of the buffer returned by ::GetSaveMemory()
/// if a cart is loaded and supports SRAM, otherwise zero.
[[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; }
private:
std::unique_ptr<CartCommon> Cart = nullptr;
u16 OpenBusDecay = 0;
};
/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass
/// that can be inserted into the emulator or used to extract information about the cart beforehand.
/// @param romdata The ROM data to parse.
/// The returned cartridge will contain a copy of this data,
/// so the caller may deallocate \c romdata after this function returns.
/// @param romlen The length of the ROM data in bytes.
/// @returns A \c GBACart::CartCommon object representing the parsed ROM,
/// or \c nullptr if the ROM data couldn't be parsed.
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen);
}
#endif // GBACART_H
/*
Copyright 2016-2023 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 GBACART_H
#define GBACART_H
#include <memory>
#include "types.h"
#include "Savestate.h"
namespace melonDS::GBACart
{
enum CartType
{
Default = 0x001,
Game = 0x101,
GameSolarSensor = 0x102,
RAMExpansion = 0x201,
};
// CartCommon -- base code shared by all cart types
class CartCommon
{
public:
virtual ~CartCommon() = default;
[[nodiscard]] u32 Type() const { return CartType; }
virtual u32 Checksum() const { return 0; }
virtual void Reset();
virtual void DoSavestate(Savestate* file);
virtual int SetInput(int num, bool pressed);
virtual u16 ROMRead(u32 addr) const;
virtual void ROMWrite(u32 addr, u16 val);
virtual u8 SRAMRead(u32 addr);
virtual void SRAMWrite(u32 addr, u8 val);
[[nodiscard]] virtual const u8* GetROM() const { return nullptr; }
[[nodiscard]] virtual u32 GetROMLength() const { return 0; }
virtual u8* GetSaveMemory() const;
virtual u32 GetSaveMemoryLength() const;
virtual void SetSaveMemory(const u8* savedata, u32 savelen);
protected:
CartCommon(GBACart::CartType type);
friend class GBACartSlot;
private:
GBACart::CartType CartType;
};
// CartGame -- regular retail game cart (ROM, SRAM)
class CartGame : public CartCommon
{
public:
CartGame(const u8* rom, u32 len, const u8* sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game);
CartGame(std::unique_ptr<u8[]>&& rom, u32 len, std::unique_ptr<u8[]>&& sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game);
~CartGame() override;
u32 Checksum() const override;
void Reset() override;
void DoSavestate(Savestate* file) override;
u16 ROMRead(u32 addr) const override;
void ROMWrite(u32 addr, u16 val) override;
u8 SRAMRead(u32 addr) override;
void SRAMWrite(u32 addr, u8 val) override;
[[nodiscard]] const u8* GetROM() const override { return ROM.get(); }
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
u8* GetSaveMemory() const override;
u32 GetSaveMemoryLength() const override;
void SetSaveMemory(const u8* savedata, u32 savelen) override;
protected:
virtual void ProcessGPIO();
u8 SRAMRead_EEPROM(u32 addr);
void SRAMWrite_EEPROM(u32 addr, u8 val);
u8 SRAMRead_FLASH(u32 addr);
void SRAMWrite_FLASH(u32 addr, u8 val);
u8 SRAMRead_SRAM(u32 addr);
void SRAMWrite_SRAM(u32 addr, u8 val);
std::unique_ptr<u8[]> ROM;
u32 ROMLength;
struct
{
u16 data;
u16 direction;
u16 control;
} GPIO {};
enum SaveType
{
S_NULL,
S_EEPROM4K,
S_EEPROM64K,
S_SRAM256K,
S_FLASH512K,
S_FLASH1M
};
// from DeSmuME
struct
{
u8 state;
u8 cmd;
u8 device;
u8 manufacturer;
u8 bank;
} SRAMFlashState {};
std::unique_ptr<u8[]> SRAM = nullptr;
u32 SRAMLength = 0;
SaveType SRAMType = S_NULL;
private:
void SetupSave(u32 type);
};
// CartGameSolarSensor -- Boktai game cart
class CartGameSolarSensor : public CartGame
{
public:
CartGameSolarSensor(const u8* rom, u32 len, const u8* sram, u32 sramlen);
CartGameSolarSensor(std::unique_ptr<u8[]>&& rom, u32 len, std::unique_ptr<u8[]>&& sram, u32 sramlen);
void Reset() override;
void DoSavestate(Savestate* file) override;
int SetInput(int num, bool pressed) override;
protected:
void ProcessGPIO() override;
private:
static const int kLuxLevels[11];
bool LightEdge = false;
u8 LightCounter = 0;
u8 LightSample = 0;
u8 LightLevel = 0;
};
// CartRAMExpansion -- RAM expansion cart (DS browser, ...)
class CartRAMExpansion : public CartCommon
{
public:
CartRAMExpansion();
~CartRAMExpansion() override;
void Reset() override;
void DoSavestate(Savestate* file) override;
u16 ROMRead(u32 addr) const override;
void ROMWrite(u32 addr, u16 val) override;
private:
u8 RAM[0x800000] {};
u16 RAMEnable = 0;
};
// possible inputs for GBA carts that might accept user input
enum
{
Input_SolarSensorDown = 0,
Input_SolarSensorUp,
};
class GBACartSlot
{
public:
GBACartSlot(std::unique_ptr<CartCommon>&& cart = nullptr) noexcept;
~GBACartSlot() noexcept = default;
void Reset() noexcept;
void DoSavestate(Savestate* file) noexcept;
/// Ejects the cart in the GBA slot (if any)
/// and inserts the given one.
///
/// To insert a cart that does not require ROM data
/// (such as the RAM expansion pack),
/// create it manually with std::make_unique and pass it here.
///
/// @param cart Movable \c unique_ptr to the GBA cart object.
/// May be \c nullptr, in which case the cart slot remains empty.
/// @post \c cart is \c nullptr and the underlying object
/// is moved into the cart slot.
void SetCart(std::unique_ptr<CartCommon>&& cart) noexcept;
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
void LoadAddon(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<CartCommon> EjectCart() noexcept;
// TODO: make more flexible, support nonbinary inputs
int SetInput(int num, bool pressed) noexcept;
void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; }
u16 ROMRead(u32 addr) const noexcept;
void ROMWrite(u32 addr, u16 val) noexcept;
u8 SRAMRead(u32 addr) noexcept;
void SRAMWrite(u32 addr, u8 val) noexcept;
/// This function is intended to allow frontends to save and load SRAM
/// without using melonDS APIs.
/// Modifying the emulated SRAM for any other reason is strongly discouraged.
/// The returned pointer may be invalidated if the emulator is reset,
/// or when a new game is loaded.
/// Consequently, don't store the returned pointer for any longer than necessary.
/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr.
[[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
[[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
/// Sets the loaded cart's SRAM.
/// Does nothing if no cart is inserted
/// or the inserted cart doesn't support SRAM.
///
/// @param savedata Buffer containing the raw contents of the SRAM.
/// The contents of this buffer are copied into the cart slot,
/// so the caller may dispose of it after this method returns.
/// @param savelen The length of the buffer in \c savedata, in bytes.
void SetSaveMemory(const u8* savedata, u32 savelen) noexcept;
/// @returns The length of the buffer returned by ::GetSaveMemory()
/// if a cart is loaded and supports SRAM, otherwise zero.
[[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; }
private:
std::unique_ptr<CartCommon> Cart = nullptr;
u16 OpenBusDecay = 0;
};
/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass
/// that can be inserted into the emulator or used to extract information about the cart beforehand.
/// @param romdata The ROM data to parse.
/// The returned cartridge will contain a copy of this data,
/// so the caller may deallocate \c romdata after this function returns.
/// @param romlen The length of the ROM data in bytes.
/// @returns A \c GBACart::CartCommon object representing the parsed ROM,
/// or \c nullptr if the ROM data couldn't be parsed.
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen);
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen);
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, const u8* sramdata, u32 sramlen);
/// @param romdata The ROM data to parse. Will be moved-from.
/// @param romlen Length of romdata in bytes.
/// @param sramdata The save data to add to the cart.
/// May be \c nullptr, in which case the cart will have no save data.
/// @param sramlen Length of sramdata in bytes.
/// May be zero, in which case the cart will have no save data.
/// @return Unique pointer to the parsed GBA cart,
/// or \c nullptr if there was an error.
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::unique_ptr<u8[]>&& sramdata, u32 sramlen);
}
#endif // GBACART_H

View File

@ -67,7 +67,7 @@ GPU::GPU(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer3d, std::uniqu
NDS(nds),
GPU2D_A(0, *this),
GPU2D_B(1, *this),
GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique<SoftRenderer>(*this)),
GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique<SoftRenderer>()),
GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique<GPU2D::SoftRenderer>(*this))
{
NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank));
@ -75,7 +75,7 @@ GPU::GPU(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer3d, std::uniqu
NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame));
NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO));
FrontBuffer = 0;
InitFramebuffers();
}
GPU::~GPU() noexcept
@ -209,7 +209,7 @@ void GPU::Stop() noexcept
memset(Framebuffer[1][0].get(), 0, fbsize*4);
memset(Framebuffer[1][1].get(), 0, fbsize*4);
GPU3D.Stop();
GPU3D.Stop(*this);
}
void GPU::DoSavestate(Savestate* file) noexcept
@ -294,10 +294,15 @@ void GPU::AssignFramebuffers() noexcept
void GPU::SetRenderer3D(std::unique_ptr<Renderer3D>&& renderer) noexcept
{
if (renderer == nullptr)
GPU3D.SetCurrentRenderer(std::make_unique<SoftRenderer>(*this));
GPU3D.SetCurrentRenderer(std::make_unique<SoftRenderer>());
else
GPU3D.SetCurrentRenderer(std::move(renderer));
InitFramebuffers();
}
void GPU::InitFramebuffers() noexcept
{
int fbsize;
if (GPU3D.IsRendererAccelerated())
fbsize = (256*3 + 1) * 192;
@ -894,7 +899,7 @@ void GPU::StartHBlank(u32 line) noexcept
}
else if (VCount == 215)
{
GPU3D.VCount215();
GPU3D.VCount215(*this);
}
else if (VCount == 262)
{
@ -920,7 +925,7 @@ void GPU::FinishFrame(u32 lines) noexcept
if (GPU3D.AbortFrame)
{
GPU3D.RestartFrame();
GPU3D.RestartFrame(*this);
GPU3D.AbortFrame = false;
}
}
@ -1013,7 +1018,7 @@ void GPU::StartScanline(u32 line) noexcept
// texture memory anyway and only update it before the start
//of the next frame.
// So we can give the rasteriser a bit more headroom
GPU3D.VCount144();
GPU3D.VCount144(*this);
// VBlank
DispStat[0] |= (1<<0);
@ -1033,7 +1038,7 @@ void GPU::StartScanline(u32 line) noexcept
// Need a better way to identify the openGL renderer in particular
if (GPU3D.IsRendererAccelerated())
GPU3D.Blit();
GPU3D.Blit(*this);
}
}
@ -1063,7 +1068,7 @@ void GPU::SetVCount(u16 val) noexcept
}
template <u32 Size, u32 MappingGranularity>
NonStupidBitField<Size/VRAMDirtyGranularity> VRAMTrackingSet<Size, MappingGranularity>::DeriveState(u32* currentMappings, GPU& gpu)
NonStupidBitField<Size/VRAMDirtyGranularity> VRAMTrackingSet<Size, MappingGranularity>::DeriveState(const u32* currentMappings, GPU& gpu)
{
NonStupidBitField<Size/VRAMDirtyGranularity> result;
u16 banksToBeZeroed = 0;
@ -1126,12 +1131,12 @@ NonStupidBitField<Size/VRAMDirtyGranularity> VRAMTrackingSet<Size, MappingGranul
return result;
}
template NonStupidBitField<32*1024/VRAMDirtyGranularity> VRAMTrackingSet<32*1024, 8*1024>::DeriveState(u32*, GPU& gpu);
template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(u32*, GPU& gpu);
template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(u32*, GPU& gpu);
template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(u32*, GPU& gpu);
template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(u32*, GPU& gpu);
template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(u32*, GPU& gpu);
template NonStupidBitField<32*1024/VRAMDirtyGranularity> VRAMTrackingSet<32*1024, 8*1024>::DeriveState(const u32*, GPU& gpu);
template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(const u32*, GPU& gpu);
template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(const u32*, GPU& gpu);
template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(const u32*, GPU& gpu);
template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(const u32*, GPU& gpu);
template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(const u32*, GPU& gpu);

View File

@ -49,7 +49,7 @@ struct VRAMTrackingSet
Mapping[i] = 0x8000;
}
}
NonStupidBitField<Size/VRAMDirtyGranularity> DeriveState(u32* currentMappings, GPU& gpu);
NonStupidBitField<Size/VRAMDirtyGranularity> DeriveState(const u32* currentMappings, GPU& gpu);
};
class GPU
@ -607,6 +607,7 @@ public:
private:
void ResetVRAMCache() noexcept;
void AssignFramebuffers() noexcept;
void InitFramebuffers() noexcept;
template<typename T>
T ReadVRAM_ABGExtPal(u32 addr) const noexcept
{

View File

@ -648,7 +648,7 @@ void Unit::CheckWindows(u32 line)
else if (line == Win1Coords[2]) Win1Active |= 0x1;
}
void Unit::CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow)
void Unit::CalculateWindowMask(u32 line, u8* windowMask, const u8* objWindow)
{
for (u32 i = 0; i < 256; i++)
windowMask[i] = WinCnt[2]; // window outside
@ -694,7 +694,7 @@ void Unit::CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow)
}
}
void Unit::GetBGVRAM(u8*& data, u32& mask)
void Unit::GetBGVRAM(u8*& data, u32& mask) const
{
if (Num == 0)
{
@ -708,7 +708,7 @@ void Unit::GetBGVRAM(u8*& data, u32& mask)
}
}
void Unit::GetOBJVRAM(u8*& data, u32& mask)
void Unit::GetOBJVRAM(u8*& data, u32& mask) const
{
if (Num == 0)
{

View File

@ -52,7 +52,7 @@ public:
void Write16(u32 addr, u16 val);
void Write32(u32 addr, u32 val);
bool UsesFIFO()
bool UsesFIFO() const
{
if (((DispCnt >> 16) & 0x3) == 3)
return true;
@ -72,11 +72,11 @@ public:
u16* GetBGExtPal(u32 slot, u32 pal);
u16* GetOBJExtPal();
void GetBGVRAM(u8*& data, u32& mask);
void GetOBJVRAM(u8*& data, u32& mask);
void GetBGVRAM(u8*& data, u32& mask) const;
void GetOBJVRAM(u8*& data, u32& mask) const;
void UpdateMosaicCounters(u32 line);
void CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow);
void CalculateWindowMask(u32 line, u8* windowMask, const u8* objWindow);
u32 Num;
bool Enabled;

View File

@ -30,7 +30,7 @@ SoftRenderer::SoftRenderer(melonDS::GPU& gpu)
// mosaic table is initialized at compile-time
}
u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2)
u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2) const
{
u32 coloreffect = 0;
u32 eva, evb;

View File

@ -117,7 +117,7 @@ private:
return rb | g | 0xFF000000;
}
u32 ColorComposite(int i, u32 val1, u32 val2);
u32 ColorComposite(int i, u32 val1, u32 val2) const;
template<u32 bgmode> void DrawScanlineBGMode(u32 line);
void DrawScanlineBGMode6(u32 line);

View File

@ -142,10 +142,16 @@ void MatrixLoadIdentity(s32* m);
GPU3D::GPU3D(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer) noexcept :
NDS(nds),
CurrentRenderer(renderer ? std::move(renderer) : std::make_unique<SoftRenderer>(nds.GPU))
CurrentRenderer(renderer ? std::move(renderer) : std::make_unique<SoftRenderer>())
{
}
void GPU3D::SetCurrentRenderer(std::unique_ptr<Renderer3D>&& renderer) noexcept
{
CurrentRenderer = std::move(renderer);
CurrentRenderer->Reset(NDS.GPU);
}
void GPU3D::ResetRenderingState() noexcept
{
RenderNumPolygons = 0;
@ -172,17 +178,6 @@ void GPU3D::Reset() noexcept
CmdStallQueue.Clear();
NumCommands = 0;
CurCommand = 0;
ParamCount = 0;
TotalParams = 0;
NumPushPopCommands = 0;
NumTestCommands = 0;
DispCnt = 0;
AlphaRef = 0;
ZeroDotWLimit = 0; // CHECKME
GXStat = 0;
@ -190,7 +185,6 @@ void GPU3D::Reset() noexcept
memset(ExecParams, 0, 32*4);
ExecParamCount = 0;
Timestamp = 0;
CycleCount = 0;
VertexPipeline = 0;
NormalPipeline = 0;
@ -198,6 +192,8 @@ void GPU3D::Reset() noexcept
VertexSlotCounter = 0;
VertexSlotsFree = 1;
NumPushPopCommands = 0;
NumTestCommands = 0;
MatrixMode = 0;
@ -215,35 +211,86 @@ void GPU3D::Reset() noexcept
memset(PosMatrixStack, 0, 31 * 16*4);
memset(VecMatrixStack, 0, 31 * 16*4);
memset(TexMatrixStack, 0, 16*4);
ProjMatrixStackPointer = 0;
PosMatrixStackPointer = 0;
TexMatrixStackPointer = 0;
NumCommands = 0;
CurCommand = 0;
ParamCount = 0;
TotalParams = 0;
GeometryEnabled = false;
RenderingEnabled = false;
DispCnt = 0;
AlphaRefVal = 0;
AlphaRef = 0;
memset(ToonTable, 0, sizeof(ToonTable));
memset(EdgeTable, 0, sizeof(EdgeTable));
// TODO: confirm initial polyid/color/fog values
FogOffset = 0;
FogColor = 0;
memset(FogDensityTable, 0, sizeof(FogDensityTable));
ClearAttr1 = 0x3F000000;
ClearAttr2 = 0x00007FFF;
ResetRenderingState();
AbortFrame = false;
Timestamp = 0;
PolygonMode = 0;
memset(CurVertex, 0, sizeof(CurVertex));
memset(VertexColor, 0, sizeof(VertexColor));
memset(TexCoords, 0, sizeof(TexCoords));
memset(RawTexCoords, 0, sizeof(RawTexCoords));
memset(Normal, 0, sizeof(Normal));
memset(LightDirection, 0, sizeof(LightDirection));
memset(LightColor, 0, sizeof(LightColor));
memset(MatDiffuse, 0, sizeof(MatDiffuse));
memset(MatAmbient, 0, sizeof(MatAmbient));
memset(MatSpecular, 0, sizeof(MatSpecular));
memset(MatEmission, 0, sizeof(MatSpecular));
UseShininessTable = false;
memset(ShininessTable, 0, sizeof(ShininessTable));
PolygonAttr = 0;
CurPolygonAttr = 0;
TexParam = 0;
TexPalette = 0;
memset(PosTestResult, 0, 4*4);
memset(VecTestResult, 0, 2*3);
memset(TempVertexBuffer, 0, sizeof(TempVertexBuffer));
VertexNum = 0;
VertexNumInPoly = 0;
NumConsecutivePolygons = 0;
LastStripPolygon = nullptr;
NumOpaquePolygons = 0;
CurRAMBank = 0;
CurVertexRAM = &VertexRAM[0];
CurPolygonRAM = &PolygonRAM[0];
NumVertices = 0;
NumPolygons = 0;
NumOpaquePolygons = 0;
// TODO: confirm initial polyid/color/fog values
ClearAttr1 = 0x3F000000;
ClearAttr2 = 0x00007FFF;
CurRAMBank = 0;
FlushRequest = 0;
FlushAttributes = 0;
ResetRenderingState();
RenderXPos = 0;
AbortFrame = false;
if (CurrentRenderer)
CurrentRenderer->Reset(NDS.GPU);
}
void GPU3D::DoSavestate(Savestate* file) noexcept
@ -1455,7 +1502,7 @@ void GPU3D::CalculateLighting() noexcept
}
void GPU3D::BoxTest(u32* params) noexcept
void GPU3D::BoxTest(const u32* params) noexcept
{
Vertex cube[8];
Vertex face[10];
@ -1588,7 +1635,7 @@ void GPU3D::VecTest(u32 param) noexcept
void GPU3D::CmdFIFOWrite(CmdFIFOEntry& entry) noexcept
void GPU3D::CmdFIFOWrite(const CmdFIFOEntry& entry) noexcept
{
if (CmdFIFO.IsEmpty() && !CmdPIPE.IsFull())
{
@ -2329,20 +2376,20 @@ void GPU3D::CheckFIFODMA() noexcept
NDS.CheckDMAs(0, 0x07);
}
void GPU3D::VCount144() noexcept
void GPU3D::VCount144(GPU& gpu) noexcept
{
CurrentRenderer->VCount144();
CurrentRenderer->VCount144(gpu);
}
void GPU3D::RestartFrame() noexcept
void GPU3D::RestartFrame(GPU& gpu) noexcept
{
CurrentRenderer->RestartFrame();
CurrentRenderer->RestartFrame(gpu);
}
void GPU3D::Stop() noexcept
void GPU3D::Stop(const GPU& gpu) noexcept
{
if (CurrentRenderer)
CurrentRenderer->Stop();
CurrentRenderer->Stop(gpu);
}
@ -2435,9 +2482,9 @@ void GPU3D::VBlank() noexcept
}
}
void GPU3D::VCount215() noexcept
void GPU3D::VCount215(GPU& gpu) noexcept
{
CurrentRenderer->RenderFrame();
CurrentRenderer->RenderFrame(gpu);
}
void GPU3D::SetRenderXPos(u16 xpos) noexcept
@ -2897,10 +2944,10 @@ void GPU3D::Write32(u32 addr, u32 val) noexcept
Log(LogLevel::Debug, "unknown GPU3D write32 %08X %08X\n", addr, val);
}
void GPU3D::Blit() noexcept
void GPU3D::Blit(const GPU& gpu) noexcept
{
if (CurrentRenderer)
CurrentRenderer->Blit();
CurrentRenderer->Blit(gpu);
}
Renderer3D::Renderer3D(bool Accelerated)

View File

@ -101,12 +101,12 @@ public:
void CheckFIFOIRQ() noexcept;
void CheckFIFODMA() noexcept;
void VCount144() noexcept;
void VCount144(GPU& gpu) noexcept;
void VBlank() noexcept;
void VCount215() noexcept;
void VCount215(GPU& gpu) noexcept;
void RestartFrame() noexcept;
void Stop() noexcept;
void RestartFrame(GPU& gpu) noexcept;
void Stop(const GPU& gpu) noexcept;
void SetRenderXPos(u16 xpos) noexcept;
[[nodiscard]] u16 GetRenderXPos() const noexcept { return RenderXPos; }
@ -117,7 +117,7 @@ public:
[[nodiscard]] bool IsRendererAccelerated() const noexcept;
[[nodiscard]] Renderer3D& GetCurrentRenderer() noexcept { return *CurrentRenderer; }
[[nodiscard]] const Renderer3D& GetCurrentRenderer() const noexcept { return *CurrentRenderer; }
void SetCurrentRenderer(std::unique_ptr<Renderer3D>&& renderer) noexcept { CurrentRenderer = std::move(renderer); }
void SetCurrentRenderer(std::unique_ptr<Renderer3D>&& renderer) noexcept;
u8 Read8(u32 addr) noexcept;
u16 Read16(u32 addr) noexcept;
@ -125,7 +125,7 @@ public:
void Write8(u32 addr, u8 val) noexcept;
void Write16(u32 addr, u16 val) noexcept;
void Write32(u32 addr, u32 val) noexcept;
void Blit() noexcept;
void Blit(const GPU& gpu) noexcept;
private:
melonDS::NDS& NDS;
typedef union
@ -147,10 +147,10 @@ private:
void SubmitPolygon() noexcept;
void SubmitVertex() noexcept;
void CalculateLighting() noexcept;
void BoxTest(u32* params) noexcept;
void BoxTest(const u32* params) noexcept;
void PosTest() noexcept;
void VecTest(u32 param) noexcept;
void CmdFIFOWrite(CmdFIFOEntry& entry) noexcept;
void CmdFIFOWrite(const CmdFIFOEntry& entry) noexcept;
CmdFIFOEntry CmdFIFORead() noexcept;
void FinishWork(s32 cycles) noexcept;
void VertexPipelineSubmitCmd() noexcept
@ -254,6 +254,7 @@ public:
u32 ClearAttr1 = 0;
u32 ClearAttr2 = 0;
u32 RenderDispCnt = 0;
u8 RenderAlphaRef = 0;
@ -333,19 +334,19 @@ public:
Renderer3D(const Renderer3D&) = delete;
Renderer3D& operator=(const Renderer3D&) = delete;
virtual void Reset() = 0;
virtual void Reset(GPU& gpu) = 0;
// This "Accelerated" flag currently communicates if the framebuffer should
// be allocated differently and other little misc handlers. Ideally there
// are more detailed "traits" that we can ask of the Renderer3D type
const bool Accelerated;
virtual void VCount144() {};
virtual void Stop() {}
virtual void RenderFrame() = 0;
virtual void RestartFrame() {};
virtual void VCount144(GPU& gpu) {};
virtual void Stop(const GPU& gpu) {}
virtual void RenderFrame(GPU& gpu) = 0;
virtual void RestartFrame(GPU& gpu) {};
virtual u32* GetLine(int line) = 0;
virtual void Blit() {};
virtual void Blit(const GPU& gpu) {};
virtual void PrepareCaptureFrame() {}
protected:
Renderer3D(bool Accelerated);

View File

@ -97,9 +97,8 @@ void SetupDefaultTexParams(GLuint tex)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
GLRenderer::GLRenderer(GLCompositor&& compositor, melonDS::GPU& gpu) noexcept :
GLRenderer::GLRenderer(GLCompositor&& compositor) noexcept :
Renderer3D(true),
GPU(gpu),
CurGLCompositor(std::move(compositor))
{
// GLRenderer::New() will be used to actually initialize the renderer;
@ -107,7 +106,7 @@ GLRenderer::GLRenderer(GLCompositor&& compositor, melonDS::GPU& gpu) noexcept :
// so we can just let the destructor clean up a half-initialized renderer.
}
std::unique_ptr<GLRenderer> GLRenderer::New(melonDS::GPU& gpu) noexcept
std::unique_ptr<GLRenderer> GLRenderer::New() noexcept
{
assert(glEnable != nullptr);
@ -117,7 +116,7 @@ std::unique_ptr<GLRenderer> GLRenderer::New(melonDS::GPU& gpu) noexcept
// Will be returned if the initialization succeeds,
// or cleaned up via RAII if it fails.
std::unique_ptr<GLRenderer> result = std::unique_ptr<GLRenderer>(new GLRenderer(std::move(*compositor), gpu));
std::unique_ptr<GLRenderer> result = std::unique_ptr<GLRenderer>(new GLRenderer(std::move(*compositor)));
compositor = std::nullopt;
glEnable(GL_DEPTH_TEST);
@ -333,7 +332,7 @@ GLRenderer::~GLRenderer()
}
}
void GLRenderer::Reset()
void GLRenderer::Reset(GPU& gpu)
{
// This is where the compositor's Reset() method would be called,
// except there's no such method right now.
@ -406,7 +405,7 @@ void GLRenderer::SetRenderSettings(bool betterpolygons, int scale) noexcept
}
void GLRenderer::SetupPolygon(GLRenderer::RendererPolygon* rp, Polygon* polygon)
void GLRenderer::SetupPolygon(GLRenderer::RendererPolygon* rp, Polygon* polygon) const
{
rp->PolyData = polygon;
@ -452,7 +451,7 @@ void GLRenderer::SetupPolygon(GLRenderer::RendererPolygon* rp, Polygon* polygon)
}
}
u32* GLRenderer::SetupVertex(Polygon* poly, int vid, Vertex* vtx, u32 vtxattr, u32* vptr)
u32* GLRenderer::SetupVertex(const Polygon* poly, int vid, const Vertex* vtx, u32 vtxattr, u32* vptr) const
{
u32 z = poly->FinalZ[vid];
u32 w = poly->FinalW[vid];
@ -735,18 +734,18 @@ void GLRenderer::BuildPolygons(GLRenderer::RendererPolygon* polygons, int npolys
NumEdgeIndices = eidx - EdgeIndicesOffset;
}
int GLRenderer::RenderSinglePolygon(int i)
int GLRenderer::RenderSinglePolygon(int i) const
{
RendererPolygon* rp = &PolygonList[i];
const RendererPolygon* rp = &PolygonList[i];
glDrawElements(rp->PrimType, rp->NumIndices, GL_UNSIGNED_SHORT, (void*)(uintptr_t)(rp->IndicesOffset * 2));
return 1;
}
int GLRenderer::RenderPolygonBatch(int i)
int GLRenderer::RenderPolygonBatch(int i) const
{
RendererPolygon* rp = &PolygonList[i];
const RendererPolygon* rp = &PolygonList[i];
GLuint primtype = rp->PrimType;
u32 key = rp->RenderKey;
int numpolys = 0;
@ -754,7 +753,7 @@ int GLRenderer::RenderPolygonBatch(int i)
for (int iend = i; iend < NumFinalPolys; iend++)
{
RendererPolygon* cur_rp = &PolygonList[iend];
const RendererPolygon* cur_rp = &PolygonList[iend];
if (cur_rp->PrimType != primtype) break;
if (cur_rp->RenderKey != key) break;
@ -766,16 +765,16 @@ int GLRenderer::RenderPolygonBatch(int i)
return numpolys;
}
int GLRenderer::RenderPolygonEdgeBatch(int i)
int GLRenderer::RenderPolygonEdgeBatch(int i) const
{
RendererPolygon* rp = &PolygonList[i];
const RendererPolygon* rp = &PolygonList[i];
u32 key = rp->RenderKey;
int numpolys = 0;
u32 numindices = 0;
for (int iend = i; iend < NumFinalPolys; iend++)
{
RendererPolygon* cur_rp = &PolygonList[iend];
const RendererPolygon* cur_rp = &PolygonList[iend];
if (cur_rp->RenderKey != key) break;
numpolys++;
@ -786,14 +785,14 @@ int GLRenderer::RenderPolygonEdgeBatch(int i)
return numpolys;
}
void GLRenderer::RenderSceneChunk(int y, int h)
void GLRenderer::RenderSceneChunk(const GPU3D& gpu3d, int y, int h)
{
u32 flags = 0;
if (GPU.GPU3D.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer;
if (gpu3d.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer;
if (h != 192) glScissor(0, y<<ScaleFactor, 256<<ScaleFactor, h<<ScaleFactor);
GLboolean fogenable = (GPU.GPU3D.RenderDispCnt & (1<<7)) ? GL_TRUE : GL_FALSE;
GLboolean fogenable = (gpu3d.RenderDispCnt & (1<<7)) ? GL_TRUE : GL_FALSE;
// TODO: proper 'equal' depth test!
// (has margin of +-0x200 in Z-buffer mode, +-0xFF in W-buffer mode)
@ -865,7 +864,7 @@ void GLRenderer::RenderSceneChunk(int y, int h)
glEnable(GL_BLEND);
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
if (GPU.GPU3D.RenderDispCnt & (1<<3))
if (gpu3d.RenderDispCnt & (1<<3))
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
else
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE);
@ -877,7 +876,7 @@ void GLRenderer::RenderSceneChunk(int y, int h)
// pass 2: if needed, render translucent pixels that are against background pixels
// when background alpha is zero, those need to be rendered with blending disabled
if ((GPU.GPU3D.RenderClearAttr1 & 0x001F0000) == 0)
if ((gpu3d.RenderClearAttr1 & 0x001F0000) == 0)
{
glDisable(GL_BLEND);
@ -941,7 +940,7 @@ void GLRenderer::RenderSceneChunk(int y, int h)
if (rp->PolyData->IsShadow)
{
// shadow against clear-plane will only pass if its polyID matches that of the clear plane
u32 clrpolyid = (GPU.GPU3D.RenderClearAttr1 >> 24) & 0x3F;
u32 clrpolyid = (gpu3d.RenderClearAttr1 >> 24) & 0x3F;
if (polyid != clrpolyid) { i++; continue; }
glEnable(GL_BLEND);
@ -1089,7 +1088,7 @@ void GLRenderer::RenderSceneChunk(int y, int h)
}
}
if (GPU.GPU3D.RenderDispCnt & 0x00A0) // fog/edge enabled
if (gpu3d.RenderDispCnt & 0x00A0) // fog/edge enabled
{
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glColorMaski(1, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
@ -1111,7 +1110,7 @@ void GLRenderer::RenderSceneChunk(int y, int h)
glBindBuffer(GL_ARRAY_BUFFER, ClearVertexBufferID);
glBindVertexArray(ClearVertexArrayID);
if (GPU.GPU3D.RenderDispCnt & (1<<5))
if (gpu3d.RenderDispCnt & (1<<5))
{
// edge marking
// TODO: depth/polyid values at screen edges
@ -1123,19 +1122,19 @@ void GLRenderer::RenderSceneChunk(int y, int h)
glDrawArrays(GL_TRIANGLES, 0, 2*3);
}
if (GPU.GPU3D.RenderDispCnt & (1<<7))
if (gpu3d.RenderDispCnt & (1<<7))
{
// fog
glUseProgram(FinalPassFogShader[2]);
if (GPU.GPU3D.RenderDispCnt & (1<<6))
if (gpu3d.RenderDispCnt & (1<<6))
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA);
else
glBlendFuncSeparate(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA);
{
u32 c = GPU.GPU3D.RenderFogColor;
u32 c = gpu3d.RenderFogColor;
u32 r = c & 0x1F;
u32 g = (c >> 5) & 0x1F;
u32 b = (c >> 10) & 0x1F;
@ -1150,7 +1149,7 @@ void GLRenderer::RenderSceneChunk(int y, int h)
}
void GLRenderer::RenderFrame()
void GLRenderer::RenderFrame(GPU& gpu)
{
CurShaderID = -1;
@ -1159,11 +1158,11 @@ void GLRenderer::RenderFrame()
ShaderConfig.uScreenSize[0] = ScreenW;
ShaderConfig.uScreenSize[1] = ScreenH;
ShaderConfig.uDispCnt = GPU.GPU3D.RenderDispCnt;
ShaderConfig.uDispCnt = gpu.GPU3D.RenderDispCnt;
for (int i = 0; i < 32; i++)
{
u16 c = GPU.GPU3D.RenderToonTable[i];
u16 c = gpu.GPU3D.RenderToonTable[i];
u32 r = c & 0x1F;
u32 g = (c >> 5) & 0x1F;
u32 b = (c >> 10) & 0x1F;
@ -1175,7 +1174,7 @@ void GLRenderer::RenderFrame()
for (int i = 0; i < 8; i++)
{
u16 c = GPU.GPU3D.RenderEdgeTable[i];
u16 c = gpu.GPU3D.RenderEdgeTable[i];
u32 r = c & 0x1F;
u32 g = (c >> 5) & 0x1F;
u32 b = (c >> 10) & 0x1F;
@ -1186,7 +1185,7 @@ void GLRenderer::RenderFrame()
}
{
u32 c = GPU.GPU3D.RenderFogColor;
u32 c = gpu.GPU3D.RenderFogColor;
u32 r = c & 0x1F;
u32 g = (c >> 5) & 0x1F;
u32 b = (c >> 10) & 0x1F;
@ -1200,12 +1199,12 @@ void GLRenderer::RenderFrame()
for (int i = 0; i < 34; i++)
{
u8 d = GPU.GPU3D.RenderFogDensityTable[i];
u8 d = gpu.GPU3D.RenderFogDensityTable[i];
ShaderConfig.uFogDensity[i][0] = (float)d / 127.0;
}
ShaderConfig.uFogOffset = GPU.GPU3D.RenderFogOffset;
ShaderConfig.uFogShift = GPU.GPU3D.RenderFogShift;
ShaderConfig.uFogOffset = gpu.GPU3D.RenderFogOffset;
ShaderConfig.uFogShift = gpu.GPU3D.RenderFogShift;
glBindBuffer(GL_UNIFORM_BUFFER, ShaderConfigUBO);
void* unibuf = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY);
@ -1218,13 +1217,13 @@ void GLRenderer::RenderFrame()
glBindTexture(GL_TEXTURE_2D, TexMemID);
for (int i = 0; i < 4; i++)
{
u32 mask = GPU.VRAMMap_Texture[i];
u32 mask = gpu.VRAMMap_Texture[i];
u8* vram;
if (!mask) continue;
else if (mask & (1<<0)) vram = GPU.VRAM_A;
else if (mask & (1<<1)) vram = GPU.VRAM_B;
else if (mask & (1<<2)) vram = GPU.VRAM_C;
else if (mask & (1<<3)) vram = GPU.VRAM_D;
else if (mask & (1<<0)) vram = gpu.VRAM_A;
else if (mask & (1<<1)) vram = gpu.VRAM_B;
else if (mask & (1<<2)) vram = gpu.VRAM_C;
else if (mask & (1<<3)) vram = gpu.VRAM_D;
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i*128, 1024, 128, GL_RED_INTEGER, GL_UNSIGNED_BYTE, vram);
}
@ -1234,12 +1233,12 @@ void GLRenderer::RenderFrame()
for (int i = 0; i < 6; i++)
{
// 6 x 16K chunks
u32 mask = GPU.VRAMMap_TexPal[i];
u32 mask = gpu.VRAMMap_TexPal[i];
u8* vram;
if (!mask) continue;
else if (mask & (1<<4)) vram = &GPU.VRAM_E[(i&3)*0x4000];
else if (mask & (1<<5)) vram = GPU.VRAM_F;
else if (mask & (1<<6)) vram = GPU.VRAM_G;
else if (mask & (1<<4)) vram = &gpu.VRAM_E[(i&3)*0x4000];
else if (mask & (1<<5)) vram = gpu.VRAM_F;
else if (mask & (1<<6)) vram = gpu.VRAM_G;
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i*8, 1024, 8, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, vram);
}
@ -1264,13 +1263,13 @@ void GLRenderer::RenderFrame()
glUseProgram(ClearShaderPlain[2]);
glDepthFunc(GL_ALWAYS);
u32 r = GPU.GPU3D.RenderClearAttr1 & 0x1F;
u32 g = (GPU.GPU3D.RenderClearAttr1 >> 5) & 0x1F;
u32 b = (GPU.GPU3D.RenderClearAttr1 >> 10) & 0x1F;
u32 fog = (GPU.GPU3D.RenderClearAttr1 >> 15) & 0x1;
u32 a = (GPU.GPU3D.RenderClearAttr1 >> 16) & 0x1F;
u32 polyid = (GPU.GPU3D.RenderClearAttr1 >> 24) & 0x3F;
u32 z = ((GPU.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF;
u32 r = gpu.GPU3D.RenderClearAttr1 & 0x1F;
u32 g = (gpu.GPU3D.RenderClearAttr1 >> 5) & 0x1F;
u32 b = (gpu.GPU3D.RenderClearAttr1 >> 10) & 0x1F;
u32 fog = (gpu.GPU3D.RenderClearAttr1 >> 15) & 0x1;
u32 a = (gpu.GPU3D.RenderClearAttr1 >> 16) & 0x1F;
u32 polyid = (gpu.GPU3D.RenderClearAttr1 >> 24) & 0x3F;
u32 z = ((gpu.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF;
glStencilFunc(GL_ALWAYS, 0xFF, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
@ -1289,20 +1288,20 @@ void GLRenderer::RenderFrame()
glDrawArrays(GL_TRIANGLES, 0, 2*3);
}
if (GPU.GPU3D.RenderNumPolygons)
if (gpu.GPU3D.RenderNumPolygons)
{
// render shit here
u32 flags = 0;
if (GPU.GPU3D.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer;
if (gpu.GPU3D.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer;
int npolys = 0;
int firsttrans = -1;
for (u32 i = 0; i < GPU.GPU3D.RenderNumPolygons; i++)
for (u32 i = 0; i < gpu.GPU3D.RenderNumPolygons; i++)
{
if (GPU.GPU3D.RenderPolygonRAM[i]->Degenerate) continue;
if (gpu.GPU3D.RenderPolygonRAM[i]->Degenerate) continue;
SetupPolygon(&PolygonList[npolys], GPU.GPU3D.RenderPolygonRAM[i]);
if (firsttrans < 0 && GPU.GPU3D.RenderPolygonRAM[i]->Translucent)
SetupPolygon(&PolygonList[npolys], gpu.GPU3D.RenderPolygonRAM[i]);
if (firsttrans < 0 && gpu.GPU3D.RenderPolygonRAM[i]->Translucent)
firsttrans = npolys;
npolys++;
@ -1319,15 +1318,15 @@ void GLRenderer::RenderFrame()
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, NumIndices * 2, IndexBuffer);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, EdgeIndicesOffset * 2, NumEdgeIndices * 2, IndexBuffer + EdgeIndicesOffset);
RenderSceneChunk(0, 192);
RenderSceneChunk(gpu.GPU3D, 0, 192);
}
FrontBuffer = FrontBuffer ? 0 : 1;
}
void GLRenderer::Stop()
void GLRenderer::Stop(const GPU& gpu)
{
CurGLCompositor.Stop(GPU);
CurGLCompositor.Stop(gpu);
}
void GLRenderer::PrepareCaptureFrame()
@ -1345,9 +1344,9 @@ void GLRenderer::PrepareCaptureFrame()
glReadPixels(0, 0, 256, 192, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
}
void GLRenderer::Blit()
void GLRenderer::Blit(const GPU& gpu)
{
CurGLCompositor.RenderFrame(GPU, *this);
CurGLCompositor.RenderFrame(gpu, *this);
}
u32* GLRenderer::GetLine(int line)

View File

@ -31,7 +31,7 @@ class GLRenderer : public Renderer3D
{
public:
~GLRenderer() override;
void Reset() override;
void Reset(GPU& gpu) override;
void SetRenderSettings(bool betterpolygons, int scale) noexcept;
void SetBetterPolygons(bool betterpolygons) noexcept;
@ -39,22 +39,22 @@ public:
[[nodiscard]] bool GetBetterPolygons() const noexcept { return BetterPolygons; }
[[nodiscard]] int GetScaleFactor() const noexcept { return ScaleFactor; }
void VCount144() override {};
void RenderFrame() override;
void Stop() override;
void VCount144(GPU& gpu) override {};
void RenderFrame(GPU& gpu) override;
void Stop(const GPU& gpu) override;
u32* GetLine(int line) override;
void SetupAccelFrame();
void PrepareCaptureFrame() override;
void Blit() override;
void Blit(const GPU& gpu) override;
[[nodiscard]] const GLCompositor& GetCompositor() const noexcept { return CurGLCompositor; }
GLCompositor& GetCompositor() noexcept { return CurGLCompositor; }
static std::unique_ptr<GLRenderer> New(melonDS::GPU& gpu) noexcept;
static std::unique_ptr<GLRenderer> New() noexcept;
private:
// Used by New()
GLRenderer(GLCompositor&& compositor, GPU& gpu) noexcept;
GLRenderer(GLCompositor&& compositor) noexcept;
// GL version requirements
// * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS)
@ -74,19 +74,18 @@ private:
u32 RenderKey;
};
melonDS::GPU& GPU;
GLCompositor CurGLCompositor;
RendererPolygon PolygonList[2048] {};
bool BuildRenderShader(u32 flags, const char* vs, const char* fs);
void UseRenderShader(u32 flags);
void SetupPolygon(RendererPolygon* rp, Polygon* polygon);
u32* SetupVertex(Polygon* poly, int vid, Vertex* vtx, u32 vtxattr, u32* vptr);
void SetupPolygon(RendererPolygon* rp, Polygon* polygon) const;
u32* SetupVertex(const Polygon* poly, int vid, const Vertex* vtx, u32 vtxattr, u32* vptr) const;
void BuildPolygons(RendererPolygon* polygons, int npolys);
int RenderSinglePolygon(int i);
int RenderPolygonBatch(int i);
int RenderPolygonEdgeBatch(int i);
void RenderSceneChunk(int y, int h);
int RenderSinglePolygon(int i) const;
int RenderPolygonBatch(int i) const;
int RenderPolygonEdgeBatch(int i) const;
void RenderSceneChunk(const GPU3D& gpu3d, int y, int h);
enum
{

View File

@ -42,14 +42,16 @@ void SoftRenderer::StopRenderThread()
}
}
void SoftRenderer::SetupRenderThread()
void SoftRenderer::SetupRenderThread(GPU& gpu)
{
if (Threaded)
{
if (!RenderThreadRunning.load(std::memory_order_relaxed))
{
RenderThreadRunning = true;
RenderThread = Platform::Thread_Create(std::bind(&SoftRenderer::RenderThreadFunc, this));
RenderThread = Platform::Thread_Create([this, &gpu]() {
RenderThreadFunc(gpu);
});
}
// otherwise more than one frame can be queued up at once
@ -71,8 +73,8 @@ void SoftRenderer::SetupRenderThread()
}
SoftRenderer::SoftRenderer(melonDS::GPU& gpu, bool threaded) noexcept
: Renderer3D(false), GPU(gpu), Threaded(threaded)
SoftRenderer::SoftRenderer(bool threaded) noexcept
: Renderer3D(false), Threaded(threaded)
{
Sema_RenderStart = Platform::Semaphore_Create();
Sema_RenderDone = Platform::Semaphore_Create();
@ -92,7 +94,7 @@ SoftRenderer::~SoftRenderer()
Platform::Semaphore_Free(Sema_ScanlineCount);
}
void SoftRenderer::Reset()
void SoftRenderer::Reset(GPU& gpu)
{
memset(ColorBuffer, 0, BufferSize * 2 * 4);
memset(DepthBuffer, 0, BufferSize * 2 * 4);
@ -100,19 +102,19 @@ void SoftRenderer::Reset()
PrevIsShadowMask = false;
SetupRenderThread();
SetupRenderThread(gpu);
}
void SoftRenderer::SetThreaded(bool threaded) noexcept
void SoftRenderer::SetThreaded(bool threaded, GPU& gpu) noexcept
{
if (Threaded != threaded)
{
Threaded = threaded;
SetupRenderThread();
SetupRenderThread(gpu);
}
}
void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha)
void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) const
{
u32 vramaddr = (texparam & 0xFFFF) << 3;
@ -167,10 +169,10 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co
case 1: // A3I5
{
vramaddr += ((t * width) + s);
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
u8 pixel = ReadVRAM_Texture<u8>(vramaddr, gpu);
texpal <<= 4;
*color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x1F)<<1));
*color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x1F)<<1), gpu);
*alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6);
}
break;
@ -178,12 +180,12 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co
case 2: // 4-color
{
vramaddr += (((t * width) + s) >> 2);
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
u8 pixel = ReadVRAM_Texture<u8>(vramaddr, gpu);
pixel >>= ((s & 0x3) << 1);
pixel &= 0x3;
texpal <<= 3;
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1), gpu);
*alpha = (pixel==0) ? alpha0 : 31;
}
break;
@ -191,12 +193,12 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co
case 3: // 16-color
{
vramaddr += (((t * width) + s) >> 1);
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
u8 pixel = ReadVRAM_Texture<u8>(vramaddr, gpu);
if (s & 0x1) pixel >>= 4;
else pixel &= 0xF;
texpal <<= 4;
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1), gpu);
*alpha = (pixel==0) ? alpha0 : 31;
}
break;
@ -204,10 +206,10 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co
case 4: // 256-color
{
vramaddr += ((t * width) + s);
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
u8 pixel = ReadVRAM_Texture<u8>(vramaddr, gpu);
texpal <<= 4;
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1), gpu);
*alpha = (pixel==0) ? alpha0 : 31;
}
break;
@ -221,30 +223,30 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co
if (vramaddr >= 0x40000)
slot1addr += 0x10000;
u8 val = ReadVRAM_Texture<u8>(vramaddr);
u8 val = ReadVRAM_Texture<u8>(vramaddr, gpu);
val >>= (2 * (s & 0x3));
u16 palinfo = ReadVRAM_Texture<u16>(slot1addr);
u16 palinfo = ReadVRAM_Texture<u16>(slot1addr, gpu);
u32 paloffset = (palinfo & 0x3FFF) << 2;
texpal <<= 4;
switch (val & 0x3)
{
case 0:
*color = ReadVRAM_TexPal<u16>(texpal + paloffset);
*color = ReadVRAM_TexPal<u16>(texpal + paloffset, gpu);
*alpha = 31;
break;
case 1:
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 2, gpu);
*alpha = 31;
break;
case 2:
if ((palinfo >> 14) == 1)
{
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset);
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset, gpu);
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2, gpu);
u32 r0 = color0 & 0x001F;
u32 g0 = color0 & 0x03E0;
@ -261,8 +263,8 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co
}
else if ((palinfo >> 14) == 3)
{
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset);
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset, gpu);
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2, gpu);
u32 r0 = color0 & 0x001F;
u32 g0 = color0 & 0x03E0;
@ -278,20 +280,20 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co
*color = r | g | b;
}
else
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 4);
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 4, gpu);
*alpha = 31;
break;
case 3:
if ((palinfo >> 14) == 2)
{
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 6);
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 6, gpu);
*alpha = 31;
}
else if ((palinfo >> 14) == 3)
{
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset);
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset, gpu);
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2, gpu);
u32 r0 = color0 & 0x001F;
u32 g0 = color0 & 0x03E0;
@ -320,10 +322,10 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co
case 6: // A5I3
{
vramaddr += ((t * width) + s);
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
u8 pixel = ReadVRAM_Texture<u8>(vramaddr, gpu);
texpal <<= 4;
*color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x7)<<1));
*color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x7)<<1), gpu);
*alpha = (pixel >> 3);
}
break;
@ -331,7 +333,7 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co
case 7: // direct color
{
vramaddr += (((t * width) + s) << 1);
*color = ReadVRAM_Texture<u16>(vramaddr);
*color = ReadVRAM_Texture<u16>(vramaddr, gpu);
*alpha = (*color & 0x8000) ? 31 : 0;
}
break;
@ -388,7 +390,7 @@ bool DepthTest_LessThan_FrontFacing(s32 dstz, s32 z, u32 dstattr)
return false;
}
u32 SoftRenderer::AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) noexcept
u32 SoftRenderer::AlphaBlend(const GPU3D& gpu3d, u32 srccolor, u32 dstcolor, u32 alpha) const noexcept
{
u32 dstalpha = dstcolor >> 24;
@ -399,7 +401,7 @@ u32 SoftRenderer::AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) noexcept
u32 srcG = (srccolor >> 8) & 0x3F;
u32 srcB = (srccolor >> 16) & 0x3F;
if (GPU.GPU3D.RenderDispCnt & (1<<3))
if (gpu3d.RenderDispCnt & (1<<3))
{
u32 dstR = dstcolor & 0x3F;
u32 dstG = (dstcolor >> 8) & 0x3F;
@ -418,7 +420,7 @@ u32 SoftRenderer::AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) noexcept
return srcR | (srcG << 8) | (srcB << 16) | (dstalpha << 24);
}
u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t)
u32 SoftRenderer::RenderPixel(const GPU& gpu, const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) const
{
u8 r, g, b, a;
@ -428,7 +430,7 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16
if (blendmode == 2)
{
if (GPU.GPU3D.RenderDispCnt & (1<<1))
if (gpu.GPU3D.RenderDispCnt & (1<<1))
{
// highlight mode: color is calculated normally
// except all vertex color components are set
@ -442,7 +444,7 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16
{
// toon mode: vertex color is replaced by toon color
u16 tooncolor = GPU.GPU3D.RenderToonTable[vr >> 1];
u16 tooncolor = gpu.GPU3D.RenderToonTable[vr >> 1];
vr = (tooncolor << 1) & 0x3E; if (vr) vr++;
vg = (tooncolor >> 4) & 0x3E; if (vg) vg++;
@ -450,12 +452,12 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16
}
}
if ((GPU.GPU3D.RenderDispCnt & (1<<0)) && (((polygon->TexParam >> 26) & 0x7) != 0))
if ((gpu.GPU3D.RenderDispCnt & (1<<0)) && (((polygon->TexParam >> 26) & 0x7) != 0))
{
u8 tr, tg, tb;
u16 tcolor; u8 talpha;
TextureLookup(polygon->TexParam, polygon->TexPalette, s, t, &tcolor, &talpha);
TextureLookup(gpu, polygon->TexParam, polygon->TexPalette, s, t, &tcolor, &talpha);
tr = (tcolor << 1) & 0x3E; if (tr) tr++;
tg = (tcolor >> 4) & 0x3E; if (tg) tg++;
@ -503,9 +505,9 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16
a = polyalpha;
}
if ((blendmode == 2) && (GPU.GPU3D.RenderDispCnt & (1<<1)))
if ((blendmode == 2) && (gpu.GPU3D.RenderDispCnt & (1<<1)))
{
u16 tooncolor = GPU.GPU3D.RenderToonTable[vr >> 1];
u16 tooncolor = gpu.GPU3D.RenderToonTable[vr >> 1];
vr = (tooncolor << 1) & 0x3E; if (vr) vr++;
vg = (tooncolor >> 4) & 0x3E; if (vg) vg++;
@ -526,7 +528,7 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16
return r | (g << 8) | (b << 16) | (a << 24);
}
void SoftRenderer::PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow)
void SoftRenderer::PlotTranslucentPixel(const GPU3D& gpu3d, u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow)
{
u32 dstattr = AttrBuffer[pixeladdr];
u32 attr = (polyattr & 0xE0F0) | ((polyattr >> 8) & 0xFF0000) | (1<<22) | (dstattr & 0xFF001F0F);
@ -556,7 +558,7 @@ void SoftRenderer::PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 pol
if (!(dstattr & (1<<15)))
attr &= ~(1<<15);
color = AlphaBlend(color, ColorBuffer[pixeladdr], color>>24);
color = AlphaBlend(gpu3d, color, ColorBuffer[pixeladdr], color>>24);
if (z != -1)
DepthBuffer[pixeladdr] = z;
@ -579,7 +581,7 @@ void SoftRenderer::CheckForLine(RendererPolygon* rp)
rp->Line = false;
}
void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y)
void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y) const
{
Polygon* polygon = rp->PolyData;
@ -606,7 +608,7 @@ void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y
polygon->FinalW[rp->CurVL], polygon->FinalW[rp->NextVL], y);
}
void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32 y)
void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32 y) const
{
Polygon* polygon = rp->PolyData;
@ -633,7 +635,7 @@ void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32
polygon->FinalW[rp->CurVR], polygon->FinalW[rp->NextVR], y);
}
void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* polygon)
void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* polygon) const
{
u32 nverts = polygon->NumVertices;
@ -687,7 +689,7 @@ void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* poly
}
}
void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
void SoftRenderer::RenderShadowMaskScanline(const GPU3D& gpu3d, RendererPolygon* rp, s32 y)
{
Polygon* polygon = rp->PolyData;
@ -772,7 +774,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
std::swap(zl, zr);
// CHECKME: edge fill rules for swapped opaque shadow mask polygons
if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe)
if ((gpu3d.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu3d.RenderDispCnt & (1<<3))) || wireframe)
{
l_filledge = true;
r_filledge = true;
@ -800,7 +802,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
rp->SlopeR.EdgeParams<false>(&r_edgelen, &r_edgecov);
// CHECKME: edge fill rules for unswapped opaque shadow mask polygons
if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe)
if ((gpu3d.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu3d.RenderDispCnt & (1<<3))) || wireframe)
{
l_filledge = true;
r_filledge = true;
@ -821,7 +823,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
// similarly, we can perform alpha test early (checkme)
if (wireframe) polyalpha = 31;
if (polyalpha <= GPU.GPU3D.RenderAlphaRef) return;
if (polyalpha <= gpu3d.RenderAlphaRef) return;
// in wireframe mode, there are special rules for equal Z (TODO)
@ -923,7 +925,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
rp->XR = rp->SlopeR.Step();
}
void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
void SoftRenderer::RenderPolygonScanline(const GPU& gpu, RendererPolygon* rp, s32 y)
{
Polygon* polygon = rp->PolyData;
@ -1015,7 +1017,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
// edges are always filled if antialiasing/edgemarking are enabled,
// if the pixels are translucent and alpha blending is enabled, or if the polygon is wireframe
// checkme: do swapped line polygons exist? if they do then they likely should also be filled
if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe)
if ((gpu.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu.GPU3D.RenderDispCnt & (1<<3))) || wireframe)
{
l_filledge = true;
r_filledge = true;
@ -1050,7 +1052,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
// * edges are filled if both sides are identical and fully overlapping
// edges are always filled if antialiasing/edgemarking are enabled,
// if the pixels are translucent and alpha blending is enabled, or if the polygon is wireframe
if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe)
if ((gpu.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu.GPU3D.RenderDispCnt & (1<<3))) || wireframe)
{
l_filledge = true;
r_filledge = true;
@ -1149,17 +1151,17 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
s16 s = interpX.Interpolate(sl, sr);
s16 t = interpX.Interpolate(tl, tr);
u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t);
u32 color = RenderPixel(gpu, polygon, vr>>3, vg>>3, vb>>3, s, t);
u8 alpha = color >> 24;
// alpha test
if (alpha <= GPU.GPU3D.RenderAlphaRef) continue;
if (alpha <= gpu.GPU3D.RenderAlphaRef) continue;
if (alpha == 31)
{
u32 attr = polyattr | edge;
if (GPU.GPU3D.RenderDispCnt & (1<<4))
if (gpu.GPU3D.RenderDispCnt & (1<<4))
{
// anti-aliasing: all edges are rendered
@ -1189,11 +1191,11 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
else
{
if (!(polygon->Attr & (1<<11))) z = -1;
PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow);
PlotTranslucentPixel(gpu.GPU3D, pixeladdr, color, z, polyattr, polygon->IsShadow);
// blend with bottom pixel too, if needed
if ((dstattr & 0xF) && (pixeladdr < BufferSize))
PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow);
PlotTranslucentPixel(gpu.GPU3D, pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow);
}
}
@ -1245,17 +1247,17 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
s16 s = interpX.Interpolate(sl, sr);
s16 t = interpX.Interpolate(tl, tr);
u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t);
u32 color = RenderPixel(gpu, polygon, vr>>3, vg>>3, vb>>3, s, t);
u8 alpha = color >> 24;
// alpha test
if (alpha <= GPU.GPU3D.RenderAlphaRef) continue;
if (alpha <= gpu.GPU3D.RenderAlphaRef) continue;
if (alpha == 31)
{
u32 attr = polyattr | edge;
if ((GPU.GPU3D.RenderDispCnt & (1<<4)) && (attr & 0xF))
if ((gpu.GPU3D.RenderDispCnt & (1<<4)) && (attr & 0xF))
{
// anti-aliasing: all edges are rendered
@ -1278,11 +1280,11 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
else
{
if (!(polygon->Attr & (1<<11))) z = -1;
PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow);
PlotTranslucentPixel(gpu.GPU3D, pixeladdr, color, z, polyattr, polygon->IsShadow);
// blend with bottom pixel too, if needed
if ((dstattr & 0xF) && (pixeladdr < BufferSize))
PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow);
PlotTranslucentPixel(gpu.GPU3D, pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow);
}
}
@ -1337,17 +1339,17 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
s16 s = interpX.Interpolate(sl, sr);
s16 t = interpX.Interpolate(tl, tr);
u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t);
u32 color = RenderPixel(gpu, polygon, vr>>3, vg>>3, vb>>3, s, t);
u8 alpha = color >> 24;
// alpha test
if (alpha <= GPU.GPU3D.RenderAlphaRef) continue;
if (alpha <= gpu.GPU3D.RenderAlphaRef) continue;
if (alpha == 31)
{
u32 attr = polyattr | edge;
if (GPU.GPU3D.RenderDispCnt & (1<<4))
if (gpu.GPU3D.RenderDispCnt & (1<<4))
{
// anti-aliasing: all edges are rendered
@ -1377,11 +1379,11 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
else
{
if (!(polygon->Attr & (1<<11))) z = -1;
PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow);
PlotTranslucentPixel(gpu.GPU3D, pixeladdr, color, z, polyattr, polygon->IsShadow);
// blend with bottom pixel too, if needed
if ((dstattr & 0xF) && (pixeladdr < BufferSize))
PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow);
PlotTranslucentPixel(gpu.GPU3D, pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow);
}
}
@ -1389,7 +1391,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
rp->XR = rp->SlopeR.Step();
}
void SoftRenderer::RenderScanline(s32 y, int npolys)
void SoftRenderer::RenderScanline(const GPU& gpu, s32 y, int npolys)
{
for (int i = 0; i < npolys; i++)
{
@ -1399,19 +1401,19 @@ void SoftRenderer::RenderScanline(s32 y, int npolys)
if (y >= polygon->YTop && (y < polygon->YBottom || (y == polygon->YTop && polygon->YBottom == polygon->YTop)))
{
if (polygon->IsShadowMask)
RenderShadowMaskScanline(rp, y);
RenderShadowMaskScanline(gpu.GPU3D, rp, y);
else
RenderPolygonScanline(rp, y);
RenderPolygonScanline(gpu, rp, y);
}
}
}
u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr)
u32 SoftRenderer::CalculateFogDensity(const GPU3D& gpu3d, u32 pixeladdr) const
{
u32 z = DepthBuffer[pixeladdr];
u32 densityid, densityfrac;
if (z < GPU.GPU3D.RenderFogOffset)
if (z < gpu3d.RenderFogOffset)
{
densityid = 0;
densityfrac = 0;
@ -1423,8 +1425,8 @@ u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr)
// on hardware, the final value can overflow the 32-bit range with a shift big enough,
// causing fog to 'wrap around' and accidentally apply to larger Z ranges
z -= GPU.GPU3D.RenderFogOffset;
z = (z >> 2) << GPU.GPU3D.RenderFogShift;
z -= gpu3d.RenderFogOffset;
z = (z >> 2) << gpu3d.RenderFogShift;
densityid = z >> 17;
if (densityid >= 32)
@ -1438,20 +1440,20 @@ u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr)
// checkme (may be too precise?)
u32 density =
((GPU.GPU3D.RenderFogDensityTable[densityid] * (0x20000-densityfrac)) +
(GPU.GPU3D.RenderFogDensityTable[densityid+1] * densityfrac)) >> 17;
((gpu3d.RenderFogDensityTable[densityid] * (0x20000-densityfrac)) +
(gpu3d.RenderFogDensityTable[densityid+1] * densityfrac)) >> 17;
if (density >= 127) density = 128;
return density;
}
void SoftRenderer::ScanlineFinalPass(s32 y)
void SoftRenderer::ScanlineFinalPass(const GPU3D& gpu3d, s32 y)
{
// to consider:
// clearing all polygon fog flags if the master flag isn't set?
// merging all final pass loops into one?
if (GPU.GPU3D.RenderDispCnt & (1<<5))
if (gpu3d.RenderDispCnt & (1<<5))
{
// edge marking
// only applied to topmost pixels
@ -1471,7 +1473,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y)
((polyid != (AttrBuffer[pixeladdr-ScanlineWidth] >> 24)) && (z < DepthBuffer[pixeladdr-ScanlineWidth])) ||
((polyid != (AttrBuffer[pixeladdr+ScanlineWidth] >> 24)) && (z < DepthBuffer[pixeladdr+ScanlineWidth])))
{
u16 edgecolor = GPU.GPU3D.RenderEdgeTable[polyid >> 3];
u16 edgecolor = gpu3d.RenderEdgeTable[polyid >> 3];
u32 edgeR = (edgecolor << 1) & 0x3E; if (edgeR) edgeR++;
u32 edgeG = (edgecolor >> 4) & 0x3E; if (edgeG) edgeG++;
u32 edgeB = (edgecolor >> 9) & 0x3E; if (edgeB) edgeB++;
@ -1484,7 +1486,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y)
}
}
if (GPU.GPU3D.RenderDispCnt & (1<<7))
if (gpu3d.RenderDispCnt & (1<<7))
{
// fog
@ -1497,12 +1499,12 @@ void SoftRenderer::ScanlineFinalPass(s32 y)
// TODO: check the 'fog alpha glitch with small Z' GBAtek talks about
bool fogcolor = !(GPU.GPU3D.RenderDispCnt & (1<<6));
bool fogcolor = !(gpu3d.RenderDispCnt & (1<<6));
u32 fogR = (GPU.GPU3D.RenderFogColor << 1) & 0x3E; if (fogR) fogR++;
u32 fogG = (GPU.GPU3D.RenderFogColor >> 4) & 0x3E; if (fogG) fogG++;
u32 fogB = (GPU.GPU3D.RenderFogColor >> 9) & 0x3E; if (fogB) fogB++;
u32 fogA = (GPU.GPU3D.RenderFogColor >> 16) & 0x1F;
u32 fogR = (gpu3d.RenderFogColor << 1) & 0x3E; if (fogR) fogR++;
u32 fogG = (gpu3d.RenderFogColor >> 4) & 0x3E; if (fogG) fogG++;
u32 fogB = (gpu3d.RenderFogColor >> 9) & 0x3E; if (fogB) fogB++;
u32 fogA = (gpu3d.RenderFogColor >> 16) & 0x1F;
for (int x = 0; x < 256; x++)
{
@ -1512,7 +1514,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y)
u32 attr = AttrBuffer[pixeladdr];
if (attr & (1<<15))
{
density = CalculateFogDensity(pixeladdr);
density = CalculateFogDensity(gpu3d, pixeladdr);
srccolor = ColorBuffer[pixeladdr];
srcR = srccolor & 0x3F;
@ -1541,7 +1543,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y)
attr = AttrBuffer[pixeladdr];
if (!(attr & (1<<15))) continue;
density = CalculateFogDensity(pixeladdr);
density = CalculateFogDensity(gpu3d, pixeladdr);
srccolor = ColorBuffer[pixeladdr];
srcR = srccolor & 0x3F;
@ -1562,7 +1564,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y)
}
}
if (GPU.GPU3D.RenderDispCnt & (1<<4))
if (gpu3d.RenderDispCnt & (1<<4))
{
// anti-aliasing
@ -1615,10 +1617,10 @@ void SoftRenderer::ScanlineFinalPass(s32 y)
}
}
void SoftRenderer::ClearBuffers()
void SoftRenderer::ClearBuffers(const GPU& gpu)
{
u32 clearz = ((GPU.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF;
u32 polyid = GPU.GPU3D.RenderClearAttr1 & 0x3F000000; // this sets the opaque polygonID
u32 clearz = ((gpu.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF;
u32 polyid = gpu.GPU3D.RenderClearAttr1 & 0x3F000000; // this sets the opaque polygonID
// fill screen borders for edge marking
@ -1648,17 +1650,17 @@ void SoftRenderer::ClearBuffers()
// clear the screen
if (GPU.GPU3D.RenderDispCnt & (1<<14))
if (gpu.GPU3D.RenderDispCnt & (1<<14))
{
u8 xoff = (GPU.GPU3D.RenderClearAttr2 >> 16) & 0xFF;
u8 yoff = (GPU.GPU3D.RenderClearAttr2 >> 24) & 0xFF;
u8 xoff = (gpu.GPU3D.RenderClearAttr2 >> 16) & 0xFF;
u8 yoff = (gpu.GPU3D.RenderClearAttr2 >> 24) & 0xFF;
for (int y = 0; y < ScanlineWidth*192; y+=ScanlineWidth)
{
for (int x = 0; x < 256; x++)
{
u16 val2 = ReadVRAM_Texture<u16>(0x40000 + (yoff << 9) + (xoff << 1));
u16 val3 = ReadVRAM_Texture<u16>(0x60000 + (yoff << 9) + (xoff << 1));
u16 val2 = ReadVRAM_Texture<u16>(0x40000 + (yoff << 9) + (xoff << 1), gpu);
u16 val3 = ReadVRAM_Texture<u16>(0x60000 + (yoff << 9) + (xoff << 1), gpu);
// TODO: confirm color conversion
u32 r = (val2 << 1) & 0x3E; if (r) r++;
@ -1683,13 +1685,13 @@ void SoftRenderer::ClearBuffers()
else
{
// TODO: confirm color conversion
u32 r = (GPU.GPU3D.RenderClearAttr1 << 1) & 0x3E; if (r) r++;
u32 g = (GPU.GPU3D.RenderClearAttr1 >> 4) & 0x3E; if (g) g++;
u32 b = (GPU.GPU3D.RenderClearAttr1 >> 9) & 0x3E; if (b) b++;
u32 a = (GPU.GPU3D.RenderClearAttr1 >> 16) & 0x1F;
u32 r = (gpu.GPU3D.RenderClearAttr1 << 1) & 0x3E; if (r) r++;
u32 g = (gpu.GPU3D.RenderClearAttr1 >> 4) & 0x3E; if (g) g++;
u32 b = (gpu.GPU3D.RenderClearAttr1 >> 9) & 0x3E; if (b) b++;
u32 a = (gpu.GPU3D.RenderClearAttr1 >> 16) & 0x1F;
u32 color = r | (g << 8) | (b << 16) | (a << 24);
polyid |= (GPU.GPU3D.RenderClearAttr1 & 0x8000);
polyid |= (gpu.GPU3D.RenderClearAttr1 & 0x8000);
for (int y = 0; y < ScanlineWidth*192; y+=ScanlineWidth)
{
@ -1704,7 +1706,7 @@ void SoftRenderer::ClearBuffers()
}
}
void SoftRenderer::RenderPolygons(bool threaded, Polygon** polygons, int npolys)
void SoftRenderer::RenderPolygons(const GPU& gpu, bool threaded, Polygon** polygons, int npolys)
{
int j = 0;
for (int i = 0; i < npolys; i++)
@ -1713,38 +1715,38 @@ void SoftRenderer::RenderPolygons(bool threaded, Polygon** polygons, int npolys)
SetupPolygon(&PolygonList[j++], polygons[i]);
}
RenderScanline(0, j);
RenderScanline(gpu, 0, j);
for (s32 y = 1; y < 192; y++)
{
RenderScanline(y, j);
ScanlineFinalPass(y-1);
RenderScanline(gpu, y, j);
ScanlineFinalPass(gpu.GPU3D, y-1);
if (threaded)
Platform::Semaphore_Post(Sema_ScanlineCount);
}
ScanlineFinalPass(191);
ScanlineFinalPass(gpu.GPU3D, 191);
if (threaded)
Platform::Semaphore_Post(Sema_ScanlineCount);
}
void SoftRenderer::VCount144()
void SoftRenderer::VCount144(GPU& gpu)
{
if (RenderThreadRunning.load(std::memory_order_relaxed) && !GPU.GPU3D.AbortFrame)
if (RenderThreadRunning.load(std::memory_order_relaxed) && !gpu.GPU3D.AbortFrame)
Platform::Semaphore_Wait(Sema_RenderDone);
}
void SoftRenderer::RenderFrame()
void SoftRenderer::RenderFrame(GPU& gpu)
{
auto textureDirty = GPU.VRAMDirty_Texture.DeriveState(GPU.VRAMMap_Texture, GPU);
auto texPalDirty = GPU.VRAMDirty_TexPal.DeriveState(GPU.VRAMMap_TexPal, GPU);
auto textureDirty = gpu.VRAMDirty_Texture.DeriveState(gpu.VRAMMap_Texture, gpu);
auto texPalDirty = gpu.VRAMDirty_TexPal.DeriveState(gpu.VRAMMap_TexPal, gpu);
bool textureChanged = GPU.MakeVRAMFlat_TextureCoherent(textureDirty);
bool texPalChanged = GPU.MakeVRAMFlat_TexPalCoherent(texPalDirty);
bool textureChanged = gpu.MakeVRAMFlat_TextureCoherent(textureDirty);
bool texPalChanged = gpu.MakeVRAMFlat_TexPalCoherent(texPalDirty);
FrameIdentical = !(textureChanged || texPalChanged) && GPU.GPU3D.RenderFrameIdentical;
FrameIdentical = !(textureChanged || texPalChanged) && gpu.GPU3D.RenderFrameIdentical;
if (RenderThreadRunning.load(std::memory_order_relaxed))
{
@ -1752,17 +1754,17 @@ void SoftRenderer::RenderFrame()
}
else if (!FrameIdentical)
{
ClearBuffers();
RenderPolygons(false, &GPU.GPU3D.RenderPolygonRAM[0], GPU.GPU3D.RenderNumPolygons);
ClearBuffers(gpu);
RenderPolygons(gpu, false, &gpu.GPU3D.RenderPolygonRAM[0], gpu.GPU3D.RenderNumPolygons);
}
}
void SoftRenderer::RestartFrame()
void SoftRenderer::RestartFrame(GPU& gpu)
{
SetupRenderThread();
SetupRenderThread(gpu);
}
void SoftRenderer::RenderThreadFunc()
void SoftRenderer::RenderThreadFunc(GPU& gpu)
{
for (;;)
{
@ -1776,8 +1778,8 @@ void SoftRenderer::RenderThreadFunc()
}
else
{
ClearBuffers();
RenderPolygons(true, &GPU.GPU3D.RenderPolygonRAM[0], GPU.GPU3D.RenderNumPolygons);
ClearBuffers(gpu);
RenderPolygons(gpu, true, &gpu.GPU3D.RenderPolygonRAM[0], gpu.GPU3D.RenderNumPolygons);
}
Platform::Semaphore_Post(Sema_RenderDone);

View File

@ -29,19 +29,19 @@ namespace melonDS
class SoftRenderer : public Renderer3D
{
public:
SoftRenderer(melonDS::GPU& gpu, bool threaded = false) noexcept;
SoftRenderer(bool threaded = false) noexcept;
~SoftRenderer() override;
void Reset() override;
void Reset(GPU& gpu) override;
void SetThreaded(bool threaded) noexcept;
void SetThreaded(bool threaded, GPU& gpu) noexcept;
[[nodiscard]] bool IsThreaded() const noexcept { return Threaded; }
void VCount144() override;
void RenderFrame() override;
void RestartFrame() override;
void VCount144(GPU& gpu) override;
void RenderFrame(GPU& gpu) override;
void RestartFrame(GPU& gpu) override;
u32* GetLine(int line) override;
void SetupRenderThread();
void SetupRenderThread(GPU& gpu);
void StopRenderThread();
private:
// Notes on the interpolator:
@ -429,16 +429,16 @@ private:
};
template <typename T>
inline T ReadVRAM_Texture(u32 addr)
inline T ReadVRAM_Texture(u32 addr, const GPU& gpu) const
{
return *(T*)&GPU.VRAMFlat_Texture[addr & 0x7FFFF];
return *(T*)&gpu.VRAMFlat_Texture[addr & 0x7FFFF];
}
template <typename T>
inline T ReadVRAM_TexPal(u32 addr)
inline T ReadVRAM_TexPal(u32 addr, const GPU& gpu) const
{
return *(T*)&GPU.VRAMFlat_TexPal[addr & 0x1FFFF];
return *(T*)&gpu.VRAMFlat_TexPal[addr & 0x1FFFF];
}
u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) noexcept;
u32 AlphaBlend(const GPU3D& gpu3d, u32 srccolor, u32 dstcolor, u32 alpha) const noexcept;
struct RendererPolygon
{
@ -453,24 +453,23 @@ private:
};
melonDS::GPU& GPU;
RendererPolygon PolygonList[2048];
void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha);
u32 RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t);
void PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow);
void TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) const;
u32 RenderPixel(const GPU& gpu, const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) const;
void PlotTranslucentPixel(const GPU3D& gpu3d, u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow);
void CheckForLine(RendererPolygon* rp);
void SetupPolygonLeftEdge(RendererPolygon* rp, s32 y);
void SetupPolygonRightEdge(RendererPolygon* rp, s32 y);
void SetupPolygon(RendererPolygon* rp, Polygon* polygon);
void RenderShadowMaskScanline(RendererPolygon* rp, s32 y);
void RenderPolygonScanline(RendererPolygon* rp, s32 y);
void RenderScanline(s32 y, int npolys);
u32 CalculateFogDensity(u32 pixeladdr);
void ScanlineFinalPass(s32 y);
void ClearBuffers();
void RenderPolygons(bool threaded, Polygon** polygons, int npolys);
void SetupPolygonLeftEdge(RendererPolygon* rp, s32 y) const;
void SetupPolygonRightEdge(RendererPolygon* rp, s32 y) const;
void SetupPolygon(RendererPolygon* rp, Polygon* polygon) const;
void RenderShadowMaskScanline(const GPU3D& gpu3d, RendererPolygon* rp, s32 y);
void RenderPolygonScanline(const GPU& gpu, RendererPolygon* rp, s32 y);
void RenderScanline(const GPU& gpu, s32 y, int npolys);
u32 CalculateFogDensity(const GPU3D& gpu3d, u32 pixeladdr) const;
void ScanlineFinalPass(const GPU3D& gpu3d, s32 y);
void ClearBuffers(const GPU& gpu);
void RenderPolygons(const GPU& gpu, bool threaded, Polygon** polygons, int npolys);
void RenderThreadFunc();
void RenderThreadFunc(GPU& gpu);
// buffer dimensions are 258x194 to add a offscreen 1px border
// which simplifies edge marking tests

View File

@ -46,12 +46,12 @@ public:
JitBlockEntry EntryPoint;
u32* AddressRanges()
{ return &Data[0]; }
u32* AddressMasks()
{ return &Data[NumAddresses]; }
u32* Literals()
{ return &Data[NumAddresses * 2]; }
const u32* AddressRanges() const { return &Data[0]; }
u32* AddressRanges() { return &Data[0]; }
const u32* AddressMasks() const { return &Data[NumAddresses]; }
u32* AddressMasks() { return &Data[NumAddresses]; }
const u32* Literals() const { return &Data[NumAddresses * 2]; }
u32* Literals() { return &Data[NumAddresses * 2]; }
private:
TinyVector<u32> Data;

View File

@ -30,6 +30,10 @@ constexpr u32 NWRAMSize = 0x40000;
constexpr u32 ARM9BIOSSize = 0x1000;
constexpr u32 ARM7BIOSSize = 0x4000;
constexpr u32 DSiBIOSSize = 0x10000;
constexpr u32 ITCMPhysicalSize = 0x8000;
constexpr u32 DTCMPhysicalSize = 0x4000;
constexpr u32 ARM7BIOSCRC32 = 0x1280f0d5;
constexpr u32 ARM9BIOSCRC32 = 0x2ab23573;
}
#endif // MELONDS_MEMCONSTANTS_H

View File

@ -34,6 +34,7 @@
#include "AREngine.h"
#include "Platform.h"
#include "FreeBIOS.h"
#include "Args.h"
#include "DSi.h"
#include "DSi_SPI_TSC.h"
@ -74,19 +75,39 @@ const s32 kIterationCycleMargin = 8;
NDS* NDS::Current = nullptr;
NDS::NDS(int type) noexcept :
NDS::NDS() noexcept :
NDS(
NDSArgs {
nullptr,
nullptr,
bios_arm9_bin,
bios_arm7_bin,
Firmware(0),
}
)
{
}
NDS::NDS(NDSArgs&& args, int type) noexcept :
ConsoleType(type),
JIT(*this),
SPU(*this),
GPU(*this),
SPI(*this),
ARM7BIOS(args.ARM7BIOS),
ARM9BIOS(args.ARM9BIOS),
ARM7BIOSNative(CRC32(ARM7BIOS.data(), ARM7BIOS.size()) == ARM7BIOSCRC32),
ARM9BIOSNative(CRC32(ARM9BIOS.data(), ARM9BIOS.size()) == ARM9BIOSCRC32),
JIT(*this, args.JIT),
SPU(*this, args.BitDepth, args.Interpolation),
GPU(*this, std::move(args.Renderer3D)),
SPI(*this, std::move(args.Firmware)),
RTC(*this),
Wifi(*this),
NDSCartSlot(*this),
GBACartSlot(),
NDSCartSlot(*this, std::move(args.NDSROM)),
GBACartSlot(type == 1 ? nullptr : std::move(args.GBAROM)),
AREngine(*this),
ARM9(*this),
ARM7(*this),
ARM9(*this, args.GDB, args.JIT.has_value()),
ARM7(*this, args.GDB, args.JIT.has_value()),
#ifdef JIT_ENABLED
EnableJIT(args.JIT.has_value()),
#endif
DMAs {
DMA(0, 0, *this),
DMA(0, 1, *this),
@ -187,6 +208,22 @@ void NDS::SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswi
}
}
#ifdef JIT_ENABLED
void NDS::SetJITArgs(std::optional<JITArgs> args) noexcept
{
if (args)
{ // If we want to turn the JIT on...
JIT.SetJITArgs(*args);
}
else if (args.has_value() != EnableJIT)
{ // Else if we want to turn the JIT off, and it wasn't already off...
JIT.ResetBlockCache();
}
EnableJIT = args.has_value();
}
#endif
void NDS::InitTimings()
{
// TODO, eventually:
@ -224,7 +261,7 @@ void NDS::InitTimings()
// handled later: GBA slot, wifi
}
bool NDS::NeedsDirectBoot()
bool NDS::NeedsDirectBoot() const
{
if (ConsoleType == 1)
{
@ -233,12 +270,12 @@ bool NDS::NeedsDirectBoot()
}
else
{
// internal BIOS does not support direct boot
if (!Platform::GetConfigBool(Platform::ExternalBIOSEnable))
// DSi/3DS firmwares aren't bootable, neither is the generated firmware
if (!SPI.GetFirmware().IsBootable())
return true;
// DSi/3DS firmwares aren't bootable
if (!SPI.GetFirmware()->IsBootable())
// FreeBIOS requires direct boot (it can't boot firmware)
if (!IsLoadedARM9BIOSKnownNative() || !IsLoadedARM7BIOSKnownNative())
return true;
return false;
@ -252,6 +289,13 @@ void NDS::SetupDirectBoot()
const u8* cartrom = NDSCartSlot.GetCart()->GetROM();
MapSharedWRAM(3);
// Copy the Nintendo logo from the NDS ROM header to the ARM9 BIOS if using FreeBIOS
// Games need this for DS<->GBA comm to work
if (!IsLoadedARM9BIOSKnownNative())
{
memcpy(ARM9BIOS.data() + 0x20, header.NintendoLogo, 0x9C);
}
// setup main RAM data
for (u32 i = 0; i < 0x170; i+=4)
@ -378,10 +422,6 @@ void NDS::Reset()
Platform::FileHandle* f;
u32 i;
#ifdef JIT_ENABLED
EnableJIT = Platform::GetConfigBool(Platform::JIT_Enable);
#endif
RunningGame = false;
LastSysClockCycles = 0;
@ -489,28 +529,6 @@ void NDS::Reset()
SPI.Reset();
RTC.Reset();
Wifi.Reset();
// TODO: move the SOUNDBIAS/degrade logic to SPU?
// The SOUNDBIAS register does nothing on DSi
SPU.SetApplyBias(ConsoleType == 0);
bool degradeAudio = true;
if (ConsoleType == 1)
{
//DSi::Reset();
KeyInput &= ~(1 << (16+6));
degradeAudio = false;
}
int bitDepth = Platform::GetConfigInt(Platform::AudioBitDepth);
if (bitDepth == 1) // Always 10-bit
degradeAudio = true;
else if (bitDepth == 2) // Always 16-bit
degradeAudio = false;
SPU.SetDegrade10Bit(degradeAudio);
}
void NDS::Start()
@ -710,42 +728,27 @@ bool NDS::DoSavestate(Savestate* file)
return true;
}
bool NDS::LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen)
void NDS::SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart)
{
if (!NDSCartSlot.LoadROM(romdata, romlen))
return false;
if (savedata && savelen)
NDSCartSlot.LoadSave(savedata, savelen);
return true;
NDSCartSlot.SetCart(std::move(cart));
// The existing cart will always be ejected;
// if cart is null, then that's equivalent to ejecting a cart
// without inserting a new one.
}
void NDS::LoadSave(const u8* savedata, u32 savelen)
void NDS::SetNDSSave(const u8* savedata, u32 savelen)
{
if (savedata && savelen)
NDSCartSlot.LoadSave(savedata, savelen);
NDSCartSlot.SetSaveMemory(savedata, savelen);
}
void NDS::EjectCart()
void NDS::SetGBASave(const u8* savedata, u32 savelen)
{
NDSCartSlot.EjectCart();
}
if (ConsoleType == 0 && savedata && savelen)
{
GBACartSlot.SetSaveMemory(savedata, savelen);
}
bool NDS::CartInserted()
{
return NDSCartSlot.GetCart() != nullptr;
}
bool NDS::LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen)
{
if (!GBACartSlot.LoadROM(romdata, romlen))
return false;
if (savedata && savelen)
GBACartSlot.LoadSave(savedata, savelen);
return true;
}
void NDS::LoadGBAAddon(int type)
@ -753,24 +756,21 @@ void NDS::LoadGBAAddon(int type)
GBACartSlot.LoadAddon(type);
}
void NDS::EjectGBACart()
{
GBACartSlot.EjectCart();
}
void NDS::LoadBIOS()
{
Reset();
}
bool NDS::IsLoadedARM9BIOSBuiltIn()
void NDS::SetARM7BIOS(const std::array<u8, ARM7BIOSSize>& bios) noexcept
{
return memcmp(ARM9BIOS, bios_arm9_bin, sizeof(NDS::ARM9BIOS)) == 0;
ARM7BIOS = bios;
ARM7BIOSNative = CRC32(ARM7BIOS.data(), ARM7BIOS.size()) == ARM7BIOSCRC32;
}
bool NDS::IsLoadedARM7BIOSBuiltIn()
void NDS::SetARM9BIOS(const std::array<u8, ARM9BIOSSize>& bios) noexcept
{
return memcmp(ARM7BIOS, bios_arm7_bin, sizeof(NDS::ARM7BIOS)) == 0;
ARM9BIOS = bios;
ARM9BIOSNative = CRC32(ARM9BIOS.data(), ARM9BIOS.size()) == ARM9BIOSCRC32;
}
u64 NDS::NextTarget()
@ -1169,7 +1169,7 @@ void NDS::SetKeyMask(u32 mask)
CheckKeyIRQ(1, oldkey, KeyInput);
}
bool NDS::IsLidClosed()
bool NDS::IsLidClosed() const
{
if (KeyInput & (1<<23)) return true;
return false;
@ -1339,7 +1339,7 @@ void NDS::SetIRQ(u32 cpu, u32 irq)
{
CPUStop &= ~CPUStop_Sleep;
CPUStop |= CPUStop_Wakeup;
GPU.GPU3D.RestartFrame();
GPU.GPU3D.RestartFrame(GPU);
}
}
}
@ -1362,7 +1362,7 @@ void NDS::ClearIRQ2(u32 irq)
UpdateIRQ(1);
}
bool NDS::HaltInterrupted(u32 cpu)
bool NDS::HaltInterrupted(u32 cpu) const
{
if (cpu == 0)
{
@ -1433,7 +1433,7 @@ void NDS::EnterSleepMode()
ARM7.Halt(2);
}
u32 NDS::GetPC(u32 cpu)
u32 NDS::GetPC(u32 cpu) const
{
return cpu ? ARM7.R[15] : ARM9.R[15];
}
@ -1661,7 +1661,7 @@ void NDS::TimerStart(u32 id, u16 cnt)
bool NDS::DMAsInMode(u32 cpu, u32 mode)
bool NDS::DMAsInMode(u32 cpu, u32 mode) const
{
cpu <<= 2;
if (DMAs[cpu+0].IsInMode(mode)) return true;
@ -1672,7 +1672,7 @@ bool NDS::DMAsInMode(u32 cpu, u32 mode)
return false;
}
bool NDS::DMAsRunning(u32 cpu)
bool NDS::DMAsRunning(u32 cpu) const
{
cpu <<= 2;
if (DMAs[cpu+0].IsRunning()) return true;
@ -2252,7 +2252,7 @@ bool NDS::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
if ((addr & 0xFFFFF000) == 0xFFFF0000 && !write)
{
region->Mem = ARM9BIOS;
region->Mem = &ARM9BIOS[0];
region->Mask = 0xFFF;
return true;
}
@ -2700,7 +2700,7 @@ bool NDS::ARM7GetMemRegion(u32 addr, bool write, MemRegion* region)
{
if (ARM7.R[15] < 0x4000 && (addr >= ARM7BIOSProt || ARM7.R[15] < ARM7BIOSProt))
{
region->Mem = ARM7BIOS;
region->Mem = &ARM7BIOS[0];
region->Mask = 0x3FFF;
return true;
}
@ -4216,4 +4216,4 @@ void NDS::ARM7IOWrite32(u32 addr, u32 val)
Log(LogLevel::Debug, "unknown ARM7 IO write32 %08X %08X %08X\n", addr, val, ARM7.R[15]);
}
}
}

119
src/NDS.h
View File

@ -21,7 +21,7 @@
#include <memory>
#include <string>
#include <memory>
#include <optional>
#include <functional>
#include "Platform.h"
@ -36,7 +36,12 @@
#include "AREngine.h"
#include "GPU.h"
#include "ARMJIT.h"
#include "MemRegion.h"
#include "ARMJIT_Memory.h"
#include "ARM.h"
#include "CRC32.h"
#include "DMA.h"
#include "FreeBIOS.h"
// when touching the main loop/timing code, pls test a lot of shit
// with this enabled, to make sure it doesn't desync
@ -44,7 +49,8 @@
namespace melonDS
{
struct NDSArgs;
class Firmware;
enum
{
Event_LCD = 0,
@ -217,11 +223,12 @@ class ARMJIT;
class NDS
{
public:
private:
#ifdef JIT_ENABLED
bool EnableJIT;
#endif
public: // TODO: Encapsulate the rest of these members
int ConsoleType;
int CurCPU;
@ -255,8 +262,14 @@ public:
u8 ROMSeed0[2*8];
u8 ROMSeed1[2*8];
u8 ARM9BIOS[0x1000];
u8 ARM7BIOS[0x4000];
protected:
// These BIOS arrays should be declared *before* the component objects (JIT, SPI, etc.)
// so that they're initialized before the component objects' constructors run.
std::array<u8, ARM9BIOSSize> ARM9BIOS;
std::array<u8, ARM7BIOSSize> ARM7BIOS;
bool ARM9BIOSNative;
bool ARM7BIOSNative;
public: // TODO: Encapsulate the rest of these members
u16 ARM7BIOSProt;
u8* MainRAM;
@ -303,25 +316,70 @@ public:
void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq);
void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq);
// 0=DS 1=DSi
void SetConsoleType(int type);
void LoadBIOS();
bool IsLoadedARM9BIOSBuiltIn();
bool IsLoadedARM7BIOSBuiltIn();
virtual bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen);
void LoadSave(const u8* savedata, u32 savelen);
virtual void EjectCart();
bool CartInserted();
/// @return \c true if the loaded ARM9 BIOS image is a known dump
/// of a native DS-compatible ARM9 BIOS.
[[nodiscard]] bool IsLoadedARM9BIOSKnownNative() const noexcept { return ARM9BIOSNative; }
[[nodiscard]] const std::array<u8, ARM9BIOSSize>& GetARM9BIOS() const noexcept { return ARM9BIOS; }
void SetARM9BIOS(const std::array<u8, ARM9BIOSSize>& bios) noexcept;
virtual bool NeedsDirectBoot();
[[nodiscard]] const std::array<u8, ARM7BIOSSize>& GetARM7BIOS() const noexcept { return ARM7BIOS; }
void SetARM7BIOS(const std::array<u8, ARM7BIOSSize>& bios) noexcept;
/// @return \c true if the loaded ARM7 BIOS image is a known dump
/// of a native DS-compatible ARM9 BIOS.
[[nodiscard]] bool IsLoadedARM7BIOSKnownNative() const noexcept { return ARM7BIOSNative; }
[[nodiscard]] NDSCart::CartCommon* GetNDSCart() { return NDSCartSlot.GetCart(); }
[[nodiscard]] const NDSCart::CartCommon* GetNDSCart() const { return NDSCartSlot.GetCart(); }
virtual void SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart);
[[nodiscard]] bool CartInserted() const noexcept { return NDSCartSlot.GetCart() != nullptr; }
virtual std::unique_ptr<NDSCart::CartCommon> EjectCart() { return NDSCartSlot.EjectCart(); }
[[nodiscard]] u8* GetNDSSave() { return NDSCartSlot.GetSaveMemory(); }
[[nodiscard]] const u8* GetNDSSave() const { return NDSCartSlot.GetSaveMemory(); }
[[nodiscard]] u32 GetNDSSaveLength() const { return NDSCartSlot.GetSaveMemoryLength(); }
void SetNDSSave(const u8* savedata, u32 savelen);
const Firmware& GetFirmware() const { return SPI.GetFirmwareMem()->GetFirmware(); }
Firmware& GetFirmware() { return SPI.GetFirmwareMem()->GetFirmware(); }
void SetFirmware(Firmware&& firmware) { SPI.GetFirmwareMem()->SetFirmware(std::move(firmware)); }
const Renderer3D& GetRenderer3D() const noexcept { return GPU.GetRenderer3D(); }
Renderer3D& GetRenderer3D() noexcept { return GPU.GetRenderer3D(); }
void SetRenderer3D(std::unique_ptr<Renderer3D>&& renderer) noexcept
{
if (renderer != nullptr)
GPU.SetRenderer3D(std::move(renderer));
}
virtual bool NeedsDirectBoot() const;
void SetupDirectBoot(const std::string& romname);
virtual void SetupDirectBoot();
bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen);
[[nodiscard]] GBACart::CartCommon* GetGBACart() { return (ConsoleType == 1) ? nullptr : GBACartSlot.GetCart(); }
[[nodiscard]] const GBACart::CartCommon* GetGBACart() const { return (ConsoleType == 1) ? nullptr : GBACartSlot.GetCart(); }
/// Inserts a GBA cart into the emulated console's Slot-2.
///
/// @param cart The GBA cart, most likely (but not necessarily) returned from GBACart::ParseROM.
/// To insert an accessory that doesn't use a ROM image
/// (e.g. the Expansion Pak), create it manually and pass it here.
/// If \c nullptr, the existing cart is ejected.
/// If this is a DSi, this method does nothing.
///
/// @post \c cart is \c nullptr and this NDS takes ownership
/// of the cart object it held, if any.
void SetGBACart(std::unique_ptr<GBACart::CartCommon>&& cart) { if (ConsoleType == 0) GBACartSlot.SetCart(std::move(cart)); }
u8* GetGBASave() { return GBACartSlot.GetSaveMemory(); }
const u8* GetGBASave() const { return GBACartSlot.GetSaveMemory(); }
u32 GetGBASaveLength() const { return GBACartSlot.GetSaveMemoryLength(); }
void SetGBASave(const u8* savedata, u32 savelen);
void LoadGBAAddon(int type);
void EjectGBACart();
std::unique_ptr<GBACart::CartCommon> EjectGBACart() { return GBACartSlot.EjectCart(); }
u32 RunFrame();
@ -332,10 +390,10 @@ public:
void SetKeyMask(u32 mask);
bool IsLidClosed();
bool IsLidClosed() const;
void SetLidClosed(bool closed);
virtual void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) {}
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);
@ -354,20 +412,20 @@ public:
void ClearIRQ(u32 cpu, u32 irq);
void SetIRQ2(u32 irq);
void ClearIRQ2(u32 irq);
bool HaltInterrupted(u32 cpu);
bool HaltInterrupted(u32 cpu) const;
void StopCPU(u32 cpu, u32 mask);
void ResumeCPU(u32 cpu, u32 mask);
void GXFIFOStall();
void GXFIFOUnstall();
u32 GetPC(u32 cpu);
u32 GetPC(u32 cpu) const;
u64 GetSysClockCycles(int num);
void NocashPrint(u32 cpu, u32 addr);
void MonitorARM9Jump(u32 addr);
virtual bool DMAsInMode(u32 cpu, u32 mode);
virtual bool DMAsRunning(u32 cpu);
virtual bool DMAsInMode(u32 cpu, u32 mode) const;
virtual bool DMAsRunning(u32 cpu) const;
virtual void CheckDMAs(u32 cpu, u32 mode);
virtual void StopDMAs(u32 cpu, u32 mode);
@ -405,6 +463,14 @@ public:
virtual void ARM7IOWrite16(u32 addr, u16 val);
virtual void ARM7IOWrite32(u32 addr, u32 val);
#ifdef JIT_ENABLED
[[nodiscard]] bool IsJITEnabled() const noexcept { return EnableJIT; }
void SetJITArgs(std::optional<JITArgs> args) noexcept;
#else
[[nodiscard]] bool IsJITEnabled() const noexcept { return false; }
void SetJITArgs(std::optional<JITArgs> args) noexcept {}
#endif
private:
void InitTimings();
u32 SchedListMask;
@ -456,7 +522,8 @@ private:
template <bool EnableJIT>
u32 RunFrame();
public:
NDS() noexcept : NDS(0) {}
NDS(NDSArgs&& args) noexcept : NDS(std::move(args), 0) {}
NDS() noexcept;
virtual ~NDS() noexcept;
NDS(const NDS&) = delete;
NDS& operator=(const NDS&) = delete;
@ -465,7 +532,7 @@ public:
// 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;
protected:
explicit NDS(int type) noexcept;
explicit NDS(NDSArgs&& args, int type) noexcept;
virtual void DoSavestateExtra(Savestate* file) {}
};

View File

@ -20,12 +20,12 @@
#include "NDS.h"
#include "DSi.h"
#include "NDSCart.h"
#include "ARM.h"
#include "CRC32.h"
#include "Platform.h"
#include "ROMList.h"
#include "melonDLDI.h"
#include "xxhash/xxhash.h"
#include "FATStorage.h"
#include "Utils.h"
namespace melonDS
{
@ -43,12 +43,12 @@ enum
// SRAM TODO: emulate write delays???
u32 ByteSwap(u32 val)
constexpr u32 ByteSwap(u32 val)
{
return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24);
}
void NDSCartSlot::Key1_Encrypt(u32* data) noexcept
void NDSCartSlot::Key1_Encrypt(u32* data) const noexcept
{
u32 y = data[0];
u32 x = data[1];
@ -69,7 +69,7 @@ void NDSCartSlot::Key1_Encrypt(u32* data) noexcept
data[1] = y ^ Key1_KeyBuf[0x11];
}
void NDSCartSlot::Key1_Decrypt(u32* data) noexcept
void NDSCartSlot::Key1_Decrypt(u32* data) const noexcept
{
u32 y = data[0];
u32 x = data[1];
@ -109,9 +109,9 @@ void NDSCartSlot::Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept
}
}
void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength) noexcept
void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept
{
if (externalBios)
if (NDS.IsLoadedARM7BIOSKnownNative())
{
u32 expected_bios_length = dsi ? 0x10000 : 0x4000;
if (biosLength != expected_bios_length)
@ -136,9 +136,9 @@ void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 bio
}
}
void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength) noexcept
void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept
{
Key1_LoadKeyBuf(dsi, Platform::GetConfigBool(Platform::ExternalBIOSEnable), bios, biosLength);
Key1_LoadKeyBuf(dsi, bios, biosLength);
u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
if (level >= 1) Key1_ApplyKeycode(keycode, mod);
@ -152,7 +152,7 @@ void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8
}
void NDSCartSlot::Key2_Encrypt(u8* data, u32 len) noexcept
void NDSCartSlot::Key2_Encrypt(const u8* data, u32 len) noexcept
{
for (u32 i = 0; i < len; i++)
{
@ -173,27 +173,29 @@ void NDSCartSlot::Key2_Encrypt(u8* data, u32 len) noexcept
}
CartCommon::CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams)
CartCommon::CartCommon(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, melonDS::NDSCart::CartType type) :
CartCommon(CopyToUnique(rom, len), len, chipid, badDSiDump, romparams, type)
{
ROM = rom;
ROMLength = len;
ChipID = chipid;
ROMParams = romparams;
}
memcpy(&Header, rom, sizeof(Header));
CartCommon::CartCommon(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, melonDS::NDSCart::CartType type) :
ROM(std::move(rom)),
ROMLength(len),
ChipID(chipid),
ROMParams(romparams),
CartType(type)
{
memcpy(&Header, ROM.get(), sizeof(Header));
IsDSi = Header.IsDSi() && !badDSiDump;
DSiBase = Header.DSiRegionStart << 19;
}
CartCommon::~CartCommon()
{
delete[] ROM;
}
CartCommon::~CartCommon() = default;
u32 CartCommon::Checksum() const
{
const NDSHeader& header = GetHeader();
u32 crc = CRC32(ROM, 0x40);
u32 crc = CRC32(ROM.get(), 0x40);
crc = CRC32(&ROM[header.ARM9ROMOffset], header.ARM9Size, crc);
crc = CRC32(&ROM[header.ARM7ROMOffset], header.ARM7Size, crc);
@ -230,15 +232,7 @@ void CartCommon::DoSavestate(Savestate* file)
file->Bool32(&DSiMode);
}
void CartCommon::SetupSave(u32 type)
{
}
void CartCommon::LoadSave(const u8* savedata, u32 savelen)
{
}
int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len)
int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len)
{
if (CmdEncMode == 0)
{
@ -267,7 +261,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* da
case 0x3C:
CmdEncMode = 1;
cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, nds.ARM7BIOS, sizeof(NDS::ARM7BIOS));
cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, nds.GetARM7BIOS().data(), ARM7BIOSSize);
DSiMode = false;
return 0;
@ -276,7 +270,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* da
{
auto& dsi = static_cast<DSi&>(nds);
CmdEncMode = 1;
cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, dsi.ARM7iBIOS, sizeof(DSi::ARM7iBIOS));
cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, &dsi.ARM7iBIOS[0], sizeof(DSi::ARM7iBIOS));
DSiMode = true;
}
return 0;
@ -351,7 +345,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* da
return 0;
}
void CartCommon::ROMCommandFinish(u8* cmd, u8* data, u32 len)
void CartCommon::ROMCommandFinish(const u8* cmd, u8* data, u32 len)
{
}
@ -360,23 +354,13 @@ u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last)
return 0xFF;
}
u8 *CartCommon::GetSaveMemory() const
{
return nullptr;
}
u32 CartCommon::GetSaveMemoryLength() const
{
return 0;
}
void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset)
void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset) const
{
if (addr >= ROMLength) return;
if ((addr+len) > ROMLength)
len = ROMLength - addr;
memcpy(data+offset, ROM+addr, len);
memcpy(data+offset, ROM.get()+addr, len);
}
const NDSBanner* CartCommon::Banner() const
@ -385,22 +369,68 @@ const NDSBanner* CartCommon::Banner() const
size_t bannersize = header.IsDSi() ? 0x23C0 : 0xA40;
if (header.BannerOffset >= 0x200 && header.BannerOffset < (ROMLength - bannersize))
{
return reinterpret_cast<const NDSBanner*>(ROM + header.BannerOffset);
return reinterpret_cast<const NDSBanner*>(ROM.get() + header.BannerOffset);
}
return nullptr;
}
CartRetail::CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams) : CartCommon(rom, len, chipid, badDSiDump, romparams)
CartRetail::CartRetail(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen, melonDS::NDSCart::CartType type) :
CartRetail(CopyToUnique(rom, len), len, chipid, badDSiDump, romparams, std::move(sram), sramlen, type)
{
SRAM = nullptr;
}
CartRetail::~CartRetail()
CartRetail::CartRetail(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen, melonDS::NDSCart::CartType type) :
CartCommon(std::move(rom), len, chipid, badDSiDump, romparams, type)
{
if (SRAM) delete[] SRAM;
u32 savememtype = ROMParams.SaveMemType <= 10 ? ROMParams.SaveMemType : 0;
constexpr int sramlengths[] =
{
0,
512,
8192, 65536, 128*1024,
256*1024, 512*1024, 1024*1024,
8192*1024, 16384*1024, 65536*1024
};
SRAMLength = sramlengths[savememtype];
if (SRAMLength)
{ // If this cart should have any save data...
if (sram && sramlen == SRAMLength)
{ // If we were given save data that already has the correct length...
SRAM = std::move(sram);
}
else
{ // Copy in what we can, truncate the rest.
SRAM = std::make_unique<u8[]>(SRAMLength);
memset(SRAM.get(), 0xFF, SRAMLength);
if (sram)
{ // If we have anything to copy, that is.
memcpy(SRAM.get(), sram.get(), std::min(sramlen, SRAMLength));
}
}
}
switch (savememtype)
{
case 1: SRAMType = 1; break; // EEPROM, small
case 2:
case 3:
case 4: SRAMType = 2; break; // EEPROM, regular
case 5:
case 6:
case 7: SRAMType = 3; break; // FLASH
case 8:
case 9:
case 10: SRAMType = 4; break; // NAND
default: SRAMType = 0; break; // ...whatever else
}
}
CartRetail::~CartRetail() = default;
// std::unique_ptr cleans up the SRAM and ROM
void CartRetail::Reset()
{
CartCommon::Reset();
@ -425,13 +455,11 @@ void CartRetail::DoSavestate(Savestate* file)
Log(LogLevel::Warn, "savestate: VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength);
Log(LogLevel::Warn, "oh well. loading it anyway. adsfgdsf\n");
if (oldlen) delete[] SRAM;
SRAM = nullptr;
if (SRAMLength) SRAM = new u8[SRAMLength];
SRAM = SRAMLength ? std::make_unique<u8[]>(SRAMLength) : nullptr;
}
if (SRAMLength)
{
file->VarArray(SRAM, SRAMLength);
file->VarArray(SRAM.get(), SRAMLength);
}
// SPI status shito
@ -441,57 +469,19 @@ void CartRetail::DoSavestate(Savestate* file)
file->Var8(&SRAMStatus);
if ((!file->Saving) && SRAM)
Platform::WriteNDSSave(SRAM, SRAMLength, 0, SRAMLength);
Platform::WriteNDSSave(SRAM.get(), SRAMLength, 0, SRAMLength);
}
void CartRetail::SetupSave(u32 type)
{
if (SRAM) delete[] SRAM;
SRAM = nullptr;
if (type > 10) type = 0;
int sramlen[] =
{
0,
512,
8192, 65536, 128*1024,
256*1024, 512*1024, 1024*1024,
8192*1024, 16384*1024, 65536*1024
};
SRAMLength = sramlen[type];
if (SRAMLength)
{
SRAM = new u8[SRAMLength];
memset(SRAM, 0xFF, SRAMLength);
}
switch (type)
{
case 1: SRAMType = 1; break; // EEPROM, small
case 2:
case 3:
case 4: SRAMType = 2; break; // EEPROM, regular
case 5:
case 6:
case 7: SRAMType = 3; break; // FLASH
case 8:
case 9:
case 10: SRAMType = 4; break; // NAND
default: SRAMType = 0; break; // ...whatever else
}
}
void CartRetail::LoadSave(const u8* savedata, u32 savelen)
void CartRetail::SetSaveMemory(const u8* savedata, u32 savelen)
{
if (!SRAM) return;
u32 len = std::min(savelen, SRAMLength);
memcpy(SRAM, savedata, len);
memcpy(SRAM.get(), savedata, len);
Platform::WriteNDSSave(savedata, len, 0, len);
}
int CartRetail::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len)
int CartRetail::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len)
{
if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
@ -551,17 +541,7 @@ u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last)
}
}
u8 *CartRetail::GetSaveMemory() const
{
return SRAM;
}
u32 CartRetail::GetSaveMemoryLength() const
{
return SRAMLength;
}
void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const
{
addr &= (ROMLength-1);
@ -578,7 +558,7 @@ void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
addr = 0x8000 + (addr & 0x1FF);
}
memcpy(data+offset, ROM+addr, len);
memcpy(data+offset, ROM.get()+addr, len);
}
u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
@ -613,7 +593,7 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
if (last)
{
SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength,
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
(SRAMFirstAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF, SRAMAddr-SRAMFirstAddr);
}
return 0;
@ -677,7 +657,7 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last)
if (last)
{
SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength,
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
}
return 0;
@ -734,7 +714,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
if (last)
{
SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength,
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
}
return 0;
@ -771,7 +751,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
if (last)
{
SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength,
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
}
return 0;
@ -817,7 +797,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
if (last)
{
SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength,
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
}
return 0;
@ -840,7 +820,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
if (last)
{
SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength,
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
}
return 0;
@ -852,15 +832,19 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
}
}
CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams)
CartRetailNAND::CartRetailNAND(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
CartRetailNAND(CopyToUnique(rom, len), len, chipid, romparams, std::move(sram), sramlen)
{
}
CartRetailNAND::~CartRetailNAND()
CartRetailNAND::CartRetailNAND(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
CartRetail(std::move(rom), len, chipid, false, romparams, std::move(sram), sramlen, CartType::RetailNAND)
{
BuildSRAMID();
}
CartRetailNAND::~CartRetailNAND() = default;
void CartRetailNAND::Reset()
{
CartRetail::Reset();
@ -889,13 +873,13 @@ void CartRetailNAND::DoSavestate(Savestate* file)
BuildSRAMID();
}
void CartRetailNAND::LoadSave(const u8* savedata, u32 savelen)
void CartRetailNAND::SetSaveMemory(const u8* savedata, u32 savelen)
{
CartRetail::LoadSave(savedata, savelen);
CartRetail::SetSaveMemory(savedata, savelen);
BuildSRAMID();
}
int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len)
int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len)
{
if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
@ -924,7 +908,7 @@ int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8
if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000))
{
memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800);
Platform::WriteNDSSave(SRAM, SRAMLength, SRAMAddr - SRAMBase, 0x800);
Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMAddr - SRAMBase, 0x800);
}
SRAMAddr = 0;
@ -1031,7 +1015,7 @@ int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8
}
}
void CartRetailNAND::ROMCommandFinish(u8* cmd, u8* data, u32 len)
void CartRetailNAND::ROMCommandFinish(const u8* cmd, u8* data, u32 len)
{
if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len);
@ -1080,15 +1064,28 @@ void CartRetailNAND::BuildSRAMID()
}
CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams) : CartRetail(rom, len, chipid, badDSiDump, romparams)
CartRetailIR::CartRetailIR(const u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
CartRetailIR(CopyToUnique(rom, len), len, chipid, irversion, badDSiDump, romparams, std::move(sram), sramlen)
{
IRVersion = irversion;
}
CartRetailIR::~CartRetailIR()
CartRetailIR::CartRetailIR(
std::unique_ptr<u8[]>&& rom,
u32 len,
u32 chipid,
u32 irversion,
bool badDSiDump,
ROMListEntry romparams,
std::unique_ptr<u8[]>&& sram,
u32 sramlen
) :
CartRetail(std::move(rom), len, chipid, badDSiDump, romparams, std::move(sram), sramlen, CartType::RetailIR),
IRVersion(irversion)
{
}
CartRetailIR::~CartRetailIR() = default;
void CartRetailIR::Reset()
{
CartRetail::Reset();
@ -1125,25 +1122,18 @@ u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last)
return 0;
}
CartRetailBT::CartRetailBT(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
CartRetailBT(CopyToUnique(rom, len), len, chipid, romparams, std::move(sram), sramlen)
{
}
CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams)
CartRetailBT::CartRetailBT(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
CartRetail(std::move(rom), len, chipid, false, romparams, std::move(sram), sramlen, CartType::RetailBT)
{
Log(LogLevel::Info,"POKETYPE CART\n");
}
CartRetailBT::~CartRetailBT()
{
}
void CartRetailBT::Reset()
{
CartRetail::Reset();
}
void CartRetailBT::DoSavestate(Savestate* file)
{
CartRetail::DoSavestate(file);
}
CartRetailBT::~CartRetailBT() = default;
u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
{
@ -1160,149 +1150,23 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
}
CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartCommon(rom, len, chipid, false, romparams)
CartSD::CartSD(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
CartSD(CopyToUnique(rom, len), len, chipid, romparams, std::move(sdcard))
{}
CartSD::CartSD(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
CartCommon(std::move(rom), len, chipid, false, romparams, CartType::Homebrew),
SD(std::move(sdcard))
{
SD = nullptr;
sdcard = std::nullopt;
// std::move on optionals usually results in an optional with a moved-from object
}
CartHomebrew::~CartHomebrew()
{
if (SD)
{
SD->Close();
delete SD;
}
}
CartSD::~CartSD() = default;
// The SD card is destroyed by the optional's destructor
void CartHomebrew::Reset()
{
CartCommon::Reset();
ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly);
if (SD)
{
SD->Close();
delete SD;
}
if (Platform::GetConfigBool(Platform::DLDI_Enable))
{
std::string folderpath;
if (Platform::GetConfigBool(Platform::DLDI_FolderSync))
folderpath = Platform::GetConfigString(Platform::DLDI_FolderPath);
else
folderpath = "";
ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), ReadOnly);
SD = new FATStorage(Platform::GetConfigString(Platform::DLDI_ImagePath),
(u64)Platform::GetConfigInt(Platform::DLDI_ImageSize) * 1024 * 1024,
ReadOnly,
folderpath);
SD->Open();
}
else
SD = nullptr;
}
void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds)
{
CartCommon::SetupDirectBoot(romname, nds);
if (SD)
{
// add the ROM to the SD volume
if (!SD->InjectFile(romname, ROM, ROMLength))
return;
// setup argv command line
char argv[512] = {0};
int argvlen;
strncpy(argv, "fat:/", 511);
strncat(argv, romname.c_str(), 511);
argvlen = strlen(argv);
const NDSHeader& header = GetHeader();
u32 argvbase = header.ARM9RAMAddress + header.ARM9Size;
argvbase = (argvbase + 0xF) & ~0xF;
for (u32 i = 0; i <= argvlen; i+=4)
nds.ARM9Write32(argvbase+i, *(u32*)&argv[i]);
nds.ARM9Write32(0x02FFFE70, 0x5F617267);
nds.ARM9Write32(0x02FFFE74, argvbase);
nds.ARM9Write32(0x02FFFE78, argvlen+1);
// The DSi version of ARM9Write32 will be called if nds is really a DSi
}
}
void CartHomebrew::DoSavestate(Savestate* file)
{
CartCommon::DoSavestate(file);
}
int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len)
{
if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
switch (cmd[0])
{
case 0xB7:
{
u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
memset(data, 0, len);
if (((addr + len - 1) >> 12) != (addr >> 12))
{
u32 len1 = 0x1000 - (addr & 0xFFF);
ReadROM_B7(addr, len1, data, 0);
ReadROM_B7(addr+len1, len-len1, data, len1);
}
else
ReadROM_B7(addr, len, data, 0);
}
return 0;
case 0xC0: // SD read
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
if (SD) SD->ReadSectors(sector, len>>9, data);
}
return 0;
case 0xC1: // SD write
return 1;
default:
return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
}
}
void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len)
{
if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len);
// TODO: delayed SD writing? like we have for SRAM
switch (cmd[0])
{
case 0xC1:
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
if (SD && (!ReadOnly)) SD->WriteSectors(sector, len>>9, data);
}
break;
default:
return CartCommon::ROMCommandFinish(cmd, data, len);
}
}
void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly)
void CartSD::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const
{
if (patch[0x0D] > binary[dldioffset+0x0F])
{
@ -1403,7 +1267,7 @@ void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch,
Log(LogLevel::Debug, "applied DLDI patch at %08X\n", dldioffset);
}
void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
void CartSD::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
{
if (*(u32*)&patch[0] != 0xBF8DA5ED ||
*(u32*)&patch[4] != 0x69684320 ||
@ -1433,23 +1297,134 @@ void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
}
}
void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
void CartSD::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const
{
// TODO: how strict should this be for homebrew?
addr &= (ROMLength-1);
memcpy(data+offset, ROM+addr, len);
memcpy(data+offset, ROM.get()+addr, len);
}
CartHomebrew::CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
CartSD(rom, len, chipid, romparams, std::move(sdcard))
{}
CartHomebrew::CartHomebrew(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
CartSD(std::move(rom), len, chipid, romparams, std::move(sdcard))
{}
NDSCartSlot::NDSCartSlot(melonDS::NDS& nds) noexcept : NDS(nds)
CartHomebrew::~CartHomebrew() = default;
void CartHomebrew::Reset()
{
CartSD::Reset();
if (SD)
ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), SD->IsReadOnly());
}
void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds)
{
CartCommon::SetupDirectBoot(romname, nds);
if (SD)
{
// add the ROM to the SD volume
if (!SD->InjectFile(romname, ROM.get(), ROMLength))
return;
// setup argv command line
char argv[512] = {0};
int argvlen;
strncpy(argv, "fat:/", 511);
strncat(argv, romname.c_str(), 511);
argvlen = strlen(argv);
const NDSHeader& header = GetHeader();
u32 argvbase = header.ARM9RAMAddress + header.ARM9Size;
argvbase = (argvbase + 0xF) & ~0xF;
for (u32 i = 0; i <= argvlen; i+=4)
nds.ARM9Write32(argvbase+i, *(u32*)&argv[i]);
nds.ARM9Write32(0x02FFFE70, 0x5F617267);
nds.ARM9Write32(0x02FFFE74, argvbase);
nds.ARM9Write32(0x02FFFE78, argvlen+1);
// The DSi version of ARM9Write32 will be called if nds is really a DSi
}
}
int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len)
{
if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
switch (cmd[0])
{
case 0xB7:
{
u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
memset(data, 0, len);
if (((addr + len - 1) >> 12) != (addr >> 12))
{
u32 len1 = 0x1000 - (addr & 0xFFF);
ReadROM_B7(addr, len1, data, 0);
ReadROM_B7(addr+len1, len-len1, data, len1);
}
else
ReadROM_B7(addr, len, data, 0);
}
return 0;
case 0xC0: // SD read
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
if (SD) SD->ReadSectors(sector, len>>9, data);
}
return 0;
case 0xC1: // SD write
return 1;
default:
return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
}
}
void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len)
{
if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len);
// TODO: delayed SD writing? like we have for SRAM
switch (cmd[0])
{
case 0xC1:
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
if (SD && !SD->IsReadOnly()) SD->WriteSectors(sector, len>>9, data);
}
break;
default:
return CartCommon::ROMCommandFinish(cmd, data, len);
}
}
NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& 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));
// All fields are default-constructed because they're listed as such in the class declaration
if (rom)
SetCart(std::move(rom));
}
NDSCartSlot::~NDSCartSlot() noexcept
@ -1569,16 +1544,13 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept
memcpy(out, &cartrom[arm9base], 0x800);
Key1_InitKeycode(false, gamecode, 2, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS));
Key1_InitKeycode(false, gamecode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
Key1_Decrypt((u32*)&out[0]);
Key1_InitKeycode(false, gamecode, 3, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS));
Key1_InitKeycode(false, gamecode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
for (u32 i = 0; i < 0x800; i += 8)
Key1_Decrypt((u32*)&out[i]);
XXH64_hash_t hash = XXH64(out, 0x800, 0);
Log(LogLevel::Debug, "Secure area post-decryption xxh64 hash: %zx\n", hash);
if (!strncmp((const char*)out, "encryObj", 8))
{
Log(LogLevel::Info, "Secure area decryption OK\n");
@ -1593,7 +1565,12 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept
}
}
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, std::optional<NDSCartArgs>&& args)
{
return ParseROM(CopyToUnique(romdata, romlen), romlen, std::move(args));
}
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::optional<NDSCartArgs>&& args)
{
if (romdata == nullptr)
{
@ -1607,28 +1584,10 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
return nullptr;
}
u32 cartromsize = 0x200;
while (cartromsize < romlen)
cartromsize <<= 1; // ROM size must be a power of 2
u8* cartrom = nullptr;
try
{
cartrom = new u8[cartromsize];
}
catch (const std::bad_alloc& e)
{
Log(LogLevel::Error, "NDSCart: failed to allocate memory for ROM (%d bytes)\n", cartromsize);
return nullptr;
}
// copy romdata into cartrom then zero out the remaining space
memcpy(cartrom, romdata, romlen);
memset(cartrom + romlen, 0, cartromsize - romlen);
auto [cartrom, cartromsize] = PadToPowerOf2(std::move(romdata), romlen);
NDSHeader header {};
memcpy(&header, cartrom, sizeof(header));
memcpy(&header, cartrom.get(), sizeof(header));
bool dsi = header.IsDSi();
bool badDSiDump = false;
@ -1640,6 +1599,7 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
dsi = false;
}
const char *gametitle = header.GameTitle;
u32 gamecode = header.GameCodeAsU32();
u32 arm9base = header.ARM9ROMOffset;
@ -1694,30 +1654,33 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
}
std::unique_ptr<CartCommon> cart;
std::unique_ptr<u8[]> sram = args ? std::move(args->SRAM) : nullptr;
u32 sramlen = args ? args->SRAMLength : 0;
if (homebrew)
cart = std::make_unique<CartHomebrew>(cartrom, cartromsize, cartid, romparams);
{
std::optional<FATStorage> sdcard = args && args->SDCard ? std::make_optional<FATStorage>(std::move(*args->SDCard)) : std::nullopt;
cart = std::make_unique<CartHomebrew>(std::move(cartrom), cartromsize, cartid, romparams, std::move(sdcard));
}
else if (gametitle[0] == 0 && !strncmp("SD/TF-NDS", gametitle + 1, 9) && gamecode == 0x414D5341)
{
std::optional<FATStorage> sdcard = args && args->SDCard ? std::make_optional<FATStorage>(std::move(*args->SDCard)) : std::nullopt;
cart = std::make_unique<CartR4>(std::move(cartrom), cartromsize, cartid, romparams, CartR4TypeR4, CartR4LanguageEnglish, std::move(sdcard));
}
else if (cartid & 0x08000000)
cart = std::make_unique<CartRetailNAND>(cartrom, cartromsize, cartid, romparams);
cart = std::make_unique<CartRetailNAND>(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen);
else if (irversion != 0)
cart = std::make_unique<CartRetailIR>(cartrom, cartromsize, cartid, irversion, badDSiDump, romparams);
cart = std::make_unique<CartRetailIR>(std::move(cartrom), cartromsize, cartid, irversion, badDSiDump, romparams, std::move(sram), sramlen);
else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx
cart = std::make_unique<CartRetailBT>(cartrom, cartromsize, cartid, romparams);
cart = std::make_unique<CartRetailBT>(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen);
else
cart = std::make_unique<CartRetail>(cartrom, cartromsize, cartid, badDSiDump, romparams);
if (romparams.SaveMemType > 0)
cart->SetupSave(romparams.SaveMemType);
cart = std::make_unique<CartRetail>(std::move(cartrom), cartromsize, cartid, badDSiDump, romparams, std::move(sram), sramlen);
args = std::nullopt;
return cart;
}
bool NDSCartSlot::InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept
void NDSCartSlot::SetCart(std::unique_ptr<CartCommon>&& cart) noexcept
{
if (!cart) {
Log(LogLevel::Error, "Failed to insert invalid cart; existing cart (if any) was not ejected.\n");
return false;
}
if (Cart)
EjectCart();
@ -1725,6 +1688,10 @@ bool NDSCartSlot::InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept
// and cloning polymorphic objects without knowing the underlying type is annoying.
Cart = std::move(cart);
if (!Cart)
// If we're ejecting an existing cart without inserting a new one...
return;
Cart->Reset();
const NDSHeader& header = Cart->GetHeader();
@ -1739,11 +1706,11 @@ bool NDSCartSlot::InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept
strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8);
Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS));
Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
for (u32 i = 0; i < 0x800; i += 8)
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]);
Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS));
Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]);
Log(LogLevel::Debug, "Re-encrypted cart secure area\n");
@ -1757,21 +1724,12 @@ bool NDSCartSlot::InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept
Log(LogLevel::Info, "Inserted cart with game code: %.4s\n", header.GameCode);
Log(LogLevel::Info, "Inserted cart with ID: %08X\n", Cart->ID());
Log(LogLevel::Info, "ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType);
return true;
}
bool NDSCartSlot::LoadROM(const u8* romdata, u32 romlen) noexcept
{
std::unique_ptr<CartCommon> cart = ParseROM(romdata, romlen);
return InsertROM(std::move(cart));
}
void NDSCartSlot::LoadSave(const u8* savedata, u32 savelen) noexcept
void NDSCartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept
{
if (Cart)
Cart->LoadSave(savedata, savelen);
Cart->SetSaveMemory(savedata, savelen);
}
void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept
@ -1780,15 +1738,15 @@ void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept
Cart->SetupDirectBoot(romname, NDS);
}
void NDSCartSlot::EjectCart() noexcept
std::unique_ptr<CartCommon> NDSCartSlot::EjectCart() noexcept
{
if (!Cart) return;
if (!Cart) return nullptr;
// ejecting the cart triggers the gamecard IRQ
NDS.SetIRQ(0, IRQ_CartIREQMC);
NDS.SetIRQ(1, IRQ_CartIREQMC);
Cart = nullptr;
return std::move(Cart);
// CHECKME: does an eject imply anything for the ROM/SPI transfer registers?
}

View File

@ -22,7 +22,7 @@
#include <array>
#include <string>
#include <memory>
#include <array>
#include <variant>
#include "types.h"
#include "Savestate.h"
@ -45,18 +45,42 @@ enum CartType
RetailIR = 0x103,
RetailBT = 0x104,
Homebrew = 0x201,
UnlicensedR4 = 0x301
};
class NDSCartSlot;
/// Arguments used to create and populate an NDS cart of unknown type.
/// Different carts take different subsets of these arguments,
/// but we won't know which ones to use
/// until we parse the header at runtime.
struct NDSCartArgs
{
/// The arguments used to load a homebrew SD card image.
/// If \c nullopt, then the cart will not have an SD card.
/// Ignored for retail ROMs.
std::optional<FATStorageArgs> SDCard = std::nullopt;
/// Save RAM to load into the cartridge.
/// If \c nullptr, then the cart's SRAM buffer will be empty.
/// Ignored for homebrew ROMs.
std::unique_ptr<u8[]> SRAM = nullptr;
/// The length of the buffer in SRAM.
/// If 0, then the cart's SRAM buffer will be empty.
/// Ignored for homebrew ROMs.
u32 SRAMLength = 0;
};
// CartCommon -- base code shared by all cart types
class CartCommon
{
public:
CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams);
CartCommon(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, CartType type);
CartCommon(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, CartType type);
virtual ~CartCommon();
virtual u32 Type() const = 0;
[[nodiscard]] u32 Type() const { return CartType; };
[[nodiscard]] u32 Checksum() const;
virtual void Reset();
@ -64,16 +88,16 @@ public:
virtual void DoSavestate(Savestate* file);
virtual void SetupSave(u32 type);
virtual void LoadSave(const u8* savedata, u32 savelen);
virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len);
virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len);
virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len);
virtual void ROMCommandFinish(const u8* cmd, u8* data, u32 len);
virtual u8 SPIWrite(u8 val, u32 pos, bool last);
virtual u8* GetSaveMemory() const;
virtual u32 GetSaveMemoryLength() const;
virtual u8* GetSaveMemory() { return nullptr; }
virtual const u8* GetSaveMemory() const { return nullptr; }
virtual u32 GetSaveMemoryLength() const { return 0; }
virtual void SetSaveMemory(const u8* savedata, u32 savelen) {};
[[nodiscard]] const NDSHeader& GetHeader() const { return Header; }
[[nodiscard]] NDSHeader& GetHeader() { return Header; }
@ -82,105 +106,120 @@ public:
[[nodiscard]] const NDSBanner* Banner() const;
[[nodiscard]] const ROMListEntry& GetROMParams() const { return ROMParams; };
[[nodiscard]] u32 ID() const { return ChipID; }
[[nodiscard]] const u8* GetROM() const { return ROM; }
[[nodiscard]] const u8* GetROM() const { return ROM.get(); }
[[nodiscard]] u32 GetROMLength() const { return ROMLength; }
protected:
void ReadROM(u32 addr, u32 len, u8* data, u32 offset);
void ReadROM(u32 addr, u32 len, u8* data, u32 offset) const;
u8* ROM;
u32 ROMLength;
u32 ChipID;
bool IsDSi;
bool DSiMode;
u32 DSiBase;
std::unique_ptr<u8[]> ROM = nullptr;
u32 ROMLength = 0;
u32 ChipID = 0;
bool IsDSi = false;
bool DSiMode = false;
u32 DSiBase = 0;
u32 CmdEncMode;
u32 DataEncMode;
u32 CmdEncMode = 0;
u32 DataEncMode = 0;
// Kept separate from the ROM data so we can decrypt the modcrypt area
// without touching the overall ROM data
NDSHeader Header;
ROMListEntry ROMParams;
NDSHeader Header {};
ROMListEntry ROMParams {};
const melonDS::NDSCart::CartType CartType = Default;
};
// CartRetail -- regular retail cart (ROM, SPI SRAM)
class CartRetail : public CartCommon
{
public:
CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams);
virtual ~CartRetail() override;
CartRetail(
const u8* rom,
u32 len,
u32 chipid,
bool badDSiDump,
ROMListEntry romparams,
std::unique_ptr<u8[]>&& sram,
u32 sramlen,
melonDS::NDSCart::CartType type = CartType::Retail
);
CartRetail(
std::unique_ptr<u8[]>&& rom,
u32 len, u32 chipid,
bool badDSiDump,
ROMListEntry romparams,
std::unique_ptr<u8[]>&& sram,
u32 sramlen,
melonDS::NDSCart::CartType type = CartType::Retail
);
~CartRetail() override;
virtual u32 Type() const override { return CartType::Retail; }
void Reset() override;
virtual void Reset() override;
void DoSavestate(Savestate* file) override;
virtual void DoSavestate(Savestate* file) override;
void SetSaveMemory(const u8* savedata, u32 savelen) override;
virtual void SetupSave(u32 type) override;
virtual void LoadSave(const u8* savedata, u32 savelen) override;
int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override;
virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override;
u8 SPIWrite(u8 val, u32 pos, bool last) override;
virtual u8 SPIWrite(u8 val, u32 pos, bool last) override;
virtual u8* GetSaveMemory() const override;
virtual u32 GetSaveMemoryLength() const override;
u8* GetSaveMemory() override { return SRAM.get(); }
const u8* GetSaveMemory() const override { return SRAM.get(); }
u32 GetSaveMemoryLength() const override { return SRAMLength; }
protected:
void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset);
void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const;
u8 SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last);
u8 SRAMWrite_EEPROM(u8 val, u32 pos, bool last);
u8 SRAMWrite_FLASH(u8 val, u32 pos, bool last);
u8* SRAM;
u32 SRAMLength;
u32 SRAMType;
std::unique_ptr<u8[]> SRAM = nullptr;
u32 SRAMLength = 0;
u32 SRAMType = 0;
u8 SRAMCmd;
u32 SRAMAddr;
u32 SRAMFirstAddr;
u8 SRAMStatus;
u8 SRAMCmd = 0;
u32 SRAMAddr = 0;
u32 SRAMFirstAddr = 0;
u8 SRAMStatus = 0;
};
// CartRetailNAND -- retail cart with NAND SRAM (WarioWare DIY, Jam with the Band, ...)
class CartRetailNAND : public CartRetail
{
public:
CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams);
CartRetailNAND(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
CartRetailNAND(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
~CartRetailNAND() override;
virtual u32 Type() const override { return CartType::RetailNAND; }
void Reset() override;
void DoSavestate(Savestate* file) override;
void LoadSave(const u8* savedata, u32 savelen) override;
void SetSaveMemory(const u8* savedata, u32 savelen) override;
int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override;
void ROMCommandFinish(u8* cmd, u8* data, u32 len) override;
int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override;
void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override;
u8 SPIWrite(u8 val, u32 pos, bool last) override;
private:
void BuildSRAMID();
u32 SRAMBase;
u32 SRAMWindow;
u32 SRAMBase = 0;
u32 SRAMWindow = 0;
u8 SRAMWriteBuffer[0x800];
u32 SRAMWritePos;
u8 SRAMWriteBuffer[0x800] {};
u32 SRAMWritePos = 0;
};
// CartRetailIR -- SPI IR device and SRAM
class CartRetailIR : public CartRetail
{
public:
CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams);
CartRetailIR(const u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
CartRetailIR(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
~CartRetailIR() override;
virtual u32 Type() const override { return CartType::RetailIR; }
void Reset() override;
void DoSavestate(Savestate* file) override;
@ -188,56 +227,119 @@ public:
u8 SPIWrite(u8 val, u32 pos, bool last) override;
private:
u32 IRVersion;
u8 IRCmd;
u32 IRVersion = 0;
u8 IRCmd = 0;
};
// CartRetailBT - Pok<6F>mon Typing Adventure (SPI BT controller)
class CartRetailBT : public CartRetail
{
public:
CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams);
CartRetailBT(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
CartRetailBT(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
~CartRetailBT() override;
virtual u32 Type() const override { return CartType::RetailBT; }
void Reset() override;
void DoSavestate(Savestate* file) override;
u8 SPIWrite(u8 val, u32 pos, bool last) override;
};
// CartHomebrew -- homebrew 'cart' (no SRAM, DLDI)
class CartHomebrew : public CartCommon
// CartSD -- any 'cart' with an SD card slot
class CartSD : public CartCommon
{
public:
CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams);
~CartHomebrew() override;
CartSD(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard = std::nullopt);
CartSD(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard = std::nullopt);
~CartSD() override;
virtual u32 Type() const override { return CartType::Homebrew; }
[[nodiscard]] const std::optional<FATStorage>& GetSDCard() const noexcept { return SD; }
void SetSDCard(FATStorage&& sdcard) noexcept { SD = std::move(sdcard); }
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept
{
SD = std::move(sdcard);
sdcard = std::nullopt;
// moving from an optional doesn't set it to nullopt,
// it just leaves behind an optional with a moved-from value
}
protected:
void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const;
void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly);
void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const;
std::optional<FATStorage> SD {};
};
// CartHomebrew -- homebrew 'cart' (no SRAM, DLDI)
class CartHomebrew : public CartSD
{
public:
CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard = std::nullopt);
CartHomebrew(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard = std::nullopt);
~CartHomebrew() override;
void Reset() override;
void SetupDirectBoot(const std::string& romname, NDS& nds) override;
int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override;
void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override;
};
// CartR4 -- unlicensed R4 'cart' (NDSCartR4.cpp)
enum CartR4Type
{
/* non-SDHC carts */
CartR4TypeM3Simply = 0,
CartR4TypeR4 = 1,
/* SDHC carts */
CartR4TypeAce3DS = 2
};
enum CartR4Language
{
CartR4LanguageJapanese = (7 << 3) | 1,
CartR4LanguageEnglish = (7 << 3) | 2,
CartR4LanguageFrench = (2 << 3) | 2,
CartR4LanguageKorean = (4 << 3) | 2,
CartR4LanguageSimplifiedChinese = (6 << 3) | 3,
CartR4LanguageTraditionalChinese = (7 << 3) | 3
};
class CartR4 : public CartSD
{
public:
CartR4(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, CartR4Type ctype, CartR4Language clanguage,
std::optional<FATStorage>&& sdcard = std::nullopt);
~CartR4() override;
void Reset() override;
void DoSavestate(Savestate* file) override;
int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override;
void ROMCommandFinish(u8* cmd, u8* data, u32 len) override;
int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override;
void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override;
private:
void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly);
void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly);
void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset);
inline u32 GetAdjustedSector(u32 sector) const
{
return R4CartType >= CartR4TypeAce3DS ? sector : sector >> 9;
}
FATStorage* SD;
bool ReadOnly;
u16 GetEncryptionKey(u16 sector);
void ReadSDToBuffer(u32 sector, bool rom);
u64 SDFATEntrySectorGet(u32 entry, u32 addr);
s32 EncryptionKey;
u32 FATEntryOffset[2];
u8 Buffer[512];
u8 InitStatus;
CartR4Type R4CartType;
CartR4Language CartLanguage;
bool BufferInitialized;
};
class NDSCartSlot
{
public:
NDSCartSlot(melonDS::NDS& nds) noexcept;
explicit NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom = nullptr) noexcept;
~NDSCartSlot() noexcept;
void Reset() noexcept;
void ResetCart() noexcept;
@ -252,25 +354,16 @@ public:
/// If the provided cart is not valid,
/// then the currently-loaded ROM will not be ejected.
///
/// @param cart Movable reference to the cart.
/// @returns \c true if the cart was successfully loaded,
/// \c false otherwise.
/// @param cart Movable reference to the cart,
/// or \c nullptr to eject the cart.
/// @post If the cart was successfully loaded,
/// then \c cart will be \c nullptr
/// and \c Cart will contain the object that \c cart previously pointed to.
/// Otherwise, \c cart and \c Cart will be both be unchanged.
bool InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept;
void SetCart(std::unique_ptr<CartCommon>&& cart) noexcept;
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
/// Parses a ROM image and loads it into the emulator.
/// This function is equivalent to calling ::ParseROM() and ::InsertROM() in sequence.
/// @param romdata Pointer to the ROM image.
/// The cart emulator maintains its own copy of this data,
/// so the caller is free to discard this data after calling this function.
/// @param romlen The length of the ROM image, in bytes.
/// @returns \c true if the ROM image was successfully loaded,
/// \c false if not.
bool LoadROM(const u8* romdata, u32 romlen) noexcept;
void LoadSave(const u8* savedata, u32 savelen) noexcept;
void SetupDirectBoot(const std::string& romname) noexcept;
/// This function is intended to allow frontends to save and load SRAM
@ -282,11 +375,15 @@ public:
/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr.
[[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
[[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
void SetSaveMemory(const u8* savedata, u32 savelen) noexcept;
/// @returns The length of the buffer returned by ::GetSaveMemory()
/// if a cart is loaded and supports SRAM, otherwise zero.
[[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; }
void EjectCart() noexcept;
/// @return The cart that was in the slot before it was ejected,
/// or \c nullptr if the slot was already empty.
std::unique_ptr<CartCommon> EjectCart() noexcept;
u32 ReadROMData() noexcept;
void WriteROMData(u32 val) noexcept;
void WriteSPICnt(u16 val) noexcept;
@ -294,9 +391,6 @@ public:
[[nodiscard]] u8 ReadSPIData() const noexcept;
void WriteSPIData(u8 val) noexcept;
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
[[nodiscard]] u8 GetROMCommand(u8 index) const noexcept { return ROMCommand[index]; }
void SetROMCommand(u8 index, u8 val) noexcept { ROMCommand[index] = val; }
@ -306,34 +400,34 @@ public:
private:
friend class CartCommon;
melonDS::NDS& NDS;
u16 SPICnt {};
u32 ROMCnt {};
u16 SPICnt = 0;
u32 ROMCnt = 0;
std::array<u8, 8> ROMCommand {};
u8 SPIData;
u32 SPIDataPos;
bool SPIHold;
u8 SPIData = 0;
u32 SPIDataPos = 0;
bool SPIHold = false;
u32 ROMData;
u32 ROMData = 0;
std::array<u8, 0x4000> TransferData;
u32 TransferPos;
u32 TransferLen;
u32 TransferDir;
std::array<u8, 8> TransferCmd;
std::array<u8, 0x4000> TransferData {};
u32 TransferPos = 0;
u32 TransferLen = 0;
u32 TransferDir = 0;
std::array<u8, 8> TransferCmd {};
std::unique_ptr<CartCommon> Cart;
std::unique_ptr<CartCommon> Cart = nullptr;
std::array<u32, 0x412> Key1_KeyBuf;
std::array<u32, 0x412> Key1_KeyBuf {};
u64 Key2_X;
u64 Key2_Y;
u64 Key2_X = 0;
u64 Key2_Y = 0;
void Key1_Encrypt(u32* data) noexcept;
void Key1_Decrypt(u32* data) noexcept;
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, bool externalBios, u8 *bios, u32 biosLength) noexcept;
void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength) noexcept;
void Key2_Encrypt(u8* data, u32 len) 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 Key2_Encrypt(const u8* data, u32 len) noexcept;
void ROMEndTransfer(u32 param) noexcept;
void ROMPrepareData(u32 param) noexcept;
void AdvanceROMTransfer() noexcept;
@ -346,9 +440,13 @@ private:
/// The returned cartridge will contain a copy of this data,
/// so the caller may deallocate \c romdata after this function returns.
/// @param romlen The length of the ROM data in bytes.
/// @param sdcard The arguments to use for initializing the SD card.
/// Ignored if the parsed ROM is not homebrew.
/// If not given, the cart will not have an SD card.
/// @returns A \c NDSCart::CartCommon object representing the parsed ROM,
/// or \c nullptr if the ROM data couldn't be parsed.
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen);
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, std::optional<NDSCartArgs>&& args = std::nullopt);
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::optional<NDSCartArgs>&& args = std::nullopt);
}
#endif

371
src/NDSCartR4.cpp Normal file
View File

@ -0,0 +1,371 @@
/*
Copyright 2016-2023 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/.
*/
#include <string.h>
#include "NDS.h"
#include "DSi.h"
#include "NDSCart.h"
#include "Platform.h"
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
namespace NDSCart
{
/*
Original algorithm discovered by yasu, 2007
http://hp.vector.co.jp/authors/VA013928/
http://www.usay.jp/
http://www.yasu.nu/
*/
static void DecryptR4Sector(u8* dest, u8* src, u16 key1)
{
for (int i = 0; i < 512; i++)
{
// Derive key2 from key1.
u8 key2 = ((key1 >> 7) & 0x80)
| ((key1 >> 6) & 0x60)
| ((key1 >> 5) & 0x10)
| ((key1 >> 4) & 0x0C)
| (key1 & 0x03);
// Decrypt byte.
dest[i] = src[i] ^ key2;
// Derive next key1 from key2.
u16 tmp = ((src[i] << 8) ^ key1);
u16 tmpXor = 0;
for (int ii = 0; ii < 16; ii++)
tmpXor ^= (tmp >> ii);
u16 newKey1 = 0;
newKey1 |= ((tmpXor & 0x80) | (tmp & 0x7C)) << 8;
newKey1 |= ((tmp ^ (tmpXor >> 14)) << 8) & 0x0300;
newKey1 |= (((tmp >> 1) ^ tmp) >> 6) & 0xFC;
newKey1 |= ((tmp ^ (tmpXor >> 1)) >> 8) & 0x03;
key1 = newKey1;
}
}
CartR4::CartR4(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, CartR4Type ctype, CartR4Language clanguage,
std::optional<FATStorage>&& sdcard)
: CartSD(std::move(rom), len, chipid, romparams, std::move(sdcard))
{
InitStatus = 0;
R4CartType = ctype;
CartLanguage = clanguage;
}
CartR4::~CartR4()
{
}
void CartR4::Reset()
{
CartSD::Reset();
BufferInitialized = false;
EncryptionKey = -1;
FATEntryOffset[0] = 0xFFFFFFFF;
FATEntryOffset[1] = 0xFFFFFFFF;
if (!SD)
InitStatus = 1;
else
{
u8 buffer[512];
if (!SD->ReadFile("_DS_MENU.DAT", 0, 512, buffer))
InitStatus = 3;
else
InitStatus = 4;
}
}
void CartR4::DoSavestate(Savestate* file)
{
CartCommon::DoSavestate(file);
file->Var32(&FATEntryOffset[0]);
file->Var32(&FATEntryOffset[1]);
file->VarArray(Buffer, 512);
}
// FIXME: Ace3DS/clone behavior is only partially verified.
int CartR4::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len)
{
if (CmdEncMode != 2)
return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
switch (cmd[0])
{
case 0xB0: /* Get card information */
{
u32 info = 0x75A00000 | (((R4CartType >= 1 ? 4 : 0) | CartLanguage) << 3) | InitStatus;
for (u32 pos = 0; pos < len; pos += 4)
*(u32*)&data[pos] = info;
return 0;
}
case 0xB4: /* FAT entry */
{
u8 entryBuffer[512];
u32 sector = ((cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]) & (~0x1F);
// set FAT entry offset to the starting cluster, to gain a bit of speed
SD->ReadSectors(sector >> 9, 1, entryBuffer);
u16 fileEntryOffset = sector & 0x1FF;
u32 clusterStart = (entryBuffer[fileEntryOffset + 27] << 8)
| entryBuffer[fileEntryOffset + 26]
| (entryBuffer[fileEntryOffset + 21] << 24)
| (entryBuffer[fileEntryOffset + 20] << 16);
FATEntryOffset[cmd[4] & 0x01] = clusterStart;
for (u32 pos = 0; pos < len; pos += 4)
*(u32*)&data[pos] = 0;
return 0;
}
case 0xB8: /* ? Get chip ID ? */
{
for (u32 pos = 0; pos < len; pos += 4)
*(u32*)&data[pos] = ChipID;
return 0;
}
case 0xB2: /* Save read request */
case 0xB6: /* ROM read request */
{
u32 sector = ((cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]);
ReadSDToBuffer(sector, cmd[0] == 0xB6);
for (u32 pos = 0; pos < len; pos += 4)
*(u32*)&data[pos] = 0;
return 0;
}
case 0xB9: /* SD read request */
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
if (SD)
SD->ReadSectors(GetAdjustedSector(sector), 1, Buffer);
for (u32 pos = 0; pos < len; pos += 4)
*(u32*)&data[pos] = 0;
return 0;
}
case 0xBB: /* SD write start */
case 0xBD: /* Save write start */
return 1;
case 0xBC: /* SD write status */
case 0xBE: /* Save write status */
{
if (R4CartType == CartR4TypeAce3DS && cmd[0] == 0xBC)
{
uint8_t checksum = 0;
for (int i = 0; i < 7; i++)
checksum ^= cmd[i];
if (checksum != cmd[7])
Log(LogLevel::Warn, "R4: invalid 0xBC command checksum (%d != %d)", cmd[7], checksum);
}
for (u32 pos = 0; pos < len; pos += 4)
*(u32*)&data[pos] = 0;
return 0;
}
case 0xB7: /* ROM read data */
{
/* If the buffer has not been initialized yet, emulate ROM. */
/* TODO: When does the R4 do this exactly? */
if (!BufferInitialized)
{
u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
memcpy(data, &ROM[addr & (ROMLength-1)], len);
return 0;
}
/* Otherwise, fall through. */
}
case 0xB3: /* Save read data */
case 0xBA: /* SD read data */
{
// TODO: Do these use separate buffers?
for (u32 pos = 0; pos < len; pos++)
data[pos] = Buffer[pos & 0x1FF];
return 0;
}
case 0xBF: /* ROM read decrypted data */
{
// TODO: Is decryption done using the sector from 0xBF or 0xB6?
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
if (len >= 512)
DecryptR4Sector(data, Buffer, GetEncryptionKey(sector >> 9));
return 0;
}
default:
Log(LogLevel::Warn, "R4: unknown command %02X %02X %02X %02X %02X %02X %02X %02X (%d)\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], len);
for (u32 pos = 0; pos < len; pos += 4)
*(u32*)&data[pos] = 0;
return 0;
}
}
void CartR4::ROMCommandFinish(const u8* cmd, u8* data, u32 len)
{
if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len);
switch (cmd[0])
{
case 0xBB: /* SD write start */
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
// The official R4 firmware sends a superfluous write to card
// (sector 0, byte 1) on boot. TODO: Is this correct?
if (GetAdjustedSector(sector) != sector && (sector & 0x1FF)) break;
if (SD && !SD->IsReadOnly())
SD->WriteSectors(GetAdjustedSector(sector), 1, data);
break;
}
case 0xBD: /* Save write start */
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
if (sector & 0x1FF) break;
if (SD && !SD->IsReadOnly())
SD->WriteSectors(
SDFATEntrySectorGet(FATEntryOffset[1], sector) >> 9,
1, data
);
break;
}
}
}
u16 CartR4::GetEncryptionKey(u16 sector)
{
if (EncryptionKey == -1)
{
u8 encryptedBuffer[512];
u8 decryptedBuffer[512];
SD->ReadFile("_DS_MENU.DAT", 0, 512, encryptedBuffer);
for (u32 key = 0; key < 0x10000; key++)
{
DecryptR4Sector(decryptedBuffer, encryptedBuffer, key);
if (decryptedBuffer[12] == '#' && decryptedBuffer[13] == '#'
&& decryptedBuffer[14] == '#' && decryptedBuffer[15] == '#')
{
EncryptionKey = key;
break;
}
}
if (EncryptionKey != -1)
{
Log(LogLevel::Warn, "R4: found cartridge key = %04X\n", EncryptionKey);
}
else
{
EncryptionKey = -2;
Log(LogLevel::Warn, "R4: could not find cartridge key\n");
}
}
return EncryptionKey ^ sector;
}
void CartR4::ReadSDToBuffer(u32 sector, bool rom)
{
if (SD)
{
if (rom && FATEntryOffset[0] == 0xFFFFFFFF)
{
// On first boot, read from _DS_MENU.DAT.
SD->ReadFile("_DS_MENU.DAT", sector & ~0x1FF, 512, Buffer);
}
else
{
// Default mode.
SD->ReadSectors(
SDFATEntrySectorGet(FATEntryOffset[rom ? 0 : 1], sector) >> 9,
1, Buffer
);
}
BufferInitialized = true;
}
}
u64 CartR4::SDFATEntrySectorGet(u32 entry, u32 addr)
{
u8 buffer[512];
u32 bufferSector = 0xFFFFFFFF;
// Parse FAT header.
SD->ReadSectors(0, 1, buffer);
u16 bytesPerSector = (buffer[12] << 8) | buffer[11];
u8 sectorsPerCluster = buffer[13];
u16 firstFatSector = (buffer[15] << 8) | buffer[14];
u8 fatTableCount = buffer[16];
u32 clustersTotal = SD->GetSectorCount() / sectorsPerCluster;
bool isFat32 = clustersTotal >= 65526;
u32 fatTableSize = (buffer[23] << 8) | buffer[22];
if (fatTableSize == 0 && isFat32)
fatTableSize = (buffer[39] << 24) | (buffer[38] << 16) | (buffer[37] << 8) | buffer[36];
u32 bytesPerCluster = bytesPerSector * sectorsPerCluster;
u32 rootDirSectors = 0;
if (!isFat32) {
u32 rootDirEntries = (buffer[18] << 8) | buffer[17];
rootDirSectors = ((rootDirEntries * 32) + (bytesPerSector - 1)) / bytesPerSector;
}
u32 firstDataSector = firstFatSector + fatTableCount * fatTableSize + rootDirSectors;
// Parse file entry (done when processing command 0xB4).
u32 clusterStart = entry;
// Parse cluster table.
u32 currentCluster = clusterStart;
while (true)
{
currentCluster &= isFat32 ? 0x0FFFFFFF : 0xFFFF;
if (addr < bytesPerCluster)
{
// Read from this cluster.
return (u64) (firstDataSector + ((currentCluster - 2) * sectorsPerCluster)) * bytesPerSector + addr;
}
else if (currentCluster >= 2 && currentCluster <= (isFat32 ? 0x0FFFFFF6 : 0xFFF6))
{
// Follow into next cluster.
u32 nextClusterOffset = firstFatSector * bytesPerSector + currentCluster * (isFat32 ? 4 : 2);
u32 nextClusterTableSector = nextClusterOffset >> 9;
if (bufferSector != nextClusterTableSector)
{
SD->ReadSectors(nextClusterTableSector, 1, buffer);
bufferSector = nextClusterTableSector;
}
nextClusterOffset &= 0x1FF;
currentCluster = (buffer[nextClusterOffset + 1] << 8) | buffer[nextClusterOffset];
if (isFat32)
currentCluster |= (buffer[nextClusterOffset + 3] << 24) | (buffer[nextClusterOffset + 2] << 16);
addr -= bytesPerCluster;
}
else
{
// End of cluster table.
return 0;
}
}
}
}
}

View File

@ -42,7 +42,7 @@ struct NonStupidBitField
NonStupidBitField<Size>& BitField;
u32 Idx;
operator bool()
operator bool() const
{
return BitField.Data[Idx >> 6] & (1ULL << (Idx & 0x3F));
}
@ -62,13 +62,13 @@ struct NonStupidBitField
u32 BitIdx;
u64 RemainingBits;
u32 operator*() { return DataIdx * 64 + BitIdx; }
u32 operator*() const { return DataIdx * 64 + BitIdx; }
bool operator==(const Iterator& other)
bool operator==(const Iterator& other) const
{
return other.DataIdx == DataIdx;
}
bool operator!=(const Iterator& other)
bool operator!=(const Iterator& other) const
{
return other.DataIdx != DataIdx;
}

View File

@ -92,56 +92,6 @@ int InstanceID();
*/
std::string InstanceFileSuffix();
// configuration values
enum ConfigEntry
{
#ifdef JIT_ENABLED
JIT_Enable,
JIT_MaxBlockSize,
JIT_LiteralOptimizations,
JIT_BranchOptimizations,
JIT_FastMemory,
#endif
ExternalBIOSEnable,
DLDI_Enable,
DLDI_ImagePath,
DLDI_ImageSize,
DLDI_ReadOnly,
DLDI_FolderSync,
DLDI_FolderPath,
DSiSD_Enable,
DSiSD_ImagePath,
DSiSD_ImageSize,
DSiSD_ReadOnly,
DSiSD_FolderSync,
DSiSD_FolderPath,
Firm_MAC,
WifiSettingsPath,
AudioBitDepth,
DSi_FullBIOSBoot,
#ifdef GDBSTUB_ENABLED
GdbEnabled,
GdbPortARM7,
GdbPortARM9,
GdbARM7BreakOnStartup,
GdbARM9BreakOnStartup,
#endif
};
int GetConfigInt(ConfigEntry entry);
bool GetConfigBool(ConfigEntry entry);
std::string GetConfigString(ConfigEntry entry);
bool GetConfigArray(ConfigEntry entry, void* data);
/**
* Denotes how a file will be opened and accessed.
* Flags may or may not correspond to the operating system's file API.

View File

@ -86,17 +86,17 @@ void RTC::DoSavestate(Savestate* file)
}
u8 RTC::BCD(u8 val)
u8 RTC::BCD(u8 val) const
{
return (val % 10) | ((val / 10) << 4);
}
u8 RTC::FromBCD(u8 val)
u8 RTC::FromBCD(u8 val) const
{
return (val & 0xF) + ((val >> 4) * 10);
}
u8 RTC::BCDIncrement(u8 val)
u8 RTC::BCDIncrement(u8 val) const
{
val++;
if ((val & 0x0F) >= 0x0A)
@ -106,7 +106,7 @@ u8 RTC::BCDIncrement(u8 val)
return val;
}
u8 RTC::BCDSanitize(u8 val, u8 vmin, u8 vmax)
u8 RTC::BCDSanitize(u8 val, u8 vmin, u8 vmax) const
{
if (val < vmin || val > vmax)
val = vmin;
@ -119,12 +119,12 @@ u8 RTC::BCDSanitize(u8 val, u8 vmin, u8 vmax)
}
void RTC::GetState(StateData& state)
void RTC::GetState(StateData& state) const
{
memcpy(&state, &State, sizeof(State));
}
void RTC::SetState(StateData& state)
void RTC::SetState(const StateData& state)
{
memcpy(&State, &state, sizeof(State));
@ -134,7 +134,7 @@ void RTC::SetState(StateData& state)
WriteDateTime(i+1, State.DateTime[i]);
}
void RTC::GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second)
void RTC::GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) const
{
year = FromBCD(State.DateTime[0]);
year += 2000;
@ -374,7 +374,7 @@ void RTC::ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write
}
u8 RTC::DaysInMonth()
u8 RTC::DaysInMonth() const
{
u8 numdays;

View File

@ -55,9 +55,9 @@ public:
void DoSavestate(Savestate* file);
void GetState(StateData& state);
void SetState(StateData& state);
void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second);
void GetState(StateData& state) const;
void SetState(const StateData& state);
void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) const;
void SetDateTime(int year, int month, int day, int hour, int minute, int second);
void ClockTimer(u32 param);
@ -87,16 +87,16 @@ private:
void ResetState();
void ScheduleTimer(bool first);
u8 BCD(u8 val);
u8 FromBCD(u8 val);
u8 BCDIncrement(u8 val);
u8 BCDSanitize(u8 val, u8 vmin, u8 vmax);
u8 BCD(u8 val) const;
u8 FromBCD(u8 val) const;
u8 BCDIncrement(u8 val) const;
u8 BCDSanitize(u8 val, u8 vmin, u8 vmax) const;
void SetIRQ(u8 irq);
void ClearIRQ(u8 irq);
void ProcessIRQ(int type);
u8 DaysInMonth();
u8 DaysInMonth() const;
void CountYear();
void CountMonth();
void CheckEndOfMonth();

View File

@ -57,33 +57,24 @@ u16 CRC16(const u8* data, u32 len, u32 start)
bool FirmwareMem::VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset)
bool FirmwareMem::VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) const
{
u16 crc_stored = *(u16*)&Firmware->Buffer()[crcoffset];
u16 crc_calced = CRC16(&Firmware->Buffer()[offset], len, start);
u16 crc_stored = *(u16*)&FirmwareData.Buffer()[crcoffset];
u16 crc_calced = CRC16(&FirmwareData.Buffer()[offset], len, start);
return (crc_stored == crc_calced);
}
FirmwareMem::FirmwareMem(melonDS::NDS& nds) : SPIDevice(nds)
FirmwareMem::FirmwareMem(melonDS::NDS& nds, melonDS::Firmware&& firmware) : SPIDevice(nds), FirmwareData(std::move(firmware))
{
}
FirmwareMem::~FirmwareMem()
{
RemoveFirmware();
}
FirmwareMem::~FirmwareMem() = default;
void FirmwareMem::Reset()
{
if (!Firmware)
{
Log(LogLevel::Warn, "SPI firmware: no firmware loaded! Using default\n");
Firmware = std::make_unique<class Firmware>(NDS.ConsoleType);
}
// fix touchscreen coords
for (auto& u : Firmware->GetUserData())
for (auto& u : FirmwareData.GetUserData())
{
u.TouchCalibrationADC1[0] = 0;
u.TouchCalibrationADC1[1] = 0;
@ -95,17 +86,17 @@ void FirmwareMem::Reset()
u.TouchCalibrationPixel2[1] = 191;
}
Firmware->UpdateChecksums();
FirmwareData.UpdateChecksums();
// disable autoboot
//Firmware[userdata+0x64] &= 0xBF;
MacAddress mac = Firmware->GetHeader().MacAddr;
MacAddress mac = FirmwareData.GetHeader().MacAddr;
Log(LogLevel::Info, "MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
// verify shit
u32 mask = Firmware->Mask();
Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware->Buffer()[0x2C], 0x2A)?"GOOD":"BAD");
u32 mask = FirmwareData.Mask();
Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&FirmwareData.Buffer()[0x2C], 0x2A)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FA00&mask, 0xFE, 0x7FAFE&mask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FB00&mask, 0xFE, 0x7FBFE&mask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FC00&mask, 0xFE, 0x7FCFE&mask)?"GOOD":"BAD");
@ -136,8 +127,8 @@ void FirmwareMem::DoSavestate(Savestate* file)
void FirmwareMem::SetupDirectBoot()
{
const auto& header = Firmware->GetHeader();
const auto& userdata = Firmware->GetEffectiveUserData();
const auto& header = FirmwareData.GetHeader();
const auto& userdata = FirmwareData.GetEffectiveUserData();
if (NDS.ConsoleType == 1)
{
// The ARMWrite methods are virtual, they'll delegate to DSi if necessary
@ -163,58 +154,9 @@ void FirmwareMem::SetupDirectBoot()
}
}
const class Firmware* FirmwareMem::GetFirmware()
bool FirmwareMem::IsLoadedFirmwareBuiltIn() const
{
return Firmware.get();
}
bool FirmwareMem::IsLoadedFirmwareBuiltIn()
{
return Firmware->GetHeader().Identifier == GENERATED_FIRMWARE_IDENTIFIER;
}
bool FirmwareMem::InstallFirmware(class Firmware&& firmware)
{
if (!firmware.Buffer())
{
Log(LogLevel::Error, "SPI firmware: firmware buffer is null!\n");
return false;
}
Firmware = std::make_unique<class Firmware>(std::move(firmware));
FirmwareIdentifier id = Firmware->GetHeader().Identifier;
Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]);
return true;
}
bool FirmwareMem::InstallFirmware(std::unique_ptr<class Firmware>&& firmware)
{
if (!firmware)
{
Log(LogLevel::Error, "SPI firmware: firmware is null!\n");
return false;
}
if (!firmware->Buffer())
{
Log(LogLevel::Error, "SPI firmware: firmware buffer is null!\n");
return false;
}
Firmware = std::move(firmware);
FirmwareIdentifier id = Firmware->GetHeader().Identifier;
Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]);
return true;
}
void FirmwareMem::RemoveFirmware()
{
Firmware.reset();
Log(LogLevel::Debug, "Removed installed firmware (if any)\n");
return FirmwareData.GetHeader().Identifier == GENERATED_FIRMWARE_IDENTIFIER;
}
void FirmwareMem::Write(u8 val)
@ -256,7 +198,7 @@ void FirmwareMem::Write(u8 val)
}
else
{
Data = Firmware->Buffer()[Addr & Firmware->Mask()];
Data = FirmwareData.Buffer()[Addr & FirmwareData.Mask()];
Addr++;
}
@ -279,7 +221,7 @@ void FirmwareMem::Write(u8 val)
}
else
{
Firmware->Buffer()[Addr & Firmware->Mask()] = val;
FirmwareData.Buffer()[Addr & FirmwareData.Mask()] = val;
Data = val;
Addr++;
}
@ -314,11 +256,11 @@ void FirmwareMem::Release()
{ // If the SPI firmware chip just finished a write...
// We only notify the frontend of changes to the Wi-fi/userdata settings region
// (although it might still decide to flush the whole thing)
u32 wifioffset = Firmware->GetWifiAccessPointOffset();
u32 wifioffset = FirmwareData.GetWifiAccessPointOffset();
// Request that the start of the Wi-fi/userdata settings region
// through the end of the firmware blob be flushed to disk
Platform::WriteFirmware(*Firmware, wifioffset, Firmware->Length() - wifioffset);
Platform::WriteFirmware(FirmwareData, wifioffset, FirmwareData.Length() - wifioffset);
}
SPIDevice::Release();
@ -366,7 +308,7 @@ void PowerMan::DoSavestate(Savestate* file)
file->VarArray(RegMasks, 8); // is that needed??
}
bool PowerMan::GetBatteryLevelOkay() { return !Registers[1]; }
bool PowerMan::GetBatteryLevelOkay() const { return !Registers[1]; }
void PowerMan::SetBatteryLevelOkay(bool okay) { Registers[1] = okay ? 0x00 : 0x01; }
void PowerMan::Write(u8 val)
@ -462,7 +404,7 @@ void TSC::SetTouchCoords(u16 x, u16 y)
NDS.KeyInput &= ~(1 << (16+6));
}
void TSC::MicInputFrame(s16* data, int samples)
void TSC::MicInputFrame(const s16* data, int samples)
{
if (!data)
{
@ -530,11 +472,11 @@ void TSC::Write(u8 val)
SPIHost::SPIHost(melonDS::NDS& nds) : NDS(nds)
SPIHost::SPIHost(melonDS::NDS& nds, Firmware&& firmware) : NDS(nds)
{
NDS.RegisterEventFunc(Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone));
Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS);
Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS, std::move(firmware));
Devices[SPIDevice_PowerMan] = new PowerMan(NDS);
if (NDS.ConsoleType == 1)
@ -607,7 +549,7 @@ void SPIHost::TransferDone(u32 param)
NDS.SetIRQ(1, IRQ_SPI);
}
u8 SPIHost::ReadData()
u8 SPIHost::ReadData() const
{
if (!(Cnt & (1<<15))) return 0;
if (Cnt & (1<<7)) return 0; // checkme

View File

@ -51,7 +51,7 @@ public:
virtual void Reset() = 0;
virtual void DoSavestate(Savestate* file) = 0;
virtual u8 Read() { return Data; }
virtual u8 Read() const { return Data; }
virtual void Write(u8 val) = 0;
virtual void Release() { Hold = false; DataPos = 0; }
@ -66,31 +66,30 @@ protected:
class FirmwareMem : public SPIDevice
{
public:
FirmwareMem(melonDS::NDS& nds);
FirmwareMem(melonDS::NDS& nds, melonDS::Firmware&& firmware);
~FirmwareMem() override;
void Reset() override;
void DoSavestate(Savestate* file) override;
void SetupDirectBoot();
const class Firmware* GetFirmware();
bool IsLoadedFirmwareBuiltIn();
bool InstallFirmware(class Firmware&& firmware);
bool InstallFirmware(std::unique_ptr<class Firmware>&& firmware);
void RemoveFirmware();
Firmware& GetFirmware() noexcept { return FirmwareData; }
[[nodiscard]] const Firmware& GetFirmware() const noexcept { return FirmwareData; }
void SetFirmware(Firmware&& firmware) { FirmwareData = std::move(firmware); }
bool IsLoadedFirmwareBuiltIn() const;
void Write(u8 val) override;
void Release() override;
private:
std::unique_ptr<class Firmware> Firmware;
Firmware FirmwareData;
u8 CurCmd;
u8 StatusReg;
u32 Addr;
bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset);
bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) const;
};
class PowerMan : public SPIDevice
@ -101,7 +100,7 @@ public:
void Reset() override;
void DoSavestate(Savestate* file) override;
bool GetBatteryLevelOkay();
bool GetBatteryLevelOkay() const;
void SetBatteryLevelOkay(bool okay);
void Write(u8 val) override;
@ -122,7 +121,7 @@ public:
virtual void DoSavestate(Savestate* file) override;
virtual void SetTouchCoords(u16 x, u16 y);
virtual void MicInputFrame(s16* data, int samples);
virtual void MicInputFrame(const s16* data, int samples);
virtual void Write(u8 val) override;
@ -141,21 +140,26 @@ protected:
class SPIHost
{
public:
SPIHost(melonDS::NDS& nds);
SPIHost(melonDS::NDS& nds, Firmware&& firmware);
~SPIHost();
void Reset();
void DoSavestate(Savestate* file);
FirmwareMem* GetFirmwareMem() { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; }
const FirmwareMem* GetFirmwareMem() const { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; }
PowerMan* GetPowerMan() { return (PowerMan*)Devices[SPIDevice_PowerMan]; }
const PowerMan* GetPowerMan() const { return (PowerMan*)Devices[SPIDevice_PowerMan]; }
TSC* GetTSC() { return (TSC*)Devices[SPIDevice_TSC]; }
const TSC* GetTSC() const { return (TSC*)Devices[SPIDevice_TSC]; }
const Firmware* GetFirmware() { return GetFirmwareMem()->GetFirmware(); }
const Firmware& GetFirmware() const { return GetFirmwareMem()->GetFirmware(); }
Firmware& GetFirmware() { return GetFirmwareMem()->GetFirmware(); }
void SetFirmware(Firmware&& firmware) { GetFirmwareMem()->SetFirmware(std::move(firmware)); }
u16 ReadCnt() { return Cnt; }
u16 ReadCnt() const { return Cnt; }
void WriteCnt(u16 val);
u8 ReadData();
u8 ReadData() const;
void WriteData(u8 val);
void TransferDone(u32 param);

View File

@ -65,23 +65,110 @@ const s16 SPUChannel::PSGTable[8][8] =
{-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF}
};
s16 SPUChannel::InterpCos[0x100];
s16 SPUChannel::InterpCubic[0x100][4];
bool SPUChannel::InterpInited = false;
template <typename T>
constexpr T ipow(T num, unsigned int pow)
{
T product = 1;
for (int i = 0; i < pow; ++i)
{
product *= num;
}
return product;
}
SPU::SPU(melonDS::NDS& nds) : NDS(nds)
template <typename T>
constexpr T factorial(T num)
{
T product = 1;
for (T i = 1; i <= num; ++i)
{
product *= i;
}
return product;
}
// We can't use std::cos in constexpr functions until C++26,
// so we need to compute the cosine ourselves with the Taylor series.
// Code adapted from https://prosepoetrycode.potterpcs.net/2015/07/a-simple-constexpr-power-function-c/
template <int Iterations = 10>
constexpr double cosine (double theta)
{
return (ipow(-1, Iterations) * ipow(theta, 2 * Iterations)) /
static_cast<double>(factorial(2ull * Iterations))
+ cosine<Iterations-1>(theta);
}
template <>
constexpr double cosine<0> (double theta)
{
return 1.0;
}
// generate interpolation tables
// values are 1:1:14 fixed-point
constexpr std::array<s16, 0x100> InterpCos = []() constexpr {
std::array<s16, 0x100> interp {};
for (int i = 0; i < 0x100; i++)
{
float ratio = (i * M_PI) / 255.0f;
ratio = 1.0f - cosine(ratio);
interp[i] = (s16)(ratio * 0x2000);
}
return interp;
}();
constexpr array2d<s16, 0x100, 4> InterpCubic = []() constexpr {
array2d<s16, 0x100, 4> interp {};
for (int i = 0; i < 0x100; i++)
{
s32 i1 = i << 6;
s32 i2 = (i * i) >> 2;
s32 i3 = (i * i * i) >> 10;
interp[i][0] = -i3 + 2*i2 - i1;
interp[i][1] = i3 - 2*i2 + 0x4000;
interp[i][2] = -i3 + i2 + i1;
interp[i][3] = i3 - i2;
}
return interp;
}();
SPU::SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpolation) :
NDS(nds),
Channels {
SPUChannel(0, nds, interpolation),
SPUChannel(1, nds, interpolation),
SPUChannel(2, nds, interpolation),
SPUChannel(3, nds, interpolation),
SPUChannel(4, nds, interpolation),
SPUChannel(5, nds, interpolation),
SPUChannel(6, nds, interpolation),
SPUChannel(7, nds, interpolation),
SPUChannel(8, nds, interpolation),
SPUChannel(9, nds, interpolation),
SPUChannel(10, nds, interpolation),
SPUChannel(11, nds, interpolation),
SPUChannel(12, nds, interpolation),
SPUChannel(13, nds, interpolation),
SPUChannel(14, nds, interpolation),
SPUChannel(15, nds, interpolation),
},
Capture {
SPUCaptureUnit(0, nds),
SPUCaptureUnit(1, nds),
},
AudioLock(Platform::Mutex_Create()),
Degrade10Bit(bitdepth == AudioBitDepth::_10Bit || (nds.ConsoleType == 1 && bitdepth == AudioBitDepth::Auto))
{
NDS.RegisterEventFunc(Event_SPU, 0, MemberEventFunc(SPU, Mix));
for (int i = 0; i < 16; i++)
Channels[i] = new SPUChannel(i, NDS);
Capture[0] = new SPUCaptureUnit(0, NDS);
Capture[1] = new SPUCaptureUnit(1, NDS);
AudioLock = Platform::Mutex_Create();
ApplyBias = true;
Degrade10Bit = false;
@ -90,50 +177,10 @@ SPU::SPU(melonDS::NDS& nds) : NDS(nds)
OutputBackbufferWritePosition = 0;
OutputFrontBufferReadPosition = 0;
OutputFrontBufferWritePosition = 0;
if (!SPUChannel::InterpInited)
{
// generate interpolation tables
// values are 1:1:14 fixed-point
float m_pi = std::acos(-1.0f);
for (int i = 0; i < 0x100; i++)
{
float ratio = (i * m_pi) / 255.0f;
ratio = 1.0f - std::cos(ratio);
SPUChannel::InterpCos[i] = (s16)(ratio * 0x2000);
}
for (int i = 0; i < 0x100; i++)
{
s32 i1 = i << 6;
s32 i2 = (i * i) >> 2;
s32 i3 = (i * i * i) >> 10;
SPUChannel::InterpCubic[i][0] = -i3 + 2*i2 - i1;
SPUChannel::InterpCubic[i][1] = i3 - 2*i2 + 0x4000;
SPUChannel::InterpCubic[i][2] = -i3 + i2 + i1;
SPUChannel::InterpCubic[i][3] = i3 - i2;
}
SPUChannel::InterpInited = true;
}
}
SPU::~SPU()
{
for (int i = 0; i < 16; i++)
{
delete Channels[i];
Channels[i] = nullptr;
}
delete Capture[0];
delete Capture[1];
Capture[0] = nullptr;
Capture[1] = nullptr;
Platform::Mutex_Free(AudioLock);
AudioLock = nullptr;
@ -149,10 +196,10 @@ void SPU::Reset()
Bias = 0;
for (int i = 0; i < 16; i++)
Channels[i]->Reset();
Channels[i].Reset();
Capture[0]->Reset();
Capture[1]->Reset();
Capture[0].Reset();
Capture[1].Reset();
NDS.ScheduleEvent(Event_SPU, false, 1024, 0, 0);
}
@ -176,11 +223,11 @@ void SPU::DoSavestate(Savestate* file)
file->Var8(&MasterVolume);
file->Var16(&Bias);
for (int i = 0; i < 16; i++)
Channels[i]->DoSavestate(file);
for (SPUChannel& channel : Channels)
channel.DoSavestate(file);
Capture[0]->DoSavestate(file);
Capture[1]->DoSavestate(file);
for (SPUCaptureUnit& capture : Capture)
capture.DoSavestate(file);
}
@ -190,10 +237,10 @@ void SPU::SetPowerCnt(u32 val)
}
void SPU::SetInterpolation(int type)
void SPU::SetInterpolation(AudioInterpolation type)
{
for (int i = 0; i < 16; i++)
Channels[i]->InterpType = type;
for (SPUChannel& channel : Channels)
channel.InterpType = type;
}
void SPU::SetBias(u16 bias)
@ -211,15 +258,26 @@ void SPU::SetDegrade10Bit(bool enable)
Degrade10Bit = enable;
}
SPUChannel::SPUChannel(u32 num, melonDS::NDS& nds) : NDS(nds)
void SPU::SetDegrade10Bit(AudioBitDepth depth)
{
Num = num;
InterpType = 0;
switch (depth)
{
case AudioBitDepth::Auto:
Degrade10Bit = (NDS.ConsoleType == 0);
break;
case AudioBitDepth::_10Bit:
Degrade10Bit = true;
break;
case AudioBitDepth::_16Bit:
Degrade10Bit = false;
break;
}
}
SPUChannel::~SPUChannel()
SPUChannel::SPUChannel(u32 num, melonDS::NDS& nds, AudioInterpolation interpolation) :
NDS(nds),
Num(num),
InterpType(interpolation)
{
}
@ -520,7 +578,7 @@ s32 SPUChannel::Run()
// for optional interpolation: save previous samples
// the interpolated audio will be delayed by a couple samples,
// but it's easier to deal with this way
if ((type < 3) && (InterpType != 0))
if ((type < 3) && (InterpType != AudioInterpolation::None))
{
PrevSample[2] = PrevSample[1];
PrevSample[1] = PrevSample[0];
@ -540,24 +598,24 @@ s32 SPUChannel::Run()
s32 val = (s32)CurSample;
// interpolation (emulation improvement, not a hardware feature)
if ((type < 3) && (InterpType != 0))
if ((type < 3) && (InterpType != AudioInterpolation::None))
{
s32 samplepos = ((Timer - TimerReload) * 0x100) / (0x10000 - TimerReload);
if (samplepos > 0xFF) samplepos = 0xFF;
switch (InterpType)
{
case 1: // linear
case AudioInterpolation::Linear:
val = ((val * samplepos) +
(PrevSample[0] * (0xFF-samplepos))) >> 8;
break;
case 2: // cosine
case AudioInterpolation::Cosine:
val = ((val * InterpCos[samplepos]) +
(PrevSample[0] * InterpCos[0xFF-samplepos])) >> 14;
break;
case 3: // cubic
case AudioInterpolation::Cubic:
val = ((PrevSample[2] * InterpCubic[samplepos][0]) +
(PrevSample[1] * InterpCubic[samplepos][1]) +
(PrevSample[0] * InterpCubic[samplepos][2]) +
@ -579,11 +637,6 @@ void SPUChannel::PanOutput(s32 in, s32& left, s32& right)
SPUCaptureUnit::SPUCaptureUnit(u32 num, melonDS::NDS& nds) : NDS(nds), Num(num)
{
Num = num;
}
SPUCaptureUnit::~SPUCaptureUnit()
{
}
@ -713,21 +766,21 @@ void SPU::Mix(u32 dummy)
if ((Cnt & (1<<15)) && (!dummy))
{
s32 ch0 = Channels[0]->DoRun();
s32 ch1 = Channels[1]->DoRun();
s32 ch2 = Channels[2]->DoRun();
s32 ch3 = Channels[3]->DoRun();
s32 ch0 = Channels[0].DoRun();
s32 ch1 = Channels[1].DoRun();
s32 ch2 = Channels[2].DoRun();
s32 ch3 = Channels[3].DoRun();
// TODO: addition from capture registers
Channels[0]->PanOutput(ch0, left, right);
Channels[2]->PanOutput(ch2, left, right);
Channels[0].PanOutput(ch0, left, right);
Channels[2].PanOutput(ch2, left, right);
if (!(Cnt & (1<<12))) Channels[1]->PanOutput(ch1, left, right);
if (!(Cnt & (1<<13))) Channels[3]->PanOutput(ch3, left, right);
if (!(Cnt & (1<<12))) Channels[1].PanOutput(ch1, left, right);
if (!(Cnt & (1<<13))) Channels[3].PanOutput(ch3, left, right);
for (int i = 4; i < 16; i++)
{
SPUChannel* chan = Channels[i];
SPUChannel* chan = &Channels[i];
s32 channel = chan->DoRun();
chan->PanOutput(channel, left, right);
@ -736,7 +789,7 @@ void SPU::Mix(u32 dummy)
// sound capture
// TODO: other sound capture sources, along with their bugs
if (Capture[0]->Cnt & (1<<7))
if (Capture[0].Cnt & (1<<7))
{
s32 val = left;
@ -744,10 +797,10 @@ void SPU::Mix(u32 dummy)
if (val < -0x8000) val = -0x8000;
else if (val > 0x7FFF) val = 0x7FFF;
Capture[0]->Run(val);
Capture[0].Run(val);
}
if (Capture[1]->Cnt & (1<<7))
if (Capture[1].Cnt & (1<<7))
{
s32 val = right;
@ -755,7 +808,7 @@ void SPU::Mix(u32 dummy)
if (val < -0x8000) val = -0x8000;
else if (val > 0x7FFF) val = 0x7FFF;
Capture[1]->Run(val);
Capture[1].Run(val);
}
// final output
@ -767,20 +820,20 @@ void SPU::Mix(u32 dummy)
break;
case 0x0100: // channel 1
{
s32 pan = 128 - Channels[1]->Pan;
s32 pan = 128 - Channels[1].Pan;
leftoutput = ((s64)ch1 * pan) >> 10;
}
break;
case 0x0200: // channel 3
{
s32 pan = 128 - Channels[3]->Pan;
s32 pan = 128 - Channels[3].Pan;
leftoutput = ((s64)ch3 * pan) >> 10;
}
break;
case 0x0300: // channel 1+3
{
s32 pan1 = 128 - Channels[1]->Pan;
s32 pan3 = 128 - Channels[3]->Pan;
s32 pan1 = 128 - Channels[1].Pan;
s32 pan3 = 128 - Channels[3].Pan;
leftoutput = (((s64)ch1 * pan1) >> 10) + (((s64)ch3 * pan3) >> 10);
}
break;
@ -793,20 +846,20 @@ void SPU::Mix(u32 dummy)
break;
case 0x0400: // channel 1
{
s32 pan = Channels[1]->Pan;
s32 pan = Channels[1].Pan;
rightoutput = ((s64)ch1 * pan) >> 10;
}
break;
case 0x0800: // channel 3
{
s32 pan = Channels[3]->Pan;
s32 pan = Channels[3].Pan;
rightoutput = ((s64)ch3 * pan) >> 10;
}
break;
case 0x0C00: // channel 1+3
{
s32 pan1 = Channels[1]->Pan;
s32 pan3 = Channels[3]->Pan;
s32 pan1 = Channels[1].Pan;
s32 pan3 = Channels[3].Pan;
rightoutput = (((s64)ch1 * pan1) >> 10) + (((s64)ch3 * pan3) >> 10);
}
break;
@ -903,7 +956,7 @@ void SPU::InitOutput()
Platform::Mutex_Unlock(AudioLock);
}
int SPU::GetOutputSize()
int SPU::GetOutputSize() const
{
Platform::Mutex_Lock(AudioLock);
@ -982,7 +1035,7 @@ u8 SPU::Read8(u32 addr)
{
if (addr < 0x04000500)
{
SPUChannel* chan = Channels[(addr >> 4) & 0xF];
SPUChannel* chan = &Channels[(addr >> 4) & 0xF];
switch (addr & 0xF)
{
@ -999,8 +1052,8 @@ u8 SPU::Read8(u32 addr)
case 0x04000500: return Cnt & 0x7F;
case 0x04000501: return Cnt >> 8;
case 0x04000508: return Capture[0]->Cnt;
case 0x04000509: return Capture[1]->Cnt;
case 0x04000508: return Capture[0].Cnt;
case 0x04000509: return Capture[1].Cnt;
}
}
@ -1012,7 +1065,7 @@ u16 SPU::Read16(u32 addr)
{
if (addr < 0x04000500)
{
SPUChannel* chan = Channels[(addr >> 4) & 0xF];
SPUChannel* chan = &Channels[(addr >> 4) & 0xF];
switch (addr & 0xF)
{
@ -1027,7 +1080,7 @@ u16 SPU::Read16(u32 addr)
case 0x04000500: return Cnt;
case 0x04000504: return Bias;
case 0x04000508: return Capture[0]->Cnt | (Capture[1]->Cnt << 8);
case 0x04000508: return Capture[0].Cnt | (Capture[1].Cnt << 8);
}
}
@ -1039,7 +1092,7 @@ u32 SPU::Read32(u32 addr)
{
if (addr < 0x04000500)
{
SPUChannel* chan = Channels[(addr >> 4) & 0xF];
SPUChannel* chan = &Channels[(addr >> 4) & 0xF];
switch (addr & 0xF)
{
@ -1053,10 +1106,10 @@ u32 SPU::Read32(u32 addr)
case 0x04000500: return Cnt;
case 0x04000504: return Bias;
case 0x04000508: return Capture[0]->Cnt | (Capture[1]->Cnt << 8);
case 0x04000508: return Capture[0].Cnt | (Capture[1].Cnt << 8);
case 0x04000510: return Capture[0]->DstAddr;
case 0x04000518: return Capture[1]->DstAddr;
case 0x04000510: return Capture[0].DstAddr;
case 0x04000518: return Capture[1].DstAddr;
}
}
@ -1068,7 +1121,7 @@ void SPU::Write8(u32 addr, u8 val)
{
if (addr < 0x04000500)
{
SPUChannel* chan = Channels[(addr >> 4) & 0xF];
SPUChannel* chan = &Channels[(addr >> 4) & 0xF];
switch (addr & 0xF)
{
@ -1092,11 +1145,11 @@ void SPU::Write8(u32 addr, u8 val)
return;
case 0x04000508:
Capture[0]->SetCnt(val);
Capture[0].SetCnt(val);
if (val & 0x03) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %02X\n", val);
return;
case 0x04000509:
Capture[1]->SetCnt(val);
Capture[1].SetCnt(val);
if (val & 0x03) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %02X\n", val);
return;
}
@ -1109,7 +1162,7 @@ void SPU::Write16(u32 addr, u16 val)
{
if (addr < 0x04000500)
{
SPUChannel* chan = Channels[(addr >> 4) & 0xF];
SPUChannel* chan = &Channels[(addr >> 4) & 0xF];
switch (addr & 0xF)
{
@ -1117,8 +1170,8 @@ void SPU::Write16(u32 addr, u16 val)
case 0x2: chan->SetCnt((chan->Cnt & 0x0000FFFF) | (val << 16)); return;
case 0x8:
chan->SetTimerReload(val);
if ((addr & 0xF0) == 0x10) Capture[0]->SetTimerReload(val);
else if ((addr & 0xF0) == 0x30) Capture[1]->SetTimerReload(val);
if ((addr & 0xF0) == 0x10) Capture[0].SetTimerReload(val);
else if ((addr & 0xF0) == 0x30) Capture[1].SetTimerReload(val);
return;
case 0xA: chan->SetLoopPos(val); return;
@ -1141,13 +1194,13 @@ void SPU::Write16(u32 addr, u16 val)
return;
case 0x04000508:
Capture[0]->SetCnt(val & 0xFF);
Capture[1]->SetCnt(val >> 8);
Capture[0].SetCnt(val & 0xFF);
Capture[1].SetCnt(val >> 8);
if (val & 0x0303) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %04X\n", val);
return;
case 0x04000514: Capture[0]->SetLength(val); return;
case 0x0400051C: Capture[1]->SetLength(val); return;
case 0x04000514: Capture[0].SetLength(val); return;
case 0x0400051C: Capture[1].SetLength(val); return;
}
}
@ -1158,7 +1211,7 @@ void SPU::Write32(u32 addr, u32 val)
{
if (addr < 0x04000500)
{
SPUChannel* chan = Channels[(addr >> 4) & 0xF];
SPUChannel* chan = &Channels[(addr >> 4) & 0xF];
switch (addr & 0xF)
{
@ -1168,8 +1221,8 @@ void SPU::Write32(u32 addr, u32 val)
chan->SetLoopPos(val >> 16);
val &= 0xFFFF;
chan->SetTimerReload(val);
if ((addr & 0xF0) == 0x10) Capture[0]->SetTimerReload(val);
else if ((addr & 0xF0) == 0x30) Capture[1]->SetTimerReload(val);
if ((addr & 0xF0) == 0x10) Capture[0].SetTimerReload(val);
else if ((addr & 0xF0) == 0x30) Capture[1].SetTimerReload(val);
return;
case 0xC: chan->SetLength(val); return;
}
@ -1189,15 +1242,15 @@ void SPU::Write32(u32 addr, u32 val)
return;
case 0x04000508:
Capture[0]->SetCnt(val & 0xFF);
Capture[1]->SetCnt(val >> 8);
Capture[0].SetCnt(val & 0xFF);
Capture[1].SetCnt(val >> 8);
if (val & 0x0303) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %04X\n", val);
return;
case 0x04000510: Capture[0]->SetDstAddr(val); return;
case 0x04000514: Capture[0]->SetLength(val & 0xFFFF); return;
case 0x04000518: Capture[1]->SetDstAddr(val); return;
case 0x0400051C: Capture[1]->SetLength(val & 0xFFFF); return;
case 0x04000510: Capture[0].SetDstAddr(val); return;
case 0x04000514: Capture[0].SetLength(val & 0xFFFF); return;
case 0x04000518: Capture[1].SetDstAddr(val); return;
case 0x0400051C: Capture[1].SetLength(val & 0xFFFF); return;
}
}
}

130
src/SPU.h
View File

@ -27,11 +27,25 @@ namespace melonDS
class NDS;
class SPU;
enum class AudioBitDepth
{
Auto,
_10Bit,
_16Bit,
};
enum class AudioInterpolation
{
None,
Linear,
Cosine,
Cubic,
};
class SPUChannel
{
public:
SPUChannel(u32 num, melonDS::NDS& nds);
~SPUChannel();
SPUChannel(u32 num, melonDS::NDS& nds, AudioInterpolation interpolation);
void Reset();
void DoSavestate(Savestate* file);
@ -39,44 +53,40 @@ public:
static const u16 ADPCMTable[89];
static const s16 PSGTable[8][8];
static s16 InterpCos[0x100];
static s16 InterpCubic[0x100][4];
static bool InterpInited;
// audio interpolation is an improvement upon the original hardware
// (which performs no interpolation)
int InterpType;
AudioInterpolation InterpType = AudioInterpolation::None;
u32 Num;
const u32 Num;
u32 Cnt;
u32 SrcAddr;
u16 TimerReload;
u32 LoopPos;
u32 Length;
u32 Cnt = 0;
u32 SrcAddr = 0;
u16 TimerReload = 0;
u32 LoopPos = 0;
u32 Length = 0;
u8 Volume;
u8 VolumeShift;
u8 Pan;
u8 Volume = 0;
u8 VolumeShift = 0;
u8 Pan = 0;
bool KeyOn;
u32 Timer;
s32 Pos;
s16 PrevSample[3];
s16 CurSample;
u16 NoiseVal;
bool KeyOn = false;
u32 Timer = 0;
s32 Pos = 0;
s16 PrevSample[3] {};
s16 CurSample = 0;
u16 NoiseVal = 0;
s32 ADPCMVal;
s32 ADPCMIndex;
s32 ADPCMValLoop;
s32 ADPCMIndexLoop;
u8 ADPCMCurByte;
s32 ADPCMVal = 0;
s32 ADPCMIndex = 0;
s32 ADPCMValLoop = 0;
s32 ADPCMIndexLoop = 0;
u8 ADPCMCurByte = 0;
u32 FIFO[8];
u32 FIFOReadPos;
u32 FIFOWritePos;
u32 FIFOReadOffset;
u32 FIFOLevel;
u32 FIFO[8] {};
u32 FIFOReadPos = 0;
u32 FIFOWritePos = 0;
u32 FIFOReadOffset = 0;
u32 FIFOLevel = 0;
void FIFO_BufferData();
template<typename T> T FIFO_ReadData();
@ -150,25 +160,24 @@ class SPUCaptureUnit
{
public:
SPUCaptureUnit(u32 num, melonDS::NDS&);
~SPUCaptureUnit();
void Reset();
void DoSavestate(Savestate* file);
u32 Num;
const u32 Num;
u8 Cnt;
u32 DstAddr;
u16 TimerReload;
u32 Length;
u8 Cnt = 0;
u32 DstAddr = 0;
u16 TimerReload = 0;
u32 Length = 0;
u32 Timer;
s32 Pos;
u32 Timer = 0;
s32 Pos = 0;
u32 FIFO[4];
u32 FIFOReadPos;
u32 FIFOWritePos;
u32 FIFOWriteOffset;
u32 FIFOLevel;
u32 FIFO[4] {};
u32 FIFOReadPos = 0;
u32 FIFOWritePos = 0;
u32 FIFOWriteOffset = 0;
u32 FIFOLevel = 0;
void FIFO_FlushData();
template<typename T> void FIFO_WriteData(T val);
@ -206,7 +215,7 @@ private:
class SPU
{
public:
SPU(melonDS::NDS& nds);
explicit SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpolation);
~SPU();
void Reset();
void DoSavestate(Savestate* file);
@ -216,10 +225,11 @@ public:
void SetPowerCnt(u32 val);
// 0=none 1=linear 2=cosine 3=cubic
void SetInterpolation(int type);
void SetInterpolation(AudioInterpolation type);
void SetBias(u16 bias);
void SetDegrade10Bit(bool enable);
void SetDegrade10Bit(AudioBitDepth depth);
void SetApplyBias(bool enable);
void Mix(u32 dummy);
@ -227,7 +237,7 @@ public:
void TrimOutput();
void DrainOutput();
void InitOutput();
int GetOutputSize();
int GetOutputSize() const;
void Sync(bool wait);
int ReadOutput(s16* data, int samples);
void TransferOutput();
@ -242,23 +252,23 @@ public:
private:
static const u32 OutputBufferSize = 2*2048;
melonDS::NDS& NDS;
s16 OutputBackbuffer[2 * OutputBufferSize];
u32 OutputBackbufferWritePosition;
s16 OutputBackbuffer[2 * OutputBufferSize] {};
u32 OutputBackbufferWritePosition = 0;
s16 OutputFrontBuffer[2 * OutputBufferSize];
u32 OutputFrontBufferWritePosition;
u32 OutputFrontBufferReadPosition;
s16 OutputFrontBuffer[2 * OutputBufferSize] {};
u32 OutputFrontBufferWritePosition = 0;
u32 OutputFrontBufferReadPosition = 0;
Platform::Mutex* AudioLock;
u16 Cnt;
u8 MasterVolume;
u16 Bias;
bool ApplyBias;
bool Degrade10Bit;
u16 Cnt = 0;
u8 MasterVolume = 0;
u16 Bias = 0;
bool ApplyBias = true;
bool Degrade10Bit = false;
SPUChannel* Channels[16];
SPUCaptureUnit* Capture[2];
std::array<SPUChannel, 16> Channels;
std::array<SPUCaptureUnit, 2> Capture;
};
}

View File

@ -97,7 +97,7 @@ struct __attribute__((packed)) TinyVector
Data[i] = Data[i + 1];*/
}
int Find(T needle)
int Find(T needle) const
{
for (int i = 0; i < Length; i++)
{
@ -125,6 +125,12 @@ struct __attribute__((packed)) TinyVector
assert(index >= 0 && index < Length);
return Data[index];
}
const T& operator[](int index) const
{
assert(index >= 0 && index < Length);
return Data[index];
}
};
}

66
src/Utils.cpp Normal file
View File

@ -0,0 +1,66 @@
/*
Copyright 2016-2023 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/.
*/
#include "Utils.h"
#include <string.h>
namespace melonDS
{
std::pair<std::unique_ptr<u8[]>, u32> PadToPowerOf2(std::unique_ptr<u8[]>&& data, u32 len) noexcept
{
if (data == nullptr || len == 0)
return {nullptr, 0};
if ((len & (len - 1)) == 0)
return {std::move(data), len};
u32 newlen = 1;
while (newlen < len)
newlen <<= 1;
auto newdata = std::make_unique<u8[]>(newlen);
memcpy(newdata.get(), data.get(), len);
data = nullptr;
return {std::move(newdata), newlen};
}
std::pair<std::unique_ptr<u8[]>, u32> PadToPowerOf2(const u8* data, u32 len) noexcept
{
if (len == 0)
return {nullptr, 0};
u32 newlen = 1;
while (newlen < len)
newlen <<= 1;
auto newdata = std::make_unique<u8[]>(newlen);
memcpy(newdata.get(), data, len);
return {std::move(newdata), newlen};
}
std::unique_ptr<u8[]> CopyToUnique(const u8* data, u32 len) noexcept
{
if (data == nullptr || len == 0)
return nullptr;
auto newdata = std::make_unique<u8[]>(len);
memcpy(newdata.get(), data, len);
return newdata;
}
}

43
src/Utils.h Normal file
View File

@ -0,0 +1,43 @@
/*
Copyright 2016-2023 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 MELONDS_UTILS_H
#define MELONDS_UTILS_H
#include <memory>
#include "types.h"
#include <utility>
namespace melonDS
{
/// Ensures that the given array is a power of 2 in length.
///
/// @return If \c len is a power of 2, returns \c data and \c len unchanged
/// without copying anything.
/// If \c data is \c nullptr, returns <tt>{nullptr, 0}</tt>.
/// Otherwise, return a copy of \c data with zero-padding to the next power of 2.
/// @post \c data is \c nullptr, even if it doesn't need to be copied.
std::pair<std::unique_ptr<u8[]>, u32> PadToPowerOf2(std::unique_ptr<u8[]>&& data, u32 len) noexcept;
std::pair<std::unique_ptr<u8[]>, u32> PadToPowerOf2(const u8* data, u32 len) noexcept;
std::unique_ptr<u8[]> CopyToUnique(const u8* data, u32 len) noexcept;
}
#endif // MELONDS_UTILS_H

View File

@ -78,12 +78,12 @@ const u8 Wifi::MPAckMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x03};
// * TX errors (if applicable)
bool MACEqual(u8* a, const u8* b)
bool MACEqual(const u8* a, const u8* b)
{
return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]);
}
bool MACIsBroadcast(u8* a)
bool MACIsBroadcast(const u8* a)
{
return (*(u32*)&a[0] == 0xFFFFFFFF) && (*(u16*)&a[4] == 0xFFFF);
}
@ -158,12 +158,12 @@ void Wifi::Reset()
}
#undef BBREG_FIXED
const Firmware* fw = NDS.SPI.GetFirmware();
const Firmware& fw = NDS.SPI.GetFirmware();
RFVersion = fw->GetHeader().RFChipType;
RFVersion = fw.GetHeader().RFChipType;
memset(RFRegs, 0, 4*0x40);
Firmware::FirmwareConsoleType console = fw->GetHeader().ConsoleType;
Firmware::FirmwareConsoleType console = fw.GetHeader().ConsoleType;
if (console == Firmware::FirmwareConsoleType::DS)
IOPORT(0x000) = 0x1440;
else if (console == Firmware::FirmwareConsoleType::DSLite)
@ -440,14 +440,14 @@ void Wifi::PowerDown()
}
int Wifi::PreambleLen(int rate)
int Wifi::PreambleLen(int rate) const
{
if (rate == 1) return 192;
if (IOPORT(W_Preamble) & 0x0004) return 96;
return 192;
}
u32 Wifi::NumClients(u16 bitmask)
u32 Wifi::NumClients(u16 bitmask) const
{
u32 ret = 0;
for (int i = 1; i < 16; i++)
@ -457,7 +457,7 @@ u32 Wifi::NumClients(u16 bitmask)
return ret;
}
void Wifi::IncrementTXCount(TXSlot* slot)
void Wifi::IncrementTXCount(const TXSlot* slot)
{
u8 cnt = RAM[slot->Addr + 0x4];
if (cnt < 0xFF) cnt++;
@ -477,7 +477,7 @@ void Wifi::ReportMPReplyErrors(u16 clientfail)
}
}
void Wifi::TXSendFrame(TXSlot* slot, int num)
void Wifi::TXSendFrame(const TXSlot* slot, int num)
{
u32 noseqno = 0;
@ -2258,12 +2258,12 @@ void Wifi::Write(u32 addr, u16 val)
}
u8* Wifi::GetMAC()
const u8* Wifi::GetMAC() const
{
return (u8*)&IOPORT(W_MACAddr0);
}
u8* Wifi::GetBSSID()
const u8* Wifi::GetBSSID() const
{
return (u8*)&IOPORT(W_BSSID0);
}

View File

@ -169,8 +169,8 @@ public:
u16 Read(u32 addr);
void Write(u32 addr, u16 val);
u8* GetMAC();
u8* GetBSSID();
const u8* GetMAC() const;
const u8* GetBSSID() const;
private:
melonDS::NDS& NDS;
@ -261,12 +261,12 @@ private:
void SetStatus(u32 status);
void PowerDown();
int PreambleLen(int rate);
u32 NumClients(u16 bitmask);
void IncrementTXCount(TXSlot* slot);
int PreambleLen(int rate) const;
u32 NumClients(u16 bitmask) const;
void IncrementTXCount(const TXSlot* slot);
void ReportMPReplyErrors(u16 clientfail);
void TXSendFrame(TXSlot* slot, int num);
void TXSendFrame(const TXSlot* slot, int num);
void StartTX_LocN(int nslot, int loc);
void StartTX_Cmd();
void StartTX_Beacon();

View File

@ -66,8 +66,8 @@ const u8 WifiAP::APMac[6] = {0x00, 0xF0, 0x77, 0x77, 0x77, 0x77};
#define PALIGN_4(p, base) while (PLEN(p,base) & 0x3) *p++ = 0xFF;
bool MACEqual(u8* a, const u8* b);
bool MACIsBroadcast(u8* a);
bool MACEqual(const u8* a, const u8* b);
bool MACIsBroadcast(const u8* a);
WifiAP::WifiAP(Wifi* client) : Client(client)
@ -107,7 +107,7 @@ void WifiAP::MSTimer()
}
int WifiAP::HandleManagementFrame(u8* data, int len)
int WifiAP::HandleManagementFrame(const u8* data, int len)
{
// TODO: perfect this
// noting that frames sent pre-auth/assoc don't have a proper BSSID
@ -258,7 +258,7 @@ int WifiAP::HandleManagementFrame(u8* data, int len)
}
int WifiAP::SendPacket(u8* data, int len)
int WifiAP::SendPacket(const u8* data, int len)
{
data += 12;

View File

@ -38,7 +38,7 @@ public:
void MSTimer();
// packet format: 12-byte TX header + original 802.11 frame
int SendPacket(u8* data, int len);
int SendPacket(const u8* data, int len);
int RecvPacket(u8* data);
private:
@ -60,7 +60,7 @@ private:
// 0=disconnected 1=authenticated 2=associated
int ClientStatus;
int HandleManagementFrame(u8* data, int len);
int HandleManagementFrame(const u8* data, int len);
};
}

View File

@ -728,13 +728,13 @@ static int dbc_2nd (BYTE c)
#if FF_USE_LFN
/* Get a Unicode code point from the TCHAR string in defined API encodeing */
/* Get a Unicode code point from the FF_TCHAR string in defined API encodeing */
static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */
const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */
const FF_TCHAR** str /* Pointer to pointer to FF_TCHAR string in configured encoding */
)
{
DWORD uc;
const TCHAR *p = *str;
const FF_TCHAR *p = *str;
#if FF_LFN_UNICODE == 1 /* UTF-16 input */
WCHAR wc;
@ -771,7 +771,7 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on
}
#elif FF_LFN_UNICODE == 3 /* UTF-32 input */
uc = (TCHAR)*p++; /* Get a unit */
uc = (FF_TCHAR)*p++; /* Get a unit */
if (uc >= 0x110000 || IsSurrogate(uc)) return 0xFFFFFFFF; /* Wrong code? */
if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */
@ -800,7 +800,7 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on
/* Store a Unicode char in defined API encoding */
static UINT put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
DWORD chr, /* UTF-16 encoded character (Surrogate pair if >=0x10000) */
TCHAR* buf, /* Output buffer */
FF_TCHAR* buf, /* Output buffer */
UINT szb /* Size of the buffer */
)
{
@ -824,20 +824,20 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over
if (chr < 0x80) { /* Single byte code? */
if (szb < 1) return 0; /* Buffer overflow? */
*buf = (TCHAR)chr;
*buf = (FF_TCHAR)chr;
return 1;
}
if (chr < 0x800) { /* 2-byte sequence? */
if (szb < 2) return 0; /* Buffer overflow? */
*buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F));
*buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
*buf++ = (FF_TCHAR)(0xC0 | (chr >> 6 & 0x1F));
*buf++ = (FF_TCHAR)(0x80 | (chr >> 0 & 0x3F));
return 2;
}
if (chr < 0x10000) { /* 3-byte sequence? */
if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer overflow or wrong code? */
*buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F));
*buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F));
*buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
*buf++ = (FF_TCHAR)(0xE0 | (chr >> 12 & 0x0F));
*buf++ = (FF_TCHAR)(0x80 | (chr >> 6 & 0x3F));
*buf++ = (FF_TCHAR)(0x80 | (chr >> 0 & 0x3F));
return 3;
}
/* 4-byte sequence */
@ -846,10 +846,10 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over
chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */
if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */
chr = (hc | chr) + 0x10000;
*buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07));
*buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F));
*buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F));
*buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
*buf++ = (FF_TCHAR)(0xF0 | (chr >> 18 & 0x07));
*buf++ = (FF_TCHAR)(0x80 | (chr >> 12 & 0x3F));
*buf++ = (FF_TCHAR)(0x80 | (chr >> 6 & 0x3F));
*buf++ = (FF_TCHAR)(0x80 | (chr >> 0 & 0x3F));
return 4;
#elif FF_LFN_UNICODE == 3 /* UTF-32 output */
@ -862,7 +862,7 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over
if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */
chr = (hc | chr) + 0x10000;
}
*buf++ = (TCHAR)chr;
*buf++ = (FF_TCHAR)chr;
return 1;
#else /* ANSI/OEM output */
@ -872,11 +872,11 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over
if (wc >= 0x100) { /* Is this a DBC? */
if (szb < 2) return 0;
*buf++ = (char)(wc >> 8); /* Store DBC 1st byte */
*buf++ = (TCHAR)wc; /* Store DBC 2nd byte */
*buf++ = (FF_TCHAR)wc; /* Store DBC 2nd byte */
return 2;
}
if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer overflow? */
*buf++ = (TCHAR)wc; /* Store the character */
*buf++ = (FF_TCHAR)wc; /* Store the character */
return 1;
#endif
}
@ -2595,7 +2595,7 @@ static void get_fileinfo (
FATFS *fs = dp->obj.fs;
UINT nw;
#else
TCHAR c;
FF_TCHAR c;
#endif
@ -2668,7 +2668,7 @@ static void get_fileinfo (
if (nw == 0) { di = 0; break; } /* Buffer overflow? */
di += nw;
#else /* ANSI/OEM output */
fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */
fno->altname[di++] = (FF_TCHAR)wc; /* Store it without any conversion */
#endif
}
fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */
@ -2681,7 +2681,7 @@ static void get_fileinfo (
wc = (WCHAR)fno->altname[si];
if (wc == '.') lcf = NS_EXT;
if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += 0x20;
fno->fname[di] = (TCHAR)wc;
fno->fname[di] = (FF_TCHAR)wc;
}
}
fno->fname[di] = 0; /* Terminate the LFN */
@ -2691,7 +2691,7 @@ static void get_fileinfo (
#else /* Non-LFN configuration */
si = di = 0;
while (si < 11) { /* Copy name body and extension */
c = (TCHAR)dp->dir[si++];
c = (FF_TCHAR)dp->dir[si++];
if (c == ' ') continue; /* Skip padding spaces */
if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */
if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */
@ -2719,7 +2719,7 @@ static void get_fileinfo (
static DWORD get_achar ( /* Get a character and advance ptr */
const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */
const FF_TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */
)
{
DWORD chr;
@ -2750,13 +2750,13 @@ static DWORD get_achar ( /* Get a character and advance ptr */
static int pattern_match ( /* 0:mismatched, 1:matched */
const TCHAR* pat, /* Matching pattern */
const TCHAR* nam, /* String to be tested */
const FF_TCHAR* pat, /* Matching pattern */
const FF_TCHAR* nam, /* String to be tested */
UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */
UINT recur /* Recursion count */
)
{
const TCHAR *pptr, *nptr;
const FF_TCHAR *pptr, *nptr;
DWORD pchr, nchr;
UINT sk;
@ -2800,7 +2800,7 @@ static int pattern_match ( /* 0:mismatched, 1:matched */
static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
FF_DIR* dp, /* Pointer to the directory object */
const TCHAR** path /* Pointer to pointer to the segment in the path string */
const FF_TCHAR** path /* Pointer to pointer to the segment in the path string */
)
{
#if FF_USE_LFN /* LFN configuration */
@ -2808,7 +2808,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr
WCHAR wc, *lfn;
DWORD uc;
UINT i, ni, si, di;
const TCHAR *p;
const FF_TCHAR *p;
/* Create LFN into LFN working buffer */
@ -3002,7 +3002,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr
static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
FF_DIR* dp, /* Directory object to return last directory and found object */
const TCHAR* path /* Full-path string to find a file or directory */
const FF_TCHAR* path /* Full-path string to find a file or directory */
)
{
FRESULT res;
@ -3088,11 +3088,11 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
/*-----------------------------------------------------------------------*/
static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
const TCHAR** path /* Pointer to pointer to the path name */
const FF_TCHAR** path /* Pointer to pointer to the path name */
)
{
const TCHAR *tp, *tt;
TCHAR tc;
const FF_TCHAR *tp, *tt;
FF_TCHAR tc;
int i;
int vol = -1;
#if FF_STR_VOLUME_ID /* Find string volume ID */
@ -3118,7 +3118,7 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb
c = *sp++; tc = *tp++;
if (IsLower(c)) c -= 0x20;
if (IsLower(tc)) tc -= 0x20;
} while (c && (TCHAR)c == tc);
} while (c && (FF_TCHAR)c == tc);
} while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */
}
#endif
@ -3138,7 +3138,7 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb
c = *sp++; tc = *(++tt);
if (IsLower(c)) c -= 0x20;
if (IsLower(tc)) tc -= 0x20;
} while (c && (TCHAR)c == tc);
} while (c && (FF_TCHAR)c == tc);
} while ((c || (tc != '/' && !IsTerminator(tc))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */
if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
vol = i; /* Drive number */
@ -3330,7 +3330,7 @@ static UINT find_volume ( /* Returns BS status found in the hosting drive */
/*-----------------------------------------------------------------------*/
static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
const TCHAR** path, /* Pointer to pointer to the path name (drive number) */
const FF_TCHAR** path, /* Pointer to pointer to the path name (drive number) */
FATFS** rfs, /* Pointer to pointer to the found filesystem object */
BYTE mode /* !=0: Check write protection for write access */
)
@ -3604,14 +3604,14 @@ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
FRESULT f_mount (
FATFS* fs, /* Pointer to the filesystem object to be registered (NULL:unmount)*/
const TCHAR* path, /* Logical drive number to be mounted/unmounted */
const FF_TCHAR* path, /* Logical drive number to be mounted/unmounted */
BYTE opt /* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */
)
{
FATFS *cfs;
int vol;
FRESULT res;
const TCHAR *rp = path;
const FF_TCHAR *rp = path;
/* Get logical drive number */
@ -3652,7 +3652,7 @@ FRESULT f_mount (
FRESULT f_open (
FF_FIL* fp, /* Pointer to the blank file object */
const TCHAR* path, /* Pointer to the file name */
const FF_TCHAR* path, /* Pointer to the file name */
BYTE mode /* Access mode and open mode flags */
)
{
@ -4186,7 +4186,7 @@ FRESULT f_close (
/*-----------------------------------------------------------------------*/
FRESULT f_chdrive (
const TCHAR* path /* Drive number to set */
const FF_TCHAR* path /* Drive number to set */
)
{
int vol;
@ -4203,7 +4203,7 @@ FRESULT f_chdrive (
FRESULT f_chdir (
const TCHAR* path /* Pointer to the directory path */
const FF_TCHAR* path /* Pointer to the directory path */
)
{
#if FF_STR_VOLUME_ID == 2
@ -4265,8 +4265,8 @@ FRESULT f_chdir (
#if FF_FS_RPATH >= 2
FRESULT f_getcwd (
TCHAR* buff, /* Pointer to the directory path */
UINT len /* Size of buff in unit of TCHAR */
FF_TCHAR* buff, /* Pointer to the directory path */
UINT len /* Size of buff in unit of FF_TCHAR */
)
{
FRESULT res;
@ -4274,7 +4274,7 @@ FRESULT f_getcwd (
FATFS *fs;
UINT i, n;
DWORD ccl;
TCHAR *tp = buff;
FF_TCHAR *tp = buff;
#if FF_VOLUMES >= 2
UINT vl;
#if FF_STR_VOLUME_ID
@ -4287,7 +4287,7 @@ FRESULT f_getcwd (
/* Get logical drive */
buff[0] = 0; /* Set null string to get current volume */
res = mount_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */
res = mount_volume((const FF_TCHAR**)&buff, &fs, 0); /* Get current volume */
if (res == FR_OK) {
dj.obj.fs = fs;
INIT_NAMBUF(fs);
@ -4328,15 +4328,15 @@ FRESULT f_getcwd (
#if FF_STR_VOLUME_ID >= 1 /* String volume ID */
for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; n++) ;
if (i >= n + 2) {
if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/';
for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ;
if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':';
if (FF_STR_VOLUME_ID == 2) *tp++ = (FF_TCHAR)'/';
for (vl = 0; vl < n; *tp++ = (FF_TCHAR)vp[vl], vl++) ;
if (FF_STR_VOLUME_ID == 1) *tp++ = (FF_TCHAR)':';
vl++;
}
#else /* Numeric volume ID */
if (i >= 3) {
*tp++ = (TCHAR)'0' + CurrVol;
*tp++ = (TCHAR)':';
*tp++ = (FF_TCHAR)'0' + CurrVol;
*tp++ = (FF_TCHAR)':';
vl = 2;
}
#endif
@ -4530,7 +4530,7 @@ FRESULT f_lseek (
FRESULT f_opendir (
FF_DIR* dp, /* Pointer to directory object to create */
const TCHAR* path /* Pointer to the directory path */
const FF_TCHAR* path /* Pointer to the directory path */
)
{
FRESULT res;
@ -4688,8 +4688,8 @@ FRESULT f_findnext (
FRESULT f_findfirst (
FF_DIR* dp, /* Pointer to the blank directory object */
FF_FILINFO* fno, /* Pointer to the file information structure */
const TCHAR* path, /* Pointer to the directory to open */
const TCHAR* pattern /* Pointer to the matching pattern */
const FF_TCHAR* path, /* Pointer to the directory to open */
const FF_TCHAR* pattern /* Pointer to the matching pattern */
)
{
FRESULT res;
@ -4713,7 +4713,7 @@ FRESULT f_findfirst (
/*-----------------------------------------------------------------------*/
FRESULT f_stat (
const TCHAR* path, /* Pointer to the file path */
const FF_TCHAR* path, /* Pointer to the file path */
FF_FILINFO* fno /* Pointer to file information to return */
)
{
@ -4748,7 +4748,7 @@ FRESULT f_stat (
/*-----------------------------------------------------------------------*/
FRESULT f_getfree (
const TCHAR* path, /* Logical drive number */
const FF_TCHAR* path, /* Logical drive number */
DWORD* nclst, /* Pointer to a variable to return number of free clusters */
FATFS** fatfs /* Pointer to return pointer to corresponding filesystem object */
)
@ -4890,7 +4890,7 @@ FRESULT f_truncate (
/*-----------------------------------------------------------------------*/
FRESULT f_unlink (
const TCHAR* path /* Pointer to the file or directory path */
const FF_TCHAR* path /* Pointer to the file or directory path */
)
{
FRESULT res;
@ -4984,7 +4984,7 @@ FRESULT f_unlink (
/*-----------------------------------------------------------------------*/
FRESULT f_mkdir (
const TCHAR* path /* Pointer to the directory path */
const FF_TCHAR* path /* Pointer to the directory path */
)
{
FRESULT res;
@ -5068,8 +5068,8 @@ FRESULT f_mkdir (
/*-----------------------------------------------------------------------*/
FRESULT f_rename (
const TCHAR* path_old, /* Pointer to the object name to be renamed */
const TCHAR* path_new /* Pointer to the new name */
const FF_TCHAR* path_old, /* Pointer to the object name to be renamed */
const FF_TCHAR* path_new /* Pointer to the new name */
)
{
FRESULT res;
@ -5178,7 +5178,7 @@ FRESULT f_rename (
/*-----------------------------------------------------------------------*/
FRESULT f_chmod (
const TCHAR* path, /* Pointer to the file path */
const FF_TCHAR* path, /* Pointer to the file path */
BYTE attr, /* Attribute bits */
BYTE mask /* Attribute mask to change */
)
@ -5225,7 +5225,7 @@ FRESULT f_chmod (
/*-----------------------------------------------------------------------*/
FRESULT f_utime (
const TCHAR* path, /* Pointer to the file/directory name */
const FF_TCHAR* path, /* Pointer to the file/directory name */
const FF_FILINFO* fno /* Pointer to the timestamp to be set */
)
{
@ -5272,8 +5272,8 @@ FRESULT f_utime (
/*-----------------------------------------------------------------------*/
FRESULT f_getlabel (
const TCHAR* path, /* Logical drive number */
TCHAR* label, /* Buffer to store the volume label */
const FF_TCHAR* path, /* Logical drive number */
FF_TCHAR* label, /* Buffer to store the volume label */
DWORD* vsn /* Variable to store the volume serial number */
)
{
@ -5322,7 +5322,7 @@ FRESULT f_getlabel (
if (wc == 0) { di = 0; break; } /* Invalid char in current code page? */
di += put_utf(wc, &label[di], 4); /* Store it in Unicode */
#else /* ANSI/OEM output */
label[di++] = (TCHAR)wc;
label[di++] = (FF_TCHAR)wc;
#endif
}
do { /* Truncate trailing spaces */
@ -5369,7 +5369,7 @@ FRESULT f_getlabel (
/*-----------------------------------------------------------------------*/
FRESULT f_setlabel (
const TCHAR* label /* Volume label to set with heading logical drive number */
const FF_TCHAR* label /* Volume label to set with heading logical drive number */
)
{
FRESULT res;
@ -5800,7 +5800,7 @@ static FRESULT create_partition (
FRESULT f_mkfs (
const TCHAR* path, /* Logical drive number */
const FF_TCHAR* path, /* Logical drive number */
const FF_MKFS_PARM* opt, /* Format options */
void* work, /* Pointer to working buffer (null: use heap memory) */
UINT len /* Size of working buffer [byte] */
@ -6335,14 +6335,14 @@ FRESULT f_fdisk (
/* Get a String from the File */
/*-----------------------------------------------------------------------*/
TCHAR* f_gets (
TCHAR* buff, /* Pointer to the buffer to store read string */
FF_TCHAR* f_gets (
FF_TCHAR* buff, /* Pointer to the buffer to store read string */
int len, /* Size of string buffer (items) */
FF_FIL* fp /* Pointer to the file object */
)
{
int nc = 0;
TCHAR *p = buff;
FF_TCHAR *p = buff;
BYTE s[4];
UINT rc;
DWORD dc;
@ -6407,32 +6407,32 @@ TCHAR* f_gets (
if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip \r off if needed */
#if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */
if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP at UTF-16? */
*p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */
*p++ = (FF_TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */
dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */
}
*p++ = (TCHAR)dc; nc++;
*p++ = (FF_TCHAR)dc; nc++;
if (dc == '\n') break; /* End of line? */
#elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */
if (dc < 0x80) { /* Single byte? */
*p++ = (TCHAR)dc;
*p++ = (FF_TCHAR)dc;
nc++;
if (dc == '\n') break; /* End of line? */
} else {
if (dc < 0x800) { /* 2-byte sequence? */
*p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F));
*p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
*p++ = (FF_TCHAR)(0xC0 | (dc >> 6 & 0x1F));
*p++ = (FF_TCHAR)(0x80 | (dc >> 0 & 0x3F));
nc += 2;
} else {
if (dc < 0x10000) { /* 3-byte sequence? */
*p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F));
*p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
*p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
*p++ = (FF_TCHAR)(0xE0 | (dc >> 12 & 0x0F));
*p++ = (FF_TCHAR)(0x80 | (dc >> 6 & 0x3F));
*p++ = (FF_TCHAR)(0x80 | (dc >> 0 & 0x3F));
nc += 3;
} else { /* 4-byte sequence? */
*p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07));
*p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F));
*p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
*p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
*p++ = (FF_TCHAR)(0xF0 | (dc >> 18 & 0x07));
*p++ = (FF_TCHAR)(0x80 | (dc >> 12 & 0x3F));
*p++ = (FF_TCHAR)(0x80 | (dc >> 6 & 0x3F));
*p++ = (FF_TCHAR)(0x80 | (dc >> 0 & 0x3F));
nc += 4;
}
}
@ -6447,7 +6447,7 @@ TCHAR* f_gets (
if (rc != 1) break; /* EOF? */
dc = s[0];
if (FF_USE_STRFUNC == 2 && dc == '\r') continue;
*p++ = (TCHAR)dc; nc++;
*p++ = (FF_TCHAR)dc; nc++;
if (dc == '\n') break;
}
#endif
@ -6485,7 +6485,7 @@ typedef struct {
/* Buffered file write with code conversion */
static void putc_bfd (putbuff* pb, TCHAR c)
static void putc_bfd (putbuff* pb, FF_TCHAR c)
{
UINT n;
int i, nc;
@ -6493,7 +6493,7 @@ static void putc_bfd (putbuff* pb, TCHAR c)
WCHAR hs, wc;
#if FF_LFN_UNICODE == 2
DWORD dc;
const TCHAR *tp;
const FF_TCHAR *tp;
#endif
#endif
@ -6535,7 +6535,7 @@ static void putc_bfd (putbuff* pb, TCHAR c)
return;
}
}
tp = (const TCHAR*)pb->bs;
tp = (const FF_TCHAR*)pb->bs;
dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */
if (dc == 0xFFFFFFFF) return; /* Wrong code? */
wc = (WCHAR)dc;
@ -6638,7 +6638,7 @@ static void putc_init (putbuff* pb, FF_FIL* fp)
int f_putc (
TCHAR c, /* A character to be output */
FF_TCHAR c, /* A character to be output */
FF_FIL* fp /* Pointer to the file object */
)
{
@ -6658,7 +6658,7 @@ int f_putc (
/*-----------------------------------------------------------------------*/
int f_puts (
const TCHAR* str, /* Pointer to the string to be output */
const FF_TCHAR* str, /* Pointer to the string to be output */
FF_FIL* fp /* Pointer to the file object */
)
{
@ -6727,7 +6727,7 @@ static void ftoa (
char* buf, /* Buffer to output the floating point string */
double val, /* Value to output */
int prec, /* Number of fractional digits */
TCHAR fmt /* Notation */
FF_TCHAR fmt /* Notation */
)
{
int d;
@ -6800,7 +6800,7 @@ static void ftoa (
int f_printf (
FF_FIL* fp, /* Pointer to the file object */
const TCHAR* fmt, /* Pointer to the format string */
const FF_TCHAR* fmt, /* Pointer to the format string */
... /* Optional arguments... */
)
{
@ -6813,8 +6813,8 @@ int f_printf (
#else
DWORD v;
#endif
TCHAR tc, pad, *tp;
TCHAR nul = 0;
FF_TCHAR tc, pad, *tp;
FF_TCHAR nul = 0;
char d, str[SZ_NUM_BUF];
@ -6879,10 +6879,10 @@ int f_printf (
case 'X': /* Unsigned hexdecimal (upper case) */
r = 16; break;
case 'c': /* Character */
putc_bfd(&pb, (TCHAR)va_arg(arp, int));
putc_bfd(&pb, (FF_TCHAR)va_arg(arp, int));
continue;
case 's': /* String */
tp = va_arg(arp, TCHAR*); /* Get a pointer argument */
tp = va_arg(arp, FF_TCHAR*); /* Get a pointer argument */
if (!tp) tp = &nul; /* Null ptr generates a null string */
for (j = 0; tp[j]; j++) ; /* j = tcslen(tp) */
if (prec >= 0 && j > (UINT)prec) j = prec; /* Limited length of string body */
@ -6937,7 +6937,7 @@ int f_printf (
if (f & 1) str[i++] = '-'; /* Sign */
/* Write it */
for (j = i; !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */
do putc_bfd(&pb, (TCHAR)str[--i]); while (i); /* Body */
do putc_bfd(&pb, (FF_TCHAR)str[--i]); while (i); /* Body */
while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */
}

View File

@ -85,24 +85,24 @@ typedef DWORD LBA_t;
/* Type of path name strings on FatFs API (TCHAR) */
/* Type of path name strings on FatFs API (FF_TCHAR) */
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
typedef WCHAR TCHAR;
typedef WCHAR FF_TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
typedef char TCHAR;
typedef char FF_TCHAR;
#define _T(x) u8 ## x
#define _TEXT(x) u8 ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
typedef DWORD TCHAR;
typedef DWORD FF_TCHAR;
#define _T(x) U ## x
#define _TEXT(x) U ## x
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
#error Wrong FF_LFN_UNICODE setting
#else /* ANSI/OEM code in SBCS/DBCS */
typedef char TCHAR;
typedef char FF_TCHAR;
#define _T(x) x
#define _TEXT(x) x
#endif
@ -236,7 +236,7 @@ typedef struct {
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
#endif
#if FF_USE_FIND
const TCHAR* pat; /* Pointer to the name matching pattern */
const FF_TCHAR* pat; /* Pointer to the name matching pattern */
#endif
} FF_DIR;
@ -250,10 +250,10 @@ typedef struct {
WORD ftime; /* Modified time */
BYTE fattrib; /* File attribute */
#if FF_USE_LFN
TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
FF_TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
FF_TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
#else
TCHAR fname[12 + 1]; /* File name */
FF_TCHAR fname[12 + 1]; /* File name */
#endif
} FF_FILINFO;
@ -301,40 +301,40 @@ typedef enum {
/*--------------------------------------------------------------*/
/* FatFs module application interface */
FRESULT f_open (FF_FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
FRESULT f_open (FF_FIL* fp, const FF_TCHAR* path, BYTE mode); /* Open or create a file */
FRESULT f_close (FF_FIL* fp); /* Close an open file object */
FRESULT f_read (FF_FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
FRESULT f_write (FF_FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
FRESULT f_lseek (FF_FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
FRESULT f_truncate (FF_FIL* fp); /* Truncate the file */
FRESULT f_sync (FF_FIL* fp); /* Flush cached data of the writing file */
FRESULT f_opendir (FF_DIR* dp, const TCHAR* path); /* Open a directory */
FRESULT f_opendir (FF_DIR* dp, const FF_TCHAR* path); /* Open a directory */
FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */
FRESULT f_readdir (FF_DIR* dp, FF_FILINFO* fno); /* Read a directory item */
FRESULT f_findfirst (FF_DIR* dp, FF_FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
FRESULT f_findfirst (FF_DIR* dp, FF_FILINFO* fno, const FF_TCHAR* path, const FF_TCHAR* pattern); /* Find first file */
FRESULT f_findnext (FF_DIR* dp, FF_FILINFO* fno); /* Find next file */
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
FRESULT f_stat (const TCHAR* path, FF_FILINFO* fno); /* Get file status */
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
FRESULT f_utime (const TCHAR* path, const FF_FILINFO* fno); /* Change timestamp of a file/dir */
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
FRESULT f_mkdir (const FF_TCHAR* path); /* Create a sub directory */
FRESULT f_unlink (const FF_TCHAR* path); /* Delete an existing file or directory */
FRESULT f_rename (const FF_TCHAR* path_old, const FF_TCHAR* path_new); /* Rename/Move a file or directory */
FRESULT f_stat (const FF_TCHAR* path, FF_FILINFO* fno); /* Get file status */
FRESULT f_chmod (const FF_TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
FRESULT f_utime (const FF_TCHAR* path, const FF_FILINFO* fno); /* Change timestamp of a file/dir */
FRESULT f_chdir (const FF_TCHAR* path); /* Change current directory */
FRESULT f_chdrive (const FF_TCHAR* path); /* Change current drive */
FRESULT f_getcwd (FF_TCHAR* buff, UINT len); /* Get current directory */
FRESULT f_getfree (const FF_TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
FRESULT f_getlabel (const FF_TCHAR* path, FF_TCHAR* label, DWORD* vsn); /* Get volume label */
FRESULT f_setlabel (const FF_TCHAR* label); /* Set volume label */
FRESULT f_forward (FF_FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
FRESULT f_expand (FF_FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
FRESULT f_mkfs (const TCHAR* path, const FF_MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
FRESULT f_mount (FATFS* fs, const FF_TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
FRESULT f_mkfs (const FF_TCHAR* path, const FF_MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
FRESULT f_setcp (WORD cp); /* Set current code page */
int f_putc (TCHAR c, FF_FIL* fp); /* Put a character to the file */
int f_puts (const TCHAR* str, FF_FIL* cp); /* Put a string to the file */
int f_printf (FF_FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR* buff, int len, FF_FIL* fp); /* Get a string from the file */
int f_putc (FF_TCHAR c, FF_FIL* fp); /* Put a character to the file */
int f_puts (const FF_TCHAR* str, FF_FIL* cp); /* Put a string to the file */
int f_printf (FF_FIL* fp, const FF_TCHAR* str, ...); /* Put a formatted string to the file */
FF_TCHAR* f_gets (FF_TCHAR* buff, int len, FF_FIL* fp); /* Get a string from the file */
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
#define f_error(fp) ((fp)->err)

View File

@ -110,7 +110,11 @@ DWORD get_fattime(void)
time_t timestamp = time(NULL);
struct tm timedata;
#if defined(_MSC_VER)
localtime_s(&timedata, &timestamp);
#else
localtime_r(&timestamp, &timedata);
#endif
DWORD ret;
ret = (timedata.tm_sec >> 1);

View File

@ -120,13 +120,12 @@ QVector<QString> ExtractFileFromArchive(QString path, QString wantedFile, QByteA
}
s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize)
s32 ExtractFileFromArchive(QString path, QString wantedFile, std::unique_ptr<u8[]>& filedata, u32* filesize)
{
struct archive *a = archive_read_new();
struct archive_entry *entry;
int r;
if (!filedata) return -1;
archive_read_support_format_all(a);
archive_read_support_filter_all(a);
@ -148,8 +147,8 @@ s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32*
size_t bytesToRead = archive_entry_size(entry);
if (filesize) *filesize = bytesToRead;
*filedata = new u8[bytesToRead];
ssize_t bytesRead = archive_read_data(a, *filedata, bytesToRead);
filedata = std::make_unique<u8[]>(bytesToRead);
ssize_t bytesRead = archive_read_data(a, filedata.get(), bytesToRead);
archive_read_close(a);
archive_read_free(a);

View File

@ -37,7 +37,7 @@ namespace Archive
using namespace melonDS;
QVector<QString> ListArchive(QString path);
s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize);
s32 ExtractFileFromArchive(QString path, QString wantedFile, std::unique_ptr<u8[]>& filedata, u32* filesize);
//QVector<QString> ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer);
//u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata);

View File

@ -369,7 +369,7 @@ void UpdateSettings(NDS& nds)
{
MicClose();
nds.SPU.SetInterpolation(Config::AudioInterp);
nds.SPU.SetInterpolation(static_cast<AudioInterpolation>(Config::AudioInterp));
SetupMicInputData();
MicOpen();

View File

@ -5,6 +5,9 @@ include(FixInterfaceIncludes)
set(SOURCES_QT_SDL
main.cpp
main_shaders.h
Screen.cpp
Window.cpp
EmuThread.cpp
CheatsDialog.cpp
Config.cpp
DateTimeDialog.cpp
@ -38,7 +41,7 @@ set(SOURCES_QT_SDL
SaveManager.cpp
CameraManager.cpp
AudioInOut.cpp
ArchiveUtil.h
ArchiveUtil.cpp
@ -82,11 +85,11 @@ if (BUILD_STATIC)
endif()
pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2)
pkg_check_modules(Slirp REQUIRED IMPORTED_TARGET slirp)
pkg_check_modules(Slirp REQUIRED slirp)
pkg_check_modules(LibArchive REQUIRED IMPORTED_TARGET libarchive)
pkg_check_modules(Zstd REQUIRED IMPORTED_TARGET libzstd)
fix_interface_includes(PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive)
fix_interface_includes(PkgConfig::SDL2 PkgConfig::LibArchive)
add_compile_definitions(ARCHIVE_SUPPORT_ENABLED)
@ -158,9 +161,12 @@ else()
target_include_directories(melonDS PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif()
target_link_libraries(melonDS PRIVATE core)
target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive PkgConfig::Zstd)
target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::LibArchive PkgConfig::Zstd)
target_link_libraries(melonDS PRIVATE ${QT_LINK_LIBS} ${CMAKE_DL_LIBS})
target_include_directories(melonDS PRIVATE "${Slirp_INCLUDE_DIRS}")
target_link_libraries(melonDS PRIVATE "${Slirp_LINK_LIBRARIES}")
if (UNIX)
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
elseif (WIN32)

View File

@ -26,7 +26,7 @@
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>5</number>
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">

View File

@ -0,0 +1,758 @@
/*
Copyright 2016-2023 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/.
*/
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <optional>
#include <vector>
#include <string>
#include <algorithm>
#include <SDL2/SDL.h>
#include "main.h"
#include "Input.h"
#include "AudioInOut.h"
#include "types.h"
#include "version.h"
#include "FrontendUtil.h"
#include "OSD.h"
#include "Args.h"
#include "NDS.h"
#include "NDSCart.h"
#include "GBACart.h"
#include "GPU.h"
#include "SPU.h"
#include "Wifi.h"
#include "Platform.h"
#include "LocalMP.h"
#include "Config.h"
#include "RTC.h"
#include "DSi.h"
#include "DSi_I2C.h"
#include "GPU3D_Soft.h"
#include "GPU3D_OpenGL.h"
#include "Savestate.h"
#include "ROMManager.h"
//#include "ArchiveUtil.h"
//#include "CameraManager.h"
//#include "CLI.h"
// TODO: uniform variable spelling
using namespace melonDS;
// TEMP
extern bool RunningSomething;
extern MainWindow* mainWindow;
extern int autoScreenSizing;
extern int videoRenderer;
extern bool videoSettingsDirty;
EmuThread::EmuThread(QObject* parent) : QThread(parent)
{
EmuStatus = emuStatus_Exit;
EmuRunning = emuStatus_Paused;
EmuPauseStack = EmuPauseStackRunning;
RunningSomething = false;
connect(this, SIGNAL(windowUpdate()), mainWindow->panelWidget, SLOT(repaint()));
connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString)));
connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart()));
connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop()));
connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger()));
connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger()));
connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger()));
connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger()));
connect(this, SIGNAL(screenLayoutChange()), mainWindow->panelWidget, SLOT(onScreenLayoutChanged()));
connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled()));
connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger()));
connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled()));
}
std::unique_ptr<NDS> EmuThread::CreateConsole(
std::unique_ptr<melonDS::NDSCart::CartCommon>&& ndscart,
std::unique_ptr<melonDS::GBACart::CartCommon>&& gbacart
) noexcept
{
auto arm7bios = ROMManager::LoadARM7BIOS();
if (!arm7bios)
return nullptr;
auto arm9bios = ROMManager::LoadARM9BIOS();
if (!arm9bios)
return nullptr;
auto firmware = ROMManager::LoadFirmware(Config::ConsoleType);
if (!firmware)
return nullptr;
#ifdef JIT_ENABLED
JITArgs jitargs {
static_cast<unsigned>(Config::JIT_MaxBlockSize),
Config::JIT_LiteralOptimisations,
Config::JIT_BranchOptimisations,
Config::JIT_FastMemory,
};
#endif
#ifdef GDBSTUB_ENABLED
GDBArgs gdbargs {
static_cast<u16>(Config::GdbPortARM7),
static_cast<u16>(Config::GdbPortARM9),
Config::GdbARM7BreakOnStartup,
Config::GdbARM9BreakOnStartup,
};
#endif
NDSArgs ndsargs {
std::move(ndscart),
std::move(gbacart),
*arm9bios,
*arm7bios,
std::move(*firmware),
#ifdef JIT_ENABLED
Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt,
#else
std::nullopt,
#endif
static_cast<AudioBitDepth>(Config::AudioBitDepth),
static_cast<AudioInterpolation>(Config::AudioInterp),
#ifdef GDBSTUB_ENABLED
Config::GdbEnabled ? std::make_optional(gdbargs) : std::nullopt,
#else
std::nullopt,
#endif
};
if (Config::ConsoleType == 1)
{
auto arm7ibios = ROMManager::LoadDSiARM7BIOS();
if (!arm7ibios)
return nullptr;
auto arm9ibios = ROMManager::LoadDSiARM9BIOS();
if (!arm9ibios)
return nullptr;
auto nand = ROMManager::LoadNAND(*arm7ibios);
if (!nand)
return nullptr;
auto sdcard = ROMManager::LoadDSiSDCard();
DSiArgs args {
std::move(ndsargs),
*arm9ibios,
*arm7ibios,
std::move(*nand),
std::move(sdcard),
Config::DSiFullBIOSBoot,
};
args.GBAROM = nullptr;
return std::make_unique<melonDS::DSi>(std::move(args));
}
return std::make_unique<melonDS::NDS>(std::move(ndsargs));
}
bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept
{
// 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.
std::unique_ptr<NDSCart::CartCommon> nextndscart;
if (std::holds_alternative<Keep>(ndsargs))
{ // If we want to keep the existing cart (if any)...
nextndscart = NDS ? NDS->EjectCart() : nullptr;
ndsargs = {};
}
else if (const auto ptr = std::get_if<std::unique_ptr<NDSCart::CartCommon>>(&ndsargs))
{
nextndscart = std::move(*ptr);
ndsargs = {};
}
if (nextndscart && nextndscart->Type() == NDSCart::Homebrew)
{
// Load DLDISDCard will return nullopt if the SD card is disabled;
// SetSDCard will accept nullopt, which means no SD card
auto& homebrew = static_cast<NDSCart::CartHomebrew&>(*nextndscart);
homebrew.SetSDCard(ROMManager::LoadDLDISDCard());
}
std::unique_ptr<GBACart::CartCommon> nextgbacart;
if (std::holds_alternative<Keep>(gbaargs))
{
nextgbacart = NDS ? NDS->EjectGBACart() : nullptr;
}
else if (const auto ptr = std::get_if<std::unique_ptr<GBACart::CartCommon>>(&gbaargs))
{
nextgbacart = std::move(*ptr);
gbaargs = {};
}
if (!NDS || NDS->ConsoleType != Config::ConsoleType)
{ // If we're switching between DS and DSi mode, or there's no console...
// To ensure the destructor is called before a new one is created,
// as the presence of global signal handlers still complicates things a bit
NDS = nullptr;
NDS::Current = nullptr;
NDS = CreateConsole(std::move(nextndscart), std::move(nextgbacart));
if (NDS == nullptr)
return false;
NDS->Reset();
NDS::Current = NDS.get();
return true;
}
auto arm9bios = ROMManager::LoadARM9BIOS();
if (!arm9bios)
return false;
auto arm7bios = ROMManager::LoadARM7BIOS();
if (!arm7bios)
return false;
auto firmware = ROMManager::LoadFirmware(NDS->ConsoleType);
if (!firmware)
return false;
if (NDS->ConsoleType == 1)
{ // If the console we're updating is a DSi...
DSi& dsi = static_cast<DSi&>(*NDS);
auto arm9ibios = ROMManager::LoadDSiARM9BIOS();
if (!arm9ibios)
return false;
auto arm7ibios = ROMManager::LoadDSiARM7BIOS();
if (!arm7ibios)
return false;
auto nandimage = ROMManager::LoadNAND(*arm7ibios);
if (!nandimage)
return false;
auto dsisdcard = ROMManager::LoadDSiSDCard();
dsi.SetFullBIOSBoot(Config::DSiFullBIOSBoot);
dsi.ARM7iBIOS = *arm7ibios;
dsi.ARM9iBIOS = *arm9ibios;
dsi.SetNAND(std::move(*nandimage));
dsi.SetSDCard(std::move(dsisdcard));
// We're moving the optional, not the card
// (inserting std::nullopt here is okay, it means no card)
dsi.EjectGBACart();
}
if (NDS->ConsoleType == 0)
{
NDS->SetGBACart(std::move(nextgbacart));
}
#ifdef JIT_ENABLED
JITArgs jitargs {
static_cast<unsigned>(Config::JIT_MaxBlockSize),
Config::JIT_LiteralOptimisations,
Config::JIT_BranchOptimisations,
Config::JIT_FastMemory,
};
NDS->SetJITArgs(Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt);
#endif
NDS->SetARM7BIOS(*arm7bios);
NDS->SetARM9BIOS(*arm9bios);
NDS->SetFirmware(std::move(*firmware));
NDS->SetNDSCart(std::move(nextndscart));
NDS->SPU.SetInterpolation(static_cast<AudioInterpolation>(Config::AudioInterp));
NDS->SPU.SetDegrade10Bit(static_cast<AudioBitDepth>(Config::AudioBitDepth));
NDS::Current = NDS.get();
return true;
}
void EmuThread::run()
{
u32 mainScreenPos[3];
Platform::FileHandle* file;
UpdateConsole(nullptr, nullptr);
// No carts are inserted when melonDS first boots
mainScreenPos[0] = 0;
mainScreenPos[1] = 0;
mainScreenPos[2] = 0;
autoScreenSizing = 0;
videoSettingsDirty = false;
if (mainWindow->hasOGL)
{
screenGL = static_cast<ScreenPanelGL*>(mainWindow->panel);
screenGL->initOpenGL();
videoRenderer = Config::_3DRenderer;
}
else
{
screenGL = nullptr;
videoRenderer = 0;
}
if (videoRenderer == 0)
{ // If we're using the software renderer...
NDS->GPU.SetRenderer3D(std::make_unique<SoftRenderer>(Config::Threaded3D != 0));
}
else
{
auto glrenderer = melonDS::GLRenderer::New();
glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor);
NDS->GPU.SetRenderer3D(std::move(glrenderer));
}
Input::Init();
u32 nframes = 0;
double perfCountsSec = 1.0 / SDL_GetPerformanceFrequency();
double lastTime = SDL_GetPerformanceCounter() * perfCountsSec;
double frameLimitError = 0.0;
double lastMeasureTime = lastTime;
u32 winUpdateCount = 0, winUpdateFreq = 1;
u8 dsiVolumeLevel = 0x1F;
file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Read);
if (file)
{
RTC::StateData state;
Platform::FileRead(&state, sizeof(state), 1, file);
Platform::CloseFile(file);
NDS->RTC.SetState(state);
}
char melontitle[100];
while (EmuRunning != emuStatus_Exit)
{
Input::Process();
if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange();
if (Input::HotkeyPressed(HK_Pause)) emit windowEmuPause();
if (Input::HotkeyPressed(HK_Reset)) emit windowEmuReset();
if (Input::HotkeyPressed(HK_FrameStep)) emit windowEmuFrameStep();
if (Input::HotkeyPressed(HK_FullscreenToggle)) emit windowFullscreenToggle();
if (Input::HotkeyPressed(HK_SwapScreens)) emit swapScreensToggle();
if (Input::HotkeyPressed(HK_SwapScreenEmphasis)) emit screenEmphasisToggle();
if (EmuRunning == emuStatus_Running || EmuRunning == emuStatus_FrameStep)
{
EmuStatus = emuStatus_Running;
if (EmuRunning == emuStatus_FrameStep) EmuRunning = emuStatus_Paused;
if (Input::HotkeyPressed(HK_SolarSensorDecrease))
{
int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorDown, true);
if (level != -1)
{
char msg[64];
sprintf(msg, "Solar sensor level: %d", level);
OSD::AddMessage(0, msg);
}
}
if (Input::HotkeyPressed(HK_SolarSensorIncrease))
{
int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorUp, true);
if (level != -1)
{
char msg[64];
sprintf(msg, "Solar sensor level: %d", level);
OSD::AddMessage(0, msg);
}
}
if (NDS->ConsoleType == 1)
{
DSi& dsi = static_cast<DSi&>(*NDS);
double currentTime = SDL_GetPerformanceCounter() * perfCountsSec;
// Handle power button
if (Input::HotkeyDown(HK_PowerButton))
{
dsi.I2C.GetBPTWL()->SetPowerButtonHeld(currentTime);
}
else if (Input::HotkeyReleased(HK_PowerButton))
{
dsi.I2C.GetBPTWL()->SetPowerButtonReleased(currentTime);
}
// Handle volume buttons
if (Input::HotkeyDown(HK_VolumeUp))
{
dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up);
}
else if (Input::HotkeyReleased(HK_VolumeUp))
{
dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up);
}
if (Input::HotkeyDown(HK_VolumeDown))
{
dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down);
}
else if (Input::HotkeyReleased(HK_VolumeDown))
{
dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down);
}
dsi.I2C.GetBPTWL()->ProcessVolumeSwitchInput(currentTime);
}
// update render settings if needed
// HACK:
// once the fast forward hotkey is released, we need to update vsync
// to the old setting again
if (videoSettingsDirty || Input::HotkeyReleased(HK_FastForward))
{
if (screenGL)
{
screenGL->setSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0);
videoRenderer = Config::_3DRenderer;
}
#ifdef OGLRENDERER_ENABLED
else
#endif
{
videoRenderer = 0;
}
videoRenderer = screenGL ? Config::_3DRenderer : 0;
videoSettingsDirty = false;
if (videoRenderer == 0)
{ // If we're using the software renderer...
NDS->GPU.SetRenderer3D(std::make_unique<SoftRenderer>(Config::Threaded3D != 0));
}
else
{
auto glrenderer = melonDS::GLRenderer::New();
glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor);
NDS->GPU.SetRenderer3D(std::move(glrenderer));
}
}
// process input and hotkeys
NDS->SetKeyMask(Input::InputMask);
if (Input::HotkeyPressed(HK_Lid))
{
bool lid = !NDS->IsLidClosed();
NDS->SetLidClosed(lid);
OSD::AddMessage(0, lid ? "Lid closed" : "Lid opened");
}
// microphone input
AudioInOut::MicProcess(*NDS);
// auto screen layout
if (Config::ScreenSizing == Frontend::screenSizing_Auto)
{
mainScreenPos[2] = mainScreenPos[1];
mainScreenPos[1] = mainScreenPos[0];
mainScreenPos[0] = NDS->PowerControl9 >> 15;
int guess;
if (mainScreenPos[0] == mainScreenPos[2] &&
mainScreenPos[0] != mainScreenPos[1])
{
// constant flickering, likely displaying 3D on both screens
// TODO: when both screens are used for 2D only...???
guess = Frontend::screenSizing_Even;
}
else
{
if (mainScreenPos[0] == 1)
guess = Frontend::screenSizing_EmphTop;
else
guess = Frontend::screenSizing_EmphBot;
}
if (guess != autoScreenSizing)
{
autoScreenSizing = guess;
emit screenLayoutChange();
}
}
// emulate
u32 nlines = NDS->RunFrame();
if (ROMManager::NDSSave)
ROMManager::NDSSave->CheckFlush();
if (ROMManager::GBASave)
ROMManager::GBASave->CheckFlush();
if (ROMManager::FirmwareSave)
ROMManager::FirmwareSave->CheckFlush();
if (!screenGL)
{
FrontBufferLock.lock();
FrontBuffer = NDS->GPU.FrontBuffer;
FrontBufferLock.unlock();
}
else
{
FrontBuffer = NDS->GPU.FrontBuffer;
screenGL->drawScreenGL();
}
#ifdef MELONCAP
MelonCap::Update();
#endif // MELONCAP
if (EmuRunning == emuStatus_Exit) break;
winUpdateCount++;
if (winUpdateCount >= winUpdateFreq && !screenGL)
{
emit windowUpdate();
winUpdateCount = 0;
}
bool fastforward = Input::HotkeyDown(HK_FastForward);
if (fastforward && screenGL && Config::ScreenVSync)
{
screenGL->setSwapInterval(0);
}
if (Config::DSiVolumeSync && NDS->ConsoleType == 1)
{
DSi& dsi = static_cast<DSi&>(*NDS);
u8 volumeLevel = dsi.I2C.GetBPTWL()->GetVolumeLevel();
if (volumeLevel != dsiVolumeLevel)
{
dsiVolumeLevel = volumeLevel;
emit syncVolumeLevel();
}
Config::AudioVolume = volumeLevel * (256.0 / 31.0);
}
if (Config::AudioSync && !fastforward)
AudioInOut::AudioSync(*this->NDS);
double frametimeStep = nlines / (60.0 * 263.0);
{
bool limitfps = Config::LimitFPS && !fastforward;
double practicalFramelimit = limitfps ? frametimeStep : 1.0 / 1000.0;
double curtime = SDL_GetPerformanceCounter() * perfCountsSec;
frameLimitError += practicalFramelimit - (curtime - lastTime);
if (frameLimitError < -practicalFramelimit)
frameLimitError = -practicalFramelimit;
if (frameLimitError > practicalFramelimit)
frameLimitError = practicalFramelimit;
if (round(frameLimitError * 1000.0) > 0.0)
{
SDL_Delay(round(frameLimitError * 1000.0));
double timeBeforeSleep = curtime;
curtime = SDL_GetPerformanceCounter() * perfCountsSec;
frameLimitError -= curtime - timeBeforeSleep;
}
lastTime = curtime;
}
nframes++;
if (nframes >= 30)
{
double time = SDL_GetPerformanceCounter() * perfCountsSec;
double dt = time - lastMeasureTime;
lastMeasureTime = time;
u32 fps = round(nframes / dt);
nframes = 0;
float fpstarget = 1.0/frametimeStep;
winUpdateFreq = fps / (u32)round(fpstarget);
if (winUpdateFreq < 1)
winUpdateFreq = 1;
int inst = Platform::InstanceID();
if (inst == 0)
sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
else
sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1);
changeWindowTitle(melontitle);
}
}
else
{
// paused
nframes = 0;
lastTime = SDL_GetPerformanceCounter() * perfCountsSec;
lastMeasureTime = lastTime;
emit windowUpdate();
EmuStatus = EmuRunning;
int inst = Platform::InstanceID();
if (inst == 0)
sprintf(melontitle, "melonDS " MELONDS_VERSION);
else
sprintf(melontitle, "melonDS (%d)", inst+1);
changeWindowTitle(melontitle);
SDL_Delay(75);
if (screenGL)
screenGL->drawScreenGL();
ContextRequestKind contextRequest = ContextRequest;
if (contextRequest == contextRequest_InitGL)
{
screenGL = static_cast<ScreenPanelGL*>(mainWindow->panel);
screenGL->initOpenGL();
ContextRequest = contextRequest_None;
}
else if (contextRequest == contextRequest_DeInitGL)
{
screenGL->deinitOpenGL();
screenGL = nullptr;
ContextRequest = contextRequest_None;
}
}
}
file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Write);
if (file)
{
RTC::StateData state;
NDS->RTC.GetState(state);
Platform::FileWrite(&state, sizeof(state), 1, file);
Platform::CloseFile(file);
}
EmuStatus = emuStatus_Exit;
NDS::Current = nullptr;
// nds is out of scope, so unique_ptr cleans it up for us
}
void EmuThread::changeWindowTitle(char* title)
{
emit windowTitleChange(QString(title));
}
void EmuThread::emuRun()
{
EmuRunning = emuStatus_Running;
EmuPauseStack = EmuPauseStackRunning;
RunningSomething = true;
// checkme
emit windowEmuStart();
AudioInOut::Enable();
}
void EmuThread::initContext()
{
ContextRequest = contextRequest_InitGL;
while (ContextRequest != contextRequest_None);
}
void EmuThread::deinitContext()
{
ContextRequest = contextRequest_DeInitGL;
while (ContextRequest != contextRequest_None);
}
void EmuThread::emuPause()
{
EmuPauseStack++;
if (EmuPauseStack > EmuPauseStackPauseThreshold) return;
PrevEmuStatus = EmuRunning;
EmuRunning = emuStatus_Paused;
while (EmuStatus != emuStatus_Paused);
AudioInOut::Disable();
}
void EmuThread::emuUnpause()
{
if (EmuPauseStack < EmuPauseStackPauseThreshold) return;
EmuPauseStack--;
if (EmuPauseStack >= EmuPauseStackPauseThreshold) return;
EmuRunning = PrevEmuStatus;
AudioInOut::Enable();
}
void EmuThread::emuStop()
{
EmuRunning = emuStatus_Exit;
EmuPauseStack = EmuPauseStackRunning;
AudioInOut::Disable();
}
void EmuThread::emuFrameStep()
{
if (EmuPauseStack < EmuPauseStackPauseThreshold) emit windowEmuPause();
EmuRunning = emuStatus_FrameStep;
}
bool EmuThread::emuIsRunning()
{
return EmuRunning == emuStatus_Running;
}
bool EmuThread::emuIsActive()
{
return (RunningSomething == 1);
}

View File

@ -0,0 +1,134 @@
/*
Copyright 2016-2023 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 EMUTHREAD_H
#define EMUTHREAD_H
#include <QThread>
#include <QMutex>
#include <atomic>
#include <variant>
#include <optional>
#include "NDSCart.h"
#include "GBACart.h"
using Keep = std::monostate;
using UpdateConsoleNDSArgs = std::variant<Keep, std::unique_ptr<melonDS::NDSCart::CartCommon>>;
using UpdateConsoleGBAArgs = std::variant<Keep, std::unique_ptr<melonDS::GBACart::CartCommon>>;
namespace melonDS
{
class NDS;
}
class ScreenPanelGL;
class EmuThread : public QThread
{
Q_OBJECT
void run() override;
public:
explicit EmuThread(QObject* parent = nullptr);
void changeWindowTitle(char* title);
// to be called from the UI thread
void emuRun();
void emuPause();
void emuUnpause();
void emuStop();
void emuFrameStep();
bool emuIsRunning();
bool emuIsActive();
void initContext();
void deinitContext();
int FrontBuffer = 0;
QMutex FrontBufferLock;
/// Applies the config in args.
/// Creates a new NDS console if needed,
/// modifies the existing one if possible.
/// @return \c true if the console was updated.
/// If this returns \c false, then the existing NDS console is not modified.
bool UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept;
std::unique_ptr<melonDS::NDS> NDS; // TODO: Proper encapsulation and synchronization
signals:
void windowUpdate();
void windowTitleChange(QString title);
void windowEmuStart();
void windowEmuStop();
void windowEmuPause();
void windowEmuReset();
void windowEmuFrameStep();
void windowLimitFPSChange();
void screenLayoutChange();
void windowFullscreenToggle();
void swapScreensToggle();
void screenEmphasisToggle();
void syncVolumeLevel();
private:
std::unique_ptr<melonDS::NDS> CreateConsole(
std::unique_ptr<melonDS::NDSCart::CartCommon>&& ndscart,
std::unique_ptr<melonDS::GBACart::CartCommon>&& gbacart
) noexcept;
enum EmuStatusKind
{
emuStatus_Exit,
emuStatus_Running,
emuStatus_Paused,
emuStatus_FrameStep,
};
std::atomic<EmuStatusKind> EmuStatus;
EmuStatusKind PrevEmuStatus;
EmuStatusKind EmuRunning;
constexpr static int EmuPauseStackRunning = 0;
constexpr static int EmuPauseStackPauseThreshold = 1;
int EmuPauseStack;
enum ContextRequestKind
{
contextRequest_None = 0,
contextRequest_InitGL,
contextRequest_DeInitGL
};
std::atomic<ContextRequestKind> ContextRequest = contextRequest_None;
ScreenPanelGL* screenGL;
int autoScreenSizing;
int videoRenderer;
bool videoSettingsDirty;
};
#endif // EMUTHREAD_H

View File

@ -19,6 +19,8 @@
#ifndef INPUT_H
#define INPUT_H
#include <SDL2/SDL.h>
#include "types.h"
namespace Input

View File

@ -130,7 +130,7 @@ bool SemInit(int num)
char semname[64];
sprintf(semname, "Local\\melonNIFI_Sem%02d", num);
HANDLE sem = CreateSemaphore(nullptr, 0, 64, semname);
HANDLE sem = CreateSemaphoreA(nullptr, 0, 64, semname);
SemPool[num] = sem;
SemInited[num] = true;
return sem != INVALID_HANDLE_VALUE;
@ -248,7 +248,9 @@ bool Init()
Log(LogLevel::Info, "MP sharedmem doesn't exist. creating\n");
if (!MPQueue->create(kQueueSize))
{
Log(LogLevel::Error, "MP sharedmem create failed :(\n");
Log(LogLevel::Error, "MP sharedmem create failed :( (%d)\n", MPQueue->error());
delete MPQueue;
MPQueue = nullptr;
return false;
}
@ -303,10 +305,13 @@ void DeInit()
if (MPQueue)
{
MPQueue->lock();
MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
header->ConnectedBitmask &= ~(1 << InstanceID);
header->InstanceBitmask &= ~(1 << InstanceID);
header->NumInstances--;
if (MPQueue->data() != nullptr)
{
MPQueueHeader *header = (MPQueueHeader *) MPQueue->data();
header->ConnectedBitmask &= ~(1 << InstanceID);
header->InstanceBitmask &= ~(1 << InstanceID);
header->NumInstances--;
}
MPQueue->unlock();
SemPoolDeinit();
@ -325,6 +330,7 @@ void SetRecvTimeout(int timeout)
void Begin()
{
if (!MPQueue) return;
MPQueue->lock();
MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
PacketReadOffset = header->PacketWriteOffset;
@ -337,6 +343,7 @@ void Begin()
void End()
{
if (!MPQueue) return;
MPQueue->lock();
MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
//SemReset(InstanceID);
@ -418,6 +425,7 @@ void FIFOWrite(int fifo, void* buf, int len)
int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp)
{
if (!MPQueue) return 0;
MPQueue->lock();
u8* data = (u8*)MPQueue->data();
MPQueueHeader* header = (MPQueueHeader*)&data[0];
@ -473,6 +481,7 @@ int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp)
int RecvPacketGeneric(u8* packet, bool block, u64* timestamp)
{
if (!MPQueue) return 0;
for (;;)
{
if (!SemWait(InstanceID, block ? RecvTimeout : 0))
@ -549,6 +558,8 @@ int SendAck(u8* packet, int len, u64 timestamp)
int RecvHostPacket(u8* packet, u64* timestamp)
{
if (!MPQueue) return -1;
if (LastHostID != -1)
{
// check if the host is still connected
@ -568,6 +579,8 @@ int RecvHostPacket(u8* packet, u64* timestamp)
u16 RecvReplies(u8* packets, u64 timestamp, u16 aidmask)
{
if (!MPQueue) return 0;
u16 ret = 0;
u16 myinstmask = (1 << InstanceID);
u16 curinstmask;

View File

@ -418,7 +418,7 @@ void DrawNative(QPainter& painter)
Rendering.unlock();
}
void DrawGL(float w, float h)
void DrawGL(float w, float h, float factor)
{
if (!Config::ShowOSD) return;
if (!mainWindow || !mainWindow->panel) return;
@ -430,7 +430,7 @@ void DrawGL(float w, float h)
glUseProgram(Shader[2]);
glUniform2f(uScreenSize, w, h);
glUniform1f(uScaleFactor, mainWindow->devicePixelRatioF());
glUniform1f(uScaleFactor, factor);
glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer);
glBindVertexArray(OSDVertexArray);

View File

@ -32,7 +32,7 @@ void AddMessage(u32 color, const char* text);
void Update();
void DrawNative(QPainter& painter);
void DrawGL(float w, float h);
void DrawGL(float w, float h, float factor);
}

Some files were not shown because too many files have changed in this diff Show More