summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
87 files changed, 3769 insertions, 2134 deletions
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>