summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules6
-rw-r--r--README.md2
-rw-r--r--src/citra/emu_window/emu_window_glfw.cpp2
-rw-r--r--src/citra_qt/bootmanager.cpp103
-rw-r--r--src/citra_qt/bootmanager.h68
-rw-r--r--src/citra_qt/debugger/disassembler.cpp81
-rw-r--r--src/citra_qt/debugger/disassembler.h7
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints_p.h2
-rw-r--r--src/citra_qt/debugger/profiler.cpp4
-rw-r--r--src/citra_qt/debugger/profiler.h2
-rw-r--r--src/citra_qt/debugger/registers.cpp33
-rw-r--r--src/citra_qt/debugger/registers.h4
-rw-r--r--src/citra_qt/main.cpp125
-rw-r--r--src/citra_qt/main.h23
-rw-r--r--src/citra_qt/main.ui3
-rw-r--r--src/citra_qt/util/spinbox.cpp3
-rw-r--r--src/common/emu_window.cpp22
-rw-r--r--src/common/emu_window.h5
-rw-r--r--src/common/thread.h81
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp10
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp35
-rw-r--r--src/core/arm/interpreter/armcopro.cpp142
-rw-r--r--src/core/arm/interpreter/arminit.cpp39
-rw-r--r--src/core/arm/skyeye_common/armdefs.h83
-rw-r--r--src/core/arm/skyeye_common/armemu.h21
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp.cpp621
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp.h22
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp_helper.h3
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpsingle.cpp72
-rw-r--r--src/core/core.h3
-rw-r--r--src/core/core_timing.cpp10
-rw-r--r--src/core/file_sys/disk_archive.h2
-rw-r--r--src/core/hle/config_mem.cpp6
-rw-r--r--src/core/hle/config_mem.h2
-rw-r--r--src/core/hle/hle.cpp55
-rw-r--r--src/core/hle/hle.h27
-rw-r--r--src/core/hle/kernel/kernel.cpp11
-rw-r--r--src/core/hle/kernel/kernel.h5
-rw-r--r--src/core/hle/kernel/thread.cpp10
-rw-r--r--src/core/hle/kernel/timer.cpp5
-rw-r--r--src/core/hle/service/apt/apt.cpp17
-rw-r--r--src/core/hle/service/cfg/cfg.cpp6
-rw-r--r--src/core/hle/service/dsp_dsp.cpp28
-rw-r--r--src/core/hle/service/hid/hid.cpp22
-rw-r--r--src/core/hle/service/ir/ir.cpp6
-rw-r--r--src/core/hle/service/nwm_uds.cpp2
-rw-r--r--src/core/hle/service/ptm/ptm.cpp7
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.cpp2
-rw-r--r--src/core/hle/service/service.cpp43
-rw-r--r--src/core/hle/service/service.h55
-rw-r--r--src/core/hle/service/y2r_u.cpp2
-rw-r--r--src/core/hle/shared_page.cpp5
-rw-r--r--src/core/hle/shared_page.h2
-rw-r--r--src/core/hle/svc.cpp37
-rw-r--r--src/core/hle/svc.h2
-rw-r--r--src/core/hw/gpu.cpp8
-rw-r--r--src/core/hw/hw.cpp2
-rw-r--r--src/core/hw/lcd.cpp1
-rw-r--r--src/core/loader/ncch.cpp33
-rw-r--r--src/core/loader/ncch.h18
-rw-r--r--src/core/mem_map.cpp71
-rw-r--r--src/core/mem_map.h6
-rw-r--r--src/core/mem_map_funcs.cpp21
-rw-r--r--src/core/system.cpp12
-rw-r--r--src/core/system.h21
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp2
-rw-r--r--src/video_core/rasterizer.cpp4
69 files changed, 661 insertions, 1537 deletions
diff --git a/.gitmodules b/.gitmodules
index a9e0a5c1a..598e4c64d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,9 +1,9 @@
1[submodule "externals/inih/inih"] 1[submodule "inih"]
2 path = externals/inih/inih 2 path = externals/inih/inih
3 url = https://github.com/svn2github/inih 3 url = https://github.com/svn2github/inih
4[submodule "externals/boost"] 4[submodule "boost"]
5 path = externals/boost 5 path = externals/boost
6 url = https://github.com/citra-emu/ext-boost.git 6 url = https://github.com/citra-emu/ext-boost.git
7[submodule "externals/nihstro"] 7[submodule "nihstro"]
8 path = externals/nihstro 8 path = externals/nihstro
9 url = https://github.com/neobrain/nihstro.git 9 url = https://github.com/neobrain/nihstro.git
diff --git a/README.md b/README.md
index 40a380f50..af65f2b0e 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ Citra Emulator
5 5
6Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward. 6Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward.
7 7
8Citra is licensed under the GPLv2. Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project. 8Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project.
9 9
10For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/?channels=citra). 10For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/?channels=citra).
11 11
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp
index 997e3bc7d..f879ee7ca 100644
--- a/src/citra/emu_window/emu_window_glfw.cpp
+++ b/src/citra/emu_window/emu_window_glfw.cpp
@@ -31,7 +31,7 @@ void EmuWindow_GLFW::OnMouseButtonEvent(GLFWwindow* win, int button, int action,
31} 31}
32 32
33void EmuWindow_GLFW::OnCursorPosEvent(GLFWwindow* win, double x, double y) { 33void EmuWindow_GLFW::OnCursorPosEvent(GLFWwindow* win, double x, double y) {
34 GetEmuWindow(win)->TouchMoved(static_cast<unsigned>(x), static_cast<unsigned>(y)); 34 GetEmuWindow(win)->TouchMoved(static_cast<unsigned>(std::max(x, 0.0)), static_cast<unsigned>(std::max(y, 0.0)));
35} 35}
36 36
37/// Called by GLFW when a key event occurs 37/// Called by GLFW when a key event occurs
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index b81bd6167..a7f949411 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -10,6 +10,7 @@
10 10
11#include "common/common.h" 11#include "common/common.h"
12#include "bootmanager.h" 12#include "bootmanager.h"
13#include "main.h"
13 14
14#include "core/core.h" 15#include "core/core.h"
15#include "core/settings.h" 16#include "core/settings.h"
@@ -27,43 +28,33 @@
27#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" 28#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
28 29
29EmuThread::EmuThread(GRenderWindow* render_window) : 30EmuThread::EmuThread(GRenderWindow* render_window) :
30 filename(""), exec_cpu_step(false), cpu_running(false), 31 exec_step(false), running(false), stop_run(false), render_window(render_window) {
31 stop_run(false), render_window(render_window)
32{
33}
34 32
35void EmuThread::SetFilename(std::string filename) 33 connect(this, SIGNAL(started()), render_window, SLOT(moveContext()));
36{
37 this->filename = filename;
38} 34}
39 35
40void EmuThread::run() 36void EmuThread::run() {
41{
42 stop_run = false; 37 stop_run = false;
43 38
44 // holds whether the cpu was running during the last iteration, 39 // holds whether the cpu was running during the last iteration,
45 // so that the DebugModeLeft signal can be emitted before the 40 // so that the DebugModeLeft signal can be emitted before the
46 // next execution step 41 // next execution step
47 bool was_active = false; 42 bool was_active = false;
48 while (!stop_run) 43 while (!stop_run) {
49 { 44 if (running) {
50 if (cpu_running)
51 {
52 if (!was_active) 45 if (!was_active)
53 emit DebugModeLeft(); 46 emit DebugModeLeft();
54 47
55 Core::RunLoop(); 48 Core::RunLoop();
56 49
57 was_active = cpu_running || exec_cpu_step; 50 was_active = running || exec_step;
58 if (!was_active) 51 if (!was_active && !stop_run)
59 emit DebugModeEntered(); 52 emit DebugModeEntered();
60 } 53 } else if (exec_step) {
61 else if (exec_cpu_step)
62 {
63 if (!was_active) 54 if (!was_active)
64 emit DebugModeLeft(); 55 emit DebugModeLeft();
65 56
66 exec_cpu_step = false; 57 exec_step = false;
67 Core::SingleStep(); 58 Core::SingleStep();
68 emit DebugModeEntered(); 59 emit DebugModeEntered();
69 yieldCurrentThread(); 60 yieldCurrentThread();
@@ -71,47 +62,10 @@ void EmuThread::run()
71 was_active = false; 62 was_active = false;
72 } 63 }
73 } 64 }
74 render_window->moveContext();
75
76 Core::Stop();
77}
78
79void EmuThread::Stop()
80{
81 if (!isRunning())
82 {
83 LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning...");
84 return;
85 }
86 stop_run = true;
87 65
88 // Release emu threads from any breakpoints, so that this doesn't hang forever. 66 render_window->moveContext();
89 Pica::g_debug_context->ClearBreakpoints();
90
91 //core::g_state = core::SYS_DIE;
92
93 // TODO: Waiting here is just a bad workaround for retarded shutdown logic.
94 wait(1000);
95 if (isRunning())
96 {
97 LOG_WARNING(Frontend, "EmuThread still running, terminating...");
98 quit();
99
100 // TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam
101 // queued... This should be fixed.
102 wait(50000);
103 if (isRunning())
104 {
105 LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here...");
106 terminate();
107 }
108 }
109 LOG_INFO(Frontend, "EmuThread stopped");
110
111 System::Shutdown();
112} 67}
113 68
114
115// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context. 69// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context.
116// The corresponding functionality is handled in EmuThread instead 70// The corresponding functionality is handled in EmuThread instead
117class GGLWidgetInternal : public QGLWidget 71class GGLWidgetInternal : public QGLWidget
@@ -133,13 +87,9 @@ private:
133 GRenderWindow* parent; 87 GRenderWindow* parent;
134}; 88};
135 89
136EmuThread& GRenderWindow::GetEmuThread() 90GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) :
137{ 91 QWidget(parent), emu_thread(emu_thread), keyboard_id(0) {
138 return emu_thread;
139}
140 92
141GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0)
142{
143 std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); 93 std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
144 setWindowTitle(QString::fromStdString(window_title)); 94 setWindowTitle(QString::fromStdString(window_title));
145 95
@@ -160,7 +110,6 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this
160 layout->addWidget(child); 110 layout->addWidget(child);
161 layout->setMargin(0); 111 layout->setMargin(0);
162 setLayout(layout); 112 setLayout(layout);
163 connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext()));
164 113
165 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 114 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
166 115
@@ -180,29 +129,17 @@ void GRenderWindow::moveContext()
180 // We need to move GL context to the swapping thread in Qt5 129 // We need to move GL context to the swapping thread in Qt5
181#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) 130#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
182 // If the thread started running, move the GL Context to the new thread. Otherwise, move it back. 131 // If the thread started running, move the GL Context to the new thread. Otherwise, move it back.
183 child->context()->moveToThread((QThread::currentThread() == qApp->thread()) ? &emu_thread : qApp->thread()); 132 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread();
133 child->context()->moveToThread(thread);
184#endif 134#endif
185} 135}
186 136
187GRenderWindow::~GRenderWindow()
188{
189 if (emu_thread.isRunning())
190 emu_thread.Stop();
191}
192
193void GRenderWindow::SwapBuffers() 137void GRenderWindow::SwapBuffers()
194{ 138{
195 // MakeCurrent is already called in renderer_opengl 139 // MakeCurrent is already called in renderer_opengl
196 child->swapBuffers(); 140 child->swapBuffers();
197} 141}
198 142
199void GRenderWindow::closeEvent(QCloseEvent* event)
200{
201 if (emu_thread.isRunning())
202 emu_thread.Stop();
203 QWidget::closeEvent(event);
204}
205
206void GRenderWindow::MakeCurrent() 143void GRenderWindow::MakeCurrent()
207{ 144{
208 child->makeCurrent(); 145 child->makeCurrent();
@@ -288,7 +225,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent *event)
288void GRenderWindow::mouseMoveEvent(QMouseEvent *event) 225void GRenderWindow::mouseMoveEvent(QMouseEvent *event)
289{ 226{
290 auto pos = event->pos(); 227 auto pos = event->pos();
291 this->TouchMoved(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y())); 228 this->TouchMoved(static_cast<unsigned>(std::max(pos.x(), 0)), static_cast<unsigned>(std::max(pos.y(), 0)));
292} 229}
293 230
294void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) 231void GRenderWindow::mouseReleaseEvent(QMouseEvent *event)
@@ -335,3 +272,11 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height)
335void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { 272void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
336 setMinimumSize(minimal_size.first, minimal_size.second); 273 setMinimumSize(minimal_size.first, minimal_size.second);
337} 274}
275
276void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
277 this->emu_thread = emu_thread;
278}
279
280void GRenderWindow::OnEmulationStopping() {
281 emu_thread = nullptr;
282}
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index 288da45a1..715faf2d7 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -9,72 +9,58 @@
9 9
10#include "common/common.h" 10#include "common/common.h"
11#include "common/emu_window.h" 11#include "common/emu_window.h"
12#include "common/thread.h"
12 13
13class QScreen; 14class QScreen;
14class QKeyEvent; 15class QKeyEvent;
15 16
16class GRenderWindow; 17class GRenderWindow;
18class GMainWindow;
17 19
18class EmuThread : public QThread 20class EmuThread : public QThread
19{ 21{
20 Q_OBJECT 22 Q_OBJECT
21 23
22public: 24public:
23 /** 25 EmuThread(GRenderWindow* render_window);
24 * Set image filename
25 *
26 * @param filename
27 * @warning Only call when not running!
28 */
29 void SetFilename(std::string filename);
30 26
31 /** 27 /**
32 * Start emulation (on new thread) 28 * Start emulation (on new thread)
33 *
34 * @warning Only call when not running! 29 * @warning Only call when not running!
35 */ 30 */
36 void run() override; 31 void run() override;
37 32
38 /** 33 /**
39 * Allow the CPU to process a single instruction (if cpu is not running) 34 * Steps the emulation thread by a single CPU instruction (if the CPU is not already running)
40 *
41 * @note This function is thread-safe 35 * @note This function is thread-safe
42 */ 36 */
43 void ExecStep() { exec_cpu_step = true; } 37 void ExecStep() { exec_step = true; }
44 38
45 /** 39 /**
46 * Allow the CPU to continue processing instructions without interruption 40 * Sets whether the emulation thread is running or not
47 * 41 * @param running Boolean value, set the emulation thread to running if true
48 * @note This function is thread-safe 42 * @note This function is thread-safe
49 */ 43 */
50 void SetCpuRunning(bool running) { cpu_running = running; } 44 void SetRunning(bool running) { this->running = running; }
51 45
52 /** 46 /**
53 * Allow the CPU to continue processing instructions without interruption 47 * Check if the emulation thread is running or not
54 * 48 * @return True if the emulation thread is running, otherwise false
55 * @note This function is thread-safe 49 * @note This function is thread-safe
56 */ 50 */
57 bool IsCpuRunning() { return cpu_running; } 51 bool IsRunning() { return running; }
58
59 52
60public slots:
61 /** 53 /**
62 * Stop emulation and wait for the thread to finish. 54 * Requests for the emulation thread to stop running
63 *
64 * @details: This function will wait a second for the thread to finish; if it hasn't finished until then, we'll terminate() it and wait another second, hoping that it will be terminated by then.
65 * @note: This function is thread-safe.
66 */ 55 */
67 void Stop(); 56 void RequestStop() {
57 stop_run = true;
58 running = false;
59 };
68 60
69private: 61private:
70 friend class GRenderWindow; 62 bool exec_step;
71 63 bool running;
72 EmuThread(GRenderWindow* render_window);
73
74 std::string filename;
75
76 bool exec_cpu_step;
77 bool cpu_running;
78 std::atomic<bool> stop_run; 64 std::atomic<bool> stop_run;
79 65
80 GRenderWindow* render_window; 66 GRenderWindow* render_window;
@@ -100,10 +86,7 @@ class GRenderWindow : public QWidget, public EmuWindow
100 Q_OBJECT 86 Q_OBJECT
101 87
102public: 88public:
103 GRenderWindow(QWidget* parent = NULL); 89 GRenderWindow(QWidget* parent, EmuThread* emu_thread);
104 ~GRenderWindow();
105
106 void closeEvent(QCloseEvent*) override;
107 90
108 // EmuWindow implementation 91 // EmuWindow implementation
109 void SwapBuffers() override; 92 void SwapBuffers() override;
@@ -116,8 +99,6 @@ public:
116 void restoreGeometry(const QByteArray& geometry); // overridden 99 void restoreGeometry(const QByteArray& geometry); // overridden
117 QByteArray saveGeometry(); // overridden 100 QByteArray saveGeometry(); // overridden
118 101
119 EmuThread& GetEmuThread();
120
121 void keyPressEvent(QKeyEvent* event) override; 102 void keyPressEvent(QKeyEvent* event) override;
122 void keyReleaseEvent(QKeyEvent* event) override; 103 void keyReleaseEvent(QKeyEvent* event) override;
123 104
@@ -134,15 +115,18 @@ public:
134public slots: 115public slots:
135 void moveContext(); // overridden 116 void moveContext(); // overridden
136 117
118 void OnEmulationStarting(EmuThread* emu_thread);
119 void OnEmulationStopping();
120
137private: 121private:
138 void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; 122 void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
139 123
140 QGLWidget* child; 124 QGLWidget* child;
141 125
142 EmuThread emu_thread;
143
144 QByteArray geometry; 126 QByteArray geometry;
145 127
146 /// Device id of keyboard for use with KeyMap 128 /// Device id of keyboard for use with KeyMap
147 int keyboard_id; 129 int keyboard_id;
130
131 EmuThread* emu_thread;
148}; 132};
diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp
index f620687ae..08c6b49bd 100644
--- a/src/citra_qt/debugger/disassembler.cpp
+++ b/src/citra_qt/debugger/disassembler.cpp
@@ -18,8 +18,8 @@
18#include "core/arm/disassembler/arm_disasm.h" 18#include "core/arm/disassembler/arm_disasm.h"
19 19
20 20
21DisassemblerModel::DisassemblerModel(QObject* parent) : QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) { 21DisassemblerModel::DisassemblerModel(QObject* parent) :
22 22 QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) {
23} 23}
24 24
25int DisassemblerModel::columnCount(const QModelIndex& parent) const { 25int DisassemblerModel::columnCount(const QModelIndex& parent) const {
@@ -158,34 +158,28 @@ void DisassemblerModel::SetNextInstruction(unsigned int address) {
158 emit dataChanged(prev_index, prev_index); 158 emit dataChanged(prev_index, prev_index);
159} 159}
160 160
161DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread& emu_thread) : QDockWidget(parent), base_addr(0), emu_thread(emu_thread) 161DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) :
162{ 162 QDockWidget(parent), emu_thread(emu_thread), base_addr(0) {
163 disasm_ui.setupUi(this);
164 163
165 model = new DisassemblerModel(this); 164 disasm_ui.setupUi(this);
166 disasm_ui.treeView->setModel(model);
167 165
168 RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut); 166 RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut);
169 RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut); 167 RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut);
170 RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut); 168 RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut);
171 RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut); 169 RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut);
172 170
173 connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint()));
174 connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep())); 171 connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep()));
175 connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause())); 172 connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause()));
176 connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue())); 173 connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue()));
177 174
178 connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
179 model, SLOT(OnSelectionChanged(const QModelIndex&)));
180
181 connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop())); 175 connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop()));
182 connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep())); 176 connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep()));
183 connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto())); 177 connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto()));
184 connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint())); 178
179 setEnabled(false);
185} 180}
186 181
187void DisassemblerWidget::Init() 182void DisassemblerWidget::Init() {
188{
189 model->ParseFromAddress(Core::g_app_core->GetPC()); 183 model->ParseFromAddress(Core::g_app_core->GetPC());
190 184
191 disasm_ui.treeView->resizeColumnToContents(0); 185 disasm_ui.treeView->resizeColumnToContents(0);
@@ -197,25 +191,21 @@ void DisassemblerWidget::Init()
197 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); 191 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
198} 192}
199 193
200void DisassemblerWidget::OnContinue() 194void DisassemblerWidget::OnContinue() {
201{ 195 emu_thread->SetRunning(true);
202 emu_thread.SetCpuRunning(true);
203} 196}
204 197
205void DisassemblerWidget::OnStep() 198void DisassemblerWidget::OnStep() {
206{
207 OnStepInto(); // change later 199 OnStepInto(); // change later
208} 200}
209 201
210void DisassemblerWidget::OnStepInto() 202void DisassemblerWidget::OnStepInto() {
211{ 203 emu_thread->SetRunning(false);
212 emu_thread.SetCpuRunning(false); 204 emu_thread->ExecStep();
213 emu_thread.ExecStep();
214} 205}
215 206
216void DisassemblerWidget::OnPause() 207void DisassemblerWidget::OnPause() {
217{ 208 emu_thread->SetRunning(false);
218 emu_thread.SetCpuRunning(false);
219 209
220 // TODO: By now, the CPU might not have actually stopped... 210 // TODO: By now, the CPU might not have actually stopped...
221 if (Core::g_app_core) { 211 if (Core::g_app_core) {
@@ -223,17 +213,15 @@ void DisassemblerWidget::OnPause()
223 } 213 }
224} 214}
225 215
226void DisassemblerWidget::OnToggleStartStop() 216void DisassemblerWidget::OnToggleStartStop() {
227{ 217 emu_thread->SetRunning(!emu_thread->IsRunning());
228 emu_thread.SetCpuRunning(!emu_thread.IsCpuRunning());
229} 218}
230 219
231void DisassemblerWidget::OnDebugModeEntered() 220void DisassemblerWidget::OnDebugModeEntered() {
232{
233 ARMword next_instr = Core::g_app_core->GetPC(); 221 ARMword next_instr = Core::g_app_core->GetPC();
234 222
235 if (model->GetBreakPoints().IsAddressBreakPoint(next_instr)) 223 if (model->GetBreakPoints().IsAddressBreakPoint(next_instr))
236 emu_thread.SetCpuRunning(false); 224 emu_thread->SetRunning(false);
237 225
238 model->SetNextInstruction(next_instr); 226 model->SetNextInstruction(next_instr);
239 227
@@ -242,16 +230,35 @@ void DisassemblerWidget::OnDebugModeEntered()
242 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); 230 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
243} 231}
244 232
245void DisassemblerWidget::OnDebugModeLeft() 233void DisassemblerWidget::OnDebugModeLeft() {
246{
247
248} 234}
249 235
250int DisassemblerWidget::SelectedRow() 236int DisassemblerWidget::SelectedRow() {
251{
252 QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex(); 237 QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex();
253 if (!index.isValid()) 238 if (!index.isValid())
254 return -1; 239 return -1;
255 240
256 return disasm_ui.treeView->selectionModel()->currentIndex().row(); 241 return disasm_ui.treeView->selectionModel()->currentIndex().row();
257} 242}
243
244void DisassemblerWidget::OnEmulationStarting(EmuThread* emu_thread) {
245 this->emu_thread = emu_thread;
246
247 model = new DisassemblerModel(this);
248 disasm_ui.treeView->setModel(model);
249
250 connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
251 model, SLOT(OnSelectionChanged(const QModelIndex&)));
252 connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint()));
253 connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint()));
254
255 Init();
256 setEnabled(true);
257}
258
259void DisassemblerWidget::OnEmulationStopping() {
260 disasm_ui.treeView->setModel(nullptr);
261 delete model;
262 emu_thread = nullptr;
263 setEnabled(false);
264}
diff --git a/src/citra_qt/debugger/disassembler.h b/src/citra_qt/debugger/disassembler.h
index 5e19d7c51..45b0a7e08 100644
--- a/src/citra_qt/debugger/disassembler.h
+++ b/src/citra_qt/debugger/disassembler.h
@@ -51,7 +51,7 @@ class DisassemblerWidget : public QDockWidget
51 Q_OBJECT 51 Q_OBJECT
52 52
53public: 53public:
54 DisassemblerWidget(QWidget* parent, EmuThread& emu_thread); 54 DisassemblerWidget(QWidget* parent, EmuThread* emu_thread);
55 55
56 void Init(); 56 void Init();
57 57
@@ -65,6 +65,9 @@ public slots:
65 void OnDebugModeEntered(); 65 void OnDebugModeEntered();
66 void OnDebugModeLeft(); 66 void OnDebugModeLeft();
67 67
68 void OnEmulationStarting(EmuThread* emu_thread);
69 void OnEmulationStopping();
70
68private: 71private:
69 // returns -1 if no row is selected 72 // returns -1 if no row is selected
70 int SelectedRow(); 73 int SelectedRow();
@@ -75,5 +78,5 @@ private:
75 78
76 u32 base_addr; 79 u32 base_addr;
77 80
78 EmuThread& emu_thread; 81 EmuThread* emu_thread;
79}; 82};
diff --git a/src/citra_qt/debugger/graphics_breakpoints_p.h b/src/citra_qt/debugger/graphics_breakpoints_p.h
index 232bfc863..34e72e859 100644
--- a/src/citra_qt/debugger/graphics_breakpoints_p.h
+++ b/src/citra_qt/debugger/graphics_breakpoints_p.h
@@ -25,7 +25,7 @@ public:
25 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 25 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
26 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; 26 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
27 27
28 bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); 28 bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
29 29
30public slots: 30public slots:
31 void OnBreakPointHit(Pica::DebugContext::Event event); 31 void OnBreakPointHit(Pica::DebugContext::Event event);
diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp
index ae0568b6a..2ac1748b7 100644
--- a/src/citra_qt/debugger/profiler.cpp
+++ b/src/citra_qt/debugger/profiler.cpp
@@ -26,7 +26,7 @@ static QVariant GetDataForColumn(int col, const AggregatedDuration& duration)
26static const TimingCategoryInfo* GetCategoryInfo(int id) 26static const TimingCategoryInfo* GetCategoryInfo(int id)
27{ 27{
28 const auto& categories = GetProfilingManager().GetTimingCategoriesInfo(); 28 const auto& categories = GetProfilingManager().GetTimingCategoriesInfo();
29 if (id >= categories.size()) { 29 if ((size_t)id >= categories.size()) {
30 return nullptr; 30 return nullptr;
31 } else { 31 } else {
32 return &categories[id]; 32 return &categories[id];
@@ -98,7 +98,7 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const
98 const TimingCategoryInfo* info = GetCategoryInfo(index.row() - 2); 98 const TimingCategoryInfo* info = GetCategoryInfo(index.row() - 2);
99 return info != nullptr ? QString(info->name) : QVariant(); 99 return info != nullptr ? QString(info->name) : QVariant();
100 } else { 100 } else {
101 if (index.row() - 2 < results.time_per_category.size()) { 101 if (index.row() - 2 < (int)results.time_per_category.size()) {
102 return GetDataForColumn(index.column(), results.time_per_category[index.row() - 2]); 102 return GetDataForColumn(index.column(), results.time_per_category[index.row() - 2]);
103 } else { 103 } else {
104 return QVariant(); 104 return QVariant();
diff --git a/src/citra_qt/debugger/profiler.h b/src/citra_qt/debugger/profiler.h
index a6d87aa0f..fabf279b8 100644
--- a/src/citra_qt/debugger/profiler.h
+++ b/src/citra_qt/debugger/profiler.h
@@ -18,7 +18,7 @@ class ProfilerModel : public QAbstractItemModel
18public: 18public:
19 ProfilerModel(QObject* parent); 19 ProfilerModel(QObject* parent);
20 20
21 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 21 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
22 QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; 22 QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
23 QModelIndex parent(const QModelIndex& child) const override; 23 QModelIndex parent(const QModelIndex& child) const override;
24 int columnCount(const QModelIndex& parent = QModelIndex()) const override; 24 int columnCount(const QModelIndex& parent = QModelIndex()) const override;
diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp
index ab3666156..5527a2afd 100644
--- a/src/citra_qt/debugger/registers.cpp
+++ b/src/citra_qt/debugger/registers.cpp
@@ -7,8 +7,7 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
9 9
10RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) 10RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
11{
12 cpu_regs_ui.setupUi(this); 11 cpu_regs_ui.setupUi(this);
13 12
14 tree = cpu_regs_ui.treeWidget; 13 tree = cpu_regs_ui.treeWidget;
@@ -18,8 +17,7 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent)
18 registers->setExpanded(true); 17 registers->setExpanded(true);
19 CSPR->setExpanded(true); 18 CSPR->setExpanded(true);
20 19
21 for (int i = 0; i < 16; ++i) 20 for (int i = 0; i < 16; ++i) {
22 {
23 QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i, 2, 10, QLatin1Char('0')))); 21 QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i, 2, 10, QLatin1Char('0'))));
24 registers->addChild(child); 22 registers->addChild(child);
25 } 23 }
@@ -39,12 +37,16 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent)
39 CSPR->addChild(new QTreeWidgetItem(QStringList("C"))); 37 CSPR->addChild(new QTreeWidgetItem(QStringList("C")));
40 CSPR->addChild(new QTreeWidgetItem(QStringList("Z"))); 38 CSPR->addChild(new QTreeWidgetItem(QStringList("Z")));
41 CSPR->addChild(new QTreeWidgetItem(QStringList("N"))); 39 CSPR->addChild(new QTreeWidgetItem(QStringList("N")));
40
41 setEnabled(false);
42} 42}
43 43
44void RegistersWidget::OnDebugModeEntered() 44void RegistersWidget::OnDebugModeEntered() {
45{
46 ARM_Interface* app_core = Core::g_app_core; 45 ARM_Interface* app_core = Core::g_app_core;
47 46
47 if (app_core == nullptr)
48 return;
49
48 for (int i = 0; i < 16; ++i) 50 for (int i = 0; i < 16; ++i)
49 registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0'))); 51 registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0')));
50 52
@@ -66,7 +68,22 @@ void RegistersWidget::OnDebugModeEntered()
66 CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than 68 CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than
67} 69}
68 70
69void RegistersWidget::OnDebugModeLeft() 71void RegistersWidget::OnDebugModeLeft() {
70{ 72}
73
74void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) {
75 setEnabled(true);
76}
77
78void RegistersWidget::OnEmulationStopping() {
79 // Reset widget text
80 for (int i = 0; i < 16; ++i)
81 registers->child(i)->setText(1, QString(""));
82
83 for (int i = 0; i < 15; ++i)
84 CSPR->child(i)->setText(1, QString(""));
85
86 CSPR->setText(1, QString(""));
71 87
88 setEnabled(false);
72} 89}
diff --git a/src/citra_qt/debugger/registers.h b/src/citra_qt/debugger/registers.h
index bf8955625..68e3fb908 100644
--- a/src/citra_qt/debugger/registers.h
+++ b/src/citra_qt/debugger/registers.h
@@ -8,6 +8,7 @@
8#include <QTreeWidgetItem> 8#include <QTreeWidgetItem>
9 9
10class QTreeWidget; 10class QTreeWidget;
11class EmuThread;
11 12
12class RegistersWidget : public QDockWidget 13class RegistersWidget : public QDockWidget
13{ 14{
@@ -20,6 +21,9 @@ public slots:
20 void OnDebugModeEntered(); 21 void OnDebugModeEntered();
21 void OnDebugModeLeft(); 22 void OnDebugModeLeft();
22 23
24 void OnEmulationStarting(EmuThread* emu_thread);
25 void OnEmulationStopping();
26
23private: 27private:
24 Ui::ARMRegisters cpu_regs_ui; 28 Ui::ARMRegisters cpu_regs_ui;
25 29
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index e5ca04124..dd0e4de8f 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -15,6 +15,7 @@
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/logging/backend.h" 16#include "common/logging/backend.h"
17#include "common/logging/filter.h" 17#include "common/logging/filter.h"
18#include "common/make_unique.h"
18#include "common/platform.h" 19#include "common/platform.h"
19#include "common/scope_exit.h" 20#include "common/scope_exit.h"
20 21
@@ -46,7 +47,7 @@
46 47
47#include "version.h" 48#include "version.h"
48 49
49GMainWindow::GMainWindow() 50GMainWindow::GMainWindow() : emu_thread(nullptr)
50{ 51{
51 Pica::g_debug_context = Pica::DebugContext::Construct(); 52 Pica::g_debug_context = Pica::DebugContext::Construct();
52 53
@@ -55,14 +56,14 @@ GMainWindow::GMainWindow()
55 ui.setupUi(this); 56 ui.setupUi(this);
56 statusBar()->hide(); 57 statusBar()->hide();
57 58
58 render_window = new GRenderWindow; 59 render_window = new GRenderWindow(this, emu_thread.get());
59 render_window->hide(); 60 render_window->hide();
60 61
61 profilerWidget = new ProfilerWidget(this); 62 profilerWidget = new ProfilerWidget(this);
62 addDockWidget(Qt::BottomDockWidgetArea, profilerWidget); 63 addDockWidget(Qt::BottomDockWidgetArea, profilerWidget);
63 profilerWidget->hide(); 64 profilerWidget->hide();
64 65
65 disasmWidget = new DisassemblerWidget(this, render_window->GetEmuThread()); 66 disasmWidget = new DisassemblerWidget(this, emu_thread.get());
66 addDockWidget(Qt::BottomDockWidgetArea, disasmWidget); 67 addDockWidget(Qt::BottomDockWidgetArea, disasmWidget);
67 disasmWidget->hide(); 68 disasmWidget->hide();
68 69
@@ -138,14 +139,12 @@ GMainWindow::GMainWindow()
138 connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); 139 connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
139 connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog())); 140 connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog()));
140 141
141 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues 142 connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*)));
142 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 143 connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
143 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 144 connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget, SLOT(OnEmulationStarting(EmuThread*)));
144 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 145 connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping()));
145 146 connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*)));
146 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); 147 connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping()));
147 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
148 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
149 148
150 // Setup hotkeys 149 // Setup hotkeys
151 RegisterHotkey("Main Window", "Load File", QKeySequence::Open); 150 RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
@@ -196,32 +195,76 @@ void GMainWindow::OnDisplayTitleBars(bool show)
196 } 195 }
197} 196}
198 197
199void GMainWindow::BootGame(std::string filename) 198void GMainWindow::BootGame(std::string filename) {
200{
201 LOG_INFO(Frontend, "Citra starting...\n"); 199 LOG_INFO(Frontend, "Citra starting...\n");
200
201 // Initialize the core emulation
202 System::Init(render_window); 202 System::Init(render_window);
203 203
204 // Load a game or die... 204 // Load the game
205 if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { 205 if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) {
206 LOG_CRITICAL(Frontend, "Failed to load ROM!"); 206 LOG_CRITICAL(Frontend, "Failed to load ROM!");
207 System::Shutdown();
208 return;
207 } 209 }
208 210
209 disasmWidget->Init(); 211 // Create and start the emulation thread
212 emu_thread = Common::make_unique<EmuThread>(render_window);
213 emit EmulationStarting(emu_thread.get());
214 emu_thread->start();
215
216 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues
217 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
218 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
219 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
220 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
221 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
222 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
223
224 // Update the GUI
210 registersWidget->OnDebugModeEntered(); 225 registersWidget->OnDebugModeEntered();
211 callstackWidget->OnDebugModeEntered(); 226 callstackWidget->OnDebugModeEntered();
212
213 render_window->GetEmuThread().SetFilename(filename);
214 render_window->GetEmuThread().start();
215
216 render_window->show(); 227 render_window->show();
228
217 OnStartGame(); 229 OnStartGame();
218} 230}
219 231
232void GMainWindow::ShutdownGame() {
233 emu_thread->RequestStop();
234
235 // Release emu threads from any breakpoints
236 // This belongs after RequestStop() and before wait() because if emulation stops on a GPU
237 // breakpoint after (or before) RequestStop() is called, the emulation would never be able
238 // to continue out to the main loop and terminate. Thus wait() would hang forever.
239 // TODO(bunnei): This function is not thread safe, but it's being used as if it were
240 Pica::g_debug_context->ClearBreakpoints();
241
242 emit EmulationStopping();
243
244 // Wait for emulation thread to complete and delete it
245 emu_thread->wait();
246 emu_thread = nullptr;
247
248 // Shutdown the core emulation
249 System::Shutdown();
250
251 // Update the GUI
252 ui.action_Start->setEnabled(false);
253 ui.action_Pause->setEnabled(false);
254 ui.action_Stop->setEnabled(false);
255 render_window->hide();
256}
257
220void GMainWindow::OnMenuLoadFile() 258void GMainWindow::OnMenuLoadFile()
221{ 259{
222 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)")); 260 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)"));
223 if (filename.size()) 261 if (filename.size()) {
224 BootGame(filename.toLatin1().data()); 262 // Shutdown previous session if the emu thread is still active...
263 if (emu_thread != nullptr)
264 ShutdownGame();
265
266 BootGame(filename.toLatin1().data());
267 }
225} 268}
226 269
227void GMainWindow::OnMenuLoadSymbolMap() { 270void GMainWindow::OnMenuLoadSymbolMap() {
@@ -232,7 +275,7 @@ void GMainWindow::OnMenuLoadSymbolMap() {
232 275
233void GMainWindow::OnStartGame() 276void GMainWindow::OnStartGame()
234{ 277{
235 render_window->GetEmuThread().SetCpuRunning(true); 278 emu_thread->SetRunning(true);
236 279
237 ui.action_Start->setEnabled(false); 280 ui.action_Start->setEnabled(false);
238 ui.action_Pause->setEnabled(true); 281 ui.action_Pause->setEnabled(true);
@@ -241,21 +284,15 @@ void GMainWindow::OnStartGame()
241 284
242void GMainWindow::OnPauseGame() 285void GMainWindow::OnPauseGame()
243{ 286{
244 render_window->GetEmuThread().SetCpuRunning(false); 287 emu_thread->SetRunning(false);
245 288
246 ui.action_Start->setEnabled(true); 289 ui.action_Start->setEnabled(true);
247 ui.action_Pause->setEnabled(false); 290 ui.action_Pause->setEnabled(false);
248 ui.action_Stop->setEnabled(true); 291 ui.action_Stop->setEnabled(true);
249} 292}
250 293
251void GMainWindow::OnStopGame() 294void GMainWindow::OnStopGame() {
252{ 295 ShutdownGame();
253 render_window->GetEmuThread().SetCpuRunning(false);
254 // TODO: Shutdown core
255
256 ui.action_Start->setEnabled(true);
257 ui.action_Pause->setEnabled(false);
258 ui.action_Stop->setEnabled(false);
259} 296}
260 297
261void GMainWindow::OnOpenHotkeysDialog() 298void GMainWindow::OnOpenHotkeysDialog()
@@ -265,24 +302,22 @@ void GMainWindow::OnOpenHotkeysDialog()
265} 302}
266 303
267 304
268void GMainWindow::ToggleWindowMode() 305void GMainWindow::ToggleWindowMode() {
269{ 306 if (ui.action_Single_Window_Mode->isChecked()) {
270 bool enable = ui.action_Single_Window_Mode->isChecked(); 307 // Render in the main window...
271 if (!enable && render_window->parent() != nullptr)
272 {
273 ui.horizontalLayout->removeWidget(render_window);
274 render_window->setParent(nullptr);
275 render_window->setVisible(true);
276 render_window->RestoreGeometry();
277 render_window->setFocusPolicy(Qt::NoFocus);
278 }
279 else if (enable && render_window->parent() == nullptr)
280 {
281 render_window->BackupGeometry(); 308 render_window->BackupGeometry();
282 ui.horizontalLayout->addWidget(render_window); 309 ui.horizontalLayout->addWidget(render_window);
283 render_window->setVisible(true); 310 render_window->setVisible(true);
284 render_window->setFocusPolicy(Qt::ClickFocus); 311 render_window->setFocusPolicy(Qt::ClickFocus);
285 render_window->setFocus(); 312 render_window->setFocus();
313
314 } else {
315 // Render in a separate window...
316 ui.horizontalLayout->removeWidget(render_window);
317 render_window->setParent(nullptr);
318 render_window->setVisible(true);
319 render_window->RestoreGeometry();
320 render_window->setFocusPolicy(Qt::NoFocus);
286 } 321 }
287} 322}
288 323
@@ -303,6 +338,8 @@ void GMainWindow::closeEvent(QCloseEvent* event)
303 settings.setValue("firstStart", false); 338 settings.setValue("firstStart", false);
304 SaveHotkeys(settings); 339 SaveHotkeys(settings);
305 340
341 ShutdownGame();
342
306 render_window->close(); 343 render_window->close();
307 344
308 QWidget::closeEvent(event); 345 QWidget::closeEvent(event);
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 9b57c5772..3e29534fb 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -5,12 +5,14 @@
5#ifndef _CITRA_QT_MAIN_HXX_ 5#ifndef _CITRA_QT_MAIN_HXX_
6#define _CITRA_QT_MAIN_HXX_ 6#define _CITRA_QT_MAIN_HXX_
7 7
8#include <memory>
8#include <QMainWindow> 9#include <QMainWindow>
9 10
10#include "ui_main.h" 11#include "ui_main.h"
11 12
12class GImageInfo; 13class GImageInfo;
13class GRenderWindow; 14class GRenderWindow;
15class EmuThread;
14class ProfilerWidget; 16class ProfilerWidget;
15class DisassemblerWidget; 17class DisassemblerWidget;
16class RegistersWidget; 18class RegistersWidget;
@@ -34,8 +36,27 @@ public:
34 GMainWindow(); 36 GMainWindow();
35 ~GMainWindow(); 37 ~GMainWindow();
36 38
39signals:
40
41 /**
42 * Signal that is emitted when a new EmuThread has been created and an emulation session is
43 * about to start. At this time, the core system emulation has been initialized, and all
44 * emulation handles and memory should be valid.
45 *
46 * @param emu_thread Pointer to the newly created EmuThread (to be used by widgets that need to
47 * access/change emulation state).
48 */
49 void EmulationStarting(EmuThread* emu_thread);
50
51 /**
52 * Signal that is emitted when emulation is about to stop. At this time, the EmuThread and core
53 * system emulation handles and memory are still valid, but are about become invalid.
54 */
55 void EmulationStopping();
56
37private: 57private:
38 void BootGame(std::string filename); 58 void BootGame(std::string filename);
59 void ShutdownGame();
39 60
40 void closeEvent(QCloseEvent* event) override; 61 void closeEvent(QCloseEvent* event) override;
41 62
@@ -55,6 +76,8 @@ private:
55 76
56 GRenderWindow* render_window; 77 GRenderWindow* render_window;
57 78
79 std::unique_ptr<EmuThread> emu_thread;
80
58 ProfilerWidget* profilerWidget; 81 ProfilerWidget* profilerWidget;
59 DisassemblerWidget* disasmWidget; 82 DisassemblerWidget* disasmWidget;
60 RegistersWidget* registersWidget; 83 RegistersWidget* registersWidget;
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index a3752ac1e..689806465 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -90,6 +90,9 @@
90 </property> 90 </property>
91 </action> 91 </action>
92 <action name="action_Start"> 92 <action name="action_Start">
93 <property name="enabled">
94 <bool>false</bool>
95 </property>
93 <property name="text"> 96 <property name="text">
94 <string>&amp;Start</string> 97 <string>&amp;Start</string>
95 </property> 98 </property>
diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp
index 2e2076a27..de4060116 100644
--- a/src/citra_qt/util/spinbox.cpp
+++ b/src/citra_qt/util/spinbox.cpp
@@ -29,6 +29,7 @@
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 31
32#include <cstdlib>
32#include <QLineEdit> 33#include <QLineEdit>
33#include <QRegExpValidator> 34#include <QRegExpValidator>
34 35
@@ -206,7 +207,7 @@ QString CSpinBox::TextFromValue()
206{ 207{
207 return prefix 208 return prefix
208 + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") 209 + QString(HasSign() ? ((value < 0) ? "-" : "+") : "")
209 + QString("%1").arg(abs(value), num_digits, base, QLatin1Char('0')).toUpper() 210 + QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper()
210 + suffix; 211 + suffix;
211} 212}
212 213
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp
index 6516fc633..f5b6c7301 100644
--- a/src/common/emu_window.cpp
+++ b/src/common/emu_window.cpp
@@ -28,6 +28,17 @@ static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsi
28 framebuffer_x < layout.bottom_screen.right); 28 framebuffer_x < layout.bottom_screen.right);
29} 29}
30 30
31std::tuple<unsigned,unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) {
32
33 new_x = std::max(new_x, framebuffer_layout.bottom_screen.left);
34 new_x = std::min(new_x, framebuffer_layout.bottom_screen.right-1);
35
36 new_y = std::max(new_y, framebuffer_layout.bottom_screen.top);
37 new_y = std::min(new_y, framebuffer_layout.bottom_screen.bottom-1);
38
39 return std::make_tuple(new_x, new_y);
40}
41
31void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { 42void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
32 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) 43 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
33 return; 44 return;
@@ -52,14 +63,13 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
52 if (!touch_pressed) 63 if (!touch_pressed)
53 return; 64 return;
54 65
55 if (IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) 66 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
56 TouchPressed(framebuffer_x, framebuffer_y); 67 std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
57 else 68
58 TouchReleased(); 69 TouchPressed(framebuffer_x, framebuffer_y);
59} 70}
60 71
61EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, 72EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) {
62 unsigned height) {
63 73
64 ASSERT(width > 0); 74 ASSERT(width > 0);
65 ASSERT(height > 0); 75 ASSERT(height > 0);
diff --git a/src/common/emu_window.h b/src/common/emu_window.h
index c8e2de04a..e0fc12a48 100644
--- a/src/common/emu_window.h
+++ b/src/common/emu_window.h
@@ -206,5 +206,10 @@ private:
206 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) 206 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
207 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) 207 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
208 208
209 /**
210 * Clip the provided coordinates to be inside the touchscreen area.
211 */
212 std::tuple<unsigned,unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
213
209 Service::HID::PadState pad_state; 214 Service::HID::PadState pad_state;
210}; 215};
diff --git a/src/common/thread.h b/src/common/thread.h
index a45728e1e..5fdb6baeb 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -51,109 +51,60 @@ int CurrentThreadId();
51void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); 51void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
52void SetCurrentThreadAffinity(u32 mask); 52void SetCurrentThreadAffinity(u32 mask);
53 53
54class Event 54class Event {
55{
56public: 55public:
57 Event() 56 Event() : is_set(false) {}
58 : is_set(false)
59 {}
60 57
61 void Set() 58 void Set() {
62 {
63 std::lock_guard<std::mutex> lk(m_mutex); 59 std::lock_guard<std::mutex> lk(m_mutex);
64 if (!is_set) 60 if (!is_set) {
65 {
66 is_set = true; 61 is_set = true;
67 m_condvar.notify_one(); 62 m_condvar.notify_one();
68 } 63 }
69 } 64 }
70 65
71 void Wait() 66 void Wait() {
72 {
73 std::unique_lock<std::mutex> lk(m_mutex); 67 std::unique_lock<std::mutex> lk(m_mutex);
74 m_condvar.wait(lk, IsSet(this)); 68 m_condvar.wait(lk, [&]{ return is_set; });
75 is_set = false; 69 is_set = false;
76 } 70 }
77 71
78 void Reset() 72 void Reset() {
79 {
80 std::unique_lock<std::mutex> lk(m_mutex); 73 std::unique_lock<std::mutex> lk(m_mutex);
81 // no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration 74 // no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration
82 is_set = false; 75 is_set = false;
83 } 76 }
84 77
85private: 78private:
86 class IsSet 79 bool is_set;
87 {
88 public:
89 IsSet(const Event* ev)
90 : m_event(ev)
91 {}
92
93 bool operator()()
94 {
95 return m_event->is_set;
96 }
97
98 private:
99 const Event* const m_event;
100 };
101
102 volatile bool is_set;
103 std::condition_variable m_condvar; 80 std::condition_variable m_condvar;
104 std::mutex m_mutex; 81 std::mutex m_mutex;
105}; 82};
106 83
107// TODO: doesn't work on windows with (count > 2) 84class Barrier {
108class Barrier
109{
110public: 85public:
111 Barrier(size_t count) 86 Barrier(size_t count) : m_count(count), m_waiting(0) {}
112 : m_count(count), m_waiting(0)
113 {}
114 87
115 // block until "count" threads call Sync() 88 /// Blocks until all "count" threads have called Sync()
116 bool Sync() 89 void Sync() {
117 {
118 std::unique_lock<std::mutex> lk(m_mutex); 90 std::unique_lock<std::mutex> lk(m_mutex);
119 91
120 // TODO: broken when next round of Sync()s 92 // TODO: broken when next round of Sync()s
121 // is entered before all waiting threads return from the notify_all 93 // is entered before all waiting threads return from the notify_all
122 94
123 if (m_count == ++m_waiting) 95 if (++m_waiting == m_count) {
124 {
125 m_waiting = 0; 96 m_waiting = 0;
126 m_condvar.notify_all(); 97 m_condvar.notify_all();
127 return true; 98 } else {
128 } 99 m_condvar.wait(lk, [&]{ return m_waiting == 0; });
129 else
130 {
131 m_condvar.wait(lk, IsDoneWating(this));
132 return false;
133 } 100 }
134 } 101 }
135 102
136private: 103private:
137 class IsDoneWating
138 {
139 public:
140 IsDoneWating(const Barrier* bar)
141 : m_bar(bar)
142 {}
143
144 bool operator()()
145 {
146 return (0 == m_bar->m_waiting);
147 }
148
149 private:
150 const Barrier* const m_bar;
151 };
152
153 std::condition_variable m_condvar; 104 std::condition_variable m_condvar;
154 std::mutex m_mutex; 105 std::mutex m_mutex;
155 const size_t m_count; 106 const size_t m_count;
156 volatile size_t m_waiting; 107 size_t m_waiting;
157}; 108};
158 109
159void SleepCurrentThread(int ms); 110void SleepCurrentThread(int ms);
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0528175ba..42733b95e 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -6,7 +6,6 @@ set(SRCS
6 arm/dyncom/arm_dyncom_interpreter.cpp 6 arm/dyncom/arm_dyncom_interpreter.cpp
7 arm/dyncom/arm_dyncom_run.cpp 7 arm/dyncom/arm_dyncom_run.cpp
8 arm/dyncom/arm_dyncom_thumb.cpp 8 arm/dyncom/arm_dyncom_thumb.cpp
9 arm/interpreter/armcopro.cpp
10 arm/interpreter/arminit.cpp 9 arm/interpreter/arminit.cpp
11 arm/interpreter/armsupp.cpp 10 arm/interpreter/armsupp.cpp
12 arm/skyeye_common/vfp/vfp.cpp 11 arm/skyeye_common/vfp/vfp.cpp
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index 1b1d01420..128413262 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/make_unique.h"
6
5#include "core/arm/skyeye_common/armemu.h" 7#include "core/arm/skyeye_common/armemu.h"
6#include "core/arm/skyeye_common/vfp/vfp.h" 8#include "core/arm/skyeye_common/vfp/vfp.h"
7 9
@@ -17,7 +19,7 @@ const static cpu_config_t s_arm11_cpu_info = {
17}; 19};
18 20
19ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { 21ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) {
20 state = std::unique_ptr<ARMul_State>(new ARMul_State); 22 state = Common::make_unique<ARMul_State>();
21 23
22 ARMul_NewState(state.get()); 24 ARMul_NewState(state.get());
23 ARMul_SelectProcessor(state.get(), ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); 25 ARMul_SelectProcessor(state.get(), ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop);
@@ -31,7 +33,6 @@ ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) {
31 33
32 // Reset the core to initial state 34 // Reset the core to initial state
33 ARMul_Reset(state.get()); 35 ARMul_Reset(state.get());
34 state->NextInstr = RESUME; // NOTE: This will be overwritten by LoadContext
35 state->Emulate = RUN; 36 state->Emulate = RUN;
36 37
37 // Switch to the desired privilege mode. 38 // Switch to the desired privilege mode.
@@ -99,7 +100,6 @@ void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 e
99 context.pc = entry_point; 100 context.pc = entry_point;
100 context.sp = stack_top; 101 context.sp = stack_top;
101 context.cpsr = 0x1F; // Usermode 102 context.cpsr = 0x1F; // Usermode
102 context.mode = 8; // Instructs dyncom CPU core to start execution as if it's "resuming" a thread.
103} 103}
104 104
105void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { 105void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
@@ -113,8 +113,6 @@ void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
113 113
114 ctx.fpscr = state->VFP[1]; 114 ctx.fpscr = state->VFP[1];
115 ctx.fpexc = state->VFP[2]; 115 ctx.fpexc = state->VFP[2];
116
117 ctx.mode = state->NextInstr;
118} 116}
119 117
120void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) { 118void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) {
@@ -128,8 +126,6 @@ void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) {
128 126
129 state->VFP[1] = ctx.fpscr; 127 state->VFP[1] = ctx.fpscr;
130 state->VFP[2] = ctx.fpexc; 128 state->VFP[2] = ctx.fpexc;
131
132 state->NextInstr = ctx.mode;
133} 129}
134 130
135void ARM_DynCom::PrepareReschedule() { 131void ARM_DynCom::PrepareReschedule() {
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 822b3bbb9..2488c879c 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -27,7 +27,7 @@ public:
27 27
28 void AddTicks(u64 ticks) override; 28 void AddTicks(u64 ticks) override;
29 29
30 void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg); 30 void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) override;
31 void SaveContext(Core::ThreadContext& ctx) override; 31 void SaveContext(Core::ThreadContext& ctx) override;
32 void LoadContext(const Core::ThreadContext& ctx) override; 32 void LoadContext(const Core::ThreadContext& ctx) override;
33 33
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index fde11e4ff..5ee99e93a 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -6,13 +6,12 @@
6 6
7#include <algorithm> 7#include <algorithm>
8#include <cstdio> 8#include <cstdio>
9#include <unordered_map>
10 9
11#include "common/logging/log.h" 10#include "common/logging/log.h"
12#include "common/profiler.h" 11#include "common/profiler.h"
13 12
14#include "core/mem_map.h" 13#include "core/mem_map.h"
15#include "core/hle/hle.h" 14#include "core/hle/svc.h"
16#include "core/arm/disassembler/arm_disasm.h" 15#include "core/arm/disassembler/arm_disasm.h"
17#include "core/arm/dyncom/arm_dyncom_interpreter.h" 16#include "core/arm/dyncom/arm_dyncom_interpreter.h"
18#include "core/arm/dyncom/arm_dyncom_thumb.h" 17#include "core/arm/dyncom/arm_dyncom_thumb.h"
@@ -3533,25 +3532,6 @@ const transop_fp_t arm_instruction_trans[] = {
3533 INTERPRETER_TRANSLATE(blx_1_thumb) 3532 INTERPRETER_TRANSLATE(blx_1_thumb)
3534}; 3533};
3535 3534
3536typedef std::unordered_map<u32, int> bb_map;
3537static bb_map CreamCache;
3538
3539static void insert_bb(unsigned int addr, int start) {
3540 CreamCache[addr] = start;
3541}
3542
3543static int find_bb(unsigned int addr, int& start) {
3544 int ret = -1;
3545 bb_map::const_iterator it = CreamCache.find(addr);
3546 if (it != CreamCache.end()) {
3547 start = static_cast<int>(it->second);
3548 ret = 0;
3549 } else {
3550 ret = -1;
3551 }
3552 return ret;
3553}
3554
3555enum { 3535enum {
3556 FETCH_SUCCESS, 3536 FETCH_SUCCESS,
3557 FETCH_FAILURE 3537 FETCH_FAILURE
@@ -3674,7 +3654,9 @@ translated:
3674 } 3654 }
3675 ret = inst_base->br; 3655 ret = inst_base->br;
3676 }; 3656 };
3677 insert_bb(pc_start, bb_start); 3657
3658 cpu->instruction_cache[pc_start] = bb_start;
3659
3678 return KEEP_GOING; 3660 return KEEP_GOING;
3679} 3661}
3680 3662
@@ -4001,9 +3983,14 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
4001 3983
4002 phys_addr = cpu->Reg[15]; 3984 phys_addr = cpu->Reg[15];
4003 3985
4004 if (find_bb(cpu->Reg[15], ptr) == -1) 3986 // Find the cached instruction cream, otherwise translate it...
3987 auto itr = cpu->instruction_cache.find(cpu->Reg[15]);
3988 if (itr != cpu->instruction_cache.end()) {
3989 ptr = itr->second;
3990 } else {
4005 if (InterpreterTranslate(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION) 3991 if (InterpreterTranslate(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION)
4006 goto END; 3992 goto END;
3993 }
4007 3994
4008 inst_base = (arm_inst *)&inst_buf[ptr]; 3995 inst_base = (arm_inst *)&inst_buf[ptr];
4009 GOTO_NEXT_INST; 3996 GOTO_NEXT_INST;
@@ -6247,7 +6234,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
6247 SWI_INST: 6234 SWI_INST:
6248 { 6235 {
6249 if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { 6236 if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
6250 HLE::CallSVC(Memory::Read32(cpu->Reg[15])); 6237 SVC::CallSVC(Memory::Read32(cpu->Reg[15]));
6251 } 6238 }
6252 6239
6253 cpu->Reg[15] += GET_INST_SIZE(cpu); 6240 cpu->Reg[15] += GET_INST_SIZE(cpu);
diff --git a/src/core/arm/interpreter/armcopro.cpp b/src/core/arm/interpreter/armcopro.cpp
deleted file mode 100644
index 4ae0c52e4..000000000
--- a/src/core/arm/interpreter/armcopro.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
1/* armcopro.c -- co-processor interface: ARM6 Instruction Emulator.
2 Copyright (C) 1994, 2000 Advanced RISC Machines Ltd.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18#include "core/arm/skyeye_common/armdefs.h"
19#include "core/arm/skyeye_common/armemu.h"
20#include "core/arm/skyeye_common/vfp/vfp.h"
21
22// Dummy Co-processors.
23
24static unsigned int NoCoPro3R(ARMul_State* state, unsigned int a, ARMword b)
25{
26 return ARMul_CANT;
27}
28
29static unsigned int NoCoPro4R(ARMul_State* state, unsigned int a, ARMword b, ARMword c)
30{
31 return ARMul_CANT;
32}
33
34static unsigned int NoCoPro4W(ARMul_State* state, unsigned int a, ARMword b, ARMword* c)
35{
36 return ARMul_CANT;
37}
38
39static unsigned int NoCoPro5R(ARMul_State* state, unsigned int a, ARMword b, ARMword c, ARMword d)
40{
41 return ARMul_CANT;
42}
43
44static unsigned int NoCoPro5W(ARMul_State* state, unsigned int a, ARMword b, ARMword* c, ARMword* d)
45{
46 return ARMul_CANT;
47}
48
49// Install co-processor instruction handlers in this routine.
50void ARMul_CoProInit(ARMul_State* state)
51{
52 // Initialise tham all first.
53 for (unsigned int i = 0; i < 16; i++)
54 ARMul_CoProDetach(state, i);
55
56 // Install CoPro Instruction handlers here.
57 // The format is:
58 // ARMul_CoProAttach (state, CP Number, Init routine, Exit routine
59 // LDC routine, STC routine, MRC routine, MCR routine,
60 // CDP routine, Read Reg routine, Write Reg routine).
61 if (state->is_v6) {
62 ARMul_CoProAttach(state, 10, VFPInit, NULL, VFPLDC, VFPSTC,
63 VFPMRC, VFPMCR, VFPMRRC, VFPMCRR, VFPCDP, NULL, NULL);
64 ARMul_CoProAttach(state, 11, VFPInit, NULL, VFPLDC, VFPSTC,
65 VFPMRC, VFPMCR, VFPMRRC, VFPMCRR, VFPCDP, NULL, NULL);
66
67 /*ARMul_CoProAttach (state, 15, MMUInit, NULL, NULL, NULL,
68 MMUMRC, MMUMCR, NULL, NULL, NULL, NULL, NULL);*/
69 }
70
71 // No handlers below here.
72
73 // Call all the initialisation routines.
74 for (unsigned int i = 0; i < 16; i++) {
75 if (state->CPInit[i])
76 (state->CPInit[i]) (state);
77 }
78}
79
80// Install co-processor finalisation routines in this routine.
81void ARMul_CoProExit(ARMul_State * state)
82{
83 for (unsigned int i = 0; i < 16; i++)
84 if (state->CPExit[i])
85 (state->CPExit[i]) (state);
86
87 // Detach all handlers.
88 for (unsigned int i = 0; i < 16; i++)
89 ARMul_CoProDetach(state, i);
90}
91
92// Routines to hook Co-processors into ARMulator.
93
94void
95ARMul_CoProAttach(ARMul_State* state,
96unsigned number,
97ARMul_CPInits* init,
98ARMul_CPExits* exit,
99ARMul_LDCs* ldc,
100ARMul_STCs* stc,
101ARMul_MRCs* mrc,
102ARMul_MCRs* mcr,
103ARMul_MRRCs* mrrc,
104ARMul_MCRRs* mcrr,
105ARMul_CDPs* cdp,
106ARMul_CPReads* read, ARMul_CPWrites* write)
107{
108 if (init != NULL)
109 state->CPInit[number] = init;
110 if (exit != NULL)
111 state->CPExit[number] = exit;
112 if (ldc != NULL)
113 state->LDC[number] = ldc;
114 if (stc != NULL)
115 state->STC[number] = stc;
116 if (mrc != NULL)
117 state->MRC[number] = mrc;
118 if (mcr != NULL)
119 state->MCR[number] = mcr;
120 if (mrrc != NULL)
121 state->MRRC[number] = mrrc;
122 if (mcrr != NULL)
123 state->MCRR[number] = mcrr;
124 if (cdp != NULL)
125 state->CDP[number] = cdp;
126 if (read != NULL)
127 state->CPRead[number] = read;
128 if (write != NULL)
129 state->CPWrite[number] = write;
130}
131
132void ARMul_CoProDetach(ARMul_State* state, unsigned number)
133{
134 ARMul_CoProAttach(state, number, NULL, NULL,
135 NoCoPro4R, NoCoPro4W, NoCoPro4W, NoCoPro4R,
136 NoCoPro5W, NoCoPro5R, NoCoPro3R, NULL, NULL);
137
138 state->CPInit[number] = NULL;
139 state->CPExit[number] = NULL;
140 state->CPRead[number] = NULL;
141 state->CPWrite[number] = NULL;
142}
diff --git a/src/core/arm/interpreter/arminit.cpp b/src/core/arm/interpreter/arminit.cpp
index 1d732fe84..31b2bab06 100644
--- a/src/core/arm/interpreter/arminit.cpp
+++ b/src/core/arm/interpreter/arminit.cpp
@@ -19,31 +19,16 @@
19#include "core/mem_map.h" 19#include "core/mem_map.h"
20#include "core/arm/skyeye_common/armdefs.h" 20#include "core/arm/skyeye_common/armdefs.h"
21#include "core/arm/skyeye_common/armemu.h" 21#include "core/arm/skyeye_common/armemu.h"
22#include "core/arm/skyeye_common/vfp/vfp.h"
22 23
23/***************************************************************************\ 24/***************************************************************************\
24* Returns a new instantiation of the ARMulator's state * 25* Returns a new instantiation of the ARMulator's state *
25\***************************************************************************/ 26\***************************************************************************/
26ARMul_State* ARMul_NewState(ARMul_State* state) 27ARMul_State* ARMul_NewState(ARMul_State* state)
27{ 28{
28 memset(state, 0, sizeof(ARMul_State));
29
30 state->Emulate = RUN; 29 state->Emulate = RUN;
31 for (unsigned int i = 0; i < 16; i++) {
32 state->Reg[i] = 0;
33 for (unsigned int j = 0; j < 7; j++)
34 state->RegBank[j][i] = 0;
35 }
36 for (unsigned int i = 0; i < 7; i++)
37 state->Spsr[i] = 0;
38
39 state->Mode = USER32MODE; 30 state->Mode = USER32MODE;
40 31
41 state->VectorCatch = 0;
42 state->Aborted = false;
43 state->Reseted = false;
44 state->Inted = 3;
45 state->LastInted = 3;
46
47 state->lateabtSig = HIGH; 32 state->lateabtSig = HIGH;
48 state->bigendSig = LOW; 33 state->bigendSig = LOW;
49 34
@@ -56,15 +41,11 @@ ARMul_State* ARMul_NewState(ARMul_State* state)
56 41
57void ARMul_SelectProcessor(ARMul_State* state, unsigned properties) 42void ARMul_SelectProcessor(ARMul_State* state, unsigned properties)
58{ 43{
59 state->is_v4 = (properties & (ARM_v4_Prop | ARM_v5_Prop)) != 0; 44 state->is_v4 = (properties & (ARM_v4_Prop | ARM_v5_Prop)) != 0;
60 state->is_v5 = (properties & ARM_v5_Prop) != 0; 45 state->is_v5 = (properties & ARM_v5_Prop) != 0;
61 state->is_v5e = (properties & ARM_v5e_Prop) != 0; 46 state->is_v5e = (properties & ARM_v5e_Prop) != 0;
62 state->is_v6 = (properties & ARM_v6_Prop) != 0; 47 state->is_v6 = (properties & ARM_v6_Prop) != 0;
63 state->is_v7 = (properties & ARM_v7_Prop) != 0; 48 state->is_v7 = (properties & ARM_v7_Prop) != 0;
64
65 // Only initialse the coprocessor support once we
66 // know what kind of chip we are dealing with.
67 ARMul_CoProInit(state);
68} 49}
69 50
70// Resets certain MPCore CP15 values to their ARM-defined reset values. 51// Resets certain MPCore CP15 values to their ARM-defined reset values.
@@ -130,26 +111,20 @@ static void ResetMPCoreCP15Registers(ARMul_State* cpu)
130\***************************************************************************/ 111\***************************************************************************/
131void ARMul_Reset(ARMul_State* state) 112void ARMul_Reset(ARMul_State* state)
132{ 113{
133 state->NextInstr = 0; 114 VFPInit(state);
134 115
135 state->Reg[15] = 0; 116 state->Reg[15] = 0;
136 state->Cpsr = INTBITS | SVC32MODE; 117 state->Cpsr = INTBITS | SVC32MODE;
137 state->Mode = SVC32MODE; 118 state->Mode = SVC32MODE;
138
139 state->Bank = SVCBANK; 119 state->Bank = SVCBANK;
140 FLUSHPIPE;
141 120
142 ResetMPCoreCP15Registers(state); 121 ResetMPCoreCP15Registers(state);
143 122
144 state->EndCondition = 0;
145 state->ErrorCode = 0;
146
147 state->NresetSig = HIGH; 123 state->NresetSig = HIGH;
148 state->NfiqSig = HIGH; 124 state->NfiqSig = HIGH;
149 state->NirqSig = HIGH; 125 state->NirqSig = HIGH;
150 state->NtransSig = (state->Mode & 3) ? HIGH : LOW; 126 state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
151 state->abortSig = LOW; 127 state->abortSig = LOW;
152 state->AbortAddr = 1;
153 128
154 state->NumInstrs = 0; 129 state->NumInstrs = 0;
155} 130}
diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h
index 743e935f0..85d523bc2 100644
--- a/src/core/arm/skyeye_common/armdefs.h
+++ b/src/core/arm/skyeye_common/armdefs.h
@@ -17,6 +17,8 @@
17 17
18#pragma once 18#pragma once
19 19
20#include <unordered_map>
21
20#include "common/common_types.h" 22#include "common/common_types.h"
21#include "core/arm/skyeye_common/arm_regformat.h" 23#include "core/arm/skyeye_common/arm_regformat.h"
22#include "core/arm/skyeye_common/skyeye_defs.h" 24#include "core/arm/skyeye_common/skyeye_defs.h"
@@ -53,26 +55,11 @@ typedef u64 ARMdword; // must be 64 bits wide
53typedef u32 ARMword; // must be 32 bits wide 55typedef u32 ARMword; // must be 32 bits wide
54typedef u16 ARMhword; // must be 16 bits wide 56typedef u16 ARMhword; // must be 16 bits wide
55typedef u8 ARMbyte; // must be 8 bits wide 57typedef u8 ARMbyte; // must be 8 bits wide
56typedef struct ARMul_State ARMul_State;
57
58typedef unsigned ARMul_CPInits(ARMul_State* state);
59typedef unsigned ARMul_CPExits(ARMul_State* state);
60typedef unsigned ARMul_LDCs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
61typedef unsigned ARMul_STCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
62typedef unsigned ARMul_MRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
63typedef unsigned ARMul_MCRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
64typedef unsigned ARMul_MRRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2);
65typedef unsigned ARMul_MCRRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2);
66typedef unsigned ARMul_CDPs(ARMul_State* state, unsigned type, ARMword instr);
67typedef unsigned ARMul_CPReads(ARMul_State* state, unsigned reg, ARMword* value);
68typedef unsigned ARMul_CPWrites(ARMul_State* state, unsigned reg, ARMword value);
69 58
70#define VFP_REG_NUM 64 59#define VFP_REG_NUM 64
71struct ARMul_State 60struct ARMul_State
72{ 61{
73 ARMword Emulate; // To start and stop emulation 62 ARMword Emulate; // To start and stop emulation
74 unsigned EndCondition; // Reason for stopping
75 unsigned ErrorCode; // Type of illegal instruction
76 63
77 // Order of the following register should not be modified 64 // Order of the following register should not be modified
78 ARMword Reg[16]; // The current register file 65 ARMword Reg[16]; // The current register file
@@ -101,8 +88,6 @@ struct ARMul_State
101 ARMword ExtReg[VFP_REG_NUM]; 88 ARMword ExtReg[VFP_REG_NUM];
102 /* ---- End of the ordered registers ---- */ 89 /* ---- End of the ordered registers ---- */
103 90
104 ARMword RegBank[7][16]; // all the registers
105
106 ARMword NFlag, ZFlag, CFlag, VFlag, IFFlags; // Dummy flags for speed 91 ARMword NFlag, ZFlag, CFlag, VFlag, IFFlags; // Dummy flags for speed
107 unsigned int shifter_carry_out; 92 unsigned int shifter_carry_out;
108 93
@@ -114,24 +99,7 @@ struct ARMul_State
114 unsigned long long NumInstrs; // The number of instructions executed 99 unsigned long long NumInstrs; // The number of instructions executed
115 unsigned NumInstrsToExecute; 100 unsigned NumInstrsToExecute;
116 101
117 unsigned NextInstr; 102 unsigned NresetSig; // Reset the processor
118 unsigned VectorCatch; // Caught exception mask
119
120 ARMul_CPInits* CPInit[16]; // Coprocessor initialisers
121 ARMul_CPExits* CPExit[16]; // Coprocessor finalisers
122 ARMul_LDCs* LDC[16]; // LDC instruction
123 ARMul_STCs* STC[16]; // STC instruction
124 ARMul_MRCs* MRC[16]; // MRC instruction
125 ARMul_MCRs* MCR[16]; // MCR instruction
126 ARMul_MRRCs* MRRC[16]; // MRRC instruction
127 ARMul_MCRRs* MCRR[16]; // MCRR instruction
128 ARMul_CDPs* CDP[16]; // CDP instruction
129 ARMul_CPReads* CPRead[16]; // Read CP register
130 ARMul_CPWrites* CPWrite[16]; // Write CP register
131 unsigned char* CPData[16]; // Coprocessor data
132 unsigned char const* CPRegWords[16]; // Map of coprocessor register sizes
133
134 unsigned NresetSig; // Reset the processor
135 unsigned NfiqSig; 103 unsigned NfiqSig;
136 unsigned NirqSig; 104 unsigned NirqSig;
137 105
@@ -173,13 +141,6 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
173*/ 141*/
174 unsigned lateabtSig; 142 unsigned lateabtSig;
175 143
176 bool Aborted; // Sticky flag for aborts
177 bool Reseted; // Sticky flag for Reset
178 ARMword Inted, LastInted; // Sticky flags for interrupts
179 ARMword Base; // Extra hand for base writeback
180 ARMword AbortAddr; // To keep track of Prefetch aborts
181 ARMword Vector; // Synthesize aborts in cycle modes
182
183 // For differentiating ARM core emulaiton. 144 // For differentiating ARM core emulaiton.
184 bool is_v4; // Are we emulating a v4 architecture (or higher)? 145 bool is_v4; // Are we emulating a v4 architecture (or higher)?
185 bool is_v5; // Are we emulating a v5 architecture? 146 bool is_v5; // Are we emulating a v5 architecture?
@@ -194,13 +155,9 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
194 // Added by ksh in 2005-10-1 155 // Added by ksh in 2005-10-1
195 cpu_config_t* cpu; 156 cpu_config_t* cpu;
196 157
197 u32 CurrInstr; 158 // TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per
198 u32 last_pc; // The last PC executed 159 // process for our purposes), not per ARMul_State (which tracks CPU core state).
199 u32 last_instr; // The last instruction executed 160 std::unordered_map<u32, int> instruction_cache;
200 u32 WriteAddr[17];
201 u32 WriteData[17];
202 u32 WritePc[17];
203 u32 CurrWrite;
204}; 161};
205 162
206/***************************************************************************\ 163/***************************************************************************\
@@ -286,34 +243,6 @@ enum {
286 ARMul_INC = 3 243 ARMul_INC = 3
287}; 244};
288 245
289enum {
290 ARMul_CP13_R0_FIQ = 0x1,
291 ARMul_CP13_R0_IRQ = 0x2,
292 ARMul_CP13_R8_PMUS = 0x1,
293
294 ARMul_CP14_R0_ENABLE = 0x0001,
295 ARMul_CP14_R0_CLKRST = 0x0004,
296 ARMul_CP14_R0_CCD = 0x0008,
297 ARMul_CP14_R0_INTEN0 = 0x0010,
298 ARMul_CP14_R0_INTEN1 = 0x0020,
299 ARMul_CP14_R0_INTEN2 = 0x0040,
300 ARMul_CP14_R0_FLAG0 = 0x0100,
301 ARMul_CP14_R0_FLAG1 = 0x0200,
302 ARMul_CP14_R0_FLAG2 = 0x0400,
303 ARMul_CP14_R10_MOE_IB = 0x0004,
304 ARMul_CP14_R10_MOE_DB = 0x0008,
305 ARMul_CP14_R10_MOE_BT = 0x000c,
306 ARMul_CP15_R1_ENDIAN = 0x0080,
307 ARMul_CP15_R1_ALIGN = 0x0002,
308 ARMul_CP15_R5_X = 0x0400,
309 ARMul_CP15_R5_ST_ALIGN = 0x0001,
310 ARMul_CP15_R5_IMPRE = 0x0406,
311 ARMul_CP15_R5_MMU_EXCPT = 0x0400,
312 ARMul_CP15_DBCON_M = 0x0100,
313 ARMul_CP15_DBCON_E1 = 0x000c,
314 ARMul_CP15_DBCON_E0 = 0x0003
315};
316
317/***************************************************************************\ 246/***************************************************************************\
318* Definitons of things in the host environment * 247* Definitons of things in the host environment *
319\***************************************************************************/ 248\***************************************************************************/
diff --git a/src/core/arm/skyeye_common/armemu.h b/src/core/arm/skyeye_common/armemu.h
index 2a1c50779..7e0965052 100644
--- a/src/core/arm/skyeye_common/armemu.h
+++ b/src/core/arm/skyeye_common/armemu.h
@@ -38,16 +38,6 @@ enum : u32 {
38 INTBITS = 0x1C0, 38 INTBITS = 0x1C0,
39}; 39};
40 40
41// Different ways to start the next instruction.
42enum {
43 SEQ = 0,
44 NONSEQ = 1,
45 PCINCEDSEQ = 2,
46 PCINCEDNONSEQ = 3,
47 PRIMEPIPE = 4,
48 RESUME = 8
49};
50
51// Values for Emulate. 41// Values for Emulate.
52enum { 42enum {
53 STOP = 0, // Stop 43 STOP = 0, // Stop
@@ -55,14 +45,3 @@ enum {
55 ONCE = 2, // Execute just one interation 45 ONCE = 2, // Execute just one interation
56 RUN = 3 // Continuous execution 46 RUN = 3 // Continuous execution
57}; 47};
58
59#define FLUSHPIPE state->NextInstr |= PRIMEPIPE
60
61// Coprocessor support functions.
62extern void ARMul_CoProInit(ARMul_State*);
63extern void ARMul_CoProExit(ARMul_State*);
64extern void ARMul_CoProAttach(ARMul_State*, unsigned, ARMul_CPInits*,
65 ARMul_CPExits*, ARMul_LDCs*, ARMul_STCs*,
66 ARMul_MRCs*, ARMul_MCRs*, ARMul_MRRCs*, ARMul_MCRRs*,
67 ARMul_CDPs*, ARMul_CPReads*, ARMul_CPWrites*);
68extern void ARMul_CoProDetach(ARMul_State*, unsigned);
diff --git a/src/core/arm/skyeye_common/vfp/vfp.cpp b/src/core/arm/skyeye_common/vfp/vfp.cpp
index d793261fd..d0fa157a2 100644
--- a/src/core/arm/skyeye_common/vfp/vfp.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfp.cpp
@@ -37,296 +37,18 @@ unsigned VFPInit(ARMul_State* state)
37 return 0; 37 return 0;
38} 38}
39 39
40unsigned VFPMRC(ARMul_State* state, unsigned type, u32 instr, u32* value) 40void VMSR(ARMul_State* state, ARMword reg, ARMword Rt)
41{
42 /* MRC<c> <coproc>,<opc1>,<Rt>,<CRn>,<CRm>{,<opc2>} */
43 int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
44 int OPC_1 = BITS(instr, 21, 23);
45 int Rt = BITS(instr, 12, 15);
46 int CRn = BITS(instr, 16, 19);
47 int CRm = BITS(instr, 0, 3);
48 int OPC_2 = BITS(instr, 5, 7);
49
50 /* TODO check access permission */
51
52 /* CRn/opc1 CRm/opc2 */
53
54 if (CoProc == 10 || CoProc == 11)
55 {
56 if (OPC_1 == 0x0 && CRm == 0 && (OPC_2 & 0x3) == 0)
57 {
58 /* VMOV r to s */
59 /* Transfering Rt is not mandatory, as the value of interest is pointed by value */
60 VMOVBRS(state, BIT(instr, 20), Rt, BIT(instr, 7)|CRn<<1, value);
61 return ARMul_DONE;
62 }
63
64 if (OPC_1 == 0x7 && CRm == 0 && OPC_2 == 0)
65 {
66 VMRS(state, CRn, Rt, value);
67 return ARMul_DONE;
68 }
69 }
70 LOG_WARNING(Core_ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, CRn %x, CRm %x, OPC_2 %x\n",
71 instr, CoProc, OPC_1, Rt, CRn, CRm, OPC_2);
72
73 return ARMul_CANT;
74}
75
76unsigned VFPMCR(ARMul_State* state, unsigned type, u32 instr, u32 value)
77{
78 /* MCR<c> <coproc>,<opc1>,<Rt>,<CRn>,<CRm>{,<opc2>} */
79 int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
80 int OPC_1 = BITS(instr, 21, 23);
81 int Rt = BITS(instr, 12, 15);
82 int CRn = BITS(instr, 16, 19);
83 int CRm = BITS(instr, 0, 3);
84 int OPC_2 = BITS(instr, 5, 7);
85
86 /* TODO check access permission */
87
88 /* CRn/opc1 CRm/opc2 */
89 if (CoProc == 10 || CoProc == 11)
90 {
91 if (OPC_1 == 0x0 && CRm == 0 && (OPC_2 & 0x3) == 0)
92 {
93 /* VMOV s to r */
94 /* Transfering Rt is not mandatory, as the value of interest is pointed by value */
95 VMOVBRS(state, BIT(instr, 20), Rt, BIT(instr, 7)|CRn<<1, &value);
96 return ARMul_DONE;
97 }
98
99 if (OPC_1 == 0x7 && CRm == 0 && OPC_2 == 0)
100 {
101 VMSR(state, CRn, Rt);
102 return ARMul_DONE;
103 }
104
105 if ((OPC_1 & 0x4) == 0 && CoProc == 11 && CRm == 0)
106 {
107 VFP_DEBUG_UNIMPLEMENTED(VMOVBRC);
108 return ARMul_DONE;
109 }
110
111 if (CoProc == 11 && CRm == 0)
112 {
113 VFP_DEBUG_UNIMPLEMENTED(VMOVBCR);
114 return ARMul_DONE;
115 }
116 }
117 LOG_WARNING(Core_ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, CRn %x, CRm %x, OPC_2 %x\n",
118 instr, CoProc, OPC_1, Rt, CRn, CRm, OPC_2);
119
120 return ARMul_CANT;
121}
122
123unsigned VFPMRRC(ARMul_State* state, unsigned type, u32 instr, u32* value1, u32* value2)
124{
125 /* MCRR<c> <coproc>,<opc1>,<Rt>,<Rt2>,<CRm> */
126 int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
127 int OPC_1 = BITS(instr, 4, 7);
128 int Rt = BITS(instr, 12, 15);
129 int Rt2 = BITS(instr, 16, 19);
130 int CRm = BITS(instr, 0, 3);
131
132 if (CoProc == 10 || CoProc == 11)
133 {
134 if (CoProc == 10 && (OPC_1 & 0xD) == 1)
135 {
136 VMOVBRRSS(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, value1, value2);
137 return ARMul_DONE;
138 }
139
140 if (CoProc == 11 && (OPC_1 & 0xD) == 1)
141 {
142 /* Transfering Rt and Rt2 is not mandatory, as the value of interest is pointed by value1 and value2 */
143 VMOVBRRD(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, value1, value2);
144 return ARMul_DONE;
145 }
146 }
147 LOG_WARNING(Core_ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, Rt2 %x, CRm %x\n",
148 instr, CoProc, OPC_1, Rt, Rt2, CRm);
149
150 return ARMul_CANT;
151}
152
153unsigned VFPMCRR(ARMul_State* state, unsigned type, u32 instr, u32 value1, u32 value2)
154{
155 /* MCRR<c> <coproc>,<opc1>,<Rt>,<Rt2>,<CRm> */
156 int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
157 int OPC_1 = BITS(instr, 4, 7);
158 int Rt = BITS(instr, 12, 15);
159 int Rt2 = BITS(instr, 16, 19);
160 int CRm = BITS(instr, 0, 3);
161
162 /* TODO check access permission */
163
164 /* CRn/opc1 CRm/opc2 */
165
166 if (CoProc == 11 || CoProc == 10)
167 {
168 if (CoProc == 10 && (OPC_1 & 0xD) == 1)
169 {
170 VMOVBRRSS(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, &value1, &value2);
171 return ARMul_DONE;
172 }
173
174 if (CoProc == 11 && (OPC_1 & 0xD) == 1)
175 {
176 /* Transfering Rt and Rt2 is not mandatory, as the value of interest is pointed by value1 and value2 */
177 VMOVBRRD(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, &value1, &value2);
178 return ARMul_DONE;
179 }
180 }
181 LOG_WARNING(Core_ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, Rt2 %x, CRm %x\n",
182 instr, CoProc, OPC_1, Rt, Rt2, CRm);
183
184 return ARMul_CANT;
185}
186
187unsigned VFPSTC(ARMul_State* state, unsigned type, u32 instr, u32 * value)
188{
189 /* STC{L}<c> <coproc>,<CRd>,[<Rn>],<option> */
190 int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
191 int CRd = BITS(instr, 12, 15);
192 int Rn = BITS(instr, 16, 19);
193 int imm8 = BITS(instr, 0, 7);
194 int P = BIT(instr, 24);
195 int U = BIT(instr, 23);
196 int D = BIT(instr, 22);
197 int W = BIT(instr, 21);
198
199 /* TODO check access permission */
200
201 /* VSTM */
202 if ( (P|U|D|W) == 0 ) {
203 LOG_ERROR(Core_ARM11, "In %s, UNDEFINED\n", __FUNCTION__);
204 exit(-1);
205 }
206 if (CoProc == 10 || CoProc == 11) {
207#if 1
208 if (P == 0 && U == 0 && W == 0) {
209 LOG_ERROR(Core_ARM11, "VSTM Related encodings\n");
210 exit(-1);
211 }
212 if (P == U && W == 1) {
213 LOG_ERROR(Core_ARM11, "UNDEFINED\n");
214 exit(-1);
215 }
216#endif
217
218 if (P == 1 && W == 0)
219 {
220 return VSTR(state, type, instr, value);
221 }
222
223 if (P == 1 && U == 0 && W == 1 && Rn == 0xD)
224 {
225 return VPUSH(state, type, instr, value);
226 }
227
228 return VSTM(state, type, instr, value);
229 }
230 LOG_WARNING(Core_ARM11, "Can't identify %x, CoProc %x, CRd %x, Rn %x, imm8 %x, P %x, U %x, D %x, W %x\n",
231 instr, CoProc, CRd, Rn, imm8, P, U, D, W);
232
233 return ARMul_CANT;
234}
235
236unsigned VFPLDC(ARMul_State* state, unsigned type, u32 instr, u32 value)
237{ 41{
238 /* LDC{L}<c> <coproc>,<CRd>,[<Rn>] */ 42 if (reg == 1)
239 int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
240 int CRd = BITS(instr, 12, 15);
241 int Rn = BITS(instr, 16, 19);
242 int imm8 = BITS(instr, 0, 7);
243 int P = BIT(instr, 24);
244 int U = BIT(instr, 23);
245 int D = BIT(instr, 22);
246 int W = BIT(instr, 21);
247
248 /* TODO check access permission */
249
250 if ( (P|U|D|W) == 0 ) {
251 LOG_ERROR(Core_ARM11, "In %s, UNDEFINED\n", __FUNCTION__);
252 exit(-1);
253 }
254 if (CoProc == 10 || CoProc == 11)
255 { 43 {
256 if (P == 1 && W == 0) 44 state->VFP[VFP_FPSCR] = state->Reg[Rt];
257 {
258 return VLDR(state, type, instr, value);
259 }
260
261 if (P == 0 && U == 1 && W == 1 && Rn == 0xD)
262 {
263 return VPOP(state, type, instr, value);
264 }
265
266 return VLDM(state, type, instr, value);
267 } 45 }
268 LOG_WARNING(Core_ARM11, "Can't identify %x, CoProc %x, CRd %x, Rn %x, imm8 %x, P %x, U %x, D %x, W %x\n", 46 else if (reg == 8)
269 instr, CoProc, CRd, Rn, imm8, P, U, D, W);
270
271 return ARMul_CANT;
272}
273
274unsigned VFPCDP(ARMul_State* state, unsigned type, u32 instr)
275{
276 /* CDP<c> <coproc>,<opc1>,<CRd>,<CRn>,<CRm>,<opc2> */
277 int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
278 int OPC_1 = BITS(instr, 20, 23);
279 int CRd = BITS(instr, 12, 15);
280 int CRn = BITS(instr, 16, 19);
281 int CRm = BITS(instr, 0, 3);
282 int OPC_2 = BITS(instr, 5, 7);
283
284 /* TODO check access permission */
285
286 /* CRn/opc1 CRm/opc2 */
287
288 if (CoProc == 10 || CoProc == 11)
289 { 47 {
290 if ((OPC_1 & 0xB) == 0xB && BITS(instr, 4, 7) == 0) 48 state->VFP[VFP_FPEXC] = state->Reg[Rt];
291 {
292 unsigned int single = BIT(instr, 8) == 0;
293 unsigned int d = (single ? BITS(instr, 12,15)<<1 | BIT(instr, 22) : BITS(instr, 12,15) | BIT(instr, 22)<<4);
294 unsigned int imm;
295 instr = BITS(instr, 16, 19) << 4 | BITS(instr, 0, 3); // FIXME dirty workaround to get a correct imm
296
297 if (single)
298 imm = BIT(instr, 7)<<31 | (BIT(instr, 6)==0)<<30 | (BIT(instr, 6) ? 0x1f : 0)<<25 | BITS(instr, 0, 5)<<19;
299 else
300 imm = BIT(instr, 7)<<31 | (BIT(instr, 6)==0)<<30 | (BIT(instr, 6) ? 0xff : 0)<<22 | BITS(instr, 0, 5)<<16;
301
302 VMOVI(state, single, d, imm);
303 return ARMul_DONE;
304 }
305
306 if ((OPC_1 & 0xB) == 0xB && CRn == 0 && (OPC_2 & 0x6) == 0x2)
307 {
308 unsigned int single = BIT(instr, 8) == 0;
309 unsigned int d = (single ? BITS(instr, 12,15)<<1 | BIT(instr, 22) : BITS(instr, 12,15) | BIT(instr, 22)<<4);
310 unsigned int m = (single ? BITS(instr, 0, 3)<<1 | BIT(instr, 5) : BITS(instr, 0, 3) | BIT(instr, 5)<<4);
311 VMOVR(state, single, d, m);
312 return ARMul_DONE;
313 }
314
315 int exceptions = 0;
316 if (CoProc == 10)
317 exceptions = vfp_single_cpdo(state, instr, state->VFP[VFP_FPSCR]);
318 else
319 exceptions = vfp_double_cpdo(state, instr, state->VFP[VFP_FPSCR]);
320
321 vfp_raise_exceptions(state, exceptions, instr, state->VFP[VFP_FPSCR]);
322
323 return ARMul_DONE;
324 } 49 }
325 LOG_WARNING(Core_ARM11, "Can't identify %x\n", instr);
326 return ARMul_CANT;
327} 50}
328 51
329/* ----------- MRC ------------ */
330void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value) 52void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value)
331{ 53{
332 if (to_arm) 54 if (to_arm)
@@ -338,43 +60,7 @@ void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword*
338 state->ExtReg[n] = *value; 60 state->ExtReg[n] = *value;
339 } 61 }
340} 62}
341void VMRS(ARMul_State* state, ARMword reg, ARMword Rt, ARMword* value) 63
342{
343 if (reg == 1)
344 {
345 if (Rt != 15)
346 {
347 *value = state->VFP[VFP_FPSCR];
348 }
349 else
350 {
351 *value = state->VFP[VFP_FPSCR] ;
352 }
353 }
354 else
355 {
356 switch (reg)
357 {
358 case 0:
359 *value = state->VFP[VFP_FPSID];
360 break;
361 case 6:
362 /* MVFR1, VFPv3 only ? */
363 LOG_TRACE(Core_ARM11, "\tr%d <= MVFR1 unimplemented\n", Rt);
364 break;
365 case 7:
366 /* MVFR0, VFPv3 only? */
367 LOG_TRACE(Core_ARM11, "\tr%d <= MVFR0 unimplemented\n", Rt);
368 break;
369 case 8:
370 *value = state->VFP[VFP_FPEXC];
371 break;
372 default:
373 LOG_TRACE(Core_ARM11, "\tSUBARCHITECTURE DEFINED\n");
374 break;
375 }
376 }
377}
378void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2) 64void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2)
379{ 65{
380 if (to_arm) 66 if (to_arm)
@@ -402,301 +88,6 @@ void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMwor
402 } 88 }
403} 89}
404 90
405/* ----------- MCR ------------ */
406void VMSR(ARMul_State* state, ARMword reg, ARMword Rt)
407{
408 if (reg == 1)
409 {
410 state->VFP[VFP_FPSCR] = state->Reg[Rt];
411 }
412 else if (reg == 8)
413 {
414 state->VFP[VFP_FPEXC] = state->Reg[Rt];
415 }
416}
417
418/* Memory operation are not inlined, as old Interpreter and Fast interpreter
419 don't have the same memory operation interface.
420 Old interpreter framework does one access to coprocessor per data, and
421 handles already data write, as well as address computation,
422 which is not the case for Fast interpreter. Therefore, implementation
423 of vfp instructions in old interpreter and fast interpreter are separate. */
424
425/* ----------- STC ------------ */
426int VSTR(ARMul_State* state, int type, ARMword instr, ARMword* value)
427{
428 static int i = 0;
429 static int single_reg, add, d, n, imm32, regs;
430 if (type == ARMul_FIRST)
431 {
432 single_reg = BIT(instr, 8) == 0; // Double precision
433 add = BIT(instr, 23);
434 imm32 = BITS(instr, 0,7)<<2; // may not be used
435 d = single_reg ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); /* Base register */
436 n = BITS(instr, 16, 19); // destination register
437
438 i = 0;
439 regs = 1;
440
441 return ARMul_DONE;
442 }
443 else if (type == ARMul_DATA)
444 {
445 if (single_reg)
446 {
447 *value = state->ExtReg[d+i];
448 i++;
449 if (i < regs)
450 return ARMul_INC;
451 else
452 return ARMul_DONE;
453 }
454 else
455 {
456 /* FIXME Careful of endianness, may need to rework this */
457 *value = state->ExtReg[d*2+i];
458 i++;
459 if (i < regs*2)
460 return ARMul_INC;
461 else
462 return ARMul_DONE;
463 }
464 }
465
466 return -1;
467}
468int VPUSH(ARMul_State* state, int type, ARMword instr, ARMword* value)
469{
470 static int i = 0;
471 static int single_regs, d, imm32, regs;
472 if (type == ARMul_FIRST)
473 {
474 single_regs = BIT(instr, 8) == 0; // Single precision
475 d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register
476 imm32 = BITS(instr, 0,7)<<2; // may not be used
477 regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 1, 7); // FSTMX if regs is odd
478
479 state->Reg[R13] = state->Reg[R13] - imm32;
480
481 i = 0;
482
483 return ARMul_DONE;
484 }
485 else if (type == ARMul_DATA)
486 {
487 if (single_regs)
488 {
489 *value = state->ExtReg[d + i];
490 i++;
491 if (i < regs)
492 return ARMul_INC;
493 else
494 return ARMul_DONE;
495 }
496 else
497 {
498 /* FIXME Careful of endianness, may need to rework this */
499 *value = state->ExtReg[d*2 + i];
500 i++;
501 if (i < regs*2)
502 return ARMul_INC;
503 else
504 return ARMul_DONE;
505 }
506 }
507
508 return -1;
509}
510int VSTM(ARMul_State* state, int type, ARMword instr, ARMword* value)
511{
512 static int i = 0;
513 static int single_regs, add, wback, d, n, imm32, regs;
514 if (type == ARMul_FIRST)
515 {
516 single_regs = BIT(instr, 8) == 0; // Single precision
517 add = BIT(instr, 23);
518 wback = BIT(instr, 21); // write-back
519 d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register
520 n = BITS(instr, 16, 19); // destination register
521 imm32 = BITS(instr, 0,7) * 4; // may not be used
522 regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 0, 7)>>1; // FSTMX if regs is odd
523
524 if (wback) {
525 state->Reg[n] = (add ? state->Reg[n] + imm32 : state->Reg[n] - imm32);
526 }
527
528 i = 0;
529
530 return ARMul_DONE;
531 }
532 else if (type == ARMul_DATA)
533 {
534 if (single_regs)
535 {
536 *value = state->ExtReg[d + i];
537 i++;
538 if (i < regs)
539 return ARMul_INC;
540 else
541 return ARMul_DONE;
542 }
543 else
544 {
545 /* FIXME Careful of endianness, may need to rework this */
546 *value = state->ExtReg[d*2 + i];
547 i++;
548 if (i < regs*2)
549 return ARMul_INC;
550 else
551 return ARMul_DONE;
552 }
553 }
554
555 return -1;
556}
557
558/* ----------- LDC ------------ */
559int VPOP(ARMul_State* state, int type, ARMword instr, ARMword value)
560{
561 static int i = 0;
562 static int single_regs, d, imm32, regs;
563 if (type == ARMul_FIRST)
564 {
565 single_regs = BIT(instr, 8) == 0; // Single precision
566 d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register
567 imm32 = BITS(instr, 0, 7)<<2; // may not be used
568 regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 1, 7); // FLDMX if regs is odd
569
570 state->Reg[R13] = state->Reg[R13] + imm32;
571
572 i = 0;
573
574 return ARMul_DONE;
575 }
576 else if (type == ARMul_TRANSFER)
577 {
578 return ARMul_DONE;
579 }
580 else if (type == ARMul_DATA)
581 {
582 if (single_regs)
583 {
584 state->ExtReg[d + i] = value;
585 i++;
586 if (i < regs)
587 return ARMul_INC;
588 else
589 return ARMul_DONE;
590 }
591 else
592 {
593 /* FIXME Careful of endianness, may need to rework this */
594 state->ExtReg[d*2 + i] = value;
595 i++;
596 if (i < regs*2)
597 return ARMul_INC;
598 else
599 return ARMul_DONE;
600 }
601 }
602
603 return -1;
604}
605int VLDR(ARMul_State* state, int type, ARMword instr, ARMword value)
606{
607 static int i = 0;
608 static int single_reg, add, d, n, imm32, regs;
609 if (type == ARMul_FIRST)
610 {
611 single_reg = BIT(instr, 8) == 0; // Double precision
612 add = BIT(instr, 23);
613 imm32 = BITS(instr, 0, 7)<<2; // may not be used
614 d = single_reg ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register
615 n = BITS(instr, 16, 19); // destination register
616
617 i = 0;
618 regs = 1;
619
620 return ARMul_DONE;
621 }
622 else if (type == ARMul_TRANSFER)
623 {
624 return ARMul_DONE;
625 }
626 else if (type == ARMul_DATA)
627 {
628 if (single_reg)
629 {
630 state->ExtReg[d+i] = value;
631 i++;
632 if (i < regs)
633 return ARMul_INC;
634 else
635 return ARMul_DONE;
636 }
637 else
638 {
639 /* FIXME Careful of endianness, may need to rework this */
640 state->ExtReg[d*2+i] = value;
641 i++;
642 if (i < regs*2)
643 return ARMul_INC;
644 else
645 return ARMul_DONE;
646 }
647 }
648
649 return -1;
650}
651int VLDM(ARMul_State* state, int type, ARMword instr, ARMword value)
652{
653 static int i = 0;
654 static int single_regs, add, wback, d, n, imm32, regs;
655 if (type == ARMul_FIRST)
656 {
657 single_regs = BIT(instr, 8) == 0; // Single precision
658 add = BIT(instr, 23);
659 wback = BIT(instr, 21); // write-back
660 d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register
661 n = BITS(instr, 16, 19); // destination register
662 imm32 = BITS(instr, 0, 7) * 4; // may not be used
663 regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 0, 7)>>1; // FLDMX if regs is odd
664
665 if (wback) {
666 state->Reg[n] = (add ? state->Reg[n] + imm32 : state->Reg[n] - imm32);
667 }
668
669 i = 0;
670
671 return ARMul_DONE;
672 }
673 else if (type == ARMul_DATA)
674 {
675 if (single_regs)
676 {
677 state->ExtReg[d + i] = value;
678 i++;
679 if (i < regs)
680 return ARMul_INC;
681 else
682 return ARMul_DONE;
683 }
684 else
685 {
686 /* FIXME Careful of endianness, may need to rework this */
687 state->ExtReg[d*2 + i] = value;
688 i++;
689 if (i < regs*2)
690 return ARMul_INC;
691 else
692 return ARMul_DONE;
693 }
694 }
695
696 return -1;
697}
698
699/* ----------- CDP ------------ */
700void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm) 91void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm)
701{ 92{
702 if (single) 93 if (single)
diff --git a/src/core/arm/skyeye_common/vfp/vfp.h b/src/core/arm/skyeye_common/vfp/vfp.h
index 1b72383e7..727350f14 100644
--- a/src/core/arm/skyeye_common/vfp/vfp.h
+++ b/src/core/arm/skyeye_common/vfp/vfp.h
@@ -28,13 +28,6 @@
28#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]); 28#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]);
29 29
30unsigned VFPInit(ARMul_State* state); 30unsigned VFPInit(ARMul_State* state);
31unsigned VFPMRC(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
32unsigned VFPMCR(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
33unsigned VFPMRRC(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2);
34unsigned VFPMCRR(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2);
35unsigned VFPSTC(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
36unsigned VFPLDC(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
37unsigned VFPCDP(ARMul_State* state, unsigned type, ARMword instr);
38 31
39s32 vfp_get_float(ARMul_State* state, u32 reg); 32s32 vfp_get_float(ARMul_State* state, u32 reg);
40void vfp_put_float(ARMul_State* state, s32 val, u32 reg); 33void vfp_put_float(ARMul_State* state, s32 val, u32 reg);
@@ -44,23 +37,10 @@ void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpsc
44u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr); 37u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr);
45u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr); 38u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr);
46 39
47// MRC 40void VMSR(ARMul_State* state, ARMword reg, ARMword Rt);
48void VMRS(ARMul_State* state, ARMword reg, ARMword Rt, ARMword* value);
49void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value); 41void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value);
50void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); 42void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2);
51void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); 43void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2);
52void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm); 44void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm);
53void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword imm); 45void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword imm);
54 46
55// MCR
56void VMSR(ARMul_State* state, ARMword reg, ARMword Rt);
57
58// STC
59int VSTM(ARMul_State* state, int type, ARMword instr, ARMword* value);
60int VPUSH(ARMul_State* state, int type, ARMword instr, ARMword* value);
61int VSTR(ARMul_State* state, int type, ARMword instr, ARMword* value);
62
63// LDC
64int VLDM(ARMul_State* state, int type, ARMword instr, ARMword value);
65int VPOP(ARMul_State* state, int type, ARMword instr, ARMword value);
66int VLDR(ARMul_State* state, int type, ARMword instr, ARMword value);
diff --git a/src/core/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h
index 5d1b4e53f..6b3dae280 100644
--- a/src/core/arm/skyeye_common/vfp/vfp_helper.h
+++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h
@@ -36,9 +36,6 @@
36#include "common/common_types.h" 36#include "common/common_types.h"
37#include "core/arm/skyeye_common/armdefs.h" 37#include "core/arm/skyeye_common/armdefs.h"
38 38
39#define pr_info //printf
40#define pr_debug //printf
41
42#define do_div(n, base) {n/=base;} 39#define do_div(n, base) {n/=base;}
43 40
44enum : u32 { 41enum : u32 {
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
index 8b2dfa388..a78bdc430 100644
--- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
@@ -51,6 +51,8 @@
51 * =========================================================================== 51 * ===========================================================================
52 */ 52 */
53 53
54#include "common/logging/log.h"
55
54#include "core/arm/skyeye_common/vfp/vfp_helper.h" 56#include "core/arm/skyeye_common/vfp/vfp_helper.h"
55#include "core/arm/skyeye_common/vfp/asm_vfp.h" 57#include "core/arm/skyeye_common/vfp/asm_vfp.h"
56#include "core/arm/skyeye_common/vfp/vfp.h" 58#include "core/arm/skyeye_common/vfp/vfp.h"
@@ -63,8 +65,8 @@ static struct vfp_single vfp_single_default_qnan = {
63 65
64static void vfp_single_dump(const char *str, struct vfp_single *s) 66static void vfp_single_dump(const char *str, struct vfp_single *s)
65{ 67{
66 pr_debug("VFP: %s: sign=%d exponent=%d significand=%08x\n", 68 LOG_DEBUG(Core_ARM11, "%s: sign=%d exponent=%d significand=%08x",
67 str, s->sign != 0, s->exponent, s->significand); 69 str, s->sign != 0, s->exponent, s->significand);
68} 70}
69 71
70static void vfp_single_normalise_denormal(struct vfp_single *vs) 72static void vfp_single_normalise_denormal(struct vfp_single *vs)
@@ -154,7 +156,7 @@ u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single *vs,
154 } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vs->sign != 0)) 156 } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vs->sign != 0))
155 incr = (1 << (VFP_SINGLE_LOW_BITS + 1)) - 1; 157 incr = (1 << (VFP_SINGLE_LOW_BITS + 1)) - 1;
156 158
157 pr_debug("VFP: rounding increment = 0x%08x\n", incr); 159 LOG_DEBUG(Core_ARM11, "rounding increment = 0x%08x", incr);
158 160
159 /* 161 /*
160 * Is our rounding going to overflow? 162 * Is our rounding going to overflow?
@@ -209,10 +211,8 @@ pack:
209 vfp_single_dump("pack: final", vs); 211 vfp_single_dump("pack: final", vs);
210 { 212 {
211 s32 d = vfp_single_pack(vs); 213 s32 d = vfp_single_pack(vs);
212#if 1 214 LOG_DEBUG(Core_ARM11, "%s: d(s%d)=%08x exceptions=%08x", func,
213 pr_debug("VFP: %s: d(s%d)=%08x exceptions=%08x\n", func, 215 sd, d, exceptions);
214 sd, d, exceptions);
215#endif
216 vfp_put_float(state, d, sd); 216 vfp_put_float(state, d, sd);
217 } 217 }
218 218
@@ -302,7 +302,7 @@ u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand)
302 u32 z, a; 302 u32 z, a;
303 303
304 if ((significand & 0xc0000000) != 0x40000000) { 304 if ((significand & 0xc0000000) != 0x40000000) {
305 pr_debug("VFP: estimate_sqrt: invalid significand\n"); 305 LOG_DEBUG(Core_ARM11, "invalid significand");
306 } 306 }
307 307
308 a = significand << 1; 308 a = significand << 1;
@@ -392,7 +392,7 @@ sqrt_invalid:
392 term = (u64)vsd.significand * vsd.significand; 392 term = (u64)vsd.significand * vsd.significand;
393 rem = ((u64)vsm.significand << 32) - term; 393 rem = ((u64)vsm.significand << 32) - term;
394 394
395 pr_debug("VFP: term=%016llx rem=%016llx\n", term, rem); 395 LOG_DEBUG(Core_ARM11, "term=%016lx rem=%016lx", term, rem);
396 396
397 while (rem < 0) { 397 while (rem < 0) {
398 vsd.significand -= 1; 398 vsd.significand -= 1;
@@ -624,7 +624,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
624 } 624 }
625 } 625 }
626 626
627 pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); 627 LOG_DEBUG(Core_ARM11, "ftoui: d(s%d)=%08x exceptions=%08x", sd, d, exceptions);
628 628
629 vfp_put_float(state, d, sd); 629 vfp_put_float(state, d, sd);
630 630
@@ -703,7 +703,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
703 } 703 }
704 } 704 }
705 705
706 pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); 706 LOG_DEBUG(Core_ARM11, "ftosi: d(s%d)=%08x exceptions=%08x", sd, d, exceptions);
707 707
708 vfp_put_float(state, (s32)d, sd); 708 vfp_put_float(state, (s32)d, sd);
709 709
@@ -800,7 +800,7 @@ vfp_single_add(struct vfp_single *vsd, struct vfp_single *vsn,
800 800
801 if (vsn->significand & 0x80000000 || 801 if (vsn->significand & 0x80000000 ||
802 vsm->significand & 0x80000000) { 802 vsm->significand & 0x80000000) {
803 pr_info("VFP: bad FP values in %s\n", __func__); 803 LOG_WARNING(Core_ARM11, "bad FP values");
804 vfp_single_dump("VSN", vsn); 804 vfp_single_dump("VSN", vsn);
805 vfp_single_dump("VSM", vsm); 805 vfp_single_dump("VSM", vsm);
806 } 806 }
@@ -871,7 +871,7 @@ vfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_s
871 struct vfp_single *t = vsn; 871 struct vfp_single *t = vsn;
872 vsn = vsm; 872 vsn = vsm;
873 vsm = t; 873 vsm = t;
874 pr_debug("VFP: swapping M <-> N\n"); 874 LOG_DEBUG(Core_ARM11, "swapping M <-> N");
875 } 875 }
876 876
877 vsd->sign = vsn->sign ^ vsm->sign; 877 vsd->sign = vsn->sign ^ vsm->sign;
@@ -924,7 +924,7 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp
924 s32 v; 924 s32 v;
925 925
926 v = vfp_get_float(state, sn); 926 v = vfp_get_float(state, sn);
927 pr_debug("VFP: s%u = %08x\n", sn, v); 927 LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, v);
928 vfp_single_unpack(&vsn, v); 928 vfp_single_unpack(&vsn, v);
929 if (vsn.exponent == 0 && vsn.significand) 929 if (vsn.exponent == 0 && vsn.significand)
930 vfp_single_normalise_denormal(&vsn); 930 vfp_single_normalise_denormal(&vsn);
@@ -939,7 +939,7 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp
939 vsp.sign = vfp_sign_negate(vsp.sign); 939 vsp.sign = vfp_sign_negate(vsp.sign);
940 940
941 v = vfp_get_float(state, sd); 941 v = vfp_get_float(state, sd);
942 pr_debug("VFP: s%u = %08x\n", sd, v); 942 LOG_DEBUG(Core_ARM11, "s%u = %08x", sd, v);
943 vfp_single_unpack(&vsn, v); 943 vfp_single_unpack(&vsn, v);
944 if (vsn.exponent == 0 && vsn.significand != 0) 944 if (vsn.exponent == 0 && vsn.significand != 0)
945 vfp_single_normalise_denormal(&vsn); 945 vfp_single_normalise_denormal(&vsn);
@@ -961,7 +961,7 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp
961 */ 961 */
962static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) 962static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
963{ 963{
964 pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd); 964 LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd);
965 return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac"); 965 return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac");
966} 966}
967 967
@@ -970,7 +970,8 @@ static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
970 */ 970 */
971static u32 vfp_single_fnmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) 971static u32 vfp_single_fnmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
972{ 972{
973 pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sd, sn); 973 // TODO: this one has its arguments inverted, investigate.
974 LOG_DEBUG(Core_ARM11, "s%u = %08x", sd, sn);
974 return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_MULTIPLY, "fnmac"); 975 return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_MULTIPLY, "fnmac");
975} 976}
976 977
@@ -979,7 +980,7 @@ static u32 vfp_single_fnmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr
979 */ 980 */
980static u32 vfp_single_fmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) 981static u32 vfp_single_fmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
981{ 982{
982 pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd); 983 LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd);
983 return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_SUBTRACT, "fmsc"); 984 return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_SUBTRACT, "fmsc");
984} 985}
985 986
@@ -988,7 +989,7 @@ static u32 vfp_single_fmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
988 */ 989 */
989static u32 vfp_single_fnmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) 990static u32 vfp_single_fnmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
990{ 991{
991 pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd); 992 LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd);
992 return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc"); 993 return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
993} 994}
994 995
@@ -1001,7 +1002,7 @@ static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
1001 u32 exceptions; 1002 u32 exceptions;
1002 s32 n = vfp_get_float(state, sn); 1003 s32 n = vfp_get_float(state, sn);
1003 1004
1004 pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, n); 1005 LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n);
1005 1006
1006 vfp_single_unpack(&vsn, n); 1007 vfp_single_unpack(&vsn, n);
1007 if (vsn.exponent == 0 && vsn.significand) 1008 if (vsn.exponent == 0 && vsn.significand)
@@ -1024,7 +1025,7 @@ static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr
1024 u32 exceptions; 1025 u32 exceptions;
1025 s32 n = vfp_get_float(state, sn); 1026 s32 n = vfp_get_float(state, sn);
1026 1027
1027 pr_debug("VFP: s%u = %08x\n", sn, n); 1028 LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n);
1028 1029
1029 vfp_single_unpack(&vsn, n); 1030 vfp_single_unpack(&vsn, n);
1030 if (vsn.exponent == 0 && vsn.significand) 1031 if (vsn.exponent == 0 && vsn.significand)
@@ -1048,7 +1049,7 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
1048 u32 exceptions; 1049 u32 exceptions;
1049 s32 n = vfp_get_float(state, sn); 1050 s32 n = vfp_get_float(state, sn);
1050 1051
1051 pr_debug("VFP: s%u = %08x\n", sn, n); 1052 LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n);
1052 1053
1053 /* 1054 /*
1054 * Unpack and normalise denormals. 1055 * Unpack and normalise denormals.
@@ -1071,7 +1072,7 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
1071 */ 1072 */
1072static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) 1073static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
1073{ 1074{
1074 pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd); 1075 LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd);
1075 /* 1076 /*
1076 * Subtraction is addition with one sign inverted. 1077 * Subtraction is addition with one sign inverted.
1077 */ 1078 */
@@ -1091,7 +1092,7 @@ static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
1091 s32 n = vfp_get_float(state, sn); 1092 s32 n = vfp_get_float(state, sn);
1092 int tm, tn; 1093 int tm, tn;
1093 1094
1094 pr_debug("VFP: s%u = %08x\n", sn, n); 1095 LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n);
1095 1096
1096 vfp_single_unpack(&vsn, n); 1097 vfp_single_unpack(&vsn, n);
1097 vfp_single_unpack(&vsm, m); 1098 vfp_single_unpack(&vsm, m);
@@ -1213,7 +1214,6 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
1213 unsigned int sm = vfp_get_sm(inst); 1214 unsigned int sm = vfp_get_sm(inst);
1214 unsigned int vecitr, veclen, vecstride; 1215 unsigned int vecitr, veclen, vecstride;
1215 struct op *fop; 1216 struct op *fop;
1216 pr_debug("In %s\n", __FUNCTION__);
1217 1217
1218 vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK); 1218 vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK);
1219 1219
@@ -1239,11 +1239,11 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
1239 else 1239 else
1240 veclen = fpscr & FPSCR_LENGTH_MASK; 1240 veclen = fpscr & FPSCR_LENGTH_MASK;
1241 1241
1242 pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride, 1242 LOG_DEBUG(Core_ARM11, "vecstride=%u veclen=%u", vecstride,
1243 (veclen >> FPSCR_LENGTH_BIT) + 1); 1243 (veclen >> FPSCR_LENGTH_BIT) + 1);
1244 1244
1245 if (!fop->fn) { 1245 if (!fop->fn) {
1246 printf("VFP: could not find single op %d, inst=0x%x@0x%x\n", FEXT_TO_IDX(inst), inst, state->Reg[15]); 1246 LOG_CRITICAL(Core_ARM11, "could not find single op %d, inst=0x%x@0x%x", FEXT_TO_IDX(inst), inst, state->Reg[15]);
1247 exit(-1); 1247 exit(-1);
1248 goto invalid; 1248 goto invalid;
1249 } 1249 }
@@ -1255,17 +1255,17 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
1255 1255
1256 type = (fop->flags & OP_DD) ? 'd' : 's'; 1256 type = (fop->flags & OP_DD) ? 'd' : 's';
1257 if (op == FOP_EXT) 1257 if (op == FOP_EXT)
1258 pr_debug("VFP: itr%d (%c%u) = op[%u] (s%u=%08x)\n", 1258 LOG_DEBUG(Core_ARM11, "itr%d (%c%u) = op[%u] (s%u=%08x)",
1259 vecitr >> FPSCR_LENGTH_BIT, type, dest, sn, 1259 vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
1260 sm, m); 1260 sm, m);
1261 else 1261 else
1262 pr_debug("VFP: itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)\n", 1262 LOG_DEBUG(Core_ARM11, "itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)",
1263 vecitr >> FPSCR_LENGTH_BIT, type, dest, sn, 1263 vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
1264 FOP_TO_IDX(op), sm, m); 1264 FOP_TO_IDX(op), sm, m);
1265 1265
1266 except = fop->fn(state, dest, sn, m, fpscr); 1266 except = fop->fn(state, dest, sn, m, fpscr);
1267 pr_debug("VFP: itr%d: exceptions=%08x\n", 1267 LOG_DEBUG(Core_ARM11, "itr%d: exceptions=%08x",
1268 vecitr >> FPSCR_LENGTH_BIT, except); 1268 vecitr >> FPSCR_LENGTH_BIT, except);
1269 1269
1270 exceptions |= except; 1270 exceptions |= except;
1271 1271
diff --git a/src/core/core.h b/src/core/core.h
index 5e132cb5a..278f0f1cc 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -21,9 +21,6 @@ struct ThreadContext {
21 u32 fpu_registers[32]; 21 u32 fpu_registers[32];
22 u32 fpscr; 22 u32 fpscr;
23 u32 fpexc; 23 u32 fpexc;
24
25 // These are not part of native ThreadContext, but needed by emu
26 u32 mode;
27}; 24};
28 25
29extern ARM_Interface* g_app_core; ///< ARM11 application core 26extern ARM_Interface* g_app_core; ///< ARM11 application core
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 6f716b1ca..f70c84c3d 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -160,6 +160,16 @@ void Init() {
160 last_global_time_us = 0; 160 last_global_time_us = 0;
161 has_ts_events = 0; 161 has_ts_events = 0;
162 mhz_change_callbacks.clear(); 162 mhz_change_callbacks.clear();
163
164 first = nullptr;
165 ts_first = nullptr;
166 ts_last = nullptr;
167
168 event_pool = nullptr;
169 event_ts_pool = nullptr;
170 allocated_ts_events = 0;
171
172 advance_callback = nullptr;
163} 173}
164 174
165void Shutdown() { 175void Shutdown() {
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h
index dbbdced74..770bd715e 100644
--- a/src/core/file_sys/disk_archive.h
+++ b/src/core/file_sys/disk_archive.h
@@ -24,7 +24,7 @@ class DiskArchive : public ArchiveBackend {
24public: 24public:
25 DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {} 25 DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
26 26
27 virtual std::string GetName() const { return "DiskArchive: " + mount_point; } 27 virtual std::string GetName() const override { return "DiskArchive: " + mount_point; }
28 28
29 std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; 29 std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
30 bool DeleteFile(const Path& path) const override; 30 bool DeleteFile(const Path& path) const override;
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp
index 40bae9346..9fcfcc285 100644
--- a/src/core/hle/config_mem.cpp
+++ b/src/core/hle/config_mem.cpp
@@ -61,12 +61,13 @@ template void Read<u16>(u16 &var, const u32 addr);
61template void Read<u8>(u8 &var, const u32 addr); 61template void Read<u8>(u8 &var, const u32 addr);
62 62
63void Init() { 63void Init() {
64 memset(&config_mem, 0, sizeof(config_mem));
65
64 config_mem.update_flag = 0; // No update 66 config_mem.update_flag = 0; // No update
65 config_mem.sys_core_ver = 0x2; 67 config_mem.sys_core_ver = 0x2;
66 config_mem.unit_info = 0x1; // Bit 0 set for Retail 68 config_mem.unit_info = 0x1; // Bit 0 set for Retail
67 config_mem.prev_firm = 0; 69 config_mem.prev_firm = 0;
68 config_mem.app_mem_type = 0x2; // Default app mem type is 0 70 config_mem.app_mem_type = 0x2; // Default app mem type is 0
69 config_mem.unit_info = 0x1; // Bit 0 set for Retail
70 config_mem.app_mem_alloc = 0x06000000; // Set to 96MB, since some games use more than the default (64MB) 71 config_mem.app_mem_alloc = 0x06000000; // Set to 96MB, since some games use more than the default (64MB)
71 config_mem.base_mem_alloc = 0x01400000; // Default base memory is 20MB 72 config_mem.base_mem_alloc = 0x01400000; // Default base memory is 20MB
72 config_mem.sys_mem_alloc = Memory::FCRAM_SIZE - (config_mem.app_mem_alloc + config_mem.base_mem_alloc); 73 config_mem.sys_mem_alloc = Memory::FCRAM_SIZE - (config_mem.app_mem_alloc + config_mem.base_mem_alloc);
@@ -77,4 +78,7 @@ void Init() {
77 config_mem.firm_sys_core_ver = 0x2; 78 config_mem.firm_sys_core_ver = 0x2;
78} 79}
79 80
81void Shutdown() {
82}
83
80} // namespace 84} // namespace
diff --git a/src/core/hle/config_mem.h b/src/core/hle/config_mem.h
index 94853901a..cbb478fb3 100644
--- a/src/core/hle/config_mem.h
+++ b/src/core/hle/config_mem.h
@@ -20,4 +20,6 @@ void Read(T &var, const u32 addr);
20 20
21void Init(); 21void Init();
22 22
23void Shutdown();
24
23} // namespace 25} // namespace
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index 1aaeaa9c9..fdeb9a028 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -2,12 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <vector> 5#include "common/assert.h"
6 6#include "common/logging/log.h"
7#include "common/profiler.h"
8 7
9#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
10#include "core/mem_map.h" 9#include "core/core.h"
11#include "core/hle/hle.h" 10#include "core/hle/hle.h"
12#include "core/hle/config_mem.h" 11#include "core/hle/config_mem.h"
13#include "core/hle/shared_page.h" 12#include "core/hle/shared_page.h"
@@ -18,35 +17,7 @@
18 17
19namespace HLE { 18namespace HLE {
20 19
21Common::Profiling::TimingCategory profiler_svc("SVC Calls"); 20bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread
22
23static std::vector<ModuleDef> g_module_db;
24
25bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a new thread
26
27static const FunctionDef* GetSVCInfo(u32 opcode) {
28 u32 func_num = opcode & 0xFFFFFF; // 8 bits
29 if (func_num > 0xFF) {
30 LOG_ERROR(Kernel_SVC,"unknown svc=0x%02X", func_num);
31 return nullptr;
32 }
33 return &g_module_db[0].func_table[func_num];
34}
35
36void CallSVC(u32 opcode) {
37 Common::Profiling::ScopeTimer timer_svc(profiler_svc);
38
39 const FunctionDef *info = GetSVCInfo(opcode);
40
41 if (!info) {
42 return;
43 }
44 if (info->func) {
45 info->func();
46 } else {
47 LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name.c_str());
48 }
49}
50 21
51void Reschedule(const char *reason) { 22void Reschedule(const char *reason) {
52 DEBUG_ASSERT_MSG(reason != nullptr && strlen(reason) < 256, "Reschedule: Invalid or too long reason."); 23 DEBUG_ASSERT_MSG(reason != nullptr && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
@@ -62,31 +33,21 @@ void Reschedule(const char *reason) {
62 g_reschedule = true; 33 g_reschedule = true;
63} 34}
64 35
65void RegisterModule(std::string name, int num_functions, const FunctionDef* func_table) {
66 ModuleDef module = {name, num_functions, func_table};
67 g_module_db.push_back(module);
68}
69
70static void RegisterAllModules() {
71 SVC::Register();
72}
73
74void Init() { 36void Init() {
75 Service::Init(); 37 Service::Init();
76
77 RegisterAllModules();
78
79 ConfigMem::Init(); 38 ConfigMem::Init();
80 SharedPage::Init(); 39 SharedPage::Init();
81 40
41 g_reschedule = false;
42
82 LOG_DEBUG(Kernel, "initialized OK"); 43 LOG_DEBUG(Kernel, "initialized OK");
83} 44}
84 45
85void Shutdown() { 46void Shutdown() {
47 ConfigMem::Shutdown();
48 SharedPage::Shutdown();
86 Service::Shutdown(); 49 Service::Shutdown();
87 50
88 g_module_db.clear();
89
90 LOG_DEBUG(Kernel, "shutdown OK"); 51 LOG_DEBUG(Kernel, "shutdown OK");
91} 52}
92 53
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index 3f6f9a4b5..23de1aab7 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -4,40 +4,13 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <string>
8
9#include "common/common_types.h"
10#include "core/core.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13
14namespace HLE { 7namespace HLE {
15 8
16extern bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread 9extern bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread
17 10
18typedef u32 Addr;
19typedef void (*Func)();
20
21struct FunctionDef {
22 u32 id;
23 Func func;
24 std::string name;
25};
26
27struct ModuleDef {
28 std::string name;
29 int num_funcs;
30 const FunctionDef* func_table;
31};
32
33void RegisterModule(std::string name, int num_functions, const FunctionDef *func_table);
34
35void CallSVC(u32 opcode);
36
37void Reschedule(const char *reason); 11void Reschedule(const char *reason);
38 12
39void Init(); 13void Init();
40
41void Shutdown(); 14void Shutdown();
42 15
43} // namespace 16} // namespace
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 6261b82b6..fca582bbe 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -14,11 +14,10 @@
14 14
15namespace Kernel { 15namespace Kernel {
16 16
17unsigned int Object::next_object_id = 0; 17unsigned int Object::next_object_id;
18 18SharedPtr<Thread> g_main_thread;
19SharedPtr<Thread> g_main_thread = nullptr;
20HandleTable g_handle_table; 19HandleTable g_handle_table;
21u64 g_program_id = 0; 20u64 g_program_id;
22 21
23void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { 22void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
24 auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); 23 auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
@@ -138,6 +137,10 @@ void HandleTable::Clear() {
138void Init() { 137void Init() {
139 Kernel::ThreadingInit(); 138 Kernel::ThreadingInit();
140 Kernel::TimersInit(); 139 Kernel::TimersInit();
140
141 Object::next_object_id = 0;
142 g_program_id = 0;
143 g_main_thread = nullptr;
141} 144}
142 145
143/// Shutdown the kernel 146/// Shutdown the kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 2d295ea00..ab06fa025 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -95,12 +95,13 @@ public:
95 return false; 95 return false;
96 } 96 }
97 97
98public:
99 static unsigned int next_object_id;
100
98private: 101private:
99 friend void intrusive_ptr_add_ref(Object*); 102 friend void intrusive_ptr_add_ref(Object*);
100 friend void intrusive_ptr_release(Object*); 103 friend void intrusive_ptr_release(Object*);
101 104
102 static unsigned int next_object_id;
103
104 unsigned int ref_count = 0; 105 unsigned int ref_count = 0;
105 unsigned int object_id = next_object_id++; 106 unsigned int object_id = next_object_id++;
106}; 107};
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 33d66b986..d678f5f6f 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -23,7 +23,7 @@
23namespace Kernel { 23namespace Kernel {
24 24
25/// Event type for the thread wake up event 25/// Event type for the thread wake up event
26static int ThreadWakeupEventType = -1; 26static int ThreadWakeupEventType;
27 27
28bool Thread::ShouldWait() { 28bool Thread::ShouldWait() {
29 return status != THREADSTATUS_DEAD; 29 return status != THREADSTATUS_DEAD;
@@ -42,7 +42,7 @@ static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> ready_queue;
42static Thread* current_thread; 42static Thread* current_thread;
43 43
44// The first available thread id at startup 44// The first available thread id at startup
45static u32 next_thread_id = 1; 45static u32 next_thread_id;
46 46
47/** 47/**
48 * Creates a new thread ID 48 * Creates a new thread ID
@@ -497,6 +497,12 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
497void ThreadingInit() { 497void ThreadingInit() {
498 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); 498 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
499 499
500 current_thread = nullptr;
501 next_thread_id = 1;
502
503 thread_list.clear();
504 ready_queue.clear();
505
500 // Setup the idle thread 506 // Setup the idle thread
501 SetupIdleThread(); 507 SetupIdleThread();
502} 508}
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index 610e26a3c..36979248d 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -12,7 +12,7 @@
12namespace Kernel { 12namespace Kernel {
13 13
14/// The event type of the generic timer callback event 14/// The event type of the generic timer callback event
15static int timer_callback_event_type = -1; 15static int timer_callback_event_type;
16// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future, allowing 16// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future, allowing
17// us to simply use a pool index or similar. 17// us to simply use a pool index or similar.
18static Kernel::HandleTable timer_callback_handle_table; 18static Kernel::HandleTable timer_callback_handle_table;
@@ -66,7 +66,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) {
66 SharedPtr<Timer> timer = timer_callback_handle_table.Get<Timer>(static_cast<Handle>(timer_handle)); 66 SharedPtr<Timer> timer = timer_callback_handle_table.Get<Timer>(static_cast<Handle>(timer_handle));
67 67
68 if (timer == nullptr) { 68 if (timer == nullptr) {
69 LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08X", timer_handle); 69 LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08lX", timer_handle);
70 return; 70 return;
71 } 71 }
72 72
@@ -89,6 +89,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) {
89} 89}
90 90
91void TimersInit() { 91void TimersInit() {
92 timer_callback_handle_table.Clear();
92 timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback); 93 timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
93} 94}
94 95
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 190c5df7a..98ae80b3a 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -28,15 +28,15 @@ namespace APT {
28static const VAddr SHARED_FONT_VADDR = 0x18000000; 28static const VAddr SHARED_FONT_VADDR = 0x18000000;
29 29
30/// Handle to shared memory region designated to for shared system font 30/// Handle to shared memory region designated to for shared system font
31static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem = nullptr; 31static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
32 32
33static Kernel::SharedPtr<Kernel::Mutex> lock = nullptr; 33static Kernel::SharedPtr<Kernel::Mutex> lock;
34static Kernel::SharedPtr<Kernel::Event> notification_event = nullptr; ///< APT notification event 34static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
35static Kernel::SharedPtr<Kernel::Event> start_event = nullptr; ///< APT start event 35static Kernel::SharedPtr<Kernel::Event> start_event; ///< APT start event
36 36
37static std::vector<u8> shared_font; 37static std::vector<u8> shared_font;
38 38
39static u32 cpu_percent = 0; ///< CPU time available to the running application 39static u32 cpu_percent; ///< CPU time available to the running application
40 40
41void Initialize(Service::Interface* self) { 41void Initialize(Service::Interface* self) {
42 u32* cmd_buff = Kernel::GetCommandBuffer(); 42 u32* cmd_buff = Kernel::GetCommandBuffer();
@@ -309,6 +309,7 @@ void Init() {
309 } 309 }
310 310
311 lock = Kernel::Mutex::Create(false, "APT_U:Lock"); 311 lock = Kernel::Mutex::Create(false, "APT_U:Lock");
312
312 cpu_percent = 0; 313 cpu_percent = 0;
313 314
314 // TODO(bunnei): Check if these are created in Initialize or on APT process startup. 315 // TODO(bunnei): Check if these are created in Initialize or on APT process startup.
@@ -317,7 +318,11 @@ void Init() {
317} 318}
318 319
319void Shutdown() { 320void Shutdown() {
320 321 shared_font.clear();
322 shared_font_mem = nullptr;
323 lock = nullptr;
324 notification_event = nullptr;
325 start_event = nullptr;
321} 326}
322 327
323} // namespace APT 328} // namespace APT
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index fe1245fe8..5eccdecf7 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -53,12 +53,12 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) {
53 }); 53 });
54 54
55 if (itr == std::end(config->block_entries)) { 55 if (itr == std::end(config->block_entries)) {
56 LOG_ERROR(Service_CFG, "Config block %u with flags %u was not found", block_id, flag); 56 LOG_ERROR(Service_CFG, "Config block 0x%X with flags %u and size %u was not found", block_id, flag, size);
57 return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); 57 return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
58 } 58 }
59 59
60 if (itr->size != size) { 60 if (itr->size != size) {
61 LOG_ERROR(Service_CFG, "Invalid size %u for config block %u with flags %u", size, block_id, flag); 61 LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag);
62 return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); 62 return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
63 } 63 }
64 64
@@ -207,6 +207,7 @@ void Init() {
207 207
208 // Initialize the Username block 208 // Initialize the Username block
209 // TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals 209 // TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals
210 memset(&CONSOLE_USERNAME_BLOCK, 0, sizeof(CONSOLE_USERNAME_BLOCK));
210 CONSOLE_USERNAME_BLOCK.ng_word = 0; 211 CONSOLE_USERNAME_BLOCK.ng_word = 0;
211 CONSOLE_USERNAME_BLOCK.zero = 0; 212 CONSOLE_USERNAME_BLOCK.zero = 0;
212 213
@@ -219,7 +220,6 @@ void Init() {
219} 220}
220 221
221void Shutdown() { 222void Shutdown() {
222
223} 223}
224 224
225} // namespace CFG 225} // namespace CFG
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index f16f84e67..2e759a3e3 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -11,7 +11,7 @@
11 11
12namespace DSP_DSP { 12namespace DSP_DSP {
13 13
14static u32 read_pipe_count = 0; 14static u32 read_pipe_count;
15static Kernel::SharedPtr<Kernel::Event> semaphore_event; 15static Kernel::SharedPtr<Kernel::Event> semaphore_event;
16static Kernel::SharedPtr<Kernel::Event> interrupt_event; 16static Kernel::SharedPtr<Kernel::Event> interrupt_event;
17 17
@@ -42,7 +42,7 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) {
42 cmd_buff[1] = 0; // No error 42 cmd_buff[1] = 0; // No error
43 cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000); 43 cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000);
44 44
45 LOG_WARNING(Service_DSP, "(STUBBED) called with address %u", addr); 45 LOG_WARNING(Service_DSP, "(STUBBED) called with address 0x%08X", addr);
46} 46}
47 47
48/** 48/**
@@ -60,12 +60,19 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) {
60static void LoadComponent(Service::Interface* self) { 60static void LoadComponent(Service::Interface* self) {
61 u32* cmd_buff = Kernel::GetCommandBuffer(); 61 u32* cmd_buff = Kernel::GetCommandBuffer();
62 62
63 u32 size = cmd_buff[1];
64 u32 unk1 = cmd_buff[2];
65 u32 unk2 = cmd_buff[3];
66 u32 new_size = cmd_buff[4];
67 u32 buffer = cmd_buff[5];
68
63 cmd_buff[1] = 0; // No error 69 cmd_buff[1] = 0; // No error
64 cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware 70 cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware
65 71
66 // TODO(bunnei): Implement real DSP firmware loading 72 // TODO(bunnei): Implement real DSP firmware loading
67 73
68 LOG_WARNING(Service_DSP, "(STUBBED) called"); 74 LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, unk1=0x%08X, unk2=0x%08X, new_size=0x%X, buffer=0x%08X",
75 size, unk1, unk2, new_size, buffer);
69} 76}
70 77
71/** 78/**
@@ -106,7 +113,7 @@ static void FlushDataCache(Service::Interface* self) {
106 113
107 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 114 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
108 115
109 LOG_DEBUG(Service_DSP, "(STUBBED) called address=0x%08X, size=0x%08X, process=0x%08X", 116 LOG_DEBUG(Service_DSP, "(STUBBED) called address=0x%08X, size=0x%X, process=0x%08X",
110 address, size, process); 117 address, size, process);
111} 118}
112 119
@@ -122,6 +129,10 @@ static void FlushDataCache(Service::Interface* self) {
122static void RegisterInterruptEvents(Service::Interface* self) { 129static void RegisterInterruptEvents(Service::Interface* self) {
123 u32* cmd_buff = Kernel::GetCommandBuffer(); 130 u32* cmd_buff = Kernel::GetCommandBuffer();
124 131
132 u32 param0 = cmd_buff[1];
133 u32 param1 = cmd_buff[2];
134 u32 event_handle = cmd_buff[4];
135
125 auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); 136 auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
126 if (evt != nullptr) { 137 if (evt != nullptr) {
127 interrupt_event = evt; 138 interrupt_event = evt;
@@ -133,7 +144,7 @@ static void RegisterInterruptEvents(Service::Interface* self) {
133 cmd_buff[1] = -1; 144 cmd_buff[1] = -1;
134 } 145 }
135 146
136 LOG_WARNING(Service_DSP, "(STUBBED) called"); 147 LOG_WARNING(Service_DSP, "(STUBBED) called param0=%u, param1=%u, event_handle=0x%08X", param0, param1, event_handle);
137} 148}
138 149
139/** 150/**
@@ -174,7 +185,7 @@ static void WriteProcessPipe(Service::Interface* self) {
174 185
175 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 186 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
176 187
177 LOG_WARNING(Service_DSP, "(STUBBED) called number=%u, size=0x%08X, new_size=0x%08X, buffer=0x%08X", 188 LOG_WARNING(Service_DSP, "(STUBBED) called number=%u, size=0x%X, new_size=0x%X, buffer=0x%08X",
178 number, size, new_size, buffer); 189 number, size, new_size, buffer);
179} 190}
180 191
@@ -192,6 +203,8 @@ static void WriteProcessPipe(Service::Interface* self) {
192static void ReadPipeIfPossible(Service::Interface* self) { 203static void ReadPipeIfPossible(Service::Interface* self) {
193 u32* cmd_buff = Kernel::GetCommandBuffer(); 204 u32* cmd_buff = Kernel::GetCommandBuffer();
194 205
206 u32 unk1 = cmd_buff[1];
207 u32 unk2 = cmd_buff[2];
195 u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size 208 u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size
196 VAddr addr = cmd_buff[0x41]; 209 VAddr addr = cmd_buff[0x41];
197 210
@@ -217,7 +230,8 @@ static void ReadPipeIfPossible(Service::Interface* self) {
217 cmd_buff[1] = 0; // No error 230 cmd_buff[1] = 0; // No error
218 cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16); 231 cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16);
219 232
220 LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr); 233 LOG_WARNING(Service_DSP, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, size=0x%X, buffer=0x%08X",
234 unk1, unk2, size, addr);
221} 235}
222 236
223/** 237/**
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 9ca5d13d4..0f30f743a 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -20,17 +20,17 @@ namespace HID {
20static const int MAX_CIRCLEPAD_POS = 0x9C; ///< Max value for a circle pad position 20static const int MAX_CIRCLEPAD_POS = 0x9C; ///< Max value for a circle pad position
21 21
22// Handle to shared memory region designated to HID_User service 22// Handle to shared memory region designated to HID_User service
23static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem = nullptr; 23static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
24 24
25// Event handles 25// Event handles
26static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1 = nullptr; 26static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1;
27static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2 = nullptr; 27static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2;
28static Kernel::SharedPtr<Kernel::Event> event_accelerometer = nullptr; 28static Kernel::SharedPtr<Kernel::Event> event_accelerometer;
29static Kernel::SharedPtr<Kernel::Event> event_gyroscope = nullptr; 29static Kernel::SharedPtr<Kernel::Event> event_gyroscope;
30static Kernel::SharedPtr<Kernel::Event> event_debug_pad = nullptr; 30static Kernel::SharedPtr<Kernel::Event> event_debug_pad;
31 31
32static u32 next_pad_index = 0; 32static u32 next_pad_index;
33static u32 next_touch_index = 0; 33static u32 next_touch_index;
34 34
35// TODO(peachum): 35// TODO(peachum):
36// Add a method for setting analog input from joystick device for the circle Pad. 36// Add a method for setting analog input from joystick device for the circle Pad.
@@ -175,6 +175,12 @@ void Init() {
175} 175}
176 176
177void Shutdown() { 177void Shutdown() {
178 shared_mem = nullptr;
179 event_pad_or_touch_1 = nullptr;
180 event_pad_or_touch_2 = nullptr;
181 event_accelerometer = nullptr;
182 event_gyroscope = nullptr;
183 event_debug_pad = nullptr;
178} 184}
179 185
180} // namespace HID 186} // namespace HID
diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp
index 58dfd8e1a..15ac477ef 100644
--- a/src/core/hle/service/ir/ir.cpp
+++ b/src/core/hle/service/ir/ir.cpp
@@ -15,8 +15,8 @@
15namespace Service { 15namespace Service {
16namespace IR { 16namespace IR {
17 17
18static Kernel::SharedPtr<Kernel::Event> handle_event = nullptr; 18static Kernel::SharedPtr<Kernel::Event> handle_event;
19static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr; 19static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
20 20
21void GetHandles(Service::Interface* self) { 21void GetHandles(Service::Interface* self) {
22 u32* cmd_buff = Kernel::GetCommandBuffer(); 22 u32* cmd_buff = Kernel::GetCommandBuffer();
@@ -41,6 +41,8 @@ void Init() {
41} 41}
42 42
43void Shutdown() { 43void Shutdown() {
44 shared_memory = nullptr;
45 handle_event = nullptr;
44} 46}
45 47
46} // namespace IR 48} // namespace IR
diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp
index 1cee81ab2..4b06efc3a 100644
--- a/src/core/hle/service/nwm_uds.cpp
+++ b/src/core/hle/service/nwm_uds.cpp
@@ -11,7 +11,7 @@
11 11
12namespace NWM_UDS { 12namespace NWM_UDS {
13 13
14static Kernel::SharedPtr<Kernel::Event> handle_event = nullptr; 14static Kernel::SharedPtr<Kernel::Event> handle_event;
15 15
16/** 16/**
17 * NWM_UDS::Shutdown service function 17 * NWM_UDS::Shutdown service function
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 57a301bec..d44510c1b 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -18,9 +18,9 @@ static const GameCoin default_game_coin = { 0x4F00, 42, 0, 0, 0, 2014, 12, 29 };
18/// Id of the SharedExtData archive used by the PTM process 18/// Id of the SharedExtData archive used by the PTM process
19static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0}; 19static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0};
20 20
21static bool shell_open = true; 21static bool shell_open;
22 22
23static bool battery_is_charging = true; 23static bool battery_is_charging;
24 24
25u32 GetAdapterState() { 25u32 GetAdapterState() {
26 // TODO(purpasmart96): This function is only a stub, 26 // TODO(purpasmart96): This function is only a stub,
@@ -43,6 +43,9 @@ void Init() {
43 AddService(new PTM_Sysm_Interface); 43 AddService(new PTM_Sysm_Interface);
44 AddService(new PTM_U_Interface); 44 AddService(new PTM_U_Interface);
45 45
46 shell_open = true;
47 battery_is_charging = true;
48
46 // Open the SharedExtSaveData archive 0xF000000B and create the gamecoin.dat file if it doesn't exist 49 // Open the SharedExtSaveData archive 0xF000000B and create the gamecoin.dat file if it doesn't exist
47 FileSys::Path archive_path(ptm_shared_extdata_id); 50 FileSys::Path archive_path(ptm_shared_extdata_id);
48 auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); 51 auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp
index 2d841f69c..13322bdbb 100644
--- a/src/core/hle/service/ptm/ptm_sysm.cpp
+++ b/src/core/hle/service/ptm/ptm_sysm.cpp
@@ -16,7 +16,7 @@ namespace PTM {
16 * 1: Result code, 0 on success, otherwise error code 16 * 1: Result code, 0 on success, otherwise error code
17 * 2: Whether the system is going through a power off 17 * 2: Whether the system is going through a power off
18 */ 18 */
19void IsLegacyPowerOff(Service::Interface* self) { 19static void IsLegacyPowerOff(Service::Interface* self) {
20 u32* cmd_buff = Kernel::GetCommandBuffer(); 20 u32* cmd_buff = Kernel::GetCommandBuffer();
21 cmd_buff[1] = RESULT_SUCCESS.raw; 21 cmd_buff[1] = RESULT_SUCCESS.raw;
22 cmd_buff[2] = 0; 22 cmd_buff[2] = 0;
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 134ff1740..d50327cb9 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -51,6 +51,49 @@ namespace Service {
51std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports; 51std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports;
52std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services; 52std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services;
53 53
54/**
55 * Creates a function string for logging, complete with the name (or header code, depending
56 * on what's passed in) the port name, and all the cmd_buff arguments.
57 */
58static std::string MakeFunctionString(const char* name, const char* port_name, const u32* cmd_buff) {
59 // Number of params == bits 0-5 + bits 6-11
60 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
61
62 std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name);
63 for (int i = 1; i <= num_params; ++i) {
64 function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
65 }
66 return function_string;
67}
68
69ResultVal<bool> Interface::SyncRequest() {
70 u32* cmd_buff = Kernel::GetCommandBuffer();
71 auto itr = m_functions.find(cmd_buff[0]);
72
73 if (itr == m_functions.end() || itr->second.func == nullptr) {
74 std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
75 LOG_ERROR(Service, "unknown / unimplemented %s", MakeFunctionString(function_name.c_str(), GetPortName().c_str(), cmd_buff).c_str());
76
77 // TODO(bunnei): Hack - ignore error
78 cmd_buff[1] = 0;
79 return MakeResult<bool>(false);
80 } else {
81 LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str());
82 }
83
84 itr->second.func(this);
85
86 return MakeResult<bool>(false); // TODO: Implement return from actual function
87}
88
89void Interface::Register(const FunctionInfo* functions, size_t n) {
90 m_functions.reserve(n);
91 for (size_t i = 0; i < n; ++i) {
92 // Usually this array is sorted by id already, so hint to instead at the end
93 m_functions.emplace_hint(m_functions.cend(), functions[i].id, functions[i]);
94 }
95}
96
54//////////////////////////////////////////////////////////////////////////////////////////////////// 97////////////////////////////////////////////////////////////////////////////////////////////////////
55// Module interface 98// Module interface
56 99
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index bfe16ebad..21ada67b5 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -4,20 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
8#include <string> 7#include <string>
9#include <unordered_map> 8#include <unordered_map>
10#include <vector>
11 9
12#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
13 11
14#include "common/common.h" 12#include "common/common.h"
15#include "common/string_util.h"
16#include "core/mem_map.h"
17 13
18#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
19#include "core/hle/kernel/session.h" 15#include "core/hle/kernel/session.h"
20#include "core/hle/svc.h"
21 16
22//////////////////////////////////////////////////////////////////////////////////////////////////// 17////////////////////////////////////////////////////////////////////////////////////////////////////
23// Namespace Service 18// Namespace Service
@@ -26,31 +21,11 @@ namespace Service {
26 21
27static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) 22static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
28 23
29class Manager;
30
31/// Interface to a CTROS service 24/// Interface to a CTROS service
32class Interface : public Kernel::Session { 25class Interface : public Kernel::Session {
33 // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be 26 // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be
34 // just something that encapsulates a session and acts as a helper to implement service 27 // just something that encapsulates a session and acts as a helper to implement service
35 // processes. 28 // processes.
36
37 friend class Manager;
38
39 /**
40 * Creates a function string for logging, complete with the name (or header code, depending
41 * on what's passed in) the port name, and all the cmd_buff arguments.
42 */
43 std::string MakeFunctionString(const char* name, const char* port_name, const u32* cmd_buff) {
44 // Number of params == bits 0-5 + bits 6-11
45 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
46
47 std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name);
48 for (int i = 1; i <= num_params; ++i) {
49 function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
50 }
51 return function_string;
52 }
53
54public: 29public:
55 std::string GetName() const override { return GetPortName(); } 30 std::string GetName() const override { return GetPortName(); }
56 31
@@ -70,25 +45,7 @@ public:
70 return "[UNKNOWN SERVICE PORT]"; 45 return "[UNKNOWN SERVICE PORT]";
71 } 46 }
72 47
73 ResultVal<bool> SyncRequest() override { 48 ResultVal<bool> SyncRequest() override;
74 u32* cmd_buff = Kernel::GetCommandBuffer();
75 auto itr = m_functions.find(cmd_buff[0]);
76
77 if (itr == m_functions.end() || itr->second.func == nullptr) {
78 std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
79 LOG_ERROR(Service, "unknown / unimplemented %s", MakeFunctionString(function_name.c_str(), GetPortName().c_str(), cmd_buff).c_str());
80
81 // TODO(bunnei): Hack - ignore error
82 cmd_buff[1] = 0;
83 return MakeResult<bool>(false);
84 } else {
85 LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str());
86 }
87
88 itr->second.func(this);
89
90 return MakeResult<bool>(false); // TODO: Implement return from actual function
91 }
92 49
93protected: 50protected:
94 51
@@ -96,14 +53,12 @@ protected:
96 * Registers the functions in the service 53 * Registers the functions in the service
97 */ 54 */
98 template <size_t N> 55 template <size_t N>
99 void Register(const FunctionInfo (&functions)[N]) { 56 inline void Register(const FunctionInfo (&functions)[N]) {
100 m_functions.reserve(N); 57 Register(functions, N);
101 for (auto& fn : functions) {
102 // Usually this array is sorted by id already, so hint to instead at the end
103 m_functions.emplace_hint(m_functions.cend(), fn.id, fn);
104 }
105 } 58 }
106 59
60 void Register(const FunctionInfo* functions, size_t n);
61
107private: 62private:
108 boost::container::flat_map<u32, FunctionInfo> m_functions; 63 boost::container::flat_map<u32, FunctionInfo> m_functions;
109 64
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index 6607965e1..33ecf64a2 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -11,7 +11,7 @@
11 11
12namespace Y2R_U { 12namespace Y2R_U {
13 13
14static Kernel::SharedPtr<Kernel::Event> completion_event = 0; 14static Kernel::SharedPtr<Kernel::Event> completion_event;
15 15
16/** 16/**
17 * Y2R_U::IsBusyConversion service function 17 * Y2R_U::IsBusyConversion service function
diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp
index 568dad684..94fae2551 100644
--- a/src/core/hle/shared_page.cpp
+++ b/src/core/hle/shared_page.cpp
@@ -62,6 +62,8 @@ template void Read<u16>(u16 &var, const u32 addr);
62template void Read<u8>(u8 &var, const u32 addr); 62template void Read<u8>(u8 &var, const u32 addr);
63 63
64void Set3DSlider(float amount) { 64void Set3DSlider(float amount) {
65 memset(&shared_page, 0, sizeof(shared_page));
66
65 shared_page.sliderstate_3d = amount; 67 shared_page.sliderstate_3d = amount;
66 shared_page.ledstate_3d = (amount == 0.0f); // off when non-zero 68 shared_page.ledstate_3d = (amount == 0.0f); // off when non-zero
67} 69}
@@ -71,4 +73,7 @@ void Init() {
71 Set3DSlider(0.0f); 73 Set3DSlider(0.0f);
72} 74}
73 75
76void Shutdown() {
77}
78
74} // namespace 79} // namespace
diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h
index 8f93545ec..1b6e4e581 100644
--- a/src/core/hle/shared_page.h
+++ b/src/core/hle/shared_page.h
@@ -23,4 +23,6 @@ void Set3DSlider(float amount);
23 23
24void Init(); 24void Init();
25 25
26void Shutdown();
27
26} // namespace 28} // namespace
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 76e9b171a..2da488d83 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -4,6 +4,7 @@
4 4
5#include <map> 5#include <map>
6 6
7#include "common/profiler.h"
7#include "common/string_util.h" 8#include "common/string_util.h"
8#include "common/symbols.h" 9#include "common/symbols.h"
9 10
@@ -606,7 +607,17 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
606 return RESULT_SUCCESS; 607 return RESULT_SUCCESS;
607} 608}
608 609
609const HLE::FunctionDef SVC_Table[] = { 610namespace {
611 struct FunctionDef {
612 using Func = void();
613
614 u32 id;
615 Func* func;
616 const char* name;
617 };
618}
619
620static const FunctionDef SVC_Table[] = {
610 {0x00, nullptr, "Unknown"}, 621 {0x00, nullptr, "Unknown"},
611 {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"}, 622 {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"},
612 {0x02, HLE::Wrap<QueryMemory>, "QueryMemory"}, 623 {0x02, HLE::Wrap<QueryMemory>, "QueryMemory"},
@@ -735,8 +746,28 @@ const HLE::FunctionDef SVC_Table[] = {
735 {0x7D, nullptr, "QueryProcessMemory"}, 746 {0x7D, nullptr, "QueryProcessMemory"},
736}; 747};
737 748
738void Register() { 749Common::Profiling::TimingCategory profiler_svc("SVC Calls");
739 HLE::RegisterModule("SVC_Table", ARRAY_SIZE(SVC_Table), SVC_Table); 750
751static const FunctionDef* GetSVCInfo(u32 opcode) {
752 u32 func_num = opcode & 0xFFFFFF; // 8 bits
753 if (func_num >= ARRAY_SIZE(SVC_Table)) {
754 LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num);
755 return nullptr;
756 }
757 return &SVC_Table[func_num];
758}
759
760void CallSVC(u32 opcode) {
761 Common::Profiling::ScopeTimer timer_svc(profiler_svc);
762
763 const FunctionDef *info = GetSVCInfo(opcode);
764 if (info) {
765 if (info->func) {
766 info->func();
767 } else {
768 LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name);
769 }
770 }
740} 771}
741 772
742} // namespace 773} // namespace
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
index 5d020a5ba..4389aa73d 100644
--- a/src/core/hle/svc.h
+++ b/src/core/hle/svc.h
@@ -41,6 +41,6 @@ enum ArbitrationType {
41 41
42namespace SVC { 42namespace SVC {
43 43
44void Register(); 44void CallSVC(u32 opcode);
45 45
46} // namespace 46} // namespace
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 308ea2035..0ad7e2963 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -29,8 +29,7 @@ namespace GPU {
29Regs g_regs; 29Regs g_regs;
30 30
31/// True if the current frame was skipped 31/// True if the current frame was skipped
32bool g_skip_frame = false; 32bool g_skip_frame;
33
34/// 268MHz / gpu_refresh_rate frames per second 33/// 268MHz / gpu_refresh_rate frames per second
35static u64 frame_ticks; 34static u64 frame_ticks;
36/// Event id for CoreTiming 35/// Event id for CoreTiming
@@ -38,7 +37,7 @@ static int vblank_event;
38/// Total number of frames drawn 37/// Total number of frames drawn
39static u64 frame_count; 38static u64 frame_count;
40/// True if the last frame was skipped 39/// True if the last frame was skipped
41static bool last_skip_frame = false; 40static bool last_skip_frame;
42 41
43template <typename T> 42template <typename T>
44inline void Read(T &var, const u32 raw_addr) { 43inline void Read(T &var, const u32 raw_addr) {
@@ -320,6 +319,8 @@ static void VBlankCallback(u64 userdata, int cycles_late) {
320 319
321/// Initialize hardware 320/// Initialize hardware
322void Init() { 321void Init() {
322 memset(&g_regs, 0, sizeof(g_regs));
323
323 auto& framebuffer_top = g_regs.framebuffer_config[0]; 324 auto& framebuffer_top = g_regs.framebuffer_config[0];
324 auto& framebuffer_sub = g_regs.framebuffer_config[1]; 325 auto& framebuffer_sub = g_regs.framebuffer_config[1];
325 326
@@ -349,6 +350,7 @@ void Init() {
349 frame_ticks = 268123480 / Settings::values.gpu_refresh_rate; 350 frame_ticks = 268123480 / Settings::values.gpu_refresh_rate;
350 last_skip_frame = false; 351 last_skip_frame = false;
351 g_skip_frame = false; 352 g_skip_frame = false;
353 frame_count = 0;
352 354
353 vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback); 355 vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback);
354 CoreTiming::ScheduleEvent(frame_ticks, vblank_event); 356 CoreTiming::ScheduleEvent(frame_ticks, vblank_event);
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index bed50af50..236958139 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -63,6 +63,8 @@ void Init() {
63 63
64/// Shutdown hardware 64/// Shutdown hardware
65void Shutdown() { 65void Shutdown() {
66 GPU::Shutdown();
67 LCD::Shutdown();
66 LOG_DEBUG(HW, "shutdown OK"); 68 LOG_DEBUG(HW, "shutdown OK");
67} 69}
68 70
diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp
index 7986f3ddb..8a09c3bc0 100644
--- a/src/core/hw/lcd.cpp
+++ b/src/core/hw/lcd.cpp
@@ -55,6 +55,7 @@ template void Write<u8>(u32 addr, const u8 data);
55 55
56/// Initialize hardware 56/// Initialize hardware
57void Init() { 57void Init() {
58 memset(&g_regs, 0, sizeof(g_regs));
58 LOG_DEBUG(HW_LCD, "initialized OK"); 59 LOG_DEBUG(HW_LCD, "initialized OK");
59} 60}
60 61
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index aaaa4d650..4efed78bf 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -198,20 +198,33 @@ ResultStatus AppLoader_NCCH::Load() {
198 if (file->ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) 198 if (file->ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header))
199 return ResultStatus::Error; 199 return ResultStatus::Error;
200 200
201 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; 201 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
202 entry_point = exheader_header.codeset_info.text.address; 202 entry_point = exheader_header.codeset_info.text.address;
203 203 code_size = exheader_header.codeset_info.text.code_size;
204 LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name); 204 stack_size = exheader_header.codeset_info.stack_size;
205 LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no"); 205 bss_size = exheader_header.codeset_info.bss_size;
206 LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point); 206 core_version = exheader_header.arm11_system_local_caps.core_version;
207 priority = exheader_header.arm11_system_local_caps.priority;
208 resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category;
209
210 LOG_INFO(Loader, "Name: %s" , exheader_header.codeset_info.name);
211 LOG_DEBUG(Loader, "Code compressed: %s" , is_compressed ? "yes" : "no");
212 LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
213 LOG_DEBUG(Loader, "Code size: 0x%08X", code_size);
214 LOG_DEBUG(Loader, "Stack size: 0x%08X", stack_size);
215 LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size);
216 LOG_DEBUG(Loader, "Core version: %d" , core_version);
217 LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority);
218 LOG_DEBUG(Loader, "Resource limit descriptor: 0x%08X", exheader_header.arm11_system_local_caps.resource_limit_descriptor);
219 LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category);
207 220
208 // Read ExeFS... 221 // Read ExeFS...
209 222
210 exefs_offset = ncch_header.exefs_offset * kBlockSize; 223 exefs_offset = ncch_header.exefs_offset * kBlockSize;
211 u32 exefs_size = ncch_header.exefs_size * kBlockSize; 224 u32 exefs_size = ncch_header.exefs_size * kBlockSize;
212 225
213 LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset); 226 LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
214 LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size); 227 LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
215 228
216 file->Seek(exefs_offset + ncch_offset, SEEK_SET); 229 file->Seek(exefs_offset + ncch_offset, SEEK_SET);
217 if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) 230 if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
@@ -247,8 +260,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
247 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; 260 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
248 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; 261 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
249 262
250 LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); 263 LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
251 LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); 264 LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
252 265
253 buffer.resize(romfs_size); 266 buffer.resize(romfs_size);
254 267
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index f6f670060..3dd151dbd 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -43,6 +43,8 @@ struct NCCH_Header {
43 u8 romfs_super_block_hash[0x20]; 43 u8 romfs_super_block_hash[0x20];
44}; 44};
45 45
46static_assert(sizeof(NCCH_Header) == 0x200, "NCCH header structure size is wrong");
47
46//////////////////////////////////////////////////////////////////////////////////////////////////// 48////////////////////////////////////////////////////////////////////////////////////////////////////
47// ExeFS (executable file system) headers 49// ExeFS (executable file system) headers
48 50
@@ -77,11 +79,11 @@ struct ExHeader_CodeSetInfo {
77 u8 name[8]; 79 u8 name[8];
78 ExHeader_SystemInfoFlags flags; 80 ExHeader_SystemInfoFlags flags;
79 ExHeader_CodeSegmentInfo text; 81 ExHeader_CodeSegmentInfo text;
80 u8 stacksize[4]; 82 u32 stack_size;
81 ExHeader_CodeSegmentInfo ro; 83 ExHeader_CodeSegmentInfo ro;
82 u8 reserved[4]; 84 u8 reserved[4];
83 ExHeader_CodeSegmentInfo data; 85 ExHeader_CodeSegmentInfo data;
84 u8 bsssize[4]; 86 u32 bss_size;
85}; 87};
86 88
87struct ExHeader_DependencyList{ 89struct ExHeader_DependencyList{
@@ -107,9 +109,9 @@ struct ExHeader_ARM11_SystemLocalCaps{
107 u32 core_version; 109 u32 core_version;
108 u8 flags[3]; 110 u8 flags[3];
109 u8 priority; 111 u8 priority;
110 u8 resource_limit_descriptor[0x16][2]; 112 u8 resource_limit_descriptor[0x10][2];
111 ExHeader_StorageInfo storage_info; 113 ExHeader_StorageInfo storage_info;
112 u8 service_access_control[0x32][8]; 114 u8 service_access_control[0x20][8];
113 u8 ex_service_access_control[0x2][8]; 115 u8 ex_service_access_control[0x2][8];
114 u8 reserved[0xf]; 116 u8 reserved[0xf];
115 u8 resource_limit_category; 117 u8 resource_limit_category;
@@ -141,6 +143,8 @@ struct ExHeader_Header{
141 } access_desc; 143 } access_desc;
142}; 144};
143 145
146static_assert(sizeof(ExHeader_Header) == 0x800, "ExHeader structure size is wrong");
147
144//////////////////////////////////////////////////////////////////////////////////////////////////// 148////////////////////////////////////////////////////////////////////////////////////////////////////
145// Loader namespace 149// Loader namespace
146 150
@@ -224,6 +228,12 @@ private:
224 bool is_compressed = false; 228 bool is_compressed = false;
225 229
226 u32 entry_point = 0; 230 u32 entry_point = 0;
231 u32 code_size = 0;
232 u32 stack_size = 0;
233 u32 bss_size = 0;
234 u32 core_version = 0;
235 u8 priority = 0;
236 u8 resource_limit_category = 0;
227 u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header 237 u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header
228 u32 exefs_offset = 0; 238 u32 exefs_offset = 0;
229 239
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp
index a14e8303e..22e359b3e 100644
--- a/src/core/mem_map.cpp
+++ b/src/core/mem_map.cpp
@@ -11,30 +11,30 @@
11 11
12namespace Memory { 12namespace Memory {
13 13
14u8* g_base = nullptr; ///< The base pointer to the auto-mirrored arena. 14u8* g_base; ///< The base pointer to the auto-mirrored arena.
15 15
16static MemArena arena; ///< The MemArena class 16static MemArena arena; ///< The MemArena class
17 17
18u8* g_exefs_code = nullptr; ///< ExeFS:/.code is loaded here 18u8* g_exefs_code; ///< ExeFS:/.code is loaded here
19u8* g_system_mem = nullptr; ///< System memory 19u8* g_system_mem; ///< System memory
20u8* g_heap = nullptr; ///< Application heap (main memory) 20u8* g_heap; ///< Application heap (main memory)
21u8* g_heap_linear = nullptr; ///< Linear heap 21u8* g_heap_linear; ///< Linear heap
22u8* g_vram = nullptr; ///< Video memory (VRAM) pointer 22u8* g_vram; ///< Video memory (VRAM) pointer
23u8* g_shared_mem = nullptr; ///< Shared memory 23u8* g_shared_mem; ///< Shared memory
24u8* g_dsp_mem = nullptr; ///< DSP memory 24u8* g_dsp_mem; ///< DSP memory
25u8* g_kernel_mem; ///< Kernel memory 25u8* g_kernel_mem; ///< Kernel memory
26 26
27static u8* physical_bootrom = nullptr; ///< Bootrom physical memory 27static u8* physical_bootrom; ///< Bootrom physical memory
28static u8* uncached_bootrom = nullptr; 28static u8* uncached_bootrom;
29 29
30static u8* physical_exefs_code = nullptr; ///< Phsical ExeFS:/.code is loaded here 30static u8* physical_exefs_code; ///< Phsical ExeFS:/.code is loaded here
31static u8* physical_system_mem = nullptr; ///< System physical memory 31static u8* physical_system_mem; ///< System physical memory
32static u8* physical_fcram = nullptr; ///< Main physical memory (FCRAM) 32static u8* physical_fcram; ///< Main physical memory (FCRAM)
33static u8* physical_heap_gsp = nullptr; ///< GSP heap physical memory 33static u8* physical_heap_gsp; ///< GSP heap physical memory
34static u8* physical_vram = nullptr; ///< Video physical memory (VRAM) 34static u8* physical_vram; ///< Video physical memory (VRAM)
35static u8* physical_shared_mem = nullptr; ///< Physical shared memory 35static u8* physical_shared_mem; ///< Physical shared memory
36static u8* physical_dsp_mem = nullptr; ///< Physical DSP memory 36static u8* physical_dsp_mem; ///< Physical DSP memory
37static u8* physical_kernel_mem; ///< Kernel memory 37static u8* physical_kernel_mem; ///< Kernel memory
38 38
39// We don't declare the IO region in here since its handled by other means. 39// We don't declare the IO region in here since its handled by other means.
40static MemoryView g_views[] = { 40static MemoryView g_views[] = {
@@ -73,6 +73,7 @@ void Init() {
73 } 73 }
74 74
75 g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &arena); 75 g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &arena);
76 MemBlock_Init();
76 77
77 LOG_DEBUG(HW_Memory, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap, 78 LOG_DEBUG(HW_Memory, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap,
78 physical_fcram); 79 physical_fcram);
@@ -81,9 +82,29 @@ void Init() {
81void Shutdown() { 82void Shutdown() {
82 u32 flags = 0; 83 u32 flags = 0;
83 MemoryMap_Shutdown(g_views, kNumMemViews, flags, &arena); 84 MemoryMap_Shutdown(g_views, kNumMemViews, flags, &arena);
84
85 arena.ReleaseSpace(); 85 arena.ReleaseSpace();
86 MemBlock_Shutdown();
87
86 g_base = nullptr; 88 g_base = nullptr;
89 g_exefs_code = nullptr;
90 g_system_mem = nullptr;
91 g_heap = nullptr;
92 g_heap_linear = nullptr;
93 g_vram = nullptr;
94 g_shared_mem = nullptr;
95 g_dsp_mem = nullptr;
96 g_kernel_mem = nullptr;
97
98 physical_bootrom = nullptr;
99 uncached_bootrom = nullptr;
100 physical_exefs_code = nullptr;
101 physical_system_mem = nullptr;
102 physical_fcram = nullptr;
103 physical_heap_gsp = nullptr;
104 physical_vram = nullptr;
105 physical_shared_mem = nullptr;
106 physical_dsp_mem = nullptr;
107 physical_kernel_mem = nullptr;
87 108
88 LOG_DEBUG(HW_Memory, "shutdown OK"); 109 LOG_DEBUG(HW_Memory, "shutdown OK");
89} 110}
diff --git a/src/core/mem_map.h b/src/core/mem_map.h
index ff730593e..1af02973b 100644
--- a/src/core/mem_map.h
+++ b/src/core/mem_map.h
@@ -171,6 +171,12 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions);
171 */ 171 */
172u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions); 172u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions);
173 173
174/// Initialize mapped memory blocks
175void MemBlock_Init();
176
177/// Shutdown mapped memory blocks
178void MemBlock_Shutdown();
179
174inline const char* GetCharPointer(const VAddr address) { 180inline const char* GetCharPointer(const VAddr address) {
175 return (const char *)GetPointer(address); 181 return (const char *)GetPointer(address);
176} 182}
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index 5878b99dc..8759ebdfb 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -15,7 +15,6 @@ namespace Memory {
15 15
16static std::map<u32, MemoryBlock> heap_map; 16static std::map<u32, MemoryBlock> heap_map;
17static std::map<u32, MemoryBlock> heap_linear_map; 17static std::map<u32, MemoryBlock> heap_linear_map;
18static std::map<u32, MemoryBlock> shared_map;
19 18
20/// Convert a physical address to virtual address 19/// Convert a physical address to virtual address
21VAddr PhysicalToVirtualAddress(const PAddr addr) { 20VAddr PhysicalToVirtualAddress(const PAddr addr) {
@@ -185,12 +184,6 @@ u8 *GetPointer(const VAddr vaddr) {
185 } 184 }
186} 185}
187 186
188/**
189 * Maps a block of memory on the heap
190 * @param size Size of block in bytes
191 * @param operation Memory map operation type
192 * @param flags Memory allocation flags
193 */
194u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) { 187u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) {
195 MemoryBlock block; 188 MemoryBlock block;
196 189
@@ -208,12 +201,6 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) {
208 return block.GetVirtualAddress(); 201 return block.GetVirtualAddress();
209} 202}
210 203
211/**
212 * Maps a block of memory on the linear heap
213 * @param size Size of block in bytes
214 * @param operation Memory map operation type
215 * @param flags Memory allocation flags
216 */
217u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) { 204u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) {
218 MemoryBlock block; 205 MemoryBlock block;
219 206
@@ -231,6 +218,14 @@ u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) {
231 return block.GetVirtualAddress(); 218 return block.GetVirtualAddress();
232} 219}
233 220
221void MemBlock_Init() {
222}
223
224void MemBlock_Shutdown() {
225 heap_map.clear();
226 heap_linear_map.clear();
227}
228
234u8 Read8(const VAddr addr) { 229u8 Read8(const VAddr addr) {
235 u8 data = 0; 230 u8 data = 0;
236 Read<u8>(data, addr); 231 Read<u8>(data, addr);
diff --git a/src/core/system.cpp b/src/core/system.cpp
index f4c2df1cd..561ff82f0 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -14,11 +14,6 @@
14 14
15namespace System { 15namespace System {
16 16
17volatile State g_state;
18
19void UpdateState(State state) {
20}
21
22void Init(EmuWindow* emu_window) { 17void Init(EmuWindow* emu_window) {
23 Core::Init(); 18 Core::Init();
24 CoreTiming::Init(); 19 CoreTiming::Init();
@@ -29,13 +24,6 @@ void Init(EmuWindow* emu_window) {
29 VideoCore::Init(emu_window); 24 VideoCore::Init(emu_window);
30} 25}
31 26
32void RunLoopFor(int cycles) {
33 RunLoopUntil(CoreTiming::GetTicks() + cycles);
34}
35
36void RunLoopUntil(u64 global_cycles) {
37}
38
39void Shutdown() { 27void Shutdown() {
40 VideoCore::Shutdown(); 28 VideoCore::Shutdown();
41 HLE::Shutdown(); 29 HLE::Shutdown();
diff --git a/src/core/system.h b/src/core/system.h
index 05d836530..59a75ca12 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -4,30 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/emu_window.h" 7class EmuWindow;
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10 8
11namespace System { 9namespace System {
12 10
13// State of the full emulator
14enum State {
15 STATE_NULL = 0, ///< System is in null state, nothing initialized
16 STATE_IDLE, ///< System is in an initialized state, but not running
17 STATE_RUNNING, ///< System is running
18 STATE_LOADING, ///< System is loading a ROM
19 STATE_HALTED, ///< System is halted (error)
20 STATE_STALLED, ///< System is stalled (unused)
21 STATE_DEBUG, ///< System is in a special debug mode (unused)
22 STATE_DIE ///< System is shutting down
23};
24
25extern volatile State g_state;
26
27void UpdateState(State state);
28void Init(EmuWindow* emu_window); 11void Init(EmuWindow* emu_window);
29void RunLoopFor(int cycles);
30void RunLoopUntil(u64 global_cycles);
31void Shutdown(); 12void Shutdown();
32 13
33} 14}
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index c460146cb..2d9d8ab1f 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -507,7 +507,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
507 // Add modifier 507 // Add modifier
508 unsigned table_index = (x < 2) ? table_index_1.Value() : table_index_2.Value(); 508 unsigned table_index = (x < 2) ? table_index_1.Value() : table_index_2.Value();
509 509
510 static const auto etc1_modifier_table = std::array<std::array<u8, 2>, 8>{{ 510 static const std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{
511 { 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 }, 511 { 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 },
512 { 18, 60 }, { 24, 80 }, { 33, 106 }, { 47, 183 } 512 { 18, 60 }, { 24, 80 }, { 33, 106 }, { 47, 183 }
513 }}; 513 }};
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index dd46f0ec3..6ec253601 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -342,10 +342,10 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0,
342 342
343 case Regs::TextureConfig::MirroredRepeat: 343 case Regs::TextureConfig::MirroredRepeat:
344 { 344 {
345 int coord = (int)((unsigned)val % (2 * size)); 345 unsigned int coord = ((unsigned)val % (2 * size));
346 if (coord >= size) 346 if (coord >= size)
347 coord = 2 * size - 1 - coord; 347 coord = 2 * size - 1 - coord;
348 return coord; 348 return (int)coord;
349 } 349 }
350 350
351 default: 351 default: