diff options
Diffstat (limited to 'src')
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(¶m_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(¶m_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(¶m_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(¶m_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(¶m_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> |