summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/arm_interface.h1
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp24
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/gdbstub/gdbstub.cpp30
-rw-r--r--src/core/hle/applets/applet.h1
-rw-r--r--src/core/hle/applets/mii_selector.cpp29
-rw-r--r--src/core/hle/applets/mii_selector.h50
-rw-r--r--src/core/hle/applets/swkbd.cpp24
-rw-r--r--src/core/hle/applets/swkbd.h7
-rw-r--r--src/core/hle/config_mem.cpp7
-rw-r--r--src/core/hle/function_wrappers.h3
-rw-r--r--src/core/hle/hle.cpp22
-rw-r--r--src/core/hle/hle.h4
-rw-r--r--src/core/hle/kernel/memory.cpp5
-rw-r--r--src/core/hle/kernel/process.cpp2
-rw-r--r--src/core/hle/kernel/process.h9
-rw-r--r--src/core/hle/kernel/shared_memory.cpp177
-rw-r--r--src/core/hle/kernel/shared_memory.h48
-rw-r--r--src/core/hle/kernel/thread.cpp89
-rw-r--r--src/core/hle/kernel/thread.h4
-rw-r--r--src/core/hle/result.h3
-rw-r--r--src/core/hle/service/ac_u.cpp26
-rw-r--r--src/core/hle/service/act_a.cpp26
-rw-r--r--src/core/hle/service/act_a.h23
-rw-r--r--src/core/hle/service/act_u.cpp3
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/apt/apt.cpp58
-rw-r--r--src/core/hle/service/apt/apt.h15
-rw-r--r--src/core/hle/service/apt/bcfnt/bcfnt.cpp71
-rw-r--r--src/core/hle/service/apt/bcfnt/bcfnt.h87
-rw-r--r--src/core/hle/service/cfg/cfg.cpp4
-rw-r--r--src/core/hle/service/cfg/cfg.h13
-rw-r--r--src/core/hle/service/csnd_snd.cpp13
-rw-r--r--src/core/hle/service/dsp_dsp.cpp199
-rw-r--r--src/core/hle/service/dsp_dsp.h19
-rw-r--r--src/core/hle/service/fs/archive.cpp1
-rw-r--r--src/core/hle/service/fs/fs_user.cpp2
-rw-r--r--src/core/hle/service/gsp_gpu.cpp75
-rw-r--r--src/core/hle/service/gsp_gpu.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp5
-rw-r--r--src/core/hle/service/ir/ir.cpp5
-rw-r--r--src/core/hle/service/ndm/ndm.cpp197
-rw-r--r--src/core/hle/service/ndm/ndm.h216
-rw-r--r--src/core/hle/service/ndm/ndm_u.cpp34
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/soc_u.cpp100
-rw-r--r--src/core/hle/service/y2r_u.cpp490
-rw-r--r--src/core/hle/service/y2r_u.h20
-rw-r--r--src/core/hle/shared_page.cpp3
-rw-r--r--src/core/hle/shared_page.h6
-rw-r--r--src/core/hle/svc.cpp70
-rw-r--r--src/core/hw/gpu.cpp327
-rw-r--r--src/core/hw/gpu.h4
-rw-r--r--src/core/hw/lcd.h2
-rw-r--r--src/core/hw/y2r.cpp2
-rw-r--r--src/core/loader/3dsx.cpp39
-rw-r--r--src/core/loader/3dsx.h9
-rw-r--r--src/core/loader/loader.cpp53
-rw-r--r--src/core/loader/loader.h57
-rw-r--r--src/core/loader/ncch.cpp31
-rw-r--r--src/core/loader/ncch.h7
-rw-r--r--src/core/memory.cpp140
-rw-r--r--src/core/memory.h22
-rw-r--r--src/core/settings.cpp19
-rw-r--r--src/core/settings.h9
-rw-r--r--src/core/tracer/recorder.cpp24
-rw-r--r--src/core/tracer/recorder.h1
69 files changed, 2437 insertions, 642 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a8d891689..12080a802 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -42,6 +42,7 @@ set(SRCS
hle/kernel/timer.cpp
hle/kernel/vm_manager.cpp
hle/service/ac_u.cpp
+ hle/service/act_a.cpp
hle/service/act_u.cpp
hle/service/am/am.cpp
hle/service/am/am_app.cpp
@@ -52,6 +53,7 @@ set(SRCS
hle/service/apt/apt_a.cpp
hle/service/apt/apt_s.cpp
hle/service/apt/apt_u.cpp
+ hle/service/apt/bcfnt/bcfnt.cpp
hle/service/boss/boss.cpp
hle/service/boss/boss_p.cpp
hle/service/boss/boss_u.cpp
@@ -175,6 +177,7 @@ set(HEADERS
hle/kernel/vm_manager.h
hle/result.h
hle/service/ac_u.h
+ hle/service/act_a.h
hle/service/act_u.h
hle/service/am/am.h
hle/service/am/am_app.h
@@ -185,6 +188,7 @@ set(HEADERS
hle/service/apt/apt_a.h
hle/service/apt/apt_s.h
hle/service/apt/apt_u.h
+ hle/service/apt/bcfnt/bcfnt.h
hle/service/boss/boss.h
hle/service/boss/boss_p.h
hle/service/boss/boss_u.h
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 533067d4f..d8abe5aeb 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -6,6 +6,7 @@
#include "common/common_types.h"
#include "core/arm/skyeye_common/arm_regformat.h"
+#include "core/arm/skyeye_common/vfp/asm_vfp.h"
namespace Core {
struct ThreadContext;
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index a3581132c..13492a08b 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -93,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 | ((entry_point & 1) << 5); // Usermode and THUMB mode
+ context.cpsr = USER32MODE | ((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 a6faf42b9..cfc67287f 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),
@@ -3496,7 +3492,6 @@ static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, cons
}
static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) {
- Common::Profiling::ScopeTimer timer_decode(profile_decode);
MICROPROFILE_SCOPE(DynCom_Decode);
// Decode instruction, get index
@@ -3530,7 +3525,6 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr)
}
static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) {
- Common::Profiling::ScopeTimer timer_decode(profile_decode);
MICROPROFILE_SCOPE(DynCom_Decode);
ARM_INST_PTR inst_base = nullptr;
@@ -3565,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;
@@ -4080,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;
@@ -5533,28 +5527,32 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
// SMUAD and SMLAD
if (BIT(op2, 1) == 0) {
- RD = (product1 + product2);
+ u32 rd_val = (product1 + product2);
if (inst_cream->Ra != 15) {
- RD += cpu->Reg[inst_cream->Ra];
+ rd_val += cpu->Reg[inst_cream->Ra];
if (ARMul_AddOverflowQ(product1 + product2, cpu->Reg[inst_cream->Ra]))
cpu->Cpsr |= (1 << 27);
}
+ RD = rd_val;
+
if (ARMul_AddOverflowQ(product1, product2))
cpu->Cpsr |= (1 << 27);
}
// SMUSD and SMLSD
else {
- RD = (product1 - product2);
+ u32 rd_val = (product1 - product2);
if (inst_cream->Ra != 15) {
- RD += cpu->Reg[inst_cream->Ra];
+ rd_val += cpu->Reg[inst_cream->Ra];
if (ARMul_AddOverflowQ(product1 - product2, cpu->Reg[inst_cream->Ra]))
cpu->Cpsr |= (1 << 27);
}
+
+ RD = rd_val;
}
}
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 3bb843aab..cabab744a 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -51,7 +51,7 @@ void RunLoop(int tight_loop) {
}
HW::Update();
- if (HLE::g_reschedule) {
+ if (HLE::IsReschedulePending()) {
Kernel::Reschedule();
}
}
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index c1a7ec5bf..820b19e1a 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -374,7 +374,7 @@ static void SendReply(const char* reply) {
memset(command_buffer, 0, sizeof(command_buffer));
- command_length = strlen(reply);
+ command_length = static_cast<u32>(strlen(reply));
if (command_length + 4 > sizeof(command_buffer)) {
LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply");
return;
@@ -437,7 +437,7 @@ static void HandleSetThread() {
*
* @param signal Signal to be sent to client.
*/
-void SendSignal(u32 signal) {
+static void SendSignal(u32 signal) {
if (gdbserver_socket == -1) {
return;
}
@@ -515,7 +515,7 @@ static bool IsDataAvailable() {
return false;
}
- return FD_ISSET(gdbserver_socket, &fd_socket);
+ return FD_ISSET(gdbserver_socket, &fd_socket) != 0;
}
/// Send requested register to gdb client.
@@ -529,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());
@@ -584,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));
@@ -633,10 +633,10 @@ static void ReadMemory() {
auto start_offset = command_buffer+1;
auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
- PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+ PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
start_offset = addr_pos+1;
- u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
+ u32 len = HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len);
@@ -658,11 +658,11 @@ static void ReadMemory() {
static void WriteMemory() {
auto start_offset = command_buffer+1;
auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
- PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+ PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
start_offset = addr_pos+1;
auto len_pos = std::find(start_offset, command_buffer+command_length, ':');
- u32 len = HexToInt(start_offset, len_pos - start_offset);
+ u32 len = HexToInt(start_offset, static_cast<u32>(len_pos - start_offset));
u8* dst = Memory::GetPointer(addr);
if (!dst) {
@@ -713,7 +713,7 @@ static void Continue() {
* @param addr Address of breakpoint.
* @param len Length of breakpoint.
*/
-bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
+static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
std::map<u32, Breakpoint>& p = GetBreakpointList(type);
Breakpoint breakpoint;
@@ -752,10 +752,10 @@ static void AddBreakpoint() {
auto start_offset = command_buffer+3;
auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
- PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+ PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
start_offset = addr_pos+1;
- u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
+ u32 len = HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
if (type == BreakpointType::Access) {
// Access is made up of Read and Write types, so add both breakpoints
@@ -800,10 +800,10 @@ static void RemoveBreakpoint() {
auto start_offset = command_buffer+3;
auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
- PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+ PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
start_offset = addr_pos+1;
- u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
+ u32 len = HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
if (type == BreakpointType::Access) {
// Access is made up of Read and Write types, so add both breakpoints
@@ -907,7 +907,7 @@ void ToggleServer(bool status) {
}
}
-void Init(u16 port) {
+static void Init(u16 port) {
if (!g_server_enabled) {
// Set the halt loop to false in case the user enabled the gdbstub mid-execution.
// This way the CPU can still execute normally.
diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h
index af442f81d..754c6f7db 100644
--- a/src/core/hle/applets/applet.h
+++ b/src/core/hle/applets/applet.h
@@ -65,6 +65,7 @@ protected:
virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0;
Service::APT::AppletId id; ///< Id of this Applet
+ std::shared_ptr<std::vector<u8>> heap_memory; ///< Heap memory for this Applet
};
/// Returns whether a library applet is currently running
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 708d2f630..bf39eca22 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -21,13 +21,6 @@
namespace HLE {
namespace Applets {
-MiiSelector::MiiSelector(Service::APT::AppletId id) : Applet(id), started(false) {
- // Create the SharedMemory that will hold the framebuffer data
- // TODO(Subv): What size should we use here?
- using Kernel::MemoryPermission;
- framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "MiiSelector Memory");
-}
-
ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
@@ -36,8 +29,23 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
return ResultCode(-1);
}
+ // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
+ // Create the SharedMemory that will hold the framebuffer data
+ Service::APT::CaptureBufferInfo capture_info;
+ ASSERT(sizeof(capture_info) == parameter.buffer_size);
+
+ memcpy(&capture_info, parameter.data, sizeof(capture_info));
+
+ using Kernel::MemoryPermission;
+ // Allocate a heap block of the required size for this applet.
+ heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
+ // Create a SharedMemory that directly points to this heap block.
+ framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(),
+ MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ "MiiSelector Memory");
+
+ // Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
- // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
result.data = nullptr;
result.buffer_size = 0;
@@ -55,6 +63,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..be6b04642 100644
--- a/src/core/hle/applets/mii_selector.h
+++ b/src/core/hle/applets/mii_selector.h
@@ -16,17 +16,61 @@
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);
+ MiiSelector(Service::APT::AppletId id) : Applet(id), started(false) { }
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
void Update() override;
bool IsRunning() const override { return started; }
- /// TODO(Subv): Find out what this is actually used for.
- /// It is believed that the application stores the current screen image here.
+ /// This SharedMemory will be created when we receive the LibAppJustStarted message.
+ /// It holds the framebuffer info retrieved by the application with GSPGPU::ImportDisplayCaptureInfo
Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
/// Whether this applet is currently running instead of the host application or not.
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index 1db6b5a17..90c6adc65 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -24,13 +24,6 @@
namespace HLE {
namespace Applets {
-SoftwareKeyboard::SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) {
- // Create the SharedMemory that will hold the framebuffer data
- // TODO(Subv): What size should we use here?
- using Kernel::MemoryPermission;
- framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "SoftwareKeyboard Memory");
-}
-
ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) {
if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
@@ -39,8 +32,23 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
return ResultCode(-1);
}
+ // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
+ // Create the SharedMemory that will hold the framebuffer data
+ Service::APT::CaptureBufferInfo capture_info;
+ ASSERT(sizeof(capture_info) == parameter.buffer_size);
+
+ memcpy(&capture_info, parameter.data, sizeof(capture_info));
+
+ using Kernel::MemoryPermission;
+ // Allocate a heap block of the required size for this applet.
+ heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
+ // Create a SharedMemory that directly points to this heap block.
+ framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(),
+ MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ "SoftwareKeyboard Memory");
+
+ // Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
- // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
result.data = nullptr;
result.buffer_size = 0;
diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h
index cb95b8d90..cf26a8fb7 100644
--- a/src/core/hle/applets/swkbd.h
+++ b/src/core/hle/applets/swkbd.h
@@ -53,8 +53,7 @@ static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config
class SoftwareKeyboard final : public Applet {
public:
- SoftwareKeyboard(Service::APT::AppletId id);
- ~SoftwareKeyboard() {}
+ SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) { }
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
@@ -72,8 +71,8 @@ public:
*/
void Finalize();
- /// TODO(Subv): Find out what this is actually used for.
- /// It is believed that the application stores the current screen image here.
+ /// This SharedMemory will be created when we receive the LibAppJustStarted message.
+ /// It holds the framebuffer info retrieved by the application with GSPGPU::ImportDisplayCaptureInfo
Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
/// SharedMemory where the output text will be stored
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/function_wrappers.h b/src/core/hle/function_wrappers.h
index 4d718b681..bf7f875b6 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -170,7 +170,8 @@ template<ResultCode func(s64*, u32, s32)> void Wrap() {
template<ResultCode func(u32*, u32, u32, u32, u32)> void Wrap() {
u32 param_1 = 0;
- u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw;
+ // The last parameter is passed in R0 instead of R4
+ u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(0)).raw;
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index 331b1b22a..5c5373517 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -8,15 +8,17 @@
#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"
////////////////////////////////////////////////////////////////////////////////////////////////////
-namespace HLE {
+namespace {
+
+bool reschedule; ///< If true, immediately reschedules the CPU to a new thread
-bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread
+}
+
+namespace HLE {
void Reschedule(const char *reason) {
DEBUG_ASSERT_MSG(reason != nullptr && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
@@ -29,13 +31,21 @@ void Reschedule(const char *reason) {
Core::g_app_core->PrepareReschedule();
- g_reschedule = true;
+ reschedule = true;
+}
+
+bool IsReschedulePending() {
+ return reschedule;
+}
+
+void DoneRescheduling() {
+ reschedule = false;
}
void Init() {
Service::Init();
- g_reschedule = false;
+ reschedule = false;
LOG_DEBUG(Kernel, "initialized OK");
}
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index e0b97797c..69ac0ade6 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -13,9 +13,9 @@ const Handle INVALID_HANDLE = 0;
namespace HLE {
-extern bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread
-
void Reschedule(const char *reason);
+bool IsReschedulePending();
+void DoneRescheduling();
void Init();
void Shutdown();
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 862643448..17ae87aef 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -55,6 +55,9 @@ void MemoryInit(u32 mem_type) {
memory_regions[i].size = memory_region_sizes[mem_type][i];
memory_regions[i].used = 0;
memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>();
+ // Reserve enough space for this region of FCRAM.
+ // We do not want this block of memory to be relocated when allocating from it.
+ memory_regions[i].linear_heap_memory->reserve(memory_regions[i].size);
base += memory_regions[i].size;
}
@@ -107,9 +110,7 @@ struct MemoryArea {
// We don't declare the IO regions in here since its handled by other means.
static MemoryArea memory_areas[] = {
- {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory
{VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM)
- {TLS_AREA_VADDR, TLS_AREA_SIZE, "TLS Area"}, // TLS memory
};
}
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 0546f6e16..69302cc82 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -209,7 +209,7 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p
return ERR_INVALID_ADDRESS;
}
- // Expansion of the linear heap is only allowed if you do an allocation immediatelly at its
+ // Expansion of the linear heap is only allowed if you do an allocation immediately at its
// end. It's possible to free gaps in the middle of the heap and then reallocate them later,
// but expansions are only allowed at the end.
if (target == heap_end) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 6d2ca96a2..d781ef32c 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -107,6 +107,8 @@ public:
ProcessFlags flags;
/// Kernel compatibility version for this process
u16 kernel_version = 0;
+ /// The default CPU for this process, threads are scheduled on this cpu by default.
+ u8 ideal_processor = 0;
/// The id of this process
u32 process_id = next_process_id++;
@@ -140,8 +142,11 @@ public:
MemoryRegionInfo* memory_region = nullptr;
- /// Bitmask of the used TLS slots
- std::bitset<300> used_tls_slots;
+ /// The Thread Local Storage area is allocated as processes create threads,
+ /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
+ /// holds the TLS for a specific thread. This vector contains which parts are in use for each page as a bitmask.
+ /// This vector will grow as more pages are allocated for new threads.
+ std::vector<std::bitset<8>> tls_slots;
VAddr GetLinearHeapAreaAddress() const;
VAddr GetLinearHeapBase() const;
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index d90f0f00f..6a22c8986 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -7,6 +7,7 @@
#include "common/logging/log.h"
#include "core/memory.h"
+#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h"
namespace Kernel {
@@ -14,93 +15,157 @@ namespace Kernel {
SharedMemory::SharedMemory() {}
SharedMemory::~SharedMemory() {}
-SharedPtr<SharedMemory> SharedMemory::Create(u32 size, MemoryPermission permissions,
- MemoryPermission other_permissions, std::string name) {
+SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions,
+ MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
+ shared_memory->owner_process = owner_process;
shared_memory->name = std::move(name);
- shared_memory->base_address = 0x0;
- shared_memory->fixed_address = 0x0;
shared_memory->size = size;
shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions;
+ if (address == 0) {
+ // We need to allocate a block from the Linear Heap ourselves.
+ // We'll manually allocate some memory from the linear heap in the specified region.
+ MemoryRegionInfo* memory_region = GetMemoryRegion(region);
+ auto& linheap_memory = memory_region->linear_heap_memory;
+
+ ASSERT_MSG(linheap_memory->size() + size <= memory_region->size, "Not enough space in region to allocate shared memory!");
+
+ shared_memory->backing_block = linheap_memory;
+ shared_memory->backing_block_offset = linheap_memory->size();
+ // Allocate some memory from the end of the linear heap for this region.
+ linheap_memory->insert(linheap_memory->end(), size, 0);
+ memory_region->used += size;
+
+ shared_memory->linear_heap_phys_address = Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset;
+
+ // Increase the amount of used linear heap memory for the owner process.
+ if (shared_memory->owner_process != nullptr) {
+ shared_memory->owner_process->linear_heap_used += size;
+ }
+
+ // Refresh the address mappings for the current process.
+ if (Kernel::g_current_process != nullptr) {
+ Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
+ }
+ } else {
+ // TODO(Subv): What happens if an application tries to create multiple memory blocks pointing to the same address?
+ auto& vm_manager = shared_memory->owner_process->vm_manager;
+ // The memory is already available and mapped in the owner process.
+ auto vma = vm_manager.FindVMA(address)->second;
+ // Copy it over to our own storage
+ shared_memory->backing_block = std::make_shared<std::vector<u8>>(vma.backing_block->data() + vma.offset,
+ vma.backing_block->data() + vma.offset + size);
+ shared_memory->backing_block_offset = 0;
+ // Unmap the existing pages
+ vm_manager.UnmapRange(address, size);
+ // Map our own block into the address space
+ vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, MemoryState::Shared);
+ // Reprotect the block with the new permissions
+ vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions));
+ }
+
+ shared_memory->base_address = address;
return shared_memory;
}
-ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions,
- MemoryPermission other_permissions) {
+SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
+ MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
+ SharedPtr<SharedMemory> shared_memory(new SharedMemory);
- if (base_address != 0) {
- LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: already mapped at 0x%08X!",
- GetObjectId(), address, name.c_str(), base_address);
- // TODO: Verify error code with hardware
- return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
- ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
- }
+ shared_memory->owner_process = nullptr;
+ shared_memory->name = std::move(name);
+ shared_memory->size = size;
+ shared_memory->permissions = permissions;
+ shared_memory->other_permissions = other_permissions;
+ shared_memory->backing_block = heap_block;
+ shared_memory->backing_block_offset = offset;
+ shared_memory->base_address = Memory::HEAP_VADDR + offset;
- // TODO(Subv): Return E0E01BEE when permissions and other_permissions don't
- // match what was specified when the memory block was created.
+ return shared_memory;
+}
- // TODO(Subv): Return E0E01BEE when address should be 0.
- // Note: Find out when that's the case.
+ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
+ MemoryPermission other_permissions) {
- if (fixed_address != 0) {
- if (address != 0 && address != fixed_address) {
- LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: fixed_addres is 0x%08X!",
- GetObjectId(), address, name.c_str(), fixed_address);
- // TODO: Verify error code with hardware
- return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
- ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
- }
+ MemoryPermission own_other_permissions = target_process == owner_process ? this->permissions : this->other_permissions;
- // HACK(yuriks): This is only here to support the APT shared font mapping right now.
- // Later, this should actually map the memory block onto the address space.
- return RESULT_SUCCESS;
+ // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
+ if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
- if (address < Memory::SHARED_MEMORY_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
- LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s outside of shared mem bounds!",
- GetObjectId(), address, name.c_str());
- // TODO: Verify error code with hardware
- return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
- ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ // Error out if the requested permissions don't match what the creator process allows.
+ if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
+ LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
+ GetObjectId(), address, name.c_str());
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
- // TODO: Test permissions
+ // Heap-backed memory blocks can not be mapped with other_permissions = DontCare
+ if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
+ LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
+ GetObjectId(), address, name.c_str());
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ }
- // HACK: Since there's no way to write to the memory block without mapping it onto the game
- // process yet, at least initialize memory the first time it's mapped.
- if (address != this->base_address) {
- std::memset(Memory::GetPointer(address), 0, size);
+ // Error out if the provided permissions are not compatible with what the creator process needs.
+ if (other_permissions != MemoryPermission::DontCare &&
+ static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
+ LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
+ GetObjectId(), address, name.c_str());
+ return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
}
- this->base_address = address;
+ // TODO(Subv): Check for the Shared Device Mem flag in the creator process.
+ /*if (was_created_with_shared_device_mem && address != 0) {
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ }*/
- return RESULT_SUCCESS;
-}
+ // TODO(Subv): The same process that created a SharedMemory object
+ // can not map it in its own address space unless it was created with addr=0, result 0xD900182C.
-ResultCode SharedMemory::Unmap(VAddr address) {
- if (base_address == 0) {
- // TODO(Subv): Verify what actually happens when you want to unmap a memory block that
- // was originally mapped with address = 0
- return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ if (address != 0) {
+ if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
+ LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, invalid address",
+ GetObjectId(), address, name.c_str());
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ }
}
- if (base_address != address)
- return ResultCode(ErrorDescription::WrongAddress, ErrorModule::OS, ErrorSummary::InvalidState, ErrorLevel::Usage);
+ VAddr target_address = address;
- base_address = 0;
+ if (base_address == 0 && target_address == 0) {
+ // Calculate the address at which to map the memory block.
+ target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address);
+ }
+
+ // Map the memory block into the target process
+ auto result = target_process->vm_manager.MapMemoryBlock(target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
+ if (result.Failed()) {
+ LOG_ERROR(Kernel, "cannot map id=%u, target_address=0x%08X name=%s, error mapping to virtual memory",
+ GetObjectId(), target_address, name.c_str());
+ return result.Code();
+ }
- return RESULT_SUCCESS;
+ return target_process->vm_manager.ReprotectRange(target_address, size, ConvertPermissions(permissions));
}
-u8* SharedMemory::GetPointer(u32 offset) {
- if (base_address != 0)
- return Memory::GetPointer(base_address + offset);
+ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) {
+ // TODO(Subv): Verify what happens if the application tries to unmap an address that is not mapped to a SharedMemory.
+ return target_process->vm_manager.UnmapRange(address, size);
+}
+
+VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
+ u32 masked_permissions = static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute);
+ return static_cast<VMAPermission>(masked_permissions);
+};
- LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId());
- return nullptr;
+u8* SharedMemory::GetPointer(u32 offset) {
+ return backing_block->data() + backing_block_offset + offset;
}
} // namespace
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index b51049ad0..0c404a9f8 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -9,6 +9,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
namespace Kernel {
@@ -29,14 +30,29 @@ enum class MemoryPermission : u32 {
class SharedMemory final : public Object {
public:
/**
- * Creates a shared memory object
+ * Creates a shared memory object.
+ * @param owner_process Process that created this shared memory object.
* @param size Size of the memory block. Must be page-aligned.
* @param permissions Permission restrictions applied to the process which created the block.
* @param other_permissions Permission restrictions applied to other processes mapping the block.
+ * @param address The address from which to map the Shared Memory.
+ * @param region If the address is 0, the shared memory will be allocated in this region of the linear heap.
* @param name Optional object name, used for debugging purposes.
*/
- static SharedPtr<SharedMemory> Create(u32 size, MemoryPermission permissions,
- MemoryPermission other_permissions, std::string name = "Unknown");
+ static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions,
+ MemoryPermission other_permissions, VAddr address = 0, MemoryRegion region = MemoryRegion::BASE, std::string name = "Unknown");
+
+ /**
+ * Creates a shared memory object from a block of memory managed by an HLE applet.
+ * @param heap_block Heap block of the HLE applet.
+ * @param offset The offset into the heap block that the SharedMemory will map.
+ * @param size Size of the memory block. Must be page-aligned.
+ * @param permissions Permission restrictions applied to the process which created the block.
+ * @param other_permissions Permission restrictions applied to other processes mapping the block.
+ * @param name Optional object name, used for debugging purposes.
+ */
+ static SharedPtr<SharedMemory> CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
+ MemoryPermission permissions, MemoryPermission other_permissions, std::string name = "Unknown Applet");
std::string GetTypeName() const override { return "SharedMemory"; }
std::string GetName() const override { return name; }
@@ -45,19 +61,27 @@ public:
HandleType GetHandleType() const override { return HANDLE_TYPE; }
/**
- * Maps a shared memory block to an address in system memory
+ * Converts the specified MemoryPermission into the equivalent VMAPermission.
+ * @param permission The MemoryPermission to convert.
+ */
+ static VMAPermission ConvertPermissions(MemoryPermission permission);
+
+ /**
+ * Maps a shared memory block to an address in the target process' address space
+ * @param target_process Process on which to map the memory block.
* @param address Address in system memory to map shared memory block to
* @param permissions Memory block map permissions (specified by SVC field)
* @param other_permissions Memory block map other permissions (specified by SVC field)
*/
- ResultCode Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions);
+ ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions, MemoryPermission other_permissions);
/**
* Unmaps a shared memory block from the specified address in system memory
+ * @param target_process Process from which to umap the memory block.
* @param address Address in system memory where the shared memory block is mapped
* @return Result code of the unmap operation
*/
- ResultCode Unmap(VAddr address);
+ ResultCode Unmap(Process* target_process, VAddr address);
/**
* Gets a pointer to the shared memory block
@@ -66,10 +90,16 @@ public:
*/
u8* GetPointer(u32 offset = 0);
- /// Address of shared memory block in the process.
+ /// Process that created this shared memory block.
+ SharedPtr<Process> owner_process;
+ /// Address of shared memory block in the owner process if specified.
VAddr base_address;
- /// Fixed address to allow mapping to. Used for blocks created from the linear heap.
- VAddr fixed_address;
+ /// Physical address of the shared memory block in the linear heap if no address was specified during creation.
+ PAddr linear_heap_phys_address;
+ /// Backing memory for this shared memory block.
+ std::shared_ptr<std::vector<u8>> backing_block;
+ /// Offset into the backing block for this shared memory.
+ u32 backing_block_offset;
/// Size of the memory block. Page-aligned.
u32 size;
/// Permission restrictions applied to the process which created the block.
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index bf32f653d..43def6146 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -117,9 +117,10 @@ void Thread::Stop() {
}
wait_objects.clear();
- Kernel::g_current_process->used_tls_slots[tls_index] = false;
- g_current_process->misc_memory_used -= Memory::TLS_ENTRY_SIZE;
- g_current_process->memory_region->used -= Memory::TLS_ENTRY_SIZE;
+ // Mark the TLS slot in the thread's page as free.
+ u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
+ u32 tls_slot = ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
+ Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot);
HLE::Reschedule(__func__);
}
@@ -366,6 +367,31 @@ static void DebugThreadQueue() {
}
}
+/**
+ * Finds a free location for the TLS section of a thread.
+ * @param tls_slots The TLS page array of the thread's owner process.
+ * Returns a tuple of (page, slot, alloc_needed) where:
+ * page: The index of the first allocated TLS page that has free slots.
+ * slot: The index of the first free slot in the indicated page.
+ * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
+ */
+std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& tls_slots) {
+ // Iterate over all the allocated pages, and try to find one where not all slots are used.
+ for (unsigned page = 0; page < tls_slots.size(); ++page) {
+ const auto& page_tls_slots = tls_slots[page];
+ if (!page_tls_slots.all()) {
+ // We found a page with at least one free slot, find which slot it is
+ for (unsigned slot = 0; slot < page_tls_slots.size(); ++slot) {
+ if (!page_tls_slots.test(slot)) {
+ return std::make_tuple(page, slot, false);
+ }
+ }
+ }
+ }
+
+ return std::make_tuple(0, 0, true);
+}
+
ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority,
u32 arg, s32 processor_id, VAddr stack_top) {
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
@@ -403,22 +429,50 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->name = std::move(name);
thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom();
thread->owner_process = g_current_process;
- thread->tls_index = -1;
thread->waitsynch_waited = false;
// Find the next available TLS index, and mark it as used
- auto& used_tls_slots = Kernel::g_current_process->used_tls_slots;
- for (unsigned int i = 0; i < used_tls_slots.size(); ++i) {
- if (used_tls_slots[i] == false) {
- thread->tls_index = i;
- used_tls_slots[i] = true;
- break;
+ auto& tls_slots = Kernel::g_current_process->tls_slots;
+ bool needs_allocation = true;
+ u32 available_page; // Which allocated page has free space
+ u32 available_slot; // Which slot within the page is free
+
+ std::tie(available_page, available_slot, needs_allocation) = GetFreeThreadLocalSlot(tls_slots);
+
+ if (needs_allocation) {
+ // There are no already-allocated pages with free slots, lets allocate a new one.
+ // TLS pages are allocated from the BASE region in the linear heap.
+ MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE);
+ auto& linheap_memory = memory_region->linear_heap_memory;
+
+ if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) {
+ LOG_ERROR(Kernel_SVC, "Not enough space in region to allocate a new TLS page for thread");
+ return ResultCode(ErrorDescription::OutOfMemory, ErrorModule::Kernel, ErrorSummary::OutOfResource, ErrorLevel::Permanent);
}
+
+ u32 offset = linheap_memory->size();
+
+ // Allocate some memory from the end of the linear heap for this region.
+ linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0);
+ memory_region->used += Memory::PAGE_SIZE;
+ Kernel::g_current_process->linear_heap_used += Memory::PAGE_SIZE;
+
+ tls_slots.emplace_back(0); // The page is completely available at the start
+ available_page = tls_slots.size() - 1;
+ available_slot = 0; // Use the first slot in the new page
+
+ auto& vm_manager = Kernel::g_current_process->vm_manager;
+ vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
+
+ // Map the page to the current process' address space.
+ // TODO(Subv): Find the correct MemoryState for this region.
+ vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
+ linheap_memory, offset, Memory::PAGE_SIZE, MemoryState::Private);
}
- ASSERT_MSG(thread->tls_index != -1, "Out of TLS space");
- g_current_process->misc_memory_used += Memory::TLS_ENTRY_SIZE;
- g_current_process->memory_region->used += Memory::TLS_ENTRY_SIZE;
+ // Mark the slot as used
+ tls_slots[available_page].set(available_slot);
+ thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE;
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
@@ -472,6 +526,8 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) {
SharedPtr<Thread> thread = thread_res.MoveFrom();
+ thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
+
// Run new "main" thread
SwitchContext(thread.get());
@@ -483,7 +539,8 @@ void Reschedule() {
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
- HLE::g_reschedule = false;
+
+ HLE::DoneRescheduling();
// Don't bother switching to the same thread
if (next == cur)
@@ -508,10 +565,6 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
context.cpu_registers[1] = output;
}
-VAddr Thread::GetTLSAddress() const {
- return Memory::TLS_AREA_VADDR + tls_index * Memory::TLS_ENTRY_SIZE;
-}
-
////////////////////////////////////////////////////////////////////////////////////////////////////
void ThreadingInit() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 97ba57fc5..deab5d5a6 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -127,7 +127,7 @@ public:
* Returns the Thread Local Storage address of the current thread
* @returns VAddr of the thread's TLS
*/
- VAddr GetTLSAddress() const;
+ VAddr GetTLSAddress() const { return tls_address; }
Core::ThreadContext context;
@@ -144,7 +144,7 @@ public:
s32 processor_id;
- s32 tls_index; ///< Index of the Thread Local Storage of the thread
+ VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
bool waitsynch_waited; ///< Set to true if the last svcWaitSynch call caused the thread to wait
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 2d22652d9..bfb3327ce 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,6 +17,8 @@
/// Detailed description of the error. This listing is likely incomplete.
enum class ErrorDescription : u32 {
Success = 0,
+ WrongPermission = 46,
+ OS_InvalidBufferDescriptor = 48,
WrongAddress = 53,
FS_NotFound = 120,
FS_AlreadyExists = 190,
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/act_a.cpp b/src/core/hle/service/act_a.cpp
new file mode 100644
index 000000000..3a775fa90
--- /dev/null
+++ b/src/core/hle/service/act_a.cpp
@@ -0,0 +1,26 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/act_a.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace ACT_A
+
+namespace ACT_A {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x041300C2, nullptr, "UpdateMiiImage"},
+ {0x041B0142, nullptr, "AgreeEula"},
+ {0x04210042, nullptr, "UploadMii"},
+ {0x04230082, nullptr, "ValidateMailAddress"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/act_a.h b/src/core/hle/service/act_a.h
new file mode 100644
index 000000000..765cae644
--- /dev/null
+++ b/src/core/hle/service/act_a.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 ACT_A
+
+namespace ACT_A {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "act:a";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp
index b23d17fba..05de4d002 100644
--- a/src/core/hle/service/act_u.cpp
+++ b/src/core/hle/service/act_u.cpp
@@ -10,7 +10,10 @@
namespace ACT_U {
const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010084, nullptr, "Initialize"},
+ {0x00020040, nullptr, "GetErrorCode"},
{0x000600C2, nullptr, "GetAccountDataBlock"},
+ {0x000D0040, nullptr, "GenerateUuid"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 9591522e5..3f71e7f2b 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -43,7 +43,7 @@ void FindContentInfos(Service::Interface* self) {
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%016lx, content_cound=%u, content_ids_pointer=0x%08x, content_info_pointer=0x%08x",
+ 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);
}
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index e6fcbc714..bbf170b71 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -12,6 +12,7 @@
#include "core/hle/service/apt/apt_a.h"
#include "core/hle/service/apt/apt_s.h"
#include "core/hle/service/apt/apt_u.h"
+#include "core/hle/service/apt/bcfnt/bcfnt.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/ptm/ptm.h"
@@ -23,23 +24,14 @@
namespace Service {
namespace APT {
-// Address used for shared font (as observed on HW)
-// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via
-// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any
-// address other than 0x18000000 due to internal pointers in the shared font dump that would need to
-// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then
-// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
-static const VAddr SHARED_FONT_VADDR = 0x18000000;
-
/// Handle to shared memory region designated to for shared system font
static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
+static bool shared_font_relocated = false;
static Kernel::SharedPtr<Kernel::Mutex> lock;
static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event
-static std::shared_ptr<std::vector<u8>> shared_font;
-
static u32 cpu_percent; ///< CPU time available to the running application
// APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode
@@ -78,23 +70,25 @@ void Initialize(Service::Interface* self) {
void GetSharedFont(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- if (shared_font != nullptr) {
- // TODO(yuriks): This is a hack to keep this working right now even with our completely
- // broken shared memory system.
- shared_font_mem->fixed_address = SHARED_FONT_VADDR;
- Kernel::g_current_process->vm_manager.MapMemoryBlock(shared_font_mem->fixed_address,
- shared_font, 0, shared_font_mem->size, Kernel::MemoryState::Shared);
-
- cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = SHARED_FONT_VADDR;
- cmd_buff[3] = IPC::MoveHandleDesc();
- cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
- } else {
- cmd_buff[0] = IPC::MakeHeader(0x44, 1, 0);
- cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
- LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
+ // The shared font has to be relocated to the new address before being passed to the application.
+ VAddr target_address = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address);
+ // The shared font dumped by 3dsutils (https://github.com/citra-emu/3dsutils) uses this address as base,
+ // so we relocate it from there to our real address.
+ // TODO(Subv): This address is wrong if the shared font is dumped from a n3DS,
+ // we need a way to automatically calculate the original address of the font from the file.
+ static const VAddr SHARED_FONT_VADDR = 0x18000000;
+ if (!shared_font_relocated) {
+ BCFNT::RelocateSharedFont(shared_font_mem, SHARED_FONT_VADDR, target_address);
+ shared_font_relocated = true;
}
+ cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ // Since the SharedMemory interface doesn't provide the address at which the memory was allocated,
+ // the real APT service calculates this address by scanning the entire address space (using svcQueryMemory)
+ // and searches for an allocation of the same size as the Shared Font.
+ cmd_buff[2] = target_address;
+ cmd_buff[3] = IPC::MoveHandleDesc();
+ cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
}
void NotifyToWait(Service::Interface* self) {
@@ -483,14 +477,12 @@ void Init() {
FileUtil::IOFile file(filepath, "rb");
if (file.IsOpen()) {
- // Read shared font data
- shared_font = std::make_shared<std::vector<u8>>((size_t)file.GetSize());
- file.ReadBytes(shared_font->data(), shared_font->size());
-
// Create shared font memory object
using Kernel::MemoryPermission;
- shared_font_mem = Kernel::SharedMemory::Create(3 * 1024 * 1024, // 3MB
- MemoryPermission::ReadWrite, MemoryPermission::Read, "APT_U:shared_font_mem");
+ shared_font_mem = Kernel::SharedMemory::Create(nullptr, 0x332000, // 3272 KB
+ MemoryPermission::ReadWrite, MemoryPermission::Read, 0, Kernel::MemoryRegion::SYSTEM, "APT:SharedFont");
+ // Read shared font data
+ file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
} else {
LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
shared_font_mem = nullptr;
@@ -510,8 +502,8 @@ void Init() {
}
void Shutdown() {
- shared_font = nullptr;
shared_font_mem = nullptr;
+ shared_font_relocated = false;
lock = nullptr;
notification_event = nullptr;
parameter_event = nullptr;
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index fd3c2bd37..ed7c47cca 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -5,6 +5,7 @@
#pragma once
#include "common/common_types.h"
+#include "common/swap.h"
#include "core/hle/kernel/kernel.h"
@@ -31,6 +32,20 @@ struct AppletStartupParameter {
u8* data = nullptr;
};
+/// Used by the application to pass information about the current framebuffer to applets.
+struct CaptureBufferInfo {
+ u32_le size;
+ u8 is_3d;
+ INSERT_PADDING_BYTES(0x3); // Padding for alignment
+ u32_le top_screen_left_offset;
+ u32_le top_screen_right_offset;
+ u32_le top_screen_format;
+ u32_le bottom_screen_left_offset;
+ u32_le bottom_screen_right_offset;
+ u32_le bottom_screen_format;
+};
+static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
+
/// Signals used by APT functions
enum class SignalType : u32 {
None = 0x0,
diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.cpp b/src/core/hle/service/apt/bcfnt/bcfnt.cpp
new file mode 100644
index 000000000..b0d39d4a5
--- /dev/null
+++ b/src/core/hle/service/apt/bcfnt/bcfnt.cpp
@@ -0,0 +1,71 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/apt/bcfnt/bcfnt.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace APT {
+namespace BCFNT {
+
+void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address) {
+ static const u32 SharedFontStartOffset = 0x80;
+ u8* data = shared_font->GetPointer(SharedFontStartOffset);
+
+ CFNT cfnt;
+ memcpy(&cfnt, data, sizeof(cfnt));
+
+ // Advance past the header
+ data = shared_font->GetPointer(SharedFontStartOffset + cfnt.header_size);
+
+ for (unsigned block = 0; block < cfnt.num_blocks; ++block) {
+
+ u32 section_size = 0;
+ if (memcmp(data, "FINF", 4) == 0) {
+ BCFNT::FINF finf;
+ memcpy(&finf, data, sizeof(finf));
+ section_size = finf.section_size;
+
+ // Relocate the offsets in the FINF section
+ finf.cmap_offset += new_address - previous_address;
+ finf.cwdh_offset += new_address - previous_address;
+ finf.tglp_offset += new_address - previous_address;
+
+ memcpy(data, &finf, sizeof(finf));
+ } else if (memcmp(data, "CMAP", 4) == 0) {
+ BCFNT::CMAP cmap;
+ memcpy(&cmap, data, sizeof(cmap));
+ section_size = cmap.section_size;
+
+ // Relocate the offsets in the CMAP section
+ cmap.next_cmap_offset += new_address - previous_address;
+
+ memcpy(data, &cmap, sizeof(cmap));
+ } else if (memcmp(data, "CWDH", 4) == 0) {
+ BCFNT::CWDH cwdh;
+ memcpy(&cwdh, data, sizeof(cwdh));
+ section_size = cwdh.section_size;
+
+ // Relocate the offsets in the CWDH section
+ cwdh.next_cwdh_offset += new_address - previous_address;
+
+ memcpy(data, &cwdh, sizeof(cwdh));
+ } else if (memcmp(data, "TGLP", 4) == 0) {
+ BCFNT::TGLP tglp;
+ memcpy(&tglp, data, sizeof(tglp));
+ section_size = tglp.section_size;
+
+ // Relocate the offsets in the TGLP section
+ tglp.sheet_data_offset += new_address - previous_address;
+
+ memcpy(data, &tglp, sizeof(tglp));
+ }
+
+ data += section_size;
+ }
+}
+
+} // namespace BCFNT
+} // namespace APT
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.h b/src/core/hle/service/apt/bcfnt/bcfnt.h
new file mode 100644
index 000000000..388c6bea0
--- /dev/null
+++ b/src/core/hle/service/apt/bcfnt/bcfnt.h
@@ -0,0 +1,87 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/swap.h"
+
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace APT {
+namespace BCFNT { ///< BCFNT Shared Font file structures
+
+struct CFNT {
+ u8 magic[4];
+ u16_le endianness;
+ u16_le header_size;
+ u32_le version;
+ u32_le file_size;
+ u32_le num_blocks;
+};
+
+struct FINF {
+ u8 magic[4];
+ u32_le section_size;
+ u8 font_type;
+ u8 line_feed;
+ u16_le alter_char_index;
+ u8 default_width[3];
+ u8 encoding;
+ u32_le tglp_offset;
+ u32_le cwdh_offset;
+ u32_le cmap_offset;
+ u8 height;
+ u8 width;
+ u8 ascent;
+ u8 reserved;
+};
+
+struct TGLP {
+ u8 magic[4];
+ u32_le section_size;
+ u8 cell_width;
+ u8 cell_height;
+ u8 baseline_position;
+ u8 max_character_width;
+ u32_le sheet_size;
+ u16_le num_sheets;
+ u16_le sheet_image_format;
+ u16_le num_columns;
+ u16_le num_rows;
+ u16_le sheet_width;
+ u16_le sheet_height;
+ u32_le sheet_data_offset;
+};
+
+struct CMAP {
+ u8 magic[4];
+ u32_le section_size;
+ u16_le code_begin;
+ u16_le code_end;
+ u16_le mapping_method;
+ u16_le reserved;
+ u32_le next_cmap_offset;
+};
+
+struct CWDH {
+ u8 magic[4];
+ u32_le section_size;
+ u16_le start_index;
+ u16_le end_index;
+ u32_le next_cwdh_offset;
+};
+
+/**
+ * Relocates the internal addresses of the BCFNT Shared Font to the new base.
+ * @param shared_font SharedMemory object that contains the Shared Font
+ * @param previous_address Previous address at which the offsets in the structure were based.
+ * @param new_address New base for the offsets in the structure.
+ */
+void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address);
+
+} // namespace BCFNT
+} // namespace APT
+} // namespace Service
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/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 6318bf2a7..d2bb8941c 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <cstring>
+#include "common/alignment.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/shared_memory.h"
@@ -41,14 +42,16 @@ static Kernel::SharedPtr<Kernel::Mutex> mutex = nullptr;
void Initialize(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- shared_memory = Kernel::SharedMemory::Create(cmd_buff[1],
- Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::ReadWrite, "CSNDSharedMem");
+ u32 size = Common::AlignUp(cmd_buff[1], Memory::PAGE_SIZE);
+ using Kernel::MemoryPermission;
+ shared_memory = Kernel::SharedMemory::Create(nullptr, size,
+ MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ 0, Kernel::MemoryRegion::BASE, "CSND:SharedMemory");
mutex = Kernel::Mutex::Create(false);
- cmd_buff[1] = 0;
- cmd_buff[2] = 0x4000000;
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = IPC::MoveHandleDesc(2);
cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom();
cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom();
}
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 08e437125..10730d7ac 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;
+ }
- for (size_t i = 0; i < size; i++) {
+ 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 (u32 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);
+ cmd_buff[2] = static_cast<u32>(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,10 +438,11 @@ 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?
+ cmd_buff[2] = 0; // Not using headphones
- LOG_WARNING(Service_DSP, "(STUBBED) called");
+ LOG_DEBUG(Service_DSP, "called");
}
/**
@@ -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/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index e9588cb72..cc51ede0c 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -114,6 +114,7 @@ ResultVal<bool> File::SyncRequest() {
return read.Code();
}
cmd_buff[2] = static_cast<u32>(*read);
+ Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(address), length);
break;
}
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 0c655395e..8ded9b09b 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"
@@ -45,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)));
@@ -291,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
@@ -337,8 +335,9 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
g_interrupt_event->name = "GSP_GPU::interrupt_event";
using Kernel::MemoryPermission;
- g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
- MemoryPermission::ReadWrite, "GSPSharedMem");
+ g_shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000,
+ MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ 0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory");
Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom();
@@ -374,6 +373,9 @@ static void UnregisterInterruptRelayQueue(Service::Interface* self) {
* @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;
@@ -408,6 +410,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
@@ -419,18 +423,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:
{
@@ -517,13 +524,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;
}
@@ -628,6 +630,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"},
@@ -651,8 +682,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"},
{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"},
@@ -673,11 +704,13 @@ Interface::Interface() {
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 55a993bb8..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"
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 1053d0f40..d216cecb4 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -280,8 +280,9 @@ void Init() {
AddService(new HID_SPVR_Interface);
using Kernel::MemoryPermission;
- shared_mem = SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
- MemoryPermission::Read, "HID:SharedMem");
+ shared_mem = SharedMemory::Create(nullptr, 0x1000,
+ MemoryPermission::ReadWrite, MemoryPermission::Read,
+ 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
next_pad_index = 0;
next_touch_index = 0;
diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp
index 505c441c6..079a87e48 100644
--- a/src/core/hle/service/ir/ir.cpp
+++ b/src/core/hle/service/ir/ir.cpp
@@ -94,8 +94,9 @@ void Init() {
AddService(new IR_User_Interface);
using Kernel::MemoryPermission;
- shared_memory = SharedMemory::Create(0x1000, Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory");
+ shared_memory = SharedMemory::Create(nullptr, 0x1000,
+ Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::ReadWrite,
+ 0, Kernel::MemoryRegion::BASE, "IR:SharedMemory");
transfer_shared_memory = nullptr;
// Create event handle(s)
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 0fe3a4d7a..d7e7d4fe3 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,6 +7,7 @@
#include "core/hle/service/service.h"
#include "core/hle/service/ac_u.h"
+#include "core/hle/service/act_a.h"
#include "core/hle/service/act_u.h"
#include "core/hle/service/csnd_snd.h"
#include "core/hle/service/dlp_srvr.h"
@@ -119,6 +120,7 @@ void Init() {
Service::PTM::Init();
AddService(new AC_U::Interface);
+ AddService(new ACT_A::Interface);
AddService(new ACT_U::Interface);
AddService(new CSND_SND::Interface);
AddService(new DLP_SRVR::Interface);
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index ff0af8f12..d3e5d4bca 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -151,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
@@ -568,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);
}
@@ -724,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"},
@@ -741,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..0ce72de87 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -6,7 +6,7 @@
#include "common/logging/log.h"
#include "common/microprofile.h"
-#include "common/profiler.h"
+#include "common/scope_exit.h"
#include "common/string_util.h"
#include "common/symbols.h"
@@ -100,6 +100,7 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add
switch (operation & MEMOP_OPERATION_MASK) {
case MEMOP_FREE:
{
+ // TODO(Subv): What happens if an application tries to FREE a block of memory that has a SharedMemory pointing to it?
if (addr0 >= Memory::HEAP_VADDR && addr0 < Memory::HEAP_VADDR_END) {
ResultCode result = process.HeapFree(addr0, size);
if (result.IsError()) return result;
@@ -161,8 +162,6 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
handle, addr, permissions, other_permissions);
- // TODO(Subv): The same process that created a SharedMemory object can not map it in its own address space
-
SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
if (shared_memory == nullptr)
return ERR_INVALID_HANDLE;
@@ -177,7 +176,7 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
case MemoryPermission::WriteExecute:
case MemoryPermission::ReadWriteExecute:
case MemoryPermission::DontCare:
- return shared_memory->Map(addr, permissions_type,
+ return shared_memory->Map(Kernel::g_current_process.get(), addr, permissions_type,
static_cast<MemoryPermission>(other_permissions));
default:
LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
@@ -197,7 +196,7 @@ static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) {
if (shared_memory == nullptr)
return ERR_INVALID_HANDLE;
- return shared_memory->Unmap(addr);
+ return shared_memory->Unmap(Kernel::g_current_process.get(), addr);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -328,9 +327,9 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
}
}
- HLE::Reschedule(__func__);
+ SCOPE_EXIT({HLE::Reschedule("WaitSynchronizationN");}); // Reschedule after putting the threads to sleep.
- // If thread should wait, then set its state to waiting and then reschedule...
+ // If thread should wait, then set its state to waiting
if (wait_thread) {
// Actually wait the current thread on each object if we decided to wait...
@@ -497,8 +496,16 @@ static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point
break;
}
+ if (processor_id == THREADPROCESSORID_1 || processor_id == THREADPROCESSORID_ALL ||
+ (processor_id == THREADPROCESSORID_DEFAULT && Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1)) {
+ LOG_WARNING(Kernel_SVC, "Newly created thread is allowed to be run in the SysCore, unimplemented.");
+ }
+
CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(
name, entry_point, priority, arg, processor_id, stack_top));
+
+ thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000
+
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread)));
LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
@@ -786,18 +793,44 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
if (size % Memory::PAGE_SIZE != 0)
return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
- // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap
-
- // TODO(Subv): Implement this function properly
+ SharedPtr<SharedMemory> shared_memory = nullptr;
using Kernel::MemoryPermission;
- SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(size,
- (MemoryPermission)my_permission, (MemoryPermission)other_permission);
- // Map the SharedMemory to the specified address
- shared_memory->base_address = addr;
+ auto VerifyPermissions = [](MemoryPermission permission) {
+ // SharedMemory blocks can not be created with Execute permissions
+ switch (permission) {
+ case MemoryPermission::None:
+ case MemoryPermission::Read:
+ case MemoryPermission::Write:
+ case MemoryPermission::ReadWrite:
+ case MemoryPermission::DontCare:
+ return true;
+ default:
+ return false;
+ }
+ };
+
+ if (!VerifyPermissions(static_cast<MemoryPermission>(my_permission)) ||
+ !VerifyPermissions(static_cast<MemoryPermission>(other_permission)))
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+
+ if (addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) {
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ }
+
+ // When trying to create a memory block with address = 0,
+ // if the process has the Shared Device Memory flag in the exheader,
+ // then we have to allocate from the same region as the caller process instead of the BASE region.
+ Kernel::MemoryRegion region = Kernel::MemoryRegion::BASE;
+ if (addr == 0 && Kernel::g_current_process->flags.shared_device_mem)
+ region = Kernel::g_current_process->flags.memory_region;
+
+ shared_memory = SharedMemory::Create(Kernel::g_current_process, size,
+ static_cast<MemoryPermission>(my_permission), static_cast<MemoryPermission>(other_permission), addr, region);
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory)));
- LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr);
+ LOG_WARNING(Kernel_SVC, "called addr=0x%08X", addr);
return RESULT_SUCCESS;
}
@@ -860,6 +893,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 +1068,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 +1079,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..a4dfb7e43 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(), static_cast<u32>(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(), static_cast<u32>(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..98e7ab48f 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 {
/*
@@ -182,11 +178,11 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
const auto& table = reloc_table[current_inprogress];
LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)", current_segment_reloc_table,
- (u32)table.skip, (u32)table.patch);
+ static_cast<u32>(table.skip), static_cast<u32>(table.patch));
pos += table.skip;
s32 num_patches = table.patch;
while (0 < num_patches && pos < end_pos) {
- u32 in_addr = (u8*)pos - program_image.data();
+ u32 in_addr = static_cast<u32>(reinterpret_cast<u8*>(pos) - program_image.data());
u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)",
base_addr + in_addr, addr, current_segment_reloc_table, *pos);
@@ -288,7 +284,7 @@ ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& ro
// Check if the 3DSX has a RomFS...
if (hdr.fs_offset != 0) {
u32 romfs_offset = hdr.fs_offset;
- u32 romfs_size = file.GetSize() - hdr.fs_offset;
+ u32 romfs_size = static_cast<u32>(file.GetSize()) - hdr.fs_offset;
LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
@@ -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 886501c41..af3f62248 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -90,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()) {
@@ -111,38 +133,29 @@ 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:
- {
- AppLoader_THREEDSX app_loader(std::move(file), filename_filename, filename);
- // Load application and 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;
- }
-
- // 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();
+ ResultStatus result = app_loader->Load();
if (ResultStatus::Success == result) {
- Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
+ Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*app_loader), Service::FS::ArchiveIdCode::RomFS);
+ return ResultStatus::Success;
}
return result;
}
+ // Standard ELF file format...
+ case FileType::ELF:
+ return app_loader->Load();
+
// CIA file format...
case FileType::CIA:
return ResultStatus::ErrorNotImplemented;
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 84a4ce5fc..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;
@@ -78,6 +80,51 @@ 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 e63cab33f..7391bdb26 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -156,6 +156,9 @@ ResultStatus AppLoader_NCCH::LoadExec() {
Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(
static_cast<Kernel::ResourceLimitCategory>(exheader_header.arm11_system_local_caps.resource_limit_category));
+ // Set the default CPU core for this process
+ Kernel::g_current_process->ideal_processor = exheader_header.arm11_system_local_caps.ideal_processor;
+
// Copy data while converting endianess
std::array<u32, ARRAY_SIZE(exheader_header.arm11_kernel_caps.descriptors)> kernel_caps;
std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps));
@@ -173,8 +176,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];
@@ -186,7 +193,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 {
@@ -215,9 +222,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;
@@ -255,7 +262,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);
@@ -282,6 +289,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..126d60471 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -100,15 +100,9 @@ enum : VAddr {
SHARED_PAGE_SIZE = 0x00001000,
SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE,
- // TODO(yuriks): The size of this area is dynamic, the kernel grows
- // it as more and more threads are created. For now we'll just use a
- // hardcoded value.
/// Area where TLS (Thread-Local Storage) buffers are allocated.
TLS_AREA_VADDR = 0x1FF82000,
TLS_ENTRY_SIZE = 0x200,
- TLS_AREA_SIZE = 300 * TLS_ENTRY_SIZE + 0x800, // Space for up to 300 threads + round to page size
- TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
-
/// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS.
NEW_LINEAR_HEAP_VADDR = 0x30000000,
@@ -148,4 +142,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 4034b795a..a61f25cbe 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 {
@@ -58,6 +59,7 @@ struct Values {
// Renderer
bool use_hw_renderer;
bool use_shader_jit;
+ bool use_scaled_resolution;
float bg_red;
float bg_green;
@@ -65,9 +67,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.cpp b/src/core/tracer/recorder.cpp
index c6dc35c83..7abaacf70 100644
--- a/src/core/tracer/recorder.cpp
+++ b/src/core/tracer/recorder.cpp
@@ -26,17 +26,17 @@ void Recorder::Finish(const std::string& filename) {
// Calculate file offsets
auto& initial = header.initial_state_offsets;
- initial.gpu_registers_size = initial_state.gpu_registers.size();
- initial.lcd_registers_size = initial_state.lcd_registers.size();
- initial.pica_registers_size = initial_state.pica_registers.size();
- initial.default_attributes_size = initial_state.default_attributes.size();
- initial.vs_program_binary_size = initial_state.vs_program_binary.size();
- initial.vs_swizzle_data_size = initial_state.vs_swizzle_data.size();
- initial.vs_float_uniforms_size = initial_state.vs_float_uniforms.size();
- initial.gs_program_binary_size = initial_state.gs_program_binary.size();
- initial.gs_swizzle_data_size = initial_state.gs_swizzle_data.size();
- initial.gs_float_uniforms_size = initial_state.gs_float_uniforms.size();
- header.stream_size = stream.size();
+ initial.gpu_registers_size = static_cast<u32>(initial_state.gpu_registers.size());
+ initial.lcd_registers_size = static_cast<u32>(initial_state.lcd_registers.size());
+ initial.pica_registers_size = static_cast<u32>(initial_state.pica_registers.size());
+ initial.default_attributes_size = static_cast<u32>(initial_state.default_attributes.size());
+ initial.vs_program_binary_size = static_cast<u32>(initial_state.vs_program_binary.size());
+ initial.vs_swizzle_data_size = static_cast<u32>(initial_state.vs_swizzle_data.size());
+ initial.vs_float_uniforms_size = static_cast<u32>(initial_state.vs_float_uniforms.size());
+ initial.gs_program_binary_size = static_cast<u32>(initial_state.gs_program_binary.size());
+ initial.gs_swizzle_data_size = static_cast<u32>(initial_state.gs_swizzle_data.size());
+ initial.gs_float_uniforms_size = static_cast<u32>(initial_state.gs_float_uniforms.size());
+ header.stream_size = static_cast<u32>(stream.size());
initial.gpu_registers = sizeof(header);
initial.lcd_registers = initial.gpu_registers + initial.gpu_registers_size * sizeof(u32);
@@ -68,7 +68,7 @@ void Recorder::Finish(const std::string& filename) {
DEBUG_ASSERT(stream_element.extra_data.size() == 0);
break;
}
- header.stream_offset += stream_element.extra_data.size();
+ header.stream_offset += static_cast<u32>(stream_element.extra_data.size());
}
try {
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>