summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/emu_window/emu_window_glfw.cpp30
-rw-r--r--src/citra/emu_window/emu_window_glfw.h4
-rw-r--r--src/citra_qt/bootmanager.cpp27
-rw-r--r--src/citra_qt/bootmanager.h4
-rw-r--r--src/common/common_paths.h11
-rw-r--r--src/common/emu_window.cpp55
-rw-r--r--src/common/emu_window.h65
-rw-r--r--src/common/platform.h6
-rw-r--r--src/core/arm/arm_interface.h6
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp5
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp214
-rw-r--r--src/core/hle/service/hid/hid.h114
-rw-r--r--src/core/hle/service/hid/hid_spvr.cpp6
-rw-r--r--src/core/hle/service/hid/hid_user.cpp18
-rw-r--r--src/core/hle/svc.cpp5
-rw-r--r--src/core/hw/gpu.cpp35
-rw-r--r--src/core/hw/gpu.h11
-rw-r--r--src/core/loader/ncch.h25
-rw-r--r--src/video_core/color.h2
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp6
-rw-r--r--src/video_core/vertex_shader.cpp44
22 files changed, 451 insertions, 243 deletions
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp
index 81231e1e5..997e3bc7d 100644
--- a/src/citra/emu_window/emu_window_glfw.cpp
+++ b/src/citra/emu_window/emu_window_glfw.cpp
@@ -16,18 +16,34 @@ EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) {
16 return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win)); 16 return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win));
17} 17}
18 18
19void EmuWindow_GLFW::OnMouseButtonEvent(GLFWwindow* win, int button, int action, int mods) {
20 if (button == GLFW_MOUSE_BUTTON_LEFT) {
21 auto emu_window = GetEmuWindow(win);
22 auto layout = emu_window->GetFramebufferLayout();
23 double x, y;
24 glfwGetCursorPos(win, &x, &y);
25
26 if (action == GLFW_PRESS)
27 emu_window->TouchPressed(static_cast<unsigned>(x), static_cast<unsigned>(y));
28 else if (action == GLFW_RELEASE)
29 emu_window->TouchReleased();
30 }
31}
32
33void EmuWindow_GLFW::OnCursorPosEvent(GLFWwindow* win, double x, double y) {
34 GetEmuWindow(win)->TouchMoved(static_cast<unsigned>(x), static_cast<unsigned>(y));
35}
36
19/// Called by GLFW when a key event occurs 37/// Called by GLFW when a key event occurs
20void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { 38void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) {
21 39 auto emu_window = GetEmuWindow(win);
22 int keyboard_id = GetEmuWindow(win)->keyboard_id; 40 int keyboard_id = emu_window->keyboard_id;
23 41
24 if (action == GLFW_PRESS) { 42 if (action == GLFW_PRESS) {
25 EmuWindow::KeyPressed({key, keyboard_id}); 43 emu_window->KeyPressed({key, keyboard_id});
26 } else if (action == GLFW_RELEASE) { 44 } else if (action == GLFW_RELEASE) {
27 EmuWindow::KeyReleased({key, keyboard_id}); 45 emu_window->KeyReleased({key, keyboard_id});
28 } 46 }
29
30 Service::HID::PadUpdateComplete();
31} 47}
32 48
33/// Whether the window is still open, and a close request hasn't yet been sent 49/// Whether the window is still open, and a close request hasn't yet been sent
@@ -88,6 +104,8 @@ EmuWindow_GLFW::EmuWindow_GLFW() {
88 104
89 // Setup callbacks 105 // Setup callbacks
90 glfwSetKeyCallback(m_render_window, OnKeyEvent); 106 glfwSetKeyCallback(m_render_window, OnKeyEvent);
107 glfwSetMouseButtonCallback(m_render_window, OnMouseButtonEvent);
108 glfwSetCursorPosCallback(m_render_window, OnCursorPosEvent);
91 glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent); 109 glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent);
92 glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent); 110 glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent);
93 111
diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h
index 5252fccc8..16c109b79 100644
--- a/src/citra/emu_window/emu_window_glfw.h
+++ b/src/citra/emu_window/emu_window_glfw.h
@@ -27,6 +27,10 @@ public:
27 27
28 static void OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods); 28 static void OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods);
29 29
30 static void OnMouseButtonEvent(GLFWwindow* window, int button, int action, int mods);
31
32 static void OnCursorPosEvent(GLFWwindow* window, double x, double y);
33
30 /// Whether the window is still open, and a close request hasn't yet been sent 34 /// Whether the window is still open, and a close request hasn't yet been sent
31 const bool IsOpen(); 35 const bool IsOpen();
32 36
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index a040e75c1..b81bd6167 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -268,14 +268,33 @@ QByteArray GRenderWindow::saveGeometry()
268 268
269void GRenderWindow::keyPressEvent(QKeyEvent* event) 269void GRenderWindow::keyPressEvent(QKeyEvent* event)
270{ 270{
271 EmuWindow::KeyPressed({event->key(), keyboard_id}); 271 this->KeyPressed({event->key(), keyboard_id});
272 Service::HID::PadUpdateComplete();
273} 272}
274 273
275void GRenderWindow::keyReleaseEvent(QKeyEvent* event) 274void GRenderWindow::keyReleaseEvent(QKeyEvent* event)
276{ 275{
277 EmuWindow::KeyReleased({event->key(), keyboard_id}); 276 this->KeyReleased({event->key(), keyboard_id});
278 Service::HID::PadUpdateComplete(); 277}
278
279void GRenderWindow::mousePressEvent(QMouseEvent *event)
280{
281 if (event->button() == Qt::LeftButton)
282 {
283 auto pos = event->pos();
284 this->TouchPressed(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y()));
285 }
286}
287
288void GRenderWindow::mouseMoveEvent(QMouseEvent *event)
289{
290 auto pos = event->pos();
291 this->TouchMoved(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y()));
292}
293
294void GRenderWindow::mouseReleaseEvent(QMouseEvent *event)
295{
296 if (event->button() == Qt::LeftButton)
297 this->TouchReleased();
279} 298}
280 299
281void GRenderWindow::ReloadSetKeymaps() 300void GRenderWindow::ReloadSetKeymaps()
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index a55db682a..288da45a1 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -121,6 +121,10 @@ public:
121 void keyPressEvent(QKeyEvent* event) override; 121 void keyPressEvent(QKeyEvent* event) override;
122 void keyReleaseEvent(QKeyEvent* event) override; 122 void keyReleaseEvent(QKeyEvent* event) override;
123 123
124 void mousePressEvent(QMouseEvent *event) override;
125 void mouseMoveEvent(QMouseEvent *event) override;
126 void mouseReleaseEvent(QMouseEvent *event) override;
127
124 void ReloadSetKeymaps() override; 128 void ReloadSetKeymaps() override;
125 129
126 void OnClientAreaResized(unsigned width, unsigned height); 130 void OnClientAreaResized(unsigned width, unsigned height);
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index eb43d589f..440b06060 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -17,13 +17,12 @@
17 17
18// The user data dir 18// The user data dir
19#define ROOT_DIR "." 19#define ROOT_DIR "."
20#ifdef _WIN32 20#define USERDATA_DIR "user"
21 #define USERDATA_DIR "user" 21#ifdef USER_DIR
22 #define EMU_DATA_DIR "Citra Emulator" 22 #define EMU_DATA_DIR USER_DIR
23#else 23#else
24 #define USERDATA_DIR "user" 24 #ifdef _WIN32
25 #ifdef USER_DIR 25 #define EMU_DATA_DIR "Citra Emulator"
26 #define EMU_DATA_DIR USER_DIR
27 #else 26 #else
28 #define EMU_DATA_DIR "citra-emu" 27 #define EMU_DATA_DIR "citra-emu"
29 #endif 28 #endif
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp
index 6459d2f32..6516fc633 100644
--- a/src/common/emu_window.cpp
+++ b/src/common/emu_window.cpp
@@ -6,18 +6,61 @@
6#include "video_core/video_core.h" 6#include "video_core/video_core.h"
7 7
8void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) { 8void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) {
9 Service::HID::PadState mapped_key = KeyMap::GetPadKey(key); 9 pad_state.hex |= KeyMap::GetPadKey(key).hex;
10
11 Service::HID::PadButtonPress(mapped_key);
12} 10}
13 11
14void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) { 12void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) {
15 Service::HID::PadState mapped_key = KeyMap::GetPadKey(key); 13 pad_state.hex &= ~KeyMap::GetPadKey(key).hex;
14}
15
16/**
17 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
18 * @param layout FramebufferLayout object describing the framebuffer size and screen positions
19 * @param framebuffer_x Framebuffer x-coordinate to check
20 * @param framebuffer_y Framebuffer y-coordinate to check
21 * @return True if the coordinates are within the touchpad, otherwise false
22 */
23static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x,
24 unsigned framebuffer_y) {
25 return (framebuffer_y >= layout.bottom_screen.top &&
26 framebuffer_y < layout.bottom_screen.bottom &&
27 framebuffer_x >= layout.bottom_screen.left &&
28 framebuffer_x < layout.bottom_screen.right);
29}
30
31void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
32 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
33 return;
34
35 touch_x = VideoCore::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) /
36 (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
37 touch_y = VideoCore::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) /
38 (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
16 39
17 Service::HID::PadButtonRelease(mapped_key); 40 touch_pressed = true;
41 pad_state.touch = 1;
18} 42}
19 43
20EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) { 44void EmuWindow::TouchReleased() {
45 touch_pressed = false;
46 touch_x = 0;
47 touch_y = 0;
48 pad_state.touch = 0;
49}
50
51void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
52 if (!touch_pressed)
53 return;
54
55 if (IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
56 TouchPressed(framebuffer_x, framebuffer_y);
57 else
58 TouchReleased();
59}
60
61EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width,
62 unsigned height) {
63
21 ASSERT(width > 0); 64 ASSERT(width > 0);
22 ASSERT(height > 0); 65 ASSERT(height > 0);
23 66
diff --git a/src/common/emu_window.h b/src/common/emu_window.h
index f6099fdb6..c8e2de04a 100644
--- a/src/common/emu_window.h
+++ b/src/common/emu_window.h
@@ -71,10 +71,48 @@ public:
71 virtual void ReloadSetKeymaps() = 0; 71 virtual void ReloadSetKeymaps() = 0;
72 72
73 /// Signals a key press action to the HID module 73 /// Signals a key press action to the HID module
74 static void KeyPressed(KeyMap::HostDeviceKey key); 74 void KeyPressed(KeyMap::HostDeviceKey key);
75 75
76 /// Signals a key release action to the HID module 76 /// Signals a key release action to the HID module
77 static void KeyReleased(KeyMap::HostDeviceKey key); 77 void KeyReleased(KeyMap::HostDeviceKey key);
78
79 /**
80 * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
81 * @param framebuffer_x Framebuffer x-coordinate that was pressed
82 * @param framebuffer_y Framebuffer y-coordinate that was pressed
83 */
84 void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
85
86 /// Signal that a touch released event has occurred (e.g. mouse click released)
87 void TouchReleased();
88
89 /**
90 * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
91 * @param framebuffer_x Framebuffer x-coordinate
92 * @param framebuffer_y Framebuffer y-coordinate
93 */
94 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
95
96 /**
97 * Gets the current pad state (which buttons are pressed and the circle pad direction).
98 * @note This should be called by the core emu thread to get a state set by the window thread.
99 * @todo Fix this function to be thread-safe.
100 * @return PadState object indicating the current pad state
101 */
102 const Service::HID::PadState GetPadState() const {
103 return pad_state;
104 }
105
106 /**
107 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
108 * @note This should be called by the core emu thread to get a state set by the window thread.
109 * @todo Fix this function to be thread-safe.
110 * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
111 * `pressed` is true if the touch screen is currently being pressed
112 */
113 const std::tuple<u16, u16, bool> GetTouchState() const {
114 return std::make_tuple(touch_x, touch_y, touch_pressed);
115 }
78 116
79 /** 117 /**
80 * Returns currently active configuration. 118 * Returns currently active configuration.
@@ -100,21 +138,15 @@ public:
100 return framebuffer_layout; 138 return framebuffer_layout;
101 } 139 }
102 140
103 /**
104 * Gets window client area width in logical coordinates.
105 * @note For high-DPI systems, this is smaller than the framebuffer size.
106 * @note This method is thread-safe
107 */
108 std::pair<unsigned,unsigned> GetClientAreaSize() const {
109 return std::make_pair(client_area_width, client_area_height);
110 }
111
112protected: 141protected:
113 EmuWindow() 142 EmuWindow() {
114 {
115 // TODO: Find a better place to set this. 143 // TODO: Find a better place to set this.
116 config.min_client_area_size = std::make_pair(400u, 480u); 144 config.min_client_area_size = std::make_pair(400u, 480u);
117 active_config = config; 145 active_config = config;
146 pad_state.hex = 0;
147 touch_x = 0;
148 touch_y = 0;
149 touch_pressed = false;
118 } 150 }
119 virtual ~EmuWindow() {} 151 virtual ~EmuWindow() {}
120 152
@@ -168,4 +200,11 @@ private:
168 200
169 WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges) 201 WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges)
170 WindowConfig active_config; ///< Internal active configuration 202 WindowConfig active_config; ///< Internal active configuration
203
204 bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
205
206 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
207 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
208
209 Service::HID::PadState pad_state;
171}; 210};
diff --git a/src/common/platform.h b/src/common/platform.h
index ba1109c9f..e27d6e31f 100644
--- a/src/common/platform.h
+++ b/src/common/platform.h
@@ -83,7 +83,7 @@ inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
83} 83}
84#endif 84#endif
85 85
86#else 86#else // EMU_PLATFORM != PLATFORM_WINDOWS
87 87
88#define EMU_FASTCALL __attribute__((fastcall)) 88#define EMU_FASTCALL __attribute__((fastcall))
89#define __stdcall 89#define __stdcall
@@ -92,10 +92,6 @@ inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
92#define BOOL bool 92#define BOOL bool
93#define DWORD u32 93#define DWORD u32
94 94
95#endif
96
97#if EMU_PLATFORM != PLATFORM_WINDOWS
98
99// TODO: Hacks.. 95// TODO: Hacks..
100#include <limits.h> 96#include <limits.h>
101 97
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index ef37ee055..fe1e584ad 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -74,12 +74,6 @@ public:
74 virtual void SetCPSR(u32 cpsr) = 0; 74 virtual void SetCPSR(u32 cpsr) = 0;
75 75
76 /** 76 /**
77 * Returns the number of clock ticks since the last rese
78 * @return Returns number of clock ticks
79 */
80 virtual u64 GetTicks() const = 0;
81
82 /**
83 * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time) 77 * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
84 * @param ticks Number of ticks to advance the CPU core 78 * @param ticks Number of ticks to advance the CPU core
85 */ 79 */
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index bbcbbdd2b..cb1a410a0 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -68,11 +68,6 @@ void ARM_DynCom::SetCPSR(u32 cpsr) {
68 state->Cpsr = cpsr; 68 state->Cpsr = cpsr;
69} 69}
70 70
71u64 ARM_DynCom::GetTicks() const {
72 // TODO(Subv): Remove ARM_DynCom::GetTicks() and use CoreTiming::GetTicks() directly once ARMemu is gone
73 return CoreTiming::GetTicks();
74}
75
76void ARM_DynCom::AddTicks(u64 ticks) { 71void ARM_DynCom::AddTicks(u64 ticks) {
77 down_count -= ticks; 72 down_count -= ticks;
78 if (down_count < 0) 73 if (down_count < 0)
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 213cac1ad..a7f95d307 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -23,7 +23,6 @@ public:
23 u32 GetCPSR() const override; 23 u32 GetCPSR() const override;
24 void SetCPSR(u32 cpsr) override; 24 void SetCPSR(u32 cpsr) override;
25 25
26 u64 GetTicks() const override;
27 void AddTicks(u64 ticks) override; 26 void AddTicks(u64 ticks) override;
28 27
29 void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg); 28 void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg);
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index e0689be2e..138603d9b 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -7,36 +7,30 @@
7#include "core/hle/service/hid/hid_spvr.h" 7#include "core/hle/service/hid/hid_spvr.h"
8#include "core/hle/service/hid/hid_user.h" 8#include "core/hle/service/hid/hid_user.h"
9 9
10#include "core/arm/arm_interface.h" 10#include "core/core_timing.h"
11#include "core/hle/kernel/event.h" 11#include "core/hle/kernel/event.h"
12#include "core/hle/kernel/shared_memory.h" 12#include "core/hle/kernel/shared_memory.h"
13#include "core/hle/hle.h" 13#include "core/hle/hle.h"
14 14
15#include "video_core/video_core.h"
16
15namespace Service { 17namespace Service {
16namespace HID { 18namespace HID {
17 19
18Kernel::SharedPtr<Kernel::SharedMemory> g_shared_mem = nullptr; 20static const int MAX_CIRCLEPAD_POS = 0x9C; ///< Max value for a circle pad position
19 21
20Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_1; 22// Handle to shared memory region designated to HID_User service
21Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2; 23static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem = nullptr;
22Kernel::SharedPtr<Kernel::Event> g_event_accelerometer; 24
23Kernel::SharedPtr<Kernel::Event> g_event_gyroscope; 25// Event handles
24Kernel::SharedPtr<Kernel::Event> g_event_debug_pad; 26static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1 = nullptr;
25 27static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2 = nullptr;
26// Next Pad state update information 28static Kernel::SharedPtr<Kernel::Event> event_accelerometer = nullptr;
27static PadState next_state = {{0}}; 29static Kernel::SharedPtr<Kernel::Event> event_gyroscope = nullptr;
28static u32 next_index = 0; 30static Kernel::SharedPtr<Kernel::Event> event_debug_pad = nullptr;
29static s16 next_circle_x = 0; 31
30static s16 next_circle_y = 0; 32static u32 next_pad_index = 0;
31 33static u32 next_touch_index = 0;
32/**
33 * Gets a pointer to the PadData structure inside HID shared memory
34 */
35static inline PadData* GetPadData() {
36 if (g_shared_mem == nullptr)
37 return nullptr;
38 return reinterpret_cast<PadData*>(g_shared_mem->GetPointer().ValueOr(nullptr));
39}
40 34
41// TODO(peachum): 35// TODO(peachum):
42// Add a method for setting analog input from joystick device for the circle Pad. 36// Add a method for setting analog input from joystick device for the circle Pad.
@@ -51,103 +45,114 @@ static inline PadData* GetPadData() {
51// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41 45// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41
52// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41 46// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41
53 47
54/** 48void HIDUpdate() {
55 * Circle Pad from keys. 49 SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer().ValueOr(nullptr));
56 * 50 const PadState state = VideoCore::g_emu_window->GetPadState();
57 * This is implemented as "pushed all the way to an edge (max) or centered (0)".
58 *
59 * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions.
60 */
61static void UpdateNextCirclePadState() {
62 static const s16 max_value = 0x9C;
63 next_circle_x = next_state.circle_left ? -max_value : 0x0;
64 next_circle_x += next_state.circle_right ? max_value : 0x0;
65 next_circle_y = next_state.circle_down ? -max_value : 0x0;
66 next_circle_y += next_state.circle_up ? max_value : 0x0;
67}
68
69/**
70 * Sets a Pad state (button or button combo) as pressed
71 */
72void PadButtonPress(const PadState& pad_state) {
73 next_state.hex |= pad_state.hex;
74 UpdateNextCirclePadState();
75}
76
77/**
78 * Sets a Pad state (button or button combo) as released
79 */
80void PadButtonRelease(const PadState& pad_state) {
81 next_state.hex &= ~pad_state.hex;
82 UpdateNextCirclePadState();
83}
84
85/**
86 * Called after all Pad changes to be included in this update have been made,
87 * including both Pad key changes and analog circle Pad changes.
88 */
89void PadUpdateComplete() {
90 PadData* pad_data = GetPadData();
91 51
92 if (pad_data == nullptr) { 52 if (mem == nullptr) {
53 LOG_DEBUG(Service_HID, "Cannot update HID prior to mapping shared memory!");
93 return; 54 return;
94 } 55 }
95 56
96 // Update PadData struct 57 mem->pad.current_state.hex = state.hex;
97 pad_data->current_state.hex = next_state.hex; 58 mem->pad.index = next_pad_index;
98 pad_data->index = next_index; 59 ++next_touch_index %= mem->pad.entries.size();
99 next_index = (next_index + 1) % pad_data->entries.size();
100 60
101 // Get the previous Pad state 61 // Get the previous Pad state
102 u32 last_entry_index = (pad_data->index - 1) % pad_data->entries.size(); 62 u32 last_entry_index = (mem->pad.index - 1) % mem->pad.entries.size();
103 PadState old_state = pad_data->entries[last_entry_index].current_state; 63 PadState old_state = mem->pad.entries[last_entry_index].current_state;
104 64
105 // Compute bitmask with 1s for bits different from the old state 65 // Compute bitmask with 1s for bits different from the old state
106 PadState changed; 66 PadState changed = { { (state.hex ^ old_state.hex) } };
107 changed.hex = (next_state.hex ^ old_state.hex);
108
109 // Compute what was added
110 PadState additions;
111 additions.hex = changed.hex & next_state.hex;
112
113 // Compute what was removed
114 PadState removals;
115 removals.hex = changed.hex & old_state.hex;
116 67
117 // Get the current Pad entry 68 // Get the current Pad entry
118 PadDataEntry* current_pad_entry = &pad_data->entries[pad_data->index]; 69 PadDataEntry* pad_entry = &mem->pad.entries[mem->pad.index];
119 70
120 // Update entry properties 71 // Update entry properties
121 current_pad_entry->current_state.hex = next_state.hex; 72 pad_entry->current_state.hex = state.hex;
122 current_pad_entry->delta_additions.hex = additions.hex; 73 pad_entry->delta_additions.hex = changed.hex & state.hex;
123 current_pad_entry->delta_removals.hex = removals.hex; 74 pad_entry->delta_removals.hex = changed.hex & old_state.hex;;
124 75
125 // Set circle Pad 76 // Set circle Pad
126 current_pad_entry->circle_pad_x = next_circle_x; 77 pad_entry->circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS :
127 current_pad_entry->circle_pad_y = next_circle_y; 78 state.circle_right ? MAX_CIRCLEPAD_POS : 0x0;
79 pad_entry->circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS :
80 state.circle_up ? MAX_CIRCLEPAD_POS : 0x0;
128 81
129 // If we just updated index 0, provide a new timestamp 82 // If we just updated index 0, provide a new timestamp
130 if (pad_data->index == 0) { 83 if (mem->pad.index == 0) {
131 pad_data->index_reset_ticks_previous = pad_data->index_reset_ticks; 84 mem->pad.index_reset_ticks_previous = mem->pad.index_reset_ticks;
132 pad_data->index_reset_ticks = (s64)Core::g_app_core->GetTicks(); 85 mem->pad.index_reset_ticks = (s64)CoreTiming::GetTicks();
86 }
87
88 mem->touch.index = next_touch_index;
89 ++next_touch_index %= mem->touch.entries.size();
90
91 // Get the current touch entry
92 TouchDataEntry* touch_entry = &mem->touch.entries[mem->touch.index];
93 bool pressed = false;
94
95 std::tie(touch_entry->x, touch_entry->y, pressed) = VideoCore::g_emu_window->GetTouchState();
96 touch_entry->valid = pressed ? 1 : 0;
97
98 // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which
99 // supposedly is "Touch-screen entry, which contains the raw coordinate data prior to being
100 // converted to pixel coordinates." (http://3dbrew.org/wiki/HID_Shared_Memory#Offset_0xA8).
101
102 // If we just updated index 0, provide a new timestamp
103 if (mem->touch.index == 0) {
104 mem->touch.index_reset_ticks_previous = mem->touch.index_reset_ticks;
105 mem->touch.index_reset_ticks = (s64)CoreTiming::GetTicks();
133 } 106 }
134 107
135 // Signal both handles when there's an update to Pad or touch 108 // Signal both handles when there's an update to Pad or touch
136 g_event_pad_or_touch_1->Signal(); 109 event_pad_or_touch_1->Signal();
137 g_event_pad_or_touch_2->Signal(); 110 event_pad_or_touch_2->Signal();
138} 111}
139 112
140void GetIPCHandles(Service::Interface* self) { 113void GetIPCHandles(Service::Interface* self) {
141 u32* cmd_buff = Kernel::GetCommandBuffer(); 114 u32* cmd_buff = Kernel::GetCommandBuffer();
142 115
143 cmd_buff[1] = 0; // No error 116 cmd_buff[1] = 0; // No error
117 cmd_buff[2] = 0x14000000; // IPC Command Structure translate-header
144 // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) 118 // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling)
145 cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::g_shared_mem).MoveFrom(); 119 cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).MoveFrom();
146 cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_1).MoveFrom(); 120 cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).MoveFrom();
147 cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_2).MoveFrom(); 121 cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).MoveFrom();
148 cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::g_event_accelerometer).MoveFrom(); 122 cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).MoveFrom();
149 cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::g_event_gyroscope).MoveFrom(); 123 cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).MoveFrom();
150 cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::g_event_debug_pad).MoveFrom(); 124 cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).MoveFrom();
125}
126
127void EnableAccelerometer(Service::Interface* self) {
128 u32* cmd_buff = Kernel::GetCommandBuffer();
129
130 event_accelerometer->Signal();
131
132 cmd_buff[1] = RESULT_SUCCESS.raw;
133
134 LOG_WARNING(Service_HID, "(STUBBED) called");
135}
136
137void EnableGyroscopeLow(Service::Interface* self) {
138 u32* cmd_buff = Kernel::GetCommandBuffer();
139
140 event_gyroscope->Signal();
141
142 cmd_buff[1] = RESULT_SUCCESS.raw;
143
144 LOG_WARNING(Service_HID, "(STUBBED) called");
145}
146
147void GetSoundVolume(Service::Interface* self) {
148 u32* cmd_buff = Kernel::GetCommandBuffer();
149
150 const u8 volume = 0x3F; // TODO(purpasmart): Find out if this is the max value for the volume
151
152 cmd_buff[1] = RESULT_SUCCESS.raw;
153 cmd_buff[2] = volume;
154
155 LOG_WARNING(Service_HID, "(STUBBED) called");
151} 156}
152 157
153void HIDInit() { 158void HIDInit() {
@@ -156,19 +161,22 @@ void HIDInit() {
156 AddService(new HID_U_Interface); 161 AddService(new HID_U_Interface);
157 AddService(new HID_SPVR_Interface); 162 AddService(new HID_SPVR_Interface);
158 163
159 g_shared_mem = SharedMemory::Create("HID:SharedMem"); 164 shared_mem = SharedMemory::Create("HID:SharedMem");
165
166 next_pad_index = 0;
167 next_touch_index = 0;
160 168
161 // Create event handles 169 // Create event handles
162 g_event_pad_or_touch_1 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch1"); 170 event_pad_or_touch_1 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch1");
163 g_event_pad_or_touch_2 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch2"); 171 event_pad_or_touch_2 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch2");
164 g_event_accelerometer = Event::Create(RESETTYPE_ONESHOT, "HID:EventAccelerometer"); 172 event_accelerometer = Event::Create(RESETTYPE_ONESHOT, "HID:EventAccelerometer");
165 g_event_gyroscope = Event::Create(RESETTYPE_ONESHOT, "HID:EventGyroscope"); 173 event_gyroscope = Event::Create(RESETTYPE_ONESHOT, "HID:EventGyroscope");
166 g_event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad"); 174 event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad");
167} 175}
168 176
169void HIDShutdown() { 177void HIDShutdown() {
170
171} 178}
172 179
173} 180} // namespace HID
174} 181
182} // namespace Service
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 9c6e86f77..97462c7f8 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -18,16 +18,6 @@ namespace Kernel {
18namespace Service { 18namespace Service {
19namespace HID { 19namespace HID {
20 20
21// Handle to shared memory region designated to HID_User service
22extern Kernel::SharedPtr<Kernel::SharedMemory> g_shared_mem;
23
24// Event handles
25extern Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_1;
26extern Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2;
27extern Kernel::SharedPtr<Kernel::Event> g_event_accelerometer;
28extern Kernel::SharedPtr<Kernel::Event> g_event_gyroscope;
29extern Kernel::SharedPtr<Kernel::Event> g_event_debug_pad;
30
31/** 21/**
32 * Structure of a Pad controller state. 22 * Structure of a Pad controller state.
33 */ 23 */
@@ -65,7 +55,7 @@ struct PadState {
65}; 55};
66 56
67/** 57/**
68 * Structure of a single entry in the PadData's Pad state history array. 58 * Structure of a single entry of Pad state history within HID shared memory
69 */ 59 */
70struct PadDataEntry { 60struct PadDataEntry {
71 PadState current_state; 61 PadState current_state;
@@ -77,24 +67,65 @@ struct PadDataEntry {
77}; 67};
78 68
79/** 69/**
80 * Structure of all data related to the 3DS Pad. 70 * Structure of a single entry of touch state history within HID shared memory
81 */ 71 */
82struct PadData { 72struct TouchDataEntry {
83 s64 index_reset_ticks; 73 u16 x; ///< Y-coordinate of a touchpad press on the lower screen
84 s64 index_reset_ticks_previous; 74 u16 y; ///< X-coordinate of a touchpad press on the lower screen
85 u32 index; // the index of the last updated Pad state history element 75 BitField<0, 7, u32> valid; ///< Set to 1 when this entry contains actual X/Y data, otherwise 0
76};
77
78/**
79 * Structure of data stored in HID shared memory
80 */
81struct SharedMem {
82 /// Pad data, this is used for buttons and the circle pad
83 struct {
84 s64 index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0
85 s64 index_reset_ticks_previous; ///< Previous `index_reset_ticks`
86 u32 index; ///< Index of the last updated pad state entry
87
88 INSERT_PADDING_WORDS(0x2);
89
90 PadState current_state; ///< Current state of the pad buttons
91
92 // TODO(bunnei): Implement `raw_circle_pad_data` field
93 u32 raw_circle_pad_data; ///< Raw (analog) circle pad data, before being converted
86 94
87 u32 pad1; 95 INSERT_PADDING_WORDS(0x1);
88 u32 pad2;
89 96
90 PadState current_state; // same as entries[index].current_state 97 std::array<PadDataEntry, 8> entries; ///< Last 8 pad entries
91 u32 raw_circle_pad_data; 98 } pad;
92 99
93 u32 pad3; 100 /// Touchpad data, this is used for touchpad input
101 struct {
102 s64 index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0
103 s64 index_reset_ticks_previous; ///< Previous `index_reset_ticks`
104 u32 index; ///< Index of the last updated touch entry
94 105
95 std::array<PadDataEntry, 8> entries; // Pad state history 106 INSERT_PADDING_WORDS(0x1);
107
108 // TODO(bunnei): Implement `raw_entry` field
109 TouchDataEntry raw_entry; ///< Raw (analog) touch data, before being converted
110
111 std::array<TouchDataEntry, 8> entries; ///< Last 8 touch entries, in pixel coordinates
112 } touch;
96}; 113};
97 114
115// TODO: MSVC does not support using offsetof() on non-static data members even though this
116// is technically allowed since C++11. This macro should be enabled once MSVC adds
117// support for that.
118#ifndef _MSC_VER
119#define ASSERT_REG_POSITION(field_name, position) \
120 static_assert(offsetof(SharedMem, field_name) == position * 4, \
121 "Field "#field_name" has invalid position")
122
123ASSERT_REG_POSITION(pad.index_reset_ticks, 0x0);
124ASSERT_REG_POSITION(touch.index_reset_ticks, 0x2A);
125
126#undef ASSERT_REG_POSITION
127#endif // !defined(_MSC_VER)
128
98// Pre-defined PadStates for single button presses 129// Pre-defined PadStates for single button presses
99const PadState PAD_NONE = {{0}}; 130const PadState PAD_NONE = {{0}};
100const PadState PAD_A = {{1u << 0}}; 131const PadState PAD_A = {{1u << 0}};
@@ -130,7 +161,7 @@ const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
130 * None 161 * None
131 * Outputs: 162 * Outputs:
132 * 1 : Result of function, 0 on success, otherwise error code 163 * 1 : Result of function, 0 on success, otherwise error code
133 * 2 : Unused 164 * 2 : IPC Command Structure translate-header
134 * 3 : Handle to HID_User shared memory 165 * 3 : Handle to HID_User shared memory
135 * 4 : Event signaled by HID_User 166 * 4 : Event signaled by HID_User
136 * 5 : Event signaled by HID_User 167 * 5 : Event signaled by HID_User
@@ -140,12 +171,41 @@ const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
140 */ 171 */
141void GetIPCHandles(Interface* self); 172void GetIPCHandles(Interface* self);
142 173
143// Methods for updating the HID module's state 174/**
144void PadButtonPress(const PadState& pad_state); 175 * HID::EnableAccelerometer service function
145void PadButtonRelease(const PadState& pad_state); 176 * Inputs:
146void PadUpdateComplete(); 177 * None
178 * Outputs:
179 * 1 : Result of function, 0 on success, otherwise error code
180 */
181void EnableAccelerometer(Interface* self);
182
183/**
184 * HID::EnableGyroscopeLow service function
185 * Inputs:
186 * None
187 * Outputs:
188 * 1 : Result of function, 0 on success, otherwise error code
189 */
190void EnableGyroscopeLow(Interface* self);
191
192/**
193 * HID::GetSoundVolume service function
194 * Inputs:
195 * None
196 * Outputs:
197 * 1 : Result of function, 0 on success, otherwise error code
198 * 2 : u8 output value
199 */
200void GetSoundVolume(Interface* self);
147 201
202/// Checks for user input updates
203void HIDUpdate();
204
205/// Initialize HID service
148void HIDInit(); 206void HIDInit();
207
208/// Shutdown HID service
149void HIDShutdown(); 209void HIDShutdown();
150 210
151} 211}
diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp
index 790dcabbf..f296b076f 100644
--- a/src/core/hle/service/hid/hid_spvr.cpp
+++ b/src/core/hle/service/hid/hid_spvr.cpp
@@ -13,13 +13,13 @@ const Interface::FunctionInfo FunctionTable[] = {
13 {0x000A0000, GetIPCHandles, "GetIPCHandles"}, 13 {0x000A0000, GetIPCHandles, "GetIPCHandles"},
14 {0x000B0000, nullptr, "StartAnalogStickCalibration"}, 14 {0x000B0000, nullptr, "StartAnalogStickCalibration"},
15 {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, 15 {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
16 {0x00110000, nullptr, "EnableAccelerometer"}, 16 {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
17 {0x00120000, nullptr, "DisableAccelerometer"}, 17 {0x00120000, nullptr, "DisableAccelerometer"},
18 {0x00130000, nullptr, "EnableGyroscopeLow"}, 18 {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
19 {0x00140000, nullptr, "DisableGyroscopeLow"}, 19 {0x00140000, nullptr, "DisableGyroscopeLow"},
20 {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"}, 20 {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
21 {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"}, 21 {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
22 {0x00170000, nullptr, "GetSoundVolume"}, 22 {0x00170000, GetSoundVolume, "GetSoundVolume"},
23}; 23};
24 24
25HID_SPVR_Interface::HID_SPVR_Interface() { 25HID_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 1d0accefe..3682c9416 100644
--- a/src/core/hle/service/hid/hid_user.cpp
+++ b/src/core/hle/service/hid/hid_user.cpp
@@ -3,8 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/hle/hle.h" 5#include "core/hle/hle.h"
6#include "core/hle/kernel/event.h"
7#include "core/hle/kernel/shared_memory.h"
8#include "core/hle/service/hid/hid.h" 6#include "core/hle/service/hid/hid.h"
9#include "core/hle/service/hid/hid_user.h" 7#include "core/hle/service/hid/hid_user.h"
10 8
@@ -12,14 +10,14 @@ namespace Service {
12namespace HID { 10namespace HID {
13 11
14const Interface::FunctionInfo FunctionTable[] = { 12const Interface::FunctionInfo FunctionTable[] = {
15 {0x000A0000, GetIPCHandles, "GetIPCHandles"}, 13 {0x000A0000, GetIPCHandles, "GetIPCHandles"},
16 {0x00110000, nullptr, "EnableAccelerometer"}, 14 {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
17 {0x00120000, nullptr, "DisableAccelerometer"}, 15 {0x00120000, nullptr, "DisableAccelerometer"},
18 {0x00130000, nullptr, "EnableGyroscopeLow"}, 16 {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
19 {0x00140000, nullptr, "DisableGyroscopeLow"}, 17 {0x00140000, nullptr, "DisableGyroscopeLow"},
20 {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"}, 18 {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
21 {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"}, 19 {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
22 {0x00170000, nullptr, "GetSoundVolume"}, 20 {0x00170000, GetSoundVolume, "GetSoundVolume"},
23}; 21};
24 22
25HID_U_Interface::HID_U_Interface() { 23HID_U_Interface::HID_U_Interface() {
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 17385f9b2..bbb4eb9cd 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -7,8 +7,9 @@
7#include "common/string_util.h" 7#include "common/string_util.h"
8#include "common/symbols.h" 8#include "common/symbols.h"
9 9
10#include "core/arm/arm_interface.h" 10#include "core/core_timing.h"
11#include "core/mem_map.h" 11#include "core/mem_map.h"
12#include "core/arm/arm_interface.h"
12 13
13#include "core/hle/kernel/address_arbiter.h" 14#include "core/hle/kernel/address_arbiter.h"
14#include "core/hle/kernel/event.h" 15#include "core/hle/kernel/event.h"
@@ -551,7 +552,7 @@ static void SleepThread(s64 nanoseconds) {
551 552
552/// This returns the total CPU ticks elapsed since the CPU was powered-on 553/// This returns the total CPU ticks elapsed since the CPU was powered-on
553static s64 GetSystemTick() { 554static s64 GetSystemTick() {
554 return (s64)Core::g_app_core->GetTicks(); 555 return (s64)CoreTiming::GetTicks();
555} 556}
556 557
557/// Creates a memory block at the specified address with the specified permissions and size 558/// Creates a memory block at the specified address with the specified permissions and size
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 30318fc06..e6983a225 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -14,6 +14,7 @@
14#include "core/hle/hle.h" 14#include "core/hle/hle.h"
15#include "core/hle/service/gsp_gpu.h" 15#include "core/hle/service/gsp_gpu.h"
16#include "core/hle/service/dsp_dsp.h" 16#include "core/hle/service/dsp_dsp.h"
17#include "core/hle/service/hid/hid.h"
17 18
18#include "core/hw/hw.h" 19#include "core/hw/hw.h"
19#include "core/hw/gpu.h" 20#include "core/hw/gpu.h"
@@ -117,8 +118,14 @@ inline void Write(u32 addr, const T data) {
117 u8* src_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress())); 118 u8* src_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress()));
118 u8* dst_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress())); 119 u8* dst_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress()));
119 120
120 unsigned horizontal_scale = (config.scale_horizontally != 0) ? 2 : 1; 121 if (config.scaling > config.ScaleXY) {
121 unsigned vertical_scale = (config.scale_vertically != 0) ? 2 : 1; 122 LOG_CRITICAL(HW_GPU, "Unimplemented display transfer scaling mode %u", config.scaling.Value());
123 UNIMPLEMENTED();
124 break;
125 }
126
127 unsigned horizontal_scale = (config.scaling != config.NoScale) ? 2 : 1;
128 unsigned vertical_scale = (config.scaling == config.ScaleXY) ? 2 : 1;
122 129
123 u32 output_width = config.output_width / horizontal_scale; 130 u32 output_width = config.output_width / horizontal_scale;
124 u32 output_height = config.output_height / vertical_scale; 131 u32 output_height = config.output_height / vertical_scale;
@@ -139,14 +146,23 @@ inline void Write(u32 addr, const T data) {
139 break; 146 break;
140 } 147 }
141 148
142 // TODO(Subv): Blend the pixels when horizontal / vertical scaling is enabled, 149 // TODO(Subv): Implement the box filter when scaling is enabled
143 // right now we're just skipping the extra pixels. 150 // right now we're just skipping the extra pixels.
144 for (u32 y = 0; y < output_height; ++y) { 151 for (u32 y = 0; y < output_height; ++y) {
145 for (u32 x = 0; x < output_width; ++x) { 152 for (u32 x = 0; x < output_width; ++x) {
146 Math::Vec4<u8> src_color = { 0, 0, 0, 0 }; 153 Math::Vec4<u8> src_color = { 0, 0, 0, 0 };
147 154
148 u32 scaled_x = x * horizontal_scale; 155 // Calculate the [x,y] position of the input image
149 u32 scaled_y = y * vertical_scale; 156 // based on the current output position and the scale
157 u32 input_x = x * horizontal_scale;
158 u32 input_y = y * vertical_scale;
159
160 if (config.flip_vertically) {
161 // Flip the y value of the output data,
162 // we do this after calculating the [x,y] position of the input image
163 // to account for the scaling options.
164 y = output_height - y - 1;
165 }
150 166
151 u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format); 167 u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format);
152 u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format); 168 u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format);
@@ -158,14 +174,14 @@ inline void Write(u32 addr, const T data) {
158 u32 coarse_y = y & ~7; 174 u32 coarse_y = y & ~7;
159 u32 stride = output_width * dst_bytes_per_pixel; 175 u32 stride = output_width * dst_bytes_per_pixel;
160 176
161 src_offset = (scaled_x + scaled_y * config.input_width) * src_bytes_per_pixel; 177 src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel;
162 dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; 178 dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride;
163 } else { 179 } else {
164 // Interpret the input as tiled and the output as linear 180 // Interpret the input as tiled and the output as linear
165 u32 coarse_y = scaled_y & ~7; 181 u32 coarse_y = input_y & ~7;
166 u32 stride = config.input_width * src_bytes_per_pixel; 182 u32 stride = config.input_width * src_bytes_per_pixel;
167 183
168 src_offset = VideoCore::GetMortonOffset(scaled_x, scaled_y, src_bytes_per_pixel) + coarse_y * stride; 184 src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride;
169 dst_offset = (x + y * output_width) * dst_bytes_per_pixel; 185 dst_offset = (x + y * output_width) * dst_bytes_per_pixel;
170 } 186 }
171 187
@@ -295,6 +311,9 @@ static void VBlankCallback(u64 userdata, int cycles_late) {
295 // this. Certain games expect this to be periodically signaled. 311 // this. Certain games expect this to be periodically signaled.
296 DSP_DSP::SignalInterrupt(); 312 DSP_DSP::SignalInterrupt();
297 313
314 // Check for user input updates
315 Service::HID::HIDUpdate();
316
298 // Reschedule recurrent event 317 // Reschedule recurrent event
299 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); 318 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event);
300} 319}
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 5b7f0a4e9..c8f884494 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -188,17 +188,22 @@ struct Regs {
188 BitField<16, 16, u32> input_height; 188 BitField<16, 16, u32> input_height;
189 }; 189 };
190 190
191 enum ScalingMode : u32 {
192 NoScale = 0, // Doesn't scale the image
193 ScaleX = 1, // Downscales the image in half in the X axis and applies a box filter
194 ScaleXY = 2, // Downscales the image in half in both the X and Y axes and applies a box filter
195 };
196
191 union { 197 union {
192 u32 flags; 198 u32 flags;
193 199
194 BitField< 0, 1, u32> flip_data; // flips input data horizontally (TODO) if true 200 BitField< 0, 1, u32> flip_vertically; // flips input data vertically
195 BitField< 1, 1, u32> output_tiled; // Converts from linear to tiled format 201 BitField< 1, 1, u32> output_tiled; // Converts from linear to tiled format
196 BitField< 3, 1, u32> raw_copy; // Copies the data without performing any processing 202 BitField< 3, 1, u32> raw_copy; // Copies the data without performing any processing
197 BitField< 8, 3, PixelFormat> input_format; 203 BitField< 8, 3, PixelFormat> input_format;
198 BitField<12, 3, PixelFormat> output_format; 204 BitField<12, 3, PixelFormat> output_format;
199 205
200 BitField<24, 1, u32> scale_horizontally; 206 BitField<24, 2, ScalingMode> scaling; // Determines the scaling mode of the transfer
201 BitField<25, 1, u32> scale_vertically;
202 }; 207 };
203 208
204 INSERT_PADDING_WORDS(0x1); 209 INSERT_PADDING_WORDS(0x1);
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index 9ae2de99f..f6f670060 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -20,8 +20,8 @@ struct NCCH_Header {
20 u16 version; 20 u16 version;
21 u8 reserved_0[4]; 21 u8 reserved_0[4];
22 u8 program_id[8]; 22 u8 program_id[8];
23 u8 temp_flag; 23 u8 reserved_1[0x10];
24 u8 reserved_1[0x2f]; 24 u8 logo_region_hash[0x20];
25 u8 product_code[0x10]; 25 u8 product_code[0x10];
26 u8 extended_header_hash[0x20]; 26 u8 extended_header_hash[0x20];
27 u32 extended_header_size; 27 u32 extended_header_size;
@@ -29,15 +29,16 @@ struct NCCH_Header {
29 u8 flags[8]; 29 u8 flags[8];
30 u32 plain_region_offset; 30 u32 plain_region_offset;
31 u32 plain_region_size; 31 u32 plain_region_size;
32 u8 reserved_3[8]; 32 u32 logo_region_offset;
33 u32 logo_region_size;
33 u32 exefs_offset; 34 u32 exefs_offset;
34 u32 exefs_size; 35 u32 exefs_size;
35 u32 exefs_hash_region_size; 36 u32 exefs_hash_region_size;
36 u8 reserved_4[4]; 37 u8 reserved_3[4];
37 u32 romfs_offset; 38 u32 romfs_offset;
38 u32 romfs_size; 39 u32 romfs_size;
39 u32 romfs_hash_region_size; 40 u32 romfs_hash_region_size;
40 u8 reserved_5[4]; 41 u8 reserved_4[4];
41 u8 exefs_super_block_hash[0x20]; 42 u8 exefs_super_block_hash[0x20];
42 u8 romfs_super_block_hash[0x20]; 43 u8 romfs_super_block_hash[0x20];
43}; 44};
@@ -88,8 +89,7 @@ struct ExHeader_DependencyList{
88}; 89};
89 90
90struct ExHeader_SystemInfo{ 91struct ExHeader_SystemInfo{
91 u32 save_data_size; 92 u64 save_data_size;
92 u8 reserved[4];
93 u8 jump_id[8]; 93 u8 jump_id[8];
94 u8 reserved_2[0x30]; 94 u8 reserved_2[0x30];
95}; 95};
@@ -104,11 +104,14 @@ struct ExHeader_StorageInfo{
104 104
105struct ExHeader_ARM11_SystemLocalCaps{ 105struct ExHeader_ARM11_SystemLocalCaps{
106 u8 program_id[8]; 106 u8 program_id[8];
107 u8 flags[8]; 107 u32 core_version;
108 u8 resource_limit_descriptor[0x10][2]; 108 u8 flags[3];
109 u8 priority;
110 u8 resource_limit_descriptor[0x16][2];
109 ExHeader_StorageInfo storage_info; 111 ExHeader_StorageInfo storage_info;
110 u8 service_access_control[0x20][8]; 112 u8 service_access_control[0x32][8];
111 u8 reserved[0x1f]; 113 u8 ex_service_access_control[0x2][8];
114 u8 reserved[0xf];
112 u8 resource_limit_category; 115 u8 resource_limit_category;
113}; 116};
114 117
diff --git a/src/video_core/color.h b/src/video_core/color.h
index 14ade74f2..43d635e2c 100644
--- a/src/video_core/color.h
+++ b/src/video_core/color.h
@@ -124,7 +124,7 @@ inline u32 DecodeD24(const u8* bytes) {
124 * @return Resulting values stored as a Math::Vec2 124 * @return Resulting values stored as a Math::Vec2
125 */ 125 */
126inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) { 126inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
127 return { (bytes[2] << 16) | (bytes[1] << 8) | bytes[0], bytes[3] }; 127 return { static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3] };
128} 128}
129 129
130/** 130/**
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 745c4f4ed..83982b4f2 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -322,7 +322,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
322 case Regs::TextureFormat::RGBA8: 322 case Regs::TextureFormat::RGBA8:
323 { 323 {
324 auto res = Color::DecodeRGBA8(source + VideoCore::GetMortonOffset(x, y, 4)); 324 auto res = Color::DecodeRGBA8(source + VideoCore::GetMortonOffset(x, y, 4));
325 return { res.r(), res.g(), res.b(), disable_alpha ? 255 : res.a() }; 325 return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) };
326 } 326 }
327 327
328 case Regs::TextureFormat::RGB8: 328 case Regs::TextureFormat::RGB8:
@@ -334,7 +334,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
334 case Regs::TextureFormat::RGB5A1: 334 case Regs::TextureFormat::RGB5A1:
335 { 335 {
336 auto res = Color::DecodeRGB5A1(source + VideoCore::GetMortonOffset(x, y, 2)); 336 auto res = Color::DecodeRGB5A1(source + VideoCore::GetMortonOffset(x, y, 2));
337 return { res.r(), res.g(), res.b(), disable_alpha ? 255 : res.a() }; 337 return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) };
338 } 338 }
339 339
340 case Regs::TextureFormat::RGB565: 340 case Regs::TextureFormat::RGB565:
@@ -346,7 +346,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
346 case Regs::TextureFormat::RGBA4: 346 case Regs::TextureFormat::RGBA4:
347 { 347 {
348 auto res = Color::DecodeRGBA4(source + VideoCore::GetMortonOffset(x, y, 2)); 348 auto res = Color::DecodeRGBA4(source + VideoCore::GetMortonOffset(x, y, 2));
349 return { res.r(), res.g(), res.b(), disable_alpha ? 255 : res.a() }; 349 return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) };
350 } 350 }
351 351
352 case Regs::TextureFormat::IA8: 352 case Regs::TextureFormat::IA8:
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp
index 4eb3e743e..e8d865172 100644
--- a/src/video_core/vertex_shader.cpp
+++ b/src/video_core/vertex_shader.cpp
@@ -72,7 +72,7 @@ struct VertexShaderState {
72 u32* program_counter; 72 u32* program_counter;
73 73
74 const float24* input_register_table[16]; 74 const float24* input_register_table[16];
75 float24* output_register_table[7*4]; 75 Math::Vec4<float24> output_registers[16];
76 76
77 Math::Vec4<float24> temporary_registers[16]; 77 Math::Vec4<float24> temporary_registers[16];
78 bool conditional_code[2]; 78 bool conditional_code[2];
@@ -198,8 +198,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
198 src2[3] = src2[3] * float24::FromFloat32(-1); 198 src2[3] = src2[3] * float24::FromFloat32(-1);
199 } 199 }
200 200
201 float24* dest = (instr.common.dest.Value() < 0x08) ? state.output_register_table[4*instr.common.dest.Value().GetIndex()] 201 float24* dest = (instr.common.dest.Value() < 0x10) ? &state.output_registers[instr.common.dest.Value().GetIndex()][0]
202 : (instr.common.dest.Value() < 0x10) ? dummy_vec4_float24
203 : (instr.common.dest.Value() < 0x20) ? &state.temporary_registers[instr.common.dest.Value().GetIndex()][0] 202 : (instr.common.dest.Value() < 0x20) ? &state.temporary_registers[instr.common.dest.Value().GetIndex()][0]
204 : dummy_vec4_float24; 203 : dummy_vec4_float24;
205 204
@@ -409,8 +408,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
409 src3[3] = src3[3] * float24::FromFloat32(-1); 408 src3[3] = src3[3] * float24::FromFloat32(-1);
410 } 409 }
411 410
412 float24* dest = (instr.mad.dest.Value() < 0x08) ? state.output_register_table[4*instr.mad.dest.Value().GetIndex()] 411 float24* dest = (instr.mad.dest.Value() < 0x10) ? &state.output_registers[instr.mad.dest.Value().GetIndex()][0]
413 : (instr.mad.dest.Value() < 0x10) ? dummy_vec4_float24
414 : (instr.mad.dest.Value() < 0x20) ? &state.temporary_registers[instr.mad.dest.Value().GetIndex()][0] 412 : (instr.mad.dest.Value() < 0x20) ? &state.temporary_registers[instr.mad.dest.Value().GetIndex()][0]
415 : dummy_vec4_float24; 413 : dummy_vec4_float24;
416 414
@@ -587,12 +585,18 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) {
587 if(num_attributes > 14) state.input_register_table[attribute_register_map.attribute14_register] = &input.attr[14].x; 585 if(num_attributes > 14) state.input_register_table[attribute_register_map.attribute14_register] = &input.attr[14].x;
588 if(num_attributes > 15) state.input_register_table[attribute_register_map.attribute15_register] = &input.attr[15].x; 586 if(num_attributes > 15) state.input_register_table[attribute_register_map.attribute15_register] = &input.attr[15].x;
589 587
590 // Setup output register table 588 state.conditional_code[0] = false;
591 OutputVertex ret; 589 state.conditional_code[1] = false;
592 // Zero output so that attributes which aren't output won't have denormals in them, which will 590
593 // slow us down later. 591 ProcessShaderCode(state);
594 memset(&ret, 0, sizeof(ret)); 592 DebugUtils::DumpShader(shader_memory.data(), state.debug.max_offset, swizzle_data.data(),
593 state.debug.max_opdesc_id, registers.vs_main_offset,
594 registers.vs_output_attributes);
595 595
596 // Setup output data
597 OutputVertex ret;
598 // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to
599 // figure out what those circumstances are and enable the remaining outputs then.
596 for (int i = 0; i < 7; ++i) { 600 for (int i = 0; i < 7; ++i) {
597 const auto& output_register_map = registers.vs_output_attributes[i]; 601 const auto& output_register_map = registers.vs_output_attributes[i];
598 602
@@ -601,18 +605,18 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) {
601 output_register_map.map_z, output_register_map.map_w 605 output_register_map.map_z, output_register_map.map_w
602 }; 606 };
603 607
604 for (int comp = 0; comp < 4; ++comp) 608 for (int comp = 0; comp < 4; ++comp) {
605 state.output_register_table[4*i+comp] = ((float24*)&ret) + semantics[comp]; 609 float24* out = ((float24*)&ret) + semantics[comp];
610 if (semantics[comp] != Regs::VSOutputAttributes::INVALID) {
611 *out = state.output_registers[i][comp];
612 } else {
613 // Zero output so that attributes which aren't output won't have denormals in them,
614 // which would slow us down later.
615 memset(out, 0, sizeof(*out));
616 }
617 }
606 } 618 }
607 619
608 state.conditional_code[0] = false;
609 state.conditional_code[1] = false;
610
611 ProcessShaderCode(state);
612 DebugUtils::DumpShader(shader_memory.data(), state.debug.max_offset, swizzle_data.data(),
613 state.debug.max_opdesc_id, registers.vs_main_offset,
614 registers.vs_output_attributes);
615
616 LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)", 620 LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)",
617 ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(), 621 ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(),
618 ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(), 622 ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(),