diff options
Diffstat (limited to 'src/core')
82 files changed, 3023 insertions, 763 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3473e2f5b..a8d891689 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -68,6 +68,7 @@ set(SRCS hle/service/cfg/cfg_s.cpp hle/service/cfg/cfg_u.cpp hle/service/csnd_snd.cpp + hle/service/dlp_srvr.cpp hle/service/dsp_dsp.cpp hle/service/err_f.cpp hle/service/frd/frd.cpp @@ -200,6 +201,7 @@ set(HEADERS hle/service/cfg/cfg_s.h hle/service/cfg/cfg_u.h hle/service/csnd_snd.h + hle/service/dlp_srvr.h hle/service/dsp_dsp.h hle/service/err_f.h hle/service/frd/frd.h diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index f3be2c857..a3581132c 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -3,8 +3,7 @@ // Refer to the license.txt file included. #include <cstring> - -#include "common/make_unique.h" +#include <memory> #include "core/arm/skyeye_common/armstate.h" #include "core/arm/skyeye_common/armsupp.h" @@ -18,7 +17,7 @@ #include "core/core_timing.h" ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { - state = Common::make_unique<ARMul_State>(initial_mode); + state = std::make_unique<ARMul_State>(initial_mode); } ARM_DynCom::~ARM_DynCom() { @@ -94,7 +93,7 @@ void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 e context.cpu_registers[0] = arg; context.pc = entry_point; context.sp = stack_top; - context.cpsr = 0x1F; // Usermode + context.cpsr = 0x1F | ((entry_point & 1) << 5); // Usermode and THUMB mode } void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 5f8826034..8d4b26815 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -10,7 +10,6 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "core/memory.h" #include "core/hle/svc.h" @@ -25,9 +24,6 @@ #include "core/gdbstub/gdbstub.h" -Common::Profiling::TimingCategory profile_execute("DynCom::Execute"); -Common::Profiling::TimingCategory profile_decode("DynCom::Decode"); - enum { COND = (1 << 0), NON_BRANCH = (1 << 1), @@ -36,7 +32,8 @@ enum { CALL = (1 << 4), RET = (1 << 5), END_OF_PAGE = (1 << 6), - THUMB = (1 << 7) + THUMB = (1 << 7), + SINGLE_STEP = (1 << 8) }; #define RM BITS(sht_oper, 0, 3) @@ -3466,8 +3463,35 @@ enum { MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64)); -static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { - Common::Profiling::ScopeTimer timer_decode(profile_decode); +static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, const u32 phys_addr, ARM_INST_PTR& inst_base) { + unsigned int inst_size = 4; + unsigned int inst = Memory::Read32(phys_addr & 0xFFFFFFFC); + + // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction + if (cpu->TFlag) { + u32 arm_inst; + ThumbDecodeStatus state = DecodeThumbInstruction(inst, phys_addr, &arm_inst, &inst_size, &inst_base); + + // We have translated the Thumb branch instruction in the Thumb decoder + if (state == ThumbDecodeStatus::BRANCH) { + return inst_size; + } + inst = arm_inst; + } + + int idx; + if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) { + std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst); + LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst); + LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]); + CITRA_IGNORE_EXIT(-1); + } + inst_base = arm_instruction_trans[idx](inst, idx); + + return inst_size; +} + +static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) { MICROPROFILE_SCOPE(DynCom_Decode); // Decode instruction, get index @@ -3475,8 +3499,6 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { // Go on next, until terminal instruction // Save start addr of basicblock in CreamCache ARM_INST_PTR inst_base = nullptr; - unsigned int inst, inst_size = 4; - int idx; int ret = NON_BRANCH; int size = 0; // instruction size of basic block bb_start = top; @@ -3485,30 +3507,10 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { u32 pc_start = cpu->Reg[15]; while (ret == NON_BRANCH) { - inst = Memory::Read32(phys_addr & 0xFFFFFFFC); + unsigned int inst_size = InterpreterTranslateInstruction(cpu, phys_addr, inst_base); size++; - // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction - if (cpu->TFlag) { - u32 arm_inst; - ThumbDecodeStatus state = DecodeThumbInstruction(inst, phys_addr, &arm_inst, &inst_size, &inst_base); - - // We have translated the Thumb branch instruction in the Thumb decoder - if (state == ThumbDecodeStatus::BRANCH) { - goto translated; - } - inst = arm_inst; - } - if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) { - std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst); - LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst); - LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]); - CITRA_IGNORE_EXIT(-1); - } - inst_base = arm_instruction_trans[idx](inst, idx); - -translated: phys_addr += inst_size; if ((phys_addr & 0xfff) == 0) { @@ -3522,6 +3524,26 @@ translated: return KEEP_GOING; } +static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) { + MICROPROFILE_SCOPE(DynCom_Decode); + + ARM_INST_PTR inst_base = nullptr; + bb_start = top; + + u32 phys_addr = addr; + u32 pc_start = cpu->Reg[15]; + + InterpreterTranslateInstruction(cpu, phys_addr, inst_base); + + if (inst_base->br == NON_BRANCH) { + inst_base->br = SINGLE_STEP; + } + + cpu->instruction_cache[pc_start] = bb_start; + + return KEEP_GOING; +} + static int clz(unsigned int x) { int n; if (x == 0) return (32); @@ -3537,7 +3559,6 @@ static int clz(unsigned int x) { MICROPROFILE_DEFINE(DynCom_Execute, "DynCom", "Execute", MP_RGB(255, 0, 0)); unsigned InterpreterMainLoop(ARMul_State* cpu) { - Common::Profiling::ScopeTimer timer_execute(profile_execute); MICROPROFILE_SCOPE(DynCom_Execute); GDBStub::BreakpointAddress breakpoint_data; @@ -3871,8 +3892,11 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { auto itr = cpu->instruction_cache.find(cpu->Reg[15]); if (itr != cpu->instruction_cache.end()) { ptr = itr->second; + } else if (cpu->NumInstrsToExecute != 1) { + if (InterpreterTranslateBlock(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION) + goto END; } else { - if (InterpreterTranslate(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION) + if (InterpreterTranslateSingle(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION) goto END; } @@ -3924,9 +3948,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { add_inst* const inst_cream = (add_inst*)inst_base->component; - u32 rn_val = RN; - if (inst_cream->Rn == 15) - rn_val += 2 * cpu->GetInstructionSize(); + u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn); bool carry; bool overflow; @@ -4051,11 +4073,12 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) { unsigned int inst = inst_cream->inst; if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) { + const u32 jump_address = cpu->Reg[inst_cream->val.Rm]; cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize()); if(cpu->TFlag) cpu->Reg[14] |= 0x1; - cpu->Reg[15] = cpu->Reg[inst_cream->val.Rm] & 0xfffffffe; - cpu->TFlag = cpu->Reg[inst_cream->val.Rm] & 0x1; + cpu->Reg[15] = jump_address & 0xfffffffe; + cpu->TFlag = jump_address & 0x1; } else { cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize()); cpu->TFlag = 0x1; @@ -6136,9 +6159,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { sub_inst* const inst_cream = (sub_inst*)inst_base->component; - u32 rn_val = RN; - if (inst_cream->Rn == 15) - rn_val += 2 * cpu->GetInstructionSize(); + u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn); bool carry; bool overflow; diff --git a/src/core/arm/skyeye_common/armstate.cpp b/src/core/arm/skyeye_common/armstate.cpp index 2d814345a..5550c112e 100644 --- a/src/core/arm/skyeye_common/armstate.cpp +++ b/src/core/arm/skyeye_common/armstate.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include "common/swap.h" #include "common/logging/log.h" #include "core/memory.h" @@ -48,8 +49,7 @@ void ARMul_State::ChangePrivilegeMode(u32 new_mode) Spsr[UNDEFBANK] = Spsr_copy; break; case FIQ32MODE: - Reg_firq[0] = Reg[13]; - Reg_firq[1] = Reg[14]; + std::copy(Reg.begin() + 8, Reg.end() - 1, Reg_firq.begin()); Spsr[FIQBANK] = Spsr_copy; break; } @@ -85,8 +85,7 @@ void ARMul_State::ChangePrivilegeMode(u32 new_mode) Bank = UNDEFBANK; break; case FIQ32MODE: - Reg[13] = Reg_firq[0]; - Reg[14] = Reg_firq[1]; + std::copy(Reg_firq.begin(), Reg_firq.end(), Reg.begin() + 8); Spsr_copy = Spsr[FIQBANK]; Bank = FIQBANK; break; diff --git a/src/core/core.cpp b/src/core/core.cpp index a156682aa..cabab744a 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -4,7 +4,6 @@ #include <memory> -#include "common/make_unique.h" #include "common/logging/log.h" #include "core/core.h" @@ -74,8 +73,8 @@ void Stop() { /// Initialize the core void Init() { - g_sys_core = Common::make_unique<ARM_DynCom>(USER32MODE); - g_app_core = Common::make_unique<ARM_DynCom>(USER32MODE); + g_sys_core = std::make_unique<ARM_DynCom>(USER32MODE); + g_app_core = std::make_unique<ARM_DynCom>(USER32MODE); LOG_DEBUG(Core, "Initialized OK"); } diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index 961264fe5..1d9eaefcb 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -3,12 +3,12 @@ // Refer to the license.txt file included. #include <algorithm> +#include <memory> #include <vector> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "core/file_sys/archive_extsavedata.h" @@ -84,7 +84,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons ErrorSummary::InvalidState, ErrorLevel::Status); } } - auto archive = Common::make_unique<DiskArchive>(fullpath); + auto archive = std::make_unique<DiskArchive>(fullpath); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h index 287a6fee1..e9a72850d 100644 --- a/src/core/file_sys/archive_extsavedata.h +++ b/src/core/file_sys/archive_extsavedata.h @@ -45,13 +45,14 @@ public: void WriteIcon(const Path& path, const u8* icon_data, size_t icon_size); private: + bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData archive + /** * This holds the full directory path for this archive, it is only set after a successful call * to Open, this is formed as <base extsavedatapath>/<type>/<high>/<low>. * See GetExtSaveDataPath for the code that extracts this data from an archive path. */ std::string mount_point; - bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData archive }; /** diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index a9a29ebde..38828b546 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp @@ -7,7 +7,6 @@ #include "common/common_types.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/file_sys/archive_romfs.h" #include "core/file_sys/ivfc_archive.h" @@ -25,7 +24,7 @@ ArchiveFactory_RomFS::ArchiveFactory_RomFS(Loader::AppLoader& app_loader) { } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) { - auto archive = Common::make_unique<IVFCArchive>(romfs_file, data_offset, data_size); + auto archive = std::make_unique<IVFCArchive>(romfs_file, data_offset, data_size); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index fe020d21c..fd5711e14 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp @@ -3,11 +3,11 @@ // Refer to the license.txt file included. #include <algorithm> +#include <memory> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "core/file_sys/archive_savedata.h" @@ -53,7 +53,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P ErrorSummary::InvalidState, ErrorLevel::Status); } - auto archive = Common::make_unique<DiskArchive>(std::move(concrete_mount_point)); + auto archive = std::make_unique<DiskArchive>(std::move(concrete_mount_point)); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp index 3db11c500..9f65e5455 100644 --- a/src/core/file_sys/archive_savedatacheck.cpp +++ b/src/core/file_sys/archive_savedatacheck.cpp @@ -3,12 +3,12 @@ // Refer to the license.txt file included. #include <algorithm> +#include <memory> #include <vector> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "core/file_sys/archive_savedatacheck.h" @@ -44,7 +44,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co } auto size = file->GetSize(); - auto archive = Common::make_unique<IVFCArchive>(file, 0, size); + auto archive = std::make_unique<IVFCArchive>(file, 0, size); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 657221cbf..9b218af58 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -3,10 +3,10 @@ // Refer to the license.txt file included. #include <algorithm> +#include <memory> #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/file_sys/archive_sdmc.h" #include "core/file_sys/disk_archive.h" @@ -36,7 +36,7 @@ bool ArchiveFactory_SDMC::Initialize() { } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) { - auto archive = Common::make_unique<DiskArchive>(sdmc_directory); + auto archive = std::make_unique<DiskArchive>(sdmc_directory); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index e1780de2f..1bcc228a1 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -3,11 +3,11 @@ // Refer to the license.txt file included. #include <algorithm> +#include <memory> #include <vector> #include "common/common_types.h" #include "common/file_util.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "core/file_sys/archive_systemsavedata.h" @@ -59,7 +59,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status); } - auto archive = Common::make_unique<DiskArchive>(fullpath); + auto archive = std::make_unique<DiskArchive>(fullpath); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp index 8e4ea01c5..489cc96fb 100644 --- a/src/core/file_sys/disk_archive.cpp +++ b/src/core/file_sys/disk_archive.cpp @@ -4,11 +4,11 @@ #include <algorithm> #include <cstdio> +#include <memory> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/file_sys/disk_archive.h" @@ -19,7 +19,7 @@ namespace FileSys { ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path, const Mode mode) const { LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); - auto file = Common::make_unique<DiskFile>(*this, path, mode); + auto file = std::make_unique<DiskFile>(*this, path, mode); ResultCode result = file->Open(); if (result.IsError()) return result; @@ -83,7 +83,7 @@ bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) c std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); - auto directory = Common::make_unique<DiskDirectory>(*this, path); + auto directory = std::make_unique<DiskDirectory>(*this, path); if (!directory->Open()) return nullptr; return std::move(directory); @@ -132,7 +132,7 @@ ResultCode DiskFile::Open() { // Open the file in binary mode, to avoid problems with CR/LF on Windows systems mode_string += "b"; - file = Common::make_unique<FileUtil::IOFile>(path, mode_string.c_str()); + file = std::make_unique<FileUtil::IOFile>(path, mode_string.c_str()); if (file->IsOpen()) return RESULT_SUCCESS; return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status); diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp index a8e9a72ef..c61791ef7 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/ivfc_archive.cpp @@ -7,7 +7,6 @@ #include "common/common_types.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/file_sys/ivfc_archive.h" @@ -21,7 +20,7 @@ std::string IVFCArchive::GetName() const { } ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { - return MakeResult<std::unique_ptr<FileBackend>>(Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); + return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); } ResultCode IVFCArchive::DeleteFile(const Path& path) const { @@ -58,7 +57,7 @@ bool IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) c } std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const { - return Common::make_unique<IVFCDirectory>(); + return std::make_unique<IVFCDirectory>(); } u64 IVFCArchive::GetFreeBytes() const { diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 3a2445241..ae0c116ef 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -60,6 +60,59 @@ const u32 R15_REGISTER = 15; const u32 CPSR_REGISTER = 25; const u32 FPSCR_REGISTER = 58; +// For sample XML files see the GDB source /gdb/features +// GDB also wants the l character at the start +// This XML defines what the registers are for this specific ARM device +static const char* target_xml = +R"(l<?xml version="1.0"?> +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target version="1.0"> + <feature name="org.gnu.gdb.arm.core"> + <reg name="r0" bitsize="32"/> + <reg name="r1" bitsize="32"/> + <reg name="r2" bitsize="32"/> + <reg name="r3" bitsize="32"/> + <reg name="r4" bitsize="32"/> + <reg name="r5" bitsize="32"/> + <reg name="r6" bitsize="32"/> + <reg name="r7" bitsize="32"/> + <reg name="r8" bitsize="32"/> + <reg name="r9" bitsize="32"/> + <reg name="r10" bitsize="32"/> + <reg name="r11" bitsize="32"/> + <reg name="r12" bitsize="32"/> + <reg name="sp" bitsize="32" type="data_ptr"/> + <reg name="lr" bitsize="32"/> + <reg name="pc" bitsize="32" type="code_ptr"/> + + <!-- The CPSR is register 25, rather than register 16, because + the FPA registers historically were placed between the PC + and the CPSR in the "g" packet. --> + + <reg name="cpsr" bitsize="32" regnum="25"/> + </feature> + <feature name="org.gnu.gdb.arm.vfp"> + <reg name="d0" bitsize="64" type="float"/> + <reg name="d1" bitsize="64" type="float"/> + <reg name="d2" bitsize="64" type="float"/> + <reg name="d3" bitsize="64" type="float"/> + <reg name="d4" bitsize="64" type="float"/> + <reg name="d5" bitsize="64" type="float"/> + <reg name="d6" bitsize="64" type="float"/> + <reg name="d7" bitsize="64" type="float"/> + <reg name="d8" bitsize="64" type="float"/> + <reg name="d9" bitsize="64" type="float"/> + <reg name="d10" bitsize="64" type="float"/> + <reg name="d11" bitsize="64" type="float"/> + <reg name="d12" bitsize="64" type="float"/> + <reg name="d13" bitsize="64" type="float"/> + <reg name="d14" bitsize="64" type="float"/> + <reg name="d15" bitsize="64" type="float"/> + <reg name="fpscr" bitsize="32" type="int" group="float"/> + </feature> +</target> +)"; + namespace GDBStub { static int gdbserver_socket = -1; @@ -211,7 +264,7 @@ static u8 ReadByte() { } /// Calculate the checksum of the current command buffer. -static u8 CalculateChecksum(u8 *buffer, u32 length) { +static u8 CalculateChecksum(u8* buffer, u32 length) { return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>())); } @@ -353,8 +406,15 @@ static void SendReply(const char* reply) { static void HandleQuery() { LOG_DEBUG(Debug_GDBStub, "gdb: query '%s'\n", command_buffer + 1); - if (!strcmp(reinterpret_cast<const char*>(command_buffer + 1), "TStatus")) { + const char* query = reinterpret_cast<const char*>(command_buffer + 1); + + if (strcmp(query, "TStatus") == 0 ) { SendReply("T0"); + } else if (strncmp(query, "Supported:", strlen("Supported:")) == 0) { + // PacketSize needs to be large enough for target xml + SendReply("PacketSize=800;qXfer:features:read+"); + } else if (strncmp(query, "Xfer:features:read:target.xml:", strlen("Xfer:features:read:target.xml:")) == 0) { + SendReply(target_xml); } else { SendReply(""); } @@ -469,7 +529,7 @@ static void ReadRegister() { id |= HexCharToValue(command_buffer[2]); } - if (id >= R0_REGISTER && id <= R15_REGISTER) { + if (id <= R15_REGISTER) { IntToGdbHex(reply, Core::g_app_core->GetReg(id)); } else if (id == CPSR_REGISTER) { IntToGdbHex(reply, Core::g_app_core->GetCPSR()); @@ -491,29 +551,25 @@ static void ReadRegisters() { memset(buffer, 0, sizeof(buffer)); u8* bufptr = buffer; - for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { - if (reg <= R15_REGISTER) { - IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetReg(reg)); - } else if (reg == CPSR_REGISTER) { - IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetCPSR()); - } else if (reg == CPSR_REGISTER - 1) { - // Dummy FPA register, ignore - IntToGdbHex(bufptr + i * CHAR_BIT, 0); - } else if (reg < CPSR_REGISTER) { - // Dummy FPA registers, ignore - IntToGdbHex(bufptr + i * CHAR_BIT, 0); - IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0); - IntToGdbHex(bufptr + (i + 2) * CHAR_BIT, 0); - i += 2; - } else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) { - IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPReg(reg - CPSR_REGISTER - 1)); - IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0); - i++; - } else if (reg == FPSCR_REGISTER) { - IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); - } + + for (int reg = 0; reg <= R15_REGISTER; reg++) { + IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetReg(reg)); } + bufptr += (16 * CHAR_BIT); + + IntToGdbHex(bufptr, Core::g_app_core->GetCPSR()); + + bufptr += CHAR_BIT; + + for (int reg = 0; reg <= 31; reg++) { + IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetVFPReg(reg)); + } + + bufptr += (32 * CHAR_BIT); + + IntToGdbHex(bufptr, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); + SendReply(reinterpret_cast<char*>(buffer)); } @@ -528,7 +584,7 @@ static void WriteRegister() { id |= HexCharToValue(command_buffer[2]); } - if (id >= R0_REGISTER && id <= R15_REGISTER) { + if (id <= R15_REGISTER) { Core::g_app_core->SetReg(id, GdbHexToInt(buffer_ptr)); } else if (id == CPSR_REGISTER) { Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr)); @@ -885,6 +941,12 @@ void Init(u16 port) { LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); } + // Set socket to SO_REUSEADDR so it can always bind on the same port + int reuse_enabled = 1; + if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled, sizeof(reuse_enabled)) < 0) { + LOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option"); + } + const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server); socklen_t server_addrlen = sizeof(saddr_server); if (bind(tmpsock, server_addr, server_addrlen) < 0) { diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index 708d2f630..5191c821d 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -55,6 +55,11 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa // TODO(Subv): Set the expected fields in the response buffer before resending it to the application. // TODO(Subv): Reverse the parameter format for the Mii Selector + if(parameter.buffer_size >= sizeof(u32)) { + // TODO: defaults return no error, but garbage in other unknown fields + memset(parameter.data, 0, sizeof(u32)); + } + // Let the application know that we're closing Service::APT::MessageParameter message; message.buffer_size = parameter.buffer_size; diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h index 6a3e7c8eb..c02dded4a 100644 --- a/src/core/hle/applets/mii_selector.h +++ b/src/core/hle/applets/mii_selector.h @@ -16,6 +16,50 @@ namespace HLE { namespace Applets { +struct MiiConfig { + u8 unk_000; + u8 unk_001; + u8 unk_002; + u8 unk_003; + u8 unk_004; + INSERT_PADDING_BYTES(3); + u16 unk_008; + INSERT_PADDING_BYTES(0x8C - 0xA); + u8 unk_08C; + INSERT_PADDING_BYTES(3); + u16 unk_090; + INSERT_PADDING_BYTES(2); + u32 unk_094; + u16 unk_098; + u8 unk_09A[0x64]; + u8 unk_0FE; + u8 unk_0FF; + u32 unk_100; +}; + +static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size"); +#define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(MiiConfig, field_name) == position, "Field "#field_name" has invalid position") +ASSERT_REG_POSITION(unk_008, 0x08); +ASSERT_REG_POSITION(unk_08C, 0x8C); +ASSERT_REG_POSITION(unk_090, 0x90); +ASSERT_REG_POSITION(unk_094, 0x94); +ASSERT_REG_POSITION(unk_0FE, 0xFE); +#undef ASSERT_REG_POSITION + +struct MiiResult { + u32 result_code; + u8 unk_04; + INSERT_PADDING_BYTES(7); + u8 unk_0C[0x60]; + u8 unk_6C[0x16]; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size"); +#define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(MiiResult, field_name) == position, "Field "#field_name" has invalid position") +ASSERT_REG_POSITION(unk_0C, 0x0C); +ASSERT_REG_POSITION(unk_6C, 0x6C); +#undef ASSERT_REG_POSITION + class MiiSelector final : public Applet { public: MiiSelector(Service::APT::AppletId id); diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp index b1a72dc0c..ccd73cfcb 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.cpp @@ -3,13 +3,6 @@ // Refer to the license.txt file included. #include <cstring> - -#include "common/assert.h" -#include "common/common_types.h" -#include "common/common_funcs.h" - -#include "core/core.h" -#include "core/memory.h" #include "core/hle/config_mem.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index 96d3ec05b..5c5373517 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp @@ -8,8 +8,6 @@ #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/hle/hle.h" -#include "core/hle/config_mem.h" -#include "core/hle/shared_page.h" #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 24b266eae..0546f6e16 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -2,10 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <memory> + #include "common/assert.h" #include "common/common_funcs.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index adaffcafe..6ddaf970e 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -16,23 +16,23 @@ namespace IPC { -inline u32 MakeHeader(u16 command_id, unsigned int regular_params, unsigned int translate_params) { +constexpr u32 MakeHeader(u16 command_id, unsigned int regular_params, unsigned int translate_params) { return ((u32)command_id << 16) | (((u32)regular_params & 0x3F) << 6) | (((u32)translate_params & 0x3F) << 0); } -inline u32 MoveHandleDesc(unsigned int num_handles = 1) { +constexpr u32 MoveHandleDesc(unsigned int num_handles = 1) { return 0x0 | ((num_handles - 1) << 26); } -inline u32 CopyHandleDesc(unsigned int num_handles = 1) { +constexpr u32 CopyHandleDesc(unsigned int num_handles = 1) { return 0x10 | ((num_handles - 1) << 26); } -inline u32 CallingPidDesc() { +constexpr u32 CallingPidDesc() { return 0x20; } -inline u32 StaticBufferDesc(u32 size, unsigned int buffer_id) { +constexpr u32 StaticBufferDesc(u32 size, unsigned int buffer_id) { return 0x2 | (size << 14) | ((buffer_id & 0xF) << 10); } @@ -42,7 +42,7 @@ enum MappedBufferPermissions { RW = R | W, }; -inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { +constexpr u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { return 0x8 | (size << 4) | (u32)perms; } diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 0cb76ba1c..3fc1ab4ee 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -5,7 +5,6 @@ #pragma once #include <new> -#include <type_traits> #include <utility> #include "common/assert.h" @@ -18,12 +17,14 @@ /// Detailed description of the error. This listing is likely incomplete. enum class ErrorDescription : u32 { Success = 0, + OS_InvalidBufferDescriptor = 48, WrongAddress = 53, FS_NotFound = 120, FS_AlreadyExists = 190, FS_InvalidOpenFlags = 230, FS_NotAFile = 250, FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive + OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage FS_InvalidPath = 702, InvalidSection = 1000, TooLarge = 1001, diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index d67325506..5241dd3e7 100644 --- a/src/core/hle/service/ac_u.cpp +++ b/src/core/hle/service/ac_u.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include "common/logging/log.h" + +#include "core/hle/kernel/event.h" #include "core/hle/service/ac_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -11,6 +13,28 @@ namespace AC_U { /** + * AC_U::CloseAsync service function + * Inputs: + * 1 : Always 0x20 + * 3 : Always 0 + * 4 : Event handle, should be signaled when AC connection is closed + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void CloseAsync(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); + + if (evt) { + evt->name = "AC_U:close_event"; + evt->Signal(); + } + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} +/** * AC_U::GetWifiStatus service function * Outputs: * 1 : Result of function, 0 on success, otherwise error code @@ -47,7 +71,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00010000, nullptr, "CreateDefaultConfig"}, {0x00040006, nullptr, "ConnectAsync"}, {0x00050002, nullptr, "GetConnectResult"}, - {0x00080004, nullptr, "CloseAsync"}, + {0x00080004, CloseAsync, "CloseAsync"}, {0x00090002, nullptr, "GetCloseResult"}, {0x000A0000, nullptr, "GetLastErrorCode"}, {0x000D0000, GetWifiStatus, "GetWifiStatus"}, diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 06be9940e..3f71e7f2b 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> + #include "common/logging/log.h" #include "core/hle/service/service.h" @@ -9,30 +11,119 @@ #include "core/hle/service/am/am_app.h" #include "core/hle/service/am/am_net.h" #include "core/hle/service/am/am_sys.h" +#include "core/hle/service/am/am_u.h" namespace Service { namespace AM { -void TitleIDListGetTotal(Service::Interface* self) { +static std::array<u32, 3> am_content_count = { 0, 0, 0 }; +static std::array<u32, 3> am_titles_count = { 0, 0, 0 }; +static std::array<u32, 3> am_titles_list_count = { 0, 0, 0 }; +static u32 am_ticket_count = 0; +static u32 am_ticket_list_count = 0; + +void GetTitleCount(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 media_type = cmd_buff[1] & 0xFF; cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; + cmd_buff[2] = am_titles_count[media_type]; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_count=0x%08x", media_type, am_titles_count[media_type]); +} + +void FindContentInfos(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 media_type = cmd_buff[1] & 0xFF; + u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2]; + u32 content_ids_pointer = cmd_buff[6]; + u32 content_info_pointer = cmd_buff[8]; + + am_content_count[media_type] = cmd_buff[4]; - LOG_WARNING(Service_AM, "(STUBBED) media_type %u", media_type); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016llx, content_cound=%u, content_ids_pointer=0x%08x, content_info_pointer=0x%08x", + media_type, title_id, am_content_count[media_type], content_ids_pointer, content_info_pointer); } -void GetTitleIDList(Service::Interface* self) { +void ListContentInfos(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 num_titles = cmd_buff[1]; + u32 media_type = cmd_buff[2] & 0xFF; - u32 addr = cmd_buff[4]; + u64 title_id = (static_cast<u64>(cmd_buff[4]) << 32) | cmd_buff[3]; + u32 start_index = cmd_buff[5]; + u32 content_info_pointer = cmd_buff[7]; + + am_content_count[media_type] = cmd_buff[1]; cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; + cmd_buff[2] = am_content_count[media_type]; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, content_count=%u, title_id=0x%016" PRIx64 ", start_index=0x%08x, content_info_pointer=0x%08X", + media_type, am_content_count[media_type], title_id, start_index, content_info_pointer); +} + +void DeleteContents(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 media_type = cmd_buff[1] & 0xFF; + u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2]; + u32 content_ids_pointer = cmd_buff[6]; - LOG_WARNING(Service_AM, "(STUBBED) Requested %u titles from media type %u. Address=0x%08X", num_titles, media_type, addr); + am_content_count[media_type] = cmd_buff[4]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016" PRIx64 ", content_count=%u, content_ids_pointer=0x%08x", + media_type, title_id, am_content_count[media_type], content_ids_pointer); +} + +void GetTitleList(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 media_type = cmd_buff[2] & 0xFF; + u32 title_ids_output_pointer = cmd_buff[4]; + + am_titles_list_count[media_type] = cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = am_titles_list_count[media_type]; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, titles_list_count=0x%08X, title_ids_output_pointer=0x%08X", + media_type, am_titles_list_count[media_type], title_ids_output_pointer); +} + +void GetTitleInfo(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 media_type = cmd_buff[1] & 0xFF; + u32 title_id_list_pointer = cmd_buff[4]; + u32 title_list_pointer = cmd_buff[6]; + + am_titles_count[media_type] = cmd_buff[2]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, total_titles=0x%08X, title_id_list_pointer=0x%08X, title_list_pointer=0x%08X", + media_type, am_titles_count[media_type], title_id_list_pointer, title_list_pointer); +} + +void GetDataTitleInfos(Service::Interface* self) { + GetTitleInfo(self); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ListDataTitleTicketInfos(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2]; + u32 start_index = cmd_buff[4]; + u32 ticket_info_pointer = cmd_buff[6]; + + am_ticket_count = cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = am_ticket_count; + LOG_WARNING(Service_AM, "(STUBBED) ticket_count=0x%08X, title_id=0x%016" PRIx64 ", start_index=0x%08X, ticket_info_pointer=0x%08X", + am_ticket_count, title_id, start_index, ticket_info_pointer); } void GetNumContentInfos(Service::Interface* self) { @@ -40,16 +131,47 @@ void GetNumContentInfos(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 1; // Number of content infos plus one - LOG_WARNING(Service_AM, "(STUBBED) called"); } +void DeleteTicket(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u64 title_id = (static_cast<u64>(cmd_buff[2]) << 32) | cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_AM, "(STUBBED) called title_id=0x%016" PRIx64 "",title_id); +} + +void GetTicketCount(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = am_ticket_count; + LOG_WARNING(Service_AM, "(STUBBED) called ticket_count=0x%08x",am_ticket_count); +} + +void GetTicketList(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 num_of_skip = cmd_buff[2]; + u32 ticket_list_pointer = cmd_buff[4]; + + am_ticket_list_count = cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = am_ticket_list_count; + LOG_WARNING(Service_AM, "(STUBBED) ticket_list_count=0x%08x, num_of_skip=0x%08x, ticket_list_pointer=0x%08x", + am_ticket_list_count, num_of_skip, ticket_list_pointer); +} + void Init() { using namespace Kernel; AddService(new AM_APP_Interface); AddService(new AM_NET_Interface); AddService(new AM_SYS_Interface); + AddService(new AM_U_Interface); } void Shutdown() { diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 15e63bc7b..5676cdd5f 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -11,7 +11,7 @@ class Interface; namespace AM { /** - * AM::TitleIDListGetTotal service function + * AM::GetTitleCount service function * Gets the number of installed titles in the requested media type * Inputs: * 0 : Command header (0x00010040) @@ -20,36 +20,140 @@ namespace AM { * 1 : Result, 0 on success, otherwise error code * 2 : The number of titles in the requested media type */ -void TitleIDListGetTotal(Service::Interface* self); +void GetTitleCount(Service::Interface* self); /** - * AM::GetTitleIDList service function + * AM::FindContentInfos service function + * Inputs: + * 1 : MediaType + * 2-3 : u64, Title ID + * 4 : Content count + * 6 : Content IDs pointer + * 8 : Content Infos pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void FindContentInfos(Service::Interface* self); + +/** + * AM::ListContentInfos service function + * Inputs: + * 1 : Content count + * 2 : MediaType + * 3-4 : u64, Title ID + * 5 : Start Index + * 7 : Content Infos pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Number of content infos returned + */ +void ListContentInfos(Service::Interface* self); + +/** + * AM::DeleteContents service function + * Inputs: + * 1 : MediaType + * 2-3 : u64, Title ID + * 4 : Content count + * 6 : Content IDs pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void DeleteContents(Service::Interface* self); + +/** + * AM::GetTitleList service function * Loads information about the desired number of titles from the desired media type into an array * Inputs: - * 0 : Command header (0x00020082) - * 1 : The maximum number of titles to load + * 1 : Title count * 2 : Media type to load the titles from - * 3 : Descriptor of the output buffer pointer - * 4 : Address of the output buffer + * 4 : Title IDs output pointer * Outputs: * 1 : Result, 0 on success, otherwise error code * 2 : The number of titles loaded from the requested media type */ -void GetTitleIDList(Service::Interface* self); +void GetTitleList(Service::Interface* self); + +/** + * AM::GetTitleInfo service function + * Inputs: + * 1 : u8 Mediatype + * 2 : Total titles + * 4 : TitleIDList pointer + * 6 : TitleList pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void GetTitleInfo(Service::Interface* self); + +/** + * AM::GetDataTitleInfos service function + * Wrapper for AM::GetTitleInfo + * Inputs: + * 1 : u8 Mediatype + * 2 : Total titles + * 4 : TitleIDList pointer + * 6 : TitleList pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void GetDataTitleInfos(Service::Interface* self); + +/** + * AM::ListDataTitleTicketInfos service function + * Inputs: + * 1 : Ticket count + * 2-3 : u64, Title ID + * 4 : Start Index? + * 5 : (TicketCount * 24) << 8 | 0x4 + * 6 : Ticket Infos pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Number of ticket infos returned + */ +void ListDataTitleTicketInfos(Service::Interface* self); /** * AM::GetNumContentInfos service function * Inputs: * 0 : Command header (0x100100C0) - * 1 : Unknown - * 2 : Unknown - * 3 : Unknown + * 1 : MediaType + * 2-3 : u64, Title ID * Outputs: * 1 : Result, 0 on success, otherwise error code * 2 : Number of content infos plus one */ void GetNumContentInfos(Service::Interface* self); +/** + * AM::DeleteTicket service function + * Inputs: + * 1-2 : u64, Title ID + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void DeleteTicket(Service::Interface* self); + +/** + * AM::GetTicketCount service function + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Total titles + */ +void GetTicketCount(Service::Interface* self); + +/** + * AM::GetTicketList service function + * Inputs: + * 1 : Number of TicketList + * 2 : Number to skip + * 4 : TicketList pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Total TicketList + */ +void GetTicketList(Service::Interface* self); + /// Initialize AM service void Init(); diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp index 16c76a1eb..d27b3defd 100644 --- a/src/core/hle/service/am/am_app.cpp +++ b/src/core/hle/service/am/am_app.cpp @@ -9,14 +9,14 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x100100C0, GetNumContentInfos, "GetNumContentInfos"}, - {0x10020104, nullptr, "FindContentInfos"}, - {0x10030142, nullptr, "ListContentInfos"}, - {0x10040102, nullptr, "DeleteContents"}, - {0x10050084, nullptr, "GetDataTitleInfos"}, - {0x10070102, nullptr, "ListDataTitleTicketInfos"}, - {0x100900C0, nullptr, "IsDataTitleInUse"}, - {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"}, + {0x100100C0, GetNumContentInfos, "GetNumContentInfos"}, + {0x10020104, FindContentInfos, "FindContentInfos"}, + {0x10030142, ListContentInfos, "ListContentInfos"}, + {0x10040102, DeleteContents, "DeleteContents"}, + {0x10050084, GetDataTitleInfos, "GetDataTitleInfos"}, + {0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"}, + {0x100900C0, nullptr, "IsDataTitleInUse"}, + {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"}, }; AM_APP_Interface::AM_APP_Interface() { diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp index 065e04118..e75755245 100644 --- a/src/core/hle/service/am/am_net.cpp +++ b/src/core/hle/service/am/am_net.cpp @@ -9,16 +9,20 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, - {0x00020082, GetTitleIDList, "GetTitleIDList"}, - {0x00030084, nullptr, "ListTitles"}, + {0x00010040, GetTitleCount, "GetTitleCount"}, + {0x00020082, GetTitleList, "GetTitleList"}, + {0x00030084, GetTitleInfo, "GetTitleInfo"}, {0x000400C0, nullptr, "DeleteApplicationTitle"}, {0x000500C0, nullptr, "GetTitleProductCode"}, - {0x00080000, nullptr, "TitleIDListGetTotal3"}, - {0x00090082, nullptr, "GetTitleIDList3"}, + {0x000600C0, nullptr, "GetTitleExtDataId"}, + {0x00070080, DeleteTicket, "DeleteTicket"}, + {0x00080000, GetTicketCount, "GetTicketCount"}, + {0x00090082, GetTicketList, "GetTicketList"}, {0x000A0000, nullptr, "GetDeviceID"}, - {0x000D0084, nullptr, "ListTitles2"}, - {0x00140040, nullptr, "FinishInstallToMedia"}, + {0x000D0084, nullptr, "GetPendingTitleInfo"}, + {0x000E00C0, nullptr, "DeletePendingTitle"}, + {0x00140040, nullptr, "FinalizePendingTitles"}, + {0x00150040, nullptr, "DeleteAllPendingTitles"}, {0x00180080, nullptr, "InitializeTitleDatabase"}, {0x00190040, nullptr, "ReloadDBS"}, {0x001A00C0, nullptr, "GetDSiWareExportSize"}, diff --git a/src/core/hle/service/am/am_sys.cpp b/src/core/hle/service/am/am_sys.cpp index e38812297..8bad5e1c9 100644 --- a/src/core/hle/service/am/am_sys.cpp +++ b/src/core/hle/service/am/am_sys.cpp @@ -9,23 +9,27 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, - {0x00020082, GetTitleIDList, "GetTitleIDList"}, - {0x00030084, nullptr, "ListTitles"}, + {0x00010040, GetTitleCount, "GetTitleCount"}, + {0x00020082, GetTitleList, "GetTitleList"}, + {0x00030084, GetTitleInfo, "GetTitleInfo"}, {0x000400C0, nullptr, "DeleteApplicationTitle"}, {0x000500C0, nullptr, "GetTitleProductCode"}, - {0x00080000, nullptr, "TitleIDListGetTotal3"}, - {0x00090082, nullptr, "GetTitleIDList3"}, + {0x000600C0, nullptr, "GetTitleExtDataId"}, + {0x00070080, DeleteTicket, "DeleteTicket"}, + {0x00080000, GetTicketCount, "GetTicketCount"}, + {0x00090082, GetTicketList, "GetTicketList"}, {0x000A0000, nullptr, "GetDeviceID"}, - {0x000D0084, nullptr, "ListTitles2"}, - {0x00140040, nullptr, "FinishInstallToMedia"}, + {0x000D0084, nullptr, "GetPendingTitleInfo"}, + {0x000E00C0, nullptr, "DeletePendingTitle"}, + {0x00140040, nullptr, "FinalizePendingTitles"}, + {0x00150040, nullptr, "DeleteAllPendingTitles"}, {0x00180080, nullptr, "InitializeTitleDatabase"}, {0x00190040, nullptr, "ReloadDBS"}, {0x001A00C0, nullptr, "GetDSiWareExportSize"}, {0x001B0144, nullptr, "ExportDSiWare"}, {0x001C0084, nullptr, "ImportDSiWare"}, - {0x00230080, nullptr, "TitleIDListGetTotal2"}, - {0x002400C2, nullptr, "GetTitleIDList2"} + {0x00230080, nullptr, "GetPendingTitleCount"}, + {0x002400C2, nullptr, "GetPendingTitleList"} }; AM_SYS_Interface::AM_SYS_Interface() { diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp index c0392b754..d583dd9e6 100644 --- a/src/core/hle/service/am/am_u.cpp +++ b/src/core/hle/service/am/am_u.cpp @@ -9,16 +9,20 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, - {0x00020082, GetTitleIDList, "GetTitleIDList"}, - {0x00030084, nullptr, "ListTitles"}, + {0x00010040, GetTitleCount, "GetTitleCount"}, + {0x00020082, GetTitleList, "GetTitleList"}, + {0x00030084, GetTitleInfo, "GetTitleInfo"}, {0x000400C0, nullptr, "DeleteApplicationTitle"}, {0x000500C0, nullptr, "GetTitleProductCode"}, - {0x00080000, nullptr, "TitleIDListGetTotal3"}, - {0x00090082, nullptr, "GetTitleIDList3"}, + {0x000600C0, nullptr, "GetTitleExtDataId"}, + {0x00070080, DeleteTicket, "DeleteTicket"}, + {0x00080000, GetTicketCount, "GetTicketCount"}, + {0x00090082, GetTicketList, "GetTicketList"}, {0x000A0000, nullptr, "GetDeviceID"}, - {0x000D0084, nullptr, "ListTitles2"}, - {0x00140040, nullptr, "FinishInstallToMedia"}, + {0x000D0084, nullptr, "GetPendingTitleInfo"}, + {0x000E00C0, nullptr, "DeletePendingTitle"}, + {0x00140040, nullptr, "FinalizePendingTitles"}, + {0x00150040, nullptr, "DeleteAllPendingTitles"}, {0x00180080, nullptr, "InitializeTitleDatabase"}, {0x00190040, nullptr, "ReloadDBS"}, {0x001A00C0, nullptr, "GetDSiWareExportSize"}, diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index a49365287..6d72e8188 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -397,6 +397,23 @@ void GetAppletInfo(Service::Interface* self) { LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id); } +void GetStartupArgument(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 parameter_size = cmd_buff[1]; + StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(cmd_buff[2]); + + if (parameter_size >= 0x300) { + LOG_ERROR(Service_APT, "Parameter size is outside the valid range (capped to 0x300): parameter_size=0x%08x", parameter_size); + return; + } + + LOG_WARNING(Service_APT,"(stubbed) called startup_argument_type=%u , parameter_size=0x%08x , parameter_value=0x%08x", + startup_argument_type, parameter_size, Memory::Read32(cmd_buff[41])); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = (parameter_size > 0) ? 1 : 0; +} + void Init() { AddService(new APT_A_Interface); AddService(new APT_S_Interface); diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 47a97c1a1..668b4a66f 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -67,6 +67,12 @@ enum class AppletId : u32 { Ed2 = 0x402, }; +enum class StartupArgumentType : u32 { + OtherApp = 0, + Restart = 1, + OtherMedia = 2, +}; + /// Send a parameter to the currently-running application, which will read it via ReceiveParameter void SendParameter(const MessageParameter& parameter); @@ -344,6 +350,17 @@ void PreloadLibraryApplet(Service::Interface* self); */ void StartLibraryApplet(Service::Interface* self); +/** + * APT::GetStartupArgument service function + * Inputs: + * 1 : Parameter Size (capped to 0x300) + * 2 : StartupArgumentType + * Outputs: + * 0 : Return header + * 1 : u8, Exists (0 = does not exist, 1 = exists) + */ +void GetStartupArgument(Service::Interface* self); + /// Initialize the APT service void Init(); diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 0c6a77305..9ff47701a 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -13,9 +13,10 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00020080, Initialize, "Initialize?"}, {0x00030040, Enable, "Enable?"}, {0x00040040, nullptr, "Finalize?"}, - {0x00050040, nullptr, "GetAppletManInfo?"}, - {0x00060040, nullptr, "GetAppletInfo?"}, + {0x00050040, GetAppletManInfo, "GetAppletManInfo"}, + {0x00060040, GetAppletInfo, "GetAppletInfo"}, {0x00090040, IsRegistered, "IsRegistered"}, + {0x000B0040, InquireNotification, "InquireNotification"}, {0x000C0104, SendParameter, "SendParameter"}, {0x000D0080, ReceiveParameter, "ReceiveParameter"}, {0x000E0080, GlanceParameter, "GlanceParameter"}, @@ -24,9 +25,13 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, {0x003B0040, nullptr, "CancelLibraryApplet?"}, + {0x003E0080, nullptr, "ReplySleepQuery"}, {0x00430040, NotifyToWait, "NotifyToWait?"}, {0x00440000, GetSharedFont, "GetSharedFont?"}, {0x004B00C2, AppletUtility, "AppletUtility?"}, + {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, + {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, + {0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00550040, nullptr, "WriteInputToNsState?"}, }; diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index 7f6e81a63..ca54e593c 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -13,8 +13,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00020080, Initialize, "Initialize"}, {0x00030040, Enable, "Enable"}, {0x00040040, nullptr, "Finalize"}, - {0x00050040, nullptr, "GetAppletManInfo"}, - {0x00060040, nullptr, "GetAppletInfo"}, + {0x00050040, GetAppletManInfo, "GetAppletManInfo"}, + {0x00060040, GetAppletInfo, "GetAppletInfo"}, {0x00070000, nullptr, "GetLastSignaledAppletId"}, {0x00080000, nullptr, "CountRegisteredApplet"}, {0x00090040, nullptr, "IsRegistered"}, @@ -87,9 +87,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x004C0000, nullptr, "SetFatalErrDispMode"}, {0x004D0080, nullptr, "GetAppletProgramInfo"}, {0x004E0000, nullptr, "HardwareResetAsync"}, - {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"}, - {0x00500040, nullptr, "GetApplicationCpuTimeLimit"}, - {0x00510080, nullptr, "GetStartupArgument"}, + {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, + {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, + {0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00520104, nullptr, "Wrap1"}, {0x00530104, nullptr, "Unwrap1"}, {0x00580002, nullptr, "GetProgramID"}, diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index b13b51549..0e85c6d08 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -89,7 +89,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x004E0000, nullptr, "HardwareResetAsync"}, {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, - {0x00510080, nullptr, "GetStartupArgument"}, + {0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00520104, nullptr, "Wrap1"}, {0x00530104, nullptr, "Unwrap1"}, {0x00580002, nullptr, "GetProgramID"}, diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index 6d79ce9b4..50c03495e 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -4,6 +4,7 @@ #include "common/logging/log.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/service.h" #include "core/hle/service/cecd/cecd.h" #include "core/hle/service/cecd/cecd_s.h" @@ -12,14 +13,47 @@ namespace Service { namespace CECD { -void Init() { - using namespace Kernel; +static Kernel::SharedPtr<Kernel::Event> cecinfo_event; +static Kernel::SharedPtr<Kernel::Event> change_state_event; + +void GetCecStateAbbreviated(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = static_cast<u32>(CecStateAbbreviated::CEC_STATE_ABBREV_IDLE); + + LOG_WARNING(Service_CECD, "(STUBBED) called"); +} + +void GetCecInfoEventHandle(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).MoveFrom(); // Event handle + + LOG_WARNING(Service_CECD, "(STUBBED) called"); +} + +void GetChangeStateEventHandle(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).MoveFrom(); // Event handle + + LOG_WARNING(Service_CECD, "(STUBBED) called"); +} + +void Init() { AddService(new CECD_S_Interface); AddService(new CECD_U_Interface); + + cecinfo_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD_U::cecinfo_event"); + change_state_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD_U::change_state_event"); } void Shutdown() { + cecinfo_event = nullptr; + change_state_event = nullptr; } } // namespace CECD diff --git a/src/core/hle/service/cecd/cecd.h b/src/core/hle/service/cecd/cecd.h index 9e158521b..435611363 100644 --- a/src/core/hle/service/cecd/cecd.h +++ b/src/core/hle/service/cecd/cecd.h @@ -5,8 +5,49 @@ #pragma once namespace Service { + +class Interface; + namespace CECD { +enum class CecStateAbbreviated { + CEC_STATE_ABBREV_IDLE = 1, ///< Corresponds to CEC_STATE_IDLE + CEC_STATE_ABBREV_NOT_LOCAL = 2, ///< Corresponds to CEC_STATEs *FINISH*, *POST, and OVER_BOSS + CEC_STATE_ABBREV_SCANNING = 3, ///< Corresponds to CEC_STATE_SCANNING + CEC_STATE_ABBREV_WLREADY = 4, ///< Corresponds to CEC_STATE_WIRELESS_READY when some unknown bool is true + CEC_STATE_ABBREV_OTHER = 5, ///< Corresponds to CEC_STATEs besides *FINISH*, *POST, and OVER_BOSS and those listed here +}; + +/** + * GetCecStateAbbreviated service function + * Inputs: + * 0: 0x000E0000 + * Outputs: + * 1: ResultCode + * 2: CecStateAbbreviated + */ +void GetCecStateAbbreviated(Service::Interface* self); + +/** + * GetCecInfoEventHandle service function + * Inputs: + * 0: 0x000F0000 + * Outputs: + * 1: ResultCode + * 3: Event Handle + */ +void GetCecInfoEventHandle(Service::Interface* self); + +/** + * GetChangeStateEventHandle service function + * Inputs: + * 0: 0x00100000 + * Outputs: + * 1: ResultCode + * 3: Event Handle + */ +void GetChangeStateEventHandle(Service::Interface* self); + /// Initialize CECD service(s) void Init(); diff --git a/src/core/hle/service/cecd/cecd_u.cpp b/src/core/hle/service/cecd/cecd_u.cpp index 9b720a738..be6d4d8f6 100644 --- a/src/core/hle/service/cecd/cecd_u.cpp +++ b/src/core/hle/service/cecd/cecd_u.cpp @@ -2,13 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/cecd/cecd.h" #include "core/hle/service/cecd/cecd_u.h" namespace Service { namespace CECD { static const Interface::FunctionInfo FunctionTable[] = { - { 0x00120104, nullptr, "ReadSavedData" }, + {0x000E0000, GetCecStateAbbreviated, "GetCecStateAbbreviated"}, + {0x000F0000, GetCecInfoEventHandle, "GetCecInfoEventHandle"}, + {0x00100000, GetChangeStateEventHandle, "GetChangeStateEventHandle"}, + {0x00120104, nullptr, "ReadSavedData"}, }; CECD_U_Interface::CECD_U_Interface() { diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 525432957..b9322c55d 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -389,6 +389,10 @@ ResultCode FormatConfig() { res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL); if (!res.IsSuccess()) return res; + // 0x00170000 - Unknown + res = CreateConfigInfoBlk(0x00170000, 0x4, 0xE, zero_buffer); + if (!res.IsSuccess()) return res; + // Save the buffer to the file res = UpdateConfigNANDSavegame(); if (!res.IsSuccess()) diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index 606ab99cf..c01806836 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h @@ -98,19 +98,6 @@ void GetCountryCodeString(Service::Interface* self); void GetCountryCodeID(Service::Interface* self); /** - * CFG::GetConfigInfoBlk2 service function - * Inputs: - * 0 : 0x00010082 - * 1 : Size - * 2 : Block ID - * 3 : Descriptor for the output buffer - * 4 : Output buffer pointer - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void GetConfigInfoBlk2(Service::Interface* self); - -/** * CFG::SecureInfoGetRegion service function * Inputs: * 1 : None diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index 0559a07b2..b18060f6d 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp @@ -9,6 +9,18 @@ namespace Service { namespace CFG { const Interface::FunctionInfo FunctionTable[] = { + // cfg common + {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, + {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, + {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, + {0x00040000, GetRegionCanadaUSA, "GetRegionCanadaUSA"}, + {0x00050000, GetSystemModel, "GetSystemModel"}, + {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"}, + {0x00070040, nullptr, "WriteToFirstByteCfgSavegame"}, + {0x00080080, nullptr, "GoThroughTable"}, + {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, + {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, + // cfg:i {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x04020082, nullptr, "SetConfigInfoBlk4"}, {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index b03d290e5..e001f7687 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -9,10 +9,18 @@ namespace Service { namespace CFG { const Interface::FunctionInfo FunctionTable[] = { + // cfg common {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, + {0x00040000, GetRegionCanadaUSA, "GetRegionCanadaUSA"}, {0x00050000, GetSystemModel, "GetSystemModel"}, + {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"}, + {0x00070040, nullptr, "WriteToFirstByteCfgSavegame"}, + {0x00080080, nullptr, "GoThroughTable"}, + {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, + {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, + // cfg:s {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x04020082, nullptr, "SetConfigInfoBlk4"}, {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index 89ae96c9e..606f7b2eb 100644 --- a/src/core/hle/service/cfg/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp @@ -9,6 +9,7 @@ namespace Service { namespace CFG { const Interface::FunctionInfo FunctionTable[] = { + // cfg common {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, diff --git a/src/core/hle/service/dlp_srvr.cpp b/src/core/hle/service/dlp_srvr.cpp new file mode 100644 index 000000000..1f30188da --- /dev/null +++ b/src/core/hle/service/dlp_srvr.cpp @@ -0,0 +1,36 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/dlp_srvr.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace DLP_SRVR + +namespace DLP_SRVR { + +static void unk_0x000E0040(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; + + LOG_WARNING(Service_DLP, "(STUBBED) called"); +} + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010183, nullptr, "Initialize"}, + {0x00020000, nullptr, "Finalize"}, + {0x000E0040, unk_0x000E0040, "unk_0x000E0040"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/dlp_srvr.h b/src/core/hle/service/dlp_srvr.h new file mode 100644 index 000000000..d65d00814 --- /dev/null +++ b/src/core/hle/service/dlp_srvr.h @@ -0,0 +1,23 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace DLP_SRVR + +namespace DLP_SRVR { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "dlp:SRVR"; + } +}; + +} // namespace diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 08e437125..995bee3f9 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cinttypes> #include "audio_core/hle/pipe.h" @@ -12,37 +13,80 @@ #include "core/hle/kernel/event.h" #include "core/hle/service/dsp_dsp.h" +using DspPipe = DSP::HLE::DspPipe; + //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace DSP_DSP namespace DSP_DSP { -static u32 read_pipe_count; static Kernel::SharedPtr<Kernel::Event> semaphore_event; -struct PairHash { - template <typename T, typename U> - std::size_t operator()(const std::pair<T, U> &x) const { - // TODO(yuriks): Replace with better hash combining function. - return std::hash<T>()(x.first) ^ std::hash<U>()(x.second); +/// There are three types of interrupts +enum class InterruptType { + Zero, One, Pipe +}; +constexpr size_t NUM_INTERRUPT_TYPE = 3; + +class InterruptEvents final { +public: + void Signal(InterruptType type, DspPipe pipe) { + Kernel::SharedPtr<Kernel::Event>& event = Get(type, pipe); + if (event) { + event->Signal(); + } } + + Kernel::SharedPtr<Kernel::Event>& Get(InterruptType type, DspPipe dsp_pipe) { + switch (type) { + case InterruptType::Zero: + return zero; + case InterruptType::One: + return one; + case InterruptType::Pipe: { + const size_t pipe_index = static_cast<size_t>(dsp_pipe); + ASSERT(pipe_index < DSP::HLE::NUM_DSP_PIPE); + return pipe[pipe_index]; + } + } + + UNREACHABLE_MSG("Invalid interrupt type = %zu", static_cast<size_t>(type)); + } + + bool HasTooManyEventsRegistered() const { + // Actual service implementation only has 6 'slots' for interrupts. + constexpr size_t max_number_of_interrupt_events = 6; + + size_t number = std::count_if(pipe.begin(), pipe.end(), [](const auto& evt) { + return evt != nullptr; + }); + + if (zero != nullptr) + number++; + if (one != nullptr) + number++; + + return number >= max_number_of_interrupt_events; + } + +private: + /// Currently unknown purpose + Kernel::SharedPtr<Kernel::Event> zero = nullptr; + /// Currently unknown purpose + Kernel::SharedPtr<Kernel::Event> one = nullptr; + /// Each DSP pipe has an associated interrupt + std::array<Kernel::SharedPtr<Kernel::Event>, DSP::HLE::NUM_DSP_PIPE> pipe = {{}}; }; -/// Map of (audio interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents -static std::unordered_map<std::pair<u32, u32>, Kernel::SharedPtr<Kernel::Event>, PairHash> interrupt_events; +static InterruptEvents interrupt_events; // DSP Interrupts: -// Interrupt #2 occurs every frame tick. Userland programs normally have a thread that's waiting -// for an interrupt event. Immediately after this interrupt event, userland normally updates the -// state in the next region and increments the relevant frame counter by two. -void SignalAllInterrupts() { - // HACK: The other interrupts have currently unknown purpose, we trigger them each tick in any case. - for (auto& interrupt_event : interrupt_events) - interrupt_event.second->Signal(); -} - -void SignalInterrupt(u32 interrupt, u32 channel) { - interrupt_events[std::make_pair(interrupt, channel)]->Signal(); +// The audio-pipe interrupt occurs every frame tick. Userland programs normally have a thread +// that's waiting for an interrupt event. Immediately after this interrupt event, userland +// normally updates the state in the next region and increments the relevant frame counter by +// two. +void SignalPipeInterrupt(DspPipe pipe) { + interrupt_events.Signal(InterruptType::Pipe, pipe); } /** @@ -58,7 +102,10 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) { u32 addr = cmd_buff[1]; + cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + // TODO(merry): There is a per-region offset missing in this calculation (that seems to be always zero). cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000); LOG_DEBUG(Service_DSP, "addr=0x%08X", addr); @@ -113,7 +160,9 @@ static void LoadComponent(Service::Interface* self) { static void GetSemaphoreEventHandle(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error + // cmd_buff[2] not set cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle LOG_WARNING(Service_DSP, "(STUBBED) called"); @@ -138,8 +187,7 @@ static void FlushDataCache(Service::Interface* self) { u32 size = cmd_buff[2]; u32 process = cmd_buff[4]; - // TODO(purpasmart96): Verify return header on HW - + cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_TRACE(Service_DSP, "called address=0x%08X, size=0x%X, process=0x%08X", address, size, process); @@ -148,8 +196,8 @@ static void FlushDataCache(Service::Interface* self) { /** * DSP_DSP::RegisterInterruptEvents service function * Inputs: - * 1 : Interrupt Number - * 2 : Channel Number + * 1 : Interrupt Type + * 2 : Pipe Number * 4 : Interrupt event handle * Outputs: * 1 : Result of function, 0 on success, otherwise error code @@ -157,23 +205,40 @@ static void FlushDataCache(Service::Interface* self) { static void RegisterInterruptEvents(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 interrupt = cmd_buff[1]; - u32 channel = cmd_buff[2]; + u32 type_index = cmd_buff[1]; + u32 pipe_index = cmd_buff[2]; u32 event_handle = cmd_buff[4]; + ASSERT_MSG(type_index < NUM_INTERRUPT_TYPE && pipe_index < DSP::HLE::NUM_DSP_PIPE, + "Invalid type or pipe: type = %u, pipe = %u", type_index, pipe_index); + + InterruptType type = static_cast<InterruptType>(cmd_buff[1]); + DspPipe pipe = static_cast<DspPipe>(cmd_buff[2]); + + cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0); + if (event_handle) { auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); - if (evt) { - interrupt_events[std::make_pair(interrupt, channel)] = evt; - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_INFO(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); - } else { - LOG_CRITICAL(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); - ASSERT(false); // This should really be handled at a IPC translation layer. + + if (!evt) { + LOG_INFO(Service_DSP, "Invalid event handle! type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); + ASSERT(false); // TODO: This should really be handled at an IPC translation layer. + } + + if (interrupt_events.HasTooManyEventsRegistered()) { + LOG_INFO(Service_DSP, "Ran out of space to register interrupts (Attempted to register type=%u, pipe=%u, event_handle=0x%08X)", + type_index, pipe_index, event_handle); + cmd_buff[1] = ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::DSP, ErrorSummary::OutOfResource, ErrorLevel::Status).raw; + return; } + + interrupt_events.Get(type, pipe) = evt; + LOG_INFO(Service_DSP, "Registered type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); + cmd_buff[1] = RESULT_SUCCESS.raw; } else { - interrupt_events.erase(std::make_pair(interrupt, channel)); - LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); + interrupt_events.Get(type, pipe) = nullptr; + LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); + cmd_buff[1] = RESULT_SUCCESS.raw; } } @@ -187,6 +252,7 @@ static void RegisterInterruptEvents(Service::Interface* self) { static void SetSemaphore(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_DSP, "(STUBBED) called"); @@ -195,7 +261,7 @@ static void SetSemaphore(Service::Interface* self) { /** * DSP_DSP::WriteProcessPipe service function * Inputs: - * 1 : Channel + * 1 : Pipe Number * 2 : Size * 3 : (size << 14) | 0x402 * 4 : Buffer @@ -206,24 +272,32 @@ static void SetSemaphore(Service::Interface* self) { static void WriteProcessPipe(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 size = cmd_buff[2]; u32 buffer = cmd_buff[4]; - ASSERT_MSG(IPC::StaticBufferDesc(size, 1) == cmd_buff[3], "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe, size, buffer); - ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); - std::vector<u8> message(size); + if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) { + LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe_index, size, buffer); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; + return; + } + + ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); + std::vector<u8> message(size); for (size_t i = 0; i < size; i++) { message[i] = Memory::Read8(buffer + i); } DSP::HLE::PipeWrite(pipe, message); + cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); + LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); } /** @@ -243,13 +317,16 @@ static void WriteProcessPipe(Service::Interface* self) { static void ReadPipeIfPossible(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 unknown = cmd_buff[2]; u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size VAddr addr = cmd_buff[0x41]; - ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); + + ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr); + cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); @@ -260,8 +337,10 @@ static void ReadPipeIfPossible(Service::Interface* self) { } else { cmd_buff[2] = 0; // Return no data } + cmd_buff[3] = IPC::StaticBufferDesc(size, 0); + cmd_buff[4] = addr; - LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]); + LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, size, addr, cmd_buff[2]); } /** @@ -278,26 +357,31 @@ static void ReadPipeIfPossible(Service::Interface* self) { static void ReadPipe(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 unknown = cmd_buff[2]; u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size VAddr addr = cmd_buff[0x41]; - ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); + + ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr); if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); Memory::WriteBlock(addr, response.data(), response.size()); + cmd_buff[0] = IPC::MakeHeader(0xE, 2, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = static_cast<u32>(response.size()); + cmd_buff[3] = IPC::StaticBufferDesc(size, 0); + cmd_buff[4] = addr; } else { // No more data is in pipe. Hardware hangs in this case; this should never happen. UNREACHABLE(); } - LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]); + LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, size, addr, cmd_buff[2]); } /** @@ -312,13 +396,16 @@ static void ReadPipe(Service::Interface* self) { static void GetPipeReadableSize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 unknown = cmd_buff[2]; + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); + + cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe); - LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, cmd_buff[2]); + LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, cmd_buff[2]); } /** @@ -333,6 +420,7 @@ static void SetSemaphoreMask(Service::Interface* self) { u32 mask = cmd_buff[1]; + cmd_buff[0] = IPC::MakeHeader(0x17, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_DSP, "(STUBBED) called mask=0x%08X", mask); @@ -350,6 +438,7 @@ static void SetSemaphoreMask(Service::Interface* self) { static void GetHeadphoneStatus(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; // Not using headphones? @@ -376,6 +465,7 @@ static void RecvData(Service::Interface* self) { // Application reads this after requesting DSP shutdown, to verify the DSP has indeed shutdown or slept. + cmd_buff[0] = IPC::MakeHeader(0x1, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; switch (DSP::HLE::GetDspState()) { case DSP::HLE::DspState::On: @@ -411,6 +501,7 @@ static void RecvDataIsReady(Service::Interface* self) { ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number); + cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 1; // Ready to read @@ -458,14 +549,14 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); - read_pipe_count = 0; + interrupt_events = {}; Register(FunctionTable); } Interface::~Interface() { semaphore_event = nullptr; - interrupt_events.clear(); + interrupt_events = {}; } } // namespace diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index 32b89e9bb..22f6687cc 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h @@ -8,6 +8,12 @@ #include "core/hle/service/service.h" +namespace DSP { +namespace HLE { +enum class DspPipe; +} +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace DSP_DSP @@ -23,15 +29,10 @@ public: } }; -/// Signal all audio related interrupts. -void SignalAllInterrupts(); - /** - * Signal a specific audio related interrupt based on interrupt id and channel id. - * @param interrupt_id The interrupt id - * @param channel_id The channel id - * The significance of various values of interrupt_id and channel_id is not yet known. + * Signal a specific DSP related interrupt of type == InterruptType::Pipe, pipe == pipe. + * @param pipe The DSP pipe for which to signal an interrupt for. */ -void SignalInterrupt(u32 interrupt_id, u32 channel_id); +void SignalPipeInterrupt(DSP::HLE::DspPipe pipe); -} // namespace +} // namespace DSP_DSP diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp index c13ffd9d2..15d604bb6 100644 --- a/src/core/hle/service/frd/frd.cpp +++ b/src/core/hle/service/frd/frd.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/string_util.h" + #include "core/hle/service/service.h" #include "core/hle/service/frd/frd.h" #include "core/hle/service/frd/frd_a.h" @@ -10,6 +12,95 @@ namespace Service { namespace FRD { +static FriendKey my_friend_key = {0, 0, 0ull}; +static MyPresence my_presence = {}; + +void GetMyPresence(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 shifted_out_size = cmd_buff[64]; + u32 my_presence_addr = cmd_buff[65]; + + ASSERT(shifted_out_size == ((sizeof(MyPresence) << 14) | 2)); + + Memory::WriteBlock(my_presence_addr, reinterpret_cast<const u8*>(&my_presence), sizeof(MyPresence)); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + +void GetFriendKeyList(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 unknown = cmd_buff[1]; + u32 frd_count = cmd_buff[2]; + u32 frd_key_addr = cmd_buff[65]; + + FriendKey zero_key = {}; + for (u32 i = 0; i < frd_count; ++i) { + Memory::WriteBlock(frd_key_addr + i * sizeof(FriendKey), + reinterpret_cast<const u8*>(&zero_key), sizeof(FriendKey)); + } + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0; // 0 friends + LOG_WARNING(Service_FRD, "(STUBBED) called, unknown=%d, frd_count=%d, frd_key_addr=0x%08X", + unknown, frd_count, frd_key_addr); +} + +void GetFriendProfile(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 count = cmd_buff[1]; + u32 frd_key_addr = cmd_buff[3]; + u32 profiles_addr = cmd_buff[65]; + + Profile zero_profile = {}; + for (u32 i = 0; i < count; ++i) { + Memory::WriteBlock(profiles_addr + i * sizeof(Profile), + reinterpret_cast<const u8*>(&zero_profile), sizeof(Profile)); + } + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_FRD, "(STUBBED) called, count=%d, frd_key_addr=0x%08X, profiles_addr=0x%08X", + count, frd_key_addr, profiles_addr); +} + +void GetFriendAttributeFlags(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 count = cmd_buff[1]; + u32 frd_key_addr = cmd_buff[3]; + u32 attr_flags_addr = cmd_buff[65]; + + for (u32 i = 0; i < count; ++i) { + //TODO:(mailwl) figure out AttributeFlag size and zero all buffer. Assume 1 byte + Memory::Write8(attr_flags_addr + i, 0); + } + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_FRD, "(STUBBED) called, count=%d, frd_key_addr=0x%08X, attr_flags_addr=0x%08X", + count, frd_key_addr, attr_flags_addr); +} + +void GetMyFriendKey(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + Memory::WriteBlock(cmd_buff[2], reinterpret_cast<const u8*>(&my_friend_key), sizeof(FriendKey)); + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + +void GetMyScreenName(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + // TODO: (mailwl) get the name from config + Common::UTF8ToUTF16("Citra").copy(reinterpret_cast<char16_t*>(&cmd_buff[2]), 11); + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + void Init() { using namespace Kernel; diff --git a/src/core/hle/service/frd/frd.h b/src/core/hle/service/frd/frd.h index f9f88b444..c8283a7f3 100644 --- a/src/core/hle/service/frd/frd.h +++ b/src/core/hle/service/frd/frd.h @@ -4,9 +4,97 @@ #pragma once +#include "common/common_types.h" + namespace Service { + +class Interface; + namespace FRD { +struct FriendKey { + u32 friend_id; + u32 unknown; + u64 friend_code; +}; + +struct MyPresence { + u8 unknown[0x12C]; +}; + +struct Profile { + u8 region; + u8 country; + u8 area; + u8 language; + u32 unknown; +}; + +/** + * FRD::GetMyPresence service function + * Inputs: + * 64 : sizeof (MyPresence) << 14 | 2 + * 65 : Address of MyPresence structure + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void GetMyPresence(Service::Interface* self); + +/** + * FRD::GetFriendKeyList service function + * Inputs: + * 1 : Unknown + * 2 : Max friends count + * 65 : Address of FriendKey List + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : FriendKey count filled + */ +void GetFriendKeyList(Service::Interface* self); + +/** + * FRD::GetFriendProfile service function + * Inputs: + * 1 : Friends count + * 2 : Friends count << 18 | 2 + * 3 : Address of FriendKey List + * 64 : (count * sizeof (Profile)) << 10 | 2 + * 65 : Address of Profiles List + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void GetFriendProfile(Service::Interface* self); + +/** + * FRD::GetFriendAttributeFlags service function + * Inputs: + * 1 : Friends count + * 2 : Friends count << 18 | 2 + * 3 : Address of FriendKey List + * 65 : Address of AttributeFlags + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void GetFriendAttributeFlags(Service::Interface* self); + +/** + * FRD::GetMyFriendKey service function + * Inputs: + * none + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-5 : FriendKey + */ +void GetMyFriendKey(Service::Interface* self); + +/** + * FRD::GetMyScreenName service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : UTF16 encoded name (max 11 symbols) + */ +void GetMyScreenName(Service::Interface* self); + /// Initialize FRD service(s) void Init(); diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp index 2c6885377..db8666416 100644 --- a/src/core/hle/service/frd/frd_u.cpp +++ b/src/core/hle/service/frd/frd_u.cpp @@ -2,65 +2,66 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/frd/frd.h" #include "core/hle/service/frd/frd_u.h" namespace Service { namespace FRD { const Interface::FunctionInfo FunctionTable[] = { - {0x00010000, nullptr, "HasLoggedIn"}, - {0x00020000, nullptr, "IsOnline"}, - {0x00030000, nullptr, "Login"}, - {0x00040000, nullptr, "Logout"}, - {0x00050000, nullptr, "GetMyFriendKey"}, - {0x00060000, nullptr, "GetMyPreference"}, - {0x00070000, nullptr, "GetMyProfile"}, - {0x00080000, nullptr, "GetMyPresence"}, - {0x00090000, nullptr, "GetMyScreenName"}, - {0x000A0000, nullptr, "GetMyMii"}, - {0x000B0000, nullptr, "GetMyLocalAccountId"}, - {0x000C0000, nullptr, "GetMyPlayingGame"}, - {0x000D0000, nullptr, "GetMyFavoriteGame"}, - {0x000E0000, nullptr, "GetMyNcPrincipalId"}, - {0x000F0000, nullptr, "GetMyComment"}, - {0x00100040, nullptr, "GetMyPassword"}, - {0x00110080, nullptr, "GetFriendKeyList"}, - {0x00120042, nullptr, "GetFriendPresence"}, - {0x00130142, nullptr, "GetFriendScreenName"}, - {0x00140044, nullptr, "GetFriendMii"}, - {0x00150042, nullptr, "GetFriendProfile"}, - {0x00160042, nullptr, "GetFriendRelationship"}, - {0x00170042, nullptr, "GetFriendAttributeFlags"}, - {0x00180044, nullptr, "GetFriendPlayingGame"}, - {0x00190042, nullptr, "GetFriendFavoriteGame"}, - {0x001A00C4, nullptr, "GetFriendInfo"}, - {0x001B0080, nullptr, "IsIncludedInFriendList"}, - {0x001C0042, nullptr, "UnscrambleLocalFriendCode"}, - {0x001D0002, nullptr, "UpdateGameModeDescription"}, - {0x001E02C2, nullptr, "UpdateGameMode"}, - {0x001F0042, nullptr, "SendInvitation"}, - {0x00200002, nullptr, "AttachToEventNotification"}, - {0x00210040, nullptr, "SetNotificationMask"}, - {0x00220040, nullptr, "GetEventNotification"}, - {0x00230000, nullptr, "GetLastResponseResult"}, - {0x00240040, nullptr, "PrincipalIdToFriendCode"}, - {0x00250080, nullptr, "FriendCodeToPrincipalId"}, - {0x00260080, nullptr, "IsValidFriendCode"}, - {0x00270040, nullptr, "ResultToErrorCode"}, - {0x00280244, nullptr, "RequestGameAuthentication"}, - {0x00290000, nullptr, "GetGameAuthenticationData"}, - {0x002A0204, nullptr, "RequestServiceLocator"}, - {0x002B0000, nullptr, "GetServiceLocatorData"}, - {0x002C0002, nullptr, "DetectNatProperties"}, - {0x002D0000, nullptr, "GetNatProperties"}, - {0x002E0000, nullptr, "GetServerTimeInterval"}, - {0x002F0040, nullptr, "AllowHalfAwake"}, - {0x00300000, nullptr, "GetServerTypes"}, - {0x00310082, nullptr, "GetFriendComment"}, - {0x00320042, nullptr, "SetClientSdkVersion"}, - {0x00330000, nullptr, "GetMyApproachContext"}, - {0x00340046, nullptr, "AddFriendWithApproach"}, - {0x00350082, nullptr, "DecryptApproachContext"}, + {0x00010000, nullptr, "HasLoggedIn"}, + {0x00020000, nullptr, "IsOnline"}, + {0x00030000, nullptr, "Login"}, + {0x00040000, nullptr, "Logout"}, + {0x00050000, GetMyFriendKey, "GetMyFriendKey"}, + {0x00060000, nullptr, "GetMyPreference"}, + {0x00070000, nullptr, "GetMyProfile"}, + {0x00080000, GetMyPresence, "GetMyPresence"}, + {0x00090000, GetMyScreenName, "GetMyScreenName"}, + {0x000A0000, nullptr, "GetMyMii"}, + {0x000B0000, nullptr, "GetMyLocalAccountId"}, + {0x000C0000, nullptr, "GetMyPlayingGame"}, + {0x000D0000, nullptr, "GetMyFavoriteGame"}, + {0x000E0000, nullptr, "GetMyNcPrincipalId"}, + {0x000F0000, nullptr, "GetMyComment"}, + {0x00100040, nullptr, "GetMyPassword"}, + {0x00110080, GetFriendKeyList, "GetFriendKeyList"}, + {0x00120042, nullptr, "GetFriendPresence"}, + {0x00130142, nullptr, "GetFriendScreenName"}, + {0x00140044, nullptr, "GetFriendMii"}, + {0x00150042, GetFriendProfile, "GetFriendProfile"}, + {0x00160042, nullptr, "GetFriendRelationship"}, + {0x00170042, GetFriendAttributeFlags, "GetFriendAttributeFlags"}, + {0x00180044, nullptr, "GetFriendPlayingGame"}, + {0x00190042, nullptr, "GetFriendFavoriteGame"}, + {0x001A00C4, nullptr, "GetFriendInfo"}, + {0x001B0080, nullptr, "IsIncludedInFriendList"}, + {0x001C0042, nullptr, "UnscrambleLocalFriendCode"}, + {0x001D0002, nullptr, "UpdateGameModeDescription"}, + {0x001E02C2, nullptr, "UpdateGameMode"}, + {0x001F0042, nullptr, "SendInvitation"}, + {0x00200002, nullptr, "AttachToEventNotification"}, + {0x00210040, nullptr, "SetNotificationMask"}, + {0x00220040, nullptr, "GetEventNotification"}, + {0x00230000, nullptr, "GetLastResponseResult"}, + {0x00240040, nullptr, "PrincipalIdToFriendCode"}, + {0x00250080, nullptr, "FriendCodeToPrincipalId"}, + {0x00260080, nullptr, "IsValidFriendCode"}, + {0x00270040, nullptr, "ResultToErrorCode"}, + {0x00280244, nullptr, "RequestGameAuthentication"}, + {0x00290000, nullptr, "GetGameAuthenticationData"}, + {0x002A0204, nullptr, "RequestServiceLocator"}, + {0x002B0000, nullptr, "GetServiceLocatorData"}, + {0x002C0002, nullptr, "DetectNatProperties"}, + {0x002D0000, nullptr, "GetNatProperties"}, + {0x002E0000, nullptr, "GetServerTimeInterval"}, + {0x002F0040, nullptr, "AllowHalfAwake"}, + {0x00300000, nullptr, "GetServerTypes"}, + {0x00310082, nullptr, "GetFriendComment"}, + {0x00320042, nullptr, "SetClientSdkVersion"}, + {0x00330000, nullptr, "GetMyApproachContext"}, + {0x00340046, nullptr, "AddFriendWithApproach"}, + {0x00350082, nullptr, "DecryptApproachContext"}, }; FRD_U_Interface::FRD_U_Interface() { diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 590697e76..cc51ede0c 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -15,7 +15,6 @@ #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/make_unique.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/archive_extsavedata.h" @@ -115,6 +114,7 @@ ResultVal<bool> File::SyncRequest() { return read.Code(); } cmd_buff[2] = static_cast<u32>(*read); + Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(address), length); break; } @@ -521,23 +521,23 @@ void ArchiveInit() { std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); - auto sdmc_factory = Common::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory); + auto sdmc_factory = std::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory); if (sdmc_factory->Initialize()) RegisterArchiveType(std::move(sdmc_factory), ArchiveIdCode::SDMC); else LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); // Create the SaveData archive - auto savedata_factory = Common::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory); + auto savedata_factory = std::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory); RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData); - auto extsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false); + auto extsavedata_factory = std::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false); if (extsavedata_factory->Initialize()) RegisterArchiveType(std::move(extsavedata_factory), ArchiveIdCode::ExtSaveData); else LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_factory->GetMountPoint().c_str()); - auto sharedextsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_ExtSaveData>(nand_directory, true); + auto sharedextsavedata_factory = std::make_unique<FileSys::ArchiveFactory_ExtSaveData>(nand_directory, true); if (sharedextsavedata_factory->Initialize()) RegisterArchiveType(std::move(sharedextsavedata_factory), ArchiveIdCode::SharedExtSaveData); else @@ -545,10 +545,10 @@ void ArchiveInit() { sharedextsavedata_factory->GetMountPoint().c_str()); // Create the SaveDataCheck archive, basically a small variation of the RomFS archive - auto savedatacheck_factory = Common::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory); + auto savedatacheck_factory = std::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory); RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck); - auto systemsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); + auto systemsavedata_factory = std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); } diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 3ec7ceb30..7df7da5a4 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -250,7 +250,7 @@ static void CreateFile(Service::Interface* self) { FileSys::Path file_path(filename_type, filename_size, filename_ptr); - LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, filename_size, file_path.DebugStr().c_str()); + LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, file_size, file_path.DebugStr().c_str()); cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 2ace2cade..b4c146e08 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -15,8 +15,6 @@ #include "video_core/gpu_debugger.h" #include "video_core/debug_utils/debug_utils.h" -#include "video_core/renderer_base.h" -#include "video_core/video_core.h" #include "gsp_gpu.h" @@ -31,6 +29,13 @@ const static u32 REGS_BEGIN = 0x1EB00000; namespace GSP_GPU { +const ResultCode ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED(ErrorDescription::OutofRangeOrMisalignedAddress, ErrorModule::GX, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02A01 +const ResultCode ERR_GSP_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BF2 +const ResultCode ERR_GSP_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BEC + /// Event triggered when GSP interrupt has been signalled Kernel::SharedPtr<Kernel::Event> g_interrupt_event; /// GSP shared memoryings @@ -38,6 +43,8 @@ Kernel::SharedPtr<Kernel::SharedMemory> g_shared_memory; /// Thread index into interrupt relay queue u32 g_thread_id = 0; +static bool gpu_right_acquired = false; + /// Gets a pointer to a thread command buffer in GSP shared memory static inline u8* GetCommandBuffer(u32 thread_id) { return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); @@ -59,47 +66,87 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { } /** - * Checks if the parameters in a register write call are valid and logs in the case that - * they are not - * @param base_address The first address in the sequence of registers that will be written - * @param size_in_bytes The number of registers that will be written - * @return true if the parameters are valid, false otherwise + * Writes sequential GSP GPU hardware registers using an array of source data + * + * @param base_address The address of the first register in the sequence + * @param size_in_bytes The number of registers to update (size of data) + * @param data A pointer to the source data + * @return RESULT_SUCCESS if the parameters are valid, error code otherwise */ -static bool CheckWriteParameters(u32 base_address, u32 size_in_bytes) { - // TODO: Return proper error codes - if (base_address + size_in_bytes >= 0x420000) { - LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)", +static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { + // This magic number is verified to be done by the gsp module + const u32 max_size_in_bytes = 0x80; + + if (base_address & 3 || base_address >= 0x420000) { + LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", base_address, size_in_bytes); - return false; - } + return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; + } else if (size_in_bytes <= max_size_in_bytes) { + if (size_in_bytes & 3) { + LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); + return ERR_GSP_REGS_MISALIGNED; + } else { + while (size_in_bytes > 0) { + HW::Write<u32>(base_address + REGS_BEGIN, *data); + + size_in_bytes -= 4; + ++data; + base_address += 4; + } + return RESULT_SUCCESS; + } - // size should be word-aligned - if ((size_in_bytes % 4) != 0) { - LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes); - return false; + } else { + LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); + return ERR_GSP_REGS_INVALID_SIZE; } - - return true; } /** - * Writes sequential GSP GPU hardware registers using an array of source data + * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. + * For each register, the value is updated only where the mask is high * * @param base_address The address of the first register in the sequence * @param size_in_bytes The number of registers to update (size of data) - * @param data A pointer to the source data + * @param data A pointer to the source data to use for updates + * @param masks A pointer to the masks + * @return RESULT_SUCCESS if the parameters are valid, error code otherwise */ -static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { - // TODO: Return proper error codes - if (!CheckWriteParameters(base_address, size_in_bytes)) - return; +static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) { + // This magic number is verified to be done by the gsp module + const u32 max_size_in_bytes = 0x80; - while (size_in_bytes > 0) { - HW::Write<u32>(base_address + REGS_BEGIN, *data); + if (base_address & 3 || base_address >= 0x420000) { + LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", + base_address, size_in_bytes); + return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; + } else if (size_in_bytes <= max_size_in_bytes) { + if (size_in_bytes & 3) { + LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); + return ERR_GSP_REGS_MISALIGNED; + } else { + while (size_in_bytes > 0) { + const u32 reg_address = base_address + REGS_BEGIN; + + u32 reg_value; + HW::Read<u32>(reg_value, reg_address); + + // Update the current value of the register only for set mask bits + reg_value = (reg_value & ~*masks) | (*data | *masks); + + HW::Write<u32>(reg_address, reg_value); + + size_in_bytes -= 4; + ++data; + ++masks; + base_address += 4; + } + return RESULT_SUCCESS; + } - size_in_bytes -= 4; - ++data; - base_address += 4; + } else { + LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); + return ERR_GSP_REGS_INVALID_SIZE; } } @@ -120,39 +167,7 @@ static void WriteHWRegs(Service::Interface* self) { u32* src = (u32*)Memory::GetPointer(cmd_buff[4]); - WriteHWRegs(reg_addr, size, src); -} - -/** - * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. - * For each register, the value is updated only where the mask is high - * - * @param base_address The address of the first register in the sequence - * @param size_in_bytes The number of registers to update (size of data) - * @param data A pointer to the source data to use for updates - * @param masks A pointer to the masks - */ -static void WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) { - // TODO: Return proper error codes - if (!CheckWriteParameters(base_address, size_in_bytes)) - return; - - while (size_in_bytes > 0) { - const u32 reg_address = base_address + REGS_BEGIN; - - u32 reg_value; - HW::Read<u32>(reg_value, reg_address); - - // Update the current value of the register only for set mask bits - reg_value = (reg_value & ~*masks) | (*data | *masks); - - HW::Write<u32>(reg_address, reg_value); - - size_in_bytes -= 4; - ++data; - ++masks; - base_address += 4; - } + cmd_buff[1] = WriteHWRegs(reg_addr, size, src).raw; } /** @@ -174,7 +189,7 @@ static void WriteHWRegsWithMask(Service::Interface* self) { u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]); u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]); - WriteHWRegsWithMask(reg_addr, size, src_data, mask_data); + cmd_buff[1] = WriteHWRegsWithMask(reg_addr, size, src_data, mask_data).raw; } /// Read a GSP GPU hardware register @@ -206,27 +221,27 @@ static void ReadHWRegs(Service::Interface* self) { } } -void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { +ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { u32 base_address = 0x400000; PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); if (info.active_fb == 0) { - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), 4, - &phys_address_left); - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), 4, - &phys_address_right); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), + 4, &phys_address_left); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), + 4, &phys_address_right); } else { - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), 4, - &phys_address_left); - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), 4, - &phys_address_right); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), + 4, &phys_address_left); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), + 4, &phys_address_right); } - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), 4, - &info.stride); - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), 4, - &info.format); - WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), 4, - &info.shown_fb); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), + 4, &info.stride); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), + 4, &info.format); + WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), + 4, &info.shown_fb); if (Pica::g_debug_context) Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); @@ -234,6 +249,8 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { if (screen_id == 0) { MicroProfileFlip(); } + + return RESULT_SUCCESS; } /** @@ -251,9 +268,8 @@ static void SetBufferSwap(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 screen_id = cmd_buff[1]; FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; - SetBufferSwap(screen_id, *fb_info); - cmd_buff[1] = 0; // No error + cmd_buff[1] = SetBufferSwap(screen_id, *fb_info).raw; } /** @@ -275,8 +291,6 @@ static void FlushDataCache(Service::Interface* self) { u32 size = cmd_buff[2]; u32 process = cmd_buff[4]; - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(address), size); - // TODO(purpasmart96): Verify return header on HW cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -286,6 +300,22 @@ static void FlushDataCache(Service::Interface* self) { } /** + * GSP_GPU::SetAxiConfigQoSMode service function + * Inputs: + * 1 : Mode, unused in emulator + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetAxiConfigQoSMode(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 mode = cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_GSP, "(STUBBED) called mode=0x%08X", mode); +} + +/** * GSP_GPU::RegisterInterruptRelayQueue service function * Inputs: * 1 : "Flags" field, purpose is unknown @@ -302,6 +332,12 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]); ASSERT_MSG((g_interrupt_event != nullptr), "handle is not valid!"); + g_interrupt_event->name = "GSP_GPU::interrupt_event"; + + using Kernel::MemoryPermission; + g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, + MemoryPermission::ReadWrite, "GSPSharedMem"); + Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); // This specific code is required for a successful initialization, rather than 0 @@ -314,12 +350,31 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { } /** + * GSP_GPU::UnregisterInterruptRelayQueue service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void UnregisterInterruptRelayQueue(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + g_shared_memory = nullptr; + g_interrupt_event = nullptr; + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_GSP, "called"); +} + +/** * Signals that the specified interrupt type has occurred to userland code * @param interrupt_id ID of interrupt that is being signalled * @todo This should probably take a thread_id parameter and only signal this thread? * @todo This probably does not belong in the GSP module, instead move to video_core */ void SignalInterrupt(InterruptId interrupt_id) { + if (!gpu_right_acquired) { + return; + } if (nullptr == g_interrupt_event) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); return; @@ -354,6 +409,8 @@ void SignalInterrupt(InterruptId interrupt_id) { g_interrupt_event->Signal(); } +MICROPROFILE_DEFINE(GPU_GSP_DMA, "GPU", "GSP DMA", MP_RGB(100, 0, 255)); + /// Executes the next GSP command static void ExecuteCommand(const Command& command, u32 thread_id) { // Utility function to convert register ID to address @@ -365,18 +422,21 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { // GX request DMA - typically used for copying memory from GSP heap to VRAM case CommandId::REQUEST_DMA: - VideoCore::g_renderer->Rasterizer()->FlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address), - command.dma_request.size); + { + MICROPROFILE_SCOPE(GPU_GSP_DMA); + + // TODO: Consider attempting rasterizer-accelerated surface blit if that usage is ever possible/likely + Memory::RasterizerFlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address), + command.dma_request.size); + Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address), + command.dma_request.size); memcpy(Memory::GetPointer(command.dma_request.dest_address), Memory::GetPointer(command.dma_request.source_address), command.dma_request.size); SignalInterrupt(InterruptId::DMA); - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address), - command.dma_request.size); break; - + } // TODO: This will need some rework in the future. (why?) case CommandId::SUBMIT_GPU_CMDLIST: { @@ -463,13 +523,8 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { case CommandId::CACHE_FLUSH: { - for (auto& region : command.cache_flush.regions) { - if (region.size == 0) - break; - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion( - Memory::VirtualToPhysicalAddress(region.address), region.size); - } + // NOTE: Rasterizer flushing handled elsewhere in CPU read/write and other GPU handlers + // Use command.cache_flush.regions to implement this handler break; } @@ -574,6 +629,35 @@ static void ImportDisplayCaptureInfo(Service::Interface* self) { LOG_WARNING(Service_GSP, "called"); } +/** + * GSP_GPU::AcquireRight service function + * Outputs: + * 1: Result code + */ +static void AcquireRight(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + gpu_right_acquired = true; + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_GSP, "called"); +} + +/** + * GSP_GPU::ReleaseRight service function + * Outputs: + * 1: Result code + */ +static void ReleaseRight(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + gpu_right_acquired = false; + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_GSP, "called"); +} const Interface::FunctionInfo FunctionTable[] = { {0x00010082, WriteHWRegs, "WriteHWRegs"}, @@ -591,14 +675,14 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000D0140, nullptr, "SetDisplayTransfer"}, {0x000E0180, nullptr, "SetTextureCopy"}, {0x000F0200, nullptr, "SetMemoryFill"}, - {0x00100040, nullptr, "SetAxiConfigQoSMode"}, + {0x00100040, SetAxiConfigQoSMode, "SetAxiConfigQoSMode"}, {0x00110040, nullptr, "SetPerfLogMode"}, {0x00120000, nullptr, "GetPerfLog"}, {0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"}, - {0x00140000, nullptr, "UnregisterInterruptRelayQueue"}, + {0x00140000, UnregisterInterruptRelayQueue, "UnregisterInterruptRelayQueue"}, {0x00150002, nullptr, "TryAcquireRight"}, - {0x00160042, nullptr, "AcquireRight"}, - {0x00170000, nullptr, "ReleaseRight"}, + {0x00160042, AcquireRight, "AcquireRight"}, + {0x00170000, ReleaseRight, "ReleaseRight"}, {0x00180000, ImportDisplayCaptureInfo, "ImportDisplayCaptureInfo"}, {0x00190000, nullptr, "SaveVramSysArea"}, {0x001A0000, nullptr, "RestoreVramSysArea"}, @@ -616,17 +700,16 @@ Interface::Interface() { Register(FunctionTable); g_interrupt_event = nullptr; - - using Kernel::MemoryPermission; - g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, "GSPSharedMem"); + g_shared_memory = nullptr; g_thread_id = 0; + gpu_right_acquired = false; } Interface::~Interface() { g_interrupt_event = nullptr; g_shared_memory = nullptr; + gpu_right_acquired = false; } } // namespace diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index 0e2f7a21e..3b4b678a3 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h @@ -10,6 +10,7 @@ #include "common/bit_field.h" #include "common/common_types.h" +#include "core/hle/result.h" #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -194,7 +195,7 @@ public: */ void SignalInterrupt(InterruptId interrupt_id); -void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); +ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); /** * Retrieves the framebuffer info stored in the GSP shared memory for the diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index cb4fd38e2..1053d0f40 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -33,6 +33,11 @@ static Kernel::SharedPtr<Kernel::Event> event_debug_pad; static u32 next_pad_index; static u32 next_touch_index; +static u32 next_accelerometer_index; +static u32 next_gyroscope_index; + +static int enable_accelerometer_count = 0; // positive means enabled +static int enable_gyroscope_count = 0; // positive means enabled const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> pad_mapping = {{ Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y, @@ -78,17 +83,17 @@ void Update() { PadState changed = { { (state.hex ^ old_state.hex) } }; // Get the current Pad entry - PadDataEntry* pad_entry = &mem->pad.entries[mem->pad.index]; + PadDataEntry& pad_entry = mem->pad.entries[mem->pad.index]; // Update entry properties - pad_entry->current_state.hex = state.hex; - pad_entry->delta_additions.hex = changed.hex & state.hex; - pad_entry->delta_removals.hex = changed.hex & old_state.hex;; + pad_entry.current_state.hex = state.hex; + pad_entry.delta_additions.hex = changed.hex & state.hex; + pad_entry.delta_removals.hex = changed.hex & old_state.hex;; // Set circle Pad - pad_entry->circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS : + pad_entry.circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS : state.circle_right ? MAX_CIRCLEPAD_POS : 0x0; - pad_entry->circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS : + pad_entry.circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS : state.circle_up ? MAX_CIRCLEPAD_POS : 0x0; // If we just updated index 0, provide a new timestamp @@ -101,11 +106,11 @@ void Update() { next_touch_index = (next_touch_index + 1) % mem->touch.entries.size(); // Get the current touch entry - TouchDataEntry* touch_entry = &mem->touch.entries[mem->touch.index]; + TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index]; bool pressed = false; - std::tie(touch_entry->x, touch_entry->y, pressed) = VideoCore::g_emu_window->GetTouchState(); - touch_entry->valid.Assign(pressed ? 1 : 0); + std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState(); + touch_entry.valid.Assign(pressed ? 1 : 0); // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which // supposedly is "Touch-screen entry, which contains the raw coordinate data prior to being @@ -120,6 +125,58 @@ void Update() { // Signal both handles when there's an update to Pad or touch event_pad_or_touch_1->Signal(); event_pad_or_touch_2->Signal(); + + // Update accelerometer + if (enable_accelerometer_count > 0) { + mem->accelerometer.index = next_accelerometer_index; + next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size(); + + AccelerometerDataEntry& accelerometer_entry = mem->accelerometer.entries[mem->accelerometer.index]; + std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) + = VideoCore::g_emu_window->GetAccelerometerState(); + + // Make up "raw" entry + // TODO(wwylele): + // From hardware testing, the raw_entry values are approximately, + // but not exactly, as twice as corresponding entries (or with a minus sign). + // It may caused by system calibration to the accelerometer. + // Figure out how it works, or, if no game reads raw_entry, + // the following three lines can be removed and leave raw_entry unimplemented. + mem->accelerometer.raw_entry.x = -2 * accelerometer_entry.x; + mem->accelerometer.raw_entry.z = 2 * accelerometer_entry.y; + mem->accelerometer.raw_entry.y = -2 * accelerometer_entry.z; + + // If we just updated index 0, provide a new timestamp + if (mem->accelerometer.index == 0) { + mem->accelerometer.index_reset_ticks_previous = mem->accelerometer.index_reset_ticks; + mem->accelerometer.index_reset_ticks = (s64)CoreTiming::GetTicks(); + } + + event_accelerometer->Signal(); + } + + // Update gyroscope + if (enable_gyroscope_count > 0) { + mem->gyroscope.index = next_gyroscope_index; + next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size(); + + GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index]; + std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) + = VideoCore::g_emu_window->GetGyroscopeState(); + + // Make up "raw" entry + mem->gyroscope.raw_entry.x = gyroscope_entry.x; + mem->gyroscope.raw_entry.z = -gyroscope_entry.y; + mem->gyroscope.raw_entry.y = gyroscope_entry.z; + + // If we just updated index 0, provide a new timestamp + if (mem->gyroscope.index == 0) { + mem->gyroscope.index_reset_ticks_previous = mem->gyroscope.index_reset_ticks; + mem->gyroscope.index_reset_ticks = (s64)CoreTiming::GetTicks(); + } + + event_gyroscope->Signal(); + } } void GetIPCHandles(Service::Interface* self) { @@ -139,40 +196,69 @@ void GetIPCHandles(Service::Interface* self) { void EnableAccelerometer(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + ++enable_accelerometer_count; event_accelerometer->Signal(); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); } void DisableAccelerometer(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + --enable_accelerometer_count; event_accelerometer->Signal(); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); } void EnableGyroscopeLow(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + ++enable_gyroscope_count; event_gyroscope->Signal(); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); } void DisableGyroscopeLow(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + --enable_gyroscope_count; event_gyroscope->Signal(); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_HID, "called"); +} + +void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + + f32 coef = VideoCore::g_emu_window->GetGyroscopeRawToDpsCoefficient(); + memcpy(&cmd_buff[2], &coef, 4); +} + +void GetGyroscopeLowCalibrateParam(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + + const s16 param_unit = 6700; // an approximate value taken from hw + GyroscopeCalibrateParam param = { + { 0, param_unit, -param_unit }, + { 0, param_unit, -param_unit }, + { 0, param_unit, -param_unit }, + }; + memcpy(&cmd_buff[2], ¶m, sizeof(param)); + LOG_WARNING(Service_HID, "(STUBBED) called"); } diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 517f4f2ae..170d19ea8 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -78,6 +78,24 @@ struct TouchDataEntry { }; /** + * Structure of a single entry of accelerometer state history within HID shared memory + */ +struct AccelerometerDataEntry { + s16 x; + s16 y; + s16 z; +}; + +/** + * Structure of a single entry of gyroscope state history within HID shared memory + */ +struct GyroscopeDataEntry { + s16 x; + s16 y; + s16 z; +}; + +/** * Structure of data stored in HID shared memory */ struct SharedMem { @@ -112,6 +130,46 @@ struct SharedMem { std::array<TouchDataEntry, 8> entries; ///< Last 8 touch entries, in pixel coordinates } touch; + + /// Accelerometer data + struct { + s64 index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0 + s64 index_reset_ticks_previous; ///< Previous `index_reset_ticks` + u32 index; ///< Index of the last updated accelerometer entry + + INSERT_PADDING_WORDS(0x1); + + AccelerometerDataEntry raw_entry; + INSERT_PADDING_BYTES(2); + + std::array<AccelerometerDataEntry, 8> entries; + } accelerometer; + + /// Gyroscope data + struct { + s64 index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0 + s64 index_reset_ticks_previous; ///< Previous `index_reset_ticks` + u32 index; ///< Index of the last updated accelerometer entry + + INSERT_PADDING_WORDS(0x1); + + GyroscopeDataEntry raw_entry; + INSERT_PADDING_BYTES(2); + + std::array<GyroscopeDataEntry, 32> entries; + } gyroscope; +}; + +/** + * Structure of calibrate params that GetGyroscopeLowCalibrateParam returns + */ +struct GyroscopeCalibrateParam { + struct { + // TODO (wwylele): figure out the exact meaning of these params + s16 zero_point; + s16 positive_unit_point; + s16 negative_unit_point; + } x, y, z; }; // TODO: MSVC does not support using offsetof() on non-static data members even though this @@ -222,6 +280,26 @@ void DisableGyroscopeLow(Interface* self); */ void GetSoundVolume(Interface* self); +/** + * HID::GetGyroscopeLowRawToDpsCoefficient service function + * Inputs: + * None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : float output value + */ +void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self); + +/** + * HID::GetGyroscopeLowCalibrateParam service function + * Inputs: + * None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2~6 (18 bytes) : struct GyroscopeCalibrateParam + */ +void GetGyroscopeLowCalibrateParam(Service::Interface* self); + /// Checks for user input updates void Update(); diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp index c50f597eb..046e65b11 100644 --- a/src/core/hle/service/hid/hid_spvr.cpp +++ b/src/core/hle/service/hid/hid_spvr.cpp @@ -9,16 +9,16 @@ namespace Service { namespace HID { const Interface::FunctionInfo FunctionTable[] = { - {0x000A0000, GetIPCHandles, "GetIPCHandles"}, - {0x000B0000, nullptr, "StartAnalogStickCalibration"}, - {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, - {0x00110000, EnableAccelerometer, "EnableAccelerometer"}, - {0x00120000, DisableAccelerometer, "DisableAccelerometer"}, - {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"}, - {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"}, - {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"}, - {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"}, - {0x00170000, GetSoundVolume, "GetSoundVolume"}, + {0x000A0000, GetIPCHandles, "GetIPCHandles"}, + {0x000B0000, nullptr, "StartAnalogStickCalibration"}, + {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, + {0x00110000, EnableAccelerometer, "EnableAccelerometer"}, + {0x00120000, DisableAccelerometer, "DisableAccelerometer"}, + {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"}, + {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"}, + {0x00150000, GetGyroscopeLowRawToDpsCoefficient, "GetGyroscopeLowRawToDpsCoefficient"}, + {0x00160000, GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"}, + {0x00170000, GetSoundVolume, "GetSoundVolume"}, }; HID_SPVR_Interface::HID_SPVR_Interface() { diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp index bbdde2abb..bb157b83d 100644 --- a/src/core/hle/service/hid/hid_user.cpp +++ b/src/core/hle/service/hid/hid_user.cpp @@ -9,16 +9,16 @@ namespace Service { namespace HID { const Interface::FunctionInfo FunctionTable[] = { - {0x000A0000, GetIPCHandles, "GetIPCHandles"}, - {0x000B0000, nullptr, "StartAnalogStickCalibration"}, - {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, - {0x00110000, EnableAccelerometer, "EnableAccelerometer"}, - {0x00120000, DisableAccelerometer, "DisableAccelerometer"}, - {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"}, - {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"}, - {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"}, - {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"}, - {0x00170000, GetSoundVolume, "GetSoundVolume"}, + {0x000A0000, GetIPCHandles, "GetIPCHandles"}, + {0x000B0000, nullptr, "StartAnalogStickCalibration"}, + {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, + {0x00110000, EnableAccelerometer, "EnableAccelerometer"}, + {0x00120000, DisableAccelerometer, "DisableAccelerometer"}, + {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"}, + {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"}, + {0x00150000, GetGyroscopeLowRawToDpsCoefficient, "GetGyroscopeLowRawToDpsCoefficient"}, + {0x00160000, GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"}, + {0x00170000, GetSoundVolume, "GetSoundVolume"}, }; HID_U_Interface::HID_U_Interface() { diff --git a/src/core/hle/service/ndm/ndm.cpp b/src/core/hle/service/ndm/ndm.cpp index 47076a7b8..bc9c3413d 100644 --- a/src/core/hle/service/ndm/ndm.cpp +++ b/src/core/hle/service/ndm/ndm.cpp @@ -11,28 +11,217 @@ namespace Service { namespace NDM { -void SuspendDaemons(Service::Interface* self) { +enum : u32 { + DEFAULT_RETRY_INTERVAL = 10, + DEFAULT_SCAN_INTERVAL = 30 +}; + +static DaemonMask daemon_bit_mask = DaemonMask::Default; +static DaemonMask default_daemon_bit_mask = DaemonMask::Default; +static std::array<DaemonStatus, 4> daemon_status = { DaemonStatus::Idle, DaemonStatus::Idle, DaemonStatus::Idle, DaemonStatus::Idle }; +static ExclusiveState exclusive_state = ExclusiveState::None; +static u32 scan_interval = DEFAULT_SCAN_INTERVAL; +static u32 retry_interval = DEFAULT_RETRY_INTERVAL; +static bool daemon_lock_enabled = false; + +void EnterExclusiveState(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + exclusive_state = static_cast<ExclusiveState>(cmd_buff[1]); + + cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state); +} + +void LeaveExclusiveState(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + exclusive_state = ExclusiveState::None; + + cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state); +} + +void QueryExclusiveMode(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_WARNING(Service_NDM, "(STUBBED) bit_mask=0x%08X ", cmd_buff[1]); + cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = static_cast<u32>(exclusive_state); + LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state); +} + +void LockState(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + daemon_lock_enabled = true; + + cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) daemon_lock_enabled=0x%08X ", daemon_lock_enabled); +} + +void UnlockState(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + daemon_lock_enabled = false; + cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) daemon_lock_enabled=0x%08X ", daemon_lock_enabled); +} + +void SuspendDaemons(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 bit_mask = cmd_buff[1] & 0xF; + daemon_bit_mask = static_cast<DaemonMask>(static_cast<u32>(default_daemon_bit_mask) & ~bit_mask); + for (size_t index = 0; index < daemon_status.size(); ++index) { + if (bit_mask & (1 << index)) { + daemon_status[index] = DaemonStatus::Suspended; + } + } + + cmd_buff[0] = IPC::MakeHeader(0x6, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X ", daemon_bit_mask); } void ResumeDaemons(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 bit_mask = cmd_buff[1] & 0xF; + daemon_bit_mask = static_cast<DaemonMask>(static_cast<u32>(daemon_bit_mask) | bit_mask); + for (size_t index = 0; index < daemon_status.size(); ++index) { + if (bit_mask & (1 << index)) { + daemon_status[index] = DaemonStatus::Idle; + } + } + + cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X ", daemon_bit_mask); +} + +void SuspendScheduler(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x8, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) called"); +} + +void ResumeScheduler(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) called"); +} + +void QueryStatus(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 daemon = cmd_buff[1] & 0xF; - LOG_WARNING(Service_NDM, "(STUBBED) bit_mask=0x%08X ", cmd_buff[1]); + cmd_buff[0] = IPC::MakeHeader(0xD, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = static_cast<u32>(daemon_status.at(daemon)); + LOG_WARNING(Service_NDM, "(STUBBED) daemon=0x%08X, daemon_status=0x%08X", daemon, cmd_buff[2]); +} + +void GetDaemonDisableCount(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 daemon = cmd_buff[1] & 0xF; + + cmd_buff[0] = IPC::MakeHeader(0xE, 3, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0; + cmd_buff[3] = 0; + LOG_WARNING(Service_NDM, "(STUBBED) daemon=0x%08X", daemon); +} + +void GetSchedulerDisableCount(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0xF, 3, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0; + cmd_buff[3] = 0; + LOG_WARNING(Service_NDM, "(STUBBED) called"); +} + +void SetScanInterval(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + scan_interval = cmd_buff[1]; + cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X ", scan_interval); +} + +void GetScanInterval(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x11, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = scan_interval; + LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X ", scan_interval); +} + +void SetRetryInterval(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + retry_interval = cmd_buff[1]; + + cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X ", retry_interval); +} + +void GetRetryInterval(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x13, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = retry_interval; + LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X ", retry_interval); } void OverrideDefaultDaemons(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 bit_mask = cmd_buff[1] & 0xF; + default_daemon_bit_mask = static_cast<DaemonMask>(bit_mask); + daemon_bit_mask = default_daemon_bit_mask; + for (size_t index = 0; index < daemon_status.size(); ++index) { + if (bit_mask & (1 << index)) { + daemon_status[index] = DaemonStatus::Idle; + } + } - LOG_WARNING(Service_NDM, "(STUBBED) bit_mask=0x%08X ", cmd_buff[1]); + cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask); +} + +void ResetDefaultDaemons(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + default_daemon_bit_mask = DaemonMask::Default; + + cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask); +} + +void GetDefaultDaemons(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = static_cast<u32>(default_daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask); +} + +void ClearHalfAwakeMacFilter(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x17, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_NDM, "(STUBBED) called"); } void Init() { diff --git a/src/core/hle/service/ndm/ndm.h b/src/core/hle/service/ndm/ndm.h index 734730f8c..5c2b968dc 100644 --- a/src/core/hle/service/ndm/ndm.h +++ b/src/core/hle/service/ndm/ndm.h @@ -12,10 +12,91 @@ class Interface; namespace NDM { +enum class Daemon : u32 { + Cec = 0, + Boss = 1, + Nim = 2, + Friend = 3 +}; + +enum class DaemonMask : u32 { + None = 0, + Cec = (1 << static_cast<u32>(Daemon::Cec)), + Boss = (1 << static_cast<u32>(Daemon::Boss)), + Nim = (1 << static_cast<u32>(Daemon::Nim)), + Friend = (1 << static_cast<u32>(Daemon::Friend)), + Default = Cec | Friend, + All = Cec | Boss | Nim | Friend +}; + +enum class DaemonStatus : u32 { + Busy = 0, + Idle = 1, + Suspending = 2, + Suspended = 3 +}; + +enum class ExclusiveState : u32 { + None = 0, + Infrastructure = 1, + LocalCommunications = 2, + Streetpass = 3, + StreetpassData = 4, +}; + +/** + * NDM::EnterExclusiveState service function + * Inputs: + * 0 : Header code [0x00010042] + * 1 : Exclusive State + * 2 : 0x20 + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void EnterExclusiveState(Service::Interface* self); + +/** + * NDM::LeaveExclusiveState service function + * Inputs: + * 0 : Header code [0x00020002] + * 1 : 0x20 + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void LeaveExclusiveState(Service::Interface* self); + +/** + * NDM::QueryExclusiveMode service function + * Inputs: + * 0 : Header code [0x00030000] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Current Exclusive State + */ +void QueryExclusiveMode(Service::Interface* self); + +/** + * NDM::LockState service function + * Inputs: + * 0 : Header code [0x00040002] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void LockState(Service::Interface* self); + +/** + * NDM::UnlockState service function + * Inputs: + * 0 : Header code [0x00050002] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void UnlockState(Service::Interface* self); + /** - * SuspendDaemons + * NDM::SuspendDaemons service function * Inputs: - * 0 : Command header (0x00020082) + * 0 : Header code [0x00060040] * 1 : Daemon bit mask * Outputs: * 1 : Result, 0 on success, otherwise error code @@ -23,9 +104,9 @@ namespace NDM { void SuspendDaemons(Service::Interface* self); /** - * ResumeDaemons + * NDM::ResumeDaemons service function * Inputs: - * 0 : Command header (0x00020082) + * 0 : Header code [0x00070040] * 1 : Daemon bit mask * Outputs: * 1 : Result, 0 on success, otherwise error code @@ -33,15 +114,138 @@ void SuspendDaemons(Service::Interface* self); void ResumeDaemons(Service::Interface* self); /** - * OverrideDefaultDaemons + * NDM::SuspendScheduler service function * Inputs: - * 0 : Command header (0x00020082) + * 0 : Header code [0x00080040] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void SuspendScheduler(Service::Interface* self); + +/** + * NDM::ResumeScheduler service function + * Inputs: + * 0 : Header code [0x00090000] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void ResumeScheduler(Service::Interface* self); + +/** + * NDM::QueryStatus service function + * Inputs: + * 0 : Header code [0x000D0040] + * 1 : Daemon + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Daemon status + */ +void QueryStatus(Service::Interface* self); + +/** + * NDM::GetDaemonDisableCount service function + * Inputs: + * 0 : Header code [0x000E0040] + * 1 : Daemon + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Current process disable count + * 3 : Total disable count + */ +void GetDaemonDisableCount(Service::Interface* self); + +/** + * NDM::GetSchedulerDisableCount service function + * Inputs: + * 0 : Header code [0x000F0000] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Current process disable count + * 3 : Total disable count + */ +void GetSchedulerDisableCount(Service::Interface* self); + +/** + * NDM::SetScanInterval service function + * Inputs: + * 0 : Header code [0x00100040] + * 1 : Interval (default = 30) + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void SetScanInterval(Service::Interface* self); + +/** + * NDM::GetScanInterval service function + * Inputs: + * 0 : Header code [0x00110000] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Interval (default = 30) + */ +void GetScanInterval(Service::Interface* self); + +/** + * NDM::SetRetryInterval service function + * Inputs: + * 0 : Header code [0x00120040] + * 1 : Interval (default = 10) + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void SetRetryInterval(Service::Interface* self); + +/** + * NDM::GetRetryInterval service function + * Inputs: + * 0 : Header code [0x00130000] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Interval (default = 10) + */ +void GetRetryInterval(Service::Interface* self); + + +/** + * NDM::OverrideDefaultDaemons service function + * Inputs: + * 0 : Header code [0x00140040] * 1 : Daemon bit mask * Outputs: * 1 : Result, 0 on success, otherwise error code */ void OverrideDefaultDaemons(Service::Interface* self); +/** + * NDM::ResetDefaultDaemons service function + * Inputs: + * 0 : Header code [0x00150000] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void ResetDefaultDaemons(Service::Interface* self); + +/** + * NDM::GetDefaultDaemons service function + * Inputs: + * 0 : Header code [0x00160000] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Daemon bit mask + * Note: + * Gets the current default daemon bit mask. The default value is (DAEMONMASK_CEC | DAEMONMASK_FRIENDS) + */ +void GetDefaultDaemons(Service::Interface* self); + +/** + * NDM::ClearHalfAwakeMacFilter service function + * Inputs: + * 0 : Header code [0x00170000] + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ +void ClearHalfAwakeMacFilter(Service::Interface* self); + /// Initialize NDM service void Init(); diff --git a/src/core/hle/service/ndm/ndm_u.cpp b/src/core/hle/service/ndm/ndm_u.cpp index bf95cc7aa..3ff0744ee 100644 --- a/src/core/hle/service/ndm/ndm_u.cpp +++ b/src/core/hle/service/ndm/ndm_u.cpp @@ -9,29 +9,29 @@ namespace Service { namespace NDM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010042, nullptr, "EnterExclusiveState"}, - {0x00020002, nullptr, "LeaveExclusiveState"}, - {0x00030000, nullptr, "QueryExclusiveMode"}, - {0x00040002, nullptr, "LockState"}, - {0x00050002, nullptr, "UnlockState"}, + {0x00010042, EnterExclusiveState, "EnterExclusiveState"}, + {0x00020002, LeaveExclusiveState, "LeaveExclusiveState"}, + {0x00030000, QueryExclusiveMode, "QueryExclusiveMode"}, + {0x00040002, LockState, "LockState"}, + {0x00050002, UnlockState, "UnlockState"}, {0x00060040, SuspendDaemons, "SuspendDaemons"}, {0x00070040, ResumeDaemons, "ResumeDaemons"}, - {0x00080040, nullptr, "DisableWifiUsage"}, - {0x00090000, nullptr, "EnableWifiUsage"}, + {0x00080040, SuspendScheduler, "SuspendScheduler"}, + {0x00090000, ResumeScheduler, "ResumeScheduler"}, {0x000A0000, nullptr, "GetCurrentState"}, {0x000B0000, nullptr, "GetTargetState"}, {0x000C0000, nullptr, "<Stubbed>"}, - {0x000D0040, nullptr, "QueryStatus"}, - {0x000E0040, nullptr, "GetDaemonDisableCount"}, - {0x000F0000, nullptr, "GetSchedulerDisableCount"}, - {0x00100040, nullptr, "SetScanInterval"}, - {0x00110000, nullptr, "GetScanInterval"}, - {0x00120040, nullptr, "SetRetryInterval"}, - {0x00130000, nullptr, "GetRetryInterval"}, + {0x000D0040, QueryStatus, "QueryStatus"}, + {0x000E0040, GetDaemonDisableCount, "GetDaemonDisableCount"}, + {0x000F0000, GetSchedulerDisableCount,"GetSchedulerDisableCount"}, + {0x00100040, SetScanInterval, "SetScanInterval"}, + {0x00110000, GetScanInterval, "GetScanInterval"}, + {0x00120040, SetRetryInterval, "SetRetryInterval"}, + {0x00130000, GetRetryInterval, "GetRetryInterval"}, {0x00140040, OverrideDefaultDaemons, "OverrideDefaultDaemons"}, - {0x00150000, nullptr, "ResetDefaultDaemons"}, - {0x00160000, nullptr, "GetDefaultDaemons"}, - {0x00170000, nullptr, "ClearHalfAwakeMacFilter"}, + {0x00150000, ResetDefaultDaemons, "ResetDefaultDaemons"}, + {0x00160000, GetDefaultDaemons, "GetDefaultDaemons"}, + {0x00170000, ClearHalfAwakeMacFilter, "ClearHalfAwakeMacFilter"}, }; NDM_U_Interface::NDM_U_Interface() { diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 35b648409..0fe3a4d7a 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -9,6 +9,7 @@ #include "core/hle/service/ac_u.h" #include "core/hle/service/act_u.h" #include "core/hle/service/csnd_snd.h" +#include "core/hle/service/dlp_srvr.h" #include "core/hle/service/dsp_dsp.h" #include "core/hle/service/err_f.h" #include "core/hle/service/gsp_gpu.h" @@ -70,9 +71,8 @@ ResultVal<bool> Interface::SyncRequest() { // TODO(bunnei): Hack - ignore error cmd_buff[1] = 0; return MakeResult<bool>(false); - } else { - LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); } + LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); itr->second.func(this); @@ -121,6 +121,7 @@ void Init() { AddService(new AC_U::Interface); AddService(new ACT_U::Interface); AddService(new CSND_SND::Interface); + AddService(new DLP_SRVR::Interface); AddService(new DSP_DSP::Interface); AddService(new GSP_GPU::Interface); AddService(new GSP_LCD::Interface); diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index b52e52d4a..d3e5d4bca 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -5,6 +5,7 @@ #include <algorithm> #include <cstring> #include <unordered_map> +#include <vector> #include "common/assert.h" #include "common/bit_field.h" @@ -150,6 +151,34 @@ static int TranslateError(int error) { return error; } +/// Holds the translation from system network socket options to 3DS network socket options +/// Note: -1 = No effect/unavailable +static const std::unordered_map<int, int> sockopt_map = { { + { 0x0004, SO_REUSEADDR }, + { 0x0080, -1 }, + { 0x0100, -1 }, + { 0x1001, SO_SNDBUF }, + { 0x1002, SO_RCVBUF }, + { 0x1003, -1 }, +#ifdef _WIN32 + /// Unsupported in WinSock2 + { 0x1004, -1 }, +#else + { 0x1004, SO_RCVLOWAT }, +#endif + { 0x1008, SO_TYPE }, + { 0x1009, SO_ERROR }, +}}; + +/// Converts a socket option from 3ds-specific to platform-specific +static int TranslateSockOpt(int console_opt_name) { + auto found = sockopt_map.find(console_opt_name); + if (found != sockopt_map.end()) { + return found->second; + } + return console_opt_name; +} + /// Holds information about a particular socket struct SocketHolder { u32 socket_fd; ///< The socket descriptor @@ -567,7 +596,7 @@ static void RecvFrom(Service::Interface* self) { socklen_t src_addr_len = sizeof(src_addr); int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len); - if (buffer_parameters.output_src_address_buffer != 0) { + if (ret >= 0 && buffer_parameters.output_src_address_buffer != 0 && src_addr_len > 0) { CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(buffer_parameters.output_src_address_buffer)); *ctr_src_addr = CTRSockAddr::FromPlatform(src_addr); } @@ -593,17 +622,13 @@ static void Poll(Service::Interface* self) { // The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes) // so we have to copy the data - pollfd* platform_pollfd = new pollfd[nfds]; - for (unsigned current_fds = 0; current_fds < nfds; ++current_fds) - platform_pollfd[current_fds] = CTRPollFD::ToPlatform(input_fds[current_fds]); + std::vector<pollfd> platform_pollfd(nfds); + std::transform(input_fds, input_fds + nfds, platform_pollfd.begin(), CTRPollFD::ToPlatform); - int ret = ::poll(platform_pollfd, nfds, timeout); + const int ret = ::poll(platform_pollfd.data(), nfds, timeout); // Now update the output pollfd structure - for (unsigned current_fds = 0; current_fds < nfds; ++current_fds) - output_fds[current_fds] = CTRPollFD::FromPlatform(platform_pollfd[current_fds]); - - delete[] platform_pollfd; + std::transform(platform_pollfd.begin(), platform_pollfd.end(), output_fds, CTRPollFD::FromPlatform); int result = 0; if (ret == SOCKET_ERROR_VALUE) @@ -727,6 +752,72 @@ static void ShutdownSockets(Service::Interface* self) { cmd_buffer[1] = 0; } +static void GetSockOpt(Service::Interface* self) { + u32* cmd_buffer = Kernel::GetCommandBuffer(); + u32 socket_handle = cmd_buffer[1]; + u32 level = cmd_buffer[2]; + int optname = TranslateSockOpt(cmd_buffer[3]); + socklen_t optlen = (socklen_t)cmd_buffer[4]; + + int ret = -1; + int err = 0; + + if(optname < 0) { +#ifdef _WIN32 + err = WSAEINVAL; +#else + err = EINVAL; +#endif + } else { + // 0x100 = static buffer offset (bytes) + // + 0x4 = 2nd pointer (u32) position + // >> 2 = convert to u32 offset instead of byte offset (cmd_buffer = u32*) + char* optval = reinterpret_cast<char *>(Memory::GetPointer(cmd_buffer[0x104 >> 2])); + + ret = ::getsockopt(socket_handle, level, optname, optval, &optlen); + err = 0; + if (ret == SOCKET_ERROR_VALUE) { + err = TranslateError(GET_ERRNO); + } + } + + cmd_buffer[0] = IPC::MakeHeader(0x11, 4, 2); + cmd_buffer[1] = ret; + cmd_buffer[2] = err; + cmd_buffer[3] = optlen; +} + +static void SetSockOpt(Service::Interface* self) { + u32* cmd_buffer = Kernel::GetCommandBuffer(); + u32 socket_handle = cmd_buffer[1]; + u32 level = cmd_buffer[2]; + int optname = TranslateSockOpt(cmd_buffer[3]); + + int ret = -1; + int err = 0; + + if(optname < 0) { +#ifdef _WIN32 + err = WSAEINVAL; +#else + err = EINVAL; +#endif + } else { + socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]); + const char* optval = reinterpret_cast<const char *>(Memory::GetPointer(cmd_buffer[8])); + + ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen)); + err = 0; + if (ret == SOCKET_ERROR_VALUE) { + err = TranslateError(GET_ERRNO); + } + } + + cmd_buffer[0] = IPC::MakeHeader(0x12, 4, 4); + cmd_buffer[1] = ret; + cmd_buffer[2] = err; +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010044, InitializeSockets, "InitializeSockets"}, {0x000200C2, Socket, "Socket"}, @@ -744,8 +835,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000E00C2, nullptr, "GetHostByAddr"}, {0x000F0106, nullptr, "GetAddrInfo"}, {0x00100102, nullptr, "GetNameInfo"}, - {0x00110102, nullptr, "GetSockOpt"}, - {0x00120104, nullptr, "SetSockOpt"}, + {0x00110102, GetSockOpt, "GetSockOpt"}, + {0x00120104, SetSockOpt, "SetSockOpt"}, {0x001300C2, Fcntl, "Fcntl"}, {0x00140084, Poll, "Poll"}, {0x00150042, nullptr, "SockAtMark"}, diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 22f373adf..d16578f87 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -4,6 +4,7 @@ #include <cstring> +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -12,9 +13,6 @@ #include "core/hle/service/y2r_u.h" #include "core/hw/y2r.h" -#include "video_core/renderer_base.h" -#include "video_core/video_core.h" - //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Y2R_U @@ -28,13 +26,17 @@ struct ConversionParameters { u16 input_line_width; u16 input_lines; StandardCoefficient standard_coefficient; - u8 reserved; + u8 padding; u16 alpha; }; static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct has incorrect size"); static Kernel::SharedPtr<Kernel::Event> completion_event; static ConversionConfiguration conversion; +static DitheringWeightParams dithering_weight_params; +static u32 temporal_dithering_enabled = 0; +static u32 transfer_end_interrupt_enabled = 0; +static u32 spacial_dithering_enabled = 0; static const CoefficientSet standard_coefficients[4] = { {{ 0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B }}, // ITU_Rec601 @@ -73,7 +75,7 @@ ResultCode ConversionConfiguration::SetInputLines(u16 lines) { ResultCode ConversionConfiguration::SetStandardCoefficient(StandardCoefficient standard_coefficient) { size_t index = static_cast<size_t>(standard_coefficient); - if (index >= 4) { + if (index >= ARRAY_SIZE(standard_coefficients)) { return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::CAM, ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053ED } @@ -86,44 +88,183 @@ static void SetInputFormat(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.input_format = static_cast<InputFormat>(cmd_buff[1]); + + cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); +} + +static void GetInputFormat(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(conversion.input_format); + + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); } static void SetOutputFormat(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.output_format = static_cast<OutputFormat>(cmd_buff[1]); + + cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); +} + +static void GetOutputFormat(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x4, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(conversion.output_format); + + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); } static void SetRotation(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.rotation = static_cast<Rotation>(cmd_buff[1]); + + cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); +} + +static void GetRotation(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x6, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(conversion.rotation); + + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); } static void SetBlockAlignment(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]); - LOG_DEBUG(Service_Y2R, "called alignment=%hhu", conversion.block_alignment); + cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); +} + +static void GetBlockAlignment(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(conversion.block_alignment); + + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); +} + +/** + * Y2R_U::SetSpacialDithering service function + * Inputs: + * 1 : u8, 0 = Disabled, 1 = Enabled + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetSpacialDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + spacial_dithering_enabled = cmd_buff[1] & 0xF; + + cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::GetSpacialDithering service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : u8, 0 = Disabled, 1 = Enabled + */ +static void GetSpacialDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = spacial_dithering_enabled; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::SetTemporalDithering service function + * Inputs: + * 1 : u8, 0 = Disabled, 1 = Enabled + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetTemporalDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + temporal_dithering_enabled = cmd_buff[1] & 0xF; + + cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } +/** + * Y2R_U::GetTemporalDithering service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : u8, 0 = Disabled, 1 = Enabled + */ +static void GetTemporalDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = temporal_dithering_enabled; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::SetTransferEndInterrupt service function + * Inputs: + * 1 : u8, 0 = Disabled, 1 = Enabled + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ static void SetTransferEndInterrupt(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + transfer_end_interrupt_enabled = cmd_buff[1] & 0xf; cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "(STUBBED) called"); + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::GetTransferEndInterrupt service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : u8, 0 = Disabled, 1 = Enabled + */ +static void GetTransferEndInterrupt(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0xE, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = transfer_end_interrupt_enabled; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } /** @@ -135,8 +276,10 @@ static void SetTransferEndInterrupt(Service::Interface* self) { static void GetTransferEndEvent(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); + LOG_DEBUG(Service_Y2R, "called"); } @@ -147,12 +290,12 @@ static void SetSendingY(Service::Interface* self) { conversion.src_Y.image_size = cmd_buff[2]; conversion.src_Y.transfer_unit = cmd_buff[3]; conversion.src_Y.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_Y.image_size, - conversion.src_Y.transfer_unit, conversion.src_Y.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_Y.image_size, conversion.src_Y.transfer_unit, conversion.src_Y.gap, cmd_buff[6]); } static void SetSendingU(Service::Interface* self) { @@ -162,12 +305,12 @@ static void SetSendingU(Service::Interface* self) { conversion.src_U.image_size = cmd_buff[2]; conversion.src_U.transfer_unit = cmd_buff[3]; conversion.src_U.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_U.image_size, - conversion.src_U.transfer_unit, conversion.src_U.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x11, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_U.image_size, conversion.src_U.transfer_unit, conversion.src_U.gap, cmd_buff[6]); } static void SetSendingV(Service::Interface* self) { @@ -177,12 +320,12 @@ static void SetSendingV(Service::Interface* self) { conversion.src_V.image_size = cmd_buff[2]; conversion.src_V.transfer_unit = cmd_buff[3]; conversion.src_V.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_V.image_size, - conversion.src_V.transfer_unit, conversion.src_V.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_V.image_size, conversion.src_V.transfer_unit, conversion.src_V.gap, cmd_buff[6]); } static void SetSendingYUYV(Service::Interface* self) { @@ -192,12 +335,76 @@ static void SetSendingYUYV(Service::Interface* self) { conversion.src_YUYV.image_size = cmd_buff[2]; conversion.src_YUYV.transfer_unit = cmd_buff[3]; conversion.src_YUYV.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_YUYV.image_size, - conversion.src_YUYV.transfer_unit, conversion.src_YUYV.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_YUYV.image_size, conversion.src_YUYV.transfer_unit, conversion.src_YUYV.gap, cmd_buff[6]); +} + +/** + * Y2R::IsFinishedSendingYuv service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingYuv(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x14, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R::IsFinishedSendingY service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingY(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x15, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R::IsFinishedSendingU service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingU(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R::IsFinishedSendingV service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingV(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x17, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } static void SetReceiving(Service::Interface* self) { @@ -207,27 +414,66 @@ static void SetReceiving(Service::Interface* self) { conversion.dst.image_size = cmd_buff[2]; conversion.dst.transfer_unit = cmd_buff[3]; conversion.dst.gap = cmd_buff[4]; - u32 dst_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "dst_process_handle=0x%08X", conversion.dst.image_size, - conversion.dst.transfer_unit, conversion.dst.gap, - dst_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x18, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, dst_process_handle=0x%08X", + conversion.dst.image_size, conversion.dst.transfer_unit, conversion.dst.gap, cmd_buff[6]); +} + +/** + * Y2R::IsFinishedReceiving service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedReceiving(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x19, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } static void SetInputLineWidth(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); + cmd_buff[0] = IPC::MakeHeader(0x1A, 1, 0); cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw; + + LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); +} + +static void GetInputLineWidth(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x1B, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = conversion.input_line_width; + + LOG_DEBUG(Service_Y2R, "called input_line_width=%u", conversion.input_line_width); } static void SetInputLines(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_DEBUG(Service_Y2R, "called input_line_number=%u", cmd_buff[1]); + cmd_buff[0] = IPC::MakeHeader(0x1C, 1, 0); cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw; + + LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]); +} + +static void GetInputLines(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x1D, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(conversion.input_lines); + + LOG_DEBUG(Service_Y2R, "called input_lines=%u", conversion.input_lines); } static void SetCoefficient(Service::Interface* self) { @@ -235,45 +481,111 @@ static void SetCoefficient(Service::Interface* self) { const u16* coefficients = reinterpret_cast<const u16*>(&cmd_buff[1]); std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet)); + + cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]", coefficients[0], coefficients[1], coefficients[2], coefficients[3], coefficients[4], coefficients[5], coefficients[6], coefficients[7]); +} +static void GetCoefficient(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x1F, 5, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], conversion.coefficients.data(), sizeof(CoefficientSet)); + + LOG_DEBUG(Service_Y2R, "called"); } static void SetStandardCoefficient(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", cmd_buff[1]); + u32 index = cmd_buff[1]; + + cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); + cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)index).raw; + + LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", index); +} + +static void GetStandardCoefficient(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 index = cmd_buff[1]; + + if (index < ARRAY_SIZE(standard_coefficients)) { + cmd_buff[0] = IPC::MakeHeader(0x21, 5, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], &standard_coefficients[index], sizeof(CoefficientSet)); - cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)cmd_buff[1]).raw; + LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u ", index); + } else { + cmd_buff[0] = IPC::MakeHeader(0x21, 1, 0); + cmd_buff[1] = -1; // TODO(bunnei): Identify the correct error code for this + + LOG_ERROR(Service_Y2R, "called standard_coefficient=%u The argument is invalid!", index); + } } static void SetAlpha(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.alpha = cmd_buff[1]; + + cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); +} + +static void GetAlpha(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x23, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = conversion.alpha; + + LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); } -static void StartConversion(Service::Interface* self) { +static void SetDitheringWeightParams(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + std::memcpy(&dithering_weight_params, &cmd_buff[1], sizeof(DitheringWeightParams)); - HW::Y2R::PerformConversion(conversion); + cmd_buff[0] = IPC::MakeHeader(0x24, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; - // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( - u32 total_output_size = conversion.input_lines * - (conversion.dst.transfer_unit + conversion.dst.gap); - VideoCore::g_renderer->Rasterizer()->InvalidateRegion( - Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); + LOG_DEBUG(Service_Y2R, "called"); +} + +static void GetDitheringWeightParams(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x25, 9, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], &dithering_weight_params, sizeof(DitheringWeightParams)); LOG_DEBUG(Service_Y2R, "called"); +} + +static void StartConversion(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( + u32 total_output_size = conversion.input_lines * (conversion.dst.transfer_unit + conversion.dst.gap); + Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); + + HW::Y2R::PerformConversion(conversion); + completion_event->Signal(); + cmd_buff[0] = IPC::MakeHeader(0x26, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called"); } static void StopConversion(Service::Interface* self) { @@ -281,6 +593,7 @@ static void StopConversion(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x27, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called"); } @@ -293,50 +606,61 @@ static void StopConversion(Service::Interface* self) { static void IsBusyConversion(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x28, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; // StartConversion always finishes immediately + LOG_DEBUG(Service_Y2R, "called"); } /** - * Y2R_U::SetConversionParams service function + * Y2R_U::SetPackageParameter service function */ -static void SetConversionParams(Service::Interface* self) { +static void SetPackageParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]); - LOG_DEBUG(Service_Y2R, - "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " - "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu " - "reserved=%hhu alpha=%hX", - params->input_format, params->output_format, params->rotation, params->block_alignment, - params->input_line_width, params->input_lines, params->standard_coefficient, - params->reserved, params->alpha); - - ResultCode result = RESULT_SUCCESS; conversion.input_format = params->input_format; conversion.output_format = params->output_format; conversion.rotation = params->rotation; conversion.block_alignment = params->block_alignment; - result = conversion.SetInputLineWidth(params->input_line_width); - if (result.IsError()) goto cleanup; + + ResultCode result = conversion.SetInputLineWidth(params->input_line_width); + + if (result.IsError()) + goto cleanup; + result = conversion.SetInputLines(params->input_lines); - if (result.IsError()) goto cleanup; + + if (result.IsError()) + goto cleanup; + result = conversion.SetStandardCoefficient(params->standard_coefficient); - if (result.IsError()) goto cleanup; + + if (result.IsError()) + goto cleanup; + + conversion.padding = params->padding; conversion.alpha = params->alpha; cleanup: cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0); cmd_buff[1] = result.raw; + + LOG_DEBUG(Service_Y2R, "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " + "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX", + params->input_format, params->output_format, params->rotation, params->block_alignment, + params->input_line_width, params->input_lines, params->standard_coefficient, params->padding, params->alpha); } static void PingProcess(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x2A, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -362,6 +686,7 @@ static void DriverInitialize(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x2B, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called"); } @@ -370,54 +695,67 @@ static void DriverFinalize(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x2C, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called"); +} + + +static void GetPackageParameter(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x2D, 4, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], &conversion, sizeof(ConversionParameters)); + LOG_DEBUG(Service_Y2R, "called"); } const Interface::FunctionInfo FunctionTable[] = { {0x00010040, SetInputFormat, "SetInputFormat"}, - {0x00020000, nullptr, "GetInputFormat"}, + {0x00020000, GetInputFormat, "GetInputFormat"}, {0x00030040, SetOutputFormat, "SetOutputFormat"}, - {0x00040000, nullptr, "GetOutputFormat"}, + {0x00040000, GetOutputFormat, "GetOutputFormat"}, {0x00050040, SetRotation, "SetRotation"}, - {0x00060000, nullptr, "GetRotation"}, + {0x00060000, GetRotation, "GetRotation"}, {0x00070040, SetBlockAlignment, "SetBlockAlignment"}, - {0x00080000, nullptr, "GetBlockAlignment"}, - {0x00090040, nullptr, "SetSpacialDithering"}, - {0x000A0000, nullptr, "GetSpacialDithering"}, - {0x000B0040, nullptr, "SetTemporalDithering"}, - {0x000C0000, nullptr, "GetTemporalDithering"}, + {0x00080000, GetBlockAlignment, "GetBlockAlignment"}, + {0x00090040, SetSpacialDithering, "SetSpacialDithering"}, + {0x000A0000, GetSpacialDithering, "GetSpacialDithering"}, + {0x000B0040, SetTemporalDithering, "SetTemporalDithering"}, + {0x000C0000, GetTemporalDithering, "GetTemporalDithering"}, {0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"}, + {0x000E0000, GetTransferEndInterrupt, "GetTransferEndInterrupt"}, {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"}, {0x00100102, SetSendingY, "SetSendingY"}, {0x00110102, SetSendingU, "SetSendingU"}, {0x00120102, SetSendingV, "SetSendingV"}, {0x00130102, SetSendingYUYV, "SetSendingYUYV"}, - {0x00140000, nullptr, "IsFinishedSendingYuv"}, - {0x00150000, nullptr, "IsFinishedSendingY"}, - {0x00160000, nullptr, "IsFinishedSendingU"}, - {0x00170000, nullptr, "IsFinishedSendingV"}, + {0x00140000, IsFinishedSendingYuv, "IsFinishedSendingYuv"}, + {0x00150000, IsFinishedSendingY, "IsFinishedSendingY"}, + {0x00160000, IsFinishedSendingU, "IsFinishedSendingU"}, + {0x00170000, IsFinishedSendingV, "IsFinishedSendingV"}, {0x00180102, SetReceiving, "SetReceiving"}, - {0x00190000, nullptr, "IsFinishedReceiving"}, + {0x00190000, IsFinishedReceiving, "IsFinishedReceiving"}, {0x001A0040, SetInputLineWidth, "SetInputLineWidth"}, - {0x001B0000, nullptr, "GetInputLineWidth"}, + {0x001B0000, GetInputLineWidth, "GetInputLineWidth"}, {0x001C0040, SetInputLines, "SetInputLines"}, - {0x001D0000, nullptr, "GetInputLines"}, + {0x001D0000, GetInputLines, "GetInputLines"}, {0x001E0100, SetCoefficient, "SetCoefficient"}, - {0x001F0000, nullptr, "GetCoefficient"}, + {0x001F0000, GetCoefficient, "GetCoefficient"}, {0x00200040, SetStandardCoefficient, "SetStandardCoefficient"}, - {0x00210040, nullptr, "GetStandardCoefficientParams"}, + {0x00210040, GetStandardCoefficient, "GetStandardCoefficient"}, {0x00220040, SetAlpha, "SetAlpha"}, - {0x00230000, nullptr, "GetAlpha"}, - {0x00240200, nullptr, "SetDitheringWeightParams"}, - {0x00250000, nullptr, "GetDitheringWeightParams"}, + {0x00230000, GetAlpha, "GetAlpha"}, + {0x00240200, SetDitheringWeightParams,"SetDitheringWeightParams"}, + {0x00250000, GetDitheringWeightParams,"GetDitheringWeightParams"}, {0x00260000, StartConversion, "StartConversion"}, {0x00270000, StopConversion, "StopConversion"}, {0x00280000, IsBusyConversion, "IsBusyConversion"}, - {0x002901C0, SetConversionParams, "SetConversionParams"}, + {0x002901C0, SetPackageParameter, "SetPackageParameter"}, {0x002A0000, PingProcess, "PingProcess"}, {0x002B0000, DriverInitialize, "DriverInitialize"}, {0x002C0000, DriverFinalize, "DriverFinalize"}, - {0x002D0000, nullptr, "GetPackageParameter"}, + {0x002D0000, GetPackageParameter, "GetPackageParameter"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index 3965a5545..95fa2fdb7 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h @@ -97,6 +97,7 @@ struct ConversionConfiguration { u16 input_line_width; u16 input_lines; CoefficientSet coefficients; + u8 padding; u16 alpha; /// Input parameters for the Y (luma) plane @@ -109,6 +110,25 @@ struct ConversionConfiguration { ResultCode SetStandardCoefficient(StandardCoefficient standard_coefficient); }; +struct DitheringWeightParams { + u16 w0_xEven_yEven; + u16 w0_xOdd_yEven; + u16 w0_xEven_yOdd; + u16 w0_xOdd_yOdd; + u16 w1_xEven_yEven; + u16 w1_xOdd_yEven; + u16 w1_xEven_yOdd; + u16 w1_xOdd_yOdd; + u16 w2_xEven_yEven; + u16 w2_xOdd_yEven; + u16 w2_xEven_yOdd; + u16 w2_xOdd_yOdd; + u16 w3_xEven_yEven; + u16 w3_xOdd_yEven; + u16 w3_xEven_yOdd; + u16 w3_xOdd_yOdd; +}; + class Interface : public Service::Interface { public: Interface(); diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp index 50c5bc01b..2a1caeaac 100644 --- a/src/core/hle/shared_page.cpp +++ b/src/core/hle/shared_page.cpp @@ -16,6 +16,9 @@ void Init() { std::memset(&shared_page, 0, sizeof(shared_page)); shared_page.running_hw = 0x1; // product + + // Some games wait until this value becomes 0x1, before asking running_hw + shared_page.unknown_value = 0x1; } } // namespace diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h index 379bb7b63..35a07c685 100644 --- a/src/core/hle/shared_page.h +++ b/src/core/hle/shared_page.h @@ -39,12 +39,14 @@ struct SharedPageDef { DateTime date_time_0; // 20 DateTime date_time_1; // 40 u8 wifi_macaddr[6]; // 60 - u8 wifi_unknown1; // 66 + u8 wifi_link_level; // 66 u8 wifi_unknown2; // 67 INSERT_PADDING_BYTES(0x80 - 0x68); // 68 float_le sliderstate_3d; // 80 u8 ledstate_3d; // 84 - INSERT_PADDING_BYTES(0xA0 - 0x85); // 85 + INSERT_PADDING_BYTES(1); // 85 + u8 unknown_value; // 86 + INSERT_PADDING_BYTES(0xA0 - 0x87); // 87 u64_le menu_title_id; // A0 u64_le active_menu_title_id; // A8 INSERT_PADDING_BYTES(0x1000 - 0xB0); // B0 diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index ae54afb1c..fb2aecbf2 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -6,7 +6,6 @@ #include "common/logging/log.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "common/string_util.h" #include "common/symbols.h" @@ -860,6 +859,10 @@ static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) { // TODO(yuriks): Type 0 returns a slightly higher number than type 2, but I'm not sure // what's the difference between them. *out = process->heap_used + process->linear_heap_used + process->misc_memory_used; + if(*out % Memory::PAGE_SIZE != 0) { + LOG_ERROR(Kernel_SVC, "called, memory size not page-aligned"); + return ERR_MISALIGNED_SIZE; + } break; case 1: case 3: @@ -1031,8 +1034,6 @@ static const FunctionDef SVC_Table[] = { {0x7D, HLE::Wrap<QueryProcessMemory>, "QueryProcessMemory"}, }; -Common::Profiling::TimingCategory profiler_svc("SVC Calls"); - static const FunctionDef* GetSVCInfo(u32 func_num) { if (func_num >= ARRAY_SIZE(SVC_Table)) { LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); @@ -1044,7 +1045,6 @@ static const FunctionDef* GetSVCInfo(u32 func_num) { MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); void CallSVC(u32 immediate) { - Common::Profiling::ScopeTimer timer_svc(profiler_svc); MICROPROFILE_SCOPE(Kernel_SVC); const FunctionDef* info = GetSVCInfo(immediate); diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 7e2f9cdfa..2fe856293 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -115,21 +115,39 @@ inline void Write(u32 addr, const T data) { u8* start = Memory::GetPhysicalPointer(config.GetStartAddress()); u8* end = Memory::GetPhysicalPointer(config.GetEndAddress()); - if (config.fill_24bit) { - // fill with 24-bit values - for (u8* ptr = start; ptr < end; ptr += 3) { - ptr[0] = config.value_24bit_r; - ptr[1] = config.value_24bit_g; - ptr[2] = config.value_24bit_b; + // TODO: Consider always accelerating and returning vector of + // regions that the accelerated fill did not cover to + // reduce/eliminate the fill that the cpu has to do. + // This would also mean that the flush below is not needed. + // Fill should first flush all surfaces that touch but are + // not completely within the fill range. + // Then fill all completely covered surfaces, and return the + // regions that were between surfaces or within the touching + // ones for cpu to manually fill here. + if (!VideoCore::g_renderer->Rasterizer()->AccelerateFill(config)) { + Memory::RasterizerFlushAndInvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); + + if (config.fill_24bit) { + // fill with 24-bit values + for (u8* ptr = start; ptr < end; ptr += 3) { + ptr[0] = config.value_24bit_r; + ptr[1] = config.value_24bit_g; + ptr[2] = config.value_24bit_b; + } + } else if (config.fill_32bit) { + // fill with 32-bit values + if (end > start) { + u32 value = config.value_32bit; + size_t len = (end - start) / sizeof(u32); + for (size_t i = 0; i < len; ++i) + memcpy(&start[i * sizeof(u32)], &value, sizeof(u32)); + } + } else { + // fill with 16-bit values + u16 value_16bit = config.value_16bit.Value(); + for (u8* ptr = start; ptr < end; ptr += sizeof(u16)) + memcpy(ptr, &value_16bit, sizeof(u16)); } - } else if (config.fill_32bit) { - // fill with 32-bit values - for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr) - *ptr = config.value_32bit; - } else { - // fill with 16-bit values - for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr) - *ptr = config.value_16bit; } LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); @@ -139,8 +157,6 @@ inline void Write(u32 addr, const T data) { } else { GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); } - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); } // Reset "trigger" flag and set the "finish" flag @@ -161,184 +177,185 @@ inline void Write(u32 addr, const T data) { if (Pica::g_debug_context) Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::IncomingDisplayTransfer, nullptr); - u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); - u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); - - if (config.is_texture_copy) { - u32 input_width = config.texture_copy.input_width * 16; - u32 input_gap = config.texture_copy.input_gap * 16; - u32 output_width = config.texture_copy.output_width * 16; - u32 output_gap = config.texture_copy.output_gap * 16; - - size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap); - VideoCore::g_renderer->Rasterizer()->FlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size); - - u32 remaining_size = config.texture_copy.size; - u32 remaining_input = input_width; - u32 remaining_output = output_width; - while (remaining_size > 0) { - u32 copy_size = std::min({ remaining_input, remaining_output, remaining_size }); + if (!VideoCore::g_renderer->Rasterizer()->AccelerateDisplayTransfer(config)) { + u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); + u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); - std::memcpy(dst_pointer, src_pointer, copy_size); - src_pointer += copy_size; - dst_pointer += copy_size; + if (config.is_texture_copy) { + u32 input_width = config.texture_copy.input_width * 16; + u32 input_gap = config.texture_copy.input_gap * 16; + u32 output_width = config.texture_copy.output_width * 16; + u32 output_gap = config.texture_copy.output_gap * 16; - remaining_input -= copy_size; - remaining_output -= copy_size; - remaining_size -= copy_size; + size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap); + Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size); - if (remaining_input == 0) { - remaining_input = input_width; - src_pointer += input_gap; - } - if (remaining_output == 0) { - remaining_output = output_width; - dst_pointer += output_gap; - } - } + size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap); + Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size); - LOG_TRACE(HW_GPU, "TextureCopy: 0x%X bytes from 0x%08X(%u+%u)-> 0x%08X(%u+%u), flags 0x%08X", - config.texture_copy.size, - config.GetPhysicalInputAddress(), input_width, input_gap, - config.GetPhysicalOutputAddress(), output_width, output_gap, - config.flags); + u32 remaining_size = config.texture_copy.size; + u32 remaining_input = input_width; + u32 remaining_output = output_width; + while (remaining_size > 0) { + u32 copy_size = std::min({ remaining_input, remaining_output, remaining_size }); - size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap); - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size); + std::memcpy(dst_pointer, src_pointer, copy_size); + src_pointer += copy_size; + dst_pointer += copy_size; - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); - break; - } + remaining_input -= copy_size; + remaining_output -= copy_size; + remaining_size -= copy_size; - if (config.scaling > config.ScaleXY) { - LOG_CRITICAL(HW_GPU, "Unimplemented display transfer scaling mode %u", config.scaling.Value()); - UNIMPLEMENTED(); - break; - } + if (remaining_input == 0) { + remaining_input = input_width; + src_pointer += input_gap; + } + if (remaining_output == 0) { + remaining_output = output_width; + dst_pointer += output_gap; + } + } - if (config.input_linear && config.scaling != config.NoScale) { - LOG_CRITICAL(HW_GPU, "Scaling is only implemented on tiled input"); - UNIMPLEMENTED(); - break; - } + LOG_TRACE(HW_GPU, "TextureCopy: 0x%X bytes from 0x%08X(%u+%u)-> 0x%08X(%u+%u), flags 0x%08X", + config.texture_copy.size, + config.GetPhysicalInputAddress(), input_width, input_gap, + config.GetPhysicalOutputAddress(), output_width, output_gap, + config.flags); - bool horizontal_scale = config.scaling != config.NoScale; - bool vertical_scale = config.scaling == config.ScaleXY; + GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); + break; + } - u32 output_width = config.output_width >> horizontal_scale; - u32 output_height = config.output_height >> vertical_scale; + if (config.scaling > config.ScaleXY) { + LOG_CRITICAL(HW_GPU, "Unimplemented display transfer scaling mode %u", config.scaling.Value()); + UNIMPLEMENTED(); + break; + } - u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); - u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); + if (config.input_linear && config.scaling != config.NoScale) { + LOG_CRITICAL(HW_GPU, "Scaling is only implemented on tiled input"); + UNIMPLEMENTED(); + break; + } - VideoCore::g_renderer->Rasterizer()->FlushRegion(config.GetPhysicalInputAddress(), input_size); + int horizontal_scale = config.scaling != config.NoScale ? 1 : 0; + int vertical_scale = config.scaling == config.ScaleXY ? 1 : 0; - for (u32 y = 0; y < output_height; ++y) { - for (u32 x = 0; x < output_width; ++x) { - Math::Vec4<u8> src_color; + u32 output_width = config.output_width >> horizontal_scale; + u32 output_height = config.output_height >> vertical_scale; - // Calculate the [x,y] position of the input image - // based on the current output position and the scale - u32 input_x = x << horizontal_scale; - u32 input_y = y << vertical_scale; + u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); + u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); - if (config.flip_vertically) { - // Flip the y value of the output data, - // we do this after calculating the [x,y] position of the input image - // to account for the scaling options. - y = output_height - y - 1; - } + Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), input_size); + Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), output_size); - u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format); - u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format); - u32 src_offset; - u32 dst_offset; + for (u32 y = 0; y < output_height; ++y) { + for (u32 x = 0; x < output_width; ++x) { + Math::Vec4<u8> src_color; - if (config.input_linear) { - if (!config.dont_swizzle) { - // Interpret the input as linear and the output as tiled - u32 coarse_y = y & ~7; - u32 stride = output_width * dst_bytes_per_pixel; + // Calculate the [x,y] position of the input image + // based on the current output position and the scale + u32 input_x = x << horizontal_scale; + u32 input_y = y << vertical_scale; - src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; - dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; - } else { - // Both input and output are linear - src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; - dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + if (config.flip_vertically) { + // Flip the y value of the output data, + // we do this after calculating the [x,y] position of the input image + // to account for the scaling options. + y = output_height - y - 1; } - } else { - if (!config.dont_swizzle) { - // Interpret the input as tiled and the output as linear - u32 coarse_y = input_y & ~7; - u32 stride = config.input_width * src_bytes_per_pixel; - src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; - dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format); + u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format); + u32 src_offset; + u32 dst_offset; + + if (config.input_linear) { + if (!config.dont_swizzle) { + // Interpret the input as linear and the output as tiled + u32 coarse_y = y & ~7; + u32 stride = output_width * dst_bytes_per_pixel; + + src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; + } else { + // Both input and output are linear + src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } } else { - // Both input and output are tiled - u32 out_coarse_y = y & ~7; - u32 out_stride = output_width * dst_bytes_per_pixel; - - u32 in_coarse_y = input_y & ~7; - u32 in_stride = config.input_width * src_bytes_per_pixel; - - src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + in_coarse_y * in_stride; - dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + out_coarse_y * out_stride; + if (!config.dont_swizzle) { + // Interpret the input as tiled and the output as linear + u32 coarse_y = input_y & ~7; + u32 stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } else { + // Both input and output are tiled + u32 out_coarse_y = y & ~7; + u32 out_stride = output_width * dst_bytes_per_pixel; + + u32 in_coarse_y = input_y & ~7; + u32 in_stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + in_coarse_y * in_stride; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + out_coarse_y * out_stride; + } } - } - const u8* src_pixel = src_pointer + src_offset; - src_color = DecodePixel(config.input_format, src_pixel); - if (config.scaling == config.ScaleX) { - Math::Vec4<u8> pixel = DecodePixel(config.input_format, src_pixel + src_bytes_per_pixel); - src_color = ((src_color + pixel) / 2).Cast<u8>(); - } else if (config.scaling == config.ScaleXY) { - Math::Vec4<u8> pixel1 = DecodePixel(config.input_format, src_pixel + 1 * src_bytes_per_pixel); - Math::Vec4<u8> pixel2 = DecodePixel(config.input_format, src_pixel + 2 * src_bytes_per_pixel); - Math::Vec4<u8> pixel3 = DecodePixel(config.input_format, src_pixel + 3 * src_bytes_per_pixel); - src_color = (((src_color + pixel1) + (pixel2 + pixel3)) / 4).Cast<u8>(); - } + const u8* src_pixel = src_pointer + src_offset; + src_color = DecodePixel(config.input_format, src_pixel); + if (config.scaling == config.ScaleX) { + Math::Vec4<u8> pixel = DecodePixel(config.input_format, src_pixel + src_bytes_per_pixel); + src_color = ((src_color + pixel) / 2).Cast<u8>(); + } else if (config.scaling == config.ScaleXY) { + Math::Vec4<u8> pixel1 = DecodePixel(config.input_format, src_pixel + 1 * src_bytes_per_pixel); + Math::Vec4<u8> pixel2 = DecodePixel(config.input_format, src_pixel + 2 * src_bytes_per_pixel); + Math::Vec4<u8> pixel3 = DecodePixel(config.input_format, src_pixel + 3 * src_bytes_per_pixel); + src_color = (((src_color + pixel1) + (pixel2 + pixel3)) / 4).Cast<u8>(); + } - u8* dst_pixel = dst_pointer + dst_offset; - switch (config.output_format) { - case Regs::PixelFormat::RGBA8: - Color::EncodeRGBA8(src_color, dst_pixel); - break; + u8* dst_pixel = dst_pointer + dst_offset; + switch (config.output_format) { + case Regs::PixelFormat::RGBA8: + Color::EncodeRGBA8(src_color, dst_pixel); + break; - case Regs::PixelFormat::RGB8: - Color::EncodeRGB8(src_color, dst_pixel); - break; + case Regs::PixelFormat::RGB8: + Color::EncodeRGB8(src_color, dst_pixel); + break; - case Regs::PixelFormat::RGB565: - Color::EncodeRGB565(src_color, dst_pixel); - break; + case Regs::PixelFormat::RGB565: + Color::EncodeRGB565(src_color, dst_pixel); + break; - case Regs::PixelFormat::RGB5A1: - Color::EncodeRGB5A1(src_color, dst_pixel); - break; + case Regs::PixelFormat::RGB5A1: + Color::EncodeRGB5A1(src_color, dst_pixel); + break; - case Regs::PixelFormat::RGBA4: - Color::EncodeRGBA4(src_color, dst_pixel); - break; + case Regs::PixelFormat::RGBA4: + Color::EncodeRGBA4(src_color, dst_pixel); + break; - default: - LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); - break; + default: + LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); + break; + } } } - } - LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x, flags 0x%08X", + LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x, flags 0x%08X", config.output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format), config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(), config.GetPhysicalOutputAddress(), output_width, output_height, config.output_format.Value(), config.flags); + } g_regs.display_transfer_config.trigger = 0; GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetPhysicalOutputAddress(), output_size); } break; } diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index a00adbf53..da4c345b4 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -78,7 +78,7 @@ struct Regs { INSERT_PADDING_WORDS(0x4); - struct { + struct MemoryFillConfig { u32 address_start; u32 address_end; @@ -165,7 +165,7 @@ struct Regs { INSERT_PADDING_WORDS(0x169); - struct { + struct DisplayTransferConfig { u32 input_address; u32 output_address; diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h index 3dd877fbf..57029c5e8 100644 --- a/src/core/hw/lcd.h +++ b/src/core/hw/lcd.h @@ -52,8 +52,6 @@ struct Regs { return content[index]; } -#undef ASSERT_MEMBER_SIZE - }; static_assert(std::is_standard_layout<Regs>::value, "Structure does not use standard layout"); diff --git a/src/core/hw/y2r.cpp b/src/core/hw/y2r.cpp index 48c45564f..083391e83 100644 --- a/src/core/hw/y2r.cpp +++ b/src/core/hw/y2r.cpp @@ -261,7 +261,7 @@ void PerformConversion(ConversionConfiguration& cvt) { ASSERT(cvt.block_alignment != BlockAlignment::Block8x8 || cvt.input_lines % 8 == 0); // Tiles per row size_t num_tiles = cvt.input_line_width / 8; - ASSERT(num_tiles < MAX_TILES); + ASSERT(num_tiles <= MAX_TILES); // Buffer used as a CDMA source/target. std::unique_ptr<u8[]> data_buffer(new u8[cvt.input_line_width * 8 * 4]); diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 8eed6a50a..48a11ef81 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -10,13 +10,9 @@ #include "core/file_sys/archive_romfs.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" -#include "core/hle/service/fs/archive.h" -#include "core/loader/elf.h" -#include "core/loader/ncch.h" +#include "core/loader/3dsx.h" #include "core/memory.h" -#include "3dsx.h" - namespace Loader { /* @@ -307,4 +303,31 @@ ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& ro return ResultStatus::ErrorNotUsed; } +ResultStatus AppLoader_THREEDSX::ReadIcon(std::vector<u8>& buffer) { + if (!file.IsOpen()) + return ResultStatus::Error; + + // Reset read pointer in case this file has been read before. + file.Seek(0, SEEK_SET); + + THREEDSX_Header hdr; + if (file.ReadBytes(&hdr, sizeof(THREEDSX_Header)) != sizeof(THREEDSX_Header)) + return ResultStatus::Error; + + if (hdr.header_size != sizeof(THREEDSX_Header)) + return ResultStatus::Error; + + // Check if the 3DSX has a SMDH... + if (hdr.smdh_offset != 0) { + file.Seek(hdr.smdh_offset, SEEK_SET); + buffer.resize(hdr.smdh_size); + + if (file.ReadBytes(&buffer[0], hdr.smdh_size) != hdr.smdh_size) + return ResultStatus::Error; + + return ResultStatus::Success; + } + return ResultStatus::ErrorNotUsed; +} + } // namespace Loader diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h index 365ddb7a5..3ee686703 100644 --- a/src/core/loader/3dsx.h +++ b/src/core/loader/3dsx.h @@ -17,7 +17,7 @@ namespace Loader { /// Loads an 3DSX file class AppLoader_THREEDSX final : public AppLoader { public: - AppLoader_THREEDSX(FileUtil::IOFile&& file, std::string filename, const std::string& filepath) + AppLoader_THREEDSX(FileUtil::IOFile&& file, const std::string& filename, const std::string& filepath) : AppLoader(std::move(file)), filename(std::move(filename)), filepath(filepath) {} /** @@ -34,6 +34,13 @@ public: ResultStatus Load() override; /** + * Get the icon (typically icon section) of the application + * @param buffer Reference to buffer to store data + * @return ResultStatus result of function + */ + ResultStatus ReadIcon(std::vector<u8>& buffer) override; + + /** * Get the RomFS of the application * @param romfs_file Reference to buffer to store data * @param offset Offset in the file to the RomFS diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index b1907cd55..0d4c1d351 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -6,7 +6,6 @@ #include <string> #include "common/logging/log.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "core/file_sys/archive_romfs.h" @@ -91,6 +90,28 @@ const char* GetFileTypeString(FileType type) { return "unknown"; } +std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, + const std::string& filename, const std::string& filepath) { + switch (type) { + + // 3DSX file format. + case FileType::THREEDSX: + return std::make_unique<AppLoader_THREEDSX>(std::move(file), filename, filepath); + + // Standard ELF file format. + case FileType::ELF: + return std::make_unique<AppLoader_ELF>(std::move(file), filename); + + // NCCH/NCSD container formats. + case FileType::CXI: + case FileType::CCI: + return std::make_unique<AppLoader_NCCH>(std::move(file), filepath); + + default: + return std::unique_ptr<AppLoader>(); + } +} + ResultStatus LoadFile(const std::string& filename) { FileUtil::IOFile file(filename, "rb"); if (!file.IsOpen()) { @@ -112,15 +133,19 @@ ResultStatus LoadFile(const std::string& filename) { LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type)); + std::unique_ptr<AppLoader> app_loader = GetLoader(std::move(file), type, filename_filename, filename); + switch (type) { - //3DSX file format... + // 3DSX file format... + // or NCCH/NCSD container formats... case FileType::THREEDSX: + case FileType::CXI: + case FileType::CCI: { - AppLoader_THREEDSX app_loader(std::move(file), filename_filename, filename); // Load application and RomFS - if (ResultStatus::Success == app_loader.Load()) { - Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); + if (ResultStatus::Success == app_loader->Load()) { + Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*app_loader), Service::FS::ArchiveIdCode::RomFS); return ResultStatus::Success; } break; @@ -128,21 +153,7 @@ ResultStatus LoadFile(const std::string& filename) { // Standard ELF file format... case FileType::ELF: - return AppLoader_ELF(std::move(file), filename_filename).Load(); - - // NCCH/NCSD container formats... - case FileType::CXI: - case FileType::CCI: - { - AppLoader_NCCH app_loader(std::move(file), filename); - - // Load application and RomFS - ResultStatus result = app_loader.Load(); - if (ResultStatus::Success == result) { - Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); - } - return result; - } + return app_loader->Load(); // CIA file format... case FileType::CIA: diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index a7f2715ba..9d3e9ed3b 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -10,8 +10,10 @@ #include <string> #include <vector> +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/file_util.h" +#include "common/swap.h" namespace Kernel { struct AddressMapping; @@ -74,10 +76,55 @@ enum class ResultStatus { ErrorEncrypted, }; -static inline u32 MakeMagic(char a, char b, char c, char d) { +constexpr u32 MakeMagic(char a, char b, char c, char d) { return a | b << 8 | c << 16 | d << 24; } +/// SMDH data structure that contains titles, icons etc. See https://www.3dbrew.org/wiki/SMDH +struct SMDH { + u32_le magic; + u16_le version; + INSERT_PADDING_BYTES(2); + + struct Title { + std::array<u16, 0x40> short_title; + std::array<u16, 0x80> long_title; + std::array<u16, 0x40> publisher; + }; + std::array<Title, 16> titles; + + std::array<u8, 16> ratings; + u32_le region_lockout; + u32_le match_maker_id; + u64_le match_maker_bit_id; + u32_le flags; + u16_le eula_version; + INSERT_PADDING_BYTES(2); + float_le banner_animation_frame; + u32_le cec_id; + INSERT_PADDING_BYTES(8); + + std::array<u8, 0x480> small_icon; + std::array<u8, 0x1200> large_icon; + + /// indicates the language used for each title entry + enum class TitleLanguage { + Japanese = 0, + English = 1, + French = 2, + German = 3, + Italian = 4, + Spanish = 5, + SimplifiedChinese = 6, + Korean= 7, + Dutch = 8, + Portuguese = 9, + Russian = 10, + TraditionalChinese = 11 + }; +}; +static_assert(sizeof(SMDH) == 0x36C0, "SMDH structure size is wrong"); + /// Interface for loading an application class AppLoader : NonCopyable { public: @@ -150,6 +197,16 @@ protected: extern const std::initializer_list<Kernel::AddressMapping> default_address_mappings; /** + * Get a loader for a file with a specific type + * @param file The file to load + * @param type The type of the file + * @param filename the file name (without path) + * @param filepath the file full path (with name) + * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type + */ +std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, const std::string& filename, const std::string& filepath); + +/** * Identifies and loads a bootable file * @param filename String filename of bootable file * @return ResultStatus result of function diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 93f21bec2..d362a4419 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -7,7 +7,6 @@ #include <memory> #include "common/logging/log.h" -#include "common/make_unique.h" #include "common/string_util.h" #include "common/swap.h" @@ -174,8 +173,12 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& if (!file.IsOpen()) return ResultStatus::Error; + ResultStatus result = LoadExeFS(); + if (result != ResultStatus::Success) + return result; + LOG_DEBUG(Loader, "%d sections:", kMaxSections); - // Iterate through the ExeFs archive until we find the .code file... + // Iterate through the ExeFs archive until we find a section with the specified name... for (unsigned section_number = 0; section_number < kMaxSections; section_number++) { const auto& section = exefs_header.section[section_number]; @@ -187,7 +190,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); file.Seek(section_offset, SEEK_SET); - if (is_compressed) { + if (strcmp(section.name, ".code") == 0 && is_compressed) { // Section is compressed, read compressed .code section... std::unique_ptr<u8[]> temp_buffer; try { @@ -216,9 +219,9 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& return ResultStatus::ErrorNotUsed; } -ResultStatus AppLoader_NCCH::Load() { - if (is_loaded) - return ResultStatus::ErrorAlreadyLoaded; +ResultStatus AppLoader_NCCH::LoadExeFS() { + if (is_exefs_loaded) + return ResultStatus::Success; if (!file.IsOpen()) return ResultStatus::Error; @@ -256,7 +259,7 @@ ResultStatus AppLoader_NCCH::Load() { resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category; LOG_INFO(Loader, "Name: %s" , exheader_header.codeset_info.name); - LOG_INFO(Loader, "Program ID: %016X" , ncch_header.program_id); + LOG_INFO(Loader, "Program ID: %016llX" , ncch_header.program_id); LOG_DEBUG(Loader, "Code compressed: %s" , is_compressed ? "yes" : "no"); LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point); LOG_DEBUG(Loader, "Code size: 0x%08X", code_size); @@ -283,6 +286,18 @@ ResultStatus AppLoader_NCCH::Load() { if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) return ResultStatus::Error; + is_exefs_loaded = true; + return ResultStatus::Success; +} + +ResultStatus AppLoader_NCCH::Load() { + if (is_loaded) + return ResultStatus::ErrorAlreadyLoaded; + + ResultStatus result = LoadExeFS(); + if (result != ResultStatus::Success) + return result; + is_loaded = true; // Set state to loaded return LoadExec(); // Load the executable into memory for booting diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index ca6772a78..fd852c3de 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -232,6 +232,13 @@ private: */ ResultStatus LoadExec(); + /** + * Ensure ExeFS is loaded and ready for reading sections + * @return ResultStatus result of function + */ + ResultStatus LoadExeFS(); + + bool is_exefs_loaded = false; bool is_compressed = false; u32 entry_point = 0; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 7de5bd15d..ee9b69f81 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -15,6 +15,9 @@ #include "core/memory_setup.h" #include "core/mmio.h" +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" + namespace Memory { enum class PageType { @@ -22,8 +25,12 @@ enum class PageType { Unmapped, /// Page is mapped to regular memory. This is the only type you can get pointers to. Memory, + /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and invalidation + RasterizerCachedMemory, /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. Special, + /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and invalidation + RasterizerCachedSpecial, }; struct SpecialRegion { @@ -57,6 +64,12 @@ struct PageTable { * the corresponding entry in `pointers` MUST be set to null. */ std::array<PageType, NUM_ENTRIES> attributes; + + /** + * Indicates the number of externally cached resources touching a page that should be + * flushed before the memory is accessed + */ + std::array<u8, NUM_ENTRIES> cached_res_count; }; /// Singular page table used for the singleton process @@ -72,8 +85,15 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { while (base != end) { ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); + // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be null here + if (current_page_table->attributes[base] == PageType::RasterizerCachedMemory || + current_page_table->attributes[base] == PageType::RasterizerCachedSpecial) { + RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(base << PAGE_BITS), PAGE_SIZE); + } + current_page_table->attributes[base] = type; current_page_table->pointers[base] = memory; + current_page_table->cached_res_count[base] = 0; base += 1; if (memory != nullptr) @@ -84,6 +104,7 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { void InitMemoryMap() { main_page_table.pointers.fill(nullptr); main_page_table.attributes.fill(PageType::Unmapped); + main_page_table.cached_res_count.fill(0); } void MapMemoryRegion(VAddr base, u32 size, u8* target) { @@ -107,6 +128,28 @@ void UnmapRegion(VAddr base, u32 size) { } /** + * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) + * using a VMA from the current process + */ +static u8* GetPointerFromVMA(VAddr vaddr) { + u8* direct_pointer = nullptr; + + auto& vma = Kernel::g_current_process->vm_manager.FindVMA(vaddr)->second; + switch (vma.type) { + case Kernel::VMAType::AllocatedMemoryBlock: + direct_pointer = vma.backing_block->data() + vma.offset; + break; + case Kernel::VMAType::BackingMemory: + direct_pointer = vma.backing_memory; + break; + default: + UNREACHABLE(); + } + + return direct_pointer + (vaddr - vma.base); +} + +/** * This function should only be called for virtual addreses with attribute `PageType::Special`. */ static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { @@ -126,6 +169,7 @@ template <typename T> T Read(const VAddr vaddr) { const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; if (page_pointer) { + // NOTE: Avoid adding any extra logic to this fast-path block T value; std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T)); return value; @@ -139,8 +183,22 @@ T Read(const VAddr vaddr) { case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); break; + case PageType::RasterizerCachedMemory: + { + RasterizerFlushRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + T value; + std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T)); + return value; + } case PageType::Special: return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr); + case PageType::RasterizerCachedSpecial: + { + RasterizerFlushRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr); + } default: UNREACHABLE(); } @@ -153,6 +211,7 @@ template <typename T> void Write(const VAddr vaddr, const T data) { u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; if (page_pointer) { + // NOTE: Avoid adding any extra logic to this fast-path block std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T)); return; } @@ -165,9 +224,23 @@ void Write(const VAddr vaddr, const T data) { case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); break; + case PageType::RasterizerCachedMemory: + { + RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T)); + break; + } case PageType::Special: WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data); break; + case PageType::RasterizerCachedSpecial: + { + RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data); + break; + } default: UNREACHABLE(); } @@ -179,6 +252,10 @@ u8* GetPointer(const VAddr vaddr) { return page_pointer + (vaddr & PAGE_MASK); } + if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) { + return GetPointerFromVMA(vaddr); + } + LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr); return nullptr; } @@ -187,6 +264,69 @@ u8* GetPhysicalPointer(PAddr address) { return GetPointer(PhysicalToVirtualAddress(address)); } +void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { + if (start == 0) { + return; + } + + u32 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1; + PAddr paddr = start; + + for (unsigned i = 0; i < num_pages; ++i) { + VAddr vaddr = PhysicalToVirtualAddress(paddr); + u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS]; + ASSERT_MSG(count_delta <= UINT8_MAX - res_count, "Rasterizer resource cache counter overflow!"); + ASSERT_MSG(count_delta >= -res_count, "Rasterizer resource cache counter underflow!"); + + // Switch page type to cached if now cached + if (res_count == 0) { + PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (page_type) { + case PageType::Memory: + page_type = PageType::RasterizerCachedMemory; + current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; + break; + case PageType::Special: + page_type = PageType::RasterizerCachedSpecial; + break; + default: + UNREACHABLE(); + } + } + + res_count += count_delta; + + // Switch page type to uncached if now uncached + if (res_count == 0) { + PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (page_type) { + case PageType::RasterizerCachedMemory: + page_type = PageType::Memory; + current_page_table->pointers[vaddr >> PAGE_BITS] = GetPointerFromVMA(vaddr & ~PAGE_MASK); + break; + case PageType::RasterizerCachedSpecial: + page_type = PageType::Special; + break; + default: + UNREACHABLE(); + } + } + paddr += PAGE_SIZE; + } +} + +void RasterizerFlushRegion(PAddr start, u32 size) { + if (VideoCore::g_renderer != nullptr) { + VideoCore::g_renderer->Rasterizer()->FlushRegion(start, size); + } +} + +void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size) { + if (VideoCore::g_renderer != nullptr) { + VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(start, size); + } +} + u8 Read8(const VAddr addr) { return Read<u8>(addr); } diff --git a/src/core/memory.h b/src/core/memory.h index 5af72b7a7..9caa3c3f5 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -148,4 +148,20 @@ VAddr PhysicalToVirtualAddress(PAddr addr); */ u8* GetPhysicalPointer(PAddr address); +/** + * Adds the supplied value to the rasterizer resource cache counter of each + * page touching the region. + */ +void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta); + +/** + * Flushes any externally cached rasterizer resources touching the given region. + */ +void RasterizerFlushRegion(PAddr start, u32 size); + +/** + * Flushes and invalidates any externally cached rasterizer resources touching the given region. + */ +void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size); + } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 8a14f75aa..77261eafe 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -4,8 +4,27 @@ #include "settings.h" +#include "audio_core/audio_core.h" + +#include "core/gdbstub/gdbstub.h" + +#include "video_core/video_core.h" + namespace Settings { Values values = {}; +void Apply() { + + GDBStub::SetServerPort(static_cast<u32>(values.gdbstub_port)); + GDBStub::ToggleServer(values.use_gdbstub); + + VideoCore::g_hw_renderer_enabled = values.use_hw_renderer; + VideoCore::g_shader_jit_enabled = values.use_shader_jit; + VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; + + AudioCore::SelectSink(values.sink_id); + } + +} // namespace diff --git a/src/core/settings.h b/src/core/settings.h index 97ddcdff9..ce2a31164 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -6,7 +6,8 @@ #include <string> #include <array> -#include <common/file_util.h> + +#include "common/common_types.h" namespace Settings { @@ -55,6 +56,7 @@ struct Values { // Renderer bool use_hw_renderer; bool use_shader_jit; + bool use_scaled_resolution; float bg_red; float bg_green; @@ -62,9 +64,14 @@ struct Values { std::string log_filter; + // Audio + std::string sink_id; + // Debugging bool use_gdbstub; u16 gdbstub_port; } extern values; +void Apply(); + } diff --git a/src/core/tracer/recorder.h b/src/core/tracer/recorder.h index a42ccc45f..febf883c8 100644 --- a/src/core/tracer/recorder.h +++ b/src/core/tracer/recorder.h @@ -4,6 +4,7 @@ #pragma once +#include <string> #include <unordered_map> #include <vector> |