summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/platform.h6
-rw-r--r--src/core/CMakeLists.txt2
-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/arm/dyncom/arm_dyncom_interpreter.cpp33
-rw-r--r--src/core/hle/service/gsp_gpu.cpp44
-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.cpp32
-rw-r--r--src/core/hw/gpu.h4
-rw-r--r--src/core/hw/hw.cpp38
-rw-r--r--src/core/hw/hw.h24
-rw-r--r--src/core/hw/lcd.cpp66
-rw-r--r--src/core/hw/lcd.h88
-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/renderer_opengl/renderer_opengl.cpp54
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h5
-rw-r--r--src/video_core/vertex_shader.cpp44
33 files changed, 724 insertions, 316 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) {
return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win));
}
+void EmuWindow_GLFW::OnMouseButtonEvent(GLFWwindow* win, int button, int action, int mods) {
+ if (button == GLFW_MOUSE_BUTTON_LEFT) {
+ auto emu_window = GetEmuWindow(win);
+ auto layout = emu_window->GetFramebufferLayout();
+ double x, y;
+ glfwGetCursorPos(win, &x, &y);
+
+ if (action == GLFW_PRESS)
+ emu_window->TouchPressed(static_cast<unsigned>(x), static_cast<unsigned>(y));
+ else if (action == GLFW_RELEASE)
+ emu_window->TouchReleased();
+ }
+}
+
+void EmuWindow_GLFW::OnCursorPosEvent(GLFWwindow* win, double x, double y) {
+ GetEmuWindow(win)->TouchMoved(static_cast<unsigned>(x), static_cast<unsigned>(y));
+}
+
/// Called by GLFW when a key event occurs
void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) {
-
- int keyboard_id = GetEmuWindow(win)->keyboard_id;
+ auto emu_window = GetEmuWindow(win);
+ int keyboard_id = emu_window->keyboard_id;
if (action == GLFW_PRESS) {
- EmuWindow::KeyPressed({key, keyboard_id});
+ emu_window->KeyPressed({key, keyboard_id});
} else if (action == GLFW_RELEASE) {
- EmuWindow::KeyReleased({key, keyboard_id});
+ emu_window->KeyReleased({key, keyboard_id});
}
-
- Service::HID::PadUpdateComplete();
}
/// Whether the window is still open, and a close request hasn't yet been sent
@@ -88,6 +104,8 @@ EmuWindow_GLFW::EmuWindow_GLFW() {
// Setup callbacks
glfwSetKeyCallback(m_render_window, OnKeyEvent);
+ glfwSetMouseButtonCallback(m_render_window, OnMouseButtonEvent);
+ glfwSetCursorPosCallback(m_render_window, OnCursorPosEvent);
glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent);
glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent);
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:
static void OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods);
+ static void OnMouseButtonEvent(GLFWwindow* window, int button, int action, int mods);
+
+ static void OnCursorPosEvent(GLFWwindow* window, double x, double y);
+
/// Whether the window is still open, and a close request hasn't yet been sent
const bool IsOpen();
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()
void GRenderWindow::keyPressEvent(QKeyEvent* event)
{
- EmuWindow::KeyPressed({event->key(), keyboard_id});
- Service::HID::PadUpdateComplete();
+ this->KeyPressed({event->key(), keyboard_id});
}
void GRenderWindow::keyReleaseEvent(QKeyEvent* event)
{
- EmuWindow::KeyReleased({event->key(), keyboard_id});
- Service::HID::PadUpdateComplete();
+ this->KeyReleased({event->key(), keyboard_id});
+}
+
+void GRenderWindow::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton)
+ {
+ auto pos = event->pos();
+ this->TouchPressed(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y()));
+ }
+}
+
+void GRenderWindow::mouseMoveEvent(QMouseEvent *event)
+{
+ auto pos = event->pos();
+ this->TouchMoved(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y()));
+}
+
+void GRenderWindow::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton)
+ this->TouchReleased();
}
void 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:
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+
void ReloadSetKeymaps() override;
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 @@
// The user data dir
#define ROOT_DIR "."
-#ifdef _WIN32
- #define USERDATA_DIR "user"
- #define EMU_DATA_DIR "Citra Emulator"
+#define USERDATA_DIR "user"
+#ifdef USER_DIR
+ #define EMU_DATA_DIR USER_DIR
#else
- #define USERDATA_DIR "user"
- #ifdef USER_DIR
- #define EMU_DATA_DIR USER_DIR
+ #ifdef _WIN32
+ #define EMU_DATA_DIR "Citra Emulator"
#else
#define EMU_DATA_DIR "citra-emu"
#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 @@
#include "video_core/video_core.h"
void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) {
- Service::HID::PadState mapped_key = KeyMap::GetPadKey(key);
-
- Service::HID::PadButtonPress(mapped_key);
+ pad_state.hex |= KeyMap::GetPadKey(key).hex;
}
void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) {
- Service::HID::PadState mapped_key = KeyMap::GetPadKey(key);
+ pad_state.hex &= ~KeyMap::GetPadKey(key).hex;
+}
+
+/**
+ * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
+ * @param layout FramebufferLayout object describing the framebuffer size and screen positions
+ * @param framebuffer_x Framebuffer x-coordinate to check
+ * @param framebuffer_y Framebuffer y-coordinate to check
+ * @return True if the coordinates are within the touchpad, otherwise false
+ */
+static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x,
+ unsigned framebuffer_y) {
+ return (framebuffer_y >= layout.bottom_screen.top &&
+ framebuffer_y < layout.bottom_screen.bottom &&
+ framebuffer_x >= layout.bottom_screen.left &&
+ framebuffer_x < layout.bottom_screen.right);
+}
+
+void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
+ if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
+ return;
+
+ touch_x = VideoCore::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) /
+ (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
+ touch_y = VideoCore::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) /
+ (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
- Service::HID::PadButtonRelease(mapped_key);
+ touch_pressed = true;
+ pad_state.touch = 1;
}
-EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) {
+void EmuWindow::TouchReleased() {
+ touch_pressed = false;
+ touch_x = 0;
+ touch_y = 0;
+ pad_state.touch = 0;
+}
+
+void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
+ if (!touch_pressed)
+ return;
+
+ if (IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
+ TouchPressed(framebuffer_x, framebuffer_y);
+ else
+ TouchReleased();
+}
+
+EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width,
+ unsigned height) {
+
ASSERT(width > 0);
ASSERT(height > 0);
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:
virtual void ReloadSetKeymaps() = 0;
/// Signals a key press action to the HID module
- static void KeyPressed(KeyMap::HostDeviceKey key);
+ void KeyPressed(KeyMap::HostDeviceKey key);
/// Signals a key release action to the HID module
- static void KeyReleased(KeyMap::HostDeviceKey key);
+ void KeyReleased(KeyMap::HostDeviceKey key);
+
+ /**
+ * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
+ * @param framebuffer_x Framebuffer x-coordinate that was pressed
+ * @param framebuffer_y Framebuffer y-coordinate that was pressed
+ */
+ void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
+
+ /// Signal that a touch released event has occurred (e.g. mouse click released)
+ void TouchReleased();
+
+ /**
+ * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
+ * @param framebuffer_x Framebuffer x-coordinate
+ * @param framebuffer_y Framebuffer y-coordinate
+ */
+ void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
+
+ /**
+ * Gets the current pad state (which buttons are pressed and the circle pad direction).
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Fix this function to be thread-safe.
+ * @return PadState object indicating the current pad state
+ */
+ const Service::HID::PadState GetPadState() const {
+ return pad_state;
+ }
+
+ /**
+ * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Fix this function to be thread-safe.
+ * @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 {
+ return std::make_tuple(touch_x, touch_y, touch_pressed);
+ }
/**
* Returns currently active configuration.
@@ -100,21 +138,15 @@ public:
return framebuffer_layout;
}
- /**
- * Gets window client area width in logical coordinates.
- * @note For high-DPI systems, this is smaller than the framebuffer size.
- * @note This method is thread-safe
- */
- std::pair<unsigned,unsigned> GetClientAreaSize() const {
- return std::make_pair(client_area_width, client_area_height);
- }
-
protected:
- EmuWindow()
- {
+ EmuWindow() {
// TODO: Find a better place to set this.
config.min_client_area_size = std::make_pair(400u, 480u);
active_config = config;
+ pad_state.hex = 0;
+ touch_x = 0;
+ touch_y = 0;
+ touch_pressed = false;
}
virtual ~EmuWindow() {}
@@ -168,4 +200,11 @@ private:
WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration
+
+ bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
+
+ u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
+ u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
+
+ Service::HID::PadState pad_state;
};
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 7b479b569..649640e72 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -45,6 +45,7 @@ static std::shared_ptr<Logger> global_logger;
SUB(Service, SOC) \
CLS(HW) \
SUB(HW, Memory) \
+ SUB(HW, LCD) \
SUB(HW, GPU) \
CLS(Frontend) \
CLS(Render) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 7b67b3c07..83d64145b 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -65,6 +65,7 @@ enum class Class : ClassType {
Service_SOC, ///< The SOC (Socket) service
HW, ///< Low-level hardware emulation
HW_Memory, ///< Memory-map and address translation
+ HW_LCD, ///< LCD register emulation
HW_GPU, ///< GPU control emulation
Frontend, ///< Emulator UI
Render, ///< Emulator video output and hardware acceleration
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) {
}
#endif
-#else
+#else // EMU_PLATFORM != PLATFORM_WINDOWS
#define EMU_FASTCALL __attribute__((fastcall))
#define __stdcall
@@ -92,10 +92,6 @@ inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
#define BOOL bool
#define DWORD u32
-#endif
-
-#if EMU_PLATFORM != PLATFORM_WINDOWS
-
// TODO: Hacks..
#include <limits.h>
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 212da25c5..33e5be3a4 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -87,6 +87,7 @@ set(SRCS
hle/svc.cpp
hw/gpu.cpp
hw/hw.cpp
+ hw/lcd.cpp
loader/elf.cpp
loader/loader.cpp
loader/ncch.cpp
@@ -196,6 +197,7 @@ set(HEADERS
hle/svc.h
hw/gpu.h
hw/hw.h
+ hw/lcd.h
loader/elf.h
loader/loader.h
loader/ncch.h
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:
virtual void SetCPSR(u32 cpsr) = 0;
/**
- * Returns the number of clock ticks since the last rese
- * @return Returns number of clock ticks
- */
- virtual u64 GetTicks() const = 0;
-
- /**
* Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
* @param ticks Number of ticks to advance the CPU core
*/
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) {
state->Cpsr = cpsr;
}
-u64 ARM_DynCom::GetTicks() const {
- // TODO(Subv): Remove ARM_DynCom::GetTicks() and use CoreTiming::GetTicks() directly once ARMemu is gone
- return CoreTiming::GetTicks();
-}
-
void ARM_DynCom::AddTicks(u64 ticks) {
down_count -= ticks;
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:
u32 GetCPSR() const override;
void SetCPSR(u32 cpsr) override;
- u64 GetTicks() const override;
void AddTicks(u64 ticks) override;
void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg);
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 2f72f5077..d953adba9 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -4488,10 +4488,6 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff;
RD = RN + operand2;
- if (inst_cream->Rn == 15 || inst_cream->Rm == 15) {
- LOG_ERROR(Core_ARM11, "invalid operands for UXTAH");
- CITRA_IGNORE_EXIT(-1);
- }
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(uxtah_inst));
@@ -4822,10 +4818,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
uint64_t rm = RM;
uint64_t rs = RS;
uint64_t rn = RN;
- if (inst_cream->Rm == 15 || inst_cream->Rs == 15 || inst_cream->Rn == 15) {
- LOG_ERROR(Core_ARM11, "invalid operands for MLA");
- CITRA_IGNORE_EXIT(-1);
- }
+
RD = static_cast<uint32_t>((rm * rs + rn) & 0xffffffff);
if (inst_cream->S) {
UPDATE_NFLAG(RD);
@@ -5104,10 +5097,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
PLD_INST:
{
- // Instruction not implemented
- //LOG_CRITICAL(Core_ARM11, "unimplemented instruction");
+ // Not implemented. PLD is a hint instruction, so it's optional.
+
cpu->Reg[15] += GET_INST_SIZE(cpu);
- INC_PC(sizeof(stc_inst));
+ INC_PC(sizeof(pld_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
@@ -6033,15 +6026,12 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
sxtb_inst* inst_cream = (sxtb_inst*)inst_base->component;
- if (inst_cream->Rm == 15) {
- LOG_ERROR(Core_ARM11, "invalid operand for SXTB");
- CITRA_IGNORE_EXIT(-1);
- }
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate);
if (BIT(operand2, 7)) {
operand2 |= 0xffffff00;
- } else
+ } else {
operand2 &= 0xff;
+ }
RD = operand2;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -6299,8 +6289,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
swp_inst* inst_cream = (swp_inst*)inst_base->component;
addr = RN;
- unsigned int value;
- value = Memory::Read32(addr);
+ unsigned int value = Memory::Read32(addr);
Memory::Write32(addr, RM);
RD = value;
@@ -6329,10 +6318,6 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
sxtab_inst* inst_cream = (sxtab_inst*)inst_base->component;
- // R15 should be check
- if(inst_cream->Rn == 15 || inst_cream->Rm == 15 || inst_cream->Rd ==15){
- CITRA_IGNORE_EXIT(-1);
- }
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff;
// Sign extend for byte
@@ -6383,10 +6368,6 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
sxtah_inst* inst_cream = (sxtah_inst*)inst_base->component;
- // R15 should be check
- if(inst_cream->Rn == 15 || inst_cream->Rm == 15 || inst_cream->Rd ==15) {
- CITRA_IGNORE_EXIT(-1);
- }
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff;
// Sign extend for half
operand2 = (0x8000 & operand2) ? (0xFFFF0000 | operand2) : operand2;
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index c23cfa3c8..cff585698 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -7,14 +7,20 @@
#include "core/mem_map.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/result.h"
#include "gsp_gpu.h"
+#include "core/hw/hw.h"
#include "core/hw/gpu.h"
+#include "core/hw/lcd.h"
#include "video_core/gpu_debugger.h"
// Main graphics debugger object - TODO: Here is probably not the best place for this
GraphicsDebugger g_debugger;
+// Beginning address of HW regs
+const static u32 REGS_BEGIN = 0x1EB00000;
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace GSP_GPU
@@ -85,7 +91,7 @@ static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
return;
while (size_in_bytes > 0) {
- GPU::Write<u32>(base_address + 0x1EB00000, *data);
+ HW::Write<u32>(base_address + REGS_BEGIN, *data);
size_in_bytes -= 4;
++data;
@@ -128,15 +134,15 @@ static void WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32*
return;
while (size_in_bytes > 0) {
- const u32 reg_address = base_address + 0x1EB00000;
+ const u32 reg_address = base_address + REGS_BEGIN;
u32 reg_value;
- GPU::Read<u32>(reg_value, reg_address);
+ 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);
- GPU::Write<u32>(reg_address, reg_value);
+ HW::Write<u32>(reg_address, reg_value);
size_in_bytes -= 4;
++data;
@@ -188,7 +194,7 @@ static void ReadHWRegs(Service::Interface* self) {
u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]);
while (size > 0) {
- GPU::Read<u32>(*dst, reg_addr + 0x1EB00000);
+ HW::Read<u32>(*dst, reg_addr + REGS_BEGIN);
size -= 4;
++dst;
@@ -427,6 +433,32 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
}
}
+/**
+ * GSP_GPU::SetLcdForceBlack service function
+ *
+ * Enable or disable REG_LCDCOLORFILL with the color black.
+ *
+ * Inputs:
+ * 1: Black color fill flag (0 = don't fill, !0 = fill)
+ * Outputs:
+ * 1: Result code
+ */
+static void SetLcdForceBlack(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ bool enable_black = cmd_buff[1] != 0;
+ LCD::Regs::ColorFill data = {0};
+
+ // Since data is already zeroed, there is no need to explicitly set
+ // the color to black (all zero).
+ data.is_enabled = enable_black;
+
+ LCD::Write(HW::VADDR_LCD + 4 * LCD_REG_INDEX(color_fill_top), data.raw); // Top LCD
+ LCD::Write(HW::VADDR_LCD + 4 * LCD_REG_INDEX(color_fill_bottom), data.raw); // Bottom LCD
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+}
+
/// This triggers handling of the GX command written to the command buffer in shared memory.
static void TriggerCmdReqQueue(Service::Interface* self) {
// Iterate through each thread's command queue...
@@ -460,7 +492,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00080082, FlushDataCache, "FlushDataCache"},
{0x00090082, nullptr, "InvalidateDataCache"},
{0x000A0044, nullptr, "RegisterInterruptEvents"},
- {0x000B0040, nullptr, "SetLcdForceBlack"},
+ {0x000B0040, SetLcdForceBlack, "SetLcdForceBlack"},
{0x000C0000, TriggerCmdReqQueue, "TriggerCmdReqQueue"},
{0x000D0140, nullptr, "SetDisplayTransfer"},
{0x000E0180, nullptr, "SetTextureCopy"},
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 @@
#include "core/hle/service/hid/hid_spvr.h"
#include "core/hle/service/hid/hid_user.h"
-#include "core/arm/arm_interface.h"
+#include "core/core_timing.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/hle.h"
+#include "video_core/video_core.h"
+
namespace Service {
namespace HID {
-Kernel::SharedPtr<Kernel::SharedMemory> g_shared_mem = nullptr;
-
-Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_1;
-Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2;
-Kernel::SharedPtr<Kernel::Event> g_event_accelerometer;
-Kernel::SharedPtr<Kernel::Event> g_event_gyroscope;
-Kernel::SharedPtr<Kernel::Event> g_event_debug_pad;
-
-// Next Pad state update information
-static PadState next_state = {{0}};
-static u32 next_index = 0;
-static s16 next_circle_x = 0;
-static s16 next_circle_y = 0;
-
-/**
- * Gets a pointer to the PadData structure inside HID shared memory
- */
-static inline PadData* GetPadData() {
- if (g_shared_mem == nullptr)
- return nullptr;
- return reinterpret_cast<PadData*>(g_shared_mem->GetPointer().ValueOr(nullptr));
-}
+static const int MAX_CIRCLEPAD_POS = 0x9C; ///< Max value for a circle pad position
+
+// Handle to shared memory region designated to HID_User service
+static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem = nullptr;
+
+// Event handles
+static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1 = nullptr;
+static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2 = nullptr;
+static Kernel::SharedPtr<Kernel::Event> event_accelerometer = nullptr;
+static Kernel::SharedPtr<Kernel::Event> event_gyroscope = nullptr;
+static Kernel::SharedPtr<Kernel::Event> event_debug_pad = nullptr;
+
+static u32 next_pad_index = 0;
+static u32 next_touch_index = 0;
// TODO(peachum):
// Add a method for setting analog input from joystick device for the circle Pad.
@@ -51,103 +45,114 @@ static inline PadData* GetPadData() {
// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41
// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41
-/**
- * Circle Pad from keys.
- *
- * This is implemented as "pushed all the way to an edge (max) or centered (0)".
- *
- * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions.
- */
-static void UpdateNextCirclePadState() {
- static const s16 max_value = 0x9C;
- next_circle_x = next_state.circle_left ? -max_value : 0x0;
- next_circle_x += next_state.circle_right ? max_value : 0x0;
- next_circle_y = next_state.circle_down ? -max_value : 0x0;
- next_circle_y += next_state.circle_up ? max_value : 0x0;
-}
-
-/**
- * Sets a Pad state (button or button combo) as pressed
- */
-void PadButtonPress(const PadState& pad_state) {
- next_state.hex |= pad_state.hex;
- UpdateNextCirclePadState();
-}
-
-/**
- * Sets a Pad state (button or button combo) as released
- */
-void PadButtonRelease(const PadState& pad_state) {
- next_state.hex &= ~pad_state.hex;
- UpdateNextCirclePadState();
-}
-
-/**
- * Called after all Pad changes to be included in this update have been made,
- * including both Pad key changes and analog circle Pad changes.
- */
-void PadUpdateComplete() {
- PadData* pad_data = GetPadData();
+void HIDUpdate() {
+ SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer().ValueOr(nullptr));
+ const PadState state = VideoCore::g_emu_window->GetPadState();
- if (pad_data == nullptr) {
+ if (mem == nullptr) {
+ LOG_DEBUG(Service_HID, "Cannot update HID prior to mapping shared memory!");
return;
}
- // Update PadData struct
- pad_data->current_state.hex = next_state.hex;
- pad_data->index = next_index;
- next_index = (next_index + 1) % pad_data->entries.size();
+ mem->pad.current_state.hex = state.hex;
+ mem->pad.index = next_pad_index;
+ ++next_touch_index %= mem->pad.entries.size();
// Get the previous Pad state
- u32 last_entry_index = (pad_data->index - 1) % pad_data->entries.size();
- PadState old_state = pad_data->entries[last_entry_index].current_state;
+ u32 last_entry_index = (mem->pad.index - 1) % mem->pad.entries.size();
+ PadState old_state = mem->pad.entries[last_entry_index].current_state;
// Compute bitmask with 1s for bits different from the old state
- PadState changed;
- changed.hex = (next_state.hex ^ old_state.hex);
-
- // Compute what was added
- PadState additions;
- additions.hex = changed.hex & next_state.hex;
-
- // Compute what was removed
- PadState removals;
- removals.hex = changed.hex & old_state.hex;
+ PadState changed = { { (state.hex ^ old_state.hex) } };
// Get the current Pad entry
- PadDataEntry* current_pad_entry = &pad_data->entries[pad_data->index];
+ PadDataEntry* pad_entry = &mem->pad.entries[mem->pad.index];
// Update entry properties
- current_pad_entry->current_state.hex = next_state.hex;
- current_pad_entry->delta_additions.hex = additions.hex;
- current_pad_entry->delta_removals.hex = removals.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
- current_pad_entry->circle_pad_x = next_circle_x;
- current_pad_entry->circle_pad_y = next_circle_y;
+ 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 :
+ state.circle_up ? MAX_CIRCLEPAD_POS : 0x0;
// If we just updated index 0, provide a new timestamp
- if (pad_data->index == 0) {
- pad_data->index_reset_ticks_previous = pad_data->index_reset_ticks;
- pad_data->index_reset_ticks = (s64)Core::g_app_core->GetTicks();
+ if (mem->pad.index == 0) {
+ mem->pad.index_reset_ticks_previous = mem->pad.index_reset_ticks;
+ mem->pad.index_reset_ticks = (s64)CoreTiming::GetTicks();
+ }
+
+ mem->touch.index = next_touch_index;
+ ++next_touch_index %= mem->touch.entries.size();
+
+ // Get the current touch entry
+ 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 = 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
+ // converted to pixel coordinates." (http://3dbrew.org/wiki/HID_Shared_Memory#Offset_0xA8).
+
+ // If we just updated index 0, provide a new timestamp
+ if (mem->touch.index == 0) {
+ mem->touch.index_reset_ticks_previous = mem->touch.index_reset_ticks;
+ mem->touch.index_reset_ticks = (s64)CoreTiming::GetTicks();
}
// Signal both handles when there's an update to Pad or touch
- g_event_pad_or_touch_1->Signal();
- g_event_pad_or_touch_2->Signal();
+ event_pad_or_touch_1->Signal();
+ event_pad_or_touch_2->Signal();
}
void GetIPCHandles(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
+ cmd_buff[2] = 0x14000000; // IPC Command Structure translate-header
// TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling)
- cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::g_shared_mem).MoveFrom();
- cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_1).MoveFrom();
- cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_2).MoveFrom();
- cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::g_event_accelerometer).MoveFrom();
- cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::g_event_gyroscope).MoveFrom();
- cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::g_event_debug_pad).MoveFrom();
+ cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).MoveFrom();
+ cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).MoveFrom();
+ cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).MoveFrom();
+ cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).MoveFrom();
+ cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).MoveFrom();
+ cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).MoveFrom();
+}
+
+void EnableAccelerometer(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ event_accelerometer->Signal();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+}
+
+void EnableGyroscopeLow(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ event_gyroscope->Signal();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+}
+
+void GetSoundVolume(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ const u8 volume = 0x3F; // TODO(purpasmart): Find out if this is the max value for the volume
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = volume;
+
+ LOG_WARNING(Service_HID, "(STUBBED) called");
}
void HIDInit() {
@@ -156,19 +161,22 @@ void HIDInit() {
AddService(new HID_U_Interface);
AddService(new HID_SPVR_Interface);
- g_shared_mem = SharedMemory::Create("HID:SharedMem");
+ shared_mem = SharedMemory::Create("HID:SharedMem");
+
+ next_pad_index = 0;
+ next_touch_index = 0;
// Create event handles
- g_event_pad_or_touch_1 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch1");
- g_event_pad_or_touch_2 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch2");
- g_event_accelerometer = Event::Create(RESETTYPE_ONESHOT, "HID:EventAccelerometer");
- g_event_gyroscope = Event::Create(RESETTYPE_ONESHOT, "HID:EventGyroscope");
- g_event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad");
+ event_pad_or_touch_1 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch1");
+ event_pad_or_touch_2 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch2");
+ event_accelerometer = Event::Create(RESETTYPE_ONESHOT, "HID:EventAccelerometer");
+ event_gyroscope = Event::Create(RESETTYPE_ONESHOT, "HID:EventGyroscope");
+ event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad");
}
void HIDShutdown() {
-
}
-}
-}
+} // namespace HID
+
+} // 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 {
namespace Service {
namespace HID {
-// Handle to shared memory region designated to HID_User service
-extern Kernel::SharedPtr<Kernel::SharedMemory> g_shared_mem;
-
-// Event handles
-extern Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_1;
-extern Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2;
-extern Kernel::SharedPtr<Kernel::Event> g_event_accelerometer;
-extern Kernel::SharedPtr<Kernel::Event> g_event_gyroscope;
-extern Kernel::SharedPtr<Kernel::Event> g_event_debug_pad;
-
/**
* Structure of a Pad controller state.
*/
@@ -65,7 +55,7 @@ struct PadState {
};
/**
- * Structure of a single entry in the PadData's Pad state history array.
+ * Structure of a single entry of Pad state history within HID shared memory
*/
struct PadDataEntry {
PadState current_state;
@@ -77,24 +67,65 @@ struct PadDataEntry {
};
/**
- * Structure of all data related to the 3DS Pad.
+ * Structure of a single entry of touch state history within HID shared memory
*/
-struct PadData {
- s64 index_reset_ticks;
- s64 index_reset_ticks_previous;
- u32 index; // the index of the last updated Pad state history element
+struct TouchDataEntry {
+ u16 x; ///< Y-coordinate of a touchpad press on the lower screen
+ u16 y; ///< X-coordinate of a touchpad press on the lower screen
+ BitField<0, 7, u32> valid; ///< Set to 1 when this entry contains actual X/Y data, otherwise 0
+};
+
+/**
+ * Structure of data stored in HID shared memory
+ */
+struct SharedMem {
+ /// Pad data, this is used for buttons and the circle pad
+ 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 pad state entry
+
+ INSERT_PADDING_WORDS(0x2);
+
+ PadState current_state; ///< Current state of the pad buttons
+
+ // TODO(bunnei): Implement `raw_circle_pad_data` field
+ u32 raw_circle_pad_data; ///< Raw (analog) circle pad data, before being converted
- u32 pad1;
- u32 pad2;
+ INSERT_PADDING_WORDS(0x1);
- PadState current_state; // same as entries[index].current_state
- u32 raw_circle_pad_data;
+ std::array<PadDataEntry, 8> entries; ///< Last 8 pad entries
+ } pad;
- u32 pad3;
+ /// Touchpad data, this is used for touchpad input
+ 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 touch entry
- std::array<PadDataEntry, 8> entries; // Pad state history
+ INSERT_PADDING_WORDS(0x1);
+
+ // TODO(bunnei): Implement `raw_entry` field
+ TouchDataEntry raw_entry; ///< Raw (analog) touch data, before being converted
+
+ std::array<TouchDataEntry, 8> entries; ///< Last 8 touch entries, in pixel coordinates
+ } touch;
};
+// TODO: MSVC does not support using offsetof() on non-static data members even though this
+// is technically allowed since C++11. This macro should be enabled once MSVC adds
+// support for that.
+#ifndef _MSC_VER
+#define ASSERT_REG_POSITION(field_name, position) \
+ static_assert(offsetof(SharedMem, field_name) == position * 4, \
+ "Field "#field_name" has invalid position")
+
+ASSERT_REG_POSITION(pad.index_reset_ticks, 0x0);
+ASSERT_REG_POSITION(touch.index_reset_ticks, 0x2A);
+
+#undef ASSERT_REG_POSITION
+#endif // !defined(_MSC_VER)
+
// Pre-defined PadStates for single button presses
const PadState PAD_NONE = {{0}};
const PadState PAD_A = {{1u << 0}};
@@ -130,7 +161,7 @@ const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
- * 2 : Unused
+ * 2 : IPC Command Structure translate-header
* 3 : Handle to HID_User shared memory
* 4 : Event signaled by HID_User
* 5 : Event signaled by HID_User
@@ -140,12 +171,41 @@ const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
*/
void GetIPCHandles(Interface* self);
-// Methods for updating the HID module's state
-void PadButtonPress(const PadState& pad_state);
-void PadButtonRelease(const PadState& pad_state);
-void PadUpdateComplete();
+/**
+ * HID::EnableAccelerometer service function
+ * Inputs:
+ * None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void EnableAccelerometer(Interface* self);
+
+/**
+ * HID::EnableGyroscopeLow service function
+ * Inputs:
+ * None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void EnableGyroscopeLow(Interface* self);
+
+/**
+ * HID::GetSoundVolume service function
+ * Inputs:
+ * None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : u8 output value
+ */
+void GetSoundVolume(Interface* self);
+/// Checks for user input updates
+void HIDUpdate();
+
+/// Initialize HID service
void HIDInit();
+
+/// Shutdown HID service
void HIDShutdown();
}
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[] = {
{0x000A0000, GetIPCHandles, "GetIPCHandles"},
{0x000B0000, nullptr, "StartAnalogStickCalibration"},
{0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
- {0x00110000, nullptr, "EnableAccelerometer"},
+ {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
{0x00120000, nullptr, "DisableAccelerometer"},
- {0x00130000, nullptr, "EnableGyroscopeLow"},
+ {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
{0x00140000, nullptr, "DisableGyroscopeLow"},
{0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
{0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
- {0x00170000, nullptr, "GetSoundVolume"},
+ {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 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 @@
// Refer to the license.txt file included.
#include "core/hle/hle.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/hid_user.h"
@@ -12,14 +10,14 @@ namespace Service {
namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
- {0x000A0000, GetIPCHandles, "GetIPCHandles"},
- {0x00110000, nullptr, "EnableAccelerometer"},
- {0x00120000, nullptr, "DisableAccelerometer"},
- {0x00130000, nullptr, "EnableGyroscopeLow"},
- {0x00140000, nullptr, "DisableGyroscopeLow"},
- {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
- {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
- {0x00170000, nullptr, "GetSoundVolume"},
+ {0x000A0000, GetIPCHandles, "GetIPCHandles"},
+ {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
+ {0x00120000, nullptr, "DisableAccelerometer"},
+ {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
+ {0x00140000, nullptr, "DisableGyroscopeLow"},
+ {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
+ {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
+ {0x00170000, GetSoundVolume, "GetSoundVolume"},
};
HID_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 @@
#include "common/string_util.h"
#include "common/symbols.h"
-#include "core/arm/arm_interface.h"
+#include "core/core_timing.h"
#include "core/mem_map.h"
+#include "core/arm/arm_interface.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/event.h"
@@ -551,7 +552,7 @@ static void SleepThread(s64 nanoseconds) {
/// This returns the total CPU ticks elapsed since the CPU was powered-on
static s64 GetSystemTick() {
- return (s64)Core::g_app_core->GetTicks();
+ return (s64)CoreTiming::GetTicks();
}
/// 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 ca33557ae..e6983a225 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -14,13 +14,15 @@
#include "core/hle/hle.h"
#include "core/hle/service/gsp_gpu.h"
#include "core/hle/service/dsp_dsp.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hw/hw.h"
#include "core/hw/gpu.h"
#include "video_core/command_processor.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
-#include <video_core/color.h>
+#include "video_core/color.h"
namespace GPU {
@@ -40,7 +42,7 @@ static bool last_skip_frame = false;
template <typename T>
inline void Read(T &var, const u32 raw_addr) {
- u32 addr = raw_addr - 0x1EF00000;
+ u32 addr = raw_addr - HW::VADDR_GPU;
u32 index = addr / 4;
// Reads other than u32 are untested, so I'd rather have them abort than silently fail
@@ -54,7 +56,7 @@ inline void Read(T &var, const u32 raw_addr) {
template <typename T>
inline void Write(u32 addr, const T data) {
- addr -= 0x1EF00000;
+ addr -= HW::VADDR_GPU;
u32 index = addr / 4;
// Writes other than u32 are untested, so I'd rather have them abort than silently fail
@@ -150,8 +152,17 @@ inline void Write(u32 addr, const T data) {
for (u32 x = 0; x < output_width; ++x) {
Math::Vec4<u8> src_color = { 0, 0, 0, 0 };
- u32 scaled_x = x * horizontal_scale;
- u32 scaled_y = y * vertical_scale;
+ // Calculate the [x,y] position of the input image
+ // based on the current output position and the scale
+ u32 input_x = x * horizontal_scale;
+ u32 input_y = y * vertical_scale;
+
+ if (config.flip_vertically) {
+ // Flip the y value of the output data,
+ // we do this after calculating the [x,y] position of the input image
+ // to account for the scaling options.
+ y = output_height - y - 1;
+ }
u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format);
u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format);
@@ -163,14 +174,14 @@ inline void Write(u32 addr, const T data) {
u32 coarse_y = y & ~7;
u32 stride = output_width * dst_bytes_per_pixel;
- src_offset = (scaled_x + scaled_y * config.input_width) * src_bytes_per_pixel;
+ src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel;
dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride;
} else {
// Interpret the input as tiled and the output as linear
- u32 coarse_y = scaled_y & ~7;
+ u32 coarse_y = input_y & ~7;
u32 stride = config.input_width * src_bytes_per_pixel;
- src_offset = VideoCore::GetMortonOffset(scaled_x, scaled_y, src_bytes_per_pixel) + coarse_y * stride;
+ src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride;
dst_offset = (x + y * output_width) * dst_bytes_per_pixel;
}
@@ -300,6 +311,9 @@ static void VBlankCallback(u64 userdata, int cycles_late) {
// this. Certain games expect this to be periodically signaled.
DSP_DSP::SignalInterrupt();
+ // Check for user input updates
+ Service::HID::HIDUpdate();
+
// Reschedule recurrent event
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event);
}
@@ -319,8 +333,6 @@ void Init() {
framebuffer_top.address_right2 = 0x182B9800;
framebuffer_sub.address_left1 = 0x1848F000;
framebuffer_sub.address_left2 = 0x184C7800;
- //framebuffer_sub.address_right1 = unknown;
- //framebuffer_sub.address_right2 = unknown;
framebuffer_top.width = 240;
framebuffer_top.height = 400;
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index d07490db0..c8f884494 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -197,7 +197,7 @@ struct Regs {
union {
u32 flags;
- BitField< 0, 1, u32> flip_data; // flips input data horizontally (TODO) if true
+ BitField< 0, 1, u32> flip_vertically; // flips input data vertically
BitField< 1, 1, u32> output_tiled; // Converts from linear to tiled format
BitField< 3, 1, u32> raw_copy; // Copies the data without performing any processing
BitField< 8, 3, PixelFormat> input_format;
@@ -251,6 +251,8 @@ struct Regs {
return content[index];
}
+#undef ASSERT_MEMBER_SIZE
+
private:
/*
* Most physical addresses which GPU registers refer to are 8-byte aligned.
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index a63ba6eeb..bed50af50 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -6,43 +6,19 @@
#include "core/hw/hw.h"
#include "core/hw/gpu.h"
+#include "core/hw/lcd.h"
namespace HW {
-enum {
- VADDR_HASH = 0x1EC01000,
- VADDR_CSND = 0x1EC03000,
- VADDR_DSP = 0x1EC40000,
- VADDR_PDN = 0x1EC41000,
- VADDR_CODEC = 0x1EC41000,
- VADDR_SPI = 0x1EC42000,
- VADDR_SPI_2 = 0x1EC43000, // Only used under TWL_FIRM?
- VADDR_I2C = 0x1EC44000,
- VADDR_CODEC_2 = 0x1EC45000,
- VADDR_HID = 0x1EC46000,
- VADDR_PAD = 0x1EC46000,
- VADDR_PTM = 0x1EC46000,
- VADDR_GPIO = 0x1EC47000,
- VADDR_I2C_2 = 0x1EC48000,
- VADDR_SPI_3 = 0x1EC60000,
- VADDR_I2C_3 = 0x1EC61000,
- VADDR_MIC = 0x1EC62000,
- VADDR_PXI = 0x1EC63000, // 0xFFFD2000
- //VADDR_NTRCARD
- VADDR_CDMA = 0xFFFDA000, // CoreLink DMA-330? Info
- VADDR_DSP_2 = 0x1ED03000,
- VADDR_HASH_2 = 0x1EE01000,
- VADDR_GPU = 0x1EF00000,
-};
-
template <typename T>
inline void Read(T &var, const u32 addr) {
switch (addr & 0xFFFFF000) {
-
case VADDR_GPU:
GPU::Read(var, addr);
break;
-
+ case VADDR_LCD:
+ LCD::Write(var, addr);
+ break;
default:
LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
}
@@ -51,11 +27,12 @@ inline void Read(T &var, const u32 addr) {
template <typename T>
inline void Write(u32 addr, const T data) {
switch (addr & 0xFFFFF000) {
-
case VADDR_GPU:
GPU::Write(addr, data);
break;
-
+ case VADDR_LCD:
+ LCD::Write(addr, data);
+ break;
default:
LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
}
@@ -80,6 +57,7 @@ void Update() {
/// Initialize hardware
void Init() {
GPU::Init();
+ LCD::Init();
LOG_DEBUG(HW, "initialized OK");
}
diff --git a/src/core/hw/hw.h b/src/core/hw/hw.h
index 991c0a07d..d65608910 100644
--- a/src/core/hw/hw.h
+++ b/src/core/hw/hw.h
@@ -8,6 +8,30 @@
namespace HW {
+/// Beginnings of IO register regions, in the user VA space.
+enum : u32 {
+ VADDR_HASH = 0x1EC01000,
+ VADDR_CSND = 0x1EC03000,
+ VADDR_DSP = 0x1EC40000,
+ VADDR_PDN = 0x1EC41000,
+ VADDR_CODEC = 0x1EC41000,
+ VADDR_SPI = 0x1EC42000,
+ VADDR_SPI_2 = 0x1EC43000, // Only used under TWL_FIRM?
+ VADDR_I2C = 0x1EC44000,
+ VADDR_CODEC_2 = 0x1EC45000,
+ VADDR_HID = 0x1EC46000,
+ VADDR_GPIO = 0x1EC47000,
+ VADDR_I2C_2 = 0x1EC48000,
+ VADDR_SPI_3 = 0x1EC60000,
+ VADDR_I2C_3 = 0x1EC61000,
+ VADDR_MIC = 0x1EC62000,
+ VADDR_PXI = 0x1EC63000,
+ VADDR_LCD = 0x1ED02000,
+ VADDR_DSP_2 = 0x1ED03000,
+ VADDR_HASH_2 = 0x1EE01000,
+ VADDR_GPU = 0x1EF00000,
+};
+
template <typename T>
void Read(T &var, const u32 addr);
diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp
new file mode 100644
index 000000000..7986f3ddb
--- /dev/null
+++ b/src/core/hw/lcd.cpp
@@ -0,0 +1,66 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+
+#include "core/arm/arm_interface.h"
+#include "core/hle/hle.h"
+#include "core/hw/hw.h"
+#include "core/hw/lcd.h"
+
+namespace LCD {
+
+Regs g_regs;
+
+template <typename T>
+inline void Read(T &var, const u32 raw_addr) {
+ u32 addr = raw_addr - HW::VADDR_LCD;
+ u32 index = addr / 4;
+
+ // Reads other than u32 are untested, so I'd rather have them abort than silently fail
+ if (index >= 0x400 || !std::is_same<T, u32>::value) {
+ LOG_ERROR(HW_LCD, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
+ return;
+ }
+
+ var = g_regs[index];
+}
+
+template <typename T>
+inline void Write(u32 addr, const T data) {
+ addr -= HW::VADDR_LCD;
+ u32 index = addr / 4;
+
+ // Writes other than u32 are untested, so I'd rather have them abort than silently fail
+ if (index >= 0x400 || !std::is_same<T, u32>::value) {
+ LOG_ERROR(HW_LCD, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
+ return;
+ }
+
+ g_regs[index] = static_cast<u32>(data);
+}
+
+// Explicitly instantiate template functions because we aren't defining this in the header:
+
+template void Read<u64>(u64 &var, const u32 addr);
+template void Read<u32>(u32 &var, const u32 addr);
+template void Read<u16>(u16 &var, const u32 addr);
+template void Read<u8>(u8 &var, const u32 addr);
+
+template void Write<u64>(u32 addr, const u64 data);
+template void Write<u32>(u32 addr, const u32 data);
+template void Write<u16>(u32 addr, const u16 data);
+template void Write<u8>(u32 addr, const u8 data);
+
+/// Initialize hardware
+void Init() {
+ LOG_DEBUG(HW_LCD, "initialized OK");
+}
+
+/// Shutdown hardware
+void Shutdown() {
+ LOG_DEBUG(HW_LCD, "shutdown OK");
+}
+
+} // namespace
diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h
new file mode 100644
index 000000000..43893a625
--- /dev/null
+++ b/src/core/hw/lcd.h
@@ -0,0 +1,88 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+
+#include "common/common_types.h"
+#include "common/bit_field.h"
+
+#define LCD_REG_INDEX(field_name) (offsetof(LCD::Regs, field_name) / sizeof(u32))
+
+namespace LCD {
+
+struct Regs {
+
+ union ColorFill {
+ u32 raw;
+
+ BitField<0, 8, u32> color_r;
+ BitField<8, 8, u32> color_g;
+ BitField<16, 8, u32> color_b;
+ BitField<24, 1, u32> is_enabled;
+ };
+
+ INSERT_PADDING_WORDS(0x81);
+ ColorFill color_fill_top;
+ INSERT_PADDING_WORDS(0xE);
+ u32 backlight_top;
+
+ INSERT_PADDING_WORDS(0x1F0);
+
+ ColorFill color_fill_bottom;
+ INSERT_PADDING_WORDS(0xE);
+ u32 backlight_bottom;
+ INSERT_PADDING_WORDS(0x16F);
+
+ static inline size_t NumIds() {
+ return sizeof(Regs) / sizeof(u32);
+ }
+
+ u32& operator [] (int index) const {
+ u32* content = (u32*)this;
+ return content[index];
+ }
+
+ u32& operator [] (int index) {
+ u32* content = (u32*)this;
+ return content[index];
+ }
+
+#undef ASSERT_MEMBER_SIZE
+
+};
+static_assert(std::is_standard_layout<Regs>::value, "Structure does not use standard layout");
+
+// TODO: MSVC does not support using offsetof() on non-static data members even though this
+// is technically allowed since C++11. This macro should be enabled once MSVC adds
+// support for that.
+#ifndef _MSC_VER
+#define ASSERT_REG_POSITION(field_name, position) \
+ static_assert(offsetof(Regs, field_name) == position * 4, \
+ "Field "#field_name" has invalid position")
+
+ASSERT_REG_POSITION(color_fill_top, 0x81);
+ASSERT_REG_POSITION(backlight_top, 0x90);
+ASSERT_REG_POSITION(color_fill_bottom, 0x281);
+ASSERT_REG_POSITION(backlight_bottom, 0x290);
+
+#undef ASSERT_REG_POSITION
+#endif // !defined(_MSC_VER)
+
+extern Regs g_regs;
+
+template <typename T>
+void Read(T &var, const u32 addr);
+
+template <typename T>
+void Write(u32 addr, const T data);
+
+/// Initialize hardware
+void Init();
+
+/// Shutdown hardware
+void Shutdown();
+
+} // namespace
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 {
u16 version;
u8 reserved_0[4];
u8 program_id[8];
- u8 temp_flag;
- u8 reserved_1[0x2f];
+ u8 reserved_1[0x10];
+ u8 logo_region_hash[0x20];
u8 product_code[0x10];
u8 extended_header_hash[0x20];
u32 extended_header_size;
@@ -29,15 +29,16 @@ struct NCCH_Header {
u8 flags[8];
u32 plain_region_offset;
u32 plain_region_size;
- u8 reserved_3[8];
+ u32 logo_region_offset;
+ u32 logo_region_size;
u32 exefs_offset;
u32 exefs_size;
u32 exefs_hash_region_size;
- u8 reserved_4[4];
+ u8 reserved_3[4];
u32 romfs_offset;
u32 romfs_size;
u32 romfs_hash_region_size;
- u8 reserved_5[4];
+ u8 reserved_4[4];
u8 exefs_super_block_hash[0x20];
u8 romfs_super_block_hash[0x20];
};
@@ -88,8 +89,7 @@ struct ExHeader_DependencyList{
};
struct ExHeader_SystemInfo{
- u32 save_data_size;
- u8 reserved[4];
+ u64 save_data_size;
u8 jump_id[8];
u8 reserved_2[0x30];
};
@@ -104,11 +104,14 @@ struct ExHeader_StorageInfo{
struct ExHeader_ARM11_SystemLocalCaps{
u8 program_id[8];
- u8 flags[8];
- u8 resource_limit_descriptor[0x10][2];
+ u32 core_version;
+ u8 flags[3];
+ u8 priority;
+ u8 resource_limit_descriptor[0x16][2];
ExHeader_StorageInfo storage_info;
- u8 service_access_control[0x20][8];
- u8 reserved[0x1f];
+ u8 service_access_control[0x32][8];
+ u8 ex_service_access_control[0x2][8];
+ u8 reserved[0xf];
u8 resource_limit_category;
};
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) {
* @return Resulting values stored as a Math::Vec2
*/
inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
- return { (bytes[2] << 16) | (bytes[1] << 8) | bytes[0], bytes[3] };
+ return { static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3] };
}
/**
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
case Regs::TextureFormat::RGBA8:
{
auto res = Color::DecodeRGBA8(source + VideoCore::GetMortonOffset(x, y, 4));
- return { res.r(), res.g(), res.b(), disable_alpha ? 255 : res.a() };
+ return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) };
}
case Regs::TextureFormat::RGB8:
@@ -334,7 +334,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
case Regs::TextureFormat::RGB5A1:
{
auto res = Color::DecodeRGB5A1(source + VideoCore::GetMortonOffset(x, y, 2));
- return { res.r(), res.g(), res.b(), disable_alpha ? 255 : res.a() };
+ return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) };
}
case Regs::TextureFormat::RGB565:
@@ -346,7 +346,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
case Regs::TextureFormat::RGBA4:
{
auto res = Color::DecodeRGBA4(source + VideoCore::GetMortonOffset(x, y, 2));
- return { res.r(), res.g(), res.b(), disable_alpha ? 255 : res.a() };
+ return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) };
}
case Regs::TextureFormat::IA8:
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 95ab96340..4273a177f 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -3,6 +3,8 @@
// Refer to the license.txt file included.
#include "core/hw/gpu.h"
+#include "core/hw/hw.h"
+#include "core/hw/lcd.h"
#include "core/mem_map.h"
#include "common/emu_window.h"
@@ -64,16 +66,33 @@ void RendererOpenGL::SwapBuffers() {
for(int i : {0, 1}) {
const auto& framebuffer = GPU::g_regs.framebuffer_config[i];
- if (textures[i].width != (GLsizei)framebuffer.width ||
- textures[i].height != (GLsizei)framebuffer.height ||
- textures[i].format != framebuffer.color_format) {
- // Reallocate texture if the framebuffer size has changed.
- // This is expected to not happen very often and hence should not be a
- // performance problem.
- ConfigureFramebufferTexture(textures[i], framebuffer);
+ // Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04
+ u32 lcd_color_addr = (i == 0) ? LCD_REG_INDEX(color_fill_top) : LCD_REG_INDEX(color_fill_bottom);
+ lcd_color_addr = HW::VADDR_LCD + 4 * lcd_color_addr;
+ LCD::Regs::ColorFill color_fill = {0};
+ LCD::Read(color_fill.raw, lcd_color_addr);
+
+ if (color_fill.is_enabled) {
+ LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, textures[i]);
+
+ // Resize the texture in case the framebuffer size has changed
+ textures[i].width = 1;
+ textures[i].height = 1;
+ } else {
+ if (textures[i].width != (GLsizei)framebuffer.width ||
+ textures[i].height != (GLsizei)framebuffer.height ||
+ textures[i].format != framebuffer.color_format) {
+ // Reallocate texture if the framebuffer size has changed.
+ // This is expected to not happen very often and hence should not be a
+ // performance problem.
+ ConfigureFramebufferTexture(textures[i], framebuffer);
+ }
+ LoadFBToActiveGLTexture(framebuffer, textures[i]);
+
+ // Resize the texture in case the framebuffer size has changed
+ textures[i].width = framebuffer.width;
+ textures[i].height = framebuffer.height;
}
-
- LoadFBToActiveGLTexture(GPU::g_regs.framebuffer_config[i], textures[i]);
}
DrawScreens();
@@ -127,10 +146,25 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig&
// TODO: Applications could theoretically crash Citra here by specifying too large
// framebuffer sizes. We should make sure that this cannot happen.
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height,
- texture.gl_format, texture.gl_type, framebuffer_data);
+ texture.gl_format, texture.gl_type, framebuffer_data);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+/**
+ * Fills active OpenGL texture with the given RGB color.
+ * Since the color is solid, the texture can be 1x1 but will stretch across whatever it's rendered on.
+ * This has the added benefit of being *really fast*.
+ */
+void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
+ const TextureInfo& texture) {
+ glBindTexture(GL_TEXTURE_2D, texture.handle);
+
+ u8 framebuffer_data[3] = { color_r, color_g, color_b };
+
+ // Update existing texture
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data);
glBindTexture(GL_TEXTURE_2D, 0);
}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index bcabab557..cd782428e 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -58,6 +58,9 @@ private:
// Loads framebuffer from emulated memory into the active OpenGL texture.
static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
const TextureInfo& texture);
+ // Fills active OpenGL texture with the given RGB color.
+ static void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
+ const TextureInfo& texture);
/// Computes the viewport rectangle
MathUtil::Rectangle<unsigned> GetViewportExtent();
@@ -72,7 +75,7 @@ private:
GLuint vertex_array_handle;
GLuint vertex_buffer_handle;
GLuint program_id;
- std::array<TextureInfo, 2> textures;
+ std::array<TextureInfo, 2> textures; ///< Textures for top and bottom screens respectively
// Shader uniform location indices
GLuint uniform_modelview_matrix;
GLuint uniform_color_texture;
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 {
u32* program_counter;
const float24* input_register_table[16];
- float24* output_register_table[7*4];
+ Math::Vec4<float24> output_registers[16];
Math::Vec4<float24> temporary_registers[16];
bool conditional_code[2];
@@ -198,8 +198,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
src2[3] = src2[3] * float24::FromFloat32(-1);
}
- float24* dest = (instr.common.dest.Value() < 0x08) ? state.output_register_table[4*instr.common.dest.Value().GetIndex()]
- : (instr.common.dest.Value() < 0x10) ? dummy_vec4_float24
+ float24* dest = (instr.common.dest.Value() < 0x10) ? &state.output_registers[instr.common.dest.Value().GetIndex()][0]
: (instr.common.dest.Value() < 0x20) ? &state.temporary_registers[instr.common.dest.Value().GetIndex()][0]
: dummy_vec4_float24;
@@ -409,8 +408,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
src3[3] = src3[3] * float24::FromFloat32(-1);
}
- float24* dest = (instr.mad.dest.Value() < 0x08) ? state.output_register_table[4*instr.mad.dest.Value().GetIndex()]
- : (instr.mad.dest.Value() < 0x10) ? dummy_vec4_float24
+ float24* dest = (instr.mad.dest.Value() < 0x10) ? &state.output_registers[instr.mad.dest.Value().GetIndex()][0]
: (instr.mad.dest.Value() < 0x20) ? &state.temporary_registers[instr.mad.dest.Value().GetIndex()][0]
: dummy_vec4_float24;
@@ -587,12 +585,18 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) {
if(num_attributes > 14) state.input_register_table[attribute_register_map.attribute14_register] = &input.attr[14].x;
if(num_attributes > 15) state.input_register_table[attribute_register_map.attribute15_register] = &input.attr[15].x;
- // Setup output register table
- OutputVertex ret;
- // Zero output so that attributes which aren't output won't have denormals in them, which will
- // slow us down later.
- memset(&ret, 0, sizeof(ret));
+ state.conditional_code[0] = false;
+ state.conditional_code[1] = false;
+
+ ProcessShaderCode(state);
+ DebugUtils::DumpShader(shader_memory.data(), state.debug.max_offset, swizzle_data.data(),
+ state.debug.max_opdesc_id, registers.vs_main_offset,
+ registers.vs_output_attributes);
+ // Setup output data
+ 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 = registers.vs_output_attributes[i];
@@ -601,18 +605,18 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) {
output_register_map.map_z, output_register_map.map_w
};
- for (int comp = 0; comp < 4; ++comp)
- state.output_register_table[4*i+comp] = ((float24*)&ret) + semantics[comp];
+ for (int comp = 0; comp < 4; ++comp) {
+ float24* out = ((float24*)&ret) + semantics[comp];
+ if (semantics[comp] != Regs::VSOutputAttributes::INVALID) {
+ *out = state.output_registers[i][comp];
+ } else {
+ // Zero output so that attributes which aren't output won't have denormals in them,
+ // which would slow us down later.
+ memset(out, 0, sizeof(*out));
+ }
+ }
}
- state.conditional_code[0] = false;
- state.conditional_code[1] = false;
-
- ProcessShaderCode(state);
- DebugUtils::DumpShader(shader_memory.data(), state.debug.max_offset, swizzle_data.data(),
- state.debug.max_opdesc_id, registers.vs_main_offset,
- registers.vs_output_attributes);
-
LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)",
ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(),
ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(),