summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt5
-rw-r--r--src/audio_core/codec.cpp122
-rw-r--r--src/audio_core/codec.h50
-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/citra_qt/game_list.cpp2
-rw-r--r--src/citra_qt/main.cpp8
-rw-r--r--src/common/emu_window.h52
-rw-r--r--src/common/file_util.cpp51
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/logging/log.h2
-rw-r--r--src/common/string_util.cpp16
-rw-r--r--src/common/string_util.h2
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp83
-rw-r--r--src/core/arm/skyeye_common/armstate.cpp7
-rw-r--r--src/core/file_sys/archive_extsavedata.h3
-rw-r--r--src/core/hle/kernel/session.h12
-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/cfg/cfg_i.cpp12
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp8
-rw-r--r--src/core/hle/service/cfg/cfg_u.cpp1
-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/hid/hid.cpp110
-rw-r--r--src/core/hle/service/hid/hid.h78
-rw-r--r--src/core/hle/service/hid/hid_spvr.cpp20
-rw-r--r--src/core/hle/service/hid/hid_user.cpp20
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/soc_u.cpp13
-rw-r--r--src/core/loader/loader.h2
-rw-r--r--src/video_core/command_processor.cpp46
-rw-r--r--src/video_core/pica.cpp15
-rw-r--r--src/video_core/pica.h15
-rw-r--r--src/video_core/pica_state.h15
-rw-r--r--src/video_core/primitive_assembly.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp6
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h12
-rw-r--r--src/video_core/shader/shader.cpp18
46 files changed, 1184 insertions, 249 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index b0d1c7eb6..869da5e83 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -1,12 +1,17 @@
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/codec.cpp b/src/audio_core/codec.cpp
new file mode 100644
index 000000000..ab65514b7
--- /dev/null
+++ b/src/audio_core/codec.cpp
@@ -0,0 +1,122 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstddef>
+#include <cstring>
+#include <vector>
+
+#include "audio_core/codec.h"
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/math_util.h"
+
+namespace Codec {
+
+StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) {
+ // GC-ADPCM with scale factor and variable coefficients.
+ // Frames are 8 bytes long containing 14 samples each.
+ // Samples are 4 bits (one nibble) long.
+
+ constexpr size_t FRAME_LEN = 8;
+ constexpr size_t SAMPLES_PER_FRAME = 14;
+ constexpr std::array<int, 16> SIGNED_NIBBLES {{ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 }};
+
+ const size_t ret_size = sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
+ StereoBuffer16 ret(ret_size);
+
+ int yn1 = state.yn1,
+ yn2 = state.yn2;
+
+ const size_t NUM_FRAMES = (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
+ for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
+ const int frame_header = data[framei * FRAME_LEN];
+ const int scale = 1 << (frame_header & 0xF);
+ const int idx = (frame_header >> 4) & 0x7;
+
+ // Coefficients are fixed point with 11 bits fractional part.
+ const int coef1 = adpcm_coeff[idx * 2 + 0];
+ const int coef2 = adpcm_coeff[idx * 2 + 1];
+
+ // Decodes an audio sample. One nibble produces one sample.
+ const auto decode_sample = [&](const int nibble) -> s16 {
+ const int xn = nibble * scale;
+ // We first transform everything into 11 bit fixed point, perform the second order digital filter, then transform back.
+ // 0x400 == 0.5 in 11 bit fixed point.
+ // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
+ int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
+ // Clamp to output range.
+ val = MathUtil::Clamp(val, -32768, 32767);
+ // Advance output feedback.
+ yn2 = yn1;
+ yn1 = val;
+ return (s16)val;
+ };
+
+ size_t outputi = framei * SAMPLES_PER_FRAME;
+ size_t datai = framei * FRAME_LEN + 1;
+ for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
+ const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
+ ret[outputi].fill(sample1);
+ outputi++;
+
+ const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
+ ret[outputi].fill(sample2);
+ outputi++;
+
+ datai++;
+ }
+ }
+
+ state.yn1 = yn1;
+ state.yn2 = yn2;
+
+ return ret;
+}
+
+static s16 SignExtendS8(u8 x) {
+ // The data is actually signed PCM8.
+ // We sign extend this to signed PCM16.
+ return static_cast<s16>(static_cast<s8>(x));
+}
+
+StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) {
+ ASSERT(num_channels == 1 || num_channels == 2);
+
+ StereoBuffer16 ret(sample_count);
+
+ if (num_channels == 1) {
+ for (size_t i = 0; i < sample_count; i++) {
+ ret[i].fill(SignExtendS8(data[i]));
+ }
+ } else {
+ for (size_t i = 0; i < sample_count; i++) {
+ ret[i][0] = SignExtendS8(data[i * 2 + 0]);
+ ret[i][1] = SignExtendS8(data[i * 2 + 1]);
+ }
+ }
+
+ return ret;
+}
+
+StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count) {
+ ASSERT(num_channels == 1 || num_channels == 2);
+
+ StereoBuffer16 ret(sample_count);
+
+ if (num_channels == 1) {
+ for (size_t i = 0; i < sample_count; i++) {
+ s16 sample;
+ std::memcpy(&sample, data + i * sizeof(s16), sizeof(s16));
+ ret[i].fill(sample);
+ }
+ } else {
+ std::memcpy(ret.data(), data, sample_count * 2 * sizeof(u16));
+ }
+
+ return ret;
+}
+
+};
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
new file mode 100644
index 000000000..e695f2edc
--- /dev/null
+++ b/src/audio_core/codec.h
@@ -0,0 +1,50 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace Codec {
+
+/// A variable length buffer of signed PCM16 stereo samples.
+using StereoBuffer16 = std::vector<std::array<s16, 2>>;
+
+/// See: Codec::DecodeADPCM
+struct ADPCMState {
+ // Two historical samples from previous processed buffer,
+ // required for ADPCM decoding
+ s16 yn1; ///< y[n-1]
+ s16 yn2; ///< y[n-2]
+};
+
+/**
+ * @param data Pointer to buffer that contains ADPCM data to decode
+ * @param sample_count Length of buffer in terms of number of samples
+ * @param adpcm_coeff ADPCM coefficients
+ * @param state ADPCM state, this is updated with new state
+ * @return Decoded stereo signed PCM16 data, sample_count in length
+ */
+StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state);
+
+/**
+ * @param num_channels Number of channels
+ * @param data Pointer to buffer that contains PCM8 data to decode
+ * @param sample_count Length of buffer in terms of number of samples
+ * @return Decoded stereo signed PCM16 data, sample_count in length
+ */
+StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count);
+
+/**
+ * @param num_channels Number of channels
+ * @param data Pointer to buffer that contains PCM16 data to decode
+ * @param sample_count Length of buffer in terms of number of samples
+ * @return Decoded stereo signed PCM16 data, sample_count in length
+ */
+StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count);
+
+};
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/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index 1f8d69a03..ffcab1f03 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -66,7 +66,7 @@ void GameList::ValidateEntry(const QModelIndex& item)
if (file_path.isEmpty())
return;
- std::string std_file_path = file_path.toStdString();
+ std::string std_file_path(file_path.toStdString());
if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path))
return;
emit GameChosen(file_path);
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index ebd52a7b3..ca0ae6f7b 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -417,7 +417,7 @@ void GMainWindow::UpdateRecentFiles() {
}
void GMainWindow::OnGameListLoadFile(QString game_path) {
- BootGame(game_path.toLocal8Bit().data());
+ BootGame(game_path.toStdString());
}
void GMainWindow::OnMenuLoadFile() {
@@ -428,7 +428,7 @@ void GMainWindow::OnMenuLoadFile() {
if (!filename.isEmpty()) {
settings.setValue("romsPath", QFileInfo(filename).path());
- BootGame(filename.toLocal8Bit().data());
+ BootGame(filename.toStdString());
}
}
@@ -440,7 +440,7 @@ void GMainWindow::OnMenuLoadSymbolMap() {
if (!filename.isEmpty()) {
settings.setValue("symbolsPath", QFileInfo(filename).path());
- LoadSymbolMap(filename.toLocal8Bit().data());
+ LoadSymbolMap(filename.toStdString());
}
}
@@ -461,7 +461,7 @@ void GMainWindow::OnMenuRecentFile() {
QString filename = action->data().toString();
QFileInfo file_info(filename);
if (file_info.exists()) {
- BootGame(filename.toLocal8Bit().data());
+ BootGame(filename.toStdString());
} else {
// Display an error message and remove the file from the list.
QMessageBox::information(this, tr("File not found"), tr("File \"%1\" not found").arg(filename));
diff --git a/src/common/emu_window.h b/src/common/emu_window.h
index a0ae4c9fa..7c3486dea 100644
--- a/src/common/emu_window.h
+++ b/src/common/emu_window.h
@@ -105,7 +105,7 @@ public:
* @todo Fix this function to be thread-safe.
* @return PadState object indicating the current pad state
*/
- const Service::HID::PadState GetPadState() const {
+ Service::HID::PadState GetPadState() const {
return pad_state;
}
@@ -116,11 +116,59 @@ public:
* @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
* `pressed` is true if the touch screen is currently being pressed
*/
- const std::tuple<u16, u16, bool> GetTouchState() const {
+ std::tuple<u16, u16, bool> GetTouchState() const {
return std::make_tuple(touch_x, touch_y, touch_pressed);
}
/**
+ * Gets the current accelerometer state (acceleration along each three axis).
+ * Axis explained:
+ * +x is the same direction as LEFT on D-pad.
+ * +y is normal to the touch screen, pointing outward.
+ * +z is the same direction as UP on D-pad.
+ * Units:
+ * 1 unit of return value = 1/512 g (measured by hw test),
+ * where g is the gravitational acceleration (9.8 m/sec2).
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Implement accelerometer input in front-end.
+ * @return std::tuple of (x, y, z)
+ */
+ std::tuple<s16, s16, s16> GetAccelerometerState() const {
+ // stubbed
+ return std::make_tuple(0, -512, 0);
+ }
+
+ /**
+ * Gets the current gyroscope state (angular rates about each three axis).
+ * Axis explained:
+ * +x is the same direction as LEFT on D-pad.
+ * +y is normal to the touch screen, pointing outward.
+ * +z is the same direction as UP on D-pad.
+ * Orientation is determined by right-hand rule.
+ * Units:
+ * 1 unit of return value = (1/coef) deg/sec,
+ * where coef is the return value of GetGyroscopeRawToDpsCoefficient().
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Implement gyroscope input in front-end.
+ * @return std::tuple of (x, y, z)
+ */
+ std::tuple<s16, s16, s16> GetGyroscopeState() const {
+ // stubbed
+ return std::make_tuple(0, 0, 0);
+ }
+
+ /**
+ * Gets the coefficient for units conversion of gyroscope state.
+ * The conversion formula is r = coefficient * v,
+ * where v is angular rate in deg/sec,
+ * and r is the gyroscope state.
+ * @return float-type coefficient
+ */
+ f32 GetGyroscopeRawToDpsCoefficient() const {
+ return 14.375f; // taken from hw test, and gyroscope's document
+ }
+
+ /**
* Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in another thread
*/
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index c3061479a..9ada09f8a 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -85,7 +85,7 @@ bool Exists(const std::string &filename)
StripTailDirSlashes(copy);
#ifdef _WIN32
- int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info);
+ int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info);
#else
int result = stat64(copy.c_str(), &file_info);
#endif
@@ -102,7 +102,7 @@ bool IsDirectory(const std::string &filename)
StripTailDirSlashes(copy);
#ifdef _WIN32
- int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info);
+ int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info);
#else
int result = stat64(copy.c_str(), &file_info);
#endif
@@ -138,7 +138,7 @@ bool Delete(const std::string &filename)
}
#ifdef _WIN32
- if (!DeleteFile(Common::UTF8ToTStr(filename).c_str()))
+ if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str()))
{
LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s",
filename.c_str(), GetLastErrorMsg());
@@ -160,7 +160,7 @@ bool CreateDir(const std::string &path)
{
LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
#ifdef _WIN32
- if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr))
+ if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr))
return true;
DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS)
@@ -241,7 +241,7 @@ bool DeleteDir(const std::string &filename)
}
#ifdef _WIN32
- if (::RemoveDirectory(Common::UTF8ToTStr(filename).c_str()))
+ if (::RemoveDirectoryW(Common::UTF8ToUTF16W(filename).c_str()))
return true;
#else
if (rmdir(filename.c_str()) == 0)
@@ -257,8 +257,13 @@ bool Rename(const std::string &srcFilename, const std::string &destFilename)
{
LOG_TRACE(Common_Filesystem, "%s --> %s",
srcFilename.c_str(), destFilename.c_str());
+#ifdef _WIN32
+ if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str()) == 0)
+ return true;
+#else
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
return true;
+#endif
LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
return false;
@@ -270,7 +275,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
LOG_TRACE(Common_Filesystem, "%s --> %s",
srcFilename.c_str(), destFilename.c_str());
#ifdef _WIN32
- if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE))
+ if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str(), FALSE))
return true;
LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
@@ -358,7 +363,7 @@ u64 GetSize(const std::string &filename)
struct stat64 buf;
#ifdef _WIN32
- if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0)
+ if (_wstat64(Common::UTF8ToUTF16W(filename).c_str(), &buf) == 0)
#else
if (stat64(filename.c_str(), &buf) == 0)
#endif
@@ -432,16 +437,16 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
#ifdef _WIN32
// Find the first file in the directory.
- WIN32_FIND_DATA ffd;
+ WIN32_FIND_DATAW ffd;
- HANDLE handle_find = FindFirstFile(Common::UTF8ToTStr(directory + "\\*").c_str(), &ffd);
+ HANDLE handle_find = FindFirstFileW(Common::UTF8ToUTF16W(directory + "\\*").c_str(), &ffd);
if (handle_find == INVALID_HANDLE_VALUE) {
FindClose(handle_find);
return false;
}
// windows loop
do {
- const std::string virtual_name(Common::TStrToUTF8(ffd.cFileName));
+ const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName));
#else
struct dirent dirent, *result = nullptr;
@@ -465,7 +470,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
found_entries += ret_entries;
#ifdef _WIN32
- } while (FindNextFile(handle_find, &ffd) != 0);
+ } while (FindNextFileW(handle_find, &ffd) != 0);
FindClose(handle_find);
#else
}
@@ -572,15 +577,23 @@ void CopyDir(const std::string &source_path, const std::string &dest_path)
// Returns the current directory
std::string GetCurrentDir()
{
- char *dir;
// Get the current working directory (getcwd uses malloc)
+#ifdef _WIN32
+ wchar_t *dir;
+ if (!(dir = _wgetcwd(nullptr, 0))) {
+#else
+ char *dir;
if (!(dir = getcwd(nullptr, 0))) {
-
+#endif
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s",
GetLastErrorMsg());
return nullptr;
}
+#ifdef _WIN32
+ std::string strDir = Common::UTF16ToUTF8(dir);
+#else
std::string strDir = dir;
+#endif
free(dir);
return strDir;
}
@@ -588,7 +601,11 @@ std::string GetCurrentDir()
// Sets the current directory to the given directory
bool SetCurrentDir(const std::string &directory)
{
+#ifdef _WIN32
+ return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0;
+#else
return chdir(directory.c_str()) == 0;
+#endif
}
#if defined(__APPLE__)
@@ -613,9 +630,9 @@ std::string& GetExeDirectory()
static std::string exe_path;
if (exe_path.empty())
{
- TCHAR tchar_exe_path[2048];
- GetModuleFileName(nullptr, tchar_exe_path, 2048);
- exe_path = Common::TStrToUTF8(tchar_exe_path);
+ wchar_t wchar_exe_path[2048];
+ GetModuleFileNameW(nullptr, wchar_exe_path, 2048);
+ exe_path = Common::UTF16ToUTF8(wchar_exe_path);
exe_path = exe_path.substr(0, exe_path.find_last_of('\\'));
}
return exe_path;
@@ -900,7 +917,7 @@ bool IOFile::Open(const std::string& filename, const char openmode[])
{
Close();
#ifdef _WIN32
- _tfopen_s(&m_file, Common::UTF8ToTStr(filename).c_str(), Common::UTF8ToTStr(openmode).c_str());
+ _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), Common::UTF8ToUTF16W(openmode).c_str());
#else
m_file = fopen(filename.c_str(), openmode);
#endif
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/common/string_util.cpp b/src/common/string_util.cpp
index 6d6fc591f..f0aa072db 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -320,27 +320,27 @@ std::u16string UTF8ToUTF16(const std::string& input)
#endif
}
-static std::string UTF16ToUTF8(const std::wstring& input)
+static std::wstring CPToUTF16(u32 code_page, const std::string& input)
{
- auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr);
+ auto const size = MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
- std::string output;
+ std::wstring output;
output.resize(size);
- if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr))
+ if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size())))
output.clear();
return output;
}
-static std::wstring CPToUTF16(u32 code_page, const std::string& input)
+std::string UTF16ToUTF8(const std::wstring& input)
{
- auto const size = MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
+ auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr);
- std::wstring output;
+ std::string output;
output.resize(size);
- if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size())))
+ if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr))
output.clear();
return output;
diff --git a/src/common/string_util.h b/src/common/string_util.h
index c5c474c6f..89d9f133e 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -95,7 +95,7 @@ std::string CP1252ToUTF8(const std::string& str);
std::string SHIFTJISToUTF8(const std::string& str);
#ifdef _WIN32
-
+std::string UTF16ToUTF8(const std::wstring& input);
std::wstring UTF8ToUTF16W(const std::string& str);
#ifdef _UNICODE
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/arm/skyeye_common/armstate.cpp b/src/core/arm/skyeye_common/armstate.cpp
index 2d814345a..5550c112e 100644
--- a/src/core/arm/skyeye_common/armstate.cpp
+++ b/src/core/arm/skyeye_common/armstate.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include "common/swap.h"
#include "common/logging/log.h"
#include "core/memory.h"
@@ -48,8 +49,7 @@ void ARMul_State::ChangePrivilegeMode(u32 new_mode)
Spsr[UNDEFBANK] = Spsr_copy;
break;
case FIQ32MODE:
- Reg_firq[0] = Reg[13];
- Reg_firq[1] = Reg[14];
+ std::copy(Reg.begin() + 8, Reg.end() - 1, Reg_firq.begin());
Spsr[FIQBANK] = Spsr_copy;
break;
}
@@ -85,8 +85,7 @@ void ARMul_State::ChangePrivilegeMode(u32 new_mode)
Bank = UNDEFBANK;
break;
case FIQ32MODE:
- Reg[13] = Reg_firq[0];
- Reg[14] = Reg_firq[1];
+ std::copy(Reg_firq.begin(), Reg_firq.end(), Reg.begin() + 8);
Spsr_copy = Spsr[FIQBANK];
Bank = FIQBANK;
break;
diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h
index 287a6fee1..e9a72850d 100644
--- a/src/core/file_sys/archive_extsavedata.h
+++ b/src/core/file_sys/archive_extsavedata.h
@@ -45,13 +45,14 @@ public:
void WriteIcon(const Path& path, const u8* icon_data, size_t icon_size);
private:
+ bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData archive
+
/**
* This holds the full directory path for this archive, it is only set after a successful call
* to Open, this is formed as <base extsavedatapath>/<type>/<high>/<low>.
* See GetExtSaveDataPath for the code that extracts this data from an archive path.
*/
std::string mount_point;
- bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData archive
};
/**
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
index adaffcafe..6ddaf970e 100644
--- a/src/core/hle/kernel/session.h
+++ b/src/core/hle/kernel/session.h
@@ -16,23 +16,23 @@
namespace IPC {
-inline u32 MakeHeader(u16 command_id, unsigned int regular_params, unsigned int translate_params) {
+constexpr u32 MakeHeader(u16 command_id, unsigned int regular_params, unsigned int translate_params) {
return ((u32)command_id << 16) | (((u32)regular_params & 0x3F) << 6) | (((u32)translate_params & 0x3F) << 0);
}
-inline u32 MoveHandleDesc(unsigned int num_handles = 1) {
+constexpr u32 MoveHandleDesc(unsigned int num_handles = 1) {
return 0x0 | ((num_handles - 1) << 26);
}
-inline u32 CopyHandleDesc(unsigned int num_handles = 1) {
+constexpr u32 CopyHandleDesc(unsigned int num_handles = 1) {
return 0x10 | ((num_handles - 1) << 26);
}
-inline u32 CallingPidDesc() {
+constexpr u32 CallingPidDesc() {
return 0x20;
}
-inline u32 StaticBufferDesc(u32 size, unsigned int buffer_id) {
+constexpr u32 StaticBufferDesc(u32 size, unsigned int buffer_id) {
return 0x2 | (size << 14) | ((buffer_id & 0xF) << 10);
}
@@ -42,7 +42,7 @@ enum MappedBufferPermissions {
RW = R | W,
};
-inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) {
+constexpr u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) {
return 0x8 | (size << 4) | (u32)perms;
}
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 0cb76ba1c..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/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp
index 0559a07b2..b18060f6d 100644
--- a/src/core/hle/service/cfg/cfg_i.cpp
+++ b/src/core/hle/service/cfg/cfg_i.cpp
@@ -9,6 +9,18 @@ namespace Service {
namespace CFG {
const Interface::FunctionInfo FunctionTable[] = {
+ // cfg common
+ {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
+ {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"},
+ {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"},
+ {0x00040000, GetRegionCanadaUSA, "GetRegionCanadaUSA"},
+ {0x00050000, GetSystemModel, "GetSystemModel"},
+ {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"},
+ {0x00070040, nullptr, "WriteToFirstByteCfgSavegame"},
+ {0x00080080, nullptr, "GoThroughTable"},
+ {0x00090040, GetCountryCodeString, "GetCountryCodeString"},
+ {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
+ // cfg:i
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
{0x04020082, nullptr, "SetConfigInfoBlk4"},
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp
index b03d290e5..e001f7687 100644
--- a/src/core/hle/service/cfg/cfg_s.cpp
+++ b/src/core/hle/service/cfg/cfg_s.cpp
@@ -9,10 +9,18 @@ namespace Service {
namespace CFG {
const Interface::FunctionInfo FunctionTable[] = {
+ // cfg common
{0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
{0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"},
{0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"},
+ {0x00040000, GetRegionCanadaUSA, "GetRegionCanadaUSA"},
{0x00050000, GetSystemModel, "GetSystemModel"},
+ {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"},
+ {0x00070040, nullptr, "WriteToFirstByteCfgSavegame"},
+ {0x00080080, nullptr, "GoThroughTable"},
+ {0x00090040, GetCountryCodeString, "GetCountryCodeString"},
+ {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
+ // cfg:s
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
{0x04020082, nullptr, "SetConfigInfoBlk4"},
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp
index 89ae96c9e..606f7b2eb 100644
--- a/src/core/hle/service/cfg/cfg_u.cpp
+++ b/src/core/hle/service/cfg/cfg_u.cpp
@@ -9,6 +9,7 @@ namespace Service {
namespace CFG {
const Interface::FunctionInfo FunctionTable[] = {
+ // cfg common
{0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
{0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"},
{0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"},
diff --git a/src/core/hle/service/dlp_srvr.cpp b/src/core/hle/service/dlp_srvr.cpp
new file mode 100644
index 000000000..1f30188da
--- /dev/null
+++ b/src/core/hle/service/dlp_srvr.cpp
@@ -0,0 +1,36 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/dlp_srvr.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace DLP_SRVR
+
+namespace DLP_SRVR {
+
+static void unk_0x000E0040(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 0;
+
+ LOG_WARNING(Service_DLP, "(STUBBED) called");
+}
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010183, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Finalize"},
+ {0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/dlp_srvr.h b/src/core/hle/service/dlp_srvr.h
new file mode 100644
index 000000000..d65d00814
--- /dev/null
+++ b/src/core/hle/service/dlp_srvr.h
@@ -0,0 +1,23 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace DLP_SRVR
+
+namespace DLP_SRVR {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "dlp:SRVR";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/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/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index cb4fd38e2..1053d0f40 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -33,6 +33,11 @@ static Kernel::SharedPtr<Kernel::Event> event_debug_pad;
static u32 next_pad_index;
static u32 next_touch_index;
+static u32 next_accelerometer_index;
+static u32 next_gyroscope_index;
+
+static int enable_accelerometer_count = 0; // positive means enabled
+static int enable_gyroscope_count = 0; // positive means enabled
const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> pad_mapping = {{
Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y,
@@ -78,17 +83,17 @@ void Update() {
PadState changed = { { (state.hex ^ old_state.hex) } };
// Get the current Pad entry
- PadDataEntry* pad_entry = &mem->pad.entries[mem->pad.index];
+ PadDataEntry& pad_entry = mem->pad.entries[mem->pad.index];
// Update entry properties
- pad_entry->current_state.hex = state.hex;
- pad_entry->delta_additions.hex = changed.hex & state.hex;
- pad_entry->delta_removals.hex = changed.hex & old_state.hex;;
+ pad_entry.current_state.hex = state.hex;
+ pad_entry.delta_additions.hex = changed.hex & state.hex;
+ pad_entry.delta_removals.hex = changed.hex & old_state.hex;;
// Set circle Pad
- pad_entry->circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS :
+ pad_entry.circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS :
state.circle_right ? MAX_CIRCLEPAD_POS : 0x0;
- pad_entry->circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS :
+ pad_entry.circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS :
state.circle_up ? MAX_CIRCLEPAD_POS : 0x0;
// If we just updated index 0, provide a new timestamp
@@ -101,11 +106,11 @@ void Update() {
next_touch_index = (next_touch_index + 1) % mem->touch.entries.size();
// Get the current touch entry
- TouchDataEntry* touch_entry = &mem->touch.entries[mem->touch.index];
+ TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index];
bool pressed = false;
- std::tie(touch_entry->x, touch_entry->y, pressed) = VideoCore::g_emu_window->GetTouchState();
- touch_entry->valid.Assign(pressed ? 1 : 0);
+ std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState();
+ touch_entry.valid.Assign(pressed ? 1 : 0);
// TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which
// supposedly is "Touch-screen entry, which contains the raw coordinate data prior to being
@@ -120,6 +125,58 @@ void Update() {
// Signal both handles when there's an update to Pad or touch
event_pad_or_touch_1->Signal();
event_pad_or_touch_2->Signal();
+
+ // Update accelerometer
+ if (enable_accelerometer_count > 0) {
+ mem->accelerometer.index = next_accelerometer_index;
+ next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size();
+
+ AccelerometerDataEntry& accelerometer_entry = mem->accelerometer.entries[mem->accelerometer.index];
+ std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z)
+ = VideoCore::g_emu_window->GetAccelerometerState();
+
+ // Make up "raw" entry
+ // TODO(wwylele):
+ // From hardware testing, the raw_entry values are approximately,
+ // but not exactly, as twice as corresponding entries (or with a minus sign).
+ // It may caused by system calibration to the accelerometer.
+ // Figure out how it works, or, if no game reads raw_entry,
+ // the following three lines can be removed and leave raw_entry unimplemented.
+ mem->accelerometer.raw_entry.x = -2 * accelerometer_entry.x;
+ mem->accelerometer.raw_entry.z = 2 * accelerometer_entry.y;
+ mem->accelerometer.raw_entry.y = -2 * accelerometer_entry.z;
+
+ // If we just updated index 0, provide a new timestamp
+ if (mem->accelerometer.index == 0) {
+ mem->accelerometer.index_reset_ticks_previous = mem->accelerometer.index_reset_ticks;
+ mem->accelerometer.index_reset_ticks = (s64)CoreTiming::GetTicks();
+ }
+
+ event_accelerometer->Signal();
+ }
+
+ // Update gyroscope
+ if (enable_gyroscope_count > 0) {
+ mem->gyroscope.index = next_gyroscope_index;
+ next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size();
+
+ GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index];
+ std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z)
+ = VideoCore::g_emu_window->GetGyroscopeState();
+
+ // Make up "raw" entry
+ mem->gyroscope.raw_entry.x = gyroscope_entry.x;
+ mem->gyroscope.raw_entry.z = -gyroscope_entry.y;
+ mem->gyroscope.raw_entry.y = gyroscope_entry.z;
+
+ // If we just updated index 0, provide a new timestamp
+ if (mem->gyroscope.index == 0) {
+ mem->gyroscope.index_reset_ticks_previous = mem->gyroscope.index_reset_ticks;
+ mem->gyroscope.index_reset_ticks = (s64)CoreTiming::GetTicks();
+ }
+
+ event_gyroscope->Signal();
+ }
}
void GetIPCHandles(Service::Interface* self) {
@@ -139,40 +196,69 @@ void GetIPCHandles(Service::Interface* self) {
void EnableAccelerometer(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
+ ++enable_accelerometer_count;
event_accelerometer->Signal();
cmd_buff[1] = RESULT_SUCCESS.raw;
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void DisableAccelerometer(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
+ --enable_accelerometer_count;
event_accelerometer->Signal();
cmd_buff[1] = RESULT_SUCCESS.raw;
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void EnableGyroscopeLow(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
+ ++enable_gyroscope_count;
event_gyroscope->Signal();
cmd_buff[1] = RESULT_SUCCESS.raw;
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void DisableGyroscopeLow(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
+ --enable_gyroscope_count;
event_gyroscope->Signal();
cmd_buff[1] = RESULT_SUCCESS.raw;
+ LOG_DEBUG(Service_HID, "called");
+}
+
+void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ f32 coef = VideoCore::g_emu_window->GetGyroscopeRawToDpsCoefficient();
+ memcpy(&cmd_buff[2], &coef, 4);
+}
+
+void GetGyroscopeLowCalibrateParam(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ const s16 param_unit = 6700; // an approximate value taken from hw
+ GyroscopeCalibrateParam param = {
+ { 0, param_unit, -param_unit },
+ { 0, param_unit, -param_unit },
+ { 0, param_unit, -param_unit },
+ };
+ memcpy(&cmd_buff[2], &param, sizeof(param));
+
LOG_WARNING(Service_HID, "(STUBBED) called");
}
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 517f4f2ae..170d19ea8 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -78,6 +78,24 @@ struct TouchDataEntry {
};
/**
+ * Structure of a single entry of accelerometer state history within HID shared memory
+ */
+struct AccelerometerDataEntry {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+/**
+ * Structure of a single entry of gyroscope state history within HID shared memory
+ */
+struct GyroscopeDataEntry {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+/**
* Structure of data stored in HID shared memory
*/
struct SharedMem {
@@ -112,6 +130,46 @@ struct SharedMem {
std::array<TouchDataEntry, 8> entries; ///< Last 8 touch entries, in pixel coordinates
} touch;
+
+ /// Accelerometer data
+ struct {
+ s64 index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0
+ s64 index_reset_ticks_previous; ///< Previous `index_reset_ticks`
+ u32 index; ///< Index of the last updated accelerometer entry
+
+ INSERT_PADDING_WORDS(0x1);
+
+ AccelerometerDataEntry raw_entry;
+ INSERT_PADDING_BYTES(2);
+
+ std::array<AccelerometerDataEntry, 8> entries;
+ } accelerometer;
+
+ /// Gyroscope data
+ struct {
+ s64 index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0
+ s64 index_reset_ticks_previous; ///< Previous `index_reset_ticks`
+ u32 index; ///< Index of the last updated accelerometer entry
+
+ INSERT_PADDING_WORDS(0x1);
+
+ GyroscopeDataEntry raw_entry;
+ INSERT_PADDING_BYTES(2);
+
+ std::array<GyroscopeDataEntry, 32> entries;
+ } gyroscope;
+};
+
+/**
+ * Structure of calibrate params that GetGyroscopeLowCalibrateParam returns
+ */
+struct GyroscopeCalibrateParam {
+ struct {
+ // TODO (wwylele): figure out the exact meaning of these params
+ s16 zero_point;
+ s16 positive_unit_point;
+ s16 negative_unit_point;
+ } x, y, z;
};
// TODO: MSVC does not support using offsetof() on non-static data members even though this
@@ -222,6 +280,26 @@ void DisableGyroscopeLow(Interface* self);
*/
void GetSoundVolume(Interface* self);
+/**
+ * HID::GetGyroscopeLowRawToDpsCoefficient service function
+ * Inputs:
+ * None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : float output value
+ */
+void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self);
+
+/**
+ * HID::GetGyroscopeLowCalibrateParam service function
+ * Inputs:
+ * None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2~6 (18 bytes) : struct GyroscopeCalibrateParam
+ */
+void GetGyroscopeLowCalibrateParam(Service::Interface* self);
+
/// Checks for user input updates
void Update();
diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp
index c50f597eb..046e65b11 100644
--- a/src/core/hle/service/hid/hid_spvr.cpp
+++ b/src/core/hle/service/hid/hid_spvr.cpp
@@ -9,16 +9,16 @@ namespace Service {
namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
- {0x000A0000, GetIPCHandles, "GetIPCHandles"},
- {0x000B0000, nullptr, "StartAnalogStickCalibration"},
- {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
- {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
- {0x00120000, DisableAccelerometer, "DisableAccelerometer"},
- {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
- {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"},
- {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
- {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
- {0x00170000, GetSoundVolume, "GetSoundVolume"},
+ {0x000A0000, GetIPCHandles, "GetIPCHandles"},
+ {0x000B0000, nullptr, "StartAnalogStickCalibration"},
+ {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
+ {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
+ {0x00120000, DisableAccelerometer, "DisableAccelerometer"},
+ {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
+ {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"},
+ {0x00150000, GetGyroscopeLowRawToDpsCoefficient, "GetGyroscopeLowRawToDpsCoefficient"},
+ {0x00160000, GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"},
+ {0x00170000, GetSoundVolume, "GetSoundVolume"},
};
HID_SPVR_Interface::HID_SPVR_Interface() {
diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp
index bbdde2abb..bb157b83d 100644
--- a/src/core/hle/service/hid/hid_user.cpp
+++ b/src/core/hle/service/hid/hid_user.cpp
@@ -9,16 +9,16 @@ namespace Service {
namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
- {0x000A0000, GetIPCHandles, "GetIPCHandles"},
- {0x000B0000, nullptr, "StartAnalogStickCalibration"},
- {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
- {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
- {0x00120000, DisableAccelerometer, "DisableAccelerometer"},
- {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
- {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"},
- {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
- {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
- {0x00170000, GetSoundVolume, "GetSoundVolume"},
+ {0x000A0000, GetIPCHandles, "GetIPCHandles"},
+ {0x000B0000, nullptr, "StartAnalogStickCalibration"},
+ {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
+ {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
+ {0x00120000, DisableAccelerometer, "DisableAccelerometer"},
+ {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
+ {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"},
+ {0x00150000, GetGyroscopeLowRawToDpsCoefficient, "GetGyroscopeLowRawToDpsCoefficient"},
+ {0x00160000, GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"},
+ {0x00170000, GetSoundVolume, "GetSoundVolume"},
};
HID_U_Interface::HID_U_Interface() {
diff --git a/src/core/hle/service/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/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index b52e52d4a..ff0af8f12 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -5,6 +5,7 @@
#include <algorithm>
#include <cstring>
#include <unordered_map>
+#include <vector>
#include "common/assert.h"
#include "common/bit_field.h"
@@ -593,17 +594,13 @@ static void Poll(Service::Interface* self) {
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
// so we have to copy the data
- pollfd* platform_pollfd = new pollfd[nfds];
- for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
- platform_pollfd[current_fds] = CTRPollFD::ToPlatform(input_fds[current_fds]);
+ std::vector<pollfd> platform_pollfd(nfds);
+ std::transform(input_fds, input_fds + nfds, platform_pollfd.begin(), CTRPollFD::ToPlatform);
- int ret = ::poll(platform_pollfd, nfds, timeout);
+ const int ret = ::poll(platform_pollfd.data(), nfds, timeout);
// Now update the output pollfd structure
- for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
- output_fds[current_fds] = CTRPollFD::FromPlatform(platform_pollfd[current_fds]);
-
- delete[] platform_pollfd;
+ std::transform(platform_pollfd.begin(), platform_pollfd.end(), output_fds, CTRPollFD::FromPlatform);
int result = 0;
if (ret == SOCKET_ERROR_VALUE)
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index a7f2715ba..84a4ce5fc 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -74,7 +74,7 @@ enum class ResultStatus {
ErrorEncrypted,
};
-static inline u32 MakeMagic(char a, char b, char c, char d) {
+constexpr u32 MakeMagic(char a, char b, char c, char d) {
return a | b << 8 | c << 16 | d << 24;
}
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 4b59984ad..028b59348 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -75,12 +75,17 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
break;
+ case PICA_REG_INDEX_WORKAROUND(triangle_topology, 0x25E):
+ g_state.primitive_assembler.Reconfigure(regs.triangle_topology);
+ break;
+
+ case PICA_REG_INDEX_WORKAROUND(restart_primitive, 0x25F):
+ g_state.primitive_assembler.Reset();
+ break;
+
case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.index, 0x232):
- if (regs.vs_default_attributes_setup.index == 15) {
- // Reset immediate primitive state
- g_state.immediate.primitive_assembler.Reconfigure(regs.triangle_topology);
- g_state.immediate.attribute_id = 0;
- }
+ g_state.immediate.current_attribute = 0;
+ default_attr_counter = 0;
break;
// Load default vertex input attributes
@@ -105,7 +110,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
break;
}
- Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index];
+ Math::Vec4<float24> attribute;
// NOTE: The destination component order indeed is "backwards"
attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8);
@@ -119,26 +124,29 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// TODO: Verify that this actually modifies the register!
if (setup.index < 15) {
+ g_state.vs.default_attributes[setup.index] = attribute;
setup.index++;
} else {
// Put each attribute into an immediate input buffer.
// When all specified immediate attributes are present, the Vertex Shader is invoked and everything is
// sent to the primitive assembler.
- auto& immediate_input = g_state.immediate.input;
- auto& immediate_attribute_id = g_state.immediate.attribute_id;
- const auto& attribute_config = regs.vertex_attributes;
+ auto& immediate_input = g_state.immediate.input_vertex;
+ auto& immediate_attribute_id = g_state.immediate.current_attribute;
immediate_input.attr[immediate_attribute_id++] = attribute;
- if (immediate_attribute_id >= attribute_config.GetNumTotalAttributes()) {
+ if (immediate_attribute_id >= regs.vs.num_input_attributes+1) {
immediate_attribute_id = 0;
Shader::UnitState<false> shader_unit;
Shader::Setup(shader_unit);
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, static_cast<void*>(&immediate_input));
+
// Send to vertex shader
- Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, attribute_config.GetNumTotalAttributes());
+ Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1);
// Send to renderer
using Pica::Shader::OutputVertex;
@@ -146,7 +154,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
};
- g_state.immediate.primitive_assembler.SubmitVertex(output, AddTriangle);
+ g_state.primitive_assembler.SubmitVertex(output, AddTriangle);
}
}
}
@@ -154,9 +162,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
}
case PICA_REG_INDEX(gpu_mode):
- if (regs.gpu_mode == Regs::GPUMode::Configuring && regs.vs_default_attributes_setup.index == 15) {
+ if (regs.gpu_mode == Regs::GPUMode::Configuring) {
// Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring
VideoCore::g_renderer->Rasterizer()->DrawTriangles();
+
+ if (g_debug_context) {
+ g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
+ }
}
break;
@@ -241,7 +253,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
DebugUtils::GeometryDumper geometry_dumper;
PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value());
#endif
- PrimitiveAssembler<Shader::OutputVertex> primitive_assembler(regs.triangle_topology.Value());
+ PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler;
if (g_debug_context) {
for (int i = 0; i < 3; ++i) {
@@ -412,16 +424,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
range.second, range.first);
}
- VideoCore::g_renderer->Rasterizer()->DrawTriangles();
-
#if PICA_DUMP_GEOMETRY
geometry_dumper.Dump();
#endif
- if (g_debug_context) {
- g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
- }
-
break;
}
diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp
index 32ad72674..ccbaf071b 100644
--- a/src/video_core/pica.cpp
+++ b/src/video_core/pica.cpp
@@ -493,12 +493,25 @@ std::string Regs::GetCommandName(int index) {
}
void Init() {
+ g_state.Reset();
}
void Shutdown() {
Shader::Shutdown();
+}
+
+template <typename T>
+void Zero(T& o) {
+ memset(&o, 0, sizeof(o));
+}
- memset(&g_state, 0, sizeof(State));
+void State::Reset() {
+ Zero(regs);
+ Zero(vs);
+ Zero(gs);
+ Zero(cmd_list);
+ Zero(immediate);
+ primitive_assembler.Reconfigure(Regs::TriangleTopology::List);
}
}
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 337cff8ce..16f9e4006 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -71,7 +71,7 @@ struct Regs {
BitField<0, 24, u32> viewport_depth_range; // float24
BitField<0, 24, u32> viewport_depth_far_plane; // float24
- INSERT_PADDING_WORDS(0x1);
+ BitField<0, 3, u32> vs_output_total;
union VSOutputAttributes {
// Maps components of output vertex attributes to semantics
@@ -1123,7 +1123,12 @@ struct Regs {
BitField<24, 8, u32> w;
} int_uniforms[4];
- INSERT_PADDING_WORDS(0x5);
+ INSERT_PADDING_WORDS(0x4);
+
+ union {
+ // Number of input attributes to shader unit - 1
+ BitField<0, 4, u32> num_input_attributes;
+ };
// Offset to shader program entry point (in words)
BitField<0, 16, u32> main_offset;
@@ -1157,8 +1162,10 @@ struct Regs {
}
} input_register_map;
- // OUTMAP_MASK, 0x28E, CODETRANSFER_END
- INSERT_PADDING_WORDS(0x3);
+ BitField<0, 16, u32> output_mask;
+
+ // 0x28E, CODETRANSFER_END
+ INSERT_PADDING_WORDS(0x2);
struct {
enum Format : u32
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
index c7616bc55..323290054 100644
--- a/src/video_core/pica_state.h
+++ b/src/video_core/pica_state.h
@@ -12,6 +12,8 @@ namespace Pica {
/// Struct used to describe current Pica state
struct State {
+ void Reset();
+
/// Pica registers
Regs regs;
@@ -46,13 +48,14 @@ struct State {
/// Struct used to describe immediate mode rendering state
struct ImmediateModeState {
- Shader::InputVertex input;
- // This is constructed with a dummy triangle topology
- PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
- int attribute_id = 0;
-
- ImmediateModeState() : primitive_assembler(Regs::TriangleTopology::List) {}
+ // Used to buffer partial vertices for immediate-mode rendering.
+ Shader::InputVertex input_vertex;
+ // Index of the next attribute to be loaded into `input_vertex`.
+ int current_attribute = 0;
} immediate;
+
+ // This is constructed with a dummy triangle topology
+ PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
};
extern State g_state; ///< Current Pica state
diff --git a/src/video_core/primitive_assembly.h b/src/video_core/primitive_assembly.h
index cc6e5fde5..9396b4c85 100644
--- a/src/video_core/primitive_assembly.h
+++ b/src/video_core/primitive_assembly.h
@@ -20,7 +20,7 @@ struct PrimitiveAssembler {
VertexType& v1,
VertexType& v2)>;
- PrimitiveAssembler(Regs::TriangleTopology topology);
+ PrimitiveAssembler(Regs::TriangleTopology topology = Regs::TriangleTopology::List);
/*
* Queues a vertex, builds primitives from the vertex queue according to the given
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 5d91aac00..cc6b3aeab 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -189,6 +189,9 @@ void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0,
}
void RasterizerOpenGL::DrawTriangles() {
+ if (vertex_batch.empty())
+ return;
+
SyncFramebuffer();
SyncDrawState();
@@ -804,6 +807,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();
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp
index 045e7d5bd..78d295c76 100644
--- a/src/video_core/shader/shader.cpp
+++ b/src/video_core/shader/shader.cpp
@@ -120,15 +120,23 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
OutputVertex ret;
// TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to
// figure out what those circumstances are and enable the remaining outputs then.
- for (int i = 0; i < 7; ++i) {
- const auto& output_register_map = g_state.regs.vs_output_attributes[i]; // TODO: Don't hardcode VS here
+ unsigned index = 0;
+ for (unsigned i = 0; i < 7; ++i) {
+
+ if (index >= g_state.regs.vs_output_total)
+ break;
+
+ if ((g_state.regs.vs.output_mask & (1 << i)) == 0)
+ continue;
+
+ const auto& output_register_map = g_state.regs.vs_output_attributes[index]; // TODO: Don't hardcode VS here
u32 semantics[4] = {
output_register_map.map_x, output_register_map.map_y,
output_register_map.map_z, output_register_map.map_w
};
- for (int comp = 0; comp < 4; ++comp) {
+ for (unsigned comp = 0; comp < 4; ++comp) {
float24* out = ((float24*)&ret) + semantics[comp];
if (semantics[comp] != Regs::VSOutputAttributes::INVALID) {
*out = state.registers.output[i][comp];
@@ -138,10 +146,12 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
memset(out, 0, sizeof(*out));
}
}
+
+ index++;
}
// The hardware takes the absolute and saturates vertex colors like this, *before* doing interpolation
- for (int i = 0; i < 4; ++i) {
+ for (unsigned i = 0; i < 4; ++i) {
ret.color[i] = float24::FromFloat32(
std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f));
}