diff options
208 files changed, 4357 insertions, 5221 deletions
diff --git a/.gitmodules b/.gitmodules index a9e0a5c1a..598e4c64d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ -[submodule "externals/inih/inih"] +[submodule "inih"] path = externals/inih/inih url = https://github.com/svn2github/inih -[submodule "externals/boost"] +[submodule "boost"] path = externals/boost url = https://github.com/citra-emu/ext-boost.git -[submodule "externals/nihstro"] +[submodule "nihstro"] path = externals/nihstro url = https://github.com/neobrain/nihstro.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cae66dec..fc742317c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,11 @@ if (NOT MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes -pthread") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") else() - # Silence deprecation warnings - add_definitions(/D_CRT_SECURE_NO_WARNINGS) + # Silence "deprecation" warnings + add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE) + # Avoid windows.h junk + add_definitions(/DNOMINMAX) + # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo CACHE STRING "" FORCE) @@ -197,3 +200,16 @@ if(ENABLE_QT) add_subdirectory(externals/qhexedit) endif() add_subdirectory(src) + +# Install freedesktop.org metadata files, following those specifications: +# http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html +# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html +# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") + install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.desktop" + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") + install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.svg" + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pixmaps") + install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.xml" + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages") +endif() @@ -5,7 +5,7 @@ Citra Emulator Citra 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. -Citra 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. +Citra 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. For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/?channels=citra). diff --git a/dist/citra.desktop b/dist/citra.desktop new file mode 100644 index 000000000..1300d62c2 --- /dev/null +++ b/dist/citra.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Citra +GenericName=3DS Emulator +GenericName[fr]=Émulateur 3DS +Comment=Nintendo 3DS video game console emulator +Comment[fr]=Émulateur de console de jeu Nintendo 3DS +Icon=citra +TryExec=citra-qt +Exec=citra-qt %f +Categories=Game;Emulator;Qt; +MimeType=application/x-ctr-3dsx;application/x-ctr-cci;application/x-ctr-cia;application/x-ctr-cxi; +Keywords=3DS;Nintendo; diff --git a/dist/citra.svg b/dist/citra.svg new file mode 100644 index 000000000..7b299cd89 --- /dev/null +++ b/dist/citra.svg @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2014 Citra Emulator Project + Licensed under GPLv2 or any later version + Refer to the license.txt file included. +--> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 341.071 338.846"> + <radialGradient id="a" cx="170.5356" cy="167.271" r="170.5332" gradientTransform="matrix(1 0 0 0.9935 0 3.2396)" gradientUnits="userSpaceOnUse"> + <stop offset="0.5193" stop-color="#FFFFFF" stop-opacity="0.1"/> + <stop offset="0.9415" stop-color="#000000" stop-opacity="0.5"/> + <stop offset="1" stop-color="#1A1818" stop-opacity="0"/> + </radialGradient> + <ellipse fill="url(#a)" cx="170.535" cy="169.423" rx="170.535" ry="169.423"/> + <circle fill="#D16F17" cx="170.536" cy="167.885" r="161.557"/> + <linearGradient id="b" gradientUnits="userSpaceOnUse" x1="234.4458" y1="33.5771" x2="97.5655" y2="321.2358"> + <stop offset="0" stop-color="#FFF8BD"/> + <stop offset="1" stop-color="#F6DCAE"/> + </linearGradient> + <circle fill="url(#b)" cx="170.536" cy="167.885" r="155.295"/> + <g> + <linearGradient id="c" gradientUnits="userSpaceOnUse" x1="332.436" y1="91.7446" x2="111.1593" y2="342.0988"> + <stop offset="0" stop-color="#F7A076"/> + <stop offset="0.4455" stop-color="#F3816C"/> + <stop offset="1" stop-color="#F06878"/> + </linearGradient> + <path fill="url(#c)" stroke="#F06564" stroke-miterlimit="10" d="M309.704,123.138 + c-5.9-7.802-128.517,44.681-128.517,44.681S303.803,221.01,309.704,212.5C322.434,194.142,323.182,140.957,309.704,123.138z"/> + <linearGradient id="d" gradientUnits="userSpaceOnUse" x1="285.5845" y1="50.3345" x2="64.3074" y2="300.6891"> + <stop offset="0" stop-color="#9DC63B"/> + <stop offset="1" stop-color="#9BC183"/> + </linearGradient> + <path fill="url(#d)" stroke="#72AA42" stroke-miterlimit="10" d="M300.518,100.96c-3.98-21.983-41.059-60.12-63.189-63.188 + c-9.688-1.345-59.28,122.469-59.28,122.469S302.364,111.149,300.518,100.96z"/> + <linearGradient id="e" gradientUnits="userSpaceOnUse" x1="229.4995" y1="0.7637" x2="8.2231" y2="251.1176"> + <stop offset="0" stop-color="#D5DE26"/> + <stop offset="1" stop-color="#C5D94B"/> + </linearGradient> + <path fill="url(#e)" stroke="#BECD30" stroke-miterlimit="10" d="M215.151,28.584c-18.357-12.73-71.543-13.478-89.362,0.001 + c-7.801,5.899,44.682,128.516,44.682,128.516S223.663,34.484,215.151,28.584z"/> + <linearGradient id="f" gradientUnits="userSpaceOnUse" x1="219.3823" y1="-8.1782" x2="-1.8941" y2="242.1756"> + <stop offset="0" stop-color="#F2D200"/> + <stop offset="1" stop-color="#FDEF52"/> + </linearGradient> + <path fill="url(#f)" stroke="#E1BE29" stroke-miterlimit="10" d="M162.893,160.239c0,0-49.092-124.315-59.281-122.469 + c-21.982,3.979-60.12,41.058-63.188,63.189C39.078,110.646,162.893,160.239,162.893,160.239z"/> + <linearGradient id="g" gradientUnits="userSpaceOnUse" x1="226.0718" y1="-2.2656" x2="4.7951" y2="248.0886"> + <stop offset="0" stop-color="#FFCD10"/> + <stop offset="1" stop-color="#F29634"/> + </linearGradient> + <path fill="url(#g)" stroke="#F79421" stroke-miterlimit="10" d="M31.236,123.136c-12.73,18.357-13.479,71.543,0,89.362 + c5.898,7.801,128.516-44.682,128.516-44.682S37.135,114.625,31.236,123.136z"/> + <linearGradient id="h" gradientUnits="userSpaceOnUse" x1="272.9214" y1="39.144" x2="51.6446" y2="289.4984"> + <stop offset="0" stop-color="#F79F1C"/> + <stop offset="0.4455" stop-color="#F08021"/> + <stop offset="1" stop-color="#ED693C"/> + </linearGradient> + <path fill="url(#h)" stroke="#F16622" stroke-miterlimit="10" d="M40.422,234.676c3.979,21.982,41.057,60.12,63.188,63.188 + c9.687,1.346,59.279-122.468,59.279-122.468S38.574,224.487,40.422,234.676z"/> + <linearGradient id="i" gradientUnits="userSpaceOnUse" x1="329.0083" y1="88.7129" x2="107.7311" y2="339.0677"> + <stop offset="0" stop-color="#E47C26"/> + <stop offset="0.4455" stop-color="#DF5B27"/> + <stop offset="1" stop-color="#DD3A3A"/> + </linearGradient> + <path fill="url(#i)" stroke="#E03827" stroke-miterlimit="10" d="M125.787,307.051c18.357,12.73,71.543,13.48,89.362,0 + c7.801-5.898-44.681-128.515-44.681-128.515S117.275,301.153,125.787,307.051z"/> + <linearGradient id="j" gradientUnits="userSpaceOnUse" x1="339.1245" y1="97.6562" x2="117.8478" y2="348.0104"> + <stop offset="0" stop-color="#F3783C"/> + <stop offset="0.4455" stop-color="#EF5339"/> + <stop offset="1" stop-color="#ED294A"/> + </linearGradient> + <path fill="url(#j)" stroke="#ED2836" stroke-miterlimit="10" d="M178.047,175.398c0,0,49.09,124.315,59.28,122.467 + c21.982-3.979,60.121-41.057,63.189-63.188C301.86,224.991,178.047,175.398,178.047,175.398z"/> + </g> + <linearGradient id="k" gradientUnits="userSpaceOnUse" x1="170.5352" y1="6.3281" x2="170.5351" y2="329.4424"> + <stop offset="0" stop-color="#FFFFFF" stop-opacity="0.2"/> + <stop offset="0.4504" stop-color="#908E8E" stop-opacity="0.05"/> + <stop offset="1" stop-color="#030003" stop-opacity="0.2"/> + </linearGradient> + <circle fill="url(#k)" cx="170.536" cy="167.885" r="161.557"/> +</svg> diff --git a/dist/citra.xml b/dist/citra.xml new file mode 100644 index 000000000..bcb9acd87 --- /dev/null +++ b/dist/citra.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info"> + <mime-type type="application/x-ctr-3dsx"> + <comment>3DS homebrew executable</comment> + <comment xml:lang="fr">Exécutable 3DS homebrew</comment> + <acronym>3DSX</acronym> + <icon name="citra"/> + <glob pattern="*.3dsx"/> + <magic><match value="3DSX" type="string" offset="0"/></magic> + </mime-type> + + <mime-type type="application/x-ctr-cci"> + <comment>3DS cartridge image</comment> + <comment xml:lang="fr">Image de cartouche 3DS</comment> + <acronym>CCI</acronym> + <expanded-acronym>CTR Cart Image</expanded-acronym> + <icon name="citra"/> + <glob pattern="*.cci"/> + <glob pattern="*.3ds"/> + <magic><match value="NCSD" type="string" offset="256"/></magic> + </mime-type> + + <mime-type type="application/x-ctr-cxi"> + <comment>3DS executable</comment> + <comment xml:lang="fr">Exécutable 3DS</comment> + <acronym>CXI</acronym> + <expanded-acronym>CTR eXecutable Image</expanded-acronym> + <icon name="citra"/> + <glob pattern="*.cxi"/> + <magic><match value="NCCH" type="string" offset="256"/></magic> + </mime-type> + + <mime-type type="application/x-ctr-cia"> + <comment>3DS importable archive</comment> + <comment xml:lang="fr">Archive importable 3DS</comment> + <acronym>CIA</acronym> + <expanded-acronym>CTR Importable Archive</expanded-acronym> + <icon name="citra"/> + <glob pattern="*.cia"/> + </mime-type> + + <mime-type type="application/x-ctr-smdh"> + <comment>3DS icon</comment> + <comment xml:lang="fr">Icône 3DS</comment> + <acronym>SMDH</acronym> + <expanded-acronym>System Menu Data Header</expanded-acronym> + <glob pattern="*.smdh"/> + <magic><match value="SMDH" type="string" offset="0"/></magic> + </mime-type> + + <mime-type type="application/x-ctr-cbmd"> + <comment>3DS banner</comment> + <comment xml:lang="fr">Bannière 3DS</comment> + <acronym>CBMD</acronym> + <expanded-acronym>CTR Banner Model Data</expanded-acronym> + <glob pattern="*.cbmd"/> + <magic><match value="CBMD" type="string" offset="0"/></magic> + </mime-type> +</mime-info> diff --git a/externals/nihstro b/externals/nihstro -Subproject 4a78588b308564f7ebae193e0ae00d9a0d5741d +Subproject 81f1804a43f625e3a1a20752c0db70a41341038 diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 2c6ced920..1d7e7f270 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -4,7 +4,7 @@ #include <thread> -#include "common/common.h" +#include "common/logging/log.h" #include "common/logging/text_formatter.h" #include "common/logging/backend.h" #include "common/logging/filter.h" @@ -19,7 +19,7 @@ #include "citra/emu_window/emu_window_glfw.h" /// Application entry point -int __cdecl main(int argc, char **argv) { +int main(int argc, char **argv) { std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger(); Log::Filter log_filter(Log::Level::Debug); Log::SetFilter(&log_filter); diff --git a/src/citra/config.cpp b/src/citra/config.cpp index e9f3dfd5b..ab564559d 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -5,7 +5,10 @@ #include <GLFW/glfw3.h> #include "citra/default_ini.h" + #include "common/file_util.h" +#include "common/logging/log.h" + #include "core/settings.h" #include "core/core.h" @@ -66,6 +69,11 @@ void Config::ReadValues() { Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 30); Settings::values.frame_skip = glfw_config->GetInteger("Core", "frame_skip", 0); + // Renderer + Settings::values.bg_red = (float)glfw_config->GetReal("Renderer", "bg_red", 1.0); + Settings::values.bg_green = (float)glfw_config->GetReal("Renderer", "bg_green", 1.0); + Settings::values.bg_blue = (float)glfw_config->GetReal("Renderer", "bg_blue", 1.0); + // Data Storage Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index fc02a3ceb..1dd971926 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -41,6 +41,13 @@ gpu_refresh_rate = # 0 (default): No frameskip, 1: x2 frameskip, 2: x4 frameskip, 3: x8 frameskip, etc. frame_skip = +[Renderer] +# The clear color for the renderer. What shows up on the sides of the bottom screen. +# Must be in range of 0.0-1.0. Defaults to 1.0 for all. +bg_red = +bg_blue = +bg_green = + [Data Storage] # Whether to create a virtual SD card. # 1 (default): Yes, 0: No diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 997e3bc7d..341b48d2a 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp @@ -4,7 +4,7 @@ #include <GLFW/glfw3.h> -#include "common/common.h" +#include "common/logging/log.h" #include "video_core/video_core.h" @@ -31,7 +31,7 @@ void EmuWindow_GLFW::OnMouseButtonEvent(GLFWwindow* win, int button, int action, } void EmuWindow_GLFW::OnCursorPosEvent(GLFWwindow* win, double x, double y) { - GetEmuWindow(win)->TouchMoved(static_cast<unsigned>(x), static_cast<unsigned>(y)); + GetEmuWindow(win)->TouchMoved(static_cast<unsigned>(std::max(x, 0.0)), static_cast<unsigned>(std::max(y, 0.0))); } /// Called by GLFW when a key event occurs diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index b81bd6167..d3df289f8 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -8,8 +8,8 @@ #include <QWindow> #endif -#include "common/common.h" #include "bootmanager.h" +#include "main.h" #include "core/core.h" #include "core/settings.h" @@ -27,43 +27,33 @@ #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" EmuThread::EmuThread(GRenderWindow* render_window) : - filename(""), exec_cpu_step(false), cpu_running(false), - stop_run(false), render_window(render_window) -{ -} + exec_step(false), running(false), stop_run(false), render_window(render_window) { -void EmuThread::SetFilename(std::string filename) -{ - this->filename = filename; + connect(this, SIGNAL(started()), render_window, SLOT(moveContext())); } -void EmuThread::run() -{ +void EmuThread::run() { stop_run = false; // holds whether the cpu was running during the last iteration, // so that the DebugModeLeft signal can be emitted before the // next execution step bool was_active = false; - while (!stop_run) - { - if (cpu_running) - { + while (!stop_run) { + if (running) { if (!was_active) emit DebugModeLeft(); Core::RunLoop(); - was_active = cpu_running || exec_cpu_step; - if (!was_active) + was_active = running || exec_step; + if (!was_active && !stop_run) emit DebugModeEntered(); - } - else if (exec_cpu_step) - { + } else if (exec_step) { if (!was_active) emit DebugModeLeft(); - exec_cpu_step = false; + exec_step = false; Core::SingleStep(); emit DebugModeEntered(); yieldCurrentThread(); @@ -71,47 +61,10 @@ void EmuThread::run() was_active = false; } } - render_window->moveContext(); - - Core::Stop(); -} - -void EmuThread::Stop() -{ - if (!isRunning()) - { - LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning..."); - return; - } - stop_run = true; - // Release emu threads from any breakpoints, so that this doesn't hang forever. - Pica::g_debug_context->ClearBreakpoints(); - - //core::g_state = core::SYS_DIE; - - // TODO: Waiting here is just a bad workaround for retarded shutdown logic. - wait(1000); - if (isRunning()) - { - LOG_WARNING(Frontend, "EmuThread still running, terminating..."); - quit(); - - // TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam - // queued... This should be fixed. - wait(50000); - if (isRunning()) - { - LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here..."); - terminate(); - } - } - LOG_INFO(Frontend, "EmuThread stopped"); - - System::Shutdown(); + render_window->moveContext(); } - // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context. // The corresponding functionality is handled in EmuThread instead class GGLWidgetInternal : public QGLWidget @@ -133,13 +86,9 @@ private: GRenderWindow* parent; }; -EmuThread& GRenderWindow::GetEmuThread() -{ - return emu_thread; -} +GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : + QWidget(parent), emu_thread(emu_thread), keyboard_id(0) { -GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0) -{ std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); setWindowTitle(QString::fromStdString(window_title)); @@ -160,7 +109,6 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this layout->addWidget(child); layout->setMargin(0); setLayout(layout); - connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); @@ -180,29 +128,17 @@ void GRenderWindow::moveContext() // We need to move GL context to the swapping thread in Qt5 #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) // If the thread started running, move the GL Context to the new thread. Otherwise, move it back. - child->context()->moveToThread((QThread::currentThread() == qApp->thread()) ? &emu_thread : qApp->thread()); + auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread(); + child->context()->moveToThread(thread); #endif } -GRenderWindow::~GRenderWindow() -{ - if (emu_thread.isRunning()) - emu_thread.Stop(); -} - void GRenderWindow::SwapBuffers() { // MakeCurrent is already called in renderer_opengl child->swapBuffers(); } -void GRenderWindow::closeEvent(QCloseEvent* event) -{ - if (emu_thread.isRunning()) - emu_thread.Stop(); - QWidget::closeEvent(event); -} - void GRenderWindow::MakeCurrent() { child->makeCurrent(); @@ -288,7 +224,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent *event) void GRenderWindow::mouseMoveEvent(QMouseEvent *event) { auto pos = event->pos(); - this->TouchMoved(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y())); + this->TouchMoved(static_cast<unsigned>(std::max(pos.x(), 0)), static_cast<unsigned>(std::max(pos.y(), 0))); } void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) @@ -335,3 +271,11 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { setMinimumSize(minimal_size.first, minimal_size.second); } + +void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { + this->emu_thread = emu_thread; +} + +void GRenderWindow::OnEmulationStopping() { + emu_thread = nullptr; +} diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 288da45a1..d5d74c949 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -7,74 +7,59 @@ #include <QThread> #include <QGLWidget> -#include "common/common.h" #include "common/emu_window.h" +#include "common/thread.h" class QScreen; class QKeyEvent; class GRenderWindow; +class GMainWindow; class EmuThread : public QThread { Q_OBJECT public: - /** - * Set image filename - * - * @param filename - * @warning Only call when not running! - */ - void SetFilename(std::string filename); + EmuThread(GRenderWindow* render_window); /** * Start emulation (on new thread) - * * @warning Only call when not running! */ void run() override; /** - * Allow the CPU to process a single instruction (if cpu is not running) - * + * Steps the emulation thread by a single CPU instruction (if the CPU is not already running) * @note This function is thread-safe */ - void ExecStep() { exec_cpu_step = true; } + void ExecStep() { exec_step = true; } /** - * Allow the CPU to continue processing instructions without interruption - * + * Sets whether the emulation thread is running or not + * @param running Boolean value, set the emulation thread to running if true * @note This function is thread-safe */ - void SetCpuRunning(bool running) { cpu_running = running; } + void SetRunning(bool running) { this->running = running; } /** - * Allow the CPU to continue processing instructions without interruption - * - * @note This function is thread-safe - */ - bool IsCpuRunning() { return cpu_running; } - + * Check if the emulation thread is running or not + * @return True if the emulation thread is running, otherwise false + * @note This function is thread-safe + */ + bool IsRunning() { return running; } -public slots: /** - * Stop emulation and wait for the thread to finish. - * - * @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. - * @note: This function is thread-safe. + * Requests for the emulation thread to stop running */ - void Stop(); + void RequestStop() { + stop_run = true; + running = false; + }; private: - friend class GRenderWindow; - - EmuThread(GRenderWindow* render_window); - - std::string filename; - - bool exec_cpu_step; - bool cpu_running; + bool exec_step; + bool running; std::atomic<bool> stop_run; GRenderWindow* render_window; @@ -100,10 +85,7 @@ class GRenderWindow : public QWidget, public EmuWindow Q_OBJECT public: - GRenderWindow(QWidget* parent = NULL); - ~GRenderWindow(); - - void closeEvent(QCloseEvent*) override; + GRenderWindow(QWidget* parent, EmuThread* emu_thread); // EmuWindow implementation void SwapBuffers() override; @@ -116,8 +98,6 @@ public: void restoreGeometry(const QByteArray& geometry); // overridden QByteArray saveGeometry(); // overridden - EmuThread& GetEmuThread(); - void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; @@ -134,15 +114,18 @@ public: public slots: void moveContext(); // overridden + void OnEmulationStarting(EmuThread* emu_thread); + void OnEmulationStopping(); + private: void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; QGLWidget* child; - EmuThread emu_thread; - QByteArray geometry; /// Device id of keyboard for use with KeyMap int keyboard_id; + + EmuThread* emu_thread; }; diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index ac250e0a5..fb85121b3 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -53,6 +53,12 @@ void Config::ReadValues() { Settings::values.frame_skip = qt_config->value("frame_skip", 0).toInt(); qt_config->endGroup(); + qt_config->beginGroup("Renderer"); + Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); + Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); + Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); + qt_config->endGroup(); + qt_config->beginGroup("Data Storage"); Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); qt_config->endGroup(); @@ -98,6 +104,13 @@ void Config::SaveValues() { qt_config->setValue("frame_skip", Settings::values.frame_skip); qt_config->endGroup(); + qt_config->beginGroup("Renderer"); + // Cast to double because Qt's written float values are not human-readable + qt_config->setValue("bg_red", (double)Settings::values.bg_red); + qt_config->setValue("bg_green", (double)Settings::values.bg_green); + qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); + qt_config->endGroup(); + qt_config->beginGroup("Data Storage"); qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); qt_config->endGroup(); diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp index 9bb22ca2e..94e204717 100644 --- a/src/citra_qt/debugger/callstack.cpp +++ b/src/citra_qt/debugger/callstack.cpp @@ -8,7 +8,7 @@ #include "core/core.h" #include "core/arm/arm_interface.h" -#include "core/mem_map.h" +#include "core/memory.h" #include "common/symbols.h" #include "core/arm/disassembler/arm_disasm.h" @@ -27,7 +27,6 @@ CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent) void CallstackWidget::OnDebugModeEntered() { - ARM_Disasm* disasm = new ARM_Disasm(); ARM_Interface* app_core = Core::g_app_core; u32 sp = app_core->GetReg(13); //stack pointer @@ -46,7 +45,7 @@ void CallstackWidget::OnDebugModeEntered() /* TODO (mattvail) clean me, move to debugger interface */ u32 insn = Memory::Read32(call_addr); - if (disasm->Decode(insn) == OP_BL) + if (ARM_Disasm::Decode(insn) == OP_BL) { std::string name; // ripped from disasm diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp index 54d21dc90..e99ec1b30 100644 --- a/src/citra_qt/debugger/disassembler.cpp +++ b/src/citra_qt/debugger/disassembler.cpp @@ -7,8 +7,7 @@ #include "../bootmanager.h" #include "../hotkeys.h" -#include "common/common.h" -#include "core/mem_map.h" +#include "core/memory.h" #include "core/core.h" #include "common/break_points.h" @@ -18,8 +17,8 @@ #include "core/arm/disassembler/arm_disasm.h" -DisassemblerModel::DisassemblerModel(QObject* parent) : QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) { - +DisassemblerModel::DisassemblerModel(QObject* parent) : + QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) { } int DisassemblerModel::columnCount(const QModelIndex& parent) const { @@ -158,34 +157,28 @@ void DisassemblerModel::SetNextInstruction(unsigned int address) { emit dataChanged(prev_index, prev_index); } -DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread& emu_thread) : QDockWidget(parent), base_addr(0), emu_thread(emu_thread) -{ - disasm_ui.setupUi(this); +DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) : + QDockWidget(parent), emu_thread(emu_thread), base_addr(0) { - model = new DisassemblerModel(this); - disasm_ui.treeView->setModel(model); + disasm_ui.setupUi(this); RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut); - connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint())); connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep())); connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause())); connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue())); - connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - model, SLOT(OnSelectionChanged(const QModelIndex&))); - connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop())); connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep())); connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto())); - connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint())); + + setEnabled(false); } -void DisassemblerWidget::Init() -{ +void DisassemblerWidget::Init() { model->ParseFromAddress(Core::g_app_core->GetPC()); disasm_ui.treeView->resizeColumnToContents(0); @@ -197,25 +190,21 @@ void DisassemblerWidget::Init() disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); } -void DisassemblerWidget::OnContinue() -{ - emu_thread.SetCpuRunning(true); +void DisassemblerWidget::OnContinue() { + emu_thread->SetRunning(true); } -void DisassemblerWidget::OnStep() -{ +void DisassemblerWidget::OnStep() { OnStepInto(); // change later } -void DisassemblerWidget::OnStepInto() -{ - emu_thread.SetCpuRunning(false); - emu_thread.ExecStep(); +void DisassemblerWidget::OnStepInto() { + emu_thread->SetRunning(false); + emu_thread->ExecStep(); } -void DisassemblerWidget::OnPause() -{ - emu_thread.SetCpuRunning(false); +void DisassemblerWidget::OnPause() { + emu_thread->SetRunning(false); // TODO: By now, the CPU might not have actually stopped... if (Core::g_app_core) { @@ -223,20 +212,15 @@ void DisassemblerWidget::OnPause() } } -void DisassemblerWidget::OnToggleStartStop() -{ - emu_thread.SetCpuRunning(!emu_thread.IsCpuRunning()); +void DisassemblerWidget::OnToggleStartStop() { + emu_thread->SetRunning(!emu_thread->IsRunning()); } -void DisassemblerWidget::OnDebugModeEntered() -{ +void DisassemblerWidget::OnDebugModeEntered() { ARMword next_instr = Core::g_app_core->GetPC(); - // TODO: Make BreakPoints less crappy (i.e. const-correct) so that this doesn't need a const_cast. - if (const_cast<BreakPoints&>(model->GetBreakPoints()).IsAddressBreakPoint(next_instr)) - { - emu_thread.SetCpuRunning(false); - } + if (model->GetBreakPoints().IsAddressBreakPoint(next_instr)) + emu_thread->SetRunning(false); model->SetNextInstruction(next_instr); @@ -245,16 +229,35 @@ void DisassemblerWidget::OnDebugModeEntered() disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); } -void DisassemblerWidget::OnDebugModeLeft() -{ - +void DisassemblerWidget::OnDebugModeLeft() { } -int DisassemblerWidget::SelectedRow() -{ +int DisassemblerWidget::SelectedRow() { QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex(); if (!index.isValid()) return -1; return disasm_ui.treeView->selectionModel()->currentIndex().row(); } + +void DisassemblerWidget::OnEmulationStarting(EmuThread* emu_thread) { + this->emu_thread = emu_thread; + + model = new DisassemblerModel(this); + disasm_ui.treeView->setModel(model); + + connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + model, SLOT(OnSelectionChanged(const QModelIndex&))); + connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint())); + connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint())); + + Init(); + setEnabled(true); +} + +void DisassemblerWidget::OnEmulationStopping() { + disasm_ui.treeView->setModel(nullptr); + delete model; + emu_thread = nullptr; + setEnabled(false); +} diff --git a/src/citra_qt/debugger/disassembler.h b/src/citra_qt/debugger/disassembler.h index 5e19d7c51..340fb9936 100644 --- a/src/citra_qt/debugger/disassembler.h +++ b/src/citra_qt/debugger/disassembler.h @@ -9,8 +9,8 @@ #include "ui_disassembler.h" -#include "common/common.h" #include "common/break_points.h" +#include "common/common_types.h" class QAction; class EmuThread; @@ -51,7 +51,7 @@ class DisassemblerWidget : public QDockWidget Q_OBJECT public: - DisassemblerWidget(QWidget* parent, EmuThread& emu_thread); + DisassemblerWidget(QWidget* parent, EmuThread* emu_thread); void Init(); @@ -65,6 +65,9 @@ public slots: void OnDebugModeEntered(); void OnDebugModeLeft(); + void OnEmulationStarting(EmuThread* emu_thread); + void OnEmulationStopping(); + private: // returns -1 if no row is selected int SelectedRow(); @@ -75,5 +78,5 @@ private: u32 base_addr; - EmuThread& emu_thread; + EmuThread* emu_thread; }; diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp index 92348be34..1da64f616 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.cpp +++ b/src/citra_qt/debugger/graphics_breakpoints.cpp @@ -8,6 +8,8 @@ #include <QVBoxLayout> #include <QLabel> +#include "common/assert.h" + #include "graphics_breakpoints.h" #include "graphics_breakpoints_p.h" 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: QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; public slots: void OnBreakPointHit(Pica::DebugContext::Event event); diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index 9bcd25821..66e11dd5b 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp @@ -159,7 +159,7 @@ void TextureInfoDockWidget::OnStrideChanged(int value) { } QPixmap TextureInfoDockWidget::ReloadPixmap() const { - u8* src = Memory::GetPointer(Pica::PAddrToVAddr(info.physical_address)); + u8* src = Memory::GetPhysicalPointer(info.physical_address); return QPixmap::fromImage(LoadTexture(src, info)); } @@ -274,7 +274,7 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { auto format = Pica::registers.GetTextures()[index].format; auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); - u8* src = Memory::GetPointer(Pica::PAddrToVAddr(config.GetPhysicalAddress())); + u8* src = Memory::GetPhysicalPointer(config.GetPhysicalAddress()); new_info_widget = new TextureInfoWidget(src, info); } else { new_info_widget = new QWidget; diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp index d621d7204..0c1a3f47f 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.cpp +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp @@ -10,6 +10,8 @@ #include <QSpinBox> #include "core/hw/gpu.h" +#include "core/memory.h" + #include "video_core/color.h" #include "video_core/pica.h" #include "video_core/utils.h" @@ -215,7 +217,7 @@ void GraphicsFramebufferWidget::OnUpdate() u32 bytes_per_pixel = GraphicsFramebufferWidget::BytesPerPixel(framebuffer_format); QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); - u8* buffer = Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address)); + u8* buffer = Memory::GetPhysicalPointer(framebuffer_address); for (unsigned int y = 0; y < framebuffer_height; ++y) { for (unsigned int x = 0; x < framebuffer_width; ++x) { 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) static const TimingCategoryInfo* GetCategoryInfo(int id) { const auto& categories = GetProfilingManager().GetTimingCategoriesInfo(); - if (id >= categories.size()) { + if ((size_t)id >= categories.size()) { return nullptr; } else { return &categories[id]; @@ -98,7 +98,7 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const const TimingCategoryInfo* info = GetCategoryInfo(index.row() - 2); return info != nullptr ? QString(info->name) : QVariant(); } else { - if (index.row() - 2 < results.time_per_category.size()) { + if (index.row() - 2 < (int)results.time_per_category.size()) { return GetDataForColumn(index.column(), results.time_per_category[index.row() - 2]); } else { 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 public: ProfilerModel(QObject* parent); - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; diff --git a/src/citra_qt/debugger/ramview.cpp b/src/citra_qt/debugger/ramview.cpp index 88570f2cd..b6ebc7fc4 100644 --- a/src/citra_qt/debugger/ramview.cpp +++ b/src/citra_qt/debugger/ramview.cpp @@ -4,8 +4,7 @@ #include "ramview.h" -#include "common/common.h" -#include "core/mem_map.h" + GRamView::GRamView(QWidget* parent) : QHexEdit(parent) { } 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 @@ #include "core/core.h" #include "core/arm/arm_interface.h" -RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) -{ +RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { cpu_regs_ui.setupUi(this); tree = cpu_regs_ui.treeWidget; @@ -18,8 +17,7 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) registers->setExpanded(true); CSPR->setExpanded(true); - for (int i = 0; i < 16; ++i) - { + for (int i = 0; i < 16; ++i) { QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i, 2, 10, QLatin1Char('0')))); registers->addChild(child); } @@ -39,12 +37,16 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) CSPR->addChild(new QTreeWidgetItem(QStringList("C"))); CSPR->addChild(new QTreeWidgetItem(QStringList("Z"))); CSPR->addChild(new QTreeWidgetItem(QStringList("N"))); + + setEnabled(false); } -void RegistersWidget::OnDebugModeEntered() -{ +void RegistersWidget::OnDebugModeEntered() { ARM_Interface* app_core = Core::g_app_core; + if (app_core == nullptr) + return; + for (int i = 0; i < 16; ++i) registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0'))); @@ -66,7 +68,22 @@ void RegistersWidget::OnDebugModeEntered() CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than } -void RegistersWidget::OnDebugModeLeft() -{ +void RegistersWidget::OnDebugModeLeft() { +} + +void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) { + setEnabled(true); +} + +void RegistersWidget::OnEmulationStopping() { + // Reset widget text + for (int i = 0; i < 16; ++i) + registers->child(i)->setText(1, QString("")); + + for (int i = 0; i < 15; ++i) + CSPR->child(i)->setText(1, QString("")); + + CSPR->setText(1, QString("")); + setEnabled(false); } 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 @@ #include <QTreeWidgetItem> class QTreeWidget; +class EmuThread; class RegistersWidget : public QDockWidget { @@ -20,6 +21,9 @@ public slots: void OnDebugModeEntered(); void OnDebugModeLeft(); + void OnEmulationStarting(EmuThread* emu_thread); + void OnEmulationStopping(); + private: Ui::ARMRegisters cpu_regs_ui; diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index e5ca04124..7b028e323 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -10,11 +10,11 @@ #include "qhexedit.h" #include "main.h" -#include "common/common.h" #include "common/logging/text_formatter.h" #include "common/logging/log.h" #include "common/logging/backend.h" #include "common/logging/filter.h" +#include "common/make_unique.h" #include "common/platform.h" #include "common/scope_exit.h" @@ -46,7 +46,7 @@ #include "version.h" -GMainWindow::GMainWindow() +GMainWindow::GMainWindow() : emu_thread(nullptr) { Pica::g_debug_context = Pica::DebugContext::Construct(); @@ -55,14 +55,14 @@ GMainWindow::GMainWindow() ui.setupUi(this); statusBar()->hide(); - render_window = new GRenderWindow; + render_window = new GRenderWindow(this, emu_thread.get()); render_window->hide(); profilerWidget = new ProfilerWidget(this); addDockWidget(Qt::BottomDockWidgetArea, profilerWidget); profilerWidget->hide(); - disasmWidget = new DisassemblerWidget(this, render_window->GetEmuThread()); + disasmWidget = new DisassemblerWidget(this, emu_thread.get()); addDockWidget(Qt::BottomDockWidgetArea, disasmWidget); disasmWidget->hide(); @@ -138,14 +138,12 @@ GMainWindow::GMainWindow() connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog())); - // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues - connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); - connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); - connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); - - connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); - connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); - connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); + connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*))); + connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping())); + connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget, SLOT(OnEmulationStarting(EmuThread*))); + connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); + connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*))); + connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); // Setup hotkeys RegisterHotkey("Main Window", "Load File", QKeySequence::Open); @@ -196,32 +194,76 @@ void GMainWindow::OnDisplayTitleBars(bool show) } } -void GMainWindow::BootGame(std::string filename) -{ +void GMainWindow::BootGame(std::string filename) { LOG_INFO(Frontend, "Citra starting...\n"); + + // Initialize the core emulation System::Init(render_window); - // Load a game or die... + // Load the game if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { LOG_CRITICAL(Frontend, "Failed to load ROM!"); + System::Shutdown(); + return; } - disasmWidget->Init(); + // Create and start the emulation thread + emu_thread = Common::make_unique<EmuThread>(render_window); + emit EmulationStarting(emu_thread.get()); + emu_thread->start(); + + // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues + connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); + connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); + connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); + connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); + connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); + connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); + + // Update the GUI registersWidget->OnDebugModeEntered(); callstackWidget->OnDebugModeEntered(); - - render_window->GetEmuThread().SetFilename(filename); - render_window->GetEmuThread().start(); - render_window->show(); + OnStartGame(); } +void GMainWindow::ShutdownGame() { + emu_thread->RequestStop(); + + // Release emu threads from any breakpoints + // This belongs after RequestStop() and before wait() because if emulation stops on a GPU + // breakpoint after (or before) RequestStop() is called, the emulation would never be able + // to continue out to the main loop and terminate. Thus wait() would hang forever. + // TODO(bunnei): This function is not thread safe, but it's being used as if it were + Pica::g_debug_context->ClearBreakpoints(); + + emit EmulationStopping(); + + // Wait for emulation thread to complete and delete it + emu_thread->wait(); + emu_thread = nullptr; + + // Shutdown the core emulation + System::Shutdown(); + + // Update the GUI + ui.action_Start->setEnabled(false); + ui.action_Pause->setEnabled(false); + ui.action_Stop->setEnabled(false); + render_window->hide(); +} + void GMainWindow::OnMenuLoadFile() { - QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)")); - if (filename.size()) - BootGame(filename.toLatin1().data()); + QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)")); + if (filename.size()) { + // Shutdown previous session if the emu thread is still active... + if (emu_thread != nullptr) + ShutdownGame(); + + BootGame(filename.toLatin1().data()); + } } void GMainWindow::OnMenuLoadSymbolMap() { @@ -232,7 +274,7 @@ void GMainWindow::OnMenuLoadSymbolMap() { void GMainWindow::OnStartGame() { - render_window->GetEmuThread().SetCpuRunning(true); + emu_thread->SetRunning(true); ui.action_Start->setEnabled(false); ui.action_Pause->setEnabled(true); @@ -241,21 +283,15 @@ void GMainWindow::OnStartGame() void GMainWindow::OnPauseGame() { - render_window->GetEmuThread().SetCpuRunning(false); + emu_thread->SetRunning(false); ui.action_Start->setEnabled(true); ui.action_Pause->setEnabled(false); ui.action_Stop->setEnabled(true); } -void GMainWindow::OnStopGame() -{ - render_window->GetEmuThread().SetCpuRunning(false); - // TODO: Shutdown core - - ui.action_Start->setEnabled(true); - ui.action_Pause->setEnabled(false); - ui.action_Stop->setEnabled(false); +void GMainWindow::OnStopGame() { + ShutdownGame(); } void GMainWindow::OnOpenHotkeysDialog() @@ -265,24 +301,22 @@ void GMainWindow::OnOpenHotkeysDialog() } -void GMainWindow::ToggleWindowMode() -{ - bool enable = ui.action_Single_Window_Mode->isChecked(); - if (!enable && render_window->parent() != nullptr) - { - ui.horizontalLayout->removeWidget(render_window); - render_window->setParent(nullptr); - render_window->setVisible(true); - render_window->RestoreGeometry(); - render_window->setFocusPolicy(Qt::NoFocus); - } - else if (enable && render_window->parent() == nullptr) - { +void GMainWindow::ToggleWindowMode() { + if (ui.action_Single_Window_Mode->isChecked()) { + // Render in the main window... render_window->BackupGeometry(); ui.horizontalLayout->addWidget(render_window); render_window->setVisible(true); render_window->setFocusPolicy(Qt::ClickFocus); render_window->setFocus(); + + } else { + // Render in a separate window... + ui.horizontalLayout->removeWidget(render_window); + render_window->setParent(nullptr); + render_window->setVisible(true); + render_window->RestoreGeometry(); + render_window->setFocusPolicy(Qt::NoFocus); } } @@ -303,6 +337,10 @@ void GMainWindow::closeEvent(QCloseEvent* event) settings.setValue("firstStart", false); SaveHotkeys(settings); + // Shutdown session if the emu thread is active... + if (emu_thread != nullptr) + ShutdownGame(); + render_window->close(); QWidget::closeEvent(event); @@ -312,7 +350,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) #undef main #endif -int __cdecl main(int argc, char* argv[]) +int main(int argc, char* argv[]) { std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger(); Log::Filter log_filter(Log::Level::Info); 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 @@ #ifndef _CITRA_QT_MAIN_HXX_ #define _CITRA_QT_MAIN_HXX_ +#include <memory> #include <QMainWindow> #include "ui_main.h" class GImageInfo; class GRenderWindow; +class EmuThread; class ProfilerWidget; class DisassemblerWidget; class RegistersWidget; @@ -34,8 +36,27 @@ public: GMainWindow(); ~GMainWindow(); +signals: + + /** + * Signal that is emitted when a new EmuThread has been created and an emulation session is + * about to start. At this time, the core system emulation has been initialized, and all + * emulation handles and memory should be valid. + * + * @param emu_thread Pointer to the newly created EmuThread (to be used by widgets that need to + * access/change emulation state). + */ + void EmulationStarting(EmuThread* emu_thread); + + /** + * Signal that is emitted when emulation is about to stop. At this time, the EmuThread and core + * system emulation handles and memory are still valid, but are about become invalid. + */ + void EmulationStopping(); + private: void BootGame(std::string filename); + void ShutdownGame(); void closeEvent(QCloseEvent* event) override; @@ -55,6 +76,8 @@ private: GRenderWindow* render_window; + std::unique_ptr<EmuThread> emu_thread; + ProfilerWidget* profilerWidget; DisassemblerWidget* disasmWidget; 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 @@ </property> </action> <action name="action_Start"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="text"> <string>&Start</string> </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 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include <cstdlib> #include <QLineEdit> #include <QRegExpValidator> @@ -206,7 +207,7 @@ QString CSpinBox::TextFromValue() { return prefix + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") - + QString("%1").arg(abs(value), num_digits, base, QLatin1Char('0')).toUpper() + + QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper() + suffix; } diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index daa2d59de..f8fc6450f 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -5,13 +5,10 @@ set(SRCS break_points.cpp emu_window.cpp file_util.cpp - hash.cpp key_map.cpp logging/filter.cpp logging/text_formatter.cpp logging/backend.cpp - math_util.cpp - mem_arena.cpp memory_util.cpp misc.cpp profiler.cpp @@ -27,7 +24,6 @@ set(HEADERS bit_field.h break_points.h chunk_file.h - common.h common_funcs.h common_paths.h common_types.h @@ -37,7 +33,6 @@ set(HEADERS emu_window.h fifo_queue.h file_util.h - hash.h key_map.h linear_disk_cache.h logging/text_formatter.h @@ -46,7 +41,6 @@ set(HEADERS logging/backend.h make_unique.h math_util.h - mem_arena.h memory_util.h platform.h profiler.h diff --git a/src/common/assert.h b/src/common/assert.h index 9ca7adb15..4f26c63e9 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -4,6 +4,7 @@ #pragma once +#include <cstdio> #include <cstdlib> #include "common/common_funcs.h" diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 8eab054b8..1f3ecf844 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -35,7 +35,7 @@ #include <limits> #include <type_traits> -#include "common/common.h" +#include "common/common_funcs.h" /* * Abstract bitfield class diff --git a/src/common/break_points.cpp b/src/common/break_points.cpp index 2655d3ce9..023a485a4 100644 --- a/src/common/break_points.cpp +++ b/src/common/break_points.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common.h" #include "common/debug_interface.h" #include "common/break_points.h" #include "common/logging/log.h" @@ -10,14 +9,14 @@ #include <sstream> #include <algorithm> -bool BreakPoints::IsAddressBreakPoint(u32 iAddress) +bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const { auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; }; auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); return it != m_BreakPoints.end(); } -bool BreakPoints::IsTempBreakPoint(u32 iAddress) +bool BreakPoints::IsTempBreakPoint(u32 iAddress) const { auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress && bp.bTemporary; }; auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); diff --git a/src/common/break_points.h b/src/common/break_points.h index 5557cd50e..f0a55e7b1 100644 --- a/src/common/break_points.h +++ b/src/common/break_points.h @@ -7,7 +7,7 @@ #include <vector> #include <string> -#include "common/common.h" +#include "common/common_types.h" class DebugInterface; @@ -56,8 +56,8 @@ public: void AddFromStrings(const TBreakPointsStr& bps); // is address breakpoint - bool IsAddressBreakPoint(u32 iAddress); - bool IsTempBreakPoint(u32 iAddress); + bool IsAddressBreakPoint(u32 iAddress) const; + bool IsTempBreakPoint(u32 iAddress) const; // Add BreakPoint void Add(u32 em_address, bool temp=false); diff --git a/src/common/chunk_file.h b/src/common/chunk_file.h index 3f97d56bf..dcd80525e 100644 --- a/src/common/chunk_file.h +++ b/src/common/chunk_file.h @@ -34,8 +34,9 @@ #include <set> #include <type_traits> -#include "common/common.h" +#include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" template <class T> struct LinkedListItem : public T diff --git a/src/common/common.h b/src/common/common.h deleted file mode 100644 index f7d0f55c5..000000000 --- a/src/common/common.h +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -// DO NOT EVER INCLUDE <windows.h> directly _or indirectly_ from this file -// since it slows down the build a lot. - -#include <cstdlib> -#include <cstdio> -#include <cstring> - -#define STACKALIGN - -// An inheritable class to disallow the copy constructor and operator= functions -class NonCopyable -{ -protected: - NonCopyable() {} - NonCopyable(const NonCopyable&&) {} - void operator=(const NonCopyable&&) {} -private: - NonCopyable(NonCopyable&); - NonCopyable& operator=(NonCopyable& other); -}; - -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/common_types.h" -#include "common/common_funcs.h" -#include "common/common_paths.h" -#include "common/platform.h" - -#ifdef __APPLE__ -// The Darwin ABI requires that stack frames be aligned to 16-byte boundaries. -// This is only needed on i386 gcc - x86_64 already aligns to 16 bytes. - #if defined __i386__ && defined __GNUC__ - #undef STACKALIGN - #define STACKALIGN __attribute__((__force_align_arg_pointer__)) - #endif -#elif defined _WIN32 -// Check MSC ver - #if defined _MSC_VER && _MSC_VER <= 1000 - #error needs at least version 1000 of MSC - #endif - - #ifndef NOMINMAX - #define NOMINMAX - #endif - -// Alignment - #define MEMORY_ALIGNED16(x) __declspec(align(16)) x - #define MEMORY_ALIGNED32(x) __declspec(align(32)) x - #define MEMORY_ALIGNED64(x) __declspec(align(64)) x - #define MEMORY_ALIGNED128(x) __declspec(align(128)) x - #define MEMORY_ALIGNED16_DECL(x) __declspec(align(16)) x - #define MEMORY_ALIGNED64_DECL(x) __declspec(align(64)) x -#endif - -// Windows compatibility -#ifndef _WIN32 - #ifdef _LP64 - #define _M_X64 1 - #else - #define _M_IX86 1 - #endif - #define __forceinline inline __attribute__((always_inline)) - #define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x - #define MEMORY_ALIGNED32(x) __attribute__((aligned(32))) x - #define MEMORY_ALIGNED64(x) __attribute__((aligned(64))) x - #define MEMORY_ALIGNED128(x) __attribute__((aligned(128))) x - #define MEMORY_ALIGNED16_DECL(x) __attribute__((aligned(16))) x - #define MEMORY_ALIGNED64_DECL(x) __attribute__((aligned(64))) x -#endif - -#ifdef _MSC_VER - #define __strdup _strdup - #define __getcwd _getcwd - #define __chdir _chdir -#else - #define __strdup strdup - #define __getcwd getcwd - #define __chdir chdir -#endif - -#if defined _M_GENERIC -# define _M_SSE 0x0 -#elif defined __GNUC__ -# if defined __SSE4_2__ -# define _M_SSE 0x402 -# elif defined __SSE4_1__ -# define _M_SSE 0x401 -# elif defined __SSSE3__ -# define _M_SSE 0x301 -# elif defined __SSE3__ -# define _M_SSE 0x300 -# endif -#elif (_MSC_VER >= 1500) || __INTEL_COMPILER // Visual Studio 2008 -# define _M_SSE 0x402 -#endif - -// Host communication. -enum HOST_COMM -{ - // Begin at 10 in case there is already messages with wParam = 0, 1, 2 and so on - WM_USER_STOP = 10, - WM_USER_CREATE, - WM_USER_SETCURSOR, -}; - -// Used for notification on emulation state -enum EMUSTATE_CHANGE -{ - EMUSTATE_CHANGE_PLAY = 1, - EMUSTATE_CHANGE_PAUSE, - EMUSTATE_CHANGE_STOP -}; - -#include "swap.h" diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index e76cb7d68..91b74c6bc 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -7,13 +7,6 @@ #include "common_types.h" #include <cstdlib> -#ifdef _WIN32 -#define SLEEP(x) Sleep(x) -#else -#include <unistd.h> -#define SLEEP(x) usleep(x*1000) -#endif - #define b2(x) ( (x) | ( (x) >> 1) ) #define b4(x) ( b2(x) | ( b2(x) >> 2) ) @@ -34,6 +27,20 @@ #define INSERT_PADDING_BYTES(num_bytes) u8 CONCAT2(pad, __LINE__)[(num_bytes)] #define INSERT_PADDING_WORDS(num_words) u32 CONCAT2(pad, __LINE__)[(num_words)] +#ifdef _WIN32 + // Alignment + #define MEMORY_ALIGNED16(x) __declspec(align(16)) x + #define MEMORY_ALIGNED32(x) __declspec(align(32)) x + #define MEMORY_ALIGNED64(x) __declspec(align(64)) x + #define MEMORY_ALIGNED128(x) __declspec(align(128)) x +#else + #define __forceinline inline __attribute__((always_inline)) + #define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x + #define MEMORY_ALIGNED32(x) __attribute__((aligned(32))) x + #define MEMORY_ALIGNED64(x) __attribute__((aligned(64))) x + #define MEMORY_ALIGNED128(x) __attribute__((aligned(128))) x +#endif + #ifndef _MSC_VER #include <errno.h> @@ -73,61 +80,11 @@ inline u64 _rotr64(u64 x, unsigned int shift){ } #else // _MSC_VER -#include <locale.h> - -// Function Cross-Compatibility - #define strcasecmp _stricmp - #define strncasecmp _strnicmp - #define unlink _unlink + // Function Cross-Compatibility #define snprintf _snprintf - #define vscprintf _vscprintf -// Locale Cross-Compatibility + // Locale Cross-Compatibility #define locale_t _locale_t - #define freelocale _free_locale - #define newlocale(mask, locale, base) _create_locale(mask, locale) - - #define LC_GLOBAL_LOCALE ((locale_t)-1) - #define LC_ALL_MASK LC_ALL - #define LC_COLLATE_MASK LC_COLLATE - #define LC_CTYPE_MASK LC_CTYPE - #define LC_MONETARY_MASK LC_MONETARY - #define LC_NUMERIC_MASK LC_NUMERIC - #define LC_TIME_MASK LC_TIME - - inline locale_t uselocale(locale_t new_locale) - { - // Retrieve the current per thread locale setting - bool bIsPerThread = (_configthreadlocale(0) == _ENABLE_PER_THREAD_LOCALE); - - // Retrieve the current thread-specific locale - locale_t old_locale = bIsPerThread ? _get_current_locale() : LC_GLOBAL_LOCALE; - - if(new_locale == LC_GLOBAL_LOCALE) - { - // Restore the global locale - _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); - } - else if(new_locale != nullptr) - { - // Configure the thread to set the locale only for this thread - _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); - - // Set all locale categories - for(int i = LC_MIN; i <= LC_MAX; i++) - setlocale(i, new_locale->locinfo->lc_category[i].locale); - } - - return old_locale; - } - -// 64 bit offsets for windows - #define fseeko _fseeki64 - #define ftello _ftelli64 - #define atoll _atoi64 - #define stat64 _stat64 - #define fstat64 _fstat64 - #define fileno _fileno extern "C" { __declspec(dllimport) void __stdcall DebugBreak(void); diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 440b06060..2903f2cf2 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -4,9 +4,6 @@ #pragma once -// Make sure we pick up USER_DIR if set in config.h -#include "common/common.h" - // Directory separators, do we need this? #define DIR_SEP "/" #define DIR_SEP_CHR '/' diff --git a/src/common/common_types.h b/src/common/common_types.h index 1b453e7f5..f6de0adfc 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h @@ -47,6 +47,11 @@ typedef std::int64_t s64; ///< 64-bit signed int typedef float f32; ///< 32-bit floating point typedef double f64; ///< 64-bit floating point +// TODO: It would be nice to eventually replace these with strong types that prevent accidental +// conversion between each other. +typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space. +typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space. + /// Union for fast 16-bit type casting union t16 { u8 _u8[2]; ///< 8-bit unsigned char(s) @@ -73,28 +78,12 @@ union t64 { u8 _u8[8]; ///< 8-bit unsigned char(s) }; -namespace Common { -/// Rectangle data structure -class Rect { -public: - Rect(int x0=0, int y0=0, int x1=0, int y1=0) { - x0_ = x0; - y0_ = y0; - x1_ = x1; - y1_ = y1; - } - ~Rect() { } - - int x0_; ///< Rect top left X-coordinate - int y0_; ///< Rect top left Y-coordinate - int x1_; ///< Rect bottom left X-coordinate - int y1_; ///< Rect bottom right Y-coordinate - - inline u32 width() const { return std::abs(x1_ - x0_); } - inline u32 height() const { return std::abs(y1_ - y0_); } +// An inheritable class to disallow the copy constructor and operator= functions +class NonCopyable { +protected: + NonCopyable() = default; + ~NonCopyable() = default; - inline bool operator == (const Rect& val) const { - return (x0_ == val.x0_ && y0_ == val.y0_ && x1_ == val.x1_ && y1_ == val.y1_); - } + NonCopyable(NonCopyable&) = delete; + NonCopyable& operator=(NonCopyable&) = delete; }; -} diff --git a/src/common/concurrent_ring_buffer.h b/src/common/concurrent_ring_buffer.h index fc18e6c86..c5889513a 100644 --- a/src/common/concurrent_ring_buffer.h +++ b/src/common/concurrent_ring_buffer.h @@ -10,7 +10,7 @@ #include <mutex> #include <thread> -#include "common/common.h" // for NonCopyable +#include "common/common_types.h" // for NonCopyable namespace Common { 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 framebuffer_x < layout.bottom_screen.right); } +std::tuple<unsigned,unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) { + + new_x = std::max(new_x, framebuffer_layout.bottom_screen.left); + new_x = std::min(new_x, framebuffer_layout.bottom_screen.right-1); + + new_y = std::max(new_y, framebuffer_layout.bottom_screen.top); + new_y = std::min(new_y, framebuffer_layout.bottom_screen.bottom-1); + + return std::make_tuple(new_x, new_y); +} + void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) return; @@ -52,14 +63,13 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { if (!touch_pressed) return; - if (IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) - TouchPressed(framebuffer_x, framebuffer_y); - else - TouchReleased(); + if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) + std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); + + TouchPressed(framebuffer_x, framebuffer_y); } -EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, - unsigned height) { +EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) { ASSERT(width > 0); ASSERT(height > 0); diff --git a/src/common/emu_window.h b/src/common/emu_window.h index c8e2de04a..8eca6b5d5 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -4,11 +4,11 @@ #pragma once -#include "common/common.h" -#include "common/scm_rev.h" -#include "common/string_util.h" +#include "common/common_types.h" #include "common/key_map.h" #include "common/math_util.h" +#include "common/scm_rev.h" +#include "common/string_util.h" /** * Abstraction class used to provide an interface between emulation code and the frontend @@ -206,5 +206,10 @@ private: u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) + /** + * Clip the provided coordinates to be inside the touchscreen area. + */ + std::tuple<unsigned,unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y); + Service::HID::PadState pad_state; }; diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 4ef4918d7..7cdd1484f 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -2,42 +2,52 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - -#include "common/common.h" +#include "common/assert.h" +#include "common/common_funcs.h" +#include "common/common_paths.h" #include "common/file_util.h" +#include "common/logging/log.h" #ifdef _WIN32 -#include <windows.h> -#include <shlobj.h> // for SHGetFolderPath -#include <shellapi.h> -#include <commdlg.h> // for GetSaveFileName -#include <io.h> -#include <direct.h> // getcwd -#include <tchar.h> + #include <windows.h> + #include <shlobj.h> // for SHGetFolderPath + #include <shellapi.h> + #include <commdlg.h> // for GetSaveFileName + #include <io.h> + #include <direct.h> // getcwd + #include <tchar.h> + + // 64 bit offsets for windows + #define fseeko _fseeki64 + #define ftello _ftelli64 + #define atoll _atoi64 + #define stat64 _stat64 + #define fstat64 _fstat64 + #define fileno _fileno #else -#include <sys/param.h> -#include <sys/types.h> -#include <dirent.h> -#include <pwd.h> -#include <unistd.h> + #include <sys/param.h> + #include <sys/types.h> + #include <dirent.h> + #include <pwd.h> + #include <unistd.h> #endif #if defined(__APPLE__) -#include <CoreFoundation/CFString.h> -#include <CoreFoundation/CFURL.h> -#include <CoreFoundation/CFBundle.h> + #include <CoreFoundation/CFString.h> + #include <CoreFoundation/CFURL.h> + #include <CoreFoundation/CFBundle.h> #endif #include <algorithm> #include <sys/stat.h> #ifndef S_ISDIR -#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) + #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) #endif #ifdef BSD4_4 -#define stat64 stat -#define fstat64 fstat + #define stat64 stat + #define fstat64 fstat #endif // This namespace has various generic functions related to files and paths. @@ -589,7 +599,7 @@ std::string GetCurrentDir() { char *dir; // Get the current working directory (getcwd uses malloc) - if (!(dir = __getcwd(nullptr, 0))) { + if (!(dir = getcwd(nullptr, 0))) { LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s", GetLastErrorMsg()); @@ -603,7 +613,7 @@ std::string GetCurrentDir() // Sets the current directory to the given directory bool SetCurrentDir(const std::string &directory) { - return __chdir(directory.c_str()) == 0; + return chdir(directory.c_str()) == 0; } #if defined(__APPLE__) diff --git a/src/common/file_util.h b/src/common/file_util.h index 86aab2e3d..b65829291 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -11,7 +11,7 @@ #include <string> #include <vector> -#include "common/common.h" +#include "common/common_types.h" #include "common/string_util.h" // User directory indices for GetUserPath diff --git a/src/common/hash.cpp b/src/common/hash.cpp deleted file mode 100644 index 0624dab8d..000000000 --- a/src/common/hash.cpp +++ /dev/null @@ -1,521 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> - -#include "common/hash.h" -#if _M_SSE >= 0x402 -#include "common/cpu_detect.h" -#include <nmmintrin.h> -#endif - -static u64 (*ptrHashFunction)(const u8 *src, int len, u32 samples) = &GetMurmurHash3; - -// uint32_t -// WARNING - may read one more byte! -// Implementation from Wikipedia. -u32 HashFletcher(const u8* data_u8, size_t length) -{ - const u16* data = (const u16*)data_u8; /* Pointer to the data to be summed */ - size_t len = (length + 1) / 2; /* Length in 16-bit words */ - u32 sum1 = 0xffff, sum2 = 0xffff; - - while (len) - { - size_t tlen = len > 360 ? 360 : len; - len -= tlen; - - do { - sum1 += *data++; - sum2 += sum1; - } - while (--tlen); - - sum1 = (sum1 & 0xffff) + (sum1 >> 16); - sum2 = (sum2 & 0xffff) + (sum2 >> 16); - } - - // Second reduction step to reduce sums to 16 bits - sum1 = (sum1 & 0xffff) + (sum1 >> 16); - sum2 = (sum2 & 0xffff) + (sum2 >> 16); - return(sum2 << 16 | sum1); -} - - -// Implementation from Wikipedia -// Slightly slower than Fletcher above, but slightly more reliable. -#define MOD_ADLER 65521 -// data: Pointer to the data to be summed; len is in bytes -u32 HashAdler32(const u8* data, size_t len) -{ - u32 a = 1, b = 0; - - while (len) - { - size_t tlen = len > 5550 ? 5550 : len; - len -= tlen; - - do - { - a += *data++; - b += a; - } - while (--tlen); - - a = (a & 0xffff) + (a >> 16) * (65536 - MOD_ADLER); - b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); - } - - // It can be shown that a <= 0x1013a here, so a single subtract will do. - if (a >= MOD_ADLER) - { - a -= MOD_ADLER; - } - - // It can be shown that b can reach 0xfff87 here. - b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); - - if (b >= MOD_ADLER) - { - b -= MOD_ADLER; - } - - return((b << 16) | a); -} - -// Stupid hash - but can't go back now :) -// Don't use for new things. At least it's reasonably fast. -u32 HashEctor(const u8* ptr, int length) -{ - u32 crc = 0; - - for (int i = 0; i < length; i++) - { - crc ^= ptr[i]; - crc = (crc << 3) | (crc >> 29); - } - - return(crc); -} - - -#ifdef _M_X64 - -//----------------------------------------------------------------------------- -// Block read - if your platform needs to do endian-swapping or can only -// handle aligned reads, do the conversion here - -inline u64 getblock(const u64 * p, int i) -{ - return p[i]; -} - -//---------- -// Block mix - combine the key bits with the hash bits and scramble everything - -inline void bmix64(u64 & h1, u64 & h2, u64 & k1, u64 & k2, u64 & c1, u64 & c2) -{ - k1 *= c1; - k1 = _rotl64(k1,23); - k1 *= c2; - h1 ^= k1; - h1 += h2; - - h2 = _rotl64(h2,41); - - k2 *= c2; - k2 = _rotl64(k2,23); - k2 *= c1; - h2 ^= k2; - h2 += h1; - - h1 = h1*3+0x52dce729; - h2 = h2*3+0x38495ab5; - - c1 = c1*5+0x7b7d159c; - c2 = c2*5+0x6bce6396; -} - -//---------- -// Finalization mix - avalanches all bits to within 0.05% bias - -inline u64 fmix64(u64 k) -{ - k ^= k >> 33; - k *= 0xff51afd7ed558ccd; - k ^= k >> 33; - k *= 0xc4ceb9fe1a85ec53; - k ^= k >> 33; - - return k; -} - -u64 GetMurmurHash3(const u8 *src, int len, u32 samples) -{ - const u8 * data = (const u8*)src; - const int nblocks = len / 16; - u32 Step = (len / 8); - if(samples == 0) samples = std::max(Step, 1u); - Step = Step / samples; - if(Step < 1) Step = 1; - - u64 h1 = 0x9368e53c2f6af274; - u64 h2 = 0x586dcd208f7cd3fd; - - u64 c1 = 0x87c37b91114253d5; - u64 c2 = 0x4cf5ad432745937f; - - - //---------- - // body - - const u64 * blocks = (const u64 *)(data); - - for(int i = 0; i < nblocks; i+=Step) - { - u64 k1 = getblock(blocks,i*2+0); - u64 k2 = getblock(blocks,i*2+1); - - bmix64(h1,h2,k1,k2,c1,c2); - } - - //---------- - // tail - - const u8 * tail = (const u8*)(data + nblocks*16); - - u64 k1 = 0; - u64 k2 = 0; - - switch(len & 15) - { - case 15: k2 ^= u64(tail[14]) << 48; - case 14: k2 ^= u64(tail[13]) << 40; - case 13: k2 ^= u64(tail[12]) << 32; - case 12: k2 ^= u64(tail[11]) << 24; - case 11: k2 ^= u64(tail[10]) << 16; - case 10: k2 ^= u64(tail[ 9]) << 8; - case 9: k2 ^= u64(tail[ 8]) << 0; - - case 8: k1 ^= u64(tail[ 7]) << 56; - case 7: k1 ^= u64(tail[ 6]) << 48; - case 6: k1 ^= u64(tail[ 5]) << 40; - case 5: k1 ^= u64(tail[ 4]) << 32; - case 4: k1 ^= u64(tail[ 3]) << 24; - case 3: k1 ^= u64(tail[ 2]) << 16; - case 2: k1 ^= u64(tail[ 1]) << 8; - case 1: k1 ^= u64(tail[ 0]) << 0; - bmix64(h1,h2,k1,k2,c1,c2); - }; - - //---------- - // finalization - - h2 ^= len; - - h1 += h2; - h2 += h1; - - h1 = fmix64(h1); - h2 = fmix64(h2); - - h1 += h2; - - return h1; -} - - -// CRC32 hash using the SSE4.2 instruction -u64 GetCRC32(const u8 *src, int len, u32 samples) -{ -#if _M_SSE >= 0x402 - u64 h = len; - u32 Step = (len / 8); - const u64 *data = (const u64 *)src; - const u64 *end = data + Step; - if(samples == 0) samples = std::max(Step, 1u); - Step = Step / samples; - if(Step < 1) Step = 1; - while(data < end) - { - h = _mm_crc32_u64(h, data[0]); - data += Step; - } - - const u8 *data2 = (const u8*)end; - return _mm_crc32_u64(h, u64(data2[0])); -#else - return 0; -#endif -} - - -/* - * NOTE: This hash function is used for custom texture loading/dumping, so - * it should not be changed, which would require all custom textures to be - * recalculated for their new hash values. If the hashing function is - * changed, make sure this one is still used when the legacy parameter is - * true. - */ -u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) -{ - const u64 m = 0xc6a4a7935bd1e995; - u64 h = len * m; - const int r = 47; - u32 Step = (len / 8); - const u64 *data = (const u64 *)src; - const u64 *end = data + Step; - if(samples == 0) samples = std::max(Step, 1u); - Step = Step / samples; - if(Step < 1) Step = 1; - while(data < end) - { - u64 k = data[0]; - data+=Step; - k *= m; - k ^= k >> r; - k *= m; - h ^= k; - h *= m; - } - - const u8 * data2 = (const u8*)end; - - switch(len & 7) - { - case 7: h ^= u64(data2[6]) << 48; - case 6: h ^= u64(data2[5]) << 40; - case 5: h ^= u64(data2[4]) << 32; - case 4: h ^= u64(data2[3]) << 24; - case 3: h ^= u64(data2[2]) << 16; - case 2: h ^= u64(data2[1]) << 8; - case 1: h ^= u64(data2[0]); - h *= m; - }; - - h ^= h >> r; - h *= m; - h ^= h >> r; - - return h; -} -#else -// CRC32 hash using the SSE4.2 instruction -u64 GetCRC32(const u8 *src, int len, u32 samples) -{ -#if _M_SSE >= 0x402 - u32 h = len; - u32 Step = (len/4); - const u32 *data = (const u32 *)src; - const u32 *end = data + Step; - if(samples == 0) samples = std::max(Step, 1u); - Step = Step / samples; - if(Step < 1) Step = 1; - while(data < end) - { - h = _mm_crc32_u32(h, data[0]); - data += Step; - } - - const u8 *data2 = (const u8*)end; - return (u64)_mm_crc32_u32(h, u32(data2[0])); -#else - return 0; -#endif -} - -//----------------------------------------------------------------------------- -// Block read - if your platform needs to do endian-swapping or can only -// handle aligned reads, do the conversion here - -inline u32 getblock(const u32 * p, int i) -{ - return p[i]; -} - -//---------- -// Finalization mix - force all bits of a hash block to avalanche - -// avalanches all bits to within 0.25% bias - -inline u32 fmix32(u32 h) -{ - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - return h; -} - -inline void bmix32(u32 & h1, u32 & h2, u32 & k1, u32 & k2, u32 & c1, u32 & c2) -{ - k1 *= c1; - k1 = _rotl(k1,11); - k1 *= c2; - h1 ^= k1; - h1 += h2; - - h2 = _rotl(h2,17); - - k2 *= c2; - k2 = _rotl(k2,11); - k2 *= c1; - h2 ^= k2; - h2 += h1; - - h1 = h1*3+0x52dce729; - h2 = h2*3+0x38495ab5; - - c1 = c1*5+0x7b7d159c; - c2 = c2*5+0x6bce6396; -} - -//---------- - -u64 GetMurmurHash3(const u8* src, int len, u32 samples) -{ - const u8 * data = (const u8*)src; - u32 out[2]; - const int nblocks = len / 8; - u32 Step = (len / 4); - if(samples == 0) samples = std::max(Step, 1u); - Step = Step / samples; - if(Step < 1) Step = 1; - - u32 h1 = 0x8de1c3ac; - u32 h2 = 0xbab98226; - - u32 c1 = 0x95543787; - u32 c2 = 0x2ad7eb25; - - //---------- - // body - - const u32 * blocks = (const u32 *)(data + nblocks*8); - - for(int i = -nblocks; i < 0; i+=Step) - { - u32 k1 = getblock(blocks,i*2+0); - u32 k2 = getblock(blocks,i*2+1); - - bmix32(h1,h2,k1,k2,c1,c2); - } - - //---------- - // tail - - const u8 * tail = (const u8*)(data + nblocks*8); - - u32 k1 = 0; - u32 k2 = 0; - - switch(len & 7) - { - case 7: k2 ^= tail[6] << 16; - case 6: k2 ^= tail[5] << 8; - case 5: k2 ^= tail[4] << 0; - case 4: k1 ^= tail[3] << 24; - case 3: k1 ^= tail[2] << 16; - case 2: k1 ^= tail[1] << 8; - case 1: k1 ^= tail[0] << 0; - bmix32(h1,h2,k1,k2,c1,c2); - }; - - //---------- - // finalization - - h2 ^= len; - - h1 += h2; - h2 += h1; - - h1 = fmix32(h1); - h2 = fmix32(h2); - - h1 += h2; - h2 += h1; - - out[0] = h1; - out[1] = h2; - - return *((u64 *)&out); -} - -/* - * FIXME: The old 32-bit version of this hash made different hashes than the - * 64-bit version. Until someone can make a new version of the 32-bit one that - * makes identical hashes, this is just a c/p of the 64-bit one. - */ -u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) -{ - const u64 m = 0xc6a4a7935bd1e995ULL; - u64 h = len * m; - const int r = 47; - u32 Step = (len / 8); - const u64 *data = (const u64 *)src; - const u64 *end = data + Step; - if(samples == 0) samples = std::max(Step, 1u); - Step = Step / samples; - if(Step < 1) Step = 1; - while(data < end) - { - u64 k = data[0]; - data+=Step; - k *= m; - k ^= k >> r; - k *= m; - h ^= k; - h *= m; - } - - const u8 * data2 = (const u8*)end; - - switch(len & 7) - { - case 7: h ^= u64(data2[6]) << 48; - case 6: h ^= u64(data2[5]) << 40; - case 5: h ^= u64(data2[4]) << 32; - case 4: h ^= u64(data2[3]) << 24; - case 3: h ^= u64(data2[2]) << 16; - case 2: h ^= u64(data2[1]) << 8; - case 1: h ^= u64(data2[0]); - h *= m; - }; - - h ^= h >> r; - h *= m; - h ^= h >> r; - - return h; -} -#endif - -u64 GetHash64(const u8 *src, int len, u32 samples) -{ - return ptrHashFunction(src, len, samples); -} - -// sets the hash function used for the texture cache -void SetHash64Function(bool useHiresTextures) -{ - if (useHiresTextures) - { - ptrHashFunction = &GetHashHiresTexture; - } -#if _M_SSE >= 0x402 - else if (cpu_info.bSSE4_2 && !useHiresTextures) // sse crc32 version - { - ptrHashFunction = &GetCRC32; - } -#endif - else - { - ptrHashFunction = &GetMurmurHash3; - } -} - - - diff --git a/src/common/hash.h b/src/common/hash.h deleted file mode 100644 index 3ac42bc44..000000000 --- a/src/common/hash.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common.h" - -u32 HashFletcher(const u8* data_u8, size_t length); // FAST. Length & 1 == 0. -u32 HashAdler32(const u8* data, size_t len); // Fairly accurate, slightly slower -u32 HashFNV(const u8* ptr, int length); // Another fast and decent hash -u32 HashEctor(const u8* ptr, int length); // JUNK. DO NOT USE FOR NEW THINGS -u64 GetCRC32(const u8 *src, int len, u32 samples); // SSE4.2 version of CRC32 -u64 GetHashHiresTexture(const u8 *src, int len, u32 samples); -u64 GetMurmurHash3(const u8 *src, int len, u32 samples); -u64 GetHash64(const u8 *src, int len, u32 samples); -void SetHash64Function(bool useHiresTextures); diff --git a/src/common/linear_disk_cache.h b/src/common/linear_disk_cache.h index 74ce74aba..48529cf42 100644 --- a/src/common/linear_disk_cache.h +++ b/src/common/linear_disk_cache.h @@ -4,7 +4,7 @@ #pragma once -#include "common/common.h" +#include "common/common_types.h" #include <fstream> // defined in Version.cpp diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 649640e72..7d3534a43 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -39,6 +39,8 @@ static std::shared_ptr<Logger> global_logger; SUB(Service, AC) \ SUB(Service, PTM) \ SUB(Service, LDR) \ + SUB(Service, NIM) \ + SUB(Service, NWM) \ SUB(Service, CFG) \ SUB(Service, DSP) \ SUB(Service, HID) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 83d64145b..123641cb4 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -59,6 +59,8 @@ enum class Class : ClassType { Service_AC, ///< The AC (WiFi status) service Service_PTM, ///< The PTM (Power status & misc.) service Service_LDR, ///< The LDR (3ds dll loader) service + Service_NIM, ///< The NIM (Network interface manager) service + Service_NWM, ///< The NWM (Network manager) service Service_CFG, ///< The CFG (Configuration) service Service_DSP, ///< The DSP (DSP control) service Service_HID, ///< The HID (User input) service diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index 36c91c4f6..45be6d0a1 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp @@ -14,6 +14,7 @@ #include "common/logging/log.h" #include "common/logging/text_formatter.h" +#include "common/common_funcs.h" #include "common/string_util.h" namespace Log { diff --git a/src/common/math_util.cpp b/src/common/math_util.cpp deleted file mode 100644 index a83592dd2..000000000 --- a/src/common/math_util.cpp +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - - -#include "common/common.h" -#include "common/math_util.h" - -#include <numeric> // Necessary on OS X, but not Linux - -namespace MathUtil -{ - -u32 ClassifyDouble(double dvalue) -{ - // TODO: Optimize the below to be as fast as possible. - IntDouble value; - value.d = dvalue; - u64 sign = value.i & DOUBLE_SIGN; - u64 exp = value.i & DOUBLE_EXP; - if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP) - { - // Nice normalized number. - return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN; - } - else - { - u64 mantissa = value.i & DOUBLE_FRAC; - if (mantissa) - { - if (exp) - { - return PPC_FPCLASS_QNAN; - } - else - { - // Denormalized number. - return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD; - } - } - else if (exp) - { - //Infinite - return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF; - } - else - { - //Zero - return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ; - } - } -} - -u32 ClassifyFloat(float fvalue) -{ - // TODO: Optimize the below to be as fast as possible. - IntFloat value; - value.f = fvalue; - u32 sign = value.i & FLOAT_SIGN; - u32 exp = value.i & FLOAT_EXP; - if (exp > FLOAT_ZERO && exp < FLOAT_EXP) - { - // Nice normalized number. - return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN; - } - else - { - u32 mantissa = value.i & FLOAT_FRAC; - if (mantissa) - { - if (exp) - { - return PPC_FPCLASS_QNAN; // Quiet NAN - } - else - { - // Denormalized number. - return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD; - } - } - else if (exp) - { - // Infinite - return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF; - } - else - { - //Zero - return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ; - } - } -} - - -} // namespace - -inline void MatrixMul(int n, const float *a, const float *b, float *result) -{ - for (int i = 0; i < n; ++i) - { - for (int j = 0; j < n; ++j) - { - float temp = 0; - for (int k = 0; k < n; ++k) - { - temp += a[i * n + k] * b[k * n + j]; - } - result[i * n + j] = temp; - } - } -} - -// Calculate sum of a float list -float MathFloatVectorSum(const std::vector<float>& Vec) -{ - return std::accumulate(Vec.begin(), Vec.end(), 0.0f); -} - -void Matrix33::LoadIdentity(Matrix33 &mtx) -{ - memset(mtx.data, 0, sizeof(mtx.data)); - mtx.data[0] = 1.0f; - mtx.data[4] = 1.0f; - mtx.data[8] = 1.0f; -} - -void Matrix33::RotateX(Matrix33 &mtx, float rad) -{ - float s = sin(rad); - float c = cos(rad); - memset(mtx.data, 0, sizeof(mtx.data)); - mtx.data[0] = 1; - mtx.data[4] = c; - mtx.data[5] = -s; - mtx.data[7] = s; - mtx.data[8] = c; -} -void Matrix33::RotateY(Matrix33 &mtx, float rad) -{ - float s = sin(rad); - float c = cos(rad); - memset(mtx.data, 0, sizeof(mtx.data)); - mtx.data[0] = c; - mtx.data[2] = s; - mtx.data[4] = 1; - mtx.data[6] = -s; - mtx.data[8] = c; -} - -void Matrix33::Multiply(const Matrix33 &a, const Matrix33 &b, Matrix33 &result) -{ - MatrixMul(3, a.data, b.data, result.data); -} - -void Matrix33::Multiply(const Matrix33 &a, const float vec[3], float result[3]) -{ - for (int i = 0; i < 3; ++i) { - result[i] = 0; - for (int k = 0; k < 3; ++k) { - result[i] += a.data[i * 3 + k] * vec[k]; - } - } -} - -void Matrix44::LoadIdentity(Matrix44 &mtx) -{ - memset(mtx.data, 0, sizeof(mtx.data)); - mtx.data[0] = 1.0f; - mtx.data[5] = 1.0f; - mtx.data[10] = 1.0f; - mtx.data[15] = 1.0f; -} - -void Matrix44::LoadMatrix33(Matrix44 &mtx, const Matrix33 &m33) -{ - for (int i = 0; i < 3; ++i) - { - for (int j = 0; j < 3; ++j) - { - mtx.data[i * 4 + j] = m33.data[i * 3 + j]; - } - } - - for (int i = 0; i < 3; ++i) - { - mtx.data[i * 4 + 3] = 0; - mtx.data[i + 12] = 0; - } - mtx.data[15] = 1.0f; -} - -void Matrix44::Set(Matrix44 &mtx, const float mtxArray[16]) -{ - for(int i = 0; i < 16; ++i) { - mtx.data[i] = mtxArray[i]; - } -} - -void Matrix44::Translate(Matrix44 &mtx, const float vec[3]) -{ - LoadIdentity(mtx); - mtx.data[3] = vec[0]; - mtx.data[7] = vec[1]; - mtx.data[11] = vec[2]; -} - -void Matrix44::Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result) -{ - MatrixMul(4, a.data, b.data, result.data); -} - diff --git a/src/common/math_util.h b/src/common/math_util.h index 43b0e0dc3..0b1400b41 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h @@ -4,11 +4,9 @@ #pragma once -#include "common/common.h" - #include <algorithm> +#include <cstdlib> #include <type_traits> -#include <vector> namespace MathUtil { @@ -19,83 +17,6 @@ inline T Clamp(const T val, const T& min, const T& max) return std::max(min, std::min(max, val)); } -static const u64 DOUBLE_SIGN = 0x8000000000000000ULL, - DOUBLE_EXP = 0x7FF0000000000000ULL, - DOUBLE_FRAC = 0x000FFFFFFFFFFFFFULL, - DOUBLE_ZERO = 0x0000000000000000ULL; - -static const u32 FLOAT_SIGN = 0x80000000, - FLOAT_EXP = 0x7F800000, - FLOAT_FRAC = 0x007FFFFF, - FLOAT_ZERO = 0x00000000; - -union IntDouble { - double d; - u64 i; -}; -union IntFloat { - float f; - u32 i; -}; - -inline bool IsNAN(double d) -{ - IntDouble x; x.d = d; - return ( ((x.i & DOUBLE_EXP) == DOUBLE_EXP) && - ((x.i & DOUBLE_FRAC) != DOUBLE_ZERO) ); -} - -inline bool IsQNAN(double d) -{ - IntDouble x; x.d = d; - return ( ((x.i & DOUBLE_EXP) == DOUBLE_EXP) && - ((x.i & 0x0007fffffffffffULL) == 0x000000000000000ULL) && - ((x.i & 0x000800000000000ULL) == 0x000800000000000ULL) ); -} - -inline bool IsSNAN(double d) -{ - IntDouble x; x.d = d; - return( ((x.i & DOUBLE_EXP) == DOUBLE_EXP) && - ((x.i & DOUBLE_FRAC) != DOUBLE_ZERO) && - ((x.i & 0x0008000000000000ULL) == DOUBLE_ZERO) ); -} - -inline float FlushToZero(float f) -{ - IntFloat x; x.f = f; - if ((x.i & FLOAT_EXP) == 0) - x.i &= FLOAT_SIGN; // turn into signed zero - return x.f; -} - -inline double FlushToZeroAsFloat(double d) -{ - IntDouble x; x.d = d; - if ((x.i & DOUBLE_EXP) < 0x3800000000000000ULL) - x.i &= DOUBLE_SIGN; // turn into signed zero - return x.d; -} - -enum PPCFpClass -{ - PPC_FPCLASS_QNAN = 0x11, - PPC_FPCLASS_NINF = 0x9, - PPC_FPCLASS_NN = 0x8, - PPC_FPCLASS_ND = 0x18, - PPC_FPCLASS_NZ = 0x12, - PPC_FPCLASS_PZ = 0x2, - PPC_FPCLASS_PD = 0x14, - PPC_FPCLASS_PN = 0x4, - PPC_FPCLASS_PINF = 0x5, -}; - -// Uses PowerPC conventions for the return value, so it can be easily -// used directly in CPU emulation. -u32 ClassifyDouble(double dvalue); -// More efficient float version. -u32 ClassifyFloat(float fvalue); - template<class T> struct Rectangle { @@ -104,101 +25,12 @@ struct Rectangle T right; T bottom; - Rectangle() - { } + Rectangle() {} - Rectangle(T theLeft, T theTop, T theRight, T theBottom) - : left(theLeft), top(theTop), right(theRight), bottom(theBottom) - { } - - bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; } + Rectangle(T left, T top, T right, T bottom) : left(left), top(top), right(right), bottom(bottom) {} T GetWidth() const { return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); } T GetHeight() const { return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); } - - // If the rectangle is in a coordinate system with a lower-left origin, use - // this Clamp. - void ClampLL(T x1, T y1, T x2, T y2) - { - if (left < x1) left = x1; - if (right > x2) right = x2; - if (top > y1) top = y1; - if (bottom < y2) bottom = y2; - } - - // If the rectangle is in a coordinate system with an upper-left origin, - // use this Clamp. - void ClampUL(T x1, T y1, T x2, T y2) - { - if (left < x1) left = x1; - if (right > x2) right = x2; - if (top < y1) top = y1; - if (bottom > y2) bottom = y2; - } }; } // namespace MathUtil - -inline float pow2f(float x) {return x * x;} -inline double pow2(double x) {return x * x;} - -float MathFloatVectorSum(const std::vector<float>&); - -#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) -#define ROUND_DOWN(x, a) ((x) & ~((a) - 1)) - -// Rounds down. 0 -> undefined -inline u64 Log2(u64 val) -{ -#if defined(__GNUC__) - return 63 - __builtin_clzll(val); - -#elif defined(_MSC_VER) && defined(_M_X64) - unsigned long result = -1; - _BitScanReverse64(&result, val); - return result; - -#else - u64 result = -1; - while (val != 0) - { - val >>= 1; - ++result; - } - return result; -#endif -} - -// Tiny matrix/vector library. -// Used for things like Free-Look in the gfx backend. - -class Matrix33 -{ -public: - static void LoadIdentity(Matrix33 &mtx); - - // set mtx to be a rotation matrix around the x axis - static void RotateX(Matrix33 &mtx, float rad); - // set mtx to be a rotation matrix around the y axis - static void RotateY(Matrix33 &mtx, float rad); - - // set result = a x b - static void Multiply(const Matrix33 &a, const Matrix33 &b, Matrix33 &result); - static void Multiply(const Matrix33 &a, const float vec[3], float result[3]); - - float data[9]; -}; - -class Matrix44 -{ -public: - static void LoadIdentity(Matrix44 &mtx); - static void LoadMatrix33(Matrix44 &mtx, const Matrix33 &m33); - static void Set(Matrix44 &mtx, const float mtxArray[16]); - - static void Translate(Matrix44 &mtx, const float vec[3]); - - static void Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result); - - float data[16]; -}; diff --git a/src/common/mem_arena.cpp b/src/common/mem_arena.cpp deleted file mode 100644 index 76c70701d..000000000 --- a/src/common/mem_arena.cpp +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include <string> - -#include "common/memory_util.h" -#include "common/mem_arena.h" -#include "common/string_util.h" - -#ifndef _WIN32 -#include <fcntl.h> -#ifdef ANDROID -#include <sys/ioctl.h> -#include <linux/ashmem.h> -#endif -#endif - -#ifdef ANDROID - -// Hopefully this ABI will never change... - - -#define ASHMEM_DEVICE "/dev/ashmem" - -/* -* ashmem_create_region - creates a new ashmem region and returns the file -* descriptor, or <0 on error -* -* `name' is an optional label to give the region (visible in /proc/pid/maps) -* `size' is the size of the region, in page-aligned bytes -*/ -int ashmem_create_region(const char *name, size_t size) -{ - int fd, ret; - - fd = open(ASHMEM_DEVICE, O_RDWR); - if (fd < 0) - return fd; - - if (name) { - char buf[ASHMEM_NAME_LEN]; - - strncpy(buf, name, sizeof(buf)); - ret = ioctl(fd, ASHMEM_SET_NAME, buf); - if (ret < 0) - goto error; - } - - ret = ioctl(fd, ASHMEM_SET_SIZE, size); - if (ret < 0) - goto error; - - return fd; - -error: - LOG_ERROR(Common_Memory, "NASTY ASHMEM ERROR: ret = %08x", ret); - close(fd); - return ret; -} - -int ashmem_set_prot_region(int fd, int prot) -{ - return ioctl(fd, ASHMEM_SET_PROT_MASK, prot); -} - -int ashmem_pin_region(int fd, size_t offset, size_t len) -{ - struct ashmem_pin pin = { offset, len }; - return ioctl(fd, ASHMEM_PIN, &pin); -} - -int ashmem_unpin_region(int fd, size_t offset, size_t len) -{ - struct ashmem_pin pin = { offset, len }; - return ioctl(fd, ASHMEM_UNPIN, &pin); -} -#endif // Android - - -#if defined(_WIN32) -SYSTEM_INFO sysInfo; -#endif - - -// Windows mappings need to be on 64K boundaries, due to Alpha legacy. -#ifdef _WIN32 -size_t roundup(size_t x) { - int gran = sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x10000; - return (x + gran - 1) & ~(gran - 1); -} -#else -size_t roundup(size_t x) { - return x; -} -#endif - - -void MemArena::GrabLowMemSpace(size_t size) -{ -#ifdef _WIN32 - hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD)(size), nullptr); - GetSystemInfo(&sysInfo); -#elif defined(ANDROID) - // Use ashmem so we don't have to allocate a file on disk! - fd = ashmem_create_region("Citra_RAM", size); - // Note that it appears that ashmem is pinned by default, so no need to pin. - if (fd < 0) - { - LOG_ERROR(Common_Memory, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno)); - return; - } -#else - // Try to find a non-existing filename for our shared memory. - // In most cases the first one will be available, but it's nicer to search - // a bit more. - for (int i = 0; i < 10000; i++) - { - std::string file_name = Common::StringFromFormat("/citramem.%d", i); - fd = shm_open(file_name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd != -1) - { - shm_unlink(file_name.c_str()); - break; - } - else if (errno != EEXIST) - { - LOG_ERROR(Common_Memory, "shm_open failed: %s", strerror(errno)); - return; - } - } - if (ftruncate(fd, size) < 0) - LOG_ERROR(Common_Memory, "Failed to allocate low memory space"); -#endif -} - - -void MemArena::ReleaseSpace() -{ -#ifdef _WIN32 - CloseHandle(hMemoryMapping); - hMemoryMapping = 0; -#else - close(fd); -#endif -} - - -void *MemArena::CreateView(s64 offset, size_t size, void *base) -{ -#ifdef _WIN32 - size = roundup(size); - void *ptr = MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base); - return ptr; -#else - void *retval = mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED | - // Do not sync memory to underlying file. Linux has this by default. -#ifdef __FreeBSD__ - MAP_NOSYNC | -#endif - ((base == nullptr) ? 0 : MAP_FIXED), fd, offset); - - if (retval == MAP_FAILED) - { - LOG_ERROR(Common_Memory, "mmap failed"); - return nullptr; - } - return retval; -#endif -} - - -void MemArena::ReleaseView(void* view, size_t size) -{ -#ifdef _WIN32 - UnmapViewOfFile(view); -#else - munmap(view, size); -#endif -} - -u8* MemArena::Find4GBBase() -{ -#ifdef _M_X64 -#ifdef _WIN32 - // 64 bit - u8* base = (u8*)VirtualAlloc(0, 0xE1000000, MEM_RESERVE, PAGE_READWRITE); - VirtualFree(base, 0, MEM_RELEASE); - return base; -#else - // Very precarious - mmap cannot return an error when trying to map already used pages. - // This makes the Windows approach above unusable on Linux, so we will simply pray... - return reinterpret_cast<u8*>(0x2300000000ULL); -#endif - -#else // 32 bit - -#ifdef _WIN32 - u8* base = (u8*)VirtualAlloc(0, 0x10000000, MEM_RESERVE, PAGE_READWRITE); - if (base) { - VirtualFree(base, 0, MEM_RELEASE); - } - return base; -#else - void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_SHARED, -1, 0); - if (base == MAP_FAILED) { - LOG_ERROR(Common_Memory, "Failed to map 256 MB of memory space: %s", strerror(errno)); - return 0; - } - munmap(base, 0x10000000); - return static_cast<u8*>(base); -#endif -#endif -} - - -// yeah, this could also be done in like two bitwise ops... -#define SKIP(a_flags, b_flags) -//if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY)) -// continue; -//if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM)) -// continue; - -static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 flags, MemArena *arena) { - // OK, we know where to find free space. Now grab it! - // We just mimic the popular BAT setup. - size_t position = 0; - size_t last_position = 0; - - // Zero all the pointers to be sure. - for (int i = 0; i < num_views; i++) - { - if (views[i].out_ptr_low) - *views[i].out_ptr_low = 0; - if (views[i].out_ptr) - *views[i].out_ptr = 0; - } - - int i; - for (i = 0; i < num_views; i++) - { - const MemoryView &view = views[i]; - if (view.size == 0) - continue; - SKIP(flags, view.flags); - if (view.flags & MV_MIRROR_PREVIOUS) { - position = last_position; - } - else { - *(view.out_ptr_low) = (u8*)arena->CreateView(position, view.size); - if (!*view.out_ptr_low) - goto bail; - } -#ifdef _M_X64 - *view.out_ptr = (u8*)arena->CreateView( - position, view.size, base + view.virtual_address); -#else - if (view.flags & MV_MIRROR_PREVIOUS) { // TODO: should check if the two & 0x3FFFFFFF are identical. - // No need to create multiple identical views. - *view.out_ptr = *views[i - 1].out_ptr; - } - else { - *view.out_ptr = (u8*)arena->CreateView( - position, view.size, base + (view.virtual_address & 0x3FFFFFFF)); - if (!*view.out_ptr) - goto bail; - } -#endif - - last_position = position; - position += roundup(view.size); - } - - return true; - -bail: - // Argh! ERROR! Free what we grabbed so far so we can try again. - for (int j = 0; j <= i; j++) - { - if (views[i].size == 0) - continue; - SKIP(flags, views[i].flags); - if (views[j].out_ptr_low && *views[j].out_ptr_low) - { - arena->ReleaseView(*views[j].out_ptr_low, views[j].size); - *views[j].out_ptr_low = nullptr; - } - if (*views[j].out_ptr) - { -#ifdef _M_X64 - arena->ReleaseView(*views[j].out_ptr, views[j].size); -#else - if (!(views[j].flags & MV_MIRROR_PREVIOUS)) - { - arena->ReleaseView(*views[j].out_ptr, views[j].size); - } -#endif - *views[j].out_ptr = nullptr; - } - } - return false; -} - -u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena *arena) -{ - size_t total_mem = 0; - int base_attempts = 0; - - for (int i = 0; i < num_views; i++) - { - if (views[i].size == 0) - continue; - SKIP(flags, views[i].flags); - if ((views[i].flags & MV_MIRROR_PREVIOUS) == 0) - total_mem += roundup(views[i].size); - } - // Grab some pagefile backed memory out of the void ... - arena->GrabLowMemSpace(total_mem); - - // Now, create views in high memory where there's plenty of space. -#ifdef _M_X64 - u8 *base = MemArena::Find4GBBase(); - // This really shouldn't fail - in 64-bit, there will always be enough - // address space. - if (!Memory_TryBase(base, views, num_views, flags, arena)) - { - LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base."); - return 0; - } -#elif defined(_WIN32) - // Try a whole range of possible bases. Return once we got a valid one. - u32 max_base_addr = 0x7FFF0000 - 0x10000000; - u8 *base = nullptr; - - for (u32 base_addr = 0x01000000; base_addr < max_base_addr; base_addr += 0x400000) - { - base_attempts++; - base = (u8 *)base_addr; - if (Memory_TryBase(base, views, num_views, flags, arena)) - { - LOG_DEBUG(Common_Memory, "Found valid memory base at %p after %i tries.", base, base_attempts); - base_attempts = 0; - break; - } - } -#else - // Linux32 is fine with the x64 method, although limited to 32-bit with no automirrors. - u8 *base = MemArena::Find4GBBase(); - if (!Memory_TryBase(base, views, num_views, flags, arena)) - { - LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base."); - return 0; - } -#endif - if (base_attempts) - LOG_ERROR(Common_Memory, "No possible memory base pointer found!"); - return base; -} - -void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena) -{ - for (int i = 0; i < num_views; i++) - { - if (views[i].size == 0) - continue; - SKIP(flags, views[i].flags); - if (views[i].out_ptr_low && *views[i].out_ptr_low) - arena->ReleaseView(*views[i].out_ptr_low, views[i].size); - if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low)) - arena->ReleaseView(*views[i].out_ptr, views[i].size); - *views[i].out_ptr = nullptr; - if (views[i].out_ptr_low) - *views[i].out_ptr_low = nullptr; - } -} diff --git a/src/common/mem_arena.h b/src/common/mem_arena.h deleted file mode 100644 index 3379d2529..000000000 --- a/src/common/mem_arena.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#pragma once - -#ifdef _WIN32 -#include <windows.h> -#endif - -#include "common/common.h" - -// This class lets you create a block of anonymous RAM, and then arbitrarily map views into it. -// Multiple views can mirror the same section of the block, which makes it very convient for emulating -// memory mirrors. - -class MemArena -{ -public: - void GrabLowMemSpace(size_t size); - void ReleaseSpace(); - void *CreateView(s64 offset, size_t size, void *base = 0); - void ReleaseView(void *view, size_t size); - - // This only finds 1 GB in 32-bit - static u8 *Find4GBBase(); -private: - -#ifdef _WIN32 - HANDLE hMemoryMapping; -#else - int fd; -#endif -}; - -enum { - MV_MIRROR_PREVIOUS = 1, - // MV_FAKE_VMEM = 2, - // MV_WII_ONLY = 4, - MV_IS_PRIMARY_RAM = 0x100, - MV_IS_EXTRA1_RAM = 0x200, - MV_IS_EXTRA2_RAM = 0x400, -}; - -struct MemoryView -{ - u8 **out_ptr_low; - u8 **out_ptr; - u32 virtual_address; - u32 size; - u32 flags; -}; - -// Uses a memory arena to set up an emulator-friendly memory map according to -// a passed-in list of MemoryView structures. -u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena *arena); -void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena); diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index 7e69d31cb..20b791a10 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp @@ -3,7 +3,8 @@ // Refer to the license.txt file included. -#include "common/common.h" +#include "common/common_funcs.h" +#include "common/logging/log.h" #include "common/memory_util.h" #include "common/string_util.h" @@ -70,7 +71,7 @@ void* AllocateExecutableMemory(size_t size, bool low) } #endif -#if defined(_M_X64) +#if EMU_ARCH_BITS == 64 if ((u64)ptr >= 0x80000000 && low == true) LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!"); #endif diff --git a/src/common/misc.cpp b/src/common/misc.cpp index e33055d10..53cacf37c 100644 --- a/src/common/misc.cpp +++ b/src/common/misc.cpp @@ -2,10 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common.h" +#include "common/common_funcs.h" #ifdef _WIN32 #include <windows.h> +#else +#include <string.h> #endif // Neither Android nor OS X support TLS diff --git a/src/common/platform.h b/src/common/platform.h index e27d6e31f..df780ac6f 100644 --- a/src/common/platform.h +++ b/src/common/platform.h @@ -57,54 +57,33 @@ #endif -#if defined(__x86_64__) || defined(_M_X64) || defined(__alpha__) || defined(__ia64__) -#define EMU_ARCHITECTURE_X64 -#else -#define EMU_ARCHITECTURE_X86 +#if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) + #define EMU_ARCH_BITS 64 +#elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM) + #define EMU_ARCH_BITS 32 #endif //////////////////////////////////////////////////////////////////////////////////////////////////// -// Compiler-Specific Definitions - -#if EMU_PLATFORM == PLATFORM_WINDOWS - -#include <time.h> - -#ifndef NOMINMAX -#define NOMINMAX +// Feature detection + +#if defined _M_GENERIC +# define _M_SSE 0x0 +#elif defined __GNUC__ +# if defined __SSE4_2__ +# define _M_SSE 0x402 +# elif defined __SSE4_1__ +# define _M_SSE 0x401 +# elif defined __SSSE3__ +# define _M_SSE 0x301 +# elif defined __SSE3__ +# define _M_SSE 0x300 +# endif +#elif (_MSC_VER >= 1500) || __INTEL_COMPILER // Visual Studio 2008 +# define _M_SSE 0x402 #endif -#define EMU_FASTCALL __fastcall - -#ifdef _MSC_VER -inline struct tm* localtime_r(const time_t *clock, struct tm *result) { - if (localtime_s(result, clock) == 0) - return result; - return nullptr; -} -#endif - -#else // EMU_PLATFORM != PLATFORM_WINDOWS - -#define EMU_FASTCALL __attribute__((fastcall)) -#define __stdcall -#define __cdecl -#define BOOL bool -#define DWORD u32 - -// TODO: Hacks.. -#include <limits.h> - -#include <strings.h> -#define stricmp(str1, str2) strcasecmp(str1, str2) -#define _stricmp(str1, str2) strcasecmp(str1, str2) -#define _snprintf snprintf -#define _getcwd getcwd -#define _tzset tzset - -typedef void EXCEPTION_POINTERS; - -#endif +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Compiler-Specific Definitions #define GCC_VERSION_AVAILABLE(major, minor) (defined(__GNUC__) && (__GNUC__ > (major) || \ (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))) diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp index 65c3df167..cf6b6b258 100644 --- a/src/common/profiler.cpp +++ b/src/common/profiler.cpp @@ -7,7 +7,6 @@ #include "common/assert.h" #if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013. -#define NOMINMAX #define WIN32_LEAN_AND_MEAN #include <Windows.h> // For QueryPerformanceCounter/Frequency #endif @@ -127,10 +126,9 @@ void TimingResultsAggregator::AddFrame(const ProfilingFrameResult& frame_result) static AggregatedDuration AggregateField(const std::vector<Duration>& v, size_t len) { AggregatedDuration result; result.avg = Duration::zero(); - result.min = result.max = (len == 0 ? Duration::zero() : v[0]); - for (size_t i = 1; i < len; ++i) { + for (size_t i = 0; i < len; ++i) { Duration value = v[i]; result.avg += value; result.min = std::min(result.min, value); diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 3264dd51a..7dc0ba7ba 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -4,7 +4,9 @@ #include <boost/range/algorithm.hpp> -#include "common/common.h" +#include "common/common_funcs.h" +#include "common/common_paths.h" +#include "common/logging/log.h" #include "common/string_util.h" #ifdef _MSC_VER @@ -285,131 +287,6 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st return result; } -// UriDecode and UriEncode are from http://www.codeguru.com/cpp/cpp/string/conversions/print.php/c12759 -// by jinq0123 (November 2, 2006) - -// Uri encode and decode. -// RFC1630, RFC1738, RFC2396 - -//#include <string> -//#include <assert.h> - -const char HEX2DEC[256] = -{ - /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ - /* 0 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - /* 1 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - /* 2 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - /* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,16,16, 16,16,16,16, - - /* 4 */ 16,10,11,12, 13,14,15,16, 16,16,16,16, 16,16,16,16, - /* 5 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - /* 6 */ 16,10,11,12, 13,14,15,16, 16,16,16,16, 16,16,16,16, - /* 7 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - - /* 8 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - /* 9 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - /* A */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - /* B */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - - /* C */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - /* D */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - /* E */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, - /* F */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16 -}; - -std::string UriDecode(const std::string & sSrc) -{ - // Note from RFC1630: "Sequences which start with a percent sign - // but are not followed by two hexadecimal characters (0-9, A-F) are reserved - // for future extension" - - const unsigned char * pSrc = (const unsigned char *)sSrc.c_str(); - const size_t SRC_LEN = sSrc.length(); - const unsigned char * const SRC_END = pSrc + SRC_LEN; - const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%' - - char * const pStart = new char[SRC_LEN]; - char * pEnd = pStart; - - while (pSrc < SRC_LAST_DEC) - { - if (*pSrc == '%') - { - char dec1, dec2; - if (16 != (dec1 = HEX2DEC[*(pSrc + 1)]) - && 16 != (dec2 = HEX2DEC[*(pSrc + 2)])) - { - *pEnd++ = (dec1 << 4) + dec2; - pSrc += 3; - continue; - } - } - - *pEnd++ = *pSrc++; - } - - // the last 2- chars - while (pSrc < SRC_END) - *pEnd++ = *pSrc++; - - std::string sResult(pStart, pEnd); - delete [] pStart; - return sResult; -} - -// Only alphanum is safe. -const char SAFE[256] = -{ - /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ - /* 0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, - /* 1 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, - /* 2 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, - /* 3 */ 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0, - - /* 4 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, - /* 5 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0, - /* 6 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, - /* 7 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0, - - /* 8 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, - /* 9 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, - /* A */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, - /* B */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, - - /* C */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, - /* D */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, - /* E */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, - /* F */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 -}; - -std::string UriEncode(const std::string & sSrc) -{ - const char DEC2HEX[16 + 1] = "0123456789ABCDEF"; - const unsigned char * pSrc = (const unsigned char *)sSrc.c_str(); - const size_t SRC_LEN = sSrc.length(); - unsigned char * const pStart = new unsigned char[SRC_LEN * 3]; - unsigned char * pEnd = pStart; - const unsigned char * const SRC_END = pSrc + SRC_LEN; - - for (; pSrc < SRC_END; ++pSrc) - { - if (SAFE[*pSrc]) - *pEnd++ = *pSrc; - else - { - // escape this char - *pEnd++ = '%'; - *pEnd++ = DEC2HEX[*pSrc >> 4]; - *pEnd++ = DEC2HEX[*pSrc & 0x0F]; - } - } - - std::string sResult((char *)pStart, (char *)pEnd); - delete [] pStart; - return sResult; -} - #ifdef _MSC_VER std::string UTF16ToUTF8(const std::u16string& input) @@ -600,4 +477,12 @@ std::string SHIFTJISToUTF8(const std::string& input) #endif +std::string StringFromFixedZeroTerminatedBuffer(const char * buffer, size_t max_len) { + size_t len = 0; + while (len < max_len && buffer[len] != '\0') + ++len; + + return std::string(buffer, len); +} + } diff --git a/src/common/string_util.h b/src/common/string_util.h index 74974263f..fdc410499 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -10,7 +10,7 @@ #include <sstream> #include <vector> -#include "common/common.h" +#include "common/common_types.h" namespace Common { @@ -86,8 +86,6 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename); std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest); -std::string UriDecode(const std::string & sSrc); -std::string UriEncode(const std::string & sSrc); std::string UTF16ToUTF8(const std::u16string& input); std::u16string UTF8ToUTF16(const std::string& input); @@ -130,4 +128,10 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) { return (begin == end) == (*other == '\0'); } +/** + * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't + * NUL-terminated then the string ends at max_len characters. + */ +std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len); + } diff --git a/src/common/symbols.h b/src/common/symbols.h index f76cb6b1e..6b62b011e 100644 --- a/src/common/symbols.h +++ b/src/common/symbols.h @@ -5,8 +5,10 @@ #pragma once #include <map> +#include <string> +#include <utility> -#include "common/common.h" +#include "common/common_types.h" struct TSymbol { diff --git a/src/common/thread.h b/src/common/thread.h index a45728e1e..7bc419497 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -4,7 +4,6 @@ #pragma once -// Don't include common.h here as it will break LogManager #include "common/common_types.h" #include <cstdio> #include <cstring> @@ -51,109 +50,60 @@ int CurrentThreadId(); void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); void SetCurrentThreadAffinity(u32 mask); -class Event -{ +class Event { public: - Event() - : is_set(false) - {} + Event() : is_set(false) {} - void Set() - { + void Set() { std::lock_guard<std::mutex> lk(m_mutex); - if (!is_set) - { + if (!is_set) { is_set = true; m_condvar.notify_one(); } } - void Wait() - { + void Wait() { std::unique_lock<std::mutex> lk(m_mutex); - m_condvar.wait(lk, IsSet(this)); + m_condvar.wait(lk, [&]{ return is_set; }); is_set = false; } - void Reset() - { + void Reset() { std::unique_lock<std::mutex> lk(m_mutex); // no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration is_set = false; } private: - class IsSet - { - public: - IsSet(const Event* ev) - : m_event(ev) - {} - - bool operator()() - { - return m_event->is_set; - } - - private: - const Event* const m_event; - }; - - volatile bool is_set; + bool is_set; std::condition_variable m_condvar; std::mutex m_mutex; }; -// TODO: doesn't work on windows with (count > 2) -class Barrier -{ +class Barrier { public: - Barrier(size_t count) - : m_count(count), m_waiting(0) - {} + Barrier(size_t count) : m_count(count), m_waiting(0) {} - // block until "count" threads call Sync() - bool Sync() - { + /// Blocks until all "count" threads have called Sync() + void Sync() { std::unique_lock<std::mutex> lk(m_mutex); // TODO: broken when next round of Sync()s // is entered before all waiting threads return from the notify_all - if (m_count == ++m_waiting) - { + if (++m_waiting == m_count) { m_waiting = 0; m_condvar.notify_all(); - return true; - } - else - { - m_condvar.wait(lk, IsDoneWating(this)); - return false; + } else { + m_condvar.wait(lk, [&]{ return m_waiting == 0; }); } } private: - class IsDoneWating - { - public: - IsDoneWating(const Barrier* bar) - : m_bar(bar) - {} - - bool operator()() - { - return (0 == m_bar->m_waiting); - } - - private: - const Barrier* const m_bar; - }; - std::condition_variable m_condvar; std::mutex m_mutex; const size_t m_count; - volatile size_t m_waiting; + size_t m_waiting; }; void SleepCurrentThread(int ms); diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h index 444abf115..12455d7c4 100644 --- a/src/common/thread_queue_list.h +++ b/src/common/thread_queue_list.h @@ -9,8 +9,6 @@ #include <boost/range/algorithm_ext/erase.hpp> -#include "common/common.h" - namespace Common { template<class T, unsigned int N> @@ -40,6 +38,18 @@ struct ThreadQueueList { return -1; } + T get_first() { + Queue *cur = first; + while (cur != nullptr) { + if (!cur->data.empty()) { + return cur->data.front(); + } + cur = cur->next_nonempty; + } + + return T(); + } + T pop_first() { Queue *cur = first; while (cur != nullptr) { @@ -79,6 +89,12 @@ struct ThreadQueueList { cur->data.push_back(thread_id); } + void move(const T& thread_id, Priority old_priority, Priority new_priority) { + remove(old_priority, thread_id); + prepare(new_priority); + push_back(new_priority, thread_id); + } + void remove(Priority priority, const T& thread_id) { Queue *cur = &queues[priority]; boost::remove_erase(cur->data, thread_id); diff --git a/src/common/thunk.h b/src/common/thunk.h index 4fb7c98e1..533480056 100644 --- a/src/common/thunk.h +++ b/src/common/thunk.h @@ -6,7 +6,7 @@ #include <map> -#include "common/common.h" +#include "common/common_types.h" // This simple class creates a wrapper around a C/C++ function that saves all fp state // before entering it, and restores it upon exit. This is required to be able to selectively diff --git a/src/common/timer.cpp b/src/common/timer.cpp index a6682ea19..b99835ac7 100644 --- a/src/common/timer.cpp +++ b/src/common/timer.cpp @@ -12,9 +12,9 @@ #include <sys/time.h> #endif -#include "common/common.h" -#include "common/timer.h" +#include "common/common_types.h" #include "common/string_util.h" +#include "common/timer.h" namespace Common { diff --git a/src/common/timer.h b/src/common/timer.h index 4b44c33a0..b5f0f2585 100644 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -4,7 +4,7 @@ #pragma once -#include "common/common.h" +#include "common/common_types.h" #include <string> namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 33e5be3a4..e8d8c96d7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -6,13 +6,15 @@ set(SRCS arm/dyncom/arm_dyncom_interpreter.cpp arm/dyncom/arm_dyncom_run.cpp arm/dyncom/arm_dyncom_thumb.cpp - arm/interpreter/armcopro.cpp arm/interpreter/arminit.cpp arm/interpreter/armsupp.cpp arm/skyeye_common/vfp/vfp.cpp arm/skyeye_common/vfp/vfpdouble.cpp arm/skyeye_common/vfp/vfpinstr.cpp arm/skyeye_common/vfp/vfpsingle.cpp + core.cpp + core_timing.cpp + file_sys/archive_backend.cpp file_sys/archive_extsavedata.cpp file_sys/archive_romfs.cpp file_sys/archive_savedata.cpp @@ -21,15 +23,18 @@ set(SRCS file_sys/archive_systemsavedata.cpp file_sys/disk_archive.cpp file_sys/ivfc_archive.cpp + hle/config_mem.cpp + hle/hle.cpp hle/kernel/address_arbiter.cpp hle/kernel/event.cpp hle/kernel/kernel.cpp hle/kernel/mutex.cpp + hle/kernel/process.cpp hle/kernel/semaphore.cpp hle/kernel/session.cpp hle/kernel/shared_memory.cpp - hle/kernel/timer.cpp hle/kernel/thread.cpp + hle/kernel/timer.cpp hle/service/ac_u.cpp hle/service/act_u.cpp hle/service/am_app.cpp @@ -56,51 +61,51 @@ set(SRCS hle/service/fs/archive.cpp hle/service/fs/fs_user.cpp hle/service/gsp_gpu.cpp + hle/service/gsp_lcd.cpp hle/service/hid/hid.cpp - hle/service/hid/hid_user.cpp hle/service/hid/hid_spvr.cpp - hle/service/gsp_lcd.cpp + hle/service/hid/hid_user.cpp hle/service/http_c.cpp - hle/service/ir_rst.cpp - hle/service/ir_u.cpp + hle/service/ir/ir.cpp + hle/service/ir/ir_rst.cpp + hle/service/ir/ir_u.cpp + hle/service/ir/ir_user.cpp hle/service/ldr_ro.cpp hle/service/mic_u.cpp hle/service/ndm_u.cpp hle/service/news_s.cpp hle/service/news_u.cpp hle/service/nim_aoc.cpp + hle/service/nim_u.cpp hle/service/ns_s.cpp hle/service/nwm_uds.cpp hle/service/pm_app.cpp hle/service/ptm/ptm.cpp hle/service/ptm/ptm_play.cpp - hle/service/ptm/ptm_u.cpp hle/service/ptm/ptm_sysm.cpp + hle/service/ptm/ptm_u.cpp hle/service/service.cpp hle/service/soc_u.cpp hle/service/srv.cpp hle/service/ssl_c.cpp hle/service/y2r_u.cpp - hle/config_mem.cpp - hle/hle.cpp hle/shared_page.cpp hle/svc.cpp hw/gpu.cpp hw/hw.cpp hw/lcd.cpp + loader/3dsx.cpp loader/elf.cpp loader/loader.cpp loader/ncch.cpp - loader/3dsx.cpp - core.cpp - core_timing.cpp mem_map.cpp - mem_map_funcs.cpp + memory.cpp settings.cpp system.cpp ) set(HEADERS + arm/arm_interface.h arm/disassembler/arm_disasm.h arm/disassembler/load_symbol_map.h arm/dyncom/arm_dyncom.h @@ -112,12 +117,11 @@ set(HEADERS arm/skyeye_common/armdefs.h arm/skyeye_common/armemu.h arm/skyeye_common/armmmu.h - arm/skyeye_common/armos.h - arm/skyeye_common/skyeye_defs.h arm/skyeye_common/vfp/asm_vfp.h arm/skyeye_common/vfp/vfp.h arm/skyeye_common/vfp/vfp_helper.h - arm/arm_interface.h + core.h + core_timing.h file_sys/archive_backend.h file_sys/archive_extsavedata.h file_sys/archive_romfs.h @@ -125,19 +129,24 @@ set(HEADERS file_sys/archive_savedatacheck.h file_sys/archive_sdmc.h file_sys/archive_systemsavedata.h + file_sys/directory_backend.h file_sys/disk_archive.h file_sys/file_backend.h file_sys/ivfc_archive.h - file_sys/directory_backend.h + hle/config_mem.h + hle/function_wrappers.h + hle/hle.h hle/kernel/address_arbiter.h hle/kernel/event.h hle/kernel/kernel.h hle/kernel/mutex.h + hle/kernel/process.h hle/kernel/semaphore.h hle/kernel/session.h hle/kernel/shared_memory.h - hle/kernel/timer.h hle/kernel/thread.h + hle/kernel/timer.h + hle/result.h hle/service/ac_u.h hle/service/act_u.h hle/service/am_app.h @@ -164,47 +173,46 @@ set(HEADERS hle/service/fs/archive.h hle/service/fs/fs_user.h hle/service/gsp_gpu.h + hle/service/gsp_lcd.h hle/service/hid/hid.h hle/service/hid/hid_spvr.h hle/service/hid/hid_user.h - hle/service/gsp_lcd.h hle/service/http_c.h - hle/service/ir_rst.h - hle/service/ir_u.h + hle/service/ir/ir.h + hle/service/ir/ir_rst.h + hle/service/ir/ir_u.h + hle/service/ir/ir_user.h hle/service/ldr_ro.h hle/service/mic_u.h hle/service/ndm_u.h hle/service/news_s.h hle/service/news_u.h hle/service/nim_aoc.h + hle/service/nim_u.h hle/service/ns_s.h hle/service/nwm_uds.h hle/service/pm_app.h hle/service/ptm/ptm.h hle/service/ptm/ptm_play.h - hle/service/ptm/ptm_u.h hle/service/ptm/ptm_sysm.h + hle/service/ptm/ptm_u.h hle/service/service.h hle/service/soc_u.h hle/service/srv.h hle/service/ssl_c.h hle/service/y2r_u.h - hle/config_mem.h - hle/result.h - hle/function_wrappers.h - hle/hle.h hle/shared_page.h hle/svc.h hw/gpu.h hw/hw.h hw/lcd.h + loader/3dsx.h loader/elf.h loader/loader.h loader/ncch.h - loader/3dsx.h - core.h - core_timing.h mem_map.h + memory.h + memory_setup.h settings.h system.h ) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index fe1e584ad..85ed2c698 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -4,8 +4,8 @@ #pragma once -#include "common/common.h" #include "common/common_types.h" +#include "core/arm/skyeye_common/arm_regformat.h" namespace Core { struct ThreadContext; @@ -74,6 +74,20 @@ public: virtual void SetCPSR(u32 cpsr) = 0; /** + * Gets the value stored in a CP15 register. + * @param reg The CP15 register to retrieve the value from. + * @return the value stored in the given CP15 register. + */ + virtual u32 GetCP15Register(CP15Register reg) = 0; + + /** + * Stores the given value into the indicated CP15 register. + * @param reg The CP15 register to store the value into. + * @param value The value to store into the CP15 register. + */ + virtual void SetCP15Register(CP15Register reg, u32 value) = 0; + + /** * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time) * @param ticks Number of ticks to advance the CPU core */ diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index cb1a410a0..0072ae533 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -2,6 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> + +#include "common/make_unique.h" + #include "core/arm/skyeye_common/armemu.h" #include "core/arm/skyeye_common/vfp/vfp.h" @@ -12,18 +16,13 @@ #include "core/core.h" #include "core/core_timing.h" -const static cpu_config_t s_arm11_cpu_info = { - "armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE -}; - ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { - state = std::unique_ptr<ARMul_State>(new ARMul_State); + state = Common::make_unique<ARMul_State>(); ARMul_NewState(state.get()); ARMul_SelectProcessor(state.get(), ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); state->abort_model = ABORT_BASE_RESTORED; - state->cpu = (cpu_config_t*)&s_arm11_cpu_info; state->bigendSig = LOW; state->lateabtSig = LOW; @@ -31,7 +30,6 @@ ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { // Reset the core to initial state ARMul_Reset(state.get()); - state->NextInstr = RESUME; // NOTE: This will be overwritten by LoadContext state->Emulate = RUN; // Switch to the desired privilege mode. @@ -68,6 +66,14 @@ void ARM_DynCom::SetCPSR(u32 cpsr) { state->Cpsr = cpsr; } +u32 ARM_DynCom::GetCP15Register(CP15Register reg) { + return state->CP15[reg]; +} + +void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) { + state->CP15[reg] = value; +} + void ARM_DynCom::AddTicks(u64 ticks) { down_count -= ticks; if (down_count < 0) @@ -91,7 +97,6 @@ void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 e context.pc = entry_point; context.sp = stack_top; context.cpsr = 0x1F; // Usermode - context.mode = 8; // Instructs dyncom CPU core to start execution as if it's "resuming" a thread. } void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { @@ -105,8 +110,6 @@ void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { ctx.fpscr = state->VFP[1]; ctx.fpexc = state->VFP[2]; - - ctx.mode = state->NextInstr; } void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) { @@ -120,8 +123,6 @@ void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) { state->VFP[1] = ctx.fpscr; state->VFP[2] = ctx.fpexc; - - state->NextInstr = ctx.mode; } void ARM_DynCom::PrepareReschedule() { diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index a7f95d307..2488c879c 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -22,10 +22,12 @@ public: void SetReg(int index, u32 value) override; u32 GetCPSR() const override; void SetCPSR(u32 cpsr) override; + u32 GetCP15Register(CP15Register reg) override; + void SetCP15Register(CP15Register reg, u32 value) override; void AddTicks(u64 ticks) override; - void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg); + void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) override; void SaveContext(Core::ThreadContext& ctx) override; void LoadContext(const Core::ThreadContext& ctx) override; diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 2765cb36e..b79fd1719 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -6,13 +6,12 @@ #include <algorithm> #include <cstdio> -#include <unordered_map> #include "common/logging/log.h" #include "common/profiler.h" -#include "core/mem_map.h" -#include "core/hle/hle.h" +#include "core/memory.h" +#include "core/hle/svc.h" #include "core/arm/disassembler/arm_disasm.h" #include "core/arm/dyncom/arm_dyncom_interpreter.h" #include "core/arm/dyncom/arm_dyncom_thumb.h" @@ -339,7 +338,7 @@ static void LnSWoUB(ScaledRegisterPreIndexed)(ARMul_State* cpu, unsigned int ins unsigned int shift_imm = BITS(inst, 7, 11); unsigned int Rn = BITS(inst, 16, 19); unsigned int Rm = BITS(inst, 0, 3); - unsigned int index; + unsigned int index = 0; unsigned int addr; unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm); unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn); @@ -390,7 +389,7 @@ static void LnSWoUB(ScaledRegisterPostIndexed)(ARMul_State* cpu, unsigned int in unsigned int shift_imm = BITS(inst, 7, 11); unsigned int Rn = BITS(inst, 16, 19); unsigned int Rm = BITS(inst, 0, 3); - unsigned int index; + unsigned int index = 0; unsigned int addr = CHECK_READ_REG15_WA(cpu, Rn); unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm); @@ -605,7 +604,7 @@ static void LnSWoUB(ScaledRegisterOffset)(ARMul_State* cpu, unsigned int inst, u unsigned int shift_imm = BITS(inst, 7, 11); unsigned int Rn = BITS(inst, 16, 19); unsigned int Rm = BITS(inst, 0, 3); - unsigned int index; + unsigned int index = 0; unsigned int addr; unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm); unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn); @@ -993,6 +992,14 @@ typedef struct _mcr_inst { unsigned int inst; } mcr_inst; +typedef struct mcrr_inst { + unsigned int opcode_1; + unsigned int cp_num; + unsigned int crm; + unsigned int rt; + unsigned int rt2; +} mcrr_inst; + typedef struct _mrs_inst { unsigned int R; unsigned int Rd; @@ -1126,7 +1133,7 @@ int CondPassed(ARMul_State* cpu, unsigned int cond) { #define CFLAG cpu->CFlag #define VFLAG cpu->VFlag - int temp; + int temp = 0; switch (cond) { case 0x0: @@ -1262,11 +1269,6 @@ static get_addr_fp_t get_calc_addr_op(unsigned int inst) { #define CHECK_RM (inst_cream->Rm == 15) #define CHECK_RS (inst_cream->Rs == 15) -#define UNIMPLEMENTED_INSTRUCTION(mnemonic) \ - LOG_ERROR(Core_ARM11, "unimplemented instruction: %s", mnemonic); \ - CITRA_IGNORE_EXIT(-1); \ - return nullptr; - static ARM_INST_PTR INTERPRETER_TRANSLATE(adc)(unsigned int inst, int index) { arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(adc_inst)); @@ -1391,7 +1393,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index) inst_base->br = NON_BRANCH; inst_base->load_r15 = 0; - inst_cream->imm = BITS(inst, 8, 19) | BITS(inst, 0, 3); + inst_cream->imm = (BITS(inst, 8, 19) << 4) | BITS(inst, 0, 3); return inst_base; } @@ -1872,7 +1874,26 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(mcr)(unsigned int inst, int index) inst_cream->inst = inst; return inst_base; } -static ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MCRR"); } + +static ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(mcrr_inst)); + mcrr_inst* const inst_cream = (mcrr_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = NON_BRANCH; + inst_base->load_r15 = 0; + + inst_cream->crm = BITS(inst, 0, 3); + inst_cream->opcode_1 = BITS(inst, 4, 7); + inst_cream->cp_num = BITS(inst, 8, 11); + inst_cream->rt = BITS(inst, 12, 15); + inst_cream->rt2 = BITS(inst, 16, 19); + + return inst_base; +} + static ARM_INST_PTR INTERPRETER_TRANSLATE(mla)(unsigned int inst, int index) { arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mla_inst)); @@ -1931,7 +1952,12 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(mrc)(unsigned int inst, int index) inst_cream->inst = inst; return inst_base; } -static ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MRRC"); } + +static ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(mcrr)(inst, index); +} + static ARM_INST_PTR INTERPRETER_TRANSLATE(mrs)(unsigned int inst, int index) { arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mrs_inst)); @@ -3533,31 +3559,12 @@ const transop_fp_t arm_instruction_trans[] = { INTERPRETER_TRANSLATE(blx_1_thumb) }; -typedef std::unordered_map<u32, int> bb_map; -static bb_map CreamCache; - -static void insert_bb(unsigned int addr, int start) { - CreamCache[addr] = start; -} - -static int find_bb(unsigned int addr, int& start) { - int ret = -1; - bb_map::const_iterator it = CreamCache.find(addr); - if (it != CreamCache.end()) { - start = static_cast<int>(it->second); - ret = 0; - } else { - ret = -1; - } - return ret; -} - enum { FETCH_SUCCESS, FETCH_FAILURE }; -static tdstate decode_thumb_instr(ARMul_State* cpu, uint32_t inst, addr_t addr, uint32_t* arm_inst, uint32_t* inst_size, ARM_INST_PTR* ptr_inst_base){ +static tdstate decode_thumb_instr(ARMul_State* cpu, u32 inst, u32 addr, u32* arm_inst, u32* inst_size, ARM_INST_PTR* ptr_inst_base) { // Check if in Thumb mode tdstate ret = thumb_translate (addr, inst, arm_inst, inst_size); if(ret == t_branch){ @@ -3620,7 +3627,7 @@ typedef struct instruction_set_encoding_item ISEITEM; extern const ISEITEM arm_instruction[]; -static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, addr_t addr) { +static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { Common::Profiling::ScopeTimer timer_decode(profile_decode); // Decode instruction, get index @@ -3638,8 +3645,8 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, addr_t addr) { if (cpu->TFlag) thumb = THUMB; - addr_t phys_addr = addr; - addr_t pc_start = cpu->Reg[15]; + u32 phys_addr = addr; + u32 pc_start = cpu->Reg[15]; while(ret == NON_BRANCH) { inst = Memory::Read32(phys_addr & 0xFFFFFFFC); @@ -3674,7 +3681,9 @@ translated: } ret = inst_base->br; }; - insert_bb(pc_start, bb_start); + + cpu->instruction_cache[pc_start] = bb_start; + return KEEP_GOING; } @@ -3690,20 +3699,16 @@ static int clz(unsigned int x) { return n; } -static bool InAPrivilegedMode(ARMul_State* core) { - return (core->Mode != USER32MODE); -} - -unsigned InterpreterMainLoop(ARMul_State* state) { +unsigned InterpreterMainLoop(ARMul_State* cpu) { Common::Profiling::ScopeTimer timer_execute(profile_execute); #undef RM #undef RS #define CRn inst_cream->crn + #define OPCODE_1 inst_cream->opcode_1 #define OPCODE_2 inst_cream->opcode_2 #define CRm inst_cream->crm - #define CP15_REG(n) cpu->CP15[CP15(n)] #define RD cpu->Reg[inst_cream->Rd] #define RD2 cpu->Reg[inst_cream->Rd + 1] #define RN cpu->Reg[inst_cream->Rn] @@ -3952,8 +3957,6 @@ unsigned InterpreterMainLoop(ARMul_State* state) { #define PC (cpu->Reg[15]) #define CHECK_EXT_INT if (!cpu->NirqSig && !(cpu->Cpsr & 0x80)) goto END; - ARMul_State* cpu = state; - // GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback // to a clunky switch statement. #if defined __GNUC__ || defined __clang__ @@ -4005,9 +4008,14 @@ unsigned InterpreterMainLoop(ARMul_State* state) { phys_addr = cpu->Reg[15]; - if (find_bb(cpu->Reg[15], ptr) == -1) + // Find the cached instruction cream, otherwise translate it... + auto itr = cpu->instruction_cache.find(cpu->Reg[15]); + if (itr != cpu->instruction_cache.end()) { + ptr = itr->second; + } else { if (InterpreterTranslate(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION) goto END; + } inst_base = (arm_inst *)&inst_buf[ptr]; GOTO_NEXT_INST; @@ -4764,94 +4772,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) { if (inst_cream->Rd == 15) { DEBUG_MSG; } else { - if (inst_cream->cp_num == 15) { - if (CRn == 1 && CRm == 0 && OPCODE_2 == 0) { - CP15_REG(CP15_CONTROL) = RD; - } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 1) { - CP15_REG(CP15_AUXILIARY_CONTROL) = RD; - } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 2) { - CP15_REG(CP15_COPROCESSOR_ACCESS_CONTROL) = RD; - } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 0) { - CP15_REG(CP15_TRANSLATION_BASE_TABLE_0) = RD; - } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 1) { - CP15_REG(CP15_TRANSLATION_BASE_TABLE_1) = RD; - } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 2) { - CP15_REG(CP15_TRANSLATION_BASE_CONTROL) = RD; - } else if (CRn == 3 && CRm == 0 && OPCODE_2 == 0) { - CP15_REG(CP15_DOMAIN_ACCESS_CONTROL) = RD; - } else if(CRn == MMU_CACHE_OPS){ - //LOG_WARNING(Core_ARM11, "cache operations have not implemented."); - } else if(CRn == MMU_TLB_OPS){ - switch (CRm) { - case 5: // ITLB - switch(OPCODE_2) { - case 0: // Invalidate all - LOG_DEBUG(Core_ARM11, "{TLB} [INSN] invalidate all"); - break; - case 1: // Invalidate by MVA - LOG_DEBUG(Core_ARM11, "{TLB} [INSN] invalidate by mva"); - break; - case 2: // Invalidate by asid - LOG_DEBUG(Core_ARM11, "{TLB} [INSN] invalidate by asid"); - break; - default: - break; - } - - break; - case 6: // DTLB - switch(OPCODE_2){ - case 0: // Invalidate all - LOG_DEBUG(Core_ARM11, "{TLB} [DATA] invalidate all"); - break; - case 1: // Invalidate by MVA - LOG_DEBUG(Core_ARM11, "{TLB} [DATA] invalidate by mva"); - break; - case 2: // Invalidate by asid - LOG_DEBUG(Core_ARM11, "{TLB} [DATA] invalidate by asid"); - break; - default: - break; - } - break; - case 7: // UNIFILED TLB - switch(OPCODE_2){ - case 0: // invalidate all - LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate all"); - break; - case 1: // Invalidate by MVA - LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate by mva"); - break; - case 2: // Invalidate by asid - LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate by asid"); - break; - default: - break; - } - break; - default: - break; - } - } else if(CRn == MMU_PID) { - if(OPCODE_2 == 0) { - CP15_REG(CP15_PID) = RD; - } else if(OPCODE_2 == 1) { - CP15_REG(CP15_CONTEXT_ID) = RD; - } else if (OPCODE_2 == 2) { - CP15_REG(CP15_THREAD_UPRW) = RD; - } else if(OPCODE_2 == 3) { - if (InAPrivilegedMode(cpu)) - CP15_REG(CP15_THREAD_URO) = RD; - } else if (OPCODE_2 == 4) { - if (InAPrivilegedMode(cpu)) - CP15_REG(CP15_THREAD_PRW) = RD; - } else { - LOG_ERROR(Core_ARM11, "mmu_mcr wrote UNKNOWN - reg %d", CRn); - } - } else { - LOG_ERROR(Core_ARM11, "mcr CRn=%d, CRm=%d OP2=%d is not implemented", CRn, CRm, OPCODE_2); - } - } + if (inst_cream->cp_num == 15) + WriteCP15Register(cpu, RD, CRn, OPCODE_1, CRm, OPCODE_2); } } cpu->Reg[15] += GET_INST_SIZE(cpu); @@ -4859,7 +4781,24 @@ unsigned InterpreterMainLoop(ARMul_State* state) { FETCH_INST; GOTO_NEXT_INST; } + MCRR_INST: + { + // Stubbed, as the MPCore doesn't have any registers that are accessible + // through this instruction. + if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { + mcrr_inst* const inst_cream = (mcrr_inst*)inst_base->component; + + LOG_ERROR(Core_ARM11, "MCRR executed | Coprocessor: %u, CRm %u, opc1: %u, Rt: %u, Rt2: %u", + inst_cream->cp_num, inst_cream->crm, inst_cream->opcode_1, inst_cream->rt, inst_cream->rt2); + } + + cpu->Reg[15] += GET_INST_SIZE(cpu); + INC_PC(sizeof(mcrr_inst)); + FETCH_INST; + GOTO_NEXT_INST; + } + MLA_INST: { if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { @@ -4926,50 +4865,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) { CITRA_IGNORE_EXIT(-1); goto END; } else { - if (inst_cream->cp_num == 15) { - if(CRn == 0 && OPCODE_2 == 0 && CRm == 0) { - RD = cpu->CP15[CP15(CP15_MAIN_ID)]; - } else if (CRn == 0 && CRm == 0 && OPCODE_2 == 1) { - RD = cpu->CP15[CP15(CP15_CACHE_TYPE)]; - } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 0) { - RD = cpu->CP15[CP15(CP15_CONTROL)]; - } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 1) { - RD = cpu->CP15[CP15(CP15_AUXILIARY_CONTROL)]; - } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 2) { - RD = cpu->CP15[CP15(CP15_COPROCESSOR_ACCESS_CONTROL)]; - } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 0) { - RD = cpu->CP15[CP15(CP15_TRANSLATION_BASE_TABLE_0)]; - } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 1) { - RD = cpu->CP15[CP15(CP15_TRANSLATION_BASE_TABLE_1)]; - } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 2) { - RD = cpu->CP15[CP15(CP15_TRANSLATION_BASE_CONTROL)]; - } else if (CRn == 3 && CRm == 0 && OPCODE_2 == 0) { - RD = cpu->CP15[CP15(CP15_DOMAIN_ACCESS_CONTROL)]; - } else if (CRn == 5 && CRm == 0 && OPCODE_2 == 0) { - RD = cpu->CP15[CP15(CP15_FAULT_STATUS)]; - } else if (CRn == 5 && CRm == 0 && OPCODE_2 == 1) { - RD = cpu->CP15[CP15(CP15_INSTR_FAULT_STATUS)]; - } else if (CRn == 6 && CRm == 0 && OPCODE_2 == 0) { - RD = cpu->CP15[CP15(CP15_FAULT_ADDRESS)]; - } else if (CRn == 13) { - if(OPCODE_2 == 0) { - RD = CP15_REG(CP15_PID); - } else if(OPCODE_2 == 1) { - RD = CP15_REG(CP15_CONTEXT_ID); - } else if (OPCODE_2 == 2) { - RD = CP15_REG(CP15_THREAD_UPRW); - } else if(OPCODE_2 == 3) { - RD = Memory::KERNEL_MEMORY_VADDR; - } else if (OPCODE_2 == 4) { - if (InAPrivilegedMode(cpu)) - RD = CP15_REG(CP15_THREAD_PRW); - } else { - LOG_ERROR(Core_ARM11, "mmu_mrr wrote UNKNOWN - reg %d", CRn); - } - } else { - LOG_ERROR(Core_ARM11, "mrc CRn=%d, CRm=%d, OP2=%d is not implemented", CRn, CRm, OPCODE_2); - } - } + if (inst_cream->cp_num == 15) + RD = ReadCP15Register(cpu, CRn, OPCODE_1, CRm, OPCODE_2); } } cpu->Reg[15] += GET_INST_SIZE(cpu); @@ -4977,7 +4874,24 @@ unsigned InterpreterMainLoop(ARMul_State* state) { FETCH_INST; GOTO_NEXT_INST; } + MRRC_INST: + { + // Stubbed, as the MPCore doesn't have any registers that are accessible + // through this instruction. + if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { + mcrr_inst* const inst_cream = (mcrr_inst*)inst_base->component; + + LOG_ERROR(Core_ARM11, "MRRC executed | Coprocessor: %u, CRm %u, opc1: %u, Rt: %u, Rt2: %u", + inst_cream->cp_num, inst_cream->crm, inst_cream->opcode_1, inst_cream->rt, inst_cream->rt2); + } + + cpu->Reg[15] += GET_INST_SIZE(cpu); + INC_PC(sizeof(mcrr_inst)); + FETCH_INST; + GOTO_NEXT_INST; + } + MRS_INST: { if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { @@ -6379,7 +6293,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) { SWI_INST: { if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { - HLE::CallSVC(Memory::Read32(cpu->Reg[15])); + SVC::CallSVC(Memory::Read32(cpu->Reg[15])); } cpu->Reg[15] += GET_INST_SIZE(cpu); @@ -6637,24 +6551,24 @@ unsigned InterpreterMainLoop(ARMul_State* state) { s16 sum4 = ((rn_val >> 24) & 0xFF) + ((rm_val >> 24) & 0xFF); if (sum1 >= 0x100) - state->Cpsr |= (1 << 16); + cpu->Cpsr |= (1 << 16); else - state->Cpsr &= ~(1 << 16); + cpu->Cpsr &= ~(1 << 16); if (sum2 >= 0x100) - state->Cpsr |= (1 << 17); + cpu->Cpsr |= (1 << 17); else - state->Cpsr &= ~(1 << 17); + cpu->Cpsr &= ~(1 << 17); if (sum3 >= 0x100) - state->Cpsr |= (1 << 18); + cpu->Cpsr |= (1 << 18); else - state->Cpsr &= ~(1 << 18); + cpu->Cpsr &= ~(1 << 18); if (sum4 >= 0x100) - state->Cpsr |= (1 << 19); + cpu->Cpsr |= (1 << 19); else - state->Cpsr &= ~(1 << 19); + cpu->Cpsr &= ~(1 << 19); lo_result = ((sum1 & 0xFF) | (sum2 & 0xFF) << 8); hi_result = ((sum3 & 0xFF) | (sum4 & 0xFF) << 8); @@ -6667,24 +6581,24 @@ unsigned InterpreterMainLoop(ARMul_State* state) { s16 diff4 = ((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF); if (diff1 >= 0) - state->Cpsr |= (1 << 16); + cpu->Cpsr |= (1 << 16); else - state->Cpsr &= ~(1 << 16); + cpu->Cpsr &= ~(1 << 16); if (diff2 >= 0) - state->Cpsr |= (1 << 17); + cpu->Cpsr |= (1 << 17); else - state->Cpsr &= ~(1 << 17); + cpu->Cpsr &= ~(1 << 17); if (diff3 >= 0) - state->Cpsr |= (1 << 18); + cpu->Cpsr |= (1 << 18); else - state->Cpsr &= ~(1 << 18); + cpu->Cpsr &= ~(1 << 18); if (diff4 >= 0) - state->Cpsr |= (1 << 19); + cpu->Cpsr |= (1 << 19); else - state->Cpsr &= ~(1 << 19); + cpu->Cpsr &= ~(1 << 19); lo_result = (diff1 & 0xFF) | ((diff2 & 0xFF) << 8); hi_result = (diff3 & 0xFF) | ((diff4 & 0xFF) << 8); diff --git a/src/core/arm/dyncom/arm_dyncom_run.h b/src/core/arm/dyncom/arm_dyncom_run.h index e17420497..85774c565 100644 --- a/src/core/arm/dyncom/arm_dyncom_run.h +++ b/src/core/arm/dyncom/arm_dyncom_run.h @@ -22,31 +22,36 @@ void switch_mode(ARMul_State* core, uint32_t mode); -/* FIXME, we temporarily think thumb instruction is always 16 bit */ +// Note that for the 3DS, a Thumb instruction will only ever be +// two bytes in size. Thus we don't need to worry about ThumbEE +// or Thumb-2 where instructions can be 4 bytes in length. static inline u32 GET_INST_SIZE(ARMul_State* core) { return core->TFlag? 2 : 4; } /** -* @brief Read R15 and forced R15 to wold align, used address calculation -* -* @param core -* @param Rn -* -* @return -*/ -static inline addr_t CHECK_READ_REG15_WA(ARMul_State* core, int Rn) { - return (Rn == 15)? ((core->Reg[15] & ~0x3) + GET_INST_SIZE(core) * 2) : core->Reg[Rn]; + * Checks if the PC is being read, and if so, word-aligns it. + * Used with address calculations. + * + * @param core The ARM CPU state instance. + * @param Rn The register being read. + * + * @return If the PC is being read, then the word-aligned PC value is returned. + * If the PC is not being read, then the value stored in the register is returned. + */ +static inline u32 CHECK_READ_REG15_WA(ARMul_State* core, int Rn) { + return (Rn == 15) ? ((core->Reg[15] & ~0x3) + GET_INST_SIZE(core) * 2) : core->Reg[Rn]; } /** -* @brief Read R15, used to data processing with pc -* -* @param core -* @param Rn -* -* @return -*/ + * Reads the PC. Used for data processing operations that use the PC. + * + * @param core The ARM CPU state instance. + * @param Rn The register being read. + * + * @return If the PC is being read, then the incremented PC value is returned. + * If the PC is not being read, then the values stored in the register is returned. + */ static inline u32 CHECK_READ_REG15(ARMul_State* core, int Rn) { - return (Rn == 15)? ((core->Reg[15] & ~0x1) + GET_INST_SIZE(core) * 2) : core->Reg[Rn]; + return (Rn == 15) ? ((core->Reg[15] & ~0x1) + GET_INST_SIZE(core) * 2) : core->Reg[Rn]; } diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.cpp b/src/core/arm/dyncom/arm_dyncom_thumb.cpp index e30d515fb..08b5c0b77 100644 --- a/src/core/arm/dyncom/arm_dyncom_thumb.cpp +++ b/src/core/arm/dyncom/arm_dyncom_thumb.cpp @@ -6,14 +6,12 @@ // ARM instruction, and using the existing ARM simulator. #include "core/arm/dyncom/arm_dyncom_thumb.h" -#include "core/arm/skyeye_common/armos.h" -#include "core/arm/skyeye_common/skyeye_defs.h" // Decode a 16bit Thumb instruction. The instruction is in the low 16-bits of the tinstr field, // with the following Thumb instruction held in the high 16-bits. Passing in two Thumb instructions // allows easier simulation of the special dual BL instruction. -tdstate thumb_translate(addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t* inst_size) { +tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { tdstate valid = t_uninitialized; ARMword tinstr = instr; @@ -288,7 +286,7 @@ tdstate thumb_translate(addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t* : 0xE28DDF00) // ADD |(tinstr & 0x007F); // off7 } else if ((tinstr & 0x0F00) == 0x0e00) - *ainstr = 0xEF000000 | SWI_Breakpoint; + *ainstr = 0xEF000000 | 0x180000; // base | BKPT mask else { static const ARMword subset[4] = { 0xE92D0000, // STMDB sp!,{rlist} @@ -320,7 +318,7 @@ tdstate thumb_translate(addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t* *ainstr |= ((tinstr & 0x00FF) << 16); // New breakpoint value. See gdb/arm-tdep.c else if ((tinstr & 0x00FF) == 0xFE) - *ainstr |= SWI_Breakpoint; + *ainstr |= 0x180000; // base |= BKPT mask else *ainstr |= (tinstr & 0x00FF); } else if ((tinstr & 0x0F00) != 0x0E00) diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.h b/src/core/arm/dyncom/arm_dyncom_thumb.h index a1785abb8..8394ff156 100644 --- a/src/core/arm/dyncom/arm_dyncom_thumb.h +++ b/src/core/arm/dyncom/arm_dyncom_thumb.h @@ -35,9 +35,9 @@ enum tdstate { t_uninitialized, }; -tdstate thumb_translate(addr_t addr, u32 instr, u32* ainstr, u32* inst_size); +tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size); -static inline u32 get_thumb_instr(u32 instr, addr_t pc) { +static inline u32 get_thumb_instr(u32 instr, u32 pc) { u32 tinstr; if ((pc & 0x3) != 0) tinstr = instr >> 16; 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 @@ -/* armcopro.c -- co-processor interface: ARM6 Instruction Emulator. - Copyright (C) 1994, 2000 Advanced RISC Machines Ltd. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -#include "core/arm/skyeye_common/armdefs.h" -#include "core/arm/skyeye_common/armemu.h" -#include "core/arm/skyeye_common/vfp/vfp.h" - -// Dummy Co-processors. - -static unsigned int NoCoPro3R(ARMul_State* state, unsigned int a, ARMword b) -{ - return ARMul_CANT; -} - -static unsigned int NoCoPro4R(ARMul_State* state, unsigned int a, ARMword b, ARMword c) -{ - return ARMul_CANT; -} - -static unsigned int NoCoPro4W(ARMul_State* state, unsigned int a, ARMword b, ARMword* c) -{ - return ARMul_CANT; -} - -static unsigned int NoCoPro5R(ARMul_State* state, unsigned int a, ARMword b, ARMword c, ARMword d) -{ - return ARMul_CANT; -} - -static unsigned int NoCoPro5W(ARMul_State* state, unsigned int a, ARMword b, ARMword* c, ARMword* d) -{ - return ARMul_CANT; -} - -// Install co-processor instruction handlers in this routine. -void ARMul_CoProInit(ARMul_State* state) -{ - // Initialise tham all first. - for (unsigned int i = 0; i < 16; i++) - ARMul_CoProDetach(state, i); - - // Install CoPro Instruction handlers here. - // The format is: - // ARMul_CoProAttach (state, CP Number, Init routine, Exit routine - // LDC routine, STC routine, MRC routine, MCR routine, - // CDP routine, Read Reg routine, Write Reg routine). - if (state->is_v6) { - ARMul_CoProAttach(state, 10, VFPInit, NULL, VFPLDC, VFPSTC, - VFPMRC, VFPMCR, VFPMRRC, VFPMCRR, VFPCDP, NULL, NULL); - ARMul_CoProAttach(state, 11, VFPInit, NULL, VFPLDC, VFPSTC, - VFPMRC, VFPMCR, VFPMRRC, VFPMCRR, VFPCDP, NULL, NULL); - - /*ARMul_CoProAttach (state, 15, MMUInit, NULL, NULL, NULL, - MMUMRC, MMUMCR, NULL, NULL, NULL, NULL, NULL);*/ - } - - // No handlers below here. - - // Call all the initialisation routines. - for (unsigned int i = 0; i < 16; i++) { - if (state->CPInit[i]) - (state->CPInit[i]) (state); - } -} - -// Install co-processor finalisation routines in this routine. -void ARMul_CoProExit(ARMul_State * state) -{ - for (unsigned int i = 0; i < 16; i++) - if (state->CPExit[i]) - (state->CPExit[i]) (state); - - // Detach all handlers. - for (unsigned int i = 0; i < 16; i++) - ARMul_CoProDetach(state, i); -} - -// Routines to hook Co-processors into ARMulator. - -void -ARMul_CoProAttach(ARMul_State* state, -unsigned number, -ARMul_CPInits* init, -ARMul_CPExits* exit, -ARMul_LDCs* ldc, -ARMul_STCs* stc, -ARMul_MRCs* mrc, -ARMul_MCRs* mcr, -ARMul_MRRCs* mrrc, -ARMul_MCRRs* mcrr, -ARMul_CDPs* cdp, -ARMul_CPReads* read, ARMul_CPWrites* write) -{ - if (init != NULL) - state->CPInit[number] = init; - if (exit != NULL) - state->CPExit[number] = exit; - if (ldc != NULL) - state->LDC[number] = ldc; - if (stc != NULL) - state->STC[number] = stc; - if (mrc != NULL) - state->MRC[number] = mrc; - if (mcr != NULL) - state->MCR[number] = mcr; - if (mrrc != NULL) - state->MRRC[number] = mrrc; - if (mcrr != NULL) - state->MCRR[number] = mcrr; - if (cdp != NULL) - state->CDP[number] = cdp; - if (read != NULL) - state->CPRead[number] = read; - if (write != NULL) - state->CPWrite[number] = write; -} - -void ARMul_CoProDetach(ARMul_State* state, unsigned number) -{ - ARMul_CoProAttach(state, number, NULL, NULL, - NoCoPro4R, NoCoPro4W, NoCoPro4W, NoCoPro4R, - NoCoPro5W, NoCoPro5R, NoCoPro3R, NULL, NULL); - - state->CPInit[number] = NULL; - state->CPExit[number] = NULL; - state->CPRead[number] = NULL; - state->CPWrite[number] = NULL; -} diff --git a/src/core/arm/interpreter/arminit.cpp b/src/core/arm/interpreter/arminit.cpp index 4ac827e0a..680a94a39 100644 --- a/src/core/arm/interpreter/arminit.cpp +++ b/src/core/arm/interpreter/arminit.cpp @@ -18,31 +18,16 @@ #include <cstring> #include "core/arm/skyeye_common/armdefs.h" #include "core/arm/skyeye_common/armemu.h" +#include "core/arm/skyeye_common/vfp/vfp.h" /***************************************************************************\ * Returns a new instantiation of the ARMulator's state * \***************************************************************************/ ARMul_State* ARMul_NewState(ARMul_State* state) { - memset(state, 0, sizeof(ARMul_State)); - state->Emulate = RUN; - for (unsigned int i = 0; i < 16; i++) { - state->Reg[i] = 0; - for (unsigned int j = 0; j < 7; j++) - state->RegBank[j][i] = 0; - } - for (unsigned int i = 0; i < 7; i++) - state->Spsr[i] = 0; - state->Mode = USER32MODE; - state->VectorCatch = 0; - state->Aborted = false; - state->Reseted = false; - state->Inted = 3; - state->LastInted = 3; - state->lateabtSig = HIGH; state->bigendSig = LOW; @@ -55,15 +40,69 @@ ARMul_State* ARMul_NewState(ARMul_State* state) void ARMul_SelectProcessor(ARMul_State* state, unsigned properties) { - state->is_v4 = (properties & (ARM_v4_Prop | ARM_v5_Prop)) != 0; - state->is_v5 = (properties & ARM_v5_Prop) != 0; - state->is_v5e = (properties & ARM_v5e_Prop) != 0; - state->is_v6 = (properties & ARM_v6_Prop) != 0; - state->is_v7 = (properties & ARM_v7_Prop) != 0; - - // Only initialse the coprocessor support once we - // know what kind of chip we are dealing with. - ARMul_CoProInit(state); + state->is_v4 = (properties & (ARM_v4_Prop | ARM_v5_Prop)) != 0; + state->is_v5 = (properties & ARM_v5_Prop) != 0; + state->is_v5e = (properties & ARM_v5e_Prop) != 0; + state->is_v6 = (properties & ARM_v6_Prop) != 0; + state->is_v7 = (properties & ARM_v7_Prop) != 0; +} + +// Resets certain MPCore CP15 values to their ARM-defined reset values. +static void ResetMPCoreCP15Registers(ARMul_State* cpu) +{ + // c0 + cpu->CP15[CP15_MAIN_ID] = 0x410FB024; + cpu->CP15[CP15_TLB_TYPE] = 0x00000800; + cpu->CP15[CP15_PROCESSOR_FEATURE_0] = 0x00000111; + cpu->CP15[CP15_PROCESSOR_FEATURE_1] = 0x00000001; + cpu->CP15[CP15_DEBUG_FEATURE_0] = 0x00000002; + cpu->CP15[CP15_MEMORY_MODEL_FEATURE_0] = 0x01100103; + cpu->CP15[CP15_MEMORY_MODEL_FEATURE_1] = 0x10020302; + cpu->CP15[CP15_MEMORY_MODEL_FEATURE_2] = 0x01222000; + cpu->CP15[CP15_MEMORY_MODEL_FEATURE_3] = 0x00000000; + cpu->CP15[CP15_ISA_FEATURE_0] = 0x00100011; + cpu->CP15[CP15_ISA_FEATURE_1] = 0x12002111; + cpu->CP15[CP15_ISA_FEATURE_2] = 0x11221011; + cpu->CP15[CP15_ISA_FEATURE_3] = 0x01102131; + cpu->CP15[CP15_ISA_FEATURE_4] = 0x00000141; + + // c1 + cpu->CP15[CP15_CONTROL] = 0x00054078; + cpu->CP15[CP15_AUXILIARY_CONTROL] = 0x0000000F; + cpu->CP15[CP15_COPROCESSOR_ACCESS_CONTROL] = 0x00000000; + + // c2 + cpu->CP15[CP15_TRANSLATION_BASE_TABLE_0] = 0x00000000; + cpu->CP15[CP15_TRANSLATION_BASE_TABLE_1] = 0x00000000; + cpu->CP15[CP15_TRANSLATION_BASE_CONTROL] = 0x00000000; + + // c3 + cpu->CP15[CP15_DOMAIN_ACCESS_CONTROL] = 0x00000000; + + // c7 + cpu->CP15[CP15_PHYS_ADDRESS] = 0x00000000; + + // c9 + cpu->CP15[CP15_DATA_CACHE_LOCKDOWN] = 0xFFFFFFF0; + + // c10 + cpu->CP15[CP15_TLB_LOCKDOWN] = 0x00000000; + cpu->CP15[CP15_PRIMARY_REGION_REMAP] = 0x00098AA4; + cpu->CP15[CP15_NORMAL_REGION_REMAP] = 0x44E048E0; + + // c13 + cpu->CP15[CP15_PID] = 0x00000000; + cpu->CP15[CP15_CONTEXT_ID] = 0x00000000; + cpu->CP15[CP15_THREAD_UPRW] = 0x00000000; + cpu->CP15[CP15_THREAD_URO] = 0x00000000; + cpu->CP15[CP15_THREAD_PRW] = 0x00000000; + + // c15 + cpu->CP15[CP15_PERFORMANCE_MONITOR_CONTROL] = 0x00000000; + cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS] = 0x00000000; + cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS] = 0x00000000; + cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE] = 0x00000000; + cpu->CP15[CP15_TLB_DEBUG_CONTROL] = 0x00000000; } /***************************************************************************\ @@ -71,24 +110,20 @@ void ARMul_SelectProcessor(ARMul_State* state, unsigned properties) \***************************************************************************/ void ARMul_Reset(ARMul_State* state) { - state->NextInstr = 0; + VFPInit(state); state->Reg[15] = 0; state->Cpsr = INTBITS | SVC32MODE; state->Mode = SVC32MODE; - state->Bank = SVCBANK; - FLUSHPIPE; - state->EndCondition = 0; - state->ErrorCode = 0; + ResetMPCoreCP15Registers(state); state->NresetSig = HIGH; state->NfiqSig = HIGH; state->NirqSig = HIGH; state->NtransSig = (state->Mode & 3) ? HIGH : LOW; state->abortSig = LOW; - state->AbortAddr = 1; state->NumInstrs = 0; } diff --git a/src/core/arm/interpreter/armsupp.cpp b/src/core/arm/interpreter/armsupp.cpp index aca2bfbbd..1b078dc71 100644 --- a/src/core/arm/interpreter/armsupp.cpp +++ b/src/core/arm/interpreter/armsupp.cpp @@ -15,7 +15,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "common/logging/log.h" + +#include "core/mem_map.h" #include "core/arm/skyeye_common/armdefs.h" +#include "core/arm/skyeye_common/arm_regformat.h" // Unsigned sum of absolute difference u8 ARMul_UnsignedAbsoluteDifference(u8 left, u8 right) @@ -207,3 +211,427 @@ bool InBigEndianMode(ARMul_State* cpu) { return (cpu->Cpsr & (1 << 9)) != 0; } + +// Whether or not the given CPU is in a mode other than user mode. +bool InAPrivilegedMode(ARMul_State* cpu) +{ + return (cpu->Mode != USER32MODE); +} + +// Reads from the CP15 registers. Used with implementation of the MRC instruction. +// Note that since the 3DS does not have the hypervisor extensions, these registers +// are not implemented. +u32 ReadCP15Register(ARMul_State* cpu, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2) +{ + // Unprivileged registers + if (crn == 13 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 2) + return cpu->CP15[CP15_THREAD_UPRW]; + + if (opcode_2 == 3) + return cpu->CP15[CP15_THREAD_URO]; + } + + if (InAPrivilegedMode(cpu)) + { + if (crn == 0 && opcode_1 == 0) + { + if (crm == 0) + { + if (opcode_2 == 0) + return cpu->CP15[CP15_MAIN_ID]; + + if (opcode_2 == 1) + return cpu->CP15[CP15_CACHE_TYPE]; + + if (opcode_2 == 3) + return cpu->CP15[CP15_TLB_TYPE]; + + if (opcode_2 == 5) + return cpu->CP15[CP15_CPU_ID]; + } + else if (crm == 1) + { + if (opcode_2 == 0) + return cpu->CP15[CP15_PROCESSOR_FEATURE_0]; + + if (opcode_2 == 1) + return cpu->CP15[CP15_PROCESSOR_FEATURE_1]; + + if (opcode_2 == 2) + return cpu->CP15[CP15_DEBUG_FEATURE_0]; + + if (opcode_2 == 4) + return cpu->CP15[CP15_MEMORY_MODEL_FEATURE_0]; + + if (opcode_2 == 5) + return cpu->CP15[CP15_MEMORY_MODEL_FEATURE_1]; + + if (opcode_2 == 6) + return cpu->CP15[CP15_MEMORY_MODEL_FEATURE_2]; + + if (opcode_2 == 7) + return cpu->CP15[CP15_MEMORY_MODEL_FEATURE_3]; + } + else if (crm == 2) + { + if (opcode_2 == 0) + return cpu->CP15[CP15_ISA_FEATURE_0]; + + if (opcode_2 == 1) + return cpu->CP15[CP15_ISA_FEATURE_1]; + + if (opcode_2 == 2) + return cpu->CP15[CP15_ISA_FEATURE_2]; + + if (opcode_2 == 3) + return cpu->CP15[CP15_ISA_FEATURE_3]; + + if (opcode_2 == 4) + return cpu->CP15[CP15_ISA_FEATURE_4]; + } + } + + if (crn == 1 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + return cpu->CP15[CP15_CONTROL]; + + if (opcode_2 == 1) + return cpu->CP15[CP15_AUXILIARY_CONTROL]; + + if (opcode_2 == 2) + return cpu->CP15[CP15_COPROCESSOR_ACCESS_CONTROL]; + } + + if (crn == 2 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + return cpu->CP15[CP15_TRANSLATION_BASE_TABLE_0]; + + if (opcode_2 == 1) + return cpu->CP15[CP15_TRANSLATION_BASE_TABLE_1]; + + if (opcode_2 == 2) + return cpu->CP15[CP15_TRANSLATION_BASE_CONTROL]; + } + + if (crn == 3 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) + return cpu->CP15[CP15_DOMAIN_ACCESS_CONTROL]; + + if (crn == 5 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + return cpu->CP15[CP15_FAULT_STATUS]; + + if (opcode_2 == 1) + return cpu->CP15[CP15_INSTR_FAULT_STATUS]; + } + + if (crn == 6 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + return cpu->CP15[CP15_FAULT_ADDRESS]; + + if (opcode_2 == 1) + return cpu->CP15[CP15_WFAR]; + } + + if (crn == 7 && opcode_1 == 0 && crm == 4 && opcode_2 == 0) + return cpu->CP15[CP15_PHYS_ADDRESS]; + + if (crn == 9 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) + return cpu->CP15[CP15_DATA_CACHE_LOCKDOWN]; + + if (crn == 10 && opcode_1 == 0) + { + if (crm == 0 && opcode_2 == 0) + return cpu->CP15[CP15_TLB_LOCKDOWN]; + + if (crm == 2) + { + if (opcode_2 == 0) + return cpu->CP15[CP15_PRIMARY_REGION_REMAP]; + + if (opcode_2 == 1) + return cpu->CP15[CP15_NORMAL_REGION_REMAP]; + } + } + + if (crn == 13 && crm == 0) + { + if (opcode_2 == 0) + return cpu->CP15[CP15_PID]; + + if (opcode_2 == 1) + return cpu->CP15[CP15_CONTEXT_ID]; + + if (opcode_2 == 4) + return cpu->CP15[CP15_THREAD_PRW]; + } + + if (crn == 15) + { + if (opcode_1 == 0 && crm == 12) + { + if (opcode_2 == 0) + return cpu->CP15[CP15_PERFORMANCE_MONITOR_CONTROL]; + + if (opcode_2 == 1) + return cpu->CP15[CP15_CYCLE_COUNTER]; + + if (opcode_2 == 2) + return cpu->CP15[CP15_COUNT_0]; + + if (opcode_2 == 3) + return cpu->CP15[CP15_COUNT_1]; + } + + if (opcode_1 == 5 && opcode_2 == 2) + { + if (crm == 5) + return cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS]; + + if (crm == 6) + return cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS]; + + if (crm == 7) + return cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE]; + } + + if (opcode_1 == 7 && crm == 1 && opcode_2 == 0) + return cpu->CP15[CP15_TLB_DEBUG_CONTROL]; + } + } + + LOG_ERROR(Core_ARM11, "MRC CRn=%u, CRm=%u, OP1=%u OP2=%u is not implemented. Returning zero.", crn, crm, opcode_1, opcode_2); + return 0; +} + +// Write to the CP15 registers. Used with implementation of the MCR instruction. +// Note that since the 3DS does not have the hypervisor extensions, these registers +// are not implemented. +void WriteCP15Register(ARMul_State* cpu, u32 value, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2) +{ + if (InAPrivilegedMode(cpu)) + { + if (crn == 1 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + cpu->CP15[CP15_CONTROL] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_AUXILIARY_CONTROL] = value; + else if (opcode_2 == 2) + cpu->CP15[CP15_COPROCESSOR_ACCESS_CONTROL] = value; + } + else if (crn == 2 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + cpu->CP15[CP15_TRANSLATION_BASE_TABLE_0] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_TRANSLATION_BASE_TABLE_1] = value; + else if (opcode_2 == 2) + cpu->CP15[CP15_TRANSLATION_BASE_CONTROL] = value; + } + else if (crn == 3 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) + { + cpu->CP15[CP15_DOMAIN_ACCESS_CONTROL] = value; + } + else if (crn == 5 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + cpu->CP15[CP15_FAULT_STATUS] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_INSTR_FAULT_STATUS] = value; + } + else if (crn == 6 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + cpu->CP15[CP15_FAULT_ADDRESS] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_WFAR] = value; + } + else if (crn == 7 && opcode_1 == 0) + { + if (crm == 0 && opcode_2 == 4) + { + cpu->CP15[CP15_WAIT_FOR_INTERRUPT] = value; + } + else if (crm == 4 && opcode_2 == 0) + { + // NOTE: Not entirely accurate. This should do permission checks. + cpu->CP15[CP15_PHYS_ADDRESS] = Memory::VirtualToPhysicalAddress(value); + } + else if (crm == 5) + { + if (opcode_2 == 0) + cpu->CP15[CP15_INVALIDATE_INSTR_CACHE] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_INVALIDATE_INSTR_CACHE_USING_MVA] = value; + else if (opcode_2 == 2) + cpu->CP15[CP15_INVALIDATE_INSTR_CACHE_USING_INDEX] = value; + else if (opcode_2 == 6) + cpu->CP15[CP15_FLUSH_BRANCH_TARGET_CACHE] = value; + else if (opcode_2 == 7) + cpu->CP15[CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY] = value; + } + else if (crm == 6) + { + if (opcode_2 == 0) + cpu->CP15[CP15_INVALIDATE_DATA_CACHE] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA] = value; + else if (opcode_2 == 2) + cpu->CP15[CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX] = value; + } + else if (crm == 7 && opcode_2 == 0) + { + cpu->CP15[CP15_INVALIDATE_DATA_AND_INSTR_CACHE] = value; + } + else if (crm == 10) + { + if (opcode_2 == 0) + cpu->CP15[CP15_CLEAN_DATA_CACHE] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_CLEAN_DATA_CACHE_LINE_USING_MVA] = value; + else if (opcode_2 == 2) + cpu->CP15[CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX] = value; + } + else if (crm == 14) + { + if (opcode_2 == 0) + cpu->CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA] = value; + else if (opcode_2 == 2) + cpu->CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX] = value; + } + } + else if (crn == 8 && opcode_1 == 0) + { + LOG_WARNING(Core_ARM11, "TLB operations not fully implemented."); + + if (crm == 5) + { + if (opcode_2 == 0) + cpu->CP15[CP15_INVALIDATE_ITLB] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_INVALIDATE_ITLB_SINGLE_ENTRY] = value; + else if (opcode_2 == 2) + cpu->CP15[CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH] = value; + else if (opcode_2 == 3) + cpu->CP15[CP15_INVALIDATE_ITLB_ENTRY_ON_MVA] = value; + } + else if (crm == 6) + { + if (opcode_2 == 0) + cpu->CP15[CP15_INVALIDATE_DTLB] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_INVALIDATE_DTLB_SINGLE_ENTRY] = value; + else if (opcode_2 == 2) + cpu->CP15[CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH] = value; + else if (opcode_2 == 3) + cpu->CP15[CP15_INVALIDATE_DTLB_ENTRY_ON_MVA] = value; + } + else if (crm == 7) + { + if (opcode_2 == 0) + cpu->CP15[CP15_INVALIDATE_UTLB] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_INVALIDATE_UTLB_SINGLE_ENTRY] = value; + else if (opcode_2 == 2) + cpu->CP15[CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH] = value; + else if (opcode_2 == 3) + cpu->CP15[CP15_INVALIDATE_UTLB_ENTRY_ON_MVA] = value; + } + } + else if (crn == 9 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) + { + cpu->CP15[CP15_DATA_CACHE_LOCKDOWN] = value; + } + else if (crn == 10 && opcode_1 == 0) + { + if (crm == 0 && opcode_2 == 0) + { + cpu->CP15[CP15_TLB_LOCKDOWN] = value; + } + else if (crm == 2) + { + if (opcode_2 == 0) + cpu->CP15[CP15_PRIMARY_REGION_REMAP] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_NORMAL_REGION_REMAP] = value; + } + } + else if (crn == 13 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + cpu->CP15[CP15_PID] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_CONTEXT_ID] = value; + else if (opcode_2 == 3) + cpu->CP15[CP15_THREAD_URO] = value; + else if (opcode_2 == 4) + cpu->CP15[CP15_THREAD_PRW] = value; + } + else if (crn == 15) + { + if (opcode_1 == 0 && crm == 12) + { + if (opcode_2 == 0) + cpu->CP15[CP15_PERFORMANCE_MONITOR_CONTROL] = value; + else if (opcode_2 == 1) + cpu->CP15[CP15_CYCLE_COUNTER] = value; + else if (opcode_2 == 2) + cpu->CP15[CP15_COUNT_0] = value; + else if (opcode_2 == 3) + cpu->CP15[CP15_COUNT_1] = value; + } + else if (opcode_1 == 5) + { + if (crm == 4) + { + if (opcode_2 == 2) + cpu->CP15[CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY] = value; + else if (opcode_2 == 4) + cpu->CP15[CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY] = value; + } + else if (crm == 5 && opcode_2 == 2) + { + cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS] = value; + } + else if (crm == 6 && opcode_2 == 2) + { + cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS] = value; + } + else if (crm == 7 && opcode_2 == 2) + { + cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE] = value; + } + } + else if (opcode_1 == 7 && crm == 1 && opcode_2 == 0) + { + cpu->CP15[CP15_TLB_DEBUG_CONTROL] = value; + } + } + } + + // Unprivileged registers + if (crn == 7 && opcode_1 == 0 && crm == 5 && opcode_2 == 4) + { + cpu->CP15[CP15_FLUSH_PREFETCH_BUFFER] = value; + } + else if (crn == 7 && opcode_1 == 0 && crm == 10) + { + if (opcode_2 == 4) + cpu->CP15[CP15_DATA_SYNC_BARRIER] = value; + else if (opcode_2 == 5) + cpu->CP15[CP15_DATA_MEMORY_BARRIER] = value; + + } + else if (crn == 13 && opcode_1 == 0 && crm == 0 && opcode_2 == 2) + { + cpu->CP15[CP15_THREAD_UPRW] = value; + } +} diff --git a/src/core/arm/skyeye_common/arm_regformat.h b/src/core/arm/skyeye_common/arm_regformat.h index 5be3a561f..6c89774eb 100644 --- a/src/core/arm/skyeye_common/arm_regformat.h +++ b/src/core/arm/skyeye_common/arm_regformat.h @@ -50,55 +50,134 @@ enum { EXCLUSIVE_TAG, EXCLUSIVE_STATE, EXCLUSIVE_RESULT, - CP15_BASE, - CP15_C0 = CP15_BASE, - CP15_C0_C0 = CP15_C0, - CP15_MAIN_ID = CP15_C0_C0, + + MAX_REG_NUM, +}; + +// VFP system registers +enum { + VFP_FPSID, + VFP_FPSCR, + VFP_FPEXC, + + // Not an actual register. + // All VFP system registers should be defined above this. + VFP_SYSTEM_REGISTER_COUNT +}; + +enum CP15Register { + // c0 - Information registers + CP15_MAIN_ID, CP15_CACHE_TYPE, CP15_TCM_STATUS, CP15_TLB_TYPE, - CP15_C0_C1, - CP15_PROCESSOR_FEATURE_0 = CP15_C0_C1, + CP15_CPU_ID, + CP15_PROCESSOR_FEATURE_0, CP15_PROCESSOR_FEATURE_1, CP15_DEBUG_FEATURE_0, CP15_AUXILIARY_FEATURE_0, - CP15_C1_C0, - CP15_CONTROL = CP15_C1_C0, + CP15_MEMORY_MODEL_FEATURE_0, + CP15_MEMORY_MODEL_FEATURE_1, + CP15_MEMORY_MODEL_FEATURE_2, + CP15_MEMORY_MODEL_FEATURE_3, + CP15_ISA_FEATURE_0, + CP15_ISA_FEATURE_1, + CP15_ISA_FEATURE_2, + CP15_ISA_FEATURE_3, + CP15_ISA_FEATURE_4, + + // c1 - Control registers + CP15_CONTROL, CP15_AUXILIARY_CONTROL, CP15_COPROCESSOR_ACCESS_CONTROL, - CP15_C2, - CP15_C2_C0 = CP15_C2, - CP15_TRANSLATION_BASE = CP15_C2_C0, - CP15_TRANSLATION_BASE_TABLE_0 = CP15_TRANSLATION_BASE, + + // c2 - Translation table registers + CP15_TRANSLATION_BASE_TABLE_0, CP15_TRANSLATION_BASE_TABLE_1, CP15_TRANSLATION_BASE_CONTROL, CP15_DOMAIN_ACCESS_CONTROL, CP15_RESERVED, - /* Fault status */ + + // c5 - Fault status registers CP15_FAULT_STATUS, CP15_INSTR_FAULT_STATUS, CP15_COMBINED_DATA_FSR = CP15_FAULT_STATUS, CP15_INST_FSR, - /* Fault Address register */ + + // c6 - Fault Address registers CP15_FAULT_ADDRESS, CP15_COMBINED_DATA_FAR = CP15_FAULT_ADDRESS, CP15_WFAR, CP15_IFAR, + + // c7 - Cache operation registers + CP15_WAIT_FOR_INTERRUPT, + CP15_PHYS_ADDRESS, + CP15_INVALIDATE_INSTR_CACHE, + CP15_INVALIDATE_INSTR_CACHE_USING_MVA, + CP15_INVALIDATE_INSTR_CACHE_USING_INDEX, + CP15_FLUSH_PREFETCH_BUFFER, + CP15_FLUSH_BRANCH_TARGET_CACHE, + CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY, + CP15_INVALIDATE_DATA_CACHE, + CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA, + CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX, + CP15_INVALIDATE_DATA_AND_INSTR_CACHE, + CP15_CLEAN_DATA_CACHE, + CP15_CLEAN_DATA_CACHE_LINE_USING_MVA, + CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX, + CP15_DATA_SYNC_BARRIER, + CP15_DATA_MEMORY_BARRIER, + CP15_CLEAN_AND_INVALIDATE_DATA_CACHE, + CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA, + CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX, + + // c8 - TLB operations + CP15_INVALIDATE_ITLB, + CP15_INVALIDATE_ITLB_SINGLE_ENTRY, + CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH, + CP15_INVALIDATE_ITLB_ENTRY_ON_MVA, + CP15_INVALIDATE_DTLB, + CP15_INVALIDATE_DTLB_SINGLE_ENTRY, + CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH, + CP15_INVALIDATE_DTLB_ENTRY_ON_MVA, + CP15_INVALIDATE_UTLB, + CP15_INVALIDATE_UTLB_SINGLE_ENTRY, + CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH, + CP15_INVALIDATE_UTLB_ENTRY_ON_MVA, + + // c9 - Data cache lockdown register + CP15_DATA_CACHE_LOCKDOWN, + + // c10 - TLB/Memory map registers + CP15_TLB_LOCKDOWN, + CP15_PRIMARY_REGION_REMAP, + CP15_NORMAL_REGION_REMAP, + + // c13 - Thread related registers CP15_PID, CP15_CONTEXT_ID, CP15_THREAD_UPRW, // Thread ID register - User/Privileged Read/Write CP15_THREAD_URO, // Thread ID register - User Read Only (Privileged R/W) CP15_THREAD_PRW, // Thread ID register - Privileged R/W only. - CP15_TLB_FAULT_ADDR, /* defined by SkyEye */ - CP15_TLB_FAULT_STATUS, /* defined by SkyEye */ - /* VFP registers */ - VFP_BASE, - VFP_FPSID = VFP_BASE, - VFP_FPSCR, - VFP_FPEXC, - MAX_REG_NUM, -}; + // c15 - Performance and TLB lockdown registers + CP15_PERFORMANCE_MONITOR_CONTROL, + CP15_CYCLE_COUNTER, + CP15_COUNT_0, + CP15_COUNT_1, + CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY, + CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY, + CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS, + CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS, + CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE, + CP15_TLB_DEBUG_CONTROL, + + // Skyeye defined + CP15_TLB_FAULT_ADDR, + CP15_TLB_FAULT_STATUS, -#define CP15(idx) (idx - CP15_BASE) -#define VFP_OFFSET(x) (x - VFP_BASE) + // Not an actual register. + // All registers should be defined above this. + CP15_REGISTER_COUNT, +}; diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h index c1a19fecc..470f9508d 100644 --- a/src/core/arm/skyeye_common/armdefs.h +++ b/src/core/arm/skyeye_common/armdefs.h @@ -17,9 +17,10 @@ #pragma once +#include <unordered_map> + #include "common/common_types.h" #include "core/arm/skyeye_common/arm_regformat.h" -#include "core/arm/skyeye_common/skyeye_defs.h" #define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1)) #define BIT(s, n) ((s >> (n)) & 1) @@ -53,26 +54,11 @@ typedef u64 ARMdword; // must be 64 bits wide typedef u32 ARMword; // must be 32 bits wide typedef u16 ARMhword; // must be 16 bits wide typedef u8 ARMbyte; // must be 8 bits wide -typedef struct ARMul_State ARMul_State; - -typedef unsigned ARMul_CPInits(ARMul_State* state); -typedef unsigned ARMul_CPExits(ARMul_State* state); -typedef unsigned ARMul_LDCs(ARMul_State* state, unsigned type, ARMword instr, ARMword value); -typedef unsigned ARMul_STCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value); -typedef unsigned ARMul_MRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value); -typedef unsigned ARMul_MCRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value); -typedef unsigned ARMul_MRRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2); -typedef unsigned ARMul_MCRRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2); -typedef unsigned ARMul_CDPs(ARMul_State* state, unsigned type, ARMword instr); -typedef unsigned ARMul_CPReads(ARMul_State* state, unsigned reg, ARMword* value); -typedef unsigned ARMul_CPWrites(ARMul_State* state, unsigned reg, ARMword value); #define VFP_REG_NUM 64 struct ARMul_State { ARMword Emulate; // To start and stop emulation - unsigned EndCondition; // Reason for stopping - unsigned ErrorCode; // Type of illegal instruction // Order of the following register should not be modified ARMword Reg[16]; // The current register file @@ -91,15 +77,15 @@ struct ARMul_State ARMword exclusive_tag; // The address for which the local monitor is in exclusive access mode ARMword exclusive_state; ARMword exclusive_result; - ARMword CP15[VFP_BASE - CP15_BASE]; - ARMword VFP[3]; // FPSID, FPSCR, and FPEXC + ARMword CP15[CP15_REGISTER_COUNT]; + + // FPSID, FPSCR, and FPEXC + ARMword VFP[VFP_SYSTEM_REGISTER_COUNT]; // VFPv2 and VFPv3-D16 has 16 doubleword registers (D0-D16 or S0-S31). // VFPv3-D32/ASIMD may have up to 32 doubleword registers (D0-D31), // and only 32 singleword registers are accessible (S0-S31). ARMword ExtReg[VFP_REG_NUM]; /* ---- End of the ordered registers ---- */ - - ARMword RegBank[7][16]; // all the registers ARMword NFlag, ZFlag, CFlag, VFlag, IFFlags; // Dummy flags for speed unsigned int shifter_carry_out; @@ -112,24 +98,7 @@ struct ARMul_State unsigned long long NumInstrs; // The number of instructions executed unsigned NumInstrsToExecute; - unsigned NextInstr; - unsigned VectorCatch; // Caught exception mask - - ARMul_CPInits* CPInit[16]; // Coprocessor initialisers - ARMul_CPExits* CPExit[16]; // Coprocessor finalisers - ARMul_LDCs* LDC[16]; // LDC instruction - ARMul_STCs* STC[16]; // STC instruction - ARMul_MRCs* MRC[16]; // MRC instruction - ARMul_MCRs* MCR[16]; // MCR instruction - ARMul_MRRCs* MRRC[16]; // MRRC instruction - ARMul_MCRRs* MCRR[16]; // MCRR instruction - ARMul_CDPs* CDP[16]; // CDP instruction - ARMul_CPReads* CPRead[16]; // Read CP register - ARMul_CPWrites* CPWrite[16]; // Write CP register - unsigned char* CPData[16]; // Coprocessor data - unsigned char const* CPRegWords[16]; // Map of coprocessor register sizes - - unsigned NresetSig; // Reset the processor + unsigned NresetSig; // Reset the processor unsigned NfiqSig; unsigned NirqSig; @@ -171,13 +140,6 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model) */ unsigned lateabtSig; - bool Aborted; // Sticky flag for aborts - bool Reseted; // Sticky flag for Reset - ARMword Inted, LastInted; // Sticky flags for interrupts - ARMword Base; // Extra hand for base writeback - ARMword AbortAddr; // To keep track of Prefetch aborts - ARMword Vector; // Synthesize aborts in cycle modes - // For differentiating ARM core emulaiton. bool is_v4; // Are we emulating a v4 architecture (or higher)? bool is_v5; // Are we emulating a v5 architecture? @@ -189,16 +151,9 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model) // 0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model int abort_model; - // Added by ksh in 2005-10-1 - cpu_config_t* cpu; - - u32 CurrInstr; - u32 last_pc; // The last PC executed - u32 last_instr; // The last instruction executed - u32 WriteAddr[17]; - u32 WriteData[17]; - u32 WritePc[17]; - u32 CurrWrite; + // TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per + // process for our purposes), not per ARMul_State (which tracks CPU core state). + std::unordered_map<u32, int> instruction_cache; }; /***************************************************************************\ @@ -284,34 +239,6 @@ enum { ARMul_INC = 3 }; -enum { - ARMul_CP13_R0_FIQ = 0x1, - ARMul_CP13_R0_IRQ = 0x2, - ARMul_CP13_R8_PMUS = 0x1, - - ARMul_CP14_R0_ENABLE = 0x0001, - ARMul_CP14_R0_CLKRST = 0x0004, - ARMul_CP14_R0_CCD = 0x0008, - ARMul_CP14_R0_INTEN0 = 0x0010, - ARMul_CP14_R0_INTEN1 = 0x0020, - ARMul_CP14_R0_INTEN2 = 0x0040, - ARMul_CP14_R0_FLAG0 = 0x0100, - ARMul_CP14_R0_FLAG1 = 0x0200, - ARMul_CP14_R0_FLAG2 = 0x0400, - ARMul_CP14_R10_MOE_IB = 0x0004, - ARMul_CP14_R10_MOE_DB = 0x0008, - ARMul_CP14_R10_MOE_BT = 0x000c, - ARMul_CP15_R1_ENDIAN = 0x0080, - ARMul_CP15_R1_ALIGN = 0x0002, - ARMul_CP15_R5_X = 0x0400, - ARMul_CP15_R5_ST_ALIGN = 0x0001, - ARMul_CP15_R5_IMPRE = 0x0406, - ARMul_CP15_R5_MMU_EXCPT = 0x0400, - ARMul_CP15_DBCON_M = 0x0100, - ARMul_CP15_DBCON_E1 = 0x000c, - ARMul_CP15_DBCON_E0 = 0x0003 -}; - /***************************************************************************\ * Definitons of things in the host environment * \***************************************************************************/ @@ -357,3 +284,7 @@ extern u32 ARMul_SignedSatQ(s32, u8, bool*); extern u32 ARMul_UnsignedSatQ(s32, u8, bool*); extern bool InBigEndianMode(ARMul_State*); +extern bool InAPrivilegedMode(ARMul_State*); + +extern u32 ReadCP15Register(ARMul_State* cpu, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2); +extern void WriteCP15Register(ARMul_State* cpu, u32 value, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2); 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 { INTBITS = 0x1C0, }; -// Different ways to start the next instruction. -enum { - SEQ = 0, - NONSEQ = 1, - PCINCEDSEQ = 2, - PCINCEDNONSEQ = 3, - PRIMEPIPE = 4, - RESUME = 8 -}; - // Values for Emulate. enum { STOP = 0, // Stop @@ -55,14 +45,3 @@ enum { ONCE = 2, // Execute just one interation RUN = 3 // Continuous execution }; - -#define FLUSHPIPE state->NextInstr |= PRIMEPIPE - -// Coprocessor support functions. -extern void ARMul_CoProInit(ARMul_State*); -extern void ARMul_CoProExit(ARMul_State*); -extern void ARMul_CoProAttach(ARMul_State*, unsigned, ARMul_CPInits*, - ARMul_CPExits*, ARMul_LDCs*, ARMul_STCs*, - ARMul_MRCs*, ARMul_MCRs*, ARMul_MRRCs*, ARMul_MCRRs*, - ARMul_CDPs*, ARMul_CPReads*, ARMul_CPWrites*); -extern void ARMul_CoProDetach(ARMul_State*, unsigned); diff --git a/src/core/arm/skyeye_common/armmmu.h b/src/core/arm/skyeye_common/armmmu.h index 22e564c3d..c67d7209b 100644 --- a/src/core/arm/skyeye_common/armmmu.h +++ b/src/core/arm/skyeye_common/armmmu.h @@ -20,7 +20,9 @@ #pragma once -#include "core/mem_map.h" +#include "common/swap.h" + +#include "core/memory.h" #include "core/arm/skyeye_common/armdefs.h" // Register numbers in the MMU diff --git a/src/core/arm/skyeye_common/armos.h b/src/core/arm/skyeye_common/armos.h deleted file mode 100644 index 1217a728b..000000000 --- a/src/core/arm/skyeye_common/armos.h +++ /dev/null @@ -1,54 +0,0 @@ -/* armos.h -- ARMulator OS definitions: ARM6 Instruction Emulator. - Copyright (C) 1994 Advanced RISC Machines Ltd. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -// -// SWI Numbers -// - -#define SWI_Syscall 0x0 -#define SWI_Exit 0x1 -#define SWI_Read 0x3 -#define SWI_Write 0x4 -#define SWI_Open 0x5 -#define SWI_Close 0x6 -#define SWI_Seek 0x13 -#define SWI_Rename 0x26 -#define SWI_Break 0x11 - -#define SWI_Times 0x2b -#define SWI_Brk 0x2d - -#define SWI_Mmap 0x5a -#define SWI_Munmap 0x5b -#define SWI_Mmap2 0xc0 - -#define SWI_GetUID32 0xc7 -#define SWI_GetGID32 0xc8 -#define SWI_GetEUID32 0xc9 -#define SWI_GetEGID32 0xca - -#define SWI_ExitGroup 0xf8 - -#define SWI_Uname 0x7a -#define SWI_Fcntl 0xdd -#define SWI_Fstat64 0xc5 -#define SWI_Gettimeofday 0x4e -#define SWI_Set_tls 0xf0005 - -#define SWI_Breakpoint 0x180000 /* see gdb's tm-arm.h */ - diff --git a/src/core/arm/skyeye_common/skyeye_defs.h b/src/core/arm/skyeye_common/skyeye_defs.h deleted file mode 100644 index edf6097e0..000000000 --- a/src/core/arm/skyeye_common/skyeye_defs.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "common/common_types.h" - -struct cpu_config_t -{ - const char* cpu_arch_name; // CPU architecture version name.e.g. ARMv4T - const char* cpu_name; // CPU name. e.g. ARM7TDMI or ARM720T - u32 cpu_val; // CPU value; also call MMU ID or processor id;see - // ARM Architecture Reference Manual B2-6 - u32 cpu_mask; // cpu_val's mask. - u32 cachetype; // CPU cache type -}; - -enum { - // No exception - No_exp = 0, - // Memory allocation exception - Malloc_exp, - // File open exception - File_open_exp, - // DLL open exception - Dll_open_exp, - // Invalid argument exception - Invarg_exp, - // Invalid module exception - Invmod_exp, - // wrong format exception for config file parsing - Conf_format_exp, - // some reference excess the predefiend range. Such as the index out of array range - Excess_range_exp, - // Can not find the desirable result - Not_found_exp, - // Unknown exception - Unknown_exp -}; - -typedef u32 addr_t; diff --git a/src/core/arm/skyeye_common/vfp/asm_vfp.h b/src/core/arm/skyeye_common/vfp/asm_vfp.h index ccb7cf4d7..1187924f4 100644 --- a/src/core/arm/skyeye_common/vfp/asm_vfp.h +++ b/src/core/arm/skyeye_common/vfp/asm_vfp.h @@ -7,15 +7,15 @@ #pragma once -// FPSID Information +// ARM11 MPCore FPSID Information // Note that these are used as values and not as flags. enum : u32 { - VFP_FPSID_IMPLMEN = 0, // Implementation code. Should be the same as cp15 0 c0 0 - VFP_FPSID_SW = 0, // Software emulation bit value - VFP_FPSID_SUBARCH = 0x2, // Subarchitecture version number - VFP_FPSID_PARTNUM = 0x1, // Part number - VFP_FPSID_VARIANT = 0x1, // Variant number - VFP_FPSID_REVISION = 0x1 // Revision number + VFP_FPSID_IMPLMEN = 0x41, // Implementation code. Should be the same as cp15 0 c0 0 + VFP_FPSID_SW = 0, // Software emulation bit value + VFP_FPSID_SUBARCH = 0x1, // Subarchitecture version number + VFP_FPSID_PARTNUM = 0x20, // Part number + VFP_FPSID_VARIANT = 0xB, // Variant number + VFP_FPSID_REVISION = 0x4 // Revision number }; // FPEXC bits diff --git a/src/core/arm/skyeye_common/vfp/vfp.cpp b/src/core/arm/skyeye_common/vfp/vfp.cpp index 6286e7b62..b88d47750 100644 --- a/src/core/arm/skyeye_common/vfp/vfp.cpp +++ b/src/core/arm/skyeye_common/vfp/vfp.cpp @@ -20,7 +20,6 @@ /* Note: this file handles interface with arm core and vfp registers */ -#include "common/common.h" #include "common/logging/log.h" #include "core/arm/skyeye_common/armdefs.h" @@ -29,304 +28,26 @@ unsigned VFPInit(ARMul_State* state) { - state->VFP[VFP_OFFSET(VFP_FPSID)] = VFP_FPSID_IMPLMEN<<24 | VFP_FPSID_SW<<23 | VFP_FPSID_SUBARCH<<16 | - VFP_FPSID_PARTNUM<<8 | VFP_FPSID_VARIANT<<4 | VFP_FPSID_REVISION; - state->VFP[VFP_OFFSET(VFP_FPEXC)] = 0; - state->VFP[VFP_OFFSET(VFP_FPSCR)] = 0; + state->VFP[VFP_FPSID] = VFP_FPSID_IMPLMEN<<24 | VFP_FPSID_SW<<23 | VFP_FPSID_SUBARCH<<16 | + VFP_FPSID_PARTNUM<<8 | VFP_FPSID_VARIANT<<4 | VFP_FPSID_REVISION; + state->VFP[VFP_FPEXC] = 0; + state->VFP[VFP_FPSCR] = 0; return 0; } -unsigned VFPMRC(ARMul_State* state, unsigned type, u32 instr, u32* value) -{ - /* MRC<c> <coproc>,<opc1>,<Rt>,<CRn>,<CRm>{,<opc2>} */ - int CoProc = BITS(instr, 8, 11); /* 10 or 11 */ - int OPC_1 = BITS(instr, 21, 23); - int Rt = BITS(instr, 12, 15); - int CRn = BITS(instr, 16, 19); - int CRm = BITS(instr, 0, 3); - int OPC_2 = BITS(instr, 5, 7); - - /* TODO check access permission */ - - /* CRn/opc1 CRm/opc2 */ - - if (CoProc == 10 || CoProc == 11) - { - if (OPC_1 == 0x0 && CRm == 0 && (OPC_2 & 0x3) == 0) - { - /* VMOV r to s */ - /* Transfering Rt is not mandatory, as the value of interest is pointed by value */ - VMOVBRS(state, BIT(instr, 20), Rt, BIT(instr, 7)|CRn<<1, value); - return ARMul_DONE; - } - - if (OPC_1 == 0x7 && CRm == 0 && OPC_2 == 0) - { - VMRS(state, CRn, Rt, value); - return ARMul_DONE; - } - } - LOG_WARNING(Core_ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, CRn %x, CRm %x, OPC_2 %x\n", - instr, CoProc, OPC_1, Rt, CRn, CRm, OPC_2); - - return ARMul_CANT; -} - -unsigned VFPMCR(ARMul_State* state, unsigned type, u32 instr, u32 value) -{ - /* MCR<c> <coproc>,<opc1>,<Rt>,<CRn>,<CRm>{,<opc2>} */ - int CoProc = BITS(instr, 8, 11); /* 10 or 11 */ - int OPC_1 = BITS(instr, 21, 23); - int Rt = BITS(instr, 12, 15); - int CRn = BITS(instr, 16, 19); - int CRm = BITS(instr, 0, 3); - int OPC_2 = BITS(instr, 5, 7); - - /* TODO check access permission */ - - /* CRn/opc1 CRm/opc2 */ - if (CoProc == 10 || CoProc == 11) - { - if (OPC_1 == 0x0 && CRm == 0 && (OPC_2 & 0x3) == 0) - { - /* VMOV s to r */ - /* Transfering Rt is not mandatory, as the value of interest is pointed by value */ - VMOVBRS(state, BIT(instr, 20), Rt, BIT(instr, 7)|CRn<<1, &value); - return ARMul_DONE; - } - - if (OPC_1 == 0x7 && CRm == 0 && OPC_2 == 0) - { - VMSR(state, CRn, Rt); - return ARMul_DONE; - } - - if ((OPC_1 & 0x4) == 0 && CoProc == 11 && CRm == 0) - { - VFP_DEBUG_UNIMPLEMENTED(VMOVBRC); - return ARMul_DONE; - } - - if (CoProc == 11 && CRm == 0) - { - VFP_DEBUG_UNIMPLEMENTED(VMOVBCR); - return ARMul_DONE; - } - } - LOG_WARNING(Core_ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, CRn %x, CRm %x, OPC_2 %x\n", - instr, CoProc, OPC_1, Rt, CRn, CRm, OPC_2); - - return ARMul_CANT; -} - -unsigned VFPMRRC(ARMul_State* state, unsigned type, u32 instr, u32* value1, u32* value2) -{ - /* MCRR<c> <coproc>,<opc1>,<Rt>,<Rt2>,<CRm> */ - int CoProc = BITS(instr, 8, 11); /* 10 or 11 */ - int OPC_1 = BITS(instr, 4, 7); - int Rt = BITS(instr, 12, 15); - int Rt2 = BITS(instr, 16, 19); - int CRm = BITS(instr, 0, 3); - - if (CoProc == 10 || CoProc == 11) - { - if (CoProc == 10 && (OPC_1 & 0xD) == 1) - { - VMOVBRRSS(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, value1, value2); - return ARMul_DONE; - } - - if (CoProc == 11 && (OPC_1 & 0xD) == 1) - { - /* Transfering Rt and Rt2 is not mandatory, as the value of interest is pointed by value1 and value2 */ - VMOVBRRD(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, value1, value2); - return ARMul_DONE; - } - } - LOG_WARNING(Core_ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, Rt2 %x, CRm %x\n", - instr, CoProc, OPC_1, Rt, Rt2, CRm); - - return ARMul_CANT; -} - -unsigned VFPMCRR(ARMul_State* state, unsigned type, u32 instr, u32 value1, u32 value2) -{ - /* MCRR<c> <coproc>,<opc1>,<Rt>,<Rt2>,<CRm> */ - int CoProc = BITS(instr, 8, 11); /* 10 or 11 */ - int OPC_1 = BITS(instr, 4, 7); - int Rt = BITS(instr, 12, 15); - int Rt2 = BITS(instr, 16, 19); - int CRm = BITS(instr, 0, 3); - - /* TODO check access permission */ - - /* CRn/opc1 CRm/opc2 */ - - if (CoProc == 11 || CoProc == 10) - { - if (CoProc == 10 && (OPC_1 & 0xD) == 1) - { - VMOVBRRSS(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, &value1, &value2); - return ARMul_DONE; - } - - if (CoProc == 11 && (OPC_1 & 0xD) == 1) - { - /* Transfering Rt and Rt2 is not mandatory, as the value of interest is pointed by value1 and value2 */ - VMOVBRRD(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, &value1, &value2); - return ARMul_DONE; - } - } - LOG_WARNING(Core_ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, Rt2 %x, CRm %x\n", - instr, CoProc, OPC_1, Rt, Rt2, CRm); - - return ARMul_CANT; -} - -unsigned VFPSTC(ARMul_State* state, unsigned type, u32 instr, u32 * value) -{ - /* STC{L}<c> <coproc>,<CRd>,[<Rn>],<option> */ - int CoProc = BITS(instr, 8, 11); /* 10 or 11 */ - int CRd = BITS(instr, 12, 15); - int Rn = BITS(instr, 16, 19); - int imm8 = BITS(instr, 0, 7); - int P = BIT(instr, 24); - int U = BIT(instr, 23); - int D = BIT(instr, 22); - int W = BIT(instr, 21); - - /* TODO check access permission */ - - /* VSTM */ - if ( (P|U|D|W) == 0 ) { - LOG_ERROR(Core_ARM11, "In %s, UNDEFINED\n", __FUNCTION__); - exit(-1); - } - if (CoProc == 10 || CoProc == 11) { -#if 1 - if (P == 0 && U == 0 && W == 0) { - LOG_ERROR(Core_ARM11, "VSTM Related encodings\n"); - exit(-1); - } - if (P == U && W == 1) { - LOG_ERROR(Core_ARM11, "UNDEFINED\n"); - exit(-1); - } -#endif - - if (P == 1 && W == 0) - { - return VSTR(state, type, instr, value); - } - - if (P == 1 && U == 0 && W == 1 && Rn == 0xD) - { - return VPUSH(state, type, instr, value); - } - - return VSTM(state, type, instr, value); - } - 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", - instr, CoProc, CRd, Rn, imm8, P, U, D, W); - - return ARMul_CANT; -} - -unsigned VFPLDC(ARMul_State* state, unsigned type, u32 instr, u32 value) +void VMSR(ARMul_State* state, ARMword reg, ARMword Rt) { - /* LDC{L}<c> <coproc>,<CRd>,[<Rn>] */ - int CoProc = BITS(instr, 8, 11); /* 10 or 11 */ - int CRd = BITS(instr, 12, 15); - int Rn = BITS(instr, 16, 19); - int imm8 = BITS(instr, 0, 7); - int P = BIT(instr, 24); - int U = BIT(instr, 23); - int D = BIT(instr, 22); - int W = BIT(instr, 21); - - /* TODO check access permission */ - - if ( (P|U|D|W) == 0 ) { - LOG_ERROR(Core_ARM11, "In %s, UNDEFINED\n", __FUNCTION__); - exit(-1); - } - if (CoProc == 10 || CoProc == 11) + if (reg == 1) { - if (P == 1 && W == 0) - { - return VLDR(state, type, instr, value); - } - - if (P == 0 && U == 1 && W == 1 && Rn == 0xD) - { - return VPOP(state, type, instr, value); - } - - return VLDM(state, type, instr, value); + state->VFP[VFP_FPSCR] = state->Reg[Rt]; } - 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", - instr, CoProc, CRd, Rn, imm8, P, U, D, W); - - return ARMul_CANT; -} - -unsigned VFPCDP(ARMul_State* state, unsigned type, u32 instr) -{ - /* CDP<c> <coproc>,<opc1>,<CRd>,<CRn>,<CRm>,<opc2> */ - int CoProc = BITS(instr, 8, 11); /* 10 or 11 */ - int OPC_1 = BITS(instr, 20, 23); - int CRd = BITS(instr, 12, 15); - int CRn = BITS(instr, 16, 19); - int CRm = BITS(instr, 0, 3); - int OPC_2 = BITS(instr, 5, 7); - - /* TODO check access permission */ - - /* CRn/opc1 CRm/opc2 */ - - if (CoProc == 10 || CoProc == 11) + else if (reg == 8) { - if ((OPC_1 & 0xB) == 0xB && BITS(instr, 4, 7) == 0) - { - unsigned int single = BIT(instr, 8) == 0; - unsigned int d = (single ? BITS(instr, 12,15)<<1 | BIT(instr, 22) : BITS(instr, 12,15) | BIT(instr, 22)<<4); - unsigned int imm; - instr = BITS(instr, 16, 19) << 4 | BITS(instr, 0, 3); // FIXME dirty workaround to get a correct imm - - if (single) - imm = BIT(instr, 7)<<31 | (BIT(instr, 6)==0)<<30 | (BIT(instr, 6) ? 0x1f : 0)<<25 | BITS(instr, 0, 5)<<19; - else - imm = BIT(instr, 7)<<31 | (BIT(instr, 6)==0)<<30 | (BIT(instr, 6) ? 0xff : 0)<<22 | BITS(instr, 0, 5)<<16; - - VMOVI(state, single, d, imm); - return ARMul_DONE; - } - - if ((OPC_1 & 0xB) == 0xB && CRn == 0 && (OPC_2 & 0x6) == 0x2) - { - unsigned int single = BIT(instr, 8) == 0; - unsigned int d = (single ? BITS(instr, 12,15)<<1 | BIT(instr, 22) : BITS(instr, 12,15) | BIT(instr, 22)<<4); - unsigned int m = (single ? BITS(instr, 0, 3)<<1 | BIT(instr, 5) : BITS(instr, 0, 3) | BIT(instr, 5)<<4); - VMOVR(state, single, d, m); - return ARMul_DONE; - } - - int exceptions = 0; - if (CoProc == 10) - exceptions = vfp_single_cpdo(state, instr, state->VFP[VFP_OFFSET(VFP_FPSCR)]); - else - exceptions = vfp_double_cpdo(state, instr, state->VFP[VFP_OFFSET(VFP_FPSCR)]); - - vfp_raise_exceptions(state, exceptions, instr, state->VFP[VFP_OFFSET(VFP_FPSCR)]); - - return ARMul_DONE; + state->VFP[VFP_FPEXC] = state->Reg[Rt]; } - LOG_WARNING(Core_ARM11, "Can't identify %x\n", instr); - return ARMul_CANT; } -/* ----------- MRC ------------ */ void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value) { if (to_arm) @@ -338,43 +59,7 @@ void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* state->ExtReg[n] = *value; } } -void VMRS(ARMul_State* state, ARMword reg, ARMword Rt, ARMword* value) -{ - if (reg == 1) - { - if (Rt != 15) - { - *value = state->VFP[VFP_OFFSET(VFP_FPSCR)]; - } - else - { - *value = state->VFP[VFP_OFFSET(VFP_FPSCR)] ; - } - } - else - { - switch (reg) - { - case 0: - *value = state->VFP[VFP_OFFSET(VFP_FPSID)]; - break; - case 6: - /* MVFR1, VFPv3 only ? */ - LOG_TRACE(Core_ARM11, "\tr%d <= MVFR1 unimplemented\n", Rt); - break; - case 7: - /* MVFR0, VFPv3 only? */ - LOG_TRACE(Core_ARM11, "\tr%d <= MVFR0 unimplemented\n", Rt); - break; - case 8: - *value = state->VFP[VFP_OFFSET(VFP_FPEXC)]; - break; - default: - LOG_TRACE(Core_ARM11, "\tSUBARCHITECTURE DEFINED\n"); - break; - } - } -} + void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2) { if (to_arm) @@ -402,301 +87,6 @@ void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMwor } } -/* ----------- MCR ------------ */ -void VMSR(ARMul_State* state, ARMword reg, ARMword Rt) -{ - if (reg == 1) - { - state->VFP[VFP_OFFSET(VFP_FPSCR)] = state->Reg[Rt]; - } - else if (reg == 8) - { - state->VFP[VFP_OFFSET(VFP_FPEXC)] = state->Reg[Rt]; - } -} - -/* Memory operation are not inlined, as old Interpreter and Fast interpreter - don't have the same memory operation interface. - Old interpreter framework does one access to coprocessor per data, and - handles already data write, as well as address computation, - which is not the case for Fast interpreter. Therefore, implementation - of vfp instructions in old interpreter and fast interpreter are separate. */ - -/* ----------- STC ------------ */ -int VSTR(ARMul_State* state, int type, ARMword instr, ARMword* value) -{ - static int i = 0; - static int single_reg, add, d, n, imm32, regs; - if (type == ARMul_FIRST) - { - single_reg = BIT(instr, 8) == 0; // Double precision - add = BIT(instr, 23); - imm32 = BITS(instr, 0,7)<<2; // may not be used - d = single_reg ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); /* Base register */ - n = BITS(instr, 16, 19); // destination register - - i = 0; - regs = 1; - - return ARMul_DONE; - } - else if (type == ARMul_DATA) - { - if (single_reg) - { - *value = state->ExtReg[d+i]; - i++; - if (i < regs) - return ARMul_INC; - else - return ARMul_DONE; - } - else - { - /* FIXME Careful of endianness, may need to rework this */ - *value = state->ExtReg[d*2+i]; - i++; - if (i < regs*2) - return ARMul_INC; - else - return ARMul_DONE; - } - } - - return -1; -} -int VPUSH(ARMul_State* state, int type, ARMword instr, ARMword* value) -{ - static int i = 0; - static int single_regs, d, imm32, regs; - if (type == ARMul_FIRST) - { - single_regs = BIT(instr, 8) == 0; // Single precision - d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register - imm32 = BITS(instr, 0,7)<<2; // may not be used - regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 1, 7); // FSTMX if regs is odd - - state->Reg[R13] = state->Reg[R13] - imm32; - - i = 0; - - return ARMul_DONE; - } - else if (type == ARMul_DATA) - { - if (single_regs) - { - *value = state->ExtReg[d + i]; - i++; - if (i < regs) - return ARMul_INC; - else - return ARMul_DONE; - } - else - { - /* FIXME Careful of endianness, may need to rework this */ - *value = state->ExtReg[d*2 + i]; - i++; - if (i < regs*2) - return ARMul_INC; - else - return ARMul_DONE; - } - } - - return -1; -} -int VSTM(ARMul_State* state, int type, ARMword instr, ARMword* value) -{ - static int i = 0; - static int single_regs, add, wback, d, n, imm32, regs; - if (type == ARMul_FIRST) - { - single_regs = BIT(instr, 8) == 0; // Single precision - add = BIT(instr, 23); - wback = BIT(instr, 21); // write-back - d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register - n = BITS(instr, 16, 19); // destination register - imm32 = BITS(instr, 0,7) * 4; // may not be used - regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 0, 7)>>1; // FSTMX if regs is odd - - if (wback) { - state->Reg[n] = (add ? state->Reg[n] + imm32 : state->Reg[n] - imm32); - } - - i = 0; - - return ARMul_DONE; - } - else if (type == ARMul_DATA) - { - if (single_regs) - { - *value = state->ExtReg[d + i]; - i++; - if (i < regs) - return ARMul_INC; - else - return ARMul_DONE; - } - else - { - /* FIXME Careful of endianness, may need to rework this */ - *value = state->ExtReg[d*2 + i]; - i++; - if (i < regs*2) - return ARMul_INC; - else - return ARMul_DONE; - } - } - - return -1; -} - -/* ----------- LDC ------------ */ -int VPOP(ARMul_State* state, int type, ARMword instr, ARMword value) -{ - static int i = 0; - static int single_regs, d, imm32, regs; - if (type == ARMul_FIRST) - { - single_regs = BIT(instr, 8) == 0; // Single precision - d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register - imm32 = BITS(instr, 0, 7)<<2; // may not be used - regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 1, 7); // FLDMX if regs is odd - - state->Reg[R13] = state->Reg[R13] + imm32; - - i = 0; - - return ARMul_DONE; - } - else if (type == ARMul_TRANSFER) - { - return ARMul_DONE; - } - else if (type == ARMul_DATA) - { - if (single_regs) - { - state->ExtReg[d + i] = value; - i++; - if (i < regs) - return ARMul_INC; - else - return ARMul_DONE; - } - else - { - /* FIXME Careful of endianness, may need to rework this */ - state->ExtReg[d*2 + i] = value; - i++; - if (i < regs*2) - return ARMul_INC; - else - return ARMul_DONE; - } - } - - return -1; -} -int VLDR(ARMul_State* state, int type, ARMword instr, ARMword value) -{ - static int i = 0; - static int single_reg, add, d, n, imm32, regs; - if (type == ARMul_FIRST) - { - single_reg = BIT(instr, 8) == 0; // Double precision - add = BIT(instr, 23); - imm32 = BITS(instr, 0, 7)<<2; // may not be used - d = single_reg ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register - n = BITS(instr, 16, 19); // destination register - - i = 0; - regs = 1; - - return ARMul_DONE; - } - else if (type == ARMul_TRANSFER) - { - return ARMul_DONE; - } - else if (type == ARMul_DATA) - { - if (single_reg) - { - state->ExtReg[d+i] = value; - i++; - if (i < regs) - return ARMul_INC; - else - return ARMul_DONE; - } - else - { - /* FIXME Careful of endianness, may need to rework this */ - state->ExtReg[d*2+i] = value; - i++; - if (i < regs*2) - return ARMul_INC; - else - return ARMul_DONE; - } - } - - return -1; -} -int VLDM(ARMul_State* state, int type, ARMword instr, ARMword value) -{ - static int i = 0; - static int single_regs, add, wback, d, n, imm32, regs; - if (type == ARMul_FIRST) - { - single_regs = BIT(instr, 8) == 0; // Single precision - add = BIT(instr, 23); - wback = BIT(instr, 21); // write-back - d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register - n = BITS(instr, 16, 19); // destination register - imm32 = BITS(instr, 0, 7) * 4; // may not be used - regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 0, 7)>>1; // FLDMX if regs is odd - - if (wback) { - state->Reg[n] = (add ? state->Reg[n] + imm32 : state->Reg[n] - imm32); - } - - i = 0; - - return ARMul_DONE; - } - else if (type == ARMul_DATA) - { - if (single_regs) - { - state->ExtReg[d + i] = value; - i++; - if (i < regs) - return ARMul_INC; - else - return ARMul_DONE; - } - else - { - /* FIXME Careful of endianness, may need to rework this */ - state->ExtReg[d*2 + i] = value; - i++; - if (i < regs*2) - return ARMul_INC; - else - return ARMul_DONE; - } - } - - return -1; -} - -/* ----------- CDP ------------ */ void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm) { if (single) @@ -774,5 +164,5 @@ void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpsc fpscr |= exceptions; - state->VFP[VFP_OFFSET(VFP_FPSCR)] = fpscr; + state->VFP[VFP_FPSCR] = fpscr; } diff --git a/src/core/arm/skyeye_common/vfp/vfp.h b/src/core/arm/skyeye_common/vfp/vfp.h index 445a224bc..727350f14 100644 --- a/src/core/arm/skyeye_common/vfp/vfp.h +++ b/src/core/arm/skyeye_common/vfp/vfp.h @@ -25,16 +25,9 @@ #define VFP_DEBUG_UNIMPLEMENTED(x) LOG_ERROR(Core_ARM11, "in func %s, " #x " unimplemented\n", __FUNCTION__); exit(-1); #define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested\n", __FUNCTION__); #define CHECK_VFP_ENABLED -#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); //if (ret == -1) {printf("VFP CDP FAILURE %x\n", inst_cream->instr); exit(-1);} +#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]); unsigned VFPInit(ARMul_State* state); -unsigned VFPMRC(ARMul_State* state, unsigned type, ARMword instr, ARMword* value); -unsigned VFPMCR(ARMul_State* state, unsigned type, ARMword instr, ARMword value); -unsigned VFPMRRC(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2); -unsigned VFPMCRR(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2); -unsigned VFPSTC(ARMul_State* state, unsigned type, ARMword instr, ARMword* value); -unsigned VFPLDC(ARMul_State* state, unsigned type, ARMword instr, ARMword value); -unsigned VFPCDP(ARMul_State* state, unsigned type, ARMword instr); s32 vfp_get_float(ARMul_State* state, u32 reg); void 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 u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr); u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr); -// MRC -void VMRS(ARMul_State* state, ARMword reg, ARMword Rt, ARMword* value); +void VMSR(ARMul_State* state, ARMword reg, ARMword Rt); void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value); void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm); void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword imm); -// MCR -void VMSR(ARMul_State* state, ARMword reg, ARMword Rt); - -// STC -int VSTM(ARMul_State* state, int type, ARMword instr, ARMword* value); -int VPUSH(ARMul_State* state, int type, ARMword instr, ARMword* value); -int VSTR(ARMul_State* state, int type, ARMword instr, ARMword* value); - -// LDC -int VLDM(ARMul_State* state, int type, ARMword instr, ARMword value); -int VPOP(ARMul_State* state, int type, ARMword instr, ARMword value); -int 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..ccc0212ab 100644 --- a/src/core/arm/skyeye_common/vfp/vfp_helper.h +++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h @@ -35,9 +35,7 @@ #include <cstdio> #include "common/common_types.h" #include "core/arm/skyeye_common/armdefs.h" - -#define pr_info //printf -#define pr_debug //printf +#include "core/arm/skyeye_common/vfp/asm_vfp.h" #define do_div(n, base) {n/=base;} @@ -239,33 +237,6 @@ struct vfp_single { #define vfp_single_packed_exponent(v) (((v) >> VFP_SINGLE_MANTISSA_BITS) & ((1 << VFP_SINGLE_EXPONENT_BITS) - 1)) #define vfp_single_packed_mantissa(v) ((v) & ((1 << VFP_SINGLE_MANTISSA_BITS) - 1)) -// Unpack a single-precision float. Note that this returns the magnitude -// of the single-precision float mantissa with the 1. if necessary, -// aligned to bit 30. -static inline void vfp_single_unpack(vfp_single* s, s32 val) -{ - u32 significand; - - s->sign = vfp_single_packed_sign(val) >> 16, - s->exponent = vfp_single_packed_exponent(val); - - significand = (u32) val; - significand = (significand << (32 - VFP_SINGLE_MANTISSA_BITS)) >> 2; - if (s->exponent && s->exponent != 255) - significand |= 0x40000000; - s->significand = significand; -} - -// Re-pack a single-precision float. This assumes that the float is -// already normalised such that the MSB is bit 30, _not_ bit 31. -static inline s32 vfp_single_pack(vfp_single* s) -{ - u32 val = (s->sign << 16) + - (s->exponent << VFP_SINGLE_MANTISSA_BITS) + - (s->significand >> VFP_SINGLE_LOW_BITS); - return (s32)val; -} - enum : u32 { VFP_NUMBER = (1 << 0), VFP_ZERO = (1 << 1), @@ -297,6 +268,39 @@ static inline int vfp_single_type(vfp_single* s) return type; } +// Unpack a single-precision float. Note that this returns the magnitude +// of the single-precision float mantissa with the 1. if necessary, +// aligned to bit 30. +static inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr) +{ + s->sign = vfp_single_packed_sign(val) >> 16, + s->exponent = vfp_single_packed_exponent(val); + + u32 significand = ((u32)val << (32 - VFP_SINGLE_MANTISSA_BITS)) >> 2; + if (s->exponent && s->exponent != 255) + significand |= 0x40000000; + s->significand = significand; + + // If flush-to-zero mode is enabled, turn the denormal into zero. + // On a VFPv2 architecture, the sign of the zero is always positive. + if ((*fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_single_type(s) & VFP_DENORMAL) != 0) { + s->sign = 0; + s->exponent = 0; + s->significand = 0; + *fpscr |= FPSCR_IDC; + } +} + +// Re-pack a single-precision float. This assumes that the float is +// already normalised such that the MSB is bit 30, _not_ bit 31. +static inline s32 vfp_single_pack(vfp_single* s) +{ + u32 val = (s->sign << 16) + + (s->exponent << VFP_SINGLE_MANTISSA_BITS) + + (s->significand >> VFP_SINGLE_LOW_BITS); + return (s32)val; +} + u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, u32 exceptions, const char* func); @@ -331,24 +335,49 @@ struct vfp_double { #define vfp_double_packed_exponent(v) (((v) >> VFP_DOUBLE_MANTISSA_BITS) & ((1 << VFP_DOUBLE_EXPONENT_BITS) - 1)) #define vfp_double_packed_mantissa(v) ((v) & ((1ULL << VFP_DOUBLE_MANTISSA_BITS) - 1)) +static inline int vfp_double_type(vfp_double* s) +{ + int type = VFP_NUMBER; + if (s->exponent == 2047) { + if (s->significand == 0) + type = VFP_INFINITY; + else if (s->significand & VFP_DOUBLE_SIGNIFICAND_QNAN) + type = VFP_QNAN; + else + type = VFP_SNAN; + } else if (s->exponent == 0) { + if (s->significand == 0) + type |= VFP_ZERO; + else + type |= VFP_DENORMAL; + } + return type; +} + // Unpack a double-precision float. Note that this returns the magnitude // of the double-precision float mantissa with the 1. if necessary, // aligned to bit 62. -static inline void vfp_double_unpack(vfp_double* s, s64 val) +static inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr) { - u64 significand; - s->sign = vfp_double_packed_sign(val) >> 48; s->exponent = vfp_double_packed_exponent(val); - significand = (u64) val; - significand = (significand << (64 - VFP_DOUBLE_MANTISSA_BITS)) >> 2; + u64 significand = ((u64)val << (64 - VFP_DOUBLE_MANTISSA_BITS)) >> 2; if (s->exponent && s->exponent != 2047) significand |= (1ULL << 62); s->significand = significand; + + // If flush-to-zero mode is enabled, turn the denormal into zero. + // On a VFPv2 architecture, the sign of the zero is always positive. + if ((*fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_double_type(s) & VFP_DENORMAL) != 0) { + s->sign = 0; + s->exponent = 0; + s->significand = 0; + *fpscr |= FPSCR_IDC; + } } -// Re-pack a double-precision float. This assumes that the float is +// Re-pack a double-precision float. This assumes that the float is // already normalised such that the MSB is bit 30, _not_ bit 31. static inline s64 vfp_double_pack(vfp_double* s) { @@ -358,25 +387,6 @@ static inline s64 vfp_double_pack(vfp_double* s) return (s64)val; } -static inline int vfp_double_type(vfp_double* s) -{ - int type = VFP_NUMBER; - if (s->exponent == 2047) { - if (s->significand == 0) - type = VFP_INFINITY; - else if (s->significand & VFP_DOUBLE_SIGNIFICAND_QNAN) - type = VFP_QNAN; - else - type = VFP_SNAN; - } else if (s->exponent == 0) { - if (s->significand == 0) - type |= VFP_ZERO; - else - type |= VFP_DENORMAL; - } - return type; -} - u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand); // A special flag to tell the normalisation code not to normalise. diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp index d76d37fd4..ab9fec39d 100644 --- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp @@ -291,7 +291,8 @@ static u32 vfp_double_fsqrt(ARMul_State* state, int dd, int unused, int dm, u32 vfp_double vdm, vdd, *vdp; int ret, tm; - vfp_double_unpack(&vdm, vfp_get_double(state, dm)); + vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + tm = vfp_double_type(&vdm); if (tm & (VFP_NAN|VFP_INFINITY)) { vdp = &vdd; @@ -473,7 +474,7 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32 u32 exceptions = 0; LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); - vfp_double_unpack(&vdm, vfp_get_double(state, dm)); + vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); tm = vfp_double_type(&vdm); @@ -543,7 +544,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32 int tm; LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); - vfp_double_unpack(&vdm, vfp_get_double(state, dm)); + vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); /* * Do we have a denormalised number? @@ -624,7 +625,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32 int tm; LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); - vfp_double_unpack(&vdm, vfp_get_double(state, dm)); + vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); vfp_double_dump("VDM", &vdm); /* @@ -896,11 +897,11 @@ vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 f struct vfp_double vdd, vdp, vdn, vdm; u32 exceptions; - vfp_double_unpack(&vdn, vfp_get_double(state, dn)); + vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); if (vdn.exponent == 0 && vdn.significand) vfp_double_normalise_denormal(&vdn); - vfp_double_unpack(&vdm, vfp_get_double(state, dm)); + vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); if (vdm.exponent == 0 && vdm.significand) vfp_double_normalise_denormal(&vdm); @@ -908,7 +909,7 @@ vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 f if (negate & NEG_MULTIPLY) vdp.sign = vfp_sign_negate(vdp.sign); - vfp_double_unpack(&vdn, vfp_get_double(state, dd)); + vfp_double_unpack(&vdn, vfp_get_double(state, dd), &fpscr); if (vdn.exponent == 0 && vdn.significand != 0) vfp_double_normalise_denormal(&vdn); @@ -969,11 +970,11 @@ static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr u32 exceptions; LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); - vfp_double_unpack(&vdn, vfp_get_double(state, dn)); + vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); if (vdn.exponent == 0 && vdn.significand) vfp_double_normalise_denormal(&vdn); - vfp_double_unpack(&vdm, vfp_get_double(state, dm)); + vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); if (vdm.exponent == 0 && vdm.significand) vfp_double_normalise_denormal(&vdm); @@ -990,11 +991,11 @@ static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpsc u32 exceptions; LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); - vfp_double_unpack(&vdn, vfp_get_double(state, dn)); + vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); if (vdn.exponent == 0 && vdn.significand) vfp_double_normalise_denormal(&vdn); - vfp_double_unpack(&vdm, vfp_get_double(state, dm)); + vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); if (vdm.exponent == 0 && vdm.significand) vfp_double_normalise_denormal(&vdm); @@ -1013,11 +1014,11 @@ static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr u32 exceptions; LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); - vfp_double_unpack(&vdn, vfp_get_double(state, dn)); + vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); if (vdn.exponent == 0 && vdn.significand) vfp_double_normalise_denormal(&vdn); - vfp_double_unpack(&vdm, vfp_get_double(state, dm)); + vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); if (vdm.exponent == 0 && vdm.significand) vfp_double_normalise_denormal(&vdm); @@ -1035,11 +1036,11 @@ static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr u32 exceptions; LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); - vfp_double_unpack(&vdn, vfp_get_double(state, dn)); + vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); if (vdn.exponent == 0 && vdn.significand) vfp_double_normalise_denormal(&vdn); - vfp_double_unpack(&vdm, vfp_get_double(state, dm)); + vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); if (vdm.exponent == 0 && vdm.significand) vfp_double_normalise_denormal(&vdm); @@ -1063,8 +1064,8 @@ static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr int tm, tn; LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); - vfp_double_unpack(&vdn, vfp_get_double(state, dn)); - vfp_double_unpack(&vdm, vfp_get_double(state, dm)); + vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); + vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); vdd.sign = vdn.sign ^ vdm.sign; diff --git a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp index 368b5a25d..72afe2164 100644 --- a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp @@ -46,9 +46,9 @@ VMLA_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -96,9 +96,9 @@ VMLS_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -146,9 +146,9 @@ VNMLA_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -197,9 +197,9 @@ VNMLS_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -247,9 +247,9 @@ VNMUL_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -297,9 +297,9 @@ VMUL_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -347,9 +347,9 @@ VADD_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -397,9 +397,9 @@ VSUB_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -447,9 +447,9 @@ VDIV_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -591,9 +591,9 @@ VABS_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -642,9 +642,9 @@ VNEG_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -692,9 +692,9 @@ VSQRT_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -742,9 +742,9 @@ VCMP_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -792,9 +792,9 @@ VCMP2_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -842,9 +842,9 @@ VCVTBDS_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -894,9 +894,9 @@ VCVTBFF_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -944,9 +944,9 @@ VCVTBFI_INST: int ret; if (inst_cream->dp_operation) - ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); else - ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); + ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_FPSCR]); CHECK_VFP_CDP_RET; } @@ -1146,14 +1146,14 @@ VMRS_INST: { if (inst_cream->Rt != 15) { - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_OFFSET(VFP_FPSCR)]; + cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPSCR]; } else { - cpu->NFlag = (cpu->VFP[VFP_OFFSET(VFP_FPSCR)] >> 31) & 1; - cpu->ZFlag = (cpu->VFP[VFP_OFFSET(VFP_FPSCR)] >> 30) & 1; - cpu->CFlag = (cpu->VFP[VFP_OFFSET(VFP_FPSCR)] >> 29) & 1; - cpu->VFlag = (cpu->VFP[VFP_OFFSET(VFP_FPSCR)] >> 28) & 1; + cpu->NFlag = (cpu->VFP[VFP_FPSCR] >> 31) & 1; + cpu->ZFlag = (cpu->VFP[VFP_FPSCR] >> 30) & 1; + cpu->CFlag = (cpu->VFP[VFP_FPSCR] >> 29) & 1; + cpu->VFlag = (cpu->VFP[VFP_FPSCR] >> 28) & 1; } } else @@ -1161,7 +1161,7 @@ VMRS_INST: switch (inst_cream->reg) { case 0: - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_OFFSET(VFP_FPSID)]; + cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPSID]; break; case 6: /* MVFR1, VFPv3 only ? */ @@ -1172,7 +1172,7 @@ VMRS_INST: LOG_TRACE(Core_ARM11, "\tr%d <= MVFR0 unimplemented\n", inst_cream->Rt); break; case 8: - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_OFFSET(VFP_FPEXC)]; + cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPEXC]; break; default: break; diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index 8b2dfa388..4dfe0254d 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp @@ -51,6 +51,8 @@ * =========================================================================== */ +#include "common/logging/log.h" + #include "core/arm/skyeye_common/vfp/vfp_helper.h" #include "core/arm/skyeye_common/vfp/asm_vfp.h" #include "core/arm/skyeye_common/vfp/vfp.h" @@ -63,8 +65,8 @@ static struct vfp_single vfp_single_default_qnan = { static void vfp_single_dump(const char *str, struct vfp_single *s) { - pr_debug("VFP: %s: sign=%d exponent=%d significand=%08x\n", - str, s->sign != 0, s->exponent, s->significand); + LOG_DEBUG(Core_ARM11, "%s: sign=%d exponent=%d significand=%08x", + str, s->sign != 0, s->exponent, s->significand); } static 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, } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vs->sign != 0)) incr = (1 << (VFP_SINGLE_LOW_BITS + 1)) - 1; - pr_debug("VFP: rounding increment = 0x%08x\n", incr); + LOG_DEBUG(Core_ARM11, "rounding increment = 0x%08x", incr); /* * Is our rounding going to overflow? @@ -209,10 +211,8 @@ pack: vfp_single_dump("pack: final", vs); { s32 d = vfp_single_pack(vs); -#if 1 - pr_debug("VFP: %s: d(s%d)=%08x exceptions=%08x\n", func, - sd, d, exceptions); -#endif + LOG_DEBUG(Core_ARM11, "%s: d(s%d)=%08x exceptions=%08x", func, + sd, d, exceptions); vfp_put_float(state, d, sd); } @@ -302,7 +302,7 @@ u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand) u32 z, a; if ((significand & 0xc0000000) != 0x40000000) { - pr_debug("VFP: estimate_sqrt: invalid significand\n"); + LOG_DEBUG(Core_ARM11, "invalid significand"); } a = significand << 1; @@ -330,7 +330,7 @@ static u32 vfp_single_fsqrt(ARMul_State* state, int sd, int unused, s32 m, u32 f struct vfp_single vsm, vsd, *vsp; int ret, tm; - vfp_single_unpack(&vsm, m); + vfp_single_unpack(&vsm, m, &fpscr); tm = vfp_single_type(&vsm); if (tm & (VFP_NAN|VFP_INFINITY)) { vsp = &vsd; @@ -392,7 +392,7 @@ sqrt_invalid: term = (u64)vsd.significand * vsd.significand; rem = ((u64)vsm.significand << 32) - term; - pr_debug("VFP: term=%016llx rem=%016llx\n", term, rem); + LOG_DEBUG(Core_ARM11, "term=%016lx rem=%016lx", term, rem); while (rem < 0) { vsd.significand -= 1; @@ -498,7 +498,7 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f int tm; u32 exceptions = 0; - vfp_single_unpack(&vsm, m); + vfp_single_unpack(&vsm, m, &fpscr); tm = vfp_single_type(&vsm); @@ -563,7 +563,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f int rmode = fpscr & FPSCR_RMODE_MASK; int tm; - vfp_single_unpack(&vsm, m); + vfp_single_unpack(&vsm, m, &fpscr); vfp_single_dump("VSM", &vsm); /* @@ -624,7 +624,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f } } - pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); + LOG_DEBUG(Core_ARM11, "ftoui: d(s%d)=%08x exceptions=%08x", sd, d, exceptions); vfp_put_float(state, d, sd); @@ -643,7 +643,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f int rmode = fpscr & FPSCR_RMODE_MASK; int tm; - vfp_single_unpack(&vsm, m); + vfp_single_unpack(&vsm, m, &fpscr); vfp_single_dump("VSM", &vsm); /* @@ -703,7 +703,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f } } - pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); + LOG_DEBUG(Core_ARM11, "ftosi: d(s%d)=%08x exceptions=%08x", sd, d, exceptions); vfp_put_float(state, (s32)d, sd); @@ -800,7 +800,7 @@ vfp_single_add(struct vfp_single *vsd, struct vfp_single *vsn, if (vsn->significand & 0x80000000 || vsm->significand & 0x80000000) { - pr_info("VFP: bad FP values in %s\n", __func__); + LOG_WARNING(Core_ARM11, "bad FP values"); vfp_single_dump("VSN", vsn); vfp_single_dump("VSM", vsm); } @@ -871,7 +871,7 @@ vfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_s struct vfp_single *t = vsn; vsn = vsm; vsm = t; - pr_debug("VFP: swapping M <-> N\n"); + LOG_DEBUG(Core_ARM11, "swapping M <-> N"); } vsd->sign = vsn->sign ^ vsm->sign; @@ -924,12 +924,12 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp s32 v; v = vfp_get_float(state, sn); - pr_debug("VFP: s%u = %08x\n", sn, v); - vfp_single_unpack(&vsn, v); + LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, v); + vfp_single_unpack(&vsn, v, &fpscr); if (vsn.exponent == 0 && vsn.significand) vfp_single_normalise_denormal(&vsn); - vfp_single_unpack(&vsm, m); + vfp_single_unpack(&vsm, m, &fpscr); if (vsm.exponent == 0 && vsm.significand) vfp_single_normalise_denormal(&vsm); @@ -939,8 +939,8 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp vsp.sign = vfp_sign_negate(vsp.sign); v = vfp_get_float(state, sd); - pr_debug("VFP: s%u = %08x\n", sd, v); - vfp_single_unpack(&vsn, v); + LOG_DEBUG(Core_ARM11, "s%u = %08x", sd, v); + vfp_single_unpack(&vsn, v, &fpscr); if (vsn.exponent == 0 && vsn.significand != 0) vfp_single_normalise_denormal(&vsn); @@ -961,7 +961,7 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp */ static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { - pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd); + LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd); return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac"); } @@ -970,7 +970,8 @@ static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) */ static u32 vfp_single_fnmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { - pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sd, sn); + // TODO: this one has its arguments inverted, investigate. + LOG_DEBUG(Core_ARM11, "s%u = %08x", sd, sn); return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_MULTIPLY, "fnmac"); } @@ -979,7 +980,7 @@ static u32 vfp_single_fnmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr */ static u32 vfp_single_fmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { - pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd); + LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd); return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_SUBTRACT, "fmsc"); } @@ -988,7 +989,7 @@ static u32 vfp_single_fmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) */ static u32 vfp_single_fnmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { - pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd); + LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd); return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc"); } @@ -1001,13 +1002,13 @@ static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) u32 exceptions; s32 n = vfp_get_float(state, sn); - pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, n); + LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n); - vfp_single_unpack(&vsn, n); + vfp_single_unpack(&vsn, n, &fpscr); if (vsn.exponent == 0 && vsn.significand) vfp_single_normalise_denormal(&vsn); - vfp_single_unpack(&vsm, m); + vfp_single_unpack(&vsm, m, &fpscr); if (vsm.exponent == 0 && vsm.significand) vfp_single_normalise_denormal(&vsm); @@ -1024,13 +1025,13 @@ static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr u32 exceptions; s32 n = vfp_get_float(state, sn); - pr_debug("VFP: s%u = %08x\n", sn, n); + LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n); - vfp_single_unpack(&vsn, n); + vfp_single_unpack(&vsn, n, &fpscr); if (vsn.exponent == 0 && vsn.significand) vfp_single_normalise_denormal(&vsn); - vfp_single_unpack(&vsm, m); + vfp_single_unpack(&vsm, m, &fpscr); if (vsm.exponent == 0 && vsm.significand) vfp_single_normalise_denormal(&vsm); @@ -1048,16 +1049,16 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) u32 exceptions; s32 n = vfp_get_float(state, sn); - pr_debug("VFP: s%u = %08x\n", sn, n); + LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n); /* * Unpack and normalise denormals. */ - vfp_single_unpack(&vsn, n); + vfp_single_unpack(&vsn, n, &fpscr); if (vsn.exponent == 0 && vsn.significand) vfp_single_normalise_denormal(&vsn); - vfp_single_unpack(&vsm, m); + vfp_single_unpack(&vsm, m, &fpscr); if (vsm.exponent == 0 && vsm.significand) vfp_single_normalise_denormal(&vsm); @@ -1071,7 +1072,7 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) */ static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { - pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd); + LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd); /* * Subtraction is addition with one sign inverted. */ @@ -1091,10 +1092,10 @@ static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) s32 n = vfp_get_float(state, sn); int tm, tn; - pr_debug("VFP: s%u = %08x\n", sn, n); + LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n); - vfp_single_unpack(&vsn, n); - vfp_single_unpack(&vsm, m); + vfp_single_unpack(&vsn, n, &fpscr); + vfp_single_unpack(&vsm, m, &fpscr); vsd.sign = vsn.sign ^ vsm.sign; @@ -1213,7 +1214,6 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr) unsigned int sm = vfp_get_sm(inst); unsigned int vecitr, veclen, vecstride; struct op *fop; - pr_debug("In %s\n", __FUNCTION__); vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK); @@ -1239,11 +1239,11 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr) else veclen = fpscr & FPSCR_LENGTH_MASK; - pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride, - (veclen >> FPSCR_LENGTH_BIT) + 1); + LOG_DEBUG(Core_ARM11, "vecstride=%u veclen=%u", vecstride, + (veclen >> FPSCR_LENGTH_BIT) + 1); if (!fop->fn) { - printf("VFP: could not find single op %d, inst=0x%x@0x%x\n", FEXT_TO_IDX(inst), inst, state->Reg[15]); + LOG_CRITICAL(Core_ARM11, "could not find single op %d, inst=0x%x@0x%x", FEXT_TO_IDX(inst), inst, state->Reg[15]); exit(-1); goto invalid; } @@ -1255,17 +1255,17 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr) type = (fop->flags & OP_DD) ? 'd' : 's'; if (op == FOP_EXT) - pr_debug("VFP: itr%d (%c%u) = op[%u] (s%u=%08x)\n", - vecitr >> FPSCR_LENGTH_BIT, type, dest, sn, - sm, m); + LOG_DEBUG(Core_ARM11, "itr%d (%c%u) = op[%u] (s%u=%08x)", + vecitr >> FPSCR_LENGTH_BIT, type, dest, sn, + sm, m); else - pr_debug("VFP: itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)\n", - vecitr >> FPSCR_LENGTH_BIT, type, dest, sn, - FOP_TO_IDX(op), sm, m); + LOG_DEBUG(Core_ARM11, "itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)", + vecitr >> FPSCR_LENGTH_BIT, type, dest, sn, + FOP_TO_IDX(op), sm, m); except = fop->fn(state, dest, sn, m, fpscr); - pr_debug("VFP: itr%d: exceptions=%08x\n", - vecitr >> FPSCR_LENGTH_BIT, except); + LOG_DEBUG(Core_ARM11, "itr%d: exceptions=%08x", + vecitr >> FPSCR_LENGTH_BIT, except); exceptions |= except; diff --git a/src/core/core.cpp b/src/core/core.cpp index 15787bc17..79038cd52 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/common_types.h" +#include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" @@ -22,9 +23,9 @@ ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core /// Run the core CPU loop void RunLoop(int tight_loop) { - // If the current thread is an idle thread, then don't execute instructions, + // If we don't have a currently active thread then don't execute instructions, // instead advance to the next event and try to yield to the next thread - if (Kernel::GetCurrentThread()->IsIdle()) { + if (Kernel::GetCurrentThread() == nullptr) { LOG_TRACE(Core_ARM11, "Idling"); CoreTiming::Idle(); CoreTiming::Advance(); 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 { u32 fpu_registers[32]; u32 fpscr; u32 fpexc; - - // These are not part of native ThreadContext, but needed by emu - u32 mode; }; extern 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() { last_global_time_us = 0; has_ts_events = 0; mhz_change_callbacks.clear(); + + first = nullptr; + ts_first = nullptr; + ts_last = nullptr; + + event_pool = nullptr; + event_ts_pool = nullptr; + allocated_ts_events = 0; + + advance_callback = nullptr; } void Shutdown() { diff --git a/src/core/core_timing.h b/src/core/core_timing.h index d62ff3604..01519608d 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -21,7 +21,7 @@ #include <functional> -#include "common/common.h" +#include "common/common_types.h" extern int g_clock_rate_arm11; diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp new file mode 100644 index 000000000..45a559ce8 --- /dev/null +++ b/src/core/file_sys/archive_backend.cpp @@ -0,0 +1,127 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <sstream> + +#include "common/logging/log.h" +#include "common/string_util.h" + +#include "core/file_sys/archive_backend.h" +#include "core/memory.h" + + +namespace FileSys { + +Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) { + switch (type) { + case Binary: + { + u8* data = Memory::GetPointer(pointer); + binary = std::vector<u8>(data, data + size); + break; + } + + case Char: + { + const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer)); + string = std::string(data, size - 1); // Data is always null-terminated. + break; + } + + case Wchar: + { + const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer)); + u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated. + break; + } + + default: + break; + } +} + +const std::string Path::DebugStr() const { + switch (GetType()) { + case Invalid: + default: + return "[Invalid]"; + case Empty: + return "[Empty]"; + case Binary: + { + std::stringstream res; + res << "[Binary: "; + for (unsigned byte : binary) + res << std::hex << std::setw(2) << std::setfill('0') << byte; + res << ']'; + return res.str(); + } + case Char: + return "[Char: " + AsString() + ']'; + case Wchar: + return "[Wchar: " + AsString() + ']'; + } +} + +const std::string Path::AsString() const { + switch (GetType()) { + case Char: + return string; + case Wchar: + return Common::UTF16ToUTF8(u16str); + case Empty: + return{}; + case Invalid: + case Binary: + default: + // TODO(yuriks): Add assert + LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); + return{}; + } +} + +const std::u16string Path::AsU16Str() const { + switch (GetType()) { + case Char: + return Common::UTF8ToUTF16(string); + case Wchar: + return u16str; + case Empty: + return{}; + case Invalid: + case Binary: + // TODO(yuriks): Add assert + LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); + return{}; + } +} + +const std::vector<u8> Path::AsBinary() const { + switch (GetType()) { + case Binary: + return binary; + case Char: + return std::vector<u8>(string.begin(), string.end()); + case Wchar: + { + // use two u8 for each character of u16str + std::vector<u8> to_return(u16str.size() * 2); + for (size_t i = 0; i < u16str.size(); ++i) { + u16 tmp_char = u16str.at(i); + to_return[i*2] = (tmp_char & 0xFF00) >> 8; + to_return[i*2 + 1] = (tmp_char & 0x00FF); + } + return to_return; + } + case Empty: + return{}; + case Invalid: + default: + // TODO(yuriks): Add assert + LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); + return{}; + } +} + +} diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h index 43a106549..c6a1be79d 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/archive_backend.h @@ -5,22 +5,21 @@ #pragma once #include <memory> +#include <string> +#include <utility> +#include <vector> -#include "common/common_types.h" -#include "common/string_util.h" #include "common/bit_field.h" +#include "common/common_types.h" -#include "core/file_sys/file_backend.h" -#include "core/file_sys/directory_backend.h" - -#include "core/mem_map.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace namespace FileSys { +class FileBackend; +class DirectoryBackend; + // Path string type enum LowPathType : u32 { Invalid = 0, @@ -39,134 +38,22 @@ union Mode { class Path { public: + Path() : type(Invalid) {} + Path(const char* path) : type(Char), string(path) {} + Path(std::vector<u8> binary_data) : type(Binary), binary(std::move(binary_data)) {} + Path(LowPathType type, u32 size, u32 pointer); - Path() : type(Invalid) { - } - - Path(const char* path) : type(Char), string(path) { - } - - Path(std::vector<u8> binary_data) : type(Binary), binary(std::move(binary_data)) { - } - - Path(LowPathType type, u32 size, u32 pointer) : type(type) { - switch (type) { - case Binary: - { - u8* data = Memory::GetPointer(pointer); - binary = std::vector<u8>(data, data + size); - break; - } - - case Char: - { - const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer)); - string = std::string(data, size - 1); // Data is always null-terminated. - break; - } - - case Wchar: - { - const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer)); - u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated. - break; - } - - default: - break; - } - } - - LowPathType GetType() const { - return type; - } + LowPathType GetType() const { return type; } /** * Gets the string representation of the path for debugging * @return String representation of the path for debugging */ - const std::string DebugStr() const { - switch (GetType()) { - case Invalid: - default: - return "[Invalid]"; - case Empty: - return "[Empty]"; - case Binary: - { - std::stringstream res; - res << "[Binary: "; - for (unsigned byte : binary) - res << std::hex << std::setw(2) << std::setfill('0') << byte; - res << ']'; - return res.str(); - } - case Char: - return "[Char: " + AsString() + ']'; - case Wchar: - return "[Wchar: " + AsString() + ']'; - } - } + const std::string DebugStr() const; - const std::string AsString() const { - switch (GetType()) { - case Char: - return string; - case Wchar: - return Common::UTF16ToUTF8(u16str); - case Empty: - return {}; - case Invalid: - case Binary: - default: - // TODO(yuriks): Add assert - LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); - return {}; - } - } - - const std::u16string AsU16Str() const { - switch (GetType()) { - case Char: - return Common::UTF8ToUTF16(string); - case Wchar: - return u16str; - case Empty: - return {}; - case Invalid: - case Binary: - // TODO(yuriks): Add assert - LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); - return {}; - } - } - - const std::vector<u8> AsBinary() const { - switch (GetType()) { - case Binary: - return binary; - case Char: - return std::vector<u8>(string.begin(), string.end()); - case Wchar: - { - // use two u8 for each character of u16str - std::vector<u8> to_return(u16str.size() * 2); - for (size_t i = 0; i < u16str.size(); ++i) { - u16 tmp_char = u16str.at(i); - to_return[i*2] = (tmp_char & 0xFF00) >> 8; - to_return[i*2 + 1] = (tmp_char & 0x00FF); - } - return to_return; - } - case Empty: - return {}; - case Invalid: - default: - // TODO(yuriks): Add assert - LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); - return {}; - } - } + const std::string AsString() const; + const std::u16string AsU16Str() const; + const std::vector<u8> AsBinary() const; private: LowPathType type; diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index 3076fa263..38d498d0e 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "common/make_unique.h" #include "core/file_sys/archive_extsavedata.h" diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index bf54a3866..d4a12ed10 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "common/make_unique.h" #include "core/file_sys/archive_romfs.h" diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index 8496e06f3..8dff51966 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp @@ -6,10 +6,12 @@ #include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "common/make_unique.h" #include "core/file_sys/archive_savedata.h" #include "core/file_sys/disk_archive.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/fs/archive.h" #include "core/settings.h" @@ -35,7 +37,7 @@ ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directo } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) { - std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_program_id); + std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->program_id); if (!FileUtil::Exists(concrete_mount_point)) { // When a SaveData archive is created for the first time, it is not yet formatted // and the save file/directory structure expected by the game has not yet been initialized. @@ -50,7 +52,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P } ResultCode ArchiveFactory_SaveData::Format(const Path& path) { - std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_program_id); + std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->program_id); FileUtil::DeleteDirRecursively(concrete_mount_point); FileUtil::CreateFullPath(concrete_mount_point); return RESULT_SUCCESS; diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp index 47d8a9d25..e7e4fbf1d 100644 --- a/src/core/file_sys/archive_savedatacheck.cpp +++ b/src/core/file_sys/archive_savedatacheck.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/file_util.h" +#include "common/logging/log.h" #include "common/make_unique.h" #include "core/file_sys/archive_savedatacheck.h" diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 92b20c7f6..c1234a186 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "common/make_unique.h" #include "core/file_sys/archive_sdmc.h" diff --git a/src/core/file_sys/directory_backend.h b/src/core/file_sys/directory_backend.h index 7f327dc42..a25dc0cfa 100644 --- a/src/core/file_sys/directory_backend.h +++ b/src/core/file_sys/directory_backend.h @@ -4,12 +4,11 @@ #pragma once +#include <array> #include <cstddef> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" - //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp index f53fd57db..9980cced1 100644 --- a/src/core/file_sys/disk_archive.cpp +++ b/src/core/file_sys/disk_archive.cpp @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "common/make_unique.h" #include "core/file_sys/disk_archive.h" diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h index dbbdced74..a22d3837a 100644 --- a/src/core/file_sys/disk_archive.h +++ b/src/core/file_sys/disk_archive.h @@ -8,6 +8,8 @@ #include "common/file_util.h" #include "core/file_sys/archive_backend.h" +#include "core/file_sys/directory_backend.h" +#include "core/file_sys/file_backend.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -24,7 +26,7 @@ class DiskArchive : public ArchiveBackend { public: DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {} - virtual std::string GetName() const { return "DiskArchive: " + mount_point; } + virtual std::string GetName() const override { return "DiskArchive: " + mount_point; } std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; bool DeleteFile(const Path& path) const override; diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h index 35890af1f..0fcff1845 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/file_backend.h @@ -6,8 +6,6 @@ #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" - //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp index 35aca54fa..2d2509d16 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/ivfc_archive.cpp @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "common/make_unique.h" #include "core/file_sys/ivfc_archive.h" diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h index 1aff9e0a4..10415798d 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/ivfc_archive.h @@ -10,6 +10,8 @@ #include "common/common_types.h" #include "core/file_sys/archive_backend.h" +#include "core/file_sys/directory_backend.h" +#include "core/file_sys/file_backend.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp index 40bae9346..aea936d2d 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.cpp @@ -2,71 +2,30 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> + +#include "common/assert.h" #include "common/common_types.h" #include "common/common_funcs.h" #include "core/core.h" -#include "core/mem_map.h" +#include "core/memory.h" #include "core/hle/config_mem.h" //////////////////////////////////////////////////////////////////////////////////////////////////// namespace ConfigMem { -struct ConfigMemDef { - u8 kernel_unk; // 0 - u8 kernel_version_rev; // 1 - u8 kernel_version_min; // 2 - u8 kernel_version_maj; // 3 - u32 update_flag; // 4 - u64 ns_tid; // 8 - u32 sys_core_ver; // 10 - u8 unit_info; // 14 - u8 boot_firm; // 15 - u8 prev_firm; // 16 - INSERT_PADDING_BYTES(0x1); // 17 - u32 ctr_sdk_ver; // 18 - INSERT_PADDING_BYTES(0x30 - 0x1C); // 1C - u32 app_mem_type; // 30 - INSERT_PADDING_BYTES(0x40 - 0x34); // 34 - u32 app_mem_alloc; // 40 - u32 sys_mem_alloc; // 44 - u32 base_mem_alloc; // 48 - INSERT_PADDING_BYTES(0x60 - 0x4C); // 4C - u8 firm_unk; // 60 - u8 firm_version_rev; // 61 - u8 firm_version_min; // 62 - u8 firm_version_maj; // 63 - u32 firm_sys_core_ver; // 64 - u32 firm_ctr_sdk_ver; // 68 - INSERT_PADDING_BYTES(0x1000 - 0x6C); // 6C -}; - -static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE, "Config Memory structure size is wrong"); - -static ConfigMemDef config_mem; - -template <typename T> -inline void Read(T &var, const u32 addr) { - u32 offset = addr - Memory::CONFIG_MEMORY_VADDR; - ASSERT(offset < Memory::CONFIG_MEMORY_SIZE); - var = *(reinterpret_cast<T*>(((uintptr_t)&config_mem) + offset)); -} - -// Explicitly instantiate template functions because we aren't defining this in the header: - -template void Read<u64>(u64 &var, const u32 addr); -template void Read<u32>(u32 &var, const u32 addr); -template void Read<u16>(u16 &var, const u32 addr); -template void Read<u8>(u8 &var, const u32 addr); +ConfigMemDef config_mem; void Init() { + std::memset(&config_mem, 0, sizeof(config_mem)); + config_mem.update_flag = 0; // No update config_mem.sys_core_ver = 0x2; config_mem.unit_info = 0x1; // Bit 0 set for Retail config_mem.prev_firm = 0; config_mem.app_mem_type = 0x2; // Default app mem type is 0 - config_mem.unit_info = 0x1; // Bit 0 set for Retail config_mem.app_mem_alloc = 0x06000000; // Set to 96MB, since some games use more than the default (64MB) config_mem.base_mem_alloc = 0x01400000; // Default base memory is 20MB config_mem.sys_mem_alloc = Memory::FCRAM_SIZE - (config_mem.app_mem_alloc + config_mem.base_mem_alloc); @@ -77,4 +36,7 @@ void Init() { config_mem.firm_sys_core_ver = 0x2; } +void Shutdown() { +} + } // namespace diff --git a/src/core/hle/config_mem.h b/src/core/hle/config_mem.h index 94853901a..9825a09e8 100644 --- a/src/core/hle/config_mem.h +++ b/src/core/hle/config_mem.h @@ -9,15 +9,49 @@ // bootrom. Because we're not emulating this, and essentially just "stubbing" the functionality, I'm // putting this as a subset of HLE for now. +#include "common/common_funcs.h" #include "common/common_types.h" +#include "common/swap.h" + +#include "core/memory.h" //////////////////////////////////////////////////////////////////////////////////////////////////// namespace ConfigMem { -template <typename T> -void Read(T &var, const u32 addr); +struct ConfigMemDef { + u8 kernel_unk; // 0 + u8 kernel_version_rev; // 1 + u8 kernel_version_min; // 2 + u8 kernel_version_maj; // 3 + u32_le update_flag; // 4 + u64_le ns_tid; // 8 + u32_le sys_core_ver; // 10 + u8 unit_info; // 14 + u8 boot_firm; // 15 + u8 prev_firm; // 16 + INSERT_PADDING_BYTES(0x1); // 17 + u32_le ctr_sdk_ver; // 18 + INSERT_PADDING_BYTES(0x30 - 0x1C); // 1C + u32_le app_mem_type; // 30 + INSERT_PADDING_BYTES(0x40 - 0x34); // 34 + u32_le app_mem_alloc; // 40 + u32_le sys_mem_alloc; // 44 + u32_le base_mem_alloc; // 48 + INSERT_PADDING_BYTES(0x60 - 0x4C); // 4C + u8 firm_unk; // 60 + u8 firm_version_rev; // 61 + u8 firm_version_min; // 62 + u8 firm_version_maj; // 63 + u32_le firm_sys_core_ver; // 64 + u32_le firm_ctr_sdk_ver; // 68 + INSERT_PADDING_BYTES(0x1000 - 0x6C); // 6C +}; +static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE, "Config Memory structure size is wrong"); + +extern ConfigMemDef config_mem; void Init(); +void Shutdown(); } // namespace diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 0b6b6f518..eb52c8fb1 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -7,7 +7,7 @@ #include "common/common_types.h" #include "core/arm/arm_interface.h" -#include "core/mem_map.h" +#include "core/memory.h" #include "core/hle/hle.h" namespace HLE { @@ -46,6 +46,13 @@ template<ResultCode func(u32*, u32, u32, u32, u32, u32)> void Wrap(){ FuncReturn(retval); } +template<ResultCode func(u32*, s32, u32, u32, u32, s32)> void Wrap() { + u32 param_1 = 0; + u32 retval = func(¶m_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw; + Core::g_app_core->SetReg(1, param_1); + FuncReturn(retval); +} + template<ResultCode func(s32*, u32*, s32, bool, s64)> void Wrap() { s32 param_1 = 0; s32 retval = func(¶m_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), @@ -102,7 +109,7 @@ template<ResultCode func(s64*, u32, void*, s32)> void Wrap(){ template<ResultCode func(u32*, const char*)> void Wrap() { u32 param_1 = 0; - u32 retval = func(¶m_1, Memory::GetCharPointer(PARAM(1))).raw; + u32 retval = func(¶m_1, (char*)Memory::GetPointer(PARAM(1))).raw; Core::g_app_core->SetReg(1, param_1); FuncReturn(retval); } @@ -156,7 +163,7 @@ template<void func(s64)> void Wrap() { } template<void func(const char*)> void Wrap() { - func(Memory::GetCharPointer(PARAM(0))); + func((char*)Memory::GetPointer(PARAM(0))); } #undef PARAM 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <vector> - -#include "common/profiler.h" +#include "common/assert.h" +#include "common/logging/log.h" #include "core/arm/arm_interface.h" -#include "core/mem_map.h" +#include "core/core.h" #include "core/hle/hle.h" #include "core/hle/config_mem.h" #include "core/hle/shared_page.h" @@ -18,35 +17,7 @@ namespace HLE { -Common::Profiling::TimingCategory profiler_svc("SVC Calls"); - -static std::vector<ModuleDef> g_module_db; - -bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a new thread - -static const FunctionDef* GetSVCInfo(u32 opcode) { - u32 func_num = opcode & 0xFFFFFF; // 8 bits - if (func_num > 0xFF) { - LOG_ERROR(Kernel_SVC,"unknown svc=0x%02X", func_num); - return nullptr; - } - return &g_module_db[0].func_table[func_num]; -} - -void CallSVC(u32 opcode) { - Common::Profiling::ScopeTimer timer_svc(profiler_svc); - - const FunctionDef *info = GetSVCInfo(opcode); - - if (!info) { - return; - } - if (info->func) { - info->func(); - } else { - LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name.c_str()); - } -} +bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread void Reschedule(const char *reason) { DEBUG_ASSERT_MSG(reason != nullptr && strlen(reason) < 256, "Reschedule: Invalid or too long reason."); @@ -62,31 +33,21 @@ void Reschedule(const char *reason) { g_reschedule = true; } -void RegisterModule(std::string name, int num_functions, const FunctionDef* func_table) { - ModuleDef module = {name, num_functions, func_table}; - g_module_db.push_back(module); -} - -static void RegisterAllModules() { - SVC::Register(); -} - void Init() { Service::Init(); - - RegisterAllModules(); - ConfigMem::Init(); SharedPage::Init(); + g_reschedule = false; + LOG_DEBUG(Kernel, "initialized OK"); } void Shutdown() { + ConfigMem::Shutdown(); + SharedPage::Shutdown(); Service::Shutdown(); - g_module_db.clear(); - LOG_DEBUG(Kernel, "shutdown OK"); } diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h index 3f6f9a4b5..e0b97797c 100644 --- a/src/core/hle/hle.h +++ b/src/core/hle/hle.h @@ -4,40 +4,20 @@ #pragma once -#include <string> - #include "common/common_types.h" -#include "core/core.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// +typedef u32 Handle; +typedef s32 Result; + +const Handle INVALID_HANDLE = 0; namespace HLE { extern bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread -typedef u32 Addr; -typedef void (*Func)(); - -struct FunctionDef { - u32 id; - Func func; - std::string name; -}; - -struct ModuleDef { - std::string name; - int num_funcs; - const FunctionDef* func_table; -}; - -void RegisterModule(std::string name, int num_functions, const FunctionDef *func_table); - -void CallSVC(u32 opcode); - void Reschedule(const char *reason); void Init(); - void Shutdown(); } // namespace diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 42f8ce2d9..a1221766e 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -3,8 +3,9 @@ // Refer to the license.txt file included. #include "common/common_types.h" +#include "common/logging/log.h" -#include "core/mem_map.h" +#include "core/memory.h" #include "core/hle/hle.h" #include "core/hle/kernel/address_arbiter.h" @@ -46,14 +47,12 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, case ArbitrationType::WaitIfLessThan: if ((s32)Memory::Read32(address) <= value) { Kernel::WaitCurrentThread_ArbitrateAddress(address); - HLE::Reschedule(__func__); } break; case ArbitrationType::WaitIfLessThanWithTimeout: if ((s32)Memory::Read32(address) <= value) { Kernel::WaitCurrentThread_ArbitrateAddress(address); GetCurrentThread()->WakeAfterDelay(nanoseconds); - HLE::Reschedule(__func__); } break; case ArbitrationType::DecrementAndWaitIfLessThan: @@ -62,7 +61,6 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, Memory::Write32(address, memory_value); if (memory_value <= value) { Kernel::WaitCurrentThread_ArbitrateAddress(address); - HLE::Reschedule(__func__); } break; } @@ -73,7 +71,6 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, if (memory_value <= value) { Kernel::WaitCurrentThread_ArbitrateAddress(address); GetCurrentThread()->WakeAfterDelay(nanoseconds); - HLE::Reschedule(__func__); } break; } diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 420906ec0..f338f3266 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -6,7 +6,7 @@ #include <algorithm> #include <vector> -#include "common/common.h" +#include "common/assert.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/event.h" diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 498b2ec98..b5c98b249 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -4,21 +4,20 @@ #include <algorithm> -#include "common/common.h" +#include "common/assert.h" +#include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" namespace Kernel { -unsigned int Object::next_object_id = 0; - -SharedPtr<Thread> g_main_thread = nullptr; +unsigned int Object::next_object_id; HandleTable g_handle_table; -u64 g_program_id = 0; void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); @@ -116,8 +115,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { if (handle == CurrentThread) { return GetCurrentThread(); } else if (handle == CurrentProcess) { - LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess); - return nullptr; + return g_current_process; } if (!IsValid(handle)) { @@ -138,6 +136,11 @@ void HandleTable::Clear() { void Init() { Kernel::ThreadingInit(); Kernel::TimersInit(); + + Object::next_object_id = 0; + // TODO(Subv): Start the process ids from 10 for now, as lower PIDs are + // reserved for low-level services + Process::next_process_id = 10; } /// Shutdown the kernel @@ -145,18 +148,7 @@ void Shutdown() { Kernel::ThreadingShutdown(); Kernel::TimersShutdown(); g_handle_table.Clear(); // Free all kernel objects -} - -/** - * Loads executable stored at specified address - * @entry_point Entry point in memory of loaded executable - * @return True on success, otherwise false - */ -bool LoadExec(u32 entry_point) { - // 0x30 is the typical main thread priority I've seen used so far - g_main_thread = Kernel::SetupMainThread(Kernel::DEFAULT_STACK_SIZE, entry_point, 0x30); - - return true; + g_current_process = nullptr; } } // namespace diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 2d295ea00..7c106d37c 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -7,21 +7,16 @@ #include <boost/intrusive_ptr.hpp> #include <array> +#include <memory> #include <string> #include <vector> -#include "common/common.h" -#include "core/hle/result.h" - -typedef u32 Handle; -typedef s32 Result; +#include "common/common_types.h" -// TODO: It would be nice to eventually replace these with strong types that prevent accidental -// conversion between each other. -typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space. -typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space. +#include "core/hle/hle.h" +#include "core/hle/result.h" -const Handle INVALID_HANDLE = 0; +struct ApplicationInfo; namespace Kernel { @@ -95,12 +90,13 @@ public: return false; } +public: + static unsigned int next_object_id; + private: friend void intrusive_ptr_add_ref(Object*); friend void intrusive_ptr_release(Object*); - static unsigned int next_object_id; - unsigned int ref_count = 0; unsigned int object_id = next_object_id++; }; @@ -277,23 +273,10 @@ private: extern HandleTable g_handle_table; -/// The ID code of the currently running game -/// TODO(Subv): This variable should not be here, -/// we need a way to store information about the currently loaded application -/// for later query during runtime, maybe using the LDR service? -extern u64 g_program_id; - /// Initialize the kernel void Init(); /// Shutdown the kernel void Shutdown(); -/** - * Loads executable stored at specified address - * @entry_point Entry point in memory of loaded executable - * @return True on success, otherwise false - */ -bool LoadExec(u32 entry_point); - } // namespace diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index be2c49706..f530217fd 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -7,7 +7,7 @@ #include <boost/range/algorithm_ext/erase.hpp> -#include "common/common.h" +#include "common/assert.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" @@ -56,7 +56,15 @@ SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { } bool Mutex::ShouldWait() { - return lock_count > 0 && holding_thread != GetCurrentThread();; + auto thread = GetCurrentThread(); + bool wait = lock_count > 0 && holding_thread != thread; + + // If the holding thread of the mutex is lower priority than this thread, that thread should + // temporarily inherit this thread's priority + if (wait && thread->current_priority < holding_thread->current_priority) + holding_thread->BoostPriority(thread->current_priority); + + return wait; } void Mutex::Acquire() { diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp new file mode 100644 index 000000000..0cdfa58d7 --- /dev/null +++ b/src/core/hle/kernel/process.cpp @@ -0,0 +1,98 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_funcs.h" +#include "common/logging/log.h" + +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/thread.h" +#include "core/memory.h" + +namespace Kernel { + +u32 Process::next_process_id; + +SharedPtr<Process> Process::Create(std::string name, u64 program_id) { + SharedPtr<Process> process(new Process); + + process->name = std::move(name); + process->program_id = program_id; + + process->flags.raw = 0; + process->flags.memory_region = MemoryRegion::APPLICATION; + + return process; +} + +void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { + for (int i = 0; i < len; ++i) { + u32 descriptor = kernel_caps[i]; + u32 type = descriptor >> 20; + + if (descriptor == 0xFFFFFFFF) { + // Unused descriptor entry + continue; + } else if ((type & 0xF00) == 0xE00) { // 0x0FFF + // Allowed interrupts list + LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored"); + } else if ((type & 0xF80) == 0xF00) { // 0x07FF + // Allowed syscalls mask + unsigned int index = ((descriptor >> 24) & 7) * 24; + u32 bits = descriptor & 0xFFFFFF; + + while (bits && index < svc_access_mask.size()) { + svc_access_mask.set(index, bits & 1); + ++index; bits >>= 1; + } + } else if ((type & 0xFF0) == 0xFE0) { // 0x00FF + // Handle table size + handle_table_size = descriptor & 0x3FF; + } else if ((type & 0xFF8) == 0xFF0) { // 0x007F + // Misc. flags + flags.raw = descriptor & 0xFFFF; + } else if ((type & 0xFFE) == 0xFF8) { // 0x001F + // Mapped memory range + if (i+1 >= len || ((kernel_caps[i+1] >> 20) & 0xFFE) != 0xFF8) { + LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored."); + continue; + } + u32 end_desc = kernel_caps[i+1]; + ++i; // Skip over the second descriptor on the next iteration + + AddressMapping mapping; + mapping.address = descriptor << 12; + mapping.size = (end_desc << 12) - mapping.address; + mapping.writable = descriptor & (1 << 20); + mapping.unk_flag = end_desc & (1 << 20); + + address_mappings.push_back(mapping); + } else if ((type & 0xFFF) == 0xFFE) { // 0x000F + // Mapped memory page + AddressMapping mapping; + mapping.address = descriptor << 12; + mapping.size = Memory::PAGE_SIZE; + mapping.writable = true; // TODO: Not sure if correct + mapping.unk_flag = false; + } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF + // Kernel version + int minor = descriptor & 0xFF; + int major = (descriptor >> 8) & 0xFF; + LOG_INFO(Loader, "ExHeader kernel version ignored: %d.%d", major, minor); + } else { + LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x%08X", descriptor); + } + } +} + +void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { + Kernel::SetupMainThread(entry_point, main_thread_priority); +} + +Kernel::Process::Process() {} +Kernel::Process::~Process() {} + +SharedPtr<Process> g_current_process; + +} diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h new file mode 100644 index 000000000..90881054c --- /dev/null +++ b/src/core/hle/kernel/process.h @@ -0,0 +1,98 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <bitset> + +#include <boost/container/static_vector.hpp> + +#include "common/bit_field.h" +#include "common/common_types.h" + +#include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" + +namespace Kernel { + +struct AddressMapping { + // Address and size must be page-aligned + VAddr address; + u32 size; + bool writable; + bool unk_flag; +}; + +enum class MemoryRegion : u16 { + APPLICATION = 1, + SYSTEM = 2, + BASE = 3, +}; + +union ProcessFlags { + u16 raw; + + BitField< 0, 1, u16> allow_debug; ///< Allows other processes to attach to and debug this process. + BitField< 1, 1, u16> force_debug; ///< Allows this process to attach to processes even if they don't have allow_debug set. + BitField< 2, 1, u16> allow_nonalphanum; + BitField< 3, 1, u16> shared_page_writable; ///< Shared page is mapped with write permissions. + BitField< 4, 1, u16> privileged_priority; ///< Can use priority levels higher than 24. + BitField< 5, 1, u16> allow_main_args; + BitField< 6, 1, u16> shared_device_mem; + BitField< 7, 1, u16> runnable_on_sleep; + BitField< 8, 4, MemoryRegion> memory_region; ///< Default region for memory allocations for this process + BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000). +}; + +class Process final : public Object { +public: + static SharedPtr<Process> Create(std::string name, u64 program_id); + + std::string GetTypeName() const override { return "Process"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Process; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + static u32 next_process_id; + + /// Name of the process + std::string name; + /// Title ID corresponding to the process + u64 program_id; + + /// The process may only call SVCs which have the corresponding bit set. + std::bitset<0x80> svc_access_mask; + /// Maximum size of the handle table for the process. + unsigned int handle_table_size = 0x200; + /// Special memory ranges mapped into this processes address space. This is used to give + /// processes access to specific I/O regions and device memory. + boost::container::static_vector<AddressMapping, 8> address_mappings; + ProcessFlags flags; + + /// The id of this process + u32 process_id = next_process_id++; + + /// Bitmask of the used TLS slots + std::bitset<300> used_tls_slots; + + /** + * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them + * to this process. + */ + void ParseKernelCaps(const u32* kernel_caps, size_t len); + + /** + * Applies address space changes and launches the process main thread. + */ + void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); + +private: + Process(); + ~Process() override; +}; + +extern SharedPtr<Process> g_current_process; + +} diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 6aecc24aa..5d6543ef4 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common.h" +#include "common/assert.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/semaphore.h" diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 9e9288e0f..54a062971 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -5,19 +5,23 @@ #pragma once #include "core/hle/kernel/kernel.h" -#include "core/mem_map.h" +#include "core/hle/kernel/thread.h" +#include "core/memory.h" namespace Kernel { static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header /** - * Returns a pointer to the command buffer in kernel memory + * Returns a pointer to the command buffer in the current thread's TLS + * TODO(Subv): This is not entirely correct, the command buffer should be copied from + * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to + * the service handler process' memory. * @param offset Optional offset into command buffer * @return Pointer to command buffer */ -inline static u32* GetCommandBuffer(const int offset=0) { - return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset); +inline static u32* GetCommandBuffer(const int offset = 0) { + return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + offset); } /** diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 4211fcf04..4137683b5 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -2,9 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common.h" +#include <cstring> -#include "core/mem_map.h" +#include "common/logging/log.h" + +#include "core/memory.h" #include "core/hle/kernel/shared_memory.h" namespace Kernel { @@ -12,10 +14,15 @@ namespace Kernel { SharedMemory::SharedMemory() {} SharedMemory::~SharedMemory() {} -SharedPtr<SharedMemory> SharedMemory::Create(std::string name) { +SharedPtr<SharedMemory> SharedMemory::Create(u32 size, MemoryPermission permissions, + MemoryPermission other_permissions, std::string name) { SharedPtr<SharedMemory> shared_memory(new SharedMemory); shared_memory->name = std::move(name); + shared_memory->base_address = 0x0; + shared_memory->size = size; + shared_memory->permissions = permissions; + shared_memory->other_permissions = other_permissions; return shared_memory; } @@ -23,7 +30,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(std::string name) { ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions) { - if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { + if (address < Memory::SHARED_MEMORY_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) { LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X outside of shared mem bounds!", GetObjectId(), address); // TODO: Verify error code with hardware @@ -31,21 +38,25 @@ ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); } + // TODO: Test permissions + + // HACK: Since there's no way to write to the memory block without mapping it onto the game + // process yet, at least initialize memory the first time it's mapped. + if (address != this->base_address) { + std::memset(Memory::GetPointer(address), 0, size); + } + this->base_address = address; - this->permissions = permissions; - this->other_permissions = other_permissions; return RESULT_SUCCESS; } -ResultVal<u8*> SharedMemory::GetPointer(u32 offset) { +u8* SharedMemory::GetPointer(u32 offset) { if (base_address != 0) - return MakeResult<u8*>(Memory::GetPointer(base_address + offset)); + return Memory::GetPointer(base_address + offset); LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId()); - // TODO(yuriks): Verify error code. - return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, - ErrorSummary::InvalidState, ErrorLevel::Permanent); + return nullptr; } } // namespace diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 5833b411c..204266896 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -27,11 +27,16 @@ class SharedMemory final : public Object { public: /** * Creates a shared memory object - * @param name Optional object name, used only for debugging purposes. + * @param size Size of the memory block. Must be page-aligned. + * @param permissions Permission restrictions applied to the process which created the block. + * @param other_permissions Permission restrictions applied to other processes mapping the block. + * @param name Optional object name, used for debugging purposes. */ - static SharedPtr<SharedMemory> Create(std::string name = "Unknown"); + static SharedPtr<SharedMemory> Create(u32 size, MemoryPermission permissions, + MemoryPermission other_permissions, std::string name = "Unknown"); std::string GetTypeName() const override { return "SharedMemory"; } + std::string GetName() const override { return name; } static const HandleType HANDLE_TYPE = HandleType::SharedMemory; HandleType GetHandleType() const override { return HANDLE_TYPE; } @@ -49,12 +54,18 @@ public: * @param offset Offset from the start of the shared memory block to get pointer * @return Pointer to the shared memory block from the specified offset */ - ResultVal<u8*> GetPointer(u32 offset = 0); + u8* GetPointer(u32 offset = 0); - VAddr base_address; ///< Address of shared memory block in RAM - MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) - MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field) - std::string name; ///< Name of shared memory object (optional) + /// Address of shared memory block in the process. + VAddr base_address; + /// Size of the memory block. Page-aligned. + u32 size; + /// Permission restrictions applied to the process which created the block. + MemoryPermission permissions; + /// Permission restrictions applied to other processes mapping the block. + MemoryPermission other_permissions; + /// Name of shared memory object. + std::string name; private: SharedMemory(); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index be1aed615..a5f1904d7 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -6,7 +6,9 @@ #include <list> #include <vector> -#include "common/common.h" +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" #include "common/math_util.h" #include "common/thread_queue_list.h" @@ -15,15 +17,16 @@ #include "core/core_timing.h" #include "core/hle/hle.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/mutex.h" #include "core/hle/result.h" -#include "core/mem_map.h" +#include "core/memory.h" namespace Kernel { /// Event type for the thread wake up event -static int ThreadWakeupEventType = -1; +static int ThreadWakeupEventType; bool Thread::ShouldWait() { return status != THREADSTATUS_DEAD; @@ -42,7 +45,7 @@ static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> ready_queue; static Thread* current_thread; // The first available thread id at startup -static u32 next_thread_id = 1; +static u32 next_thread_id; /** * Creates a new thread ID @@ -104,6 +107,8 @@ void Thread::Stop() { for (auto& wait_object : wait_objects) { wait_object->RemoveWaitingThread(this); } + + Kernel::g_current_process->used_tls_slots[tls_index] = false; } Thread* ArbitrateHighestPriorityThread(u32 address) { @@ -140,17 +145,38 @@ void ArbitrateAllThreads(u32 address) { } } +/// Boost low priority threads (temporarily) that have been starved +static void PriorityBoostStarvedThreads() { + u64 current_ticks = CoreTiming::GetTicks(); + + for (auto& thread : thread_list) { + // TODO(bunnei): Threads that have been waiting to be scheduled for `boost_ticks` (or + // longer) will have their priority temporarily adjusted to 1 higher than the highest + // priority thread to prevent thread starvation. This general behavior has been verified + // on hardware. However, this is almost certainly not perfect, and the real CTR OS scheduler + // should probably be reversed to verify this. + + const u64 boost_timeout = 2000000; // Boost threads that have been ready for > this long + + u64 delta = current_ticks - thread->last_running_ticks; + + if (thread->status == THREADSTATUS_READY && delta > boost_timeout) { + const s32 priority = std::max(ready_queue.get_first()->current_priority - 1, 0); + thread->BoostPriority(priority); + } + } +} + /** * Switches the CPU's active thread context to that of the specified thread * @param new_thread The thread to switch to */ static void SwitchContext(Thread* new_thread) { - DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); - Thread* previous_thread = GetCurrentThread(); // Save context for previous thread if (previous_thread) { + previous_thread->last_running_ticks = CoreTiming::GetTicks(); Core::g_app_core->SaveContext(previous_thread->context); if (previous_thread->status == THREADSTATUS_RUNNING) { @@ -163,12 +189,18 @@ static void SwitchContext(Thread* new_thread) { // Load context of new thread if (new_thread) { + DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); + current_thread = new_thread; ready_queue.remove(new_thread->current_priority, new_thread); new_thread->status = THREADSTATUS_RUNNING; + // Restores thread to its nominal priority if it has been temporarily changed + new_thread->current_priority = new_thread->nominal_priority; + Core::g_app_core->LoadContext(new_thread->context); + Core::g_app_core->SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); } else { current_thread = nullptr; } @@ -186,6 +218,10 @@ static Thread* PopNextReadyThread() { // We have to do better than the current thread. // This call returns null when that's not possible. next = ready_queue.pop_first_better(thread->current_priority); + if (!next) { + // Otherwise just keep going with the current thread + next = thread; + } } else { next = ready_queue.pop_first(); } @@ -364,7 +400,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, thread->status = THREADSTATUS_DORMANT; thread->entry_point = entry_point; thread->stack_top = stack_top; - thread->initial_priority = thread->current_priority = priority; + thread->nominal_priority = thread->current_priority = priority; + thread->last_running_ticks = CoreTiming::GetTicks(); thread->processor_id = processor_id; thread->wait_set_output = false; thread->wait_all = false; @@ -372,6 +409,20 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, thread->wait_address = 0; thread->name = std::move(name); thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); + thread->owner_process = g_current_process; + thread->tls_index = -1; + + // Find the next available TLS index, and mark it as used + auto& used_tls_slots = Kernel::g_current_process->used_tls_slots; + for (unsigned int i = 0; i < used_tls_slots.size(); ++i) { + if (used_tls_slots[i] == false) { + thread->tls_index = i; + used_tls_slots[i] = true; + break; + } + } + + ASSERT_MSG(thread->tls_index != -1, "Out of TLS space"); // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used // to initialize the context @@ -400,35 +451,26 @@ static void ClampPriority(const Thread* thread, s32* priority) { void Thread::SetPriority(s32 priority) { ClampPriority(this, &priority); - if (current_priority == priority) { - return; - } - - if (status == THREADSTATUS_READY) { - // If thread was ready, adjust queues - ready_queue.remove(current_priority, this); + // If thread was ready, adjust queues + if (status == THREADSTATUS_READY) + ready_queue.move(this, current_priority, priority); + else ready_queue.prepare(priority); - ready_queue.push_back(priority, this); - } - - current_priority = priority; -} -SharedPtr<Thread> SetupIdleThread() { - // We need to pass a few valid values to get around parameter checking in Thread::Create. - auto thread = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, - THREADPROCESSORID_0, 0).MoveFrom(); + nominal_priority = current_priority = priority; +} - thread->idle = true; - return thread; +void Thread::BoostPriority(s32 priority) { + ready_queue.move(this, current_priority, priority); + current_priority = priority; } -SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority) { +SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) { DEBUG_ASSERT(!GetCurrentThread()); // Initialize new "main" thread auto thread_res = Thread::Create("main", entry_point, priority, 0, - THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END); + THREADPROCESSORID_0, Memory::HEAP_VADDR_END); SharedPtr<Thread> thread = thread_res.MoveFrom(); @@ -439,21 +481,25 @@ SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority) } void Reschedule() { - Thread* prev = GetCurrentThread(); + PriorityBoostStarvedThreads(); + + Thread* cur = GetCurrentThread(); Thread* next = PopNextReadyThread(); HLE::g_reschedule = false; - if (next != nullptr) { - LOG_TRACE(Kernel, "context switch %u -> %u", prev->GetObjectId(), next->GetObjectId()); - SwitchContext(next); - } else { - LOG_TRACE(Kernel, "cannot context switch from %u, no higher priority thread!", prev->GetObjectId()); + // Don't bother switching to the same thread + if (next == cur) + return; - for (auto& thread : thread_list) { - LOG_TRACE(Kernel, "\tid=%u prio=0x%02X, status=0x%08X", thread->GetObjectId(), - thread->current_priority, thread->status); - } + if (cur && next) { + LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); + } else if (cur) { + LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); + } else if (next) { + LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); } + + SwitchContext(next); } void Thread::SetWaitSynchronizationResult(ResultCode result) { @@ -464,13 +510,20 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { context.cpu_registers[1] = output; } +VAddr Thread::GetTLSAddress() const { + return Memory::TLS_AREA_VADDR + tls_index * 0x200; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); - // Setup the idle thread - SetupIdleThread(); + current_thread = nullptr; + next_thread_id = 1; + + thread_list.clear(); + ready_queue.clear(); } void ThreadingShutdown() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index cfd073a70..389928178 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -12,22 +12,23 @@ #include "common/common_types.h" #include "core/core.h" -#include "core/mem_map.h" #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" -enum ThreadPriority { - THREADPRIO_HIGHEST = 0, ///< Highest thread priority - THREADPRIO_DEFAULT = 16, ///< Default thread priority for userland apps - THREADPRIO_LOW = 31, ///< Low range of thread priority for userland apps - THREADPRIO_LOWEST = 63, ///< Thread priority max checked by svcCreateThread +enum ThreadPriority : s32{ + THREADPRIO_HIGHEST = 0, ///< Highest thread priority + THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps + THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps + THREADPRIO_LOWEST = 63, ///< Lowest thread priority }; -enum ThreadProcessorId { - THREADPROCESSORID_0 = 0xFFFFFFFE, ///< Enables core appcode - THREADPROCESSORID_1 = 0xFFFFFFFD, ///< Enables core syscore - THREADPROCESSORID_ALL = 0xFFFFFFFC, ///< Enables both cores +enum ThreadProcessorId : s32 { + THREADPROCESSORID_DEFAULT = -2, ///< Run thread on default core specified by exheader + THREADPROCESSORID_ALL = -1, ///< Run thread on either core + THREADPROCESSORID_0 = 0, ///< Run thread on core 0 (AppCore) + THREADPROCESSORID_1 = 1, ///< Run thread on core 1 (SysCore) + THREADPROCESSORID_MAX = 2, ///< Processor ID must be less than this }; enum ThreadStatus { @@ -43,6 +44,7 @@ enum ThreadStatus { namespace Kernel { class Mutex; +class Process; class Thread final : public WaitObject { public: @@ -70,12 +72,6 @@ public: void Acquire() override; /** - * Checks if the thread is an idle (stub) thread - * @return True if the thread is an idle (stub) thread, false otherwise - */ - inline bool IsIdle() const { return idle; } - - /** * Gets the thread's current priority * @return The current thread's priority */ @@ -88,6 +84,12 @@ public: void SetPriority(s32 priority); /** + * Temporarily boosts the thread's priority until the next time it is scheduled + * @param priority The new priority + */ + void BoostPriority(s32 priority); + + /** * Gets the thread's thread ID * @return The thread's ID */ @@ -127,6 +129,12 @@ public: */ void Stop(); + /* + * Returns the Thread Local Storage address of the current thread + * @returns VAddr of the thread's TLS + */ + VAddr GetTLSAddress() const; + Core::ThreadContext context; u32 thread_id; @@ -135,14 +143,19 @@ public: u32 entry_point; u32 stack_top; - s32 initial_priority; - s32 current_priority; + s32 nominal_priority; ///< Nominal thread priority, as set by the emulated application + s32 current_priority; ///< Current thread priority, can be temporarily changed + + u64 last_running_ticks; ///< CPU tick when thread was last running s32 processor_id; + s32 tls_index; ///< Index of the Thread Local Storage of the thread + /// Mutexes currently held by this thread, which will be released when it exits. boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; + SharedPtr<Process> owner_process; ///< Process that owns this thread std::vector<SharedPtr<WaitObject>> wait_objects; ///< Objects that the thread is waiting on VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address bool wait_all; ///< True if the thread is waiting on all objects before resuming @@ -150,9 +163,6 @@ public: std::string name; - /// Whether this thread is intended to never actually be executed, i.e. always idle - bool idle = false; - private: Thread(); ~Thread() override; @@ -161,16 +171,13 @@ private: Handle callback_handle; }; -extern SharedPtr<Thread> g_main_thread; - /** * Sets up the primary application thread - * @param stack_size The size of the thread's stack * @param entry_point The address at which the thread should start execution * @param priority The priority to give the main thread * @return A shared pointer to the main thread */ -SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority); +SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority); /** * Reschedules to the next available thread (call after current thread is suspended) @@ -214,14 +221,6 @@ void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wa void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); /** - * Sets up the idle thread, this is a thread that is intended to never execute instructions, - * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue - * and will try to yield on every call. - * @return The handle of the idle thread - */ -SharedPtr<Thread> SetupIdleThread(); - -/** * Initialize threading */ void ThreadingInit(); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 610e26a3c..e69fece65 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -2,7 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common.h" +#include "common/assert.h" +#include "common/logging/log.h" #include "core/core_timing.h" #include "core/hle/kernel/kernel.h" @@ -12,7 +13,7 @@ namespace Kernel { /// The event type of the generic timer callback event -static int timer_callback_event_type = -1; +static int timer_callback_event_type; // TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future, allowing // us to simply use a pool index or similar. static Kernel::HandleTable timer_callback_handle_table; @@ -66,7 +67,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { SharedPtr<Timer> timer = timer_callback_handle_table.Get<Timer>(static_cast<Handle>(timer_handle)); if (timer == nullptr) { - LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08X", timer_handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08lX", timer_handle); return; } @@ -89,6 +90,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { } void TimersInit() { + timer_callback_handle_table.Clear(); timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback); } diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 3648a168b..ce633d841 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -8,6 +8,7 @@ #include <type_traits> #include <utility> +#include "common/assert.h" #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" diff --git a/src/core/hle/service/am_sys.cpp b/src/core/hle/service/am_sys.cpp index b244190a2..f9e3fe4b7 100644 --- a/src/core/hle/service/am_sys.cpp +++ b/src/core/hle/service/am_sys.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" + #include "core/hle/hle.h" #include "core/hle/service/am_sys.h" diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 5971f860b..09d463dd5 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -2,7 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/common_paths.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "core/hle/service/service.h" #include "core/hle/service/apt/apt.h" @@ -32,23 +34,31 @@ static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; static Kernel::SharedPtr<Kernel::Mutex> lock; static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event -static Kernel::SharedPtr<Kernel::Event> pause_event = 0; ///< APT pause event +static Kernel::SharedPtr<Kernel::Event> start_event; ///< APT start event + static std::vector<u8> shared_font; +static u32 cpu_percent; ///< CPU time available to the running application + void Initialize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 app_id = cmd_buff[1]; + u32 flags = cmd_buff[2]; + cmd_buff[2] = 0x04000000; // According to 3dbrew, this value should be 0x04000000 cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(pause_event).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(start_event).MoveFrom(); - // TODO(bunnei): Check if these events are cleared/signaled every time Initialize is called. + // TODO(bunnei): Check if these events are cleared every time Initialize is called. notification_event->Clear(); - pause_event->Signal(); // Fire start event + start_event->Clear(); ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); lock->Release(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_TRACE(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); } void GetSharedFont(Service::Interface* self) { @@ -74,7 +84,7 @@ void NotifyToWait(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further. - pause_event->Signal(); + start_event->Signal(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); @@ -190,7 +200,38 @@ void CancelParameter(Service::Interface* self) { cmd_buff[2] = 1; // Set to Success LOG_WARNING(Service_APT, "(STUBBED) called flag1=0x%08X, unk=0x%08X, flag2=0x%08X, app_id=0x%08X", - flag1, unk, flag2, app_id); + flag1, unk, flag2, app_id); +} + +void PrepareToStartApplication(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 title_info1 = cmd_buff[1]; + u32 title_info2 = cmd_buff[2]; + u32 title_info3 = cmd_buff[3]; + u32 title_info4 = cmd_buff[4]; + u32 flags = cmd_buff[5]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_APT, "(STUBBED) called title_info1=0x%08X, title_info2=0x%08X, title_info3=0x%08X," + "title_info4=0x%08X, flags=0x%08X", title_info1, title_info2, title_info3, title_info4, flags); +} + +void StartApplication(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 buffer1_size = cmd_buff[1]; + u32 buffer2_size = cmd_buff[2]; + u32 flag = cmd_buff[3]; + u32 size1 = cmd_buff[4]; + u32 buffer1_ptr = cmd_buff[5]; + u32 size2 = cmd_buff[6]; + u32 buffer2_ptr = cmd_buff[7]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_APT, "(STUBBED) called buffer1_size=0x%08X, buffer2_size=0x%08X, flag=0x%08X," + "size1=0x%08X, buffer1_ptr=0x%08X, size2=0x%08X, buffer2_ptr=0x%08X", + buffer1_size, buffer2_size, flag, size1, buffer1_ptr, size2, buffer2_ptr); } void AppletUtility(Service::Interface* self) { @@ -205,15 +246,15 @@ void AppletUtility(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, " - "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size, + LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " + "buffer1_addr=0x%08X, buffer2_addr=0x%08X", unk, buffer1_size, buffer2_size, buffer1_addr, buffer2_addr); } void SetAppCpuTimeLimit(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 value = cmd_buff[1]; - u32 percent = cmd_buff[2]; + u32 value = cmd_buff[1]; + cpu_percent = cmd_buff[2]; if (value != 1) { LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value); @@ -221,27 +262,26 @@ void SetAppCpuTimeLimit(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_APT, "(STUBBED) called percent=0x%08X, value=0x%08x", percent, value); + LOG_WARNING(Service_APT, "(STUBBED) called cpu_percent=%u, value=%u", cpu_percent, value); } void GetAppCpuTimeLimit(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 value = cmd_buff[1]; + ASSERT(cpu_percent != 0); + if (value != 1) { LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value); } - // TODO(purpasmart96): This is incorrect, I'm pretty sure the percentage should - // be set by the application. - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0x80; // Set to 80% + cmd_buff[2] = cpu_percent; - LOG_WARNING(Service_APT, "(STUBBED) called value=0x%08x", value); + LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value); } -void APTInit() { +void Init() { AddService(new APT_A_Interface); AddService(new APT_S_Interface); AddService(new APT_U_Interface); @@ -264,21 +304,29 @@ void APTInit() { file.ReadBytes(shared_font.data(), (size_t)file.GetSize()); // Create shared font memory object - shared_font_mem = Kernel::SharedMemory::Create("APT_U:shared_font_mem"); + using Kernel::MemoryPermission; + shared_font_mem = Kernel::SharedMemory::Create(3 * 1024 * 1024, // 3MB + MemoryPermission::ReadWrite, MemoryPermission::Read, "APT_U:shared_font_mem"); } else { LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str()); shared_font_mem = nullptr; } lock = Kernel::Mutex::Create(false, "APT_U:Lock"); - + + cpu_percent = 0; + // TODO(bunnei): Check if these are created in Initialize or on APT process startup. notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification"); - pause_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Pause"); + start_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Start"); } -void APTShutdown() { - +void Shutdown() { + shared_font.clear(); + shared_font_mem = nullptr; + lock = nullptr; + notification_event = nullptr; + start_event = nullptr; } } // namespace APT diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index a39adbff9..e7fa39325 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -13,10 +13,13 @@ namespace APT { /// Signals used by APT functions enum class SignalType : u32 { - None = 0x0, - AppJustStarted = 0x1, - ReturningToApp = 0xB, - ExitingApp = 0xC, + None = 0x0, + AppJustStarted = 0x1, + LibAppJustStarted = 0x2, + LibAppFinished = 0x3, + LibAppClosed = 0xA, + ReturningToApp = 0xB, + ExitingApp = 0xC, }; /// App Id's used by APT functions @@ -179,6 +182,40 @@ void GlanceParameter(Service::Interface* self); void CancelParameter(Service::Interface* self); /** + * APT::PrepareToStartApplication service function. When the input title-info programID is zero, + * NS will load the actual program ID via AMNet:GetTitleIDList. After doing some checks with the + * programID, NS will then set a NS state flag to value 1, then set the programID for AppID + * 0x300(application) to the input program ID(or the one from GetTitleIDList). A media-type field + * in the NS state is also set to the input media-type value + * (other state fields are set at this point as well). With 8.0.0-18, NS will set an u8 NS state + * field to value 1 when input flags bit8 is set + * Inputs: + * 1-4 : 0x10-byte title-info struct + * 4 : Flags + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +void PrepareToStartApplication(Service::Interface* self); + +/** + * APT::StartApplication service function. Buf0 is copied to NS FIRMparams+0x0, then Buf1 is copied + * to the NS FIRMparams+0x480. Then the application is launched. + * Inputs: + * 1 : Buffer 0 size, max size is 0x300 + * 2 : Buffer 1 size, max size is 0x20 (this can be zero) + * 3 : u8 flag + * 4 : (Size0<<14) | 2 + * 5 : Buffer 0 pointer + * 6 : (Size1<<14) | 0x802 + * 7 : Buffer 1 pointer + * Outputs: + * 0 : Return Header + * 1 : Result of function, 0 on success, otherwise error code +*/ +void StartApplication(Service::Interface* self); + +/** * APT::AppletUtility service function * Inputs: * 1 : Unknown, but clearly used for something @@ -213,10 +250,10 @@ void SetAppCpuTimeLimit(Service::Interface* self); void GetAppCpuTimeLimit(Service::Interface* self); /// Initialize the APT service -void APTInit(); +void Init(); /// Shutdown the APT service -void APTShutdown(); +void Shutdown(); } // namespace APT } // namespace Service diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index dbe5c1d87..864934245 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -12,16 +12,16 @@ namespace APT { const Interface::FunctionInfo FunctionTable[] = { {0x00010040, GetLockHandle, "GetLockHandle?"}, {0x00020080, Initialize, "Initialize?"}, - {0x00030040, nullptr, "Enable?"}, + {0x00030040, Enable, "Enable?"}, {0x00040040, nullptr, "Finalize?"}, {0x00050040, nullptr, "GetAppletManInfo?"}, {0x00060040, nullptr, "GetAppletInfo?"}, {0x000D0080, ReceiveParameter, "ReceiveParameter?"}, {0x000E0080, GlanceParameter, "GlanceParameter?"}, {0x003B0040, nullptr, "CancelLibraryApplet?"}, - {0x00430040, nullptr, "NotifyToWait?"}, + {0x00430040, NotifyToWait, "NotifyToWait?"}, {0x00440000, GetSharedFont, "GetSharedFont?"}, - {0x004B00C2, nullptr, "AppletUtility?"}, + {0x004B00C2, AppletUtility, "AppletUtility?"}, {0x00550040, nullptr, "WriteInputToNsState?"}, }; diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index 3fd348651..396d1f04a 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -3,9 +3,6 @@ // Refer to the license.txt file included. -#include "common/common.h" -#include "common/file_util.h" - #include "core/hle/hle.h" #include "core/hle/service/apt/apt.h" #include "core/hle/service/apt/apt_s.h" diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index 5ab23801e..d006b5930 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. -#include "common/common.h" #include "common/file_util.h" #include "core/hle/service/apt/apt.h" diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 6adadb224..2d26c9330 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -4,12 +4,16 @@ #include <algorithm> -#include "core/hle/service/fs/archive.h" -#include "core/hle/service/service.h" +#include "common/logging/log.h" +#include "common/string_util.h" + +#include "core/file_sys/file_backend.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_i.h" #include "core/hle/service/cfg/cfg_s.h" #include "core/hle/service/cfg/cfg_u.h" +#include "core/hle/service/fs/archive.h" +#include "core/hle/service/service.h" namespace Service { namespace CFG { @@ -53,12 +57,12 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { }); if (itr == std::end(config->block_entries)) { - LOG_ERROR(Service_CFG, "Config block %u with flags %u was not found", block_id, flag); + LOG_ERROR(Service_CFG, "Config block 0x%X with flags %u and size %u was not found", block_id, flag, size); return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); } if (itr->size != size) { - LOG_ERROR(Service_CFG, "Invalid size %u for config block %u with flags %u", size, block_id, flag); + LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag); return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); } @@ -170,7 +174,7 @@ ResultCode FormatConfig() { return RESULT_SUCCESS; } -void CFGInit() { +void Init() { AddService(new CFG_I_Interface); AddService(new CFG_S_Interface); AddService(new CFG_U_Interface); @@ -207,6 +211,7 @@ void CFGInit() { // Initialize the Username block // TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals + memset(&CONSOLE_USERNAME_BLOCK, 0, sizeof(CONSOLE_USERNAME_BLOCK)); CONSOLE_USERNAME_BLOCK.ng_word = 0; CONSOLE_USERNAME_BLOCK.zero = 0; @@ -218,8 +223,7 @@ void CFGInit() { FormatConfig(); } -void CFGShutdown() { - +void Shutdown() { } } // namespace CFG diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index e818d7bdc..3488c40d0 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h @@ -135,10 +135,10 @@ ResultCode UpdateConfigNANDSavegame(); ResultCode FormatConfig(); /// Initialize the config service -void CFGInit(); +void Init(); /// Shutdown the config service -void CFGShutdown(); +void Shutdown(); } // namespace CFG } // namespace Service diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index c8c1c5b17..221de9918 100644 --- a/src/core/hle/service/cfg/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp @@ -3,7 +3,9 @@ // Refer to the license.txt file included. #include "common/file_util.h" +#include "common/logging/log.h" #include "common/string_util.h" + #include "core/settings.h" #include "core/file_sys/archive_systemsavedata.h" #include "core/hle/hle.h" diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 0b3603ce1..fafb43a2f 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" + #include "core/hle/hle.h" #include "core/hle/kernel/event.h" #include "core/hle/service/dsp_dsp.h" @@ -11,7 +13,7 @@ namespace DSP_DSP { -static u32 read_pipe_count = 0; +static u32 read_pipe_count; static Kernel::SharedPtr<Kernel::Event> semaphore_event; static Kernel::SharedPtr<Kernel::Event> interrupt_event; @@ -40,9 +42,9 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) { u32 addr = cmd_buff[1]; cmd_buff[1] = 0; // No error - cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000); + cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000); - LOG_WARNING(Service_DSP, "(STUBBED) called with address %u", addr); + LOG_WARNING(Service_DSP, "(STUBBED) called with address 0x%08X", addr); } /** @@ -60,12 +62,19 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) { static void LoadComponent(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 size = cmd_buff[1]; + u32 unk1 = cmd_buff[2]; + u32 unk2 = cmd_buff[3]; + u32 new_size = cmd_buff[4]; + u32 buffer = cmd_buff[5]; + cmd_buff[1] = 0; // No error cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware // TODO(bunnei): Implement real DSP firmware loading - LOG_WARNING(Service_DSP, "(STUBBED) called"); + LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, unk1=0x%08X, unk2=0x%08X, new_size=0x%X, buffer=0x%08X", + size, unk1, unk2, new_size, buffer); } /** @@ -84,6 +93,33 @@ static void GetSemaphoreEventHandle(Service::Interface* self) { } /** + * DSP_DSP::FlushDataCache service function + * + * This Function is a no-op, We aren't emulating the CPU cache any time soon. + * + * Inputs: + * 1 : Address + * 2 : Size + * 3 : Value 0, some descriptor for the KProcess Handle + * 4 : KProcess handle + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void FlushDataCache(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 address = cmd_buff[1]; + u32 size = cmd_buff[2]; + u32 process = cmd_buff[4]; + + // TODO(purpasmart96): Verify return header on HW + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_DEBUG(Service_DSP, "(STUBBED) called address=0x%08X, size=0x%X, process=0x%08X", + address, size, process); +} + +/** * DSP_DSP::RegisterInterruptEvents service function * Inputs: * 1 : Parameter 0 (purpose unknown) @@ -95,6 +131,10 @@ static void GetSemaphoreEventHandle(Service::Interface* self) { static void RegisterInterruptEvents(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 param0 = cmd_buff[1]; + u32 param1 = cmd_buff[2]; + u32 event_handle = cmd_buff[4]; + auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); if (evt != nullptr) { interrupt_event = evt; @@ -106,7 +146,7 @@ static void RegisterInterruptEvents(Service::Interface* self) { cmd_buff[1] = -1; } - LOG_WARNING(Service_DSP, "(STUBBED) called"); + LOG_WARNING(Service_DSP, "(STUBBED) called param0=%u, param1=%u, event_handle=0x%08X", param0, param1, event_handle); } /** @@ -147,7 +187,7 @@ static void WriteProcessPipe(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_DSP, "(STUBBED) called number=%u, size=0x%08X, new_size=0x%08X, buffer=0x%08X", + LOG_WARNING(Service_DSP, "(STUBBED) called number=%u, size=0x%X, new_size=0x%X, buffer=0x%08X", number, size, new_size, buffer); } @@ -165,6 +205,8 @@ static void WriteProcessPipe(Service::Interface* self) { static void ReadPipeIfPossible(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 unk1 = cmd_buff[1]; + u32 unk2 = cmd_buff[2]; u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size VAddr addr = cmd_buff[0x41]; @@ -190,7 +232,8 @@ static void ReadPipeIfPossible(Service::Interface* self) { cmd_buff[1] = 0; // No error cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16); - LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr); + LOG_WARNING(Service_DSP, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, size=0x%X, buffer=0x%08X", + unk1, unk2, size, addr); } /** @@ -225,7 +268,7 @@ static void GetHeadphoneStatus(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; // Not using headphones? - LOG_WARNING(Service_DSP, "(STUBBED) called"); + LOG_DEBUG(Service_DSP, "(STUBBED) called"); } const Interface::FunctionInfo FunctionTable[] = { @@ -242,7 +285,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"}, {0x001100C2, LoadComponent, "LoadComponent"}, {0x00120000, nullptr, "UnloadComponent"}, - {0x00130082, nullptr, "FlushDataCache"}, + {0x00130082, FlushDataCache, "FlushDataCache"}, {0x00140082, nullptr, "InvalidateDCache"}, {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"}, {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"}, diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 58c5acd1e..e8c06c1cf 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" + #include "core/hle/hle.h" #include "core/hle/service/err_f.h" diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index b0fd834c7..6d4a9c7c9 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -9,6 +9,7 @@ #include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "common/make_unique.h" #include "common/math_util.h" @@ -78,6 +79,11 @@ enum class DirectoryCommand : u32 { Close = 0x08020000, }; +File::File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path & path) + : path(path), priority(0), backend(std::move(backend)) {} + +File::~File() {} + ResultVal<bool> File::SyncRequest() { u32* cmd_buff = Kernel::GetCommandBuffer(); FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); @@ -172,6 +178,11 @@ ResultVal<bool> File::SyncRequest() { return MakeResult<bool>(false); } +Directory::Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path & path) + : path(path), backend(std::move(backend)) {} + +Directory::~Directory() {} + ResultVal<bool> Directory::SyncRequest() { u32* cmd_buff = Kernel::GetCommandBuffer(); DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index b00f0fd60..faab0cb79 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -45,31 +45,27 @@ typedef u64 ArchiveHandle; class File : public Kernel::Session { public: - File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path) - : path(path), priority(0), backend(std::move(backend)) { - } + File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path); + ~File(); std::string GetName() const override { return "Path: " + path.DebugStr(); } + ResultVal<bool> SyncRequest() override; FileSys::Path path; ///< Path of the file u32 priority; ///< Priority of the file. TODO(Subv): Find out what this means std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface - - ResultVal<bool> SyncRequest() override; }; class Directory : public Kernel::Session { public: - Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path) - : path(path), backend(std::move(backend)) { - } + Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path); + ~Directory(); std::string GetName() const override { return "Directory: " + path.DebugStr(); } + ResultVal<bool> SyncRequest() override; FileSys::Path path; ///< Path of the directory std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface - - ResultVal<bool> SyncRequest() override; }; /** diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index d8d1d5547..0d2a426b0 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -2,10 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common.h" +#include "common/assert.h" +#include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "common/scope_exit.h" #include "common/string_util.h" + #include "core/hle/result.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" @@ -20,6 +23,8 @@ using Kernel::Session; namespace Service { namespace FS { +static u32 priority = -1; ///< For SetPriority and GetPriority service functions + static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) { return (u64)low_word | ((u64)high_word << 32); } @@ -215,7 +220,7 @@ static void DeleteDirectory(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); - + cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw; } @@ -424,7 +429,7 @@ static void IsSdmcWriteable(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // If the SD isn't enabled, it can't be writeable...else, stubbed true cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; - + LOG_DEBUG(Service_FS, " (STUBBED)"); } @@ -511,7 +516,7 @@ static void CreateExtSaveData(Service::Interface* self) { MediaType media_type = static_cast<MediaType>(cmd_buff[1] & 0xFF); u32 save_low = cmd_buff[2]; u32 save_high = cmd_buff[3]; - + LOG_WARNING(Service_FS, "(STUBBED) savedata_high=%08X savedata_low=%08X cmd_buff[3]=%08X " "cmd_buff[4]=%08X cmd_buff[5]=%08X cmd_buff[6]=%08X cmd_buff[7]=%08X cmd_buff[8]=%08X " "cmd_buff[9]=%08X cmd_buff[10]=%08X cmd_buff[11]=%08X", save_high, save_low, @@ -573,7 +578,7 @@ static void DeleteSystemSaveData(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 savedata_high = cmd_buff[1]; u32 savedata_low = cmd_buff[2]; - + cmd_buff[1] = DeleteSystemSaveData(savedata_high, savedata_low).raw; } @@ -601,12 +606,72 @@ static void CreateSystemSaveData(Service::Interface* self) { LOG_WARNING(Service_FS, "(STUBBED) savedata_high=%08X savedata_low=%08X cmd_buff[3]=%08X " "cmd_buff[4]=%08X cmd_buff[5]=%08X cmd_buff[6]=%08X cmd_buff[7]=%08X cmd_buff[8]=%08X " - "cmd_buff[9]=%08X", savedata_high, savedata_low, cmd_buff[3], cmd_buff[4], cmd_buff[5], + "cmd_buff[9]=%08X", savedata_high, savedata_low, cmd_buff[3], cmd_buff[4], cmd_buff[5], cmd_buff[6], cmd_buff[7], cmd_buff[8], cmd_buff[9]); cmd_buff[1] = CreateSystemSaveData(savedata_high, savedata_low).raw; } +/** + * FS_User::InitializeWithSdkVersion service function. + * Inputs: + * 0 : 0x08610042 + * 1 : Unknown + * 2 : Unknown + * 3 : Unknown + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void InitializeWithSdkVersion(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 unk1 = cmd_buff[1]; + u32 unk2 = cmd_buff[2]; + u32 unk3 = cmd_buff[3]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_FS, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, unk3=0x%08X", + unk1, unk2, unk3); +} + +/** + * FS_User::SetPriority service function. + * Inputs: + * 0 : 0x08620040 + * 1 : priority + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetPriority(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + priority = cmd_buff[1]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_FS, "called priority=0x%08X", priority); +} + +/** + * FS_User::GetPriority service function. + * Inputs: + * 0 : 0x08630000 + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : priority + */ +static void GetPriority(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + ASSERT(priority != -1); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = priority; + + LOG_DEBUG(Service_FS, "called priority=0x%08X", priority); +} + const Interface::FunctionInfo FunctionTable[] = { {0x000100C6, nullptr, "Dummy1"}, {0x040100C4, nullptr, "Control"}, @@ -695,15 +760,17 @@ const Interface::FunctionInfo FunctionTable[] = { {0x08560240, CreateSystemSaveData, "CreateSystemSaveData"}, {0x08570080, DeleteSystemSaveData, "DeleteSystemSaveData"}, {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"}, - {0x08610042, nullptr, "InitializeWithSdkVersion"}, - {0x08620040, nullptr, "SetPriority"}, - {0x08630000, nullptr, "GetPriority"}, + {0x08610042, InitializeWithSdkVersion, "InitializeWithSdkVersion"}, + {0x08620040, SetPriority, "SetPriority"}, + {0x08630000, GetPriority, "GetPriority"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// // Interface class Interface::Interface() { + + priority = -1; Register(FunctionTable); } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index cff585698..c11c5faba 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -5,6 +5,7 @@ #include "common/bit_field.h" #include "core/mem_map.h" +#include "core/memory.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" @@ -30,13 +31,12 @@ namespace GSP_GPU { Kernel::SharedPtr<Kernel::Event> g_interrupt_event; /// GSP shared memoryings Kernel::SharedPtr<Kernel::SharedMemory> g_shared_memory; -/// Thread index into interrupt relay queue, 1 is arbitrary -u32 g_thread_id = 1; +/// Thread index into interrupt relay queue +u32 g_thread_id = 0; /// Gets a pointer to a thread command buffer in GSP shared memory static inline u8* GetCommandBuffer(u32 thread_id) { - ResultVal<u8*> ptr = g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); - return ptr.ValueOr(nullptr); + return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); } static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { @@ -44,14 +44,14 @@ static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_in // For each thread there are two FrameBufferUpdate fields u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate); - ResultVal<u8*> ptr = g_shared_memory->GetPointer(offset); - return reinterpret_cast<FrameBufferUpdate*>(ptr.ValueOr(nullptr)); + u8* ptr = g_shared_memory->GetPointer(offset); + return reinterpret_cast<FrameBufferUpdate*>(ptr); } /// Gets a pointer to the interrupt relay queue for a given thread index static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { - ResultVal<u8*> ptr = g_shared_memory->GetPointer(sizeof(InterruptRelayQueue) * thread_id); - return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr)); + u8* ptr = g_shared_memory->GetPointer(sizeof(InterruptRelayQueue) * thread_id); + return reinterpret_cast<InterruptRelayQueue*>(ptr); } /** @@ -204,16 +204,18 @@ static void ReadHWRegs(Service::Interface* self) { static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { u32 base_address = 0x400000; + PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); + PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); if (info.active_fb == 0) { WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), 4, - &info.address_left); + &phys_address_left); WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), 4, - &info.address_right); + &phys_address_right); } else { WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), 4, - &info.address_left); + &phys_address_left); WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), 4, - &info.address_right); + &phys_address_right); } WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), 4, &info.stride); @@ -265,6 +267,9 @@ static void FlushDataCache(Service::Interface* self) { // TODO(purpasmart96): Verify return header on HW cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_DEBUG(Service_GSP, "(STUBBED) called address=0x%08X, size=0x%08X, process=0x%08X", + address, size, process); } /** @@ -273,7 +278,7 @@ static void FlushDataCache(Service::Interface* self) { * 1 : "Flags" field, purpose is unknown * 3 : Handle to GSP synchronization event * Outputs: - * 0 : Result of function, 0 on success, otherwise error code + * 1 : Result of function, 0x2A07 on success, otherwise error code * 2 : Thread index into GSP command buffer * 4 : Handle to GSP shared memory */ @@ -283,11 +288,12 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]); ASSERT_MSG((g_interrupt_event != nullptr), "handle is not valid!"); - g_shared_memory = Kernel::SharedMemory::Create("GSPSharedMem"); Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); - cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init + // This specific code is required for a successful initialization, rather than 0 + cmd_buff[1] = ResultCode((ErrorDescription)519, ErrorModule::GX, + ErrorSummary::Success, ErrorLevel::Success).raw; cmd_buff[2] = g_thread_id++; // Thread ID cmd_buff[4] = shmem_handle; // GSP shared memory @@ -522,8 +528,12 @@ Interface::Interface() { Register(FunctionTable); g_interrupt_event = 0; - g_shared_memory = 0; - g_thread_id = 1; + + using Kernel::MemoryPermission; + g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, + MemoryPermission::ReadWrite, "GSPSharedMem"); + + g_thread_id = 0; } } // namespace diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 138603d9b..9695f7e56 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" + #include "core/hle/service/service.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid_spvr.h" @@ -20,17 +22,17 @@ namespace HID { static const int MAX_CIRCLEPAD_POS = 0x9C; ///< Max value for a circle pad position // Handle to shared memory region designated to HID_User service -static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem = nullptr; +static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; // Event handles -static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1 = nullptr; -static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2 = nullptr; -static Kernel::SharedPtr<Kernel::Event> event_accelerometer = nullptr; -static Kernel::SharedPtr<Kernel::Event> event_gyroscope = nullptr; -static Kernel::SharedPtr<Kernel::Event> event_debug_pad = nullptr; +static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1; +static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2; +static Kernel::SharedPtr<Kernel::Event> event_accelerometer; +static Kernel::SharedPtr<Kernel::Event> event_gyroscope; +static Kernel::SharedPtr<Kernel::Event> event_debug_pad; -static u32 next_pad_index = 0; -static u32 next_touch_index = 0; +static u32 next_pad_index; +static u32 next_touch_index; // TODO(peachum): // Add a method for setting analog input from joystick device for the circle Pad. @@ -45,8 +47,8 @@ static u32 next_touch_index = 0; // * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41 // * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41 -void HIDUpdate() { - SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer().ValueOr(nullptr)); +void Update() { + SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); const PadState state = VideoCore::g_emu_window->GetPadState(); if (mem == nullptr) { @@ -155,13 +157,15 @@ void GetSoundVolume(Service::Interface* self) { LOG_WARNING(Service_HID, "(STUBBED) called"); } -void HIDInit() { +void Init() { using namespace Kernel; AddService(new HID_U_Interface); AddService(new HID_SPVR_Interface); - shared_mem = SharedMemory::Create("HID:SharedMem"); + using Kernel::MemoryPermission; + shared_mem = SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, + MemoryPermission::Read, "HID:SharedMem"); next_pad_index = 0; next_touch_index = 0; @@ -174,7 +178,13 @@ void HIDInit() { event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad"); } -void HIDShutdown() { +void Shutdown() { + shared_mem = nullptr; + event_pad_or_touch_1 = nullptr; + event_pad_or_touch_2 = nullptr; + event_accelerometer = nullptr; + event_gyroscope = nullptr; + event_debug_pad = nullptr; } } // namespace HID diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 97462c7f8..897bd6764 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -200,13 +200,13 @@ void EnableGyroscopeLow(Interface* self); void GetSoundVolume(Interface* self); /// Checks for user input updates -void HIDUpdate(); +void Update(); /// Initialize HID service -void HIDInit(); +void Init(); /// Shutdown HID service -void HIDShutdown(); +void Shutdown(); } } diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp new file mode 100644 index 000000000..adfbb258d --- /dev/null +++ b/src/core/hle/service/ir/ir.cpp @@ -0,0 +1,52 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/service.h" +#include "core/hle/service/ir/ir.h" +#include "core/hle/service/ir/ir_rst.h" +#include "core/hle/service/ir/ir_u.h" +#include "core/hle/service/ir/ir_user.h" + +#include "core/hle/hle.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/shared_memory.h" + +namespace Service { +namespace IR { + +static Kernel::SharedPtr<Kernel::Event> handle_event; +static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; + +void GetHandles(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0x4000000; + cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(Service::IR::handle_event).MoveFrom(); +} + +void Init() { + using namespace Kernel; + + AddService(new IR_RST_Interface); + AddService(new IR_U_Interface); + AddService(new IR_User_Interface); + + using Kernel::MemoryPermission; + shared_memory = SharedMemory::Create(0x1000, Kernel::MemoryPermission::ReadWrite, + Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory"); + + // Create event handle(s) + handle_event = Event::Create(RESETTYPE_ONESHOT, "IR:HandleEvent"); +} + +void Shutdown() { + shared_memory = nullptr; + handle_event = nullptr; +} + +} // namespace IR + +} // namespace Service diff --git a/src/core/hle/service/ir/ir.h b/src/core/hle/service/ir/ir.h new file mode 100644 index 000000000..c16d963e7 --- /dev/null +++ b/src/core/hle/service/ir/ir.h @@ -0,0 +1,30 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace IR { + +/** + * IR::GetHandles service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Translate header, used by the ARM11-kernel + * 3 : Shared memory handle + * 4 : Event handle + */ +void GetHandles(Interface* self); + +/// Initialize IR service +void Init(); + +/// Shutdown IR service +void Shutdown(); + +} // namespace IR +} // namespace Service diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp new file mode 100644 index 000000000..96ae63420 --- /dev/null +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -0,0 +1,24 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/hle.h" +#include "core/hle/service/ir/ir.h" +#include "core/hle/service/ir/ir_rst.h" + +namespace Service { +namespace IR { + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010000, GetHandles, "GetHandles"}, + {0x00020080, nullptr, "Initialize"}, + {0x00030000, nullptr, "Shutdown"}, + {0x00090000, nullptr, "WriteToTwoFields"}, +}; + +IR_RST_Interface::IR_RST_Interface() { + Register(FunctionTable); +} + +} // namespace IR +} // namespace Service diff --git a/src/core/hle/service/ir_rst.h b/src/core/hle/service/ir/ir_rst.h index deef701c5..a492e15c9 100644 --- a/src/core/hle/service/ir_rst.h +++ b/src/core/hle/service/ir/ir_rst.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace IR_RST +namespace Service { +namespace IR { -namespace IR_RST { - -class Interface : public Service::Interface { +class IR_RST_Interface : public Service::Interface { public: - Interface(); + IR_RST_Interface(); std::string GetPortName() const override { return "ir:rst"; } }; -} // namespace +} // namespace IR +} // namespace Service diff --git a/src/core/hle/service/ir_u.cpp b/src/core/hle/service/ir/ir_u.cpp index 608ed3c06..1b1e3078b 100644 --- a/src/core/hle/service/ir_u.cpp +++ b/src/core/hle/service/ir/ir_u.cpp @@ -3,12 +3,11 @@ // Refer to the license.txt file included. #include "core/hle/hle.h" -#include "core/hle/service/ir_u.h" +#include "core/hle/service/ir/ir.h" +#include "core/hle/service/ir/ir_u.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace IR_U - -namespace IR_U { +namespace Service { +namespace IR { const Interface::FunctionInfo FunctionTable[] = { {0x00010000, nullptr, "Initialize"}, @@ -31,11 +30,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00120040, nullptr, "SetSleepModeState"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +IR_U_Interface::IR_U_Interface() { Register(FunctionTable); } -} // namespace +} // namespace IR +} // namespace Service diff --git a/src/core/hle/service/ir/ir_u.h b/src/core/hle/service/ir/ir_u.h new file mode 100644 index 000000000..056d2ce1a --- /dev/null +++ b/src/core/hle/service/ir/ir_u.h @@ -0,0 +1,22 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace IR { + +class IR_U_Interface : public Service::Interface { +public: + IR_U_Interface(); + + std::string GetPortName() const override { + return "ir:u"; + } +}; + +} // namespace IR +} // namespace Service diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp new file mode 100644 index 000000000..8e3ff140f --- /dev/null +++ b/src/core/hle/service/ir/ir_user.cpp @@ -0,0 +1,34 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/hle.h" +#include "core/hle/service/ir/ir.h" +#include "core/hle/service/ir/ir_user.h" + +namespace Service { +namespace IR { + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010182, nullptr, "InitializeIrNop"}, + {0x00020000, nullptr, "FinalizeIrNop"}, + {0x00030000, nullptr, "ClearReceiveBuffer"}, + {0x00040000, nullptr, "ClearSendBuffer"}, + {0x00060040, nullptr, "RequireConnection"}, + {0x00090000, nullptr, "Disconnect"}, + {0x000A0000, nullptr, "GetReceiveEvent"}, + {0x000B0000, nullptr, "GetSendEvent"}, + {0x000C0000, nullptr, "GetConnectionStatusEvent"}, + {0x000D0042, nullptr, "SendIrNop"}, + {0x000E0042, nullptr, "SendIrNopLarge"}, + {0x00180182, nullptr, "InitializeIrNopShared"}, + {0x00190040, nullptr, "ReleaseReceivedData"}, + {0x001A0040, nullptr, "SetOwnMachineId"}, +}; + +IR_User_Interface::IR_User_Interface() { + Register(FunctionTable); +} + +} // namespace IR +} // namespace Service diff --git a/src/core/hle/service/ir/ir_user.h b/src/core/hle/service/ir/ir_user.h new file mode 100644 index 000000000..71c932ffa --- /dev/null +++ b/src/core/hle/service/ir/ir_user.h @@ -0,0 +1,22 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace IR { + +class IR_User_Interface : public Service::Interface { +public: + IR_User_Interface(); + + std::string GetPortName() const override { + return "ir:USER"; + } +}; + +} // namespace IR +} // namespace Service diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp deleted file mode 100644 index 4c26c2f03..000000000 --- a/src/core/hle/service/ir_rst.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/hle.h" -#include "core/hle/service/ir_rst.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace IR_RST - -namespace IR_RST { - -const Interface::FunctionInfo FunctionTable[] = { - {0x00010000, nullptr, "GetHandles"}, - {0x00020080, nullptr, "Initialize"}, - {0x00030000, nullptr, "Shutdown"}, - {0x00090000, nullptr, "WriteToTwoFields"}, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { - Register(FunctionTable); -} - -} // namespace diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp index c0c4a2344..155b97f69 100644 --- a/src/core/hle/service/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" + #include "core/hle/hle.h" #include "core/hle/service/ldr_ro.h" diff --git a/src/core/hle/service/nim_u.cpp b/src/core/hle/service/nim_u.cpp new file mode 100644 index 000000000..5f13bd98e --- /dev/null +++ b/src/core/hle/service/nim_u.cpp @@ -0,0 +1,48 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" + +#include "core/hle/hle.h" +#include "core/hle/service/nim_u.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace NIM_U + +namespace NIM_U { + +/** + * NIM_U::CheckSysUpdateAvailable service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : flag, 0 = no system update available, 1 = system update available. + */ +static void CheckSysUpdateAvailable(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; // No update available + + LOG_WARNING(Service_NWM, "(STUBBED) called"); +} + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010000, nullptr, "StartSysUpdate"}, + {0x00020000, nullptr, "GetUpdateDownloadProgress"}, + {0x00040000, nullptr, "FinishTitlesInstall"}, + {0x00050000, nullptr, "CheckForSysUpdateEvent"}, + {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, + {0x000A0000, nullptr, "GetState"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/ir_u.h b/src/core/hle/service/nim_u.h index ec47a1524..57a1f6acf 100644 --- a/src/core/hle/service/ir_u.h +++ b/src/core/hle/service/nim_u.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2015 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -7,16 +7,16 @@ #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace IR_U +// Namespace NIM_U -namespace IR_U { +namespace NIM_U { class Interface : public Service::Interface { public: Interface(); std::string GetPortName() const override { - return "ir:u"; + return "nim:u"; } }; diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp index 5cf3e2039..6b3ef6ece 100644 --- a/src/core/hle/service/ns_s.cpp +++ b/src/core/hle/service/ns_s.cpp @@ -3,8 +3,6 @@ // Refer to the license.txt file included. -#include "common/common.h" - #include "core/hle/hle.h" #include "core/hle/service/ns_s.h" diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp index 88be6c8d9..25b01860e 100644 --- a/src/core/hle/service/nwm_uds.cpp +++ b/src/core/hle/service/nwm_uds.cpp @@ -2,7 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" + #include "core/hle/hle.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/nwm_uds.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -10,21 +13,115 @@ namespace NWM_UDS { +static Kernel::SharedPtr<Kernel::Event> handle_event; + +/** + * NWM_UDS::Shutdown service function + * Inputs: + * 1 : None + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void Shutdown(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + // TODO(purpasmart): Verify return header on HW + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_NWM, "(STUBBED) called"); +} + +/** + * NWM_UDS::RecvBeaconBroadcastData service function + * Inputs: + * 1 : Output buffer max size + * 2 : Unknown + * 3 : Unknown + * 4 : MAC address? + * 6-14 : Unknown, usually zero / uninitialized? + * 15 : WLan Comm ID + * 16 : This is the ID also located at offset 0xE in the CTR-generation structure. + * 17 : Value 0 + * 18 : Input handle + * 19 : (Size<<4) | 12 + * 20 : Output buffer ptr + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void RecvBeaconBroadcastData(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 out_buffer_size = cmd_buff[1]; + u32 unk1 = cmd_buff[2]; + u32 unk2 = cmd_buff[3]; + u32 mac_address = cmd_buff[4]; + + u32 unk3 = cmd_buff[6]; + + u32 wlan_comm_id = cmd_buff[15]; + u32 ctr_gen_id = cmd_buff[16]; + u32 value = cmd_buff[17]; + u32 input_handle = cmd_buff[18]; + u32 new_buffer_size = cmd_buff[19]; + u32 out_buffer_ptr = cmd_buff[20]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_NWM, "(STUBBED) called out_buffer_size=0x%08X, unk1=0x%08X, unk2=0x%08X," + "mac_address=0x%08X, unk3=0x%08X, wlan_comm_id=0x%08X, ctr_gen_id=0x%08X," + "value=%u, input_handle=0x%08X, new_buffer_size=0x%08X, out_buffer_ptr=0x%08X", + out_buffer_size, unk1, unk2, mac_address, unk3, wlan_comm_id, ctr_gen_id, value, + input_handle, new_buffer_size, out_buffer_ptr); +} + +/** + * NWM_UDS::Initialize service function + * Inputs: + * 1 : Unknown + * 2-11 : Input Structure + * 12 : Unknown u16 + * 13 : Value 0 + * 14 : Handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Value 0 + * 3 : Output handle + */ +static void Initialize(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 unk1 = cmd_buff[1]; + u32 unk2 = cmd_buff[12]; + u32 value = cmd_buff[13]; + u32 handle = cmd_buff[14]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; + cmd_buff[3] = Kernel::g_handle_table.Create(handle_event).MoveFrom(); //TODO(purpasmart): Verify if this is a event handle + + LOG_WARNING(Service_NWM, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, value=%u, handle=0x%08X", + unk1, unk2, value, handle); +} + const Interface::FunctionInfo FunctionTable[] = { - {0x00030000, nullptr, "Shutdown"}, - {0x000F0404, nullptr, "RecvBeaconBroadcastData"}, - {0x00100042, nullptr, "SetBeaconAdditionalData"}, - {0x001400C0, nullptr, "RecvBroadcastDataFrame"}, - {0x001B0302, nullptr, "Initialize"}, - {0x001D0044, nullptr, "BeginHostingNetwork"}, - {0x001E0084, nullptr, "ConnectToNetwork"}, - {0x001F0006, nullptr, "DecryptBeaconData"}, + {0x00030000, Shutdown, "Shutdown"}, + {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"}, + {0x00100042, nullptr, "SetBeaconAdditionalData"}, + {0x001400C0, nullptr, "RecvBroadcastDataFrame"}, + {0x001B0302, Initialize, "Initialize"}, + {0x001D0044, nullptr, "BeginHostingNetwork"}, + {0x001E0084, nullptr, "ConnectToNetwork"}, + {0x001F0006, nullptr, "DecryptBeaconData"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// // Interface class Interface::Interface() { + handle_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "NWM_UDS::handle_event"); + Register(FunctionTable); } diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h index 9043f5aa7..82abdff28 100644 --- a/src/core/hle/service/nwm_uds.h +++ b/src/core/hle/service/nwm_uds.h @@ -18,7 +18,7 @@ public: Interface(); std::string GetPortName() const override { - return "nwm:UDS"; + return "nwm::UDS"; } }; diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index 56c918d4f..2c7d49c9f 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp @@ -2,12 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/service/service.h" +#include "common/logging/log.h" + +#include "core/file_sys/file_backend.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/ptm/ptm.h" #include "core/hle/service/ptm/ptm_play.h" #include "core/hle/service/ptm/ptm_sysm.h" #include "core/hle/service/ptm/ptm_u.h" +#include "core/hle/service/service.h" namespace Service { namespace PTM { @@ -18,31 +21,70 @@ static const GameCoin default_game_coin = { 0x4F00, 42, 0, 0, 0, 2014, 12, 29 }; /// Id of the SharedExtData archive used by the PTM process static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0}; -static bool shell_open = true; +static bool shell_open; -static bool battery_is_charging = true; +static bool battery_is_charging; + +void GetAdapterState(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); -u32 GetAdapterState() { // TODO(purpasmart96): This function is only a stub, // it returns a valid result without implementing full functionality. - return battery_is_charging ? 1 : 0; + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = battery_is_charging ? 1 : 0; + + LOG_WARNING(Service_PTM, "(STUBBED) called"); } -u32 GetShellState() { - return shell_open ? 1 : 0; +void GetShellState(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = shell_open ? 1 : 0; +} + +void GetBatteryLevel(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + // TODO(purpasmart96): This function is only a stub, + // it returns a valid result without implementing full functionality. + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(ChargeLevels::CompletelyFull); // Set to a completely full battery + + LOG_WARNING(Service_PTM, "(STUBBED) called"); } -ChargeLevels GetBatteryLevel() { +void GetBatteryChargeState(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + // TODO(purpasmart96): This function is only a stub, // it returns a valid result without implementing full functionality. - return ChargeLevels::CompletelyFull; // Set to a completely full battery + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = battery_is_charging ? 1 : 0; + + LOG_WARNING(Service_PTM, "(STUBBED) called"); } -void PTMInit() { +void IsLegacyPowerOff(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; + + LOG_WARNING(Service_PTM, "(STUBBED) called"); +} + +void Init() { AddService(new PTM_Play_Interface); AddService(new PTM_Sysm_Interface); AddService(new PTM_U_Interface); + shell_open = true; + battery_is_charging = true; + // Open the SharedExtSaveData archive 0xF000000B and create the gamecoin.dat file if it doesn't exist FileSys::Path archive_path(ptm_shared_extdata_id); auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); @@ -68,7 +110,7 @@ void PTMInit() { } } -void PTMShutdown() { +void Shutdown() { } diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h index f697aae4d..493e6a11f 100644 --- a/src/core/hle/service/ptm/ptm.h +++ b/src/core/hle/service/ptm/ptm.h @@ -5,6 +5,7 @@ #pragma once #include <array> +#include "core/hle/service/service.h" #include "core/hle/result.h" namespace Service { @@ -35,31 +36,54 @@ struct GameCoin { }; /** - * Returns whether the battery is charging or not. * It is unknown if GetAdapterState is the same as GetBatteryChargeState, * it is likely to just be a duplicate function of GetBatteryChargeState * that controls another part of the HW. - * @returns 1 if the battery is charging, and 0 otherwise. + * PTM::GetAdapterState service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Output of function, 0 = not charging, 1 = charging. */ -u32 GetAdapterState(); +void GetAdapterState(Interface* self); /** - * Returns whether the 3DS's physical shell casing is open or closed - * @returns 1 if the shell is open, and 0 if otherwise + * PTM::GetShellState service function. + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0) */ -u32 GetShellState(); +void GetShellState(Interface* self); /** - * Get the current battery's charge level. - * @returns The battery's charge level. + * PTM::GetBatteryLevel service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery, + * 3 = half full battery, 2 = low battery, 1 = critical battery. */ -ChargeLevels GetBatteryLevel(); +void GetBatteryLevel(Interface* self); + +/** + * PTM::GetBatteryChargeState service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Output of function, 0 = not charging, 1 = charging. + */ +void GetBatteryChargeState(Interface* self); + +/** + * PTM::IsLegacyPowerOff service function + * Outputs: + * 1: Result code, 0 on success, otherwise error code + * 2: Whether the system is going through a power off + */ +void IsLegacyPowerOff(Interface* self); /// Initialize the PTM service -void PTMInit(); +void Init(); /// Shutdown the PTM service -void PTMShutdown(); +void Shutdown(); } // namespace PTM } // namespace Service diff --git a/src/core/hle/service/ptm/ptm_play.cpp b/src/core/hle/service/ptm/ptm_play.cpp index 8e8ae8558..48e68a3d8 100644 --- a/src/core/hle/service/ptm/ptm_play.cpp +++ b/src/core/hle/service/ptm/ptm_play.cpp @@ -9,10 +9,10 @@ namespace Service { namespace PTM { const Interface::FunctionInfo FunctionTable[] = { - { 0x08070082, nullptr, "GetPlayHistory" }, - { 0x08080000, nullptr, "GetPlayHistoryStart" }, - { 0x08090000, nullptr, "GetPlayHistoryLength" }, - { 0x080B0080, nullptr, "CalcPlayHistoryStart" }, + {0x08070082, nullptr, "GetPlayHistory"}, + {0x08080000, nullptr, "GetPlayHistoryStart"}, + {0x08090000, nullptr, "GetPlayHistoryLength"}, + {0x080B0080, nullptr, "CalcPlayHistoryStart"}, }; PTM_Play_Interface::PTM_Play_Interface() { diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp index 2d841f69c..655658f3b 100644 --- a/src/core/hle/service/ptm/ptm_sysm.cpp +++ b/src/core/hle/service/ptm/ptm_sysm.cpp @@ -2,57 +2,44 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/make_unique.h" -#include "core/file_sys/archive_extsavedata.h" #include "core/hle/hle.h" +#include "core/hle/service/ptm/ptm.h" #include "core/hle/service/ptm/ptm_sysm.h" namespace Service { namespace PTM { -/** - * Returns whether the system is powering off (?) - * Outputs: - * 1: Result code, 0 on success, otherwise error code - * 2: Whether the system is going through a power off - */ -void IsLegacyPowerOff(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; -} - const Interface::FunctionInfo FunctionTable[] = { - {0x040100C0, nullptr, "SetRtcAlarmEx"}, - {0x04020042, nullptr, "ReplySleepQuery"}, - {0x04030042, nullptr, "NotifySleepPreparationComplete"}, - {0x04040102, nullptr, "SetWakeupTrigger"}, - {0x04050000, nullptr, "GetAwakeReason"}, - {0x04060000, nullptr, "RequestSleep"}, - {0x040700C0, nullptr, "ShutdownAsync"}, - {0x04080000, nullptr, "Awake"}, - {0x04090080, nullptr, "RebootAsync"}, - {0x040A0000, nullptr, "CheckNew3DS"}, - {0x08010640, nullptr, "SetInfoLEDPattern"}, - {0x08020040, nullptr, "SetInfoLEDPatternHeader"}, - {0x08030000, nullptr, "GetInfoLEDStatus"}, - {0x08040040, nullptr, "SetBatteryEmptyLEDPattern"}, - {0x08050000, nullptr, "ClearStepHistory"}, - {0x080600C2, nullptr, "SetStepHistory"}, - {0x08070082, nullptr, "GetPlayHistory"}, - {0x08080000, nullptr, "GetPlayHistoryStart"}, - {0x08090000, nullptr, "GetPlayHistoryLength"}, - {0x080A0000, nullptr, "ClearPlayHistory"}, - {0x080B0080, nullptr, "CalcPlayHistoryStart"}, - {0x080C0080, nullptr, "SetUserTime"}, - {0x080D0000, nullptr, "InvalidateSystemTime"}, - {0x080E0140, nullptr, "NotifyPlayEvent"}, - {0x080F0000, IsLegacyPowerOff, "IsLegacyPowerOff"}, - {0x08100000, nullptr, "ClearLegacyPowerOff"}, - {0x08110000, nullptr, "GetShellStatus"}, - {0x08120000, nullptr, "IsShutdownByBatteryEmpty"}, - {0x08130000, nullptr, "FormatSavedata"}, - {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"} + {0x040100C0, nullptr, "SetRtcAlarmEx"}, + {0x04020042, nullptr, "ReplySleepQuery"}, + {0x04030042, nullptr, "NotifySleepPreparationComplete"}, + {0x04040102, nullptr, "SetWakeupTrigger"}, + {0x04050000, nullptr, "GetAwakeReason"}, + {0x04060000, nullptr, "RequestSleep"}, + {0x040700C0, nullptr, "ShutdownAsync"}, + {0x04080000, nullptr, "Awake"}, + {0x04090080, nullptr, "RebootAsync"}, + {0x040A0000, nullptr, "CheckNew3DS"}, + {0x08010640, nullptr, "SetInfoLEDPattern"}, + {0x08020040, nullptr, "SetInfoLEDPatternHeader"}, + {0x08030000, nullptr, "GetInfoLEDStatus"}, + {0x08040040, nullptr, "SetBatteryEmptyLEDPattern"}, + {0x08050000, nullptr, "ClearStepHistory"}, + {0x080600C2, nullptr, "SetStepHistory"}, + {0x08070082, nullptr, "GetPlayHistory"}, + {0x08080000, nullptr, "GetPlayHistoryStart"}, + {0x08090000, nullptr, "GetPlayHistoryLength"}, + {0x080A0000, nullptr, "ClearPlayHistory"}, + {0x080B0080, nullptr, "CalcPlayHistoryStart"}, + {0x080C0080, nullptr, "SetUserTime"}, + {0x080D0000, nullptr, "InvalidateSystemTime"}, + {0x080E0140, nullptr, "NotifyPlayEvent"}, + {0x080F0000, IsLegacyPowerOff, "IsLegacyPowerOff"}, + {0x08100000, nullptr, "ClearLegacyPowerOff"}, + {0x08110000, nullptr, "GetShellStatus"}, + {0x08120000, nullptr, "IsShutdownByBatteryEmpty"}, + {0x08130000, nullptr, "FormatSavedata"}, + {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"} }; PTM_Sysm_Interface::PTM_Sysm_Interface() { diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp index 0af7c8bf6..3f5e9c7c1 100644 --- a/src/core/hle/service/ptm/ptm_u.cpp +++ b/src/core/hle/service/ptm/ptm_u.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/make_unique.h" +#include "common/logging/log.h" #include "core/hle/hle.h" #include "core/hle/service/ptm/ptm.h" @@ -11,68 +11,6 @@ namespace Service { namespace PTM { -/** - * PTM_U::GetAdapterState service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Output of function, 0 = not charging, 1 = charging. - */ -static void GetAdapterState(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = GetAdapterState(); - - LOG_WARNING(Service_PTM, "(STUBBED) called"); -} - -/* - * PTM_User::GetShellState service function. - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0) - */ -static void GetShellState(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = GetShellState(); -} - -/** - * PTM_U::GetBatteryLevel service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery, - * 3 = half full battery, 2 = low battery, 1 = critical battery. - */ -static void GetBatteryLevel(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = static_cast<u32>(GetBatteryLevel()); - - LOG_WARNING(Service_PTM, "(STUBBED) called"); -} - -/** - * PTM_U::GetBatteryChargeState service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Output of function, 0 = not charging, 1 = charging. - */ -static void GetBatteryChargeState(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - // TODO(purpasmart96): This function is only a stub, - // it returns a valid result without implementing full functionality. - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = GetAdapterState(); - - LOG_WARNING(Service_PTM, "(STUBBED) called"); -} - const Interface::FunctionInfo FunctionTable[] = { {0x00010002, nullptr, "RegisterAlarmClient"}, {0x00020080, nullptr, "SetRtcAlarm"}, diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 91f13cd7e..64185c62e 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common.h" +#include "common/logging/log.h" #include "common/string_util.h" #include "core/hle/service/service.h" @@ -24,14 +24,13 @@ #include "core/hle/service/gsp_gpu.h" #include "core/hle/service/gsp_lcd.h" #include "core/hle/service/http_c.h" -#include "core/hle/service/ir_rst.h" -#include "core/hle/service/ir_u.h" #include "core/hle/service/ldr_ro.h" #include "core/hle/service/mic_u.h" #include "core/hle/service/ndm_u.h" #include "core/hle/service/news_s.h" #include "core/hle/service/news_u.h" #include "core/hle/service/nim_aoc.h" +#include "core/hle/service/nim_u.h" #include "core/hle/service/ns_s.h" #include "core/hle/service/nwm_uds.h" #include "core/hle/service/pm_app.h" @@ -44,6 +43,7 @@ #include "core/hle/service/fs/archive.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/ir/ir.h" #include "core/hle/service/ptm/ptm.h" namespace Service { @@ -51,6 +51,49 @@ namespace Service { std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports; std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services; +/** + * Creates a function string for logging, complete with the name (or header code, depending + * on what's passed in) the port name, and all the cmd_buff arguments. + */ +static std::string MakeFunctionString(const char* name, const char* port_name, const u32* cmd_buff) { + // Number of params == bits 0-5 + bits 6-11 + int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); + + std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name); + for (int i = 1; i <= num_params; ++i) { + function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); + } + return function_string; +} + +ResultVal<bool> Interface::SyncRequest() { + u32* cmd_buff = Kernel::GetCommandBuffer(); + auto itr = m_functions.find(cmd_buff[0]); + + if (itr == m_functions.end() || itr->second.func == nullptr) { + std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name; + LOG_ERROR(Service, "unknown / unimplemented %s", MakeFunctionString(function_name.c_str(), GetPortName().c_str(), cmd_buff).c_str()); + + // TODO(bunnei): Hack - ignore error + cmd_buff[1] = 0; + return MakeResult<bool>(false); + } else { + LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); + } + + itr->second.func(this); + + return MakeResult<bool>(false); // TODO: Implement return from actual function +} + +void Interface::Register(const FunctionInfo* functions, size_t n) { + m_functions.reserve(n); + for (size_t i = 0; i < n; ++i) { + // Usually this array is sorted by id already, so hint to instead at the end + m_functions.emplace_hint(m_functions.cend(), functions[i].id, functions[i]); + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Module interface @@ -68,10 +111,11 @@ void Init() { AddNamedPort(new ERR_F::Interface); Service::FS::ArchiveInit(); - Service::CFG::CFGInit(); - Service::APT::APTInit(); - Service::PTM::PTMInit(); - Service::HID::HIDInit(); + Service::CFG::Init(); + Service::APT::Init(); + Service::PTM::Init(); + Service::HID::Init(); + Service::IR::Init(); AddService(new AC_U::Interface); AddService(new ACT_U::Interface); @@ -90,14 +134,13 @@ void Init() { AddService(new GSP_GPU::Interface); AddService(new GSP_LCD::Interface); AddService(new HTTP_C::Interface); - AddService(new IR_RST::Interface); - AddService(new IR_U::Interface); AddService(new LDR_RO::Interface); AddService(new MIC_U::Interface); AddService(new NDM_U::Interface); AddService(new NEWS_S::Interface); AddService(new NEWS_U::Interface); AddService(new NIM_AOC::Interface); + AddService(new NIM_U::Interface); AddService(new NS_S::Interface); AddService(new NWM_UDS::Interface); AddService(new PM_APP::Interface); @@ -110,10 +153,11 @@ void Init() { /// Shutdown ServiceManager void Shutdown() { - Service::HID::HIDShutdown(); - Service::PTM::PTMShutdown(); - Service::APT::APTShutdown(); - Service::CFG::CFGShutdown(); + Service::IR::Shutdown(); + Service::HID::Shutdown(); + Service::PTM::Shutdown(); + Service::APT::Shutdown(); + Service::CFG::Shutdown(); Service::FS::ArchiveShutdown(); g_srv_services.clear(); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index bfe16ebad..77bfb9ff1 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -4,20 +4,15 @@ #pragma once -#include <algorithm> #include <string> #include <unordered_map> -#include <vector> #include <boost/container/flat_map.hpp> -#include "common/common.h" -#include "common/string_util.h" -#include "core/mem_map.h" +#include "common/common_types.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/session.h" -#include "core/hle/svc.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Service @@ -26,31 +21,11 @@ namespace Service { static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) -class Manager; - /// Interface to a CTROS service class Interface : public Kernel::Session { // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be // just something that encapsulates a session and acts as a helper to implement service // processes. - - friend class Manager; - - /** - * Creates a function string for logging, complete with the name (or header code, depending - * on what's passed in) the port name, and all the cmd_buff arguments. - */ - std::string MakeFunctionString(const char* name, const char* port_name, const u32* cmd_buff) { - // Number of params == bits 0-5 + bits 6-11 - int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); - - std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name); - for (int i = 1; i <= num_params; ++i) { - function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); - } - return function_string; - } - public: std::string GetName() const override { return GetPortName(); } @@ -70,25 +45,7 @@ public: return "[UNKNOWN SERVICE PORT]"; } - ResultVal<bool> SyncRequest() override { - u32* cmd_buff = Kernel::GetCommandBuffer(); - auto itr = m_functions.find(cmd_buff[0]); - - if (itr == m_functions.end() || itr->second.func == nullptr) { - std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name; - LOG_ERROR(Service, "unknown / unimplemented %s", MakeFunctionString(function_name.c_str(), GetPortName().c_str(), cmd_buff).c_str()); - - // TODO(bunnei): Hack - ignore error - cmd_buff[1] = 0; - return MakeResult<bool>(false); - } else { - LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); - } - - itr->second.func(this); - - return MakeResult<bool>(false); // TODO: Implement return from actual function - } + ResultVal<bool> SyncRequest() override; protected: @@ -96,14 +53,12 @@ protected: * Registers the functions in the service */ template <size_t N> - void Register(const FunctionInfo (&functions)[N]) { - m_functions.reserve(N); - for (auto& fn : functions) { - // Usually this array is sorted by id already, so hint to instead at the end - m_functions.emplace_hint(m_functions.cend(), fn.id, fn); - } + inline void Register(const FunctionInfo (&functions)[N]) { + Register(functions, N); } + void Register(const FunctionInfo* functions, size_t n); + private: boost::container::flat_map<u32, FunctionInfo> m_functions; diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 231ead185..39b8d65fd 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" #include "common/platform.h" #if EMU_PLATFORM == PLATFORM_WINDOWS diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index cc59a03ce..6c49fa6cf 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" + #include "core/hle/hle.h" #include "core/hle/service/srv.h" #include "core/hle/kernel/event.h" diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 6607965e1..085192a07 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" + #include "core/hle/hle.h" #include "core/hle/kernel/event.h" #include "core/hle/service/y2r_u.h" @@ -11,7 +13,7 @@ namespace Y2R_U { -static Kernel::SharedPtr<Kernel::Event> completion_event = 0; +static Kernel::SharedPtr<Kernel::Event> completion_event; /** * Y2R_U::IsBusyConversion service function diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp index 568dad684..4014eee98 100644 --- a/src/core/hle/shared_page.cpp +++ b/src/core/hle/shared_page.cpp @@ -2,11 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> + #include "common/common_types.h" #include "common/common_funcs.h" #include "core/core.h" -#include "core/mem_map.h" +#include "core/memory.h" #include "core/hle/config_mem.h" #include "core/hle/shared_page.h" @@ -14,61 +16,15 @@ namespace SharedPage { -// see http://3dbrew.org/wiki/Configuration_Memory#Shared_Memory_Page_For_ARM11_Processes - -#pragma pack(1) -struct DateTime { - u64 date_time; // 0x0 - u64 update_tick; // 0x8 - INSERT_PADDING_BYTES(0x20 - 0x10); // 0x10 -}; - -struct SharedPageDef { - // most of these names are taken from the 3dbrew page linked above. - u32 date_time_selector; // 0x0 - u8 running_hw; // 0x4 - u8 mcu_hw_info; // 0x5: don't know what the acronyms mean - INSERT_PADDING_BYTES(0x20 - 0x6); // 0x6 - DateTime date_time_0; // 0x20 - DateTime date_time_1; // 0x40 - u8 wifi_macaddr[6]; // 0x60 - u8 wifi_unknown1; // 0x66: 3dbrew says these are "Likely wifi hardware related" - u8 wifi_unknown2; // 0x67 - INSERT_PADDING_BYTES(0x80 - 0x68); // 0x68 - float sliderstate_3d; // 0x80 - u8 ledstate_3d; // 0x84 - INSERT_PADDING_BYTES(0xA0 - 0x85); // 0x85 - u64 menu_title_id; // 0xA0 - u64 active_menu_title_id; // 0xA8 - INSERT_PADDING_BYTES(0x1000 - 0xB0); // 0xB0 -}; -#pragma pack() +SharedPageDef shared_page; -static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong"); -static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE, "Shared page structure size is wrong"); - -static SharedPageDef shared_page; - -template <typename T> -inline void Read(T &var, const u32 addr) { - u32 offset = addr - Memory::SHARED_PAGE_VADDR; - var = *(reinterpret_cast<T*>(((uintptr_t)&shared_page) + offset)); -} - -// Explicitly instantiate template functions because we aren't defining this in the header: -template void Read<u64>(u64 &var, const u32 addr); -template void Read<u32>(u32 &var, const u32 addr); -template void Read<u16>(u16 &var, const u32 addr); -template void Read<u8>(u8 &var, const u32 addr); +void Init() { + std::memset(&shared_page, 0, sizeof(shared_page)); -void Set3DSlider(float amount) { - shared_page.sliderstate_3d = amount; - shared_page.ledstate_3d = (amount == 0.0f); // off when non-zero + shared_page.running_hw = 0x1; // product } -void Init() { - shared_page.running_hw = 0x1; // product - Set3DSlider(0.0f); +void Shutdown() { } } // namespace diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h index 8f93545ec..fd2ab66a2 100644 --- a/src/core/hle/shared_page.h +++ b/src/core/hle/shared_page.h @@ -11,16 +11,46 @@ */ #include "common/common_types.h" +#include "common/swap.h" //////////////////////////////////////////////////////////////////////////////////////////////////// namespace SharedPage { -template <typename T> -void Read(T &var, const u32 addr); - -void Set3DSlider(float amount); +// See http://3dbrew.org/wiki/Configuration_Memory#Shared_Memory_Page_For_ARM11_Processes + +struct DateTime { + u64_le date_time; // 0 + u64_le update_tick; // 8 + INSERT_PADDING_BYTES(0x20 - 0x10); // 10 +}; +static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong"); + +struct SharedPageDef { + // Most of these names are taken from the 3dbrew page linked above. + u32_le date_time_selector; // 0 + u8 running_hw; // 4 + /// "Microcontroller hardware info" + u8 mcu_hw_info; // 5 + INSERT_PADDING_BYTES(0x20 - 0x6); // 6 + DateTime date_time_0; // 20 + DateTime date_time_1; // 40 + u8 wifi_macaddr[6]; // 60 + u8 wifi_unknown1; // 66 + u8 wifi_unknown2; // 67 + INSERT_PADDING_BYTES(0x80 - 0x68); // 68 + float_le sliderstate_3d; // 80 + u8 ledstate_3d; // 84 + INSERT_PADDING_BYTES(0xA0 - 0x85); // 85 + u64_le menu_title_id; // A0 + u64_le active_menu_title_id; // A8 + INSERT_PADDING_BYTES(0x1000 - 0xB0); // B0 +}; +static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE, "Shared page structure size is wrong"); + +extern SharedPageDef shared_page; void Init(); +void Shutdown(); } // namespace diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index bbb4eb9cd..9bf886256 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -4,6 +4,8 @@ #include <map> +#include "common/logging/log.h" +#include "common/profiler.h" #include "common/string_util.h" #include "common/symbols.h" @@ -14,6 +16,7 @@ #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/thread.h" @@ -283,8 +286,13 @@ static ResultCode ArbitrateAddress(Handle handle, u32 address, u32 type, u32 val if (arbiter == nullptr) return ERR_INVALID_HANDLE; - return arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), - address, value, nanoseconds); + auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), + address, value, nanoseconds); + + if (res == RESULT_SUCCESS) + HLE::Reschedule(__func__); + + return res; } /// Used to output a message on a debug hardware unit - does nothing on a retail unit @@ -305,14 +313,14 @@ static ResultCode GetResourceLimit(Handle* resource_limit, Handle process) { /// Get resource limit current values static ResultCode GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names, s32 name_count) { - LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d", + LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%p, name_count=%d", resource_limit, names, name_count); - Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now + values[0] = 0; // Normmatt: Set used memory to 0 for now return RESULT_SUCCESS; } /// Creates a new thread -static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { +static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { using Kernel::Thread; std::string name; @@ -323,6 +331,27 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u name = Common::StringFromFormat("unknown-%08x", entry_point); } + // TODO(bunnei): Implement resource limits to return an error code instead of the below assert. + // The error code should be: Description::NotAuthorized, Module::OS, Summary::WrongArgument, + // Level::Permanent + ASSERT_MSG(priority >= THREADPRIO_USERLAND_MAX, "Unexpected thread priority!"); + + if (priority > THREADPRIO_LOWEST) { + return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); + } + + switch (processor_id) { + case THREADPROCESSORID_DEFAULT: + case THREADPROCESSORID_0: + case THREADPROCESSORID_1: + break; + default: + // TODO(bunnei): Implement support for other processor IDs + ASSERT_MSG(false, "Unsupported thread processor ID: %d", processor_id); + break; + } + CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create( name, entry_point, priority, arg, processor_id, stack_top)); CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); @@ -331,10 +360,7 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); - if (THREADPROCESSORID_1 == processor_id) { - LOG_WARNING(Kernel_SVC, - "thread designated for system CPU core (UNIMPLEMENTED) will be run with app core scheduling"); - } + HLE::Reschedule(__func__); return RESULT_SUCCESS; } @@ -374,8 +400,11 @@ static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) { SharedPtr<Mutex> mutex = Mutex::Create(initial_locked != 0); CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(mutex))); + HLE::Reschedule(__func__); + LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X", initial_locked ? "true" : "false", *out_handle); + return RESULT_SUCCESS; } @@ -390,6 +419,37 @@ static ResultCode ReleaseMutex(Handle handle) { return ERR_INVALID_HANDLE; mutex->Release(); + + HLE::Reschedule(__func__); + + return RESULT_SUCCESS; +} + +/// Get the ID of the specified process +static ResultCode GetProcessId(u32* process_id, Handle process_handle) { + LOG_TRACE(Kernel_SVC, "called process=0x%08X", process_handle); + + const SharedPtr<Kernel::Process> process = Kernel::g_handle_table.Get<Kernel::Process>(process_handle); + if (process == nullptr) + return ERR_INVALID_HANDLE; + + *process_id = process->process_id; + return RESULT_SUCCESS; +} + +/// Get the ID of the process that owns the specified thread +static ResultCode GetProcessIdOfThread(u32* process_id, Handle thread_handle) { + LOG_TRACE(Kernel_SVC, "called thread=0x%08X", thread_handle); + + const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(thread_handle); + if (thread == nullptr) + return ERR_INVALID_HANDLE; + + const SharedPtr<Kernel::Process> process = thread->owner_process; + + ASSERT_MSG(process != nullptr, "Invalid parent process for thread=0x%08X", thread_handle); + + *process_id = process->process_id; return RESULT_SUCCESS; } @@ -428,6 +488,9 @@ static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) return ERR_INVALID_HANDLE; CASCADE_RESULT(*count, semaphore->Release(release_count)); + + HLE::Reschedule(__func__); + return RESULT_SUCCESS; } @@ -520,6 +583,9 @@ static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { return ERR_INVALID_HANDLE; timer->Set(initial, interval); + + HLE::Reschedule(__func__); + return RESULT_SUCCESS; } @@ -534,6 +600,9 @@ static ResultCode CancelTimer(Handle handle) { return ERR_INVALID_HANDLE; timer->Cancel(); + + HLE::Reschedule(__func__); + return RESULT_SUCCESS; } @@ -561,14 +630,26 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 using Kernel::SharedMemory; // TODO(Subv): Implement this function - SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(); + using Kernel::MemoryPermission; + SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(size, + (MemoryPermission)my_permission, (MemoryPermission)other_permission); CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory))); LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr); return RESULT_SUCCESS; } -const HLE::FunctionDef SVC_Table[] = { +namespace { + struct FunctionDef { + using Func = void(); + + u32 id; + Func* func; + const char* name; + }; +} + +static const FunctionDef SVC_Table[] = { {0x00, nullptr, "Unknown"}, {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"}, {0x02, HLE::Wrap<QueryMemory>, "QueryMemory"}, @@ -622,8 +703,8 @@ const HLE::FunctionDef SVC_Table[] = { {0x32, HLE::Wrap<SendSyncRequest>, "SendSyncRequest"}, {0x33, nullptr, "OpenProcess"}, {0x34, nullptr, "OpenThread"}, - {0x35, nullptr, "GetProcessId"}, - {0x36, nullptr, "GetProcessIdOfThread"}, + {0x35, HLE::Wrap<GetProcessId>, "GetProcessId"}, + {0x36, HLE::Wrap<GetProcessIdOfThread>, "GetProcessIdOfThread"}, {0x37, HLE::Wrap<GetThreadId>, "GetThreadId"}, {0x38, HLE::Wrap<GetResourceLimit>, "GetResourceLimit"}, {0x39, nullptr, "GetResourceLimitLimitValues"}, @@ -697,8 +778,28 @@ const HLE::FunctionDef SVC_Table[] = { {0x7D, nullptr, "QueryProcessMemory"}, }; -void Register() { - HLE::RegisterModule("SVC_Table", ARRAY_SIZE(SVC_Table), SVC_Table); +Common::Profiling::TimingCategory profiler_svc("SVC Calls"); + +static const FunctionDef* GetSVCInfo(u32 opcode) { + u32 func_num = opcode & 0xFFFFFF; // 8 bits + if (func_num >= ARRAY_SIZE(SVC_Table)) { + LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); + return nullptr; + } + return &SVC_Table[func_num]; +} + +void CallSVC(u32 opcode) { + Common::Profiling::ScopeTimer timer_svc(profiler_svc); + + const FunctionDef *info = GetSVCInfo(opcode); + if (info) { + if (info->func) { + info->func(); + } else { + LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); + } + } } } // 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 { namespace SVC { -void Register(); +void CallSVC(u32 opcode); } // namespace diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index e6983a225..8ef1f70df 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -8,7 +8,7 @@ #include "core/settings.h" #include "core/core.h" -#include "core/mem_map.h" +#include "core/memory.h" #include "core/core_timing.h" #include "core/hle/hle.h" @@ -29,8 +29,7 @@ namespace GPU { Regs g_regs; /// True if the current frame was skipped -bool g_skip_frame = false; - +bool g_skip_frame; /// 268MHz / gpu_refresh_rate frames per second static u64 frame_ticks; /// Event id for CoreTiming @@ -38,7 +37,7 @@ static int vblank_event; /// Total number of frames drawn static u64 frame_count; /// True if the last frame was skipped -static bool last_skip_frame = false; +static bool last_skip_frame; template <typename T> inline void Read(T &var, const u32 raw_addr) { @@ -77,8 +76,8 @@ inline void Write(u32 addr, const T data) { auto& config = g_regs.memory_fill_config[is_second_filler]; if (config.address_start && config.trigger) { - u8* start = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetStartAddress())); - u8* end = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetEndAddress())); + u8* start = Memory::GetPhysicalPointer(config.GetStartAddress()); + u8* end = Memory::GetPhysicalPointer(config.GetEndAddress()); if (config.fill_24bit) { // fill with 24-bit values @@ -115,8 +114,8 @@ inline void Write(u32 addr, const T data) { { const auto& config = g_regs.display_transfer_config; if (config.trigger & 1) { - u8* src_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress())); - u8* dst_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress())); + u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); + u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); if (config.scaling > config.ScaleXY) { LOG_CRITICAL(HW_GPU, "Unimplemented display transfer scaling mode %u", config.scaling.Value()); @@ -136,7 +135,7 @@ inline void Write(u32 addr, const T data) { memcpy(dst_pointer, src_pointer, config.output_width * config.output_height * GPU::Regs::BytesPerPixel(config.output_format)); - LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), flags 0x%08X, Raw copy", + LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), output format: %x, flags 0x%08X, Raw copy", config.output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format), config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(), config.GetPhysicalOutputAddress(), config.output_width.Value(), config.output_height.Value(), @@ -258,7 +257,7 @@ inline void Write(u32 addr, const T data) { const auto& config = g_regs.command_processor_config; if (config.trigger & 1) { - u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress())); + u32* buffer = (u32*)Memory::GetPhysicalPointer(config.GetPhysicalAddress()); Pica::CommandProcessor::ProcessCommandList(buffer, config.size); } break; @@ -312,7 +311,7 @@ static void VBlankCallback(u64 userdata, int cycles_late) { DSP_DSP::SignalInterrupt(); // Check for user input updates - Service::HID::HIDUpdate(); + Service::HID::Update(); // Reschedule recurrent event CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); @@ -320,6 +319,8 @@ static void VBlankCallback(u64 userdata, int cycles_late) { /// Initialize hardware void Init() { + memset(&g_regs, 0, sizeof(g_regs)); + auto& framebuffer_top = g_regs.framebuffer_config[0]; auto& framebuffer_sub = g_regs.framebuffer_config[1]; @@ -349,6 +350,7 @@ void Init() { frame_ticks = 268123480 / Settings::values.gpu_refresh_rate; last_skip_frame = false; g_skip_frame = false; + frame_count = 0; vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback); CoreTiming::ScheduleEvent(frame_ticks, vblank_event); diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index c8f884494..699bcd2a5 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -6,8 +6,10 @@ #include <cstddef> -#include "common/common_types.h" +#include "common/assert.h" #include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" namespace GPU { diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index bed50af50..f4906cc7e 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/common_types.h" +#include "common/logging/log.h" #include "core/hw/hw.h" #include "core/hw/gpu.h" @@ -63,6 +64,8 @@ void Init() { /// Shutdown hardware void Shutdown() { + GPU::Shutdown(); + LCD::Shutdown(); LOG_DEBUG(HW, "shutdown OK"); } diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp index 7986f3ddb..09134c95b 100644 --- a/src/core/hw/lcd.cpp +++ b/src/core/hw/lcd.cpp @@ -2,7 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> + #include "common/common_types.h" +#include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/hle/hle.h" @@ -55,6 +58,7 @@ template void Write<u8>(u32 addr, const u8 data); /// Initialize hardware void Init() { + memset(&g_regs, 0, sizeof(g_regs)); LOG_DEBUG(HW_LCD, "initialized OK"); } diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h index 43893a625..fb14c3b21 100644 --- a/src/core/hw/lcd.h +++ b/src/core/hw/lcd.h @@ -6,8 +6,9 @@ #include <cstddef> -#include "common/common_types.h" #include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" #define LCD_REG_INDEX(field_name) (offsetof(LCD::Regs, field_name) / sizeof(u32)) diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 958dd03e8..84b13ee52 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -5,11 +5,14 @@ #include <algorithm> #include <vector> +#include "common/logging/log.h" + #include "core/file_sys/archive_romfs.h" +#include "core/hle/kernel/process.h" +#include "core/hle/service/fs/archive.h" #include "core/loader/elf.h" #include "core/loader/ncch.h" -#include "core/hle/service/fs/archive.h" -#include "core/mem_map.h" +#include "core/memory.h" #include "3dsx.h" @@ -227,8 +230,13 @@ ResultStatus AppLoader_THREEDSX::Load() { if (!file->IsOpen()) return ResultStatus::Error; - Load3DSXFile(*file, 0x00100000); - Kernel::LoadExec(0x00100000); + Kernel::g_current_process = Kernel::Process::Create(filename, 0); + Kernel::g_current_process->svc_access_mask.set(); + Kernel::g_current_process->address_mappings = default_address_mappings; + + Load3DSXFile(*file, Memory::PROCESS_IMAGE_VADDR); + + Kernel::g_current_process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h index a11667400..096b3ec20 100644 --- a/src/core/loader/3dsx.h +++ b/src/core/loader/3dsx.h @@ -4,6 +4,8 @@ #pragma once +#include <string> + #include "common/common_types.h" #include "core/loader/loader.h" @@ -15,7 +17,8 @@ namespace Loader { /// Loads an 3DSX file class AppLoader_THREEDSX final : public AppLoader { public: - AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } + AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file, std::string filename) + : AppLoader(std::move(file)), filename(std::move(filename)) {} /** * Returns the type of the file @@ -29,6 +32,9 @@ public: * @return ResultStatus result of function */ ResultStatus Load() override; + +private: + std::string filename; }; } // namespace Loader diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 773eaf771..a951bc80f 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -5,13 +5,14 @@ #include <string> #include <memory> -#include "common/common.h" +#include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "common/symbols.h" -#include "core/mem_map.h" -#include "core/loader/elf.h" #include "core/hle/kernel/kernel.h" +#include "core/loader/elf.h" +#include "core/memory.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // ELF Header Constants @@ -349,9 +350,15 @@ ResultStatus AppLoader_ELF::Load() { if (file->ReadBytes(&buffer[0], size) != size) return ResultStatus::Error; + Kernel::g_current_process = Kernel::Process::Create(filename, 0); + Kernel::g_current_process->svc_access_mask.set(); + Kernel::g_current_process->address_mappings = default_address_mappings; + ElfReader elf_reader(&buffer[0]); - elf_reader.LoadInto(0x00100000); - Kernel::LoadExec(elf_reader.GetEntryPoint()); + elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); + // TODO: Fill application title + + Kernel::g_current_process->Run(elf_reader.GetEntryPoint(), 48, Kernel::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index b6e6651f5..32841606a 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -4,6 +4,8 @@ #pragma once +#include <string> + #include "common/common_types.h" #include "core/loader/loader.h" @@ -15,7 +17,8 @@ namespace Loader { /// Loads an ELF/AXF file class AppLoader_ELF final : public AppLoader { public: - AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } + AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file, std::string filename) + : AppLoader(std::move(file)), filename(std::move(filename)) { } /** * Returns the type of the file @@ -29,6 +32,9 @@ public: * @return ResultStatus result of function */ ResultStatus Load() override; + +private: + std::string filename; }; } // namespace Loader diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index aca09b374..8b14edf00 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -4,19 +4,26 @@ #include <string> +#include "common/logging/log.h" #include "common/make_unique.h" #include "core/file_sys/archive_romfs.h" +#include "core/hle/kernel/process.h" +#include "core/hle/service/fs/archive.h" #include "core/loader/3dsx.h" #include "core/loader/elf.h" #include "core/loader/ncch.h" -#include "core/hle/service/fs/archive.h" -#include "core/mem_map.h" //////////////////////////////////////////////////////////////////////////////////////////////////// namespace Loader { +const std::initializer_list<Kernel::AddressMapping> default_address_mappings = { + { 0x1FF50000, 0x8000, true }, // part of DSP RAM + { 0x1FF70000, 0x8000, true }, // part of DSP RAM + { 0x1F000000, 0x600000, false }, // entire VRAM +}; + /** * Identifies the type of a bootable file * @param file open file @@ -41,19 +48,11 @@ static FileType IdentifyFile(FileUtil::IOFile& file) { /** * Guess the type of a bootable file from its extension - * @param filename String filename of bootable file + * @param extension String extension of bootable file * @return FileType of file */ -static FileType GuessFromFilename(const std::string& filename) { - if (filename.size() == 0) { - LOG_ERROR(Loader, "invalid filename %s", filename.c_str()); - return FileType::Error; - } - - size_t extension_loc = filename.find_last_of('.'); - if (extension_loc == std::string::npos) - return FileType::Unknown; - std::string extension = Common::ToLower(filename.substr(extension_loc)); +static FileType GuessFromExtension(const std::string& extension_) { + std::string extension = Common::ToLower(extension_); if (extension == ".elf") return FileType::ELF; @@ -63,8 +62,6 @@ static FileType GuessFromFilename(const std::string& filename) { return FileType::CXI; else if (extension == ".cci") return FileType::CCI; - else if (extension == ".bin") - return FileType::BIN; else if (extension == ".3ds") return FileType::CCI; else if (extension == ".3dsx") @@ -82,8 +79,6 @@ static const char* GetFileTypeString(FileType type) { return "ELF"; case FileType::THREEDSX: return "3DSX"; - case FileType::BIN: - return "raw"; case FileType::Error: case FileType::Unknown: break; @@ -99,8 +94,11 @@ ResultStatus LoadFile(const std::string& filename) { return ResultStatus::Error; } + std::string filename_filename, filename_extension; + Common::SplitPath(filename, nullptr, &filename_filename, &filename_extension); + FileType type = IdentifyFile(*file); - FileType filename_type = GuessFromFilename(filename); + FileType filename_type = GuessFromExtension(filename_extension); if (type != filename_type) { LOG_WARNING(Loader, "File %s has a different type than its extension.", filename.c_str()); @@ -114,11 +112,11 @@ ResultStatus LoadFile(const std::string& filename) { //3DSX file format... case FileType::THREEDSX: - return AppLoader_THREEDSX(std::move(file)).Load(); + return AppLoader_THREEDSX(std::move(file), filename_filename).Load(); // Standard ELF file format... case FileType::ELF: - return AppLoader_ELF(std::move(file)).Load(); + return AppLoader_ELF(std::move(file), filename_filename).Load(); // NCCH/NCSD container formats... case FileType::CXI: @@ -128,24 +126,12 @@ ResultStatus LoadFile(const std::string& filename) { // Load application and RomFS if (ResultStatus::Success == app_loader.Load()) { - Kernel::g_program_id = app_loader.GetProgramId(); Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); return ResultStatus::Success; } break; } - // Raw BIN file format... - case FileType::BIN: - { - size_t size = (size_t)file->GetSize(); - if (file->ReadBytes(Memory::GetPointer(Memory::EXEFS_CODE_VADDR), size) != size) - return ResultStatus::Error; - - Kernel::LoadExec(Memory::EXEFS_CODE_VADDR); - return ResultStatus::Success; - } - // Error occurred durring IdentifyFile... case FileType::Error: diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 3510c6b28..87e16fb98 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -6,9 +6,11 @@ #include <vector> -#include "common/common.h" +#include "common/common_types.h" #include "common/file_util.h" +#include "core/hle/kernel/process.h" + //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace @@ -22,7 +24,6 @@ enum class FileType { CXI, CIA, ELF, - BIN, THREEDSX, //3DSX }; @@ -105,6 +106,12 @@ protected: }; /** + * Common address mappings found in most games, used for binary formats that don't have this + * information. + */ +extern const std::initializer_list<Kernel::AddressMapping> default_address_mappings; + +/** * Identifies and loads a bootable file * @param filename String filename of bootable file * @return ResultStatus result of function diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index aaaa4d650..36e341fd4 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -2,11 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <memory> -#include "core/loader/ncch.h" +#include "common/logging/log.h" +#include "common/make_unique.h" +#include "common/string_util.h" +#include "common/swap.h" + #include "core/hle/kernel/kernel.h" -#include "core/mem_map.h" +#include "core/loader/ncch.h" +#include "core/memory.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace @@ -115,8 +121,21 @@ ResultStatus AppLoader_NCCH::LoadExec() const { std::vector<u8> code; if (ResultStatus::Success == ReadCode(code)) { + std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( + (const char*)exheader_header.codeset_info.name, 8); + u64 program_id = *reinterpret_cast<u64_le const*>(&ncch_header.program_id[0]); + Kernel::g_current_process = Kernel::Process::Create(process_name, program_id); + + // Copy data while converting endianess + std::array<u32, ARRAY_SIZE(exheader_header.arm11_kernel_caps.descriptors)> kernel_caps; + std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps)); + Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); + Memory::WriteBlock(entry_point, &code[0], code.size()); - Kernel::LoadExec(entry_point); + + s32 priority = exheader_header.arm11_system_local_caps.priority; + u32 stack_size = exheader_header.codeset_info.stack_size; + Kernel::g_current_process->Run(entry_point, priority, stack_size); return ResultStatus::Success; } return ResultStatus::Error; @@ -198,20 +217,33 @@ ResultStatus AppLoader_NCCH::Load() { if (file->ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) return ResultStatus::Error; - is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; - entry_point = exheader_header.codeset_info.text.address; - - LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name); - LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no"); - LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point); + is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; + entry_point = exheader_header.codeset_info.text.address; + code_size = exheader_header.codeset_info.text.code_size; + stack_size = exheader_header.codeset_info.stack_size; + bss_size = exheader_header.codeset_info.bss_size; + core_version = exheader_header.arm11_system_local_caps.core_version; + priority = exheader_header.arm11_system_local_caps.priority; + resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category; + + LOG_INFO(Loader, "Name: %s" , exheader_header.codeset_info.name); + LOG_DEBUG(Loader, "Code compressed: %s" , is_compressed ? "yes" : "no"); + LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point); + LOG_DEBUG(Loader, "Code size: 0x%08X", code_size); + LOG_DEBUG(Loader, "Stack size: 0x%08X", stack_size); + LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size); + LOG_DEBUG(Loader, "Core version: %d" , core_version); + LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority); + LOG_DEBUG(Loader, "Resource limit descriptor: 0x%08X", exheader_header.arm11_system_local_caps.resource_limit_descriptor); + LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category); // Read ExeFS... exefs_offset = ncch_header.exefs_offset * kBlockSize; u32 exefs_size = ncch_header.exefs_size * kBlockSize; - LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset); - LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size); + LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset); + LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size); file->Seek(exefs_offset + ncch_offset, SEEK_SET); if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) @@ -247,8 +279,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; - LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); - LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); + LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); + LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); buffer.resize(romfs_size); @@ -262,8 +294,4 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { return ResultStatus::ErrorNotUsed; } -u64 AppLoader_NCCH::GetProgramId() const { - return *reinterpret_cast<u64 const*>(&ncch_header.program_id[0]); -} - } // namespace Loader diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index f6f670060..29e39d2c0 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -4,7 +4,11 @@ #pragma once -#include "common/common.h" +#include <memory> + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "common/swap.h" #include "core/loader/loader.h" @@ -43,6 +47,8 @@ struct NCCH_Header { u8 romfs_super_block_hash[0x20]; }; +static_assert(sizeof(NCCH_Header) == 0x200, "NCCH header structure size is wrong"); + //////////////////////////////////////////////////////////////////////////////////////////////////// // ExeFS (executable file system) headers @@ -61,13 +67,13 @@ struct ExeFs_Header { //////////////////////////////////////////////////////////////////////////////////////////////////// // ExHeader (executable file system header) headers -struct ExHeader_SystemInfoFlags{ +struct ExHeader_SystemInfoFlags { u8 reserved[5]; u8 flag; u8 remaster_version[2]; }; -struct ExHeader_CodeSegmentInfo{ +struct ExHeader_CodeSegmentInfo { u32 address; u32 num_max_pages; u32 code_size; @@ -77,24 +83,24 @@ struct ExHeader_CodeSetInfo { u8 name[8]; ExHeader_SystemInfoFlags flags; ExHeader_CodeSegmentInfo text; - u8 stacksize[4]; + u32 stack_size; ExHeader_CodeSegmentInfo ro; u8 reserved[4]; ExHeader_CodeSegmentInfo data; - u8 bsssize[4]; + u32 bss_size; }; -struct ExHeader_DependencyList{ +struct ExHeader_DependencyList { u8 program_id[0x30][8]; }; -struct ExHeader_SystemInfo{ +struct ExHeader_SystemInfo { u64 save_data_size; u8 jump_id[8]; u8 reserved_2[0x30]; }; -struct ExHeader_StorageInfo{ +struct ExHeader_StorageInfo { u8 ext_save_data_id[8]; u8 system_save_data_id[8]; u8 reserved[8]; @@ -102,30 +108,36 @@ struct ExHeader_StorageInfo{ u8 other_attributes; }; -struct ExHeader_ARM11_SystemLocalCaps{ +struct ExHeader_ARM11_SystemLocalCaps { u8 program_id[8]; u32 core_version; - u8 flags[3]; + u8 reserved_flags[2]; + union { + u8 flags0; + BitField<0, 2, u8> ideal_processor; + BitField<2, 2, u8> affinity_mask; + BitField<4, 4, u8> system_mode; + }; u8 priority; - u8 resource_limit_descriptor[0x16][2]; + u8 resource_limit_descriptor[0x10][2]; ExHeader_StorageInfo storage_info; - u8 service_access_control[0x32][8]; + u8 service_access_control[0x20][8]; u8 ex_service_access_control[0x2][8]; u8 reserved[0xf]; u8 resource_limit_category; }; -struct ExHeader_ARM11_KernelCaps{ - u8 descriptors[28][4]; +struct ExHeader_ARM11_KernelCaps { + u32_le descriptors[28]; u8 reserved[0x10]; }; -struct ExHeader_ARM9_AccessControl{ +struct ExHeader_ARM9_AccessControl { u8 descriptors[15]; u8 descversion; }; -struct ExHeader_Header{ +struct ExHeader_Header { ExHeader_CodeSetInfo codeset_info; ExHeader_DependencyList dependency_list; ExHeader_SystemInfo system_info; @@ -141,6 +153,8 @@ struct ExHeader_Header{ } access_desc; }; +static_assert(sizeof(ExHeader_Header) == 0x800, "ExHeader structure size is wrong"); + //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace @@ -199,12 +213,6 @@ public: */ ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; - /* - * Gets the program id from the NCCH header - * @return u64 Program id - */ - u64 GetProgramId() const; - private: /** @@ -224,6 +232,12 @@ private: bool is_compressed = false; u32 entry_point = 0; + u32 code_size = 0; + u32 stack_size = 0; + u32 bss_size = 0; + u32 core_version = 0; + u8 priority = 0; + u8 resource_limit_category = 0; u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header u32 exefs_offset = 0; diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp index a14e8303e..5ecec9566 100644 --- a/src/core/mem_map.cpp +++ b/src/core/mem_map.cpp @@ -2,88 +2,160 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common.h" -#include "common/mem_arena.h" +#include <map> +#include "common/common_types.h" +#include "common/logging/log.h" + +#include "core/hle/config_mem.h" +#include "core/hle/shared_page.h" #include "core/mem_map.h" +#include "core/memory.h" +#include "core/memory_setup.h" //////////////////////////////////////////////////////////////////////////////////////////////////// namespace Memory { -u8* g_base = nullptr; ///< The base pointer to the auto-mirrored arena. - -static MemArena arena; ///< The MemArena class - -u8* g_exefs_code = nullptr; ///< ExeFS:/.code is loaded here -u8* g_system_mem = nullptr; ///< System memory -u8* g_heap = nullptr; ///< Application heap (main memory) -u8* g_heap_linear = nullptr; ///< Linear heap -u8* g_vram = nullptr; ///< Video memory (VRAM) pointer -u8* g_shared_mem = nullptr; ///< Shared memory -u8* g_dsp_mem = nullptr; ///< DSP memory -u8* g_kernel_mem; ///< Kernel memory - -static u8* physical_bootrom = nullptr; ///< Bootrom physical memory -static u8* uncached_bootrom = nullptr; - -static u8* physical_exefs_code = nullptr; ///< Phsical ExeFS:/.code is loaded here -static u8* physical_system_mem = nullptr; ///< System physical memory -static u8* physical_fcram = nullptr; ///< Main physical memory (FCRAM) -static u8* physical_heap_gsp = nullptr; ///< GSP heap physical memory -static u8* physical_vram = nullptr; ///< Video physical memory (VRAM) -static u8* physical_shared_mem = nullptr; ///< Physical shared memory -static u8* physical_dsp_mem = nullptr; ///< Physical DSP memory -static u8* physical_kernel_mem; ///< Kernel memory - -// We don't declare the IO region in here since its handled by other means. -static MemoryView g_views[] = { - {&g_exefs_code, &physical_exefs_code, EXEFS_CODE_VADDR, EXEFS_CODE_SIZE, 0}, - {&g_vram, &physical_vram, VRAM_VADDR, VRAM_SIZE, 0}, - {&g_heap, &physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM}, - {&g_shared_mem, &physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0}, - {&g_system_mem, &physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0}, - {&g_dsp_mem, &physical_dsp_mem, DSP_MEMORY_VADDR, DSP_MEMORY_SIZE, 0}, - {&g_kernel_mem, &physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0}, - {&g_heap_linear, &physical_heap_gsp, HEAP_LINEAR_VADDR, HEAP_LINEAR_SIZE, 0}, +u8* g_exefs_code; ///< ExeFS:/.code is loaded here +u8* g_heap; ///< Application heap (main memory) +u8* g_shared_mem; ///< Shared memory +u8* g_heap_linear; ///< Linear heap +u8* g_vram; ///< Video memory (VRAM) pointer +u8* g_dsp_mem; ///< DSP memory +u8* g_tls_mem; ///< TLS memory + +namespace { + +struct MemoryArea { + u8** ptr; + u32 base; + u32 size; }; -/*static MemoryView views[] = -{ - {&m_pScratchPad, &m_pPhysicalScratchPad, 0x00010000, SCRATCHPAD_SIZE, 0}, - {NULL, &m_pUncachedScratchPad, 0x40010000, SCRATCHPAD_SIZE, MV_MIRROR_PREVIOUS}, - {&m_pVRAM, &m_pPhysicalVRAM, 0x04000000, 0x00800000, 0}, - {NULL, &m_pUncachedVRAM, 0x44000000, 0x00800000, MV_MIRROR_PREVIOUS}, - {&m_pRAM, &m_pPhysicalRAM, 0x08000000, g_MemorySize, MV_IS_PRIMARY_RAM}, // only from 0x08800000 is it usable (last 24 megs) - {NULL, &m_pUncachedRAM, 0x48000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM}, - {NULL, &m_pKernelRAM, 0x88000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM}, +// We don't declare the IO regions in here since its handled by other means. +static MemoryArea memory_areas[] = { + {&g_exefs_code, PROCESS_IMAGE_VADDR, PROCESS_IMAGE_MAX_SIZE}, + {&g_heap, HEAP_VADDR, HEAP_SIZE }, + {&g_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE }, + {&g_heap_linear, LINEAR_HEAP_VADDR, LINEAR_HEAP_SIZE }, + {&g_vram, VRAM_VADDR, VRAM_SIZE }, + {&g_dsp_mem, DSP_RAM_VADDR, DSP_RAM_SIZE }, + {&g_tls_mem, TLS_AREA_VADDR, TLS_AREA_SIZE }, +}; - // TODO: There are a few swizzled mirrors of VRAM, not sure about the best way to - // implement those. -};*/ +/// Represents a block of memory mapped by ControlMemory/MapMemoryBlock +struct MemoryBlock { + MemoryBlock() : handle(0), base_address(0), address(0), size(0), operation(0), permissions(0) { + } + u32 handle; + u32 base_address; + u32 address; + u32 size; + u32 operation; + u32 permissions; + + const u32 GetVirtualAddress() const{ + return base_address + address; + } +}; -static const int kNumMemViews = sizeof(g_views) / sizeof(MemoryView); ///< Number of mem views +static std::map<u32, MemoryBlock> heap_map; +static std::map<u32, MemoryBlock> heap_linear_map; -void Init() { - int flags = 0; +} + +u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) { + MemoryBlock block; - for (size_t i = 0; i < ARRAY_SIZE(g_views); i++) { - if (g_views[i].flags & MV_IS_PRIMARY_RAM) - g_views[i].size = FCRAM_SIZE; + block.base_address = HEAP_VADDR; + block.size = size; + block.operation = operation; + block.permissions = permissions; + + if (heap_map.size() > 0) { + const MemoryBlock last_block = heap_map.rbegin()->second; + block.address = last_block.address + last_block.size; } + heap_map[block.GetVirtualAddress()] = block; + + return block.GetVirtualAddress(); +} + +u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) { + MemoryBlock block; - g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &arena); + block.base_address = LINEAR_HEAP_VADDR; + block.size = size; + block.operation = operation; + block.permissions = permissions; + + if (heap_linear_map.size() > 0) { + const MemoryBlock last_block = heap_linear_map.rbegin()->second; + block.address = last_block.address + last_block.size; + } + heap_linear_map[block.GetVirtualAddress()] = block; - LOG_DEBUG(HW_Memory, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap, - physical_fcram); + return block.GetVirtualAddress(); +} + +PAddr VirtualToPhysicalAddress(const VAddr addr) { + if (addr == 0) { + return 0; + } else if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) { + return addr - VRAM_VADDR + VRAM_PADDR; + } else if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) { + return addr - LINEAR_HEAP_VADDR + FCRAM_PADDR; + } else if (addr >= DSP_RAM_VADDR && addr < DSP_RAM_VADDR_END) { + return addr - DSP_RAM_VADDR + DSP_RAM_PADDR; + } else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) { + return addr - IO_AREA_VADDR + IO_AREA_PADDR; + } + + LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08x", addr); + // To help with debugging, set bit on address so that it's obviously invalid. + return addr | 0x80000000; +} + +VAddr PhysicalToVirtualAddress(const PAddr addr) { + if (addr == 0) { + return 0; + } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { + return addr - VRAM_PADDR + VRAM_VADDR; + } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { + return addr - FCRAM_PADDR + LINEAR_HEAP_VADDR; + } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) { + return addr - DSP_RAM_PADDR + DSP_RAM_VADDR; + } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) { + return addr - IO_AREA_PADDR + IO_AREA_VADDR; + } + + LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08x", addr); + // To help with debugging, set bit on address so that it's obviously invalid. + return addr | 0x80000000; +} + +void Init() { + InitMemoryMap(); + + for (MemoryArea& area : memory_areas) { + *area.ptr = new u8[area.size]; + MapMemoryRegion(area.base, area.size, *area.ptr); + } + MapMemoryRegion(CONFIG_MEMORY_VADDR, CONFIG_MEMORY_SIZE, (u8*)&ConfigMem::config_mem); + MapMemoryRegion(SHARED_PAGE_VADDR, SHARED_PAGE_SIZE, (u8*)&SharedPage::shared_page); + + LOG_DEBUG(HW_Memory, "initialized OK, RAM at %p", g_heap); } void Shutdown() { - u32 flags = 0; - MemoryMap_Shutdown(g_views, kNumMemViews, flags, &arena); + heap_map.clear(); + heap_linear_map.clear(); - arena.ReleaseSpace(); - g_base = nullptr; + for (MemoryArea& area : memory_areas) { + delete[] *area.ptr; + *area.ptr = nullptr; + } LOG_DEBUG(HW_Memory, "shutdown OK"); } diff --git a/src/core/mem_map.h b/src/core/mem_map.h index bce99dffa..945815cd6 100644 --- a/src/core/mem_map.h +++ b/src/core/mem_map.h @@ -4,163 +4,21 @@ #pragma once -#include "common/common.h" #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" - namespace Memory { -//////////////////////////////////////////////////////////////////////////////////////////////////// - -enum : u32 { - BOOTROM_SIZE = 0x00010000, ///< Bootrom (super secret code/data @ 0x8000) size - BOOTROM_PADDR = 0x00000000, ///< Bootrom physical address - BOOTROM_PADDR_END = (BOOTROM_PADDR + BOOTROM_SIZE), - - BOOTROM_MIRROR_SIZE = 0x00010000, ///< Bootrom Mirror size - BOOTROM_MIRROR_PADDR = 0x00010000, ///< Bootrom Mirror physical address - BOOTROM_MIRROR_PADDR_END = (BOOTROM_MIRROR_PADDR + BOOTROM_MIRROR_SIZE), - - MPCORE_PRIV_SIZE = 0x00002000, ///< MPCore private memory region size - MPCORE_PRIV_PADDR = 0x17E00000, ///< MPCore private memory region physical address - MPCORE_PRIV_PADDR_END = (MPCORE_PRIV_PADDR + MPCORE_PRIV_SIZE), - - FCRAM_SIZE = 0x08000000, ///< FCRAM size - FCRAM_PADDR = 0x20000000, ///< FCRAM physical address - FCRAM_PADDR_END = (FCRAM_PADDR + FCRAM_SIZE), ///< FCRAM end of physical space - FCRAM_VADDR = 0x08000000, ///< FCRAM virtual address - FCRAM_VADDR_END = (FCRAM_VADDR + FCRAM_SIZE), ///< FCRAM end of virtual space - - AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size - AXI_WRAM_PADDR = 0x1FF80000, ///< AXI WRAM physical address - AXI_WRAM_PADDR_END = (AXI_WRAM_PADDR + AXI_WRAM_SIZE), - - SHARED_MEMORY_SIZE = 0x04000000, ///< Shared memory size - SHARED_MEMORY_VADDR = 0x10000000, ///< Shared memory - SHARED_MEMORY_VADDR_END = (SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE), - - DSP_MEMORY_SIZE = 0x00080000, ///< DSP memory size - DSP_MEMORY_VADDR = 0x1FF00000, ///< DSP memory virtual address - DSP_MEMORY_VADDR_END = (DSP_MEMORY_VADDR + DSP_MEMORY_SIZE), - - CONFIG_MEMORY_SIZE = 0x00001000, ///< Configuration memory size - CONFIG_MEMORY_VADDR = 0x1FF80000, ///< Configuration memory virtual address - CONFIG_MEMORY_VADDR_END = (CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE), - - SHARED_PAGE_SIZE = 0x00001000, ///< Shared page size - SHARED_PAGE_VADDR = 0x1FF81000, ///< Shared page virtual address - SHARED_PAGE_VADDR_END = (SHARED_PAGE_VADDR + SHARED_PAGE_SIZE), - - KERNEL_MEMORY_SIZE = 0x00001000, ///< Kernel memory size - KERNEL_MEMORY_VADDR = 0xFFFF0000, ///< Kernel memory where the kthread objects etc are - KERNEL_MEMORY_VADDR_END = (KERNEL_MEMORY_VADDR + KERNEL_MEMORY_SIZE), - - EXEFS_CODE_SIZE = 0x03F00000, - EXEFS_CODE_VADDR = 0x00100000, ///< ExeFS:/.code is loaded here - EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE), - - // Region of FCRAM used by system - SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB - SYSTEM_MEMORY_VADDR = 0x04000000, - SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE), - - HEAP_SIZE = FCRAM_SIZE, ///< Application heap size - //HEAP_PADDR = HEAP_GSP_SIZE, - //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE), - HEAP_VADDR = 0x08000000, - HEAP_VADDR_END = (HEAP_VADDR + HEAP_SIZE), - - HEAP_LINEAR_SIZE = 0x08000000, ///< Linear heap size... TODO: Define correctly? - HEAP_LINEAR_VADDR = 0x14000000, - HEAP_LINEAR_VADDR_END = (HEAP_LINEAR_VADDR + HEAP_LINEAR_SIZE), - HEAP_LINEAR_PADDR = 0x00000000, - HEAP_LINEAR_PADDR_END = (HEAP_LINEAR_PADDR + HEAP_LINEAR_SIZE), - - HARDWARE_IO_SIZE = 0x01000000, - HARDWARE_IO_PADDR = 0x10000000, ///< IO physical address start - HARDWARE_IO_VADDR = 0x1EC00000, ///< IO virtual address start - HARDWARE_IO_PADDR_END = (HARDWARE_IO_PADDR + HARDWARE_IO_SIZE), - HARDWARE_IO_VADDR_END = (HARDWARE_IO_VADDR + HARDWARE_IO_SIZE), - - VRAM_SIZE = 0x00600000, - VRAM_PADDR = 0x18000000, - VRAM_VADDR = 0x1F000000, - VRAM_PADDR_END = (VRAM_PADDR + VRAM_SIZE), - VRAM_VADDR_END = (VRAM_VADDR + VRAM_SIZE), - - SCRATCHPAD_SIZE = 0x00004000, ///< Typical stack size - TODO: Read from exheader - SCRATCHPAD_VADDR_END = 0x10000000, - SCRATCHPAD_VADDR = (SCRATCHPAD_VADDR_END - SCRATCHPAD_SIZE), ///< Stack space -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// Represents a block of memory mapped by ControlMemory/MapMemoryBlock -struct MemoryBlock { - MemoryBlock() : handle(0), base_address(0), address(0), size(0), operation(0), permissions(0) { - } - u32 handle; - u32 base_address; - u32 address; - u32 size; - u32 operation; - u32 permissions; - - const u32 GetVirtualAddress() const{ - return base_address + address; - } -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -// Base is a pointer to the base of the memory map. Yes, some MMU tricks -// are used to set up a full GC or Wii memory map in process memory. on -// 32-bit, you have to mask your offsets with 0x3FFFFFFF. This means that -// some things are mirrored too many times, but eh... it works. - -// In 64-bit, this might point to "high memory" (above the 32-bit limit), -// so be sure to load it into a 64-bit register. -extern u8 *g_base; - -// These are guaranteed to point to "low memory" addresses (sub-32-bit). -// 64-bit: Pointers to low-mem (sub-0x10000000) mirror -// 32-bit: Same as the corresponding physical/virtual pointers. -extern u8* g_heap_linear; ///< Linear heap (main memory) -extern u8* g_heap; ///< Application heap (main memory) -extern u8* g_vram; ///< Video memory (VRAM) -extern u8* g_shared_mem; ///< Shared memory -extern u8* g_kernel_mem; ///< Kernel memory -extern u8* g_dsp_mem; ///< DSP memory -extern u8* g_system_mem; ///< System memory -extern u8* g_exefs_code; ///< ExeFS:/.code is loaded here +extern u8* g_exefs_code; ///< ExeFS:/.code is loaded here +extern u8* g_heap; ///< Application heap (main memory) +extern u8* g_shared_mem; ///< Shared memory +extern u8* g_heap_linear; ///< Linear heap (main memory) +extern u8* g_vram; ///< Video memory (VRAM) +extern u8* g_dsp_mem; ///< DSP memory +extern u8* g_tls_mem; ///< TLS memory void Init(); void Shutdown(); -template <typename T> -inline void Read(T &var, VAddr addr); - -template <typename T> -inline void Write(VAddr addr, T data); - -u8 Read8(VAddr addr); -u16 Read16(VAddr addr); -u32 Read32(VAddr addr); -u64 Read64(VAddr addr); - -u32 Read8_ZX(VAddr addr); -u32 Read16_ZX(VAddr addr); - -void Write8(VAddr addr, u8 data); -void Write16(VAddr addr, u16 data); -void Write32(VAddr addr, u32 data); -void Write64(VAddr addr, u64 data); - -void WriteBlock(VAddr addr, const u8* data, size_t size); - -u8* GetPointer(VAddr virtual_address); - /** * Maps a block of memory on the heap * @param size Size of block in bytes @@ -177,14 +35,15 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions); */ u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions); -inline const char* GetCharPointer(const VAddr address) { - return (const char *)GetPointer(address); -} +/** + * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical + * address. This should be used by services to translate addresses for use by the hardware. + */ +PAddr VirtualToPhysicalAddress(VAddr addr); -/// Converts a physical address to virtual address +/** + * Undoes a mapping performed by VirtualToPhysicalAddress(). + */ VAddr PhysicalToVirtualAddress(PAddr addr); -/// Converts a virtual address to physical address -PAddr VirtualToPhysicalAddress(VAddr addr); - } // namespace diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp deleted file mode 100644 index a161a8204..000000000 --- a/src/core/mem_map_funcs.cpp +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <map> - -#include "common/common.h" - -#include "core/mem_map.h" -#include "core/hw/hw.h" -#include "hle/config_mem.h" -#include "hle/shared_page.h" - -namespace Memory { - -static std::map<u32, MemoryBlock> heap_map; -static std::map<u32, MemoryBlock> heap_linear_map; -static std::map<u32, MemoryBlock> shared_map; - -/// Convert a physical address to virtual address -VAddr PhysicalToVirtualAddress(const PAddr addr) { - // Our memory interface read/write functions assume virtual addresses. Put any physical address - // to virtual address translations here. This is quite hacky, but necessary until we implement - // proper MMU emulation. - // TODO: Screw it, I'll let bunnei figure out how to do this properly. - if ((addr >= VRAM_PADDR) && (addr < VRAM_PADDR_END)) { - return addr - VRAM_PADDR + VRAM_VADDR; - }else if ((addr >= FCRAM_PADDR) && (addr < FCRAM_PADDR_END)) { - return addr - FCRAM_PADDR + FCRAM_VADDR; - } - - LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08x", addr); - return addr; -} - -/// Convert a physical address to virtual address -PAddr VirtualToPhysicalAddress(const VAddr addr) { - // Our memory interface read/write functions assume virtual addresses. Put any physical address - // to virtual address translations here. This is quite hacky, but necessary until we implement - // proper MMU emulation. - // TODO: Screw it, I'll let bunnei figure out how to do this properly. - if ((addr >= VRAM_VADDR) && (addr < VRAM_VADDR_END)) { - return addr - 0x07000000; - } else if ((addr >= FCRAM_VADDR) && (addr < FCRAM_VADDR_END)) { - return addr - FCRAM_VADDR + FCRAM_PADDR; - } - - LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08x", addr); - return addr; -} - -template <typename T> -inline void Read(T &var, const VAddr vaddr) { - // TODO: Figure out the fastest order of tests for both read and write (they are probably different). - // TODO: Make sure this represents the mirrors in a correct way. - // Could just do a base-relative read, too.... TODO - - // Kernel memory command buffer - if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { - var = *((const T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR]); - - // ExeFS:/.code is loaded here - } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { - var = *((const T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR]); - - // FCRAM - linear heap - } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) { - var = *((const T*)&g_heap_linear[vaddr - HEAP_LINEAR_VADDR]); - - // FCRAM - application heap - } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { - var = *((const T*)&g_heap[vaddr - HEAP_VADDR]); - - // Shared memory - } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { - var = *((const T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR]); - - // System memory - } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { - var = *((const T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR]); - - // Config memory - } else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) { - ConfigMem::Read<T>(var, vaddr); - - // Shared page - } else if ((vaddr >= SHARED_PAGE_VADDR) && (vaddr < SHARED_PAGE_VADDR_END)) { - SharedPage::Read<T>(var, vaddr); - - // DSP memory - } else if ((vaddr >= DSP_MEMORY_VADDR) && (vaddr < DSP_MEMORY_VADDR_END)) { - var = *((const T*)&g_dsp_mem[vaddr - DSP_MEMORY_VADDR]); - - // VRAM - } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { - var = *((const T*)&g_vram[vaddr - VRAM_VADDR]); - - } else { - LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, vaddr); - } -} - -template <typename T> -inline void Write(const VAddr vaddr, const T data) { - - // Kernel memory command buffer - if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { - *(T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR] = data; - - // ExeFS:/.code is loaded here - } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { - *(T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR] = data; - - // FCRAM - linear heap - } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) { - *(T*)&g_heap_linear[vaddr - HEAP_LINEAR_VADDR] = data; - - // FCRAM - application heap - } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { - *(T*)&g_heap[vaddr - HEAP_VADDR] = data; - - // Shared memory - } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { - *(T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR] = data; - - // System memory - } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { - *(T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR] = data; - - // VRAM - } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { - *(T*)&g_vram[vaddr - VRAM_VADDR] = data; - - // DSP memory - } else if ((vaddr >= DSP_MEMORY_VADDR) && (vaddr < DSP_MEMORY_VADDR_END)) { - *(T*)&g_dsp_mem[vaddr - DSP_MEMORY_VADDR] = data; - - //} else if ((vaddr & 0xFFFF0000) == 0x1FF80000) { - // ASSERT_MSG(MEMMAP, false, "umimplemented write to Configuration Memory"); - //} else if ((vaddr & 0xFFFFF000) == 0x1FF81000) { - // ASSERT_MSG(MEMMAP, false, "umimplemented write to shared page"); - - // Error out... - } else { - LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, vaddr); - } -} - -u8 *GetPointer(const VAddr vaddr) { - // Kernel memory command buffer - if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { - return g_kernel_mem + (vaddr - KERNEL_MEMORY_VADDR); - - // ExeFS:/.code is loaded here - } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { - return g_exefs_code + (vaddr - EXEFS_CODE_VADDR); - - // FCRAM - linear heap - } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) { - return g_heap_linear + (vaddr - HEAP_LINEAR_VADDR); - - // FCRAM - application heap - } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { - return g_heap + (vaddr - HEAP_VADDR); - - // Shared memory - } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { - return g_shared_mem + (vaddr - SHARED_MEMORY_VADDR); - - // System memory - } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { - return g_system_mem + (vaddr - SYSTEM_MEMORY_VADDR); - - // VRAM - } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { - return g_vram + (vaddr - VRAM_VADDR); - - } else { - LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr); - return 0; - } -} - -/** - * Maps a block of memory on the heap - * @param size Size of block in bytes - * @param operation Memory map operation type - * @param flags Memory allocation flags - */ -u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) { - MemoryBlock block; - - block.base_address = HEAP_VADDR; - block.size = size; - block.operation = operation; - block.permissions = permissions; - - if (heap_map.size() > 0) { - const MemoryBlock last_block = heap_map.rbegin()->second; - block.address = last_block.address + last_block.size; - } - heap_map[block.GetVirtualAddress()] = block; - - return block.GetVirtualAddress(); -} - -/** - * Maps a block of memory on the linear heap - * @param size Size of block in bytes - * @param operation Memory map operation type - * @param flags Memory allocation flags - */ -u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) { - MemoryBlock block; - - block.base_address = HEAP_LINEAR_VADDR; - block.size = size; - block.operation = operation; - block.permissions = permissions; - - if (heap_linear_map.size() > 0) { - const MemoryBlock last_block = heap_linear_map.rbegin()->second; - block.address = last_block.address + last_block.size; - } - heap_linear_map[block.GetVirtualAddress()] = block; - - return block.GetVirtualAddress(); -} - -u8 Read8(const VAddr addr) { - u8 data = 0; - Read<u8>(data, addr); - return data; -} - -u16 Read16(const VAddr addr) { - u16_le data = 0; - Read<u16_le>(data, addr); - return (u16)data; -} - -u32 Read32(const VAddr addr) { - u32_le data = 0; - Read<u32_le>(data, addr); - return (u32)data; -} - -u64 Read64(const VAddr addr) { - u64_le data = 0; - Read<u64_le>(data, addr); - return (u64)data; -} - -u32 Read8_ZX(const VAddr addr) { - return (u32)Read8(addr); -} - -u32 Read16_ZX(const VAddr addr) { - return (u32)Read16(addr); -} - -void Write8(const VAddr addr, const u8 data) { - Write<u8>(addr, data); -} - -void Write16(const VAddr addr, const u16 data) { - Write<u16_le>(addr, data); -} - -void Write32(const VAddr addr, const u32 data) { - Write<u32_le>(addr, data); -} - -void Write64(const VAddr addr, const u64 data) { - Write<u64_le>(addr, data); -} - -void WriteBlock(const VAddr addr, const u8* data, const size_t size) { - u32 offset = 0; - while (offset < (size & ~3)) { - Write32(addr + offset, *(u32*)&data[offset]); - offset += 4; - } - - if (size & 2) { - Write16(addr + offset, *(u16*)&data[offset]); - offset += 2; - } - - if (size & 1) - Write8(addr + offset, data[offset]); -} - -} // namespace diff --git a/src/core/memory.cpp b/src/core/memory.cpp new file mode 100644 index 000000000..5d8069acd --- /dev/null +++ b/src/core/memory.cpp @@ -0,0 +1,202 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <array> + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/swap.h" + +#include "core/hle/config_mem.h" +#include "core/hle/shared_page.h" +#include "core/hw/hw.h" +#include "core/mem_map.h" +#include "core/memory.h" + +namespace Memory { + +const u32 PAGE_MASK = PAGE_SIZE - 1; +const int PAGE_BITS = 12; + +enum class PageType { + /// Page is unmapped and should cause an access error. + Unmapped, + /// Page is mapped to regular memory. This is the only type you can get pointers to. + Memory, + /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. + Special, +}; + +/** + * A (reasonably) fast way of allowing switchable and remmapable process address spaces. It loosely + * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and + * fetching requirements when acessing. In the usual case of an access to regular memory, it only + * requires an indexed fetch and a check for NULL. + */ +struct PageTable { + static const size_t NUM_ENTRIES = 1 << (32 - PAGE_BITS); + + /** + * Array of memory pointers backing each page. An entry can only be non-null if the + * corresponding entry in the `attributes` array is of type `Memory`. + */ + std::array<u8*, NUM_ENTRIES> pointers; + + /** + * Array of fine grained page attributes. If it is set to any value other than `Memory`, then + * the corresponding entry in `pointer` MUST be set to null. + */ + std::array<PageType, NUM_ENTRIES> attributes; +}; + +/// Singular page table used for the singleton process +static PageTable main_page_table; +/// Currently active page table +static PageTable* current_page_table = &main_page_table; + +static void MapPages(u32 base, u32 size, u8* memory, PageType type) { + LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, (base + size) * PAGE_SIZE); + + u32 end = base + size; + + while (base != end) { + ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); + + if (current_page_table->attributes[base] != PageType::Unmapped) { + LOG_ERROR(HW_Memory, "overlapping memory ranges at %08X", base * PAGE_SIZE); + } + current_page_table->attributes[base] = type; + current_page_table->pointers[base] = memory; + + base += 1; + memory += PAGE_SIZE; + } +} + +void InitMemoryMap() { + main_page_table.pointers.fill(nullptr); + main_page_table.attributes.fill(PageType::Unmapped); +} + +void MapMemoryRegion(VAddr base, u32 size, u8* target) { + ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); + ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); + MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); +} + +void MapIoRegion(VAddr base, u32 size) { + ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); + ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); + MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); +} + +template <typename T> +T Read(const VAddr vaddr) { + const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; + if (page_pointer) { + return *reinterpret_cast<const T*>(page_pointer + (vaddr & PAGE_MASK)); + } + + PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (type) { + case PageType::Unmapped: + LOG_ERROR(HW_Memory, "unmapped Read%lu @ 0x%08X", sizeof(T) * 8, vaddr); + return 0; + case PageType::Memory: + ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); + case PageType::Special: + LOG_ERROR(HW_Memory, "I/O reads aren't implemented yet @ %08X", vaddr); + return 0; + default: + UNREACHABLE(); + } +} + +template <typename T> +void Write(const VAddr vaddr, const T data) { + u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; + if (page_pointer) { + *reinterpret_cast<T*>(page_pointer + (vaddr & PAGE_MASK)) = data; + return; + } + + PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (type) { + case PageType::Unmapped: + LOG_ERROR(HW_Memory, "unmapped Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32) data, vaddr); + return; + case PageType::Memory: + ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); + case PageType::Special: + LOG_ERROR(HW_Memory, "I/O writes aren't implemented yet @ %08X", vaddr); + return; + default: + UNREACHABLE(); + } +} + +u8* GetPointer(const VAddr vaddr) { + u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; + if (page_pointer) { + return page_pointer + (vaddr & PAGE_MASK); + } + + LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr); + return nullptr; +} + +u8* GetPhysicalPointer(PAddr address) { + return GetPointer(PhysicalToVirtualAddress(address)); +} + +u8 Read8(const VAddr addr) { + return Read<u8>(addr); +} + +u16 Read16(const VAddr addr) { + return Read<u16_le>(addr); +} + +u32 Read32(const VAddr addr) { + return Read<u32_le>(addr); +} + +u64 Read64(const VAddr addr) { + return Read<u64_le>(addr); +} + +void Write8(const VAddr addr, const u8 data) { + Write<u8>(addr, data); +} + +void Write16(const VAddr addr, const u16 data) { + Write<u16_le>(addr, data); +} + +void Write32(const VAddr addr, const u32 data) { + Write<u32_le>(addr, data); +} + +void Write64(const VAddr addr, const u64 data) { + Write<u64_le>(addr, data); +} + +void WriteBlock(const VAddr addr, const u8* data, const size_t size) { + u32 offset = 0; + while (offset < (size & ~3)) { + Write32(addr + offset, *(u32*)&data[offset]); + offset += 4; + } + + if (size & 2) { + Write16(addr + offset, *(u16*)&data[offset]); + offset += 2; + } + + if (size & 1) + Write8(addr + offset, data[offset]); +} + +} // namespace diff --git a/src/core/memory.h b/src/core/memory.h new file mode 100644 index 000000000..2d225801b --- /dev/null +++ b/src/core/memory.h @@ -0,0 +1,129 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Memory { + +/** + * Page size used by the ARM architecture. This is the smallest granularity with which memory can + * be mapped. + */ +const u32 PAGE_SIZE = 0x1000; + +/// Physical memory regions as seen from the ARM11 +enum : PAddr { + /// IO register area + IO_AREA_PADDR = 0x10100000, + IO_AREA_SIZE = 0x01000000, ///< IO area size (16MB) + IO_AREA_PADDR_END = IO_AREA_PADDR + IO_AREA_SIZE, + + /// MPCore internal memory region + MPCORE_RAM_PADDR = 0x17E00000, + MPCORE_RAM_SIZE = 0x00002000, ///< MPCore internal memory size (8KB) + MPCORE_RAM_PADDR_END = MPCORE_RAM_PADDR + MPCORE_RAM_SIZE, + + /// Video memory + VRAM_PADDR = 0x18000000, + VRAM_SIZE = 0x00600000, ///< VRAM size (6MB) + VRAM_PADDR_END = VRAM_PADDR + VRAM_SIZE, + + /// DSP memory + DSP_RAM_PADDR = 0x1FF00000, + DSP_RAM_SIZE = 0x00080000, ///< DSP memory size (512KB) + DSP_RAM_PADDR_END = DSP_RAM_PADDR + DSP_RAM_SIZE, + + /// AXI WRAM + AXI_WRAM_PADDR = 0x1FF80000, + AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size (512KB) + AXI_WRAM_PADDR_END = AXI_WRAM_PADDR + AXI_WRAM_SIZE, + + /// Main FCRAM + FCRAM_PADDR = 0x20000000, + FCRAM_SIZE = 0x08000000, ///< FCRAM size (128MB) + FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE, +}; + +/// Virtual user-space memory regions +enum : VAddr { + /// Where the application text, data and bss reside. + PROCESS_IMAGE_VADDR = 0x00100000, + PROCESS_IMAGE_MAX_SIZE = 0x03F00000, + PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE, + + /// Area where IPC buffers are mapped onto. + IPC_MAPPING_VADDR = 0x04000000, + IPC_MAPPING_SIZE = 0x04000000, + IPC_MAPPING_VADDR_END = IPC_MAPPING_VADDR + IPC_MAPPING_SIZE, + + /// Application heap (includes stack). + HEAP_VADDR = 0x08000000, + HEAP_SIZE = 0x08000000, + HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE, + + /// Area where shared memory buffers are mapped onto. + SHARED_MEMORY_VADDR = 0x10000000, + SHARED_MEMORY_SIZE = 0x04000000, + SHARED_MEMORY_VADDR_END = SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE, + + /// Maps 1:1 to an offset in FCRAM. Used for HW allocations that need to be linear in physical memory. + LINEAR_HEAP_VADDR = 0x14000000, + LINEAR_HEAP_SIZE = 0x08000000, + LINEAR_HEAP_VADDR_END = LINEAR_HEAP_VADDR + LINEAR_HEAP_SIZE, + + /// Maps 1:1 to the IO register area. + IO_AREA_VADDR = 0x1EC00000, + IO_AREA_VADDR_END = IO_AREA_VADDR + IO_AREA_SIZE, + + /// Maps 1:1 to VRAM. + VRAM_VADDR = 0x1F000000, + VRAM_VADDR_END = VRAM_VADDR + VRAM_SIZE, + + /// Maps 1:1 to DSP memory. + DSP_RAM_VADDR = 0x1FF00000, + DSP_RAM_VADDR_END = DSP_RAM_VADDR + DSP_RAM_SIZE, + + /// Read-only page containing kernel and system configuration values. + CONFIG_MEMORY_VADDR = 0x1FF80000, + CONFIG_MEMORY_SIZE = 0x00001000, + CONFIG_MEMORY_VADDR_END = CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE, + + /// Usually read-only page containing mostly values read from hardware. + SHARED_PAGE_VADDR = 0x1FF81000, + SHARED_PAGE_SIZE = 0x00001000, + SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE, + + // TODO(yuriks): The size of this area is dynamic, the kernel grows + // it as more and more threads are created. For now we'll just use a + // hardcoded value. + /// Area where TLS (Thread-Local Storage) buffers are allocated. + TLS_AREA_VADDR = 0x1FF82000, + TLS_AREA_SIZE = 0x00030000, // Each TLS buffer is 0x200 bytes, allows for 300 threads + TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE, +}; + +u8 Read8(VAddr addr); +u16 Read16(VAddr addr); +u32 Read32(VAddr addr); +u64 Read64(VAddr addr); + +void Write8(VAddr addr, u8 data); +void Write16(VAddr addr, u16 data); +void Write32(VAddr addr, u32 data); +void Write64(VAddr addr, u64 data); + +void WriteBlock(VAddr addr, const u8* data, size_t size); + +u8* GetPointer(VAddr virtual_address); + +/** + * Gets a pointer to the memory region beginning at the specified physical address. + * + * @note This is currently implemented using PhysicalToVirtualAddress(). + */ +u8* GetPhysicalPointer(PAddr address); + +} diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h new file mode 100644 index 000000000..46263495f --- /dev/null +++ b/src/core/memory_setup.h @@ -0,0 +1,29 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Memory { + +void InitMemoryMap(); + +/** + * Maps an allocated buffer onto a region of the emulated process address space. + * + * @param base The address to start mapping at. Must be page-aligned. + * @param size The amount of bytes to map. Must be page-aligned. + * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. + */ +void MapMemoryRegion(VAddr base, u32 size, u8* target); + +/** + * Maps a region of the emulated process address space as a IO region. + * @note Currently this can only be used to mark a region as being IO, since actual memory-mapped + * IO isn't yet supported. + */ +void MapIoRegion(VAddr base, u32 size); + +} diff --git a/src/core/settings.h b/src/core/settings.h index 870eea958..0f4700241 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -44,6 +44,11 @@ struct Values { // System Region int region_value; + // Renderer + float bg_red; + float bg_green; + float bg_blue; + std::string log_filter; } extern values; 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 @@ namespace System { -volatile State g_state; - -void UpdateState(State state) { -} - void Init(EmuWindow* emu_window) { Core::Init(); CoreTiming::Init(); @@ -29,13 +24,6 @@ void Init(EmuWindow* emu_window) { VideoCore::Init(emu_window); } -void RunLoopFor(int cycles) { - RunLoopUntil(CoreTiming::GetTicks() + cycles); -} - -void RunLoopUntil(u64 global_cycles) { -} - void Shutdown() { VideoCore::Shutdown(); 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 @@ #pragma once -#include "common/emu_window.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// +class EmuWindow; namespace System { -// State of the full emulator -enum State { - STATE_NULL = 0, ///< System is in null state, nothing initialized - STATE_IDLE, ///< System is in an initialized state, but not running - STATE_RUNNING, ///< System is running - STATE_LOADING, ///< System is loading a ROM - STATE_HALTED, ///< System is halted (error) - STATE_STALLED, ///< System is stalled (unused) - STATE_DEBUG, ///< System is in a special debug mode (unused) - STATE_DIE ///< System is shutting down -}; - -extern volatile State g_state; - -void UpdateState(State state); void Init(EmuWindow* emu_window); -void RunLoopFor(int cycles); -void RunLoopUntil(u64 global_cycles); void Shutdown(); } diff --git a/src/video_core/color.h b/src/video_core/color.h index 43d635e2c..4d2026eb0 100644 --- a/src/video_core/color.h +++ b/src/video_core/color.h @@ -5,6 +5,8 @@ #pragma once #include "common/common_types.h" +#include "common/swap.h" + #include "video_core/math.h" namespace Color { diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index e031871e8..1ea7cad07 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -27,6 +27,10 @@ static int float_regs_counter = 0; static u32 uniform_write_buffer[4]; +static int default_attr_counter = 0; + +static u32 default_attr_write_buffer[3]; + Common::Profiling::TimingCategory category_drawing("Drawing"); static inline void WritePicaReg(u32 id, u32 value, u32 mask) { @@ -71,12 +75,9 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { u32 vertex_attribute_sources[16]; boost::fill(vertex_attribute_sources, 0xdeadbeef); u32 vertex_attribute_strides[16]; - u32 vertex_attribute_formats[16]; + Regs::VertexAttributeFormat vertex_attribute_formats[16]; - // HACK: Initialize vertex_attribute_elements to zero to prevent infinite loops below. - // This is one of the hacks required to deal with uninitalized vertex attributes. - // TODO: Fix this properly. - u32 vertex_attribute_elements[16] = {}; + u32 vertex_attribute_elements[16]; u32 vertex_attribute_element_size[16]; // Setup attribute data from loaders @@ -90,7 +91,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { u32 attribute_index = loader_config.GetComponent(component); vertex_attribute_sources[attribute_index] = load_address; vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count); - vertex_attribute_formats[attribute_index] = static_cast<u32>(attribute_config.GetFormat(attribute_index)); + vertex_attribute_formats[attribute_index] = attribute_config.GetFormat(attribute_index); vertex_attribute_elements[attribute_index] = attribute_config.GetNumElements(attribute_index); vertex_attribute_element_size[attribute_index] = attribute_config.GetElementSizeInBytes(attribute_index); load_address += attribute_config.GetStride(attribute_index); @@ -101,7 +102,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { bool is_indexed = (id == PICA_REG_INDEX(trigger_draw_indexed)); const auto& index_info = registers.index_array; - const u8* index_address_8 = Memory::GetPointer(PAddrToVAddr(base_address + index_info.offset)); + const u8* index_address_8 = Memory::GetPhysicalPointer(base_address + index_info.offset); const u16* index_address_16 = (u16*)index_address_8; bool index_u16 = index_info.format != 0; @@ -126,26 +127,29 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { input.attr[0].w = debug_token; for (int i = 0; i < attribute_config.GetNumTotalAttributes(); ++i) { - for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) { - const u8* srcdata = Memory::GetPointer(PAddrToVAddr(vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i])); - - // TODO(neobrain): Ocarina of Time 3D has GetNumTotalAttributes return 8, - // yet only provides 2 valid source data addresses. Need to figure out - // what's wrong there, until then we just continue when address lookup fails - if (srcdata == nullptr) - continue; - - const float srcval = (vertex_attribute_formats[i] == 0) ? *(s8*)srcdata : - (vertex_attribute_formats[i] == 1) ? *(u8*)srcdata : - (vertex_attribute_formats[i] == 2) ? *(s16*)srcdata : - *(float*)srcdata; - input.attr[i][comp] = float24::FromFloat32(srcval); - LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", - comp, i, vertex, index, - attribute_config.GetPhysicalBaseAddress(), - vertex_attribute_sources[i] - base_address, - vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i], - input.attr[i][comp].ToFloat32()); + if (attribute_config.IsDefaultAttribute(i)) { + input.attr[i] = VertexShader::GetDefaultAttribute(i); + LOG_TRACE(HW_GPU, "Loaded default attribute %x for vertex %x (index %x): (%f, %f, %f, %f)", + i, vertex, index, + input.attr[i][0].ToFloat32(), input.attr[i][1].ToFloat32(), + input.attr[i][2].ToFloat32(), input.attr[i][3].ToFloat32()); + } else { + for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) { + const u8* srcdata = Memory::GetPhysicalPointer(vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i]); + + const float srcval = (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::BYTE) ? *(s8*)srcdata : + (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::UBYTE) ? *(u8*)srcdata : + (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? *(s16*)srcdata : + *(float*)srcdata; + + input.attr[i][comp] = float24::FromFloat32(srcval); + LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", + comp, i, vertex, index, + attribute_config.GetPhysicalBaseAddress(), + vertex_attribute_sources[i] - base_address, + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i], + input.attr[i][comp].ToFloat32()); + } } } @@ -224,7 +228,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { // it directly write the values? uniform_write_buffer[float_regs_counter++] = value; - // Uniforms are written in a packed format such that 4 float24 values are encoded in + // Uniforms are written in a packed format such that four float24 values are encoded in // three 32-bit numbers. We write to internal memory once a full such vector is // written. if ((float_regs_counter >= 4 && uniform_setup.IsFloat32()) || @@ -259,6 +263,46 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { } break; } + + // Load default vertex input attributes + case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[0], 0x233): + case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[1], 0x234): + case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[2], 0x235): + { + // TODO: Does actual hardware indeed keep an intermediate buffer or does + // it directly write the values? + default_attr_write_buffer[default_attr_counter++] = value; + + // Default attributes are written in a packed format such that four float24 values are encoded in + // three 32-bit numbers. We write to internal memory once a full such vector is + // written. + if (default_attr_counter >= 3) { + default_attr_counter = 0; + + auto& setup = registers.vs_default_attributes_setup; + + if (setup.index >= 16) { + LOG_ERROR(HW_GPU, "Invalid VS default attribute index %d", (int)setup.index); + break; + } + + Math::Vec4<float24>& attribute = VertexShader::GetDefaultAttribute(setup.index); + + // NOTE: The destination component order indeed is "backwards" + attribute.w = float24::FromRawFloat24(default_attr_write_buffer[0] >> 8); + attribute.z = float24::FromRawFloat24(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF)); + attribute.y = float24::FromRawFloat24(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF)); + attribute.x = float24::FromRawFloat24(default_attr_write_buffer[2] & 0xFFFFFF); + + LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index, + attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(), + attribute.w.ToFloat32()); + + // TODO: Verify that this actually modifies the register! + setup.index = setup.index + 1; + } + break; + } // Load shader program code case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[0], 0x2cc): diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 83982b4f2..883df48a5 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -393,6 +393,17 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture } } + case Regs::TextureFormat::I4: + { + u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1); + const u8* source_ptr = source + morton_offset / 2; + + u8 i = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF); + i = Color::Convert4To8(i); + + return { i, i, i, 255 }; + } + case Regs::TextureFormat::A4: { u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1); @@ -507,7 +518,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture // Add modifier unsigned table_index = (x < 2) ? table_index_1.Value() : table_index_2.Value(); - static const auto etc1_modifier_table = std::array<std::array<u8, 2>, 8>{{ + static const std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{ { 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 }, { 18, 60 }, { 24, 80 }, { 33, 106 }, { 47, 183 } }}; @@ -597,7 +608,7 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { png_init_io(png_ptr, fp.GetHandle()); - // Write header (8 bit colour depth) + // Write header (8 bit color depth) png_set_IHDR(png_ptr, info_ptr, texture_config.width, texture_config.height, 8, PNG_COLOR_TYPE_RGB /*_ALPHA*/, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); diff --git a/src/video_core/pica.h b/src/video_core/pica.h index fe20cd77d..3fbf95721 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -10,10 +10,11 @@ #include <map> #include <vector> +#include "common/assert.h" #include "common/bit_field.h" +#include "common/common_funcs.h" #include "common/common_types.h" - -#include "core/mem_map.h" +#include "common/logging/log.h" namespace Pica { @@ -153,7 +154,7 @@ struct Regs { I8 = 7, A8 = 8, IA4 = 9, - + I4 = 10, A4 = 11, ETC1 = 12, // compressed ETC1A4 = 13, // compressed @@ -223,7 +224,8 @@ struct Regs { Texture1 = 0x4, Texture2 = 0x5, Texture3 = 0x6, - // 0x7-0xc = primary color?? + + PreviousBuffer = 0xd, Constant = 0xe, Previous = 0xf, }; @@ -296,7 +298,18 @@ struct Regs { BitField<24, 8, u32> const_a; }; - INSERT_PADDING_WORDS(0x1); + union { + BitField< 0, 2, u32> color_scale; + BitField<16, 2, u32> alpha_scale; + }; + + inline unsigned GetColorMultiplier() const { + return (color_scale < 3) ? (1 << color_scale) : 1; + } + + inline unsigned GetAlphaMultiplier() const { + return (alpha_scale < 3) ? (1 << alpha_scale) : 1; + } }; TevStageConfig tev_stage0; @@ -306,11 +319,36 @@ struct Regs { TevStageConfig tev_stage2; INSERT_PADDING_WORDS(0x3); TevStageConfig tev_stage3; - INSERT_PADDING_WORDS(0x13); + INSERT_PADDING_WORDS(0x3); + + union { + // Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in + // these masks are set + BitField< 8, 4, u32> update_mask_rgb; + BitField<12, 4, u32> update_mask_a; + + bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { + return (stage_index < 4) && (update_mask_rgb & (1 << stage_index)); + } + + bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { + return (stage_index < 4) && (update_mask_a & (1 << stage_index)); + } + } tev_combiner_buffer_input; + + INSERT_PADDING_WORDS(0xf); TevStageConfig tev_stage4; INSERT_PADDING_WORDS(0x3); TevStageConfig tev_stage5; - INSERT_PADDING_WORDS(0x3); + + union { + BitField< 0, 8, u32> r; + BitField< 8, 8, u32> g; + BitField<16, 8, u32> b; + BitField<24, 8, u32> a; + } tev_combiner_buffer_color; + + INSERT_PADDING_WORDS(0x2); const std::array<Regs::TevStageConfig,6> GetTevStages() const { return { tev_stage0, tev_stage1, @@ -423,9 +461,7 @@ struct Regs { D24S8 = 3 }; - /* - * Returns the number of bytes in the specified depth format - */ + // Returns the number of bytes in the specified depth format static u32 BytesPerDepthPixel(DepthFormat format) { switch (format) { case DepthFormat::D16: @@ -440,6 +476,20 @@ struct Regs { } } + // Returns the number of bits per depth component of the specified depth format + static u32 DepthBitsPerPixel(DepthFormat format) { + switch (format) { + case DepthFormat::D16: + return 16; + case DepthFormat::D24: + case DepthFormat::D24S8: + return 24; + default: + LOG_CRITICAL(HW_GPU, "Unknown depth format %u", format); + UNIMPLEMENTED(); + } + } + struct { // Components are laid out in reverse byte order, most significant bits first. enum ColorFormat : u32 { @@ -489,14 +539,14 @@ struct Regs { INSERT_PADDING_WORDS(0xe0); - struct { - enum class Format : u64 { - BYTE = 0, - UBYTE = 1, - SHORT = 2, - FLOAT = 3, - }; + enum class VertexAttributeFormat : u64 { + BYTE = 0, + UBYTE = 1, + SHORT = 2, + FLOAT = 3, + }; + struct { BitField<0, 29, u32> base_address; u32 GetPhysicalBaseAddress() const { @@ -505,29 +555,29 @@ struct Regs { // Descriptor for internal vertex attributes union { - BitField< 0, 2, Format> format0; // size of one element + BitField< 0, 2, VertexAttributeFormat> format0; // size of one element BitField< 2, 2, u64> size0; // number of elements minus 1 - BitField< 4, 2, Format> format1; + BitField< 4, 2, VertexAttributeFormat> format1; BitField< 6, 2, u64> size1; - BitField< 8, 2, Format> format2; + BitField< 8, 2, VertexAttributeFormat> format2; BitField<10, 2, u64> size2; - BitField<12, 2, Format> format3; + BitField<12, 2, VertexAttributeFormat> format3; BitField<14, 2, u64> size3; - BitField<16, 2, Format> format4; + BitField<16, 2, VertexAttributeFormat> format4; BitField<18, 2, u64> size4; - BitField<20, 2, Format> format5; + BitField<20, 2, VertexAttributeFormat> format5; BitField<22, 2, u64> size5; - BitField<24, 2, Format> format6; + BitField<24, 2, VertexAttributeFormat> format6; BitField<26, 2, u64> size6; - BitField<28, 2, Format> format7; + BitField<28, 2, VertexAttributeFormat> format7; BitField<30, 2, u64> size7; - BitField<32, 2, Format> format8; + BitField<32, 2, VertexAttributeFormat> format8; BitField<34, 2, u64> size8; - BitField<36, 2, Format> format9; + BitField<36, 2, VertexAttributeFormat> format9; BitField<38, 2, u64> size9; - BitField<40, 2, Format> format10; + BitField<40, 2, VertexAttributeFormat> format10; BitField<42, 2, u64> size10; - BitField<44, 2, Format> format11; + BitField<44, 2, VertexAttributeFormat> format11; BitField<46, 2, u64> size11; BitField<48, 12, u64> attribute_mask; @@ -536,8 +586,8 @@ struct Regs { BitField<60, 4, u64> num_extra_attributes; }; - inline Format GetFormat(int n) const { - Format formats[] = { + inline VertexAttributeFormat GetFormat(int n) const { + VertexAttributeFormat formats[] = { format0, format1, format2, format3, format4, format5, format6, format7, format8, format9, format10, format11 @@ -555,14 +605,18 @@ struct Regs { } inline int GetElementSizeInBytes(int n) const { - return (GetFormat(n) == Format::FLOAT) ? 4 : - (GetFormat(n) == Format::SHORT) ? 2 : 1; + return (GetFormat(n) == VertexAttributeFormat::FLOAT) ? 4 : + (GetFormat(n) == VertexAttributeFormat::SHORT) ? 2 : 1; } inline int GetStride(int n) const { return GetNumElements(n) * GetElementSizeInBytes(n); } + inline bool IsDefaultAttribute(int id) const { + return (id >= 12) || (attribute_mask & (1 << id)) != 0; + } + inline int GetNumTotalAttributes() const { return (int)num_extra_attributes+1; } @@ -625,7 +679,18 @@ struct Regs { u32 trigger_draw; u32 trigger_draw_indexed; - INSERT_PADDING_WORDS(0x2e); + INSERT_PADDING_WORDS(0x2); + + // These registers are used to setup the default "fall-back" vertex shader attributes + struct { + // Index of the current default attribute + u32 index; + + // Writing to these registers sets the "current" default attribute. + u32 set_value[3]; + } vs_default_attributes_setup; + + INSERT_PADDING_WORDS(0x28); enum class TriangleTopology : u32 { List = 0, @@ -669,7 +734,7 @@ struct Regs { BitField<56, 4, u64> attribute14_register; BitField<60, 4, u64> attribute15_register; - int GetRegisterForAttribute(int attribute_index) { + int GetRegisterForAttribute(int attribute_index) const { u64 fields[] = { attribute0_register, attribute1_register, attribute2_register, attribute3_register, attribute4_register, attribute5_register, attribute6_register, attribute7_register, @@ -766,8 +831,10 @@ struct Regs { ADD_FIELD(tev_stage1); ADD_FIELD(tev_stage2); ADD_FIELD(tev_stage3); + ADD_FIELD(tev_combiner_buffer_input); ADD_FIELD(tev_stage4); ADD_FIELD(tev_stage5); + ADD_FIELD(tev_combiner_buffer_color); ADD_FIELD(output_merger); ADD_FIELD(framebuffer); ADD_FIELD(vertex_attributes); @@ -775,6 +842,7 @@ struct Regs { ADD_FIELD(num_vertices); ADD_FIELD(trigger_draw); ADD_FIELD(trigger_draw_indexed); + ADD_FIELD(vs_default_attributes_setup); ADD_FIELD(triangle_topology); ADD_FIELD(vs_bool_uniforms); ADD_FIELD(vs_int_uniforms); @@ -840,8 +908,10 @@ ASSERT_REG_POSITION(tev_stage0, 0xc0); ASSERT_REG_POSITION(tev_stage1, 0xc8); ASSERT_REG_POSITION(tev_stage2, 0xd0); ASSERT_REG_POSITION(tev_stage3, 0xd8); +ASSERT_REG_POSITION(tev_combiner_buffer_input, 0xe0); ASSERT_REG_POSITION(tev_stage4, 0xf0); ASSERT_REG_POSITION(tev_stage5, 0xf8); +ASSERT_REG_POSITION(tev_combiner_buffer_color, 0xfd); ASSERT_REG_POSITION(output_merger, 0x100); ASSERT_REG_POSITION(framebuffer, 0x110); ASSERT_REG_POSITION(vertex_attributes, 0x200); @@ -849,6 +919,7 @@ ASSERT_REG_POSITION(index_array, 0x227); ASSERT_REG_POSITION(num_vertices, 0x228); ASSERT_REG_POSITION(trigger_draw, 0x22e); ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); +ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232); ASSERT_REG_POSITION(triangle_topology, 0x25e); ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1); @@ -978,15 +1049,4 @@ union CommandHeader { BitField<31, 1, u32> group_commands; }; -// TODO: Ugly, should fix PhysicalToVirtualAddress instead -inline static u32 PAddrToVAddr(u32 addr) { - if (addr >= Memory::VRAM_PADDR && addr < Memory::VRAM_PADDR + Memory::VRAM_SIZE) { - return addr - Memory::VRAM_PADDR + Memory::VRAM_VADDR; - } else if (addr >= Memory::FCRAM_PADDR && addr < Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - return addr - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; - } else { - return 0; - } -} - } // namespace diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index dd46f0ec3..59eff48f9 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -6,8 +6,11 @@ #include "common/common_types.h" #include "common/math_util.h" +#include "common/profiler.h" #include "core/hw/gpu.h" +#include "core/memory.h" + #include "debug_utils/debug_utils.h" #include "math.h" #include "color.h" @@ -30,7 +33,7 @@ static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) { const u32 coarse_y = y & ~7; u32 bytes_per_pixel = GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(registers.framebuffer.color_format.Value())); u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * registers.framebuffer.width * bytes_per_pixel; - u8* dst_pixel = Memory::GetPointer(PAddrToVAddr(addr)) + dst_offset; + u8* dst_pixel = Memory::GetPhysicalPointer(addr) + dst_offset; switch (registers.framebuffer.color_format) { case registers.framebuffer.RGBA8: @@ -67,7 +70,7 @@ static const Math::Vec4<u8> GetPixel(int x, int y) { const u32 coarse_y = y & ~7; u32 bytes_per_pixel = GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(registers.framebuffer.color_format.Value())); u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * registers.framebuffer.width * bytes_per_pixel; - u8* src_pixel = Memory::GetPointer(PAddrToVAddr(addr)) + src_offset; + u8* src_pixel = Memory::GetPhysicalPointer(addr) + src_offset; switch (registers.framebuffer.color_format) { case registers.framebuffer.RGBA8: @@ -90,12 +93,12 @@ static const Math::Vec4<u8> GetPixel(int x, int y) { UNIMPLEMENTED(); } - return {}; + return {0, 0, 0, 0}; } static u32 GetDepth(int x, int y) { const PAddr addr = registers.framebuffer.GetDepthBufferPhysicalAddress(); - u8* depth_buffer = Memory::GetPointer(PAddrToVAddr(addr)); + u8* depth_buffer = Memory::GetPhysicalPointer(addr); y = (registers.framebuffer.height - y); @@ -122,7 +125,7 @@ static u32 GetDepth(int x, int y) { static void SetDepth(int x, int y, u32 value) { const PAddr addr = registers.framebuffer.GetDepthBufferPhysicalAddress(); - u8* depth_buffer = Memory::GetPointer(PAddrToVAddr(addr)); + u8* depth_buffer = Memory::GetPhysicalPointer(addr); y = (registers.framebuffer.height - y); @@ -186,6 +189,8 @@ static int SignedArea (const Math::Vec2<Fix12P4>& vtx1, return Math::Cross(vec1, vec2).z; }; +static Common::Profiling::TimingCategory rasterization_category("Rasterization"); + /** * Helper function for ProcessTriangle with the "reversed" flag to allow for implementing * culling via recursion. @@ -195,6 +200,8 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, const VertexShader::OutputVertex& v2, bool reversed = false) { + Common::Profiling::ScopeTimer timer(rasterization_category); + // vertex positions in rasterizer coordinates static auto FloatToFix = [](float24 flt) { // TODO: Rounding here is necessary to prevent garbage pixels at @@ -342,10 +349,10 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, case Regs::TextureConfig::MirroredRepeat: { - int coord = (int)((unsigned)val % (2 * size)); + unsigned int coord = ((unsigned)val % (2 * size)); if (coord >= size) coord = 2 * size - 1 - coord; - return coord; + return (int)coord; } default: @@ -361,7 +368,7 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, s = GetWrappedTexCoord(texture.config.wrap_s, s, texture.config.width); t = texture.config.height - 1 - GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height); - u8* texture_data = Memory::GetPointer(PAddrToVAddr(texture.config.GetPhysicalAddress())); + u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); auto info = DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format); texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info); @@ -376,7 +383,13 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, // with some basic arithmetic. Alpha combiners can be configured separately but work // analogously. Math::Vec4<u8> combiner_output; - for (const auto& tev_stage : tev_stages) { + Math::Vec4<u8> combiner_buffer = { + registers.tev_combiner_buffer_color.r, registers.tev_combiner_buffer_color.g, + registers.tev_combiner_buffer_color.b, registers.tev_combiner_buffer_color.a + }; + + for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { + const auto& tev_stage = tev_stages[tev_stage_index]; using Source = Regs::TevStageConfig::Source; using ColorModifier = Regs::TevStageConfig::ColorModifier; using AlphaModifier = Regs::TevStageConfig::AlphaModifier; @@ -398,6 +411,9 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, case Source::Texture2: return texture_color[2]; + case Source::PreviousBuffer: + return combiner_buffer; + case Source::Constant: return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b, tev_stage.const_a}; @@ -407,7 +423,7 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, default: LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source); UNIMPLEMENTED(); - return {}; + return {0, 0, 0, 0}; } }; @@ -490,6 +506,16 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, return result.Cast<u8>(); } + case Operation::AddSigned: + { + // TODO(bunnei): Verify that the color conversion from (float) 0.5f to (byte) 128 is correct + auto result = input[0].Cast<int>() + input[1].Cast<int>() - Math::MakeVec<int>(128, 128, 128); + result.r() = MathUtil::Clamp<int>(result.r(), 0, 255); + result.g() = MathUtil::Clamp<int>(result.g(), 0, 255); + result.b() = MathUtil::Clamp<int>(result.b(), 0, 255); + return result.Cast<u8>(); + } + case Operation::Lerp: return ((input[0] * input[2] + input[1] * (Math::MakeVec<u8>(255, 255, 255) - input[2]).Cast<u8>()) / 255).Cast<u8>(); @@ -524,7 +550,7 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, default: LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); UNIMPLEMENTED(); - return {}; + return {0, 0, 0}; } }; @@ -578,7 +604,20 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, }; auto alpha_output = AlphaCombine(tev_stage.alpha_op, alpha_result); - combiner_output = Math::MakeVec(color_output, alpha_output); + combiner_output[0] = std::min((unsigned)255, color_output.r() * tev_stage.GetColorMultiplier()); + combiner_output[1] = std::min((unsigned)255, color_output.g() * tev_stage.GetColorMultiplier()); + combiner_output[2] = std::min((unsigned)255, color_output.b() * tev_stage.GetColorMultiplier()); + combiner_output[3] = std::min((unsigned)255, alpha_output * tev_stage.GetAlphaMultiplier()); + + if (registers.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index)) { + combiner_buffer.r() = combiner_output.r(); + combiner_buffer.g() = combiner_output.g(); + combiner_buffer.b() = combiner_output.b(); + } + + if (registers.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)) { + combiner_buffer.a() = combiner_output.a(); + } } if (registers.output_merger.alpha_test.enable) { @@ -624,9 +663,10 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, // TODO: Does depth indeed only get written even if depth testing is enabled? if (registers.output_merger.depth_test_enable) { - u16 z = (u16)((v0.screenpos[2].ToFloat32() * w0 + - v1.screenpos[2].ToFloat32() * w1 + - v2.screenpos[2].ToFloat32() * w2) * 65535.f / wsum); + unsigned num_bits = Pica::Regs::DepthBitsPerPixel(registers.framebuffer.depth_format); + u32 z = (u32)((v0.screenpos[2].ToFloat32() * w0 + + v1.screenpos[2].ToFloat32() * w1 + + v2.screenpos[2].ToFloat32() * w2) * ((1 << num_bits) - 1) / wsum); u32 ref_z = GetDepth(x >> 4, y >> 4); bool pass = false; diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index b77f29c11..b62409538 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -4,7 +4,7 @@ #pragma once -#include "common/common.h" +#include "common/common_types.h" class RendererBase : NonCopyable { public: diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 4273a177f..71ceb021b 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -5,9 +5,11 @@ #include "core/hw/gpu.h" #include "core/hw/hw.h" #include "core/hw/lcd.h" -#include "core/mem_map.h" +#include "core/memory.h" +#include "core/settings.h" #include "common/emu_window.h" +#include "common/logging/log.h" #include "common/profiler_reporting.h" #include "video_core/video_core.h" @@ -117,15 +119,15 @@ void RendererOpenGL::SwapBuffers() { void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, const TextureInfo& texture) { - const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress( - framebuffer.active_fb == 0 ? framebuffer.address_left1 : framebuffer.address_left2); + const PAddr framebuffer_addr = framebuffer.active_fb == 0 ? + framebuffer.address_left1 : framebuffer.address_left2; LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x", framebuffer.stride * framebuffer.height, - framebuffer_vaddr, (int)framebuffer.width, + framebuffer_addr, (int)framebuffer.width, (int)framebuffer.height, (int)framebuffer.format); - const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr); + const u8* framebuffer_data = Memory::GetPhysicalPointer(framebuffer_addr); int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format); size_t pixel_stride = framebuffer.stride / bpp; @@ -172,7 +174,7 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color * Initializes the OpenGL state and creates persistent objects. */ void RendererOpenGL::InitOpenGLObjects() { - glClearColor(1.0f, 1.0f, 1.0f, 0.0f); + glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); glDisable(GL_DEPTH_TEST); // Link shaders and get variable locations diff --git a/src/video_core/utils.h b/src/video_core/utils.h index bda793fa5..ffb3e73a3 100644 --- a/src/video_core/utils.h +++ b/src/video_core/utils.h @@ -13,10 +13,10 @@ namespace VideoCore { /// Structure for the TGA texture format (for dumping) struct TGAHeader { char idlength; - char colourmaptype; + char colormaptype; char datatypecode; - short int colourmaporigin; - short int colourmaplength; + short int colormaporigin; + short int colormaplength; short int x_origin; short int y_origin; short width; diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp index e8d865172..981d1a356 100644 --- a/src/video_core/vertex_shader.cpp +++ b/src/video_core/vertex_shader.cpp @@ -8,10 +8,9 @@ #include <common/file_util.h> -#include <core/mem_map.h> - #include <nihstro/shader_bytecode.h> +#include "common/profiler.h" #include "pica.h" #include "vertex_shader.h" @@ -35,6 +34,8 @@ static struct { std::array<Math::Vec4<u8>,4> i; } shader_uniforms; +static Math::Vec4<float24> vs_default_attributes[16]; + // TODO: Not sure where the shader binary and swizzle patterns are supposed to be loaded to! // For now, we just keep these local arrays around. static std::array<u32, 1024> shader_memory; @@ -60,6 +61,10 @@ Math::Vec4<u8>& GetIntUniform(u32 index) { return shader_uniforms.i[index]; } +Math::Vec4<float24>& GetDefaultAttribute(u32 index) { + return vs_default_attributes[index]; +} + const std::array<u32, 1024>& GetShaderBinary() { return shader_memory; } @@ -229,6 +234,15 @@ static void ProcessShaderCode(VertexShaderState& state) { break; } + case OpCode::Id::FLR: + for (int i = 0; i < 4; ++i) { + if (!swizzle.DestComponentEnabled(i)) + continue; + + dest[i] = float24::FromFloat32(std::floor(src1[i].ToFloat32())); + } + break; + case OpCode::Id::MAX: for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) @@ -360,12 +374,15 @@ static void ProcessShaderCode(VertexShaderState& state) { case OpCode::Type::MultiplyAdd: { - if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD) { + if ((instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD) || + (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI)) { const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.mad.operand_desc_id]; - const float24* src1_ = LookupSourceRegister(instr.mad.src1); - const float24* src2_ = LookupSourceRegister(instr.mad.src2); - const float24* src3_ = LookupSourceRegister(instr.mad.src3); + bool is_inverted = (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI); + + const float24* src1_ = LookupSourceRegister(instr.mad.GetSrc1(is_inverted)); + const float24* src2_ = LookupSourceRegister(instr.mad.GetSrc2(is_inverted)); + const float24* src3_ = LookupSourceRegister(instr.mad.GetSrc3(is_inverted)); const bool negate_src1 = ((bool)swizzle.negate_src1 != false); const bool negate_src2 = ((bool)swizzle.negate_src2 != false); @@ -556,7 +573,11 @@ static void ProcessShaderCode(VertexShaderState& state) { } } +static Common::Profiling::TimingCategory shader_category("Vertex Shader"); + OutputVertex RunShader(const InputVertex& input, int num_attributes) { + Common::Profiling::ScopeTimer timer(shader_category); + VertexShaderState state; const u32* main = &shader_memory[registers.vs_main_offset]; @@ -568,22 +589,23 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) { const auto& attribute_register_map = registers.vs_input_register_map; float24 dummy_register; boost::fill(state.input_register_table, &dummy_register); - if(num_attributes > 0) state.input_register_table[attribute_register_map.attribute0_register] = &input.attr[0].x; - if(num_attributes > 1) state.input_register_table[attribute_register_map.attribute1_register] = &input.attr[1].x; - if(num_attributes > 2) state.input_register_table[attribute_register_map.attribute2_register] = &input.attr[2].x; - if(num_attributes > 3) state.input_register_table[attribute_register_map.attribute3_register] = &input.attr[3].x; - if(num_attributes > 4) state.input_register_table[attribute_register_map.attribute4_register] = &input.attr[4].x; - if(num_attributes > 5) state.input_register_table[attribute_register_map.attribute5_register] = &input.attr[5].x; - if(num_attributes > 6) state.input_register_table[attribute_register_map.attribute6_register] = &input.attr[6].x; - if(num_attributes > 7) state.input_register_table[attribute_register_map.attribute7_register] = &input.attr[7].x; - if(num_attributes > 8) state.input_register_table[attribute_register_map.attribute8_register] = &input.attr[8].x; - if(num_attributes > 9) state.input_register_table[attribute_register_map.attribute9_register] = &input.attr[9].x; - if(num_attributes > 10) state.input_register_table[attribute_register_map.attribute10_register] = &input.attr[10].x; - if(num_attributes > 11) state.input_register_table[attribute_register_map.attribute11_register] = &input.attr[11].x; - if(num_attributes > 12) state.input_register_table[attribute_register_map.attribute12_register] = &input.attr[12].x; - if(num_attributes > 13) state.input_register_table[attribute_register_map.attribute13_register] = &input.attr[13].x; - if(num_attributes > 14) state.input_register_table[attribute_register_map.attribute14_register] = &input.attr[14].x; - if(num_attributes > 15) state.input_register_table[attribute_register_map.attribute15_register] = &input.attr[15].x; + + if (num_attributes > 0) state.input_register_table[attribute_register_map.attribute0_register] = &input.attr[0].x; + if (num_attributes > 1) state.input_register_table[attribute_register_map.attribute1_register] = &input.attr[1].x; + if (num_attributes > 2) state.input_register_table[attribute_register_map.attribute2_register] = &input.attr[2].x; + if (num_attributes > 3) state.input_register_table[attribute_register_map.attribute3_register] = &input.attr[3].x; + if (num_attributes > 4) state.input_register_table[attribute_register_map.attribute4_register] = &input.attr[4].x; + if (num_attributes > 5) state.input_register_table[attribute_register_map.attribute5_register] = &input.attr[5].x; + if (num_attributes > 6) state.input_register_table[attribute_register_map.attribute6_register] = &input.attr[6].x; + if (num_attributes > 7) state.input_register_table[attribute_register_map.attribute7_register] = &input.attr[7].x; + if (num_attributes > 8) state.input_register_table[attribute_register_map.attribute8_register] = &input.attr[8].x; + if (num_attributes > 9) state.input_register_table[attribute_register_map.attribute9_register] = &input.attr[9].x; + if (num_attributes > 10) state.input_register_table[attribute_register_map.attribute10_register] = &input.attr[10].x; + if (num_attributes > 11) state.input_register_table[attribute_register_map.attribute11_register] = &input.attr[11].x; + if (num_attributes > 12) state.input_register_table[attribute_register_map.attribute12_register] = &input.attr[12].x; + if (num_attributes > 13) state.input_register_table[attribute_register_map.attribute13_register] = &input.attr[13].x; + if (num_attributes > 14) state.input_register_table[attribute_register_map.attribute14_register] = &input.attr[14].x; + if (num_attributes > 15) state.input_register_table[attribute_register_map.attribute15_register] = &input.attr[15].x; state.conditional_code[0] = false; state.conditional_code[1] = false; diff --git a/src/video_core/vertex_shader.h b/src/video_core/vertex_shader.h index 3a68a3409..c26709bbc 100644 --- a/src/video_core/vertex_shader.h +++ b/src/video_core/vertex_shader.h @@ -74,6 +74,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes); Math::Vec4<float24>& GetFloatUniform(u32 index); bool& GetBoolUniform(u32 index); Math::Vec4<u8>& GetIntUniform(u32 index); +Math::Vec4<float24>& GetDefaultAttribute(u32 index); const std::array<u32, 1024>& GetShaderBinary(); const std::array<u32, 1024>& GetSwizzlePatterns(); diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index b9d4ede3a..42e3bdd5b 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common.h" +#include "common/logging/log.h" #include "common/emu_window.h" #include "core/core.h" diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index 1b51d39bf..f885bec21 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -4,7 +4,6 @@ #pragma once -#include "common/common.h" #include "common/emu_window.h" #include "renderer_base.h" |