summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--CMakeLists.txt2
-rw-r--r--CONTRIBUTING.md102
-rw-r--r--README.md2
-rw-r--r--src/citra/citra.cpp9
-rw-r--r--src/citra/emu_window/emu_window_glfw.cpp5
-rw-r--r--src/citra_qt/CMakeLists.txt4
-rw-r--r--src/citra_qt/citra_qt.vcxproj7
-rw-r--r--src/citra_qt/citra_qt.vcxproj.filters23
-rw-r--r--src/citra_qt/debugger/callstack.hxx2
-rw-r--r--src/citra_qt/debugger/disassembler.cpp2
-rw-r--r--src/citra_qt/debugger/disassembler.hxx2
-rw-r--r--src/citra_qt/debugger/graphics.cpp83
-rw-r--r--src/citra_qt/debugger/graphics.hxx43
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp139
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.hxx64
-rw-r--r--src/citra_qt/debugger/registers.hxx2
-rw-r--r--src/citra_qt/main.cpp14
-rw-r--r--src/citra_qt/main.hxx4
-rw-r--r--src/citra_qt/ui_callstack.h68
-rw-r--r--src/citra_qt/ui_controller_config.h222
-rw-r--r--src/citra_qt/ui_disassembler.h112
-rw-r--r--src/citra_qt/ui_hotkeys.h77
-rw-r--r--src/citra_qt/ui_main.h153
-rw-r--r--src/citra_qt/ui_registers.h69
-rw-r--r--src/common/common.h2
-rw-r--r--src/common/common.vcxproj4
-rw-r--r--src/common/common.vcxproj.filters6
-rw-r--r--src/common/common_funcs.h5
-rw-r--r--src/common/console_listener.cpp16
-rw-r--r--src/common/log.h45
-rw-r--r--src/common/log_manager.cpp36
-rw-r--r--src/common/log_manager.h6
-rw-r--r--src/common/platform.h4
-rw-r--r--src/common/register_set.h161
-rw-r--r--src/common/thread_queue_list.h216
-rw-r--r--src/core/CMakeLists.txt16
-rw-r--r--src/core/arm/arm_interface.h33
-rw-r--r--src/core/arm/interpreter/arm_interpreter.cpp99
-rw-r--r--src/core/arm/interpreter/arm_interpreter.h23
-rw-r--r--src/core/arm/interpreter/armdefs.h4
-rw-r--r--src/core/arm/interpreter/armemu.cpp23
-rw-r--r--src/core/arm/interpreter/arminit.cpp3
-rw-r--r--src/core/arm/interpreter/vfp/vfp.h2
-rw-r--r--src/core/core.cpp24
-rw-r--r--src/core/core.vcxproj20
-rw-r--r--src/core/core.vcxproj.filters57
-rw-r--r--src/core/hle/config_mem.cpp2
-rw-r--r--src/core/hle/coprocessor.cpp2
-rw-r--r--src/core/hle/function_wrappers.h743
-rw-r--r--src/core/hle/hle.cpp27
-rw-r--r--src/core/hle/hle.h12
-rw-r--r--src/core/hle/kernel/event.cpp159
-rw-r--r--src/core/hle/kernel/event.h52
-rw-r--r--src/core/hle/kernel/kernel.cpp161
-rw-r--r--src/core/hle/kernel/kernel.h183
-rw-r--r--src/core/hle/kernel/mutex.cpp170
-rw-r--r--src/core/hle/kernel/mutex.h28
-rw-r--r--src/core/hle/kernel/thread.cpp436
-rw-r--r--src/core/hle/kernel/thread.h83
-rw-r--r--src/core/hle/service/apt.cpp191
-rw-r--r--src/core/hle/service/apt.h2
-rw-r--r--src/core/hle/service/gsp.cpp149
-rw-r--r--src/core/hle/service/gsp.h19
-rw-r--r--src/core/hle/service/hid.cpp10
-rw-r--r--src/core/hle/service/hid.h2
-rw-r--r--src/core/hle/service/ndm.cpp32
-rw-r--r--src/core/hle/service/ndm.h33
-rw-r--r--src/core/hle/service/service.cpp38
-rw-r--r--src/core/hle/service/service.h106
-rw-r--r--src/core/hle/service/srv.cpp37
-rw-r--r--src/core/hle/service/srv.h8
-rw-r--r--src/core/hle/svc.cpp467
-rw-r--r--src/core/hle/svc.h61
-rw-r--r--src/core/hle/syscall.cpp270
-rw-r--r--src/core/hle/syscall.h19
-rw-r--r--src/core/hw/gpu.cpp (renamed from src/core/hw/lcd.cpp)75
-rw-r--r--src/core/hw/gpu.h (renamed from src/core/hw/lcd.h)39
-rw-r--r--src/core/hw/hw.cpp16
-rw-r--r--src/core/hw/ndma.cpp4
-rw-r--r--src/core/loader.cpp20
-rw-r--r--src/core/mem_map.cpp3
-rw-r--r--src/core/mem_map.h7
-rw-r--r--src/core/mem_map_funcs.cpp19
-rw-r--r--src/core/system.cpp3
-rw-r--r--src/video_core/gpu_debugger.h157
-rw-r--r--src/video_core/pica.h130
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp6
-rw-r--r--src/video_core/video_core.cpp3
-rw-r--r--src/video_core/video_core.vcxproj6
-rw-r--r--src/video_core/video_core.vcxproj.filters4
-rw-r--r--vsprops/qt.props2
92 files changed, 3877 insertions, 2137 deletions
diff --git a/.gitignore b/.gitignore
index 10ad35373..001894c3c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,6 @@ bin/
# Generated source files
src/common/scm_rev.cpp
+
+# Generated header files
+src/citra_qt/ui_*.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 114e39207..1c7d98347 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.6)
project(citra)
-SET(CXX_COMPILE_FLAGS "-std=c++11 -fpermissive")
+SET(CXX_COMPILE_FLAGS "-std=c++11")
# silence some spam
add_definitions(-Wno-attributes)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..cb1067c92
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,102 @@
+# Contributing
+Citra is a brand new project, so we have a great opportunity to keep things clean and well organized early on. As such, coding style is very important when making commits. They aren't very strict rules since we want to be flexible and we understand that under certain circumstances some of them can be counterproductive. Just try to follow as many of them as possible:
+
+### General Rules
+* A lot of code was taken from other projects (e.g. Dolphin, PPSSPP, Gekko, SkyEye). In general, when editing other people's code, follow the style of the module you're in (or better yet, fix the style if it drastically differs from our guide).
+* Line width is typically 100 characters, but this isn't strictly enforced. Please do not use 80-characters.
+* Don't ever introduce new external dependencies into Core
+* Don't use any platform specific code in Core
+* Use namespaces often
+
+### Naming Rules
+* Functions
+ * CamelCase, "_" may also be used for clarity (e.g. ARM_InitCore)
+* Variables
+ * lower_case_underscored
+ * Prefix "g_" if global
+* Classes
+ * CamelCase, "_" may also be used for clarity (e.g. OGL_VideoInterface)
+* Files/Folders
+ * lower_case_underscored
+* Namespaces
+ * CamelCase, "_" may also be used for clarity (e.g. ARM_InitCore)
+
+### Indentation/Whitespace Style
+Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead.
+
+```cpp
+namespace Example {
+
+// Namespace contents are not indented
+
+// Declare globals at the top
+int g_foo = 0;
+char* g_some_pointer; // Notice the position of the *
+
+enum SomeEnum {
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_BLUE
+};
+
+struct Position {
+ int x, y;
+};
+
+// Use "typename" rather than "class" here, just to be consistent
+template
+void FooBar() {
+ int some_array[] = {
+ 5,
+ 25,
+ 7,
+ 42
+ };
+
+ if (note == the_space_after_the_if) {
+ CallAfunction();
+ } else {
+ // Use a space after the // when commenting
+ }
+
+ // Comment directly above code when possible
+ if (some_condition) single_statement();
+
+ // Place a single space after the for loop semicolons
+ for (int i = 0; i != 25; ++i) {
+ // This is how we write loops
+ }
+
+ DoStuff(this, function, call, takes, up, multiple,
+ lines, like, this);
+
+ if (this || condition_takes_up_multiple &&
+ lines && like && this || everything ||
+ alright || then) {
+ }
+
+ switch (var) {
+ // No indentation for case label
+ case 1: {
+ int case_var = var + 3;
+ DoSomething(case_var);
+ break;
+ }
+ case 3:
+ DoSomething(var);
+ return;
+
+ default:
+ // Yes, even break for the last case
+ break;
+ }
+
+ std::vector
+ you_can_declare,
+ a_few,
+ variables,
+ like_this;
+}
+
+}
+```
diff --git a/README.md b/README.md
index 79de09d98..e9bd8967e 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ For development discussion, please join us @ #citra on [freenode](http://webchat
### Development
-If you want to contribute please take a took at the [Coding Style](https://github.com/citra-emu/citra/wiki/Coding-Style), [Roadmap](https://github.com/citra-emu/citra/wiki/Roadmap) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information) pages. You should as well contact any of the developers in the forum in order to know about the current state of the emulator.
+If you want to contribute please take a took at the [Contributor's Guide](CONTRIBUTING.md), [Roadmap](https://github.com/citra-emu/citra/wiki/Roadmap) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information) pages. You should as well contact any of the developers in the forum in order to know about the current state of the emulator.
### Building
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index 6f3bc6f84..5a8642d1b 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -24,7 +24,14 @@ int __cdecl main(int argc, char **argv) {
System::Init(emu_window);
- std::string boot_filename = "homebrew.elf";
+ std::string boot_filename;
+
+ if (argc < 2) {
+ ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified");
+ }
+ else {
+ boot_filename = argv[1];
+ }
std::string error_str;
bool res = Loader::LoadFile(boot_filename, &error_str);
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp
index 73c116373..f882a825e 100644
--- a/src/citra/emu_window/emu_window_glfw.cpp
+++ b/src/citra/emu_window/emu_window_glfw.cpp
@@ -28,8 +28,13 @@ EmuWindow_GLFW::EmuWindow_GLFW() {
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+
+#if EMU_PLATFORM == PLATFORM_MACOSX
+ // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context.
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+#endif
+
m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
(VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
m_window_title.c_str(), NULL, NULL);
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 549f69217..7f880df8b 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -2,6 +2,8 @@ set(SRCS
bootmanager.cpp
debugger/callstack.cpp
debugger/disassembler.cpp
+ debugger/graphics.cpp
+ debugger/graphics_cmdlists.cpp
debugger/ramview.cpp
debugger/registers.cpp
hotkeys.cpp
@@ -38,6 +40,8 @@ qt4_wrap_cpp(MOC_SRCS
bootmanager.hxx
debugger/callstack.hxx
debugger/disassembler.hxx
+ debugger/graphics.hxx
+ debugger/graphics_cmdlists.hxx
debugger/registers.hxx
debugger/ramview.hxx
hotkeys.hxx
diff --git a/src/citra_qt/citra_qt.vcxproj b/src/citra_qt/citra_qt.vcxproj
index 3f24bbfbf..746c12a5b 100644
--- a/src/citra_qt/citra_qt.vcxproj
+++ b/src/citra_qt/citra_qt.vcxproj
@@ -130,6 +130,8 @@
<ClCompile Include="config\controller_config.cpp" />
<ClCompile Include="config\controller_config_util.cpp" />
<ClCompile Include="debugger\callstack.cpp" />
+ <ClCompile Include="debugger\graphics.cpp" />
+ <ClCompile Include="debugger\graphics_cmdlists.cpp" />
<ClCompile Include="debugger\registers.cpp" />
<ClCompile Include="debugger\disassembler.cpp" />
<ClCompile Include="debugger\ramview.cpp" />
@@ -143,9 +145,11 @@
<MOC Include="..\..\externals\qhexedit\qhexedit_p.h" />
<MOC Include="..\..\externals\qhexedit\xbytearray.h" />
<MOC Include="debugger\callstack.hxx" />
- <MOC Include="debugger\registers.hxx" />
<MOC Include="debugger\disassembler.hxx" />
+ <MOC Include="debugger\graphics.hxx" />
+ <MOC Include="debugger\graphics_cmdlists.hxx" />
<MOC Include="debugger\ramview.hxx" />
+ <MOC Include="debugger\registers.hxx" />
<MOC Include="bootmanager.hxx" />
<MOC Include="hotkeys.hxx" />
<MOC Include="main.hxx" />
@@ -164,7 +168,6 @@
<ItemGroup>
<ClInclude Include="config\controller_config.hxx" />
<ClInclude Include="config\controller_config_util.hxx" />
- <ClInclude Include="ui_controller_config.h" />
<ClInclude Include="version.h" />
</ItemGroup>
<ItemGroup>
diff --git a/src/citra_qt/citra_qt.vcxproj.filters b/src/citra_qt/citra_qt.vcxproj.filters
index 2b3838e29..8f699e50e 100644
--- a/src/citra_qt/citra_qt.vcxproj.filters
+++ b/src/citra_qt/citra_qt.vcxproj.filters
@@ -36,10 +36,16 @@
<ClCompile Include="debugger\callstack.cpp">
<Filter>debugger</Filter>
</ClCompile>
- <ClCompile Include="debugger\ramview.cpp">
+ <ClCompile Include="debugger\disassembler.cpp">
<Filter>debugger</Filter>
</ClCompile>
- <ClCompile Include="debugger\disassembler.cpp">
+ <ClCompile Include="debugger\graphics.cpp">
+ <Filter>debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="debugger\graphics_cmdlists.cpp">
+ <Filter>debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="debugger\ramview.cpp">
<Filter>debugger</Filter>
</ClCompile>
<ClCompile Include="debugger\registers.cpp">
@@ -65,10 +71,16 @@
<MOC Include="debugger\callstack.hxx">
<Filter>debugger</Filter>
</MOC>
- <MOC Include="debugger\ramview.hxx">
+ <MOC Include="debugger\disassembler.hxx">
<Filter>debugger</Filter>
</MOC>
- <MOC Include="debugger\disassembler.hxx">
+ <MOC Include="debugger\graphics.hxx">
+ <Filter>debugger</Filter>
+ </MOC>
+ <MOC Include="debugger\graphics_cmdlists.hxx">
+ <Filter>debugger</Filter>
+ </MOC>
+ <MOC Include="debugger\ramview.hxx">
<Filter>debugger</Filter>
</MOC>
<MOC Include="debugger\registers.hxx">
@@ -83,9 +95,6 @@
<ClInclude Include="config\controller_config_util.hxx">
<Filter>config</Filter>
</ClInclude>
- <ClInclude Include="ui_controller_config.h">
- <Filter>config</Filter>
- </ClInclude>
</ItemGroup>
<ItemGroup>
<UIC Include="hotkeys.ui" />
diff --git a/src/citra_qt/debugger/callstack.hxx b/src/citra_qt/debugger/callstack.hxx
index 3ad2af28b..680a73b6d 100644
--- a/src/citra_qt/debugger/callstack.hxx
+++ b/src/citra_qt/debugger/callstack.hxx
@@ -1,5 +1,5 @@
#include <QDockWidget>
-#include "../ui_callstack.h"
+#include "ui_callstack.h"
class QStandardItemModel;
diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp
index ccc83abf2..4e8f841f4 100644
--- a/src/citra_qt/debugger/disassembler.cpp
+++ b/src/citra_qt/debugger/disassembler.cpp
@@ -48,7 +48,7 @@ void DisassemblerWidget::Init()
unsigned int curInstAddr = base_addr;
char result[255];
- for (int i = 0; i < 10000; i++) // fixed for now
+ for (int i = 0; i < 20000; i++) // fixed for now
{
disasm->disasm(curInstAddr, Memory::Read32(curInstAddr), result);
model->setItem(i, 0, new QStandardItem(QString("0x%1").arg((uint)(curInstAddr), 8, 16, QLatin1Char('0'))));
diff --git a/src/citra_qt/debugger/disassembler.hxx b/src/citra_qt/debugger/disassembler.hxx
index e5b152d20..e668bbbeb 100644
--- a/src/citra_qt/debugger/disassembler.hxx
+++ b/src/citra_qt/debugger/disassembler.hxx
@@ -1,5 +1,5 @@
#include <QDockWidget>
-#include "../ui_disassembler.h"
+#include "ui_disassembler.h"
#include "common/common.h"
#include "common/break_points.h"
diff --git a/src/citra_qt/debugger/graphics.cpp b/src/citra_qt/debugger/graphics.cpp
new file mode 100644
index 000000000..9aaade8f9
--- /dev/null
+++ b/src/citra_qt/debugger/graphics.cpp
@@ -0,0 +1,83 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "graphics.hxx"
+#include <QListView>
+#include <QVBoxLayout>
+#include <QDebug>
+
+extern GraphicsDebugger g_debugger;
+
+GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent) : QAbstractListModel(parent), command_count(0)
+{
+ connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int)));
+}
+
+int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const
+{
+ return command_count;
+}
+
+QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ int command_index = index.row();
+ const GSP_GPU::GXCommand& command = GetDebugger()->ReadGXCommandHistory(command_index);
+ if (role == Qt::DisplayRole)
+ {
+ std::map<GSP_GPU::GXCommandId, const char*> command_names;
+ command_names[GSP_GPU::GXCommandId::REQUEST_DMA] = "REQUEST_DMA";
+ command_names[GSP_GPU::GXCommandId::SET_COMMAND_LIST_FIRST] = "SET_COMMAND_LIST_FIRST";
+ command_names[GSP_GPU::GXCommandId::SET_MEMORY_FILL] = "SET_MEMORY_FILL";
+ command_names[GSP_GPU::GXCommandId::SET_DISPLAY_TRANSFER] = "SET_DISPLAY_TRANSFER";
+ command_names[GSP_GPU::GXCommandId::SET_TEXTURE_COPY] = "SET_TEXTURE_COPY";
+ command_names[GSP_GPU::GXCommandId::SET_COMMAND_LIST_LAST] = "SET_COMMAND_LIST_LAST";
+ QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9").arg(command_names[static_cast<GSP_GPU::GXCommandId>(command.id)])
+ .arg(command.data[0], 8, 16, QLatin1Char('0'))
+ .arg(command.data[1], 8, 16, QLatin1Char('0'))
+ .arg(command.data[2], 8, 16, QLatin1Char('0'))
+ .arg(command.data[3], 8, 16, QLatin1Char('0'))
+ .arg(command.data[4], 8, 16, QLatin1Char('0'))
+ .arg(command.data[5], 8, 16, QLatin1Char('0'))
+ .arg(command.data[6], 8, 16, QLatin1Char('0'))
+ .arg(command.data[7], 8, 16, QLatin1Char('0'));
+ return QVariant(str);
+ }
+ else
+ {
+ return QVariant();
+ }
+}
+
+void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count)
+{
+ emit GXCommandFinished(total_command_count);
+}
+
+void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count)
+{
+ if (total_command_count == 0)
+ return;
+
+ int prev_command_count = command_count;
+ command_count = total_command_count;
+ emit dataChanged(index(prev_command_count,0), index(total_command_count-1,0));
+}
+
+
+GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent) : QDockWidget(tr("Graphics Debugger"), parent)
+{
+ // TODO: set objectName!
+
+ GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this);
+ g_debugger.RegisterObserver(command_model);
+
+ QListView* command_list = new QListView;
+ command_list->setModel(command_model);
+ command_list->setFont(QFont("monospace"));
+
+ setWidget(command_list);
+}
diff --git a/src/citra_qt/debugger/graphics.hxx b/src/citra_qt/debugger/graphics.hxx
new file mode 100644
index 000000000..72656f93c
--- /dev/null
+++ b/src/citra_qt/debugger/graphics.hxx
@@ -0,0 +1,43 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QAbstractListModel>
+#include <QDockWidget>
+
+#include "video_core/gpu_debugger.h"
+
+class GPUCommandStreamItemModel : public QAbstractListModel, public GraphicsDebugger::DebuggerObserver
+{
+ Q_OBJECT
+
+public:
+ GPUCommandStreamItemModel(QObject* parent);
+
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+
+public:
+ void GXCommandProcessed(int total_command_count) override;
+
+public slots:
+ void OnGXCommandFinishedInternal(int total_command_count);
+
+signals:
+ void GXCommandFinished(int total_command_count);
+
+private:
+ int command_count;
+};
+
+class GPUCommandStreamWidget : public QDockWidget
+{
+ Q_OBJECT
+
+public:
+ GPUCommandStreamWidget(QWidget* parent = 0);
+
+private:
+};
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
new file mode 100644
index 000000000..195197ef5
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -0,0 +1,139 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "graphics_cmdlists.hxx"
+#include <QTreeView>
+
+extern GraphicsDebugger g_debugger;
+
+GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractItemModel(parent)
+{
+ root_item = new TreeItem(TreeItem::ROOT, 0, NULL, this);
+
+ connect(this, SIGNAL(CommandListCalled()), this, SLOT(OnCommandListCalledInternal()), Qt::UniqueConnection);
+}
+
+QModelIndex GPUCommandListModel::index(int row, int column, const QModelIndex& parent) const
+{
+ TreeItem* item;
+
+ if (!parent.isValid()) {
+ item = root_item;
+ } else {
+ item = (TreeItem*)parent.internalPointer();
+ }
+
+ return createIndex(row, column, item->children[row]);
+}
+
+QModelIndex GPUCommandListModel::parent(const QModelIndex& child) const
+{
+ if (!child.isValid())
+ return QModelIndex();
+
+ TreeItem* item = (TreeItem*)child.internalPointer();
+
+ if (item->parent == NULL)
+ return QModelIndex();
+
+ return createIndex(item->parent->index, 0, item->parent);
+}
+
+int GPUCommandListModel::rowCount(const QModelIndex& parent) const
+{
+ TreeItem* item;
+ if (!parent.isValid()) {
+ item = root_item;
+ } else {
+ item = (TreeItem*)parent.internalPointer();
+ }
+ return item->children.size();
+}
+
+int GPUCommandListModel::columnCount(const QModelIndex& parent) const
+{
+ return 2;
+}
+
+QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ const TreeItem* item = (const TreeItem*)index.internalPointer();
+
+ if (item->type == TreeItem::COMMAND_LIST)
+ {
+ const GraphicsDebugger::PicaCommandList& cmdlist = command_lists[item->index].second;
+ u32 address = command_lists[item->index].first;
+
+ if (role == Qt::DisplayRole && index.column() == 0)
+ {
+ return QVariant(QString("0x%1 bytes at 0x%2").arg(cmdlist.size(), 0, 16).arg(address, 8, 16, QLatin1Char('0')));
+ }
+ }
+ else
+ {
+ // index refers to a specific command
+ const GraphicsDebugger::PicaCommandList& cmdlist = command_lists[item->parent->index].second;
+ const GraphicsDebugger::PicaCommand& cmd = cmdlist[item->index];
+ const Pica::CommandHeader& header = cmd.GetHeader();
+
+ if (role == Qt::DisplayRole) {
+ QString content;
+ if (index.column() == 0) {
+ content = Pica::command_names[header.cmd_id];
+ content.append(" ");
+ } else if (index.column() == 1) {
+ for (int j = 0; j < cmd.size(); ++j)
+ content.append(QString("%1 ").arg(cmd[j], 8, 16, QLatin1Char('0')));
+ }
+
+ return QVariant(content);
+ }
+ }
+
+ return QVariant();
+}
+
+void GPUCommandListModel::OnCommandListCalled(const GraphicsDebugger::PicaCommandList& lst, bool is_new)
+{
+ emit CommandListCalled();
+}
+
+
+void GPUCommandListModel::OnCommandListCalledInternal()
+{
+ beginResetModel();
+
+ command_lists = GetDebugger()->GetCommandLists();
+
+ // delete root item and rebuild tree
+ delete root_item;
+ root_item = new TreeItem(TreeItem::ROOT, 0, NULL, this);
+
+ for (int command_list_idx = 0; command_list_idx < command_lists.size(); ++command_list_idx) {
+ TreeItem* command_list_item = new TreeItem(TreeItem::COMMAND_LIST, command_list_idx, root_item, root_item);
+ root_item->children.push_back(command_list_item);
+
+ const GraphicsDebugger::PicaCommandList& command_list = command_lists[command_list_idx].second;
+ for (int command_idx = 0; command_idx < command_list.size(); ++command_idx) {
+ TreeItem* command_item = new TreeItem(TreeItem::COMMAND, command_idx, command_list_item, command_list_item);
+ command_list_item->children.push_back(command_item);
+ }
+ }
+
+ endResetModel();
+}
+
+GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent)
+{
+ GPUCommandListModel* model = new GPUCommandListModel(this);
+ g_debugger.RegisterObserver(model);
+
+ QTreeView* tree_widget = new QTreeView;
+ tree_widget->setModel(model);
+ tree_widget->setFont(QFont("monospace"));
+ setWidget(tree_widget);
+}
diff --git a/src/citra_qt/debugger/graphics_cmdlists.hxx b/src/citra_qt/debugger/graphics_cmdlists.hxx
new file mode 100644
index 000000000..b4e6e3c8a
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_cmdlists.hxx
@@ -0,0 +1,64 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QAbstractItemModel>
+#include <QDockWidget>
+
+#include "video_core/gpu_debugger.h"
+
+// TODO: Rename class, since it's not actually a list model anymore...
+class GPUCommandListModel : public QAbstractItemModel, public GraphicsDebugger::DebuggerObserver
+{
+ Q_OBJECT
+
+public:
+ GPUCommandListModel(QObject* parent);
+
+ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex& child) const;
+ int columnCount(const QModelIndex& parent = QModelIndex()) const;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+
+public:
+ void OnCommandListCalled(const GraphicsDebugger::PicaCommandList& lst, bool is_new) override;
+
+public slots:
+ void OnCommandListCalledInternal();
+
+signals:
+ void CommandListCalled();
+
+private:
+ struct TreeItem : public QObject
+ {
+ enum Type {
+ ROOT,
+ COMMAND_LIST,
+ COMMAND
+ };
+
+ TreeItem(Type type, int index, TreeItem* item_parent, QObject* parent) : QObject(parent), type(type), index(index), parent(item_parent) {}
+
+ Type type;
+ int index;
+ std::vector<TreeItem*> children;
+ TreeItem* parent;
+ };
+
+ std::vector<std::pair<u32,GraphicsDebugger::PicaCommandList>> command_lists;
+ TreeItem* root_item;
+};
+
+class GPUCommandListWidget : public QDockWidget
+{
+ Q_OBJECT
+
+public:
+ GPUCommandListWidget(QWidget* parent = 0);
+
+private:
+};
diff --git a/src/citra_qt/debugger/registers.hxx b/src/citra_qt/debugger/registers.hxx
index 318d95820..9645feb2a 100644
--- a/src/citra_qt/debugger/registers.hxx
+++ b/src/citra_qt/debugger/registers.hxx
@@ -1,4 +1,4 @@
-#include "../ui_registers.h"
+#include "ui_registers.h"
#include <QDockWidget>
#include <QTreeWidgetItem>
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 76e0c68c3..087716c01 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -19,6 +19,8 @@
#include "debugger/registers.hxx"
#include "debugger/callstack.hxx"
#include "debugger/ramview.hxx"
+#include "debugger/graphics.hxx"
+#include "debugger/graphics_cmdlists.hxx"
#include "core/system.h"
#include "core/loader.h"
@@ -47,10 +49,20 @@ GMainWindow::GMainWindow()
addDockWidget(Qt::RightDockWidgetArea, callstackWidget);
callstackWidget->hide();
+ graphicsWidget = new GPUCommandStreamWidget(this);
+ addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
+ callstackWidget->hide();
+
+ graphicsCommandsWidget = new GPUCommandListWidget(this);
+ addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
+ callstackWidget->hide();
+
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
debug_menu->addAction(disasmWidget->toggleViewAction());
debug_menu->addAction(registersWidget->toggleViewAction());
debug_menu->addAction(callstackWidget->toggleViewAction());
+ debug_menu->addAction(graphicsWidget->toggleViewAction());
+ debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
// Set default UI state
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
@@ -142,7 +154,7 @@ void GMainWindow::BootGame(const char* filename)
void GMainWindow::OnMenuLoadFile()
{
- QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS homebrew (*.elf *.dat *.bin)"));
+ QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS homebrew (*.elf *.axf *.dat *.bin)"));
if (filename.size())
BootGame(filename.toLatin1().data());
}
diff --git a/src/citra_qt/main.hxx b/src/citra_qt/main.hxx
index fa122f76e..6bcb37a30 100644
--- a/src/citra_qt/main.hxx
+++ b/src/citra_qt/main.hxx
@@ -10,6 +10,8 @@ class GRenderWindow;
class DisassemblerWidget;
class RegistersWidget;
class CallstackWidget;
+class GPUCommandStreamWidget;
+class GPUCommandListWidget;
class GMainWindow : public QMainWindow
{
@@ -50,6 +52,8 @@ private:
DisassemblerWidget* disasmWidget;
RegistersWidget* registersWidget;
CallstackWidget* callstackWidget;
+ GPUCommandStreamWidget* graphicsWidget;
+ GPUCommandListWidget* graphicsCommandsWidget;
};
#endif // _CITRA_QT_MAIN_HXX_
diff --git a/src/citra_qt/ui_callstack.h b/src/citra_qt/ui_callstack.h
deleted file mode 100644
index 75d10253c..000000000
--- a/src/citra_qt/ui_callstack.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/********************************************************************************
-** Form generated from reading UI file 'callstack.ui'
-**
-** Created by: Qt User Interface Compiler version 4.8.5
-**
-** WARNING! All changes made in this file will be lost when recompiling UI file!
-********************************************************************************/
-
-#ifndef UI_CALLSTACK_H
-#define UI_CALLSTACK_H
-
-#include <QtCore/QVariant>
-#include <QtGui/QAction>
-#include <QtGui/QApplication>
-#include <QtGui/QButtonGroup>
-#include <QtGui/QDockWidget>
-#include <QtGui/QHeaderView>
-#include <QtGui/QTreeView>
-#include <QtGui/QVBoxLayout>
-#include <QtGui/QWidget>
-
-QT_BEGIN_NAMESPACE
-
-class Ui_CallStack
-{
-public:
- QWidget *dockWidgetContents;
- QVBoxLayout *verticalLayout;
- QTreeView *treeView;
-
- void setupUi(QDockWidget *CallStack)
- {
- if (CallStack->objectName().isEmpty())
- CallStack->setObjectName(QString::fromUtf8("CallStack"));
- CallStack->resize(400, 300);
- dockWidgetContents = new QWidget();
- dockWidgetContents->setObjectName(QString::fromUtf8("dockWidgetContents"));
- verticalLayout = new QVBoxLayout(dockWidgetContents);
- verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
- treeView = new QTreeView(dockWidgetContents);
- treeView->setObjectName(QString::fromUtf8("treeView"));
- treeView->setAlternatingRowColors(true);
- treeView->setRootIsDecorated(false);
- treeView->setItemsExpandable(false);
-
- verticalLayout->addWidget(treeView);
-
- CallStack->setWidget(dockWidgetContents);
-
- retranslateUi(CallStack);
-
- QMetaObject::connectSlotsByName(CallStack);
- } // setupUi
-
- void retranslateUi(QDockWidget *CallStack)
- {
- CallStack->setWindowTitle(QApplication::translate("CallStack", "Call stack", 0, QApplication::UnicodeUTF8));
- } // retranslateUi
-
-};
-
-namespace Ui {
- class CallStack: public Ui_CallStack {};
-} // namespace Ui
-
-QT_END_NAMESPACE
-
-#endif // UI_CALLSTACK_H
diff --git a/src/citra_qt/ui_controller_config.h b/src/citra_qt/ui_controller_config.h
deleted file mode 100644
index f84364a77..000000000
--- a/src/citra_qt/ui_controller_config.h
+++ /dev/null
@@ -1,222 +0,0 @@
-/********************************************************************************
-** Form generated from reading UI file 'controller_config.ui'
-**
-** Created by: Qt User Interface Compiler version 4.8.5
-**
-** WARNING! All changes made in this file will be lost when recompiling UI file!
-********************************************************************************/
-
-#ifndef UI_CONTROLLER_CONFIG_H
-#define UI_CONTROLLER_CONFIG_H
-
-#include <QtCore/QVariant>
-#include <QtGui/QAction>
-#include <QtGui/QApplication>
-#include <QtGui/QButtonGroup>
-#include <QtGui/QCheckBox>
-#include <QtGui/QComboBox>
-#include <QtGui/QGridLayout>
-#include <QtGui/QHeaderView>
-#include <QtGui/QLabel>
-#include <QtGui/QSpacerItem>
-#include <QtGui/QTabWidget>
-#include <QtGui/QVBoxLayout>
-#include <QtGui/QWidget>
-
-QT_BEGIN_NAMESPACE
-
-class Ui_ControllerConfig
-{
-public:
- QVBoxLayout *verticalLayout;
- QGridLayout *gridLayout;
- QComboBox *activeControllerCB;
- QSpacerItem *horizontalSpacer;
- QCheckBox *checkBox;
- QComboBox *inputSourceCB;
- QLabel *label_2;
- QLabel *label;
- QTabWidget *tabWidget;
- QWidget *mainStickTab;
- QGridLayout *gridLayout_3;
- QSpacerItem *verticalSpacer_2;
- QSpacerItem *verticalSpacer_3;
- QSpacerItem *horizontalSpacer_4;
- QSpacerItem *horizontalSpacer_3;
- QWidget *cStickTab;
- QGridLayout *gridLayout_4;
- QSpacerItem *horizontalSpacer_6;
- QSpacerItem *verticalSpacer_5;
- QSpacerItem *verticalSpacer_4;
- QSpacerItem *horizontalSpacer_5;
- QWidget *dPadTab;
- QGridLayout *gridLayout_5;
- QSpacerItem *horizontalSpacer_7;
- QSpacerItem *verticalSpacer_7;
- QSpacerItem *verticalSpacer_6;
- QSpacerItem *horizontalSpacer_8;
- QWidget *buttonsTab;
- QVBoxLayout *verticalLayout_2;
-
- void setupUi(QWidget *ControllerConfig)
- {
- if (ControllerConfig->objectName().isEmpty())
- ControllerConfig->setObjectName(QString::fromUtf8("ControllerConfig"));
- ControllerConfig->resize(503, 293);
- QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- sizePolicy.setHorizontalStretch(0);
- sizePolicy.setVerticalStretch(0);
- sizePolicy.setHeightForWidth(ControllerConfig->sizePolicy().hasHeightForWidth());
- ControllerConfig->setSizePolicy(sizePolicy);
- verticalLayout = new QVBoxLayout(ControllerConfig);
- verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
- verticalLayout->setSizeConstraint(QLayout::SetFixedSize);
- gridLayout = new QGridLayout();
- gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
- activeControllerCB = new QComboBox(ControllerConfig);
- activeControllerCB->setObjectName(QString::fromUtf8("activeControllerCB"));
-
- gridLayout->addWidget(activeControllerCB, 1, 1, 1, 1);
-
- horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
-
- gridLayout->addItem(horizontalSpacer, 0, 2, 1, 1);
-
- checkBox = new QCheckBox(ControllerConfig);
- checkBox->setObjectName(QString::fromUtf8("checkBox"));
-
- gridLayout->addWidget(checkBox, 1, 2, 1, 1);
-
- inputSourceCB = new QComboBox(ControllerConfig);
- inputSourceCB->setObjectName(QString::fromUtf8("inputSourceCB"));
-
- gridLayout->addWidget(inputSourceCB, 0, 1, 1, 1);
-
- label_2 = new QLabel(ControllerConfig);
- label_2->setObjectName(QString::fromUtf8("label_2"));
-
- gridLayout->addWidget(label_2, 1, 0, 1, 1);
-
- label = new QLabel(ControllerConfig);
- label->setObjectName(QString::fromUtf8("label"));
-
- gridLayout->addWidget(label, 0, 0, 1, 1);
-
-
- verticalLayout->addLayout(gridLayout);
-
- tabWidget = new QTabWidget(ControllerConfig);
- tabWidget->setObjectName(QString::fromUtf8("tabWidget"));
- mainStickTab = new QWidget();
- mainStickTab->setObjectName(QString::fromUtf8("mainStickTab"));
- gridLayout_3 = new QGridLayout(mainStickTab);
- gridLayout_3->setObjectName(QString::fromUtf8("gridLayout_3"));
- verticalSpacer_2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
-
- gridLayout_3->addItem(verticalSpacer_2, 2, 2, 1, 1);
-
- verticalSpacer_3 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
-
- gridLayout_3->addItem(verticalSpacer_3, 0, 2, 1, 1);
-
- horizontalSpacer_4 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
-
- gridLayout_3->addItem(horizontalSpacer_4, 1, 0, 1, 1);
-
- horizontalSpacer_3 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
-
- gridLayout_3->addItem(horizontalSpacer_3, 1, 4, 1, 1);
-
- tabWidget->addTab(mainStickTab, QString());
- cStickTab = new QWidget();
- cStickTab->setObjectName(QString::fromUtf8("cStickTab"));
- gridLayout_4 = new QGridLayout(cStickTab);
- gridLayout_4->setObjectName(QString::fromUtf8("gridLayout_4"));
- horizontalSpacer_6 = new QSpacerItem(40, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
-
- gridLayout_4->addItem(horizontalSpacer_6, 1, 0, 1, 1);
-
- verticalSpacer_5 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
-
- gridLayout_4->addItem(verticalSpacer_5, 0, 1, 1, 1);
-
- verticalSpacer_4 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
-
- gridLayout_4->addItem(verticalSpacer_4, 2, 1, 1, 1);
-
- horizontalSpacer_5 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
-
- gridLayout_4->addItem(horizontalSpacer_5, 1, 2, 1, 1);
-
- tabWidget->addTab(cStickTab, QString());
- dPadTab = new QWidget();
- dPadTab->setObjectName(QString::fromUtf8("dPadTab"));
- gridLayout_5 = new QGridLayout(dPadTab);
- gridLayout_5->setObjectName(QString::fromUtf8("gridLayout_5"));
- horizontalSpacer_7 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
-
- gridLayout_5->addItem(horizontalSpacer_7, 1, 2, 1, 1);
-
- verticalSpacer_7 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
-
- gridLayout_5->addItem(verticalSpacer_7, 0, 1, 1, 1);
-
- verticalSpacer_6 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
-
- gridLayout_5->addItem(verticalSpacer_6, 2, 1, 1, 1);
-
- horizontalSpacer_8 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
-
- gridLayout_5->addItem(horizontalSpacer_8, 1, 0, 1, 1);
-
- tabWidget->addTab(dPadTab, QString());
- buttonsTab = new QWidget();
- buttonsTab->setObjectName(QString::fromUtf8("buttonsTab"));
- verticalLayout_2 = new QVBoxLayout(buttonsTab);
- verticalLayout_2->setObjectName(QString::fromUtf8("verticalLayout_2"));
- tabWidget->addTab(buttonsTab, QString());
-
- verticalLayout->addWidget(tabWidget);
-
-
- retranslateUi(ControllerConfig);
-
- tabWidget->setCurrentIndex(0);
-
-
- QMetaObject::connectSlotsByName(ControllerConfig);
- } // setupUi
-
- void retranslateUi(QWidget *ControllerConfig)
- {
- ControllerConfig->setWindowTitle(QApplication::translate("ControllerConfig", "Controller Configuration", 0, QApplication::UnicodeUTF8));
- activeControllerCB->clear();
- activeControllerCB->insertItems(0, QStringList()
- << QApplication::translate("ControllerConfig", "Controller 1", 0, QApplication::UnicodeUTF8)
- << QApplication::translate("ControllerConfig", "Controller 2", 0, QApplication::UnicodeUTF8)
- << QApplication::translate("ControllerConfig", "Controller 3", 0, QApplication::UnicodeUTF8)
- << QApplication::translate("ControllerConfig", "Controller 4", 0, QApplication::UnicodeUTF8)
- );
- checkBox->setText(QApplication::translate("ControllerConfig", "Enabled", 0, QApplication::UnicodeUTF8));
- inputSourceCB->clear();
- inputSourceCB->insertItems(0, QStringList()
- << QApplication::translate("ControllerConfig", "Keyboard", 0, QApplication::UnicodeUTF8)
- << QApplication::translate("ControllerConfig", "Joypad", 0, QApplication::UnicodeUTF8)
- );
- label_2->setText(QApplication::translate("ControllerConfig", "Active Controller:", 0, QApplication::UnicodeUTF8));
- label->setText(QApplication::translate("ControllerConfig", "Input Source:", 0, QApplication::UnicodeUTF8));
- tabWidget->setTabText(tabWidget->indexOf(mainStickTab), QApplication::translate("ControllerConfig", "Main Stick", 0, QApplication::UnicodeUTF8));
- tabWidget->setTabText(tabWidget->indexOf(cStickTab), QApplication::translate("ControllerConfig", "C-Stick", 0, QApplication::UnicodeUTF8));
- tabWidget->setTabText(tabWidget->indexOf(dPadTab), QApplication::translate("ControllerConfig", "D-Pad", 0, QApplication::UnicodeUTF8));
- tabWidget->setTabText(tabWidget->indexOf(buttonsTab), QApplication::translate("ControllerConfig", "Buttons", 0, QApplication::UnicodeUTF8));
- } // retranslateUi
-
-};
-
-namespace Ui {
- class ControllerConfig: public Ui_ControllerConfig {};
-} // namespace Ui
-
-QT_END_NAMESPACE
-
-#endif // UI_CONTROLLER_CONFIG_H
diff --git a/src/citra_qt/ui_disassembler.h b/src/citra_qt/ui_disassembler.h
deleted file mode 100644
index cc9f6b540..000000000
--- a/src/citra_qt/ui_disassembler.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/********************************************************************************
-** Form generated from reading UI file 'disassembler.ui'
-**
-** Created by: Qt User Interface Compiler version 4.8.5
-**
-** WARNING! All changes made in this file will be lost when recompiling UI file!
-********************************************************************************/
-
-#ifndef UI_DISASSEMBLER_H
-#define UI_DISASSEMBLER_H
-
-#include <QtCore/QVariant>
-#include <QtGui/QAction>
-#include <QtGui/QApplication>
-#include <QtGui/QButtonGroup>
-#include <QtGui/QDockWidget>
-#include <QtGui/QHBoxLayout>
-#include <QtGui/QHeaderView>
-#include <QtGui/QPushButton>
-#include <QtGui/QTreeView>
-#include <QtGui/QVBoxLayout>
-#include <QtGui/QWidget>
-
-QT_BEGIN_NAMESPACE
-
-class Ui_DockWidget
-{
-public:
- QWidget *dockWidgetContents;
- QVBoxLayout *verticalLayout;
- QHBoxLayout *horizontalLayout;
- QPushButton *button_step;
- QPushButton *button_pause;
- QPushButton *button_continue;
- QPushButton *pushButton;
- QPushButton *button_breakpoint;
- QTreeView *treeView;
-
- void setupUi(QDockWidget *DockWidget)
- {
- if (DockWidget->objectName().isEmpty())
- DockWidget->setObjectName(QString::fromUtf8("DockWidget"));
- DockWidget->resize(430, 401);
- dockWidgetContents = new QWidget();
- dockWidgetContents->setObjectName(QString::fromUtf8("dockWidgetContents"));
- verticalLayout = new QVBoxLayout(dockWidgetContents);
- verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
- horizontalLayout = new QHBoxLayout();
- horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
- button_step = new QPushButton(dockWidgetContents);
- button_step->setObjectName(QString::fromUtf8("button_step"));
-
- horizontalLayout->addWidget(button_step);
-
- button_pause = new QPushButton(dockWidgetContents);
- button_pause->setObjectName(QString::fromUtf8("button_pause"));
-
- horizontalLayout->addWidget(button_pause);
-
- button_continue = new QPushButton(dockWidgetContents);
- button_continue->setObjectName(QString::fromUtf8("button_continue"));
-
- horizontalLayout->addWidget(button_continue);
-
- pushButton = new QPushButton(dockWidgetContents);
- pushButton->setObjectName(QString::fromUtf8("pushButton"));
-
- horizontalLayout->addWidget(pushButton);
-
- button_breakpoint = new QPushButton(dockWidgetContents);
- button_breakpoint->setObjectName(QString::fromUtf8("button_breakpoint"));
-
- horizontalLayout->addWidget(button_breakpoint);
-
-
- verticalLayout->addLayout(horizontalLayout);
-
- treeView = new QTreeView(dockWidgetContents);
- treeView->setObjectName(QString::fromUtf8("treeView"));
- treeView->setAlternatingRowColors(true);
- treeView->setIndentation(20);
- treeView->setRootIsDecorated(false);
- treeView->header()->setVisible(false);
-
- verticalLayout->addWidget(treeView);
-
- DockWidget->setWidget(dockWidgetContents);
-
- retranslateUi(DockWidget);
-
- QMetaObject::connectSlotsByName(DockWidget);
- } // setupUi
-
- void retranslateUi(QDockWidget *DockWidget)
- {
- DockWidget->setWindowTitle(QApplication::translate("DockWidget", "Disassembly", 0, QApplication::UnicodeUTF8));
- button_step->setText(QApplication::translate("DockWidget", "Step", 0, QApplication::UnicodeUTF8));
- button_pause->setText(QApplication::translate("DockWidget", "Pause", 0, QApplication::UnicodeUTF8));
- button_continue->setText(QApplication::translate("DockWidget", "Continue", 0, QApplication::UnicodeUTF8));
- pushButton->setText(QApplication::translate("DockWidget", "Step Into", 0, QApplication::UnicodeUTF8));
- button_breakpoint->setText(QApplication::translate("DockWidget", "Set Breakpoint", 0, QApplication::UnicodeUTF8));
- } // retranslateUi
-
-};
-
-namespace Ui {
- class DockWidget: public Ui_DockWidget {};
-} // namespace Ui
-
-QT_END_NAMESPACE
-
-#endif // UI_DISASSEMBLER_H
diff --git a/src/citra_qt/ui_hotkeys.h b/src/citra_qt/ui_hotkeys.h
deleted file mode 100644
index 5b2cee6f4..000000000
--- a/src/citra_qt/ui_hotkeys.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/********************************************************************************
-** Form generated from reading UI file 'hotkeys.ui'
-**
-** Created by: Qt User Interface Compiler version 4.8.5
-**
-** WARNING! All changes made in this file will be lost when recompiling UI file!
-********************************************************************************/
-
-#ifndef UI_HOTKEYS_H
-#define UI_HOTKEYS_H
-
-#include <QtCore/QVariant>
-#include <QtGui/QAction>
-#include <QtGui/QApplication>
-#include <QtGui/QButtonGroup>
-#include <QtGui/QDialog>
-#include <QtGui/QDialogButtonBox>
-#include <QtGui/QHeaderView>
-#include <QtGui/QTreeWidget>
-#include <QtGui/QVBoxLayout>
-
-QT_BEGIN_NAMESPACE
-
-class Ui_hotkeys
-{
-public:
- QVBoxLayout *verticalLayout;
- QTreeWidget *treeWidget;
- QDialogButtonBox *buttonBox;
-
- void setupUi(QDialog *hotkeys)
- {
- if (hotkeys->objectName().isEmpty())
- hotkeys->setObjectName(QString::fromUtf8("hotkeys"));
- hotkeys->resize(363, 388);
- verticalLayout = new QVBoxLayout(hotkeys);
- verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
- treeWidget = new QTreeWidget(hotkeys);
- treeWidget->setObjectName(QString::fromUtf8("treeWidget"));
- treeWidget->setSelectionBehavior(QAbstractItemView::SelectItems);
- treeWidget->setHeaderHidden(false);
-
- verticalLayout->addWidget(treeWidget);
-
- buttonBox = new QDialogButtonBox(hotkeys);
- buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
- buttonBox->setOrientation(Qt::Horizontal);
- buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset);
-
- verticalLayout->addWidget(buttonBox);
-
-
- retranslateUi(hotkeys);
- QObject::connect(buttonBox, SIGNAL(accepted()), hotkeys, SLOT(accept()));
- QObject::connect(buttonBox, SIGNAL(rejected()), hotkeys, SLOT(reject()));
-
- QMetaObject::connectSlotsByName(hotkeys);
- } // setupUi
-
- void retranslateUi(QDialog *hotkeys)
- {
- hotkeys->setWindowTitle(QApplication::translate("hotkeys", "Hotkey Settings", 0, QApplication::UnicodeUTF8));
- QTreeWidgetItem *___qtreewidgetitem = treeWidget->headerItem();
- ___qtreewidgetitem->setText(2, QApplication::translate("hotkeys", "Context", 0, QApplication::UnicodeUTF8));
- ___qtreewidgetitem->setText(1, QApplication::translate("hotkeys", "Hotkey", 0, QApplication::UnicodeUTF8));
- ___qtreewidgetitem->setText(0, QApplication::translate("hotkeys", "Action", 0, QApplication::UnicodeUTF8));
- } // retranslateUi
-
-};
-
-namespace Ui {
- class hotkeys: public Ui_hotkeys {};
-} // namespace Ui
-
-QT_END_NAMESPACE
-
-#endif // UI_HOTKEYS_H
diff --git a/src/citra_qt/ui_main.h b/src/citra_qt/ui_main.h
deleted file mode 100644
index 04979e5ab..000000000
--- a/src/citra_qt/ui_main.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/********************************************************************************
-** Form generated from reading UI file 'main.ui'
-**
-** Created by: Qt User Interface Compiler version 4.8.5
-**
-** WARNING! All changes made in this file will be lost when recompiling UI file!
-********************************************************************************/
-
-#ifndef UI_MAIN_H
-#define UI_MAIN_H
-
-#include <QtCore/QVariant>
-#include <QtGui/QAction>
-#include <QtGui/QApplication>
-#include <QtGui/QButtonGroup>
-#include <QtGui/QHBoxLayout>
-#include <QtGui/QHeaderView>
-#include <QtGui/QMainWindow>
-#include <QtGui/QMenu>
-#include <QtGui/QMenuBar>
-#include <QtGui/QStatusBar>
-#include <QtGui/QWidget>
-
-QT_BEGIN_NAMESPACE
-
-class Ui_MainWindow
-{
-public:
- QAction *action_Load_File;
- QAction *action_Load_Symbol_Map;
- QAction *action_Exit;
- QAction *action_Start;
- QAction *action_Pause;
- QAction *action_Stop;
- QAction *action_About;
- QAction *action_Popout_Window_Mode;
- QAction *action_Hotkeys;
- QAction *action_Configure;
- QWidget *centralwidget;
- QHBoxLayout *horizontalLayout;
- QMenuBar *menubar;
- QMenu *menu_File;
- QMenu *menu_Emulation;
- QMenu *menu_View;
- QMenu *menu_Help;
- QStatusBar *statusbar;
-
- void setupUi(QMainWindow *MainWindow)
- {
- if (MainWindow->objectName().isEmpty())
- MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
- MainWindow->resize(1081, 730);
- QIcon icon;
- icon.addFile(QString::fromUtf8("src/pcafe/res/icon3_64x64.ico"), QSize(), QIcon::Normal, QIcon::Off);
- MainWindow->setWindowIcon(icon);
- MainWindow->setTabShape(QTabWidget::Rounded);
- MainWindow->setDockNestingEnabled(true);
- action_Load_File = new QAction(MainWindow);
- action_Load_File->setObjectName(QString::fromUtf8("action_Load_File"));
- action_Load_Symbol_Map = new QAction(MainWindow);
- action_Load_Symbol_Map->setObjectName(QString::fromUtf8("action_Load_Symbol_Map"));
- action_Exit = new QAction(MainWindow);
- action_Exit->setObjectName(QString::fromUtf8("action_Exit"));
- action_Start = new QAction(MainWindow);
- action_Start->setObjectName(QString::fromUtf8("action_Start"));
- action_Pause = new QAction(MainWindow);
- action_Pause->setObjectName(QString::fromUtf8("action_Pause"));
- action_Pause->setEnabled(false);
- action_Stop = new QAction(MainWindow);
- action_Stop->setObjectName(QString::fromUtf8("action_Stop"));
- action_Stop->setEnabled(false);
- action_About = new QAction(MainWindow);
- action_About->setObjectName(QString::fromUtf8("action_About"));
- action_Popout_Window_Mode = new QAction(MainWindow);
- action_Popout_Window_Mode->setObjectName(QString::fromUtf8("action_Popout_Window_Mode"));
- action_Popout_Window_Mode->setCheckable(true);
- action_Hotkeys = new QAction(MainWindow);
- action_Hotkeys->setObjectName(QString::fromUtf8("action_Hotkeys"));
- action_Configure = new QAction(MainWindow);
- action_Configure->setObjectName(QString::fromUtf8("action_Configure"));
- centralwidget = new QWidget(MainWindow);
- centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
- horizontalLayout = new QHBoxLayout(centralwidget);
- horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
- MainWindow->setCentralWidget(centralwidget);
- menubar = new QMenuBar(MainWindow);
- menubar->setObjectName(QString::fromUtf8("menubar"));
- menubar->setGeometry(QRect(0, 0, 1081, 20));
- menu_File = new QMenu(menubar);
- menu_File->setObjectName(QString::fromUtf8("menu_File"));
- menu_Emulation = new QMenu(menubar);
- menu_Emulation->setObjectName(QString::fromUtf8("menu_Emulation"));
- menu_View = new QMenu(menubar);
- menu_View->setObjectName(QString::fromUtf8("menu_View"));
- menu_Help = new QMenu(menubar);
- menu_Help->setObjectName(QString::fromUtf8("menu_Help"));
- MainWindow->setMenuBar(menubar);
- statusbar = new QStatusBar(MainWindow);
- statusbar->setObjectName(QString::fromUtf8("statusbar"));
- MainWindow->setStatusBar(statusbar);
-
- menubar->addAction(menu_File->menuAction());
- menubar->addAction(menu_Emulation->menuAction());
- menubar->addAction(menu_View->menuAction());
- menubar->addAction(menu_Help->menuAction());
- menu_File->addAction(action_Load_File);
- menu_File->addAction(action_Load_Symbol_Map);
- menu_File->addSeparator();
- menu_File->addAction(action_Exit);
- menu_Emulation->addAction(action_Start);
- menu_Emulation->addAction(action_Pause);
- menu_Emulation->addAction(action_Stop);
- menu_Emulation->addSeparator();
- menu_Emulation->addAction(action_Configure);
- menu_View->addAction(action_Popout_Window_Mode);
- menu_View->addAction(action_Hotkeys);
- menu_Help->addAction(action_About);
-
- retranslateUi(MainWindow);
- QObject::connect(action_Exit, SIGNAL(triggered()), MainWindow, SLOT(close()));
- QObject::connect(action_Configure, SIGNAL(triggered()), MainWindow, SLOT(OnConfigure()));
-
- QMetaObject::connectSlotsByName(MainWindow);
- } // setupUi
-
- void retranslateUi(QMainWindow *MainWindow)
- {
- MainWindow->setWindowTitle(QApplication::translate("MainWindow", "Citra", 0, QApplication::UnicodeUTF8));
- action_Load_File->setText(QApplication::translate("MainWindow", "Load file...", 0, QApplication::UnicodeUTF8));
- action_Load_Symbol_Map->setText(QApplication::translate("MainWindow", "Load symbol map...", 0, QApplication::UnicodeUTF8));
- action_Exit->setText(QApplication::translate("MainWindow", "E&xit", 0, QApplication::UnicodeUTF8));
- action_Start->setText(QApplication::translate("MainWindow", "&Start", 0, QApplication::UnicodeUTF8));
- action_Pause->setText(QApplication::translate("MainWindow", "&Pause", 0, QApplication::UnicodeUTF8));
- action_Stop->setText(QApplication::translate("MainWindow", "&Stop", 0, QApplication::UnicodeUTF8));
- action_About->setText(QApplication::translate("MainWindow", "About Citra", 0, QApplication::UnicodeUTF8));
- action_Popout_Window_Mode->setText(QApplication::translate("MainWindow", "Popout window", 0, QApplication::UnicodeUTF8));
- action_Hotkeys->setText(QApplication::translate("MainWindow", "Configure &Hotkeys ...", 0, QApplication::UnicodeUTF8));
- action_Configure->setText(QApplication::translate("MainWindow", "Configure ...", 0, QApplication::UnicodeUTF8));
- menu_File->setTitle(QApplication::translate("MainWindow", "&File", 0, QApplication::UnicodeUTF8));
- menu_Emulation->setTitle(QApplication::translate("MainWindow", "&Emulation", 0, QApplication::UnicodeUTF8));
- menu_View->setTitle(QApplication::translate("MainWindow", "&View", 0, QApplication::UnicodeUTF8));
- menu_Help->setTitle(QApplication::translate("MainWindow", "&Help", 0, QApplication::UnicodeUTF8));
- } // retranslateUi
-
-};
-
-namespace Ui {
- class MainWindow: public Ui_MainWindow {};
-} // namespace Ui
-
-QT_END_NAMESPACE
-
-#endif // UI_MAIN_H
diff --git a/src/citra_qt/ui_registers.h b/src/citra_qt/ui_registers.h
deleted file mode 100644
index 3111cd09e..000000000
--- a/src/citra_qt/ui_registers.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/********************************************************************************
-** Form generated from reading UI file 'registers.ui'
-**
-** Created by: Qt User Interface Compiler version 4.8.5
-**
-** WARNING! All changes made in this file will be lost when recompiling UI file!
-********************************************************************************/
-
-#ifndef UI_REGISTERS_H
-#define UI_REGISTERS_H
-
-#include <QtCore/QVariant>
-#include <QtGui/QAction>
-#include <QtGui/QApplication>
-#include <QtGui/QButtonGroup>
-#include <QtGui/QDockWidget>
-#include <QtGui/QHeaderView>
-#include <QtGui/QTreeWidget>
-#include <QtGui/QVBoxLayout>
-#include <QtGui/QWidget>
-
-QT_BEGIN_NAMESPACE
-
-class Ui_ARMRegisters
-{
-public:
- QWidget *dockWidgetContents;
- QVBoxLayout *verticalLayout;
- QTreeWidget *treeWidget;
-
- void setupUi(QDockWidget *ARMRegisters)
- {
- if (ARMRegisters->objectName().isEmpty())
- ARMRegisters->setObjectName(QString::fromUtf8("ARMRegisters"));
- ARMRegisters->resize(400, 300);
- dockWidgetContents = new QWidget();
- dockWidgetContents->setObjectName(QString::fromUtf8("dockWidgetContents"));
- verticalLayout = new QVBoxLayout(dockWidgetContents);
- verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
- treeWidget = new QTreeWidget(dockWidgetContents);
- treeWidget->setObjectName(QString::fromUtf8("treeWidget"));
- treeWidget->setAlternatingRowColors(true);
-
- verticalLayout->addWidget(treeWidget);
-
- ARMRegisters->setWidget(dockWidgetContents);
-
- retranslateUi(ARMRegisters);
-
- QMetaObject::connectSlotsByName(ARMRegisters);
- } // setupUi
-
- void retranslateUi(QDockWidget *ARMRegisters)
- {
- ARMRegisters->setWindowTitle(QApplication::translate("ARMRegisters", "ARM registers", 0, QApplication::UnicodeUTF8));
- QTreeWidgetItem *___qtreewidgetitem = treeWidget->headerItem();
- ___qtreewidgetitem->setText(1, QApplication::translate("ARMRegisters", "Value", 0, QApplication::UnicodeUTF8));
- ___qtreewidgetitem->setText(0, QApplication::translate("ARMRegisters", "Register", 0, QApplication::UnicodeUTF8));
- } // retranslateUi
-
-};
-
-namespace Ui {
- class ARMRegisters: public Ui_ARMRegisters {};
-} // namespace Ui
-
-QT_END_NAMESPACE
-
-#endif // UI_REGISTERS_H
diff --git a/src/common/common.h b/src/common/common.h
index 2578d0010..09027cae1 100644
--- a/src/common/common.h
+++ b/src/common/common.h
@@ -96,8 +96,6 @@ private:
// Windows compatibility
#ifndef _WIN32
-#include <limits.h>
-#define MAX_PATH PATH_MAX
#ifdef _LP64
#define _M_X64 1
#else
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index 5dc6ff790..1f5c714c3 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -182,6 +182,7 @@
<ClInclude Include="mem_arena.h" />
<ClInclude Include="msg_handler.h" />
<ClInclude Include="platform.h" />
+ <ClInclude Include="register_set.h" />
<ClInclude Include="scm_rev.h" />
<ClInclude Include="std_condition_variable.h" />
<ClInclude Include="std_mutex.h" />
@@ -190,6 +191,7 @@
<ClInclude Include="swap.h" />
<ClInclude Include="symbols.h" />
<ClInclude Include="thread.h" />
+ <ClInclude Include="thread_queue_list.h" />
<ClInclude Include="thunk.h" />
<ClInclude Include="timer.h" />
<ClInclude Include="utf8.h" />
@@ -220,4 +222,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
index 268730228..e8c4ce360 100644
--- a/src/common/common.vcxproj.filters
+++ b/src/common/common.vcxproj.filters
@@ -4,6 +4,7 @@
<ClInclude Include="atomic.h" />
<ClInclude Include="atomic_gcc.h" />
<ClInclude Include="atomic_win32.h" />
+ <ClInclude Include="bit_field.h" />
<ClInclude Include="break_points.h" />
<ClInclude Include="chunk_file.h" />
<ClInclude Include="common.h" />
@@ -28,6 +29,7 @@
<ClInclude Include="memory_util.h" />
<ClInclude Include="msg_handler.h" />
<ClInclude Include="platform.h" />
+ <ClInclude Include="register_set.h" />
<ClInclude Include="std_condition_variable.h" />
<ClInclude Include="std_mutex.h" />
<ClInclude Include="std_thread.h" />
@@ -39,7 +41,7 @@
<ClInclude Include="utf8.h" />
<ClInclude Include="symbols.h" />
<ClInclude Include="scm_rev.h" />
- <ClInclude Include="bit_field.h" />
+ <ClInclude Include="thread_queue_list.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="break_points.cpp" />
@@ -64,4 +66,4 @@
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index f8d10eb3e..dca4dc47f 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -22,6 +22,11 @@ template<> struct CompileTimeAssert<true> {};
#define b32(x) (b16(x) | (b16(x) >>16) )
#define ROUND_UP_POW2(x) (b32(x - 1) + 1)
+#define MIN(a, b) ((a)<(b)?(a):(b))
+#define MAX(a, b) ((a)>(b)?(a):(b))
+
+#define CLAMP(x, min, max) (((x) > max) ? max : (((x) < min) ? min : (x)))
+
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#ifndef _WIN32
diff --git a/src/common/console_listener.cpp b/src/common/console_listener.cpp
index b5f32d1bd..db48abbf6 100644
--- a/src/common/console_listener.cpp
+++ b/src/common/console_listener.cpp
@@ -259,14 +259,17 @@ void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
switch (Level)
{
+ case OS_LEVEL: // light yellow
+ Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+ break;
case NOTICE_LEVEL: // light green
Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break;
case ERROR_LEVEL: // light red
Color = FOREGROUND_RED | FOREGROUND_INTENSITY;
break;
- case WARNING_LEVEL: // light yellow
- Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+ case WARNING_LEVEL: // light purple
+ Color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
break;
case INFO_LEVEL: // cyan
Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
@@ -278,15 +281,8 @@ void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
break;
}
- if (strlen(Text) > 10)
- {
- // First 10 chars white
- SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
- WriteConsole(hConsole, Text, 10, &cCharsWritten, NULL);
- Text += 10;
- }
SetConsoleTextAttribute(hConsole, Color);
- WriteConsole(hConsole, Text, (DWORD)strlen(Text), &cCharsWritten, NULL);
+ printf(Text);
#else
char ColorAttr[16] = "";
char ResetAttr[16] = "";
diff --git a/src/common/log.h b/src/common/log.h
index d95f51f56..e923224ed 100644
--- a/src/common/log.h
+++ b/src/common/log.h
@@ -5,11 +5,16 @@
#ifndef _LOG_H_
#define _LOG_H_
-#define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and OSReports.
-#define ERROR_LEVEL 2 // Critical errors
-#define WARNING_LEVEL 3 // Something is suspicious.
-#define INFO_LEVEL 4 // General information.
-#define DEBUG_LEVEL 5 // Detailed debugging - might make things slow.
+#define LOGGING
+
+enum {
+ OS_LEVEL, // Printed by the emulated operating system
+ NOTICE_LEVEL, // VERY important information that is NOT errors. Like startup and OSReports.
+ ERROR_LEVEL, // Critical errors
+ WARNING_LEVEL, // Something is suspicious.
+ INFO_LEVEL, // General information.
+ DEBUG_LEVEL, // Detailed debugging - might make things slow.
+};
namespace LogTypes
{
@@ -53,12 +58,12 @@ enum LOG_TYPE {
WII_IPC_ES,
WII_IPC_FILEIO,
WII_IPC_HID,
- WII_IPC_HLE,
+ KERNEL,
SVC,
NDMA,
HLE,
RENDER,
- LCD,
+ GPU,
HW,
TIME,
NETPLAY,
@@ -68,6 +73,7 @@ enum LOG_TYPE {
// FIXME: should this be removed?
enum LOG_LEVELS {
+ LOS = OS_LEVEL,
LNOTICE = NOTICE_LEVEL,
LERROR = ERROR_LEVEL,
LWARNING = WARNING_LEVEL,
@@ -80,31 +86,34 @@ enum LOG_LEVELS {
} // namespace
-void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type,
- const char *file, int line, const char *fmt, ...)
+void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int line,
+ const char* function, const char* fmt, ...)
#ifdef __GNUC__
- __attribute__((format(printf, 5, 6)))
+ __attribute__((format(printf, 6, 7)))
#endif
;
#if defined LOGGING || defined _DEBUG || defined DEBUGFAST
-#define MAX_LOGLEVEL DEBUG_LEVEL
+#define MAX_LOGLEVEL LDEBUG
#else
#ifndef MAX_LOGLEVEL
-#define MAX_LOGLEVEL WARNING_LEVEL
+#define MAX_LOGLEVEL LWARNING
#endif // loglevel
#endif // logging
-#ifdef GEKKO
-#define GENERIC_LOG(t, v, ...)
-#else
+#ifdef _WIN32
+#ifndef __func__
+#define __func__ __FUNCTION__
+#endif
+#endif
+
// Let the compiler optimize this out
#define GENERIC_LOG(t, v, ...) { \
- if (v <= MAX_LOGLEVEL) \
- GenericLog(v, t, __FILE__, __LINE__, __VA_ARGS__); \
+ if (v <= LogTypes::MAX_LOGLEVEL) \
+ GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \
}
-#endif
+#define OS_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LOS, __VA_ARGS__) } while (0)
#define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0)
#define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0)
#define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0)
diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp
index 80fd473b9..4e1cb60bd 100644
--- a/src/common/log_manager.cpp
+++ b/src/common/log_manager.cpp
@@ -10,14 +10,16 @@
#include "common/thread.h"
#include "common/file_util.h"
-void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
- const char *file, int line, const char* fmt, ...)
+void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
+ const char* function, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
- if (LogManager::GetInstance())
+
+ if (LogManager::GetInstance()) {
LogManager::GetInstance()->Log(level, type,
- file, line, fmt, args);
+ file, line, function, fmt, args);
+ }
va_end(args);
}
@@ -60,13 +62,13 @@ LogManager::LogManager()
m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader");
m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System");
m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID");
- m_Log[LogTypes::WII_IPC_HLE] = new LogContainer("WII_IPC_HLE", "WII IPC HLE");
+ m_Log[LogTypes::KERNEL] = new LogContainer("KERNEL", "KERNEL HLE");
m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD");
m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES");
m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO");
m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER");
- m_Log[LogTypes::LCD] = new LogContainer("LCD", "LCD");
- m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call");
+ m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU");
+ m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE");
m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA");
m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation");
m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware");
@@ -88,6 +90,8 @@ LogManager::LogManager()
m_Log[i]->AddListener(m_debuggerLog);
#endif
}
+
+ m_consoleLog->Open();
}
LogManager::~LogManager()
@@ -107,8 +111,8 @@ LogManager::~LogManager()
delete m_debuggerLog;
}
-void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
- const char *file, int line, const char *format, va_list args)
+void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file,
+ int line, const char* function, const char *fmt, va_list args)
{
char temp[MAX_MSGLEN];
char msg[MAX_MSGLEN * 2];
@@ -117,17 +121,15 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners())
return;
- CharArrayFromFormatV(temp, MAX_MSGLEN, format, args);
+ CharArrayFromFormatV(temp, MAX_MSGLEN, fmt, args);
- static const char level_to_char[7] = "-NEWID";
- sprintf(msg, "%s %s:%u %c[%s]: %s\n",
- Common::Timer::GetTimeFormatted().c_str(),
- file, line, level_to_char[(int)level],
- log->GetShortName(), temp);
+ static const char level_to_char[7] = "ONEWID";
+ sprintf(msg, "%s %s:%u %c[%s] %s: %s\n", Common::Timer::GetTimeFormatted().c_str(), file, line,
+ level_to_char[(int)level], log->GetShortName(), function, temp);
+
#ifdef ANDROID
Host_SysMessage(msg);
#endif
- printf(msg); // TODO(ShizZy): RemoveMe when I no longer need this
log->Trigger(level, msg);
}
@@ -147,7 +149,7 @@ LogContainer::LogContainer(const char* shortName, const char* fullName, bool ena
{
strncpy(m_fullName, fullName, 128);
strncpy(m_shortName, shortName, 32);
- m_level = (LogTypes::LOG_LEVELS)MAX_LOGLEVEL;
+ m_level = LogTypes::MAX_LOGLEVEL;
}
// LogContainer
diff --git a/src/common/log_manager.h b/src/common/log_manager.h
index 580860b4d..6d3d7c7ff 100644
--- a/src/common/log_manager.h
+++ b/src/common/log_manager.h
@@ -97,10 +97,10 @@ private:
~LogManager();
public:
- static u32 GetMaxLevel() { return MAX_LOGLEVEL; }
+ static u32 GetMaxLevel() { return LogTypes::MAX_LOGLEVEL; }
- void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
- const char *file, int line, const char *fmt, va_list args);
+ void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
+ const char* function, const char *fmt, va_list args);
void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level)
{
diff --git a/src/common/platform.h b/src/common/platform.h
index 1e8dffbd4..b02b52cd2 100644
--- a/src/common/platform.h
+++ b/src/common/platform.h
@@ -47,7 +47,7 @@
#define EMU_PLATFORM PLATFORM_WINDOWS
#elif defined( __APPLE__ ) || defined( __APPLE_CC__ )
-#define EMU_PLATFORM PLATFORM_MAXOSX
+#define EMU_PLATFORM PLATFORM_MACOSX
#elif defined(__linux__)
#define EMU_PLATFORM PLATFORM_LINUX
@@ -87,7 +87,6 @@ inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
#define __stdcall
#define __cdecl
-#define LONG long
#define BOOL bool
#define DWORD u32
@@ -97,7 +96,6 @@ inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
// TODO: Hacks..
#include <limits.h>
-#define MAX_PATH PATH_MAX
#include <strings.h>
#define stricmp(str1, str2) strcasecmp(str1, str2)
diff --git a/src/common/register_set.h b/src/common/register_set.h
new file mode 100644
index 000000000..0418551b3
--- /dev/null
+++ b/src/common/register_set.h
@@ -0,0 +1,161 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/*
+ * Standardized way to define a group of registers and corresponding data structures. To define
+ * a new register set, first define struct containing an enumeration called "Id" containing
+ * all register IDs and a template union called "Struct". Specialize the Struct union for any
+ * register ID which needs to be accessed in a specialized way. You can then declare the object
+ * containing all register values using the RegisterSet<BaseType, DefiningStruct> type, where
+ * BaseType is the underlying type of each register (e.g. u32).
+ * Of course, you'll usually want to implement the Struct template such that they are of the same
+ * size as BaseType. However, it's also possible to make it larger, e.g. when you want to describe
+ * multiple registers with the same structure.
+ *
+ * Example:
+ *
+ * struct Regs {
+ * enum Id : u32 {
+ * Value1 = 0,
+ * Value2 = 1,
+ * Value3 = 2,
+ * NumIds = 3
+ * };
+ *
+ * // declare register definition structures
+ * template<Id id>
+ * union Struct;
+ * };
+ *
+ * // Define register set object
+ * RegisterSet<u32, CommandIds> registers;
+ *
+ * // define register definition structures
+ * template<>
+ * union Regs::Struct<Regs::Value1> {
+ * BitField<0, 4, u32> some_field;
+ * BitField<4, 3, u32> some_other_field;
+ * };
+ *
+ * Usage in external code (within SomeNamespace scope):
+ *
+ * For a register which maps to a single index:
+ * registers.Get<Regs::Value1>().some_field = some_value;
+ *
+ * For a register which maps to different indices, e.g. a group of similar registers
+ * registers.Get<Regs::Value1>(index).some_field = some_value;
+ *
+ *
+ * @tparam BaseType Base type used for storing individual registers, e.g. u32
+ * @tparam RegDefinition Class defining an enumeration called "Id" and a template<Id id> union, as described above.
+ * @note RegDefinition::Id needs to have an enum value called NumIds defining the number of registers to be allocated.
+ */
+template<typename BaseType, typename RegDefinition>
+struct RegisterSet {
+ // Register IDs
+ using Id = typename RegDefinition::Id;
+
+ // type used for *this
+ using ThisType = RegisterSet<BaseType, RegDefinition>;
+
+ // Register definition structs, defined in RegDefinition
+ template<Id id>
+ using Struct = typename RegDefinition::template Struct<id>;
+
+
+ /*
+ * Lookup register with the given id and return it as the corresponding structure type.
+ * @note This just forwards the arguments to Get(Id).
+ */
+ template<Id id>
+ const Struct<id>& Get() const {
+ return Get<id>(id);
+ }
+
+ /*
+ * Lookup register with the given id and return it as the corresponding structure type.
+ * @note This just forwards the arguments to Get(Id).
+ */
+ template<Id id>
+ Struct<id>& Get() {
+ return Get<id>(id);
+ }
+
+ /*
+ * Lookup register with the given index and return it as the corresponding structure type.
+ * @todo Is this portable with regards to structures larger than BaseType?
+ * @note if index==id, you don't need to specify the function parameter.
+ */
+ template<Id id>
+ const Struct<id>& Get(const Id& index) const {
+ const int idx = static_cast<size_t>(index);
+ return *reinterpret_cast<const Struct<id>*>(&raw[idx]);
+ }
+
+ /*
+ * Lookup register with the given index and return it as the corresponding structure type.
+ * @note This just forwards the arguments to the const version of Get(Id).
+ * @note if index==id, you don't need to specify the function parameter.
+ */
+ template<Id id>
+ Struct<id>& Get(const Id& index) {
+ return const_cast<Struct<id>&>(GetThis().Get<id>(index));
+ }
+
+ /*
+ * Plain array access.
+ * @note If you want to have this casted to a register defininition struct, use Get() instead.
+ */
+ const BaseType& operator[] (const Id& id) const {
+ return raw[static_cast<size_t>(id)];
+ }
+
+ /*
+ * Plain array access.
+ * @note If you want to have this casted to a register defininition struct, use Get() instead.
+ * @note This operator just forwards its argument to the const version.
+ */
+ BaseType& operator[] (const Id& id) {
+ return const_cast<BaseType&>(GetThis()[id]);
+ }
+
+private:
+ /*
+ * Returns a const reference to "this".
+ */
+ const ThisType& GetThis() const {
+ return static_cast<const ThisType&>(*this);
+ }
+
+ BaseType raw[Id::NumIds];
+};
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
new file mode 100644
index 000000000..4a89572f6
--- /dev/null
+++ b/src/common/thread_queue_list.h
@@ -0,0 +1,216 @@
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common.h"
+
+namespace Common {
+
+template<class IdType>
+struct ThreadQueueList {
+ // Number of queues (number of priority levels starting at 0.)
+ static const int NUM_QUEUES = 128;
+
+ // Initial number of threads a single queue can handle.
+ static const int INITIAL_CAPACITY = 32;
+
+ struct Queue {
+ // Next ever-been-used queue (worse priority.)
+ Queue *next;
+ // First valid item in data.
+ int first;
+ // One after last valid item in data.
+ int end;
+ // A too-large array with room on the front and end.
+ IdType *data;
+ // Size of data array.
+ int capacity;
+ };
+
+ ThreadQueueList() {
+ memset(queues, 0, sizeof(queues));
+ first = invalid();
+ }
+
+ ~ThreadQueueList() {
+ for (int i = 0; i < NUM_QUEUES; ++i)
+ {
+ if (queues[i].data != NULL)
+ free(queues[i].data);
+ }
+ }
+
+ // Only for debugging, returns priority level.
+ int contains(const IdType uid) {
+ for (int i = 0; i < NUM_QUEUES; ++i)
+ {
+ if (queues[i].data == NULL)
+ continue;
+
+ Queue *cur = &queues[i];
+ for (int j = cur->first; j < cur->end; ++j)
+ {
+ if (cur->data[j] == uid)
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ inline IdType pop_first() {
+ Queue *cur = first;
+ while (cur != invalid())
+ {
+ if (cur->end - cur->first > 0)
+ return cur->data[cur->first++];
+ cur = cur->next;
+ }
+
+ //_dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty.");
+ return 0;
+ }
+
+ inline IdType pop_first_better(u32 priority) {
+ Queue *cur = first;
+ Queue *stop = &queues[priority];
+ while (cur < stop)
+ {
+ if (cur->end - cur->first > 0)
+ return cur->data[cur->first++];
+ cur = cur->next;
+ }
+
+ return 0;
+ }
+
+ inline void push_front(u32 priority, const IdType threadID) {
+ Queue *cur = &queues[priority];
+ cur->data[--cur->first] = threadID;
+ if (cur->first == 0)
+ rebalance(priority);
+ }
+
+ inline void push_back(u32 priority, const IdType threadID) {
+ Queue *cur = &queues[priority];
+ cur->data[cur->end++] = threadID;
+ if (cur->end == cur->capacity)
+ rebalance(priority);
+ }
+
+ inline void remove(u32 priority, const IdType threadID) {
+ Queue *cur = &queues[priority];
+ //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
+
+ for (int i = cur->first; i < cur->end; ++i)
+ {
+ if (cur->data[i] == threadID)
+ {
+ int remaining = --cur->end - i;
+ if (remaining > 0)
+ memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(IdType));
+ return;
+ }
+ }
+
+ // Wasn't there.
+ }
+
+ inline void rotate(u32 priority) {
+ Queue *cur = &queues[priority];
+ //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
+
+ if (cur->end - cur->first > 1)
+ {
+ cur->data[cur->end++] = cur->data[cur->first++];
+ if (cur->end == cur->capacity)
+ rebalance(priority);
+ }
+ }
+
+ inline void clear() {
+ for (int i = 0; i < NUM_QUEUES; ++i)
+ {
+ if (queues[i].data != NULL)
+ free(queues[i].data);
+ }
+ memset(queues, 0, sizeof(queues));
+ first = invalid();
+ }
+
+ inline bool empty(u32 priority) const {
+ const Queue *cur = &queues[priority];
+ return cur->first == cur->end;
+ }
+
+ inline void prepare(u32 priority) {
+ Queue *cur = &queues[priority];
+ if (cur->next == NULL)
+ link(priority, INITIAL_CAPACITY);
+ }
+
+private:
+ Queue *invalid() const {
+ return (Queue *) -1;
+ }
+
+ void link(u32 priority, int size) {
+ //_dbg_assert_msg_(SCEKERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once.");
+
+ if (size <= INITIAL_CAPACITY)
+ size = INITIAL_CAPACITY;
+ else
+ {
+ int goal = size;
+ size = INITIAL_CAPACITY;
+ while (size < goal)
+ size *= 2;
+ }
+ Queue *cur = &queues[priority];
+ cur->data = (IdType *) malloc(sizeof(IdType) * size);
+ cur->capacity = size;
+ cur->first = size / 2;
+ cur->end = size / 2;
+
+ for (int i = (int) priority - 1; i >= 0; --i)
+ {
+ if (queues[i].next != NULL)
+ {
+ cur->next = queues[i].next;
+ queues[i].next = cur;
+ return;
+ }
+ }
+
+ cur->next = first;
+ first = cur;
+ }
+
+ void rebalance(u32 priority) {
+ Queue *cur = &queues[priority];
+ int size = cur->end - cur->first;
+ if (size >= cur->capacity - 2) {
+ IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType));
+ if (new_data != NULL) {
+ cur->capacity *= 2;
+ cur->data = new_data;
+ }
+ }
+
+ int newFirst = (cur->capacity - size) / 2;
+ if (newFirst != cur->first) {
+ memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(IdType));
+ cur->first = newFirst;
+ cur->end = newFirst + size;
+ }
+ }
+
+ // The first queue that's ever been used.
+ Queue *first;
+ // The priority level queues of thread ids.
+ Queue queues[NUM_QUEUES];
+};
+
+} // namespace
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 14c598bf3..7116b88e9 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -33,14 +33,19 @@ set(SRCS core.cpp
hle/hle.cpp
hle/config_mem.cpp
hle/coprocessor.cpp
- hle/syscall.cpp
+ hle/svc.cpp
+ hle/kernel/event.cpp
+ hle/kernel/kernel.cpp
+ hle/kernel/mutex.cpp
+ hle/kernel/thread.cpp
hle/service/apt.cpp
hle/service/gsp.cpp
hle/service/hid.cpp
+ hle/service/ndm.cpp
hle/service/service.cpp
hle/service/srv.cpp
+ hw/gpu.cpp
hw/hw.cpp
- hw/lcd.cpp
hw/ndma.cpp)
set(HEADERS core.h
@@ -75,15 +80,18 @@ set(HEADERS core.h
hle/config_mem.h
hle/coprocessor.h
hle/hle.h
- hle/syscall.h
+ hle/svc.h
+ hle/kernel/kernel.h
+ hle/kernel/mutex.h
+ hle/kernel/thread.h
hle/function_wrappers.h
hle/service/apt.h
hle/service/gsp.h
hle/service/hid.h
hle/service/service.h
hle/service/srv.h
+ hw/gpu.h
hw/hw.h
- hw/lcd.h
hw/ndma.h)
add_library(core STATIC ${SRCS} ${HEADERS})
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 9fdc7ba3c..be677ae20 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -7,11 +7,13 @@
#include "common/common.h"
#include "common/common_types.h"
+#include "core/hle/svc.h"
+
/// Generic ARM11 CPU interface
class ARM_Interface : NonCopyable {
public:
ARM_Interface() {
- m_num_instructions = 0;
+ num_instructions = 0;
}
~ARM_Interface() {
@@ -23,7 +25,7 @@ public:
*/
void Run(int num_instructions) {
ExecuteInstructions(num_instructions);
- m_num_instructions += num_instructions;
+ this->num_instructions += num_instructions;
}
/// Step CPU by one instruction
@@ -64,14 +66,35 @@ public:
virtual u32 GetCPSR() const = 0;
/**
+ * Set the current CPSR register
+ * @param cpsr Value to set CPSR to
+ */
+ 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;
- /// Getter for m_num_instructions
+ /**
+ * Saves the current CPU context
+ * @param ctx Thread context to save
+ */
+ virtual void SaveContext(ThreadContext& ctx) = 0;
+
+ /**
+ * Loads a CPU context
+ * @param ctx Thread context to load
+ */
+ virtual void LoadContext(const ThreadContext& ctx) = 0;
+
+ /// Prepare core for thread reschedule (if needed to correctly handle state)
+ virtual void PrepareReschedule() = 0;
+
+ /// Getter for num_instructions
u64 GetNumInstructions() {
- return m_num_instructions;
+ return num_instructions;
}
protected:
@@ -84,6 +107,6 @@ protected:
private:
- u64 m_num_instructions; ///< Number of instructions executed
+ u64 num_instructions; ///< Number of instructions executed
};
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp
index 23d96d292..0e893f182 100644
--- a/src/core/arm/interpreter/arm_interpreter.cpp
+++ b/src/core/arm/interpreter/arm_interpreter.cpp
@@ -9,30 +9,30 @@ const static cpu_config_t s_arm11_cpu_info = {
};
ARM_Interpreter::ARM_Interpreter() {
- m_state = new ARMul_State;
+ state = new ARMul_State;
ARMul_EmulateInit();
- ARMul_NewState(m_state);
+ ARMul_NewState(state);
- m_state->abort_model = 0;
- m_state->cpu = (cpu_config_t*)&s_arm11_cpu_info;
- m_state->bigendSig = LOW;
+ state->abort_model = 0;
+ state->cpu = (cpu_config_t*)&s_arm11_cpu_info;
+ state->bigendSig = LOW;
- ARMul_SelectProcessor(m_state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop);
- m_state->lateabtSig = LOW;
- mmu_init(m_state);
+ ARMul_SelectProcessor(state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop);
+ state->lateabtSig = LOW;
+ mmu_init(state);
// Reset the core to initial state
- ARMul_Reset(m_state);
- m_state->NextInstr = 0;
- m_state->Emulate = 3;
+ ARMul_Reset(state);
+ state->NextInstr = 0;
+ state->Emulate = 3;
- m_state->pc = m_state->Reg[15] = 0x00000000;
- m_state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack
+ state->pc = state->Reg[15] = 0x00000000;
+ state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack
}
ARM_Interpreter::~ARM_Interpreter() {
- delete m_state;
+ delete state;
}
/**
@@ -40,7 +40,7 @@ ARM_Interpreter::~ARM_Interpreter() {
* @param addr Address to set PC to
*/
void ARM_Interpreter::SetPC(u32 pc) {
- m_state->pc = m_state->Reg[15] = pc;
+ state->pc = state->Reg[15] = pc;
}
/*
@@ -48,7 +48,7 @@ void ARM_Interpreter::SetPC(u32 pc) {
* @return Returns current PC
*/
u32 ARM_Interpreter::GetPC() const {
- return m_state->pc;
+ return state->pc;
}
/**
@@ -57,7 +57,7 @@ u32 ARM_Interpreter::GetPC() const {
* @return Returns the value in the register
*/
u32 ARM_Interpreter::GetReg(int index) const {
- return m_state->Reg[index];
+ return state->Reg[index];
}
/**
@@ -66,7 +66,7 @@ u32 ARM_Interpreter::GetReg(int index) const {
* @param value Value to set register to
*/
void ARM_Interpreter::SetReg(int index, u32 value) {
- m_state->Reg[index] = value;
+ state->Reg[index] = value;
}
/**
@@ -74,7 +74,15 @@ void ARM_Interpreter::SetReg(int index, u32 value) {
* @return Returns the value of the CPSR register
*/
u32 ARM_Interpreter::GetCPSR() const {
- return m_state->Cpsr;
+ return state->Cpsr;
+}
+
+/**
+ * Set the current CPSR register
+ * @param cpsr Value to set CPSR to
+ */
+void ARM_Interpreter::SetCPSR(u32 cpsr) {
+ state->Cpsr = cpsr;
}
/**
@@ -82,7 +90,7 @@ u32 ARM_Interpreter::GetCPSR() const {
* @return Returns number of clock ticks
*/
u64 ARM_Interpreter::GetTicks() const {
- return ARMul_Time(m_state);
+ return ARMul_Time(state);
}
/**
@@ -90,6 +98,53 @@ u64 ARM_Interpreter::GetTicks() const {
* @param num_instructions Number of instructions to executes
*/
void ARM_Interpreter::ExecuteInstructions(int num_instructions) {
- m_state->NumInstrsToExecute = num_instructions;
- ARMul_Emulate32(m_state);
+ state->NumInstrsToExecute = num_instructions - 1;
+ ARMul_Emulate32(state);
+}
+
+/**
+ * Saves the current CPU context
+ * @param ctx Thread context to save
+ * @todo Do we need to save Reg[15] and NextInstr?
+ */
+void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
+ memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
+ memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
+
+ ctx.sp = state->Reg[13];
+ ctx.lr = state->Reg[14];
+ ctx.pc = state->pc;
+ ctx.cpsr = state->Cpsr;
+
+ ctx.fpscr = state->VFP[1];
+ ctx.fpexc = state->VFP[2];
+
+ ctx.reg_15 = state->Reg[15];
+ ctx.mode = state->NextInstr;
+}
+
+/**
+ * Loads a CPU context
+ * @param ctx Thread context to load
+ * @param Do we need to load Reg[15] and NextInstr?
+ */
+void ARM_Interpreter::LoadContext(const ThreadContext& ctx) {
+ memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
+ memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
+
+ state->Reg[13] = ctx.sp;
+ state->Reg[14] = ctx.lr;
+ state->pc = ctx.pc;
+ state->Cpsr = ctx.cpsr;
+
+ state->VFP[1] = ctx.fpscr;
+ state->VFP[2] = ctx.fpexc;
+
+ state->Reg[15] = ctx.reg_15;
+ state->NextInstr = ctx.mode;
+}
+
+/// Prepare core for thread reschedule (if needed to correctly handle state)
+void ARM_Interpreter::PrepareReschedule() {
+ state->NumInstrsToExecute = 0;
}
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h
index 509025080..1e82883a2 100644
--- a/src/core/arm/interpreter/arm_interpreter.h
+++ b/src/core/arm/interpreter/arm_interpreter.h
@@ -49,11 +49,32 @@ public:
u32 GetCPSR() const;
/**
+ * Set the current CPSR register
+ * @param cpsr Value to set CPSR to
+ */
+ void SetCPSR(u32 cpsr);
+
+ /**
* Returns the number of clock ticks since the last reset
* @return Returns number of clock ticks
*/
u64 GetTicks() const;
+ /**
+ * Saves the current CPU context
+ * @param ctx Thread context to save
+ */
+ void SaveContext(ThreadContext& ctx);
+
+ /**
+ * Loads a CPU context
+ * @param ctx Thread context to load
+ */
+ void LoadContext(const ThreadContext& ctx);
+
+ /// Prepare core for thread reschedule (if needed to correctly handle state)
+ void PrepareReschedule();
+
protected:
/**
@@ -64,6 +85,6 @@ protected:
private:
- ARMul_State* m_state;
+ ARMul_State* state;
};
diff --git a/src/core/arm/interpreter/armdefs.h b/src/core/arm/interpreter/armdefs.h
index 5b2abc7f7..d8eae4d3f 100644
--- a/src/core/arm/interpreter/armdefs.h
+++ b/src/core/arm/interpreter/armdefs.h
@@ -24,10 +24,6 @@
#include "common/platform.h"
-#if EMU_PLATFORM == PLATFORM_WINDOWS
-#include <windows.h>
-#endif
-
//teawater add for arm2x86 2005.02.14-------------------------------------------
// koodailar remove it for mingw 2005.12.18----------------
//anthonylee modify it for portable 2007.01.30
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp
index 32e315f4b..f3c14e608 100644
--- a/src/core/arm/interpreter/armemu.cpp
+++ b/src/core/arm/interpreter/armemu.cpp
@@ -4456,6 +4456,7 @@ ARMul_Emulate26 (ARMul_State * state)
}
/* Drop through. */
+ case 0xe0:
case 0xe4:
case 0xe6:
case 0xe8:
@@ -4478,8 +4479,7 @@ ARMul_Emulate26 (ARMul_State * state)
isize) &
R15PCBITS));
#endif
- }
- else
+ } else if (instr != 0xDEADC0DE) // thumbemu uses 0xDEADCODE for debugging to catch non updates
ARMul_MCR (state, instr,
DEST);
}
@@ -4490,7 +4490,6 @@ ARMul_Emulate26 (ARMul_State * state)
/* Co-Processor Register Transfers (MRC) and Data Ops. */
- case 0xe0:
case 0xe1:
case 0xe3:
case 0xe5:
@@ -4534,23 +4533,7 @@ ARMul_Emulate26 (ARMul_State * state)
case 0xfd:
case 0xfe:
case 0xff:
- if (instr == ARMul_ABORTWORD
- && state->AbortAddr == pc) {
- /* A prefetch abort. */
- XScale_set_fsr_far (state,
- ARMul_CP15_R5_MMU_EXCPT,
- pc);
- ARMul_Abort (state,
- ARMul_PrefetchAbortV);
- break;
- }
- //sky_pref_t* pref = get_skyeye_pref();
- //if(pref->user_mode_sim){
- // ARMul_OSHandleSWI (state, BITS (0, 23));
- // break;
- //}
- HLE::CallSyscall(instr);
- ARMul_Abort (state, ARMul_SWIV);
+ HLE::CallSVC(instr);
break;
}
}
diff --git a/src/core/arm/interpreter/arminit.cpp b/src/core/arm/interpreter/arminit.cpp
index 2c771cdda..e05667bea 100644
--- a/src/core/arm/interpreter/arminit.cpp
+++ b/src/core/arm/interpreter/arminit.cpp
@@ -17,8 +17,11 @@
#include "common/platform.h"
+
#if EMU_PLATFORM == PLATFORM_LINUX
#include <unistd.h>
+#elif EMU_PLATFORM == PLATFORM_WINDOWS
+#include <windows.h>
#endif
#include <math.h>
diff --git a/src/core/arm/interpreter/vfp/vfp.h b/src/core/arm/interpreter/vfp/vfp.h
index f738a615b..bbf4caeb0 100644
--- a/src/core/arm/interpreter/vfp/vfp.h
+++ b/src/core/arm/interpreter/vfp/vfp.h
@@ -21,7 +21,7 @@
#ifndef __VFP_H__
#define __VFP_H__
-#define DBG(...) DEBUG_LOG(ARM11, __VA_ARGS__)
+#define DBG(...) //DEBUG_LOG(ARM11, __VA_ARGS__)
#define vfpdebug //printf
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 61c237b2c..7dc0809d0 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -9,26 +9,40 @@
#include "core/core.h"
#include "core/mem_map.h"
#include "core/hw/hw.h"
+#include "core/hw/gpu.h"
#include "core/arm/disassembler/arm_disasm.h"
#include "core/arm/interpreter/arm_interpreter.h"
+#include "core/hle/hle.h"
+#include "core/hle/kernel/thread.h"
+
namespace Core {
-ARM_Disasm* g_disasm = NULL; ///< ARM disassembler
-ARM_Interface* g_app_core = NULL; ///< ARM11 application core
-ARM_Interface* g_sys_core = NULL; ///< ARM11 system (OS) core
+u64 g_last_ticks = 0; ///< Last CPU ticks
+ARM_Disasm* g_disasm = nullptr; ///< ARM disassembler
+ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
+ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
/// Run the core CPU loop
void RunLoop() {
for (;;){
- g_app_core->Run(10000);
+ g_app_core->Run(GPU::kFrameTicks);
HW::Update();
+ Kernel::Reschedule();
}
}
/// Step the CPU one instruction
void SingleStep() {
g_app_core->Step();
+
+ // Update and reschedule after approx. 1 frame
+ u64 current_ticks = Core::g_app_core->GetTicks();
+ if ((current_ticks - g_last_ticks) >= GPU::kFrameTicks || HLE::g_reschedule) {
+ g_last_ticks = current_ticks;
+ HW::Update();
+ Kernel::Reschedule();
+ }
}
/// Halt the core
@@ -49,6 +63,8 @@ int Init() {
g_app_core = new ARM_Interpreter();
g_sys_core = new ARM_Interpreter();
+ g_last_ticks = Core::g_app_core->GetTicks();
+
return 0;
}
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index 41af5801d..8eb189a8b 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -168,14 +168,19 @@
<ClCompile Include="hle\config_mem.cpp" />
<ClCompile Include="hle\coprocessor.cpp" />
<ClCompile Include="hle\hle.cpp" />
+ <ClCompile Include="hle\kernel\event.cpp" />
+ <ClCompile Include="hle\kernel\kernel.cpp" />
+ <ClCompile Include="hle\kernel\mutex.cpp" />
+ <ClCompile Include="hle\kernel\thread.cpp" />
<ClCompile Include="hle\service\apt.cpp" />
<ClCompile Include="hle\service\gsp.cpp" />
<ClCompile Include="hle\service\hid.cpp" />
+ <ClCompile Include="hle\service\ndm.cpp" />
<ClCompile Include="hle\service\service.cpp" />
<ClCompile Include="hle\service\srv.cpp" />
- <ClCompile Include="hle\syscall.cpp" />
+ <ClCompile Include="hle\svc.cpp" />
+ <ClCompile Include="hw\gpu.cpp" />
<ClCompile Include="hw\hw.cpp" />
- <ClCompile Include="hw\lcd.cpp" />
<ClCompile Include="hw\ndma.cpp" />
<ClCompile Include="loader.cpp" />
<ClCompile Include="mem_map.cpp" />
@@ -214,14 +219,19 @@
<ClInclude Include="hle\coprocessor.h" />
<ClInclude Include="hle\function_wrappers.h" />
<ClInclude Include="hle\hle.h" />
+ <ClInclude Include="hle\kernel\event.h" />
+ <ClInclude Include="hle\kernel\kernel.h" />
+ <ClInclude Include="hle\kernel\mutex.h" />
+ <ClInclude Include="hle\kernel\thread.h" />
<ClInclude Include="hle\service\apt.h" />
<ClInclude Include="hle\service\gsp.h" />
<ClInclude Include="hle\service\hid.h" />
+ <ClInclude Include="hle\service\ndm.h" />
<ClInclude Include="hle\service\service.h" />
<ClInclude Include="hle\service\srv.h" />
- <ClInclude Include="hle\syscall.h" />
+ <ClInclude Include="hle\svc.h" />
+ <ClInclude Include="hw\gpu.h" />
<ClInclude Include="hw\hw.h" />
- <ClInclude Include="hw\lcd.h" />
<ClInclude Include="hw\ndma.h" />
<ClInclude Include="loader.h" />
<ClInclude Include="mem_map.h" />
@@ -233,4 +243,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters
index edf34ce2f..da781f816 100644
--- a/src/core/core.vcxproj.filters
+++ b/src/core/core.vcxproj.filters
@@ -31,6 +31,9 @@
<Filter Include="arm\interpreter\mmu">
<UniqueIdentifier>{13ef9860-2ba0-47e9-a93d-b4052adab269}</UniqueIdentifier>
</Filter>
+ <Filter Include="hle\kernel">
+ <UniqueIdentifier>{8089d94b-5faa-43dc-854b-ffd2fa2e7fe3}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="arm\disassembler\arm_disasm.cpp">
@@ -81,9 +84,6 @@
<ClCompile Include="hle\hle.cpp">
<Filter>hle</Filter>
</ClCompile>
- <ClCompile Include="hle\syscall.cpp">
- <Filter>hle</Filter>
- </ClCompile>
<ClCompile Include="hle\service\service.cpp">
<Filter>hle\service</Filter>
</ClCompile>
@@ -102,7 +102,7 @@
<ClCompile Include="hw\ndma.cpp">
<Filter>hw</Filter>
</ClCompile>
- <ClCompile Include="hw\lcd.cpp">
+ <ClCompile Include="hw\gpu.cpp">
<Filter>hw</Filter>
</ClCompile>
<ClCompile Include="arm\disassembler\load_symbol_map.cpp">
@@ -147,12 +147,30 @@
<ClCompile Include="arm\interpreter\mmu\wb.cpp">
<Filter>arm\interpreter\mmu</Filter>
</ClCompile>
- <ClCompile Include="arm\interpreter\armcopro.cpp">
- <Filter>arm</Filter>
- </ClCompile>
<ClCompile Include="arm\interpreter\mmu\maverick.cpp">
<Filter>arm\interpreter\mmu</Filter>
</ClCompile>
+ <ClCompile Include="hle\kernel\kernel.cpp">
+ <Filter>hle\kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="hle\kernel\thread.cpp">
+ <Filter>hle\kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="hle\svc.cpp">
+ <Filter>hle</Filter>
+ </ClCompile>
+ <ClCompile Include="hle\kernel\mutex.cpp">
+ <Filter>hle\kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="arm\interpreter\armcopro.cpp">
+ <Filter>arm\interpreter</Filter>
+ </ClCompile>
+ <ClCompile Include="hle\kernel\event.cpp">
+ <Filter>hle\kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="hle\service\ndm.cpp">
+ <Filter>hle\service</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="arm\disassembler\arm_disasm.h">
@@ -217,9 +235,6 @@
<ClInclude Include="hle\service\service.h">
<Filter>hle\service</Filter>
</ClInclude>
- <ClInclude Include="hle\syscall.h">
- <Filter>hle</Filter>
- </ClInclude>
<ClInclude Include="hle\service\apt.h">
<Filter>hle\service</Filter>
</ClInclude>
@@ -235,7 +250,7 @@
<ClInclude Include="hw\ndma.h">
<Filter>hw</Filter>
</ClInclude>
- <ClInclude Include="hw\lcd.h">
+ <ClInclude Include="hw\gpu.h">
<Filter>hw</Filter>
</ClInclude>
<ClInclude Include="arm\disassembler\load_symbol_map.h">
@@ -274,8 +289,26 @@
<ClInclude Include="arm\interpreter\mmu\sa_mmu.h">
<Filter>arm\interpreter\mmu</Filter>
</ClInclude>
+ <ClInclude Include="hle\kernel\kernel.h">
+ <Filter>hle\kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="hle\kernel\thread.h">
+ <Filter>hle\kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="hle\svc.h">
+ <Filter>hle</Filter>
+ </ClInclude>
+ <ClInclude Include="hle\kernel\mutex.h">
+ <Filter>hle\kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="hle\kernel\event.h">
+ <Filter>hle\kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="hle\service\ndm.h">
+ <Filter>hle\service</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp
index 48aa878cc..8c898b265 100644
--- a/src/core/hle/config_mem.cpp
+++ b/src/core/hle/config_mem.cpp
@@ -55,7 +55,7 @@ inline void Read(T &var, const u32 addr) {
break;
default:
- ERROR_LOG(HLE, "unknown ConfigMem::Read%d @ 0x%08X", sizeof(var) * 8, addr);
+ ERROR_LOG(HLE, "unknown addr=0x%08X", addr);
}
}
diff --git a/src/core/hle/coprocessor.cpp b/src/core/hle/coprocessor.cpp
index 39674ee64..9a5b0deda 100644
--- a/src/core/hle/coprocessor.cpp
+++ b/src/core/hle/coprocessor.cpp
@@ -25,7 +25,7 @@ s32 CallMRC(u32 instruction) {
return GetThreadCommandBuffer();
default:
- //DEBUG_LOG(OSHLE, "unknown MRC call 0x%08X", instruction);
+ DEBUG_LOG(OSHLE, "unknown MRC call 0x%08X", instruction);
break;
}
return -1;
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index d934eafb4..0bed78653 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -1,19 +1,6 @@
-// Copyright (c) 2012- PPSSPP Project.
-
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 2.0 or later versions.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License 2.0 for more details.
-
-// A copy of the GPL 2.0 should have been included with the program.
-// If not, see http://www.gnu.org/licenses/
-
-// Official git repository and contact information can be found at
-// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
#pragma once
@@ -21,715 +8,107 @@
#include "core/mem_map.h"
#include "core/hle/hle.h"
-// For easy parameter parsing and return value processing.
-
-//32bit wrappers
-template<void func()> void WrapV_V() {
- func();
-}
-
-template<u32 func()> void WrapU_V() {
- RETURN(func());
-}
-
-template<int func(void *, const char *)> void WrapI_VC() {
- u32 retval = func(Memory::GetPointer(PARAM(0)), Memory::GetCharPointer(PARAM(1)));
- RETURN(retval);
-}
-
-template<u32 func(int, void *, int)> void WrapU_IVI() {
- u32 retval = func(PARAM(0), Memory::GetPointer(PARAM(1)), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(const char *, int, int, u32)> void WrapI_CIIU() {
- u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(int, const char *, u32, void *, void *, u32, int)> void WrapI_ICUVVUI() {
- u32 retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), Memory::GetPointer(PARAM(3)),Memory::GetPointer(PARAM(4)), PARAM(5), PARAM(6) );
- RETURN(retval);
-}
-
-// Hm, do so many params get passed in registers?
-template<int func(const char *, int, const char *, int, int, int, int, int)> void WrapI_CICIIIII() {
- u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), Memory::GetCharPointer(PARAM(2)),
- PARAM(3), PARAM(4), PARAM(5), PARAM(6), PARAM(7));
- RETURN(retval);
-}
-
-// Hm, do so many params get passed in registers?
-template<int func(const char *, int, int, int, int, int, int)> void WrapI_CIIIIII() {
- u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
- PARAM(3), PARAM(4), PARAM(5), PARAM(6));
- RETURN(retval);
-}
+namespace HLE {
-// Hm, do so many params get passed in registers?
-template<int func(int, int, int, int, int, int, u32)> void WrapI_IIIIIIU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
- RETURN(retval);
-}
+#define PARAM(n) Core::g_app_core->GetReg(n)
+#define RETURN(n) Core::g_app_core->SetReg(0, n)
-// Hm, do so many params get passed in registers?
-template<int func(int, int, int, int, int, int, int, int, u32)> void WrapI_IIIIIIIIU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6), PARAM(7), PARAM(8));
- RETURN(retval);
-}
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Function wrappers that return type s32
-template<u32 func(int, void *)> void WrapU_IV() {
- u32 retval = func(PARAM(0), Memory::GetPointer(PARAM(1)));
- RETURN(retval);
+template<s32 func(u32, u32, u32, u32)> void Wrap() {
+ RETURN(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)));
}
-template<u32 func(u32)> void WrapU_U() {
- u32 retval = func(PARAM(0));
- RETURN(retval);
+template<s32 func(u32, u32, u32, u32, u32)> void Wrap() {
+ RETURN(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)));
}
-template<u32 func(u32, int)> void WrapU_UI() {
- u32 retval = func(PARAM(0), PARAM(1));
+template<s32 func(u32*, u32, u32, u32, u32, u32)> void Wrap(){
+ u32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
+ Core::g_app_core->SetReg(1, param_1);
RETURN(retval);
}
-template<int func(u32)> void WrapI_U() {
- int retval = func(PARAM(0));
+template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() {
+ s32 param_1 = 0;
+ s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
+ (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0)));
+ Core::g_app_core->SetReg(1, (u32)param_1);
RETURN(retval);
}
-template<int func(u32, int)> void WrapI_UI() {
- int retval = func(PARAM(0), PARAM(1));
- RETURN(retval);
+// TODO(bunnei): Is this correct? Probably not
+template<s32 func(u32, u32, u32, u32, s64)> void Wrap() {
+ RETURN(func(PARAM(5), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(4) << 32) | PARAM(0))));
}
-template<int func(u32, int, int, u32)> void WrapI_UIIU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
+template<s32 func(u32, s64)> void Wrap() {
+ RETURN(func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))));
}
-template<u32 func(int, u32, int)> void WrapU_IUI() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(u32, u32)> void WrapI_UU() {
- int retval = func(PARAM(0), PARAM(1));
- RETURN(retval);
+template<s32 func(void*, void*, u32)> void Wrap(){
+ RETURN(func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2)));
}
-template<int func(u32, u32, u32)> void WrapI_UUU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2));
+template<s32 func(s32*, u32)> void Wrap(){
+ s32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(1));
+ Core::g_app_core->SetReg(1, param_1);
RETURN(retval);
}
-template<int func(u32, u32, u32, int)> void WrapI_UUUI() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
+template<s32 func(u32, s32)> void Wrap() {
+ RETURN(func(PARAM(0), (s32)PARAM(1)));
}
-template<int func(u32, u32, u32, int, int, int,int )> void WrapI_UUUIIII() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
+template<s32 func(u32*, u32)> void Wrap(){
+ u32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(1));
+ Core::g_app_core->SetReg(1, param_1);
RETURN(retval);
}
-template<int func(u32, u32, u32, u32)> void WrapI_UUUU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
+template<s32 func(u32)> void Wrap() {
+ RETURN(func(PARAM(0)));
}
-template<int func(u32, u32, u32, u32, u32)> void WrapI_UUUUU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<int func(void*)> void WrapI_V() {
- u32 retval = func(Memory::GetPointer(PARAM(0)));
- RETURN(retval);
-}
-
-template<u32 func(int)> void WrapU_I() {
- u32 retval = func(PARAM(0));
- RETURN(retval);
-}
-
-template<u32 func(int, int, u32)> void WrapU_IIU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(int)> void WrapI_I() {
- int retval = func(PARAM(0));
- RETURN(retval);
-}
-
-template<void func(u32)> void WrapV_U() {
- func(PARAM(0));
-}
-
-template<void func(int)> void WrapV_I() {
- func(PARAM(0));
-}
-
-template<void func(u32, u32)> void WrapV_UU() {
- func(PARAM(0), PARAM(1));
-}
-
-template<void func(int, int)> void WrapV_II() {
- func(PARAM(0), PARAM(1));
-}
-
-template<void func(u32, const char *)> void WrapV_UC() {
- func(PARAM(0), Memory::GetCharPointer(PARAM(1)));
-}
-
-template<int func(u32, const char *)> void WrapI_UC() {
- int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)));
- RETURN(retval);
-}
-
-template<int func(u32, const char *, int)> void WrapI_UCI() {
- int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2));
- RETURN(retval);
-}
-
-template<u32 func(u32, int , int , int, int, int)> void WrapU_UIIIII() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
- RETURN(retval);
-}
-
-template<u32 func(u32, int , int , int, u32)> void WrapU_UIIIU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<u32 func(u32, int , int , int, int, int, int)> void WrapU_UIIIIII() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32)> void WrapU_UU() {
- u32 retval = func(PARAM(0), PARAM(1));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32, int)> void WrapU_UUI() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32, int, int)> void WrapU_UUII() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<u32 func(const char *, u32, u32, u32)> void WrapU_CUUU() {
- u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
+template<s32 func(void*)> void Wrap() {
+ RETURN(func(Memory::GetPointer(PARAM(0))));
}
-template<void func(u32, int, u32, int, int)> void WrapV_UIUII() {
- func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
+template<s32 func(s64*, u32, void*, s32)> void Wrap(){
+ RETURN(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)),
+ (s32)PARAM(3)));
}
-template<u32 func(u32, int, u32, int, int)> void WrapU_UIUII() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
+template<s32 func(u32*, const char*)> void Wrap() {
+ u32 param_1 = 0;
+ u32 retval = func(&param_1, Memory::GetCharPointer(PARAM(1)));
+ Core::g_app_core->SetReg(1, param_1);
RETURN(retval);
}
-template<int func(u32, int, u32, int, int)> void WrapI_UIUII() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<u32 func(u32, int, u32, int)> void WrapU_UIUI() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(u32, int, u32, int)> void WrapI_UIUI() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<u32 func(u32, int, u32)> void WrapU_UIU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<u32 func(u32, int, u32, u32)> void WrapU_UIUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<u32 func(u32, int, int)> void WrapU_UII() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<u32 func(u32, int, int, u32)> void WrapU_UIIU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(u32, int, int, u32, u32)> void WrapI_UIIUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<int func(u32, u32, int, int)> void WrapI_UUII() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(u32, u32, int, int, int)> void WrapI_UUIII() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<void func(u32, int, int, int)> void WrapV_UIII() {
- func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
-}
-
-template<void func(u32, int, int, int, int, int)> void WrapV_UIIIII() {
- func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
-}
-
-template<void func(u32, int, int)> void WrapV_UII() {
- func(PARAM(0), PARAM(1), PARAM(2));
-}
-
-template<u32 func(int, u32)> void WrapU_IU() {
- int retval = func(PARAM(0), PARAM(1));
- RETURN(retval);
-}
-
-template<int func(int, u32)> void WrapI_IU() {
- int retval = func(PARAM(0), PARAM(1));
- RETURN(retval);
-}
-
-template<int func(u32, u32, int)> void WrapI_UUI() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(u32, u32, int, u32)> void WrapI_UUIU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(int, int)> void WrapI_II() {
- int retval = func(PARAM(0), PARAM(1));
- RETURN(retval);
-}
-
-template<int func(int, int, int)> void WrapI_III() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(int, u32, int)> void WrapI_IUI() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(int, int, int, int)> void WrapI_IIII() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(u32, int, int, int)> void WrapI_UIII() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(int, int, int, u32, int)> void WrapI_IIIUI() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<int func(int, u32, u32, int, int)> void WrapI_IUUII() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<int func(int, const char *, int, u32, u32)> void WrapI_ICIUU() {
- int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<int func(int, int, u32)> void WrapI_IIU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<void func(int, u32)> void WrapV_IU() {
- func(PARAM(0), PARAM(1));
-}
-
-template<void func(u32, int)> void WrapV_UI() {
- func(PARAM(0), PARAM(1));
-}
-
-template<u32 func(const char *)> void WrapU_C() {
- u32 retval = func(Memory::GetCharPointer(PARAM(0)));
- RETURN(retval);
-}
-
-template<u32 func(const char *, const char *, const char *, u32)> void WrapU_CCCU() {
- u32 retval = func(Memory::GetCharPointer(PARAM(0)),
- Memory::GetCharPointer(PARAM(1)), Memory::GetCharPointer(PARAM(2)),
- PARAM(3));
- RETURN(retval);
-}
-
-template<int func(const char *)> void WrapI_C() {
- int retval = func(Memory::GetCharPointer(PARAM(0)));
- RETURN(retval);
-}
-
-template<int func(const char *, u32)> void WrapI_CU() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1));
- RETURN(retval);
-}
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Function wrappers that return type u32
-template<int func(const char *, u32, int)> void WrapI_CUI() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(int, const char *, int, u32)> void WrapI_ICIU() {
- int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(const char *, int, u32)> void WrapI_CIU() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(const char *, u32, u32)> void WrapI_CUU() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(const char *, u32, u32, u32)> void WrapI_CUUU() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
- PARAM(3));
- RETURN(retval);
-}
-
-template<int func(const char *, const char*, int, int)> void WrapI_CCII() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(const char *, u32, u32, int, u32, u32)> void WrapI_CUUIUU() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
- PARAM(3), PARAM(4), PARAM(5));
- RETURN(retval);
-}
-
-template<int func(const char *, int, int, u32, int, int)> void WrapI_CIIUII() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
- PARAM(3), PARAM(4), PARAM(5));
- RETURN(retval);
-}
-
-template<int func(const char *, int, u32, u32, u32)> void WrapI_CIUUU() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
- PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<int func(const char *, u32, u32, u32, u32, u32)> void WrapI_CUUUUU() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
- PARAM(3), PARAM(4), PARAM(5));
- RETURN(retval);
-}
-
-template<u32 func(const char *, u32)> void WrapU_CU() {
- u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1));
- RETURN((u32) retval);
-}
-
-template<u32 func(u32, const char *)> void WrapU_UC() {
- u32 retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)));
- RETURN(retval);
-}
-
-template<u32 func(const char *, u32, u32)> void WrapU_CUU() {
- u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
- RETURN((u32) retval);
-}
-
-template<u32 func(int, int, int)> void WrapU_III() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<u32 func(int, int)> void WrapU_II() {
- u32 retval = func(PARAM(0), PARAM(1));
- RETURN(retval);
-}
-
-template<u32 func(int, int, int, int)> void WrapU_IIII() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<u32 func(int, u32, u32)> void WrapU_IUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<u32 func(int, u32, u32, u32)> void WrapU_IUUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<u32 func(int, u32, u32, u32, u32)> void WrapU_IUUUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32, u32)> void WrapU_UUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<void func(int, u32, u32)> void WrapV_IUU() {
- func(PARAM(0), PARAM(1), PARAM(2));
-}
-
-template<void func(int, int, u32)> void WrapV_IIU() {
- func(PARAM(0), PARAM(1), PARAM(2));
-}
-
-template<void func(u32, int, u32)> void WrapV_UIU() {
- func(PARAM(0), PARAM(1), PARAM(2));
-}
-
-template<int func(u32, int, u32)> void WrapI_UIU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<void func(int, u32, u32, u32, u32)> void WrapV_IUUUU() {
- func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
-}
-
-template<void func(u32, u32, u32)> void WrapV_UUU() {
- func(PARAM(0), PARAM(1), PARAM(2));
-}
-
-template<void func(u32, u32, u32, u32)> void WrapV_UUUU() {
- func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
-}
-
-template<void func(const char *, u32, int, u32)> void WrapV_CUIU() {
- func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
-}
-
-template<int func(const char *, u32, int, u32)> void WrapI_CUIU() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<void func(u32, const char *, u32, int, u32)> void WrapV_UCUIU() {
- func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3),
- PARAM(4));
-}
-
-template<int func(u32, const char *, u32, int, u32)> void WrapI_UCUIU() {
- int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2),
- PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<void func(const char *, u32, int, int, u32)> void WrapV_CUIIU() {
- func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3),
- PARAM(4));
-}
-
-template<int func(const char *, u32, int, int, u32)> void WrapI_CUIIU() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
- PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32, u32, u32)> void WrapU_UUUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<u32 func(u32, const char *, u32, u32)> void WrapU_UCUU() {
- u32 retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32, u32, int)> void WrapU_UUUI() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32, u32, int, u32)> void WrapU_UUUIU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32, u32, int, u32, int)> void WrapU_UUUIUI() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32, int, u32)> void WrapU_UUIU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<u32 func(u32, int, int, int)> void WrapU_UIII() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(int, u32, u32, u32, u32)> void WrapI_IUUUU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<int func(int, u32, u32, u32, u32, u32)> void WrapI_IUUUUU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
- RETURN(retval);
+template<u32 func()> void Wrap() {
+ RETURN(func());
}
-template<int func(int, u32, int, int)> void WrapI_IUII() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-template<u32 func(u32, u32, u32, u32, u32)> void WrapU_UUUUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
- RETURN(retval);
-}
+////////////////////////////////////////////////////////////////////////////////////////////////////
+/// Function wrappers that return type void
-template<void func(u32, u32, u32, u32, u32)> void WrapV_UUUUU() {
- func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
+template<void func(s64)> void Wrap() {
+ func(((s64)PARAM(1) << 32) | PARAM(0));
}
-template<u32 func(const char *, const char *)> void WrapU_CC() {
- int retval = func(Memory::GetCharPointer(PARAM(0)),
- Memory::GetCharPointer(PARAM(1)));
- RETURN(retval);
-}
-
-template<void func(const char*)> void WrapV_C() {
+template<void func(const char*)> void Wrap() {
func(Memory::GetCharPointer(PARAM(0)));
}
-template<void func(const char *, int)> void WrapV_CI() {
- func(Memory::GetCharPointer(PARAM(0)), PARAM(1));
-}
-
-template<u32 func(const char *, int)> void WrapU_CI() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1));
- RETURN(retval);
-}
-
-template<u32 func(const char *, int, int)> void WrapU_CII() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(const char *, int, u32, int, u32)> void WrapU_CIUIU() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
- PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template<u32 func(const char *, int, u32, int, u32, int)> void WrapU_CIUIUI() {
- u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
- PARAM(3), PARAM(4), PARAM(5));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32, u32, u32, u32, u32)> void WrapU_UUUUUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4),
- PARAM(5));
- RETURN(retval);
-}
-
-template<int func(int, u32, u32, u32)> void WrapI_IUUU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(int, u32, u32)> void WrapI_IUU() {
- int retval = func(PARAM(0), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template<u32 func(u32, u32, u32, u32, u32, u32, u32)> void WrapU_UUUUUUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
- RETURN(retval);
-}
+#undef PARAM
+#undef RETURN
-template<int func(u32, int, u32, u32)> void WrapI_UIUU() {
- u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(int, const char *)> void WrapI_IC() {
- int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)));
- RETURN(retval);
-}
-
-template <int func(int, const char *, const char *, u32, int)> void WrapI_ICCUI() {
- int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), Memory::GetCharPointer(PARAM(2)), PARAM(3), PARAM(4));
- RETURN(retval);
-}
-
-template <int func(int, const char *, const char *, int)> void WrapI_ICCI() {
- int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), Memory::GetCharPointer(PARAM(2)), PARAM(3));
- RETURN(retval);
-}
-
-template <int func(const char *, int, int)> void WrapI_CII() {
- int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
- RETURN(retval);
-}
-
-template <int func(int, const char *, int)> void WrapI_ICI() {
- int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2));
- RETURN(retval);
-}
-
-template<int func(int, void *, void *, void *, void *, u32, int)> void WrapI_IVVVVUI(){
- u32 retval = func(PARAM(0), Memory::GetPointer(PARAM(1)), Memory::GetPointer(PARAM(2)), Memory::GetPointer(PARAM(3)), Memory::GetPointer(PARAM(4)), PARAM(5), PARAM(6) );
- RETURN(retval);
-}
-
-template<int func(int, const char *, u32, void *, int, int, int)> void WrapI_ICUVIII(){
- u32 retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), Memory::GetPointer(PARAM(3)), PARAM(4), PARAM(5), PARAM(6));
- RETURN(retval);
-}
-
-template<int func(void*, u32)> void WrapI_VU(){
- u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1));
- RETURN(retval);
-}
-
-template<int func(void*, u32, void*, int)> void WrapI_VUVI(){
- u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), PARAM(3));
- RETURN(retval);
-}
-
-template<int func(void*, u32, u32, u32, u32, u32)> void WrapI_VUUUUU(){
- u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
- RETURN(retval);
-}
-
-template<int func(u32, s64)> void WrapI_US64() {
- int retval = func(PARAM(0), PARAM64(2));
- RETURN(retval);
-}
+} // namespace HLE
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index be151665b..53cda4a61 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -6,7 +6,8 @@
#include "core/mem_map.h"
#include "core/hle/hle.h"
-#include "core/hle/syscall.h"
+#include "core/hle/svc.h"
+#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,17 +16,19 @@ namespace HLE {
static std::vector<ModuleDef> g_module_db;
-const FunctionDef* GetSyscallInfo(u32 opcode) {
+bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a new thread
+
+const FunctionDef* GetSVCInfo(u32 opcode) {
u32 func_num = opcode & 0xFFFFFF; // 8 bits
if (func_num > 0xFF) {
- ERROR_LOG(HLE,"Unknown syscall: 0x%02X", func_num);
- return NULL;
+ ERROR_LOG(HLE,"unknown svc=0x%02X", func_num);
+ return nullptr;
}
return &g_module_db[0].func_table[func_num];
}
-void CallSyscall(u32 opcode) {
- const FunctionDef *info = GetSyscallInfo(opcode);
+void CallSVC(u32 opcode) {
+ const FunctionDef *info = GetSVCInfo(opcode);
if (!info) {
return;
@@ -33,17 +36,25 @@ void CallSyscall(u32 opcode) {
if (info->func) {
info->func();
} else {
- ERROR_LOG(HLE, "Unimplemented SysCall function %s(..)", info->name.c_str());
+ ERROR_LOG(HLE, "unimplemented SVC function %s(..)", info->name.c_str());
}
}
+void Reschedule(const char *reason) {
+#ifdef _DEBUG
+ _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
+#endif
+ Core::g_app_core->PrepareReschedule();
+ g_reschedule = true;
+}
+
void RegisterModule(std::string name, int num_functions, const FunctionDef* func_table) {
ModuleDef module = {name, num_functions, func_table};
g_module_db.push_back(module);
}
void RegisterAllModules() {
- Syscall::Register();
+ SVC::Register();
}
void Init() {
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index 42f37e29c..bf4d84575 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -9,14 +9,10 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
-#define PARAM(n) Core::g_app_core->GetReg(n)
-#define PARAM64(n) (Core::g_app_core->GetReg(n) | ((u64)Core::g_app_core->GetReg(n + 1) << 32))
-#define RETURN(n) Core::g_app_core->SetReg(0, n)
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
namespace HLE {
+extern bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread
+
typedef u32 Addr;
typedef void (*Func)();
@@ -34,7 +30,9 @@ struct ModuleDef {
void RegisterModule(std::string name, int num_functions, const FunctionDef *func_table);
-void CallSyscall(u32 opcode);
+void CallSVC(u32 opcode);
+
+void Reschedule(const char *reason);
void Init();
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
new file mode 100644
index 000000000..127c0cfc6
--- /dev/null
+++ b/src/core/hle/kernel/event.cpp
@@ -0,0 +1,159 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <map>
+#include <algorithm>
+#include <vector>
+
+#include "common/common.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Event : public Object {
+public:
+ const char* GetTypeName() const { return "Event"; }
+ const char* GetName() const { return name.c_str(); }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; }
+ Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Event; }
+
+ ResetType intitial_reset_type; ///< ResetType specified at Event initialization
+ ResetType reset_type; ///< Current ResetType
+
+ bool locked; ///< Event signal wait
+ bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough)
+ std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
+ std::string name; ///< Name of event (optional)
+
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result WaitSynchronization(bool* wait) {
+ *wait = locked;
+ if (locked) {
+ Handle thread = GetCurrentThreadHandle();
+ if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
+ waiting_threads.push_back(thread);
+ }
+ Kernel::WaitCurrentThread(WAITTYPE_EVENT);
+ }
+ if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
+ locked = true;
+ }
+ return 0;
+ }
+};
+
+/**
+ * Hackish function to set an events permanent lock state, used to pass through synch blocks
+ * @param handle Handle to event to change
+ * @param permanent_locked Boolean permanent locked value to set event
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SetPermanentLock(Handle handle, const bool permanent_locked) {
+ Event* evt = g_object_pool.GetFast<Event>(handle);
+ _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+
+ evt->permanent_locked = permanent_locked;
+ return 0;
+}
+
+/**
+ * Changes whether an event is locked or not
+ * @param handle Handle to event to change
+ * @param locked Boolean locked value to set event
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SetEventLocked(const Handle handle, const bool locked) {
+ Event* evt = g_object_pool.GetFast<Event>(handle);
+ _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+
+ if (!evt->permanent_locked) {
+ evt->locked = locked;
+ }
+ return 0;
+}
+
+/**
+ * Signals an event
+ * @param handle Handle to event to signal
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SignalEvent(const Handle handle) {
+ Event* evt = g_object_pool.GetFast<Event>(handle);
+ _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+
+ // Resume threads waiting for event to signal
+ bool event_caught = false;
+ for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
+ ResumeThreadFromWait( evt->waiting_threads[i]);
+
+ // If any thread is signalled awake by this event, assume the event was "caught" and reset
+ // the event. This will result in the next thread waiting on the event to block. Otherwise,
+ // the event will not be reset, and the next thread to call WaitSynchronization on it will
+ // not block. Not sure if this is correct behavior, but it seems to work.
+ event_caught = true;
+ }
+ evt->waiting_threads.clear();
+
+ if (!evt->permanent_locked) {
+ evt->locked = event_caught;
+ }
+ return 0;
+}
+
+/**
+ * Clears an event
+ * @param handle Handle to event to clear
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result ClearEvent(Handle handle) {
+ Event* evt = g_object_pool.GetFast<Event>(handle);
+ _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+
+ if (!evt->permanent_locked) {
+ evt->locked = true;
+ }
+ return 0;
+}
+
+/**
+ * Creates an event
+ * @param handle Reference to handle for the newly created mutex
+ * @param reset_type ResetType describing how to create event
+ * @param name Optional name of event
+ * @return Newly created Event object
+ */
+Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) {
+ Event* evt = new Event;
+
+ handle = Kernel::g_object_pool.Create(evt);
+
+ evt->locked = true;
+ evt->permanent_locked = false;
+ evt->reset_type = evt->intitial_reset_type = reset_type;
+ evt->name = name;
+
+ return evt;
+}
+
+/**
+ * Creates an event
+ * @param reset_type ResetType describing how to create event
+ * @param name Optional name of event
+ * @return Handle to newly created Event object
+ */
+Handle CreateEvent(const ResetType reset_type, const std::string& name) {
+ Handle handle;
+ Event* evt = CreateEvent(handle, reset_type, name);
+ return handle;
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
new file mode 100644
index 000000000..c39b33180
--- /dev/null
+++ b/src/core/hle/kernel/event.h
@@ -0,0 +1,52 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/svc.h"
+
+namespace Kernel {
+
+/**
+ * Changes whether an event is locked or not
+ * @param handle Handle to event to change
+ * @param locked Boolean locked value to set event
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SetEventLocked(const Handle handle, const bool locked);
+
+/**
+ * Hackish function to set an events permanent lock state, used to pass through synch blocks
+ * @param handle Handle to event to change
+ * @param permanent_locked Boolean permanent locked value to set event
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SetPermanentLock(Handle handle, const bool permanent_locked);
+
+/**
+ * Signals an event
+ * @param handle Handle to event to signal
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SignalEvent(const Handle handle);
+
+/**
+ * Clears an event
+ * @param handle Handle to event to clear
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result ClearEvent(Handle handle);
+
+/**
+ * Creates an event
+ * @param reset_type ResetType describing how to create event
+ * @param name Optional name of event
+ * @return Handle to newly created Event object
+ */
+Handle CreateEvent(const ResetType reset_type, const std::string& name="Unknown");
+
+} // namespace
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
new file mode 100644
index 000000000..cda183add
--- /dev/null
+++ b/src/core/hle/kernel/kernel.cpp
@@ -0,0 +1,161 @@
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <string.h>
+
+#include "common/common.h"
+
+#include "core/core.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+Handle g_main_thread = 0;
+ObjectPool g_object_pool;
+
+ObjectPool::ObjectPool() {
+ memset(occupied, 0, sizeof(bool) * MAX_COUNT);
+ next_id = INITIAL_NEXT_ID;
+}
+
+Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) {
+ if (range_top > MAX_COUNT) {
+ range_top = MAX_COUNT;
+ }
+ if (next_id >= range_bottom && next_id < range_top) {
+ range_bottom = next_id++;
+ }
+ for (int i = range_bottom; i < range_top; i++) {
+ if (!occupied[i]) {
+ occupied[i] = true;
+ pool[i] = obj;
+ pool[i]->handle = i + HANDLE_OFFSET;
+ return i + HANDLE_OFFSET;
+ }
+ }
+ ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use.");
+ return 0;
+}
+
+bool ObjectPool::IsValid(Handle handle) {
+ int index = handle - HANDLE_OFFSET;
+ if (index < 0)
+ return false;
+ if (index >= MAX_COUNT)
+ return false;
+
+ return occupied[index];
+}
+
+void ObjectPool::Clear() {
+ for (int i = 0; i < MAX_COUNT; i++) {
+ //brutally clear everything, no validation
+ if (occupied[i])
+ delete pool[i];
+ occupied[i] = false;
+ }
+ memset(pool, 0, sizeof(Object*)*MAX_COUNT);
+ next_id = INITIAL_NEXT_ID;
+}
+
+Object* &ObjectPool::operator [](Handle handle)
+{
+ _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
+ return pool[handle - HANDLE_OFFSET];
+}
+
+void ObjectPool::List() {
+ for (int i = 0; i < MAX_COUNT; i++) {
+ if (occupied[i]) {
+ if (pool[i]) {
+ INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName(),
+ pool[i]->GetName());
+ }
+ }
+ }
+}
+
+int ObjectPool::GetCount() {
+ int count = 0;
+ for (int i = 0; i < MAX_COUNT; i++) {
+ if (occupied[i])
+ count++;
+ }
+ return count;
+}
+
+Object* ObjectPool::CreateByIDType(int type) {
+ // Used for save states. This is ugly, but what other way is there?
+ switch (type) {
+ //case SCE_KERNEL_TMID_Alarm:
+ // return __KernelAlarmObject();
+ //case SCE_KERNEL_TMID_EventFlag:
+ // return __KernelEventFlagObject();
+ //case SCE_KERNEL_TMID_Mbox:
+ // return __KernelMbxObject();
+ //case SCE_KERNEL_TMID_Fpl:
+ // return __KernelMemoryFPLObject();
+ //case SCE_KERNEL_TMID_Vpl:
+ // return __KernelMemoryVPLObject();
+ //case PPSSPP_KERNEL_TMID_PMB:
+ // return __KernelMemoryPMBObject();
+ //case PPSSPP_KERNEL_TMID_Module:
+ // return __KernelModuleObject();
+ //case SCE_KERNEL_TMID_Mpipe:
+ // return __KernelMsgPipeObject();
+ //case SCE_KERNEL_TMID_Mutex:
+ // return __KernelMutexObject();
+ //case SCE_KERNEL_TMID_LwMutex:
+ // return __KernelLwMutexObject();
+ //case SCE_KERNEL_TMID_Semaphore:
+ // return __KernelSemaphoreObject();
+ //case SCE_KERNEL_TMID_Callback:
+ // return __KernelCallbackObject();
+ //case SCE_KERNEL_TMID_Thread:
+ // return __KernelThreadObject();
+ //case SCE_KERNEL_TMID_VTimer:
+ // return __KernelVTimerObject();
+ //case SCE_KERNEL_TMID_Tlspl:
+ // return __KernelTlsplObject();
+ //case PPSSPP_KERNEL_TMID_File:
+ // return __KernelFileNodeObject();
+ //case PPSSPP_KERNEL_TMID_DirList:
+ // return __KernelDirListingObject();
+
+ default:
+ ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type);
+ return nullptr;
+ }
+}
+
+/// Initialize the kernel
+void Init() {
+ Kernel::ThreadingInit();
+}
+
+/// Shutdown the kernel
+void Shutdown() {
+ Kernel::ThreadingShutdown();
+
+ g_object_pool.Clear(); // Free all kernel objects
+}
+
+/**
+ * Loads executable stored at specified address
+ * @entry_point Entry point in memory of loaded executable
+ * @return True on success, otherwise false
+ */
+bool LoadExec(u32 entry_point) {
+ Init();
+
+ Core::g_app_core->SetPC(entry_point);
+
+ // 0x30 is the typical main thread priority I've seen used so far
+ g_main_thread = Kernel::SetupMainThread(0x30);
+
+ return true;
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
new file mode 100644
index 000000000..3f15da0ac
--- /dev/null
+++ b/src/core/hle/kernel/kernel.h
@@ -0,0 +1,183 @@
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common.h"
+
+typedef u32 Handle;
+typedef s32 Result;
+
+namespace Kernel {
+
+enum KernelHandle {
+ CurrentThread = 0xFFFF8000,
+ CurrentProcess = 0xFFFF8001,
+};
+
+enum class HandleType : u32 {
+ Unknown = 0,
+ Port = 1,
+ Service = 2,
+ Event = 3,
+ Mutex = 4,
+ SharedMemory = 5,
+ Redirection = 6,
+ Thread = 7,
+ Process = 8,
+ Arbiter = 9,
+ File = 10,
+ Semaphore = 11,
+};
+
+enum {
+ MAX_NAME_LENGTH = 0x100,
+ DEFAULT_STACK_SIZE = 0x4000,
+};
+
+class ObjectPool;
+
+class Object : NonCopyable {
+ friend class ObjectPool;
+ u32 handle;
+public:
+ virtual ~Object() {}
+ Handle GetHandle() const { return handle; }
+ virtual const char* GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; }
+ virtual const char* GetName() const { return "[UNKNOWN KERNEL OBJECT]"; }
+ virtual Kernel::HandleType GetHandleType() const = 0;
+
+ /**
+ * Synchronize kernel object
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ virtual Result SyncRequest(bool* wait) {
+ ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
+ return -1;
+ }
+
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ virtual Result WaitSynchronization(bool* wait) = 0;
+};
+
+class ObjectPool : NonCopyable {
+public:
+ ObjectPool();
+ ~ObjectPool() {}
+
+ // Allocates a handle within the range and inserts the object into the map.
+ Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF);
+
+ static Object* CreateByIDType(int type);
+
+ template <class T>
+ u32 Destroy(Handle handle) {
+ u32 error;
+ if (Get<T>(handle, error)) {
+ occupied[handle - HANDLE_OFFSET] = false;
+ delete pool[handle - HANDLE_OFFSET];
+ }
+ return error;
+ };
+
+ bool IsValid(Handle handle);
+
+ template <class T>
+ T* Get(Handle handle, u32& outError) {
+ if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
+ // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP
+ if (handle != 0 && (u32)handle != 0x80020001) {
+ WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+ }
+ outError = 0;//T::GetMissingErrorCode();
+ return 0;
+ } else {
+ // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally,
+ // it just acted as a static case and everything worked. This means that we will never
+ // see the Wrong type object error below, but we'll just have to live with that danger.
+ T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]);
+ if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) {
+ WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
+ outError = 0;//T::GetMissingErrorCode();
+ return 0;
+ }
+ outError = 0;//SCE_KERNEL_ERROR_OK;
+ return t;
+ }
+ }
+
+ // ONLY use this when you know the handle is valid.
+ template <class T>
+ T *GetFast(Handle handle) {
+ const Handle realHandle = handle - HANDLE_OFFSET;
+ _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
+ return static_cast<T*>(pool[realHandle]);
+ }
+
+ template <class T, typename ArgT>
+ void Iterate(bool func(T*, ArgT), ArgT arg) {
+ int type = T::GetStaticIDType();
+ for (int i = 0; i < MAX_COUNT; i++)
+ {
+ if (!occupied[i])
+ continue;
+ T* t = static_cast<T*>(pool[i]);
+ if (t->GetIDType() == type) {
+ if (!func(t, arg))
+ break;
+ }
+ }
+ }
+
+ bool GetIDType(Handle handle, HandleType* type) const {
+ if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
+ !occupied[handle - HANDLE_OFFSET]) {
+ ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+ return false;
+ }
+ Object* t = pool[handle - HANDLE_OFFSET];
+ *type = t->GetHandleType();
+ return true;
+ }
+
+ Object* &operator [](Handle handle);
+ void List();
+ void Clear();
+ int GetCount();
+
+private:
+
+ enum {
+ MAX_COUNT = 0x1000,
+ HANDLE_OFFSET = 0x100,
+ INITIAL_NEXT_ID = 0x10,
+ };
+
+ Object* pool[MAX_COUNT];
+ bool occupied[MAX_COUNT];
+ int next_id;
+};
+
+extern ObjectPool g_object_pool;
+extern Handle g_main_thread;
+
+/// Initialize the kernel
+void Init();
+
+/// Shutdown the kernel
+void Shutdown();
+
+/**
+ * Loads executable stored at specified address
+ * @entry_point Entry point in memory of loaded executable
+ * @return True on success, otherwise false
+ */
+bool LoadExec(u32 entry_point);
+
+} // namespace
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
new file mode 100644
index 000000000..1ccf1eb73
--- /dev/null
+++ b/src/core/hle/kernel/mutex.cpp
@@ -0,0 +1,170 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <map>
+#include <vector>
+
+#include "common/common.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Mutex : public Object {
+public:
+ const char* GetTypeName() const { return "Mutex"; }
+ const char* GetName() const { return name.c_str(); }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; }
+ Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; }
+
+ bool initial_locked; ///< Initial lock state when mutex was created
+ bool locked; ///< Current locked state
+ Handle lock_thread; ///< Handle to thread that currently has mutex
+ std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
+ std::string name; ///< Name of mutex (optional)
+
+ /**
+ * Synchronize kernel object
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result SyncRequest(bool* wait) {
+ // TODO(bunnei): ImplementMe
+ locked = true;
+ return 0;
+ }
+
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result WaitSynchronization(bool* wait) {
+ // TODO(bunnei): ImplementMe
+ *wait = locked;
+
+ if (locked) {
+ Kernel::WaitCurrentThread(WAITTYPE_MUTEX);
+ }
+
+ return 0;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+typedef std::multimap<Handle, Handle> MutexMap;
+static MutexMap g_mutex_held_locks;
+
+void MutexAcquireLock(Mutex* mutex, Handle thread) {
+ g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
+ mutex->lock_thread = thread;
+}
+
+void MutexAcquireLock(Mutex* mutex) {
+ Handle thread = GetCurrentThreadHandle();
+ MutexAcquireLock(mutex, thread);
+}
+
+void MutexEraseLock(Mutex* mutex) {
+ Handle handle = mutex->GetHandle();
+ auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
+ for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
+ if ((*iter).second == handle) {
+ g_mutex_held_locks.erase(iter);
+ break;
+ }
+ }
+ mutex->lock_thread = -1;
+}
+
+bool LockMutex(Mutex* mutex) {
+ // Mutex alread locked?
+ if (mutex->locked) {
+ return false;
+ }
+ MutexAcquireLock(mutex);
+ return true;
+}
+
+bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
+ MutexAcquireLock(mutex, thread);
+ Kernel::ResumeThreadFromWait(thread);
+ return true;
+}
+
+bool ReleaseMutex(Mutex* mutex) {
+ MutexEraseLock(mutex);
+ bool woke_threads = false;
+
+ // Find the next waiting thread for the mutex...
+ while (!woke_threads && !mutex->waiting_threads.empty()) {
+ std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
+ woke_threads |= ReleaseMutexForThread(mutex, *iter);
+ mutex->waiting_threads.erase(iter);
+ }
+ // Reset mutex lock thread handle, nothing is waiting
+ if (!woke_threads) {
+ mutex->locked = false;
+ mutex->lock_thread = -1;
+ }
+ return woke_threads;
+}
+
+/**
+ * Releases a mutex
+ * @param handle Handle to mutex to release
+ */
+Result ReleaseMutex(Handle handle) {
+ Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
+
+ _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!");
+
+ if (!ReleaseMutex(mutex)) {
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Creates a mutex
+ * @param handle Reference to handle for the newly created mutex
+ * @param initial_locked Specifies if the mutex should be locked initially
+ * @param name Optional name of mutex
+ * @return Pointer to new Mutex object
+ */
+Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) {
+ Mutex* mutex = new Mutex;
+ handle = Kernel::g_object_pool.Create(mutex);
+
+ mutex->locked = mutex->initial_locked = initial_locked;
+ mutex->name = name;
+
+ // Acquire mutex with current thread if initialized as locked...
+ if (mutex->locked) {
+ MutexAcquireLock(mutex);
+
+ // Otherwise, reset lock thread handle
+ } else {
+ mutex->lock_thread = -1;
+ }
+ return mutex;
+}
+
+/**
+ * Creates a mutex
+ * @param initial_locked Specifies if the mutex should be locked initially
+ * @param name Optional name of mutex
+ * @return Handle to newly created object
+ */
+Handle CreateMutex(bool initial_locked, const std::string& name) {
+ Handle handle;
+ Mutex* mutex = CreateMutex(handle, initial_locked, name);
+ return handle;
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
new file mode 100644
index 000000000..7d7b5137e
--- /dev/null
+++ b/src/core/hle/kernel/mutex.h
@@ -0,0 +1,28 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+/**
+ * Releases a mutex
+ * @param handle Handle to mutex to release
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result ReleaseMutex(Handle handle);
+
+/**
+ * Creates a mutex
+ * @param initial_locked Specifies if the mutex should be locked initially
+ * @param name Optional name of mutex
+ * @return Handle to newly created object
+ */
+Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
+
+} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
new file mode 100644
index 000000000..ab5a5559e
--- /dev/null
+++ b/src/core/hle/kernel/thread.cpp
@@ -0,0 +1,436 @@
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <stdio.h>
+
+#include <list>
+#include <algorithm>
+#include <vector>
+#include <map>
+#include <string>
+
+#include "common/common.h"
+#include "common/thread_queue_list.h"
+
+#include "core/core.h"
+#include "core/mem_map.h"
+#include "core/hle/hle.h"
+#include "core/hle/svc.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Thread : public Kernel::Object {
+public:
+
+ const char* GetName() const { return name; }
+ const char* GetTypeName() const { return "Thread"; }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; }
+ Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; }
+
+ inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
+ inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
+ inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
+ inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
+ inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
+
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result WaitSynchronization(bool* wait) {
+ if (status != THREADSTATUS_DORMANT) {
+ Handle thread = GetCurrentThreadHandle();
+ if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
+ waiting_threads.push_back(thread);
+ }
+ WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
+ *wait = true;
+ }
+ return 0;
+ }
+
+ ThreadContext context;
+
+ u32 status;
+ u32 entry_point;
+ u32 stack_top;
+ u32 stack_size;
+
+ s32 initial_priority;
+ s32 current_priority;
+
+ s32 processor_id;
+
+ WaitType wait_type;
+ Handle wait_handle;
+
+ std::vector<Handle> waiting_threads;
+
+ char name[Kernel::MAX_NAME_LENGTH + 1];
+};
+
+// Lists all thread ids that aren't deleted/etc.
+std::vector<Handle> g_thread_queue;
+
+// Lists only ready thread ids.
+Common::ThreadQueueList<Handle> g_thread_ready_queue;
+
+Handle g_current_thread_handle;
+Thread* g_current_thread;
+
+/// Gets the current thread
+inline Thread* GetCurrentThread() {
+ return g_current_thread;
+}
+
+/// Gets the current thread handle
+Handle GetCurrentThreadHandle() {
+ return GetCurrentThread()->GetHandle();
+}
+
+/// Sets the current thread
+inline void SetCurrentThread(Thread* t) {
+ g_current_thread = t;
+ g_current_thread_handle = t->GetHandle();
+}
+
+/// Saves the current CPU context
+void SaveContext(ThreadContext& ctx) {
+ Core::g_app_core->SaveContext(ctx);
+}
+
+/// Loads a CPU context
+void LoadContext(ThreadContext& ctx) {
+ Core::g_app_core->LoadContext(ctx);
+}
+
+/// Resets a thread
+void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
+ memset(&t->context, 0, sizeof(ThreadContext));
+
+ t->context.cpu_registers[0] = arg;
+ t->context.pc = t->context.reg_15 = t->entry_point;
+ t->context.sp = t->stack_top;
+ t->context.cpsr = 0x1F; // Usermode
+
+ if (t->current_priority < lowest_priority) {
+ t->current_priority = t->initial_priority;
+ }
+ t->wait_type = WAITTYPE_NONE;
+ t->wait_handle = 0;
+}
+
+/// Change a thread to "ready" state
+void ChangeReadyState(Thread* t, bool ready) {
+ Handle handle = t->GetHandle();
+ if (t->IsReady()) {
+ if (!ready) {
+ g_thread_ready_queue.remove(t->current_priority, handle);
+ }
+ } else if (ready) {
+ if (t->IsRunning()) {
+ g_thread_ready_queue.push_front(t->current_priority, handle);
+ } else {
+ g_thread_ready_queue.push_back(t->current_priority, handle);
+ }
+ t->status = THREADSTATUS_READY;
+ }
+}
+
+/// Verify that a thread has not been released from waiting
+inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) {
+ Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
+
+ if (type != thread->wait_type || wait_handle != thread->wait_handle)
+ return false;
+
+ return true;
+}
+
+/// Stops the current thread
+void StopThread(Handle handle, const char* reason) {
+ Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
+
+ ChangeReadyState(thread, false);
+ thread->status = THREADSTATUS_DORMANT;
+ for (size_t i = 0; i < thread->waiting_threads.size(); ++i) {
+ const Handle waiting_thread = thread->waiting_threads[i];
+ if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
+ ResumeThreadFromWait(waiting_thread);
+ }
+ }
+ thread->waiting_threads.clear();
+
+ // Stopped threads are never waiting.
+ thread->wait_type = WAITTYPE_NONE;
+ thread->wait_handle = 0;
+}
+
+/// Changes a threads state
+void ChangeThreadState(Thread* t, ThreadStatus new_status) {
+ if (!t || t->status == new_status) {
+ return;
+ }
+ ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
+ t->status = new_status;
+
+ if (new_status == THREADSTATUS_WAIT) {
+ if (t->wait_type == WAITTYPE_NONE) {
+ ERROR_LOG(KERNEL, "Waittype none not allowed");
+ }
+ }
+}
+
+/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
+void CallThread(Thread* t) {
+ // Stop waiting
+ if (t->wait_type != WAITTYPE_NONE) {
+ t->wait_type = WAITTYPE_NONE;
+ }
+ ChangeThreadState(t, THREADSTATUS_READY);
+}
+
+/// Switches CPU context to that of the specified thread
+void SwitchContext(Thread* t) {
+ Thread* cur = GetCurrentThread();
+
+ // Save context for current thread
+ if (cur) {
+ SaveContext(cur->context);
+
+ if (cur->IsRunning()) {
+ ChangeReadyState(cur, true);
+ }
+ }
+ // Load context of new thread
+ if (t) {
+ SetCurrentThread(t);
+ ChangeReadyState(t, false);
+ t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY;
+ t->wait_type = WAITTYPE_NONE;
+ LoadContext(t->context);
+ } else {
+ SetCurrentThread(nullptr);
+ }
+}
+
+/// Gets the next thread that is ready to be run by priority
+Thread* NextThread() {
+ Handle next;
+ Thread* cur = GetCurrentThread();
+
+ if (cur && cur->IsRunning()) {
+ next = g_thread_ready_queue.pop_first_better(cur->current_priority);
+ } else {
+ next = g_thread_ready_queue.pop_first();
+ }
+ if (next == 0) {
+ return nullptr;
+ }
+ return Kernel::g_object_pool.GetFast<Thread>(next);
+}
+
+/// Puts the current thread in the wait state for the given type
+void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
+ Thread* thread = GetCurrentThread();
+ thread->wait_type = wait_type;
+ thread->wait_handle = wait_handle;
+ ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
+}
+
+/// Resumes a thread from waiting by marking it as "ready"
+void ResumeThreadFromWait(Handle handle) {
+ u32 error;
+ Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
+ if (thread) {
+ thread->status &= ~THREADSTATUS_WAIT;
+ if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
+ ChangeReadyState(thread, true);
+ }
+ }
+}
+
+/// Prints the thread queue for debugging purposes
+void DebugThreadQueue() {
+ Thread* thread = GetCurrentThread();
+ if (!thread) {
+ return;
+ }
+ INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
+ for (u32 i = 0; i < g_thread_queue.size(); i++) {
+ Handle handle = g_thread_queue[i];
+ s32 priority = g_thread_ready_queue.contains(handle);
+ if (priority != -1) {
+ INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
+ }
+ }
+}
+
+/// Creates a new thread
+Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
+ s32 processor_id, u32 stack_top, int stack_size) {
+
+ _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
+ "CreateThread priority=%d, outside of allowable range!", priority)
+
+ Thread* thread = new Thread;
+
+ handle = Kernel::g_object_pool.Create(thread);
+
+ g_thread_queue.push_back(handle);
+ g_thread_ready_queue.prepare(priority);
+
+ thread->status = THREADSTATUS_DORMANT;
+ thread->entry_point = entry_point;
+ thread->stack_top = stack_top;
+ thread->stack_size = stack_size;
+ thread->initial_priority = thread->current_priority = priority;
+ thread->processor_id = processor_id;
+ thread->wait_type = WAITTYPE_NONE;
+ thread->wait_handle = 0;
+
+ strncpy(thread->name, name, Kernel::MAX_NAME_LENGTH);
+ thread->name[Kernel::MAX_NAME_LENGTH] = '\0';
+
+ return thread;
+}
+
+/// Creates a new thread - wrapper for external user
+Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
+ u32 stack_top, int stack_size) {
+
+ if (name == nullptr) {
+ ERROR_LOG(KERNEL, "CreateThread(): nullptr name");
+ return -1;
+ }
+ if ((u32)stack_size < 0x200) {
+ ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name,
+ stack_size);
+ return -1;
+ }
+ if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
+ s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
+ WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X",
+ name, priority, new_priority);
+ // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
+ // validity of this
+ priority = new_priority;
+ }
+ if (!Memory::GetPointer(entry_point)) {
+ ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point);
+ return -1;
+ }
+ Handle handle;
+ Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
+ stack_size);
+
+ ResetThread(thread, arg, 0);
+ CallThread(thread);
+
+ return handle;
+}
+
+/// Get the priority of the thread specified by handle
+u32 GetThreadPriority(const Handle handle) {
+ Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
+ return thread->current_priority;
+}
+
+/// Set the priority of the thread specified by handle
+Result SetThreadPriority(Handle handle, s32 priority) {
+ Thread* thread = nullptr;
+ if (!handle) {
+ thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
+ } else {
+ thread = g_object_pool.GetFast<Thread>(handle);
+ }
+ _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
+
+ // If priority is invalid, clamp to valid range
+ if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
+ s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
+ WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority);
+ // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
+ // validity of this
+ priority = new_priority;
+ }
+
+ // Change thread priority
+ s32 old = thread->current_priority;
+ g_thread_ready_queue.remove(old, handle);
+ thread->current_priority = priority;
+ g_thread_ready_queue.prepare(thread->current_priority);
+
+ // Change thread status to "ready" and push to ready queue
+ if (thread->IsRunning()) {
+ thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
+ }
+ if (thread->IsReady()) {
+ g_thread_ready_queue.push_back(thread->current_priority, handle);
+ }
+
+ return 0;
+}
+
+/// Sets up the primary application thread
+Handle SetupMainThread(s32 priority, int stack_size) {
+ Handle handle;
+
+ // Initialize new "main" thread
+ Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
+ THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
+
+ ResetThread(thread, 0, 0);
+
+ // If running another thread already, set it to "ready" state
+ Thread* cur = GetCurrentThread();
+ if (cur && cur->IsRunning()) {
+ ChangeReadyState(cur, true);
+ }
+
+ // Run new "main" thread
+ SetCurrentThread(thread);
+ thread->status = THREADSTATUS_RUNNING;
+ LoadContext(thread->context);
+
+ return handle;
+}
+
+
+/// Reschedules to the next available thread (call after current thread is suspended)
+void Reschedule() {
+ Thread* prev = GetCurrentThread();
+ Thread* next = NextThread();
+ HLE::g_reschedule = false;
+ if (next > 0) {
+ INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
+
+ SwitchContext(next);
+
+ // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
+ // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
+ // This results in the current thread yielding on a VBLANK once, and then it will be
+ // immediately placed back in the queue for execution.
+ if (prev->wait_type == WAITTYPE_VBLANK) {
+ ResumeThreadFromWait(prev->GetHandle());
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ThreadingInit() {
+}
+
+void ThreadingShutdown() {
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
new file mode 100644
index 000000000..04914ba90
--- /dev/null
+++ b/src/core/hle/kernel/thread.h
@@ -0,0 +1,83 @@
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/kernel/kernel.h"
+
+enum ThreadPriority {
+ THREADPRIO_HIGHEST = 0, ///< Highest thread priority
+ THREADPRIO_DEFAULT = 16, ///< Default thread priority for userland apps
+ THREADPRIO_LOW = 31, ///< Low range of thread priority for userland apps
+ THREADPRIO_LOWEST = 63, ///< Thread priority max checked by svcCreateThread
+};
+
+enum ThreadProcessorId {
+ THREADPROCESSORID_0 = 0xFFFFFFFE, ///< Enables core appcode
+ THREADPROCESSORID_1 = 0xFFFFFFFD, ///< Enables core syscore
+ THREADPROCESSORID_ALL = 0xFFFFFFFC, ///< Enables both cores
+};
+
+enum ThreadStatus {
+ THREADSTATUS_RUNNING = 1,
+ THREADSTATUS_READY = 2,
+ THREADSTATUS_WAIT = 4,
+ THREADSTATUS_SUSPEND = 8,
+ THREADSTATUS_DORMANT = 16,
+ THREADSTATUS_DEAD = 32,
+ THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND
+};
+
+enum WaitType {
+ WAITTYPE_NONE,
+ WAITTYPE_SLEEP,
+ WAITTYPE_SEMA,
+ WAITTYPE_EVENT,
+ WAITTYPE_THREADEND,
+ WAITTYPE_VBLANK,
+ WAITTYPE_MUTEX,
+ WAITTYPE_SYNCH,
+};
+
+namespace Kernel {
+
+/// Creates a new thread - wrapper for external user
+Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
+ u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE);
+
+/// Sets up the primary application thread
+Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
+
+/// Reschedules to the next available thread (call after current thread is suspended)
+void Reschedule();
+
+/// Stops the current thread
+void StopThread(Handle thread, const char* reason);
+
+/// Resumes a thread from waiting by marking it as "ready"
+void ResumeThreadFromWait(Handle handle);
+
+/// Gets the current thread handle
+Handle GetCurrentThreadHandle();
+
+/// Puts the current thread in the wait state for the given type
+void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
+
+/// Put current thread in a wait state - on WaitSynchronization
+void WaitThread_Synchronization();
+
+/// Get the priority of the thread specified by handle
+u32 GetThreadPriority(const Handle handle);
+
+/// Set the priority of the thread specified by handle
+Result SetThreadPriority(Handle handle, s32 priority);
+
+/// Initialize threading
+void ThreadingInit();
+
+/// Shutdown threading
+void ThreadingShutdown();
+
+} // namespace
diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp
index 709ac5493..a0012b5dd 100644
--- a/src/core/hle/service/apt.cpp
+++ b/src/core/hle/service/apt.cpp
@@ -3,9 +3,11 @@
// Refer to the license.txt file included.
-#include "common/log.h"
+#include "common/common.h"
#include "core/hle/hle.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/mutex.h"
#include "core/hle/service/apt.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -14,93 +16,120 @@
namespace APT_U {
void Initialize(Service::Interface* self) {
- NOTICE_LOG(OSHLE, "APT_U::Sync - Initialize");
+ u32* cmd_buff = Service::GetCommandBuffer();
+ DEBUG_LOG(KERNEL, "called");
+
+ cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
+ cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
+
+ Kernel::SetEventLocked(cmd_buff[3], true);
+ Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
+
+ cmd_buff[1] = 0; // No error
}
void GetLockHandle(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
- cmd_buff[5] = 0x00000000; // TODO: This should be an actual mutex handle
+ u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
+ cmd_buff[1] = 0; // No error
+ cmd_buff[5] = Kernel::CreateMutex(false, "APT_U:Lock");
+ DEBUG_LOG(KERNEL, "called handle=0x%08X", cmd_buff[5]);
+}
+
+void Enable(Service::Interface* self) {
+ u32* cmd_buff = Service::GetCommandBuffer();
+ u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
+ cmd_buff[1] = 0; // No error
+ ERROR_LOG(KERNEL, "(UNIMPEMENTED) called unk=0x%08X", unk);
+}
+
+void InquireNotification(Service::Interface* self) {
+ u32* cmd_buff = Service::GetCommandBuffer();
+ u32 app_id = cmd_buff[2];
+ cmd_buff[1] = 0; // No error
+ cmd_buff[3] = 0; // Signal type
+ ERROR_LOG(KERNEL, "(UNIMPEMENTED) called app_id=0x%08X", app_id);
}
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, GetLockHandle, "GetLockHandle"},
- {0x00020080, Initialize, "Initialize"},
- {0x00030040, NULL, "Enable"},
- {0x00040040, NULL, "Finalize"},
- {0x00050040, NULL, "GetAppletManInfo"},
- {0x00060040, NULL, "GetAppletInfo"},
- {0x00070000, NULL, "GetLastSignaledAppletId"},
- {0x00080000, NULL, "CountRegisteredApplet"},
- {0x00090040, NULL, "IsRegistered"},
- {0x000A0040, NULL, "GetAttribute"},
- {0x000B0040, NULL, "InquireNotification"},
- {0x000C0104, NULL, "SendParameter"},
- {0x000D0080, NULL, "ReceiveParameter"},
- {0x000E0080, NULL, "GlanceParameter"},
- {0x000F0100, NULL, "CancelParameter"},
- {0x001000C2, NULL, "DebugFunc"},
- {0x001100C0, NULL, "MapProgramIdForDebug"},
- {0x00120040, NULL, "SetHomeMenuAppletIdForDebug"},
- {0x00130000, NULL, "GetPreparationState"},
- {0x00140040, NULL, "SetPreparationState"},
- {0x00150140, NULL, "PrepareToStartApplication"},
- {0x00160040, NULL, "PreloadLibraryApplet"},
- {0x00170040, NULL, "FinishPreloadingLibraryApplet"},
- {0x00180040, NULL, "PrepareToStartLibraryApplet"},
- {0x00190040, NULL, "PrepareToStartSystemApplet"},
- {0x001A0000, NULL, "PrepareToStartNewestHomeMenu"},
- {0x001B00C4, NULL, "StartApplication"},
- {0x001C0000, NULL, "WakeupApplication"},
- {0x001D0000, NULL, "CancelApplication"},
- {0x001E0084, NULL, "StartLibraryApplet"},
- {0x001F0084, NULL, "StartSystemApplet"},
- {0x00200044, NULL, "StartNewestHomeMenu"},
- {0x00210000, NULL, "OrderToCloseApplication"},
- {0x00220040, NULL, "PrepareToCloseApplication"},
- {0x00230040, NULL, "PrepareToJumpToApplication"},
- {0x00240044, NULL, "JumpToApplication"},
- {0x002500C0, NULL, "PrepareToCloseLibraryApplet"},
- {0x00260000, NULL, "PrepareToCloseSystemApplet"},
- {0x00270044, NULL, "CloseApplication"},
- {0x00280044, NULL, "CloseLibraryApplet"},
- {0x00290044, NULL, "CloseSystemApplet"},
- {0x002A0000, NULL, "OrderToCloseSystemApplet"},
- {0x002B0000, NULL, "PrepareToJumpToHomeMenu"},
- {0x002C0044, NULL, "JumpToHomeMenu"},
- {0x002D0000, NULL, "PrepareToLeaveHomeMenu"},
- {0x002E0044, NULL, "LeaveHomeMenu"},
- {0x002F0040, NULL, "PrepareToLeaveResidentApplet"},
- {0x00300044, NULL, "LeaveResidentApplet"},
- {0x00310100, NULL, "PrepareToDoApplicationJump"},
- {0x00320084, NULL, "DoApplicationJump"},
- {0x00330000, NULL, "GetProgramIdOnApplicationJump"},
- {0x00340084, NULL, "SendDeliverArg"},
- {0x00350080, NULL, "ReceiveDeliverArg"},
- {0x00360040, NULL, "LoadSysMenuArg"},
- {0x00370042, NULL, "StoreSysMenuArg"},
- {0x00380040, NULL, "PreloadResidentApplet"},
- {0x00390040, NULL, "PrepareToStartResidentApplet"},
- {0x003A0044, NULL, "StartResidentApplet"},
- {0x003B0040, NULL, "CancelLibraryApplet"},
- {0x003C0042, NULL, "SendDspSleep"},
- {0x003D0042, NULL, "SendDspWakeUp"},
- {0x003E0080, NULL, "ReplySleepQuery"},
- {0x003F0040, NULL, "ReplySleepNotificationComplete"},
- {0x00400042, NULL, "SendCaptureBufferInfo"},
- {0x00410040, NULL, "ReceiveCaptureBufferInfo"},
- {0x00420080, NULL, "SleepSystem"},
- {0x00430040, NULL, "NotifyToWait"},
- {0x00440000, NULL, "GetSharedFont"},
- {0x00450040, NULL, "GetWirelessRebootInfo"},
- {0x00460104, NULL, "Wrap"},
- {0x00470104, NULL, "Unwrap"},
- {0x00480100, NULL, "GetProgramInfo"},
- {0x00490180, NULL, "Reboot"},
- {0x004A0040, NULL, "GetCaptureInfo"},
- {0x004B00C2, NULL, "AppletUtility"},
- {0x004C0000, NULL, "SetFatalErrDispMode"},
- {0x004D0080, NULL, "GetAppletProgramInfo"},
- {0x004E0000, NULL, "HardwareResetAsync"},
+ {0x00010040, GetLockHandle, "GetLockHandle"},
+ {0x00020080, Initialize, "Initialize"},
+ {0x00030040, Enable, "Enable"},
+ {0x00040040, nullptr, "Finalize"},
+ {0x00050040, nullptr, "GetAppletManInfo"},
+ {0x00060040, nullptr, "GetAppletInfo"},
+ {0x00070000, nullptr, "GetLastSignaledAppletId"},
+ {0x00080000, nullptr, "CountRegisteredApplet"},
+ {0x00090040, nullptr, "IsRegistered"},
+ {0x000A0040, nullptr, "GetAttribute"},
+ {0x000B0040, InquireNotification, "InquireNotification"},
+ {0x000C0104, nullptr, "SendParameter"},
+ {0x000D0080, nullptr, "ReceiveParameter"},
+ {0x000E0080, nullptr, "GlanceParameter"},
+ {0x000F0100, nullptr, "CancelParameter"},
+ {0x001000C2, nullptr, "DebugFunc"},
+ {0x001100C0, nullptr, "MapProgramIdForDebug"},
+ {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
+ {0x00130000, nullptr, "GetPreparationState"},
+ {0x00140040, nullptr, "SetPreparationState"},
+ {0x00150140, nullptr, "PrepareToStartApplication"},
+ {0x00160040, nullptr, "PreloadLibraryApplet"},
+ {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
+ {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
+ {0x00190040, nullptr, "PrepareToStartSystemApplet"},
+ {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
+ {0x001B00C4, nullptr, "StartApplication"},
+ {0x001C0000, nullptr, "WakeupApplication"},
+ {0x001D0000, nullptr, "CancelApplication"},
+ {0x001E0084, nullptr, "StartLibraryApplet"},
+ {0x001F0084, nullptr, "StartSystemApplet"},
+ {0x00200044, nullptr, "StartNewestHomeMenu"},
+ {0x00210000, nullptr, "OrderToCloseApplication"},
+ {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00230040, nullptr, "PrepareToJumpToApplication"},
+ {0x00240044, nullptr, "JumpToApplication"},
+ {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
+ {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
+ {0x00270044, nullptr, "CloseApplication"},
+ {0x00280044, nullptr, "CloseLibraryApplet"},
+ {0x00290044, nullptr, "CloseSystemApplet"},
+ {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
+ {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, nullptr, "JumpToHomeMenu"},
+ {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
+ {0x00300044, nullptr, "LeaveResidentApplet"},
+ {0x00310100, nullptr, "PrepareToDoApplicationJump"},
+ {0x00320084, nullptr, "DoApplicationJump"},
+ {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
+ {0x00340084, nullptr, "SendDeliverArg"},
+ {0x00350080, nullptr, "ReceiveDeliverArg"},
+ {0x00360040, nullptr, "LoadSysMenuArg"},
+ {0x00370042, nullptr, "StoreSysMenuArg"},
+ {0x00380040, nullptr, "PreloadResidentApplet"},
+ {0x00390040, nullptr, "PrepareToStartResidentApplet"},
+ {0x003A0044, nullptr, "StartResidentApplet"},
+ {0x003B0040, nullptr, "CancelLibraryApplet"},
+ {0x003C0042, nullptr, "SendDspSleep"},
+ {0x003D0042, nullptr, "SendDspWakeUp"},
+ {0x003E0080, nullptr, "ReplySleepQuery"},
+ {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
+ {0x00400042, nullptr, "SendCaptureBufferInfo"},
+ {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
+ {0x00420080, nullptr, "SleepSystem"},
+ {0x00430040, nullptr, "NotifyToWait"},
+ {0x00440000, nullptr, "GetSharedFont"},
+ {0x00450040, nullptr, "GetWirelessRebootInfo"},
+ {0x00460104, nullptr, "Wrap"},
+ {0x00470104, nullptr, "Unwrap"},
+ {0x00480100, nullptr, "GetProgramInfo"},
+ {0x00490180, nullptr, "Reboot"},
+ {0x004A0040, nullptr, "GetCaptureInfo"},
+ {0x004B00C2, nullptr, "AppletUtility"},
+ {0x004C0000, nullptr, "SetFatalErrDispMode"},
+ {0x004D0080, nullptr, "GetAppletProgramInfo"},
+ {0x004E0000, nullptr, "HardwareResetAsync"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/apt.h b/src/core/hle/service/apt.h
index 4c7dd07e7..dca3097ed 100644
--- a/src/core/hle/service/apt.h
+++ b/src/core/hle/service/apt.h
@@ -29,7 +29,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ const char *GetPortName() const {
return "APT:U";
}
};
diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp
index 12c7dabcd..f75ba75c2 100644
--- a/src/core/hle/service/gsp.cpp
+++ b/src/core/hle/service/gsp.cpp
@@ -8,27 +8,32 @@
#include "core/mem_map.h"
#include "core/hle/hle.h"
+#include "core/hle/kernel/event.h"
#include "core/hle/service/gsp.h"
-#include "core/hw/lcd.h"
+#include "core/hw/gpu.h"
+
+#include "video_core/gpu_debugger.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
+// Main graphics debugger object - TODO: Here is probably not the best place for this
+GraphicsDebugger g_debugger;
+
/// GSP shared memory GX command buffer header
union GX_CmdBufferHeader {
u32 hex;
- // Current command index. This index is updated by GSP module after loading the command data,
- // right before the command is processed. When this index is updated by GSP module, the total
+ // Current command index. This index is updated by GSP module after loading the command data,
+ // right before the command is processed. When this index is updated by GSP module, the total
// commands field is decreased by one as well.
BitField<0,8,u32> index;
-
+
// Total commands to process, must not be value 0 when GSP module handles commands. This must be
- // <=15 when writing a command to shared memory. This is incremented by the application when
- // writing a command to shared memory, after increasing this value TriggerCmdReqQueue is only
+ // <=15 when writing a command to shared memory. This is incremented by the application when
+ // writing a command to shared memory, after increasing this value TriggerCmdReqQueue is only
// used if this field is value 1.
- BitField<8,8,u32> number_commands;
-
+ BitField<8,8,u32> number_commands;
};
/// Gets the address of the start (header) of a command buffer in GSP shared memory
@@ -44,7 +49,11 @@ static inline u8* GX_GetCmdBufferPointer(u32 thread_id, u32 offset=0) {
/// Finishes execution of a GSP command
void GX_FinishCommand(u32 thread_id) {
GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(thread_id);
+
+ g_debugger.GXCommandProcessed(GX_GetCmdBufferPointer(thread_id, 0x20 + (header->index * 0x20)));
+
header->number_commands = header->number_commands - 1;
+ // TODO: Increment header->index?
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -52,27 +61,24 @@ void GX_FinishCommand(u32 thread_id) {
namespace GSP_GPU {
+Handle g_event_handle = 0;
u32 g_thread_id = 0;
enum {
- CMD_GX_REQUEST_DMA = 0x00000000,
-};
-
-enum {
REG_FRAMEBUFFER_1 = 0x00400468,
REG_FRAMEBUFFER_2 = 0x00400494,
};
/// Read a GSP GPU hardware register
void ReadHWRegs(Service::Interface* self) {
- static const u32 framebuffer_1[] = {LCD::PADDR_VRAM_TOP_LEFT_FRAME1, LCD::PADDR_VRAM_TOP_RIGHT_FRAME1};
- static const u32 framebuffer_2[] = {LCD::PADDR_VRAM_TOP_LEFT_FRAME2, LCD::PADDR_VRAM_TOP_RIGHT_FRAME2};
+ static const u32 framebuffer_1[] = {GPU::PADDR_VRAM_TOP_LEFT_FRAME1, GPU::PADDR_VRAM_TOP_RIGHT_FRAME1};
+ static const u32 framebuffer_2[] = {GPU::PADDR_VRAM_TOP_LEFT_FRAME2, GPU::PADDR_VRAM_TOP_RIGHT_FRAME2};
u32* cmd_buff = Service::GetCommandBuffer();
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]);
-
+
switch (reg_addr) {
// NOTE: Calling SetFramebufferLocation here is a hack... Not sure the correct way yet to set
@@ -81,18 +87,18 @@ void ReadHWRegs(Service::Interface* self) {
// Top framebuffer 1 addresses
case REG_FRAMEBUFFER_1:
- LCD::SetFramebufferLocation(LCD::FRAMEBUFFER_LOCATION_VRAM);
+ GPU::SetFramebufferLocation(GPU::FRAMEBUFFER_LOCATION_VRAM);
memcpy(dst, framebuffer_1, size);
break;
// Top framebuffer 2 addresses
case REG_FRAMEBUFFER_2:
- LCD::SetFramebufferLocation(LCD::FRAMEBUFFER_LOCATION_VRAM);
+ GPU::SetFramebufferLocation(GPU::FRAMEBUFFER_LOCATION_VRAM);
memcpy(dst, framebuffer_2, size);
break;
default:
- ERROR_LOG(GSP, "ReadHWRegs unknown register read at address %08X", reg_addr);
+ ERROR_LOG(GSP, "unknown register read at address %08X", reg_addr);
}
}
@@ -100,62 +106,101 @@ void ReadHWRegs(Service::Interface* self) {
void RegisterInterruptRelayQueue(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32 flags = cmd_buff[1];
- u32 event_handle = cmd_buff[3]; // TODO(bunnei): Implement event handling
-
+ u32 event_handle = cmd_buff[3];
+
+ _assert_msg_(GSP, (event_handle != 0), "called, but event is nullptr!");
+
+ g_event_handle = event_handle;
+
+ Kernel::SetEventLocked(event_handle, false);
+
+ // Hack - This function will permanently set the state of the GSP event such that GPU command
+ // synchronization barriers always passthrough. Correct solution would be to set this after the
+ // GPU as processed all queued up commands, but due to the emulator being single-threaded they
+ // will always be ready.
+ Kernel::SetPermanentLock(event_handle, true);
+
cmd_buff[2] = g_thread_id; // ThreadID
- cmd_buff[4] = self->NewHandle();
}
+
/// This triggers handling of the GX command written to the command buffer in shared memory.
void TriggerCmdReqQueue(Service::Interface* self) {
GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(g_thread_id);
u32* cmd_buff = (u32*)GX_GetCmdBufferPointer(g_thread_id, 0x20 + (header->index * 0x20));
- switch (cmd_buff[0]) {
+ switch (static_cast<GXCommandId>(cmd_buff[0])) {
// GX request DMA - typically used for copying memory from GSP heap to VRAM
- case CMD_GX_REQUEST_DMA:
+ case GXCommandId::REQUEST_DMA:
memcpy(Memory::GetPointer(cmd_buff[2]), Memory::GetPointer(cmd_buff[1]), cmd_buff[3]);
break;
+ case GXCommandId::SET_COMMAND_LIST_LAST:
+ GPU::Write<u32>(GPU::Registers::CommandListAddress, cmd_buff[1] >> 3);
+ GPU::Write<u32>(GPU::Registers::CommandListSize, cmd_buff[2] >> 3);
+ GPU::Write<u32>(GPU::Registers::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this
+
+ // TODO: Move this to GPU
+ // TODO: Not sure what units the size is measured in
+ g_debugger.CommandListCalled(cmd_buff[1], (u32*)Memory::GetPointer(cmd_buff[1]), cmd_buff[2]);
+ break;
+
+ case GXCommandId::SET_MEMORY_FILL:
+ break;
+
+ case GXCommandId::SET_DISPLAY_TRANSFER:
+ break;
+
+ case GXCommandId::SET_TEXTURE_COPY:
+ break;
+
+ case GXCommandId::SET_COMMAND_LIST_FIRST:
+ {
+ //u32* buf0_data = (u32*)Memory::GetPointer(cmd_buff[1]);
+ //u32* buf1_data = (u32*)Memory::GetPointer(cmd_buff[3]);
+ //u32* buf2_data = (u32*)Memory::GetPointer(cmd_buff[5]);
+ break;
+ }
+
default:
- ERROR_LOG(GSP, "TriggerCmdReqQueue unknown command 0x%08X", cmd_buff[0]);
+ ERROR_LOG(GSP, "unknown command 0x%08X", cmd_buff[0]);
}
-
+
GX_FinishCommand(g_thread_id);
}
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010082, NULL, "WriteHWRegs"},
- {0x00020084, NULL, "WriteHWRegsWithMask"},
- {0x00030082, NULL, "WriteHWRegRepeat"},
+ {0x00010082, nullptr, "WriteHWRegs"},
+ {0x00020084, nullptr, "WriteHWRegsWithMask"},
+ {0x00030082, nullptr, "WriteHWRegRepeat"},
{0x00040080, ReadHWRegs, "ReadHWRegs"},
- {0x00050200, NULL, "SetBufferSwap"},
- {0x00060082, NULL, "SetCommandList"},
- {0x000700C2, NULL, "RequestDma"},
- {0x00080082, NULL, "FlushDataCache"},
- {0x00090082, NULL, "InvalidateDataCache"},
- {0x000A0044, NULL, "RegisterInterruptEvents"},
- {0x000B0040, NULL, "SetLcdForceBlack"},
+ {0x00050200, nullptr, "SetBufferSwap"},
+ {0x00060082, nullptr, "SetCommandList"},
+ {0x000700C2, nullptr, "RequestDma"},
+ {0x00080082, nullptr, "FlushDataCache"},
+ {0x00090082, nullptr, "InvalidateDataCache"},
+ {0x000A0044, nullptr, "RegisterInterruptEvents"},
+ {0x000B0040, nullptr, "SetLcdForceBlack"},
{0x000C0000, TriggerCmdReqQueue, "TriggerCmdReqQueue"},
- {0x000D0140, NULL, "SetDisplayTransfer"},
- {0x000E0180, NULL, "SetTextureCopy"},
- {0x000F0200, NULL, "SetMemoryFill"},
- {0x00100040, NULL, "SetAxiConfigQoSMode"},
- {0x00110040, NULL, "SetPerfLogMode"},
- {0x00120000, NULL, "GetPerfLog"},
+ {0x000D0140, nullptr, "SetDisplayTransfer"},
+ {0x000E0180, nullptr, "SetTextureCopy"},
+ {0x000F0200, nullptr, "SetMemoryFill"},
+ {0x00100040, nullptr, "SetAxiConfigQoSMode"},
+ {0x00110040, nullptr, "SetPerfLogMode"},
+ {0x00120000, nullptr, "GetPerfLog"},
{0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"},
- {0x00140000, NULL, "UnregisterInterruptRelayQueue"},
- {0x00150002, NULL, "TryAcquireRight"},
- {0x00160042, NULL, "AcquireRight"},
- {0x00170000, NULL, "ReleaseRight"},
- {0x00180000, NULL, "ImportDisplayCaptureInfo"},
- {0x00190000, NULL, "SaveVramSysArea"},
- {0x001A0000, NULL, "RestoreVramSysArea"},
- {0x001B0000, NULL, "ResetGpuCore"},
- {0x001C0040, NULL, "SetLedForceOff"},
- {0x001D0040, NULL, "SetTestCommand"},
- {0x001E0080, NULL, "SetInternalPriorities"},
+ {0x00140000, nullptr, "UnregisterInterruptRelayQueue"},
+ {0x00150002, nullptr, "TryAcquireRight"},
+ {0x00160042, nullptr, "AcquireRight"},
+ {0x00170000, nullptr, "ReleaseRight"},
+ {0x00180000, nullptr, "ImportDisplayCaptureInfo"},
+ {0x00190000, nullptr, "SaveVramSysArea"},
+ {0x001A0000, nullptr, "RestoreVramSysArea"},
+ {0x001B0000, nullptr, "ResetGpuCore"},
+ {0x001C0040, nullptr, "SetLedForceOff"},
+ {0x001D0040, nullptr, "SetTestCommand"},
+ {0x001E0080, nullptr, "SetInternalPriorities"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/gsp.h b/src/core/hle/service/gsp.h
index 5ba09ab70..214de140f 100644
--- a/src/core/hle/service/gsp.h
+++ b/src/core/hle/service/gsp.h
@@ -11,6 +11,23 @@
namespace GSP_GPU {
+enum class GXCommandId : u32 {
+ REQUEST_DMA = 0x00000000,
+ SET_COMMAND_LIST_LAST = 0x00000001,
+ SET_MEMORY_FILL = 0x00000002, // TODO: Confirm? (lictru uses 0x01000102)
+ SET_DISPLAY_TRANSFER = 0x00000003,
+ SET_TEXTURE_COPY = 0x00000004,
+ SET_COMMAND_LIST_FIRST = 0x00000005,
+};
+
+union GXCommand {
+ struct {
+ GXCommandId id;
+ };
+
+ u32 data[0x20];
+};
+
/// Interface to "srv:" service
class Interface : public Service::Interface {
public:
@@ -23,7 +40,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ const char *GetPortName() const {
return "gsp::Gpu";
}
diff --git a/src/core/hle/service/hid.cpp b/src/core/hle/service/hid.cpp
index 5542e5bf2..ab78f47d7 100644
--- a/src/core/hle/service/hid.cpp
+++ b/src/core/hle/service/hid.cpp
@@ -13,11 +13,11 @@
namespace HID_User {
const Interface::FunctionInfo FunctionTable[] = {
- {0x000A0000, NULL, "GetIPCHandles"},
- {0x00110000, NULL, "EnableAccelerometer"},
- {0x00130000, NULL, "EnableGyroscopeLow"},
- {0x00150000, NULL, "GetGyroscopeLowRawToDpsCoefficient"},
- {0x00160000, NULL, "GetGyroscopeLowCalibrateParam"},
+ {0x000A0000, nullptr, "GetIPCHandles"},
+ {0x00110000, nullptr, "EnableAccelerometer"},
+ {0x00130000, nullptr, "EnableGyroscopeLow"},
+ {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
+ {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/hid.h b/src/core/hle/service/hid.h
index b17fcfa86..81c29eb2e 100644
--- a/src/core/hle/service/hid.h
+++ b/src/core/hle/service/hid.h
@@ -25,7 +25,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ const char *GetPortName() const {
return "hid:USER";
}
diff --git a/src/core/hle/service/ndm.cpp b/src/core/hle/service/ndm.cpp
new file mode 100644
index 000000000..48755b6a7
--- /dev/null
+++ b/src/core/hle/service/ndm.cpp
@@ -0,0 +1,32 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+
+#include "core/hle/hle.h"
+#include "core/hle/service/ndm.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NDM_U
+
+namespace NDM_U {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00060040, nullptr, "SuspendDaemons"},
+ {0x00080040, nullptr, "DisableWifiUsage"},
+ {0x00090000, nullptr, "EnableWifiUsage"},
+ {0x00140040, nullptr, "OverrideDefaultDaemons"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/ndm.h b/src/core/hle/service/ndm.h
new file mode 100644
index 000000000..fbe88fb8f
--- /dev/null
+++ b/src/core/hle/service/ndm.h
@@ -0,0 +1,33 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NDM
+
+// No idea what this is
+
+namespace NDM_U {
+
+class Interface : public Service::Interface {
+public:
+
+ Interface();
+
+ ~Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ const char *GetPortName() const {
+ return "ndm:u";
+ }
+
+};
+
+} // namespace
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index e6605a398..4a1ac857e 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,15 +7,19 @@
#include "common/string_util.h"
#include "core/hle/hle.h"
+
#include "core/hle/service/service.h"
#include "core/hle/service/apt.h"
#include "core/hle/service/gsp.h"
#include "core/hle/service/hid.h"
+#include "core/hle/service/ndm.h"
#include "core/hle/service/srv.h"
+#include "core/hle/kernel/kernel.h"
+
namespace Service {
-Manager* g_manager = NULL; ///< Service manager
+Manager* g_manager = nullptr; ///< Service manager
////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Manager class
@@ -31,41 +35,30 @@ Manager::~Manager() {
/// Add a service to the manager (does not create it though)
void Manager::AddService(Interface* service) {
- int index = m_services.size();
- u32 new_uid = GetUIDFromIndex(index);
-
+ m_port_map[service->GetPortName()] = Kernel::g_object_pool.Create(service);
m_services.push_back(service);
-
- m_port_map[service->GetPortName()] = new_uid;
- service->m_uid = new_uid;
}
/// Removes a service from the manager, also frees memory
void Manager::DeleteService(std::string port_name) {
- auto service = FetchFromPortName(port_name);
-
- m_services.erase(m_services.begin() + GetIndexFromUID(service->m_uid));
+ Interface* service = FetchFromPortName(port_name);
+ m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end());
m_port_map.erase(port_name);
-
delete service;
}
-/// Get a Service Interface from its UID
-Interface* Manager::FetchFromUID(u32 uid) {
- int index = GetIndexFromUID(uid);
- if (index < (int)m_services.size()) {
- return m_services[index];
- }
- return NULL;
+/// Get a Service Interface from its Handle
+Interface* Manager::FetchFromHandle(Handle handle) {
+ return Kernel::g_object_pool.GetFast<Interface>(handle);
}
/// Get a Service Interface from its port
Interface* Manager::FetchFromPortName(std::string port_name) {
auto itr = m_port_map.find(port_name);
if (itr == m_port_map.end()) {
- return NULL;
+ return nullptr;
}
- return FetchFromUID(itr->second);
+ return FetchFromHandle(itr->second);
}
@@ -80,14 +73,15 @@ void Init() {
g_manager->AddService(new APT_U::Interface);
g_manager->AddService(new GSP_GPU::Interface);
g_manager->AddService(new HID_User::Interface);
+ g_manager->AddService(new NDM_U::Interface);
- NOTICE_LOG(HLE, "Services initialized OK");
+ NOTICE_LOG(HLE, "initialized OK");
}
/// Shutdown ServiceManager
void Shutdown() {
delete g_manager;
- NOTICE_LOG(HLE, "Services shutdown OK");
+ NOTICE_LOG(HLE, "shutdown OK");
}
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index b260a290a..dcd525727 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -4,22 +4,22 @@
#pragma once
+#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include "common/common.h"
-#include "common/common_types.h"
#include "core/mem_map.h"
-#include "core/hle/syscall.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/svc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace Service
namespace Service {
-typedef s32 NativeUID; ///< Native handle for a service
-
static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters)
static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
@@ -35,15 +35,15 @@ inline static u32* GetCommandBuffer(const int offset=0) {
class Manager;
/// Interface to a CTROS service
-class Interface : NonCopyable {
+class Interface : public Kernel::Object {
friend class Manager;
public:
+
+ const char *GetName() const { return GetPortName(); }
+ const char *GetTypeName() const { return GetPortName(); }
- Interface() {
- }
-
- virtual ~Interface() {
- }
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; }
+ Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Service; }
typedef void (*Function)(Interface*);
@@ -54,55 +54,53 @@ public:
};
/**
- * Gets the UID for the serice
- * @return UID of service in native format
- */
- NativeUID GetUID() const {
- return (NativeUID)m_uid;
- }
-
- /**
* Gets the string name used by CTROS for a service
* @return Port name of service
*/
- virtual std::string GetPortName() const {
+ virtual const char *GetPortName() const {
return "[UNKNOWN SERVICE PORT]";
}
/// Allocates a new handle for the service
- Syscall::Handle NewHandle() {
- Syscall::Handle handle = (m_handles.size() << 16) | m_uid;
+ Handle CreateHandle(Kernel::Object *obj) {
+ Handle handle = Kernel::g_object_pool.Create(obj);
m_handles.push_back(handle);
return handle;
}
/// Frees a handle from the service
- void DeleteHandle(Syscall::Handle handle) {
- for(auto iter = m_handles.begin(); iter != m_handles.end(); ++iter) {
- if(*iter == handle) {
- m_handles.erase(iter);
- break;
- }
- }
+ template <class T>
+ void DeleteHandle(const Handle handle) {
+ Kernel::g_object_pool.Destroy<T>(handle);
+ m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end());
}
/**
- * Called when svcSendSyncRequest is called, loads command buffer and executes comand
- * @return Return result of svcSendSyncRequest passed back to user app
+ * Synchronize kernel object
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
*/
- Syscall::Result Sync() {
+ Result SyncRequest(bool* wait) {
u32* cmd_buff = GetCommandBuffer();
auto itr = m_functions.find(cmd_buff[0]);
if (itr == m_functions.end()) {
- ERROR_LOG(OSHLE, "Unknown/unimplemented function: port = %s, command = 0x%08X!",
- GetPortName().c_str(), cmd_buff[0]);
- return -1;
+ ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X",
+ GetPortName(), cmd_buff[0]);
+
+ // TODO(bunnei): Hack - ignore error
+ u32* cmd_buff = Service::GetCommandBuffer();
+ cmd_buff[1] = 0;
+ return 0;
}
- if (itr->second.func == NULL) {
- ERROR_LOG(OSHLE, "Unimplemented function: port = %s, name = %s!",
- GetPortName().c_str(), itr->second.name.c_str());
- return -1;
+ if (itr->second.func == nullptr) {
+ ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s",
+ GetPortName(), itr->second.name.c_str());
+
+ // TODO(bunnei): Hack - ignore error
+ u32* cmd_buff = Service::GetCommandBuffer();
+ cmd_buff[1] = 0;
+ return 0;
}
itr->second.func(this);
@@ -110,6 +108,17 @@ public:
return 0; // TODO: Implement return from actual function
}
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result WaitSynchronization(bool* wait) {
+ // TODO(bunnei): ImplementMe
+ ERROR_LOG(OSHLE, "unimplemented function");
+ return 0;
+ }
+
protected:
/**
@@ -122,10 +131,10 @@ protected:
}
private:
- u32 m_uid;
-
- std::vector<Syscall::Handle> m_handles;
- std::map<u32, FunctionInfo> m_functions;
+
+ std::vector<Handle> m_handles;
+ std::map<u32, FunctionInfo> m_functions;
+
};
/// Simple class to manage accessing services from ports and UID handles
@@ -143,25 +152,16 @@ public:
void DeleteService(std::string port_name);
/// Get a Service Interface from its UID
- Interface* FetchFromUID(u32 uid);
+ Interface* FetchFromHandle(u32 uid);
/// Get a Service Interface from its port
Interface* FetchFromPortName(std::string port_name);
private:
- /// Convert an index into m_services vector into a UID
- static u32 GetUIDFromIndex(const int index) {
- return index | 0x10000000;
- }
-
- /// Convert a UID into an index into m_services
- static int GetIndexFromUID(const u32 uid) {
- return uid & 0x0FFFFFFF;
- }
-
std::vector<Interface*> m_services;
std::map<std::string, u32> m_port_map;
+
};
/// Initialize ServiceManager
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 071741444..f45c0efc2 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -5,43 +5,52 @@
#include "core/hle/hle.h"
#include "core/hle/service/srv.h"
#include "core/hle/service/service.h"
-
+#include "core/hle/kernel/mutex.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace SRV
namespace SRV {
+Handle g_mutex = 0;
+
void Initialize(Service::Interface* self) {
- NOTICE_LOG(OSHLE, "SRV::Sync - Initialize");
+ DEBUG_LOG(OSHLE, "called");
+ if (!g_mutex) {
+ g_mutex = Kernel::CreateMutex(true, "SRV:Lock");
+ }
+}
+
+void GetProcSemaphore(Service::Interface* self) {
+ DEBUG_LOG(OSHLE, "called");
+ // Get process semaphore?
+ u32* cmd_buff = Service::GetCommandBuffer();
+ cmd_buff[1] = 0; // No error
+ cmd_buff[3] = g_mutex; // Return something... 0 == nullptr, raises an exception
}
void GetServiceHandle(Service::Interface* self) {
- Syscall::Result res = 0;
+ Result res = 0;
u32* cmd_buff = Service::GetCommandBuffer();
std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
- NOTICE_LOG(OSHLE, "SRV::Sync - GetHandle - port: %s, handle: 0x%08X", port_name.c_str(),
- service->GetUID());
-
- if (NULL != service) {
- cmd_buff[3] = service->GetUID();
+ if (nullptr != service) {
+ cmd_buff[3] = service->GetHandle();
+ DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
} else {
- ERROR_LOG(OSHLE, "Service %s does not exist", port_name.c_str());
+ ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
res = -1;
}
cmd_buff[1] = res;
-
- //return res;
}
const Interface::FunctionInfo FunctionTable[] = {
{0x00010002, Initialize, "Initialize"},
- {0x00020000, NULL, "GetProcSemaphore"},
- {0x00030100, NULL, "RegisterService"},
- {0x000400C0, NULL, "UnregisterService"},
+ {0x00020000, GetProcSemaphore, "GetProcSemaphore"},
+ {0x00030100, nullptr, "RegisterService"},
+ {0x000400C0, nullptr, "UnregisterService"},
{0x00050100, GetServiceHandle, "GetServiceHandle"},
};
diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h
index 760c976b4..81109a2a8 100644
--- a/src/core/hle/service/srv.h
+++ b/src/core/hle/service/srv.h
@@ -22,16 +22,10 @@ public:
* Gets the string name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ const char *GetPortName() const {
return "srv:";
}
- /**
- * Called when svcSendSyncRequest is called, loads command buffer and executes comand
- * @return Return result of svcSendSyncRequest passed back to user app
- */
- Syscall::Result Sync();
-
};
} // namespace
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
new file mode 100644
index 000000000..441d8ce8d
--- /dev/null
+++ b/src/core/hle/svc.cpp
@@ -0,0 +1,467 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <map>
+#include <string>
+
+#include "common/symbols.h"
+
+#include "core/mem_map.h"
+
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/thread.h"
+
+#include "core/hle/function_wrappers.h"
+#include "core/hle/svc.h"
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace SVC
+
+namespace SVC {
+
+enum ControlMemoryOperation {
+ MEMORY_OPERATION_HEAP = 0x00000003,
+ MEMORY_OPERATION_GSP_HEAP = 0x00010003,
+};
+
+enum MapMemoryPermission {
+ MEMORY_PERMISSION_UNMAP = 0x00000000,
+ MEMORY_PERMISSION_NORMAL = 0x00000001,
+};
+
+/// Map application or GSP heap memory
+Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
+ DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
+ operation, addr0, addr1, size, permissions);
+
+ switch (operation) {
+
+ // Map normal heap memory
+ case MEMORY_OPERATION_HEAP:
+ *out_addr = Memory::MapBlock_Heap(size, operation, permissions);
+ break;
+
+ // Map GSP heap memory
+ case MEMORY_OPERATION_GSP_HEAP:
+ *out_addr = Memory::MapBlock_HeapGSP(size, operation, permissions);
+ break;
+
+ // Unknown ControlMemory operation
+ default:
+ ERROR_LOG(SVC, "unknown operation=0x%08X", operation);
+ }
+ return 0;
+}
+
+/// Maps a memory block to specified address
+Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherpermission) {
+ DEBUG_LOG(SVC, "called memblock=0x08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
+ memblock, addr, mypermissions, otherpermission);
+ switch (mypermissions) {
+ case MEMORY_PERMISSION_NORMAL:
+ case MEMORY_PERMISSION_NORMAL + 1:
+ case MEMORY_PERMISSION_NORMAL + 2:
+ Memory::MapBlock_Shared(memblock, addr, mypermissions);
+ break;
+ default:
+ ERROR_LOG(OSHLE, "unknown permissions=0x%08X", mypermissions);
+ }
+ return 0;
+}
+
+/// Connect to an OS service given the port name, returns the handle to the port to out
+Result ConnectToPort(Handle* out, const char* port_name) {
+ Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
+
+ DEBUG_LOG(SVC, "called port_name=%s", port_name);
+ _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!");
+
+ *out = service->GetHandle();
+
+ return 0;
+}
+
+/// Synchronize to an OS service
+Result SendSyncRequest(Handle handle) {
+ Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
+
+ _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
+ DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName());
+
+ bool wait = false;
+ Result res = object->SyncRequest(&wait);
+ if (wait) {
+ Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
+ }
+
+ return res;
+}
+
+/// Close a handle
+Result CloseHandle(Handle handle) {
+ // ImplementMe
+ ERROR_LOG(SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
+ return 0;
+}
+
+/// Wait for a handle to synchronize, timeout after the specified nanoseconds
+Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
+ // TODO(bunnei): Do something with nano_seconds, currently ignoring this
+ bool wait = false;
+ bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
+
+ Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
+
+ DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%d", handle, object->GetTypeName(),
+ object->GetName(), nano_seconds);
+
+ _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
+
+ Result res = object->WaitSynchronization(&wait);
+
+ // Check for next thread to schedule
+ if (wait) {
+ HLE::Reschedule(__func__);
+ return 0;
+ }
+
+ return res;
+}
+
+/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
+Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
+ s64 nano_seconds) {
+ // TODO(bunnei): Do something with nano_seconds, currently ignoring this
+ bool unlock_all = true;
+ bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
+
+ DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%d",
+ handle_count, (wait_all ? "true" : "false"), nano_seconds);
+
+ // Iterate through each handle, synchronize kernel object
+ for (s32 i = 0; i < handle_count; i++) {
+ bool wait = false;
+ Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]);
+
+ _assert_msg_(KERNEL, (object != nullptr), "called handle=0x%08X, but kernel object "
+ "is nullptr!", handles[i]);
+
+ DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName(),
+ object->GetName());
+
+ Result res = object->WaitSynchronization(&wait);
+
+ if (!wait && !wait_all) {
+ *out = i;
+ return 0;
+ } else {
+ unlock_all = false;
+ }
+ }
+
+ if (wait_all && unlock_all) {
+ *out = handle_count;
+ return 0;
+ }
+
+ // Check for next thread to schedule
+ HLE::Reschedule(__func__);
+
+ return 0;
+}
+
+/// Create an address arbiter (to allocate access to shared resources)
+Result CreateAddressArbiter(void* arbiter) {
+ ERROR_LOG(SVC, "(UNIMPLEMENTED) called");
+ Core::g_app_core->SetReg(1, 0xFABBDADD);
+ return 0;
+}
+
+/// Arbitrate address
+Result ArbitrateAddress(Handle arbiter, u32 addr, u32 _type, u32 value, s64 nanoseconds) {
+ ERROR_LOG(SVC, "(UNIMPLEMENTED) called");
+ ArbitrationType type = (ArbitrationType)_type;
+ Memory::Write32(addr, type);
+ return 0;
+}
+
+/// Used to output a message on a debug hardware unit - does nothing on a retail unit
+void OutputDebugString(const char* string) {
+ OS_LOG(SVC, "%s", string);
+}
+
+/// Get resource limit
+Result GetResourceLimit(Handle* resource_limit, Handle process) {
+ // With regards to proceess values:
+ // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
+ // the current KThread.
+ *resource_limit = 0xDEADBEEF;
+ ERROR_LOG(SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
+ return 0;
+}
+
+/// Get resource limit current values
+Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
+ s32 name_count) {
+ ERROR_LOG(SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
+ resource_limit, names, name_count);
+ Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now
+ return 0;
+}
+
+/// Creates a new thread
+Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
+ std::string name;
+ if (Symbols::HasSymbol(entry_point)) {
+ TSymbol symbol = Symbols::GetSymbol(entry_point);
+ name = symbol.name;
+ } else {
+ char buff[100];
+ sprintf(buff, "%s", "unknown-%08X", entry_point);
+ name = buff;
+ }
+
+ Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, arg, processor_id,
+ stack_top);
+
+ Core::g_app_core->SetReg(1, thread);
+
+ DEBUG_LOG(SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
+ "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
+ name.c_str(), arg, stack_top, priority, processor_id, thread);
+
+ return 0;
+}
+
+/// Called when a thread exits
+u32 ExitThread() {
+ Handle thread = Kernel::GetCurrentThreadHandle();
+
+ DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
+
+ Kernel::StopThread(thread, __func__);
+ HLE::Reschedule(__func__);
+ return 0;
+}
+
+/// Gets the priority for the specified thread
+Result GetThreadPriority(s32* priority, Handle handle) {
+ *priority = Kernel::GetThreadPriority(handle);
+ return 0;
+}
+
+/// Sets the priority for the specified thread
+Result SetThreadPriority(Handle handle, s32 priority) {
+ return Kernel::SetThreadPriority(handle, priority);
+}
+
+/// Create a mutex
+Result CreateMutex(Handle* mutex, u32 initial_locked) {
+ *mutex = Kernel::CreateMutex((initial_locked != 0));
+ DEBUG_LOG(SVC, "called initial_locked=%s : created handle=0x%08X",
+ initial_locked ? "true" : "false", *mutex);
+ return 0;
+}
+
+/// Release a mutex
+Result ReleaseMutex(Handle handle) {
+ DEBUG_LOG(SVC, "called handle=0x%08X", handle);
+ _assert_msg_(KERNEL, (handle != 0), "called, but handle is nullptr!");
+ Kernel::ReleaseMutex(handle);
+ return 0;
+}
+
+/// Get current thread ID
+Result GetThreadId(u32* thread_id, Handle thread) {
+ ERROR_LOG(SVC, "(UNIMPLEMENTED) called thread=0x%08X", thread);
+ return 0;
+}
+
+/// Query memory
+Result QueryMemory(void* info, void* out, u32 addr) {
+ ERROR_LOG(SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
+ return 0;
+}
+
+/// Create an event
+Result CreateEvent(Handle* evt, u32 reset_type) {
+ *evt = Kernel::CreateEvent((ResetType)reset_type);
+ DEBUG_LOG(SVC, "called reset_type=0x%08X : created handle=0x%08X",
+ reset_type, *evt);
+ return 0;
+}
+
+/// Duplicates a kernel handle
+Result DuplicateHandle(Handle* out, Handle handle) {
+ DEBUG_LOG(SVC, "called handle=0x%08X", handle);
+
+ // Translate kernel handles -> real handles
+ if (handle == Kernel::CurrentThread) {
+ handle = Kernel::GetCurrentThreadHandle();
+ }
+ _assert_msg_(KERNEL, (handle != Kernel::CurrentProcess),
+ "(UNIMPLEMENTED) process handle duplication!");
+
+ // TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate.
+ *out = handle;
+
+ return 0;
+}
+
+/// Signals an event
+Result SignalEvent(Handle evt) {
+ Result res = Kernel::SignalEvent(evt);
+ DEBUG_LOG(SVC, "called event=0x%08X", evt);
+ return res;
+}
+
+/// Clears an event
+Result ClearEvent(Handle evt) {
+ Result res = Kernel::ClearEvent(evt);
+ DEBUG_LOG(SVC, "called event=0x%08X", evt);
+ return res;
+}
+
+/// Sleep the current thread
+void SleepThread(s64 nanoseconds) {
+ DEBUG_LOG(SVC, "called nanoseconds=%d", nanoseconds);
+}
+
+const HLE::FunctionDef SVC_Table[] = {
+ {0x00, nullptr, "Unknown"},
+ {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"},
+ {0x02, HLE::Wrap<QueryMemory>, "QueryMemory"},
+ {0x03, nullptr, "ExitProcess"},
+ {0x04, nullptr, "GetProcessAffinityMask"},
+ {0x05, nullptr, "SetProcessAffinityMask"},
+ {0x06, nullptr, "GetProcessIdealProcessor"},
+ {0x07, nullptr, "SetProcessIdealProcessor"},
+ {0x08, HLE::Wrap<CreateThread>, "CreateThread"},
+ {0x09, HLE::Wrap<ExitThread>, "ExitThread"},
+ {0x0A, HLE::Wrap<SleepThread>, "SleepThread"},
+ {0x0B, HLE::Wrap<GetThreadPriority>, "GetThreadPriority"},
+ {0x0C, HLE::Wrap<SetThreadPriority>, "SetThreadPriority"},
+ {0x0D, nullptr, "GetThreadAffinityMask"},
+ {0x0E, nullptr, "SetThreadAffinityMask"},
+ {0x0F, nullptr, "GetThreadIdealProcessor"},
+ {0x10, nullptr, "SetThreadIdealProcessor"},
+ {0x11, nullptr, "GetCurrentProcessorNumber"},
+ {0x12, nullptr, "Run"},
+ {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"},
+ {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"},
+ {0x15, nullptr, "CreateSemaphore"},
+ {0x16, nullptr, "ReleaseSemaphore"},
+ {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
+ {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
+ {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
+ {0x1A, nullptr, "CreateTimer"},
+ {0x1B, nullptr, "SetTimer"},
+ {0x1C, nullptr, "CancelTimer"},
+ {0x1D, nullptr, "ClearTimer"},
+ {0x1E, nullptr, "CreateMemoryBlock"},
+ {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"},
+ {0x20, nullptr, "UnmapMemoryBlock"},
+ {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"},
+ {0x22, HLE::Wrap<ArbitrateAddress>, "ArbitrateAddress"},
+ {0x23, HLE::Wrap<CloseHandle>, "CloseHandle"},
+ {0x24, HLE::Wrap<WaitSynchronization1>, "WaitSynchronization1"},
+ {0x25, HLE::Wrap<WaitSynchronizationN>, "WaitSynchronizationN"},
+ {0x26, nullptr, "SignalAndWait"},
+ {0x27, HLE::Wrap<DuplicateHandle>, "DuplicateHandle"},
+ {0x28, nullptr, "GetSystemTick"},
+ {0x29, nullptr, "GetHandleInfo"},
+ {0x2A, nullptr, "GetSystemInfo"},
+ {0x2B, nullptr, "GetProcessInfo"},
+ {0x2C, nullptr, "GetThreadInfo"},
+ {0x2D, HLE::Wrap<ConnectToPort>, "ConnectToPort"},
+ {0x2E, nullptr, "SendSyncRequest1"},
+ {0x2F, nullptr, "SendSyncRequest2"},
+ {0x30, nullptr, "SendSyncRequest3"},
+ {0x31, nullptr, "SendSyncRequest4"},
+ {0x32, HLE::Wrap<SendSyncRequest>, "SendSyncRequest"},
+ {0x33, nullptr, "OpenProcess"},
+ {0x34, nullptr, "OpenThread"},
+ {0x35, nullptr, "GetProcessId"},
+ {0x36, nullptr, "GetProcessIdOfThread"},
+ {0x37, HLE::Wrap<GetThreadId>, "GetThreadId"},
+ {0x38, HLE::Wrap<GetResourceLimit>, "GetResourceLimit"},
+ {0x39, nullptr, "GetResourceLimitLimitValues"},
+ {0x3A, HLE::Wrap<GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"},
+ {0x3B, nullptr, "GetThreadContext"},
+ {0x3C, nullptr, "Break"},
+ {0x3D, HLE::Wrap<OutputDebugString>, "OutputDebugString"},
+ {0x3E, nullptr, "ControlPerformanceCounter"},
+ {0x3F, nullptr, "Unknown"},
+ {0x40, nullptr, "Unknown"},
+ {0x41, nullptr, "Unknown"},
+ {0x42, nullptr, "Unknown"},
+ {0x43, nullptr, "Unknown"},
+ {0x44, nullptr, "Unknown"},
+ {0x45, nullptr, "Unknown"},
+ {0x46, nullptr, "Unknown"},
+ {0x47, nullptr, "CreatePort"},
+ {0x48, nullptr, "CreateSessionToPort"},
+ {0x49, nullptr, "CreateSession"},
+ {0x4A, nullptr, "AcceptSession"},
+ {0x4B, nullptr, "ReplyAndReceive1"},
+ {0x4C, nullptr, "ReplyAndReceive2"},
+ {0x4D, nullptr, "ReplyAndReceive3"},
+ {0x4E, nullptr, "ReplyAndReceive4"},
+ {0x4F, nullptr, "ReplyAndReceive"},
+ {0x50, nullptr, "BindInterrupt"},
+ {0x51, nullptr, "UnbindInterrupt"},
+ {0x52, nullptr, "InvalidateProcessDataCache"},
+ {0x53, nullptr, "StoreProcessDataCache"},
+ {0x54, nullptr, "FlushProcessDataCache"},
+ {0x55, nullptr, "StartInterProcessDma"},
+ {0x56, nullptr, "StopDma"},
+ {0x57, nullptr, "GetDmaState"},
+ {0x58, nullptr, "RestartDma"},
+ {0x59, nullptr, "Unknown"},
+ {0x5A, nullptr, "Unknown"},
+ {0x5B, nullptr, "Unknown"},
+ {0x5C, nullptr, "Unknown"},
+ {0x5D, nullptr, "Unknown"},
+ {0x5E, nullptr, "Unknown"},
+ {0x5F, nullptr, "Unknown"},
+ {0x60, nullptr, "DebugActiveProcess"},
+ {0x61, nullptr, "BreakDebugProcess"},
+ {0x62, nullptr, "TerminateDebugProcess"},
+ {0x63, nullptr, "GetProcessDebugEvent"},
+ {0x64, nullptr, "ContinueDebugEvent"},
+ {0x65, nullptr, "GetProcessList"},
+ {0x66, nullptr, "GetThreadList"},
+ {0x67, nullptr, "GetDebugThreadContext"},
+ {0x68, nullptr, "SetDebugThreadContext"},
+ {0x69, nullptr, "QueryDebugProcessMemory"},
+ {0x6A, nullptr, "ReadProcessMemory"},
+ {0x6B, nullptr, "WriteProcessMemory"},
+ {0x6C, nullptr, "SetHardwareBreakPoint"},
+ {0x6D, nullptr, "GetDebugThreadParam"},
+ {0x6E, nullptr, "Unknown"},
+ {0x6F, nullptr, "Unknown"},
+ {0x70, nullptr, "ControlProcessMemory"},
+ {0x71, nullptr, "MapProcessMemory"},
+ {0x72, nullptr, "UnmapProcessMemory"},
+ {0x73, nullptr, "Unknown"},
+ {0x74, nullptr, "Unknown"},
+ {0x75, nullptr, "Unknown"},
+ {0x76, nullptr, "TerminateProcess"},
+ {0x77, nullptr, "Unknown"},
+ {0x78, nullptr, "CreateResourceLimit"},
+ {0x79, nullptr, "Unknown"},
+ {0x7A, nullptr, "Unknown"},
+ {0x7B, nullptr, "Unknown"},
+ {0x7C, nullptr, "KernelSetState"},
+ {0x7D, nullptr, "QueryProcessMemory"},
+};
+
+void Register() {
+ HLE::RegisterModule("SVC_Table", ARRAY_SIZE(SVC_Table), SVC_Table);
+}
+
+} // namespace
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
new file mode 100644
index 000000000..1d125faf6
--- /dev/null
+++ b/src/core/hle/svc.h
@@ -0,0 +1,61 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// SVC types
+
+struct MemoryInfo {
+ u32 base_address;
+ u32 size;
+ u32 permission;
+ u32 state;
+};
+
+struct PageInfo {
+ u32 flags;
+};
+
+struct ThreadContext {
+ u32 cpu_registers[13];
+ u32 sp;
+ u32 lr;
+ u32 pc;
+ u32 cpsr;
+ u32 fpu_registers[32];
+ u32 fpscr;
+ u32 fpexc;
+
+ // These are not part of native ThreadContext, but needed by emu
+ u32 reg_15;
+ u32 mode;
+};
+
+enum ResetType {
+ RESETTYPE_ONESHOT,
+ RESETTYPE_STICKY,
+ RESETTYPE_PULSE,
+ RESETTYPE_MAX_BIT = (1u << 31),
+};
+
+enum ArbitrationType {
+ ARBITRATIONTYPE_SIGNAL,
+ ARBITRATIONTYPE_WAIT_IF_LESS_THAN,
+ ARBITRATIONTYPE_DECREMENT_AND_WAIT_IF_LESS_THAN,
+ ARBITRATIONTYPE_WAIT_IF_LESS_THAN_WITH_TIMEOUT,
+ ARBITRATIONTYPE_DECREMENT_AND_WAIT_IF_LESS_THAN_WITH_TIMEOUT,
+ ARBITRATIONTYPE_MAX_BIT = (1u << 31)
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace SVC
+
+namespace SVC {
+
+void Register();
+
+} // namespace
diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp
deleted file mode 100644
index d47df6038..000000000
--- a/src/core/hle/syscall.cpp
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include <map>
-
-#include "core/mem_map.h"
-
-#include "core/hle/function_wrappers.h"
-#include "core/hle/syscall.h"
-#include "core/hle/service/service.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace Syscall
-
-namespace Syscall {
-
-enum ControlMemoryOperation {
- MEMORY_OPERATION_HEAP = 0x00000003,
- MEMORY_OPERATION_GSP_HEAP = 0x00010003,
-};
-
-enum MapMemoryPermission {
- MEMORY_PERMISSION_UNMAP = 0x00000000,
- MEMORY_PERMISSION_NORMAL = 0x00000001,
-};
-
-/// Map application or GSP heap memory
-Result ControlMemory(u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
- u32 virtual_address = 0x00000000;
-
- DEBUG_LOG(SVC, "ControlMemory called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
- operation, addr0, addr1, size, permissions);
-
- switch (operation) {
-
- // Map normal heap memory
- case MEMORY_OPERATION_HEAP:
- virtual_address = Memory::MapBlock_Heap(size, operation, permissions);
- break;
-
- // Map GSP heap memory
- case MEMORY_OPERATION_GSP_HEAP:
- virtual_address = Memory::MapBlock_HeapGSP(size, operation, permissions);
- break;
-
- // Unknown ControlMemory operation
- default:
- ERROR_LOG(SVC, "ControlMemory unknown operation=0x%08X", operation);
- }
-
- Core::g_app_core->SetReg(1, virtual_address);
-
- return 0;
-}
-
-/// Maps a memory block to specified address
-Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherpermission) {
- DEBUG_LOG(SVC, "MapMemoryBlock called memblock=0x08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
- memblock, addr, mypermissions, otherpermission);
- switch (mypermissions) {
- case MEMORY_PERMISSION_NORMAL:
- case MEMORY_PERMISSION_NORMAL + 1:
- case MEMORY_PERMISSION_NORMAL + 2:
- Memory::MapBlock_Shared(memblock, addr, mypermissions);
- break;
- default:
- ERROR_LOG(OSHLE, "MapMemoryBlock unknown permissions=0x%08X", mypermissions);
- }
- return 0;
-}
-
-/// Connect to an OS service given the port name, returns the handle to the port to out
-Result ConnectToPort(void* out, const char* port_name) {
-
- Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
- Core::g_app_core->SetReg(1, service->GetUID());
- DEBUG_LOG(SVC, "ConnectToPort called port_name=%s", port_name);
- return 0;
-}
-
-/// Synchronize to an OS service
-Result SendSyncRequest(Handle session) {
- DEBUG_LOG(SVC, "SendSyncRequest called session=0x%08X");
- Service::Interface* service = Service::g_manager->FetchFromUID(session);
- service->Sync();
- return 0;
-}
-
-/// Close a handle
-Result CloseHandle(Handle handle) {
- // ImplementMe
- DEBUG_LOG(SVC, "(UNIMPLEMENTED) CloseHandle called handle=0x%08X", handle);
- return 0;
-}
-
-/// Wait for a handle to synchronize, timeout after the specified nanoseconds
-Result WaitSynchronization1(Handle handle, s64 nanoseconds) {
- // ImplementMe
- DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronization1 called handle=0x%08X, nanoseconds=%d",
- handle, nanoseconds);
- return 0;
-}
-
-/// Create an address arbiter (to allocate access to shared resources)
-Result CreateAddressArbiter(void* arbiter) {
- // ImplementMe
- DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateAddressArbiter called");
- Core::g_app_core->SetReg(1, 0xDEADBEEF);
- return 0;
-}
-
-/// Used to output a message on a debug hardware unit - does nothing on a retail unit
-void OutputDebugString(const char* string) {
- NOTICE_LOG(SVC, "## OSDEBUG: %08X %s", Core::g_app_core->GetPC(), string);
-}
-
-/// Get resource limit
-Result GetResourceLimit(void* resource_limit, Handle process) {
- // With regards to proceess values:
- // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
- // the current KThread.
- DEBUG_LOG(SVC, "(UNIMPLEMENTED) GetResourceLimit called process=0x%08X", process);
- Core::g_app_core->SetReg(1, 0xDEADBEEF);
- return 0;
-}
-
-/// Get resource limit current values
-Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void* names, s32 name_count) {
- //s64* values = (s64*)_values;
- DEBUG_LOG(SVC, "(UNIMPLEMENTED) GetResourceLimitCurrentValues called resource_limit=%08X, names=%s, name_count=%d",
- resource_limit, names, name_count);
- Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now
- return 0;
-}
-
-const HLE::FunctionDef Syscall_Table[] = {
- {0x00, NULL, "Unknown"},
- {0x01, WrapI_UUUUU<ControlMemory>, "ControlMemory"},
- {0x02, NULL, "QueryMemory"},
- {0x03, NULL, "ExitProcess"},
- {0x04, NULL, "GetProcessAffinityMask"},
- {0x05, NULL, "SetProcessAffinityMask"},
- {0x06, NULL, "GetProcessIdealProcessor"},
- {0x07, NULL, "SetProcessIdealProcessor"},
- {0x08, NULL, "CreateThread"},
- {0x09, NULL, "ExitThread"},
- {0x0A, NULL, "SleepThread"},
- {0x0B, NULL, "GetThreadPriority"},
- {0x0C, NULL, "SetThreadPriority"},
- {0x0D, NULL, "GetThreadAffinityMask"},
- {0x0E, NULL, "SetThreadAffinityMask"},
- {0x0F, NULL, "GetThreadIdealProcessor"},
- {0x10, NULL, "SetThreadIdealProcessor"},
- {0x11, NULL, "GetCurrentProcessorNumber"},
- {0x12, NULL, "Run"},
- {0x13, NULL, "CreateMutex"},
- {0x14, NULL, "ReleaseMutex"},
- {0x15, NULL, "CreateSemaphore"},
- {0x16, NULL, "ReleaseSemaphore"},
- {0x17, NULL, "CreateEvent"},
- {0x18, NULL, "SignalEvent"},
- {0x19, NULL, "ClearEvent"},
- {0x1A, NULL, "CreateTimer"},
- {0x1B, NULL, "SetTimer"},
- {0x1C, NULL, "CancelTimer"},
- {0x1D, NULL, "ClearTimer"},
- {0x1E, NULL, "CreateMemoryBlock"},
- {0x1F, WrapI_UUUU<MapMemoryBlock>, "MapMemoryBlock"},
- {0x20, NULL, "UnmapMemoryBlock"},
- {0x21, WrapI_V<CreateAddressArbiter>, "CreateAddressArbiter"},
- {0x22, NULL, "ArbitrateAddress"},
- {0x23, WrapI_U<CloseHandle>, "CloseHandle"},
- {0x24, WrapI_US64<WaitSynchronization1>, "WaitSynchronization1"},
- {0x25, NULL, "WaitSynchronizationN"},
- {0x26, NULL, "SignalAndWait"},
- {0x27, NULL, "DuplicateHandle"},
- {0x28, NULL, "GetSystemTick"},
- {0x29, NULL, "GetHandleInfo"},
- {0x2A, NULL, "GetSystemInfo"},
- {0x2B, NULL, "GetProcessInfo"},
- {0x2C, NULL, "GetThreadInfo"},
- {0x2D, WrapI_VC<ConnectToPort>, "ConnectToPort"},
- {0x2E, NULL, "SendSyncRequest1"},
- {0x2F, NULL, "SendSyncRequest2"},
- {0x30, NULL, "SendSyncRequest3"},
- {0x31, NULL, "SendSyncRequest4"},
- {0x32, WrapI_U<SendSyncRequest>, "SendSyncRequest"},
- {0x33, NULL, "OpenProcess"},
- {0x34, NULL, "OpenThread"},
- {0x35, NULL, "GetProcessId"},
- {0x36, NULL, "GetProcessIdOfThread"},
- {0x37, NULL, "GetThreadId"},
- {0x38, WrapI_VU<GetResourceLimit>, "GetResourceLimit"},
- {0x39, NULL, "GetResourceLimitLimitValues"},
- {0x3A, WrapI_VUVI<GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"},
- {0x3B, NULL, "GetThreadContext"},
- {0x3C, NULL, "Break"},
- {0x3D, WrapV_C<OutputDebugString>, "OutputDebugString"},
- {0x3E, NULL, "ControlPerformanceCounter"},
- {0x3F, NULL, "Unknown"},
- {0x40, NULL, "Unknown"},
- {0x41, NULL, "Unknown"},
- {0x42, NULL, "Unknown"},
- {0x43, NULL, "Unknown"},
- {0x44, NULL, "Unknown"},
- {0x45, NULL, "Unknown"},
- {0x46, NULL, "Unknown"},
- {0x47, NULL, "CreatePort"},
- {0x48, NULL, "CreateSessionToPort"},
- {0x49, NULL, "CreateSession"},
- {0x4A, NULL, "AcceptSession"},
- {0x4B, NULL, "ReplyAndReceive1"},
- {0x4C, NULL, "ReplyAndReceive2"},
- {0x4D, NULL, "ReplyAndReceive3"},
- {0x4E, NULL, "ReplyAndReceive4"},
- {0x4F, NULL, "ReplyAndReceive"},
- {0x50, NULL, "BindInterrupt"},
- {0x51, NULL, "UnbindInterrupt"},
- {0x52, NULL, "InvalidateProcessDataCache"},
- {0x53, NULL, "StoreProcessDataCache"},
- {0x54, NULL, "FlushProcessDataCache"},
- {0x55, NULL, "StartInterProcessDma"},
- {0x56, NULL, "StopDma"},
- {0x57, NULL, "GetDmaState"},
- {0x58, NULL, "RestartDma"},
- {0x59, NULL, "Unknown"},
- {0x5A, NULL, "Unknown"},
- {0x5B, NULL, "Unknown"},
- {0x5C, NULL, "Unknown"},
- {0x5D, NULL, "Unknown"},
- {0x5E, NULL, "Unknown"},
- {0x5F, NULL, "Unknown"},
- {0x60, NULL, "DebugActiveProcess"},
- {0x61, NULL, "BreakDebugProcess"},
- {0x62, NULL, "TerminateDebugProcess"},
- {0x63, NULL, "GetProcessDebugEvent"},
- {0x64, NULL, "ContinueDebugEvent"},
- {0x65, NULL, "GetProcessList"},
- {0x66, NULL, "GetThreadList"},
- {0x67, NULL, "GetDebugThreadContext"},
- {0x68, NULL, "SetDebugThreadContext"},
- {0x69, NULL, "QueryDebugProcessMemory"},
- {0x6A, NULL, "ReadProcessMemory"},
- {0x6B, NULL, "WriteProcessMemory"},
- {0x6C, NULL, "SetHardwareBreakPoint"},
- {0x6D, NULL, "GetDebugThreadParam"},
- {0x6E, NULL, "Unknown"},
- {0x6F, NULL, "Unknown"},
- {0x70, NULL, "ControlProcessMemory"},
- {0x71, NULL, "MapProcessMemory"},
- {0x72, NULL, "UnmapProcessMemory"},
- {0x73, NULL, "Unknown"},
- {0x74, NULL, "Unknown"},
- {0x75, NULL, "Unknown"},
- {0x76, NULL, "TerminateProcess"},
- {0x77, NULL, "Unknown"},
- {0x78, NULL, "CreateResourceLimit"},
- {0x79, NULL, "Unknown"},
- {0x7A, NULL, "Unknown"},
- {0x7B, NULL, "Unknown"},
- {0x7C, NULL, "KernelSetState"},
- {0x7D, NULL, "QueryProcessMemory"},
-};
-
-void Register() {
- HLE::RegisterModule("SyscallTable", ARRAY_SIZE(Syscall_Table), Syscall_Table);
-}
-
-} // namespace
diff --git a/src/core/hle/syscall.h b/src/core/hle/syscall.h
deleted file mode 100644
index 7a94e0136..000000000
--- a/src/core/hle/syscall.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace Syscall
-
-namespace Syscall {
-
-typedef u32 Handle;
-typedef s32 Result;
-
-void Register();
-
-} // namespace
diff --git a/src/core/hw/lcd.cpp b/src/core/hw/gpu.cpp
index 6468053f2..f0ca4eada 100644
--- a/src/core/hw/lcd.cpp
+++ b/src/core/hw/gpu.cpp
@@ -7,15 +7,15 @@
#include "core/core.h"
#include "core/mem_map.h"
-#include "core/hw/lcd.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hw/gpu.h"
#include "video_core/video_core.h"
-namespace LCD {
-Registers g_regs;
+namespace GPU {
-static const u32 kFrameTicks = 268123480 / 60; ///< 268MHz / 60 frames per second
+Registers g_regs;
u64 g_last_ticks = 0; ///< Last CPU ticks
@@ -59,7 +59,7 @@ const FramebufferLocation GetFramebufferLocation() {
} else if ((g_regs.framebuffer_top_right_1 & ~Memory::FCRAM_MASK) == Memory::FCRAM_PADDR) {
return FRAMEBUFFER_LOCATION_FCRAM;
} else {
- ERROR_LOG(LCD, "unknown framebuffer location!");
+ ERROR_LOG(GPU, "unknown framebuffer location!");
}
return FRAMEBUFFER_LOCATION_UNKNOWN;
}
@@ -76,7 +76,7 @@ const u8* GetFramebufferPointer(const u32 address) {
case FRAMEBUFFER_LOCATION_VRAM:
return (const u8*)Memory::GetPointer(Memory::VirtualAddressFromPhysical_VRAM(address));
default:
- ERROR_LOG(LCD, "unknown framebuffer location");
+ ERROR_LOG(GPU, "unknown framebuffer location");
}
return NULL;
}
@@ -84,34 +84,73 @@ const u8* GetFramebufferPointer(const u32 address) {
template <typename T>
inline void Read(T &var, const u32 addr) {
switch (addr) {
- case REG_FRAMEBUFFER_TOP_LEFT_1:
+ case Registers::FramebufferTopLeft1:
var = g_regs.framebuffer_top_left_1;
break;
- case REG_FRAMEBUFFER_TOP_LEFT_2:
+
+ case Registers::FramebufferTopLeft2:
var = g_regs.framebuffer_top_left_2;
break;
- case REG_FRAMEBUFFER_TOP_RIGHT_1:
+
+ case Registers::FramebufferTopRight1:
var = g_regs.framebuffer_top_right_1;
break;
- case REG_FRAMEBUFFER_TOP_RIGHT_2:
+
+ case Registers::FramebufferTopRight2:
var = g_regs.framebuffer_top_right_2;
break;
- case REG_FRAMEBUFFER_SUB_LEFT_1:
+
+ case Registers::FramebufferSubLeft1:
var = g_regs.framebuffer_sub_left_1;
break;
- case REG_FRAMEBUFFER_SUB_RIGHT_1:
+
+ case Registers::FramebufferSubRight1:
var = g_regs.framebuffer_sub_right_1;
break;
+
+ case Registers::CommandListSize:
+ var = g_regs.command_list_size;
+ break;
+
+ case Registers::CommandListAddress:
+ var = g_regs.command_list_address;
+ break;
+
+ case Registers::ProcessCommandList:
+ var = g_regs.command_processing_enabled;
+ break;
+
default:
- ERROR_LOG(LCD, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr);
+ ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr);
break;
}
-
}
template <typename T>
inline void Write(u32 addr, const T data) {
- ERROR_LOG(LCD, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
+ switch (static_cast<Registers::Id>(addr)) {
+ case Registers::CommandListSize:
+ g_regs.command_list_size = data;
+ break;
+
+ case Registers::CommandListAddress:
+ g_regs.command_list_address = data;
+ break;
+
+ case Registers::ProcessCommandList:
+ g_regs.command_processing_enabled = data;
+ if (g_regs.command_processing_enabled & 1)
+ {
+ // u32* buffer = (u32*)Memory::GetPointer(g_regs.command_list_address << 3);
+ ERROR_LOG(GPU, "Beginning %x bytes of commands from address %x", g_regs.command_list_size, g_regs.command_list_address << 3);
+ // TODO: Process command list!
+ }
+ break;
+
+ default:
+ ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
+ break;
+ }
}
// Explicitly instantiate template functions because we aren't defining this in the header:
@@ -130,9 +169,11 @@ template void Write<u8>(u32 addr, const u8 data);
void Update() {
u64 current_ticks = Core::g_app_core->GetTicks();
+ // Fake a vertical blank
if ((current_ticks - g_last_ticks) >= kFrameTicks) {
g_last_ticks = current_ticks;
VideoCore::g_renderer->SwapBuffers();
+ Kernel::WaitCurrentThread(WAITTYPE_VBLANK);
}
}
@@ -140,12 +181,12 @@ void Update() {
void Init() {
g_last_ticks = Core::g_app_core->GetTicks();
SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM);
- NOTICE_LOG(LCD, "initialized OK");
+ NOTICE_LOG(GPU, "initialized OK");
}
/// Shutdown hardware
void Shutdown() {
- NOTICE_LOG(LCD, "shutdown OK");
+ NOTICE_LOG(GPU, "shutdown OK");
}
} // namespace
diff --git a/src/core/hw/lcd.h b/src/core/hw/gpu.h
index 2dd3b4adc..3314ba989 100644
--- a/src/core/hw/lcd.h
+++ b/src/core/hw/gpu.h
@@ -6,9 +6,27 @@
#include "common/common_types.h"
-namespace LCD {
+namespace GPU {
+
+static const u32 kFrameCycles = 268123480 / 60; ///< 268MHz / 60 frames per second
+static const u32 kFrameTicks = kFrameCycles / 3; ///< Approximate number of instructions/frame
struct Registers {
+ enum Id : u32 {
+ FramebufferTopLeft1 = 0x1EF00468, // Main LCD, first framebuffer for 3D left
+ FramebufferTopLeft2 = 0x1EF0046C, // Main LCD, second framebuffer for 3D left
+ FramebufferTopRight1 = 0x1EF00494, // Main LCD, first framebuffer for 3D right
+ FramebufferTopRight2 = 0x1EF00498, // Main LCD, second framebuffer for 3D right
+ FramebufferSubLeft1 = 0x1EF00568, // Sub LCD, first framebuffer
+ FramebufferSubLeft2 = 0x1EF0056C, // Sub LCD, second framebuffer
+ FramebufferSubRight1 = 0x1EF00594, // Sub LCD, unused first framebuffer
+ FramebufferSubRight2 = 0x1EF00598, // Sub LCD, unused second framebuffer
+
+ CommandListSize = 0x1EF018E0,
+ CommandListAddress = 0x1EF018E8,
+ ProcessCommandList = 0x1EF018F0,
+ };
+
u32 framebuffer_top_left_1;
u32 framebuffer_top_left_2;
u32 framebuffer_top_right_1;
@@ -17,6 +35,10 @@ struct Registers {
u32 framebuffer_sub_left_2;
u32 framebuffer_sub_right_1;
u32 framebuffer_sub_right_2;
+
+ u32 command_list_size;
+ u32 command_list_address;
+ u32 command_processing_enabled;
};
extern Registers g_regs;
@@ -24,7 +46,7 @@ extern Registers g_regs;
enum {
TOP_ASPECT_X = 0x5,
TOP_ASPECT_Y = 0x3,
-
+
TOP_HEIGHT = 240,
TOP_WIDTH = 400,
BOTTOM_WIDTH = 320,
@@ -48,21 +70,10 @@ enum {
PADDR_VRAM_SUB_FRAME2 = 0x18249CF0,
};
-enum {
- REG_FRAMEBUFFER_TOP_LEFT_1 = 0x1EF00468, // Main LCD, first framebuffer for 3D left
- REG_FRAMEBUFFER_TOP_LEFT_2 = 0x1EF0046C, // Main LCD, second framebuffer for 3D left
- REG_FRAMEBUFFER_TOP_RIGHT_1 = 0x1EF00494, // Main LCD, first framebuffer for 3D right
- REG_FRAMEBUFFER_TOP_RIGHT_2 = 0x1EF00498, // Main LCD, second framebuffer for 3D right
- REG_FRAMEBUFFER_SUB_LEFT_1 = 0x1EF00568, // Sub LCD, first framebuffer
- REG_FRAMEBUFFER_SUB_LEFT_2 = 0x1EF0056C, // Sub LCD, second framebuffer
- REG_FRAMEBUFFER_SUB_RIGHT_1 = 0x1EF00594, // Sub LCD, unused first framebuffer
- REG_FRAMEBUFFER_SUB_RIGHT_2 = 0x1EF00598, // Sub LCD, unused second framebuffer
-};
-
/// Framebuffer location
enum FramebufferLocation {
FRAMEBUFFER_LOCATION_UNKNOWN, ///< Framebuffer location is unknown
- FRAMEBUFFER_LOCATION_FCRAM, ///< Framebuffer is in the GSP heap
+ FRAMEBUFFER_LOCATION_FCRAM, ///< Framebuffer is in the GSP heap
FRAMEBUFFER_LOCATION_VRAM, ///< Framebuffer is in VRAM
};
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index 85669ae7f..ed70486e6 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -6,7 +6,7 @@
#include "common/log.h"
#include "core/hw/hw.h"
-#include "core/hw/lcd.h"
+#include "core/hw/gpu.h"
#include "core/hw/ndma.h"
namespace HW {
@@ -34,7 +34,7 @@ enum {
VADDR_CDMA = 0xFFFDA000, // CoreLink DMA-330? Info
VADDR_DSP_2 = 0x1ED03000,
VADDR_HASH_2 = 0x1EE01000,
- VADDR_LCD = 0x1EF00000,
+ VADDR_GPU = 0x1EF00000,
};
template <typename T>
@@ -46,8 +46,8 @@ inline void Read(T &var, const u32 addr) {
// NDMA::Read(var, addr);
// break;
- case VADDR_LCD:
- LCD::Read(var, addr);
+ case VADDR_GPU:
+ GPU::Read(var, addr);
break;
default:
@@ -64,8 +64,8 @@ inline void Write(u32 addr, const T data) {
// NDMA::Write(addr, data);
// break;
- case VADDR_LCD:
- LCD::Write(addr, data);
+ case VADDR_GPU:
+ GPU::Write(addr, data);
break;
default:
@@ -87,13 +87,13 @@ template void Write<u8>(u32 addr, const u8 data);
/// Update hardware
void Update() {
- LCD::Update();
+ GPU::Update();
NDMA::Update();
}
/// Initialize hardware
void Init() {
- LCD::Init();
+ GPU::Init();
NDMA::Init();
NOTICE_LOG(HW, "initialized OK");
}
diff --git a/src/core/hw/ndma.cpp b/src/core/hw/ndma.cpp
index 52e459ebd..f6aa72d16 100644
--- a/src/core/hw/ndma.cpp
+++ b/src/core/hw/ndma.cpp
@@ -37,12 +37,12 @@ void Update() {
/// Initialize hardware
void Init() {
- NOTICE_LOG(LCD, "initialized OK");
+ NOTICE_LOG(GPU, "initialized OK");
}
/// Shutdown hardware
void Shutdown() {
- NOTICE_LOG(LCD, "shutdown OK");
+ NOTICE_LOG(GPU, "shutdown OK");
}
} // namespace
diff --git a/src/core/loader.cpp b/src/core/loader.cpp
index 8756588ae..ff1c873bb 100644
--- a/src/core/loader.cpp
+++ b/src/core/loader.cpp
@@ -10,7 +10,7 @@
#include "core/core.h"
#include "core/file_sys/directory_file_system.h"
#include "core/elf/elf_reader.h"
-
+#include "core/hle/kernel/kernel.h"
#include "core/mem_map.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -56,7 +56,7 @@ bool Load_ELF(std::string &filename) {
elf_reader = new ElfReader(buffer);
elf_reader->LoadInto(0x00100000);
- Core::g_app_core->SetPC(elf_reader->GetEntryPoint());
+ Kernel::LoadExec(elf_reader->GetEntryPoint());
delete[] buffer;
delete elf_reader;
@@ -89,11 +89,11 @@ bool Load_DAT(std::string &filename) {
* but for the sake of making it easier... we'll temporarily/hackishly
* allow it. No sense in making a proper reader for this.
*/
- u32 entrypoint = 0x00100000; // write to same entrypoint as elf
+ u32 entry_point = 0x00100000; // write to same entrypoint as elf
u32 payload_offset = 0xA150;
const u8 *src = &buffer[payload_offset];
- u8 *dst = Memory::GetPointer(entrypoint);
+ u8 *dst = Memory::GetPointer(entry_point);
u32 srcSize = size - payload_offset; //just load everything...
u32 *s = (u32*)src;
u32 *d = (u32*)dst;
@@ -102,7 +102,8 @@ bool Load_DAT(std::string &filename) {
*d++ = (*s++);
}
- Core::g_app_core->SetPC(entrypoint);
+ Kernel::LoadExec(entry_point);
+
delete[] buffer;
}
@@ -131,10 +132,10 @@ bool Load_BIN(std::string &filename) {
f.ReadBytes(buffer, size);
- u32 entrypoint = 0x00100000; // Hardcoded, read from exheader
+ u32 entry_point = 0x00100000; // Hardcoded, read from exheader
const u8 *src = buffer;
- u8 *dst = Memory::GetPointer(entrypoint);
+ u8 *dst = Memory::GetPointer(entry_point);
u32 srcSize = size;
u32 *s = (u32*)src;
u32 *d = (u32*)dst;
@@ -143,7 +144,7 @@ bool Load_BIN(std::string &filename) {
*d++ = (*s++);
}
- Core::g_app_core->SetPC(entrypoint);
+ Kernel::LoadExec(entry_point);
delete[] buffer;
}
@@ -186,6 +187,9 @@ FileType IdentifyFile(std::string &filename) {
else if (!strcasecmp(extension.c_str(), ".elf")) {
return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p
}
+ else if (!strcasecmp(extension.c_str(), ".axf")) {
+ return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p
+ }
else if (!strcasecmp(extension.c_str(), ".bin")) {
return FILETYPE_CTR_BIN;
}
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp
index 59560b87d..c45746be9 100644
--- a/src/core/mem_map.cpp
+++ b/src/core/mem_map.cpp
@@ -17,6 +17,7 @@ u8* g_base = NULL; ///< The base pointer to the aut
MemArena g_arena; ///< The MemArena class
u8* g_exefs_code = NULL; ///< ExeFS:/.code is loaded here
+u8* g_system_mem = NULL; ///< System memory
u8* g_heap = NULL; ///< Application heap (main memory)
u8* g_heap_gsp = NULL; ///< GSP heap (main memory)
u8* g_vram = NULL; ///< Video memory (VRAM) pointer
@@ -27,6 +28,7 @@ u8* g_physical_bootrom = NULL; ///< Bootrom physical memory
u8* g_uncached_bootrom = NULL;
u8* g_physical_exefs_code = NULL; ///< Phsical ExeFS:/.code is loaded here
+u8* g_physical_system_mem = NULL; ///< System physical memory
u8* g_physical_fcram = NULL; ///< Main physical memory (FCRAM)
u8* g_physical_heap_gsp = NULL; ///< GSP heap physical memory
u8* g_physical_vram = NULL; ///< Video physical memory (VRAM)
@@ -39,6 +41,7 @@ static MemoryView g_views[] = {
{&g_vram, &g_physical_vram, VRAM_VADDR, VRAM_SIZE, 0},
{&g_heap, &g_physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM},
{&g_shared_mem, &g_physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0},
+ {&g_system_mem, &g_physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0},
{&g_kernel_mem, &g_physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0},
{&g_heap_gsp, &g_physical_heap_gsp, HEAP_GSP_VADDR, HEAP_GSP_SIZE, 0},
};
diff --git a/src/core/mem_map.h b/src/core/mem_map.h
index af2212a5f..12d497ef3 100644
--- a/src/core/mem_map.h
+++ b/src/core/mem_map.h
@@ -47,6 +47,12 @@ enum {
EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE),
EXEFS_CODE_MASK = 0x03FFFFFF,
+ // Region of FCRAM used by system
+ SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB
+ SYSTEM_MEMORY_VADDR = 0x04000000,
+ SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE),
+ SYSTEM_MEMORY_MASK = 0x03FFFFFF,
+
HEAP_SIZE = FCRAM_SIZE, ///< Application heap size
//HEAP_PADDR = HEAP_GSP_SIZE,
//HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE),
@@ -116,6 +122,7 @@ extern u8* g_heap; ///< Application heap (main memory)
extern u8* g_vram; ///< Video memory (VRAM)
extern u8* g_shared_mem; ///< Shared memory
extern u8* g_kernel_mem; ///< Kernel memory
+extern u8* g_system_mem; ///< System memory
extern u8* g_exefs_code; ///< ExeFS:/.code is loaded here
void Init();
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index 8ab647714..ab014a596 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -73,6 +73,10 @@ inline void _Read(T &var, const u32 addr) {
} else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]);
+ // System memory
+ } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
+ var = *((const T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK]);
+
// Config memory
} else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) {
ConfigMem::Read<T>(var, vaddr);
@@ -82,7 +86,7 @@ inline void _Read(T &var, const u32 addr) {
var = *((const T*)&g_vram[vaddr & VRAM_MASK]);
} else {
- //_assert_msg_(MEMMAP, false, "unknown Read%d @ 0x%08X", sizeof(var) * 8, vaddr);
+ ERROR_LOG(MEMMAP, "unknown Read%d @ 0x%08X", sizeof(var) * 8, vaddr);
}
}
@@ -115,6 +119,10 @@ inline void _Write(u32 addr, const T data) {
} else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
*(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data;
+ // System memory
+ } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
+ *(T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK] = data;
+
// VRAM
} else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
*(T*)&g_vram[vaddr & VRAM_MASK] = data;
@@ -128,8 +136,7 @@ inline void _Write(u32 addr, const T data) {
// Error out...
} else {
- _assert_msg_(MEMMAP, false, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8,
- data, vaddr);
+ ERROR_LOG(MEMMAP, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, vaddr);
}
}
@@ -153,9 +160,13 @@ u8 *GetPointer(const u32 addr) {
return g_heap + (vaddr & HEAP_MASK);
// Shared memory
- } else if ((vaddr > SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
+ } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
return g_shared_mem + (vaddr & SHARED_MEMORY_MASK);
+ // System memory
+ } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
+ return g_system_mem + (vaddr & SYSTEM_MEMORY_MASK);
+
// VRAM
} else if ((vaddr > VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
return g_vram + (vaddr & VRAM_MASK);
diff --git a/src/core/system.cpp b/src/core/system.cpp
index c77092327..9b1e96888 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -8,6 +8,7 @@
#include "core/system.h"
#include "core/hw/hw.h"
#include "core/hle/hle.h"
+#include "core/hle/kernel/kernel.h"
#include "video_core/video_core.h"
@@ -26,6 +27,7 @@ void Init(EmuWindow* emu_window) {
HLE::Init();
CoreTiming::Init();
VideoCore::Init(emu_window);
+ Kernel::Init();
}
void RunLoopFor(int cycles) {
@@ -42,6 +44,7 @@ void Shutdown() {
HLE::Shutdown();
CoreTiming::Shutdown();
VideoCore::Shutdown();
+ Kernel::Shutdown();
g_ctr_file_system.Shutdown();
}
diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h
new file mode 100644
index 000000000..5d909beba
--- /dev/null
+++ b/src/video_core/gpu_debugger.h
@@ -0,0 +1,157 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+
+#include "common/log.h"
+
+#include "core/hle/service/gsp.h"
+#include "pica.h"
+
+class GraphicsDebugger
+{
+public:
+ // A few utility structs used to expose data
+ // A vector of commands represented by their raw byte sequence
+ struct PicaCommand : public std::vector<u32>
+ {
+ const Pica::CommandHeader& GetHeader() const
+ {
+ const u32& val = at(1);
+ return *(Pica::CommandHeader*)&val;
+ }
+ };
+
+ typedef std::vector<PicaCommand> PicaCommandList;
+
+ // Base class for all objects which need to be notified about GPU events
+ class DebuggerObserver
+ {
+ public:
+ DebuggerObserver() : observed(nullptr) { }
+
+ virtual ~DebuggerObserver()
+ {
+ if (observed)
+ observed->UnregisterObserver(this);
+ }
+
+ /**
+ * Called when a GX command has been processed and is ready for being
+ * read via GraphicsDebugger::ReadGXCommandHistory.
+ * @param total_command_count Total number of commands in the GX history
+ * @note All methods in this class are called from the GSP thread
+ */
+ virtual void GXCommandProcessed(int total_command_count)
+ {
+ const GSP_GPU::GXCommand& cmd = observed->ReadGXCommandHistory(total_command_count-1);
+ ERROR_LOG(GSP, "Received command: id=%x", cmd.id);
+ }
+
+ /**
+ * @param lst command list which triggered this call
+ * @param is_new true if the command list was called for the first time
+ * @todo figure out how to make sure called functions don't keep references around beyond their life time
+ */
+ virtual void OnCommandListCalled(const PicaCommandList& lst, bool is_new)
+ {
+ ERROR_LOG(GSP, "Command list called: %d", (int)is_new);
+ }
+
+ protected:
+ const GraphicsDebugger* GetDebugger() const
+ {
+ return observed;
+ }
+
+ private:
+ GraphicsDebugger* observed;
+ bool in_destruction;
+
+ friend class GraphicsDebugger;
+ };
+
+ void GXCommandProcessed(u8* command_data)
+ {
+ gx_command_history.push_back(GSP_GPU::GXCommand());
+ GSP_GPU::GXCommand& cmd = gx_command_history[gx_command_history.size()-1];
+
+ const int cmd_length = sizeof(GSP_GPU::GXCommand);
+ memcpy(cmd.data, command_data, cmd_length);
+
+ ForEachObserver([this](DebuggerObserver* observer) {
+ observer->GXCommandProcessed(this->gx_command_history.size());
+ } );
+ }
+
+ void CommandListCalled(u32 address, u32* command_list, u32 size_in_words)
+ {
+ PicaCommandList cmdlist;
+ for (u32* parse_pointer = command_list; parse_pointer < command_list + size_in_words;)
+ {
+ const Pica::CommandHeader header = static_cast<Pica::CommandHeader>(parse_pointer[1]);
+
+ cmdlist.push_back(PicaCommand());
+ auto& cmd = cmdlist.back();
+
+ size_t size = 2 + header.extra_data_length;
+ size = (size + 1) / 2 * 2; // align to 8 bytes
+ cmd.reserve(size);
+ std::copy(parse_pointer, parse_pointer + size, std::back_inserter(cmd));
+
+ parse_pointer += size;
+ }
+
+ auto obj = std::pair<u32,PicaCommandList>(address, cmdlist);
+ auto it = std::find(command_lists.begin(), command_lists.end(), obj);
+ bool is_new = (it == command_lists.end());
+ if (is_new)
+ command_lists.push_back(obj);
+
+ ForEachObserver([&](DebuggerObserver* observer) {
+ observer->OnCommandListCalled(obj.second, is_new);
+ } );
+ }
+
+ const GSP_GPU::GXCommand& ReadGXCommandHistory(int index) const
+ {
+ // TODO: Is this thread-safe?
+ return gx_command_history[index];
+ }
+
+ const std::vector<std::pair<u32,PicaCommandList>>& GetCommandLists() const
+ {
+ return command_lists;
+ }
+
+ void RegisterObserver(DebuggerObserver* observer)
+ {
+ // TODO: Check for duplicates
+ observers.push_back(observer);
+ observer->observed = this;
+ }
+
+ void UnregisterObserver(DebuggerObserver* observer)
+ {
+ std::remove(observers.begin(), observers.end(), observer);
+ observer->observed = nullptr;
+ }
+
+private:
+ void ForEachObserver(std::function<void (DebuggerObserver*)> func)
+ {
+ std::for_each(observers.begin(),observers.end(), func);
+ }
+
+ std::vector<DebuggerObserver*> observers;
+
+ std::vector<GSP_GPU::GXCommand> gx_command_history;
+
+ // vector of pairs of command lists and their storage address
+ std::vector<std::pair<u32,PicaCommandList>> command_lists;
+};
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
new file mode 100644
index 000000000..f0fa3aba9
--- /dev/null
+++ b/src/video_core/pica.h
@@ -0,0 +1,130 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <initializer_list>
+#include <map>
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "common/register_set.h"
+
+namespace Pica {
+
+struct Regs {
+ enum Id : u32 {
+ ViewportSizeX = 0x41,
+ ViewportInvSizeX = 0x42,
+ ViewportSizeY = 0x43,
+ ViewportInvSizeY = 0x44,
+ ViewportCorner = 0x68,
+ DepthBufferFormat = 0x116,
+ ColorBufferFormat = 0x117,
+ DepthBufferAddress = 0x11C,
+ ColorBufferAddress = 0x11D,
+ ColorBufferSize = 0x11E,
+
+ VertexArrayBaseAddr = 0x200,
+ VertexDescriptor = 0x201, // 0x202
+ VertexAttributeOffset = 0x203, // 0x206,0x209,0x20C,0x20F,0x212,0x215,0x218,0x21B,0x21E,0x221,0x224
+ VertexAttributeInfo0 = 0x204, // 0x207,0x20A,0x20D,0x210,0x213,0x216,0x219,0x21C,0x21F,0x222,0x225
+ VertexAttributeInfo1 = 0x205, // 0x208,0x20B,0x20E,0x211,0x214,0x217,0x21A,0x21D,0x220,0x223,0x226
+
+ NumIds = 0x300,
+ };
+
+ template<Id id>
+ union Struct;
+};
+
+static inline Regs::Id VertexAttributeOffset(int n)
+{
+ return static_cast<Regs::Id>(0x203 + 3*n);
+}
+
+static inline Regs::Id VertexAttributeInfo0(int n)
+{
+ return static_cast<Regs::Id>(0x204 + 3*n);
+}
+
+static inline Regs::Id VertexAttributeInfo1(int n)
+{
+ return static_cast<Regs::Id>(0x205 + 3*n);
+}
+
+union CommandHeader {
+ CommandHeader(u32 h) : hex(h) {}
+
+ u32 hex;
+
+ BitField< 0, 16, Regs::Id> cmd_id;
+ BitField<16, 4, u32> parameter_mask;
+ BitField<20, 11, u32> extra_data_length;
+ BitField<31, 1, u32> group_commands;
+};
+
+static std::map<Regs::Id, const char*> command_names = {
+ {Regs::ViewportSizeX, "ViewportSizeX" },
+ {Regs::ViewportInvSizeX, "ViewportInvSizeX" },
+ {Regs::ViewportSizeY, "ViewportSizeY" },
+ {Regs::ViewportInvSizeY, "ViewportInvSizeY" },
+ {Regs::ViewportCorner, "ViewportCorner" },
+ {Regs::DepthBufferFormat, "DepthBufferFormat" },
+ {Regs::ColorBufferFormat, "ColorBufferFormat" },
+ {Regs::DepthBufferAddress, "DepthBufferAddress" },
+ {Regs::ColorBufferAddress, "ColorBufferAddress" },
+ {Regs::ColorBufferSize, "ColorBufferSize" },
+};
+
+template<>
+union Regs::Struct<Regs::ViewportSizeX> {
+ BitField<0, 24, u32> value;
+};
+
+template<>
+union Regs::Struct<Regs::ViewportSizeY> {
+ BitField<0, 24, u32> value;
+};
+
+template<>
+union Regs::Struct<Regs::VertexDescriptor> {
+ enum class Format : u64 {
+ BYTE = 0,
+ UBYTE = 1,
+ SHORT = 2,
+ FLOAT = 3,
+ };
+
+ BitField< 0, 2, Format> format0;
+ BitField< 2, 2, u64> size0; // number of elements minus 1
+ BitField< 4, 2, Format> format1;
+ BitField< 6, 2, u64> size1;
+ BitField< 8, 2, Format> format2;
+ BitField<10, 2, u64> size2;
+ BitField<12, 2, Format> format3;
+ BitField<14, 2, u64> size3;
+ BitField<16, 2, Format> format4;
+ BitField<18, 2, u64> size4;
+ BitField<20, 2, Format> format5;
+ BitField<22, 2, u64> size5;
+ BitField<24, 2, Format> format6;
+ BitField<26, 2, u64> size6;
+ BitField<28, 2, Format> format7;
+ BitField<30, 2, u64> size7;
+ BitField<32, 2, Format> format8;
+ BitField<34, 2, u64> size8;
+ BitField<36, 2, Format> format9;
+ BitField<38, 2, u64> size9;
+ BitField<40, 2, Format> format10;
+ BitField<42, 2, u64> size10;
+ BitField<44, 2, Format> format11;
+ BitField<46, 2, u64> size11;
+
+ BitField<48, 12, u64> attribute_mask;
+ BitField<60, 4, u64> num_attributes; // number of total attributes minus 1
+};
+
+
+} // namespace
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index bb5eb34aa..70af47c59 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -2,7 +2,7 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
-#include "core/hw/lcd.h"
+#include "core/hw/gpu.h"
#include "video_core/video_core.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
@@ -77,8 +77,8 @@ void RendererOpenGL::FlipFramebuffer(const u8* in, u8* out) {
*/
void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect) {
- FlipFramebuffer(LCD::GetFramebufferPointer(LCD::g_regs.framebuffer_top_left_1), m_xfb_top_flipped);
- FlipFramebuffer(LCD::GetFramebufferPointer(LCD::g_regs.framebuffer_sub_left_1), m_xfb_bottom_flipped);
+ FlipFramebuffer(GPU::GetFramebufferPointer(GPU::g_regs.framebuffer_top_left_1), m_xfb_top_flipped);
+ FlipFramebuffer(GPU::GetFramebufferPointer(GPU::g_regs.framebuffer_sub_left_1), m_xfb_bottom_flipped);
// Blit the top framebuffer
// ------------------------
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index cbd540bdf..3b8039de4 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -30,8 +30,11 @@ void Start() {
/// Initialize the video core
void Init(EmuWindow* emu_window) {
+
+#if EMU_PLATFORM == PLATFORM_MACOSX
// Known problem with GLEW prevents contexts above 2.x on OSX unless glewExperimental is enabled.
glewExperimental = GL_TRUE;
+#endif
g_emu_window = emu_window;
g_emu_window->MakeCurrent();
diff --git a/src/video_core/video_core.vcxproj b/src/video_core/video_core.vcxproj
index d8c53271e..d77be2bef 100644
--- a/src/video_core/video_core.vcxproj
+++ b/src/video_core/video_core.vcxproj
@@ -24,10 +24,12 @@
<ClCompile Include="video_core.cpp" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="gpu_debugger.h" />
+ <ClInclude Include="pica.h" />
<ClInclude Include="renderer_base.h" />
- <ClInclude Include="renderer_opengl\renderer_opengl.h" />
<ClInclude Include="utils.h" />
<ClInclude Include="video_core.h" />
+ <ClInclude Include="renderer_opengl\renderer_opengl.h" />
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
@@ -128,4 +130,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/video_core/video_core.vcxproj.filters b/src/video_core/video_core.vcxproj.filters
index 4eb2ef0a4..b89ac1ac4 100644
--- a/src/video_core/video_core.vcxproj.filters
+++ b/src/video_core/video_core.vcxproj.filters
@@ -16,6 +16,8 @@
<ClInclude Include="renderer_opengl\renderer_opengl.h">
<Filter>renderer_opengl</Filter>
</ClInclude>
+ <ClInclude Include="gpu_debugger.h" />
+ <ClInclude Include="pica.h" />
<ClInclude Include="renderer_base.h" />
<ClInclude Include="utils.h" />
<ClInclude Include="video_core.h" />
@@ -23,4 +25,4 @@
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/vsprops/qt.props b/vsprops/qt.props
index f8bb44776..f74a7b5ea 100644
--- a/vsprops/qt.props
+++ b/vsprops/qt.props
@@ -7,7 +7,7 @@
<QtBinaryDir>$(QTDIR)\bin\</QtBinaryDir>
</PropertyGroup>
<PropertyGroup>
- <IncludePath>$(QtIncludeDir);$(QtIncludeDir)QtGui;$(QtIncludeDir)QtCore;$(QtIncludeDir)Qt;$(QtIncludeDir)QtOpenGL;$(QtIncludeDir)QtANGLE;$(QtIncludeDir)QtWidgets;$(IncludePath)</IncludePath>
+ <IncludePath>$(QtIncludeDir);$(QtIncludeDir)QtGui;$(QtIncludeDir)QtCore;$(QtIncludeDir)Qt;$(QtIncludeDir)QtOpenGL;$(QtIncludeDir)QtANGLE;$(QtIncludeDir)QtWidgets;$(ProjectDir);$(IncludePath)</IncludePath>
<LibraryPath>$(QtLibraryDir);$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup>