diff options
m--------- | externals/boost | 0 | ||||
-rw-r--r-- | src/citra_qt/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/citra_qt/debugger/graphics_breakpoint_observer.cpp | 32 | ||||
-rw-r--r-- | src/citra_qt/debugger/graphics_breakpoint_observer.h | 33 | ||||
-rw-r--r-- | src/citra_qt/debugger/graphics_framebuffer.cpp | 27 | ||||
-rw-r--r-- | src/citra_qt/debugger/graphics_framebuffer.h | 24 | ||||
-rw-r--r-- | src/citra_qt/debugger/graphics_vertex_shader.cpp | 298 | ||||
-rw-r--r-- | src/citra_qt/debugger/graphics_vertex_shader.h | 51 | ||||
-rw-r--r-- | src/citra_qt/main.cpp | 6 | ||||
-rw-r--r-- | src/core/arm/dyncom/arm_dyncom.cpp | 7 | ||||
-rw-r--r-- | src/core/arm/interpreter/arminit.cpp | 40 | ||||
-rw-r--r-- | src/core/arm/skyeye_common/armdefs.h | 7 | ||||
-rw-r--r-- | src/core/arm/skyeye_common/armemu.h | 8 | ||||
-rw-r--r-- | src/core/hle/service/gsp_gpu.cpp | 97 |
14 files changed, 517 insertions, 117 deletions
diff --git a/externals/boost b/externals/boost -Subproject a1afc91d3aaa3da06bdbc13c78613e146665340 +Subproject 728a4d7d1c8b28355544ae829df9c4b5f28373c diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index bbc521f8a..586bc84b0 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -8,9 +8,11 @@ set(SRCS debugger/callstack.cpp debugger/disassembler.cpp debugger/graphics.cpp + debugger/graphics_breakpoint_observer.cpp debugger/graphics_breakpoints.cpp debugger/graphics_cmdlists.cpp debugger/graphics_framebuffer.cpp + debugger/graphics_vertex_shader.cpp debugger/ramview.cpp debugger/registers.cpp util/spinbox.cpp @@ -27,10 +29,12 @@ set(HEADERS debugger/callstack.h debugger/disassembler.h debugger/graphics.h + debugger/graphics_breakpoint_observer.h debugger/graphics_breakpoints.h debugger/graphics_breakpoints_p.h debugger/graphics_cmdlists.h debugger/graphics_framebuffer.h + debugger/graphics_vertex_shader.h debugger/ramview.h debugger/registers.h util/spinbox.h diff --git a/src/citra_qt/debugger/graphics_breakpoint_observer.cpp b/src/citra_qt/debugger/graphics_breakpoint_observer.cpp new file mode 100644 index 000000000..10ac1ebad --- /dev/null +++ b/src/citra_qt/debugger/graphics_breakpoint_observer.cpp @@ -0,0 +1,32 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QMetaType> + +#include "graphics_breakpoint_observer.h" + +BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, + const QString& title, QWidget* parent) + : QDockWidget(title, parent), BreakPointObserver(debug_context) +{ + qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); + + connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); + + // NOTE: This signal is emitted from a non-GUI thread, but connect() takes + // care of delaying its handling to the GUI thread. + connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), + this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), + Qt::BlockingQueuedConnection); +} + +void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) +{ + emit BreakPointHit(event, data); +} + +void BreakPointObserverDock::OnPicaResume() +{ + emit Resumed(); +} diff --git a/src/citra_qt/debugger/graphics_breakpoint_observer.h b/src/citra_qt/debugger/graphics_breakpoint_observer.h new file mode 100644 index 000000000..f0d3361f8 --- /dev/null +++ b/src/citra_qt/debugger/graphics_breakpoint_observer.h @@ -0,0 +1,33 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <QDockWidget> + +#include "video_core/debug_utils/debug_utils.h" + +/** + * Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots. + * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while + * the widget usually wants to perform reactions in the GUI thread. + */ +class BreakPointObserverDock : public QDockWidget, private Pica::DebugContext::BreakPointObserver { + Q_OBJECT + +public: + BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title, + QWidget* parent = nullptr); + + void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; + void OnPicaResume() override; + +private slots: + virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0; + virtual void OnResumed() = 0; + +signals: + void Resumed(); + void BreakPointHit(Pica::DebugContext::Event event, void* data); +}; diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp index 43c59738f..1ba60021f 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.cpp +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp @@ -6,7 +6,6 @@ #include <QComboBox> #include <QDebug> #include <QLabel> -#include <QMetaType> #include <QPushButton> #include <QSpinBox> @@ -17,32 +16,6 @@ #include "util/spinbox.h" -BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, - const QString& title, QWidget* parent) - : QDockWidget(title, parent), BreakPointObserver(debug_context) -{ - qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); - - connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); - - // NOTE: This signal is emitted from a non-GUI thread, but connect() takes - // care of delaying its handling to the GUI thread. - connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), - this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), - Qt::BlockingQueuedConnection); -} - -void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) -{ - emit BreakPointHit(event, data); -} - -void BreakPointObserverDock::OnPicaResume() -{ - emit Resumed(); -} - - GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent) : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent), diff --git a/src/citra_qt/debugger/graphics_framebuffer.h b/src/citra_qt/debugger/graphics_framebuffer.h index 56215761e..c6e293bc9 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.h +++ b/src/citra_qt/debugger/graphics_framebuffer.h @@ -6,7 +6,7 @@ #include <QDockWidget> -#include "video_core/debug_utils/debug_utils.h" +#include "graphics_breakpoint_observer.h" class QComboBox; class QLabel; @@ -14,28 +14,6 @@ class QSpinBox; class CSpinBox; -// Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots. -// This is because the Pica breakpoint callbacks are called from a non-GUI thread, while -// the widget usually wants to perform reactions in the GUI thread. -class BreakPointObserverDock : public QDockWidget, Pica::DebugContext::BreakPointObserver { - Q_OBJECT - -public: - BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title, - QWidget* parent = nullptr); - - void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; - void OnPicaResume() override; - -private slots: - virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0; - virtual void OnResumed() = 0; - -signals: - void Resumed(); - void BreakPointHit(Pica::DebugContext::Event event, void* data); -}; - class GraphicsFramebufferWidget : public BreakPointObserverDock { Q_OBJECT diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp new file mode 100644 index 000000000..06eaf0bf0 --- /dev/null +++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp @@ -0,0 +1,298 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <iomanip> +#include <sstream> + +#include <QBoxLayout> +#include <QTreeView> + +#include "video_core/vertex_shader.h" + +#include "graphics_vertex_shader.h" + +using nihstro::Instruction; +using nihstro::SourceRegister; +using nihstro::SwizzlePattern; + +GraphicsVertexShaderModel::GraphicsVertexShaderModel(QObject* parent): QAbstractItemModel(parent) { + +} + +QModelIndex GraphicsVertexShaderModel::index(int row, int column, const QModelIndex& parent) const { + return createIndex(row, column); +} + +QModelIndex GraphicsVertexShaderModel::parent(const QModelIndex& child) const { + return QModelIndex(); +} + +int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { + return 3; +} + +int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { + return info.code.size(); +} + +QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { + switch(role) { + case Qt::DisplayRole: + { + if (section == 0) { + return tr("Offset"); + } else if (section == 1) { + return tr("Raw"); + } else if (section == 2) { + return tr("Disassembly"); + } + + break; + } + } + + return QVariant(); +} + +QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { + switch (role) { + case Qt::DisplayRole: + { + switch (index.column()) { + case 0: + if (info.HasLabel(index.row())) + return QString::fromStdString(info.GetLabel(index.row())); + + return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0')); + + case 1: + return QString("%1").arg(info.code[index.row()].hex, 8, 16, QLatin1Char('0')); + + case 2: + { + std::stringstream output; + output.flags(std::ios::hex); + + Instruction instr = info.code[index.row()]; + const SwizzlePattern& swizzle = info.swizzle_info[instr.common.operand_desc_id].pattern; + + // longest known instruction name: "setemit " + output << std::setw(8) << std::left << instr.opcode.GetInfo().name; + + // e.g. "-c92.xyzw" + static auto print_input = [](std::stringstream& output, const SourceRegister& input, + bool negate, const std::string& swizzle_mask) { + output << std::setw(4) << std::right << (negate ? "-" : "") + input.GetName(); + output << "." << swizzle_mask; + }; + + // e.g. "-c92[a0.x].xyzw" + static auto print_input_indexed = [](std::stringstream& output, const SourceRegister& input, + bool negate, const std::string& swizzle_mask, + const std::string& address_register_name) { + std::string relative_address; + if (!address_register_name.empty()) + relative_address = "[" + address_register_name + "]"; + + output << std::setw(10) << std::right << (negate ? "-" : "") + input.GetName() + relative_address; + output << "." << swizzle_mask; + }; + + // Use print_input or print_input_indexed depending on whether relative addressing is used or not. + static auto print_input_indexed_compact = [](std::stringstream& output, const SourceRegister& input, + bool negate, const std::string& swizzle_mask, + const std::string& address_register_name) { + if (address_register_name.empty()) + print_input(output, input, negate, swizzle_mask); + else + print_input_indexed(output, input, negate, swizzle_mask, address_register_name); + }; + + switch (instr.opcode.GetInfo().type) { + case Instruction::OpCodeType::Trivial: + // Nothing to do here + break; + + case Instruction::OpCodeType::Arithmetic: + { + // Use custom code for special instructions + switch (instr.opcode.EffectiveOpCode()) { + case Instruction::OpCode::CMP: + { + // NOTE: CMP always writes both cc components, so we do not consider the dest mask here. + output << std::setw(4) << std::right << "cc."; + output << "xy "; + + SourceRegister src1 = instr.common.GetSrc1(false); + SourceRegister src2 = instr.common.GetSrc2(false); + + print_input_indexed_compact(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), instr.common.AddressRegisterName()); + output << " " << instr.common.compare_op.ToString(instr.common.compare_op.x) << " "; + print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false).substr(0,1)); + + output << ", "; + + print_input_indexed_compact(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), instr.common.AddressRegisterName()); + output << " " << instr.common.compare_op.ToString(instr.common.compare_op.y) << " "; + print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false).substr(1,1)); + + break; + } + + default: + { + bool src_is_inverted = 0 != (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::SrcInversed); + + if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Dest) { + // e.g. "r12.xy__" + output << std::setw(4) << std::right << instr.common.dest.GetName() + "."; + output << swizzle.DestMaskToString(); + } else if (instr.opcode.GetInfo().subtype == Instruction::OpCodeInfo::MOVA) { + output << std::setw(4) << std::right << "a0."; + output << swizzle.DestMaskToString(); + } else { + output << " "; + } + output << " "; + + if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Src1) { + SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); + print_input_indexed(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), instr.common.AddressRegisterName()); + } else { + output << " "; + } + + // TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1 + if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Src2) { + SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); + print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false)); + } + break; + } + } + + break; + } + + case Instruction::OpCodeType::Conditional: + { + switch (instr.opcode.EffectiveOpCode()) { + case Instruction::OpCode::LOOP: + output << "(unknown instruction format)"; + break; + + default: + output << "if "; + + if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasCondition) { + const char* ops[] = { + " || ", " && ", "", "" + }; + if (instr.flow_control.op != instr.flow_control.JustY) + output << ((!instr.flow_control.refx) ? "!" : " ") << "cc.x"; + + output << ops[instr.flow_control.op]; + + if (instr.flow_control.op != instr.flow_control.JustX) + output << ((!instr.flow_control.refy) ? "!" : " ") << "cc.y"; + + output << " "; + } else if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasUniformIndex) { + output << "b" << instr.flow_control.bool_uniform_id << " "; + } + + u32 target_addr = instr.flow_control.dest_offset; + u32 target_addr_else = instr.flow_control.dest_offset; + + if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasAlternative) { + output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " "; + } else if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasExplicitDest) { + output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " "; + } else { + // TODO: Handle other cases + } + + if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasFinishPoint) { + output << "(return on " << std::setw(4) << std::right << std::setfill('0') + << 4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions << ")"; + } + + break; + } + break; + } + + default: + output << "(unknown instruction format)"; + break; + } + + return QString::fromLatin1(output.str().c_str()); + } + + default: + break; + } + } + + case Qt::FontRole: + return QFont("monospace"); + + default: + break; + } + + return QVariant(); +} + +void GraphicsVertexShaderModel::OnUpdate() +{ + beginResetModel(); + + info.Clear(); + + for (auto instr : Pica::VertexShader::GetShaderBinary()) + info.code.push_back({instr}); + + for (auto pattern : Pica::VertexShader::GetSwizzlePatterns()) + info.swizzle_info.push_back({pattern}); + + info.labels.insert({Pica::registers.vs_main_offset, "main"}); + + endResetModel(); +} + + +GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context, + QWidget* parent) + : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { + setObjectName("PicaVertexShader"); + + auto binary_model = new GraphicsVertexShaderModel(this); + auto binary_list = new QTreeView; + binary_list->setModel(binary_model); + binary_list->setRootIsDecorated(false); + binary_list->setAlternatingRowColors(true); + + connect(this, SIGNAL(Update()), binary_model, SLOT(OnUpdate())); + + auto main_widget = new QWidget; + auto main_layout = new QVBoxLayout; + { + auto sub_layout = new QHBoxLayout; + sub_layout->addWidget(binary_list); + main_layout->addLayout(sub_layout); + } + main_widget->setLayout(main_layout); + setWidget(main_widget); +} + +void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { + emit Update(); + widget()->setEnabled(true); +} + +void GraphicsVertexShaderWidget::OnResumed() { + widget()->setEnabled(false); +} diff --git a/src/citra_qt/debugger/graphics_vertex_shader.h b/src/citra_qt/debugger/graphics_vertex_shader.h new file mode 100644 index 000000000..38339dc05 --- /dev/null +++ b/src/citra_qt/debugger/graphics_vertex_shader.h @@ -0,0 +1,51 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <QAbstractListModel> + +#include "graphics_breakpoint_observer.h" + +#include "nihstro/parser_shbin.h" + +class GraphicsVertexShaderModel : public QAbstractItemModel { + Q_OBJECT + +public: + GraphicsVertexShaderModel(QObject* parent); + + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& child) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + +public slots: + void OnUpdate(); + +private: + nihstro::ShaderInfo info; +}; + +class GraphicsVertexShaderWidget : public BreakPointObserverDock { + Q_OBJECT + + using Event = Pica::DebugContext::Event; + +public: + GraphicsVertexShaderWidget(std::shared_ptr<Pica::DebugContext> debug_context, + QWidget* parent = nullptr); + +private slots: + void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; + void OnResumed() override; + +signals: + void Update(); + +private: + +}; diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 653ffec75..881c7d337 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -34,6 +34,7 @@ #include "debugger/graphics_breakpoints.h" #include "debugger/graphics_cmdlists.h" #include "debugger/graphics_framebuffer.h" +#include "debugger/graphics_vertex_shader.h" #include "core/settings.h" #include "core/system.h" @@ -84,6 +85,10 @@ GMainWindow::GMainWindow() addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget); graphicsFramebufferWidget->hide(); + auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this); + addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); + graphicsVertexShaderWidget->hide(); + QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); debug_menu->addAction(disasmWidget->toggleViewAction()); debug_menu->addAction(registersWidget->toggleViewAction()); @@ -92,6 +97,7 @@ GMainWindow::GMainWindow() debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); + debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); // Set default UI state // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index 1977112dd..c4af85242 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -18,10 +18,7 @@ const static cpu_config_t s_arm11_cpu_info = { ARM_DynCom::ARM_DynCom() { state = std::unique_ptr<ARMul_State>(new ARMul_State); - ARMul_EmulateInit(); - memset(state.get(), 0, sizeof(ARMul_State)); - - ARMul_NewState((ARMul_State*)state.get()); + ARMul_NewState(state.get()); state->abort_model = ABORT_BASE_RESTORED; state->cpu = (cpu_config_t*)&s_arm11_cpu_info; @@ -41,8 +38,6 @@ ARM_DynCom::ARM_DynCom() { state->NirqSig = HIGH; VFPInit(state.get()); // Initialize the VFP - - ARMul_EmulateInit(); } ARM_DynCom::~ARM_DynCom() { diff --git a/src/core/arm/interpreter/arminit.cpp b/src/core/arm/interpreter/arminit.cpp index e7545728e..0c0ce6c91 100644 --- a/src/core/arm/interpreter/arminit.cpp +++ b/src/core/arm/interpreter/arminit.cpp @@ -19,46 +19,6 @@ #include "core/arm/skyeye_common/armemu.h" /***************************************************************************\ -* Definitions for the emulator architecture * -\***************************************************************************/ - -void ARMul_EmulateInit(); -ARMul_State* ARMul_NewState(ARMul_State* state); -void ARMul_Reset (ARMul_State* state); - -unsigned ARMul_MultTable[32] = { - 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, - 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16 -}; -ARMword ARMul_ImmedTable[4096]; // immediate DP LHS values -char ARMul_BitList[256]; // number of bits in a byte table - -/***************************************************************************\ -* Call this routine once to set up the emulator's tables. * -\***************************************************************************/ -void ARMul_EmulateInit() -{ - unsigned int i, j; - - // the values of 12 bit dp rhs's - for (i = 0; i < 4096; i++) { - ARMul_ImmedTable[i] = ROTATER (i & 0xffL, (i >> 7L) & 0x1eL); - } - - // how many bits in LSM - for (i = 0; i < 256; ARMul_BitList[i++] = 0); - for (j = 1; j < 256; j <<= 1) - for (i = 0; i < 256; i++) - if ((i & j) > 0) - ARMul_BitList[i]++; - - // you always need 4 times these values - for (i = 0; i < 256; i++) - ARMul_BitList[i] *= 4; - -} - -/***************************************************************************\ * Returns a new instantiation of the ARMulator's state * \***************************************************************************/ ARMul_State* ARMul_NewState(ARMul_State* state) diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h index 012c43c61..02f54f385 100644 --- a/src/core/arm/skyeye_common/armdefs.h +++ b/src/core/arm/skyeye_common/armdefs.h @@ -294,14 +294,7 @@ enum { /***************************************************************************\ * Definitons of things in the emulator * \***************************************************************************/ -#ifdef __cplusplus -extern "C" { -#endif -extern void ARMul_EmulateInit(); extern void ARMul_Reset(ARMul_State* state); -#ifdef __cplusplus - } -#endif extern ARMul_State* ARMul_NewState(ARMul_State* state); /***************************************************************************\ diff --git a/src/core/arm/skyeye_common/armemu.h b/src/core/arm/skyeye_common/armemu.h index 5d4c06837..2467f4319 100644 --- a/src/core/arm/skyeye_common/armemu.h +++ b/src/core/arm/skyeye_common/armemu.h @@ -95,14 +95,6 @@ enum { #define FLUSHPIPE state->NextInstr |= PRIMEPIPE -// Macro to rotate n right by b bits. -#define ROTATER(n, b) (((n) >> (b)) | ((n) << (32 - (b)))) - -// Stuff that is shared across modes. -extern unsigned ARMul_MultTable[]; // Number of I cycles for a mult. -extern ARMword ARMul_ImmedTable[]; // Immediate DP LHS values. -extern char ARMul_BitList[]; // Number of bits in a byte table. - // Coprocessor support functions. extern void ARMul_CoProInit(ARMul_State*); extern void ARMul_CoProExit(ARMul_State*); diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 4c3ac845b..dcc1b6942 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -48,20 +48,42 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr)); } -static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { +/** + * Checks if the parameters in a register write call are valid and logs in the case that + * they are not + * @param base_address The first address in the sequence of registers that will be written + * @param size_in_bytes The number of registers that will be written + * @return true if the parameters are valid, false otherwise + */ +static bool CheckWriteParameters(u32 base_address, u32 size_in_bytes) { // TODO: Return proper error codes if (base_address + size_in_bytes >= 0x420000) { LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)", base_address, size_in_bytes); - return; + return false; } // size should be word-aligned if ((size_in_bytes % 4) != 0) { LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes); - return; + return false; } + return true; +} + +/** + * Writes sequential GSP GPU hardware registers using an array of source data + * + * @param base_address The address of the first register in the sequence + * @param size_in_bytes The number of registers to update (size of data) + * @param data A pointer to the source data + */ +static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { + // TODO: Return proper error codes + if (!CheckWriteParameters(base_address, size_in_bytes)) + return; + while (size_in_bytes > 0) { GPU::Write<u32>(base_address + 0x1EB00000, *data); @@ -71,17 +93,80 @@ static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { } } -/// Write a GSP GPU hardware register +/** + * GSP_GPU::WriteHWRegs service function + * + * Writes sequential GSP GPU hardware registers + * + * Inputs: + * 1 : address of first GPU register + * 2 : number of registers to write sequentially + * 4 : pointer to source data array + */ static void WriteHWRegs(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 reg_addr = cmd_buff[1]; u32 size = cmd_buff[2]; - u32* src = (u32*)Memory::GetPointer(cmd_buff[0x4]); + u32* src = (u32*)Memory::GetPointer(cmd_buff[4]); WriteHWRegs(reg_addr, size, src); } +/** + * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. + * For each register, the value is updated only where the mask is high + * + * @param base_address The address of the first register in the sequence + * @param size_in_bytes The number of registers to update (size of data) + * @param data A pointer to the source data to use for updates + * @param masks A pointer to the masks + */ +static void WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) { + // TODO: Return proper error codes + if (!CheckWriteParameters(base_address, size_in_bytes)) + return; + + while (size_in_bytes > 0) { + const u32 reg_address = base_address + 0x1EB00000; + + u32 reg_value; + GPU::Read<u32>(reg_value, reg_address); + + // Update the current value of the register only for set mask bits + reg_value = (reg_value & ~*masks) | (*data | *masks); + + GPU::Write<u32>(reg_address, reg_value); + + size_in_bytes -= 4; + ++data; + ++masks; + base_address += 4; + } +} + +/** + * GSP_GPU::WriteHWRegsWithMask service function + * + * Updates sequential GSP GPU hardware registers using masks + * + * Inputs: + * 1 : address of first GPU register + * 2 : number of registers to update sequentially + * 4 : pointer to source data array + * 6 : pointer to mask array + */ +static void WriteHWRegsWithMask(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 reg_addr = cmd_buff[1]; + u32 size = cmd_buff[2]; + + u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]); + u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]); + + WriteHWRegsWithMask(reg_addr, size, src_data, mask_data); +} + /// Read a GSP GPU hardware register static void ReadHWRegs(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -350,7 +435,7 @@ static void TriggerCmdReqQueue(Service::Interface* self) { const Interface::FunctionInfo FunctionTable[] = { {0x00010082, WriteHWRegs, "WriteHWRegs"}, - {0x00020084, nullptr, "WriteHWRegsWithMask"}, + {0x00020084, WriteHWRegsWithMask, "WriteHWRegsWithMask"}, {0x00030082, nullptr, "WriteHWRegRepeat"}, {0x00040080, ReadHWRegs, "ReadHWRegs"}, {0x00050200, SetBufferSwap, "SetBufferSwap"}, |