summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt3
-rw-r--r--src/audio_core/hle/common.h35
-rw-r--r--src/audio_core/hle/dsp.h17
-rw-r--r--src/audio_core/hle/filter.cpp115
-rw-r--r--src/audio_core/hle/filter.h112
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/logging/log.h2
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp83
-rw-r--r--src/core/hle/result.h1
-rw-r--r--src/core/hle/service/cecd/cecd.cpp29
-rw-r--r--src/core/hle/service/cecd/cecd.h23
-rw-r--r--src/core/hle/service/cecd/cecd_u.cpp5
-rw-r--r--src/core/hle/service/dlp_srvr.cpp36
-rw-r--r--src/core/hle/service/dlp_srvr.h23
-rw-r--r--src/core/hle/service/gsp_gpu.cpp221
-rw-r--r--src/core/hle/service/gsp_gpu.h2
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp3
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h12
20 files changed, 600 insertions, 128 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index c4bad8cb0..869da5e83 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -2,13 +2,16 @@ set(SRCS
audio_core.cpp
codec.cpp
hle/dsp.cpp
+ hle/filter.cpp
hle/pipe.cpp
)
set(HEADERS
audio_core.h
codec.h
+ hle/common.h
hle/dsp.h
+ hle/filter.h
hle/pipe.h
sink.h
)
diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h
new file mode 100644
index 000000000..37d441eb2
--- /dev/null
+++ b/src/audio_core/hle/common.h
@@ -0,0 +1,35 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+
+#include "audio_core/audio_core.h"
+
+#include "common/common_types.h"
+
+namespace DSP {
+namespace HLE {
+
+/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
+using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>;
+
+/// The DSP is quadraphonic internally.
+using QuadFrame32 = std::array<std::array<s32, 4>, AudioCore::samples_per_frame>;
+
+/**
+ * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place.
+ * FilterT::ProcessSample is called sequentially on the samples.
+ */
+template<typename FrameT, typename FilterT>
+void FilterFrame(FrameT& frame, FilterT& filter) {
+ std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const typename FrameT::value_type& sample) {
+ return filter.ProcessSample(sample);
+ });
+}
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
index 376436c29..c15ef0b7a 100644
--- a/src/audio_core/hle/dsp.h
+++ b/src/audio_core/hle/dsp.h
@@ -126,8 +126,11 @@ struct SourceConfiguration {
union {
u32_le dirty_raw;
+ BitField<0, 1, u32_le> format_dirty;
+ BitField<1, 1, u32_le> mono_or_stereo_dirty;
BitField<2, 1, u32_le> adpcm_coefficients_dirty;
BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
+ BitField<4, 1, u32_le> partial_reset_flag;
BitField<16, 1, u32_le> enable_dirty;
BitField<17, 1, u32_le> interpolation_dirty;
@@ -143,8 +146,7 @@ struct SourceConfiguration {
BitField<27, 1, u32_le> gain_2_dirty;
BitField<28, 1, u32_le> sync_dirty;
BitField<29, 1, u32_le> reset_flag;
-
- BitField<31, 1, u32_le> embedded_buffer_dirty;
+ BitField<30, 1, u32_le> embedded_buffer_dirty;
};
// Gain control
@@ -175,7 +177,8 @@ struct SourceConfiguration {
/**
* This is the simplest normalized first-order digital recursive filter.
* The transfer function of this filter is:
- * H(z) = b0 / (1 + a1 z^-1)
+ * H(z) = b0 / (1 - a1 z^-1)
+ * Note the feedbackward coefficient is negated.
* Values are signed fixed point with 15 fractional bits.
*/
struct SimpleFilter {
@@ -192,11 +195,11 @@ struct SourceConfiguration {
* Values are signed fixed point with 14 fractional bits.
*/
struct BiquadFilter {
- s16_le b0;
- s16_le b1;
- s16_le b2;
- s16_le a1;
s16_le a2;
+ s16_le a1;
+ s16_le b2;
+ s16_le b1;
+ s16_le b0;
};
union {
diff --git a/src/audio_core/hle/filter.cpp b/src/audio_core/hle/filter.cpp
new file mode 100644
index 000000000..2c65ef026
--- /dev/null
+++ b/src/audio_core/hle/filter.cpp
@@ -0,0 +1,115 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstddef>
+
+#include "audio_core/hle/common.h"
+#include "audio_core/hle/dsp.h"
+#include "audio_core/hle/filter.h"
+
+#include "common/common_types.h"
+#include "common/math_util.h"
+
+namespace DSP {
+namespace HLE {
+
+void SourceFilters::Reset() {
+ Enable(false, false);
+}
+
+void SourceFilters::Enable(bool simple, bool biquad) {
+ simple_filter_enabled = simple;
+ biquad_filter_enabled = biquad;
+
+ if (!simple)
+ simple_filter.Reset();
+ if (!biquad)
+ biquad_filter.Reset();
+}
+
+void SourceFilters::Configure(SourceConfiguration::Configuration::SimpleFilter config) {
+ simple_filter.Configure(config);
+}
+
+void SourceFilters::Configure(SourceConfiguration::Configuration::BiquadFilter config) {
+ biquad_filter.Configure(config);
+}
+
+void SourceFilters::ProcessFrame(StereoFrame16& frame) {
+ if (!simple_filter_enabled && !biquad_filter_enabled)
+ return;
+
+ if (simple_filter_enabled) {
+ FilterFrame(frame, simple_filter);
+ }
+
+ if (biquad_filter_enabled) {
+ FilterFrame(frame, biquad_filter);
+ }
+}
+
+// SimpleFilter
+
+void SourceFilters::SimpleFilter::Reset() {
+ y1.fill(0);
+ // Configure as passthrough.
+ a1 = 0;
+ b0 = 1 << 15;
+}
+
+void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) {
+ a1 = config.a1;
+ b0 = config.b0;
+}
+
+std::array<s16, 2> SourceFilters::SimpleFilter::ProcessSample(const std::array<s16, 2>& x0) {
+ std::array<s16, 2> y0;
+ for (size_t i = 0; i < 2; i++) {
+ const s32 tmp = (b0 * x0[i] + a1 * y1[i]) >> 15;
+ y0[i] = MathUtil::Clamp(tmp, -32768, 32767);
+ }
+
+ y1 = y0;
+
+ return y0;
+}
+
+// BiquadFilter
+
+void SourceFilters::BiquadFilter::Reset() {
+ x1.fill(0);
+ x2.fill(0);
+ y1.fill(0);
+ y2.fill(0);
+ // Configure as passthrough.
+ a1 = a2 = b1 = b2 = 0;
+ b0 = 1 << 14;
+}
+
+void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) {
+ a1 = config.a1;
+ a2 = config.a2;
+ b0 = config.b0;
+ b1 = config.b1;
+ b2 = config.b2;
+}
+
+std::array<s16, 2> SourceFilters::BiquadFilter::ProcessSample(const std::array<s16, 2>& x0) {
+ std::array<s16, 2> y0;
+ for (size_t i = 0; i < 2; i++) {
+ const s32 tmp = (b0 * x0[i] + b1 * x1[i] + b2 * x2[i] + a1 * y1[i] + a2 * y2[i]) >> 14;
+ y0[i] = MathUtil::Clamp(tmp, -32768, 32767);
+ }
+
+ x2 = x1;
+ x1 = x0;
+ y2 = y1;
+ y1 = y0;
+
+ return y0;
+}
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/filter.h b/src/audio_core/hle/filter.h
new file mode 100644
index 000000000..75738f600
--- /dev/null
+++ b/src/audio_core/hle/filter.h
@@ -0,0 +1,112 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "audio_core/hle/common.h"
+#include "audio_core/hle/dsp.h"
+
+#include "common/common_types.h"
+
+namespace DSP {
+namespace HLE {
+
+/// Preprocessing filters. There is an independent set of filters for each Source.
+class SourceFilters final {
+ SourceFilters() { Reset(); }
+
+ /// Reset internal state.
+ void Reset();
+
+ /**
+ * Enable/Disable filters
+ * See also: SourceConfiguration::Configuration::simple_filter_enabled,
+ * SourceConfiguration::Configuration::biquad_filter_enabled.
+ * @param simple If true, enables the simple filter. If false, disables it.
+ * @param simple If true, enables the biquad filter. If false, disables it.
+ */
+ void Enable(bool simple, bool biquad);
+
+ /**
+ * Configure simple filter.
+ * @param config Configuration from DSP shared memory.
+ */
+ void Configure(SourceConfiguration::Configuration::SimpleFilter config);
+
+ /**
+ * Configure biquad filter.
+ * @param config Configuration from DSP shared memory.
+ */
+ void Configure(SourceConfiguration::Configuration::BiquadFilter config);
+
+ /**
+ * Processes a frame in-place.
+ * @param frame Audio samples to process. Modified in-place.
+ */
+ void ProcessFrame(StereoFrame16& frame);
+
+private:
+ bool simple_filter_enabled;
+ bool biquad_filter_enabled;
+
+ struct SimpleFilter {
+ SimpleFilter() { Reset(); }
+
+ /// Resets internal state.
+ void Reset();
+
+ /**
+ * Configures this filter with application settings.
+ * @param config Configuration from DSP shared memory.
+ */
+ void Configure(SourceConfiguration::Configuration::SimpleFilter config);
+
+ /**
+ * Processes a single stereo PCM16 sample.
+ * @param x0 Input sample
+ * @return Output sample
+ */
+ std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0);
+
+ private:
+ // Configuration
+ s32 a1, b0;
+ // Internal state
+ std::array<s16, 2> y1;
+ } simple_filter;
+
+ struct BiquadFilter {
+ BiquadFilter() { Reset(); }
+
+ /// Resets internal state.
+ void Reset();
+
+ /**
+ * Configures this filter with application settings.
+ * @param config Configuration from DSP shared memory.
+ */
+ void Configure(SourceConfiguration::Configuration::BiquadFilter config);
+
+ /**
+ * Processes a single stereo PCM16 sample.
+ * @param x0 Input sample
+ * @return Output sample
+ */
+ std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0);
+
+ private:
+ // Configuration
+ s32 a1, a2, b0, b1, b2;
+ // Internal state
+ std::array<s16, 2> x1;
+ std::array<s16, 2> x2;
+ std::array<s16, 2> y1;
+ std::array<s16, 2> y2;
+ } biquad_filter;
+};
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 4c86151ab..cfbfbc2a7 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -46,8 +46,10 @@ namespace Log {
SUB(Service, NIM) \
SUB(Service, NWM) \
SUB(Service, CAM) \
+ SUB(Service, CECD) \
SUB(Service, CFG) \
SUB(Service, DSP) \
+ SUB(Service, DLP) \
SUB(Service, HID) \
SUB(Service, SOC) \
SUB(Service, IR) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index e4c39c308..4f6856f3d 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -61,8 +61,10 @@ enum class Class : ClassType {
Service_NIM, ///< The NIM (Network interface manager) service
Service_NWM, ///< The NWM (Network wlan manager) service
Service_CAM, ///< The CAM (Camera) service
+ Service_CECD, ///< The CECD service
Service_CFG, ///< The CFG (Configuration) service
Service_DSP, ///< The DSP (DSP control) service
+ Service_DLP, ///< The DLP (Download Play) service
Service_HID, ///< The HID (Human interface device) service
Service_SOC, ///< The SOC (Socket) service
Service_IR, ///< The IR service
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 3473e2f5b..a8d891689 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -68,6 +68,7 @@ set(SRCS
hle/service/cfg/cfg_s.cpp
hle/service/cfg/cfg_u.cpp
hle/service/csnd_snd.cpp
+ hle/service/dlp_srvr.cpp
hle/service/dsp_dsp.cpp
hle/service/err_f.cpp
hle/service/frd/frd.cpp
@@ -200,6 +201,7 @@ set(HEADERS
hle/service/cfg/cfg_s.h
hle/service/cfg/cfg_u.h
hle/service/csnd_snd.h
+ hle/service/dlp_srvr.h
hle/service/dsp_dsp.h
hle/service/err_f.h
hle/service/frd/frd.h
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 5f8826034..9ed61947e 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -36,7 +36,8 @@ enum {
CALL = (1 << 4),
RET = (1 << 5),
END_OF_PAGE = (1 << 6),
- THUMB = (1 << 7)
+ THUMB = (1 << 7),
+ SINGLE_STEP = (1 << 8)
};
#define RM BITS(sht_oper, 0, 3)
@@ -3466,7 +3467,35 @@ enum {
MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64));
-static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) {
+static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, const u32 phys_addr, ARM_INST_PTR& inst_base) {
+ unsigned int inst_size = 4;
+ unsigned int inst = Memory::Read32(phys_addr & 0xFFFFFFFC);
+
+ // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction
+ if (cpu->TFlag) {
+ u32 arm_inst;
+ ThumbDecodeStatus state = DecodeThumbInstruction(inst, phys_addr, &arm_inst, &inst_size, &inst_base);
+
+ // We have translated the Thumb branch instruction in the Thumb decoder
+ if (state == ThumbDecodeStatus::BRANCH) {
+ return inst_size;
+ }
+ inst = arm_inst;
+ }
+
+ int idx;
+ if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) {
+ std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst);
+ LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst);
+ LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
+ CITRA_IGNORE_EXIT(-1);
+ }
+ inst_base = arm_instruction_trans[idx](inst, idx);
+
+ return inst_size;
+}
+
+static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) {
Common::Profiling::ScopeTimer timer_decode(profile_decode);
MICROPROFILE_SCOPE(DynCom_Decode);
@@ -3475,8 +3504,6 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) {
// Go on next, until terminal instruction
// Save start addr of basicblock in CreamCache
ARM_INST_PTR inst_base = nullptr;
- unsigned int inst, inst_size = 4;
- int idx;
int ret = NON_BRANCH;
int size = 0; // instruction size of basic block
bb_start = top;
@@ -3485,30 +3512,10 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) {
u32 pc_start = cpu->Reg[15];
while (ret == NON_BRANCH) {
- inst = Memory::Read32(phys_addr & 0xFFFFFFFC);
+ unsigned int inst_size = InterpreterTranslateInstruction(cpu, phys_addr, inst_base);
size++;
- // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction
- if (cpu->TFlag) {
- u32 arm_inst;
- ThumbDecodeStatus state = DecodeThumbInstruction(inst, phys_addr, &arm_inst, &inst_size, &inst_base);
-
- // We have translated the Thumb branch instruction in the Thumb decoder
- if (state == ThumbDecodeStatus::BRANCH) {
- goto translated;
- }
- inst = arm_inst;
- }
-
- if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) {
- std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst);
- LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst);
- LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
- CITRA_IGNORE_EXIT(-1);
- }
- inst_base = arm_instruction_trans[idx](inst, idx);
-translated:
phys_addr += inst_size;
if ((phys_addr & 0xfff) == 0) {
@@ -3522,6 +3529,27 @@ translated:
return KEEP_GOING;
}
+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;
+ bb_start = top;
+
+ u32 phys_addr = addr;
+ u32 pc_start = cpu->Reg[15];
+
+ InterpreterTranslateInstruction(cpu, phys_addr, inst_base);
+
+ if (inst_base->br == NON_BRANCH) {
+ inst_base->br = SINGLE_STEP;
+ }
+
+ cpu->instruction_cache[pc_start] = bb_start;
+
+ return KEEP_GOING;
+}
+
static int clz(unsigned int x) {
int n;
if (x == 0) return (32);
@@ -3871,8 +3899,11 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
auto itr = cpu->instruction_cache.find(cpu->Reg[15]);
if (itr != cpu->instruction_cache.end()) {
ptr = itr->second;
+ } else if (cpu->NumInstrsToExecute != 1) {
+ if (InterpreterTranslateBlock(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION)
+ goto END;
} else {
- if (InterpreterTranslate(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION)
+ if (InterpreterTranslateSingle(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION)
goto END;
}
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 0cb76ba1c..2d22652d9 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -24,6 +24,7 @@ enum class ErrorDescription : u32 {
FS_InvalidOpenFlags = 230,
FS_NotAFile = 250,
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
+ OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage
FS_InvalidPath = 702,
InvalidSection = 1000,
TooLarge = 1001,
diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp
index 6d79ce9b4..e6e36e7ec 100644
--- a/src/core/hle/service/cecd/cecd.cpp
+++ b/src/core/hle/service/cecd/cecd.cpp
@@ -4,6 +4,7 @@
#include "common/logging/log.h"
+#include "core/hle/kernel/event.h"
#include "core/hle/service/service.h"
#include "core/hle/service/cecd/cecd.h"
#include "core/hle/service/cecd/cecd_s.h"
@@ -12,14 +13,38 @@
namespace Service {
namespace CECD {
-void Init() {
- using namespace Kernel;
+static Kernel::SharedPtr<Kernel::Event> cecinfo_event;
+static Kernel::SharedPtr<Kernel::Event> change_state_event;
+
+void GetCecInfoEventHandle(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).MoveFrom(); // Event handle
+
+ LOG_WARNING(Service_CECD, "(STUBBED) called");
+}
+
+void GetChangeStateEventHandle(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).MoveFrom(); // Event handle
+
+ LOG_WARNING(Service_CECD, "(STUBBED) called");
+}
+
+void Init() {
AddService(new CECD_S_Interface);
AddService(new CECD_U_Interface);
+
+ cecinfo_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD_U::cecinfo_event");
+ change_state_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD_U::change_state_event");
}
void Shutdown() {
+ cecinfo_event = nullptr;
+ change_state_event = nullptr;
}
} // namespace CECD
diff --git a/src/core/hle/service/cecd/cecd.h b/src/core/hle/service/cecd/cecd.h
index 9e158521b..89a8d67bb 100644
--- a/src/core/hle/service/cecd/cecd.h
+++ b/src/core/hle/service/cecd/cecd.h
@@ -5,8 +5,31 @@
#pragma once
namespace Service {
+
+class Interface;
+
namespace CECD {
+/**
+ * GetCecInfoEventHandle service function
+ * Inputs:
+ * 0: 0x000F0000
+ * Outputs:
+ * 1: ResultCode
+ * 3: Event Handle
+ */
+void GetCecInfoEventHandle(Service::Interface* self);
+
+/**
+ * GetChangeStateEventHandle service function
+ * Inputs:
+ * 0: 0x00100000
+ * Outputs:
+ * 1: ResultCode
+ * 3: Event Handle
+ */
+void GetChangeStateEventHandle(Service::Interface* self);
+
/// Initialize CECD service(s)
void Init();
diff --git a/src/core/hle/service/cecd/cecd_u.cpp b/src/core/hle/service/cecd/cecd_u.cpp
index 9b720a738..ace1c73c0 100644
--- a/src/core/hle/service/cecd/cecd_u.cpp
+++ b/src/core/hle/service/cecd/cecd_u.cpp
@@ -2,13 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/service/cecd/cecd.h"
#include "core/hle/service/cecd/cecd_u.h"
namespace Service {
namespace CECD {
static const Interface::FunctionInfo FunctionTable[] = {
- { 0x00120104, nullptr, "ReadSavedData" },
+ {0x000F0000, GetCecInfoEventHandle, "GetCecInfoEventHandle"},
+ {0x00100000, GetChangeStateEventHandle, "GetChangeStateEventHandle"},
+ {0x00120104, nullptr, "ReadSavedData"},
};
CECD_U_Interface::CECD_U_Interface() {
diff --git a/src/core/hle/service/dlp_srvr.cpp b/src/core/hle/service/dlp_srvr.cpp
new file mode 100644
index 000000000..1f30188da
--- /dev/null
+++ b/src/core/hle/service/dlp_srvr.cpp
@@ -0,0 +1,36 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/dlp_srvr.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace DLP_SRVR
+
+namespace DLP_SRVR {
+
+static void unk_0x000E0040(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 0;
+
+ LOG_WARNING(Service_DLP, "(STUBBED) called");
+}
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010183, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Finalize"},
+ {0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/dlp_srvr.h b/src/core/hle/service/dlp_srvr.h
new file mode 100644
index 000000000..d65d00814
--- /dev/null
+++ b/src/core/hle/service/dlp_srvr.h
@@ -0,0 +1,23 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace DLP_SRVR
+
+namespace DLP_SRVR {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "dlp:SRVR";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 2ace2cade..0c655395e 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -31,6 +31,13 @@ const static u32 REGS_BEGIN = 0x1EB00000;
namespace GSP_GPU {
+const ResultCode ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED(ErrorDescription::OutofRangeOrMisalignedAddress, ErrorModule::GX,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02A01
+const ResultCode ERR_GSP_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BF2
+const ResultCode ERR_GSP_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BEC
+
/// Event triggered when GSP interrupt has been signalled
Kernel::SharedPtr<Kernel::Event> g_interrupt_event;
/// GSP shared memoryings
@@ -59,47 +66,87 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
}
/**
- * Checks if the parameters in a register write call are valid and logs in the case that
- * they are not
- * @param base_address The first address in the sequence of registers that will be written
- * @param size_in_bytes The number of registers that will be written
- * @return true if the parameters are valid, false otherwise
+ * Writes sequential GSP GPU hardware registers using an array of source data
+ *
+ * @param base_address The address of the first register in the sequence
+ * @param size_in_bytes The number of registers to update (size of data)
+ * @param data A pointer to the source data
+ * @return RESULT_SUCCESS if the parameters are valid, error code otherwise
*/
-static bool CheckWriteParameters(u32 base_address, u32 size_in_bytes) {
- // TODO: Return proper error codes
- if (base_address + size_in_bytes >= 0x420000) {
- LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)",
+static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
+ // This magic number is verified to be done by the gsp module
+ const u32 max_size_in_bytes = 0x80;
+
+ if (base_address & 3 || base_address >= 0x420000) {
+ LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)",
base_address, size_in_bytes);
- return false;
- }
+ return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED;
+ } else if (size_in_bytes <= max_size_in_bytes) {
+ if (size_in_bytes & 3) {
+ LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes);
+ return ERR_GSP_REGS_MISALIGNED;
+ } else {
+ while (size_in_bytes > 0) {
+ HW::Write<u32>(base_address + REGS_BEGIN, *data);
+
+ size_in_bytes -= 4;
+ ++data;
+ base_address += 4;
+ }
+ return RESULT_SUCCESS;
+ }
- // size should be word-aligned
- if ((size_in_bytes % 4) != 0) {
- LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes);
- return false;
+ } else {
+ LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes);
+ return ERR_GSP_REGS_INVALID_SIZE;
}
-
- return true;
}
/**
- * Writes sequential GSP GPU hardware registers using an array of source data
+ * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks.
+ * For each register, the value is updated only where the mask is high
*
* @param base_address The address of the first register in the sequence
* @param size_in_bytes The number of registers to update (size of data)
- * @param data A pointer to the source data
+ * @param data A pointer to the source data to use for updates
+ * @param masks A pointer to the masks
+ * @return RESULT_SUCCESS if the parameters are valid, error code otherwise
*/
-static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
- // TODO: Return proper error codes
- if (!CheckWriteParameters(base_address, size_in_bytes))
- return;
+static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) {
+ // This magic number is verified to be done by the gsp module
+ const u32 max_size_in_bytes = 0x80;
- while (size_in_bytes > 0) {
- HW::Write<u32>(base_address + REGS_BEGIN, *data);
+ if (base_address & 3 || base_address >= 0x420000) {
+ LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)",
+ base_address, size_in_bytes);
+ return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED;
+ } else if (size_in_bytes <= max_size_in_bytes) {
+ if (size_in_bytes & 3) {
+ LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes);
+ return ERR_GSP_REGS_MISALIGNED;
+ } else {
+ while (size_in_bytes > 0) {
+ const u32 reg_address = base_address + REGS_BEGIN;
+
+ u32 reg_value;
+ HW::Read<u32>(reg_value, reg_address);
+
+ // Update the current value of the register only for set mask bits
+ reg_value = (reg_value & ~*masks) | (*data | *masks);
+
+ HW::Write<u32>(reg_address, reg_value);
+
+ size_in_bytes -= 4;
+ ++data;
+ ++masks;
+ base_address += 4;
+ }
+ return RESULT_SUCCESS;
+ }
- size_in_bytes -= 4;
- ++data;
- base_address += 4;
+ } else {
+ LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes);
+ return ERR_GSP_REGS_INVALID_SIZE;
}
}
@@ -120,39 +167,7 @@ static void WriteHWRegs(Service::Interface* self) {
u32* src = (u32*)Memory::GetPointer(cmd_buff[4]);
- WriteHWRegs(reg_addr, size, src);
-}
-
-/**
- * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks.
- * For each register, the value is updated only where the mask is high
- *
- * @param base_address The address of the first register in the sequence
- * @param size_in_bytes The number of registers to update (size of data)
- * @param data A pointer to the source data to use for updates
- * @param masks A pointer to the masks
- */
-static void WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) {
- // TODO: Return proper error codes
- if (!CheckWriteParameters(base_address, size_in_bytes))
- return;
-
- while (size_in_bytes > 0) {
- const u32 reg_address = base_address + REGS_BEGIN;
-
- u32 reg_value;
- HW::Read<u32>(reg_value, reg_address);
-
- // Update the current value of the register only for set mask bits
- reg_value = (reg_value & ~*masks) | (*data | *masks);
-
- HW::Write<u32>(reg_address, reg_value);
-
- size_in_bytes -= 4;
- ++data;
- ++masks;
- base_address += 4;
- }
+ cmd_buff[1] = WriteHWRegs(reg_addr, size, src).raw;
}
/**
@@ -174,7 +189,7 @@ static void WriteHWRegsWithMask(Service::Interface* self) {
u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]);
u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]);
- WriteHWRegsWithMask(reg_addr, size, src_data, mask_data);
+ cmd_buff[1] = WriteHWRegsWithMask(reg_addr, size, src_data, mask_data).raw;
}
/// Read a GSP GPU hardware register
@@ -206,27 +221,27 @@ static void ReadHWRegs(Service::Interface* self) {
}
}
-void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
+ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
u32 base_address = 0x400000;
PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left);
PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right);
if (info.active_fb == 0) {
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), 4,
- &phys_address_left);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), 4,
- &phys_address_right);
+ WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)),
+ 4, &phys_address_left);
+ WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)),
+ 4, &phys_address_right);
} else {
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), 4,
- &phys_address_left);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), 4,
- &phys_address_right);
+ WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)),
+ 4, &phys_address_left);
+ WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)),
+ 4, &phys_address_right);
}
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), 4,
- &info.stride);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), 4,
- &info.format);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), 4,
- &info.shown_fb);
+ WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)),
+ 4, &info.stride);
+ WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)),
+ 4, &info.format);
+ WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)),
+ 4, &info.shown_fb);
if (Pica::g_debug_context)
Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr);
@@ -234,6 +249,8 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
if (screen_id == 0) {
MicroProfileFlip();
}
+
+ return RESULT_SUCCESS;
}
/**
@@ -251,9 +268,8 @@ static void SetBufferSwap(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 screen_id = cmd_buff[1];
FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2];
- SetBufferSwap(screen_id, *fb_info);
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = SetBufferSwap(screen_id, *fb_info).raw;
}
/**
@@ -286,6 +302,22 @@ static void FlushDataCache(Service::Interface* self) {
}
/**
+ * GSP_GPU::SetAxiConfigQoSMode service function
+ * Inputs:
+ * 1 : Mode, unused in emulator
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetAxiConfigQoSMode(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 mode = cmd_buff[1];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+
+ LOG_WARNING(Service_GSP, "(STUBBED) called mode=0x%08X", mode);
+}
+
+/**
* GSP_GPU::RegisterInterruptRelayQueue service function
* Inputs:
* 1 : "Flags" field, purpose is unknown
@@ -302,6 +334,12 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]);
ASSERT_MSG((g_interrupt_event != nullptr), "handle is not valid!");
+ g_interrupt_event->name = "GSP_GPU::interrupt_event";
+
+ using Kernel::MemoryPermission;
+ g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
+ MemoryPermission::ReadWrite, "GSPSharedMem");
+
Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom();
// This specific code is required for a successful initialization, rather than 0
@@ -314,6 +352,22 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
}
/**
+ * GSP_GPU::UnregisterInterruptRelayQueue service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void UnregisterInterruptRelayQueue(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ g_shared_memory = nullptr;
+ g_interrupt_event = nullptr;
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_GSP, "called");
+}
+
+/**
* Signals that the specified interrupt type has occurred to userland code
* @param interrupt_id ID of interrupt that is being signalled
* @todo This should probably take a thread_id parameter and only signal this thread?
@@ -591,11 +645,11 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000D0140, nullptr, "SetDisplayTransfer"},
{0x000E0180, nullptr, "SetTextureCopy"},
{0x000F0200, nullptr, "SetMemoryFill"},
- {0x00100040, nullptr, "SetAxiConfigQoSMode"},
+ {0x00100040, SetAxiConfigQoSMode, "SetAxiConfigQoSMode"},
{0x00110040, nullptr, "SetPerfLogMode"},
{0x00120000, nullptr, "GetPerfLog"},
{0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"},
- {0x00140000, nullptr, "UnregisterInterruptRelayQueue"},
+ {0x00140000, UnregisterInterruptRelayQueue, "UnregisterInterruptRelayQueue"},
{0x00150002, nullptr, "TryAcquireRight"},
{0x00160042, nullptr, "AcquireRight"},
{0x00170000, nullptr, "ReleaseRight"},
@@ -616,10 +670,7 @@ Interface::Interface() {
Register(FunctionTable);
g_interrupt_event = nullptr;
-
- using Kernel::MemoryPermission;
- g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
- MemoryPermission::ReadWrite, "GSPSharedMem");
+ g_shared_memory = nullptr;
g_thread_id = 0;
}
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h
index 0e2f7a21e..55a993bb8 100644
--- a/src/core/hle/service/gsp_gpu.h
+++ b/src/core/hle/service/gsp_gpu.h
@@ -194,7 +194,7 @@ public:
*/
void SignalInterrupt(InterruptId interrupt_id);
-void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info);
+ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info);
/**
* Retrieves the framebuffer info stored in the GSP shared memory for the
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 35b648409..7844d2330 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -9,6 +9,7 @@
#include "core/hle/service/ac_u.h"
#include "core/hle/service/act_u.h"
#include "core/hle/service/csnd_snd.h"
+#include "core/hle/service/dlp_srvr.h"
#include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/err_f.h"
#include "core/hle/service/gsp_gpu.h"
@@ -121,6 +122,7 @@ void Init() {
AddService(new AC_U::Interface);
AddService(new ACT_U::Interface);
AddService(new CSND_SND::Interface);
+ AddService(new DLP_SRVR::Interface);
AddService(new DSP_DSP::Interface);
AddService(new GSP_GPU::Interface);
AddService(new GSP_LCD::Interface);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 1fadcf5ae..21ad9ee77 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -808,6 +808,9 @@ void RasterizerOpenGL::SyncFramebuffer() {
ReloadDepthBuffer();
}
+
+ ASSERT_MSG(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE,
+ "OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
void RasterizerOpenGL::SyncCullMode() {
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h
index 3d6c4e9e5..fd3617d77 100644
--- a/src/video_core/renderer_opengl/pica_to_gl.h
+++ b/src/video_core/renderer_opengl/pica_to_gl.h
@@ -22,7 +22,7 @@ inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) {
};
// Range check table for input
- if (mode >= ARRAY_SIZE(filter_mode_table)) {
+ if (static_cast<size_t>(mode) >= ARRAY_SIZE(filter_mode_table)) {
LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode);
UNREACHABLE();
@@ -51,7 +51,7 @@ inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) {
};
// Range check table for input
- if (mode >= ARRAY_SIZE(wrap_mode_table)) {
+ if (static_cast<size_t>(mode) >= ARRAY_SIZE(wrap_mode_table)) {
LOG_CRITICAL(Render_OpenGL, "Unknown texture wrap mode %d", mode);
UNREACHABLE();
@@ -91,7 +91,7 @@ inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) {
};
// Range check table for input
- if ((unsigned)factor >= ARRAY_SIZE(blend_func_table)) {
+ if (static_cast<size_t>(factor) >= ARRAY_SIZE(blend_func_table)) {
LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %d", factor);
UNREACHABLE();
@@ -122,7 +122,7 @@ inline GLenum LogicOp(Pica::Regs::LogicOp op) {
};
// Range check table for input
- if ((unsigned)op >= ARRAY_SIZE(logic_op_table)) {
+ if (static_cast<size_t>(op) >= ARRAY_SIZE(logic_op_table)) {
LOG_CRITICAL(Render_OpenGL, "Unknown logic op %d", op);
UNREACHABLE();
@@ -145,7 +145,7 @@ inline GLenum CompareFunc(Pica::Regs::CompareFunc func) {
};
// Range check table for input
- if ((unsigned)func >= ARRAY_SIZE(compare_func_table)) {
+ if (static_cast<size_t>(func) >= ARRAY_SIZE(compare_func_table)) {
LOG_CRITICAL(Render_OpenGL, "Unknown compare function %d", func);
UNREACHABLE();
@@ -168,7 +168,7 @@ inline GLenum StencilOp(Pica::Regs::StencilAction action) {
};
// Range check table for input
- if ((unsigned)action >= ARRAY_SIZE(stencil_op_table)) {
+ if (static_cast<size_t>(action) >= ARRAY_SIZE(stencil_op_table)) {
LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %d", action);
UNREACHABLE();