Merge remote-tracking branch 'upstream/master' into line-check-correction
This commit is contained in:
commit
546e8c4ad8
|
@ -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}")
|
||||
|
||||
|
|
|
@ -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")
|
|
@ -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)
|
|
@ -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)
|
|
@ -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.
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
17
src/ARM.cpp
17
src/ARM.cpp
|
@ -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
|
||||
|
|
20
src/ARM.h
20
src/ARM.h
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
85
src/ARMJIT.h
85
src/ARMJIT.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -99,7 +99,7 @@ public:
|
|||
LiteralsLoaded &= ~(1 << reg);
|
||||
}
|
||||
|
||||
bool IsLiteral(int reg)
|
||||
bool IsLiteral(int reg) const
|
||||
{
|
||||
return LiteralsLoaded & (1 << reg);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
109
src/DSi.cpp
109
src/DSi.cpp
|
@ -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:
|
||||
|
|
39
src/DSi.h
39
src/DSi.h
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
void Reset();
|
||||
void DoSavestate(Savestate* file);
|
||||
|
||||
u32 ReadCnt();
|
||||
u32 ReadCnt() const;
|
||||
void WriteCnt(u32 val);
|
||||
void WriteBlkCnt(u32 val);
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
208
src/DSi_SD.cpp
208
src/DSi_SD.cpp
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
79
src/DSi_SD.h
79
src/DSi_SD.h
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
24
src/FIFO.h
24
src/FIFO.h
|
@ -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;
|
||||
|
|
2192
src/FreeBIOS.cpp
2192
src/FreeBIOS.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
1779
src/GBACart.cpp
1779
src/GBACart.cpp
File diff suppressed because it is too large
Load Diff
546
src/GBACart.h
546
src/GBACart.h
|
@ -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
|
||||
|
|
35
src/GPU.cpp
35
src/GPU.cpp
|
@ -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);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
115
src/GPU3D.cpp
115
src/GPU3D.cpp
|
@ -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)
|
||||
|
|
29
src/GPU3D.h
29
src/GPU3D.h
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
170
src/NDS.cpp
170
src/NDS.cpp
|
@ -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
119
src/NDS.h
|
@ -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) {}
|
||||
};
|
||||
|
||||
|
|
632
src/NDSCart.cpp
632
src/NDSCart.cpp
|
@ -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?
|
||||
}
|
||||
|
|
328
src/NDSCart.h
328
src/NDSCart.h
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
16
src/RTC.cpp
16
src/RTC.cpp
|
@ -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;
|
||||
|
||||
|
|
16
src/RTC.h
16
src/RTC.h
|
@ -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();
|
||||
|
|
104
src/SPI.cpp
104
src/SPI.cpp
|
@ -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
|
||||
|
|
34
src/SPI.h
34
src/SPI.h
|
@ -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);
|
||||
|
|
309
src/SPU.cpp
309
src/SPU.cpp
|
@ -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
130
src/SPU.h
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
22
src/Wifi.cpp
22
src/Wifi.cpp
|
@ -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);
|
||||
}
|
||||
|
|
12
src/Wifi.h
12
src/Wifi.h
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
186
src/fatfs/ff.c
186
src/fatfs/ff.c
|
@ -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 */
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -110,7 +110,11 @@ DWORD get_fattime(void)
|
|||
|
||||
time_t timestamp = time(NULL);
|
||||
struct tm timedata;
|
||||
#if defined(_MSC_VER)
|
||||
localtime_s(&timedata, ×tamp);
|
||||
#else
|
||||
localtime_r(×tamp, &timedata);
|
||||
#endif
|
||||
|
||||
DWORD ret;
|
||||
ret = (timedata.tm_sec >> 1);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -369,7 +369,7 @@ void UpdateSettings(NDS& nds)
|
|||
{
|
||||
MicClose();
|
||||
|
||||
nds.SPU.SetInterpolation(Config::AudioInterp);
|
||||
nds.SPU.SetInterpolation(static_cast<AudioInterpolation>(Config::AudioInterp));
|
||||
SetupMicInputData();
|
||||
|
||||
MicOpen();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef INPUT_H
|
||||
#define INPUT_H
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Input
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue