mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Fix merge conflicts
This commit is contained in:
		
						commit
						8ba9ac0f74
					
				
					 288 changed files with 17413 additions and 13969 deletions
				
			
		|  | @ -12,25 +12,23 @@ set(HEADERS | |||
| 
 | ||||
| create_directory_groups(${SRCS} ${HEADERS}) | ||||
| 
 | ||||
| # NOTE: This is a workaround for CMake bug 0006976 (missing X11_xf86vmode_LIB variable) | ||||
| if (NOT X11_xf86vmode_LIB) | ||||
|     set(X11_xv86vmode_LIB Xxf86vm) | ||||
| endif() | ||||
| 
 | ||||
| add_executable(citra ${SRCS} ${HEADERS}) | ||||
| target_link_libraries(citra core common video_core) | ||||
| target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih) | ||||
| 
 | ||||
| if (UNIX) | ||||
|     target_link_libraries(citra -pthread) | ||||
| endif() | ||||
| 
 | ||||
| if (APPLE) | ||||
|     target_link_libraries(citra iconv pthread ${COREFOUNDATION_LIBRARY}) | ||||
|     target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY}) | ||||
| elseif (WIN32) | ||||
|     target_link_libraries(citra winmm) | ||||
|     if (MINGW) | ||||
|         target_link_libraries(citra iconv) | ||||
|     endif()     | ||||
| else() # Unix | ||||
|     target_link_libraries(citra pthread rt) | ||||
|     target_link_libraries(citra ${X11_X11_LIB} ${X11_Xi_LIB} ${X11_Xcursor_LIB} ${X11_Xrandr_LIB} ${X11_xv86vmode_LIB}) | ||||
|     target_link_libraries(citra rt) | ||||
| endif() | ||||
| 
 | ||||
| #install(TARGETS citra RUNTIME DESTINATION ${bindir}) | ||||
|  |  | |||
|  | @ -1,9 +1,14 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <thread> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/log_manager.h" | ||||
| #include "common/logging/text_formatter.h" | ||||
| #include "common/logging/backend.h" | ||||
| #include "common/logging/filter.h" | ||||
| #include "common/scope_exit.h" | ||||
| 
 | ||||
| #include "core/settings.h" | ||||
| #include "core/system.h" | ||||
|  | @ -15,17 +20,21 @@ | |||
| 
 | ||||
| /// Application entry point
 | ||||
| int __cdecl main(int argc, char **argv) { | ||||
|     LogManager::Init(); | ||||
|     std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger(); | ||||
|     Log::Filter log_filter(Log::Level::Debug); | ||||
|     std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter); | ||||
|     SCOPE_EXIT({ | ||||
|         logger->Close(); | ||||
|         logging_thread.join(); | ||||
|     }); | ||||
| 
 | ||||
|     if (argc < 2) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); | ||||
|         LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     Config config; | ||||
| 
 | ||||
|     if (!Settings::values.enable_log) | ||||
|         LogManager::Shutdown(); | ||||
|     log_filter.ParseFilterString(Settings::values.log_filter); | ||||
| 
 | ||||
|     std::string boot_filename = argv[1]; | ||||
|     EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; | ||||
|  | @ -34,7 +43,7 @@ int __cdecl main(int argc, char **argv) { | |||
| 
 | ||||
|     Loader::ResultStatus load_result = Loader::LoadFile(boot_filename); | ||||
|     if (Loader::ResultStatus::Success != load_result) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM (Error %i)!", load_result); | ||||
|         LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <GLFW/glfw3.h> | ||||
|  | @ -22,21 +22,22 @@ Config::Config() { | |||
| bool Config::LoadINI(INIReader* config, const char* location, const std::string& default_contents, bool retry) { | ||||
|     if (config->ParseError() < 0) { | ||||
|         if (retry) { | ||||
|             ERROR_LOG(CONFIG, "Failed to load %s. Creating file from defaults...", location); | ||||
|             LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location); | ||||
|             FileUtil::CreateFullPath(location); | ||||
|             FileUtil::WriteStringToFile(true, default_contents, location); | ||||
|             *config = INIReader(location); // Reopen file
 | ||||
| 
 | ||||
|             return LoadINI(config, location, default_contents, false); | ||||
|         } | ||||
|         ERROR_LOG(CONFIG, "Failed."); | ||||
|         LOG_ERROR(Config, "Failed."); | ||||
|         return false; | ||||
|     } | ||||
|     INFO_LOG(CONFIG, "Successfully loaded %s", location); | ||||
|     LOG_INFO(Config, "Successfully loaded %s", location); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void Config::ReadControls() { | ||||
| void Config::ReadValues() { | ||||
|     // Controls
 | ||||
|     Settings::values.pad_a_key = glfw_config->GetInteger("Controls", "pad_a", GLFW_KEY_A); | ||||
|     Settings::values.pad_b_key = glfw_config->GetInteger("Controls", "pad_b", GLFW_KEY_S); | ||||
|     Settings::values.pad_x_key = glfw_config->GetInteger("Controls", "pad_x", GLFW_KEY_Z); | ||||
|  | @ -54,27 +55,22 @@ void Config::ReadControls() { | |||
|     Settings::values.pad_sdown_key  = glfw_config->GetInteger("Controls", "pad_sdown",  GLFW_KEY_DOWN); | ||||
|     Settings::values.pad_sleft_key  = glfw_config->GetInteger("Controls", "pad_sleft",  GLFW_KEY_LEFT); | ||||
|     Settings::values.pad_sright_key = glfw_config->GetInteger("Controls", "pad_sright", GLFW_KEY_RIGHT); | ||||
| } | ||||
| 
 | ||||
| void Config::ReadCore() { | ||||
|     // Core
 | ||||
|     Settings::values.cpu_core = glfw_config->GetInteger("Core", "cpu_core", Core::CPU_Interpreter); | ||||
|     Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 60); | ||||
| } | ||||
|     Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 30); | ||||
|     Settings::values.frame_skip = glfw_config->GetInteger("Core", "frame_skip", 0); | ||||
| 
 | ||||
| void Config::ReadData() { | ||||
|     // Data Storage
 | ||||
|     Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true); | ||||
| } | ||||
| 
 | ||||
| void Config::ReadMiscellaneous() { | ||||
|     Settings::values.enable_log = glfw_config->GetBoolean("Miscellaneous", "enable_log", true); | ||||
|     // Miscellaneous
 | ||||
|     Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info"); | ||||
| } | ||||
| 
 | ||||
| void Config::Reload() { | ||||
|     LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file); | ||||
|     ReadControls(); | ||||
|     ReadCore(); | ||||
|     ReadData(); | ||||
|     ReadMiscellaneous(); | ||||
|     ReadValues(); | ||||
| } | ||||
| 
 | ||||
| Config::~Config() { | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -15,10 +15,7 @@ class Config { | |||
|     std::string glfw_config_loc; | ||||
| 
 | ||||
|     bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true); | ||||
|     void ReadControls(); | ||||
|     void ReadCore(); | ||||
|     void ReadData(); | ||||
|     void ReadMiscellaneous(); | ||||
|     void ReadValues(); | ||||
| public: | ||||
|     Config(); | ||||
|     ~Config(); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -28,13 +28,14 @@ pad_sright = | |||
| 
 | ||||
| [Core] | ||||
| cpu_core = ## 0: Interpreter (default), 1: FastInterpreter (experimental) | ||||
| gpu_refresh_rate = ## 60 (default) | ||||
| gpu_refresh_rate = ## 30 (default) | ||||
| frame_skip = ## 0: No frameskip (default), 1 : 2x frameskip, 2 : 4x frameskip, etc. | ||||
| 
 | ||||
| [Data Storage] | ||||
| use_virtual_sd = | ||||
| 
 | ||||
| [Miscellaneous] | ||||
| enable_log = | ||||
| log_filter = *:Info  ## Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | ||||
| )"; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <GLFW/glfw3.h> | ||||
|  | @ -36,15 +36,15 @@ const bool EmuWindow_GLFW::IsOpen() { | |||
| } | ||||
| 
 | ||||
| void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) { | ||||
|     _dbg_assert_(GUI, width > 0); | ||||
|     _dbg_assert_(GUI, height > 0); | ||||
|     _dbg_assert_(Frontend, width > 0); | ||||
|     _dbg_assert_(Frontend, height > 0); | ||||
| 
 | ||||
|     GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair<unsigned,unsigned>(width, height)); | ||||
| } | ||||
| 
 | ||||
| void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) { | ||||
|     _dbg_assert_(GUI, width > 0); | ||||
|     _dbg_assert_(GUI, height > 0); | ||||
|     _dbg_assert_(Frontend, width > 0); | ||||
|     _dbg_assert_(Frontend, height > 0); | ||||
| 
 | ||||
|     // NOTE: GLFW provides no proper way to set a minimal window size.
 | ||||
|     //       Hence, we just ignore the corresponding EmuWindow hint.
 | ||||
|  | @ -58,9 +58,13 @@ EmuWindow_GLFW::EmuWindow_GLFW() { | |||
| 
 | ||||
|     ReloadSetKeymaps(); | ||||
| 
 | ||||
|     glfwSetErrorCallback([](int error, const char *desc){ | ||||
|         LOG_ERROR(Frontend, "GLFW 0x%08x: %s", error, desc); | ||||
|     }); | ||||
| 
 | ||||
|     // Initialize the window
 | ||||
|     if(glfwInit() != GL_TRUE) { | ||||
|         printf("Failed to initialize GLFW! Exiting..."); | ||||
|         LOG_CRITICAL(Frontend, "Failed to initialize GLFW! Exiting..."); | ||||
|         exit(1); | ||||
|     } | ||||
|     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | ||||
|  | @ -72,10 +76,10 @@ EmuWindow_GLFW::EmuWindow_GLFW() { | |||
|     std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | ||||
|     m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, | ||||
|         (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), | ||||
|         window_title.c_str(), NULL, NULL); | ||||
|         window_title.c_str(), nullptr, nullptr); | ||||
| 
 | ||||
|     if (m_render_window == NULL) { | ||||
|         printf("Failed to create GLFW window! Exiting..."); | ||||
|     if (m_render_window == nullptr) { | ||||
|         LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting..."); | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|  | @ -119,7 +123,7 @@ void EmuWindow_GLFW::MakeCurrent() { | |||
| 
 | ||||
| /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
 | ||||
| void EmuWindow_GLFW::DoneCurrent() { | ||||
|     glfwMakeContextCurrent(NULL); | ||||
|     glfwMakeContextCurrent(nullptr); | ||||
| } | ||||
| 
 | ||||
| void EmuWindow_GLFW::ReloadSetKeymaps() { | ||||
|  | @ -145,7 +149,7 @@ void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,u | |||
|     std::pair<int,int> current_size; | ||||
|     glfwGetWindowSize(m_render_window, ¤t_size.first, ¤t_size.second); | ||||
| 
 | ||||
|     _dbg_assert_(GUI, (int)minimal_size.first > 0 && (int)minimal_size.second > 0); | ||||
|     _dbg_assert_(Frontend, (int)minimal_size.first > 0 && (int)minimal_size.second > 0); | ||||
|     int new_width  = std::max(current_size.first,  (int)minimal_size.first); | ||||
|     int new_height = std::max(current_size.second, (int)minimal_size.second); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -8,9 +8,12 @@ set(SRCS | |||
|             debugger/callstack.cpp | ||||
|             debugger/disassembler.cpp | ||||
|             debugger/graphics.cpp | ||||
|             debugger/graphics_breakpoints.cpp | ||||
|             debugger/graphics_cmdlists.cpp | ||||
|             debugger/graphics_framebuffer.cpp | ||||
|             debugger/ramview.cpp | ||||
|             debugger/registers.cpp | ||||
|             util/spinbox.cpp | ||||
|             bootmanager.cpp | ||||
|             hotkeys.cpp | ||||
|             main.cpp | ||||
|  | @ -23,9 +26,13 @@ set(HEADERS | |||
|             debugger/callstack.hxx | ||||
|             debugger/disassembler.hxx | ||||
|             debugger/graphics.hxx | ||||
|             debugger/graphics_breakpoints.hxx | ||||
|             debugger/graphics_breakpoints_p.hxx | ||||
|             debugger/graphics_cmdlists.hxx | ||||
|             debugger/graphics_framebuffer.hxx | ||||
|             debugger/ramview.hxx | ||||
|             debugger/registers.hxx | ||||
|             util/spinbox.hxx | ||||
|             bootmanager.hxx | ||||
|             hotkeys.hxx | ||||
|             main.hxx | ||||
|  | @ -53,6 +60,10 @@ add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) | |||
| target_link_libraries(citra-qt core common video_core qhexedit) | ||||
| target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) | ||||
| 
 | ||||
| if (UNIX) | ||||
|     target_link_libraries(citra-qt -pthread) | ||||
| endif() | ||||
| 
 | ||||
| if (APPLE) | ||||
|     target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY}) | ||||
| elseif (WIN32) | ||||
|  |  | |||
|  | @ -14,6 +14,8 @@ | |||
| #include "core/core.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
| 
 | ||||
| #include "video_core/video_core.h" | ||||
| 
 | ||||
| #include "citra_qt/version.h" | ||||
|  | @ -60,26 +62,33 @@ void EmuThread::Stop() | |||
| { | ||||
|     if (!isRunning()) | ||||
|     { | ||||
|         INFO_LOG(MASTER_LOG, "EmuThread::Stop called while emu thread wasn't running, returning..."); | ||||
|         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;
 | ||||
| 
 | ||||
|     wait(500); | ||||
|     // TODO: Waiting here is just a bad workaround for retarded shutdown logic.
 | ||||
|     wait(1000); | ||||
|     if (isRunning()) | ||||
|     { | ||||
|         WARN_LOG(MASTER_LOG, "EmuThread still running, terminating..."); | ||||
|         LOG_WARNING(Frontend, "EmuThread still running, terminating..."); | ||||
|         quit(); | ||||
|         wait(1000); | ||||
| 
 | ||||
|         // 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()) | ||||
|         { | ||||
|             WARN_LOG(MASTER_LOG, "EmuThread STILL running, something is wrong here..."); | ||||
|             LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here..."); | ||||
|             terminate(); | ||||
|         } | ||||
|     } | ||||
|     INFO_LOG(MASTER_LOG, "EmuThread stopped"); | ||||
|     LOG_INFO(Frontend, "EmuThread stopped"); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -230,7 +239,7 @@ QByteArray GRenderWindow::saveGeometry() | |||
| { | ||||
|     // If we are a top-level widget, store the current geometry
 | ||||
|     // otherwise, store the last backup
 | ||||
|     if (parent() == NULL) | ||||
|     if (parent() == nullptr) | ||||
|         return ((QGLWidget*)this)->saveGeometry(); | ||||
|     else | ||||
|         return geometry; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QString> | ||||
|  | @ -21,7 +21,7 @@ Config::Config() { | |||
|     Reload(); | ||||
| } | ||||
| 
 | ||||
| void Config::ReadControls() { | ||||
| void Config::ReadValues() { | ||||
|     qt_config->beginGroup("Controls"); | ||||
|     Settings::values.pad_a_key = qt_config->value("pad_a", Qt::Key_A).toInt(); | ||||
|     Settings::values.pad_b_key = qt_config->value("pad_b", Qt::Key_S).toInt(); | ||||
|  | @ -41,9 +41,23 @@ void Config::ReadControls() { | |||
|     Settings::values.pad_sleft_key  = qt_config->value("pad_sleft",  Qt::Key_Left).toInt(); | ||||
|     Settings::values.pad_sright_key = qt_config->value("pad_sright", Qt::Key_Right).toInt(); | ||||
|     qt_config->endGroup(); | ||||
| 
 | ||||
|     qt_config->beginGroup("Core"); | ||||
|     Settings::values.cpu_core = qt_config->value("cpu_core", Core::CPU_Interpreter).toInt(); | ||||
|     Settings::values.gpu_refresh_rate = qt_config->value("gpu_refresh_rate", 30).toInt(); | ||||
|     Settings::values.frame_skip = qt_config->value("frame_skip", 0).toInt(); | ||||
|     qt_config->endGroup(); | ||||
| 
 | ||||
|     qt_config->beginGroup("Data Storage"); | ||||
|     Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); | ||||
|     qt_config->endGroup(); | ||||
| 
 | ||||
|     qt_config->beginGroup("Miscellaneous"); | ||||
|     Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::SaveControls() { | ||||
| void Config::SaveValues() { | ||||
|     qt_config->beginGroup("Controls"); | ||||
|     qt_config->setValue("pad_a", Settings::values.pad_a_key); | ||||
|     qt_config->setValue("pad_b", Settings::values.pad_b_key); | ||||
|  | @ -63,58 +77,28 @@ void Config::SaveControls() { | |||
|     qt_config->setValue("pad_sleft",  Settings::values.pad_sleft_key); | ||||
|     qt_config->setValue("pad_sright", Settings::values.pad_sright_key); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::ReadCore() { | ||||
|     qt_config->beginGroup("Core"); | ||||
|     Settings::values.cpu_core = qt_config->value("cpu_core", Core::CPU_Interpreter).toInt(); | ||||
|     Settings::values.gpu_refresh_rate = qt_config->value("gpu_refresh_rate", 60).toInt(); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::SaveCore() { | ||||
|     qt_config->beginGroup("Core"); | ||||
|     qt_config->setValue("cpu_core", Settings::values.cpu_core); | ||||
|     qt_config->setValue("gpu_refresh_rate", Settings::values.gpu_refresh_rate); | ||||
|     qt_config->setValue("frame_skip", Settings::values.frame_skip); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::ReadData() { | ||||
|     qt_config->beginGroup("Data Storage"); | ||||
|     Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::SaveData() { | ||||
|     qt_config->beginGroup("Data Storage"); | ||||
|     qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::ReadMiscellaneous() { | ||||
|     qt_config->beginGroup("Miscellaneous"); | ||||
|     Settings::values.enable_log = qt_config->value("enable_log", true).toBool(); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::SaveMiscellaneous() { | ||||
|     qt_config->beginGroup("Miscellaneous"); | ||||
|     qt_config->setValue("enable_log", Settings::values.enable_log); | ||||
|     qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::Reload() { | ||||
|     ReadControls(); | ||||
|     ReadCore(); | ||||
|     ReadData(); | ||||
|     ReadMiscellaneous(); | ||||
|     ReadValues(); | ||||
| } | ||||
| 
 | ||||
| void Config::Save() { | ||||
|     SaveControls(); | ||||
|     SaveCore(); | ||||
|     SaveData(); | ||||
|     SaveMiscellaneous(); | ||||
|     SaveValues(); | ||||
| } | ||||
| 
 | ||||
| Config::~Config() { | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -12,15 +12,8 @@ class Config { | |||
|     QSettings* qt_config; | ||||
|     std::string qt_config_loc; | ||||
| 
 | ||||
|     void ReadControls(); | ||||
|     void SaveControls(); | ||||
|     void ReadCore(); | ||||
|     void SaveCore(); | ||||
|     void ReadData(); | ||||
|     void SaveData(); | ||||
| 
 | ||||
|     void ReadMiscellaneous(); | ||||
|     void SaveMiscellaneous(); | ||||
|     void ReadValues(); | ||||
|     void SaveValues(); | ||||
| public: | ||||
|     Config(); | ||||
|     ~Config(); | ||||
|  |  | |||
|  | @ -27,10 +27,10 @@ void CallstackWidget::OnCPUStepped() | |||
|     ARM_Interface* app_core = Core::g_app_core; | ||||
| 
 | ||||
|     u32 sp = app_core->GetReg(13); //stack pointer
 | ||||
|     u32 addr, ret_addr, call_addr, func_addr; | ||||
|     u32 ret_addr, call_addr, func_addr; | ||||
| 
 | ||||
|     int counter = 0; | ||||
|     for (int addr = 0x10000000; addr >= sp; addr -= 4) | ||||
|     for (u32 addr = 0x10000000; addr >= sp; addr -= 4) | ||||
|     { | ||||
|         ret_addr = Memory::Read32(addr); | ||||
|         call_addr = ret_addr - 4; //get call address???
 | ||||
|  |  | |||
|  | @ -220,7 +220,9 @@ void DisassemblerWidget::OnPause() | |||
|     emu_thread.SetCpuRunning(false); | ||||
| 
 | ||||
|     // TODO: By now, the CPU might not have actually stopped...
 | ||||
|     model->SetNextInstruction(Core::g_app_core->GetPC()); | ||||
|     if (Core::g_app_core) { | ||||
|         model->SetNextInstruction(Core::g_app_core->GetPC()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void DisassemblerWidget::OnToggleStartStop() | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "graphics.hxx" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
							
								
								
									
										263
									
								
								src/citra_qt/debugger/graphics_breakpoints.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/citra_qt/debugger/graphics_breakpoints.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,263 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QMetaType> | ||||
| #include <QPushButton> | ||||
| #include <QTreeWidget> | ||||
| #include <QVBoxLayout> | ||||
| #include <QLabel> | ||||
| 
 | ||||
| #include "graphics_breakpoints.hxx" | ||||
| #include "graphics_breakpoints_p.hxx" | ||||
| 
 | ||||
| BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) | ||||
|     : QAbstractListModel(parent), context_weak(debug_context), | ||||
|       at_breakpoint(debug_context->at_breakpoint), | ||||
|       active_breakpoint(debug_context->active_breakpoint) | ||||
| { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| int BreakPointModel::columnCount(const QModelIndex& parent) const | ||||
| { | ||||
|     return 2; | ||||
| } | ||||
| 
 | ||||
| int BreakPointModel::rowCount(const QModelIndex& parent) const | ||||
| { | ||||
|     return static_cast<int>(Pica::DebugContext::Event::NumEvents); | ||||
| } | ||||
| 
 | ||||
| QVariant BreakPointModel::data(const QModelIndex& index, int role) const | ||||
| { | ||||
|     const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||||
| 
 | ||||
|     switch (role) { | ||||
|     case Qt::DisplayRole: | ||||
|     { | ||||
|         switch (index.column()) { | ||||
|         case 0: | ||||
|         { | ||||
|             static const std::map<Pica::DebugContext::Event, QString> map = { | ||||
|                 { Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded") }, | ||||
|                 { Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed") }, | ||||
|                 { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, | ||||
|                 { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, | ||||
|                 { Pica::DebugContext::Event::VertexLoaded, tr("Vertex Loaded") } | ||||
|             }; | ||||
| 
 | ||||
|             _dbg_assert_(Debug_GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); | ||||
| 
 | ||||
|             return (map.find(event) != map.end()) ? map.at(event) : QString(); | ||||
|         } | ||||
| 
 | ||||
|         case 1: | ||||
|             return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled"); | ||||
| 
 | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case Qt::BackgroundRole: | ||||
|     { | ||||
|         if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { | ||||
|             return QBrush(QColor(0xE0, 0xE0, 0x10)); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case Role_IsEnabled: | ||||
|     { | ||||
|         auto context = context_weak.lock(); | ||||
|         return context && context->breakpoints[event].enabled; | ||||
|     } | ||||
| 
 | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|     return QVariant(); | ||||
| } | ||||
| 
 | ||||
| QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const | ||||
| { | ||||
|     switch(role) { | ||||
|     case Qt::DisplayRole: | ||||
|     { | ||||
|         if (section == 0) { | ||||
|             return tr("Event"); | ||||
|         } else if (section == 1) { | ||||
|             return tr("Status"); | ||||
|         } | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
|     } | ||||
| 
 | ||||
|     return QVariant(); | ||||
| } | ||||
| 
 | ||||
| bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) | ||||
| { | ||||
|     const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||||
| 
 | ||||
|     switch (role) { | ||||
|     case Role_IsEnabled: | ||||
|     { | ||||
|         auto context = context_weak.lock(); | ||||
|         if (!context) | ||||
|             return false; | ||||
| 
 | ||||
|         context->breakpoints[event].enabled = value.toBool(); | ||||
|         QModelIndex changed_index = createIndex(index.row(), 1); | ||||
|         emit dataChanged(changed_index, changed_index); | ||||
|         return true; | ||||
|     } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) | ||||
| { | ||||
|     auto context = context_weak.lock(); | ||||
|     if (!context) | ||||
|         return; | ||||
| 
 | ||||
|     active_breakpoint = context->active_breakpoint; | ||||
|     at_breakpoint = context->at_breakpoint; | ||||
|     emit dataChanged(createIndex(static_cast<int>(event), 0), | ||||
|                      createIndex(static_cast<int>(event), 1)); | ||||
| } | ||||
| 
 | ||||
| void BreakPointModel::OnResumed() | ||||
| { | ||||
|     auto context = context_weak.lock(); | ||||
|     if (!context) | ||||
|         return; | ||||
| 
 | ||||
|     at_breakpoint = context->at_breakpoint; | ||||
|     emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), | ||||
|                      createIndex(static_cast<int>(active_breakpoint), 1)); | ||||
|     active_breakpoint = context->active_breakpoint; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                                                      QWidget* parent) | ||||
|     : QDockWidget(tr("Pica Breakpoints"), parent), | ||||
|       Pica::DebugContext::BreakPointObserver(debug_context) | ||||
| { | ||||
|     setObjectName("PicaBreakPointsWidget"); | ||||
| 
 | ||||
|     status_text = new QLabel(tr("Emulation running")); | ||||
|     resume_button = new QPushButton(tr("Resume")); | ||||
|     resume_button->setEnabled(false); | ||||
| 
 | ||||
|     breakpoint_model = new BreakPointModel(debug_context, this); | ||||
|     breakpoint_list = new QTreeView; | ||||
|     breakpoint_list->setModel(breakpoint_model); | ||||
| 
 | ||||
|     toggle_breakpoint_button = new QPushButton(tr("Enable")); | ||||
|     toggle_breakpoint_button->setEnabled(false); | ||||
| 
 | ||||
|     qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||||
| 
 | ||||
|     connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); | ||||
| 
 | ||||
|     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             Qt::BlockingQueuedConnection); | ||||
|     connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||||
| 
 | ||||
|     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)), | ||||
|             Qt::BlockingQueuedConnection); | ||||
|     connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); | ||||
| 
 | ||||
|     connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), | ||||
|             breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); | ||||
| 
 | ||||
|     connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), | ||||
|             this, SLOT(OnBreakpointSelectionChanged(QModelIndex))); | ||||
| 
 | ||||
|     connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled())); | ||||
| 
 | ||||
|     QWidget* main_widget = new QWidget; | ||||
|     auto main_layout = new QVBoxLayout; | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(status_text); | ||||
|         sub_layout->addWidget(resume_button); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     main_layout->addWidget(breakpoint_list); | ||||
|     main_layout->addWidget(toggle_breakpoint_button); | ||||
|     main_widget->setLayout(main_layout); | ||||
| 
 | ||||
|     setWidget(main_widget); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) | ||||
| { | ||||
|     // Process in GUI thread
 | ||||
|     emit BreakPointHit(event, data); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) | ||||
| { | ||||
|     status_text->setText(tr("Emulation halted at breakpoint")); | ||||
|     resume_button->setEnabled(true); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnPicaResume() | ||||
| { | ||||
|     // Process in GUI thread
 | ||||
|     emit Resumed(); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnResumed() | ||||
| { | ||||
|     status_text->setText(tr("Emulation running")); | ||||
|     resume_button->setEnabled(false); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnResumeRequested() | ||||
| { | ||||
|     if (auto context = context_weak.lock()) | ||||
|         context->Resume(); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index) | ||||
| { | ||||
|     if (!index.isValid()) { | ||||
|         toggle_breakpoint_button->setEnabled(false); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     toggle_breakpoint_button->setEnabled(true); | ||||
|     UpdateToggleBreakpointButton(index); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled() | ||||
| { | ||||
|     QModelIndex index = breakpoint_list->selectionModel()->currentIndex(); | ||||
|     bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()); | ||||
| 
 | ||||
|     breakpoint_model->setData(index, new_state, | ||||
|                               BreakPointModel::Role_IsEnabled); | ||||
|     UpdateToggleBreakpointButton(index); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index) | ||||
| { | ||||
|     if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) { | ||||
|         toggle_breakpoint_button->setText(tr("Disable")); | ||||
|     } else { | ||||
|         toggle_breakpoint_button->setText(tr("Enable")); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										53
									
								
								src/citra_qt/debugger/graphics_breakpoints.hxx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/citra_qt/debugger/graphics_breakpoints.hxx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,53 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QAbstractListModel> | ||||
| #include <QDockWidget> | ||||
| 
 | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
| 
 | ||||
| class QLabel; | ||||
| class QPushButton; | ||||
| class QTreeView; | ||||
| 
 | ||||
| class BreakPointModel; | ||||
| 
 | ||||
| class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
|     using Event = Pica::DebugContext::Event; | ||||
| 
 | ||||
| public: | ||||
|     GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                               QWidget* parent = nullptr); | ||||
| 
 | ||||
|     void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||||
|     void OnPicaResume() override; | ||||
| 
 | ||||
| public slots: | ||||
|     void OnBreakPointHit(Pica::DebugContext::Event event, void* data); | ||||
|     void OnResumeRequested(); | ||||
|     void OnResumed(); | ||||
|     void OnBreakpointSelectionChanged(const QModelIndex&); | ||||
|     void OnToggleBreakpointEnabled(); | ||||
| 
 | ||||
| signals: | ||||
|     void Resumed(); | ||||
|     void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||||
|     void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); | ||||
| 
 | ||||
| private: | ||||
|     void UpdateToggleBreakpointButton(const QModelIndex& index); | ||||
| 
 | ||||
|     QLabel* status_text; | ||||
|     QPushButton* resume_button; | ||||
|     QPushButton* toggle_breakpoint_button; | ||||
| 
 | ||||
|     BreakPointModel* breakpoint_model; | ||||
|     QTreeView* breakpoint_list; | ||||
| }; | ||||
							
								
								
									
										38
									
								
								src/citra_qt/debugger/graphics_breakpoints_p.hxx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/citra_qt/debugger/graphics_breakpoints_p.hxx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QAbstractListModel> | ||||
| 
 | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
| 
 | ||||
| class BreakPointModel : public QAbstractListModel { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     enum { | ||||
|         Role_IsEnabled = Qt::UserRole, | ||||
|     }; | ||||
| 
 | ||||
|     BreakPointModel(std::shared_ptr<Pica::DebugContext> context, QObject* parent); | ||||
| 
 | ||||
|     int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||||
|     int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||
|     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | ||||
|     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; | ||||
| 
 | ||||
|     bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); | ||||
| 
 | ||||
| public slots: | ||||
|     void OnBreakPointHit(Pica::DebugContext::Event event); | ||||
|     void OnResumed(); | ||||
| 
 | ||||
| private: | ||||
|     std::weak_ptr<Pica::DebugContext> context_weak; | ||||
|     bool at_breakpoint; | ||||
|     Pica::DebugContext::Event active_breakpoint; | ||||
| }; | ||||
|  | @ -1,31 +1,179 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QLabel> | ||||
| #include <QListView> | ||||
| #include <QMainWindow> | ||||
| #include <QPushButton> | ||||
| #include <QVBoxLayout> | ||||
| #include <QTreeView> | ||||
| #include <QSpinBox> | ||||
| #include <QComboBox> | ||||
| 
 | ||||
| #include "video_core/pica.h" | ||||
| #include "video_core/math.h" | ||||
| 
 | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
| 
 | ||||
| #include "graphics_cmdlists.hxx" | ||||
| 
 | ||||
| GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) | ||||
| { | ||||
| #include "util/spinbox.hxx" | ||||
| 
 | ||||
| QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { | ||||
|     QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); | ||||
|     for (int y = 0; y < info.height; ++y) { | ||||
|         for (int x = 0; x < info.width; ++x) { | ||||
|             Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info, true); | ||||
|             decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return decoded_image; | ||||
| } | ||||
| 
 | ||||
| class TextureInfoWidget : public QWidget { | ||||
| public: | ||||
|     TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) : QWidget(parent) { | ||||
|         QLabel* image_widget = new QLabel; | ||||
|         QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info)); | ||||
|         image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation); | ||||
|         image_widget->setPixmap(image_pixmap); | ||||
| 
 | ||||
|         QVBoxLayout* layout = new QVBoxLayout; | ||||
|         layout->addWidget(image_widget); | ||||
|         setLayout(layout); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent) | ||||
|     : QDockWidget(tr("Texture 0x%1").arg(info.physical_address, 8, 16, QLatin1Char('0'))), | ||||
|       info(info) { | ||||
| 
 | ||||
|     QWidget* main_widget = new QWidget; | ||||
| 
 | ||||
|     QLabel* image_widget = new QLabel; | ||||
| 
 | ||||
|     connect(this, SIGNAL(UpdatePixmap(const QPixmap&)), image_widget, SLOT(setPixmap(const QPixmap&))); | ||||
| 
 | ||||
|     CSpinBox* phys_address_spinbox = new CSpinBox; | ||||
|     phys_address_spinbox->SetBase(16); | ||||
|     phys_address_spinbox->SetRange(0, 0xFFFFFFFF); | ||||
|     phys_address_spinbox->SetPrefix("0x"); | ||||
|     phys_address_spinbox->SetValue(info.physical_address); | ||||
|     connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64))); | ||||
| 
 | ||||
|     QComboBox* format_choice = new QComboBox; | ||||
|     format_choice->addItem(tr("RGBA8")); | ||||
|     format_choice->addItem(tr("RGB8")); | ||||
|     format_choice->addItem(tr("RGBA5551")); | ||||
|     format_choice->addItem(tr("RGB565")); | ||||
|     format_choice->addItem(tr("RGBA4")); | ||||
|     format_choice->addItem(tr("IA8")); | ||||
|     format_choice->addItem(tr("UNK6")); | ||||
|     format_choice->addItem(tr("I8")); | ||||
|     format_choice->addItem(tr("A8")); | ||||
|     format_choice->addItem(tr("IA4")); | ||||
|     format_choice->addItem(tr("UNK10")); | ||||
|     format_choice->addItem(tr("A4")); | ||||
|     format_choice->setCurrentIndex(static_cast<int>(info.format)); | ||||
|     connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int))); | ||||
| 
 | ||||
|     QSpinBox* width_spinbox = new QSpinBox; | ||||
|     width_spinbox->setMaximum(65535); | ||||
|     width_spinbox->setValue(info.width); | ||||
|     connect(width_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnWidthChanged(int))); | ||||
| 
 | ||||
|     QSpinBox* height_spinbox = new QSpinBox; | ||||
|     height_spinbox->setMaximum(65535); | ||||
|     height_spinbox->setValue(info.height); | ||||
|     connect(height_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnHeightChanged(int))); | ||||
| 
 | ||||
|     QSpinBox* stride_spinbox = new QSpinBox; | ||||
|     stride_spinbox->setMaximum(65535 * 4); | ||||
|     stride_spinbox->setValue(info.stride); | ||||
|     connect(stride_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnStrideChanged(int))); | ||||
| 
 | ||||
|     QVBoxLayout* main_layout = new QVBoxLayout; | ||||
|     main_layout->addWidget(image_widget); | ||||
| 
 | ||||
|     { | ||||
|         QHBoxLayout* sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Source Address:"))); | ||||
|         sub_layout->addWidget(phys_address_spinbox); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         QHBoxLayout* sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Format"))); | ||||
|         sub_layout->addWidget(format_choice); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         QHBoxLayout* sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Width:"))); | ||||
|         sub_layout->addWidget(width_spinbox); | ||||
|         sub_layout->addStretch(); | ||||
|         sub_layout->addWidget(new QLabel(tr("Height:"))); | ||||
|         sub_layout->addWidget(height_spinbox); | ||||
|         sub_layout->addStretch(); | ||||
|         sub_layout->addWidget(new QLabel(tr("Stride:"))); | ||||
|         sub_layout->addWidget(stride_spinbox); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
| 
 | ||||
|     main_widget->setLayout(main_layout); | ||||
| 
 | ||||
|     emit UpdatePixmap(ReloadPixmap()); | ||||
| 
 | ||||
|     setWidget(main_widget); | ||||
| } | ||||
| 
 | ||||
| void TextureInfoDockWidget::OnAddressChanged(qint64 value) { | ||||
|     info.physical_address = value; | ||||
|     emit UpdatePixmap(ReloadPixmap()); | ||||
| } | ||||
| 
 | ||||
| void TextureInfoDockWidget::OnFormatChanged(int value) { | ||||
|     info.format = static_cast<Pica::Regs::TextureFormat>(value); | ||||
|     emit UpdatePixmap(ReloadPixmap()); | ||||
| } | ||||
| 
 | ||||
| void TextureInfoDockWidget::OnWidthChanged(int value) { | ||||
|     info.width = value; | ||||
|     emit UpdatePixmap(ReloadPixmap()); | ||||
| } | ||||
| 
 | ||||
| void TextureInfoDockWidget::OnHeightChanged(int value) { | ||||
|     info.height = value; | ||||
|     emit UpdatePixmap(ReloadPixmap()); | ||||
| } | ||||
| 
 | ||||
| void TextureInfoDockWidget::OnStrideChanged(int value) { | ||||
|     info.stride = value; | ||||
|     emit UpdatePixmap(ReloadPixmap()); | ||||
| } | ||||
| 
 | ||||
| QPixmap TextureInfoDockWidget::ReloadPixmap() const { | ||||
|     u8* src = Memory::GetPointer(Pica::PAddrToVAddr(info.physical_address)); | ||||
|     return QPixmap::fromImage(LoadTexture(src, info)); | ||||
| } | ||||
| 
 | ||||
| GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| int GPUCommandListModel::rowCount(const QModelIndex& parent) const | ||||
| { | ||||
| int GPUCommandListModel::rowCount(const QModelIndex& parent) const { | ||||
|     return pica_trace.writes.size(); | ||||
| } | ||||
| 
 | ||||
| int GPUCommandListModel::columnCount(const QModelIndex& parent) const | ||||
| { | ||||
| int GPUCommandListModel::columnCount(const QModelIndex& parent) const { | ||||
|     return 2; | ||||
| } | ||||
| 
 | ||||
| QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const | ||||
| { | ||||
| QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const { | ||||
|     if (!index.isValid()) | ||||
|         return QVariant(); | ||||
| 
 | ||||
|  | @ -36,21 +184,39 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const | |||
|     if (role == Qt::DisplayRole) { | ||||
|         QString content; | ||||
|         if (index.column() == 0) { | ||||
|             content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str()); | ||||
|             QString content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str()); | ||||
|             content.append(" "); | ||||
|             return content; | ||||
|         } else if (index.column() == 1) { | ||||
|             content.append(QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0'))); | ||||
|             QString content = QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0')); | ||||
|             content.append(QString("%1 ").arg(val, 8, 16, QLatin1Char('0'))); | ||||
|             return content; | ||||
|         } | ||||
| 
 | ||||
|         return QVariant(content); | ||||
|     } else if (role == CommandIdRole) { | ||||
|         return QVariant::fromValue<int>(cmd.cmd_id.Value()); | ||||
|     } | ||||
| 
 | ||||
|     return QVariant(); | ||||
| } | ||||
| 
 | ||||
| void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace) | ||||
| { | ||||
| QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const { | ||||
|     switch(role) { | ||||
|     case Qt::DisplayRole: | ||||
|     { | ||||
|         if (section == 0) { | ||||
|             return tr("Command Name"); | ||||
|         } else if (section == 1) { | ||||
|             return tr("Data"); | ||||
|         } | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
|     } | ||||
| 
 | ||||
|     return QVariant(); | ||||
| } | ||||
| 
 | ||||
| void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace) { | ||||
|     beginResetModel(); | ||||
| 
 | ||||
|     pica_trace = trace; | ||||
|  | @ -58,38 +224,107 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& | |||
|     endResetModel(); | ||||
| } | ||||
| 
 | ||||
| #define COMMAND_IN_RANGE(cmd_id, reg_name)   \ | ||||
|     (cmd_id >= PICA_REG_INDEX(reg_name) &&   \ | ||||
|      cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::registers.reg_name)) / 4) | ||||
| 
 | ||||
| GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) | ||||
| { | ||||
| void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { | ||||
|     const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); | ||||
|     if (COMMAND_IN_RANGE(command_id, texture0) || | ||||
|         COMMAND_IN_RANGE(command_id, texture1) || | ||||
|         COMMAND_IN_RANGE(command_id, texture2)) { | ||||
| 
 | ||||
|         unsigned index; | ||||
|         if (COMMAND_IN_RANGE(command_id, texture0)) { | ||||
|             index = 0; | ||||
|         } else if (COMMAND_IN_RANGE(command_id, texture1)) { | ||||
|             index = 1; | ||||
|         } else { | ||||
|             index = 2; | ||||
|         } | ||||
|         auto config = Pica::registers.GetTextures()[index].config; | ||||
|         auto format = Pica::registers.GetTextures()[index].format; | ||||
|         auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); | ||||
| 
 | ||||
|         // TODO: Instead, emit a signal here to be caught by the main window widget.
 | ||||
|         auto main_window = static_cast<QMainWindow*>(parent()); | ||||
|         main_window->tabifyDockWidget(this, new TextureInfoDockWidget(info, main_window)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { | ||||
|     QWidget* new_info_widget; | ||||
| 
 | ||||
|     const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); | ||||
|     if (COMMAND_IN_RANGE(command_id, texture0) || | ||||
|         COMMAND_IN_RANGE(command_id, texture1) || | ||||
|         COMMAND_IN_RANGE(command_id, texture2)) { | ||||
| 
 | ||||
|         unsigned index; | ||||
|         if (COMMAND_IN_RANGE(command_id, texture0)) { | ||||
|             index = 0; | ||||
|         } else if (COMMAND_IN_RANGE(command_id, texture1)) { | ||||
|             index = 1; | ||||
|         } else { | ||||
|             index = 2; | ||||
|         } | ||||
|         auto config = Pica::registers.GetTextures()[index].config; | ||||
|         auto format = Pica::registers.GetTextures()[index].format; | ||||
| 
 | ||||
|         auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); | ||||
|         u8* src = Memory::GetPointer(Pica::PAddrToVAddr(config.GetPhysicalAddress())); | ||||
|         new_info_widget = new TextureInfoWidget(src, info); | ||||
|     } else { | ||||
|         new_info_widget = new QWidget; | ||||
|     } | ||||
| 
 | ||||
|     widget()->layout()->removeWidget(command_info_widget); | ||||
|     delete command_info_widget; | ||||
|     widget()->layout()->addWidget(new_info_widget); | ||||
|     command_info_widget = new_info_widget; | ||||
| } | ||||
| #undef COMMAND_IN_RANGE | ||||
| 
 | ||||
| GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) { | ||||
|     setObjectName("Pica Command List"); | ||||
|     GPUCommandListModel* model = new GPUCommandListModel(this); | ||||
| 
 | ||||
|     QWidget* main_widget = new QWidget; | ||||
| 
 | ||||
|     QTreeView* list_widget = new QTreeView; | ||||
|     list_widget = new QTreeView; | ||||
|     list_widget->setModel(model); | ||||
|     list_widget->setFont(QFont("monospace")); | ||||
|     list_widget->setRootIsDecorated(false); | ||||
| 
 | ||||
|     QPushButton* toggle_tracing = new QPushButton(tr("Start Tracing")); | ||||
|     connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)), | ||||
|             this, SLOT(SetCommandInfo(const QModelIndex&))); | ||||
|     connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), | ||||
|             this, SLOT(OnCommandDoubleClicked(const QModelIndex&))); | ||||
| 
 | ||||
|     toggle_tracing = new QPushButton(tr("Start Tracing")); | ||||
| 
 | ||||
|     connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); | ||||
|     connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), | ||||
|             model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); | ||||
| 
 | ||||
|     command_info_widget = new QWidget; | ||||
| 
 | ||||
|     QVBoxLayout* main_layout = new QVBoxLayout; | ||||
|     main_layout->addWidget(list_widget); | ||||
|     main_layout->addWidget(toggle_tracing); | ||||
|     main_layout->addWidget(command_info_widget); | ||||
|     main_widget->setLayout(main_layout); | ||||
| 
 | ||||
|     setWidget(main_widget); | ||||
| } | ||||
| 
 | ||||
| void GPUCommandListWidget::OnToggleTracing() | ||||
| { | ||||
| void GPUCommandListWidget::OnToggleTracing() { | ||||
|     if (!Pica::DebugUtils::IsPicaTracing()) { | ||||
|         Pica::DebugUtils::StartPicaTracing(); | ||||
|         toggle_tracing->setText(tr("Finish Tracing")); | ||||
|     } else { | ||||
|         pica_trace = Pica::DebugUtils::FinishPicaTracing(); | ||||
|         emit TracingFinished(*pica_trace); | ||||
|         toggle_tracing->setText(tr("Start Tracing")); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -10,16 +10,24 @@ | |||
| #include "video_core/gpu_debugger.h" | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
| 
 | ||||
| class QPushButton; | ||||
| class QTreeView; | ||||
| 
 | ||||
| class GPUCommandListModel : public QAbstractListModel | ||||
| { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     enum { | ||||
|         CommandIdRole = Qt::UserRole, | ||||
|     }; | ||||
| 
 | ||||
|     GPUCommandListModel(QObject* parent); | ||||
| 
 | ||||
|     int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||||
|     int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||
|     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | ||||
|     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; | ||||
| 
 | ||||
| public slots: | ||||
|     void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); | ||||
|  | @ -37,10 +45,39 @@ public: | |||
| 
 | ||||
| public slots: | ||||
|     void OnToggleTracing(); | ||||
|     void OnCommandDoubleClicked(const QModelIndex&); | ||||
| 
 | ||||
|     void SetCommandInfo(const QModelIndex&); | ||||
| 
 | ||||
| signals: | ||||
|     void TracingFinished(const Pica::DebugUtils::PicaTrace&); | ||||
| 
 | ||||
| private: | ||||
|     std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace; | ||||
| 
 | ||||
|     QTreeView* list_widget; | ||||
|     QWidget* command_info_widget; | ||||
|     QPushButton* toggle_tracing; | ||||
| }; | ||||
| 
 | ||||
| class TextureInfoDockWidget : public QDockWidget { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr); | ||||
| 
 | ||||
| signals: | ||||
|     void UpdatePixmap(const QPixmap& pixmap); | ||||
| 
 | ||||
| private slots: | ||||
|     void OnAddressChanged(qint64 value); | ||||
|     void OnFormatChanged(int value); | ||||
|     void OnWidthChanged(int value); | ||||
|     void OnHeightChanged(int value); | ||||
|     void OnStrideChanged(int value); | ||||
| 
 | ||||
| private: | ||||
|     QPixmap ReloadPixmap() const; | ||||
| 
 | ||||
|     Pica::DebugUtils::TextureInfo info; | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										283
									
								
								src/citra_qt/debugger/graphics_framebuffer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								src/citra_qt/debugger/graphics_framebuffer.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,283 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QBoxLayout> | ||||
| #include <QComboBox> | ||||
| #include <QDebug> | ||||
| #include <QLabel> | ||||
| #include <QMetaType> | ||||
| #include <QPushButton> | ||||
| #include <QSpinBox> | ||||
| 
 | ||||
| #include "video_core/pica.h" | ||||
| 
 | ||||
| #include "graphics_framebuffer.hxx" | ||||
| 
 | ||||
| #include "util/spinbox.hxx" | ||||
| 
 | ||||
| BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                                                const QString& title, QWidget* parent) | ||||
|     : QDockWidget(title, parent), BreakPointObserver(debug_context) | ||||
| { | ||||
|     qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||||
| 
 | ||||
|     connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||||
| 
 | ||||
|     // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
 | ||||
|     //       care of delaying its handling to the GUI thread.
 | ||||
|     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             Qt::BlockingQueuedConnection); | ||||
| } | ||||
| 
 | ||||
| void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) | ||||
| { | ||||
|     emit BreakPointHit(event, data); | ||||
| } | ||||
| 
 | ||||
| void BreakPointObserverDock::OnPicaResume() | ||||
| { | ||||
|     emit Resumed(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                                                      QWidget* parent) | ||||
|     : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent), | ||||
|       framebuffer_source(Source::PicaTarget) | ||||
| { | ||||
|     setObjectName("PicaFramebuffer"); | ||||
| 
 | ||||
|     framebuffer_source_list = new QComboBox; | ||||
|     framebuffer_source_list->addItem(tr("Active Render Target")); | ||||
|     framebuffer_source_list->addItem(tr("Custom")); | ||||
|     framebuffer_source_list->setCurrentIndex(static_cast<int>(framebuffer_source)); | ||||
| 
 | ||||
|     framebuffer_address_control = new CSpinBox; | ||||
|     framebuffer_address_control->SetBase(16); | ||||
|     framebuffer_address_control->SetRange(0, 0xFFFFFFFF); | ||||
|     framebuffer_address_control->SetPrefix("0x"); | ||||
| 
 | ||||
|     framebuffer_width_control = new QSpinBox; | ||||
|     framebuffer_width_control->setMinimum(1); | ||||
|     framebuffer_width_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
 | ||||
| 
 | ||||
|     framebuffer_height_control = new QSpinBox; | ||||
|     framebuffer_height_control->setMinimum(1); | ||||
|     framebuffer_height_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
 | ||||
| 
 | ||||
|     framebuffer_format_control = new QComboBox; | ||||
|     framebuffer_format_control->addItem(tr("RGBA8")); | ||||
|     framebuffer_format_control->addItem(tr("RGB8")); | ||||
|     framebuffer_format_control->addItem(tr("RGBA5551")); | ||||
|     framebuffer_format_control->addItem(tr("RGB565")); | ||||
|     framebuffer_format_control->addItem(tr("RGBA4")); | ||||
| 
 | ||||
|     // TODO: This QLabel should shrink the image to the available space rather than just expanding...
 | ||||
|     framebuffer_picture_label = new QLabel; | ||||
| 
 | ||||
|     auto enlarge_button = new QPushButton(tr("Enlarge")); | ||||
| 
 | ||||
|     // Connections
 | ||||
|     connect(this, SIGNAL(Update()), this, SLOT(OnUpdate())); | ||||
|     connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int))); | ||||
|     connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64))); | ||||
|     connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int))); | ||||
|     connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int))); | ||||
|     connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int))); | ||||
| 
 | ||||
|     auto main_widget = new QWidget; | ||||
|     auto main_layout = new QVBoxLayout; | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Source:"))); | ||||
|         sub_layout->addWidget(framebuffer_source_list); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Virtual Address:"))); | ||||
|         sub_layout->addWidget(framebuffer_address_control); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Width:"))); | ||||
|         sub_layout->addWidget(framebuffer_width_control); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Height:"))); | ||||
|         sub_layout->addWidget(framebuffer_height_control); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Format:"))); | ||||
|         sub_layout->addWidget(framebuffer_format_control); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     main_layout->addWidget(framebuffer_picture_label); | ||||
|     main_layout->addWidget(enlarge_button); | ||||
|     main_widget->setLayout(main_layout); | ||||
|     setWidget(main_widget); | ||||
| 
 | ||||
|     // Load current data - TODO: Make sure this works when emulation is not running
 | ||||
|     if (debug_context && debug_context->at_breakpoint) | ||||
|         emit Update(); | ||||
|     widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint
 | ||||
| } | ||||
| 
 | ||||
| void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) | ||||
| { | ||||
|     emit Update(); | ||||
|     widget()->setEnabled(true); | ||||
| } | ||||
| 
 | ||||
| void GraphicsFramebufferWidget::OnResumed() | ||||
| { | ||||
|     widget()->setEnabled(false); | ||||
| } | ||||
| 
 | ||||
| void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value) | ||||
| { | ||||
|     framebuffer_source = static_cast<Source>(new_value); | ||||
|     emit Update(); | ||||
| } | ||||
| 
 | ||||
| void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value) | ||||
| { | ||||
|     if (framebuffer_address != new_value) { | ||||
|         framebuffer_address = static_cast<unsigned>(new_value); | ||||
| 
 | ||||
|         framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||||
|         emit Update(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value) | ||||
| { | ||||
|     if (framebuffer_width != new_value) { | ||||
|         framebuffer_width = new_value; | ||||
| 
 | ||||
|         framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||||
|         emit Update(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value) | ||||
| { | ||||
|     if (framebuffer_height != new_value) { | ||||
|         framebuffer_height = new_value; | ||||
| 
 | ||||
|         framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||||
|         emit Update(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value) | ||||
| { | ||||
|     if (framebuffer_format != static_cast<Format>(new_value)) { | ||||
|         framebuffer_format = static_cast<Format>(new_value); | ||||
| 
 | ||||
|         framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||||
|         emit Update(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsFramebufferWidget::OnUpdate() | ||||
| { | ||||
|     QPixmap pixmap; | ||||
| 
 | ||||
|     switch (framebuffer_source) { | ||||
|     case Source::PicaTarget: | ||||
|     { | ||||
|         // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
 | ||||
| 
 | ||||
|         auto framebuffer = Pica::registers.framebuffer; | ||||
|         using Framebuffer = decltype(framebuffer); | ||||
| 
 | ||||
|         framebuffer_address = framebuffer.GetColorBufferPhysicalAddress(); | ||||
|         framebuffer_width = framebuffer.GetWidth(); | ||||
|         framebuffer_height = framebuffer.GetHeight(); | ||||
|         framebuffer_format = static_cast<Format>(framebuffer.color_format); | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case Source::Custom: | ||||
|     { | ||||
|         // Keep user-specified values
 | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     default: | ||||
|         qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     // TODO: Implement a good way to visualize alpha components!
 | ||||
|     // TODO: Unify this decoding code with the texture decoder
 | ||||
|     switch (framebuffer_format) { | ||||
|     case Format::RGBA8: | ||||
|     { | ||||
|         QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | ||||
|         u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address)); | ||||
|         for (unsigned y = 0; y < framebuffer_height; ++y) { | ||||
|             for (unsigned x = 0; x < framebuffer_width; ++x) { | ||||
|                 u32 value = *(color_buffer + x + y * framebuffer_width); | ||||
| 
 | ||||
|                 decoded_image.setPixel(x, y, qRgba((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255/*value >> 24*/)); | ||||
|             } | ||||
|         } | ||||
|         pixmap = QPixmap::fromImage(decoded_image); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case Format::RGB8: | ||||
|     { | ||||
|         QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | ||||
|         u8* color_buffer = Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address)); | ||||
|         for (unsigned y = 0; y < framebuffer_height; ++y) { | ||||
|             for (unsigned x = 0; x < framebuffer_width; ++x) { | ||||
|                 u8* pixel_pointer = color_buffer + x * 3 + y * 3 * framebuffer_width; | ||||
| 
 | ||||
|                 decoded_image.setPixel(x, y, qRgba(pixel_pointer[0], pixel_pointer[1], pixel_pointer[2], 255/*value >> 24*/)); | ||||
|             } | ||||
|         } | ||||
|         pixmap = QPixmap::fromImage(decoded_image); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case Format::RGBA5551: | ||||
|     { | ||||
|         QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | ||||
|         u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address)); | ||||
|         for (unsigned y = 0; y < framebuffer_height; ++y) { | ||||
|             for (unsigned x = 0; x < framebuffer_width; ++x) { | ||||
|                 u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2); | ||||
|                 u8 r = (value >> 11) & 0x1F; | ||||
|                 u8 g = (value >> 6) & 0x1F; | ||||
|                 u8 b = (value >> 1) & 0x1F; | ||||
|                 u8 a = value & 1; | ||||
| 
 | ||||
|                 decoded_image.setPixel(x, y, qRgba(r, g, b, 255/*a*/)); | ||||
|             } | ||||
|         } | ||||
|         pixmap = QPixmap::fromImage(decoded_image); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     default: | ||||
|         qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     framebuffer_address_control->SetValue(framebuffer_address); | ||||
|     framebuffer_width_control->setValue(framebuffer_width); | ||||
|     framebuffer_height_control->setValue(framebuffer_height); | ||||
|     framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format)); | ||||
|     framebuffer_picture_label->setPixmap(pixmap); | ||||
| } | ||||
							
								
								
									
										92
									
								
								src/citra_qt/debugger/graphics_framebuffer.hxx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/citra_qt/debugger/graphics_framebuffer.hxx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <QDockWidget> | ||||
| 
 | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
| 
 | ||||
| class QComboBox; | ||||
| class QLabel; | ||||
| class QSpinBox; | ||||
| 
 | ||||
| class CSpinBox; | ||||
| 
 | ||||
| // Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots.
 | ||||
| // This is because the Pica breakpoint callbacks are called from a non-GUI thread, while
 | ||||
| // the widget usually wants to perform reactions in the GUI thread.
 | ||||
| class BreakPointObserverDock : public QDockWidget, Pica::DebugContext::BreakPointObserver { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title, | ||||
|                            QWidget* parent = nullptr); | ||||
| 
 | ||||
|     void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||||
|     void OnPicaResume() override; | ||||
| 
 | ||||
| private slots: | ||||
|     virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0; | ||||
|     virtual void OnResumed() = 0; | ||||
| 
 | ||||
| signals: | ||||
|     void Resumed(); | ||||
|     void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||||
| }; | ||||
| 
 | ||||
| class GraphicsFramebufferWidget : public BreakPointObserverDock { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
|     using Event = Pica::DebugContext::Event; | ||||
| 
 | ||||
|     enum class Source { | ||||
|         PicaTarget = 0, | ||||
|         Custom = 1, | ||||
| 
 | ||||
|         // TODO: Add GPU framebuffer sources!
 | ||||
|     }; | ||||
| 
 | ||||
|     enum class Format { | ||||
|         RGBA8    = 0, | ||||
|         RGB8     = 1, | ||||
|         RGBA5551 = 2, | ||||
|         RGB565   = 3, | ||||
|         RGBA4    = 4, | ||||
|     }; | ||||
| 
 | ||||
| public: | ||||
|     GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); | ||||
| 
 | ||||
| public slots: | ||||
|     void OnFramebufferSourceChanged(int new_value); | ||||
|     void OnFramebufferAddressChanged(qint64 new_value); | ||||
|     void OnFramebufferWidthChanged(int new_value); | ||||
|     void OnFramebufferHeightChanged(int new_value); | ||||
|     void OnFramebufferFormatChanged(int new_value); | ||||
|     void OnUpdate(); | ||||
| 
 | ||||
| private slots: | ||||
|     void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||||
|     void OnResumed() override; | ||||
| 
 | ||||
| signals: | ||||
|     void Update(); | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     QComboBox* framebuffer_source_list; | ||||
|     CSpinBox* framebuffer_address_control; | ||||
|     QSpinBox* framebuffer_width_control; | ||||
|     QSpinBox* framebuffer_height_control; | ||||
|     QComboBox* framebuffer_format_control; | ||||
| 
 | ||||
|     QLabel* framebuffer_picture_label; | ||||
| 
 | ||||
|     Source framebuffer_source; | ||||
|     unsigned framebuffer_address; | ||||
|     unsigned framebuffer_width; | ||||
|     unsigned framebuffer_height; | ||||
|     Format framebuffer_format; | ||||
| }; | ||||
|  | @ -5,7 +5,7 @@ | |||
| 
 | ||||
| struct Hotkey | ||||
| { | ||||
|     Hotkey() : shortcut(NULL), context(Qt::WindowShortcut) {} | ||||
|     Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} | ||||
| 
 | ||||
|     QKeySequence keyseq; | ||||
|     QShortcut* shortcut; | ||||
|  | @ -81,7 +81,7 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge | |||
|     Hotkey& hk = hotkey_groups[group][action]; | ||||
| 
 | ||||
|     if (!hk.shortcut) | ||||
|         hk.shortcut = new QShortcut(hk.keyseq, widget, NULL, NULL, hk.context); | ||||
|         hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context); | ||||
| 
 | ||||
|     return hk.shortcut; | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| #include <thread> | ||||
| 
 | ||||
| #include <QtGui> | ||||
| #include <QDesktopWidget> | ||||
| #include <QFileDialog> | ||||
|  | @ -5,8 +7,13 @@ | |||
| #include "main.hxx" | ||||
| 
 | ||||
| #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/platform.h" | ||||
| #include "common/log_manager.h" | ||||
| #include "common/scope_exit.h" | ||||
| 
 | ||||
| #if EMU_PLATFORM == PLATFORM_LINUX | ||||
| #include <unistd.h> | ||||
| #endif | ||||
|  | @ -20,7 +27,9 @@ | |||
| #include "debugger/callstack.hxx" | ||||
| #include "debugger/ramview.hxx" | ||||
| #include "debugger/graphics.hxx" | ||||
| #include "debugger/graphics_breakpoints.hxx" | ||||
| #include "debugger/graphics_cmdlists.hxx" | ||||
| #include "debugger/graphics_framebuffer.hxx" | ||||
| 
 | ||||
| #include "core/settings.h" | ||||
| #include "core/system.h" | ||||
|  | @ -31,16 +40,12 @@ | |||
| 
 | ||||
| #include "version.h" | ||||
| 
 | ||||
| 
 | ||||
| GMainWindow::GMainWindow() | ||||
| { | ||||
|     LogManager::Init(); | ||||
|     Pica::g_debug_context = Pica::DebugContext::Construct(); | ||||
| 
 | ||||
|     Config config; | ||||
| 
 | ||||
|     if (!Settings::values.enable_log) | ||||
|         LogManager::Shutdown(); | ||||
| 
 | ||||
|     ui.setupUi(this); | ||||
|     statusBar()->hide(); | ||||
| 
 | ||||
|  | @ -67,12 +72,22 @@ GMainWindow::GMainWindow() | |||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); | ||||
|     graphicsCommandsWidget->hide(); | ||||
| 
 | ||||
|     auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this); | ||||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget); | ||||
|     graphicsBreakpointsWidget->hide(); | ||||
| 
 | ||||
|     auto graphicsFramebufferWidget = new GraphicsFramebufferWidget(Pica::g_debug_context, this); | ||||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget); | ||||
|     graphicsFramebufferWidget->hide(); | ||||
| 
 | ||||
|     QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | ||||
|     debug_menu->addAction(disasmWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(registersWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(callstackWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); | ||||
| 
 | ||||
|     // Set default UI state
 | ||||
|     // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
 | ||||
|  | @ -131,24 +146,20 @@ GMainWindow::GMainWindow() | |||
| GMainWindow::~GMainWindow() | ||||
| { | ||||
|     // will get automatically deleted otherwise
 | ||||
|     if (render_window->parent() == NULL) | ||||
|     if (render_window->parent() == nullptr) | ||||
|         delete render_window; | ||||
| 
 | ||||
|     Pica::g_debug_context.reset(); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::BootGame(std::string filename) | ||||
| { | ||||
|     NOTICE_LOG(MASTER_LOG, "Citra starting...\n"); | ||||
|     LOG_INFO(Frontend, "Citra starting...\n"); | ||||
|     System::Init(render_window); | ||||
| 
 | ||||
|     if (Core::Init()) { | ||||
|         ERROR_LOG(MASTER_LOG, "Core initialization failed, exiting..."); | ||||
|         Core::Stop(); | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|     // Load a game or die...
 | ||||
|     if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM!"); | ||||
|         LOG_CRITICAL(Frontend, "Failed to load ROM!"); | ||||
|     } | ||||
| 
 | ||||
|     disasmWidget->Init(); | ||||
|  | @ -164,7 +175,7 @@ void GMainWindow::BootGame(std::string filename) | |||
| 
 | ||||
| void GMainWindow::OnMenuLoadFile() | ||||
| { | ||||
|     QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.elf *.axf *.bin *.cci *.cxi)")); | ||||
|     QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.3dsx *.elf *.axf *.bin *.cci *.cxi)")); | ||||
|     if (filename.size()) | ||||
|        BootGame(filename.toLatin1().data()); | ||||
| } | ||||
|  | @ -213,18 +224,21 @@ void GMainWindow::OnOpenHotkeysDialog() | |||
| void GMainWindow::ToggleWindowMode() | ||||
| { | ||||
|     bool enable = ui.action_Popout_Window_Mode->isChecked(); | ||||
|     if (enable && render_window->parent() != NULL) | ||||
|     if (enable && render_window->parent() != nullptr) | ||||
|     { | ||||
|         ui.horizontalLayout->removeWidget(render_window); | ||||
|         render_window->setParent(NULL); | ||||
|         render_window->setParent(nullptr); | ||||
|         render_window->setVisible(true); | ||||
|         render_window->RestoreGeometry(); | ||||
|         render_window->setFocusPolicy(Qt::NoFocus); | ||||
|     } | ||||
|     else if (!enable && render_window->parent() == NULL) | ||||
|     else if (!enable && render_window->parent() == nullptr) | ||||
|     { | ||||
|         render_window->BackupGeometry(); | ||||
|         ui.horizontalLayout->addWidget(render_window); | ||||
|         render_window->setVisible(true); | ||||
|         render_window->setFocusPolicy(Qt::ClickFocus); | ||||
|         render_window->setFocus(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -255,9 +269,21 @@ void GMainWindow::closeEvent(QCloseEvent* event) | |||
| 
 | ||||
| int __cdecl main(int argc, char* argv[]) | ||||
| { | ||||
|     std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger(); | ||||
|     Log::Filter log_filter(Log::Level::Info); | ||||
|     std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter); | ||||
|     SCOPE_EXIT({ | ||||
|         logger->Close(); | ||||
|         logging_thread.join(); | ||||
|     }); | ||||
| 
 | ||||
|     QApplication::setAttribute(Qt::AA_X11InitThreads); | ||||
|     QApplication app(argc, argv); | ||||
| 
 | ||||
|     GMainWindow main_window; | ||||
|     // After settings have been loaded by GMainWindow, apply the filter
 | ||||
|     log_filter.ParseFilterString(Settings::values.log_filter); | ||||
| 
 | ||||
|     main_window.show(); | ||||
|     return app.exec(); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										303
									
								
								src/citra_qt/util/spinbox.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								src/citra_qt/util/spinbox.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,303 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
| // Copyright 2014 Tony Wasserka
 | ||||
| // All rights reserved.
 | ||||
| //
 | ||||
| // Redistribution and use in source and binary forms, with or without
 | ||||
| // modification, are permitted provided that the following conditions are met:
 | ||||
| //
 | ||||
| //     * Redistributions of source code must retain the above copyright
 | ||||
| //       notice, this list of conditions and the following disclaimer.
 | ||||
| //     * Redistributions in binary form must reproduce the above copyright
 | ||||
| //       notice, this list of conditions and the following disclaimer in the
 | ||||
| //       documentation and/or other materials provided with the distribution.
 | ||||
| //     * Neither the name of the owner nor the names of its contributors may
 | ||||
| //       be used to endorse or promote products derived from this software
 | ||||
| //       without specific prior written permission.
 | ||||
| //
 | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||
| 
 | ||||
| #include <QLineEdit> | ||||
| #include <QRegExpValidator> | ||||
| 
 | ||||
| #include "common/log.h" | ||||
| 
 | ||||
| #include "spinbox.hxx" | ||||
| 
 | ||||
| CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), base(10), min_value(-100), max_value(100), value(0), num_digits(0) | ||||
| { | ||||
|     // TODO: Might be nice to not immediately call the slot.
 | ||||
|     //       Think of an address that is being replaced by a different one, in which case a lot
 | ||||
|     //       invalid intermediate addresses would be read from during editing.
 | ||||
|     connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(OnEditingFinished())); | ||||
| 
 | ||||
|     UpdateText(); | ||||
| } | ||||
| 
 | ||||
| void CSpinBox::SetValue(qint64 val) | ||||
| { | ||||
|     auto old_value = value; | ||||
|     value = std::max(std::min(val, max_value), min_value); | ||||
| 
 | ||||
|     if (old_value != value) { | ||||
|         UpdateText(); | ||||
|         emit ValueChanged(value); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CSpinBox::SetRange(qint64 min, qint64 max) | ||||
| { | ||||
|     min_value = min; | ||||
|     max_value = max; | ||||
| 
 | ||||
|     SetValue(value); | ||||
|     UpdateText(); | ||||
| } | ||||
| 
 | ||||
| void CSpinBox::stepBy(int steps) | ||||
| { | ||||
|     auto new_value = value; | ||||
|     // Scale number of steps by the currently selected digit
 | ||||
|     // TODO: Move this code elsewhere and enable it.
 | ||||
|     // TODO: Support for num_digits==0, too
 | ||||
|     // TODO: Support base!=16, too
 | ||||
|     // TODO: Make the cursor not jump back to the end of the line...
 | ||||
|     /*if (base == 16 && num_digits > 0) {
 | ||||
|         int digit = num_digits - (lineEdit()->cursorPosition() - prefix.length()) - 1; | ||||
|         digit = std::max(0, std::min(digit, num_digits - 1)); | ||||
|         steps <<= digit * 4; | ||||
|     }*/ | ||||
| 
 | ||||
|     // Increment "new_value" by "steps", and perform annoying overflow checks, too.
 | ||||
|     if (steps < 0 && new_value + steps > new_value) { | ||||
|         new_value = std::numeric_limits<qint64>::min(); | ||||
|     } else if (steps > 0 && new_value + steps < new_value) { | ||||
|         new_value = std::numeric_limits<qint64>::max(); | ||||
|     } else { | ||||
|         new_value += steps; | ||||
|     } | ||||
| 
 | ||||
|     SetValue(new_value); | ||||
|     UpdateText(); | ||||
| } | ||||
| 
 | ||||
| QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const | ||||
| { | ||||
|     StepEnabled ret = StepNone; | ||||
| 
 | ||||
|     if (value > min_value) | ||||
|         ret |= StepDownEnabled; | ||||
| 
 | ||||
|     if (value < max_value) | ||||
|         ret |= StepUpEnabled; | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void CSpinBox::SetBase(int base) | ||||
| { | ||||
|     this->base = base; | ||||
| 
 | ||||
|     UpdateText(); | ||||
| } | ||||
| 
 | ||||
| void CSpinBox::SetNumDigits(int num_digits) | ||||
| { | ||||
|     this->num_digits = num_digits; | ||||
| 
 | ||||
|     UpdateText(); | ||||
| } | ||||
| 
 | ||||
| void CSpinBox::SetPrefix(const QString& prefix) | ||||
| { | ||||
|     this->prefix = prefix; | ||||
| 
 | ||||
|     UpdateText(); | ||||
| } | ||||
| 
 | ||||
| void CSpinBox::SetSuffix(const QString& suffix) | ||||
| { | ||||
|     this->suffix = suffix; | ||||
| 
 | ||||
|     UpdateText(); | ||||
| } | ||||
| 
 | ||||
| static QString StringToInputMask(const QString& input) { | ||||
|     QString mask = input; | ||||
| 
 | ||||
|     // ... replace any special characters by their escaped counterparts ...
 | ||||
|     mask.replace("\\", "\\\\"); | ||||
|     mask.replace("A", "\\A"); | ||||
|     mask.replace("a", "\\a"); | ||||
|     mask.replace("N", "\\N"); | ||||
|     mask.replace("n", "\\n"); | ||||
|     mask.replace("X", "\\X"); | ||||
|     mask.replace("x", "\\x"); | ||||
|     mask.replace("9", "\\9"); | ||||
|     mask.replace("0", "\\0"); | ||||
|     mask.replace("D", "\\D"); | ||||
|     mask.replace("d", "\\d"); | ||||
|     mask.replace("#", "\\#"); | ||||
|     mask.replace("H", "\\H"); | ||||
|     mask.replace("h", "\\h"); | ||||
|     mask.replace("B", "\\B"); | ||||
|     mask.replace("b", "\\b"); | ||||
|     mask.replace(">", "\\>"); | ||||
|     mask.replace("<", "\\<"); | ||||
|     mask.replace("!", "\\!"); | ||||
| 
 | ||||
|     return mask; | ||||
| } | ||||
| 
 | ||||
| void CSpinBox::UpdateText() | ||||
| { | ||||
|     // If a fixed number of digits is used, we put the line edit in insertion mode by setting an
 | ||||
|     // input mask.
 | ||||
|     QString mask; | ||||
|     if (num_digits != 0) { | ||||
|         mask += StringToInputMask(prefix); | ||||
| 
 | ||||
|         // For base 10 and negative range, demand a single sign character
 | ||||
|         if (HasSign()) | ||||
|             mask += "X"; // identified as "-" or "+" in the validator
 | ||||
| 
 | ||||
|         // Uppercase digits greater than 9.
 | ||||
|         mask += ">"; | ||||
| 
 | ||||
|         // The greatest signed 64-bit number has 19 decimal digits.
 | ||||
|         // TODO: Could probably make this more generic with some logarithms.
 | ||||
|         // For reference, unsigned 64-bit can have up to 20 decimal digits.
 | ||||
|         int digits = (num_digits != 0) ? num_digits | ||||
|                      : (base == 16) ? 16 | ||||
|                      : (base == 10) ? 19 | ||||
|                      : 0xFF; // fallback case...
 | ||||
| 
 | ||||
|         // Match num_digits digits
 | ||||
|         // Digits irrelevant to the chosen number base are filtered in the validator
 | ||||
|         mask += QString("H").repeated(std::max(num_digits, 1)); | ||||
| 
 | ||||
|         // Switch off case conversion
 | ||||
|         mask += "!"; | ||||
| 
 | ||||
|         mask += StringToInputMask(suffix); | ||||
|     } | ||||
|     lineEdit()->setInputMask(mask); | ||||
| 
 | ||||
|     // Set new text without changing the cursor position. This will cause the cursor to briefly
 | ||||
|     // appear at the end of the line and then to jump back to its original position. That's
 | ||||
|     // a bit ugly, but better than having setText() move the cursor permanently all the time.
 | ||||
|     int cursor_position = lineEdit()->cursorPosition(); | ||||
|     lineEdit()->setText(TextFromValue()); | ||||
|     lineEdit()->setCursorPosition(cursor_position); | ||||
| } | ||||
| 
 | ||||
| QString CSpinBox::TextFromValue() | ||||
| { | ||||
|     return prefix | ||||
|            + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") | ||||
|            + QString("%1").arg(abs(value), num_digits, base, QLatin1Char('0')).toUpper() | ||||
|            + suffix; | ||||
| } | ||||
| 
 | ||||
| qint64 CSpinBox::ValueFromText() | ||||
| { | ||||
|     unsigned strpos = prefix.length(); | ||||
| 
 | ||||
|     QString num_string = text().mid(strpos, text().length() - strpos - suffix.length()); | ||||
|     return num_string.toLongLong(nullptr, base); | ||||
| } | ||||
| 
 | ||||
| bool CSpinBox::HasSign() const | ||||
| { | ||||
|     return base == 10 && min_value < 0; | ||||
| } | ||||
| 
 | ||||
| void CSpinBox::OnEditingFinished() | ||||
| { | ||||
|     // Only update for valid input
 | ||||
|     QString input = lineEdit()->text(); | ||||
|     int pos = 0; | ||||
|     if (QValidator::Acceptable == validate(input, pos)) | ||||
|         SetValue(ValueFromText()); | ||||
| } | ||||
| 
 | ||||
| QValidator::State CSpinBox::validate(QString& input, int& pos) const | ||||
| { | ||||
|     if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) | ||||
|         return QValidator::Invalid; | ||||
| 
 | ||||
|     int strpos = prefix.length(); | ||||
| 
 | ||||
|     // Empty "numbers" allowed as intermediate values
 | ||||
|     if (strpos >= input.length() - HasSign() - suffix.length()) | ||||
|         return QValidator::Intermediate; | ||||
| 
 | ||||
|     _dbg_assert_(Frontend, base <= 10 || base == 16); | ||||
|     QString regexp; | ||||
| 
 | ||||
|     // Demand sign character for negative ranges
 | ||||
|     if (HasSign()) | ||||
|         regexp += "[+\\-]"; | ||||
| 
 | ||||
|     // Match digits corresponding to the chosen number base.
 | ||||
|     regexp += QString("[0-%1").arg(std::min(base, 9)); | ||||
|     if (base == 16) { | ||||
|         regexp += "a-fA-F"; | ||||
|     } | ||||
|     regexp += "]"; | ||||
| 
 | ||||
|     // Specify number of digits
 | ||||
|     if (num_digits > 0) { | ||||
|         regexp += QString("{%1}").arg(num_digits); | ||||
|     } else { | ||||
|         regexp += "+"; | ||||
|     } | ||||
| 
 | ||||
|     // Match string
 | ||||
|     QRegExp num_regexp(regexp); | ||||
|     int num_pos = strpos; | ||||
|     QString sub_input = input.mid(strpos, input.length() - strpos - suffix.length()); | ||||
| 
 | ||||
|     if (!num_regexp.exactMatch(sub_input) && num_regexp.matchedLength() == 0) | ||||
|         return QValidator::Invalid; | ||||
| 
 | ||||
|     sub_input = sub_input.left(num_regexp.matchedLength()); | ||||
|     bool ok; | ||||
|     qint64 val = sub_input.toLongLong(&ok, base); | ||||
| 
 | ||||
|     if (!ok) | ||||
|         return QValidator::Invalid; | ||||
| 
 | ||||
|     // Outside boundaries => don't accept
 | ||||
|     if (val < min_value || val > max_value) | ||||
|         return QValidator::Invalid; | ||||
| 
 | ||||
|     // Make sure we are actually at the end of this string...
 | ||||
|     strpos += num_regexp.matchedLength(); | ||||
| 
 | ||||
|     if (!suffix.isEmpty() && input.mid(strpos) != suffix) { | ||||
|         return QValidator::Invalid; | ||||
|     } else { | ||||
|         strpos += suffix.length(); | ||||
|     } | ||||
| 
 | ||||
|     if (strpos != input.length()) | ||||
|         return QValidator::Invalid; | ||||
| 
 | ||||
|     // At this point we can say for sure that the input is fine. Let's fix it up a bit though
 | ||||
|     input.replace(num_pos, sub_input.length(), sub_input.toUpper()); | ||||
| 
 | ||||
|     return QValidator::Acceptable; | ||||
| } | ||||
							
								
								
									
										88
									
								
								src/citra_qt/util/spinbox.hxx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/citra_qt/util/spinbox.hxx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,88 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
| // Copyright 2014 Tony Wasserka
 | ||||
| // All rights reserved.
 | ||||
| //
 | ||||
| // Redistribution and use in source and binary forms, with or without
 | ||||
| // modification, are permitted provided that the following conditions are met:
 | ||||
| //
 | ||||
| //     * Redistributions of source code must retain the above copyright
 | ||||
| //       notice, this list of conditions and the following disclaimer.
 | ||||
| //     * Redistributions in binary form must reproduce the above copyright
 | ||||
| //       notice, this list of conditions and the following disclaimer in the
 | ||||
| //       documentation and/or other materials provided with the distribution.
 | ||||
| //     * Neither the name of the owner nor the names of its contributors may
 | ||||
| //       be used to endorse or promote products derived from this software
 | ||||
| //       without specific prior written permission.
 | ||||
| //
 | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||
| 
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <QAbstractSpinBox> | ||||
| #include <QtGlobal> | ||||
| 
 | ||||
| class QVariant; | ||||
| 
 | ||||
| /**
 | ||||
|  * A custom spin box widget with enhanced functionality over Qt's QSpinBox | ||||
|  */ | ||||
| class CSpinBox : public QAbstractSpinBox { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     CSpinBox(QWidget* parent = nullptr); | ||||
| 
 | ||||
|     void stepBy(int steps) override; | ||||
|     StepEnabled stepEnabled() const override; | ||||
| 
 | ||||
|     void SetValue(qint64 val); | ||||
| 
 | ||||
|     void SetRange(qint64 min, qint64 max); | ||||
| 
 | ||||
|     void SetBase(int base); | ||||
| 
 | ||||
|     void SetPrefix(const QString& prefix); | ||||
|     void SetSuffix(const QString& suffix); | ||||
| 
 | ||||
|     void SetNumDigits(int num_digits); | ||||
| 
 | ||||
|     QValidator::State validate(QString& input, int& pos) const override; | ||||
| 
 | ||||
| signals: | ||||
|     void ValueChanged(qint64 val); | ||||
| 
 | ||||
| private slots: | ||||
|     void OnEditingFinished(); | ||||
| 
 | ||||
| private: | ||||
|     void UpdateText(); | ||||
| 
 | ||||
|     bool HasSign() const; | ||||
| 
 | ||||
|     QString TextFromValue(); | ||||
|     qint64 ValueFromText(); | ||||
| 
 | ||||
|     qint64 min_value, max_value; | ||||
| 
 | ||||
|     qint64 value; | ||||
| 
 | ||||
|     QString prefix, suffix; | ||||
| 
 | ||||
|     int base; | ||||
| 
 | ||||
|     int num_digits; | ||||
| }; | ||||
|  | @ -3,14 +3,15 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU | |||
| 
 | ||||
| set(SRCS | ||||
|             break_points.cpp | ||||
|             console_listener.cpp | ||||
|             emu_window.cpp | ||||
|             extended_trace.cpp | ||||
|             file_search.cpp | ||||
|             file_util.cpp | ||||
|             hash.cpp | ||||
|             key_map.cpp | ||||
|             log_manager.cpp | ||||
|             logging/filter.cpp | ||||
|             logging/text_formatter.cpp | ||||
|             logging/backend.cpp | ||||
|             math_util.cpp | ||||
|             mem_arena.cpp | ||||
|             memory_util.cpp | ||||
|  | @ -32,7 +33,7 @@ set(HEADERS | |||
|             common_funcs.h | ||||
|             common_paths.h | ||||
|             common_types.h | ||||
|             console_listener.h | ||||
|             concurrent_ring_buffer.h | ||||
|             cpu_detect.h | ||||
|             debug_interface.h | ||||
|             emu_window.h | ||||
|  | @ -44,13 +45,18 @@ set(HEADERS | |||
|             key_map.h | ||||
|             linear_disk_cache.h | ||||
|             log.h | ||||
|             log_manager.h | ||||
|             logging/text_formatter.h | ||||
|             logging/filter.h | ||||
|             logging/log.h | ||||
|             logging/backend.h | ||||
|             make_unique.h | ||||
|             math_util.h | ||||
|             mem_arena.h | ||||
|             memory_util.h | ||||
|             msg_handler.h | ||||
|             platform.h | ||||
|             scm_rev.h | ||||
|             scope_exit.h | ||||
|             string_util.h | ||||
|             swap.h | ||||
|             symbols.h | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -142,7 +142,7 @@ public: | |||
| 
 | ||||
|     __forceinline BitField& operator=(T val) | ||||
|     { | ||||
|         storage = (storage & ~GetMask()) | (((StorageType)val << position) & GetMask()); | ||||
|         Assign(val); | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|  | @ -151,6 +151,10 @@ public: | |||
|         return Value(); | ||||
|     } | ||||
| 
 | ||||
|     __forceinline void Assign(const T& value) { | ||||
|         storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask()); | ||||
|     } | ||||
| 
 | ||||
|     __forceinline T Value() const | ||||
|     { | ||||
|         if (std::numeric_limits<T>::is_signed) | ||||
|  | @ -164,6 +168,12 @@ public: | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
 | ||||
|     __forceinline bool ToBool() const | ||||
|     { | ||||
|         return Value() != 0; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     // StorageType is T for non-enum types and the underlying type of T if
 | ||||
|     // T is an enumeration. Note that T is wrapped within an enable_if in the
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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" | ||||
|  | @ -180,7 +180,7 @@ void TMemCheck::Action(DebugInterface *debug_interface, u32 iValue, u32 addr, | |||
|     { | ||||
|         if (Log) | ||||
|         { | ||||
|             INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)", | ||||
|             LOG_DEBUG(Debug_Breakpoint, "CHK %08x (%s) %s%i %0*x at %08x (%s)", | ||||
|                 pc, debug_interface->getDescription(pc).c_str(), | ||||
|                 write ? "Write" : "Read", size*8, size*2, iValue, addr, | ||||
|                 debug_interface->getDescription(addr).c_str() | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  |  | |||
|  | @ -154,7 +154,7 @@ public: | |||
|             Do(foundVersion); | ||||
| 
 | ||||
|         if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { | ||||
|             WARN_LOG(COMMON, "Savestate failure: wrong version %d found for %s", foundVersion, title); | ||||
|             LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title); | ||||
|             SetError(ERROR_FAILURE); | ||||
|             return PointerWrapSection(*this, -1, title); | ||||
|         } | ||||
|  | @ -178,7 +178,14 @@ public: | |||
|         case MODE_READ:    if (memcmp(data, *ptr, size) != 0) return false; break; | ||||
|         case MODE_WRITE: memcpy(*ptr, data, size); break; | ||||
|         case MODE_MEASURE: break;  // MODE_MEASURE - don't need to do anything
 | ||||
|         case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break; | ||||
|         case MODE_VERIFY: | ||||
|             for (int i = 0; i < size; i++) { | ||||
|                 _dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i], | ||||
|                     "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", | ||||
|                     ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], | ||||
|                     (*ptr)[i], (*ptr)[i], &(*ptr)[i]); | ||||
|             } | ||||
|             break; | ||||
|         default: break;  // throw an error?
 | ||||
|         } | ||||
|         (*ptr) += size; | ||||
|  | @ -191,7 +198,14 @@ public: | |||
|         case MODE_READ:    memcpy(data, *ptr, size); break; | ||||
|         case MODE_WRITE: memcpy(*ptr, data, size); break; | ||||
|         case MODE_MEASURE: break;  // MODE_MEASURE - don't need to do anything
 | ||||
|         case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break; | ||||
|         case MODE_VERIFY: | ||||
|             for (int i = 0; i < size; i++) { | ||||
|                 _dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i], | ||||
|                     "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", | ||||
|                     ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], | ||||
|                     (*ptr)[i], (*ptr)[i], &(*ptr)[i]); | ||||
|             } | ||||
|             break; | ||||
|         default: break;  // throw an error?
 | ||||
|         } | ||||
|         (*ptr) += size; | ||||
|  | @ -204,11 +218,11 @@ public: | |||
|         { | ||||
|             for (auto it = x.begin(), end = x.end(); it != end; ++it) | ||||
|             { | ||||
|                 if (it->second != NULL) | ||||
|                 if (it->second != nullptr) | ||||
|                     delete it->second; | ||||
|             } | ||||
|         } | ||||
|         T *dv = NULL; | ||||
|         T *dv = nullptr; | ||||
|         DoMap(x, dv); | ||||
|     } | ||||
| 
 | ||||
|  | @ -264,11 +278,11 @@ public: | |||
|         { | ||||
|             for (auto it = x.begin(), end = x.end(); it != end; ++it) | ||||
|             { | ||||
|                 if (it->second != NULL) | ||||
|                 if (it->second != nullptr) | ||||
|                     delete it->second; | ||||
|             } | ||||
|         } | ||||
|         T *dv = NULL; | ||||
|         T *dv = nullptr; | ||||
|         DoMultimap(x, dv); | ||||
|     } | ||||
| 
 | ||||
|  | @ -320,7 +334,7 @@ public: | |||
|     template<class T> | ||||
|     void Do(std::vector<T *> &x) | ||||
|     { | ||||
|         T *dv = NULL; | ||||
|         T *dv = nullptr; | ||||
|         DoVector(x, dv); | ||||
|     } | ||||
| 
 | ||||
|  | @ -369,7 +383,7 @@ public: | |||
|     template<class T> | ||||
|     void Do(std::deque<T *> &x) | ||||
|     { | ||||
|         T *dv = NULL; | ||||
|         T *dv = nullptr; | ||||
|         DoDeque(x, dv); | ||||
|     } | ||||
| 
 | ||||
|  | @ -395,7 +409,7 @@ public: | |||
|     template<class T> | ||||
|     void Do(std::list<T *> &x) | ||||
|     { | ||||
|         T *dv = NULL; | ||||
|         T *dv = nullptr; | ||||
|         Do(x, dv); | ||||
|     } | ||||
| 
 | ||||
|  | @ -433,7 +447,7 @@ public: | |||
|         { | ||||
|             for (auto it = x.begin(), end = x.end(); it != end; ++it) | ||||
|             { | ||||
|                 if (*it != NULL) | ||||
|                 if (*it != nullptr) | ||||
|                     delete *it; | ||||
|             } | ||||
|         } | ||||
|  | @ -476,7 +490,7 @@ public: | |||
|             break; | ||||
| 
 | ||||
|         default: | ||||
|             ERROR_LOG(COMMON, "Savestate error: invalid mode %d.", mode); | ||||
|             LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -490,7 +504,12 @@ public: | |||
|         case MODE_READ:        x = (char*)*ptr; break; | ||||
|         case MODE_WRITE:    memcpy(*ptr, x.c_str(), stringLen); break; | ||||
|         case MODE_MEASURE: break; | ||||
|         case MODE_VERIFY: _dbg_assert_msg_(COMMON, !strcmp(x.c_str(), (char*)*ptr), "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", x.c_str(), (char*)*ptr, ptr); break; | ||||
|         case MODE_VERIFY: | ||||
|             _dbg_assert_msg_(Common, | ||||
|                 !strcmp(x.c_str(), (char*)*ptr), | ||||
|                 "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", | ||||
|                 x.c_str(), (char*)*ptr, ptr); | ||||
|             break; | ||||
|         } | ||||
|         (*ptr) += stringLen; | ||||
|     } | ||||
|  | @ -504,7 +523,11 @@ public: | |||
|         case MODE_READ:        x = (wchar_t*)*ptr; break; | ||||
|         case MODE_WRITE:    memcpy(*ptr, x.c_str(), stringLen); break; | ||||
|         case MODE_MEASURE: break; | ||||
|         case MODE_VERIFY: _dbg_assert_msg_(COMMON, x == (wchar_t*)*ptr, "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), (wchar_t*)*ptr, ptr); break; | ||||
|         case MODE_VERIFY: | ||||
|             _dbg_assert_msg_(Common, x == (wchar_t*)*ptr, | ||||
|                 "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", | ||||
|                 x.c_str(), (wchar_t*)*ptr, ptr); | ||||
|             break; | ||||
|         } | ||||
|         (*ptr) += stringLen; | ||||
|     } | ||||
|  | @ -518,7 +541,7 @@ public: | |||
|     void DoClass(T *&x) { | ||||
|         if (mode == MODE_READ) | ||||
|         { | ||||
|             if (x != NULL) | ||||
|             if (x != nullptr) | ||||
|                 delete x; | ||||
|             x = new T(); | ||||
|         } | ||||
|  | @ -567,7 +590,7 @@ public: | |||
|                 { | ||||
|                     if (mode == MODE_READ) | ||||
|                     { | ||||
|                         cur->next = 0; | ||||
|                         cur->next = nullptr; | ||||
|                         list_cur = cur; | ||||
|                         if (prev) | ||||
|                             prev->next = cur; | ||||
|  | @ -586,13 +609,13 @@ public: | |||
|                 if (mode == MODE_READ) | ||||
|                 { | ||||
|                     if (prev) | ||||
|                         prev->next = 0; | ||||
|                         prev->next = nullptr; | ||||
|                     if (list_end) | ||||
|                         *list_end = prev; | ||||
|                     if (list_cur) | ||||
|                     { | ||||
|                         if (list_start == list_cur) | ||||
|                             list_start = 0; | ||||
|                             list_start = nullptr; | ||||
|                         do | ||||
|                         { | ||||
|                             LinkedListItem<T>* next = list_cur->next; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  |  | |||
|  | @ -1,9 +1,12 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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_types.h" | ||||
| #include <cstdlib> | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #define SLEEP(x) Sleep(x) | ||||
| #else | ||||
|  | @ -73,6 +76,8 @@ inline u64 _rotr64(u64 x, unsigned int shift){ | |||
| } | ||||
| 
 | ||||
| #else // _MSC_VER
 | ||||
| #include <locale.h> | ||||
| 
 | ||||
| // Function Cross-Compatibility
 | ||||
|     #define strcasecmp _stricmp | ||||
|     #define strncasecmp _strnicmp | ||||
|  | @ -106,7 +111,7 @@ inline u64 _rotr64(u64 x, unsigned int shift){ | |||
|             // Restore the global locale
 | ||||
|             _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); | ||||
|         } | ||||
|         else if(new_locale != NULL) | ||||
|         else if(new_locale != nullptr) | ||||
|         { | ||||
|             // Configure the thread to set the locale only for this thread
 | ||||
|             _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  | @ -29,19 +29,6 @@ | |||
|     #endif | ||||
| #endif | ||||
| 
 | ||||
| // Shared data dirs (Sys and shared User for linux)
 | ||||
| #ifdef _WIN32 | ||||
|     #define SYSDATA_DIR "sys" | ||||
| #else | ||||
|     #ifdef DATA_DIR | ||||
|         #define SYSDATA_DIR DATA_DIR "sys" | ||||
|         #define SHARED_USER_DIR  DATA_DIR USERDATA_DIR DIR_SEP | ||||
|     #else | ||||
|         #define SYSDATA_DIR "sys" | ||||
|         #define SHARED_USER_DIR  ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP | ||||
|     #endif | ||||
| #endif | ||||
| 
 | ||||
| // Dirs in both User and Sys
 | ||||
| #define EUR_DIR "EUR" | ||||
| #define USA_DIR "USA" | ||||
|  | @ -53,6 +40,9 @@ | |||
| #define MAPS_DIR          "maps" | ||||
| #define CACHE_DIR         "cache" | ||||
| #define SDMC_DIR          "sdmc" | ||||
| #define SAVEDATA_DIR      "savedata" | ||||
| #define SYSDATA_DIR       "sysdata" | ||||
| #define SYSSAVEDATA_DIR   "syssavedata" | ||||
| #define SHADERCACHE_DIR   "shader_cache" | ||||
| #define STATESAVES_DIR    "state_saves" | ||||
| #define SCREENSHOTS_DIR   "screenShots" | ||||
|  | @ -70,6 +60,9 @@ | |||
| #define DEBUGGER_CONFIG   "debugger.ini" | ||||
| #define LOGGER_CONFIG     "logger.ini" | ||||
| 
 | ||||
| // Sys files
 | ||||
| #define SHARED_FONT       "shared_font.bin" | ||||
| 
 | ||||
| // Files in the directory returned by GetUserPath(D_LOGS_IDX)
 | ||||
| #define MAIN_LOG "emu.log" | ||||
| 
 | ||||
|  |  | |||
|  | @ -41,8 +41,6 @@ typedef std::int64_t s64; ///< 64-bit signed int | |||
| typedef float   f32; ///< 32-bit floating point
 | ||||
| typedef double  f64; ///< 64-bit floating point
 | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| 
 | ||||
| /// Union for fast 16-bit type casting
 | ||||
| union t16 { | ||||
|     u8  _u8[2];             ///< 8-bit unsigned char(s)
 | ||||
|  |  | |||
							
								
								
									
										164
									
								
								src/common/concurrent_ring_buffer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								src/common/concurrent_ring_buffer.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,164 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include <condition_variable> | ||||
| #include <cstdint> | ||||
| #include <mutex> | ||||
| #include <thread> | ||||
| 
 | ||||
| #include "common/common.h" // for NonCopyable | ||||
| #include "common/log.h" // for _dbg_assert_ | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| /**
 | ||||
|  * A MPMC (Multiple-Producer Multiple-Consumer) concurrent ring buffer. This data structure permits | ||||
|  * multiple threads to push and pop from a queue of bounded size. | ||||
|  */ | ||||
| template <typename T, size_t ArraySize> | ||||
| class ConcurrentRingBuffer : private NonCopyable { | ||||
| public: | ||||
|     /// Value returned by the popping functions when the queue has been closed.
 | ||||
|     static const size_t QUEUE_CLOSED = -1; | ||||
| 
 | ||||
|     ConcurrentRingBuffer() {} | ||||
| 
 | ||||
|     ~ConcurrentRingBuffer() { | ||||
|         // If for whatever reason the queue wasn't completely drained, destroy the left over items.
 | ||||
|         for (size_t i = reader_index, end = writer_index; i != end; i = (i + 1) % ArraySize) { | ||||
|             Data()[i].~T(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Pushes a value to the queue. If the queue is full, this method will block. Does nothing if | ||||
|      * the queue is closed. | ||||
|      */ | ||||
|     void Push(T val) { | ||||
|         std::unique_lock<std::mutex> lock(mutex); | ||||
|         if (closed) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // If the buffer is full, wait
 | ||||
|         writer.wait(lock, [&]{ | ||||
|             return (writer_index + 1) % ArraySize != reader_index; | ||||
|         }); | ||||
| 
 | ||||
|         T* item = &Data()[writer_index]; | ||||
|         new (item) T(std::move(val)); | ||||
| 
 | ||||
|         writer_index = (writer_index + 1) % ArraySize; | ||||
| 
 | ||||
|         // Wake up waiting readers
 | ||||
|         lock.unlock(); | ||||
|         reader.notify_one(); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Pops up to `dest_len` items from the queue, storing them in `dest`. This function will not | ||||
|      * block, and might return 0 values if there are no elements in the queue when it is called. | ||||
|      * | ||||
|      * @return The number of elements stored in `dest`. If the queue has been closed, returns | ||||
|      *          `QUEUE_CLOSED`. | ||||
|      */ | ||||
|     size_t Pop(T* dest, size_t dest_len) { | ||||
|         std::unique_lock<std::mutex> lock(mutex); | ||||
|         if (closed && !CanRead()) { | ||||
|             return QUEUE_CLOSED; | ||||
|         } | ||||
|         return PopInternal(dest, dest_len); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Pops up to `dest_len` items from the queue, storing them in `dest`. This function will block | ||||
|      * if there are no elements in the queue when it is called. | ||||
|      * | ||||
|      * @return The number of elements stored in `dest`. If the queue has been closed, returns | ||||
|      *         `QUEUE_CLOSED`. | ||||
|      */ | ||||
|     size_t BlockingPop(T* dest, size_t dest_len) { | ||||
|         std::unique_lock<std::mutex> lock(mutex); | ||||
|         if (closed && !CanRead()) { | ||||
|             return QUEUE_CLOSED; | ||||
|         } | ||||
| 
 | ||||
|         while (!CanRead()) { | ||||
|             reader.wait(lock); | ||||
|             if (closed && !CanRead()) { | ||||
|                 return QUEUE_CLOSED; | ||||
|             } | ||||
|         } | ||||
|         _dbg_assert_(Common, CanRead()); | ||||
|         return PopInternal(dest, dest_len); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Closes the queue. After calling this method, `Push` operations won't have any effect, and | ||||
|      * `PopMany` and `PopManyBlock` will start returning `QUEUE_CLOSED`. This is intended to allow | ||||
|      * a graceful shutdown of all consumers. | ||||
|      */ | ||||
|     void Close() { | ||||
|         std::unique_lock<std::mutex> lock(mutex); | ||||
|         closed = true; | ||||
|         // We need to wake up any reader that are waiting for an item that will never come.
 | ||||
|         lock.unlock(); | ||||
|         reader.notify_all(); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns true if `Close()` has been called.
 | ||||
|     bool IsClosed() const { | ||||
|         return closed; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     size_t PopInternal(T* dest, size_t dest_len) { | ||||
|         size_t output_count = 0; | ||||
|         while (output_count < dest_len && CanRead()) { | ||||
|             _dbg_assert_(Common, CanRead()); | ||||
| 
 | ||||
|             T* item = &Data()[reader_index]; | ||||
|             T out_val = std::move(*item); | ||||
|             item->~T(); | ||||
| 
 | ||||
|             size_t prev_index = (reader_index + ArraySize - 1) % ArraySize; | ||||
|             reader_index = (reader_index + 1) % ArraySize; | ||||
|             if (writer_index == prev_index) { | ||||
|                 writer.notify_one(); | ||||
|             } | ||||
|             dest[output_count++] = std::move(out_val); | ||||
|         } | ||||
|         return output_count; | ||||
|     } | ||||
| 
 | ||||
|     bool CanRead() const { | ||||
|         return reader_index != writer_index; | ||||
|     } | ||||
| 
 | ||||
|     T* Data() { | ||||
|         return static_cast<T*>(static_cast<void*>(&storage)); | ||||
|     } | ||||
| 
 | ||||
|     /// Storage for entries
 | ||||
|     typename std::aligned_storage<ArraySize * sizeof(T), | ||||
|                                   std::alignment_of<T>::value>::type storage; | ||||
| 
 | ||||
|     /// Data is valid in the half-open interval [reader, writer). If they are `QUEUE_CLOSED` then the
 | ||||
|     /// queue has been closed.
 | ||||
|     size_t writer_index = 0, reader_index = 0; | ||||
|     // True if the queue has been closed.
 | ||||
|     bool closed = false; | ||||
| 
 | ||||
|     /// Mutex that protects the entire data structure.
 | ||||
|     std::mutex mutex; | ||||
|     /// Signaling wakes up reader which is waiting for storage to be non-empty.
 | ||||
|     std::condition_variable reader; | ||||
|     /// Signaling wakes up writer which is waiting for storage to be non-full.
 | ||||
|     std::condition_variable writer; | ||||
| }; | ||||
| 
 | ||||
| } // namespace
 | ||||
|  | @ -1,319 +0,0 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <windows.h> | ||||
| #include <array> | ||||
| #endif | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/log_manager.h" // Common
 | ||||
| #include "common/console_listener.h" // Common
 | ||||
| 
 | ||||
| ConsoleListener::ConsoleListener() | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|     hConsole = NULL; | ||||
|     bUseColor = true; | ||||
| #else | ||||
|     bUseColor = isatty(fileno(stdout)); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| ConsoleListener::~ConsoleListener() | ||||
| { | ||||
|     Close(); | ||||
| } | ||||
| 
 | ||||
| // 100, 100, "Dolphin Log Console"
 | ||||
| // Open console window - width and height is the size of console window
 | ||||
| // Name is the window title
 | ||||
| void ConsoleListener::Open(bool Hidden, int Width, int Height, const char *Title) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|     if (!GetConsoleWindow()) | ||||
|     { | ||||
|         // Open the console window and create the window handle for GetStdHandle()
 | ||||
|         AllocConsole(); | ||||
|         // Hide
 | ||||
|         if (Hidden) ShowWindow(GetConsoleWindow(), SW_HIDE); | ||||
|         // Save the window handle that AllocConsole() created
 | ||||
|         hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|         // Set the console window title
 | ||||
|         SetConsoleTitle(Common::UTF8ToTStr(Title).c_str()); | ||||
|         // Set letter space
 | ||||
|         LetterSpace(80, 4000); | ||||
|         //MoveWindow(GetConsoleWindow(), 200,200, 800,800, true);
 | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void ConsoleListener::UpdateHandle() | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|     hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| // Close the console window and close the eventual file handle
 | ||||
| void ConsoleListener::Close() | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|     if (hConsole == NULL) | ||||
|         return; | ||||
|     FreeConsole(); | ||||
|     hConsole = NULL; | ||||
| #else | ||||
|     fflush(NULL); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| bool ConsoleListener::IsOpen() | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|     return (hConsole != NULL); | ||||
| #else | ||||
|     return true; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|   LetterSpace: SetConsoleScreenBufferSize and SetConsoleWindowInfo are | ||||
|     dependent on each other, that's the reason for the additional checks. | ||||
| */ | ||||
| void ConsoleListener::BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|     BOOL SB, SW; | ||||
|     if (BufferFirst) | ||||
|     { | ||||
|         // Change screen buffer size
 | ||||
|         COORD Co = {BufferWidth, BufferHeight}; | ||||
|         SB = SetConsoleScreenBufferSize(hConsole, Co); | ||||
|         // Change the screen buffer window size
 | ||||
|         SMALL_RECT coo = {0,0,ScreenWidth, ScreenHeight}; // top, left, right, bottom
 | ||||
|         SW = SetConsoleWindowInfo(hConsole, TRUE, &coo); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Change the screen buffer window size
 | ||||
|         SMALL_RECT coo = {0,0, ScreenWidth, ScreenHeight}; // top, left, right, bottom
 | ||||
|         SW = SetConsoleWindowInfo(hConsole, TRUE, &coo); | ||||
|         // Change screen buffer size
 | ||||
|         COORD Co = {BufferWidth, BufferHeight}; | ||||
|         SB = SetConsoleScreenBufferSize(hConsole, Co); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
| void ConsoleListener::LetterSpace(int Width, int Height) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|     // Get console info
 | ||||
|     CONSOLE_SCREEN_BUFFER_INFO ConInfo; | ||||
|     GetConsoleScreenBufferInfo(hConsole, &ConInfo); | ||||
| 
 | ||||
|     //
 | ||||
|     int OldBufferWidth = ConInfo.dwSize.X; | ||||
|     int OldBufferHeight = ConInfo.dwSize.Y; | ||||
|     int OldScreenWidth = (ConInfo.srWindow.Right - ConInfo.srWindow.Left); | ||||
|     int OldScreenHeight = (ConInfo.srWindow.Bottom - ConInfo.srWindow.Top); | ||||
|     //
 | ||||
|     int NewBufferWidth = Width; | ||||
|     int NewBufferHeight = Height; | ||||
|     int NewScreenWidth = NewBufferWidth - 1; | ||||
|     int NewScreenHeight = OldScreenHeight; | ||||
| 
 | ||||
|     // Width
 | ||||
|     BufferWidthHeight(NewBufferWidth, OldBufferHeight, NewScreenWidth, OldScreenHeight, (NewBufferWidth > OldScreenWidth-1)); | ||||
|     // Height
 | ||||
|     BufferWidthHeight(NewBufferWidth, NewBufferHeight, NewScreenWidth, NewScreenHeight, (NewBufferHeight > OldScreenHeight-1)); | ||||
| 
 | ||||
|     // Resize the window too
 | ||||
|     //MoveWindow(GetConsoleWindow(), 200,200, (Width*8 + 50),(NewScreenHeight*12 + 200), true);
 | ||||
| #endif | ||||
| } | ||||
| #ifdef _WIN32 | ||||
| COORD ConsoleListener::GetCoordinates(int BytesRead, int BufferWidth) | ||||
| { | ||||
|     COORD Ret = {0, 0}; | ||||
|     // Full rows
 | ||||
|     int Step = (int)floor((float)BytesRead / (float)BufferWidth); | ||||
|     Ret.Y += Step; | ||||
|     // Partial row
 | ||||
|     Ret.X = BytesRead - (BufferWidth * Step); | ||||
|     return Ret; | ||||
| } | ||||
| #endif | ||||
| void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool Resize) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
|     // Check size
 | ||||
|     if (Width < 8 || Height < 12) return; | ||||
| 
 | ||||
|     bool DBef = true; | ||||
|     bool DAft = true; | ||||
|     std::string SLog = ""; | ||||
| 
 | ||||
|     const HWND hWnd = GetConsoleWindow(); | ||||
|     const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
| 
 | ||||
|     // Get console info
 | ||||
|     CONSOLE_SCREEN_BUFFER_INFO ConInfo; | ||||
|     GetConsoleScreenBufferInfo(hConsole, &ConInfo); | ||||
|     DWORD BufferSize = ConInfo.dwSize.X * ConInfo.dwSize.Y; | ||||
| 
 | ||||
|     // ---------------------------------------------------------------------
 | ||||
|     //  Save the current text
 | ||||
|     // ------------------------
 | ||||
|     DWORD cCharsRead = 0; | ||||
|     COORD coordScreen = { 0, 0 }; | ||||
| 
 | ||||
|     static const int MAX_BYTES = 1024 * 16; | ||||
| 
 | ||||
|     std::vector<std::array<TCHAR, MAX_BYTES>> Str; | ||||
|     std::vector<std::array<WORD, MAX_BYTES>> Attr; | ||||
| 
 | ||||
|     // ReadConsoleOutputAttribute seems to have a limit at this level
 | ||||
|     static const int ReadBufferSize = MAX_BYTES - 32; | ||||
| 
 | ||||
|     DWORD cAttrRead = ReadBufferSize; | ||||
|     DWORD BytesRead = 0; | ||||
|     while (BytesRead < BufferSize) | ||||
|     { | ||||
|         Str.resize(Str.size() + 1); | ||||
|         if (!ReadConsoleOutputCharacter(hConsole, Str.back().data(), ReadBufferSize, coordScreen, &cCharsRead)) | ||||
|             SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error"); | ||||
| 
 | ||||
|         Attr.resize(Attr.size() + 1); | ||||
|         if (!ReadConsoleOutputAttribute(hConsole, Attr.back().data(), ReadBufferSize, coordScreen, &cAttrRead)) | ||||
|             SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error"); | ||||
| 
 | ||||
|         // Break on error
 | ||||
|         if (cAttrRead == 0) break; | ||||
|         BytesRead += cAttrRead; | ||||
|         coordScreen = GetCoordinates(BytesRead, ConInfo.dwSize.X); | ||||
|     } | ||||
|     // Letter space
 | ||||
|     int LWidth = (int)(floor((float)Width / 8.0f) - 1.0f); | ||||
|     int LHeight = (int)(floor((float)Height / 12.0f) - 1.0f); | ||||
|     int LBufWidth = LWidth + 1; | ||||
|     int LBufHeight = (int)floor((float)BufferSize / (float)LBufWidth); | ||||
|     // Change screen buffer size
 | ||||
|     LetterSpace(LBufWidth, LBufHeight); | ||||
| 
 | ||||
| 
 | ||||
|     ClearScreen(true); | ||||
|     coordScreen.Y = 0; | ||||
|     coordScreen.X = 0; | ||||
|     DWORD cCharsWritten = 0; | ||||
| 
 | ||||
|     int BytesWritten = 0; | ||||
|     DWORD cAttrWritten = 0; | ||||
|     for (size_t i = 0; i < Attr.size(); i++) | ||||
|     { | ||||
|         if (!WriteConsoleOutputCharacter(hConsole, Str[i].data(), ReadBufferSize, coordScreen, &cCharsWritten)) | ||||
|             SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error"); | ||||
|         if (!WriteConsoleOutputAttribute(hConsole, Attr[i].data(), ReadBufferSize, coordScreen, &cAttrWritten)) | ||||
|             SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error"); | ||||
| 
 | ||||
|         BytesWritten += cAttrWritten; | ||||
|         coordScreen = GetCoordinates(BytesWritten, LBufWidth); | ||||
|     } | ||||
| 
 | ||||
|     const int OldCursor = ConInfo.dwCursorPosition.Y * ConInfo.dwSize.X + ConInfo.dwCursorPosition.X; | ||||
|     COORD Coo = GetCoordinates(OldCursor, LBufWidth); | ||||
|     SetConsoleCursorPosition(hConsole, Coo); | ||||
| 
 | ||||
|     if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str()); | ||||
| 
 | ||||
|     // Resize the window too
 | ||||
|     if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|     WORD Color; | ||||
| 
 | ||||
|     switch (Level) | ||||
|     { | ||||
|     case OS_LEVEL: // light yellow
 | ||||
|         Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; | ||||
|         break; | ||||
|     case NOTICE_LEVEL: // light green
 | ||||
|         Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY; | ||||
|         break; | ||||
|     case ERROR_LEVEL: // light red
 | ||||
|         Color = FOREGROUND_RED | FOREGROUND_INTENSITY; | ||||
|         break; | ||||
|     case WARNING_LEVEL: // light purple
 | ||||
|         Color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; | ||||
|         break; | ||||
|     case INFO_LEVEL: // cyan
 | ||||
|         Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; | ||||
|         break; | ||||
|     case DEBUG_LEVEL: // gray
 | ||||
|         Color = FOREGROUND_INTENSITY; | ||||
|         break; | ||||
|     default: // off-white
 | ||||
|         Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; | ||||
|         break; | ||||
|     } | ||||
|     SetConsoleTextAttribute(hConsole, Color); | ||||
|     printf(Text); | ||||
| #else | ||||
|     char ColorAttr[16] = ""; | ||||
|     char ResetAttr[16] = ""; | ||||
| 
 | ||||
|     if (bUseColor) | ||||
|     { | ||||
|         strcpy(ResetAttr, "\033[0m"); | ||||
|         switch (Level) | ||||
|         { | ||||
|         case NOTICE_LEVEL: // light green
 | ||||
|             strcpy(ColorAttr, "\033[92m"); | ||||
|             break; | ||||
|         case ERROR_LEVEL: // light red
 | ||||
|             strcpy(ColorAttr, "\033[91m"); | ||||
|             break; | ||||
|         case WARNING_LEVEL: // light yellow
 | ||||
|             strcpy(ColorAttr, "\033[93m"); | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr); | ||||
| #endif | ||||
| } | ||||
| // Clear console screen
 | ||||
| void ConsoleListener::ClearScreen(bool Cursor) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|     COORD coordScreen = { 0, 0 }; | ||||
|     DWORD cCharsWritten; | ||||
|     CONSOLE_SCREEN_BUFFER_INFO csbi; | ||||
|     DWORD dwConSize; | ||||
| 
 | ||||
|     HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
| 
 | ||||
|     GetConsoleScreenBufferInfo(hConsole, &csbi); | ||||
|     dwConSize = csbi.dwSize.X * csbi.dwSize.Y; | ||||
|     // Write space to the entire console
 | ||||
|     FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten); | ||||
|     GetConsoleScreenBufferInfo(hConsole, &csbi); | ||||
|     FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten); | ||||
|     // Reset cursor
 | ||||
|     if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1,38 +0,0 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/log_manager.h" | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <windows.h> | ||||
| #endif | ||||
| 
 | ||||
| class ConsoleListener : public LogListener | ||||
| { | ||||
| public: | ||||
|     ConsoleListener(); | ||||
|     ~ConsoleListener(); | ||||
| 
 | ||||
|     void Open(bool Hidden = false, int Width = 100, int Height = 100, const char * Name = "Console"); | ||||
|     void UpdateHandle(); | ||||
|     void Close(); | ||||
|     bool IsOpen(); | ||||
|     void LetterSpace(int Width, int Height); | ||||
|     void BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst); | ||||
|     void PixelSpace(int Left, int Top, int Width, int Height, bool); | ||||
| #ifdef _WIN32 | ||||
|     COORD GetCoordinates(int BytesRead, int BufferWidth); | ||||
| #endif | ||||
|     void Log(LogTypes::LOG_LEVELS, const char *Text) override; | ||||
|     void ClearScreen(bool Cursor = true); | ||||
| 
 | ||||
| private: | ||||
| #ifdef _WIN32 | ||||
|     HWND GetHwnd(void); | ||||
|     HANDLE hConsole; | ||||
| #endif | ||||
|     bool bUseColor; | ||||
| }; | ||||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "emu_window.h" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -82,7 +82,7 @@ static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath ) | |||
|     } | ||||
| 
 | ||||
|     // Add user defined path
 | ||||
|     if ( lpszIniPath != NULL ) | ||||
|     if ( lpszIniPath != nullptr ) | ||||
|         if ( lpszIniPath[0] != '\0' ) | ||||
|         { | ||||
|             strcat( lpszSymbolPath, ";" ); | ||||
|  | @ -138,7 +138,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L | |||
|     DWORD             dwSymSize = 10000; | ||||
|     TCHAR             lpszUnDSymbol[BUFFERSIZE]=_T("?"); | ||||
|     CHAR              lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; | ||||
|     LPTSTR            lpszParamSep = NULL; | ||||
|     LPTSTR            lpszParamSep = nullptr; | ||||
|     LPTSTR            lpszParsed = lpszUnDSymbol; | ||||
|     PIMAGEHLP_SYMBOL  pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize ); | ||||
| 
 | ||||
|  | @ -187,13 +187,13 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L | |||
| 
 | ||||
|         // Let's go through the stack, and modify the function prototype, and insert the actual
 | ||||
|         // parameter values from the stack
 | ||||
|         if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL) | ||||
|         if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == nullptr && _tcsstr( lpszUnDSymbol, _T("()") ) == nullptr) | ||||
|         { | ||||
|             ULONG index = 0; | ||||
|             for( ; ; index++ ) | ||||
|             { | ||||
|                 lpszParamSep = _tcschr( lpszParsed, _T(',') ); | ||||
|                 if ( lpszParamSep == NULL ) | ||||
|                 if ( lpszParamSep == nullptr ) | ||||
|                     break; | ||||
| 
 | ||||
|                 *lpszParamSep = _T('\0'); | ||||
|  | @ -205,7 +205,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L | |||
|             } | ||||
| 
 | ||||
|             lpszParamSep = _tcschr( lpszParsed, _T(')') ); | ||||
|             if ( lpszParamSep != NULL ) | ||||
|             if ( lpszParamSep != nullptr ) | ||||
|             { | ||||
|                 *lpszParamSep = _T('\0'); | ||||
| 
 | ||||
|  | @ -248,7 +248,7 @@ static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo ) | |||
|         PCSTR2LPTSTR( lineInfo.FileName, lpszFileName ); | ||||
|         TCHAR fname[_MAX_FNAME]; | ||||
|         TCHAR ext[_MAX_EXT]; | ||||
|         _tsplitpath(lpszFileName, NULL, NULL, fname, ext); | ||||
|         _tsplitpath(lpszFileName, nullptr, nullptr, fname, ext); | ||||
|         _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber ); | ||||
|         ret = TRUE; | ||||
|     } | ||||
|  | @ -332,11 +332,11 @@ void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file ) | |||
|                 hProcess, | ||||
|                 hThread, | ||||
|                 &callStack, | ||||
|                 NULL, | ||||
|                 NULL, | ||||
|                 nullptr, | ||||
|                 nullptr, | ||||
|                 SymFunctionTableAccess, | ||||
|                 SymGetModuleBase, | ||||
|                 NULL); | ||||
|                 nullptr); | ||||
| 
 | ||||
|             if ( index == 0 ) | ||||
|                 continue; | ||||
|  | @ -389,11 +389,11 @@ void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip, | |||
|                 hProcess, | ||||
|                 hThread, | ||||
|                 &callStack, | ||||
|                 NULL, | ||||
|                 NULL, | ||||
|                 nullptr, | ||||
|                 nullptr, | ||||
|                 SymFunctionTableAccess, | ||||
|                 SymGetModuleBase, | ||||
|                 NULL); | ||||
|                 nullptr); | ||||
| 
 | ||||
|             if ( index == 0 ) | ||||
|                 continue; | ||||
|  |  | |||
|  | @ -57,7 +57,7 @@ public: | |||
|         // advance the read pointer
 | ||||
|         m_read_ptr = m_read_ptr->next; | ||||
|         // set the next element to NULL to stop the recursive deletion
 | ||||
|         tmpptr->next = NULL; | ||||
|         tmpptr->next = nullptr; | ||||
|         delete tmpptr;    // this also deletes the element
 | ||||
|     } | ||||
| 
 | ||||
|  | @ -86,7 +86,7 @@ private: | |||
|     class ElementPtr | ||||
|     { | ||||
|     public: | ||||
|         ElementPtr() : current(NULL), next(NULL) {} | ||||
|         ElementPtr() : current(nullptr), next(nullptr) {} | ||||
| 
 | ||||
|         ~ElementPtr() | ||||
|         { | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -88,7 +88,7 @@ bool IsDirectory(const std::string &filename) | |||
| #endif | ||||
| 
 | ||||
|     if (result < 0) { | ||||
|         WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s", | ||||
|         LOG_WARNING(Common_Filesystem, "stat failed on %s: %s", | ||||
|                  filename.c_str(), GetLastErrorMsg()); | ||||
|         return false; | ||||
|     } | ||||
|  | @ -100,33 +100,33 @@ bool IsDirectory(const std::string &filename) | |||
| // Doesn't supports deleting a directory
 | ||||
| bool Delete(const std::string &filename) | ||||
| { | ||||
|     INFO_LOG(COMMON, "Delete: file %s", filename.c_str()); | ||||
|     LOG_INFO(Common_Filesystem, "file %s", filename.c_str()); | ||||
| 
 | ||||
|     // Return true because we care about the file no
 | ||||
|     // being there, not the actual delete.
 | ||||
|     if (!Exists(filename)) | ||||
|     { | ||||
|         WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str()); | ||||
|         LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str()); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     // We can't delete a directory
 | ||||
|     if (IsDirectory(filename)) | ||||
|     { | ||||
|         WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str()); | ||||
|         LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str()); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
|     if (!DeleteFile(Common::UTF8ToTStr(filename).c_str())) | ||||
|     { | ||||
|         WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s", | ||||
|                  filename.c_str(), GetLastErrorMsg()); | ||||
|         return false; | ||||
|     } | ||||
| #else | ||||
|     if (unlink(filename.c_str()) == -1) { | ||||
|         WARN_LOG(COMMON, "Delete: unlink failed on %s: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s", | ||||
|                  filename.c_str(), GetLastErrorMsg()); | ||||
|         return false; | ||||
|     } | ||||
|  | @ -138,17 +138,17 @@ bool Delete(const std::string &filename) | |||
| // Returns true if successful, or path already exists.
 | ||||
| bool CreateDir(const std::string &path) | ||||
| { | ||||
|     INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str()); | ||||
|     LOG_TRACE(Common_Filesystem, "directory %s", path.c_str()); | ||||
| #ifdef _WIN32 | ||||
|     if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), NULL)) | ||||
|     if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr)) | ||||
|         return true; | ||||
|     DWORD error = GetLastError(); | ||||
|     if (error == ERROR_ALREADY_EXISTS) | ||||
|     { | ||||
|         WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str()); | ||||
|         LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str()); | ||||
|         return true; | ||||
|     } | ||||
|     ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error); | ||||
|     LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error); | ||||
|     return false; | ||||
| #else | ||||
|     if (mkdir(path.c_str(), 0755) == 0) | ||||
|  | @ -158,11 +158,11 @@ bool CreateDir(const std::string &path) | |||
| 
 | ||||
|     if (err == EEXIST) | ||||
|     { | ||||
|         WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str()); | ||||
|         LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str()); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err)); | ||||
|     LOG_ERROR(Common_Filesystem, "mkdir failed on %s: %s", path.c_str(), strerror(err)); | ||||
|     return false; | ||||
| #endif | ||||
| } | ||||
|  | @ -171,11 +171,11 @@ bool CreateDir(const std::string &path) | |||
| bool CreateFullPath(const std::string &fullPath) | ||||
| { | ||||
|     int panicCounter = 100; | ||||
|     INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str()); | ||||
|     LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str()); | ||||
| 
 | ||||
|     if (FileUtil::Exists(fullPath)) | ||||
|     { | ||||
|         INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str()); | ||||
|         LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str()); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|  | @ -192,7 +192,7 @@ bool CreateFullPath(const std::string &fullPath) | |||
|         // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
 | ||||
|         std::string const subPath(fullPath.substr(0, position + 1)); | ||||
|         if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { | ||||
|             ERROR_LOG(COMMON, "CreateFullPath: directory creation failed"); | ||||
|             LOG_ERROR(Common, "CreateFullPath: directory creation failed"); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|  | @ -200,7 +200,7 @@ bool CreateFullPath(const std::string &fullPath) | |||
|         panicCounter--; | ||||
|         if (panicCounter <= 0) | ||||
|         { | ||||
|             ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep"); | ||||
|             LOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); | ||||
|             return false; | ||||
|         } | ||||
|         position++; | ||||
|  | @ -211,12 +211,12 @@ bool CreateFullPath(const std::string &fullPath) | |||
| // Deletes a directory filename, returns true on success
 | ||||
| bool DeleteDir(const std::string &filename) | ||||
| { | ||||
|     INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str()); | ||||
|     LOG_INFO(Common_Filesystem, "directory %s", filename.c_str()); | ||||
| 
 | ||||
|     // check if a directory
 | ||||
|     if (!FileUtil::IsDirectory(filename)) | ||||
|     { | ||||
|         ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str()); | ||||
|         LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str()); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|  | @ -227,7 +227,7 @@ bool DeleteDir(const std::string &filename) | |||
|     if (rmdir(filename.c_str()) == 0) | ||||
|         return true; | ||||
| #endif | ||||
|     ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg()); | ||||
|     LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg()); | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
|  | @ -235,11 +235,11 @@ bool DeleteDir(const std::string &filename) | |||
| // renames file srcFilename to destFilename, returns true on success
 | ||||
| bool Rename(const std::string &srcFilename, const std::string &destFilename) | ||||
| { | ||||
|     INFO_LOG(COMMON, "Rename: %s --> %s", | ||||
|     LOG_TRACE(Common_Filesystem, "%s --> %s", | ||||
|             srcFilename.c_str(), destFilename.c_str()); | ||||
|     if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) | ||||
|         return true; | ||||
|     ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s", | ||||
|     LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", | ||||
|               srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||||
|     return false; | ||||
| } | ||||
|  | @ -247,13 +247,13 @@ bool Rename(const std::string &srcFilename, const std::string &destFilename) | |||
| // copies file srcFilename to destFilename, returns true on success
 | ||||
| bool Copy(const std::string &srcFilename, const std::string &destFilename) | ||||
| { | ||||
|     INFO_LOG(COMMON, "Copy: %s --> %s", | ||||
|     LOG_TRACE(Common_Filesystem, "%s --> %s", | ||||
|             srcFilename.c_str(), destFilename.c_str()); | ||||
| #ifdef _WIN32 | ||||
|     if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE)) | ||||
|         return true; | ||||
| 
 | ||||
|     ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s", | ||||
|     LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", | ||||
|             srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||||
|     return false; | ||||
| #else | ||||
|  | @ -267,7 +267,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
|     FILE *input = fopen(srcFilename.c_str(), "rb"); | ||||
|     if (!input) | ||||
|     { | ||||
|         ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s", | ||||
|                 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||||
|         return false; | ||||
|     } | ||||
|  | @ -277,7 +277,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
|     if (!output) | ||||
|     { | ||||
|         fclose(input); | ||||
|         ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s", | ||||
|                 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||||
|         return false; | ||||
|     } | ||||
|  | @ -291,8 +291,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
|         { | ||||
|             if (ferror(input) != 0) | ||||
|             { | ||||
|                 ERROR_LOG(COMMON, | ||||
|                         "Copy: failed reading from source, %s --> %s: %s", | ||||
|                 LOG_ERROR(Common_Filesystem, | ||||
|                         "failed reading from source, %s --> %s: %s", | ||||
|                         srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||||
|                 goto bail; | ||||
|             } | ||||
|  | @ -302,8 +302,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
|         int wnum = fwrite(buffer, sizeof(char), rnum, output); | ||||
|         if (wnum != rnum) | ||||
|         { | ||||
|             ERROR_LOG(COMMON, | ||||
|                     "Copy: failed writing to output, %s --> %s: %s", | ||||
|             LOG_ERROR(Common_Filesystem, | ||||
|                     "failed writing to output, %s --> %s: %s", | ||||
|                     srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||||
|             goto bail; | ||||
|         } | ||||
|  | @ -326,13 +326,13 @@ u64 GetSize(const std::string &filename) | |||
| { | ||||
|     if (!Exists(filename)) | ||||
|     { | ||||
|         WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str()); | ||||
|         LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str()); | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     if (IsDirectory(filename)) | ||||
|     { | ||||
|         WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str()); | ||||
|         LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str()); | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|  | @ -343,12 +343,12 @@ u64 GetSize(const std::string &filename) | |||
|     if (stat64(filename.c_str(), &buf) == 0) | ||||
| #endif | ||||
|     { | ||||
|         DEBUG_LOG(COMMON, "GetSize: %s: %lld", | ||||
|         LOG_TRACE(Common_Filesystem, "%s: %lld", | ||||
|                 filename.c_str(), (long long)buf.st_size); | ||||
|         return buf.st_size; | ||||
|     } | ||||
| 
 | ||||
|     ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s", | ||||
|     LOG_ERROR(Common_Filesystem, "Stat failed %s: %s", | ||||
|             filename.c_str(), GetLastErrorMsg()); | ||||
|     return 0; | ||||
| } | ||||
|  | @ -358,7 +358,7 @@ u64 GetSize(const int fd) | |||
| { | ||||
|     struct stat64 buf; | ||||
|     if (fstat64(fd, &buf) != 0) { | ||||
|         ERROR_LOG(COMMON, "GetSize: stat failed %i: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s", | ||||
|             fd, GetLastErrorMsg()); | ||||
|         return 0; | ||||
|     } | ||||
|  | @ -371,13 +371,13 @@ u64 GetSize(FILE *f) | |||
|     // can't use off_t here because it can be 32-bit
 | ||||
|     u64 pos = ftello(f); | ||||
|     if (fseeko(f, 0, SEEK_END) != 0) { | ||||
|         ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", | ||||
|               f, GetLastErrorMsg()); | ||||
|         return 0; | ||||
|     } | ||||
|     u64 size = ftello(f); | ||||
|     if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { | ||||
|         ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", | ||||
|               f, GetLastErrorMsg()); | ||||
|         return 0; | ||||
|     } | ||||
|  | @ -387,11 +387,11 @@ u64 GetSize(FILE *f) | |||
| // creates an empty file filename, returns true on success
 | ||||
| bool CreateEmptyFile(const std::string &filename) | ||||
| { | ||||
|     INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str()); | ||||
|     LOG_TRACE(Common_Filesystem, "%s", filename.c_str()); | ||||
| 
 | ||||
|     if (!FileUtil::IOFile(filename, "wb")) | ||||
|     { | ||||
|         ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "failed %s: %s", | ||||
|                   filename.c_str(), GetLastErrorMsg()); | ||||
|         return false; | ||||
|     } | ||||
|  | @ -404,7 +404,7 @@ bool CreateEmptyFile(const std::string &filename) | |||
| // results into parentEntry. Returns the number of files+directories found
 | ||||
| u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | ||||
| { | ||||
|     INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str()); | ||||
|     LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str()); | ||||
|     // How many files + directories we found
 | ||||
|     u32 foundEntries = 0; | ||||
| #ifdef _WIN32 | ||||
|  | @ -423,7 +423,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | |||
|         FSTEntry entry; | ||||
|         const std::string virtualName(Common::TStrToUTF8(ffd.cFileName)); | ||||
| #else | ||||
|     struct dirent dirent, *result = NULL; | ||||
|     struct dirent dirent, *result = nullptr; | ||||
| 
 | ||||
|     DIR *dirp = opendir(directory.c_str()); | ||||
|     if (!dirp) | ||||
|  | @ -474,7 +474,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | |||
| // Deletes the given directory and anything under it. Returns true on success.
 | ||||
| bool DeleteDirRecursively(const std::string &directory) | ||||
| { | ||||
|     INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str()); | ||||
|     LOG_TRACE(Common_Filesystem, "%s", directory.c_str()); | ||||
| #ifdef _WIN32 | ||||
|     // Find the first file in the directory.
 | ||||
|     WIN32_FIND_DATA ffd; | ||||
|  | @ -491,7 +491,7 @@ bool DeleteDirRecursively(const std::string &directory) | |||
|     { | ||||
|         const std::string virtualName(Common::TStrToUTF8(ffd.cFileName)); | ||||
| #else | ||||
|     struct dirent dirent, *result = NULL; | ||||
|     struct dirent dirent, *result = nullptr; | ||||
|     DIR *dirp = opendir(directory.c_str()); | ||||
|     if (!dirp) | ||||
|         return false; | ||||
|  | @ -552,7 +552,7 @@ void CopyDir(const std::string &source_path, const std::string &dest_path) | |||
|     if (!FileUtil::Exists(source_path)) return; | ||||
|     if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path); | ||||
| 
 | ||||
|     struct dirent dirent, *result = NULL; | ||||
|     struct dirent dirent, *result = nullptr; | ||||
|     DIR *dirp = opendir(source_path.c_str()); | ||||
|     if (!dirp) return; | ||||
| 
 | ||||
|  | @ -586,11 +586,11 @@ std::string GetCurrentDir() | |||
| { | ||||
|     char *dir; | ||||
|     // Get the current working directory (getcwd uses malloc)
 | ||||
|     if (!(dir = __getcwd(NULL, 0))) { | ||||
|     if (!(dir = __getcwd(nullptr, 0))) { | ||||
| 
 | ||||
|         ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s", | ||||
|                 GetLastErrorMsg()); | ||||
|         return NULL; | ||||
|         return nullptr; | ||||
|     } | ||||
|     std::string strDir = dir; | ||||
|     free(dir); | ||||
|  | @ -626,7 +626,7 @@ std::string& GetExeDirectory() | |||
|     if (DolphinPath.empty()) | ||||
|     { | ||||
|         TCHAR Dolphin_exe_Path[2048]; | ||||
|         GetModuleFileName(NULL, Dolphin_exe_Path, 2048); | ||||
|         GetModuleFileName(nullptr, Dolphin_exe_Path, 2048); | ||||
|         DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path); | ||||
|         DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\')); | ||||
|     } | ||||
|  | @ -647,7 +647,7 @@ std::string GetSysDirectory() | |||
| #endif | ||||
|     sysDir += DIR_SEP; | ||||
| 
 | ||||
|     INFO_LOG(COMMON, "GetSysDirectory: Setting to %s:", sysDir.c_str()); | ||||
|     LOG_DEBUG(Common_Filesystem, "Setting to %s:", sysDir.c_str()); | ||||
|     return sysDir; | ||||
| } | ||||
| 
 | ||||
|  | @ -676,6 +676,9 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
|         paths[D_MAPS_IDX]           = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | ||||
|         paths[D_CACHE_IDX]          = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | ||||
|         paths[D_SDMC_IDX]           = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | ||||
|         paths[D_SAVEDATA_IDX]       = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||||
|         paths[D_SYSDATA_IDX]        = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; | ||||
|         paths[D_SYSSAVEDATA_IDX]    = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP; | ||||
|         paths[D_SHADERCACHE_IDX]    = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | ||||
|         paths[D_SHADERS_IDX]        = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | ||||
|         paths[D_STATESAVES_IDX]     = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | ||||
|  | @ -694,7 +697,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
|     { | ||||
|         if (!FileUtil::IsDirectory(newPath)) | ||||
|         { | ||||
|             WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str()); | ||||
|             LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str()); | ||||
|             return paths[DirIDX]; | ||||
|         } | ||||
|         else | ||||
|  | @ -717,6 +720,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
|             paths[D_MAPS_IDX]           = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | ||||
|             paths[D_CACHE_IDX]          = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | ||||
|             paths[D_SDMC_IDX]           = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | ||||
|             paths[D_SAVEDATA_IDX]       = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||||
|             paths[D_SYSSAVEDATA_IDX]    = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP; | ||||
|             paths[D_SHADERCACHE_IDX]    = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | ||||
|             paths[D_SHADERS_IDX]        = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | ||||
|             paths[D_STATESAVES_IDX]     = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | ||||
|  | @ -753,19 +758,6 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
|     return paths[DirIDX]; | ||||
| } | ||||
| 
 | ||||
| //std::string GetThemeDir(const std::string& theme_name)
 | ||||
| //{
 | ||||
| //    std::string dir = FileUtil::GetUserPath(D_THEMES_IDX) + theme_name + "/";
 | ||||
| //
 | ||||
| //#if !defined(_WIN32)
 | ||||
| //    // If theme does not exist in user's dir load from shared directory
 | ||||
| //    if (!FileUtil::Exists(dir))
 | ||||
| //        dir = SHARED_USER_DIR THEMES_DIR "/" + theme_name + "/";
 | ||||
| //#endif
 | ||||
| //
 | ||||
| //    return dir;
 | ||||
| //}
 | ||||
| 
 | ||||
| size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename) | ||||
| { | ||||
|     return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); | ||||
|  | @ -826,7 +818,7 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam | |||
| } | ||||
| 
 | ||||
| IOFile::IOFile() | ||||
|     : m_file(NULL), m_good(true) | ||||
|     : m_file(nullptr), m_good(true) | ||||
| {} | ||||
| 
 | ||||
| IOFile::IOFile(std::FILE* file) | ||||
|  | @ -834,7 +826,7 @@ IOFile::IOFile(std::FILE* file) | |||
| {} | ||||
| 
 | ||||
| IOFile::IOFile(const std::string& filename, const char openmode[]) | ||||
|     : m_file(NULL), m_good(true) | ||||
|     : m_file(nullptr), m_good(true) | ||||
| { | ||||
|     Open(filename, openmode); | ||||
| } | ||||
|  | @ -845,7 +837,7 @@ IOFile::~IOFile() | |||
| } | ||||
| 
 | ||||
| IOFile::IOFile(IOFile&& other) | ||||
|     : m_file(NULL), m_good(true) | ||||
|     : m_file(nullptr), m_good(true) | ||||
| { | ||||
|     Swap(other); | ||||
| } | ||||
|  | @ -880,14 +872,14 @@ bool IOFile::Close() | |||
|     if (!IsOpen() || 0 != std::fclose(m_file)) | ||||
|         m_good = false; | ||||
| 
 | ||||
|     m_file = NULL; | ||||
|     m_file = nullptr; | ||||
|     return m_good; | ||||
| } | ||||
| 
 | ||||
| std::FILE* IOFile::ReleaseHandle() | ||||
| { | ||||
|     std::FILE* const ret = m_file; | ||||
|     m_file = NULL; | ||||
|     m_file = nullptr; | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  | @ -27,6 +27,9 @@ enum { | |||
|     D_STATESAVES_IDX, | ||||
|     D_SCREENSHOTS_IDX, | ||||
|     D_SDMC_IDX, | ||||
|     D_SAVEDATA_IDX, | ||||
|     D_SYSDATA_IDX, | ||||
|     D_SYSSAVEDATA_IDX, | ||||
|     D_HIRESTEXTURES_IDX, | ||||
|     D_DUMP_IDX, | ||||
|     D_DUMPFRAMES_IDX, | ||||
|  | @ -202,11 +205,11 @@ public: | |||
|         return WriteArray(reinterpret_cast<const char*>(data), length); | ||||
|     } | ||||
| 
 | ||||
|     bool IsOpen() { return NULL != m_file; } | ||||
|     bool IsOpen() { return nullptr != m_file; } | ||||
| 
 | ||||
|     // m_good is set to false when a read, write or other function fails
 | ||||
|     bool IsGood() {    return m_good; } | ||||
|     operator void*() { return m_good ? m_file : NULL; } | ||||
|     operator void*() { return m_good ? m_file : nullptr; } | ||||
| 
 | ||||
|     std::FILE* ReleaseHandle(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "key_map.h" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  | @ -70,7 +70,7 @@ public: | |||
|             // good header, read some key/value pairs
 | ||||
|             K key; | ||||
| 
 | ||||
|             V *value = NULL; | ||||
|             V *value = nullptr; | ||||
|             u32 value_size; | ||||
|             u32 entry_number; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										133
									
								
								src/common/log.h
									
										
									
									
									
								
							
							
						
						
									
										133
									
								
								src/common/log.h
									
										
									
									
									
								
							|  | @ -1,108 +1,12 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
| 
 | ||||
| #ifndef LOGGING | ||||
| #define LOGGING | ||||
| #endif | ||||
| 
 | ||||
| enum { | ||||
|     OS_LEVEL,       // Printed by the emulated operating system
 | ||||
|     NOTICE_LEVEL,   // VERY important information that is NOT errors. Like startup and OSReports.
 | ||||
|     ERROR_LEVEL,    // Critical errors
 | ||||
|     WARNING_LEVEL,  // Something is suspicious.
 | ||||
|     INFO_LEVEL,     // General information.
 | ||||
|     DEBUG_LEVEL,    // Detailed debugging - might make things slow.
 | ||||
| }; | ||||
| 
 | ||||
| namespace LogTypes | ||||
| { | ||||
| 
 | ||||
| enum LOG_TYPE { | ||||
|     ACTIONREPLAY, | ||||
|     AUDIO, | ||||
|     AUDIO_INTERFACE, | ||||
|     BOOT, | ||||
|     COMMANDPROCESSOR, | ||||
|     COMMON, | ||||
|     CONSOLE, | ||||
|     CONFIG, | ||||
|     DISCIO, | ||||
|     FILEMON, | ||||
|     DSPHLE, | ||||
|     DSPLLE, | ||||
|     DSP_MAIL, | ||||
|     DSPINTERFACE, | ||||
|     DVDINTERFACE, | ||||
|     DYNA_REC, | ||||
|     EXPANSIONINTERFACE, | ||||
|     GDB_STUB, | ||||
|     ARM11, | ||||
|     GSP, | ||||
|     OSHLE, | ||||
|     MASTER_LOG, | ||||
|     MEMMAP, | ||||
|     MEMCARD_MANAGER, | ||||
|     OSREPORT, | ||||
|     PAD, | ||||
|     PROCESSORINTERFACE, | ||||
|     PIXELENGINE, | ||||
|     SERIALINTERFACE, | ||||
|     SP1, | ||||
|     STREAMINGINTERFACE, | ||||
|     VIDEO, | ||||
|     VIDEOINTERFACE, | ||||
|     LOADER, | ||||
|     FILESYS, | ||||
|     WII_IPC_DVD, | ||||
|     WII_IPC_ES, | ||||
|     WII_IPC_FILEIO, | ||||
|     WII_IPC_HID, | ||||
|     KERNEL, | ||||
|     SVC, | ||||
|     NDMA, | ||||
|     HLE, | ||||
|     RENDER, | ||||
|     GPU, | ||||
|     HW, | ||||
|     TIME, | ||||
|     NETPLAY, | ||||
|     GUI, | ||||
| 
 | ||||
|     NUMBER_OF_LOGS // Must be last
 | ||||
| }; | ||||
| 
 | ||||
| // FIXME: should this be removed?
 | ||||
| enum LOG_LEVELS { | ||||
|     LOS = OS_LEVEL, | ||||
|     LNOTICE = NOTICE_LEVEL, | ||||
|     LERROR = ERROR_LEVEL, | ||||
|     LWARNING = WARNING_LEVEL, | ||||
|     LINFO = INFO_LEVEL, | ||||
|     LDEBUG = DEBUG_LEVEL, | ||||
| }; | ||||
| 
 | ||||
| #define LOGTYPES_LEVELS LogTypes::LOG_LEVELS | ||||
| #define LOGTYPES_TYPE LogTypes::LOG_TYPE | ||||
| 
 | ||||
| }  // namespace
 | ||||
| 
 | ||||
| void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int line, | ||||
|     const char* function, const char* fmt, ...) | ||||
| #ifdef __GNUC__ | ||||
|         __attribute__((format(printf, 6, 7))) | ||||
| #endif | ||||
|         ; | ||||
| 
 | ||||
| #if defined LOGGING || defined _DEBUG || defined DEBUGFAST | ||||
| #define MAX_LOGLEVEL LDEBUG | ||||
| #else | ||||
| #ifndef MAX_LOGLEVEL | ||||
| #define MAX_LOGLEVEL LWARNING | ||||
| #endif // loglevel
 | ||||
| #endif // logging
 | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/msg_handler.h" | ||||
| #include "common/logging/log.h" | ||||
| 
 | ||||
| #ifdef MSVC_VER | ||||
| #ifndef __func__ | ||||
|  | @ -110,29 +14,16 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int | |||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| // Let the compiler optimize this out
 | ||||
| #define GENERIC_LOG(t, v, ...) { \ | ||||
|     if (v <= LogTypes::MAX_LOGLEVEL) \ | ||||
|         GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \ | ||||
|     } | ||||
| 
 | ||||
| #define OS_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LOS, __VA_ARGS__) } while (0) | ||||
| #define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0) | ||||
| #define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0) | ||||
| #define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0) | ||||
| #define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0) | ||||
| #define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0) | ||||
| 
 | ||||
| #if MAX_LOGLEVEL >= DEBUG_LEVEL | ||||
| #if _DEBUG | ||||
| #define _dbg_assert_(_t_, _a_) \ | ||||
|     if (!(_a_)) {\ | ||||
|         ERROR_LOG(_t_, "Error...\n\n  Line: %d\n  File: %s\n  Time: %s\n\nIgnore and continue?", \ | ||||
|         LOG_CRITICAL(_t_, "Error...\n\n  Line: %d\n  File: %s\n  Time: %s\n\nIgnore and continue?", \ | ||||
|                        __LINE__, __FILE__, __TIME__); \ | ||||
|         if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \ | ||||
|     } | ||||
| #define _dbg_assert_msg_(_t_, _a_, ...)\ | ||||
|     if (!(_a_)) {\ | ||||
|         ERROR_LOG(_t_, __VA_ARGS__); \ | ||||
|         LOG_CRITICAL(_t_, __VA_ARGS__); \ | ||||
|         if (!PanicYesNo(__VA_ARGS__)) {Crash();} \ | ||||
|     } | ||||
| #define _dbg_update_() Host_UpdateLogDisplay(); | ||||
|  | @ -144,12 +35,12 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int | |||
| #define _dbg_assert_(_t_, _a_) {} | ||||
| #define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {} | ||||
| #endif // dbg_assert
 | ||||
| #endif // MAX_LOGLEVEL DEBUG
 | ||||
| #endif | ||||
| 
 | ||||
| #define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_) | ||||
| 
 | ||||
| #ifndef GEKKO | ||||
| #ifdef MSVC_VER | ||||
| #ifdef _WIN32 | ||||
| #define _assert_msg_(_t_, _a_, _fmt_, ...)        \ | ||||
|     if (!(_a_)) {\ | ||||
|         if (!PanicYesNo(_fmt_, __VA_ARGS__)) {Crash();} \ | ||||
|  | @ -159,7 +50,7 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int | |||
|     if (!(_a_)) {\ | ||||
|         if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \ | ||||
|     } | ||||
| #endif // MSVC_VER
 | ||||
| #endif // _WIN32
 | ||||
| #else // GEKKO
 | ||||
| #define _assert_msg_(_t_, _a_, _fmt_, ...) | ||||
| #endif | ||||
| #endif | ||||
|  | @ -1,199 +0,0 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #include "common/log_manager.h" | ||||
| #include "common/console_listener.h" | ||||
| #include "common/timer.h" | ||||
| 
 | ||||
| void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line, | ||||
|     const char* function, const char* fmt, ...) | ||||
| { | ||||
|     va_list args; | ||||
|     va_start(args, fmt); | ||||
| 
 | ||||
|     if (LogManager::GetInstance()) { | ||||
|         LogManager::GetInstance()->Log(level, type, | ||||
|             file, line, function, fmt, args); | ||||
|     } | ||||
|     va_end(args); | ||||
| } | ||||
| 
 | ||||
| LogManager *LogManager::m_logManager = NULL; | ||||
| 
 | ||||
| LogManager::LogManager() | ||||
| { | ||||
|     // create log files
 | ||||
|     m_Log[LogTypes::MASTER_LOG]         = new LogContainer("*",                 "Master Log"); | ||||
|     m_Log[LogTypes::BOOT]               = new LogContainer("BOOT",              "Boot"); | ||||
|     m_Log[LogTypes::COMMON]             = new LogContainer("COMMON",            "Common"); | ||||
|     m_Log[LogTypes::CONFIG]             = new LogContainer("CONFIG",            "Configuration"); | ||||
|     m_Log[LogTypes::DISCIO]             = new LogContainer("DIO",               "Disc IO"); | ||||
|     m_Log[LogTypes::FILEMON]            = new LogContainer("FileMon",           "File Monitor"); | ||||
|     m_Log[LogTypes::PAD]                = new LogContainer("PAD",               "Pad"); | ||||
|     m_Log[LogTypes::PIXELENGINE]        = new LogContainer("PE",                "PixelEngine"); | ||||
|     m_Log[LogTypes::COMMANDPROCESSOR]   = new LogContainer("CP",                "CommandProc"); | ||||
|     m_Log[LogTypes::VIDEOINTERFACE]     = new LogContainer("VI",                "VideoInt"); | ||||
|     m_Log[LogTypes::SERIALINTERFACE]    = new LogContainer("SI",                "SerialInt"); | ||||
|     m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI",                "ProcessorInt"); | ||||
|     m_Log[LogTypes::MEMMAP]             = new LogContainer("MI",                "MI & memmap"); | ||||
|     m_Log[LogTypes::SP1]                = new LogContainer("SP1",               "Serial Port 1"); | ||||
|     m_Log[LogTypes::STREAMINGINTERFACE] = new LogContainer("Stream",            "StreamingInt"); | ||||
|     m_Log[LogTypes::DSPINTERFACE]       = new LogContainer("DSP",               "DSPInterface"); | ||||
|     m_Log[LogTypes::DVDINTERFACE]       = new LogContainer("DVD",               "DVDInterface"); | ||||
|     m_Log[LogTypes::GSP]                = new LogContainer("GSP",               "GSP"); | ||||
|     m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI",               "ExpansionInt"); | ||||
|     m_Log[LogTypes::GDB_STUB]           = new LogContainer("GDB_STUB",          "GDB Stub"); | ||||
|     m_Log[LogTypes::AUDIO_INTERFACE]    = new LogContainer("AI",                "AudioInt"); | ||||
|     m_Log[LogTypes::ARM11]              = new LogContainer("ARM11",             "ARM11"); | ||||
|     m_Log[LogTypes::OSHLE]              = new LogContainer("HLE",               "HLE"); | ||||
|     m_Log[LogTypes::DSPHLE]             = new LogContainer("DSPHLE",            "DSP HLE"); | ||||
|     m_Log[LogTypes::DSPLLE]             = new LogContainer("DSPLLE",            "DSP LLE"); | ||||
|     m_Log[LogTypes::DSP_MAIL]           = new LogContainer("DSPMails",          "DSP Mails"); | ||||
|     m_Log[LogTypes::VIDEO]              = new LogContainer("Video",             "Video Backend"); | ||||
|     m_Log[LogTypes::AUDIO]              = new LogContainer("Audio",             "Audio Emulator"); | ||||
|     m_Log[LogTypes::DYNA_REC]           = new LogContainer("JIT",               "JIT"); | ||||
|     m_Log[LogTypes::CONSOLE]            = new LogContainer("CONSOLE",           "Dolphin Console"); | ||||
|     m_Log[LogTypes::OSREPORT]           = new LogContainer("OSREPORT",          "OSReport"); | ||||
|     m_Log[LogTypes::TIME]               = new LogContainer("Time",              "Core Timing"); | ||||
|     m_Log[LogTypes::LOADER]             = new LogContainer("Loader",            "Loader"); | ||||
|     m_Log[LogTypes::FILESYS]            = new LogContainer("FileSys",           "File System"); | ||||
|     m_Log[LogTypes::WII_IPC_HID]        = new LogContainer("WII_IPC_HID",       "WII IPC HID"); | ||||
|     m_Log[LogTypes::KERNEL]             = new LogContainer("KERNEL",            "KERNEL HLE"); | ||||
|     m_Log[LogTypes::WII_IPC_DVD]        = new LogContainer("WII_IPC_DVD",       "WII IPC DVD"); | ||||
|     m_Log[LogTypes::WII_IPC_ES]         = new LogContainer("WII_IPC_ES",        "WII IPC ES"); | ||||
|     m_Log[LogTypes::WII_IPC_FILEIO]     = new LogContainer("WII_IPC_FILEIO",    "WII IPC FILEIO"); | ||||
|     m_Log[LogTypes::RENDER]             = new LogContainer("RENDER",            "RENDER"); | ||||
|     m_Log[LogTypes::GPU]                = new LogContainer("GPU",               "GPU"); | ||||
|     m_Log[LogTypes::SVC]                = new LogContainer("SVC",               "Supervisor Call HLE"); | ||||
|     m_Log[LogTypes::NDMA]               = new LogContainer("NDMA",              "NDMA"); | ||||
|     m_Log[LogTypes::HLE]                = new LogContainer("HLE",               "High Level Emulation"); | ||||
|     m_Log[LogTypes::HW]                 = new LogContainer("HW",                "Hardware"); | ||||
|     m_Log[LogTypes::ACTIONREPLAY]       = new LogContainer("ActionReplay",      "ActionReplay"); | ||||
|     m_Log[LogTypes::MEMCARD_MANAGER]    = new LogContainer("MemCard Manager",   "MemCard Manager"); | ||||
|     m_Log[LogTypes::NETPLAY]            = new LogContainer("NETPLAY",           "Netplay"); | ||||
|     m_Log[LogTypes::GUI]                = new LogContainer("GUI",               "GUI"); | ||||
| 
 | ||||
|     m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str()); | ||||
|     m_consoleLog = new ConsoleListener(); | ||||
|     m_debuggerLog = new DebuggerLogListener(); | ||||
| 
 | ||||
|     for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) | ||||
|     { | ||||
|         m_Log[i]->SetEnable(true); | ||||
|         m_Log[i]->AddListener(m_fileLog); | ||||
|         m_Log[i]->AddListener(m_consoleLog); | ||||
| #ifdef _MSC_VER | ||||
|         if (IsDebuggerPresent()) | ||||
|             m_Log[i]->AddListener(m_debuggerLog); | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|     m_consoleLog->Open(); | ||||
| } | ||||
| 
 | ||||
| LogManager::~LogManager() | ||||
| { | ||||
|     for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) | ||||
|     { | ||||
|         m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog); | ||||
|         m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog); | ||||
|         m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) | ||||
|         delete m_Log[i]; | ||||
| 
 | ||||
|     delete m_fileLog; | ||||
|     delete m_consoleLog; | ||||
|     delete m_debuggerLog; | ||||
| } | ||||
| 
 | ||||
| void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, | ||||
|     int line, const char* function, const char *fmt, va_list args) | ||||
| { | ||||
|     char temp[MAX_MSGLEN]; | ||||
|     char msg[MAX_MSGLEN * 2]; | ||||
|     LogContainer *log = m_Log[type]; | ||||
| 
 | ||||
|     if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners()) | ||||
|         return; | ||||
| 
 | ||||
|     Common::CharArrayFromFormatV(temp, MAX_MSGLEN, fmt, args); | ||||
| 
 | ||||
|     static const char level_to_char[7] = "ONEWID"; | ||||
|     sprintf(msg, "%s %s:%u %c[%s] %s: %s\n", Common::Timer::GetTimeFormatted().c_str(), file, line, | ||||
|         level_to_char[(int)level], log->GetShortName(), function, temp); | ||||
| 
 | ||||
| #ifdef ANDROID | ||||
|     Host_SysMessage(msg); | ||||
| #endif | ||||
|     log->Trigger(level, msg); | ||||
| } | ||||
| 
 | ||||
| void LogManager::Init() | ||||
| { | ||||
|     m_logManager = new LogManager(); | ||||
| } | ||||
| 
 | ||||
| void LogManager::Shutdown() | ||||
| { | ||||
|     delete m_logManager; | ||||
|     m_logManager = NULL; | ||||
| } | ||||
| 
 | ||||
| LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable) | ||||
|     : m_enable(enable) | ||||
| { | ||||
|     strncpy(m_fullName, fullName, 128); | ||||
|     strncpy(m_shortName, shortName, 32); | ||||
|     m_level = LogTypes::MAX_LOGLEVEL; | ||||
| } | ||||
| 
 | ||||
| // LogContainer
 | ||||
| void LogContainer::AddListener(LogListener *listener) | ||||
| { | ||||
|     std::lock_guard<std::mutex> lk(m_listeners_lock); | ||||
|     m_listeners.insert(listener); | ||||
| } | ||||
| 
 | ||||
| void LogContainer::RemoveListener(LogListener *listener) | ||||
| { | ||||
|     std::lock_guard<std::mutex> lk(m_listeners_lock); | ||||
|     m_listeners.erase(listener); | ||||
| } | ||||
| 
 | ||||
| void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg) | ||||
| { | ||||
|     std::lock_guard<std::mutex> lk(m_listeners_lock); | ||||
| 
 | ||||
|     std::set<LogListener*>::const_iterator i; | ||||
|     for (i = m_listeners.begin(); i != m_listeners.end(); ++i) | ||||
|     { | ||||
|         (*i)->Log(level, msg); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| FileLogListener::FileLogListener(const char *filename) | ||||
| { | ||||
|     OpenFStream(m_logfile, filename, std::ios::app); | ||||
|     SetEnable(true); | ||||
| } | ||||
| 
 | ||||
| void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) | ||||
| { | ||||
|     if (!IsEnabled() || !IsValid()) | ||||
|         return; | ||||
| 
 | ||||
|     std::lock_guard<std::mutex> lk(m_log_lock); | ||||
|     m_logfile << msg << std::flush; | ||||
| } | ||||
| 
 | ||||
| void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) | ||||
| { | ||||
| #if _MSC_VER | ||||
|     ::OutputDebugStringA(msg); | ||||
| #endif | ||||
| } | ||||
|  | @ -1,166 +0,0 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/log.h" | ||||
| #include "common/string_util.h" | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <set> | ||||
| #include <mutex> | ||||
| 
 | ||||
| #define MAX_MESSAGES 8000 | ||||
| #define MAX_MSGLEN  1024 | ||||
| 
 | ||||
| 
 | ||||
| // pure virtual interface
 | ||||
| class LogListener | ||||
| { | ||||
| public: | ||||
|     virtual ~LogListener() {} | ||||
| 
 | ||||
|     virtual void Log(LogTypes::LOG_LEVELS, const char *msg) = 0; | ||||
| }; | ||||
| 
 | ||||
| class FileLogListener : public LogListener | ||||
| { | ||||
| public: | ||||
|     FileLogListener(const char *filename); | ||||
| 
 | ||||
|     void Log(LogTypes::LOG_LEVELS, const char *msg) override; | ||||
| 
 | ||||
|     bool IsValid() { return !m_logfile.fail(); } | ||||
|     bool IsEnabled() const { return m_enable; } | ||||
|     void SetEnable(bool enable) { m_enable = enable; } | ||||
| 
 | ||||
|     const char* GetName() const { return "file"; } | ||||
| 
 | ||||
| private: | ||||
|     std::mutex m_log_lock; | ||||
|     std::ofstream m_logfile; | ||||
|     bool m_enable; | ||||
| }; | ||||
| 
 | ||||
| class DebuggerLogListener : public LogListener | ||||
| { | ||||
| public: | ||||
|     void Log(LogTypes::LOG_LEVELS, const char *msg) override; | ||||
| }; | ||||
| 
 | ||||
| class LogContainer | ||||
| { | ||||
| public: | ||||
|     LogContainer(const char* shortName, const char* fullName, bool enable = false); | ||||
| 
 | ||||
|     const char* GetShortName() const { return m_shortName; } | ||||
|     const char* GetFullName() const { return m_fullName; } | ||||
| 
 | ||||
|     void AddListener(LogListener* listener); | ||||
|     void RemoveListener(LogListener* listener); | ||||
| 
 | ||||
|     void Trigger(LogTypes::LOG_LEVELS, const char *msg); | ||||
| 
 | ||||
|     bool IsEnabled() const { return m_enable; } | ||||
|     void SetEnable(bool enable) { m_enable = enable; } | ||||
| 
 | ||||
|     LogTypes::LOG_LEVELS GetLevel() const { return m_level;    } | ||||
| 
 | ||||
|     void SetLevel(LogTypes::LOG_LEVELS level) {    m_level = level; } | ||||
| 
 | ||||
|     bool HasListeners() const { return !m_listeners.empty(); } | ||||
| 
 | ||||
| private: | ||||
|     char m_fullName[128]; | ||||
|     char m_shortName[32]; | ||||
|     bool m_enable; | ||||
|     LogTypes::LOG_LEVELS m_level; | ||||
|     std::mutex m_listeners_lock; | ||||
|     std::set<LogListener*> m_listeners; | ||||
| }; | ||||
| 
 | ||||
| class ConsoleListener; | ||||
| 
 | ||||
| class LogManager : NonCopyable | ||||
| { | ||||
| private: | ||||
|     LogContainer* m_Log[LogTypes::NUMBER_OF_LOGS]; | ||||
|     FileLogListener *m_fileLog; | ||||
|     ConsoleListener *m_consoleLog; | ||||
|     DebuggerLogListener *m_debuggerLog; | ||||
|     static LogManager *m_logManager;  // Singleton. Ugh.
 | ||||
| 
 | ||||
|     LogManager(); | ||||
|     ~LogManager(); | ||||
| public: | ||||
| 
 | ||||
|     static u32 GetMaxLevel() { return LogTypes::MAX_LOGLEVEL;    } | ||||
| 
 | ||||
|     void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line, | ||||
|         const char* function, const char *fmt, va_list args); | ||||
| 
 | ||||
|     void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level) | ||||
|     { | ||||
|         m_Log[type]->SetLevel(level); | ||||
|     } | ||||
| 
 | ||||
|     void SetEnable(LogTypes::LOG_TYPE type, bool enable) | ||||
|     { | ||||
|         m_Log[type]->SetEnable(enable); | ||||
|     } | ||||
| 
 | ||||
|     bool IsEnabled(LogTypes::LOG_TYPE type) const | ||||
|     { | ||||
|         return m_Log[type]->IsEnabled(); | ||||
|     } | ||||
| 
 | ||||
|     const char* GetShortName(LogTypes::LOG_TYPE type) const | ||||
|     { | ||||
|         return m_Log[type]->GetShortName(); | ||||
|     } | ||||
| 
 | ||||
|     const char* GetFullName(LogTypes::LOG_TYPE type) const | ||||
|     { | ||||
|         return m_Log[type]->GetFullName(); | ||||
|     } | ||||
| 
 | ||||
|     void AddListener(LogTypes::LOG_TYPE type, LogListener *listener) | ||||
|     { | ||||
|         m_Log[type]->AddListener(listener); | ||||
|     } | ||||
| 
 | ||||
|     void RemoveListener(LogTypes::LOG_TYPE type, LogListener *listener) | ||||
|     { | ||||
|         m_Log[type]->RemoveListener(listener); | ||||
|     } | ||||
| 
 | ||||
|     FileLogListener *GetFileListener() const | ||||
|     { | ||||
|         return m_fileLog; | ||||
|     } | ||||
| 
 | ||||
|     ConsoleListener *GetConsoleListener() const | ||||
|     { | ||||
|         return m_consoleLog; | ||||
|     } | ||||
| 
 | ||||
|     DebuggerLogListener *GetDebuggerListener() const | ||||
|     { | ||||
|         return m_debuggerLog; | ||||
|     } | ||||
| 
 | ||||
|     static LogManager* GetInstance() | ||||
|     { | ||||
|         return m_logManager; | ||||
|     } | ||||
| 
 | ||||
|     static void SetInstance(LogManager *logManager) | ||||
|     { | ||||
|         m_logManager = logManager; | ||||
|     } | ||||
| 
 | ||||
|     static void Init(); | ||||
|     static void Shutdown(); | ||||
| }; | ||||
							
								
								
									
										151
									
								
								src/common/logging/backend.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								src/common/logging/backend.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,151 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #include "common/log.h" // For _dbg_assert_
 | ||||
| 
 | ||||
| #include "common/logging/backend.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/logging/text_formatter.h" | ||||
| 
 | ||||
| namespace Log { | ||||
| 
 | ||||
| static std::shared_ptr<Logger> global_logger; | ||||
| 
 | ||||
| /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
 | ||||
| #define ALL_LOG_CLASSES() \ | ||||
|         CLS(Log) \ | ||||
|         CLS(Common) \ | ||||
|         SUB(Common, Filesystem) \ | ||||
|         SUB(Common, Memory) \ | ||||
|         CLS(Core) \ | ||||
|         SUB(Core, ARM11) \ | ||||
|         CLS(Config) \ | ||||
|         CLS(Debug) \ | ||||
|         SUB(Debug, Emulated) \ | ||||
|         SUB(Debug, GPU) \ | ||||
|         SUB(Debug, Breakpoint) \ | ||||
|         CLS(Kernel) \ | ||||
|         SUB(Kernel, SVC) \ | ||||
|         CLS(Service) \ | ||||
|         SUB(Service, SRV) \ | ||||
|         SUB(Service, FS) \ | ||||
|         SUB(Service, APT) \ | ||||
|         SUB(Service, GSP) \ | ||||
|         SUB(Service, AC) \ | ||||
|         SUB(Service, PTM) \ | ||||
|         SUB(Service, CFG) \ | ||||
|         SUB(Service, DSP) \ | ||||
|         SUB(Service, HID) \ | ||||
|         CLS(HW) \ | ||||
|         SUB(HW, Memory) \ | ||||
|         SUB(HW, GPU) \ | ||||
|         CLS(Frontend) \ | ||||
|         CLS(Render) \ | ||||
|         SUB(Render, Software) \ | ||||
|         SUB(Render, OpenGL) \ | ||||
|         CLS(Loader) | ||||
| 
 | ||||
| Logger::Logger() { | ||||
|     // Register logging classes so that they can be queried at runtime
 | ||||
|     size_t parent_class; | ||||
|     all_classes.reserve((size_t)Class::Count); | ||||
| 
 | ||||
| #define CLS(x) \ | ||||
|         all_classes.push_back(Class::x); \ | ||||
|         parent_class = all_classes.size() - 1; | ||||
| #define SUB(x, y) \ | ||||
|         all_classes.push_back(Class::x##_##y); \ | ||||
|         all_classes[parent_class].num_children += 1; | ||||
| 
 | ||||
|     ALL_LOG_CLASSES() | ||||
| #undef CLS | ||||
| #undef SUB | ||||
| 
 | ||||
|     // Ensures that ALL_LOG_CLASSES isn't missing any entries.
 | ||||
|     _dbg_assert_(Log, all_classes.size() == (size_t)Class::Count); | ||||
| } | ||||
| 
 | ||||
| // GetClassName is a macro defined by Windows.h, grrr...
 | ||||
| const char* Logger::GetLogClassName(Class log_class) { | ||||
|     switch (log_class) { | ||||
| #define CLS(x) case Class::x: return #x; | ||||
| #define SUB(x, y) case Class::x##_##y: return #x "." #y; | ||||
|         ALL_LOG_CLASSES() | ||||
| #undef CLS | ||||
| #undef SUB | ||||
|     } | ||||
|     return "Unknown"; | ||||
| } | ||||
| 
 | ||||
| const char* Logger::GetLevelName(Level log_level) { | ||||
| #define LVL(x) case Level::x: return #x | ||||
|     switch (log_level) { | ||||
|         LVL(Trace); | ||||
|         LVL(Debug); | ||||
|         LVL(Info); | ||||
|         LVL(Warning); | ||||
|         LVL(Error); | ||||
|         LVL(Critical); | ||||
|     } | ||||
|     return "Unknown"; | ||||
| #undef LVL | ||||
| } | ||||
| 
 | ||||
| void Logger::LogMessage(Entry entry) { | ||||
|     ring_buffer.Push(std::move(entry)); | ||||
| } | ||||
| 
 | ||||
| size_t Logger::GetEntries(Entry* out_buffer, size_t buffer_len) { | ||||
|     return ring_buffer.BlockingPop(out_buffer, buffer_len); | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<Logger> InitGlobalLogger() { | ||||
|     global_logger = std::make_shared<Logger>(); | ||||
|     return global_logger; | ||||
| } | ||||
| 
 | ||||
| Entry CreateEntry(Class log_class, Level log_level, | ||||
|                         const char* filename, unsigned int line_nr, const char* function, | ||||
|                         const char* format, va_list args) { | ||||
|     using std::chrono::steady_clock; | ||||
|     using std::chrono::duration_cast; | ||||
| 
 | ||||
|     static steady_clock::time_point time_origin = steady_clock::now(); | ||||
| 
 | ||||
|     std::array<char, 4 * 1024> formatting_buffer; | ||||
| 
 | ||||
|     Entry entry; | ||||
|     entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); | ||||
|     entry.log_class = log_class; | ||||
|     entry.log_level = log_level; | ||||
| 
 | ||||
|     snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function, line_nr); | ||||
|     entry.location = std::string(formatting_buffer.data()); | ||||
| 
 | ||||
|     vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args); | ||||
|     entry.message = std::string(formatting_buffer.data()); | ||||
| 
 | ||||
|     return std::move(entry); | ||||
| } | ||||
| 
 | ||||
| void LogMessage(Class log_class, Level log_level, | ||||
|                 const char* filename, unsigned int line_nr, const char* function, | ||||
|                 const char* format, ...) { | ||||
|     va_list args; | ||||
|     va_start(args, format); | ||||
|     Entry entry = CreateEntry(log_class, log_level, | ||||
|             filename, line_nr, function, format, args); | ||||
|     va_end(args); | ||||
| 
 | ||||
|     if (global_logger != nullptr && !global_logger->IsClosed()) { | ||||
|         global_logger->LogMessage(std::move(entry)); | ||||
|     } else { | ||||
|         // Fall back to directly printing to stderr
 | ||||
|         PrintMessage(entry); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										134
									
								
								src/common/logging/backend.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/common/logging/backend.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <cstdarg> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/concurrent_ring_buffer.h" | ||||
| 
 | ||||
| #include "common/logging/log.h" | ||||
| 
 | ||||
| namespace Log { | ||||
| 
 | ||||
| /**
 | ||||
|  * A log entry. Log entries are store in a structured format to permit more varied output | ||||
|  * formatting on different frontends, as well as facilitating filtering and aggregation. | ||||
|  */ | ||||
| struct Entry { | ||||
|     std::chrono::microseconds timestamp; | ||||
|     Class log_class; | ||||
|     Level log_level; | ||||
|     std::string location; | ||||
|     std::string message; | ||||
| 
 | ||||
|     Entry() = default; | ||||
| 
 | ||||
|     // TODO(yuriks) Use defaulted move constructors once MSVC supports them
 | ||||
| #define MOVE(member) member(std::move(o.member)) | ||||
|     Entry(Entry&& o) | ||||
|         : MOVE(timestamp), MOVE(log_class), MOVE(log_level), | ||||
|         MOVE(location), MOVE(message) | ||||
|     {} | ||||
| #undef MOVE | ||||
| 
 | ||||
|     Entry& operator=(const Entry&& o) { | ||||
| #define MOVE(member) member = std::move(o.member) | ||||
|         MOVE(timestamp); | ||||
|         MOVE(log_class); | ||||
|         MOVE(log_level); | ||||
|         MOVE(location); | ||||
|         MOVE(message); | ||||
| #undef MOVE | ||||
|         return *this; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| struct ClassInfo { | ||||
|     Class log_class; | ||||
| 
 | ||||
|     /**
 | ||||
|         * Total number of (direct or indirect) sub classes this class has. If any, they follow in | ||||
|         * sequence after this class in the class list. | ||||
|         */ | ||||
|     unsigned int num_children = 0; | ||||
| 
 | ||||
|     ClassInfo(Class log_class) : log_class(log_class) {} | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Logging management class. This class has the dual purpose of acting as an exchange point between | ||||
|  * the logging clients and the log outputter, as well as containing reflection info about available | ||||
|  * log classes. | ||||
|  */ | ||||
| class Logger { | ||||
| private: | ||||
|     using Buffer = Common::ConcurrentRingBuffer<Entry, 16 * 1024 / sizeof(Entry)>; | ||||
| 
 | ||||
| public: | ||||
|     static const size_t QUEUE_CLOSED = Buffer::QUEUE_CLOSED; | ||||
| 
 | ||||
|     Logger(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns a list of all vector classes and subclasses. The sequence returned is a pre-order of | ||||
|      * classes and subclasses, which together with the `num_children` field in ClassInfo, allows | ||||
|      * you to recover the hierarchy. | ||||
|      */ | ||||
|     const std::vector<ClassInfo>& GetClasses() const { return all_classes; } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns the name of the passed log class as a C-string. Subclasses are separated by periods | ||||
|      * instead of underscores as in the enumeration. | ||||
|      */ | ||||
|     static const char* GetLogClassName(Class log_class); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns the name of the passed log level as a C-string. | ||||
|      */ | ||||
|     static const char* GetLevelName(Level log_level); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Appends a messages to the log buffer. | ||||
|      * @note This function is thread safe. | ||||
|      */ | ||||
|     void LogMessage(Entry entry); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Retrieves a batch of messages from the log buffer, blocking until they are available. | ||||
|      * @note This function is thread safe. | ||||
|      * | ||||
|      * @param out_buffer Destination buffer that will receive the log entries. | ||||
|      * @param buffer_len The maximum size of `out_buffer`. | ||||
|      * @return The number of entries stored. In case the logger is shutting down, `QUEUE_CLOSED` is | ||||
|      *         returned, no entries are stored and the logger should shutdown. | ||||
|      */ | ||||
|     size_t GetEntries(Entry* out_buffer, size_t buffer_len); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Initiates a shutdown of the logger. This will indicate to log output clients that they | ||||
|      * should shutdown. | ||||
|      */ | ||||
|     void Close() { ring_buffer.Close(); } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns true if Close() has already been called on the Logger. | ||||
|      */ | ||||
|     bool IsClosed() const { return ring_buffer.IsClosed(); } | ||||
| 
 | ||||
| private: | ||||
|     Buffer ring_buffer; | ||||
|     std::vector<ClassInfo> all_classes; | ||||
| }; | ||||
| 
 | ||||
| /// Creates a log entry by formatting the given source location, and message.
 | ||||
| Entry CreateEntry(Class log_class, Level log_level, | ||||
|                         const char* filename, unsigned int line_nr, const char* function, | ||||
|                         const char* format, va_list args); | ||||
| /// Initializes the default Logger.
 | ||||
| std::shared_ptr<Logger> InitGlobalLogger(); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										132
									
								
								src/common/logging/filter.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/common/logging/filter.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,132 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #include "common/logging/filter.h" | ||||
| #include "common/logging/backend.h" | ||||
| #include "common/string_util.h" | ||||
| 
 | ||||
| namespace Log { | ||||
| 
 | ||||
| Filter::Filter(Level default_level) { | ||||
|     ResetAll(default_level); | ||||
| } | ||||
| 
 | ||||
| void Filter::ResetAll(Level level) { | ||||
|     class_levels.fill(level); | ||||
| } | ||||
| 
 | ||||
| void Filter::SetClassLevel(Class log_class, Level level) { | ||||
|     class_levels[static_cast<size_t>(log_class)] = level; | ||||
| } | ||||
| 
 | ||||
| void Filter::SetSubclassesLevel(const ClassInfo& log_class, Level level) { | ||||
|     const size_t log_class_i = static_cast<size_t>(log_class.log_class); | ||||
| 
 | ||||
|     const size_t begin = log_class_i + 1; | ||||
|     const size_t end = begin + log_class.num_children; | ||||
|     for (size_t i = begin; begin < end; ++i) { | ||||
|         class_levels[i] = level; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Filter::ParseFilterString(const std::string& filter_str) { | ||||
|     auto clause_begin = filter_str.cbegin(); | ||||
|     while (clause_begin != filter_str.cend()) { | ||||
|         auto clause_end = std::find(clause_begin, filter_str.cend(), ' '); | ||||
| 
 | ||||
|         // If clause isn't empty
 | ||||
|         if (clause_end != clause_begin) { | ||||
|             ParseFilterRule(clause_begin, clause_end); | ||||
|         } | ||||
| 
 | ||||
|         if (clause_end != filter_str.cend()) { | ||||
|             // Skip over the whitespace
 | ||||
|             ++clause_end; | ||||
|         } | ||||
|         clause_begin = clause_end; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename It> | ||||
| static Level GetLevelByName(const It begin, const It end) { | ||||
|     for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) { | ||||
|         const char* level_name = Logger::GetLevelName(static_cast<Level>(i)); | ||||
|         if (Common::ComparePartialString(begin, end, level_name)) { | ||||
|             return static_cast<Level>(i); | ||||
|         } | ||||
|     } | ||||
|     return Level::Count; | ||||
| } | ||||
| 
 | ||||
| template <typename It> | ||||
| static Class GetClassByName(const It begin, const It end) { | ||||
|     for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) { | ||||
|         const char* level_name = Logger::GetLogClassName(static_cast<Class>(i)); | ||||
|         if (Common::ComparePartialString(begin, end, level_name)) { | ||||
|             return static_cast<Class>(i); | ||||
|         } | ||||
|     } | ||||
|     return Class::Count; | ||||
| } | ||||
| 
 | ||||
| template <typename InputIt, typename T> | ||||
| static InputIt find_last(InputIt begin, const InputIt end, const T& value) { | ||||
|     auto match = end; | ||||
|     while (begin != end) { | ||||
|         auto new_match = std::find(begin, end, value); | ||||
|         if (new_match != end) { | ||||
|             match = new_match; | ||||
|             ++new_match; | ||||
|         } | ||||
|         begin = new_match; | ||||
|     } | ||||
|     return match; | ||||
| } | ||||
| 
 | ||||
| bool Filter::ParseFilterRule(const std::string::const_iterator begin, | ||||
|         const std::string::const_iterator end) { | ||||
|     auto level_separator = std::find(begin, end, ':'); | ||||
|     if (level_separator == end) { | ||||
|         LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s", | ||||
|                 std::string(begin, end).c_str()); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     const Level level = GetLevelByName(level_separator + 1, end); | ||||
|     if (level == Level::Count) { | ||||
|         LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str()); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (Common::ComparePartialString(begin, level_separator, "*")) { | ||||
|         ResetAll(level); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     auto class_name_end = find_last(begin, level_separator, '.'); | ||||
|     if (class_name_end != level_separator && | ||||
|             !Common::ComparePartialString(class_name_end + 1, level_separator, "*")) { | ||||
|         class_name_end = level_separator; | ||||
|     } | ||||
| 
 | ||||
|     const Class log_class = GetClassByName(begin, class_name_end); | ||||
|     if (log_class == Class::Count) { | ||||
|         LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str()); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (class_name_end == level_separator) { | ||||
|         SetClassLevel(log_class, level); | ||||
|     } | ||||
|     SetSubclassesLevel(log_class, level); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool Filter::CheckMessage(Class log_class, Level level) const { | ||||
|     return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]); | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										63
									
								
								src/common/logging/filter.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/common/logging/filter.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <array> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common/logging/log.h" | ||||
| 
 | ||||
| namespace Log { | ||||
| 
 | ||||
| struct ClassInfo; | ||||
| 
 | ||||
| /**
 | ||||
|  * Implements a log message filter which allows different log classes to have different minimum | ||||
|  * severity levels. The filter can be changed at runtime and can be parsed from a string to allow | ||||
|  * editing via the interface or loading from a configuration file. | ||||
|  */ | ||||
| class Filter { | ||||
| public: | ||||
|     /// Initializes the filter with all classes having `default_level` as the minimum level.
 | ||||
|     Filter(Level default_level); | ||||
| 
 | ||||
|     /// Resets the filter so that all classes have `level` as the minimum displayed level.
 | ||||
|     void ResetAll(Level level); | ||||
|     /// Sets the minimum level of `log_class` (and not of its subclasses) to `level`.
 | ||||
|     void SetClassLevel(Class log_class, Level level); | ||||
|     /**
 | ||||
|      * Sets the minimum level of all of `log_class` subclasses to `level`. The level of `log_class` | ||||
|      * itself is not changed. | ||||
|      */ | ||||
|     void SetSubclassesLevel(const ClassInfo& log_class, Level level); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Parses a filter string and applies it to this filter. | ||||
|      * | ||||
|      * A filter string consists of a space-separated list of filter rules, each of the format | ||||
|      * `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods. | ||||
|      * A rule for a given class also affects all of its subclasses. `*` wildcards are allowed and | ||||
|      * can be used to apply a rule to all classes or to all subclasses of a class without affecting | ||||
|      * the parent class. `<level>` a severity level name which will be set as the minimum logging | ||||
|      * level of the matched classes. Rules are applied left to right, with each rule overriding | ||||
|      * previous ones in the sequence. | ||||
|      * | ||||
|      * A few examples of filter rules: | ||||
|      *  - `*:Info` -- Resets the level of all classes to Info. | ||||
|      *  - `Service:Info` -- Sets the level of Service and all subclasses (Service.FS, Service.APT, | ||||
|      *                       etc.) to Info. | ||||
|      *  - `Service.*:Debug` -- Sets the level of all Service subclasses to Debug, while leaving the | ||||
|      *                         level of Service unchanged. | ||||
|      *  - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace. | ||||
|      */ | ||||
|     void ParseFilterString(const std::string& filter_str); | ||||
|     bool ParseFilterRule(const std::string::const_iterator start, const std::string::const_iterator end); | ||||
| 
 | ||||
|     /// Matches class/level combination against the filter, returning true if it passed.
 | ||||
|     bool CheckMessage(Class log_class, Level level) const; | ||||
| 
 | ||||
| private: | ||||
|     std::array<Level, (size_t)Class::Count> class_levels; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										115
									
								
								src/common/logging/log.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/common/logging/log.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <cassert> | ||||
| #include <chrono> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Log { | ||||
| 
 | ||||
| /// Specifies the severity or level of detail of the log message.
 | ||||
| enum class Level : u8 { | ||||
|     Trace,    ///< Extremely detailed and repetitive debugging information that is likely to
 | ||||
|               ///  pollute logs.
 | ||||
|     Debug,    ///< Less detailed debugging information.
 | ||||
|     Info,     ///< Status information from important points during execution.
 | ||||
|     Warning,  ///< Minor or potential problems found during execution of a task.
 | ||||
|     Error,    ///< Major problems found during execution of a task that prevent it from being
 | ||||
|               ///  completed.
 | ||||
|     Critical, ///< Major problems during execution that threathen the stability of the entire
 | ||||
|               ///  application.
 | ||||
| 
 | ||||
|     Count ///< Total number of logging levels
 | ||||
| }; | ||||
| 
 | ||||
| typedef u8 ClassType; | ||||
| 
 | ||||
| /**
 | ||||
|  * Specifies the sub-system that generated the log message. | ||||
|  * | ||||
|  * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in log.cpp. | ||||
|  */ | ||||
| enum class Class : ClassType { | ||||
|     Log,                        ///< Messages about the log system itself
 | ||||
|     Common,                     ///< Library routines
 | ||||
|     Common_Filesystem,          ///< Filesystem interface library
 | ||||
|     Common_Memory,              ///< Memory mapping and management functions
 | ||||
|     Core,                       ///< LLE emulation core
 | ||||
|     Core_ARM11,                 ///< ARM11 CPU core
 | ||||
|     Config,                     ///< Emulator configuration (including commandline)
 | ||||
|     Debug,                      ///< Debugging tools
 | ||||
|     Debug_Emulated,             ///< Debug messages from the emulated programs
 | ||||
|     Debug_GPU,                  ///< GPU debugging tools
 | ||||
|     Debug_Breakpoint,           ///< Logging breakpoints and watchpoints
 | ||||
|     Kernel,                     ///< The HLE implementation of the CTR kernel
 | ||||
|     Kernel_SVC,                 ///< Kernel system calls
 | ||||
|     Service,                    ///< HLE implementation of system services. Each major service
 | ||||
|                                 ///  should have its own subclass.
 | ||||
|     Service_SRV,                ///< The SRV (Service Directory) implementation
 | ||||
|     Service_FS,                 ///< The FS (Filesystem) service implementation
 | ||||
|     Service_APT,                ///< The APT (Applets) service
 | ||||
|     Service_GSP,                ///< The GSP (GPU control) service
 | ||||
|     Service_AC,                 ///< The AC (WiFi status) service
 | ||||
|     Service_PTM,                ///< The PTM (Power status & misc.) service
 | ||||
|     Service_CFG,                ///< The CFG (Configuration) service
 | ||||
|     Service_DSP,                ///< The DSP (DSP control) service
 | ||||
|     Service_HID,                ///< The HID (User input) service
 | ||||
|     HW,                         ///< Low-level hardware emulation
 | ||||
|     HW_Memory,                  ///< Memory-map and address translation
 | ||||
|     HW_GPU,                     ///< GPU control emulation
 | ||||
|     Frontend,                   ///< Emulator UI
 | ||||
|     Render,                     ///< Emulator video output and hardware acceleration
 | ||||
|     Render_Software,            ///< Software renderer backend
 | ||||
|     Render_OpenGL,              ///< OpenGL backend
 | ||||
|     Loader,                     ///< ROM loader
 | ||||
| 
 | ||||
|     Count ///< Total number of logging classes
 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Level below which messages are simply discarded without buffering regardless of the display | ||||
|  * settings. | ||||
|  */ | ||||
| const Level MINIMUM_LEVEL = | ||||
| #ifdef _DEBUG | ||||
|     Level::Trace; | ||||
| #else | ||||
|     Level::Debug; | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * Logs a message to the global logger. This proxy exists to avoid exposing the details of the | ||||
|  * Logger class, including the ConcurrentRingBuffer template, to all files that desire to log | ||||
|  * messages, reducing unecessary recompilations. | ||||
|  */ | ||||
| void LogMessage(Class log_class, Level log_level, | ||||
|     const char* filename, unsigned int line_nr, const char* function, | ||||
| #ifdef _MSC_VER | ||||
|     _Printf_format_string_ | ||||
| #endif | ||||
|     const char* format, ...) | ||||
| #ifdef __GNUC__ | ||||
|     __attribute__((format(printf, 6, 7))) | ||||
| #endif | ||||
|     ; | ||||
| 
 | ||||
| } // namespace Log
 | ||||
| 
 | ||||
| #define LOG_GENERIC(log_class, log_level, ...) \ | ||||
|     do { \ | ||||
|         if (::Log::Level::log_level >= ::Log::MINIMUM_LEVEL) \ | ||||
|             ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \ | ||||
|                        __FILE__, __LINE__, __func__, __VA_ARGS__); \ | ||||
|     } while (0) | ||||
| 
 | ||||
| #define LOG_TRACE(   log_class, ...) LOG_GENERIC(log_class, Trace,    __VA_ARGS__) | ||||
| #define LOG_DEBUG(   log_class, ...) LOG_GENERIC(log_class, Debug,    __VA_ARGS__) | ||||
| #define LOG_INFO(    log_class, ...) LOG_GENERIC(log_class, Info,     __VA_ARGS__) | ||||
| #define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning,  __VA_ARGS__) | ||||
| #define LOG_ERROR(   log_class, ...) LOG_GENERIC(log_class, Error,    __VA_ARGS__) | ||||
| #define LOG_CRITICAL(log_class, ...) LOG_GENERIC(log_class, Critical, __VA_ARGS__) | ||||
							
								
								
									
										136
									
								
								src/common/logging/text_formatter.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/common/logging/text_formatter.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,136 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <array> | ||||
| #include <cstdio> | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #   define WIN32_LEAN_AND_MEAN | ||||
| #   include <Windows.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "common/logging/backend.h" | ||||
| #include "common/logging/filter.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/logging/text_formatter.h" | ||||
| 
 | ||||
| #include "common/string_util.h" | ||||
| 
 | ||||
| namespace Log { | ||||
| 
 | ||||
| // TODO(bunnei): This should be moved to a generic path manipulation library
 | ||||
| const char* TrimSourcePath(const char* path, const char* root) { | ||||
|     const char* p = path; | ||||
| 
 | ||||
|     while (*p != '\0') { | ||||
|         const char* next_slash = p; | ||||
|         while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') { | ||||
|             ++next_slash; | ||||
|         } | ||||
| 
 | ||||
|         bool is_src = Common::ComparePartialString(p, next_slash, root); | ||||
|         p = next_slash; | ||||
| 
 | ||||
|         if (*p != '\0') { | ||||
|             ++p; | ||||
|         } | ||||
|         if (is_src) { | ||||
|             path = p; | ||||
|         } | ||||
|     } | ||||
|     return path; | ||||
| } | ||||
| 
 | ||||
| void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) { | ||||
|     unsigned int time_seconds    = static_cast<unsigned int>(entry.timestamp.count() / 1000000); | ||||
|     unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000); | ||||
| 
 | ||||
|     const char* class_name = Logger::GetLogClassName(entry.log_class); | ||||
|     const char* level_name = Logger::GetLevelName(entry.log_level); | ||||
| 
 | ||||
|     snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s", | ||||
|         time_seconds, time_fractional, class_name, level_name, | ||||
|         TrimSourcePath(entry.location.c_str()), entry.message.c_str()); | ||||
| } | ||||
| 
 | ||||
| void PrintMessage(const Entry& entry) { | ||||
|     std::array<char, 4 * 1024> format_buffer; | ||||
|     FormatLogMessage(entry, format_buffer.data(), format_buffer.size()); | ||||
|     fputs(format_buffer.data(), stderr); | ||||
|     fputc('\n', stderr); | ||||
| } | ||||
| 
 | ||||
| void PrintColoredMessage(const Entry& entry) { | ||||
| #ifdef _WIN32 | ||||
|     static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE); | ||||
| 
 | ||||
|     CONSOLE_SCREEN_BUFFER_INFO original_info = {0}; | ||||
|     GetConsoleScreenBufferInfo(console_handle, &original_info); | ||||
| 
 | ||||
|     WORD color = 0; | ||||
|     switch (entry.log_level) { | ||||
|     case Level::Trace: // Grey
 | ||||
|         color = FOREGROUND_INTENSITY; break; | ||||
|     case Level::Debug: // Cyan
 | ||||
|         color = FOREGROUND_GREEN | FOREGROUND_BLUE; break; | ||||
|     case Level::Info: // Bright gray
 | ||||
|         color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; | ||||
|     case Level::Warning: // Bright yellow
 | ||||
|         color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; | ||||
|     case Level::Error: // Bright red
 | ||||
|         color = FOREGROUND_RED | FOREGROUND_INTENSITY; break; | ||||
|     case Level::Critical: // Bright magenta
 | ||||
|         color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; | ||||
|     } | ||||
| 
 | ||||
|     SetConsoleTextAttribute(console_handle, color); | ||||
| #else | ||||
| #   define ESC "\x1b" | ||||
|     const char* color = ""; | ||||
|     switch (entry.log_level) { | ||||
|     case Level::Trace: // Grey
 | ||||
|         color = ESC "[1;30m"; break; | ||||
|     case Level::Debug: // Cyan
 | ||||
|         color = ESC "[0;36m"; break; | ||||
|     case Level::Info: // Bright gray
 | ||||
|         color = ESC "[0;37m"; break; | ||||
|     case Level::Warning: // Bright yellow
 | ||||
|         color = ESC "[1;33m"; break; | ||||
|     case Level::Error: // Bright red
 | ||||
|         color = ESC "[1;31m"; break; | ||||
|     case Level::Critical: // Bright magenta
 | ||||
|         color = ESC "[1;35m"; break; | ||||
|     } | ||||
| 
 | ||||
|     fputs(color, stderr); | ||||
| #endif | ||||
| 
 | ||||
|     PrintMessage(entry); | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
|     SetConsoleTextAttribute(console_handle, original_info.wAttributes); | ||||
| #else | ||||
|     fputs(ESC "[0m", stderr); | ||||
| #   undef ESC | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter) { | ||||
|     std::array<Entry, 256> entry_buffer; | ||||
| 
 | ||||
|     while (true) { | ||||
|         size_t num_entries = logger->GetEntries(entry_buffer.data(), entry_buffer.size()); | ||||
|         if (num_entries == Logger::QUEUE_CLOSED) { | ||||
|             break; | ||||
|         } | ||||
|         for (size_t i = 0; i < num_entries; ++i) { | ||||
|             const Entry& entry = entry_buffer[i]; | ||||
|             if (filter->CheckMessage(entry.log_class, entry.log_level)) { | ||||
|                 PrintColoredMessage(entry); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										41
									
								
								src/common/logging/text_formatter.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/common/logging/text_formatter.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <cstddef> | ||||
| #include <memory> | ||||
| 
 | ||||
| namespace Log { | ||||
| 
 | ||||
| class Logger; | ||||
| struct Entry; | ||||
| class Filter; | ||||
| 
 | ||||
| /**
 | ||||
|  * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's | ||||
|  * intended to be used to strip a system-specific build directory from the `__FILE__` macro, | ||||
|  * leaving only the path relative to the sources root. | ||||
|  * | ||||
|  * @param path The input file path as a null-terminated string | ||||
|  * @param root The name of the root source directory as a null-terminated string. Path up to and | ||||
|  *             including the last occurence of this name will be stripped | ||||
|  * @return A pointer to the same string passed as `path`, but starting at the trimmed portion | ||||
|  */ | ||||
| const char* TrimSourcePath(const char* path, const char* root = "src"); | ||||
| 
 | ||||
| /// Formats a log entry into the provided text buffer.
 | ||||
| void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len); | ||||
| /// Formats and prints a log entry to stderr.
 | ||||
| void PrintMessage(const Entry& entry); | ||||
| /// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
 | ||||
| void PrintColoredMessage(const Entry& entry); | ||||
| 
 | ||||
| /**
 | ||||
|  * Logging loop that repeatedly reads messages from the provided logger and prints them to the | ||||
|  * console. It is the baseline barebones log outputter. | ||||
|  */ | ||||
| void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/common/make_unique.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/common/make_unique.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| template <typename T, typename... Args> | ||||
| std::unique_ptr<T> make_unique(Args&&... args) { | ||||
|     return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ | |||
| #endif | ||||
| 
 | ||||
| #ifdef IOS | ||||
| void* globalbase = NULL; | ||||
| void* globalbase = nullptr; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef ANDROID | ||||
|  | @ -71,7 +71,7 @@ int ashmem_create_region(const char *name, size_t size) | |||
|     return fd; | ||||
| 
 | ||||
| error: | ||||
|     ERROR_LOG(MEMMAP, "NASTY ASHMEM ERROR: ret = %08x", ret); | ||||
|     LOG_ERROR(Common_Memory, "NASTY ASHMEM ERROR: ret = %08x", ret); | ||||
|     close(fd); | ||||
|     return ret; | ||||
| } | ||||
|  | @ -121,7 +121,7 @@ void MemArena::GrabLowMemSpace(size_t size) | |||
| { | ||||
| #ifdef _WIN32 | ||||
| #ifndef _XBOX | ||||
|     hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL); | ||||
|     hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD)(size), nullptr); | ||||
|     GetSystemInfo(&sysInfo); | ||||
| #endif | ||||
| #elif defined(ANDROID) | ||||
|  | @ -130,7 +130,7 @@ void MemArena::GrabLowMemSpace(size_t size) | |||
|     // Note that it appears that ashmem is pinned by default, so no need to pin.
 | ||||
|     if (fd < 0) | ||||
|     { | ||||
|         ERROR_LOG(MEMMAP, "Failed to grab ashmem space of size: %08x  errno: %d", (int)size, (int)(errno)); | ||||
|         LOG_ERROR(Common_Memory, "Failed to grab ashmem space of size: %08x  errno: %d", (int)size, (int)(errno)); | ||||
|         return; | ||||
|     } | ||||
| #else | ||||
|  | @ -148,12 +148,12 @@ void MemArena::GrabLowMemSpace(size_t size) | |||
|         } | ||||
|         else if (errno != EEXIST) | ||||
|         { | ||||
|             ERROR_LOG(MEMMAP, "shm_open failed: %s", strerror(errno)); | ||||
|             LOG_ERROR(Common_Memory, "shm_open failed: %s", strerror(errno)); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     if (ftruncate(fd, size) < 0) | ||||
|         ERROR_LOG(MEMMAP, "Failed to allocate low memory space"); | ||||
|         LOG_ERROR(Common_Memory, "Failed to allocate low memory space"); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
|  | @ -178,7 +178,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base) | |||
| #ifdef _XBOX | ||||
|     size = roundup(size); | ||||
|     // use 64kb pages
 | ||||
|     void * ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); | ||||
|     void * ptr = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); | ||||
|     return ptr; | ||||
| #else | ||||
|     size = roundup(size); | ||||
|  | @ -197,7 +197,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base) | |||
| 
 | ||||
|     if (retval == MAP_FAILED) | ||||
|     { | ||||
|         NOTICE_LOG(MEMMAP, "mmap failed"); | ||||
|         LOG_ERROR(Common_Memory, "mmap failed"); | ||||
|         return nullptr; | ||||
|     } | ||||
|     return retval; | ||||
|  | @ -243,8 +243,8 @@ u8* MemArena::Find4GBBase() | |||
|     return base; | ||||
| #else | ||||
| #ifdef IOS | ||||
|     void* base = NULL; | ||||
|     if (globalbase == NULL){ | ||||
|     void* base = nullptr; | ||||
|     if (globalbase == nullptr){ | ||||
|         base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE, | ||||
|             MAP_ANON | MAP_SHARED, -1, 0); | ||||
|         if (base == MAP_FAILED) { | ||||
|  | @ -357,7 +357,7 @@ bail: | |||
|         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 = NULL; | ||||
|             *views[j].out_ptr_low = nullptr; | ||||
|         } | ||||
|         if (*views[j].out_ptr) | ||||
|         { | ||||
|  | @ -369,7 +369,7 @@ bail: | |||
|                 arena->ReleaseView(*views[j].out_ptr, views[j].size); | ||||
|             } | ||||
| #endif | ||||
|             *views[j].out_ptr = NULL; | ||||
|             *views[j].out_ptr = nullptr; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|  | @ -415,7 +415,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
| #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 = NULL; | ||||
|     u8 *base = nullptr; | ||||
| 
 | ||||
|     for (u32 base_addr = 0x01000000; base_addr < max_base_addr; base_addr += 0x400000) | ||||
|     { | ||||
|  | @ -423,7 +423,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
|         base = (u8 *)base_addr; | ||||
|         if (Memory_TryBase(base, views, num_views, flags, arena)) | ||||
|         { | ||||
|             INFO_LOG(MEMMAP, "Found valid memory base at %p after %i tries.", base, base_attempts); | ||||
|             LOG_DEBUG(Common_Memory, "Found valid memory base at %p after %i tries.", base, base_attempts); | ||||
|             base_attempts = 0; | ||||
|             break; | ||||
|         } | ||||
|  | @ -442,7 +442,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
|     u8 *base = MemArena::Find4GBBase(); | ||||
|     if (!Memory_TryBase(base, views, num_views, flags, arena)) | ||||
|     { | ||||
|         ERROR_LOG(MEMMAP, "MemoryMap_Setup: Failed finding a memory base."); | ||||
|         LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base."); | ||||
|         PanicAlert("MemoryMap_Setup: Failed finding a memory base."); | ||||
|         return 0; | ||||
|     } | ||||
|  | @ -463,8 +463,8 @@ void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemAr | |||
|             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 = NULL; | ||||
|         *views[i].out_ptr = nullptr; | ||||
|         if (views[i].out_ptr_low) | ||||
|             *views[i].out_ptr_low = NULL; | ||||
|             *views[i].out_ptr_low = nullptr; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -93,7 +93,7 @@ void* AllocateMemoryPages(size_t size) | |||
|     // printf("Mapped memory at %p (size %ld)\n", ptr,
 | ||||
|     //    (unsigned long)size);
 | ||||
| 
 | ||||
|     if (ptr == NULL) | ||||
|     if (ptr == nullptr) | ||||
|         PanicAlert("Failed to allocate raw memory"); | ||||
| 
 | ||||
|     return ptr; | ||||
|  | @ -104,19 +104,19 @@ void* AllocateAlignedMemory(size_t size,size_t alignment) | |||
| #ifdef _WIN32 | ||||
|     void* ptr =  _aligned_malloc(size,alignment); | ||||
| #else | ||||
|     void* ptr = NULL; | ||||
|     void* ptr = nullptr; | ||||
| #ifdef ANDROID | ||||
|     ptr = memalign(alignment, size); | ||||
| #else | ||||
|     if (posix_memalign(&ptr, alignment, size) != 0) | ||||
|         ERROR_LOG(MEMMAP, "Failed to allocate aligned memory"); | ||||
|         LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
|     // printf("Mapped memory at %p (size %ld)\n", ptr,
 | ||||
|     //    (unsigned long)size);
 | ||||
| 
 | ||||
|     if (ptr == NULL) | ||||
|     if (ptr == nullptr) | ||||
|         PanicAlert("Failed to allocate aligned memory"); | ||||
| 
 | ||||
|     return ptr; | ||||
|  | @ -130,7 +130,7 @@ void FreeMemoryPages(void* ptr, size_t size) | |||
| 
 | ||||
|         if (!VirtualFree(ptr, 0, MEM_RELEASE)) | ||||
|             PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg()); | ||||
|         ptr = NULL; // Is this our responsibility?
 | ||||
|         ptr = nullptr; // Is this our responsibility?
 | ||||
| 
 | ||||
| #else | ||||
|         munmap(ptr, size); | ||||
|  | @ -184,7 +184,7 @@ std::string MemUsage() | |||
|     // Print information about the memory usage of the process.
 | ||||
| 
 | ||||
|     hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); | ||||
|     if (NULL == hProcess) return "MemUsage Error"; | ||||
|     if (nullptr == hProcess) return "MemUsage Error"; | ||||
| 
 | ||||
|     if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) | ||||
|         Ret = Common::StringFromFormat("%s K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str()); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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" | ||||
|  | @ -23,9 +23,9 @@ const char* GetLastErrorMsg() | |||
| #ifdef _WIN32 | ||||
|     static __declspec(thread) char err_str[buff_size] = {}; | ||||
| 
 | ||||
|     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), | ||||
|     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), | ||||
|         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | ||||
|         err_str, buff_size, NULL); | ||||
|         err_str, buff_size, nullptr); | ||||
| #else | ||||
|     static __thread char err_str[buff_size] = {}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <cstdio> | ||||
|  | @ -75,7 +75,7 @@ bool MsgAlert(bool yes_no, int Style, const char* format, ...) | |||
|     Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args); | ||||
|     va_end(args); | ||||
| 
 | ||||
|     ERROR_LOG(MASTER_LOG, "%s: %s", caption.c_str(), buffer); | ||||
|     LOG_INFO(Common, "%s: %s", caption.c_str(), buffer); | ||||
| 
 | ||||
|     // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored
 | ||||
|     if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL)) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  |  | |||
|  | @ -80,7 +80,7 @@ | |||
| inline struct tm* localtime_r(const time_t *clock, struct tm *result) { | ||||
|     if (localtime_s(result, clock) == 0) | ||||
|         return result; | ||||
|     return NULL; | ||||
|     return nullptr; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
							
								
								
									
										37
									
								
								src/common/scope_exit.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/common/scope_exit.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| namespace detail { | ||||
|     template <typename Func> | ||||
|     struct ScopeExitHelper { | ||||
|         explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {} | ||||
|         ~ScopeExitHelper() { func(); } | ||||
| 
 | ||||
|         Func func; | ||||
|     }; | ||||
| 
 | ||||
|     template <typename Func> | ||||
|     ScopeExitHelper<Func> ScopeExit(Func&& func) { return ScopeExitHelper<Func>(std::move(func)); } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * This macro allows you to conveniently specify a block of code that will run on scope exit. Handy | ||||
|  * for doing ad-hoc clean-up tasks in a function with multiple returns. | ||||
|  * | ||||
|  * Example usage: | ||||
|  * \code | ||||
|  * const int saved_val = g_foo; | ||||
|  * g_foo = 55; | ||||
|  * SCOPE_EXIT({ g_foo = saved_val; }); | ||||
|  * | ||||
|  * if (Bar()) { | ||||
|  *     return 0; | ||||
|  * } else { | ||||
|  *     return 20; | ||||
|  * } | ||||
|  * \endcode | ||||
|  */ | ||||
| #define SCOPE_EXIT(body) auto scope_exit_helper_##__LINE__ = detail::ScopeExit([&]() body) | ||||
|  | @ -1,8 +1,8 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 <boost/range/algorithm.hpp> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/string_util.h" | ||||
|  | @ -18,20 +18,20 @@ namespace Common { | |||
| 
 | ||||
| /// Make a string lowercase
 | ||||
| std::string ToLower(std::string str) { | ||||
|     std::transform(str.begin(), str.end(), str.begin(), ::tolower); | ||||
|     boost::transform(str, str.begin(), ::tolower); | ||||
|     return str; | ||||
| } | ||||
| 
 | ||||
| /// Make a string uppercase
 | ||||
| std::string ToUpper(std::string str) { | ||||
|     std::transform(str.begin(), str.end(), str.begin(), ::toupper); | ||||
|     boost::transform(str, str.begin(), ::toupper); | ||||
|     return str; | ||||
| } | ||||
| 
 | ||||
| // faster than sscanf
 | ||||
| bool AsciiToHex(const char* _szValue, u32& result) | ||||
| { | ||||
|     char *endptr = NULL; | ||||
|     char *endptr = nullptr; | ||||
|     const u32 value = strtoul(_szValue, &endptr, 16); | ||||
| 
 | ||||
|     if (!endptr || *endptr) | ||||
|  | @ -69,7 +69,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar | |||
|     // will be present in the middle of a multibyte sequence.
 | ||||
|     //
 | ||||
|     // This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l.
 | ||||
|     static locale_t c_locale = NULL; | ||||
|     static locale_t c_locale = nullptr; | ||||
|     if (!c_locale) | ||||
|         c_locale = _create_locale(LC_ALL, ".1252"); | ||||
|     writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args); | ||||
|  | @ -92,7 +92,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar | |||
| std::string StringFromFormat(const char* format, ...) | ||||
| { | ||||
|     va_list args; | ||||
|     char *buf = NULL; | ||||
|     char *buf = nullptr; | ||||
| #ifdef _WIN32 | ||||
|     int required = 0; | ||||
| 
 | ||||
|  | @ -107,7 +107,7 @@ std::string StringFromFormat(const char* format, ...) | |||
| #else | ||||
|     va_start(args, format); | ||||
|     if (vasprintf(&buf, format, args) < 0) | ||||
|         ERROR_LOG(COMMON, "Unable to allocate memory for string"); | ||||
|         LOG_ERROR(Common, "Unable to allocate memory for string"); | ||||
|     va_end(args); | ||||
| 
 | ||||
|     std::string temp = buf; | ||||
|  | @ -162,7 +162,7 @@ std::string StripQuotes(const std::string& s) | |||
| 
 | ||||
| bool TryParse(const std::string &str, u32 *const output) | ||||
| { | ||||
|     char *endptr = NULL; | ||||
|     char *endptr = nullptr; | ||||
| 
 | ||||
|     // Reset errno to a value other than ERANGE
 | ||||
|     errno = 0; | ||||
|  | @ -475,7 +475,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& | |||
|     iconv_t const conv_desc = iconv_open("UTF-8", fromcode); | ||||
|     if ((iconv_t)(-1) == conv_desc) | ||||
|     { | ||||
|         ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno)); | ||||
|         LOG_ERROR(Common, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno)); | ||||
|         iconv_close(conv_desc); | ||||
|         return {}; | ||||
|     } | ||||
|  | @ -510,7 +510,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& | |||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno)); | ||||
|                 LOG_ERROR(Common, "iconv failure [%s]: %s", fromcode, strerror(errno)); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | @ -528,10 +528,10 @@ std::u16string UTF8ToUTF16(const std::string& input) | |||
| { | ||||
|     std::u16string result; | ||||
| 
 | ||||
|     iconv_t const conv_desc = iconv_open("UTF-16", "UTF-8"); | ||||
|     iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8"); | ||||
|     if ((iconv_t)(-1) == conv_desc) | ||||
|     { | ||||
|         ERROR_LOG(COMMON, "Iconv initialization failure [UTF-8]: %s", strerror(errno)); | ||||
|         LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: %s", strerror(errno)); | ||||
|         iconv_close(conv_desc); | ||||
|         return {}; | ||||
|     } | ||||
|  | @ -566,7 +566,7 @@ std::u16string UTF8ToUTF16(const std::string& input) | |||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ERROR_LOG(COMMON, "iconv failure [UTF-8]: %s", strerror(errno)); | ||||
|                 LOG_ERROR(Common, "iconv failure [UTF-8]: %s", strerror(errno)); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | @ -582,7 +582,7 @@ std::u16string UTF8ToUTF16(const std::string& input) | |||
| 
 | ||||
| std::string UTF16ToUTF8(const std::u16string& input) | ||||
| { | ||||
|     return CodeToUTF8("UTF-16", input); | ||||
|     return CodeToUTF8("UTF-16LE", input); | ||||
| } | ||||
| 
 | ||||
| std::string CP1252ToUTF8(const std::string& input) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  | @ -115,4 +115,19 @@ inline std::string UTF8ToTStr(const std::string& str) | |||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string | ||||
|  * `other` for equality. | ||||
|  */ | ||||
| template <typename InIt> | ||||
| bool ComparePartialString(InIt begin, InIt end, const char* other) { | ||||
|     for (; begin != end && *other != '\0'; ++begin, ++other) { | ||||
|         if (*begin != *other) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     // Only return true if both strings finished at the same point
 | ||||
|     return (begin == end) == (*other == '\0'); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/symbols.h" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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/thread.h" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  | @ -21,6 +21,7 @@ | |||
| //for gettimeofday and struct time(spec|val)
 | ||||
| #include <time.h> | ||||
| #include <sys/time.h> | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| 
 | ||||
| namespace Common | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project / PPSSPP Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -37,7 +37,7 @@ struct ThreadQueueList { | |||
|     ~ThreadQueueList() { | ||||
|         for (int i = 0; i < NUM_QUEUES; ++i) | ||||
|         { | ||||
|             if (queues[i].data != NULL) | ||||
|             if (queues[i].data != nullptr) | ||||
|                 free(queues[i].data); | ||||
|         } | ||||
|     } | ||||
|  | @ -46,7 +46,7 @@ struct ThreadQueueList { | |||
|     int contains(const IdType uid) { | ||||
|         for (int i = 0; i < NUM_QUEUES; ++i) | ||||
|         { | ||||
|             if (queues[i].data == NULL) | ||||
|             if (queues[i].data == nullptr) | ||||
|                 continue; | ||||
| 
 | ||||
|             Queue *cur = &queues[i]; | ||||
|  | @ -133,7 +133,7 @@ struct ThreadQueueList { | |||
|     inline void clear() { | ||||
|         for (int i = 0; i < NUM_QUEUES; ++i) | ||||
|         { | ||||
|             if (queues[i].data != NULL) | ||||
|             if (queues[i].data != nullptr) | ||||
|                 free(queues[i].data); | ||||
|         } | ||||
|         memset(queues, 0, sizeof(queues)); | ||||
|  | @ -147,7 +147,7 @@ struct ThreadQueueList { | |||
| 
 | ||||
|     inline void prepare(u32 priority) { | ||||
|         Queue *cur = &queues[priority]; | ||||
|         if (cur->next == NULL) | ||||
|         if (cur->next == nullptr) | ||||
|             link(priority, INITIAL_CAPACITY); | ||||
|     } | ||||
| 
 | ||||
|  | @ -176,7 +176,7 @@ private: | |||
| 
 | ||||
|         for (int i = (int) priority - 1; i >= 0; --i) | ||||
|         { | ||||
|             if (queues[i].next != NULL) | ||||
|             if (queues[i].next != nullptr) | ||||
|             { | ||||
|                 cur->next = queues[i].next; | ||||
|                 queues[i].next = cur; | ||||
|  | @ -193,7 +193,7 @@ private: | |||
|         int size = cur->end - cur->first; | ||||
|         if (size >= cur->capacity - 2)  { | ||||
|             IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType)); | ||||
|             if (new_data != NULL)  { | ||||
|             if (new_data != nullptr)  { | ||||
|                 cur->capacity *= 2; | ||||
|                 cur->data = new_data; | ||||
|             } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <time.h> | ||||
|  | @ -25,7 +25,7 @@ u32 Timer::GetTimeMs() | |||
|     return timeGetTime(); | ||||
| #else | ||||
|     struct timeval t; | ||||
|     (void)gettimeofday(&t, NULL); | ||||
|     (void)gettimeofday(&t, nullptr); | ||||
|     return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000)); | ||||
| #endif | ||||
| } | ||||
|  | @ -183,7 +183,7 @@ std::string Timer::GetTimeFormatted() | |||
|     return StringFromFormat("%s:%03i", tmp, tp.millitm); | ||||
| #else | ||||
|     struct timeval t; | ||||
|     (void)gettimeofday(&t, NULL); | ||||
|     (void)gettimeofday(&t, nullptr); | ||||
|     return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000)); | ||||
| #endif | ||||
| } | ||||
|  | @ -197,7 +197,7 @@ double Timer::GetDoubleTime() | |||
|     (void)::ftime(&tp); | ||||
| #else | ||||
|     struct timeval t; | ||||
|     (void)gettimeofday(&t, NULL); | ||||
|     (void)gettimeofday(&t, nullptr); | ||||
| #endif | ||||
|     // Get continuous timestamp
 | ||||
|     u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970(); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // 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 | ||||
|  |  | |||
|  | @ -281,28 +281,28 @@ int u8_read_escape_sequence(const char *str, u32 *dest) | |||
|     do { | ||||
|       digs[dno++] = str[i++]; | ||||
|     } while (octal_digit(str[i]) && dno < 3); | ||||
|     ch = strtol(digs, NULL, 8); | ||||
|     ch = strtol(digs, nullptr, 8); | ||||
|   } | ||||
|   else if (str[0] == 'x') { | ||||
|     while (hex_digit(str[i]) && dno < 2) { | ||||
|       digs[dno++] = str[i++]; | ||||
|     } | ||||
|     if (dno > 0) | ||||
|       ch = strtol(digs, NULL, 16); | ||||
|       ch = strtol(digs, nullptr, 16); | ||||
|   } | ||||
|   else if (str[0] == 'u') { | ||||
|     while (hex_digit(str[i]) && dno < 4) { | ||||
|       digs[dno++] = str[i++]; | ||||
|     } | ||||
|     if (dno > 0) | ||||
|       ch = strtol(digs, NULL, 16); | ||||
|       ch = strtol(digs, nullptr, 16); | ||||
|   } | ||||
|   else if (str[0] == 'U') { | ||||
|     while (hex_digit(str[i]) && dno < 8) { | ||||
|       digs[dno++] = str[i++]; | ||||
|     } | ||||
|     if (dno > 0) | ||||
|       ch = strtol(digs, NULL, 16); | ||||
|       ch = strtol(digs, nullptr, 16); | ||||
|   } | ||||
|   *dest = ch; | ||||
| 
 | ||||
|  | @ -353,7 +353,7 @@ const char *u8_strchr(const char *s, u32 ch, int *charn) | |||
|     lasti = i; | ||||
|     (*charn)++; | ||||
|   } | ||||
|   return NULL; | ||||
|   return nullptr; | ||||
| } | ||||
| 
 | ||||
| const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn) | ||||
|  | @ -378,7 +378,7 @@ const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn) | |||
|     lasti = i; | ||||
|     (*charn)++; | ||||
|   } | ||||
|   return NULL; | ||||
|   return nullptr; | ||||
| } | ||||
| 
 | ||||
| int u8_is_locale_utf8(const char *locale) | ||||
|  | @ -419,35 +419,35 @@ bool UTF8StringHasNonASCII(const char *utf8string) { | |||
| 
 | ||||
| std::string ConvertWStringToUTF8(const wchar_t *wstr) { | ||||
|     int len = (int)wcslen(wstr); | ||||
|     int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, NULL, NULL); | ||||
|     int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, nullptr, nullptr); | ||||
|     std::string s; | ||||
|     s.resize(size); | ||||
|     if (size > 0) { | ||||
|         WideCharToMultiByte(CP_UTF8, 0, wstr, len, &s[0], size, NULL, NULL); | ||||
|         WideCharToMultiByte(CP_UTF8, 0, wstr, len, &s[0], size, nullptr, nullptr); | ||||
|     } | ||||
|     return s; | ||||
| } | ||||
| 
 | ||||
| std::string ConvertWStringToUTF8(const std::wstring &wstr) { | ||||
|     int len = (int)wstr.size(); | ||||
|     int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, NULL, NULL); | ||||
|     int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, nullptr, nullptr); | ||||
|     std::string s; | ||||
|     s.resize(size); | ||||
|     if (size > 0) { | ||||
|         WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, &s[0], size, NULL, NULL); | ||||
|         WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, &s[0], size, nullptr, nullptr); | ||||
|     } | ||||
|     return s; | ||||
| } | ||||
| 
 | ||||
| void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) { | ||||
|     int len = (int)source.size(); | ||||
|     int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, NULL, 0); | ||||
|     int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0); | ||||
|     MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, dest, std::min((int)destSize, size)); | ||||
| } | ||||
| 
 | ||||
| std::wstring ConvertUTF8ToWString(const std::string &source) { | ||||
|     int len = (int)source.size(); | ||||
|     int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, NULL, 0); | ||||
|     int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0); | ||||
|     std::wstring str; | ||||
|     str.resize(size); | ||||
|     if (size > 0) { | ||||
|  |  | |||
|  | @ -18,35 +18,46 @@ set(SRCS | |||
|             arm/skyeye_common/vfp/vfpinstr.cpp | ||||
|             arm/skyeye_common/vfp/vfpsingle.cpp | ||||
|             file_sys/archive_romfs.cpp | ||||
|             file_sys/archive_savedata.cpp | ||||
|             file_sys/archive_sdmc.cpp | ||||
|             file_sys/archive_systemsavedata.cpp | ||||
|             file_sys/disk_archive.cpp | ||||
|             file_sys/file_romfs.cpp | ||||
|             file_sys/file_sdmc.cpp | ||||
|             file_sys/directory_romfs.cpp | ||||
|             file_sys/directory_sdmc.cpp | ||||
|             hle/kernel/address_arbiter.cpp | ||||
|             hle/kernel/archive.cpp | ||||
|             hle/kernel/event.cpp | ||||
|             hle/kernel/kernel.cpp | ||||
|             hle/kernel/mutex.cpp | ||||
|             hle/kernel/semaphore.cpp | ||||
|             hle/kernel/shared_memory.cpp | ||||
|             hle/kernel/thread.cpp | ||||
|             hle/service/ac_u.cpp | ||||
|             hle/service/act_u.cpp | ||||
|             hle/service/am_app.cpp | ||||
|             hle/service/am_net.cpp | ||||
|             hle/service/apt_a.cpp | ||||
|             hle/service/apt_u.cpp | ||||
|             hle/service/boss_u.cpp | ||||
|             hle/service/cfg_i.cpp | ||||
|             hle/service/cfg_u.cpp | ||||
|             hle/service/cecd_u.cpp | ||||
|             hle/service/cfg/cfg.cpp | ||||
|             hle/service/cfg/cfg_i.cpp | ||||
|             hle/service/cfg/cfg_u.cpp | ||||
|             hle/service/csnd_snd.cpp | ||||
|             hle/service/dsp_dsp.cpp | ||||
|             hle/service/err_f.cpp | ||||
|             hle/service/fs_user.cpp | ||||
|             hle/service/frd_u.cpp | ||||
|             hle/service/fs/archive.cpp | ||||
|             hle/service/fs/fs_user.cpp | ||||
|             hle/service/gsp_gpu.cpp | ||||
|             hle/service/hid_user.cpp | ||||
|             hle/service/http_c.cpp | ||||
|             hle/service/ir_rst.cpp | ||||
|             hle/service/ir_u.cpp | ||||
|             hle/service/ldr_ro.cpp | ||||
|             hle/service/mic_u.cpp | ||||
|             hle/service/ndm_u.cpp | ||||
|             hle/service/news_u.cpp | ||||
|             hle/service/nim_aoc.cpp | ||||
|             hle/service/nwm_uds.cpp | ||||
|             hle/service/pm_app.cpp | ||||
|             hle/service/ptm_u.cpp | ||||
|  | @ -59,10 +70,10 @@ set(SRCS | |||
|             hle/svc.cpp | ||||
|             hw/gpu.cpp | ||||
|             hw/hw.cpp | ||||
|             hw/ndma.cpp | ||||
|             loader/elf.cpp | ||||
|             loader/loader.cpp | ||||
|             loader/ncch.cpp | ||||
|             loader/3dsx.cpp | ||||
|             core.cpp | ||||
|             core_timing.cpp | ||||
|             mem_map.cpp | ||||
|  | @ -92,39 +103,51 @@ set(HEADERS | |||
|             arm/skyeye_common/vfp/vfp.h | ||||
|             arm/skyeye_common/vfp/vfp_helper.h | ||||
|             arm/arm_interface.h | ||||
|             file_sys/archive.h | ||||
|             file_sys/archive_backend.h | ||||
|             file_sys/archive_romfs.h | ||||
|             file_sys/archive_savedata.h | ||||
|             file_sys/archive_sdmc.h | ||||
|             file_sys/file.h | ||||
|             file_sys/archive_systemsavedata.h | ||||
|             file_sys/disk_archive.h | ||||
|             file_sys/file_backend.h | ||||
|             file_sys/file_romfs.h | ||||
|             file_sys/file_sdmc.h | ||||
|             file_sys/directory.h | ||||
|             file_sys/directory_backend.h | ||||
|             file_sys/directory_romfs.h | ||||
|             file_sys/directory_sdmc.h | ||||
|             hle/kernel/address_arbiter.h | ||||
|             hle/kernel/archive.h | ||||
|             hle/kernel/event.h | ||||
|             hle/kernel/kernel.h | ||||
|             hle/kernel/mutex.h | ||||
|             hle/kernel/semaphore.h | ||||
|             hle/kernel/session.h | ||||
|             hle/kernel/shared_memory.h | ||||
|             hle/kernel/thread.h | ||||
|             hle/service/ac_u.h | ||||
|             hle/service/act_u.h | ||||
|             hle/service/am_app.h | ||||
|             hle/service/am_net.h | ||||
|             hle/service/apt_a.h | ||||
|             hle/service/apt_u.h | ||||
|             hle/service/boss_u.h | ||||
|             hle/service/cfg_i.h | ||||
|             hle/service/cfg_u.h | ||||
|             hle/service/cecd_u.h | ||||
|             hle/service/cfg/cfg.h | ||||
|             hle/service/cfg/cfg_i.h | ||||
|             hle/service/cfg/cfg_u.h | ||||
|             hle/service/csnd_snd.h | ||||
|             hle/service/dsp_dsp.h | ||||
|             hle/service/err_f.h | ||||
|             hle/service/fs_user.h | ||||
|             hle/service/frd_u.h | ||||
|             hle/service/fs/archive.h | ||||
|             hle/service/fs/fs_user.h | ||||
|             hle/service/gsp_gpu.h | ||||
|             hle/service/hid_user.h | ||||
|             hle/service/http_c.h | ||||
|             hle/service/ir_rst.h | ||||
|             hle/service/ir_u.h | ||||
|             hle/service/ldr_ro.h | ||||
|             hle/service/mic_u.h | ||||
|             hle/service/ndm_u.h | ||||
|             hle/service/news_u.h | ||||
|             hle/service/nim_aoc.h | ||||
|             hle/service/nwm_uds.h | ||||
|             hle/service/pm_app.h | ||||
|             hle/service/ptm_u.h | ||||
|  | @ -139,10 +162,10 @@ set(HEADERS | |||
|             hle/svc.h | ||||
|             hw/gpu.h | ||||
|             hw/hw.h | ||||
|             hw/ndma.h | ||||
|             loader/elf.h | ||||
|             loader/loader.h | ||||
|             loader/ncch.h | ||||
|             loader/3dsx.h | ||||
|             core.h | ||||
|             core_timing.h | ||||
|             mem_map.h | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -77,6 +77,12 @@ public: | |||
|      */ | ||||
|     virtual u64 GetTicks() const = 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 | ||||
|      */ | ||||
|     virtual void AddTicks(u64 ticks) = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Saves the current CPU context | ||||
|      * @param ctx Thread context to save | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <string> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "core/arm/skyeye_common/armcpu.h" | ||||
|  | @ -47,68 +47,38 @@ ARM_DynCom::ARM_DynCom() : ticks(0) { | |||
| ARM_DynCom::~ARM_DynCom() { | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Set the Program Counter to an address | ||||
|  * @param addr Address to set PC to | ||||
|  */ | ||||
| void ARM_DynCom::SetPC(u32 pc) { | ||||
|     state->pc = state->Reg[15] = pc; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Get the current Program Counter | ||||
|  * @return Returns current PC | ||||
|  */ | ||||
| u32 ARM_DynCom::GetPC() const { | ||||
|     return state->Reg[15]; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get an ARM register | ||||
|  * @param index Register index (0-15) | ||||
|  * @return Returns the value in the register | ||||
|  */ | ||||
| u32 ARM_DynCom::GetReg(int index) const { | ||||
|     return state->Reg[index]; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Set an ARM register | ||||
|  * @param index Register index (0-15) | ||||
|  * @param value Value to set register to | ||||
|  */ | ||||
| void ARM_DynCom::SetReg(int index, u32 value) { | ||||
|     state->Reg[index] = value; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the current CPSR register | ||||
|  * @return Returns the value of the CPSR register | ||||
|  */ | ||||
| u32 ARM_DynCom::GetCPSR() const { | ||||
|     return state->Cpsr; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Set the current CPSR register | ||||
|  * @param cpsr Value to set CPSR to | ||||
|  */ | ||||
| void ARM_DynCom::SetCPSR(u32 cpsr) { | ||||
|     state->Cpsr = cpsr; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns the number of clock ticks since the last reset | ||||
|  * @return Returns number of clock ticks | ||||
|  */ | ||||
| u64 ARM_DynCom::GetTicks() const { | ||||
|     return ticks; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Executes the given number of instructions | ||||
|  * @param num_instructions Number of instructions to executes | ||||
|  */ | ||||
| void ARM_DynCom::AddTicks(u64 ticks) { | ||||
|     this->ticks += ticks; | ||||
| } | ||||
| 
 | ||||
| void ARM_DynCom::ExecuteInstructions(int num_instructions) { | ||||
|     state->NumInstrsToExecute = num_instructions; | ||||
| 
 | ||||
|  | @ -118,11 +88,6 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) { | |||
|     ticks += InterpreterMainLoop(state.get()); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Saves the current CPU context | ||||
|  * @param ctx Thread context to save | ||||
|  * @todo Do we need to save Reg[15] and NextInstr? | ||||
|  */ | ||||
| void ARM_DynCom::SaveContext(ThreadContext& ctx) { | ||||
|     memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); | ||||
|     memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); | ||||
|  | @ -139,11 +104,6 @@ void ARM_DynCom::SaveContext(ThreadContext& ctx) { | |||
|     ctx.mode = state->NextInstr; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads a CPU context | ||||
|  * @param ctx Thread context to load | ||||
|  * @param Do we need to load Reg[15] and NextInstr? | ||||
|  */ | ||||
| void ARM_DynCom::LoadContext(const ThreadContext& ctx) { | ||||
|     memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); | ||||
|     memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); | ||||
|  | @ -160,7 +120,6 @@ void ARM_DynCom::LoadContext(const ThreadContext& ctx) { | |||
|     state->NextInstr = ctx.mode; | ||||
| } | ||||
| 
 | ||||
| /// Prepare core for thread reschedule (if needed to correctly handle state)
 | ||||
| void ARM_DynCom::PrepareReschedule() { | ||||
|     state->NumInstrsToExecute = 0; | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -27,14 +27,14 @@ public: | |||
|      * Get the current Program Counter | ||||
|      * @return Returns current PC | ||||
|      */ | ||||
|     u32 GetPC() const; | ||||
|     u32 GetPC() const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Get an ARM register | ||||
|      * @param index Register index (0-15) | ||||
|      * @return Returns the value in the register | ||||
|      */ | ||||
|     u32 GetReg(int index) const; | ||||
|     u32 GetReg(int index) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Set an ARM register | ||||
|  | @ -47,7 +47,7 @@ public: | |||
|      * Get the current CPSR register | ||||
|      * @return Returns the value of the CPSR register | ||||
|      */ | ||||
|     u32 GetCPSR() const; | ||||
|     u32 GetCPSR() const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Set the current CPSR register | ||||
|  | @ -59,7 +59,13 @@ public: | |||
|      * Returns the number of clock ticks since the last reset | ||||
|      * @return Returns number of clock ticks | ||||
|      */ | ||||
|     u64 GetTicks() const; | ||||
|     u64 GetTicks() const override; | ||||
| 
 | ||||
|     /**
 | ||||
|     * 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 | ||||
|     */ | ||||
|     void AddTicks(u64 ticks) override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Saves the current CPU context | ||||
|  |  | |||
|  | @ -1,402 +1,443 @@ | |||
| /* Copyright (C) 
 | ||||
| * 2012 - Michael.Kang blackfin.kang@gmail.com | ||||
| * 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. | ||||
| *  | ||||
| */ | ||||
| /**
 | ||||
| * @file arm_dyncom_dec.cpp | ||||
| * @brief Some common utility for arm decoder | ||||
| * @author Michael.Kang blackfin.kang@gmail.com | ||||
| * @version 7849 | ||||
| * @date 2012-03-15 | ||||
| */ | ||||
| // Copyright 2012 Michael Kang, 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "core/arm/skyeye_common/arm_regformat.h" | ||||
| #include "core/arm/skyeye_common/armdefs.h" | ||||
| #include "core/arm/dyncom/arm_dyncom_dec.h" | ||||
| 
 | ||||
| const ISEITEM arm_instruction[] = { | ||||
| 	#define VFP_DECODE | ||||
| 	#include "core/arm/skyeye_common/vfp/vfpinstr.cpp" | ||||
| 	#undef VFP_DECODE | ||||
| 	{"srs"	,  4	,  6	, 25, 31, 0x0000007c, 22, 22, 0x00000001, 16, 20, 0x0000000d,  8, 11, 0x00000005}, | ||||
| 	{"rfe"	,  4	,  6	, 25, 31, 0x0000007c, 22, 22, 0x00000000, 20, 20, 0x00000001,  8, 11, 0x0000000a}, | ||||
| 	{"bkpt"	,  2	,  3	, 20, 31, 0x00000e12,  4,  7, 0x00000007}, | ||||
| 	{"blx"	,  1	,  3	, 25, 31, 0x0000007d}, | ||||
| 	{"cps"	,  3	,  6	, 20, 31, 0x00000f10, 16, 16, 0x00000000,  5,  5, 0x00000000}, | ||||
| 	{"pld"	,  4	,  4	, 26, 31, 0x0000003d, 24, 24, 0x00000001, 20, 22, 0x00000005, 12, 15, 0x0000000f}, | ||||
| 	{"setend"	,  2	,  6	, 16, 31, 0x0000f101,  4,  7, 0x00000000}, | ||||
| 	{"clrex"	,  1	,  6	,  0, 31, 0xf57ff01f}, | ||||
| 	{"rev16"	,  2	,  6	, 16, 27, 0x000006bf,  4, 11, 0x000000fb}, | ||||
| 	{"usad8"	,  3	,  6	, 20, 27, 0x00000078, 12, 15, 0x0000000f,  4,  7, 0x00000001}, | ||||
| 	{"sxtb"	,  2	,  6	, 16, 27, 0x000006af,  4,  7, 0x00000007}, | ||||
| 	{"uxtb"	,  2	,  6	, 16, 27, 0x000006ef,  4,  7, 0x00000007}, | ||||
| 	{"sxth"	,  2	,  6	, 16, 27, 0x000006bf,  4,  7, 0x00000007}, | ||||
| 	{"sxtb16"	,  2	,  6	, 16, 27, 0x0000068f,  4,  7, 0x00000007}, | ||||
| 	{"uxth"	,  2	,  6	, 16, 27, 0x000006ff,  4,  7, 0x00000007}, | ||||
| 	{"uxtb16"	,  2	,  6	, 16, 27, 0x000006cf,  4,  7, 0x00000007}, | ||||
| 	{"cpy"	,  2	,  6	, 20, 27, 0x0000001a,  4, 11, 0x00000000}, | ||||
| 	{"uxtab"	,  2	,  6	, 20, 27, 0x0000006e,  4,  9, 0x00000007}, | ||||
| 	{"ssub8"	,  2	,  6	, 20, 27, 0x00000061,  4,  7, 0x0000000f}, | ||||
| 	{"shsub8"	,  2	,  6	, 20, 27, 0x00000063,  4,  7, 0x0000000f}, | ||||
| 	{"ssubaddx"	,  2	,  6	, 20, 27, 0x00000061,  4,  7, 0x00000005}, | ||||
| 	{"strex"	,  2	,  6	, 20, 27, 0x00000018,  4,  7, 0x00000009}, | ||||
| 	{"strexb"	,  2	,  7	, 20, 27, 0x0000001c,  4,  7, 0x00000009}, | ||||
| 	{"swp"	,  2	,  0	, 20, 27, 0x00000010,  4,  7, 0x00000009}, | ||||
| 	{"swpb"	,  2	,  0	, 20, 27, 0x00000014,  4,  7, 0x00000009}, | ||||
| 	{"ssub16"	,  2	,  6	, 20, 27, 0x00000061,  4,  7, 0x00000007}, | ||||
| 	{"ssat16"	,  2	,  6	, 20, 27, 0x0000006a,  4,  7, 0x00000003}, | ||||
| 	{"shsubaddx"	,  2	,  6	, 20, 27, 0x00000063,  4,  7, 0x00000005}, | ||||
| 	{"qsubaddx"	,  2	,  6	, 20, 27, 0x00000062,  4,  7, 0x00000005}, | ||||
| 	{"shaddsubx"	,  2	,  6	, 20, 27, 0x00000063,  4,  7, 0x00000003}, | ||||
| 	{"shadd8"	,  2	,  6	, 20, 27, 0x00000063,  4,  7, 0x00000009}, | ||||
| 	{"shadd16"	,  2	,  6	, 20, 27, 0x00000063,  4,  7, 0x00000001}, | ||||
| 	{"sel"	,  2	,  6	, 20, 27, 0x00000068,  4,  7, 0x0000000b}, | ||||
| 	{"saddsubx"	,  2	,  6	, 20, 27, 0x00000061,  4,  7, 0x00000003}, | ||||
| 	{"sadd8"	,  2	,  6	, 20, 27, 0x00000061,  4,  7, 0x00000009}, | ||||
| 	{"sadd16"	,  2	,  6	, 20, 27, 0x00000061,  4,  7, 0x00000001}, | ||||
| 	{"shsub16"	,  2	,  6	, 20, 27, 0x00000063,  4,  7, 0x00000007}, | ||||
| 	{"umaal"	,  2	,  6	, 20, 27, 0x00000004,  4,  7, 0x00000009}, | ||||
| 	{"uxtab16"	,  2	,  6	, 20, 27, 0x0000006c,  4,  7, 0x00000007}, | ||||
| 	{"usubaddx"	,  2	,  6	, 20, 27, 0x00000065,  4,  7, 0x00000005}, | ||||
| 	{"usub8"	,  2	,  6	, 20, 27, 0x00000065,  4,  7, 0x0000000f}, | ||||
| 	{"usub16"	,  2	,  6	, 20, 27, 0x00000065,  4,  7, 0x00000007}, | ||||
| 	{"usat16"	,  2	,  6	, 20, 27, 0x0000006e,  4,  7, 0x00000003}, | ||||
| 	{"usada8"	,  2	,  6	, 20, 27, 0x00000078,  4,  7, 0x00000001}, | ||||
| 	{"uqsubaddx"	,  2	,  6	, 20, 27, 0x00000066,  4,  7, 0x00000005}, | ||||
| 	{"uqsub8"	,  2	,  6	, 20, 27, 0x00000066,  4,  7, 0x0000000f}, | ||||
| 	{"uqsub16"	,  2	,  6	, 20, 27, 0x00000066,  4,  7, 0x00000007}, | ||||
| 	{"uqaddsubx"	,  2	,  6	, 20, 27, 0x00000066,  4,  7, 0x00000003}, | ||||
| 	{"uqadd8"	,  2	,  6	, 20, 27, 0x00000066,  4,  7, 0x00000009}, | ||||
| 	{"uqadd16"	,  2	,  6	, 20, 27, 0x00000066,  4,  7, 0x00000001}, | ||||
| 	{"sxtab"	,  2	,  6	, 20, 27, 0x0000006a,  4,  7, 0x00000007}, | ||||
| 	{"uhsubaddx"	,  2	,  6	, 20, 27, 0x00000067,  4,  7, 0x00000005}, | ||||
| 	{"uhsub8"	,  2	,  6	, 20, 27, 0x00000067,  4,  7, 0x0000000f}, | ||||
| 	{"uhsub16"	,  2	,  6	, 20, 27, 0x00000067,  4,  7, 0x00000007}, | ||||
| 	{"uhaddsubx"	,  2	,  6	, 20, 27, 0x00000067,  4,  7, 0x00000003}, | ||||
| 	{"uhadd8"	,  2	,  6	, 20, 27, 0x00000067,  4,  7, 0x00000009}, | ||||
| 	{"uhadd16"	,  2	,  6	, 20, 27, 0x00000067,  4,  7, 0x00000001}, | ||||
| 	{"uaddsubx"	,  2	,  6	, 20, 27, 0x00000065,  4,  7, 0x00000003}, | ||||
| 	{"uadd8"	,  2	,  6	, 20, 27, 0x00000065,  4,  7, 0x00000009}, | ||||
| 	{"uadd16"	,  2	,  6	, 20, 27, 0x00000065,  4,  7, 0x00000001}, | ||||
| 	{"sxtah"	,  2	,  6	, 20, 27, 0x0000006b,  4,  7, 0x00000007}, | ||||
| 	{"sxtab16"	,  2	,  6	, 20, 27, 0x00000068,  4,  7, 0x00000007}, | ||||
| 	{"qadd8"	,  2	,  6	, 20, 27, 0x00000062,  4,  7, 0x00000009}, | ||||
| 	{"bxj"	,  2	,  5	, 20, 27, 0x00000012,  4,  7, 0x00000002}, | ||||
| 	{"clz"	,  2	,  3	, 20, 27, 0x00000016,  4,  7, 0x00000001}, | ||||
| 	{"uxtah"	,  2	,  6	, 20, 27, 0x0000006f,  4,  7, 0x00000007}, | ||||
| 	{"bx"	,  2	,  2	, 20, 27, 0x00000012,  4,  7, 0x00000001}, | ||||
| 	{"rev"	,  2	,  6	, 20, 27, 0x0000006b,  4,  7, 0x00000003}, | ||||
| 	{"blx"	,  2	,  3	, 20, 27, 0x00000012,  4,  7, 0x00000003}, | ||||
| 	{"revsh"	,  2	,  6	, 20, 27, 0x0000006f,  4,  7, 0x0000000b}, | ||||
| 	{"qadd"	,  2	,  4	, 20, 27, 0x00000010,  4,  7, 0x00000005}, | ||||
| 	{"qadd16"	,  2	,  6	, 20, 27, 0x00000062,  4,  7, 0x00000001}, | ||||
| 	{"qaddsubx"	,  2	,  6	, 20, 27, 0x00000062,  4,  7, 0x00000003}, | ||||
| 	{"ldrex"	,  2	,  0	, 20, 27, 0x00000019,  4,  7, 0x00000009}, | ||||
| 	{"qdadd"	,  2	,  4	, 20, 27, 0x00000014,  4,  7, 0x00000005}, | ||||
| 	{"qdsub"	,  2	,  4	, 20, 27, 0x00000016,  4,  7, 0x00000005}, | ||||
| 	{"qsub"	,  2	,  4	, 20, 27, 0x00000012,  4,  7, 0x00000005}, | ||||
| 	{"ldrexb"	,  2	,  7	, 20, 27, 0x0000001d,  4,  7, 0x00000009}, | ||||
| 	{"qsub8"	,  2	,  6	, 20, 27, 0x00000062,  4,  7, 0x0000000f}, | ||||
| 	{"qsub16"	,  2	,  6	, 20, 27, 0x00000062,  4,  7, 0x00000007}, | ||||
| 	{"smuad"	,  4	,  6	, 20, 27, 0x00000070, 12, 15, 0x0000000f,  6,  7, 0x00000000,  4,  4, 0x00000001}, | ||||
| 	{"smmul"	,  4	,  6	, 20, 27, 0x00000075, 12, 15, 0x0000000f,  6,  7, 0x00000000,  4,  4, 0x00000001}, | ||||
| 	{"smusd"	,  4	,  6	, 20, 27, 0x00000070, 12, 15, 0x0000000f,  6,  7, 0x00000001,  4,  4, 0x00000001}, | ||||
| 	{"smlsd"	,  3	,  6	, 20, 27, 0x00000070,  6,  7, 0x00000001,  4,  4, 0x00000001}, | ||||
| 	{"smlsld"	,  3	,  6	, 20, 27, 0x00000074,  6,  7, 0x00000001,  4,  4, 0x00000001}, | ||||
| 	{"smmla"	,  3	,  6	, 20, 27, 0x00000075,  6,  7, 0x00000000,  4,  4, 0x00000001}, | ||||
| 	{"smmls"	,  3	,  6	, 20, 27, 0x00000075,  6,  7, 0x00000003,  4,  4, 0x00000001}, | ||||
| 	{"smlald"	,  3	,  6	, 20, 27, 0x00000074,  6,  7, 0x00000000,  4,  4, 0x00000001}, | ||||
| 	{"smlad"	,  3	,  6	, 20, 27, 0x00000070,  6,  7, 0x00000000,  4,  4, 0x00000001}, | ||||
| 	{"smlaw"	,  3	,  4	, 20, 27, 0x00000012,  7,  7, 0x00000001,  4,  5, 0x00000000}, | ||||
| 	{"smulw"	,  3	,  4	, 20, 27, 0x00000012,  7,  7, 0x00000001,  4,  5, 0x00000002}, | ||||
| 	{"pkhtb"	,  2	,  6	, 20, 27, 0x00000068,  4,  6, 0x00000005}, | ||||
| 	{"pkhbt"	,  2	,  6	, 20, 27, 0x00000068,  4,  6, 0x00000001}, | ||||
| 	{"smul"	,  3	,  4	, 20, 27, 0x00000016,  7,  7, 0x00000001,  4,  4, 0x00000000}, | ||||
| 	{"smlalxy"	,  3	,  4	, 20, 27, 0x00000014,  7,  7, 0x00000001,  4,  4, 0x00000000}, | ||||
| //	{"smlal"	,  2	,  4	, 21, 27, 0x00000007,  4,  7, 0x00000009},
 | ||||
| 	{"smla"	,  3	,  4	, 20, 27, 0x00000010,  7,  7, 0x00000001,  4,  4, 0x00000000}, | ||||
| 	{"mcrr"	,  1	,  6	, 20, 27, 0x000000c4}, | ||||
| 	{"mrrc"	,  1	,  6	, 20, 27, 0x000000c5}, | ||||
| 	{"cmp"	,  2	,  0	, 26, 27, 0x00000000, 20, 24, 0x00000015}, | ||||
| 	{"tst"	,  2	,  0	, 26, 27, 0x00000000, 20, 24, 0x00000011}, | ||||
| 	{"teq"	,  2	,  0	, 26, 27, 0x00000000, 20, 24, 0x00000013}, | ||||
| 	{"cmn"	,  2	,  0	, 26, 27, 0x00000000, 20, 24, 0x00000017}, | ||||
| 	{"smull"	,  2	,  0	, 21, 27, 0x00000006,  4,  7, 0x00000009}, | ||||
| 	{"umull"	,  2	,  0	, 21, 27, 0x00000004,  4,  7, 0x00000009}, | ||||
| 	{"umlal"	,  2	,  0	, 21, 27, 0x00000005,  4,  7, 0x00000009}, | ||||
| 	{"smlal"	,  2	,  0	, 21, 27, 0x00000007,  4,  7, 0x00000009}, | ||||
| 	{"mul"	,  2	,  0	, 21, 27, 0x00000000,  4,  7, 0x00000009}, | ||||
| 	{"mla"	,  2	,  0	, 21, 27, 0x00000001,  4,  7, 0x00000009}, | ||||
| 	{"ssat"	,  2	,  6	, 21, 27, 0x00000035,  4,  5, 0x00000001}, | ||||
| 	{"usat"	,  2	,  6	, 21, 27, 0x00000037,  4,  5, 0x00000001}, | ||||
| 	{"mrs"	,  4	,  0	, 23, 27, 0x00000002, 20, 21, 0x00000000, 16, 19, 0x0000000f,  0, 11, 0x00000000}, | ||||
| 	{"msr"	,  3	,  0	, 23, 27, 0x00000002, 20, 21, 0x00000002,  4,  7, 0x00000000}, | ||||
| 	{"and"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x00000000}, | ||||
| 	{"bic"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x0000000e}, | ||||
| 	{"ldm"	,  3	,  0	, 25, 27, 0x00000004, 20, 22, 0x00000005, 15, 15, 0x00000000}, | ||||
| 	{"eor"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x00000001}, | ||||
| 	{"add"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x00000004}, | ||||
| 	{"rsb"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x00000003}, | ||||
| 	{"rsc"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x00000007}, | ||||
| 	{"sbc"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x00000006}, | ||||
| 	{"adc"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x00000005}, | ||||
| 	{"sub"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x00000002}, | ||||
| 	{"orr"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x0000000c}, | ||||
| 	{"mvn"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x0000000f}, | ||||
| 	{"mov"	,  2	,  0	, 26, 27, 0x00000000, 21, 24, 0x0000000d}, | ||||
| 	{"stm"	,  2	,  0	, 25, 27, 0x00000004, 20, 22, 0x00000004}, | ||||
| 	{"ldm"	,  4	,  0	, 25, 27, 0x00000004, 22, 22, 0x00000001, 20, 20, 0x00000001, 15, 15, 0x00000001}, | ||||
| 	{"ldrsh"	,  3	,  2	, 25, 27, 0x00000000, 20, 20, 0x00000001,  4,  7, 0x0000000f}, | ||||
| 	{"stm"	,  3	,  0	, 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000000}, | ||||
| 	{"ldm"	,  3	,  0	, 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000001}, | ||||
| 	{"ldrsb"	,  3	,  2	, 25, 27, 0x00000000, 20, 20, 0x00000001,  4,  7, 0x0000000d}, | ||||
| 	{"strd"	,  3	,  4	, 25, 27, 0x00000000, 20, 20, 0x00000000,  4,  7, 0x0000000f}, | ||||
| 	{"ldrh"	,  3	,  0	, 25, 27, 0x00000000, 20, 20, 0x00000001,  4,  7, 0x0000000b}, | ||||
| 	{"strh"	,  3	,  0	, 25, 27, 0x00000000, 20, 20, 0x00000000,  4,  7, 0x0000000b}, | ||||
| 	{"ldrd"	,  3	,  4	, 25, 27, 0x00000000, 20, 20, 0x00000000,  4,  7, 0x0000000d}, | ||||
| 	{"strt"	,  3	,  0	, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000002}, | ||||
| 	{"strbt"	,  3	,  0	, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000006}, | ||||
| 	{"ldrbt"	,  3	,  0	, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000007}, | ||||
| 	{"ldrt"	,  3	,  0	, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000003}, | ||||
| 	{"mrc"	,  3	,  6	, 24, 27, 0x0000000e, 20, 20, 0x00000001,  4,  4, 0x00000001}, | ||||
| 	{"mcr"	,  3	,  0	, 24, 27, 0x0000000e, 20, 20, 0x00000000,  4,  4, 0x00000001}, | ||||
| 	{"msr"	,  2	,  0	, 23, 27, 0x00000006, 20, 21, 0x00000002}, | ||||
| 	{"ldrb"	,  3	,  0	, 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000001}, | ||||
| 	{"strb"	,  3	,  0	, 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000000}, | ||||
| 	{"ldr"	,  4	,  0	, 28, 31, 0x0000000e, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001}, | ||||
| 	{"ldrcond"	,  3	,  0	, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001}, | ||||
| 	{"str"	,  3	,  0	, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000000}, | ||||
| 	{"cdp"	,  2	,  0	, 24, 27, 0x0000000e,  4,  4, 0x00000000}, | ||||
| 	{"stc"	,  2	,  0	, 25, 27, 0x00000006, 20, 20, 0x00000000}, | ||||
| 	{"ldc"	,  2	,  0	, 25, 27, 0x00000006, 20, 20, 0x00000001}, | ||||
| 	{"swi"	,  1	,  0	, 24, 27, 0x0000000f}, | ||||
| 	{"bbl"	,  1	,  0	, 25, 27, 0x00000005}, | ||||
|     { "vmla", 4, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 4, 4, 0 }, | ||||
|     { "vmls", 7, ARMVFP2, 28, 31, 0xF, 25, 27, 0x1, 23, 23, 1, 11, 11, 0, 8, 9, 0x2, 6, 6, 1, 4, 4, 0 }, | ||||
|     { "vnmla", 4, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 4, 4, 0 }, | ||||
|     { "vnmla", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }, | ||||
|     { "vnmls", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }, | ||||
|     { "vnmul", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }, | ||||
|     { "vmul", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }, | ||||
|     { "vadd", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x3, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }, | ||||
|     { "vsub", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x3, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }, | ||||
|     { "vdiv", 5, ARMVFP2, 23, 27, 0x1D, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }, | ||||
|     { "vmov(i)", 4, ARMVFP3, 23, 27, 0x1D, 20, 21, 0x3, 9, 11, 0x5, 4, 7, 0 }, | ||||
|     { "vmov(r)", 5, ARMVFP3, 23, 27, 0x1D, 16, 21, 0x30, 9, 11, 0x5, 6, 7, 1, 4, 4, 0 }, | ||||
|     { "vabs", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x30, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 }, | ||||
|     { "vneg", 5, ARMVFP2, 23, 27, 0x1D, 17, 21, 0x18, 9, 11, 0x5, 6, 7, 1, 4, 4, 0 }, | ||||
|     { "vsqrt", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x31, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 }, | ||||
|     { "vcmp", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x34, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }, | ||||
|     { "vcmp2", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x35, 9, 11, 0x5, 0, 6, 0x40 }, | ||||
|     { "vcvt(bds)", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x37, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 }, | ||||
|     { "vcvt(bff)", 6, ARMVFP3, 23, 27, 0x1D, 19, 21, 0x7, 17, 17, 0x1, 9, 11, 5, 6, 6, 1 }, | ||||
|     { "vcvt(bfi)", 5, ARMVFP2, 23, 27, 0x1D, 19, 21, 0x7, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }, | ||||
|     { "vmovbrs", 3, ARMVFP2, 21, 27, 0x70, 8, 11, 0xA, 0, 6, 0x10 }, | ||||
|     { "vmsr", 2, ARMVFP2, 20, 27, 0xEE, 0, 11, 0xA10 }, | ||||
|     { "vmovbrc", 4, ARMVFP2, 23, 27, 0x1C, 20, 20, 0x0, 8, 11, 0xB, 0, 4, 0x10 }, | ||||
|     { "vmrs", 2, ARMVFP2, 20, 27, 0xEF, 0, 11, 0xA10 }, | ||||
|     { "vmovbcr", 4, ARMVFP2, 24, 27, 0xE, 20, 20, 1, 8, 11, 0xB, 0, 4, 0x10 }, | ||||
|     { "vmovbrrss", 3, ARMVFP2, 21, 27, 0x62, 8, 11, 0xA, 4, 4, 1 }, | ||||
|     { "vmovbrrd", 3, ARMVFP2, 21, 27, 0x62, 6, 11, 0x2C, 4, 4, 1 }, | ||||
|     { "vstr", 3, ARMVFP2, 24, 27, 0xD, 20, 21, 0, 9, 11, 5 }, | ||||
|     { "vpush", 3, ARMVFP2, 23, 27, 0x1A, 16, 21, 0x2D, 9, 11, 5 }, | ||||
|     { "vstm", 3, ARMVFP2, 25, 27, 0x6, 20, 20, 0, 9, 11, 5 }, | ||||
|     { "vpop", 3, ARMVFP2, 23, 27, 0x19, 16, 21, 0x3D, 9, 11, 5 }, | ||||
|     { "vldr", 3, ARMVFP2, 24, 27, 0xD, 20, 21, 1, 9, 11, 5 }, | ||||
|     { "vldm", 3, ARMVFP2, 25, 27, 0x6, 20, 20, 1, 9, 11, 5 }, | ||||
| 
 | ||||
|     { "srs", 4, 6, 25, 31, 0x0000007c, 22, 22, 0x00000001, 16, 20, 0x0000000d, 8, 11, 0x00000005 }, | ||||
|     { "rfe", 4, 6, 25, 31, 0x0000007c, 22, 22, 0x00000000, 20, 20, 0x00000001, 8, 11, 0x0000000a }, | ||||
|     { "bkpt", 2, 3, 20, 31, 0x00000e12, 4, 7, 0x00000007 }, | ||||
|     { "blx", 1, 3, 25, 31, 0x0000007d }, | ||||
|     { "cps", 3, 6, 20, 31, 0x00000f10, 16, 16, 0x00000000, 5, 5, 0x00000000 }, | ||||
|     { "pld", 4, 4, 26, 31, 0x0000003d, 24, 24, 0x00000001, 20, 22, 0x00000005, 12, 15, 0x0000000f }, | ||||
|     { "setend", 2, 6, 16, 31, 0x0000f101, 4, 7, 0x00000000 }, | ||||
|     { "clrex", 1, 6, 0, 31, 0xf57ff01f }, | ||||
|     { "rev16", 2, 6, 16, 27, 0x000006bf, 4, 11, 0x000000fb }, | ||||
|     { "usad8", 3, 6, 20, 27, 0x00000078, 12, 15, 0x0000000f, 4, 7, 0x00000001 }, | ||||
|     { "sxtb", 2, 6, 16, 27, 0x000006af, 4, 7, 0x00000007 }, | ||||
|     { "uxtb", 2, 6, 16, 27, 0x000006ef, 4, 7, 0x00000007 }, | ||||
|     { "sxth", 2, 6, 16, 27, 0x000006bf, 4, 7, 0x00000007 }, | ||||
|     { "sxtb16", 2, 6, 16, 27, 0x0000068f, 4, 7, 0x00000007 }, | ||||
|     { "uxth", 2, 6, 16, 27, 0x000006ff, 4, 7, 0x00000007 }, | ||||
|     { "uxtb16", 2, 6, 16, 27, 0x000006cf, 4, 7, 0x00000007 }, | ||||
|     { "cpy", 2, 6, 20, 27, 0x0000001a, 4, 11, 0x00000000 }, | ||||
|     { "uxtab", 2, 6, 20, 27, 0x0000006e, 4, 9, 0x00000007 }, | ||||
|     { "ssub8", 2, 6, 20, 27, 0x00000061, 4, 7, 0x0000000f }, | ||||
|     { "shsub8", 2, 6, 20, 27, 0x00000063, 4, 7, 0x0000000f }, | ||||
|     { "ssubaddx", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000005 }, | ||||
|     { "strex", 2, 6, 20, 27, 0x00000018, 4, 7, 0x00000009 }, | ||||
|     { "strexb", 2, 7, 20, 27, 0x0000001c, 4, 7, 0x00000009 }, | ||||
|     { "swp", 2, 0, 20, 27, 0x00000010, 4, 7, 0x00000009 }, | ||||
|     { "swpb", 2, 0, 20, 27, 0x00000014, 4, 7, 0x00000009 }, | ||||
|     { "ssub16", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000007 }, | ||||
|     { "ssat16", 2, 6, 20, 27, 0x0000006a, 4, 7, 0x00000003 }, | ||||
|     { "shsubaddx", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000005 }, | ||||
|     { "qsubaddx", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000005 }, | ||||
|     { "shaddsubx", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000003 }, | ||||
|     { "shadd8", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000009 }, | ||||
|     { "shadd16", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000001 }, | ||||
|     { "sel", 2, 6, 20, 27, 0x00000068, 4, 7, 0x0000000b }, | ||||
|     { "saddsubx", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000003 }, | ||||
|     { "sadd8", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000009 }, | ||||
|     { "sadd16", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000001 }, | ||||
|     { "shsub16", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000007 }, | ||||
|     { "umaal", 2, 6, 20, 27, 0x00000004, 4, 7, 0x00000009 }, | ||||
|     { "uxtab16", 2, 6, 20, 27, 0x0000006c, 4, 7, 0x00000007 }, | ||||
|     { "usubaddx", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000005 }, | ||||
|     { "usub8", 2, 6, 20, 27, 0x00000065, 4, 7, 0x0000000f }, | ||||
|     { "usub16", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000007 }, | ||||
|     { "usat16", 2, 6, 20, 27, 0x0000006e, 4, 7, 0x00000003 }, | ||||
|     { "usada8", 2, 6, 20, 27, 0x00000078, 4, 7, 0x00000001 }, | ||||
|     { "uqsubaddx", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000005 }, | ||||
|     { "uqsub8", 2, 6, 20, 27, 0x00000066, 4, 7, 0x0000000f }, | ||||
|     { "uqsub16", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000007 }, | ||||
|     { "uqaddsubx", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000003 }, | ||||
|     { "uqadd8", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000009 }, | ||||
|     { "uqadd16", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000001 }, | ||||
|     { "sxtab", 2, 6, 20, 27, 0x0000006a, 4, 7, 0x00000007 }, | ||||
|     { "uhsubaddx", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000005 }, | ||||
|     { "uhsub8", 2, 6, 20, 27, 0x00000067, 4, 7, 0x0000000f }, | ||||
|     { "uhsub16", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000007 }, | ||||
|     { "uhaddsubx", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000003 }, | ||||
|     { "uhadd8", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000009 }, | ||||
|     { "uhadd16", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000001 }, | ||||
|     { "uaddsubx", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000003 }, | ||||
|     { "uadd8", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000009 }, | ||||
|     { "uadd16", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000001 }, | ||||
|     { "sxtah", 2, 6, 20, 27, 0x0000006b, 4, 7, 0x00000007 }, | ||||
|     { "sxtab16", 2, 6, 20, 27, 0x00000068, 4, 7, 0x00000007 }, | ||||
|     { "qadd8", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000009 }, | ||||
|     { "bxj", 2, 5, 20, 27, 0x00000012, 4, 7, 0x00000002 }, | ||||
|     { "clz", 2, 3, 20, 27, 0x00000016, 4, 7, 0x00000001 }, | ||||
|     { "uxtah", 2, 6, 20, 27, 0x0000006f, 4, 7, 0x00000007 }, | ||||
|     { "bx", 2, 2, 20, 27, 0x00000012, 4, 7, 0x00000001 }, | ||||
|     { "rev", 2, 6, 20, 27, 0x0000006b, 4, 7, 0x00000003 }, | ||||
|     { "blx", 2, 3, 20, 27, 0x00000012, 4, 7, 0x00000003 }, | ||||
|     { "revsh", 2, 6, 20, 27, 0x0000006f, 4, 7, 0x0000000b }, | ||||
|     { "qadd", 2, 4, 20, 27, 0x00000010, 4, 7, 0x00000005 }, | ||||
|     { "qadd16", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000001 }, | ||||
|     { "qaddsubx", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000003 }, | ||||
|     { "ldrex", 2, 0, 20, 27, 0x00000019, 4, 7, 0x00000009 }, | ||||
|     { "qdadd", 2, 4, 20, 27, 0x00000014, 4, 7, 0x00000005 }, | ||||
|     { "qdsub", 2, 4, 20, 27, 0x00000016, 4, 7, 0x00000005 }, | ||||
|     { "qsub", 2, 4, 20, 27, 0x00000012, 4, 7, 0x00000005 }, | ||||
|     { "ldrexb", 2, 7, 20, 27, 0x0000001d, 4, 7, 0x00000009 }, | ||||
|     { "qsub8", 2, 6, 20, 27, 0x00000062, 4, 7, 0x0000000f }, | ||||
|     { "qsub16", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000007 }, | ||||
|     { "smuad", 4, 6, 20, 27, 0x00000070, 12, 15, 0x0000000f, 6, 7, 0x00000000, 4, 4, 0x00000001 }, | ||||
|     { "smmul", 4, 6, 20, 27, 0x00000075, 12, 15, 0x0000000f, 6, 7, 0x00000000, 4, 4, 0x00000001 }, | ||||
|     { "smusd", 4, 6, 20, 27, 0x00000070, 12, 15, 0x0000000f, 6, 7, 0x00000001, 4, 4, 0x00000001 }, | ||||
|     { "smlsd", 3, 6, 20, 27, 0x00000070, 6, 7, 0x00000001, 4, 4, 0x00000001 }, | ||||
|     { "smlsld", 3, 6, 20, 27, 0x00000074, 6, 7, 0x00000001, 4, 4, 0x00000001 }, | ||||
|     { "smmla", 3, 6, 20, 27, 0x00000075, 6, 7, 0x00000000, 4, 4, 0x00000001 }, | ||||
|     { "smmls", 3, 6, 20, 27, 0x00000075, 6, 7, 0x00000003, 4, 4, 0x00000001 }, | ||||
|     { "smlald", 3, 6, 20, 27, 0x00000074, 6, 7, 0x00000000, 4, 4, 0x00000001 }, | ||||
|     { "smlad", 3, 6, 20, 27, 0x00000070, 6, 7, 0x00000000, 4, 4, 0x00000001 }, | ||||
|     { "smlaw", 3, 4, 20, 27, 0x00000012, 7, 7, 0x00000001, 4, 5, 0x00000000 }, | ||||
|     { "smulw", 3, 4, 20, 27, 0x00000012, 7, 7, 0x00000001, 4, 5, 0x00000002 }, | ||||
|     { "pkhtb", 2, 6, 20, 27, 0x00000068, 4, 6, 0x00000005 }, | ||||
|     { "pkhbt", 2, 6, 20, 27, 0x00000068, 4, 6, 0x00000001 }, | ||||
|     { "smul", 3, 4, 20, 27, 0x00000016, 7, 7, 0x00000001, 4, 4, 0x00000000 }, | ||||
|     { "smlalxy", 3, 4, 20, 27, 0x00000014, 7, 7, 0x00000001, 4, 4, 0x00000000 }, | ||||
|     //	{"smlal"	,  2	,  4	, 21, 27, 0x00000007,  4,  7, 0x00000009},
 | ||||
|     { "smla", 3, 4, 20, 27, 0x00000010, 7, 7, 0x00000001, 4, 4, 0x00000000 }, | ||||
|     { "mcrr", 1, 6, 20, 27, 0x000000c4 }, | ||||
|     { "mrrc", 1, 6, 20, 27, 0x000000c5 }, | ||||
|     { "cmp", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000015 }, | ||||
|     { "tst", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000011 }, | ||||
|     { "teq", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000013 }, | ||||
|     { "cmn", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000017 }, | ||||
|     { "smull", 2, 0, 21, 27, 0x00000006, 4, 7, 0x00000009 }, | ||||
|     { "umull", 2, 0, 21, 27, 0x00000004, 4, 7, 0x00000009 }, | ||||
|     { "umlal", 2, 0, 21, 27, 0x00000005, 4, 7, 0x00000009 }, | ||||
|     { "smlal", 2, 0, 21, 27, 0x00000007, 4, 7, 0x00000009 }, | ||||
|     { "mul", 2, 0, 21, 27, 0x00000000, 4, 7, 0x00000009 }, | ||||
|     { "mla", 2, 0, 21, 27, 0x00000001, 4, 7, 0x00000009 }, | ||||
|     { "ssat", 2, 6, 21, 27, 0x00000035, 4, 5, 0x00000001 }, | ||||
|     { "usat", 2, 6, 21, 27, 0x00000037, 4, 5, 0x00000001 }, | ||||
|     { "mrs", 4, 0, 23, 27, 0x00000002, 20, 21, 0x00000000, 16, 19, 0x0000000f, 0, 11, 0x00000000 }, | ||||
|     { "msr", 3, 0, 23, 27, 0x00000002, 20, 21, 0x00000002, 4, 7, 0x00000000 }, | ||||
|     { "and", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000000 }, | ||||
|     { "bic", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000e }, | ||||
|     { "ldm", 3, 0, 25, 27, 0x00000004, 20, 22, 0x00000005, 15, 15, 0x00000000 }, | ||||
|     { "eor", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000001 }, | ||||
|     { "add", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000004 }, | ||||
|     { "rsb", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000003 }, | ||||
|     { "rsc", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000007 }, | ||||
|     { "sbc", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000006 }, | ||||
|     { "adc", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000005 }, | ||||
|     { "sub", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000002 }, | ||||
|     { "orr", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000c }, | ||||
|     { "mvn", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000f }, | ||||
|     { "mov", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000d }, | ||||
|     { "stm", 2, 0, 25, 27, 0x00000004, 20, 22, 0x00000004 }, | ||||
|     { "ldm", 4, 0, 25, 27, 0x00000004, 22, 22, 0x00000001, 20, 20, 0x00000001, 15, 15, 0x00000001 }, | ||||
|     { "ldrsh", 3, 2, 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000f }, | ||||
|     { "stm", 3, 0, 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000000 }, | ||||
|     { "ldm", 3, 0, 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000001 }, | ||||
|     { "ldrsb", 3, 2, 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000d }, | ||||
|     { "strd", 3, 4, 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000f }, | ||||
|     { "ldrh", 3, 0, 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000b }, | ||||
|     { "strh", 3, 0, 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000b }, | ||||
|     { "ldrd", 3, 4, 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000d }, | ||||
|     { "strt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000002 }, | ||||
|     { "strbt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000006 }, | ||||
|     { "ldrbt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000007 }, | ||||
|     { "ldrt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000003 }, | ||||
|     { "mrc", 3, 6, 24, 27, 0x0000000e, 20, 20, 0x00000001, 4, 4, 0x00000001 }, | ||||
|     { "mcr", 3, 0, 24, 27, 0x0000000e, 20, 20, 0x00000000, 4, 4, 0x00000001 }, | ||||
|     { "msr", 2, 0, 23, 27, 0x00000006, 20, 21, 0x00000002 }, | ||||
|     { "ldrb", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000001 }, | ||||
|     { "strb", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000000 }, | ||||
|     { "ldr", 4, 0, 28, 31, 0x0000000e, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001 }, | ||||
|     { "ldrcond", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001 }, | ||||
|     { "str", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000000 }, | ||||
|     { "cdp", 2, 0, 24, 27, 0x0000000e, 4, 4, 0x00000000 }, | ||||
|     { "stc", 2, 0, 25, 27, 0x00000006, 20, 20, 0x00000000 }, | ||||
|     { "ldc", 2, 0, 25, 27, 0x00000006, 20, 20, 0x00000001 }, | ||||
|     { "swi", 1, 0, 24, 27, 0x0000000f }, | ||||
|     { "bbl", 1, 0, 25, 27, 0x00000005 }, | ||||
| }; | ||||
| 
 | ||||
| const ISEITEM arm_exclusion_code[] = { | ||||
| 	#define VFP_DECODE_EXCLUSION | ||||
| 	#include "core/arm/skyeye_common/vfp/vfpinstr.cpp" | ||||
| 	#undef VFP_DECODE_EXCLUSION | ||||
| 	{"srs"	,  0	,  6	,  0}, | ||||
| 	{"rfe"	,  0	,  6	,  0}, | ||||
| 	{"bkpt"	,  0	,  3	,  0}, | ||||
| 	{"blx"	,  0	,  3	,  0}, | ||||
| 	{"cps"	,  0	,  6	,  0}, | ||||
| 	{"pld"	,  0	,  4	,  0}, | ||||
| 	{"setend"	,  0	,  6	,  0}, | ||||
| 	{"clrex"	,  0	,  6	,  0}, | ||||
| 	{"rev16"	,  0	,  6	,  0}, | ||||
| 	{"usad8"	,  0	,  6	,  0}, | ||||
| 	{"sxtb"	,  0	,  6	,  0}, | ||||
| 	{"uxtb"	,  0	,  6	,  0}, | ||||
| 	{"sxth"	,  0	,  6	,  0}, | ||||
| 	{"sxtb16"	,  0	,  6	,  0}, | ||||
| 	{"uxth"	,  0	,  6	,  0}, | ||||
| 	{"uxtb16"	,  0	,  6	,  0}, | ||||
| 	{"cpy"	,  0	,  6	,  0}, | ||||
| 	{"uxtab"	,  0	,  6	,  0}, | ||||
| 	{"ssub8"	,  0	,  6	,  0}, | ||||
| 	{"shsub8"	,  0	,  6	,  0}, | ||||
| 	{"ssubaddx"	,  0	,  6	,  0}, | ||||
| 	{"strex"	,  0	,  6	,  0}, | ||||
| 	{"strexb"	,  0	,  7	,  0}, | ||||
| 	{"swp"	,  0	,  0	,  0}, | ||||
| 	{"swpb"	,  0	,  0	,  0}, | ||||
| 	{"ssub16"	,  0	,  6	,  0}, | ||||
| 	{"ssat16"	,  0	,  6	,  0}, | ||||
| 	{"shsubaddx"	,  0	,  6	,  0}, | ||||
| 	{"qsubaddx"	,  0	,  6	,  0}, | ||||
| 	{"shaddsubx"	,  0	,  6	,  0}, | ||||
| 	{"shadd8"	,  0	,  6	,  0}, | ||||
| 	{"shadd16"	,  0	,  6	,  0}, | ||||
| 	{"sel"	,  0	,  6	,  0}, | ||||
| 	{"saddsubx"	,  0	,  6	,  0}, | ||||
| 	{"sadd8"	,  0	,  6	,  0}, | ||||
| 	{"sadd16"	,  0	,  6	,  0}, | ||||
| 	{"shsub16"	,  0	,  6	,  0}, | ||||
| 	{"umaal"	,  0	,  6	,  0}, | ||||
| 	{"uxtab16"	,  0	,  6	,  0}, | ||||
| 	{"usubaddx"	,  0	,  6	,  0}, | ||||
| 	{"usub8"	,  0	,  6	,  0}, | ||||
| 	{"usub16"	,  0	,  6	,  0}, | ||||
| 	{"usat16"	,  0	,  6	,  0}, | ||||
| 	{"usada8"	,  0	,  6	,  0}, | ||||
| 	{"uqsubaddx"	,  0	,  6	,  0}, | ||||
| 	{"uqsub8"	,  0	,  6	,  0}, | ||||
| 	{"uqsub16"	,  0	,  6	,  0}, | ||||
| 	{"uqaddsubx"	,  0	,  6	,  0}, | ||||
| 	{"uqadd8"	,  0	,  6	,  0}, | ||||
| 	{"uqadd16"	,  0	,  6	,  0}, | ||||
| 	{"sxtab"	,  0	,  6	,  0}, | ||||
| 	{"uhsubaddx"	,  0	,  6	,  0}, | ||||
| 	{"uhsub8"	,  0	,  6	,  0}, | ||||
| 	{"uhsub16"	,  0	,  6	,  0}, | ||||
| 	{"uhaddsubx"	,  0	,  6	,  0}, | ||||
| 	{"uhadd8"	,  0	,  6	,  0}, | ||||
| 	{"uhadd16"	,  0	,  6	,  0}, | ||||
| 	{"uaddsubx"	,  0	,  6	,  0}, | ||||
| 	{"uadd8"	,  0	,  6	,  0}, | ||||
| 	{"uadd16"	,  0	,  6	,  0}, | ||||
| 	{"sxtah"	,  0	,  6	,  0}, | ||||
| 	{"sxtab16"	,  0	,  6	,  0}, | ||||
| 	{"qadd8"	,  0	,  6	,  0}, | ||||
| 	{"bxj"	,  0	,  5	,  0}, | ||||
| 	{"clz"	,  0	,  3	,  0}, | ||||
| 	{"uxtah"	,  0	,  6	,  0}, | ||||
| 	{"bx"	,  0	,  2	,  0}, | ||||
| 	{"rev"	,  0	,  6	,  0}, | ||||
| 	{"blx"	,  0	,  3	,  0}, | ||||
| 	{"revsh"	,  0	,  6	,  0}, | ||||
| 	{"qadd"	,  0	,  4	,  0}, | ||||
| 	{"qadd16"	,  0	,  6	,  0}, | ||||
| 	{"qaddsubx"	,  0	,  6	,  0}, | ||||
| 	{"ldrex"	,  0	,  0	,  0}, | ||||
| 	{"qdadd"	,  0	,  4	,  0}, | ||||
| 	{"qdsub"	,  0	,  4	,  0}, | ||||
| 	{"qsub"	,  0	,  4	,  0}, | ||||
| 	{"ldrexb"	,  0	,  7	,  0}, | ||||
| 	{"qsub8"	,  0	,  6	,  0}, | ||||
| 	{"qsub16"	,  0	,  6	,  0}, | ||||
| 	{"smuad"	,  0	,  6	,  0}, | ||||
| 	{"smmul"	,  0	,  6	,  0}, | ||||
| 	{"smusd"	,  0	,  6	,  0}, | ||||
| 	{"smlsd"	,  0	,  6	,  0}, | ||||
| 	{"smlsld"	,  0	,  6	,  0}, | ||||
| 	{"smmla"	,  0	,  6	,  0}, | ||||
| 	{"smmls"	,  0	,  6	,  0}, | ||||
| 	{"smlald"	,  0	,  6	,  0}, | ||||
| 	{"smlad"	,  0	,  6	,  0}, | ||||
| 	{"smlaw"	,  0	,  4	,  0}, | ||||
| 	{"smulw"	,  0	,  4	,  0}, | ||||
| 	{"pkhtb"	,  0	,  6	,  0}, | ||||
| 	{"pkhbt"	,  0	,  6	,  0}, | ||||
| 	{"smul"	,  0	,  4	,  0}, | ||||
| 	{"smlal"	,  0	,  4	,  0}, | ||||
| 	{"smla"	,  0	,  4	,  0}, | ||||
| 	{"mcrr"	,  0	,  6	,  0}, | ||||
| 	{"mrrc"	,  0	,  6	,  0}, | ||||
| 	{"cmp"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"tst"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"teq"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"cmn"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"smull"	,  0	,  0	,  0}, | ||||
| 	{"umull"	,  0	,  0	,  0}, | ||||
| 	{"umlal"	,  0	,  0	,  0}, | ||||
| 	{"smlal"	,  0	,  0	,  0}, | ||||
| 	{"mul"	,  0	,  0	,  0}, | ||||
| 	{"mla"	,  0	,  0	,  0}, | ||||
| 	{"ssat"	,  0	,  6	,  0}, | ||||
| 	{"usat"	,  0	,  6	,  0}, | ||||
| 	{"mrs"	,  0	,  0	,  0}, | ||||
| 	{"msr"	,  0	,  0	,  0}, | ||||
| 	{"and"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"bic"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"ldm"	,  0	,  0	,  0}, | ||||
| 	{"eor"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"add"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"rsb"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"rsc"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"sbc"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"adc"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"sub"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"orr"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"mvn"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"mov"	,  3	,  0	,  4,  4, 0x00000001,  7,  7, 0x00000001, 25, 25, 0x00000000}, | ||||
| 	{"stm"	,  0	,  0	,  0}, | ||||
| 	{"ldm"	,  0	,  0	,  0}, | ||||
| 	{"ldrsh"	,  0	,  2	,  0}, | ||||
| 	{"stm"	,  0	,  0	,  0}, | ||||
| 	{"ldm"	,  0	,  0	,  0}, | ||||
| 	{"ldrsb"	,  0	,  2	,  0}, | ||||
| 	{"strd"	,  0	,  4	,  0}, | ||||
| 	{"ldrh"	,  0	,  0	,  0}, | ||||
| 	{"strh"	,  0	,  0	,  0}, | ||||
| 	{"ldrd"	,  0	,  4	,  0}, | ||||
| 	{"strt"	,  0	,  0	,  0}, | ||||
| 	{"strbt"	,  0	,  0	,  0}, | ||||
| 	{"ldrbt"	,  0	,  0	,  0}, | ||||
| 	{"ldrt"	,  0	,  0	,  0}, | ||||
| 	{"mrc"	,  0	,  6	,  0}, | ||||
| 	{"mcr"	,  0	,  0	,  0}, | ||||
| 	{"msr"	,  0	,  0	,  0}, | ||||
| 	{"ldrb"	,  0	,  0	,  0}, | ||||
| 	{"strb"	,  0	,  0	,  0}, | ||||
| 	{"ldr"	,  0	,  0	,  0}, | ||||
| 	{"ldrcond"	,  1	,  0	,  28, 31, 0x0000000e}, | ||||
| 	{"str"	,  0	,  0	,  0}, | ||||
| 	{"cdp"	,  0	,  0	,  0}, | ||||
| 	{"stc"	,  0	,  0	,  0}, | ||||
| 	{"ldc"	,  0	,  0	,  0}, | ||||
| 	{"swi"	,  0	,  0	,  0}, | ||||
| 	{"bbl"	,  0	,  0	,  0}, | ||||
|         {"bl_1_thumb",      0,      INVALID, 0},/* should be table[-4] */          | ||||
|         {"bl_2_thumb",      0,      INVALID, 0}, /* should be located at the end of the table[-3] */ | ||||
| 	{"blx_1_thumb",      0,      INVALID, 0}, /* should be located at table[-2] */ | ||||
|         {"invalid",      0,      INVALID, 0}          | ||||
|     { "vmla", 0, ARMVFP2, 0 }, | ||||
|     { "vmls", 0, ARMVFP2, 0 }, | ||||
|     { "vnmla", 0, ARMVFP2, 0 }, | ||||
|     { "vnmla", 0, ARMVFP2, 0 }, | ||||
|     { "vnmls", 0, ARMVFP2, 0 }, | ||||
|     { "vnmul", 0, ARMVFP2, 0 }, | ||||
|     { "vmul", 0, ARMVFP2, 0 }, | ||||
|     { "vadd", 0, ARMVFP2, 0 }, | ||||
|     { "vsub", 0, ARMVFP2, 0 }, | ||||
|     { "vdiv", 0, ARMVFP2, 0 }, | ||||
|     { "vmov(i)", 0, ARMVFP3, 0 }, | ||||
|     { "vmov(r)", 0, ARMVFP3, 0 }, | ||||
|     { "vabs", 0, ARMVFP2, 0 }, | ||||
|     { "vneg", 0, ARMVFP2, 0 }, | ||||
|     { "vsqrt", 0, ARMVFP2, 0 }, | ||||
|     { "vcmp", 0, ARMVFP2, 0 }, | ||||
|     { "vcmp2", 0, ARMVFP2, 0 }, | ||||
|     { "vcvt(bff)", 0, ARMVFP3, 4, 4, 1 }, | ||||
|     { "vcvt(bds)", 0, ARMVFP2, 0 }, | ||||
|     { "vcvt(bfi)", 0, ARMVFP2, 0 }, | ||||
|     { "vmovbrs", 0, ARMVFP2, 0 }, | ||||
|     { "vmsr", 0, ARMVFP2, 0 }, | ||||
|     { "vmovbrc", 0, ARMVFP2, 0 }, | ||||
|     { "vmrs", 0, ARMVFP2, 0 }, | ||||
|     { "vmovbcr", 0, ARMVFP2, 0 }, | ||||
|     { "vmovbrrss", 0, ARMVFP2, 0 }, | ||||
|     { "vmovbrrd", 0, ARMVFP2, 0 }, | ||||
|     { "vstr", 0, ARMVFP2, 0 }, | ||||
|     { "vpush", 0, ARMVFP2, 0 }, | ||||
|     { "vstm", 0, ARMVFP2, 0 }, | ||||
|     { "vpop", 0, ARMVFP2, 0 }, | ||||
|     { "vldr", 0, ARMVFP2, 0 }, | ||||
|     { "vldm", 0, ARMVFP2, 0 }, | ||||
| 
 | ||||
|     { "srs", 0, 6, 0 }, | ||||
|     { "rfe", 0, 6, 0 }, | ||||
|     { "bkpt", 0, 3, 0 }, | ||||
|     { "blx", 0, 3, 0 }, | ||||
|     { "cps", 0, 6, 0 }, | ||||
|     { "pld", 0, 4, 0 }, | ||||
|     { "setend", 0, 6, 0 }, | ||||
|     { "clrex", 0, 6, 0 }, | ||||
|     { "rev16", 0, 6, 0 }, | ||||
|     { "usad8", 0, 6, 0 }, | ||||
|     { "sxtb", 0, 6, 0 }, | ||||
|     { "uxtb", 0, 6, 0 }, | ||||
|     { "sxth", 0, 6, 0 }, | ||||
|     { "sxtb16", 0, 6, 0 }, | ||||
|     { "uxth", 0, 6, 0 }, | ||||
|     { "uxtb16", 0, 6, 0 }, | ||||
|     { "cpy", 0, 6, 0 }, | ||||
|     { "uxtab", 0, 6, 0 }, | ||||
|     { "ssub8", 0, 6, 0 }, | ||||
|     { "shsub8", 0, 6, 0 }, | ||||
|     { "ssubaddx", 0, 6, 0 }, | ||||
|     { "strex", 0, 6, 0 }, | ||||
|     { "strexb", 0, 7, 0 }, | ||||
|     { "swp", 0, 0, 0 }, | ||||
|     { "swpb", 0, 0, 0 }, | ||||
|     { "ssub16", 0, 6, 0 }, | ||||
|     { "ssat16", 0, 6, 0 }, | ||||
|     { "shsubaddx", 0, 6, 0 }, | ||||
|     { "qsubaddx", 0, 6, 0 }, | ||||
|     { "shaddsubx", 0, 6, 0 }, | ||||
|     { "shadd8", 0, 6, 0 }, | ||||
|     { "shadd16", 0, 6, 0 }, | ||||
|     { "sel", 0, 6, 0 }, | ||||
|     { "saddsubx", 0, 6, 0 }, | ||||
|     { "sadd8", 0, 6, 0 }, | ||||
|     { "sadd16", 0, 6, 0 }, | ||||
|     { "shsub16", 0, 6, 0 }, | ||||
|     { "umaal", 0, 6, 0 }, | ||||
|     { "uxtab16", 0, 6, 0 }, | ||||
|     { "usubaddx", 0, 6, 0 }, | ||||
|     { "usub8", 0, 6, 0 }, | ||||
|     { "usub16", 0, 6, 0 }, | ||||
|     { "usat16", 0, 6, 0 }, | ||||
|     { "usada8", 0, 6, 0 }, | ||||
|     { "uqsubaddx", 0, 6, 0 }, | ||||
|     { "uqsub8", 0, 6, 0 }, | ||||
|     { "uqsub16", 0, 6, 0 }, | ||||
|     { "uqaddsubx", 0, 6, 0 }, | ||||
|     { "uqadd8", 0, 6, 0 }, | ||||
|     { "uqadd16", 0, 6, 0 }, | ||||
|     { "sxtab", 0, 6, 0 }, | ||||
|     { "uhsubaddx", 0, 6, 0 }, | ||||
|     { "uhsub8", 0, 6, 0 }, | ||||
|     { "uhsub16", 0, 6, 0 }, | ||||
|     { "uhaddsubx", 0, 6, 0 }, | ||||
|     { "uhadd8", 0, 6, 0 }, | ||||
|     { "uhadd16", 0, 6, 0 }, | ||||
|     { "uaddsubx", 0, 6, 0 }, | ||||
|     { "uadd8", 0, 6, 0 }, | ||||
|     { "uadd16", 0, 6, 0 }, | ||||
|     { "sxtah", 0, 6, 0 }, | ||||
|     { "sxtab16", 0, 6, 0 }, | ||||
|     { "qadd8", 0, 6, 0 }, | ||||
|     { "bxj", 0, 5, 0 }, | ||||
|     { "clz", 0, 3, 0 }, | ||||
|     { "uxtah", 0, 6, 0 }, | ||||
|     { "bx", 0, 2, 0 }, | ||||
|     { "rev", 0, 6, 0 }, | ||||
|     { "blx", 0, 3, 0 }, | ||||
|     { "revsh", 0, 6, 0 }, | ||||
|     { "qadd", 0, 4, 0 }, | ||||
|     { "qadd16", 0, 6, 0 }, | ||||
|     { "qaddsubx", 0, 6, 0 }, | ||||
|     { "ldrex", 0, 0, 0 }, | ||||
|     { "qdadd", 0, 4, 0 }, | ||||
|     { "qdsub", 0, 4, 0 }, | ||||
|     { "qsub", 0, 4, 0 }, | ||||
|     { "ldrexb", 0, 7, 0 }, | ||||
|     { "qsub8", 0, 6, 0 }, | ||||
|     { "qsub16", 0, 6, 0 }, | ||||
|     { "smuad", 0, 6, 0 }, | ||||
|     { "smmul", 0, 6, 0 }, | ||||
|     { "smusd", 0, 6, 0 }, | ||||
|     { "smlsd", 0, 6, 0 }, | ||||
|     { "smlsld", 0, 6, 0 }, | ||||
|     { "smmla", 0, 6, 0 }, | ||||
|     { "smmls", 0, 6, 0 }, | ||||
|     { "smlald", 0, 6, 0 }, | ||||
|     { "smlad", 0, 6, 0 }, | ||||
|     { "smlaw", 0, 4, 0 }, | ||||
|     { "smulw", 0, 4, 0 }, | ||||
|     { "pkhtb", 0, 6, 0 }, | ||||
|     { "pkhbt", 0, 6, 0 }, | ||||
|     { "smul", 0, 4, 0 }, | ||||
|     { "smlal", 0, 4, 0 }, | ||||
|     { "smla", 0, 4, 0 }, | ||||
|     { "mcrr", 0, 6, 0 }, | ||||
|     { "mrrc", 0, 6, 0 }, | ||||
|     { "cmp", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "tst", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "teq", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "cmn", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "smull", 0, 0, 0 }, | ||||
|     { "umull", 0, 0, 0 }, | ||||
|     { "umlal", 0, 0, 0 }, | ||||
|     { "smlal", 0, 0, 0 }, | ||||
|     { "mul", 0, 0, 0 }, | ||||
|     { "mla", 0, 0, 0 }, | ||||
|     { "ssat", 0, 6, 0 }, | ||||
|     { "usat", 0, 6, 0 }, | ||||
|     { "mrs", 0, 0, 0 }, | ||||
|     { "msr", 0, 0, 0 }, | ||||
|     { "and", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "bic", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "ldm", 0, 0, 0 }, | ||||
|     { "eor", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "add", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "rsb", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "rsc", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "sbc", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "adc", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "sub", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "orr", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "mvn", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "mov", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }, | ||||
|     { "stm", 0, 0, 0 }, | ||||
|     { "ldm", 0, 0, 0 }, | ||||
|     { "ldrsh", 0, 2, 0 }, | ||||
|     { "stm", 0, 0, 0 }, | ||||
|     { "ldm", 0, 0, 0 }, | ||||
|     { "ldrsb", 0, 2, 0 }, | ||||
|     { "strd", 0, 4, 0 }, | ||||
|     { "ldrh", 0, 0, 0 }, | ||||
|     { "strh", 0, 0, 0 }, | ||||
|     { "ldrd", 0, 4, 0 }, | ||||
|     { "strt", 0, 0, 0 }, | ||||
|     { "strbt", 0, 0, 0 }, | ||||
|     { "ldrbt", 0, 0, 0 }, | ||||
|     { "ldrt", 0, 0, 0 }, | ||||
|     { "mrc", 0, 6, 0 }, | ||||
|     { "mcr", 0, 0, 0 }, | ||||
|     { "msr", 0, 0, 0 }, | ||||
|     { "ldrb", 0, 0, 0 }, | ||||
|     { "strb", 0, 0, 0 }, | ||||
|     { "ldr", 0, 0, 0 }, | ||||
|     { "ldrcond", 1, 0, 28, 31, 0x0000000e }, | ||||
|     { "str", 0, 0, 0 }, | ||||
|     { "cdp", 0, 0, 0 }, | ||||
|     { "stc", 0, 0, 0 }, | ||||
|     { "ldc", 0, 0, 0 }, | ||||
|     { "swi", 0, 0, 0 }, | ||||
|     { "bbl", 0, 0, 0 }, | ||||
|     { "bl_1_thumb", 0, INVALID, 0 },    // Should be table[-4]
 | ||||
|     { "bl_2_thumb", 0, INVALID, 0 },    // Should be located at the end of the table[-3]
 | ||||
|     { "blx_1_thumb", 0, INVALID, 0 },   // Should be located at table[-2]
 | ||||
|     { "invalid", 0, INVALID, 0 } | ||||
| }; | ||||
| 
 | ||||
| int decode_arm_instr(uint32_t instr, int32_t *idx) | ||||
| { | ||||
| 	int n = 0; | ||||
| 	int base = 0; | ||||
| 	int ret = DECODE_FAILURE; | ||||
| 	int i = 0; | ||||
| 	int instr_slots = sizeof(arm_instruction)/sizeof(ISEITEM); | ||||
| 	for (i = 0; i < instr_slots; i++) | ||||
| 	{ | ||||
| //		ret = DECODE_SUCCESS;
 | ||||
| 		n = arm_instruction[i].attribute_value; | ||||
| 		base = 0; | ||||
| 		while (n) { | ||||
| 			if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) { | ||||
| 				/* clrex */ | ||||
| 				if (instr != arm_instruction[i].content[base + 2]) { | ||||
| 					break; | ||||
| 				} | ||||
| 			} else if (BITS(arm_instruction[i].content[base], arm_instruction[i].content[base + 1]) != arm_instruction[i].content[base + 2]) { | ||||
| 				break; | ||||
| 			} | ||||
| 			base += 3; | ||||
| 			n --; | ||||
| 		} | ||||
| 		//All conditions is satisfied.
 | ||||
| 		if (n == 0) | ||||
| 			ret = DECODE_SUCCESS; | ||||
| int decode_arm_instr(uint32_t instr, int32_t *idx) { | ||||
|     int n = 0; | ||||
|     int base = 0; | ||||
|     int ret = DECODE_FAILURE; | ||||
|     int i = 0; | ||||
|     int instr_slots = sizeof(arm_instruction) / sizeof(ISEITEM); | ||||
|     for (i = 0; i < instr_slots; i++) { | ||||
|         n = arm_instruction[i].attribute_value; | ||||
|         base = 0; | ||||
| 
 | ||||
| 		if (ret == DECODE_SUCCESS) { | ||||
| 			n = arm_exclusion_code[i].attribute_value; | ||||
| 			if (n != 0) { | ||||
| 				base = 0; | ||||
| 				while (n) { | ||||
| 					if (BITS(arm_exclusion_code[i].content[base], arm_exclusion_code[i].content[base + 1]) != arm_exclusion_code[i].content[base + 2]) { | ||||
| 						break;					} | ||||
| 					base += 3; | ||||
| 					n --; | ||||
| 				} | ||||
| 				//All conditions is satisfied.
 | ||||
| 				if (n == 0) | ||||
| 					ret = DECODE_FAILURE; | ||||
| 			} | ||||
| 		} | ||||
|         while (n) { | ||||
|             if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) { | ||||
|                 // clrex
 | ||||
|                 if (instr != arm_instruction[i].content[base + 2]) { | ||||
|                     break; | ||||
|                 } | ||||
|             } else if (BITS(arm_instruction[i].content[base], arm_instruction[i].content[base + 1]) != arm_instruction[i].content[base + 2]) { | ||||
|                 break; | ||||
|             } | ||||
|             base += 3; | ||||
|             n--; | ||||
|         } | ||||
| 
 | ||||
| 		if (ret == DECODE_SUCCESS) { | ||||
| 			*idx = i; | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} | ||||
| 	return ret; | ||||
|         // All conditions is satisfied.
 | ||||
|         if (n == 0) | ||||
|             ret = DECODE_SUCCESS; | ||||
| 
 | ||||
|         if (ret == DECODE_SUCCESS) { | ||||
|             n = arm_exclusion_code[i].attribute_value; | ||||
|             if (n != 0) { | ||||
|                 base = 0; | ||||
|                 while (n) { | ||||
|                     if (BITS(arm_exclusion_code[i].content[base], arm_exclusion_code[i].content[base + 1]) != arm_exclusion_code[i].content[base + 2]) { | ||||
|                         break; | ||||
|                     } | ||||
|                     base += 3; | ||||
|                     n--; | ||||
|                 } | ||||
| 
 | ||||
|                 // All conditions is satisfied.
 | ||||
|                 if (n == 0) | ||||
|                     ret = DECODE_FAILURE; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (ret == DECODE_SUCCESS) { | ||||
|             *idx = i; | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -56,8 +56,6 @@ | |||
| #define RN ((instr >> 16) & 0xF) | ||||
| /*xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1111 */ | ||||
| #define RM (instr & 0xF) | ||||
| #define BIT(n) ((instr >> (n)) & 1) | ||||
| #define BITS(a,b) ((instr >> (a)) & ((1 << (1+(b)-(a)))-1)) | ||||
| 
 | ||||
| /* CP15 registers */ | ||||
| #define OPCODE_1        BITS(21, 23) | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,42 +1,15 @@ | |||
| /* Copyright (C)
 | ||||
| * 2011 - Michael.Kang blackfin.kang@gmail.com | ||||
| * 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. | ||||
| * | ||||
| */ | ||||
| /**
 | ||||
| * @file arm_dyncom_run.cpp | ||||
| * @brief The dyncom run implementation for arm | ||||
| * @author Michael.Kang blackfin.kang@gmail.com | ||||
| * @version 78.77 | ||||
| * @date 2011-11-20 | ||||
| */ | ||||
| // Copyright 2012 Michael Kang, 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <assert.h> | ||||
| 
 | ||||
| #include "core/arm/skyeye_common/armdefs.h" | ||||
| 
 | ||||
| void switch_mode(arm_core_t *core, uint32_t mode) | ||||
| { | ||||
|     uint32_t tmp1, tmp2; | ||||
|     if (core->Mode == mode) { | ||||
|         //Mode not changed.
 | ||||
|         //printf("mode not changed\n");
 | ||||
| void switch_mode(arm_core_t *core, uint32_t mode) { | ||||
|     if (core->Mode == mode) | ||||
|         return; | ||||
|     } | ||||
|     //printf("%d --->>> %d\n", core->Mode, mode);
 | ||||
|     //printf("In %s, Cpsr=0x%x, R15=0x%x, last_pc=0x%x, cpsr=0x%x, spsr_copy=0x%x, icounter=%lld\n", __FUNCTION__, core->Cpsr, core->Reg[15], core->last_pc, core->Cpsr, core->Spsr_copy, core->icounter);
 | ||||
| 
 | ||||
|     if (mode != USERBANK) { | ||||
|         switch (core->Mode) { | ||||
|         case USER32MODE: | ||||
|  | @ -110,11 +83,8 @@ void switch_mode(arm_core_t *core, uint32_t mode) | |||
| 
 | ||||
|         } | ||||
|         core->Mode = mode; | ||||
|         //printf("In %si end, Cpsr=0x%x, R15=0x%x, last_pc=0x%x, cpsr=0x%x, spsr_copy=0x%x, icounter=%lld\n", __FUNCTION__, core->Cpsr, core->Reg[15], core->last_pc, core->Cpsr, core->Spsr_copy, core->icounter);
 | ||||
|         //printf("\n--------------------------------------\n");
 | ||||
|     } | ||||
|     else { | ||||
|         printf("user mode\n"); | ||||
|     } else { | ||||
|         LOG_CRITICAL(Core_ARM11, "user mode"); | ||||
|         exit(-2); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,35 +1,13 @@ | |||
| /* Copyright (C) 
 | ||||
| * 2011 - Michael.Kang blackfin.kang@gmail.com | ||||
| * 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. | ||||
| *  | ||||
| */ | ||||
| /**
 | ||||
| * @file arm_dyncom_thumb.c | ||||
| * @brief The thumb dynamic interpreter | ||||
| * @author Michael.Kang blackfin.kang@gmail.com | ||||
| * @version 78.77 | ||||
| * @date 2011-11-07 | ||||
| */ | ||||
| // Copyright 2012 Michael Kang, 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| /* We can provide simple Thumb simulation by decoding the Thumb
 | ||||
| instruction into its corresponding ARM instruction, and using the | ||||
| existing ARM simulator.  */ | ||||
| // We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding
 | ||||
| // ARM instruction, and using the existing ARM simulator.
 | ||||
| 
 | ||||
| #include "core/arm/skyeye_common/skyeye_defs.h" | ||||
| 
 | ||||
| #ifndef MODET			/* required for the Thumb instruction support */ | ||||
| #ifndef MODET // Required for the Thumb instruction support
 | ||||
| #if 1 | ||||
| #error "MODET needs to be defined for the Thumb world to work" | ||||
| #else | ||||
|  | @ -40,482 +18,359 @@ existing ARM simulator.  */ | |||
| #include "core/arm/skyeye_common/armos.h" | ||||
| #include "core/arm/dyncom/arm_dyncom_thumb.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.  */ | ||||
| // 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 (addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t* inst_size) { | ||||
|     tdstate valid = t_uninitialized; | ||||
| 	ARMword next_instr; | ||||
| 	ARMword tinstr; | ||||
| 	tinstr = instr; | ||||
| 	/* The endian should be judge here */ | ||||
| 	#if 0 | ||||
| 	if (state->bigendSig) { | ||||
| 		next_instr = tinstr & 0xFFFF; | ||||
| 		tinstr >>= 16; | ||||
| 	} | ||||
| 	else { | ||||
| 		next_instr = tinstr >> 16; | ||||
| 		tinstr &= 0xFFFF; | ||||
| 	} | ||||
| 	#endif | ||||
| 	if((addr & 0x3) != 0) | ||||
| 		tinstr = instr >> 16; | ||||
| 	else | ||||
| 		tinstr &= 0xFFFF; | ||||
|     ARMword tinstr; | ||||
|     tinstr = instr; | ||||
| 
 | ||||
| 	//printf("In %s, instr=0x%x, tinstr=0x%x, r15=0x%x\n", __FUNCTION__, instr, tinstr, cpu->translate_pc);
 | ||||
| #if 1				/* debugging to catch non updates */ | ||||
| 	*ainstr = 0xDEADC0DE; | ||||
| #endif | ||||
|     // The endian should be judge here
 | ||||
|     if((addr & 0x3) != 0) | ||||
|         tinstr = instr >> 16; | ||||
|     else | ||||
|         tinstr &= 0xFFFF; | ||||
| 
 | ||||
| 	switch ((tinstr & 0xF800) >> 11) { | ||||
| 	case 0:		/* LSL */ | ||||
| 	case 1:		/* LSR */ | ||||
| 	case 2:		/* ASR */ | ||||
| 		/* Format 1 */ | ||||
| 		*ainstr = 0xE1B00000	/* base opcode */ | ||||
| 			| ((tinstr & 0x1800) >> (11 - 5))	/* shift type */ | ||||
| 			|((tinstr & 0x07C0) << (7 - 6))	/* imm5 */ | ||||
| 			|((tinstr & 0x0038) >> 3)	/* Rs */ | ||||
| 			|((tinstr & 0x0007) << 12);	/* Rd */ | ||||
| 		break; | ||||
| 	case 3:		/* ADD/SUB */ | ||||
| 		/* Format 2 */ | ||||
| 		{ | ||||
| 			ARMword subset[4] = { | ||||
| 				0xE0900000,	/* ADDS Rd,Rs,Rn    */ | ||||
| 				0xE0500000,	/* SUBS Rd,Rs,Rn    */ | ||||
| 				0xE2900000,	/* ADDS Rd,Rs,#imm3 */ | ||||
| 				0xE2500000	/* SUBS Rd,Rs,#imm3 */ | ||||
| 			}; | ||||
| 			/* It is quicker indexing into a table, than performing switch
 | ||||
| 			   or conditionals: */ | ||||
| 			*ainstr = subset[(tinstr & 0x0600) >> 9]	/* base opcode */ | ||||
| 				|((tinstr & 0x01C0) >> 6)	/* Rn or imm3 */ | ||||
| 				|((tinstr & 0x0038) << (16 - 3))	/* Rs */ | ||||
| 				|((tinstr & 0x0007) << (12 - 0));	/* Rd */ | ||||
| 		} | ||||
| 		break; | ||||
| 	case 4:		/* MOV */ | ||||
| 	case 5:		/* CMP */ | ||||
| 	case 6:		/* ADD */ | ||||
| 	case 7:		/* SUB */ | ||||
| 		/* Format 3 */ | ||||
| 		{ | ||||
| 			ARMword subset[4] = { | ||||
| 				0xE3B00000,	/* MOVS Rd,#imm8    */ | ||||
| 				0xE3500000,	/* CMP  Rd,#imm8    */ | ||||
| 				0xE2900000,	/* ADDS Rd,Rd,#imm8 */ | ||||
| 				0xE2500000,	/* SUBS Rd,Rd,#imm8 */ | ||||
| 			}; | ||||
| 			*ainstr = subset[(tinstr & 0x1800) >> 11]	/* base opcode */ | ||||
| 				|((tinstr & 0x00FF) >> 0)	/* imm8 */ | ||||
| 				|((tinstr & 0x0700) << (16 - 8))	/* Rn */ | ||||
| 				|((tinstr & 0x0700) << (12 - 8));	/* Rd */ | ||||
| 		} | ||||
| 		break; | ||||
| 	case 8:		/* Arithmetic and high register transfers */ | ||||
| 		/* TODO: Since the subsets for both Format 4 and Format 5
 | ||||
| 		   instructions are made up of different ARM encodings, we could | ||||
| 		   save the following conditional, and just have one large | ||||
| 		   subset. */ | ||||
| 		if ((tinstr & (1 << 10)) == 0) { | ||||
| 			typedef enum | ||||
| 			{ t_norm, t_shift, t_neg, t_mul }otype_t; | ||||
|     *ainstr = 0xDEADC0DE; // Debugging to catch non updates
 | ||||
| 
 | ||||
| 			/* Format 4 */ | ||||
| 			struct | ||||
| 			{ | ||||
| 				ARMword opcode; | ||||
| 				otype_t otype; | ||||
| 			} | ||||
| 			subset[16] = { | ||||
| 				{ | ||||
| 				0xE0100000, t_norm},	/* ANDS Rd,Rd,Rs     */ | ||||
| 				{ | ||||
| 				0xE0300000, t_norm},	/* EORS Rd,Rd,Rs     */ | ||||
| 				{ | ||||
| 				0xE1B00010, t_shift},	/* MOVS Rd,Rd,LSL Rs */ | ||||
| 				{ | ||||
| 				0xE1B00030, t_shift},	/* MOVS Rd,Rd,LSR Rs */ | ||||
| 				{ | ||||
| 				0xE1B00050, t_shift},	/* MOVS Rd,Rd,ASR Rs */ | ||||
| 				{ | ||||
| 				0xE0B00000, t_norm},	/* ADCS Rd,Rd,Rs     */ | ||||
| 				{ | ||||
| 				0xE0D00000, t_norm},	/* SBCS Rd,Rd,Rs     */ | ||||
| 				{ | ||||
| 				0xE1B00070, t_shift},	/* MOVS Rd,Rd,ROR Rs */ | ||||
| 				{ | ||||
| 				0xE1100000, t_norm},	/* TST  Rd,Rs        */ | ||||
| 				{ | ||||
| 				0xE2700000, t_neg},	/* RSBS Rd,Rs,#0     */ | ||||
| 				{ | ||||
| 				0xE1500000, t_norm},	/* CMP  Rd,Rs        */ | ||||
| 				{ | ||||
| 				0xE1700000, t_norm},	/* CMN  Rd,Rs        */ | ||||
| 				{ | ||||
| 				0xE1900000, t_norm},	/* ORRS Rd,Rd,Rs     */ | ||||
| 				{ | ||||
| 				0xE0100090, t_mul},	/* MULS Rd,Rd,Rs     */ | ||||
| 				{ | ||||
| 				0xE1D00000, t_norm},	/* BICS Rd,Rd,Rs     */ | ||||
| 				{ | ||||
| 				0xE1F00000, t_norm}	/* MVNS Rd,Rs        */ | ||||
| 			}; | ||||
| 			*ainstr = subset[(tinstr & 0x03C0) >> 6].opcode;	/* base */ | ||||
| 			switch (subset[(tinstr & 0x03C0) >> 6].otype) { | ||||
| 			case t_norm: | ||||
| 				*ainstr |= ((tinstr & 0x0007) << 16)	/* Rn */ | ||||
| 					|((tinstr & 0x0007) << 12)	/* Rd */ | ||||
| 					|((tinstr & 0x0038) >> 3);	/* Rs */ | ||||
| 				break; | ||||
| 			case t_shift: | ||||
| 				*ainstr |= ((tinstr & 0x0007) << 12)	/* Rd */ | ||||
| 					|((tinstr & 0x0007) >> 0)	/* Rm */ | ||||
| 					|((tinstr & 0x0038) << (8 - 3));	/* Rs */ | ||||
| 				break; | ||||
| 			case t_neg: | ||||
| 				*ainstr |= ((tinstr & 0x0007) << 12)	/* Rd */ | ||||
| 					|((tinstr & 0x0038) << (16 - 3));	/* Rn */ | ||||
| 				break; | ||||
| 			case t_mul: | ||||
| 				*ainstr |= ((tinstr & 0x0007) << 16)	/* Rd */ | ||||
| 					|((tinstr & 0x0007) << 8)	/* Rs */ | ||||
| 					|((tinstr & 0x0038) >> 3);	/* Rm */ | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		else { | ||||
| 			/* Format 5 */ | ||||
| 			ARMword Rd = ((tinstr & 0x0007) >> 0); | ||||
| 			ARMword Rs = ((tinstr & 0x0038) >> 3); | ||||
| 			if (tinstr & (1 << 7)) | ||||
| 				Rd += 8; | ||||
| 			if (tinstr & (1 << 6)) | ||||
| 				Rs += 8; | ||||
| 			switch ((tinstr & 0x03C0) >> 6) { | ||||
| 			case 0x1:	/* ADD Rd,Rd,Hs */ | ||||
| 			case 0x2:	/* ADD Hd,Hd,Rs */ | ||||
| 			case 0x3:	/* ADD Hd,Hd,Hs */ | ||||
| 				*ainstr = 0xE0800000	/* base */ | ||||
| 					| (Rd << 16)	/* Rn */ | ||||
| 					|(Rd << 12)	/* Rd */ | ||||
| 					|(Rs << 0);	/* Rm */ | ||||
| 				break; | ||||
| 			case 0x5:	/* CMP Rd,Hs */ | ||||
| 			case 0x6:	/* CMP Hd,Rs */ | ||||
| 			case 0x7:	/* CMP Hd,Hs */ | ||||
| 				*ainstr = 0xE1500000	/* base */ | ||||
| 					| (Rd << 16)	/* Rn */ | ||||
| 					|(Rd << 12)	/* Rd */ | ||||
| 					|(Rs << 0);	/* Rm */ | ||||
| 				break; | ||||
| 			case 0x9:	/* MOV Rd,Hs */ | ||||
| 			case 0xA:	/* MOV Hd,Rs */ | ||||
| 			case 0xB:	/* MOV Hd,Hs */ | ||||
| 				*ainstr = 0xE1A00000	/* base */ | ||||
| 					| (Rd << 16)	/* Rn */ | ||||
| 					|(Rd << 12)	/* Rd */ | ||||
| 					|(Rs << 0);	/* Rm */ | ||||
| 				break; | ||||
| 			case 0xC:	/* BX Rs */ | ||||
| 			case 0xD:	/* BX Hs */ | ||||
| 				*ainstr = 0xE12FFF10	/* base */ | ||||
| 					| ((tinstr & 0x0078) >> 3);	/* Rd */ | ||||
| 				break; | ||||
| 			case 0x0:	/* UNDEFINED */ | ||||
| 			case 0x4:	/* UNDEFINED */ | ||||
| 			case 0x8:	/* UNDEFINED */ | ||||
| 				valid = t_undefined; | ||||
| 				break; | ||||
| 			case 0xE:	/* BLX */ | ||||
| 			case 0xF:	/* BLX */ | ||||
| 				 | ||||
| 				//if (state->is_v5) {
 | ||||
| 				if(1){ | ||||
| 					//valid = t_branch;
 | ||||
| 					#if 1 | ||||
| 					*ainstr = 0xE1200030	/* base */ | ||||
| 						|(Rs << 0);	/* Rm */ | ||||
| 					#endif | ||||
| 				} else { | ||||
| 					valid = t_undefined; | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		break; | ||||
| 	case 9:		/* LDR Rd,[PC,#imm8] */ | ||||
| 		/* Format 6 */ | ||||
| 		*ainstr = 0xE59F0000	/* base */ | ||||
| 			| ((tinstr & 0x0700) << (12 - 8))	/* Rd */ | ||||
| 			|((tinstr & 0x00FF) << (2 - 0));	/* off8 */ | ||||
| 		break; | ||||
| 	case 10: | ||||
| 	case 11: | ||||
| 		/* TODO: Format 7 and Format 8 perform the same ARM encoding, so
 | ||||
| 		   the following could be merged into a single subset, saving on | ||||
| 		   the following boolean: */ | ||||
| 		if ((tinstr & (1 << 9)) == 0) { | ||||
| 			/* Format 7 */ | ||||
| 			ARMword subset[4] = { | ||||
| 				0xE7800000,	/* STR  Rd,[Rb,Ro] */ | ||||
| 				0xE7C00000,	/* STRB Rd,[Rb,Ro] */ | ||||
| 				0xE7900000,	/* LDR  Rd,[Rb,Ro] */ | ||||
| 				0xE7D00000	/* LDRB Rd,[Rb,Ro] */ | ||||
| 			}; | ||||
| 			*ainstr = subset[(tinstr & 0x0C00) >> 10]	/* base */ | ||||
| 				|((tinstr & 0x0007) << (12 - 0))	/* Rd */ | ||||
| 				|((tinstr & 0x0038) << (16 - 3))	/* Rb */ | ||||
| 				|((tinstr & 0x01C0) >> 6);	/* Ro */ | ||||
| 		} | ||||
| 		else { | ||||
| 			/* Format 8 */ | ||||
| 			ARMword subset[4] = { | ||||
| 				0xE18000B0,	/* STRH  Rd,[Rb,Ro] */ | ||||
| 				0xE19000D0,	/* LDRSB Rd,[Rb,Ro] */ | ||||
| 				0xE19000B0,	/* LDRH  Rd,[Rb,Ro] */ | ||||
| 				0xE19000F0	/* LDRSH Rd,[Rb,Ro] */ | ||||
| 			}; | ||||
| 			*ainstr = subset[(tinstr & 0x0C00) >> 10]	/* base */ | ||||
| 				|((tinstr & 0x0007) << (12 - 0))	/* Rd */ | ||||
| 				|((tinstr & 0x0038) << (16 - 3))	/* Rb */ | ||||
| 				|((tinstr & 0x01C0) >> 6);	/* Ro */ | ||||
| 		} | ||||
| 		break; | ||||
| 	case 12:		/* STR Rd,[Rb,#imm5] */ | ||||
| 	case 13:		/* LDR Rd,[Rb,#imm5] */ | ||||
| 	case 14:		/* STRB Rd,[Rb,#imm5] */ | ||||
| 	case 15:		/* LDRB Rd,[Rb,#imm5] */ | ||||
| 		/* Format 9 */ | ||||
| 		{ | ||||
| 			ARMword subset[4] = { | ||||
| 				0xE5800000,	/* STR  Rd,[Rb,#imm5] */ | ||||
| 				0xE5900000,	/* LDR  Rd,[Rb,#imm5] */ | ||||
| 				0xE5C00000,	/* STRB Rd,[Rb,#imm5] */ | ||||
| 				0xE5D00000	/* LDRB Rd,[Rb,#imm5] */ | ||||
| 			}; | ||||
| 			/* The offset range defends on whether we are transferring a
 | ||||
| 			   byte or word value: */ | ||||
| 			*ainstr = subset[(tinstr & 0x1800) >> 11]	/* base */ | ||||
| 				|((tinstr & 0x0007) << (12 - 0))	/* Rd */ | ||||
| 				|((tinstr & 0x0038) << (16 - 3))	/* Rb */ | ||||
| 				|((tinstr & 0x07C0) >> (6 - ((tinstr & (1 << 12)) ? 0 : 2)));	/* off5 */ | ||||
| 		} | ||||
| 		break; | ||||
| 	case 16:		/* STRH Rd,[Rb,#imm5] */ | ||||
| 	case 17:		/* LDRH Rd,[Rb,#imm5] */ | ||||
| 		/* Format 10 */ | ||||
| 		*ainstr = ((tinstr & (1 << 11))	/* base */ | ||||
| 			   ? 0xE1D000B0	/* LDRH */ | ||||
| 			   : 0xE1C000B0)	/* STRH */ | ||||
| 			|((tinstr & 0x0007) << (12 - 0))	/* Rd */ | ||||
| 			|((tinstr & 0x0038) << (16 - 3))	/* Rb */ | ||||
| 			|((tinstr & 0x01C0) >> (6 - 1))	/* off5, low nibble */ | ||||
| 			|((tinstr & 0x0600) >> (9 - 8));	/* off5, high nibble */ | ||||
| 		break; | ||||
| 	case 18:		/* STR Rd,[SP,#imm8] */ | ||||
| 	case 19:		/* LDR Rd,[SP,#imm8] */ | ||||
| 		/* Format 11 */ | ||||
| 		*ainstr = ((tinstr & (1 << 11))	/* base */ | ||||
| 			   ? 0xE59D0000	/* LDR */ | ||||
| 			   : 0xE58D0000)	/* STR */ | ||||
| 			|((tinstr & 0x0700) << (12 - 8))	/* Rd */ | ||||
| 			|((tinstr & 0x00FF) << 2);	/* off8 */ | ||||
| 		break; | ||||
| 	case 20:		/* ADD Rd,PC,#imm8 */ | ||||
| 	case 21:		/* ADD Rd,SP,#imm8 */ | ||||
| 		/* Format 12 */ | ||||
| 		if ((tinstr & (1 << 11)) == 0) { | ||||
| 			/* NOTE: The PC value used here should by word aligned */ | ||||
| 			/* We encode shift-left-by-2 in the rotate immediate field,
 | ||||
| 			   so no shift of off8 is needed.  */ | ||||
| 			*ainstr = 0xE28F0F00	/* base */ | ||||
| 				| ((tinstr & 0x0700) << (12 - 8))	/* Rd */ | ||||
| 				|(tinstr & 0x00FF);	/* off8 */ | ||||
| 		} | ||||
| 		else { | ||||
| 			/* We encode shift-left-by-2 in the rotate immediate field,
 | ||||
| 			   so no shift of off8 is needed.  */ | ||||
| 			*ainstr = 0xE28D0F00	/* base */ | ||||
| 				| ((tinstr & 0x0700) << (12 - 8))	/* Rd */ | ||||
| 				|(tinstr & 0x00FF);	/* off8 */ | ||||
| 		} | ||||
| 		break; | ||||
| 	case 22: | ||||
| 	case 23: | ||||
| 		if ((tinstr & 0x0F00) == 0x0000) { | ||||
| 			/* Format 13 */ | ||||
| 			/* NOTE: The instruction contains a shift left of 2
 | ||||
| 			   equivalent (implemented as ROR #30): */ | ||||
| 			*ainstr = ((tinstr & (1 << 7))	/* base */ | ||||
| 				   ? 0xE24DDF00	/* SUB */ | ||||
| 				   : 0xE28DDF00)	/* ADD */ | ||||
| 				|(tinstr & 0x007F);	/* off7 */ | ||||
| 		} | ||||
| 		else if ((tinstr & 0x0F00) == 0x0e00) | ||||
| 			*ainstr = 0xEF000000 | SWI_Breakpoint; | ||||
| 		else { | ||||
| 			/* Format 14 */ | ||||
| 			ARMword subset[4] = { | ||||
| 				0xE92D0000,	/* STMDB sp!,{rlist}    */ | ||||
| 				0xE92D4000,	/* STMDB sp!,{rlist,lr} */ | ||||
| 				0xE8BD0000,	/* LDMIA sp!,{rlist}    */ | ||||
| 				0xE8BD8000	/* LDMIA sp!,{rlist,pc} */ | ||||
| 			}; | ||||
| 			*ainstr = subset[((tinstr & (1 << 11)) >> 10) | ((tinstr & (1 << 8)) >> 8)]	/* base */ | ||||
| 				|(tinstr & 0x00FF);	/* mask8 */ | ||||
| 		} | ||||
| 		break; | ||||
| 	case 24:		/* STMIA */ | ||||
| 	case 25:		/* LDMIA */ | ||||
| 		/* Format 15 */ | ||||
| 		*ainstr = ((tinstr & (1 << 11))	/* base */ | ||||
| 			   ? 0xE8B00000	/* LDMIA */ | ||||
| 			   : 0xE8A00000)	/* STMIA */ | ||||
| 			|((tinstr & 0x0700) << (16 - 8))	/* Rb */ | ||||
| 			|(tinstr & 0x00FF);	/* mask8 */ | ||||
| 		break; | ||||
| 	case 26:		/* Bcc */ | ||||
| 	case 27:		/* Bcc/SWI */ | ||||
| 		if ((tinstr & 0x0F00) == 0x0F00) { | ||||
| 			#if 0 | ||||
| 			if (tinstr == (ARMul_ABORTWORD & 0xffff) && | ||||
| 					state->AbortAddr == pc) { | ||||
| 				*ainstr = ARMul_ABORTWORD; | ||||
| 				break; | ||||
| 			} | ||||
| 			#endif | ||||
| 			/* Format 17 : SWI */ | ||||
| 			*ainstr = 0xEF000000; | ||||
| 			/* Breakpoint must be handled specially.  */ | ||||
| 			if ((tinstr & 0x00FF) == 0x18) | ||||
| 				*ainstr |= ((tinstr & 0x00FF) << 16); | ||||
| 			/* New breakpoint value.  See gdb/arm-tdep.c  */ | ||||
| 			else if ((tinstr & 0x00FF) == 0xFE) | ||||
| 				*ainstr |= SWI_Breakpoint; | ||||
| 			else | ||||
| 				*ainstr |= (tinstr & 0x00FF); | ||||
| 		} | ||||
| 		else if ((tinstr & 0x0F00) != 0x0E00) { | ||||
| 			/* Format 16 */ | ||||
| 			#if 0 | ||||
| 			int doit = FALSE; | ||||
| 			/* TODO: Since we are doing a switch here, we could just add
 | ||||
| 			   the SWI and undefined instruction checks into this | ||||
| 			   switch to same on a couple of conditionals: */ | ||||
| 			switch ((tinstr & 0x0F00) >> 8) { | ||||
| 			case EQ: | ||||
| 				doit = ZFLAG; | ||||
| 				break; | ||||
| 			case NE: | ||||
| 				doit = !ZFLAG; | ||||
| 				break; | ||||
| 			case VS: | ||||
| 				doit = VFLAG; | ||||
| 				break; | ||||
| 			case VC: | ||||
| 				doit = !VFLAG; | ||||
| 				break; | ||||
| 			case MI: | ||||
| 				doit = NFLAG; | ||||
| 				break; | ||||
| 			case PL: | ||||
| 				doit = !NFLAG; | ||||
| 				break; | ||||
| 			case CS: | ||||
| 				doit = CFLAG; | ||||
| 				break; | ||||
| 			case CC: | ||||
| 				doit = !CFLAG; | ||||
| 				break; | ||||
| 			case HI: | ||||
| 				doit = (CFLAG && !ZFLAG); | ||||
| 				break; | ||||
| 			case LS: | ||||
| 				doit = (!CFLAG || ZFLAG); | ||||
| 				break; | ||||
| 			case GE: | ||||
| 				doit = ((!NFLAG && !VFLAG) | ||||
| 					|| (NFLAG && VFLAG)); | ||||
| 				break; | ||||
| 			case LT: | ||||
| 				doit = ((NFLAG && !VFLAG) | ||||
| 					|| (!NFLAG && VFLAG)); | ||||
| 				break; | ||||
| 			case GT: | ||||
| 				doit = ((!NFLAG && !VFLAG && !ZFLAG) | ||||
| 					|| (NFLAG && VFLAG && !ZFLAG)); | ||||
| 				break; | ||||
| 			case LE: | ||||
| 				doit = ((NFLAG && !VFLAG) | ||||
| 					|| (!NFLAG && VFLAG)) || ZFLAG; | ||||
| 				break; | ||||
| 			} | ||||
| 			if (doit) { | ||||
| 				state->Reg[15] = (pc + 4 | ||||
| 						  + (((tinstr & 0x7F) << 1) | ||||
| 						     | ((tinstr & (1 << 7)) ? | ||||
| 							0xFFFFFF00 : 0))); | ||||
| 				FLUSHPIPE; | ||||
| 			} | ||||
| 			#endif | ||||
| 			valid = t_branch; | ||||
| 		} | ||||
| 		else		/* UNDEFINED : cc=1110(AL) uses different format */ | ||||
| 			valid = t_undefined; | ||||
| 		break; | ||||
| 	case 28:		/* B */ | ||||
| 		/* Format 18 */ | ||||
| 		#if 0 | ||||
| 		state->Reg[15] = (pc + 4 + (((tinstr & 0x3FF) << 1) | ||||
| 					    | ((tinstr & (1 << 10)) ? | ||||
| 					       0xFFFFF800 : 0))); | ||||
| 		#endif | ||||
| 		//FLUSHPIPE;
 | ||||
| 		valid = t_branch; | ||||
| 		break; | ||||
| 	case 29: | ||||
| 		if(tinstr & 0x1) | ||||
| 			valid = t_undefined; | ||||
| 		else{ | ||||
| 			/* BLX 1 for armv5t and above */ | ||||
| 			//printf("In %s, After  BLX(1),LR=0x%x,PC=0x%x, offset=0x%x\n", __FUNCTION__, state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1);
 | ||||
| 			valid = t_branch; | ||||
| 		} | ||||
| 		break; | ||||
| 	case 30:		/* BL instruction 1 */ | ||||
| 		/* Format 19 */ | ||||
| 		/* There is no single ARM instruction equivalent for this Thumb
 | ||||
| 		   instruction. To keep the simulation simple (from the user | ||||
| 		   perspective) we check if the following instruction is the | ||||
| 		   second half of this BL, and if it is we simulate it | ||||
| 		   immediately.  */ | ||||
| 		valid = t_branch; | ||||
| 		break; | ||||
| 	case 31:		/* BL instruction 2 */ | ||||
| 		/* Format 19 */ | ||||
| 		/* There is no single ARM instruction equivalent for this
 | ||||
| 		   instruction. Also, it should only ever be matched with the | ||||
| 		   fmt19 "BL instruction 1" instruction. However, we do allow | ||||
| 		   the simulation of it on its own, with undefined results if | ||||
| 		   r14 is not suitably initialised.  */ | ||||
| 		{ | ||||
| 			#if 0 | ||||
| 			ARMword tmp = (pc + 2); | ||||
| 			state->Reg[15] = | ||||
| 				(state->Reg[14] + ((tinstr & 0x07FF) << 1)); | ||||
| 			state->Reg[14] = (tmp | 1); | ||||
| 			#endif | ||||
| 			valid = t_branch; | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 	*inst_size = 2; | ||||
| 	return valid; | ||||
|     switch ((tinstr & 0xF800) >> 11) { | ||||
|     case 0: // LSL
 | ||||
|     case 1: // LSR
 | ||||
|     case 2: // ASR
 | ||||
|         *ainstr = 0xE1B00000                    // base opcode
 | ||||
|             | ((tinstr & 0x1800) >> (11 - 5))   // shift type
 | ||||
|             |((tinstr & 0x07C0) << (7 - 6))     // imm5
 | ||||
|             |((tinstr & 0x0038) >> 3)           // Rs
 | ||||
|             |((tinstr & 0x0007) << 12);         // Rd
 | ||||
|         break; | ||||
| 
 | ||||
|     case 3: // ADD/SUB
 | ||||
|         { | ||||
|             ARMword subset[4] = { | ||||
|                 0xE0900000,     // ADDS Rd,Rs,Rn
 | ||||
|                 0xE0500000,     // SUBS Rd,Rs,Rn
 | ||||
|                 0xE2900000,     // ADDS Rd,Rs,#imm3
 | ||||
|                 0xE2500000      // SUBS Rd,Rs,#imm3
 | ||||
|             }; | ||||
|             // It is quicker indexing into a table, than performing switch or conditionals:
 | ||||
|             *ainstr = subset[(tinstr & 0x0600) >> 9]    // base opcode
 | ||||
|                 |((tinstr & 0x01C0) >> 6)               // Rn or imm3
 | ||||
|                 |((tinstr & 0x0038) << (16 - 3))        // Rs
 | ||||
|                 |((tinstr & 0x0007) << (12 - 0));       // Rd
 | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case 4: // MOV
 | ||||
|     case 5: // CMP
 | ||||
|     case 6: // ADD
 | ||||
|     case 7: // SUB
 | ||||
|         { | ||||
|             ARMword subset[4] = { | ||||
|                 0xE3B00000,     // MOVS Rd,#imm8
 | ||||
|                 0xE3500000,     // CMP  Rd,#imm8
 | ||||
|                 0xE2900000,     // ADDS Rd,Rd,#imm8
 | ||||
|                 0xE2500000,     // SUBS Rd,Rd,#imm8
 | ||||
|             }; | ||||
| 
 | ||||
|             *ainstr = subset[(tinstr & 0x1800) >> 11]   // base opcode
 | ||||
|                 |((tinstr & 0x00FF) >> 0)               // imm8
 | ||||
|                 |((tinstr & 0x0700) << (16 - 8))        // Rn
 | ||||
|                 |((tinstr & 0x0700) << (12 - 8));       // Rd
 | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case 8: // Arithmetic and high register transfers
 | ||||
| 
 | ||||
|         // TODO: Since the subsets for both Format 4 and Format 5 instructions are made up of
 | ||||
|         // different ARM encodings, we could save the following conditional, and just have one
 | ||||
|         // large subset
 | ||||
| 
 | ||||
|         if ((tinstr & (1 << 10)) == 0) { | ||||
|             enum otype { | ||||
|                 t_norm, | ||||
|                 t_shift, | ||||
|                 t_neg, | ||||
|                 t_mul | ||||
|             }; | ||||
| 
 | ||||
|             struct { | ||||
|                 ARMword opcode; | ||||
|                 otype type; | ||||
|             } subset[16] = { | ||||
|                 { 0xE0100000, t_norm },     // ANDS Rd,Rd,Rs
 | ||||
|                 { 0xE0300000, t_norm },     // EORS Rd,Rd,Rs
 | ||||
|                 { 0xE1B00010, t_shift },    // MOVS Rd,Rd,LSL Rs
 | ||||
|                 { 0xE1B00030, t_shift },    // MOVS Rd,Rd,LSR Rs
 | ||||
|                 { 0xE1B00050, t_shift },    // MOVS Rd,Rd,ASR Rs
 | ||||
|                 { 0xE0B00000, t_norm },     // ADCS Rd,Rd,Rs
 | ||||
|                 { 0xE0D00000, t_norm },     // SBCS Rd,Rd,Rs
 | ||||
|                 { 0xE1B00070, t_shift },    // MOVS Rd,Rd,ROR Rs
 | ||||
|                 { 0xE1100000, t_norm },     // TST  Rd,Rs
 | ||||
|                 { 0xE2700000, t_neg },      // RSBS Rd,Rs,#0
 | ||||
|                 { 0xE1500000, t_norm },     // CMP  Rd,Rs
 | ||||
|                 { 0xE1700000, t_norm },     // CMN  Rd,Rs
 | ||||
|                 { 0xE1900000, t_norm },     // ORRS Rd,Rd,Rs
 | ||||
|                 { 0xE0100090, t_mul },      // MULS Rd,Rd,Rs
 | ||||
|                 { 0xE1D00000, t_norm },     // BICS Rd,Rd,Rs
 | ||||
|                 { 0xE1F00000, t_norm }      // MVNS Rd,Rs
 | ||||
|             }; | ||||
| 
 | ||||
|             *ainstr = subset[(tinstr & 0x03C0) >> 6].opcode; // base
 | ||||
| 
 | ||||
|             switch (subset[(tinstr & 0x03C0) >> 6].type) { | ||||
|             case t_norm: | ||||
|                 *ainstr |= ((tinstr & 0x0007) << 16)    // Rn
 | ||||
|                     |((tinstr & 0x0007) << 12)          // Rd
 | ||||
|                     |((tinstr & 0x0038) >> 3);          // Rs
 | ||||
|                 break; | ||||
|             case t_shift: | ||||
|                 *ainstr |= ((tinstr & 0x0007) << 12)    // Rd
 | ||||
|                     |((tinstr & 0x0007) >> 0)           // Rm
 | ||||
|                     |((tinstr & 0x0038) << (8 - 3));    // Rs
 | ||||
|                 break; | ||||
|             case t_neg: | ||||
|                 *ainstr |= ((tinstr & 0x0007) << 12)    // Rd
 | ||||
|                     |((tinstr & 0x0038) << (16 - 3));   // Rn
 | ||||
|                 break; | ||||
|             case t_mul: | ||||
|                 *ainstr |= ((tinstr & 0x0007) << 16)    // Rd
 | ||||
|                     |((tinstr & 0x0007) << 8)           // Rs
 | ||||
|                     |((tinstr & 0x0038) >> 3);          // Rm
 | ||||
|                 break; | ||||
|             } | ||||
|         } else { | ||||
|             ARMword Rd = ((tinstr & 0x0007) >> 0); | ||||
|             ARMword Rs = ((tinstr & 0x0038) >> 3); | ||||
| 
 | ||||
|             if (tinstr & (1 << 7)) | ||||
|                 Rd += 8; | ||||
|             if (tinstr & (1 << 6)) | ||||
|                 Rs += 8; | ||||
| 
 | ||||
|             switch ((tinstr & 0x03C0) >> 6) { | ||||
|             case 0x1:                           // ADD Rd,Rd,Hs
 | ||||
|             case 0x2:                           // ADD Hd,Hd,Rs
 | ||||
|             case 0x3:                           // ADD Hd,Hd,Hs
 | ||||
|                 *ainstr = 0xE0800000            // base
 | ||||
|                     | (Rd << 16)                // Rn
 | ||||
|                     |(Rd << 12)                 // Rd
 | ||||
|                     |(Rs << 0);                 // Rm
 | ||||
|                 break; | ||||
|             case 0x5:                           // CMP Rd,Hs
 | ||||
|             case 0x6:                           // CMP Hd,Rs
 | ||||
|             case 0x7:                           // CMP Hd,Hs
 | ||||
|                 *ainstr = 0xE1500000            // base
 | ||||
|                     | (Rd << 16)                // Rn
 | ||||
|                     |(Rd << 12)                 // Rd
 | ||||
|                     |(Rs << 0);                 // Rm
 | ||||
|                 break; | ||||
|             case 0x9:                           // MOV Rd,Hs
 | ||||
|             case 0xA:                           // MOV Hd,Rs
 | ||||
|             case 0xB:                           // MOV Hd,Hs
 | ||||
|                 *ainstr = 0xE1A00000            // base
 | ||||
|                     | (Rd << 16)                // Rn
 | ||||
|                     |(Rd << 12)                 // Rd
 | ||||
|                     |(Rs << 0);                 // Rm
 | ||||
|                 break; | ||||
|             case 0xC:                           // BX Rs
 | ||||
|             case 0xD:                           // BX Hs
 | ||||
|                 *ainstr = 0xE12FFF10            // base
 | ||||
|                     | ((tinstr & 0x0078) >> 3); // Rd
 | ||||
|                 break; | ||||
|             case 0x0:                           // UNDEFINED
 | ||||
|             case 0x4:                           // UNDEFINED
 | ||||
|             case 0x8:                           // UNDEFINED
 | ||||
|                 valid = t_undefined; | ||||
|                 break; | ||||
|             case 0xE:                           // BLX
 | ||||
|             case 0xF:                           // BLX
 | ||||
|                 *ainstr = 0xE1200030            // base
 | ||||
|                     | (Rs << 0);                // Rm
 | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case 9: // LDR Rd,[PC,#imm8]
 | ||||
|         *ainstr = 0xE59F0000                    // base
 | ||||
|             | ((tinstr & 0x0700) << (12 - 8))   // Rd
 | ||||
|             |((tinstr & 0x00FF) << (2 - 0));    // off8 
 | ||||
|         break; | ||||
| 
 | ||||
|     case 10: | ||||
|     case 11: | ||||
|         // TODO: Format 7 and Format 8 perform the same ARM encoding, so the following could be
 | ||||
|         // merged into a single subset, saving on the following boolean:
 | ||||
| 
 | ||||
|         if ((tinstr & (1 << 9)) == 0) { | ||||
|             ARMword subset[4] = { | ||||
|                 0xE7800000, // STR  Rd,[Rb,Ro]
 | ||||
|                 0xE7C00000, // STRB Rd,[Rb,Ro]
 | ||||
|                 0xE7900000, // LDR  Rd,[Rb,Ro]
 | ||||
|                 0xE7D00000  // LDRB Rd,[Rb,Ro]
 | ||||
|             }; | ||||
| 
 | ||||
|             *ainstr = subset[(tinstr & 0x0C00) >> 10]   // base
 | ||||
|                 |((tinstr & 0x0007) << (12 - 0))        // Rd
 | ||||
|                 |((tinstr & 0x0038) << (16 - 3))        // Rb
 | ||||
|                 |((tinstr & 0x01C0) >> 6);              // Ro
 | ||||
| 
 | ||||
|         } else { | ||||
|             ARMword subset[4] = { | ||||
|                 0xE18000B0, // STRH  Rd,[Rb,Ro]
 | ||||
|                 0xE19000D0, // LDRSB Rd,[Rb,Ro]
 | ||||
|                 0xE19000B0, // LDRH  Rd,[Rb,Ro]
 | ||||
|                 0xE19000F0  // LDRSH Rd,[Rb,Ro]
 | ||||
|             }; | ||||
|             *ainstr = subset[(tinstr & 0x0C00) >> 10]   // base
 | ||||
|                 |((tinstr & 0x0007) << (12 - 0))        // Rd
 | ||||
|                 |((tinstr & 0x0038) << (16 - 3))        // Rb
 | ||||
|                 |((tinstr & 0x01C0) >> 6);              // Ro
 | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case 12: // STR Rd,[Rb,#imm5]
 | ||||
|     case 13: // LDR Rd,[Rb,#imm5]
 | ||||
|     case 14: // STRB Rd,[Rb,#imm5]
 | ||||
|     case 15: // LDRB Rd,[Rb,#imm5]
 | ||||
|         { | ||||
|             ARMword subset[4] = { | ||||
|                 0xE5800000,     // STR  Rd,[Rb,#imm5]
 | ||||
|                 0xE5900000,     // LDR  Rd,[Rb,#imm5]
 | ||||
|                 0xE5C00000,     // STRB Rd,[Rb,#imm5]
 | ||||
|                 0xE5D00000      // LDRB Rd,[Rb,#imm5]
 | ||||
|             }; | ||||
|             // The offset range defends on whether we are transferring a byte or word value:
 | ||||
|             *ainstr = subset[(tinstr & 0x1800) >> 11]   // base
 | ||||
|                 |((tinstr & 0x0007) << (12 - 0))        // Rd
 | ||||
|                 |((tinstr & 0x0038) << (16 - 3))        // Rb
 | ||||
|                 |((tinstr & 0x07C0) >> (6 - ((tinstr & (1 << 12)) ? 0 : 2))); // off5
 | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case 16: // STRH Rd,[Rb,#imm5]
 | ||||
|     case 17: // LDRH Rd,[Rb,#imm5]
 | ||||
|         *ainstr = ((tinstr & (1 << 11))         // base
 | ||||
|                ? 0xE1D000B0                     // LDRH
 | ||||
|                : 0xE1C000B0)                    // STRH
 | ||||
|             |((tinstr & 0x0007) << (12 - 0))    // Rd
 | ||||
|             |((tinstr & 0x0038) << (16 - 3))    // Rb
 | ||||
|             |((tinstr & 0x01C0) >> (6 - 1))     // off5, low nibble
 | ||||
|             |((tinstr & 0x0600) >> (9 - 8));    // off5, high nibble
 | ||||
|         break; | ||||
| 
 | ||||
|     case 18: // STR Rd,[SP,#imm8]
 | ||||
|     case 19: // LDR Rd,[SP,#imm8]
 | ||||
|         *ainstr = ((tinstr & (1 << 11))         // base
 | ||||
|                ? 0xE59D0000                     // LDR
 | ||||
|                : 0xE58D0000)                    // STR
 | ||||
|             |((tinstr & 0x0700) << (12 - 8))    // Rd
 | ||||
|             |((tinstr & 0x00FF) << 2);          // off8
 | ||||
|         break; | ||||
| 
 | ||||
|     case 20: // ADD Rd,PC,#imm8
 | ||||
|     case 21: // ADD Rd,SP,#imm8
 | ||||
| 
 | ||||
|         if ((tinstr & (1 << 11)) == 0) { | ||||
| 
 | ||||
|             // NOTE: The PC value used here should by word aligned. We encode shift-left-by-2 in the
 | ||||
|             // rotate immediate field, so no shift of off8 is needed.
 | ||||
| 
 | ||||
|             *ainstr = 0xE28F0F00                    // base
 | ||||
|                 | ((tinstr & 0x0700) << (12 - 8))   // Rd
 | ||||
|                 |(tinstr & 0x00FF);                 // off8
 | ||||
|         } else { | ||||
|             // We encode shift-left-by-2 in the rotate immediate field, so no shift of off8 is needed.
 | ||||
|             *ainstr = 0xE28D0F00                    // base
 | ||||
|                 | ((tinstr & 0x0700) << (12 - 8))   // Rd
 | ||||
|                 |(tinstr & 0x00FF);                 // off8
 | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case 22: | ||||
|     case 23: | ||||
|         if ((tinstr & 0x0F00) == 0x0000) { | ||||
|             // NOTE: The instruction contains a shift left of 2 equivalent (implemented as ROR #30):
 | ||||
|             *ainstr = ((tinstr & (1 << 7))  // base
 | ||||
|                    ? 0xE24DDF00             // SUB
 | ||||
|                    : 0xE28DDF00)            // ADD
 | ||||
|                 |(tinstr & 0x007F);         // off7
 | ||||
|         } else if ((tinstr & 0x0F00) == 0x0e00) | ||||
|             *ainstr = 0xEF000000 | SWI_Breakpoint; | ||||
|         else { | ||||
|             ARMword subset[4] = { | ||||
|                 0xE92D0000, // STMDB sp!,{rlist}
 | ||||
|                 0xE92D4000, // STMDB sp!,{rlist,lr}
 | ||||
|                 0xE8BD0000, // LDMIA sp!,{rlist}
 | ||||
|                 0xE8BD8000  // LDMIA sp!,{rlist,pc}
 | ||||
|             }; | ||||
|             *ainstr = subset[((tinstr & (1 << 11)) >> 10) | ((tinstr & (1 << 8)) >> 8)] // base
 | ||||
|                 |(tinstr & 0x00FF); // mask8
 | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case 24: //  STMIA
 | ||||
|     case 25: //  LDMIA
 | ||||
|         *ainstr = ((tinstr & (1 << 11))         // base
 | ||||
|                ? 0xE8B00000                     // LDMIA
 | ||||
|                : 0xE8A00000)                    // STMIA
 | ||||
|             |((tinstr & 0x0700) << (16 - 8))    // Rb
 | ||||
|             |(tinstr & 0x00FF);                 // mask8
 | ||||
|         break; | ||||
| 
 | ||||
|     case 26: // Bcc
 | ||||
|     case 27: // Bcc/SWI
 | ||||
|         if ((tinstr & 0x0F00) == 0x0F00) { | ||||
|             // Format 17 : SWI
 | ||||
|             *ainstr = 0xEF000000; | ||||
|             // Breakpoint must be handled specially.
 | ||||
|             if ((tinstr & 0x00FF) == 0x18) | ||||
|                 *ainstr |= ((tinstr & 0x00FF) << 16); | ||||
|             // New breakpoint value.  See gdb/arm-tdep.c
 | ||||
|             else if ((tinstr & 0x00FF) == 0xFE) | ||||
|                 *ainstr |= SWI_Breakpoint; | ||||
|             else | ||||
|                 *ainstr |= (tinstr & 0x00FF); | ||||
|         } else if ((tinstr & 0x0F00) != 0x0E00) | ||||
|             valid = t_branch; | ||||
|         else //  UNDEFINED : cc=1110(AL) uses different format
 | ||||
|             valid = t_undefined; | ||||
| 
 | ||||
|         break; | ||||
| 
 | ||||
|     case 28: // B
 | ||||
|         valid = t_branch; | ||||
|         break; | ||||
| 
 | ||||
|     case 29: | ||||
|         if(tinstr & 0x1) | ||||
|             valid = t_undefined; | ||||
|         else | ||||
|             valid = t_branch; | ||||
|         break; | ||||
| 
 | ||||
|     case 30: // BL instruction 1
 | ||||
| 
 | ||||
|         // There is no single ARM instruction equivalent for this Thumb instruction. To keep the
 | ||||
|         // simulation simple (from the user perspective) we check if the following instruction is
 | ||||
|         // the second half of this BL, and if it is we simulate it immediately
 | ||||
| 
 | ||||
|         valid = t_branch; | ||||
|         break; | ||||
| 
 | ||||
|     case 31: // BL instruction 2
 | ||||
| 
 | ||||
|         // There is no single ARM instruction equivalent for this instruction. Also, it should only
 | ||||
|         // ever be matched with the fmt19 "BL instruction 1" instruction. However, we do allow the
 | ||||
|         // simulation of it on its own, with undefined results if r14 is not suitably initialised.
 | ||||
| 
 | ||||
|         valid = t_branch; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     *inst_size = 2; | ||||
| 
 | ||||
|     return valid; | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "core/arm/interpreter/arm_interpreter.h" | ||||
|  | @ -38,78 +38,43 @@ ARM_Interpreter::~ARM_Interpreter() { | |||
|     delete state; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Set the Program Counter to an address | ||||
|  * @param addr Address to set PC to | ||||
|  */ | ||||
| void ARM_Interpreter::SetPC(u32 pc) { | ||||
|     state->pc = state->Reg[15] = pc; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Get the current Program Counter | ||||
|  * @return Returns current PC | ||||
|  */ | ||||
| u32 ARM_Interpreter::GetPC() const { | ||||
|     return state->pc; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get an ARM register | ||||
|  * @param index Register index (0-15) | ||||
|  * @return Returns the value in the register | ||||
|  */ | ||||
| u32 ARM_Interpreter::GetReg(int index) const { | ||||
|     return state->Reg[index]; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Set an ARM register | ||||
|  * @param index Register index (0-15) | ||||
|  * @param value Value to set register to | ||||
|  */ | ||||
| void ARM_Interpreter::SetReg(int index, u32 value) { | ||||
|     state->Reg[index] = value; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the current CPSR register | ||||
|  * @return Returns the value of the CPSR register | ||||
|  */ | ||||
| u32 ARM_Interpreter::GetCPSR() const { | ||||
|     return state->Cpsr; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Set the current CPSR register | ||||
|  * @param cpsr Value to set CPSR to | ||||
|  */ | ||||
| void ARM_Interpreter::SetCPSR(u32 cpsr) { | ||||
|     state->Cpsr = cpsr; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns the number of clock ticks since the last reset | ||||
|  * @return Returns number of clock ticks | ||||
|  */ | ||||
| u64 ARM_Interpreter::GetTicks() const { | ||||
|     return ARMul_Time(state); | ||||
|     return state->NumInstrs; | ||||
| } | ||||
| 
 | ||||
| void ARM_Interpreter::AddTicks(u64 ticks) { | ||||
|     state->NumInstrs += ticks; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Executes the given number of instructions | ||||
|  * @param num_instructions Number of instructions to executes | ||||
|  */ | ||||
| void ARM_Interpreter::ExecuteInstructions(int num_instructions) { | ||||
|     state->NumInstrsToExecute = num_instructions - 1; | ||||
|     ARMul_Emulate32(state); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Saves the current CPU context | ||||
|  * @param ctx Thread context to save | ||||
|  * @todo Do we need to save Reg[15] and NextInstr? | ||||
|  */ | ||||
| void ARM_Interpreter::SaveContext(ThreadContext& ctx) { | ||||
|     memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); | ||||
|     memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); | ||||
|  | @ -126,11 +91,6 @@ void ARM_Interpreter::SaveContext(ThreadContext& ctx) { | |||
|     ctx.mode = state->NextInstr; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads a CPU context | ||||
|  * @param ctx Thread context to load | ||||
|  * @param Do we need to load Reg[15] and NextInstr? | ||||
|  */ | ||||
| void ARM_Interpreter::LoadContext(const ThreadContext& ctx) { | ||||
|     memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); | ||||
|     memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); | ||||
|  | @ -147,7 +107,6 @@ void ARM_Interpreter::LoadContext(const ThreadContext& ctx) { | |||
|     state->NextInstr = ctx.mode; | ||||
| } | ||||
| 
 | ||||
| /// Prepare core for thread reschedule (if needed to correctly handle state)
 | ||||
| void ARM_Interpreter::PrepareReschedule() { | ||||
|     state->NumInstrsToExecute = 0; | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -60,6 +60,12 @@ public: | |||
|      */ | ||||
|     u64 GetTicks() const override; | ||||
| 
 | ||||
|     /**
 | ||||
|     * 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 | ||||
|     */ | ||||
|     void AddTicks(u64 ticks) override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Saves the current CPU context | ||||
|      * @param ctx Thread context to save | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue