diff options
Diffstat (limited to 'src')
199 files changed, 4980 insertions, 3380 deletions
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index 713f49193c..9186873126 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -16,6 +16,11 @@ create_directory_groups(${SRCS} ${HEADERS}) add_executable(citra ${SRCS} ${HEADERS}) target_link_libraries(citra core common video_core) target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih) +if (MSVC) + target_link_libraries(citra getopt) +endif() target_link_libraries(citra ${PLATFORM_LIBRARIES}) -#install(TARGETS citra RUNTIME DESTINATION ${bindir}) +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") + install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") +endif()
\ No newline at end of file diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index ce8d7dd258..182646f4cd 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -2,13 +2,20 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <string> #include <thread> +#include <iostream> + +#ifdef _MSC_VER +#include <getopt.h> +#else +#include <unistd.h> +#include <getopt.h> +#endif #include "common/logging/log.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" @@ -20,12 +27,39 @@ #include "video_core/video_core.h" + +static void PrintHelp() +{ + std::cout << "Usage: citra <filename>" << std::endl; +} + /// Application entry point int main(int argc, char **argv) { + int option_index = 0; + std::string boot_filename; + static struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + while (optind < argc) { + char arg = getopt_long(argc, argv, ":h", long_options, &option_index); + if (arg != -1) { + switch (arg) { + case 'h': + PrintHelp(); + return 0; + } + } else { + boot_filename = argv[optind]; + optind++; + } + } + Log::Filter log_filter(Log::Level::Debug); Log::SetFilter(&log_filter); - if (argc < 2) { + if (boot_filename.empty()) { LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); return -1; } @@ -33,7 +67,7 @@ int main(int argc, char **argv) { Config config; log_filter.ParseFilterString(Settings::values.log_filter); - std::string boot_filename = argv[1]; + EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; VideoCore::g_hw_renderer_enabled = Settings::values.use_hw_renderer; diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 1378567c16..2c1407a6f2 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -2,7 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#define GLFW_INCLUDE_NONE #include <GLFW/glfw3.h> +#include <inih/cpp/INIReader.h> #include "citra/default_ini.h" @@ -10,7 +12,6 @@ #include "common/logging/log.h" #include "core/settings.h" -#include "core/core.h" #include "config.h" @@ -39,31 +40,21 @@ bool Config::LoadINI(INIReader* config, const char* location, const std::string& return true; } +static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { + GLFW_KEY_A, GLFW_KEY_S, GLFW_KEY_Z, GLFW_KEY_X, + GLFW_KEY_Q, GLFW_KEY_W, GLFW_KEY_1, GLFW_KEY_2, + GLFW_KEY_M, GLFW_KEY_N, GLFW_KEY_B, + GLFW_KEY_T, GLFW_KEY_G, GLFW_KEY_F, GLFW_KEY_H, + GLFW_KEY_UP, GLFW_KEY_DOWN, GLFW_KEY_LEFT, GLFW_KEY_RIGHT, + GLFW_KEY_I, GLFW_KEY_K, GLFW_KEY_J, GLFW_KEY_L +}; + 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); - Settings::values.pad_y_key = glfw_config->GetInteger("Controls", "pad_y", GLFW_KEY_X); - Settings::values.pad_l_key = glfw_config->GetInteger("Controls", "pad_l", GLFW_KEY_Q); - Settings::values.pad_r_key = glfw_config->GetInteger("Controls", "pad_r", GLFW_KEY_W); - Settings::values.pad_zl_key = glfw_config->GetInteger("Controls", "pad_zl", GLFW_KEY_1); - Settings::values.pad_zr_key = glfw_config->GetInteger("Controls", "pad_zr", GLFW_KEY_2); - Settings::values.pad_start_key = glfw_config->GetInteger("Controls", "pad_start", GLFW_KEY_M); - Settings::values.pad_select_key = glfw_config->GetInteger("Controls", "pad_select", GLFW_KEY_N); - Settings::values.pad_home_key = glfw_config->GetInteger("Controls", "pad_home", GLFW_KEY_B); - Settings::values.pad_dup_key = glfw_config->GetInteger("Controls", "pad_dup", GLFW_KEY_T); - Settings::values.pad_ddown_key = glfw_config->GetInteger("Controls", "pad_ddown", GLFW_KEY_G); - Settings::values.pad_dleft_key = glfw_config->GetInteger("Controls", "pad_dleft", GLFW_KEY_F); - Settings::values.pad_dright_key = glfw_config->GetInteger("Controls", "pad_dright", GLFW_KEY_H); - Settings::values.pad_sup_key = glfw_config->GetInteger("Controls", "pad_sup", GLFW_KEY_UP); - 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); - Settings::values.pad_cup_key = glfw_config->GetInteger("Controls", "pad_cup", GLFW_KEY_I); - Settings::values.pad_cdown_key = glfw_config->GetInteger("Controls", "pad_cdown", GLFW_KEY_K); - Settings::values.pad_cleft_key = glfw_config->GetInteger("Controls", "pad_cleft", GLFW_KEY_J); - Settings::values.pad_cright_key = glfw_config->GetInteger("Controls", "pad_cright", GLFW_KEY_L); + for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { + Settings::values.input_mappings[Settings::NativeInput::All[i]] = + glfw_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]); + } // Core Settings::values.frame_skip = glfw_config->GetInteger("Core", "frame_skip", 0); diff --git a/src/citra/config.h b/src/citra/config.h index 0eb176c7d2..c326ec6696 100644 --- a/src/citra/config.h +++ b/src/citra/config.h @@ -4,11 +4,9 @@ #pragma once -#include <map> +#include <string> -#include <inih/cpp/INIReader.h> - -#include "common/common_types.h" +class INIReader; class Config { INIReader* glfw_config; diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index fd5a90d568..1925bece89 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -33,10 +33,6 @@ pad_cleft = pad_cright = [Core] -# The refresh rate for the GPU -# Defaults to 30 -gpu_refresh_rate = - # The applied frameskip amount. Must be a power of two. # 0 (default): No frameskip, 1: x2 frameskip, 2: x4 frameskip, 3: x8 frameskip, etc. frame_skip = diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 341b48d2a8..6d6656b5a1 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp @@ -2,13 +2,25 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <cstdlib> +#include <string> + +// Let’s use our own GL header, instead of one from GLFW. +#include "video_core/renderer_opengl/generated/gl_3_2_core.h" +#define GLFW_INCLUDE_NONE #include <GLFW/glfw3.h> +#include "common/assert.h" +#include "common/key_map.h" #include "common/logging/log.h" +#include "common/scm_rev.h" +#include "common/string_util.h" #include "video_core/video_core.h" #include "core/settings.h" +#include "core/hle/service/hid/hid.h" #include "citra/emu_window/emu_window_glfw.h" @@ -138,32 +150,9 @@ void EmuWindow_GLFW::DoneCurrent() { } void EmuWindow_GLFW::ReloadSetKeymaps() { - KeyMap::SetKeyMapping({Settings::values.pad_a_key, keyboard_id}, Service::HID::PAD_A); - KeyMap::SetKeyMapping({Settings::values.pad_b_key, keyboard_id}, Service::HID::PAD_B); - KeyMap::SetKeyMapping({Settings::values.pad_select_key, keyboard_id}, Service::HID::PAD_SELECT); - KeyMap::SetKeyMapping({Settings::values.pad_start_key, keyboard_id}, Service::HID::PAD_START); - KeyMap::SetKeyMapping({Settings::values.pad_dright_key, keyboard_id}, Service::HID::PAD_RIGHT); - KeyMap::SetKeyMapping({Settings::values.pad_dleft_key, keyboard_id}, Service::HID::PAD_LEFT); - KeyMap::SetKeyMapping({Settings::values.pad_dup_key, keyboard_id}, Service::HID::PAD_UP); - KeyMap::SetKeyMapping({Settings::values.pad_ddown_key, keyboard_id}, Service::HID::PAD_DOWN); - KeyMap::SetKeyMapping({Settings::values.pad_r_key, keyboard_id}, Service::HID::PAD_R); - KeyMap::SetKeyMapping({Settings::values.pad_l_key, keyboard_id}, Service::HID::PAD_L); - KeyMap::SetKeyMapping({Settings::values.pad_x_key, keyboard_id}, Service::HID::PAD_X); - KeyMap::SetKeyMapping({Settings::values.pad_y_key, keyboard_id}, Service::HID::PAD_Y); - - KeyMap::SetKeyMapping({Settings::values.pad_zl_key, keyboard_id}, Service::HID::PAD_ZL); - KeyMap::SetKeyMapping({Settings::values.pad_zr_key, keyboard_id}, Service::HID::PAD_ZR); - - // KeyMap::SetKeyMapping({Settings::values.pad_touch_key, keyboard_id}, Service::HID::PAD_TOUCH); - - KeyMap::SetKeyMapping({Settings::values.pad_cright_key, keyboard_id}, Service::HID::PAD_C_RIGHT); - KeyMap::SetKeyMapping({Settings::values.pad_cleft_key, keyboard_id}, Service::HID::PAD_C_LEFT); - KeyMap::SetKeyMapping({Settings::values.pad_cup_key, keyboard_id}, Service::HID::PAD_C_UP); - KeyMap::SetKeyMapping({Settings::values.pad_cdown_key, keyboard_id}, Service::HID::PAD_C_DOWN); - KeyMap::SetKeyMapping({Settings::values.pad_sright_key, keyboard_id}, Service::HID::PAD_CIRCLE_RIGHT); - KeyMap::SetKeyMapping({Settings::values.pad_sleft_key, keyboard_id}, Service::HID::PAD_CIRCLE_LEFT); - KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, Service::HID::PAD_CIRCLE_UP); - KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, Service::HID::PAD_CIRCLE_DOWN); + for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { + KeyMap::SetKeyMapping({Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, Service::HID::pad_mapping[i]); + } } void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h index 16c109b792..7ccd5e6aa6 100644 --- a/src/citra/emu_window/emu_window_glfw.h +++ b/src/citra/emu_window/emu_window_glfw.h @@ -4,6 +4,8 @@ #pragma once +#include <utility> + #include "common/emu_window.h" struct GLFWwindow; diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index c2d1ad240b..47aaeca245 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -12,6 +12,7 @@ set(SRCS debugger/graphics_breakpoints.cpp debugger/graphics_cmdlists.cpp debugger/graphics_framebuffer.cpp + debugger/graphics_tracing.cpp debugger/graphics_vertex_shader.cpp debugger/profiler.cpp debugger/ramview.cpp @@ -35,6 +36,7 @@ set(HEADERS debugger/graphics_breakpoints_p.h debugger/graphics_cmdlists.h debugger/graphics_framebuffer.h + debugger/graphics_tracing.h debugger/graphics_vertex_shader.h debugger/profiler.h debugger/ramview.h @@ -73,7 +75,9 @@ target_link_libraries(citra-qt core common video_core qhexedit) target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) target_link_libraries(citra-qt ${PLATFORM_LIBRARIES}) -#install(TARGETS citra-qt RUNTIME DESTINATION ${bindir}) +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") + install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") +endif() if (Qt5_FOUND AND MSVC) set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 3db09c65b9..b12bd858bd 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -11,6 +11,10 @@ #include "bootmanager.h" #include "main.h" +#include "common/string_util.h" +#include "common/scm_rev.h" +#include "common/key_map.h" + #include "core/core.h" #include "core/settings.h" #include "core/system.h" @@ -61,7 +65,7 @@ void EmuThread::run() { was_active = false; } else { std::unique_lock<std::mutex> lock(running_mutex); - running_cv.wait(lock, [this]{ return IsRunning() || stop_run; }); + running_cv.wait(lock, [this]{ return IsRunning() || exec_step || stop_run; }); } } @@ -244,32 +248,9 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) void GRenderWindow::ReloadSetKeymaps() { - KeyMap::SetKeyMapping({Settings::values.pad_a_key, keyboard_id}, Service::HID::PAD_A); - KeyMap::SetKeyMapping({Settings::values.pad_b_key, keyboard_id}, Service::HID::PAD_B); - KeyMap::SetKeyMapping({Settings::values.pad_select_key, keyboard_id}, Service::HID::PAD_SELECT); - KeyMap::SetKeyMapping({Settings::values.pad_start_key, keyboard_id}, Service::HID::PAD_START); - KeyMap::SetKeyMapping({Settings::values.pad_dright_key, keyboard_id}, Service::HID::PAD_RIGHT); - KeyMap::SetKeyMapping({Settings::values.pad_dleft_key, keyboard_id}, Service::HID::PAD_LEFT); - KeyMap::SetKeyMapping({Settings::values.pad_dup_key, keyboard_id}, Service::HID::PAD_UP); - KeyMap::SetKeyMapping({Settings::values.pad_ddown_key, keyboard_id}, Service::HID::PAD_DOWN); - KeyMap::SetKeyMapping({Settings::values.pad_r_key, keyboard_id}, Service::HID::PAD_R); - KeyMap::SetKeyMapping({Settings::values.pad_l_key, keyboard_id}, Service::HID::PAD_L); - KeyMap::SetKeyMapping({Settings::values.pad_x_key, keyboard_id}, Service::HID::PAD_X); - KeyMap::SetKeyMapping({Settings::values.pad_y_key, keyboard_id}, Service::HID::PAD_Y); - - KeyMap::SetKeyMapping({Settings::values.pad_zl_key, keyboard_id}, Service::HID::PAD_ZL); - KeyMap::SetKeyMapping({Settings::values.pad_zr_key, keyboard_id}, Service::HID::PAD_ZR); - - // KeyMap::SetKeyMapping({Settings::values.pad_touch_key, keyboard_id}, Service::HID::PAD_TOUCH); - - KeyMap::SetKeyMapping({Settings::values.pad_cright_key, keyboard_id}, Service::HID::PAD_C_RIGHT); - KeyMap::SetKeyMapping({Settings::values.pad_cleft_key, keyboard_id}, Service::HID::PAD_C_LEFT); - KeyMap::SetKeyMapping({Settings::values.pad_cup_key, keyboard_id}, Service::HID::PAD_C_UP); - KeyMap::SetKeyMapping({Settings::values.pad_cdown_key, keyboard_id}, Service::HID::PAD_C_DOWN); - KeyMap::SetKeyMapping({Settings::values.pad_sright_key, keyboard_id}, Service::HID::PAD_CIRCLE_RIGHT); - KeyMap::SetKeyMapping({Settings::values.pad_sleft_key, keyboard_id}, Service::HID::PAD_CIRCLE_LEFT); - KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, Service::HID::PAD_CIRCLE_UP); - KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, Service::HID::PAD_CIRCLE_DOWN); + for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { + KeyMap::SetKeyMapping({Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, Service::HID::pad_mapping[i]); + } } void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 4751243195..1a1e0e6a50 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -35,7 +35,10 @@ public: * Steps the emulation thread by a single CPU instruction (if the CPU is not already running) * @note This function is thread-safe */ - void ExecStep() { exec_step = true; } + void ExecStep() { + exec_step = true; + running_cv.notify_all(); + } /** * Sets whether the emulation thread is running or not diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 2a9af1f38b..5716634ee3 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -2,11 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <QSettings> #include <QString> #include <QStringList> #include "core/settings.h" -#include "core/core.h" #include "common/file_util.h" #include "config.h" @@ -21,31 +21,21 @@ Config::Config() { Reload(); } +static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults = { + Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, + Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, + Qt::Key_M, Qt::Key_N, Qt::Key_B, + Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, + Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, + Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L +}; + 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(); - Settings::values.pad_x_key = qt_config->value("pad_x", Qt::Key_Z).toInt(); - Settings::values.pad_y_key = qt_config->value("pad_y", Qt::Key_X).toInt(); - Settings::values.pad_l_key = qt_config->value("pad_l", Qt::Key_Q).toInt(); - Settings::values.pad_r_key = qt_config->value("pad_r", Qt::Key_W).toInt(); - Settings::values.pad_zl_key = qt_config->value("pad_zl", Qt::Key_1).toInt(); - Settings::values.pad_zr_key = qt_config->value("pad_zr", Qt::Key_2).toInt(); - Settings::values.pad_start_key = qt_config->value("pad_start", Qt::Key_M).toInt(); - Settings::values.pad_select_key = qt_config->value("pad_select", Qt::Key_N).toInt(); - Settings::values.pad_home_key = qt_config->value("pad_home", Qt::Key_B).toInt(); - Settings::values.pad_dup_key = qt_config->value("pad_dup", Qt::Key_T).toInt(); - Settings::values.pad_ddown_key = qt_config->value("pad_ddown", Qt::Key_G).toInt(); - Settings::values.pad_dleft_key = qt_config->value("pad_dleft", Qt::Key_F).toInt(); - Settings::values.pad_dright_key = qt_config->value("pad_dright", Qt::Key_H).toInt(); - Settings::values.pad_sup_key = qt_config->value("pad_sup", Qt::Key_Up).toInt(); - Settings::values.pad_sdown_key = qt_config->value("pad_sdown", Qt::Key_Down).toInt(); - 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(); - Settings::values.pad_cup_key = qt_config->value("pad_cup", Qt::Key_I).toInt(); - Settings::values.pad_cdown_key = qt_config->value("pad_cdown", Qt::Key_K).toInt(); - Settings::values.pad_cleft_key = qt_config->value("pad_cleft", Qt::Key_J).toInt(); - Settings::values.pad_cright_key = qt_config->value("pad_cright", Qt::Key_L).toInt(); + for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { + Settings::values.input_mappings[Settings::NativeInput::All[i]] = + qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]).toInt(); + } qt_config->endGroup(); qt_config->beginGroup("Core"); @@ -75,29 +65,10 @@ void Config::ReadValues() { 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); - qt_config->setValue("pad_x", Settings::values.pad_x_key); - qt_config->setValue("pad_y", Settings::values.pad_y_key); - qt_config->setValue("pad_l", Settings::values.pad_l_key); - qt_config->setValue("pad_r", Settings::values.pad_r_key); - qt_config->setValue("pad_zl", Settings::values.pad_zl_key); - qt_config->setValue("pad_zr", Settings::values.pad_zr_key); - qt_config->setValue("pad_start", Settings::values.pad_start_key); - qt_config->setValue("pad_select", Settings::values.pad_select_key); - qt_config->setValue("pad_home", Settings::values.pad_home_key); - qt_config->setValue("pad_dup", Settings::values.pad_dup_key); - qt_config->setValue("pad_ddown", Settings::values.pad_ddown_key); - qt_config->setValue("pad_dleft", Settings::values.pad_dleft_key); - qt_config->setValue("pad_dright", Settings::values.pad_dright_key); - qt_config->setValue("pad_sup", Settings::values.pad_sup_key); - qt_config->setValue("pad_sdown", Settings::values.pad_sdown_key); - qt_config->setValue("pad_sleft", Settings::values.pad_sleft_key); - qt_config->setValue("pad_sright", Settings::values.pad_sright_key); - qt_config->setValue("pad_cup", Settings::values.pad_cup_key); - qt_config->setValue("pad_cdown", Settings::values.pad_cdown_key); - qt_config->setValue("pad_cleft", Settings::values.pad_cleft_key); - qt_config->setValue("pad_cright", Settings::values.pad_cright_key); + for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { + qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), + Settings::values.input_mappings[Settings::NativeInput::All[i]]); + } qt_config->endGroup(); qt_config->beginGroup("Core"); diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h index 4485cae735..dd0b2ef0b4 100644 --- a/src/citra_qt/config.h +++ b/src/citra_qt/config.h @@ -4,9 +4,9 @@ #pragma once -#include <QSettings> +#include <string> -#include "common/common_types.h" +class QSettings; class Config { QSettings* qt_config; diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp index e99ec1b308..1e5ef52996 100644 --- a/src/citra_qt/debugger/disassembler.cpp +++ b/src/citra_qt/debugger/disassembler.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <QShortcut> + #include "disassembler.h" #include "../bootmanager.h" @@ -13,7 +15,6 @@ #include "common/break_points.h" #include "common/symbols.h" #include "core/arm/arm_interface.h" -#include "core/arm/skyeye_common/armdefs.h" #include "core/arm/disassembler/arm_disasm.h" @@ -217,7 +218,7 @@ void DisassemblerWidget::OnToggleStartStop() { } void DisassemblerWidget::OnDebugModeEntered() { - ARMword next_instr = Core::g_app_core->GetPC(); + u32 next_instr = Core::g_app_core->GetPC(); if (model->GetBreakPoints().IsAddressBreakPoint(next_instr)) emu_thread->SetRunning(false); diff --git a/src/citra_qt/debugger/graphics_breakpoint_observer.h b/src/citra_qt/debugger/graphics_breakpoint_observer.h index f0d3361f8f..02a0f4f4f1 100644 --- a/src/citra_qt/debugger/graphics_breakpoint_observer.h +++ b/src/citra_qt/debugger/graphics_breakpoint_observer.h @@ -13,7 +13,7 @@ * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while * the widget usually wants to perform reactions in the GUI thread. */ -class BreakPointObserverDock : public QDockWidget, private Pica::DebugContext::BreakPointObserver { +class BreakPointObserverDock : public QDockWidget, protected Pica::DebugContext::BreakPointObserver { Q_OBJECT public: diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp index 1da64f6166..5202c168ce 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.cpp +++ b/src/citra_qt/debugger/graphics_breakpoints.cpp @@ -4,7 +4,7 @@ #include <QMetaType> #include <QPushButton> -#include <QTreeWidget> +#include <QTreeView> #include <QVBoxLayout> #include <QLabel> @@ -23,7 +23,7 @@ BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_conte int BreakPointModel::columnCount(const QModelIndex& parent) const { - return 2; + return 1; } int BreakPointModel::rowCount(const QModelIndex& parent) const @@ -38,29 +38,29 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const switch (role) { case Qt::DisplayRole: { - switch (index.column()) { - case 0: - { + if (index.column() == 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::PicaCommandLoaded, tr("Pica command loaded") }, + { Pica::DebugContext::Event::PicaCommandProcessed, 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") } + { Pica::DebugContext::Event::VertexLoaded, tr("Vertex loaded") }, + { Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") }, + { Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") }, + { Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") } }; DEBUG_ASSERT(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::CheckStateRole: + { + if (index.column() == 0) + return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked; break; } @@ -84,37 +84,34 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const return QVariant(); } -QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const +Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const { - switch(role) { - case Qt::DisplayRole: - { - if (section == 0) { - return tr("Event"); - } else if (section == 1) { - return tr("Status"); - } - - break; - } - } + if (!index.isValid()) + return 0; - return QVariant(); + Qt::ItemFlags flags = Qt::ItemIsEnabled; + if (index.column() == 0) + flags |= Qt::ItemIsUserCheckable; + return flags; } + 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: + case Qt::CheckStateRole: { + if (index.column() != 0) + return false; + auto context = context_weak.lock(); if (!context) return false; - context->breakpoints[event].enabled = value.toBool(); - QModelIndex changed_index = createIndex(index.row(), 1); + context->breakpoints[event].enabled = value == Qt::Checked; + QModelIndex changed_index = createIndex(index.row(), 0); emit dataChanged(changed_index, changed_index); return true; } @@ -133,7 +130,7 @@ void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) active_breakpoint = context->active_breakpoint; at_breakpoint = context->at_breakpoint; emit dataChanged(createIndex(static_cast<int>(event), 0), - createIndex(static_cast<int>(event), 1)); + createIndex(static_cast<int>(event), 0)); } void BreakPointModel::OnResumed() @@ -144,7 +141,7 @@ void BreakPointModel::OnResumed() at_breakpoint = context->at_breakpoint; emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), - createIndex(static_cast<int>(active_breakpoint), 1)); + createIndex(static_cast<int>(active_breakpoint), 0)); active_breakpoint = context->active_breakpoint; } @@ -162,13 +159,15 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug breakpoint_model = new BreakPointModel(debug_context, this); breakpoint_list = new QTreeView; + breakpoint_list->setRootIsDecorated(false); + breakpoint_list->setHeaderHidden(true); 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(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), + this, SLOT(OnItemDoubleClicked(const QModelIndex&))); + connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), @@ -184,11 +183,6 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug 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; { @@ -198,7 +192,6 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug main_layout->addLayout(sub_layout); } main_layout->addWidget(breakpoint_list); - main_layout->addWidget(toggle_breakpoint_button); main_widget->setLayout(main_layout); setWidget(main_widget); @@ -234,32 +227,15 @@ void GraphicsBreakPointsWidget::OnResumeRequested() context->Resume(); } -void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index) +void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) { - if (!index.isValid()) { - toggle_breakpoint_button->setEnabled(false); + if (!index.isValid()) 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")); - } + QModelIndex check_index = breakpoint_list->model()->index(index.row(), 0); + QVariant enabled = breakpoint_list->model()->data(check_index, Qt::CheckStateRole); + QVariant new_state = Qt::Unchecked; + if (enabled == Qt::Unchecked) + new_state = Qt::Checked; + breakpoint_list->model()->setData(check_index, new_state, Qt::CheckStateRole); } diff --git a/src/citra_qt/debugger/graphics_breakpoints.h b/src/citra_qt/debugger/graphics_breakpoints.h index 5b9ba324e3..d900729da6 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.h +++ b/src/citra_qt/debugger/graphics_breakpoints.h @@ -31,10 +31,9 @@ public: public slots: void OnBreakPointHit(Pica::DebugContext::Event event, void* data); + void OnItemDoubleClicked(const QModelIndex&); void OnResumeRequested(); void OnResumed(); - void OnBreakpointSelectionChanged(const QModelIndex&); - void OnToggleBreakpointEnabled(); signals: void Resumed(); @@ -42,11 +41,8 @@ signals: 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; diff --git a/src/citra_qt/debugger/graphics_breakpoints_p.h b/src/citra_qt/debugger/graphics_breakpoints_p.h index 34e72e8591..00d8d51013 100644 --- a/src/citra_qt/debugger/graphics_breakpoints_p.h +++ b/src/citra_qt/debugger/graphics_breakpoints_p.h @@ -23,7 +23,7 @@ public: 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; + Qt::ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index cabf5fe07d..7ac3ea542d 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp @@ -2,12 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <QApplication> +#include <QClipboard> #include <QLabel> #include <QListView> #include <QMainWindow> #include <QPushButton> #include <QVBoxLayout> #include <QTreeView> +#include <QHeaderView> #include <QSpinBox> #include <QComboBox> @@ -74,7 +77,7 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo format_choice->addItem(tr("I8")); format_choice->addItem(tr("A8")); format_choice->addItem(tr("IA4")); - format_choice->addItem(tr("UNK10")); + format_choice->addItem(tr("I4")); format_choice->addItem(tr("A4")); format_choice->addItem(tr("ETC1")); format_choice->addItem(tr("ETC1A4")); @@ -168,11 +171,11 @@ GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(p } int GPUCommandListModel::rowCount(const QModelIndex& parent) const { - return pica_trace.writes.size(); + return static_cast<int>(pica_trace.writes.size()); } int GPUCommandListModel::columnCount(const QModelIndex& parent) const { - return 2; + return 3; } QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const { @@ -185,14 +188,13 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const { if (role == Qt::DisplayRole) { QString content; - if (index.column() == 0) { - QString content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str()); - content.append(" "); - return content; - } else if (index.column() == 1) { - QString content = QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0')); - content.append(QString("%1 ").arg(val, 8, 16, QLatin1Char('0'))); - return content; + switch ( index.column() ) { + case 0: + return QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str()); + case 1: + return QString("%1").arg(cmd.cmd_id, 3, 16, QLatin1Char('0')); + case 2: + return QString("%1").arg(val, 8, 16, QLatin1Char('0')); } } else if (role == CommandIdRole) { return QVariant::fromValue<int>(cmd.cmd_id.Value()); @@ -205,10 +207,13 @@ QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientatio switch(role) { case Qt::DisplayRole: { - if (section == 0) { + switch (section) { + case 0: return tr("Command Name"); - } else if (section == 1) { - return tr("Data"); + case 1: + return tr("Register"); + case 2: + return tr("New Value"); } break; @@ -297,6 +302,13 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi list_widget->setModel(model); list_widget->setFont(QFont("monospace")); list_widget->setRootIsDecorated(false); + list_widget->setUniformRowHeights(true); + +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + list_widget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); +#else + list_widget->header()->setResizeMode(QHeaderView::ResizeToContents); +#endif connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)), this, SLOT(SetCommandInfo(const QModelIndex&))); @@ -304,16 +316,24 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi this, SLOT(OnCommandDoubleClicked(const QModelIndex&))); toggle_tracing = new QPushButton(tr("Start Tracing")); + QPushButton* copy_all = new QPushButton(tr("Copy All")); connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); + connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard())); + command_info_widget = new QWidget; QVBoxLayout* main_layout = new QVBoxLayout; main_layout->addWidget(list_widget); - main_layout->addWidget(toggle_tracing); + { + QHBoxLayout* sub_layout = new QHBoxLayout; + sub_layout->addWidget(toggle_tracing); + sub_layout->addWidget(copy_all); + main_layout->addLayout(sub_layout); + } main_layout->addWidget(command_info_widget); main_widget->setLayout(main_layout); @@ -330,3 +350,21 @@ void GPUCommandListWidget::OnToggleTracing() { toggle_tracing->setText(tr("Start Tracing")); } } + +void GPUCommandListWidget::CopyAllToClipboard() { + QClipboard* clipboard = QApplication::clipboard(); + QString text; + + QAbstractItemModel* model = static_cast<QAbstractListModel*>(list_widget->model()); + + for (int row = 0; row < model->rowCount({}); ++row) { + for (int col = 0; col < model->columnCount({}); ++col) { + QModelIndex index = model->index(row, col); + text += model->data(index).value<QString>(); + text += '\t'; + } + text += '\n'; + } + + clipboard->setText(text); +} diff --git a/src/citra_qt/debugger/graphics_cmdlists.h b/src/citra_qt/debugger/graphics_cmdlists.h index a465d044c3..4859b6ec82 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.h +++ b/src/citra_qt/debugger/graphics_cmdlists.h @@ -49,6 +49,8 @@ public slots: void SetCommandInfo(const QModelIndex&); + void CopyAllToClipboard(); + signals: void TracingFinished(const Pica::DebugUtils::PicaTrace&); diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp index 6bbe7572ca..39eefbf75c 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.cpp +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp @@ -55,7 +55,9 @@ GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::Debug framebuffer_format_control->addItem(tr("RGBA4")); framebuffer_format_control->addItem(tr("D16")); framebuffer_format_control->addItem(tr("D24")); - framebuffer_format_control->addItem(tr("D24S8")); + framebuffer_format_control->addItem(tr("D24X8")); + framebuffer_format_control->addItem(tr("X24S8")); + framebuffer_format_control->addItem(tr("(unknown)")); // TODO: This QLabel should shrink the image to the available space rather than just expanding... framebuffer_picture_label = new QLabel; @@ -184,8 +186,32 @@ void GraphicsFramebufferWidget::OnUpdate() framebuffer_address = framebuffer.GetColorBufferPhysicalAddress(); framebuffer_width = framebuffer.GetWidth(); framebuffer_height = framebuffer.GetHeight(); - // TODO: It's unknown how this format is actually specified - framebuffer_format = Format::RGBA8; + + switch (framebuffer.color_format) { + case Pica::Regs::ColorFormat::RGBA8: + framebuffer_format = Format::RGBA8; + break; + + case Pica::Regs::ColorFormat::RGB8: + framebuffer_format = Format::RGB8; + break; + + case Pica::Regs::ColorFormat::RGB5A1: + framebuffer_format = Format::RGB5A1; + break; + + case Pica::Regs::ColorFormat::RGB565: + framebuffer_format = Format::RGB565; + break; + + case Pica::Regs::ColorFormat::RGBA4: + framebuffer_format = Format::RGBA4; + break; + + default: + framebuffer_format = Format::Unknown; + break; + } break; } @@ -197,7 +223,24 @@ void GraphicsFramebufferWidget::OnUpdate() framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress(); framebuffer_width = framebuffer.GetWidth(); framebuffer_height = framebuffer.GetHeight(); - framebuffer_format = Format::D16; + + switch (framebuffer.depth_format) { + case Pica::Regs::DepthFormat::D16: + framebuffer_format = Format::D16; + break; + + case Pica::Regs::DepthFormat::D24: + framebuffer_format = Format::D24; + break; + + case Pica::Regs::DepthFormat::D24S8: + framebuffer_format = Format::D24X8; + break; + + default: + framebuffer_format = Format::Unknown; + break; + } break; } @@ -258,7 +301,7 @@ void GraphicsFramebufferWidget::OnUpdate() color.b() = (data >> 16) & 0xFF; break; } - case Format::D24S8: + case Format::D24X8: { Math::Vec2<u32> data = Color::DecodeD24S8(pixel); color.r() = data.x & 0xFF; @@ -266,6 +309,12 @@ void GraphicsFramebufferWidget::OnUpdate() color.b() = (data.x >> 16) & 0xFF; break; } + case Format::X24S8: + { + Math::Vec2<u32> data = Color::DecodeD24S8(pixel); + color.r() = color.g() = color.b() = data.y; + break; + } default: qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format); break; @@ -286,7 +335,8 @@ void GraphicsFramebufferWidget::OnUpdate() u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) { switch (format) { case Format::RGBA8: - case Format::D24S8: + case Format::D24X8: + case Format::X24S8: return 4; case Format::RGB8: case Format::D24: diff --git a/src/citra_qt/debugger/graphics_framebuffer.h b/src/citra_qt/debugger/graphics_framebuffer.h index 4cb396ffe3..e9eae679f4 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.h +++ b/src/citra_qt/debugger/graphics_framebuffer.h @@ -35,7 +35,9 @@ class GraphicsFramebufferWidget : public BreakPointObserverDock { RGBA4 = 4, D16 = 5, D24 = 6, - D24S8 = 7 + D24X8 = 7, + X24S8 = 8, + Unknown = 9 }; static u32 BytesPerPixel(Format format); diff --git a/src/citra_qt/debugger/graphics_tracing.cpp b/src/citra_qt/debugger/graphics_tracing.cpp new file mode 100644 index 0000000000..3f20f149dc --- /dev/null +++ b/src/citra_qt/debugger/graphics_tracing.cpp @@ -0,0 +1,170 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include <QBoxLayout> +#include <QComboBox> +#include <QFileDialog> +#include <QLabel> +#include <QMessageBox> +#include <QPushButton> +#include <QSpinBox> + +#include <boost/range/algorithm/copy.hpp> + +#include "core/hw/gpu.h" +#include "core/hw/lcd.h" + +#include "video_core/pica.h" + +#include "nihstro/float24.h" + +#include "graphics_tracing.h" + +GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, + QWidget* parent) + : BreakPointObserverDock(debug_context, tr("CiTrace Recorder"), parent) { + + setObjectName("CiTracing"); + + QPushButton* start_recording = new QPushButton(tr("Start Recording")); + QPushButton* stop_recording = new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save")); + QPushButton* abort_recording = new QPushButton(tr("Abort Recording")); + + connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording, SLOT(setVisible(bool))); + connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording, SLOT(setVisible(bool))); + connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording, SLOT(setVisible(bool))); + connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording())); + connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording())); + connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording())); + + stop_recording->setVisible(false); + abort_recording->setVisible(false); + + auto main_widget = new QWidget; + auto main_layout = new QVBoxLayout; + { + auto sub_layout = new QHBoxLayout; + sub_layout->addWidget(start_recording); + sub_layout->addWidget(stop_recording); + sub_layout->addWidget(abort_recording); + main_layout->addLayout(sub_layout); + } + main_widget->setLayout(main_layout); + setWidget(main_widget); +} + +void GraphicsTracingWidget::StartRecording() { + auto context = context_weak.lock(); + if (!context) + return; + + auto shader_binary = Pica::g_state.vs.program_code; + auto swizzle_data = Pica::g_state.vs.swizzle_data; + + // Encode floating point numbers to 24-bit values + // TODO: Drop this explicit conversion once we store float24 values bit-correctly internally. + std::array<uint32_t, 4 * 16> default_attributes; + for (unsigned i = 0; i < 16; ++i) { + for (unsigned comp = 0; comp < 3; ++comp) { + default_attributes[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.default_attributes[i][comp].ToFloat32()); + } + } + + std::array<uint32_t, 4 * 96> vs_float_uniforms; + for (unsigned i = 0; i < 96; ++i) + for (unsigned comp = 0; comp < 3; ++comp) + vs_float_uniforms[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32()); + + CiTrace::Recorder::InitialState state; + std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32), std::back_inserter(state.gpu_registers)); + std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32), std::back_inserter(state.lcd_registers)); + std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32), std::back_inserter(state.pica_registers)); + boost::copy(default_attributes, std::back_inserter(state.default_attributes)); + boost::copy(shader_binary, std::back_inserter(state.vs_program_binary)); + boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data)); + boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms)); + //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary)); + //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data)); + //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms)); + + auto recorder = new CiTrace::Recorder(state); + context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder); + + emit SetStartTracingButtonEnabled(false); + emit SetStopTracingButtonEnabled(true); + emit SetAbortTracingButtonEnabled(true); +} + +void GraphicsTracingWidget::StopRecording() { + auto context = context_weak.lock(); + if (!context) + return; + + QString filename = QFileDialog::getSaveFileName(this, tr("Save CiTrace"), "citrace.ctf", + tr("CiTrace File (*.ctf)")); + + if (filename.isEmpty()) { + // If the user canceled the dialog, keep recording + return; + } + + context->recorder->Finish(filename.toStdString()); + context->recorder = nullptr; + + emit SetStopTracingButtonEnabled(false); + emit SetAbortTracingButtonEnabled(false); + emit SetStartTracingButtonEnabled(true); +} + +void GraphicsTracingWidget::AbortRecording() { + auto context = context_weak.lock(); + if (!context) + return; + + context->recorder = nullptr; + + emit SetStopTracingButtonEnabled(false); + emit SetAbortTracingButtonEnabled(false); + emit SetStartTracingButtonEnabled(true); +} + +void GraphicsTracingWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { + widget()->setEnabled(true); +} + +void GraphicsTracingWidget::OnResumed() { + widget()->setEnabled(false); +} + +void GraphicsTracingWidget::OnEmulationStarting(EmuThread* emu_thread) { + // Disable tracing starting/stopping until a GPU breakpoint is reached + widget()->setEnabled(false); +} + +void GraphicsTracingWidget::OnEmulationStopping() { + // TODO: Is it safe to access the context here? + + auto context = context_weak.lock(); + if (!context) + return; + + + if (context->recorder) { + auto reply = QMessageBox::question(this, tr("CiTracing still active"), + tr("A CiTrace is still being recorded. Do you want to save it? If not, all recorded data will be discarded."), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (reply == QMessageBox::Yes) { + StopRecording(); + } else { + AbortRecording(); + } + } + + // If the widget was disabled before, enable it now to allow starting + // tracing before starting the next emulation session + widget()->setEnabled(true); +} diff --git a/src/citra_qt/debugger/graphics_tracing.h b/src/citra_qt/debugger/graphics_tracing.h new file mode 100644 index 0000000000..2a0e4819bb --- /dev/null +++ b/src/citra_qt/debugger/graphics_tracing.h @@ -0,0 +1,32 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "graphics_breakpoint_observer.h" + +class EmuThread; + +class GraphicsTracingWidget : public BreakPointObserverDock { + Q_OBJECT + +public: + GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); + +private slots: + void StartRecording(); + void StopRecording(); + void AbortRecording(); + + void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; + void OnResumed() override; + + void OnEmulationStarting(EmuThread* emu_thread); + void OnEmulationStopping(); + +signals: + void SetStartTracingButtonEnabled(bool enable); + void SetStopTracingButtonEnabled(bool enable); + void SetAbortTracingButtonEnabled(bool enable); +}; diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp index 14d3f8f398..f42a2f4ce6 100644 --- a/src/citra_qt/debugger/graphics_vertex_shader.cpp +++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp @@ -34,7 +34,7 @@ int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { } int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { - return info.code.size(); + return static_cast<int>(info.code.size()); } QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { @@ -259,7 +259,7 @@ void GraphicsVertexShaderModel::OnUpdate() for (auto pattern : Pica::g_state.vs.swizzle_data) info.swizzle_info.push_back({pattern}); - info.labels.insert({ Pica::g_state.regs.vs_main_offset, "main" }); + info.labels.insert({ Pica::g_state.regs.vs.main_offset, "main" }); endResetModel(); } diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp index 2ac1748b76..89b28c2f4c 100644 --- a/src/citra_qt/debugger/profiler.cpp +++ b/src/citra_qt/debugger/profiler.cpp @@ -74,7 +74,7 @@ int ProfilerModel::rowCount(const QModelIndex& parent) const if (parent.isValid()) { return 0; } else { - return results.time_per_category.size() + 2; + return static_cast<int>(results.time_per_category.size() + 2); } } diff --git a/src/citra_qt/hotkeys.cpp b/src/citra_qt/hotkeys.cpp index 322c25c9ee..5ed6cf0b14 100644 --- a/src/citra_qt/hotkeys.cpp +++ b/src/citra_qt/hotkeys.cpp @@ -2,10 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <map> + #include <QKeySequence> #include <QSettings> +#include <QShortcut> + #include "hotkeys.h" -#include <map> struct Hotkey { diff --git a/src/citra_qt/hotkeys.h b/src/citra_qt/hotkeys.h index 75c7cc6251..2317f8188b 100644 --- a/src/citra_qt/hotkeys.h +++ b/src/citra_qt/hotkeys.h @@ -2,12 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <QShortcut> -#include <QDialog> #include "ui_hotkeys.h" +class QDialog; class QKeySequence; class QSettings; +class QShortcut; /** * Register a hotkey. diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 8041816a0b..34831f2ecd 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -10,18 +10,16 @@ #include "qhexedit.h" #include "main.h" +#include "common/string_util.h" #include "common/logging/text_formatter.h" #include "common/logging/log.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/make_unique.h" #include "common/platform.h" +#include "common/scm_rev.h" #include "common/scope_exit.h" -#if EMU_PLATFORM == PLATFORM_LINUX -#include <unistd.h> -#endif - #include "bootmanager.h" #include "hotkeys.h" @@ -34,6 +32,7 @@ #include "debugger/graphics_breakpoints.h" #include "debugger/graphics_cmdlists.h" #include "debugger/graphics_framebuffer.h" +#include "debugger/graphics_tracing.h" #include "debugger/graphics_vertex_shader.h" #include "debugger/profiler.h" @@ -96,6 +95,10 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); graphicsVertexShaderWidget->hide(); + auto graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this); + addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget); + graphicsTracingWidget->hide(); + QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); debug_menu->addAction(profilerWidget->toggleViewAction()); debug_menu->addAction(disasmWidget->toggleViewAction()); @@ -106,6 +109,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); + debug_menu->addAction(graphicsTracingWidget->toggleViewAction()); // Set default UI state // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half @@ -150,6 +154,9 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); + connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, SLOT(OnEmulationStarting(EmuThread*))); + connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping())); + // Setup hotkeys RegisterHotkey("Main Window", "Load File", QKeySequence::Open); @@ -256,6 +263,7 @@ void GMainWindow::ShutdownGame() { // Update the GUI ui.action_Start->setEnabled(false); + ui.action_Start->setText(tr("Start")); ui.action_Pause->setEnabled(false); ui.action_Stop->setEnabled(false); render_window->hide(); @@ -284,6 +292,8 @@ void GMainWindow::OnStartGame() emu_thread->SetRunning(true); ui.action_Start->setEnabled(false); + ui.action_Start->setText(tr("Continue")); + ui.action_Pause->setEnabled(true); ui.action_Stop->setEnabled(true); } diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e78f4f1441..4c086cd2fb 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -31,7 +31,6 @@ set(HEADERS cpu_detect.h debug_interface.h emu_window.h - fifo_queue.h file_util.h key_map.h linear_disk_cache.h @@ -53,7 +52,6 @@ set(HEADERS synchronized_wrapper.h thread.h thread_queue_list.h - thunk.h timer.h vector_math.h ) diff --git a/src/common/assert.h b/src/common/assert.h index 7b7d8bf28c..6849778b79 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -4,7 +4,6 @@ #pragma once -#include <cstdio> #include <cstdlib> #include "common/common_funcs.h" diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 1f3ecf8441..f64ebdaf69 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -32,6 +32,7 @@ #pragma once +#include <cstddef> #include <limits> #include <type_traits> @@ -160,7 +161,7 @@ public: if (std::numeric_limits<T>::is_signed) { std::size_t shift = 8 * sizeof(T)-bits; - return (T)(((storage & GetMask()) << (shift - position)) >> shift); + return (T)((storage << (shift - position)) >> shift); } else { @@ -188,7 +189,7 @@ private: __forceinline StorageType GetMask() const { - return ((~(StorageTypeU)0) >> (8 * sizeof(T)-bits)) << position; + return (((StorageTypeU)~0) >> (8 * sizeof(T)-bits)) << position; } StorageType storage; diff --git a/src/common/chunk_file.h b/src/common/chunk_file.h index dcd80525ef..8be0b11094 100644 --- a/src/common/chunk_file.h +++ b/src/common/chunk_file.h @@ -26,16 +26,18 @@ // - Zero backwards/forwards compatibility // - Serialization code for anything complex has to be manually written. -#include <map> -#include <vector> +#include <cstring> #include <deque> -#include <string> #include <list> +#include <map> #include <set> +#include <string> #include <type_traits> +#include <utility> +#include <vector> +#include "common/assert.h" #include "common/common_types.h" -#include "common/file_util.h" #include "common/logging/log.h" template <class T> diff --git a/src/common/color.h b/src/common/color.h index 422fdc8af8..9dafdca0ca 100644 --- a/src/common/color.h +++ b/src/common/color.h @@ -208,7 +208,32 @@ inline void EncodeD24(u32 value, u8* bytes) { * @param bytes Pointer where to store the encoded value */ inline void EncodeD24S8(u32 depth, u8 stencil, u8* bytes) { - *reinterpret_cast<u32_le*>(bytes) = (stencil << 24) | depth; + bytes[0] = depth & 0xFF; + bytes[1] = (depth >> 8) & 0xFF; + bytes[2] = (depth >> 16) & 0xFF; + bytes[3] = stencil; +} + +/** + * Encode a 24 bit depth value as D24X8 format (32 bits per pixel with 8 bits unused) + * @param depth 24 bit source depth value to encode + * @param bytes Pointer where to store the encoded value + * @note unused bits will not be modified + */ +inline void EncodeD24X8(u32 depth, u8* bytes) { + bytes[0] = depth & 0xFF; + bytes[1] = (depth >> 8) & 0xFF; + bytes[2] = (depth >> 16) & 0xFF; +} + +/** + * Encode an 8 bit stencil value as X24S8 format (32 bits per pixel with 24 bits unused) + * @param stencil 8 bit source stencil value to encode + * @param bytes Pointer where to store the encoded value + * @note unused bits will not be modified + */ +inline void EncodeX24S8(u8 stencil, u8* bytes) { + bytes[3] = stencil; } } // namespace diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 91b74c6bcd..59bd16dbfe 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -5,15 +5,6 @@ #pragma once #include "common_types.h" -#include <cstdlib> - - -#define b2(x) ( (x) | ( (x) >> 1) ) -#define b4(x) ( b2(x) | ( b2(x) >> 2) ) -#define b8(x) ( b4(x) | ( b4(x) >> 4) ) -#define b16(x) ( b8(x) | ( b8(x) >> 8) ) -#define b32(x) (b16(x) | (b16(x) >>16) ) -#define ROUND_UP_POW2(x) (b32(x - 1) + 1) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) @@ -43,8 +34,6 @@ #ifndef _MSC_VER -#include <errno.h> - #if defined(__x86_64__) || defined(_M_X64) #define Crash() __asm__ __volatile__("int $3") #elif defined(_M_ARM) @@ -80,8 +69,10 @@ inline u64 _rotr64(u64 x, unsigned int shift){ } #else // _MSC_VER - // Function Cross-Compatibility - #define snprintf _snprintf + #if (_MSC_VER < 1900) + // Function Cross-Compatibility + #define snprintf _snprintf + #endif // Locale Cross-Compatibility #define locale_t _locale_t diff --git a/src/common/common_types.h b/src/common/common_types.h index f6de0adfc1..fa3e0b8d64 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h @@ -24,9 +24,7 @@ #pragma once -#include <cmath> #include <cstdint> -#include <cstdlib> #ifdef _MSC_VER #ifndef __func__ @@ -52,32 +50,6 @@ typedef double f64; ///< 64-bit floating point typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space. typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space. -/// Union for fast 16-bit type casting -union t16 { - u8 _u8[2]; ///< 8-bit unsigned char(s) - u16 _u16; ///< 16-bit unsigned shorts(s) -}; - -/// Union for fast 32-bit type casting -union t32 { - f32 _f32; ///< 32-bit floating point(s) - u32 _u32; ///< 32-bit unsigned int(s) - s32 _s32; ///< 32-bit signed int(s) - u16 _u16[2]; ///< 16-bit unsigned shorts(s) - u8 _u8[4]; ///< 8-bit unsigned char(s) -}; - -/// Union for fast 64-bit type casting -union t64 { - f64 _f64; ///< 64-bit floating point - u64 _u64; ///< 64-bit unsigned long - f32 _f32[2]; ///< 32-bit floating point(s) - u32 _u32[2]; ///< 32-bit unsigned int(s) - s32 _s32[2]; ///< 32-bit signed int(s) - u16 _u16[4]; ///< 16-bit unsigned shorts(s) - u8 _u8[8]; ///< 8-bit unsigned char(s) -}; - // An inheritable class to disallow the copy constructor and operator= functions class NonCopyable { protected: diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp index 43facb85c0..b69b05cb9a 100644 --- a/src/common/emu_window.cpp +++ b/src/common/emu_window.cpp @@ -2,6 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <cmath> + +#include "common/assert.h" +#include "common/key_map.h" + #include "emu_window.h" #include "video_core/video_core.h" diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 8eca6b5d5d..a0ae4c9fad 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -4,11 +4,17 @@ #pragma once +#include <tuple> +#include <utility> + #include "common/common_types.h" -#include "common/key_map.h" #include "common/math_util.h" -#include "common/scm_rev.h" -#include "common/string_util.h" + +#include "core/hle/service/hid/hid.h" + +namespace KeyMap { +struct HostDeviceKey; +} /** * Abstraction class used to provide an interface between emulation code and the frontend diff --git a/src/common/fifo_queue.h b/src/common/fifo_queue.h deleted file mode 100644 index b426e65963..0000000000 --- a/src/common/fifo_queue.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -// a simple lockless thread-safe, -// single reader, single writer queue - -#include "common/atomic.h" - -namespace Common -{ - -template <typename T> -class FifoQueue -{ -public: - FifoQueue() : m_size(0) - { - m_write_ptr = m_read_ptr = new ElementPtr(); - } - - ~FifoQueue() - { - // this will empty out the whole queue - delete m_read_ptr; - } - - u32 Size() const - { - return m_size; - } - - bool Empty() const - { - //return (m_read_ptr == m_write_ptr); - return (0 == m_size); - } - - T& Front() const - { - return *m_read_ptr->current; - } - - template <typename Arg> - void Push(Arg&& t) - { - // create the element, add it to the queue - m_write_ptr->current = new T(std::forward<Arg>(t)); - // set the next pointer to a new element ptr - // then advance the write pointer - m_write_ptr = m_write_ptr->next = new ElementPtr(); - Common::AtomicIncrement(m_size); - } - - void Pop() - { - Common::AtomicDecrement(m_size); - ElementPtr *const tmpptr = m_read_ptr; - // advance the read pointer - m_read_ptr = m_read_ptr->next; - // set the next element to NULL to stop the recursive deletion - tmpptr->next = nullptr; - delete tmpptr; // this also deletes the element - } - - bool Pop(T& t) - { - if (Empty()) - return false; - - t = std::move(Front()); - Pop(); - - return true; - } - - // not thread-safe - void Clear() - { - m_size = 0; - delete m_read_ptr; - m_write_ptr = m_read_ptr = new ElementPtr(); - } - -private: - // stores a pointer to element - // and a pointer to the next ElementPtr - class ElementPtr - { - public: - ElementPtr() : current(nullptr), next(nullptr) {} - - ~ElementPtr() - { - if (current) - { - delete current; - // recusion ftw - if (next) - delete next; - } - } - - T *volatile current; - ElementPtr *volatile next; - }; - - ElementPtr *volatile m_write_ptr; - ElementPtr *volatile m_read_ptr; - volatile u32 m_size; -}; - -} diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 24648ea33b..836b58d527 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -17,6 +17,8 @@ #include <direct.h> // getcwd #include <tchar.h> + #include "common/string_util.h" + // 64 bit offsets for windows #define fseeko _fseeki64 #define ftello _ftelli64 @@ -25,8 +27,13 @@ #define fstat64 _fstat64 #define fileno _fileno #else - #include <sys/param.h> - #include <sys/types.h> + #ifdef __APPLE__ + #include <sys/param.h> + #endif + #include <cctype> + #include <cerrno> + #include <cstdlib> + #include <cstring> #include <dirent.h> #include <pwd.h> #include <unistd.h> diff --git a/src/common/file_util.h b/src/common/file_util.h index b65829291e..d0dccdf69a 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -6,13 +6,12 @@ #include <array> #include <fstream> +#include <cstddef> #include <cstdio> -#include <cstring> #include <string> #include <vector> #include "common/common_types.h" -#include "common/string_util.h" // User directory indices for GetUserPath enum { @@ -117,9 +116,6 @@ bool SetCurrentDir(const std::string &directory); // directory. To be used in "multi-user" mode (that is, installed). const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath=""); -// probably doesn't belong here -//std::string GetThemeDir(const std::string& theme_name); - // Returns the path to where the sys file are std::string GetSysDirectory(); @@ -182,6 +178,10 @@ public: template <typename T> size_t WriteArray(const T* data, size_t length) { + static_assert(std::is_standard_layout<T>::value, "Given array does not consist of standard layout objects"); + // TODO: gcc 4.8 does not support is_trivially_copyable, but we really should check for it here. + //static_assert(std::is_trivially_copyable<T>::value, "Given array does not consist of trivially copyable objects"); + if (!IsOpen()) { m_good = false; return -1; @@ -204,6 +204,12 @@ public: return WriteArray(reinterpret_cast<const char*>(data), length); } + template<typename T> + size_t WriteObject(const T& object) { + static_assert(!std::is_pointer<T>::value, "Given object is a pointer"); + return WriteArray(&object, 1); + } + bool IsOpen() { return nullptr != m_file; } // m_good is set to false when a read, write or other function fails diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h index 0b71ea3b21..a2b4eca430 100644 --- a/src/common/logging/filter.h +++ b/src/common/logging/filter.h @@ -5,6 +5,7 @@ #pragma once #include <array> +#include <cstddef> #include <string> #include "common/logging/log.h" diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 5b3a731e9b..e16dde7fce 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -4,10 +4,6 @@ #pragma once -#include <cassert> -#include <chrono> -#include <string> - #include "common/common_types.h" namespace Log { diff --git a/src/common/make_unique.h b/src/common/make_unique.h index 2a7b764124..f6e7f017cd 100644 --- a/src/common/make_unique.h +++ b/src/common/make_unique.h @@ -4,6 +4,7 @@ #pragma once +#include <algorithm> #include <memory> namespace Common { diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index 20b791a101..2b3ace5288 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp @@ -3,14 +3,17 @@ // Refer to the license.txt file included. -#include "common/common_funcs.h" #include "common/logging/log.h" #include "common/memory_util.h" -#include "common/string_util.h" #ifdef _WIN32 -#include <windows.h> -#include <psapi.h> + #include <windows.h> + #include <psapi.h> + #include "common/common_funcs.h" + #include "common/string_util.h" +#else + #include <cstdlib> + #include <sys/mman.h> #endif #if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) diff --git a/src/common/memory_util.h b/src/common/memory_util.h index 9fdbf1f121..9bf37c44fd 100644 --- a/src/common/memory_util.h +++ b/src/common/memory_util.h @@ -4,9 +4,7 @@ #pragma once -#ifndef _WIN32 -#include <sys/mman.h> -#endif +#include <cstddef> #include <string> void* AllocateExecutableMemory(size_t size, bool low = true); diff --git a/src/common/misc.cpp b/src/common/misc.cpp index 53cacf37cf..d2a049b636 100644 --- a/src/common/misc.cpp +++ b/src/common/misc.cpp @@ -2,12 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common_funcs.h" +#include <cstddef> #ifdef _WIN32 #include <windows.h> #else -#include <string.h> +#include <cerrno> +#include <cstring> #endif // Neither Android nor OS X support TLS diff --git a/src/common/platform.h b/src/common/platform.h index df780ac6f0..0a912dda31 100644 --- a/src/common/platform.h +++ b/src/common/platform.h @@ -24,66 +24,11 @@ #pragma once -#include "common/common_types.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Platform definitions - -/// Enumeration for defining the supported platforms -#define PLATFORM_NULL 0 -#define PLATFORM_WINDOWS 1 -#define PLATFORM_MACOSX 2 -#define PLATFORM_LINUX 3 -#define PLATFORM_ANDROID 4 - //////////////////////////////////////////////////////////////////////////////////////////////////// // Platform detection -#ifndef EMU_PLATFORM - -#if defined( __WIN32__ ) || defined( _WIN32 ) -#define EMU_PLATFORM PLATFORM_WINDOWS - -#elif defined( __APPLE__ ) || defined( __APPLE_CC__ ) -#define EMU_PLATFORM PLATFORM_MACOSX - -#elif defined(__linux__) -#define EMU_PLATFORM PLATFORM_LINUX - -#else // Assume linux otherwise -#define EMU_PLATFORM PLATFORM_LINUX - -#endif - -#endif - #if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) #define EMU_ARCH_BITS 64 #elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM) #define EMU_ARCH_BITS 32 #endif - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Feature detection - -#if defined _M_GENERIC -# define _M_SSE 0x0 -#elif defined __GNUC__ -# if defined __SSE4_2__ -# define _M_SSE 0x402 -# elif defined __SSE4_1__ -# define _M_SSE 0x401 -# elif defined __SSSE3__ -# define _M_SSE 0x301 -# elif defined __SSE3__ -# define _M_SSE 0x300 -# endif -#elif (_MSC_VER >= 1500) || __INTEL_COMPILER // Visual Studio 2008 -# define _M_SSE 0x402 -#endif - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Compiler-Specific Definitions - -#define GCC_VERSION_AVAILABLE(major, minor) (defined(__GNUC__) && (__GNUC__ > (major) || \ - (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))) diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp index cf6b6b258d..7792edd2fc 100644 --- a/src/common/profiler.cpp +++ b/src/common/profiler.cpp @@ -2,13 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <cstddef> +#include <vector> + +#include "common/assert.h" #include "common/profiler.h" #include "common/profiler_reporting.h" -#include "common/assert.h" +#include "common/synchronized_wrapper.h" #if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013. -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> // For QueryPerformanceCounter/Frequency + #define WIN32_LEAN_AND_MEAN + #include <Windows.h> // For QueryPerformanceCounter/Frequency #endif namespace Common { diff --git a/src/common/profiler_reporting.h b/src/common/profiler_reporting.h index 3abb733155..df98e05b78 100644 --- a/src/common/profiler_reporting.h +++ b/src/common/profiler_reporting.h @@ -4,10 +4,7 @@ #pragma once -#include <array> -#include <chrono> -#include <mutex> -#include <utility> +#include <cstddef> #include <vector> #include "common/profiler.h" diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 7dc0ba7baf..b2f7f7b1d0 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -2,9 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <boost/range/algorithm.hpp> +#include <cctype> +#include <cerrno> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <boost/range/algorithm/transform.hpp> -#include "common/common_funcs.h" #include "common/common_paths.h" #include "common/logging/log.h" #include "common/string_util.h" @@ -12,6 +16,7 @@ #ifdef _MSC_VER #include <Windows.h> #include <codecvt> + #include "common/common_funcs.h" #else #include <iconv.h> #endif @@ -308,7 +313,7 @@ static std::string UTF16ToUTF8(const std::wstring& input) std::string output; output.resize(size); - if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), &output[0], output.size(), nullptr, nullptr)) + if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr)) output.clear(); return output; diff --git a/src/common/string_util.h b/src/common/string_util.h index fdc4104998..c5c474c6fe 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -5,9 +5,10 @@ #pragma once #include <cstdarg> +#include <cstddef> #include <iomanip> -#include <string> #include <sstream> +#include <string> #include <vector> #include "common/common_types.h" diff --git a/src/common/swap.h b/src/common/swap.h index 588cebc708..b92e5bfa49 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -17,12 +17,16 @@ #pragma once -#if defined(__linux__) -#include <byteswap.h> +#if defined(_MSC_VER) + #include <cstdlib> +#elif defined(__linux__) + #include <byteswap.h> #elif defined(__FreeBSD__) -#include <sys/endian.h> + #include <sys/endian.h> #endif +#include "common/common_types.h" + // GCC 4.6+ #if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) diff --git a/src/common/synchronized_wrapper.h b/src/common/synchronized_wrapper.h index 946252b8c1..ae5e8b1ed5 100644 --- a/src/common/synchronized_wrapper.h +++ b/src/common/synchronized_wrapper.h @@ -4,6 +4,7 @@ #pragma once +#include <algorithm> #include <mutex> namespace Common { diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 8bf0058574..7bbf080bc0 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -5,11 +5,20 @@ #include "common/thread.h" #ifdef __APPLE__ -#include <mach/mach.h> -#elif defined(BSD4_4) || defined(__OpenBSD__) -#include <pthread_np.h> + #include <mach/mach.h> #elif defined(_WIN32) -#include <Windows.h> + #include <Windows.h> +#else + #if defined(BSD4_4) || defined(__OpenBSD__) + #include <pthread_np.h> + #else + #include <pthread.h> + #endif + #include <sched.h> +#endif + +#ifndef _WIN32 + #include <unistd.h> #endif namespace Common diff --git a/src/common/thread.h b/src/common/thread.h index 7bc419497b..8255ee6d31 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -4,24 +4,12 @@ #pragma once -#include "common/common_types.h" -#include <cstdio> -#include <cstring> +#include <cstddef> #include <thread> #include <condition_variable> #include <mutex> -// This may not be defined outside _WIN32 -#ifndef _WIN32 -#ifndef INFINITE -#define INFINITE 0xffffffff -#endif - -//for gettimeofday and struct time(spec|val) -#include <time.h> -#include <sys/time.h> -#include <unistd.h> -#endif +#include "common/common_types.h" // Support for C++11's thread_local keyword was surprisingly spotty in compilers until very // recently. Fortunately, thread local variables have been well supported for compilers for a while, diff --git a/src/common/thunk.h b/src/common/thunk.h deleted file mode 100644 index 5334800560..0000000000 --- a/src/common/thunk.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <map> - -#include "common/common_types.h" - -// This simple class creates a wrapper around a C/C++ function that saves all fp state -// before entering it, and restores it upon exit. This is required to be able to selectively -// call functions from generated code, without inflicting the performance hit and increase -// of complexity that it means to protect the generated code from this problem. - -// This process is called thunking. - -// There will only ever be one level of thunking on the stack, plus, -// we don't want to pollute the stack, so we store away regs somewhere global. -// NOT THREAD SAFE. This may only be used from the CPU thread. -// Any other thread using this stuff will be FATAL. - -class ThunkManager : public Gen::XCodeBlock -{ - std::map<void *, const u8 *> thunks; - - const u8 *save_regs; - const u8 *load_regs; - -public: - ThunkManager() { - Init(); - } - ~ThunkManager() { - Shutdown(); - } - void *ProtectFunction(void *function, int num_params); -private: - void Init(); - void Shutdown(); - void Reset(); -}; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4fcda48748..6cc60fd582 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -4,10 +4,9 @@ set(SRCS arm/dyncom/arm_dyncom.cpp arm/dyncom/arm_dyncom_dec.cpp arm/dyncom/arm_dyncom_interpreter.cpp - arm/dyncom/arm_dyncom_run.cpp arm/dyncom/arm_dyncom_thumb.cpp - arm/interpreter/arminit.cpp - arm/interpreter/armsupp.cpp + arm/skyeye_common/armstate.cpp + arm/skyeye_common/armsupp.cpp arm/skyeye_common/vfp/vfp.cpp arm/skyeye_common/vfp/vfpdouble.cpp arm/skyeye_common/vfp/vfpinstr.cpp @@ -25,6 +24,8 @@ set(SRCS file_sys/ivfc_archive.cpp hle/config_mem.cpp hle/hle.cpp + hle/applets/applet.cpp + hle/applets/swkbd.cpp hle/kernel/address_arbiter.cpp hle/kernel/event.cpp hle/kernel/kernel.cpp @@ -113,6 +114,7 @@ set(SRCS loader/elf.cpp loader/loader.cpp loader/ncch.cpp + tracer/recorder.cpp mem_map.cpp memory.cpp settings.cpp @@ -129,8 +131,8 @@ set(HEADERS arm/dyncom/arm_dyncom_run.h arm/dyncom/arm_dyncom_thumb.h arm/skyeye_common/arm_regformat.h - arm/skyeye_common/armdefs.h - arm/skyeye_common/armmmu.h + arm/skyeye_common/armstate.h + arm/skyeye_common/armsupp.h arm/skyeye_common/vfp/asm_vfp.h arm/skyeye_common/vfp/vfp.h arm/skyeye_common/vfp/vfp_helper.h @@ -150,6 +152,8 @@ set(HEADERS hle/config_mem.h hle/function_wrappers.h hle/hle.h + hle/applets/applet.h + hle/applets/swkbd.h hle/kernel/address_arbiter.h hle/kernel/event.h hle/kernel/kernel.h @@ -239,6 +243,8 @@ set(HEADERS loader/elf.h loader/loader.h loader/ncch.h + tracer/recorder.h + tracer/citrace.h mem_map.h memory.h memory_setup.h diff --git a/src/core/arm/disassembler/load_symbol_map.cpp b/src/core/arm/disassembler/load_symbol_map.cpp index 13d26d1709..eb20bf6f76 100644 --- a/src/core/arm/disassembler/load_symbol_map.cpp +++ b/src/core/arm/disassembler/load_symbol_map.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <sstream> #include <string> #include <vector> diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index 529c4ac708..c665f706f3 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -6,7 +6,8 @@ #include "common/make_unique.h" -#include "core/arm/skyeye_common/armdefs.h" +#include "core/arm/skyeye_common/armstate.h" +#include "core/arm/skyeye_common/armsupp.h" #include "core/arm/skyeye_common/vfp/vfp.h" #include "core/arm/dyncom/arm_dyncom.h" @@ -17,26 +18,7 @@ #include "core/core_timing.h" ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { - state = Common::make_unique<ARMul_State>(); - - ARMul_NewState(state.get()); - ARMul_SelectProcessor(state.get(), ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); - - state->abort_model = ABORT_BASE_RESTORED; - - state->bigendSig = LOW; - state->lateabtSig = LOW; - state->NirqSig = HIGH; - - // Reset the core to initial state - ARMul_Reset(state.get()); - state->Emulate = RUN; - - // Switch to the desired privilege mode. - switch_mode(state.get(), initial_mode); - - state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack - state->Reg[15] = 0x00000000; + state = Common::make_unique<ARMul_State>(initial_mode); } ARM_DynCom::~ARM_DynCom() { @@ -100,8 +82,8 @@ void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 e } void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { - memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); - memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); + memcpy(ctx.cpu_registers, state->Reg.data(), sizeof(ctx.cpu_registers)); + memcpy(ctx.fpu_registers, state->ExtReg.data(), sizeof(ctx.fpu_registers)); ctx.sp = state->Reg[13]; ctx.lr = state->Reg[14]; @@ -113,8 +95,8 @@ void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { } void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) { - memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); - memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); + memcpy(state->Reg.data(), ctx.cpu_registers, sizeof(ctx.cpu_registers)); + memcpy(state->ExtReg.data(), ctx.fpu_registers, sizeof(ctx.fpu_registers)); state->Reg[13] = ctx.sp; state->Reg[14] = ctx.lr; diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 2488c879c0..87ab6908ab 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -9,7 +9,12 @@ #include "common/common_types.h" #include "core/arm/arm_interface.h" -#include "core/arm/skyeye_common/armdefs.h" +#include "core/arm/skyeye_common/arm_regformat.h" +#include "core/arm/skyeye_common/armstate.h" + +namespace Core { +struct ThreadContext; +} class ARM_DynCom final : virtual public ARM_Interface { public: diff --git a/src/core/arm/dyncom/arm_dyncom_dec.cpp b/src/core/arm/dyncom/arm_dyncom_dec.cpp index 697be9556a..ee4288314f 100644 --- a/src/core/arm/dyncom/arm_dyncom_dec.cpp +++ b/src/core/arm/dyncom/arm_dyncom_dec.cpp @@ -2,10 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/arm/skyeye_common/armdefs.h" #include "core/arm/dyncom/arm_dyncom_dec.h" +#include "core/arm/skyeye_common/armsupp.h" -const ISEITEM arm_instruction[] = { +const InstructionSetEncodingItem arm_instruction[] = { { "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 }}, @@ -207,7 +207,7 @@ const ISEITEM arm_instruction[] = { { "bbl", 1, 0, { 25, 27, 0x00000005 }}, }; -const ISEITEM arm_exclusion_code[] = { +const InstructionSetEncodingItem arm_exclusion_code[] = { { "vmla", 0, ARMVFP2, { 0 }}, { "vmls", 0, ARMVFP2, { 0 }}, { "vnmla", 0, ARMVFP2, { 0 }}, @@ -414,14 +414,13 @@ const ISEITEM arm_exclusion_code[] = { { "invalid", 0, INVALID, { 0 }} }; -int decode_arm_instr(uint32_t instr, int32_t *idx) { +ARMDecodeStatus DecodeARMInstruction(u32 instr, s32* idx) { int n = 0; int base = 0; - int ret = DECODE_FAILURE; - int i = 0; - int instr_slots = sizeof(arm_instruction) / sizeof(ISEITEM); + int instr_slots = sizeof(arm_instruction) / sizeof(InstructionSetEncodingItem); + ARMDecodeStatus ret = ARMDecodeStatus::FAILURE; - for (i = 0; i < instr_slots; i++) { + for (int i = 0; i < instr_slots; i++) { n = arm_instruction[i].attribute_value; base = 0; @@ -438,11 +437,11 @@ int decode_arm_instr(uint32_t instr, int32_t *idx) { n--; } - // All conditions is satisfied. + // All conditions are satisfied. if (n == 0) - ret = DECODE_SUCCESS; + ret = ARMDecodeStatus::SUCCESS; - if (ret == DECODE_SUCCESS) { + if (ret == ARMDecodeStatus::SUCCESS) { n = arm_exclusion_code[i].attribute_value; if (n != 0) { base = 0; @@ -454,13 +453,13 @@ int decode_arm_instr(uint32_t instr, int32_t *idx) { n--; } - // All conditions is satisfied. + // All conditions are satisfied. if (n == 0) - ret = DECODE_FAILURE; + ret = ARMDecodeStatus::FAILURE; } } - if (ret == DECODE_SUCCESS) { + if (ret == ARMDecodeStatus::SUCCESS) { *idx = i; return ret; } diff --git a/src/core/arm/dyncom/arm_dyncom_dec.h b/src/core/arm/dyncom/arm_dyncom_dec.h index 4b5f5ad7e2..d7170e0fcc 100644 --- a/src/core/arm/dyncom/arm_dyncom_dec.h +++ b/src/core/arm/dyncom/arm_dyncom_dec.h @@ -4,22 +4,22 @@ #pragma once -int decode_arm_instr(uint32_t instr, int32_t *idx); +#include "common/common_types.h" -enum DECODE_STATUS { - DECODE_SUCCESS, - DECODE_FAILURE +enum class ARMDecodeStatus { + SUCCESS, + FAILURE }; -struct instruction_set_encoding_item { +ARMDecodeStatus DecodeARMInstruction(u32 instr, s32* idx); + +struct InstructionSetEncodingItem { const char *name; int attribute_value; int version; u32 content[21]; }; -typedef struct instruction_set_encoding_item ISEITEM; - // ARM versions enum { INVALID = 0, @@ -36,4 +36,4 @@ enum { ARMV6K, }; -extern const ISEITEM arm_instruction[]; +extern const InstructionSetEncodingItem arm_instruction[]; diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index b00eb49a91..d022546edb 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -17,8 +17,8 @@ #include "core/arm/dyncom/arm_dyncom_interpreter.h" #include "core/arm/dyncom/arm_dyncom_thumb.h" #include "core/arm/dyncom/arm_dyncom_run.h" -#include "core/arm/skyeye_common/armdefs.h" -#include "core/arm/skyeye_common/armmmu.h" +#include "core/arm/skyeye_common/armstate.h" +#include "core/arm/skyeye_common/armsupp.h" #include "core/arm/skyeye_common/vfp/vfp.h" Common::Profiling::TimingCategory profile_execute("DynCom::Execute"); @@ -47,28 +47,6 @@ enum { typedef unsigned int (*shtop_fp_t)(ARMul_State* cpu, unsigned int sht_oper); -// Defines a reservation granule of 2 words, which protects the first 2 words starting at the tag. -// This is the smallest granule allowed by the v7 spec, and is coincidentally just large enough to -// support LDR/STREXD. -static const ARMword RESERVATION_GRANULE_MASK = 0xFFFFFFF8; - -// Exclusive memory access -static int exclusive_detect(ARMul_State* state, ARMword addr) { - if(state->exclusive_tag == (addr & RESERVATION_GRANULE_MASK)) - return 0; - else - return -1; -} - -static void add_exclusive_addr(ARMul_State* state, ARMword addr){ - state->exclusive_tag = addr & RESERVATION_GRANULE_MASK; - return; -} - -static void remove_exclusive(ARMul_State* state, ARMword addr){ - state->exclusive_tag = 0xFFFFFFFF; -} - static int CondPassed(ARMul_State* cpu, unsigned int cond) { const u32 NFLAG = cpu->NFlag; const u32 ZFLAG = cpu->ZFlag; @@ -3490,21 +3468,15 @@ enum { FETCH_FAILURE }; -static tdstate decode_thumb_instr(u32 inst, u32 addr, u32* arm_inst, u32* inst_size, ARM_INST_PTR* ptr_inst_base) { +static ThumbDecodeStatus DecodeThumbInstruction(u32 inst, u32 addr, u32* arm_inst, u32* inst_size, ARM_INST_PTR* ptr_inst_base) { // Check if in Thumb mode - tdstate ret = thumb_translate (addr, inst, arm_inst, inst_size); - if(ret == t_branch){ - // TODO: FIXME, endian should be judged - u32 tinstr; - if((addr & 0x3) != 0) - tinstr = inst >> 16; - else - tinstr = inst & 0xFFFF; - + ThumbDecodeStatus ret = TranslateThumbInstruction (addr, inst, arm_inst, inst_size); + if (ret == ThumbDecodeStatus::BRANCH) { int inst_index; int table_length = sizeof(arm_instruction_trans) / sizeof(transop_fp_t); + u32 tinstr = GetThumbInstruction(inst, addr); - switch((tinstr & 0xF800) >> 11){ + switch ((tinstr & 0xF800) >> 11) { case 26: case 27: if (((tinstr & 0x0F00) != 0x0E00) && ((tinstr & 0x0F00) != 0x0F00)){ @@ -3537,7 +3509,7 @@ static tdstate decode_thumb_instr(u32 inst, u32 addr, u32* arm_inst, u32* inst_s *ptr_inst_base = arm_instruction_trans[inst_index](tinstr, inst_index); break; default: - ret = t_undefined; + ret = ThumbDecodeStatus::UNDEFINED; break; } } @@ -3549,10 +3521,6 @@ enum { FETCH_EXCEPTION }; -typedef struct instruction_set_encoding_item ISEITEM; - -extern const ISEITEM arm_instruction[]; - static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { Common::Profiling::ScopeTimer timer_decode(profile_decode); @@ -3574,20 +3542,19 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { inst = Memory::Read32(phys_addr & 0xFFFFFFFC); size++; - // If we are in thumb instruction, we will translate one thumb to one corresponding arm instruction + // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction if (cpu->TFlag) { uint32_t arm_inst; - tdstate state = decode_thumb_instr(inst, phys_addr, &arm_inst, &inst_size, &inst_base); + ThumbDecodeStatus state = DecodeThumbInstruction(inst, phys_addr, &arm_inst, &inst_size, &inst_base); - // We have translated the branch instruction of thumb in thumb decoder - if(state == t_branch){ + // We have translated the Thumb branch instruction in the Thumb decoder + if (state == ThumbDecodeStatus::BRANCH) { goto translated; } inst = arm_inst; } - ret = decode_arm_instr(inst, &idx); - if (ret == DECODE_FAILURE) { + if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) { std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst); LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst); LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]); @@ -3964,7 +3931,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Spsr_copy & 0x1f); + cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -3978,7 +3945,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(adc_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -3990,7 +3957,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { u32 rn_val = RN; if (inst_cream->Rn == 15) - rn_val += 2 * GET_INST_SIZE(cpu); + rn_val += 2 * cpu->GetInstructionSize(); bool carry; bool overflow; @@ -3999,7 +3966,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Cpsr & 0x1f); + cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -4013,7 +3980,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(add_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4028,7 +3995,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Cpsr & 0x1f); + cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -4041,7 +4008,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(and_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4057,7 +4024,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { INC_PC(sizeof(bbl_inst)); goto DISPATCH; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(bbl_inst)); goto DISPATCH; } @@ -4067,14 +4034,14 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { u32 lop = RN; if (inst_cream->Rn == 15) { - lop += 2 * GET_INST_SIZE(cpu); + lop += 2 * cpu->GetInstructionSize(); } u32 rop = SHIFTER_OPERAND; RD = lop & (~rop); if ((inst_cream->S) && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Spsr_copy & 0x1f); + cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -4087,7 +4054,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(bic_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4098,7 +4065,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { bkpt_inst* const inst_cream = (bkpt_inst*)inst_base->component; LOG_DEBUG(Core_ARM11, "Breakpoint instruction hit. Immediate: 0x%08X", inst_cream->imm); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(bkpt_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4109,13 +4076,13 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { unsigned int inst = inst_cream->inst; if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) { - cpu->Reg[14] = (cpu->Reg[15] + GET_INST_SIZE(cpu)); + cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize()); if(cpu->TFlag) cpu->Reg[14] |= 0x1; cpu->Reg[15] = cpu->Reg[inst_cream->val.Rm] & 0xfffffffe; cpu->TFlag = cpu->Reg[inst_cream->val.Rm] & 0x1; } else { - cpu->Reg[14] = (cpu->Reg[15] + GET_INST_SIZE(cpu)); + cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize()); cpu->TFlag = 0x1; int signed_int = inst_cream->val.signed_immed_24; signed_int = (signed_int & 0x800000) ? (0x3F000000 | signed_int) : signed_int; @@ -4125,7 +4092,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { INC_PC(sizeof(blx_inst)); goto DISPATCH; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(blx_inst)); goto DISPATCH; } @@ -4144,16 +4111,18 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { bx_inst* const inst_cream = (bx_inst*)inst_base->component; + u32 address = RM; + if (inst_cream->Rm == 15) - LOG_WARNING(Core_ARM11, "BX at pc %x: use of Rm = R15 is discouraged", cpu->Reg[15]); + address += 2 * cpu->GetInstructionSize(); - cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1; - cpu->Reg[15] = cpu->Reg[inst_cream->Rm] & 0xfffffffe; + cpu->TFlag = address & 1; + cpu->Reg[15] = address & 0xfffffffe; INC_PC(sizeof(bx_inst)); goto DISPATCH; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(bx_inst)); goto DISPATCH; } @@ -4165,7 +4134,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->NumInstrsToExecute = 0; return num_instrs; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(cdp_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4173,10 +4142,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { CLREX_INST: { - remove_exclusive(cpu, 0); - cpu->exclusive_state = 0; - - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->UnsetExclusiveMemoryAddress(); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(clrex_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4187,7 +4154,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { clz_inst* inst_cream = (clz_inst*)inst_base->component; RD = clz(RM); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(clz_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4206,7 +4173,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->CFlag = carry; cpu->VFlag = overflow; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(cmn_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4218,7 +4185,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { u32 rn_val = RN; if (inst_cream->Rn == 15) - rn_val += 2 * GET_INST_SIZE(cpu); + rn_val += 2 * cpu->GetInstructionSize(); bool carry; bool overflow; @@ -4229,7 +4196,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->CFlag = carry; cpu->VFlag = overflow; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(cmp_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4239,7 +4206,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cps_inst *inst_cream = (cps_inst *)inst_base->component; uint32_t aif_val = 0; uint32_t aif_mask = 0; - if (InAPrivilegedMode(cpu)) { + if (cpu->InAPrivilegedMode()) { if (inst_cream->imod1) { if (inst_cream->A) { aif_val |= (inst_cream->imod0 << 8); @@ -4258,10 +4225,10 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } if (inst_cream->mmod) { cpu->Cpsr = (cpu->Cpsr & 0xffffffe0) | inst_cream->mode; - switch_mode(cpu, inst_cream->mode); + cpu->ChangePrivilegeMode(inst_cream->mode); } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(cps_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4277,7 +4244,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mov_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4289,14 +4256,14 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { u32 lop = RN; if (inst_cream->Rn == 15) { - lop += 2 * GET_INST_SIZE(cpu); + lop += 2 * cpu->GetInstructionSize(); } u32 rop = SHIFTER_OPERAND; RD = lop ^ rop; if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Spsr_copy & 0x1f); + cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -4309,7 +4276,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(eor_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4318,7 +4285,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { { // Instruction not implemented //LOG_CRITICAL(Core_ARM11, "unimplemented instruction"); - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldc_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4333,30 +4300,30 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (BIT(inst, 22) && !BIT(inst, 15)) { for (int i = 0; i < 13; i++) { if(BIT(inst, i)) { - cpu->Reg[i] = ReadMemory32(cpu, addr); + cpu->Reg[i] = cpu->ReadMemory32(addr); addr += 4; } } if (BIT(inst, 13)) { if (cpu->Mode == USER32MODE) - cpu->Reg[13] = ReadMemory32(cpu, addr); + cpu->Reg[13] = cpu->ReadMemory32(addr); else - cpu->Reg_usr[0] = ReadMemory32(cpu, addr); + cpu->Reg_usr[0] = cpu->ReadMemory32(addr); addr += 4; } if (BIT(inst, 14)) { if (cpu->Mode == USER32MODE) - cpu->Reg[14] = ReadMemory32(cpu, addr); + cpu->Reg[14] = cpu->ReadMemory32(addr); else - cpu->Reg_usr[1] = ReadMemory32(cpu, addr); + cpu->Reg_usr[1] = cpu->ReadMemory32(addr); addr += 4; } } else if (!BIT(inst, 22)) { for(int i = 0; i < 16; i++ ){ if(BIT(inst, i)){ - unsigned int ret = ReadMemory32(cpu, addr); + unsigned int ret = cpu->ReadMemory32(addr); // For armv5t, should enter thumb when bits[0] is non-zero. if(i == 15){ @@ -4371,18 +4338,18 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } else if (BIT(inst, 22) && BIT(inst, 15)) { for(int i = 0; i < 15; i++ ){ if(BIT(inst, i)){ - cpu->Reg[i] = ReadMemory32(cpu, addr); + cpu->Reg[i] = cpu->ReadMemory32(addr); addr += 4; } } if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Cpsr & 0x1f); + cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F); LOAD_NZCVT; } - cpu->Reg[15] = ReadMemory32(cpu, addr); + cpu->Reg[15] = cpu->ReadMemory32(addr); } if (BIT(inst, 15)) { @@ -4390,7 +4357,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4408,7 +4375,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } RD = operand2; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(sxth_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4418,7 +4385,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { ldst_inst *inst_cream = (ldst_inst *)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); - unsigned int value = ReadMemory32(cpu, addr); + unsigned int value = cpu->ReadMemory32(addr); cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; if (BITS(inst_cream->inst, 12, 15) == 15) { @@ -4429,7 +4396,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4440,7 +4407,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { ldst_inst *inst_cream = (ldst_inst *)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); - unsigned int value = ReadMemory32(cpu, addr); + unsigned int value = cpu->ReadMemory32(addr); cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; if (BITS(inst_cream->inst, 12, 15) == 15) { @@ -4451,7 +4418,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4462,7 +4429,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { uxth_inst* inst_cream = (uxth_inst*)inst_base->component; RD = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(uxth_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4475,7 +4442,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = RN + operand2; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(uxtah_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4493,7 +4460,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4511,7 +4478,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4525,8 +4492,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { // The 3DS doesn't have LPAE (Large Physical Access Extension), so it // wouldn't do this as a single read. - cpu->Reg[BITS(inst_cream->inst, 12, 15) + 0] = ReadMemory32(cpu, addr); - cpu->Reg[BITS(inst_cream->inst, 12, 15) + 1] = ReadMemory32(cpu, addr + 4); + cpu->Reg[BITS(inst_cream->inst, 12, 15) + 0] = cpu->ReadMemory32(addr); + cpu->Reg[BITS(inst_cream->inst, 12, 15) + 1] = cpu->ReadMemory32(addr + 4); // No dispatch since this operation should not modify R15 } @@ -4542,16 +4509,15 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int read_addr = RN; - add_exclusive_addr(cpu, read_addr); - cpu->exclusive_state = 1; + cpu->SetExclusiveMemoryAddress(read_addr); - RD = ReadMemory32(cpu, read_addr); + RD = cpu->ReadMemory32(read_addr); if (inst_cream->Rd == 15) { INC_PC(sizeof(generic_arm_inst)); goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4562,8 +4528,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int read_addr = RN; - add_exclusive_addr(cpu, read_addr); - cpu->exclusive_state = 1; + cpu->SetExclusiveMemoryAddress(read_addr); RD = Memory::Read8(read_addr); if (inst_cream->Rd == 15) { @@ -4571,7 +4536,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4582,16 +4547,15 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int read_addr = RN; - add_exclusive_addr(cpu, read_addr); - cpu->exclusive_state = 1; + cpu->SetExclusiveMemoryAddress(read_addr); - RD = ReadMemory16(cpu, read_addr); + RD = cpu->ReadMemory16(read_addr); if (inst_cream->Rd == 15) { INC_PC(sizeof(generic_arm_inst)); goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4602,18 +4566,17 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int read_addr = RN; - add_exclusive_addr(cpu, read_addr); - cpu->exclusive_state = 1; + cpu->SetExclusiveMemoryAddress(read_addr); - RD = ReadMemory32(cpu, read_addr); - RD2 = ReadMemory32(cpu, read_addr + 4); + RD = cpu->ReadMemory32(read_addr); + RD2 = cpu->ReadMemory32(read_addr + 4); if (inst_cream->Rd == 15) { INC_PC(sizeof(generic_arm_inst)); goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4624,13 +4587,13 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); - cpu->Reg[BITS(inst_cream->inst, 12, 15)] = ReadMemory16(cpu, addr); + cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory16(addr); if (BITS(inst_cream->inst, 12, 15) == 15) { INC_PC(sizeof(ldst_inst)); goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4650,7 +4613,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4661,7 +4624,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); - unsigned int value = ReadMemory16(cpu, addr); + unsigned int value = cpu->ReadMemory16(addr); if (BIT(value, 15)) { value |= 0xffff0000; } @@ -4671,7 +4634,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4682,7 +4645,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); - unsigned int value = ReadMemory32(cpu, addr); + unsigned int value = cpu->ReadMemory32(addr); cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; if (BITS(inst_cream->inst, 12, 15) == 15) { @@ -4690,7 +4653,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4705,10 +4668,10 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { DEBUG_MSG; } else { if (inst_cream->cp_num == 15) - WriteCP15Register(cpu, RD, CRn, OPCODE_1, CRm, OPCODE_2); + cpu->WriteCP15Register(RD, CRn, OPCODE_1, CRm, OPCODE_2); } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mcr_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4725,7 +4688,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { inst_cream->cp_num, inst_cream->crm, inst_cream->opcode_1, inst_cream->rt, inst_cream->rt2); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mcrr_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4750,7 +4713,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mla_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4764,7 +4727,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Spsr_copy & 0x1f); + cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -4777,7 +4740,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mov_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4798,10 +4761,10 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto END; } else { if (inst_cream->cp_num == 15) - RD = ReadCP15Register(cpu, CRn, OPCODE_1, CRm, OPCODE_2); + RD = cpu->ReadCP15Register(CRn, OPCODE_1, CRm, OPCODE_2); } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mrc_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4818,7 +4781,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { inst_cream->cp_num, inst_cream->crm, inst_cream->opcode_1, inst_cream->rt, inst_cream->rt2); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mcrr_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4836,7 +4799,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = cpu->Cpsr; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mrs_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4859,7 +4822,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | (BIT(inst, 18) ? 0xff0000 : 0) | (BIT(inst, 19) ? 0xff000000 : 0); uint32_t mask = 0; if (!inst_cream->R) { - if (InAPrivilegedMode(cpu)) { + if (cpu->InAPrivilegedMode()) { if ((operand & StateMask) != 0) { /// UNPREDICTABLE DEBUG_MSG; @@ -4871,7 +4834,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { SAVE_NZCVT; cpu->Cpsr = (cpu->Cpsr & ~mask) | (operand & mask); - switch_mode(cpu, cpu->Cpsr & 0x1f); + cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F); LOAD_NZCVT; } else { if (CurrentModeHasSPSR) { @@ -4880,7 +4843,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(msr_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4902,7 +4865,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mul_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4917,7 +4880,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Spsr_copy & 0x1f); + cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -4930,7 +4893,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mvn_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4947,7 +4910,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Spsr_copy & 0x1f); + cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -4960,7 +4923,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(orr_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4968,7 +4931,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { NOP_INST: { - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC_STUB; FETCH_INST; GOTO_NEXT_INST; @@ -4980,7 +4943,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { pkh_inst *inst_cream = (pkh_inst *)inst_base->component; RD = (RN & 0xFFFF) | ((RM << inst_cream->imm) & 0xFFFF0000); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(pkh_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -4993,7 +4956,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { int shift_imm = inst_cream->imm ? inst_cream->imm : 31; RD = ((static_cast<s32>(RM) >> shift_imm) & 0xFFFF) | (RN & 0xFFFF0000); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(pkh_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5003,7 +4966,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { { // Not implemented. PLD is a hint instruction, so it's optional. - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(pld_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5076,7 +5039,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = result; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5138,7 +5101,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5171,7 +5134,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(rev_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5185,8 +5148,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { u32 address = 0; inst_cream->get_addr(cpu, inst_cream->inst, address); - cpu->Cpsr = ReadMemory32(cpu, address); - cpu->Reg[15] = ReadMemory32(cpu, address + 4); + cpu->Cpsr = cpu->ReadMemory32(address); + cpu->Reg[15] = cpu->ReadMemory32(address + 4); INC_PC(sizeof(ldst_inst)); goto DISPATCH; @@ -5199,7 +5162,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { u32 rn_val = RN; if (inst_cream->Rn == 15) - rn_val += 2 * GET_INST_SIZE(cpu); + rn_val += 2 * cpu->GetInstructionSize(); bool carry; bool overflow; @@ -5208,7 +5171,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Spsr_copy & 0x1f); + cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -5222,7 +5185,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(rsb_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5239,7 +5202,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Spsr_copy & 0x1f); + cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -5253,7 +5216,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(rsc_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5361,7 +5324,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5379,7 +5342,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Spsr_copy & 0x1f); + cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -5393,7 +5356,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(sbc_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5432,7 +5395,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = result; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5451,7 +5414,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { LOG_WARNING(Core_ARM11, "SETEND %s executed", big_endian ? "BE" : "LE"); - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(setend_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5464,7 +5427,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { LOG_TRACE(Core_ARM11, "SEV executed."); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC_STUB; FETCH_INST; GOTO_NEXT_INST; @@ -5536,7 +5499,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5561,7 +5524,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (AddOverflow(operand1 * operand2, RN, RD)) cpu->Cpsr |= (1 << 27); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(smla_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5617,7 +5580,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(smlad_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5646,7 +5609,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->ZFlag = (RDHI == 0 && RDLO == 0); } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(umlal_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5676,7 +5639,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RDHI = ((dest >> 32) & 0xFFFFFFFF); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(smlalxy_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5695,13 +5658,13 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { const s16 operand2 = (high) ? ((rm_val >> 16) & 0xFFFF) : (rm_val & 0xFFFF); const s64 result = (s64)(s32)rn_val * (s64)(s32)operand2 + ((s64)(s32)ra_val << 16); - RD = (result & (0xFFFFFFFFFFFFFFFFLL >> 15)) >> 16; + RD = BITS(result, 16, 47); if ((result >> 16) != (s32)RD) cpu->Cpsr |= (1 << 27); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(smlad_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5739,7 +5702,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RDHI = ((result >> 32) & 0xFFFFFFFF); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(smlald_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5775,7 +5738,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = ((result >> 32) & 0xFFFFFFFF); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(smlad_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5797,7 +5760,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { operand2 = (BIT(RS, 31)) ? (BITS(RS, 16, 31) | 0xffff0000) : BITS(RS, 16, 31); RD = operand1 * operand2; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(smul_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5823,7 +5786,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->ZFlag = (RDHI == 0 && RDLO == 0); } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(umull_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5839,7 +5802,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { s64 result = (s64)rm * (s64)(s32)RN; RD = BITS(result, 16, 47); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(smlad_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5853,10 +5816,10 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { u32 address = 0; inst_cream->get_addr(cpu, inst_cream->inst, address); - WriteMemory32(cpu, address + 0, cpu->Reg[14]); - WriteMemory32(cpu, address + 4, cpu->Spsr_copy); + cpu->WriteMemory32(address + 0, cpu->Reg[14]); + cpu->WriteMemory32(address + 4, cpu->Spsr_copy); - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5889,7 +5852,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = rn_val; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ssat_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5911,7 +5874,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->Cpsr |= (1 << 27); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ssat_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5921,7 +5884,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { { // Instruction not implemented //LOG_CRITICAL(Core_ARM11, "unimplemented instruction"); - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(stc_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5939,36 +5902,36 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (BIT(inst_cream->inst, 22) == 1) { for (int i = 0; i < 13; i++) { if (BIT(inst_cream->inst, i)) { - WriteMemory32(cpu, addr, cpu->Reg[i]); + cpu->WriteMemory32(addr, cpu->Reg[i]); addr += 4; } } if (BIT(inst_cream->inst, 13)) { if (cpu->Mode == USER32MODE) - WriteMemory32(cpu, addr, cpu->Reg[13]); + cpu->WriteMemory32(addr, cpu->Reg[13]); else - WriteMemory32(cpu, addr, cpu->Reg_usr[0]); + cpu->WriteMemory32(addr, cpu->Reg_usr[0]); addr += 4; } if (BIT(inst_cream->inst, 14)) { if (cpu->Mode == USER32MODE) - WriteMemory32(cpu, addr, cpu->Reg[14]); + cpu->WriteMemory32(addr, cpu->Reg[14]); else - WriteMemory32(cpu, addr, cpu->Reg_usr[1]); + cpu->WriteMemory32(addr, cpu->Reg_usr[1]); addr += 4; } if (BIT(inst_cream->inst, 15)) { - WriteMemory32(cpu, addr, cpu->Reg_usr[1] + 8); + cpu->WriteMemory32(addr, cpu->Reg_usr[1] + 8); } } else { for (int i = 0; i < 15; i++) { if (BIT(inst_cream->inst, i)) { if (i == Rn) - WriteMemory32(cpu, addr, old_RN); + cpu->WriteMemory32(addr, old_RN); else - WriteMemory32(cpu, addr, cpu->Reg[i]); + cpu->WriteMemory32(addr, cpu->Reg[i]); addr += 4; } @@ -5976,10 +5939,10 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { // Check PC reg if (BIT(inst_cream->inst, 15)) - WriteMemory32(cpu, addr, cpu->Reg_usr[1] + 8); + cpu->WriteMemory32(addr, cpu->Reg_usr[1] + 8); } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -5997,7 +5960,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } RD = operand2; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(sxtb_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6009,9 +5972,9 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { inst_cream->get_addr(cpu, inst_cream->inst, addr); unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)]; - WriteMemory32(cpu, addr, value); + cpu->WriteMemory32(addr, value); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6022,7 +5985,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { uxtb_inst* inst_cream = (uxtb_inst*)inst_base->component; RD = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(uxtb_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6035,7 +5998,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff; RD = RN + operand2; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(uxtab_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6048,7 +6011,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff; Memory::Write8(addr, value); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6061,7 +6024,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff; Memory::Write8(addr, value); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6074,10 +6037,10 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { // The 3DS doesn't have the Large Physical Access Extension (LPAE) // so STRD wouldn't store these as a single write. - WriteMemory32(cpu, addr + 0, cpu->Reg[BITS(inst_cream->inst, 12, 15)]); - WriteMemory32(cpu, addr + 4, cpu->Reg[BITS(inst_cream->inst, 12, 15) + 1]); + cpu->WriteMemory32(addr + 0, cpu->Reg[BITS(inst_cream->inst, 12, 15)]); + cpu->WriteMemory32(addr + 4, cpu->Reg[BITS(inst_cream->inst, 12, 15) + 1]); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6088,18 +6051,16 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int write_addr = cpu->Reg[inst_cream->Rn]; - if ((exclusive_detect(cpu, write_addr) == 0) && (cpu->exclusive_state == 1)) { - remove_exclusive(cpu, write_addr); - cpu->exclusive_state = 0; - - WriteMemory32(cpu, write_addr, RM); + if (cpu->IsExclusiveMemoryAccess(write_addr)) { + cpu->UnsetExclusiveMemoryAddress(); + cpu->WriteMemory32(write_addr, RM); RD = 0; } else { // Failed to write due to mutex access RD = 1; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6110,10 +6071,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int write_addr = cpu->Reg[inst_cream->Rn]; - if ((exclusive_detect(cpu, write_addr) == 0) && (cpu->exclusive_state == 1)) { - remove_exclusive(cpu, write_addr); - cpu->exclusive_state = 0; - + if (cpu->IsExclusiveMemoryAccess(write_addr)) { + cpu->UnsetExclusiveMemoryAddress(); Memory::Write8(write_addr, cpu->Reg[inst_cream->Rm]); RD = 0; } else { @@ -6121,7 +6080,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = 1; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6132,20 +6091,19 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int write_addr = cpu->Reg[inst_cream->Rn]; - if ((exclusive_detect(cpu, write_addr) == 0) && (cpu->exclusive_state == 1)) { - remove_exclusive(cpu, write_addr); - cpu->exclusive_state = 0; + if (cpu->IsExclusiveMemoryAccess(write_addr)) { + cpu->UnsetExclusiveMemoryAddress(); const u32 rt = cpu->Reg[inst_cream->Rm + 0]; const u32 rt2 = cpu->Reg[inst_cream->Rm + 1]; u64 value; - if (InBigEndianMode(cpu)) + if (cpu->InBigEndianMode()) value = (((u64)rt << 32) | rt2); else value = (((u64)rt2 << 32) | rt); - WriteMemory64(cpu, write_addr, value); + cpu->WriteMemory64(write_addr, value); RD = 0; } else { @@ -6153,7 +6111,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = 1; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6164,18 +6122,16 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int write_addr = cpu->Reg[inst_cream->Rn]; - if ((exclusive_detect(cpu, write_addr) == 0) && (cpu->exclusive_state == 1)) { - remove_exclusive(cpu, write_addr); - cpu->exclusive_state = 0; - - WriteMemory16(cpu, write_addr, RM); + if (cpu->IsExclusiveMemoryAccess(write_addr)) { + cpu->UnsetExclusiveMemoryAddress(); + cpu->WriteMemory16(write_addr, RM); RD = 0; } else { // Failed to write due to mutex access RD = 1; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6187,9 +6143,9 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { inst_cream->get_addr(cpu, inst_cream->inst, addr); unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xffff; - WriteMemory16(cpu, addr, value); + cpu->WriteMemory16(addr, value); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6201,9 +6157,9 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { inst_cream->get_addr(cpu, inst_cream->inst, addr); unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)]; - WriteMemory32(cpu, addr, value); + cpu->WriteMemory32(addr, value); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6224,7 +6180,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; - switch_mode(cpu, cpu->Spsr_copy & 0x1f); + cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { @@ -6238,7 +6194,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { goto DISPATCH; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(sub_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6246,10 +6202,11 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { SWI_INST: { if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { - SVC::CallSVC(Memory::Read32(cpu->Reg[15])); + swi_inst* const inst_cream = (swi_inst*)inst_base->component; + SVC::CallSVC(inst_cream->num & 0xFFFF); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(swi_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6260,12 +6217,12 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { swp_inst* inst_cream = (swp_inst*)inst_base->component; addr = RN; - unsigned int value = ReadMemory32(cpu, addr); - WriteMemory32(cpu, addr, RM); + unsigned int value = cpu->ReadMemory32(addr); + cpu->WriteMemory32(addr, RM); RD = value; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(swp_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6279,7 +6236,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { Memory::Write8(addr, (RM & 0xFF)); RD = value; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(swp_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6295,7 +6252,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { operand2 = (0x80 & operand2)? (0xFFFFFF00 | operand2):operand2; RD = RN + operand2; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(uxtab_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6328,7 +6285,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(sxtab_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6344,7 +6301,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { operand2 = (0x8000 & operand2) ? (0xFFFF0000 | operand2) : operand2; RD = RN + operand2; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(sxtah_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6359,7 +6316,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { u32 rop = SHIFTER_OPERAND; if (inst_cream->Rn == 15) - lop += GET_INST_SIZE(cpu) * 2; + lop += cpu->GetInstructionSize() * 2; u32 result = lop ^ rop; @@ -6367,7 +6324,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { UPDATE_ZFLAG(result); UPDATE_CFLAG_WITH_SC; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(teq_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6381,7 +6338,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { u32 rop = SHIFTER_OPERAND; if (inst_cream->Rn == 15) - lop += GET_INST_SIZE(cpu) * 2; + lop += cpu->GetInstructionSize() * 2; u32 result = lop & rop; @@ -6389,7 +6346,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { UPDATE_ZFLAG(result); UPDATE_CFLAG_WITH_SC; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(tst_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6560,7 +6517,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6640,7 +6597,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6659,7 +6616,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RDLO = (result & 0xFFFFFFFF); RDHI = ((result >> 32) & 0xFFFFFFFF); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(umaal_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6682,7 +6639,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->ZFlag = (RDHI == 0 && RDLO == 0); } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(umlal_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6702,7 +6659,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->ZFlag = (RDHI == 0 && RDLO == 0); } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(umull_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6730,7 +6687,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { { bl_1_thumb* inst_cream = (bl_1_thumb*)inst_base->component; cpu->Reg[14] = cpu->Reg[15] + 4 + inst_cream->imm; - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(bl_1_thumb)); FETCH_INST; GOTO_NEXT_INST; @@ -6811,7 +6768,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = ((lo_val & 0xFFFF) | hi_val << 16); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6841,7 +6798,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = finalDif; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6874,7 +6831,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = rn_val; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ssat_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6896,7 +6853,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->Cpsr |= (1 << 27); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ssat_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6927,7 +6884,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(uxtab_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -6940,7 +6897,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { LOG_TRACE(Core_ARM11, "WFE executed."); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC_STUB; FETCH_INST; GOTO_NEXT_INST; @@ -6953,7 +6910,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { LOG_TRACE(Core_ARM11, "WFI executed."); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC_STUB; FETCH_INST; GOTO_NEXT_INST; @@ -6966,7 +6923,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { LOG_TRACE(Core_ARM11, "YIELD executed."); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC_STUB; FETCH_INST; GOTO_NEXT_INST; diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.h b/src/core/arm/dyncom/arm_dyncom_interpreter.h index 1c324d29cc..7a46dcc940 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.h +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.h @@ -4,6 +4,6 @@ #pragma once -#include "core/arm/skyeye_common/armdefs.h" +struct ARMul_State; unsigned InterpreterMainLoop(ARMul_State* state); diff --git a/src/core/arm/dyncom/arm_dyncom_run.cpp b/src/core/arm/dyncom/arm_dyncom_run.cpp deleted file mode 100644 index 5a9a6a788d..0000000000 --- a/src/core/arm/dyncom/arm_dyncom_run.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// 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/dyncom/arm_dyncom_run.h" -#include "core/arm/skyeye_common/armdefs.h" - -void switch_mode(ARMul_State* core, uint32_t mode) { - if (core->Mode == mode) - return; - - if (mode != USERBANK) { - switch (core->Mode) { - case SYSTEM32MODE: // Shares registers with user mode - case USER32MODE: - core->Reg_usr[0] = core->Reg[13]; - core->Reg_usr[1] = core->Reg[14]; - break; - case IRQ32MODE: - core->Reg_irq[0] = core->Reg[13]; - core->Reg_irq[1] = core->Reg[14]; - core->Spsr[IRQBANK] = core->Spsr_copy; - break; - case SVC32MODE: - core->Reg_svc[0] = core->Reg[13]; - core->Reg_svc[1] = core->Reg[14]; - core->Spsr[SVCBANK] = core->Spsr_copy; - break; - case ABORT32MODE: - core->Reg_abort[0] = core->Reg[13]; - core->Reg_abort[1] = core->Reg[14]; - core->Spsr[ABORTBANK] = core->Spsr_copy; - break; - case UNDEF32MODE: - core->Reg_undef[0] = core->Reg[13]; - core->Reg_undef[1] = core->Reg[14]; - core->Spsr[UNDEFBANK] = core->Spsr_copy; - break; - case FIQ32MODE: - core->Reg_firq[0] = core->Reg[13]; - core->Reg_firq[1] = core->Reg[14]; - core->Spsr[FIQBANK] = core->Spsr_copy; - break; - } - - switch (mode) { - case USER32MODE: - core->Reg[13] = core->Reg_usr[0]; - core->Reg[14] = core->Reg_usr[1]; - core->Bank = USERBANK; - break; - case IRQ32MODE: - core->Reg[13] = core->Reg_irq[0]; - core->Reg[14] = core->Reg_irq[1]; - core->Spsr_copy = core->Spsr[IRQBANK]; - core->Bank = IRQBANK; - break; - case SVC32MODE: - core->Reg[13] = core->Reg_svc[0]; - core->Reg[14] = core->Reg_svc[1]; - core->Spsr_copy = core->Spsr[SVCBANK]; - core->Bank = SVCBANK; - break; - case ABORT32MODE: - core->Reg[13] = core->Reg_abort[0]; - core->Reg[14] = core->Reg_abort[1]; - core->Spsr_copy = core->Spsr[ABORTBANK]; - core->Bank = ABORTBANK; - break; - case UNDEF32MODE: - core->Reg[13] = core->Reg_undef[0]; - core->Reg[14] = core->Reg_undef[1]; - core->Spsr_copy = core->Spsr[UNDEFBANK]; - core->Bank = UNDEFBANK; - break; - case FIQ32MODE: - core->Reg[13] = core->Reg_firq[0]; - core->Reg[14] = core->Reg_firq[1]; - core->Spsr_copy = core->Spsr[FIQBANK]; - core->Bank = FIQBANK; - break; - case SYSTEM32MODE: // Shares registers with user mode. - core->Reg[13] = core->Reg_usr[0]; - core->Reg[14] = core->Reg_usr[1]; - core->Bank = SYSTEMBANK; - break; - } - - // Set the mode bits in the APSR - core->Cpsr = (core->Cpsr & ~core->Mode) | mode; - core->Mode = mode; - } -} diff --git a/src/core/arm/dyncom/arm_dyncom_run.h b/src/core/arm/dyncom/arm_dyncom_run.h index 85774c565d..13bef17fc3 100644 --- a/src/core/arm/dyncom/arm_dyncom_run.h +++ b/src/core/arm/dyncom/arm_dyncom_run.h @@ -18,40 +18,31 @@ #pragma once -#include "core/arm/skyeye_common/armdefs.h" - -void switch_mode(ARMul_State* core, uint32_t mode); - -// Note that for the 3DS, a Thumb instruction will only ever be -// two bytes in size. Thus we don't need to worry about ThumbEE -// or Thumb-2 where instructions can be 4 bytes in length. -static inline u32 GET_INST_SIZE(ARMul_State* core) { - return core->TFlag? 2 : 4; -} +#include "core/arm/skyeye_common/armstate.h" /** * Checks if the PC is being read, and if so, word-aligns it. * Used with address calculations. * - * @param core The ARM CPU state instance. + * @param cpu The ARM CPU state instance. * @param Rn The register being read. * * @return If the PC is being read, then the word-aligned PC value is returned. * If the PC is not being read, then the value stored in the register is returned. */ -static inline u32 CHECK_READ_REG15_WA(ARMul_State* core, int Rn) { - return (Rn == 15) ? ((core->Reg[15] & ~0x3) + GET_INST_SIZE(core) * 2) : core->Reg[Rn]; +static inline u32 CHECK_READ_REG15_WA(ARMul_State* cpu, int Rn) { + return (Rn == 15) ? ((cpu->Reg[15] & ~0x3) + cpu->GetInstructionSize() * 2) : cpu->Reg[Rn]; } /** * Reads the PC. Used for data processing operations that use the PC. * - * @param core The ARM CPU state instance. + * @param cpu The ARM CPU state instance. * @param Rn The register being read. * * @return If the PC is being read, then the incremented PC value is returned. * If the PC is not being read, then the values stored in the register is returned. */ -static inline u32 CHECK_READ_REG15(ARMul_State* core, int Rn) { - return (Rn == 15) ? ((core->Reg[15] & ~0x1) + GET_INST_SIZE(core) * 2) : core->Reg[Rn]; +static inline u32 CHECK_READ_REG15(ARMul_State* cpu, int Rn) { + return (Rn == 15) ? ((cpu->Reg[15] & ~0x1) + cpu->GetInstructionSize() * 2) : cpu->Reg[Rn]; } diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.cpp b/src/core/arm/dyncom/arm_dyncom_thumb.cpp index 3e79c44c0b..29272fd5d2 100644 --- a/src/core/arm/dyncom/arm_dyncom_thumb.cpp +++ b/src/core/arm/dyncom/arm_dyncom_thumb.cpp @@ -6,20 +6,15 @@ // ARM instruction, and using the existing ARM simulator. #include "core/arm/dyncom/arm_dyncom_thumb.h" +#include "core/arm/skyeye_common/armsupp.h" // Decode a 16bit Thumb instruction. The instruction is in the low 16-bits of the tinstr field, // with the following Thumb instruction held in the high 16-bits. Passing in two Thumb instructions // allows easier simulation of the special dual BL instruction. -tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { - tdstate valid = t_uninitialized; - ARMword tinstr = instr; - - // The endian should be judge here - if((addr & 0x3) != 0) - tinstr = instr >> 16; - else - tinstr &= 0xFFFF; +ThumbDecodeStatus TranslateThumbInstruction(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { + ThumbDecodeStatus valid = ThumbDecodeStatus::UNINITIALIZED; + u32 tinstr = GetThumbInstruction(instr, addr); *ainstr = 0xDEADC0DE; // Debugging to catch non updates @@ -36,7 +31,7 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { case 3: // ADD/SUB { - static const ARMword subset[4] = { + static const u32 subset[4] = { 0xE0900000, // ADDS Rd,Rs,Rn 0xE0500000, // SUBS Rd,Rs,Rn 0xE2900000, // ADDS Rd,Rs,#imm3 @@ -55,7 +50,7 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { case 6: // ADD case 7: // SUB { - static const ARMword subset[4] = { + static const u32 subset[4] = { 0xE3B00000, // MOVS Rd,#imm8 0xE3500000, // CMP Rd,#imm8 0xE2900000, // ADDS Rd,Rd,#imm8 @@ -84,7 +79,7 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { }; static const struct { - ARMword opcode; + u32 opcode; otype type; } subset[16] = { { 0xE0100000, t_norm }, // ANDS Rd,Rd,Rs @@ -129,15 +124,14 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { break; } } else { - ARMword Rd = ((tinstr & 0x0007) >> 0); - ARMword Rs = ((tinstr & 0x0038) >> 3); + u32 Rd = ((tinstr & 0x0007) >> 0); + u32 Rs = ((tinstr & 0x0078) >> 3); if (tinstr & (1 << 7)) Rd += 8; - if (tinstr & (1 << 6)) - Rs += 8; switch ((tinstr & 0x03C0) >> 6) { + case 0x0: // ADD Rd,Rd,Rs case 0x1: // ADD Rd,Rd,Hs case 0x2: // ADD Hd,Hd,Rs case 0x3: // ADD Hd,Hd,Hs @@ -146,19 +140,19 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { |(Rd << 12) // Rd |(Rs << 0); // Rm break; + case 0x4: // CMP Rd,Rs 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 0x8: // MOV Rd,Rs 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; @@ -167,11 +161,6 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { *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 @@ -190,7 +179,7 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { case 10: case 11: { - static const ARMword subset[8] = { + static const u32 subset[8] = { 0xE7800000, // STR Rd,[Rb,Ro] 0xE18000B0, // STRH Rd,[Rb,Ro] 0xE7C00000, // STRB Rd,[Rb,Ro] @@ -213,7 +202,7 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { case 14: // STRB Rd,[Rb,#imm5] case 15: // LDRB Rd,[Rb,#imm5] { - static const ARMword subset[4] = { + static const u32 subset[4] = { 0xE5800000, // STR Rd,[Rb,#imm5] 0xE5900000, // LDR Rd,[Rb,#imm5] 0xE5C00000, // STRB Rd,[Rb,#imm5] @@ -280,7 +269,7 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { | BITS(tinstr, 0, 3) // imm4 field; | (BITS(tinstr, 4, 7) << 8); // beginning 4 bits of imm12 } else if ((tinstr & 0x0F00) == 0x0200) { - static const ARMword subset[4] = { + static const u32 subset[4] = { 0xE6BF0070, // SXTH 0xE6AF0070, // SXTB 0xE6FF0070, // UXTH @@ -304,7 +293,7 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { | (BIT(tinstr, 4) << 18); // enable bit } } else if ((tinstr & 0x0F00) == 0x0a00) { - static const ARMword subset[3] = { + static const u32 subset[3] = { 0xE6BF0F30, // REV 0xE6BF0FB0, // REV16 0xE6FF0FB0, // REVSH @@ -314,7 +303,7 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { | (BITS(tinstr, 0, 2) << 12) // Rd | BITS(tinstr, 3, 5); // Rm } else { - static const ARMword subset[4] = { + static const u32 subset[4] = { 0xE92D0000, // STMDB sp!,{rlist} 0xE92D4000, // STMDB sp!,{rlist,lr} 0xE8BD0000, // LDMIA sp!,{rlist} @@ -362,21 +351,21 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { else *ainstr |= (tinstr & 0x00FF); } else if ((tinstr & 0x0F00) != 0x0E00) - valid = t_branch; + valid = ThumbDecodeStatus::BRANCH; else // UNDEFINED : cc=1110(AL) uses different format - valid = t_undefined; + valid = ThumbDecodeStatus::UNDEFINED; break; case 28: // B - valid = t_branch; + valid = ThumbDecodeStatus::BRANCH; break; case 29: - if(tinstr & 0x1) - valid = t_undefined; + if (tinstr & 0x1) + valid = ThumbDecodeStatus::UNDEFINED; else - valid = t_branch; + valid = ThumbDecodeStatus::BRANCH; break; case 30: // BL instruction 1 @@ -385,7 +374,7 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { // 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; + valid = ThumbDecodeStatus::BRANCH; break; case 31: // BL instruction 2 @@ -394,7 +383,7 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { // 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; + valid = ThumbDecodeStatus::BRANCH; break; } diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.h b/src/core/arm/dyncom/arm_dyncom_thumb.h index 8394ff156e..4479743639 100644 --- a/src/core/arm/dyncom/arm_dyncom_thumb.h +++ b/src/core/arm/dyncom/arm_dyncom_thumb.h @@ -26,22 +26,24 @@ #pragma once -#include "core/arm/skyeye_common/armdefs.h" +#include "common/common_types.h" -enum tdstate { - t_undefined, // Undefined Thumb instruction - t_decoded, // Instruction decoded to ARM equivalent - t_branch, // Thumb branch (already processed) - t_uninitialized, +enum class ThumbDecodeStatus { + UNDEFINED, // Undefined Thumb instruction + DECODED, // Instruction decoded to ARM equivalent + BRANCH, // Thumb branch (already processed) + UNINITIALIZED, }; -tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size); +// Translates a Thumb mode instruction into its ARM equivalent. +ThumbDecodeStatus TranslateThumbInstruction(u32 addr, u32 instr, u32* ainstr, u32* inst_size); -static inline u32 get_thumb_instr(u32 instr, u32 pc) { - u32 tinstr; - if ((pc & 0x3) != 0) - tinstr = instr >> 16; - else - tinstr = instr & 0xFFFF; - return tinstr; +static inline u32 GetThumbInstruction(u32 instr, u32 address) { + // Normally you would need to handle instruction endianness, + // however, it is fixed to little-endian on the MPCore, so + // there's no need to check for this beforehand. + if ((address & 0x3) != 0) + return instr >> 16; + + return instr & 0xFFFF; } diff --git a/src/core/arm/interpreter/arminit.cpp b/src/core/arm/interpreter/arminit.cpp deleted file mode 100644 index 4f7a48fab8..0000000000 --- a/src/core/arm/interpreter/arminit.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* arminit.c -- ARMulator initialization: ARM6 Instruction Emulator. - Copyright (C) 1994 Advanced RISC Machines Ltd. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -#include <cstring> -#include "core/arm/skyeye_common/armdefs.h" -#include "core/arm/skyeye_common/vfp/vfp.h" - -/***************************************************************************\ -* Returns a new instantiation of the ARMulator's state * -\***************************************************************************/ -ARMul_State* ARMul_NewState(ARMul_State* state) -{ - state->Emulate = RUN; - state->Mode = USER32MODE; - - state->lateabtSig = HIGH; - state->bigendSig = LOW; - - return state; -} - -/***************************************************************************\ -* Call this routine to set ARMulator to model a certain processor * -\***************************************************************************/ - -void ARMul_SelectProcessor(ARMul_State* state, unsigned properties) -{ - state->is_v4 = (properties & (ARM_v4_Prop | ARM_v5_Prop)) != 0; - state->is_v5 = (properties & ARM_v5_Prop) != 0; - state->is_v5e = (properties & ARM_v5e_Prop) != 0; - state->is_v6 = (properties & ARM_v6_Prop) != 0; - state->is_v7 = (properties & ARM_v7_Prop) != 0; -} - -// Resets certain MPCore CP15 values to their ARM-defined reset values. -static void ResetMPCoreCP15Registers(ARMul_State* cpu) -{ - // c0 - cpu->CP15[CP15_MAIN_ID] = 0x410FB024; - cpu->CP15[CP15_TLB_TYPE] = 0x00000800; - cpu->CP15[CP15_PROCESSOR_FEATURE_0] = 0x00000111; - cpu->CP15[CP15_PROCESSOR_FEATURE_1] = 0x00000001; - cpu->CP15[CP15_DEBUG_FEATURE_0] = 0x00000002; - cpu->CP15[CP15_MEMORY_MODEL_FEATURE_0] = 0x01100103; - cpu->CP15[CP15_MEMORY_MODEL_FEATURE_1] = 0x10020302; - cpu->CP15[CP15_MEMORY_MODEL_FEATURE_2] = 0x01222000; - cpu->CP15[CP15_MEMORY_MODEL_FEATURE_3] = 0x00000000; - cpu->CP15[CP15_ISA_FEATURE_0] = 0x00100011; - cpu->CP15[CP15_ISA_FEATURE_1] = 0x12002111; - cpu->CP15[CP15_ISA_FEATURE_2] = 0x11221011; - cpu->CP15[CP15_ISA_FEATURE_3] = 0x01102131; - cpu->CP15[CP15_ISA_FEATURE_4] = 0x00000141; - - // c1 - cpu->CP15[CP15_CONTROL] = 0x00054078; - cpu->CP15[CP15_AUXILIARY_CONTROL] = 0x0000000F; - cpu->CP15[CP15_COPROCESSOR_ACCESS_CONTROL] = 0x00000000; - - // c2 - cpu->CP15[CP15_TRANSLATION_BASE_TABLE_0] = 0x00000000; - cpu->CP15[CP15_TRANSLATION_BASE_TABLE_1] = 0x00000000; - cpu->CP15[CP15_TRANSLATION_BASE_CONTROL] = 0x00000000; - - // c3 - cpu->CP15[CP15_DOMAIN_ACCESS_CONTROL] = 0x00000000; - - // c7 - cpu->CP15[CP15_PHYS_ADDRESS] = 0x00000000; - - // c9 - cpu->CP15[CP15_DATA_CACHE_LOCKDOWN] = 0xFFFFFFF0; - - // c10 - cpu->CP15[CP15_TLB_LOCKDOWN] = 0x00000000; - cpu->CP15[CP15_PRIMARY_REGION_REMAP] = 0x00098AA4; - cpu->CP15[CP15_NORMAL_REGION_REMAP] = 0x44E048E0; - - // c13 - cpu->CP15[CP15_PID] = 0x00000000; - cpu->CP15[CP15_CONTEXT_ID] = 0x00000000; - cpu->CP15[CP15_THREAD_UPRW] = 0x00000000; - cpu->CP15[CP15_THREAD_URO] = 0x00000000; - cpu->CP15[CP15_THREAD_PRW] = 0x00000000; - - // c15 - cpu->CP15[CP15_PERFORMANCE_MONITOR_CONTROL] = 0x00000000; - cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS] = 0x00000000; - cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS] = 0x00000000; - cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE] = 0x00000000; - cpu->CP15[CP15_TLB_DEBUG_CONTROL] = 0x00000000; -} - -/***************************************************************************\ -* Call this routine to set up the initial machine state (or perform a RESET * -\***************************************************************************/ -void ARMul_Reset(ARMul_State* state) -{ - VFPInit(state); - - state->Reg[15] = 0; - state->Cpsr = INTBITS | SVC32MODE; - state->Mode = SVC32MODE; - state->Bank = SVCBANK; - - ResetMPCoreCP15Registers(state); - - state->NresetSig = HIGH; - state->NfiqSig = HIGH; - state->NirqSig = HIGH; - state->NtransSig = (state->Mode & 3) ? HIGH : LOW; - state->abortSig = LOW; - - state->NumInstrs = 0; -} diff --git a/src/core/arm/interpreter/armsupp.cpp b/src/core/arm/interpreter/armsupp.cpp deleted file mode 100644 index 83f7f3e2c4..0000000000 --- a/src/core/arm/interpreter/armsupp.cpp +++ /dev/null @@ -1,637 +0,0 @@ -/* armsupp.c -- ARMulator support code: ARM6 Instruction Emulator. - Copyright (C) 1994 Advanced RISC Machines Ltd. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -#include "common/logging/log.h" - -#include "core/mem_map.h" -#include "core/arm/skyeye_common/armdefs.h" -#include "core/arm/skyeye_common/arm_regformat.h" - -// Unsigned sum of absolute difference -u8 ARMul_UnsignedAbsoluteDifference(u8 left, u8 right) -{ - if (left > right) - return left - right; - - return right - left; -} - -// Add with carry, indicates if a carry-out or signed overflow occurred. -u32 AddWithCarry(u32 left, u32 right, u32 carry_in, bool* carry_out_occurred, bool* overflow_occurred) -{ - u64 unsigned_sum = (u64)left + (u64)right + (u64)carry_in; - s64 signed_sum = (s64)(s32)left + (s64)(s32)right + (s64)carry_in; - u64 result = (unsigned_sum & 0xFFFFFFFF); - - if (carry_out_occurred) - *carry_out_occurred = (result != unsigned_sum); - - if (overflow_occurred) - *overflow_occurred = ((s64)(s32)result != signed_sum); - - return (u32)result; -} - -// Compute whether an addition of A and B, giving RESULT, overflowed. -bool AddOverflow(ARMword a, ARMword b, ARMword result) -{ - return ((NEG(a) && NEG(b) && POS(result)) || - (POS(a) && POS(b) && NEG(result))); -} - -// Compute whether a subtraction of A and B, giving RESULT, overflowed. -bool SubOverflow(ARMword a, ARMword b, ARMword result) -{ - return ((NEG(a) && POS(b) && POS(result)) || - (POS(a) && NEG(b) && NEG(result))); -} - -// Returns true if the Q flag should be set as a result of overflow. -bool ARMul_AddOverflowQ(ARMword a, ARMword b) -{ - u32 result = a + b; - if (((result ^ a) & (u32)0x80000000) && ((a ^ b) & (u32)0x80000000) == 0) - return true; - - return false; -} - -// 8-bit signed saturated addition -u8 ARMul_SignedSaturatedAdd8(u8 left, u8 right) -{ - u8 result = left + right; - - if (((result ^ left) & 0x80) && ((left ^ right) & 0x80) == 0) { - if (left & 0x80) - result = 0x80; - else - result = 0x7F; - } - - return result; -} - -// 8-bit signed saturated subtraction -u8 ARMul_SignedSaturatedSub8(u8 left, u8 right) -{ - u8 result = left - right; - - if (((result ^ left) & 0x80) && ((left ^ right) & 0x80) != 0) { - if (left & 0x80) - result = 0x80; - else - result = 0x7F; - } - - return result; -} - -// 16-bit signed saturated addition -u16 ARMul_SignedSaturatedAdd16(u16 left, u16 right) -{ - u16 result = left + right; - - if (((result ^ left) & 0x8000) && ((left ^ right) & 0x8000) == 0) { - if (left & 0x8000) - result = 0x8000; - else - result = 0x7FFF; - } - - return result; -} - -// 16-bit signed saturated subtraction -u16 ARMul_SignedSaturatedSub16(u16 left, u16 right) -{ - u16 result = left - right; - - if (((result ^ left) & 0x8000) && ((left ^ right) & 0x8000) != 0) { - if (left & 0x8000) - result = 0x8000; - else - result = 0x7FFF; - } - - return result; -} - -// 8-bit unsigned saturated addition -u8 ARMul_UnsignedSaturatedAdd8(u8 left, u8 right) -{ - u8 result = left + right; - - if (result < left) - result = 0xFF; - - return result; -} - -// 16-bit unsigned saturated addition -u16 ARMul_UnsignedSaturatedAdd16(u16 left, u16 right) -{ - u16 result = left + right; - - if (result < left) - result = 0xFFFF; - - return result; -} - -// 8-bit unsigned saturated subtraction -u8 ARMul_UnsignedSaturatedSub8(u8 left, u8 right) -{ - if (left <= right) - return 0; - - return left - right; -} - -// 16-bit unsigned saturated subtraction -u16 ARMul_UnsignedSaturatedSub16(u16 left, u16 right) -{ - if (left <= right) - return 0; - - return left - right; -} - -// Signed saturation. -u32 ARMul_SignedSatQ(s32 value, u8 shift, bool* saturation_occurred) -{ - const u32 max = (1 << shift) - 1; - const s32 top = (value >> shift); - - if (top > 0) { - *saturation_occurred = true; - return max; - } - else if (top < -1) { - *saturation_occurred = true; - return ~max; - } - - *saturation_occurred = false; - return (u32)value; -} - -// Unsigned saturation -u32 ARMul_UnsignedSatQ(s32 value, u8 shift, bool* saturation_occurred) -{ - const u32 max = (1 << shift) - 1; - - if (value < 0) { - *saturation_occurred = true; - return 0; - } else if ((u32)value > max) { - *saturation_occurred = true; - return max; - } - - *saturation_occurred = false; - return (u32)value; -} - -// Whether or not the given CPU is in big endian mode (E bit is set) -bool InBigEndianMode(ARMul_State* cpu) -{ - return (cpu->Cpsr & (1 << 9)) != 0; -} - -// Whether or not the given CPU is in a mode other than user mode. -bool InAPrivilegedMode(ARMul_State* cpu) -{ - return (cpu->Mode != USER32MODE); -} - -// Reads from the CP15 registers. Used with implementation of the MRC instruction. -// Note that since the 3DS does not have the hypervisor extensions, these registers -// are not implemented. -u32 ReadCP15Register(ARMul_State* cpu, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2) -{ - // Unprivileged registers - if (crn == 13 && opcode_1 == 0 && crm == 0) - { - if (opcode_2 == 2) - return cpu->CP15[CP15_THREAD_UPRW]; - - if (opcode_2 == 3) - return cpu->CP15[CP15_THREAD_URO]; - } - - if (InAPrivilegedMode(cpu)) - { - if (crn == 0 && opcode_1 == 0) - { - if (crm == 0) - { - if (opcode_2 == 0) - return cpu->CP15[CP15_MAIN_ID]; - - if (opcode_2 == 1) - return cpu->CP15[CP15_CACHE_TYPE]; - - if (opcode_2 == 3) - return cpu->CP15[CP15_TLB_TYPE]; - - if (opcode_2 == 5) - return cpu->CP15[CP15_CPU_ID]; - } - else if (crm == 1) - { - if (opcode_2 == 0) - return cpu->CP15[CP15_PROCESSOR_FEATURE_0]; - - if (opcode_2 == 1) - return cpu->CP15[CP15_PROCESSOR_FEATURE_1]; - - if (opcode_2 == 2) - return cpu->CP15[CP15_DEBUG_FEATURE_0]; - - if (opcode_2 == 4) - return cpu->CP15[CP15_MEMORY_MODEL_FEATURE_0]; - - if (opcode_2 == 5) - return cpu->CP15[CP15_MEMORY_MODEL_FEATURE_1]; - - if (opcode_2 == 6) - return cpu->CP15[CP15_MEMORY_MODEL_FEATURE_2]; - - if (opcode_2 == 7) - return cpu->CP15[CP15_MEMORY_MODEL_FEATURE_3]; - } - else if (crm == 2) - { - if (opcode_2 == 0) - return cpu->CP15[CP15_ISA_FEATURE_0]; - - if (opcode_2 == 1) - return cpu->CP15[CP15_ISA_FEATURE_1]; - - if (opcode_2 == 2) - return cpu->CP15[CP15_ISA_FEATURE_2]; - - if (opcode_2 == 3) - return cpu->CP15[CP15_ISA_FEATURE_3]; - - if (opcode_2 == 4) - return cpu->CP15[CP15_ISA_FEATURE_4]; - } - } - - if (crn == 1 && opcode_1 == 0 && crm == 0) - { - if (opcode_2 == 0) - return cpu->CP15[CP15_CONTROL]; - - if (opcode_2 == 1) - return cpu->CP15[CP15_AUXILIARY_CONTROL]; - - if (opcode_2 == 2) - return cpu->CP15[CP15_COPROCESSOR_ACCESS_CONTROL]; - } - - if (crn == 2 && opcode_1 == 0 && crm == 0) - { - if (opcode_2 == 0) - return cpu->CP15[CP15_TRANSLATION_BASE_TABLE_0]; - - if (opcode_2 == 1) - return cpu->CP15[CP15_TRANSLATION_BASE_TABLE_1]; - - if (opcode_2 == 2) - return cpu->CP15[CP15_TRANSLATION_BASE_CONTROL]; - } - - if (crn == 3 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) - return cpu->CP15[CP15_DOMAIN_ACCESS_CONTROL]; - - if (crn == 5 && opcode_1 == 0 && crm == 0) - { - if (opcode_2 == 0) - return cpu->CP15[CP15_FAULT_STATUS]; - - if (opcode_2 == 1) - return cpu->CP15[CP15_INSTR_FAULT_STATUS]; - } - - if (crn == 6 && opcode_1 == 0 && crm == 0) - { - if (opcode_2 == 0) - return cpu->CP15[CP15_FAULT_ADDRESS]; - - if (opcode_2 == 1) - return cpu->CP15[CP15_WFAR]; - } - - if (crn == 7 && opcode_1 == 0 && crm == 4 && opcode_2 == 0) - return cpu->CP15[CP15_PHYS_ADDRESS]; - - if (crn == 9 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) - return cpu->CP15[CP15_DATA_CACHE_LOCKDOWN]; - - if (crn == 10 && opcode_1 == 0) - { - if (crm == 0 && opcode_2 == 0) - return cpu->CP15[CP15_TLB_LOCKDOWN]; - - if (crm == 2) - { - if (opcode_2 == 0) - return cpu->CP15[CP15_PRIMARY_REGION_REMAP]; - - if (opcode_2 == 1) - return cpu->CP15[CP15_NORMAL_REGION_REMAP]; - } - } - - if (crn == 13 && crm == 0) - { - if (opcode_2 == 0) - return cpu->CP15[CP15_PID]; - - if (opcode_2 == 1) - return cpu->CP15[CP15_CONTEXT_ID]; - - if (opcode_2 == 4) - return cpu->CP15[CP15_THREAD_PRW]; - } - - if (crn == 15) - { - if (opcode_1 == 0 && crm == 12) - { - if (opcode_2 == 0) - return cpu->CP15[CP15_PERFORMANCE_MONITOR_CONTROL]; - - if (opcode_2 == 1) - return cpu->CP15[CP15_CYCLE_COUNTER]; - - if (opcode_2 == 2) - return cpu->CP15[CP15_COUNT_0]; - - if (opcode_2 == 3) - return cpu->CP15[CP15_COUNT_1]; - } - - if (opcode_1 == 5 && opcode_2 == 2) - { - if (crm == 5) - return cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS]; - - if (crm == 6) - return cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS]; - - if (crm == 7) - return cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE]; - } - - if (opcode_1 == 7 && crm == 1 && opcode_2 == 0) - return cpu->CP15[CP15_TLB_DEBUG_CONTROL]; - } - } - - LOG_ERROR(Core_ARM11, "MRC CRn=%u, CRm=%u, OP1=%u OP2=%u is not implemented. Returning zero.", crn, crm, opcode_1, opcode_2); - return 0; -} - -// Write to the CP15 registers. Used with implementation of the MCR instruction. -// Note that since the 3DS does not have the hypervisor extensions, these registers -// are not implemented. -void WriteCP15Register(ARMul_State* cpu, u32 value, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2) -{ - if (InAPrivilegedMode(cpu)) - { - if (crn == 1 && opcode_1 == 0 && crm == 0) - { - if (opcode_2 == 0) - cpu->CP15[CP15_CONTROL] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_AUXILIARY_CONTROL] = value; - else if (opcode_2 == 2) - cpu->CP15[CP15_COPROCESSOR_ACCESS_CONTROL] = value; - } - else if (crn == 2 && opcode_1 == 0 && crm == 0) - { - if (opcode_2 == 0) - cpu->CP15[CP15_TRANSLATION_BASE_TABLE_0] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_TRANSLATION_BASE_TABLE_1] = value; - else if (opcode_2 == 2) - cpu->CP15[CP15_TRANSLATION_BASE_CONTROL] = value; - } - else if (crn == 3 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) - { - cpu->CP15[CP15_DOMAIN_ACCESS_CONTROL] = value; - } - else if (crn == 5 && opcode_1 == 0 && crm == 0) - { - if (opcode_2 == 0) - cpu->CP15[CP15_FAULT_STATUS] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_INSTR_FAULT_STATUS] = value; - } - else if (crn == 6 && opcode_1 == 0 && crm == 0) - { - if (opcode_2 == 0) - cpu->CP15[CP15_FAULT_ADDRESS] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_WFAR] = value; - } - else if (crn == 7 && opcode_1 == 0) - { - if (crm == 0 && opcode_2 == 4) - { - cpu->CP15[CP15_WAIT_FOR_INTERRUPT] = value; - } - else if (crm == 4 && opcode_2 == 0) - { - // NOTE: Not entirely accurate. This should do permission checks. - cpu->CP15[CP15_PHYS_ADDRESS] = Memory::VirtualToPhysicalAddress(value); - } - else if (crm == 5) - { - if (opcode_2 == 0) - cpu->CP15[CP15_INVALIDATE_INSTR_CACHE] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_INVALIDATE_INSTR_CACHE_USING_MVA] = value; - else if (opcode_2 == 2) - cpu->CP15[CP15_INVALIDATE_INSTR_CACHE_USING_INDEX] = value; - else if (opcode_2 == 6) - cpu->CP15[CP15_FLUSH_BRANCH_TARGET_CACHE] = value; - else if (opcode_2 == 7) - cpu->CP15[CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY] = value; - } - else if (crm == 6) - { - if (opcode_2 == 0) - cpu->CP15[CP15_INVALIDATE_DATA_CACHE] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA] = value; - else if (opcode_2 == 2) - cpu->CP15[CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX] = value; - } - else if (crm == 7 && opcode_2 == 0) - { - cpu->CP15[CP15_INVALIDATE_DATA_AND_INSTR_CACHE] = value; - } - else if (crm == 10) - { - if (opcode_2 == 0) - cpu->CP15[CP15_CLEAN_DATA_CACHE] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_CLEAN_DATA_CACHE_LINE_USING_MVA] = value; - else if (opcode_2 == 2) - cpu->CP15[CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX] = value; - } - else if (crm == 14) - { - if (opcode_2 == 0) - cpu->CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA] = value; - else if (opcode_2 == 2) - cpu->CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX] = value; - } - } - else if (crn == 8 && opcode_1 == 0) - { - LOG_WARNING(Core_ARM11, "TLB operations not fully implemented."); - - if (crm == 5) - { - if (opcode_2 == 0) - cpu->CP15[CP15_INVALIDATE_ITLB] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_INVALIDATE_ITLB_SINGLE_ENTRY] = value; - else if (opcode_2 == 2) - cpu->CP15[CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH] = value; - else if (opcode_2 == 3) - cpu->CP15[CP15_INVALIDATE_ITLB_ENTRY_ON_MVA] = value; - } - else if (crm == 6) - { - if (opcode_2 == 0) - cpu->CP15[CP15_INVALIDATE_DTLB] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_INVALIDATE_DTLB_SINGLE_ENTRY] = value; - else if (opcode_2 == 2) - cpu->CP15[CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH] = value; - else if (opcode_2 == 3) - cpu->CP15[CP15_INVALIDATE_DTLB_ENTRY_ON_MVA] = value; - } - else if (crm == 7) - { - if (opcode_2 == 0) - cpu->CP15[CP15_INVALIDATE_UTLB] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_INVALIDATE_UTLB_SINGLE_ENTRY] = value; - else if (opcode_2 == 2) - cpu->CP15[CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH] = value; - else if (opcode_2 == 3) - cpu->CP15[CP15_INVALIDATE_UTLB_ENTRY_ON_MVA] = value; - } - } - else if (crn == 9 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) - { - cpu->CP15[CP15_DATA_CACHE_LOCKDOWN] = value; - } - else if (crn == 10 && opcode_1 == 0) - { - if (crm == 0 && opcode_2 == 0) - { - cpu->CP15[CP15_TLB_LOCKDOWN] = value; - } - else if (crm == 2) - { - if (opcode_2 == 0) - cpu->CP15[CP15_PRIMARY_REGION_REMAP] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_NORMAL_REGION_REMAP] = value; - } - } - else if (crn == 13 && opcode_1 == 0 && crm == 0) - { - if (opcode_2 == 0) - cpu->CP15[CP15_PID] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_CONTEXT_ID] = value; - else if (opcode_2 == 3) - cpu->CP15[CP15_THREAD_URO] = value; - else if (opcode_2 == 4) - cpu->CP15[CP15_THREAD_PRW] = value; - } - else if (crn == 15) - { - if (opcode_1 == 0 && crm == 12) - { - if (opcode_2 == 0) - cpu->CP15[CP15_PERFORMANCE_MONITOR_CONTROL] = value; - else if (opcode_2 == 1) - cpu->CP15[CP15_CYCLE_COUNTER] = value; - else if (opcode_2 == 2) - cpu->CP15[CP15_COUNT_0] = value; - else if (opcode_2 == 3) - cpu->CP15[CP15_COUNT_1] = value; - } - else if (opcode_1 == 5) - { - if (crm == 4) - { - if (opcode_2 == 2) - cpu->CP15[CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY] = value; - else if (opcode_2 == 4) - cpu->CP15[CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY] = value; - } - else if (crm == 5 && opcode_2 == 2) - { - cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS] = value; - } - else if (crm == 6 && opcode_2 == 2) - { - cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS] = value; - } - else if (crm == 7 && opcode_2 == 2) - { - cpu->CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE] = value; - } - } - else if (opcode_1 == 7 && crm == 1 && opcode_2 == 0) - { - cpu->CP15[CP15_TLB_DEBUG_CONTROL] = value; - } - } - } - - // Unprivileged registers - if (crn == 7 && opcode_1 == 0 && crm == 5 && opcode_2 == 4) - { - cpu->CP15[CP15_FLUSH_PREFETCH_BUFFER] = value; - } - else if (crn == 7 && opcode_1 == 0 && crm == 10) - { - if (opcode_2 == 4) - cpu->CP15[CP15_DATA_SYNC_BARRIER] = value; - else if (opcode_2 == 5) - cpu->CP15[CP15_DATA_MEMORY_BARRIER] = value; - - } - else if (crn == 13 && opcode_1 == 0 && crm == 0 && opcode_2 == 2) - { - cpu->CP15[CP15_THREAD_UPRW] = value; - } -} diff --git a/src/core/arm/skyeye_common/arm_regformat.h b/src/core/arm/skyeye_common/arm_regformat.h index a92effbb4d..d1c721809d 100644 --- a/src/core/arm/skyeye_common/arm_regformat.h +++ b/src/core/arm/skyeye_common/arm_regformat.h @@ -59,6 +59,8 @@ enum { VFP_FPSID, VFP_FPSCR, VFP_FPEXC, + VFP_FPINST, + VFP_FPINST2, VFP_MVFR0, VFP_MVFR1, diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h deleted file mode 100644 index d2c901100a..0000000000 --- a/src/core/arm/skyeye_common/armdefs.h +++ /dev/null @@ -1,318 +0,0 @@ -/* armdefs.h -- ARMulator common definitions: ARM6 Instruction Emulator. - Copyright (C) 1994 Advanced RISC Machines Ltd. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -#pragma once - -#include <unordered_map> - -#include "common/common_types.h" -#include "core/arm/skyeye_common/arm_regformat.h" - -#define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1)) -#define BIT(s, n) ((s >> (n)) & 1) - -// Signal levels -enum { - LOW = 0, - HIGH = 1, - LOWHIGH = 1, - HIGHLOW = 2 -}; - -// Cache types -enum { - NONCACHE = 0, - DATACACHE = 1, - INSTCACHE = 2, -}; - -// Abort models -enum { - ABORT_BASE_RESTORED = 0, - ABORT_EARLY = 1, - ABORT_BASE_UPDATED = 2 -}; - -#define POS(i) ( (~(i)) >> 31 ) -#define NEG(i) ( (i) >> 31 ) - -typedef u64 ARMdword; // must be 64 bits wide -typedef u32 ARMword; // must be 32 bits wide -typedef u16 ARMhword; // must be 16 bits wide -typedef u8 ARMbyte; // must be 8 bits wide - -#define VFP_REG_NUM 64 -struct ARMul_State -{ - ARMword Emulate; // To start and stop emulation - - // Order of the following register should not be modified - ARMword Reg[16]; // The current register file - ARMword Cpsr; // The current PSR - ARMword Spsr_copy; - ARMword phys_pc; - ARMword Reg_usr[2]; - ARMword Reg_svc[2]; // R13_SVC R14_SVC - ARMword Reg_abort[2]; // R13_ABORT R14_ABORT - ARMword Reg_undef[2]; // R13 UNDEF R14 UNDEF - ARMword Reg_irq[2]; // R13_IRQ R14_IRQ - ARMword Reg_firq[7]; // R8---R14 FIRQ - ARMword Spsr[7]; // The exception psr's - ARMword Mode; // The current mode - ARMword Bank; // The current register bank - ARMword exclusive_tag; // The address for which the local monitor is in exclusive access mode - ARMword exclusive_state; - ARMword exclusive_result; - ARMword CP15[CP15_REGISTER_COUNT]; - - // FPSID, FPSCR, and FPEXC - ARMword VFP[VFP_SYSTEM_REGISTER_COUNT]; - // VFPv2 and VFPv3-D16 has 16 doubleword registers (D0-D16 or S0-S31). - // VFPv3-D32/ASIMD may have up to 32 doubleword registers (D0-D31), - // and only 32 singleword registers are accessible (S0-S31). - ARMword ExtReg[VFP_REG_NUM]; - /* ---- End of the ordered registers ---- */ - - ARMword NFlag, ZFlag, CFlag, VFlag, IFFlags; // Dummy flags for speed - unsigned int shifter_carry_out; - - // Add armv6 flags dyf:2010-08-09 - ARMword GEFlag, EFlag, AFlag, QFlag; - - ARMword TFlag; // Thumb state - - unsigned long long NumInstrs; // The number of instructions executed - unsigned NumInstrsToExecute; - - unsigned NresetSig; // Reset the processor - unsigned NfiqSig; - unsigned NirqSig; - - unsigned abortSig; - unsigned NtransSig; - unsigned bigendSig; - unsigned syscallSig; - -/* 2004-05-09 chy ----------------------------------------------------------- -read ARM Architecture Reference Manual -2.6.5 Data Abort -There are three Abort Model in ARM arch. - -Early Abort Model: used in some ARMv3 and earlier implementations. In this -model, base register wirteback occurred for LDC,LDM,STC,STM instructions, and -the base register was unchanged for all other instructions. (oldest) - -Base Restored Abort Model: If a Data Abort occurs in an instruction which -specifies base register writeback, the value in the base register is -unchanged. (strongarm, xscale) - -Base Updated Abort Model: If a Data Abort occurs in an instruction which -specifies base register writeback, the base register writeback still occurs. -(arm720T) - -read PART B -chap2 The System Control Coprocessor CP15 -2.4 Register1:control register -L(bit 6): in some ARMv3 and earlier implementations, the abort model of the -processor could be configured: -0=early Abort Model Selected(now obsolete) -1=Late Abort Model selceted(same as Base Updated Abort Model) - -on later processors, this bit reads as 1 and ignores writes. -------------------------------------------------------------- -So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model) - if lateabtSig=0, then it means Base Restored Abort Model -*/ - unsigned lateabtSig; - - // For differentiating ARM core emulaiton. - bool is_v4; // Are we emulating a v4 architecture (or higher)? - bool is_v5; // Are we emulating a v5 architecture? - bool is_v5e; // Are we emulating a v5e architecture? - bool is_v6; // Are we emulating a v6 architecture? - bool is_v7; // Are we emulating a v7 architecture? - - // ARM_ARM A2-18 - // 0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model - int abort_model; - - // TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per - // process for our purposes), not per ARMul_State (which tracks CPU core state). - std::unordered_map<u32, int> instruction_cache; -}; - -/***************************************************************************\ -* Types of ARM we know about * -\***************************************************************************/ - -enum { - ARM_v4_Prop = 0x01, - ARM_v5_Prop = 0x02, - ARM_v5e_Prop = 0x04, - ARM_v6_Prop = 0x08, - ARM_v7_Prop = 0x10, -}; - -/***************************************************************************\ -* The hardware vector addresses * -\***************************************************************************/ - -enum { - ARMResetV = 0, - ARMUndefinedInstrV = 4, - ARMSWIV = 8, - ARMPrefetchAbortV = 12, - ARMDataAbortV = 16, - ARMAddrExceptnV = 20, - ARMIRQV = 24, - ARMFIQV = 28, - ARMErrorV = 32, // This is an offset, not an address! - - ARMul_ResetV = ARMResetV, - ARMul_UndefinedInstrV = ARMUndefinedInstrV, - ARMul_SWIV = ARMSWIV, - ARMul_PrefetchAbortV = ARMPrefetchAbortV, - ARMul_DataAbortV = ARMDataAbortV, - ARMul_AddrExceptnV = ARMAddrExceptnV, - ARMul_IRQV = ARMIRQV, - ARMul_FIQV = ARMFIQV -}; - -/***************************************************************************\ -* Mode and Bank Constants * -\***************************************************************************/ - -enum PrivilegeMode { - USER32MODE = 16, - FIQ32MODE = 17, - IRQ32MODE = 18, - SVC32MODE = 19, - ABORT32MODE = 23, - UNDEF32MODE = 27, - SYSTEM32MODE = 31 -}; - -enum { - USERBANK = 0, - FIQBANK = 1, - IRQBANK = 2, - SVCBANK = 3, - ABORTBANK = 4, - UNDEFBANK = 5, - DUMMYBANK = 6, - SYSTEMBANK = 7 -}; - -/***************************************************************************\ -* Definitons of things in the emulator * -\***************************************************************************/ -extern void ARMul_Reset(ARMul_State* state); -extern ARMul_State* ARMul_NewState(ARMul_State* state); - -/***************************************************************************\ -* Definitons of things in the co-processor interface * -\***************************************************************************/ - -enum { - ARMul_FIRST = 0, - ARMul_TRANSFER = 1, - ARMul_BUSY = 2, - ARMul_DATA = 3, - ARMul_INTERRUPT = 4, - ARMul_DONE = 0, - ARMul_CANT = 1, - ARMul_INC = 3 -}; - -/***************************************************************************\ -* Definitons of things in the host environment * -\***************************************************************************/ - -enum ConditionCode { - EQ = 0, - NE = 1, - CS = 2, - CC = 3, - MI = 4, - PL = 5, - VS = 6, - VC = 7, - HI = 8, - LS = 9, - GE = 10, - LT = 11, - GT = 12, - LE = 13, - AL = 14, - NV = 15, -}; - -// Flags for use with the APSR. -enum : u32 { - NBIT = (1U << 31U), - ZBIT = (1 << 30), - CBIT = (1 << 29), - VBIT = (1 << 28), - QBIT = (1 << 27), - JBIT = (1 << 24), - EBIT = (1 << 9), - ABIT = (1 << 8), - IBIT = (1 << 7), - FBIT = (1 << 6), - TBIT = (1 << 5), - - // Masks for groups of bits in the APSR. - MODEBITS = 0x1F, - INTBITS = 0x1C0, -}; - -// Values for Emulate. -enum { - STOP = 0, // Stop - CHANGEMODE = 1, // Change mode - ONCE = 2, // Execute just one iteration - RUN = 3 // Continuous execution -}; - - -extern bool AddOverflow(ARMword, ARMword, ARMword); -extern bool SubOverflow(ARMword, ARMword, ARMword); - -extern void ARMul_SelectProcessor(ARMul_State*, unsigned); - -extern u32 AddWithCarry(u32, u32, u32, bool*, bool*); -extern bool ARMul_AddOverflowQ(ARMword, ARMword); - -extern u8 ARMul_SignedSaturatedAdd8(u8, u8); -extern u8 ARMul_SignedSaturatedSub8(u8, u8); -extern u16 ARMul_SignedSaturatedAdd16(u16, u16); -extern u16 ARMul_SignedSaturatedSub16(u16, u16); - -extern u8 ARMul_UnsignedSaturatedAdd8(u8, u8); -extern u16 ARMul_UnsignedSaturatedAdd16(u16, u16); -extern u8 ARMul_UnsignedSaturatedSub8(u8, u8); -extern u16 ARMul_UnsignedSaturatedSub16(u16, u16); -extern u8 ARMul_UnsignedAbsoluteDifference(u8, u8); -extern u32 ARMul_SignedSatQ(s32, u8, bool*); -extern u32 ARMul_UnsignedSatQ(s32, u8, bool*); - -extern bool InBigEndianMode(ARMul_State*); -extern bool InAPrivilegedMode(ARMul_State*); - -extern u32 ReadCP15Register(ARMul_State* cpu, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2); -extern void WriteCP15Register(ARMul_State* cpu, u32 value, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2); diff --git a/src/core/arm/skyeye_common/armmmu.h b/src/core/arm/skyeye_common/armmmu.h deleted file mode 100644 index c67d7209b7..0000000000 --- a/src/core/arm/skyeye_common/armmmu.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - armmmu.c - Memory Management Unit emulation. - ARMulator extensions for the ARM7100 family. - Copyright (C) 1999 Ben Williamson - - 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 -*/ - -#pragma once - -#include "common/swap.h" - -#include "core/memory.h" -#include "core/arm/skyeye_common/armdefs.h" - -// Register numbers in the MMU -enum -{ - MMU_ID = 0, - MMU_CONTROL = 1, - MMU_TRANSLATION_TABLE_BASE = 2, - MMU_DOMAIN_ACCESS_CONTROL = 3, - MMU_FAULT_STATUS = 5, - MMU_FAULT_ADDRESS = 6, - MMU_CACHE_OPS = 7, - MMU_TLB_OPS = 8, - MMU_CACHE_LOCKDOWN = 9, - MMU_TLB_LOCKDOWN = 10, - MMU_PID = 13, - - // MMU_V4 - MMU_V4_CACHE_OPS = 7, - MMU_V4_TLB_OPS = 8, - - // MMU_V3 - MMU_V3_FLUSH_TLB = 5, - MMU_V3_FLUSH_TLB_ENTRY = 6, - MMU_V3_FLUSH_CACHE = 7, -}; - -// Reads data in big/little endian format based on the -// state of the E (endian) bit in the emulated CPU's APSR. -inline u16 ReadMemory16(ARMul_State* cpu, u32 address) { - u16 data = Memory::Read16(address); - - if (InBigEndianMode(cpu)) - data = Common::swap16(data); - - return data; -} - -inline u32 ReadMemory32(ARMul_State* cpu, u32 address) { - u32 data = Memory::Read32(address); - - if (InBigEndianMode(cpu)) - data = Common::swap32(data); - - return data; -} - -inline u64 ReadMemory64(ARMul_State* cpu, u32 address) { - u64 data = Memory::Read64(address); - - if (InBigEndianMode(cpu)) - data = Common::swap64(data); - - return data; -} - -// Writes data in big/little endian format based on the -// state of the E (endian) bit in the emulated CPU's APSR. -inline void WriteMemory16(ARMul_State* cpu, u32 address, u16 data) { - if (InBigEndianMode(cpu)) - data = Common::swap16(data); - - Memory::Write16(address, data); -} - -inline void WriteMemory32(ARMul_State* cpu, u32 address, u32 data) { - if (InBigEndianMode(cpu)) - data = Common::swap32(data); - - Memory::Write32(address, data); -} - -inline void WriteMemory64(ARMul_State* cpu, u32 address, u64 data) { - if (InBigEndianMode(cpu)) - data = Common::swap64(data); - - Memory::Write64(address, data); -} diff --git a/src/core/arm/skyeye_common/armstate.cpp b/src/core/arm/skyeye_common/armstate.cpp new file mode 100644 index 0000000000..ccb2eb0ebe --- /dev/null +++ b/src/core/arm/skyeye_common/armstate.cpp @@ -0,0 +1,657 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/swap.h" +#include "common/logging/log.h" +#include "core/mem_map.h" +#include "core/memory.h" +#include "core/arm/skyeye_common/armstate.h" +#include "core/arm/skyeye_common/vfp/vfp.h" + +ARMul_State::ARMul_State(PrivilegeMode initial_mode) +{ + Reset(); + ChangePrivilegeMode(initial_mode); +} + +void ARMul_State::ChangePrivilegeMode(u32 new_mode) +{ + if (Mode == new_mode) + return; + + if (new_mode != USERBANK) { + switch (Mode) { + case SYSTEM32MODE: // Shares registers with user mode + case USER32MODE: + Reg_usr[0] = Reg[13]; + Reg_usr[1] = Reg[14]; + break; + case IRQ32MODE: + Reg_irq[0] = Reg[13]; + Reg_irq[1] = Reg[14]; + Spsr[IRQBANK] = Spsr_copy; + break; + case SVC32MODE: + Reg_svc[0] = Reg[13]; + Reg_svc[1] = Reg[14]; + Spsr[SVCBANK] = Spsr_copy; + break; + case ABORT32MODE: + Reg_abort[0] = Reg[13]; + Reg_abort[1] = Reg[14]; + Spsr[ABORTBANK] = Spsr_copy; + break; + case UNDEF32MODE: + Reg_undef[0] = Reg[13]; + Reg_undef[1] = Reg[14]; + Spsr[UNDEFBANK] = Spsr_copy; + break; + case FIQ32MODE: + Reg_firq[0] = Reg[13]; + Reg_firq[1] = Reg[14]; + Spsr[FIQBANK] = Spsr_copy; + break; + } + + switch (new_mode) { + case USER32MODE: + Reg[13] = Reg_usr[0]; + Reg[14] = Reg_usr[1]; + Bank = USERBANK; + break; + case IRQ32MODE: + Reg[13] = Reg_irq[0]; + Reg[14] = Reg_irq[1]; + Spsr_copy = Spsr[IRQBANK]; + Bank = IRQBANK; + break; + case SVC32MODE: + Reg[13] = Reg_svc[0]; + Reg[14] = Reg_svc[1]; + Spsr_copy = Spsr[SVCBANK]; + Bank = SVCBANK; + break; + case ABORT32MODE: + Reg[13] = Reg_abort[0]; + Reg[14] = Reg_abort[1]; + Spsr_copy = Spsr[ABORTBANK]; + Bank = ABORTBANK; + break; + case UNDEF32MODE: + Reg[13] = Reg_undef[0]; + Reg[14] = Reg_undef[1]; + Spsr_copy = Spsr[UNDEFBANK]; + Bank = UNDEFBANK; + break; + case FIQ32MODE: + Reg[13] = Reg_firq[0]; + Reg[14] = Reg_firq[1]; + Spsr_copy = Spsr[FIQBANK]; + Bank = FIQBANK; + break; + case SYSTEM32MODE: // Shares registers with user mode. + Reg[13] = Reg_usr[0]; + Reg[14] = Reg_usr[1]; + Bank = SYSTEMBANK; + break; + } + + // Set the mode bits in the APSR + Cpsr = (Cpsr & ~Mode) | new_mode; + Mode = new_mode; + } +} + +// Performs a reset +void ARMul_State::Reset() +{ + VFPInit(this); + + // Set stack pointer to the top of the stack + Reg[13] = 0x10000000; + Reg[15] = 0; + + Cpsr = INTBITS | SVC32MODE; + Mode = SVC32MODE; + Bank = SVCBANK; + + ResetMPCoreCP15Registers(); + + NresetSig = HIGH; + NfiqSig = HIGH; + NirqSig = HIGH; + NtransSig = (Mode & 3) ? HIGH : LOW; + abortSig = LOW; + + NumInstrs = 0; + Emulate = RUN; +} + +// Resets certain MPCore CP15 values to their ARM-defined reset values. +void ARMul_State::ResetMPCoreCP15Registers() +{ + // c0 + CP15[CP15_MAIN_ID] = 0x410FB024; + CP15[CP15_TLB_TYPE] = 0x00000800; + CP15[CP15_PROCESSOR_FEATURE_0] = 0x00000111; + CP15[CP15_PROCESSOR_FEATURE_1] = 0x00000001; + CP15[CP15_DEBUG_FEATURE_0] = 0x00000002; + CP15[CP15_MEMORY_MODEL_FEATURE_0] = 0x01100103; + CP15[CP15_MEMORY_MODEL_FEATURE_1] = 0x10020302; + CP15[CP15_MEMORY_MODEL_FEATURE_2] = 0x01222000; + CP15[CP15_MEMORY_MODEL_FEATURE_3] = 0x00000000; + CP15[CP15_ISA_FEATURE_0] = 0x00100011; + CP15[CP15_ISA_FEATURE_1] = 0x12002111; + CP15[CP15_ISA_FEATURE_2] = 0x11221011; + CP15[CP15_ISA_FEATURE_3] = 0x01102131; + CP15[CP15_ISA_FEATURE_4] = 0x00000141; + + // c1 + CP15[CP15_CONTROL] = 0x00054078; + CP15[CP15_AUXILIARY_CONTROL] = 0x0000000F; + CP15[CP15_COPROCESSOR_ACCESS_CONTROL] = 0x00000000; + + // c2 + CP15[CP15_TRANSLATION_BASE_TABLE_0] = 0x00000000; + CP15[CP15_TRANSLATION_BASE_TABLE_1] = 0x00000000; + CP15[CP15_TRANSLATION_BASE_CONTROL] = 0x00000000; + + // c3 + CP15[CP15_DOMAIN_ACCESS_CONTROL] = 0x00000000; + + // c7 + CP15[CP15_PHYS_ADDRESS] = 0x00000000; + + // c9 + CP15[CP15_DATA_CACHE_LOCKDOWN] = 0xFFFFFFF0; + + // c10 + CP15[CP15_TLB_LOCKDOWN] = 0x00000000; + CP15[CP15_PRIMARY_REGION_REMAP] = 0x00098AA4; + CP15[CP15_NORMAL_REGION_REMAP] = 0x44E048E0; + + // c13 + CP15[CP15_PID] = 0x00000000; + CP15[CP15_CONTEXT_ID] = 0x00000000; + CP15[CP15_THREAD_UPRW] = 0x00000000; + CP15[CP15_THREAD_URO] = 0x00000000; + CP15[CP15_THREAD_PRW] = 0x00000000; + + // c15 + CP15[CP15_PERFORMANCE_MONITOR_CONTROL] = 0x00000000; + CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS] = 0x00000000; + CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS] = 0x00000000; + CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE] = 0x00000000; + CP15[CP15_TLB_DEBUG_CONTROL] = 0x00000000; +} + +u16 ARMul_State::ReadMemory16(u32 address) const +{ + u16 data = Memory::Read16(address); + + if (InBigEndianMode()) + data = Common::swap16(data); + + return data; +} + +u32 ARMul_State::ReadMemory32(u32 address) const +{ + u32 data = Memory::Read32(address); + + if (InBigEndianMode()) + data = Common::swap32(data); + + return data; +} + +u64 ARMul_State::ReadMemory64(u32 address) const +{ + u64 data = Memory::Read64(address); + + if (InBigEndianMode()) + data = Common::swap64(data); + + return data; +} + +void ARMul_State::WriteMemory16(u32 address, u16 data) +{ + if (InBigEndianMode()) + data = Common::swap16(data); + + Memory::Write16(address, data); +} + +void ARMul_State::WriteMemory32(u32 address, u32 data) +{ + if (InBigEndianMode()) + data = Common::swap32(data); + + Memory::Write32(address, data); +} + +void ARMul_State::WriteMemory64(u32 address, u64 data) +{ + if (InBigEndianMode()) + data = Common::swap64(data); + + Memory::Write64(address, data); +} + + +// Reads from the CP15 registers. Used with implementation of the MRC instruction. +// Note that since the 3DS does not have the hypervisor extensions, these registers +// are not implemented. +u32 ARMul_State::ReadCP15Register(u32 crn, u32 opcode_1, u32 crm, u32 opcode_2) const +{ + // Unprivileged registers + if (crn == 13 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 2) + return CP15[CP15_THREAD_UPRW]; + + if (opcode_2 == 3) + return CP15[CP15_THREAD_URO]; + } + + if (InAPrivilegedMode()) + { + if (crn == 0 && opcode_1 == 0) + { + if (crm == 0) + { + if (opcode_2 == 0) + return CP15[CP15_MAIN_ID]; + + if (opcode_2 == 1) + return CP15[CP15_CACHE_TYPE]; + + if (opcode_2 == 3) + return CP15[CP15_TLB_TYPE]; + + if (opcode_2 == 5) + return CP15[CP15_CPU_ID]; + } + else if (crm == 1) + { + if (opcode_2 == 0) + return CP15[CP15_PROCESSOR_FEATURE_0]; + + if (opcode_2 == 1) + return CP15[CP15_PROCESSOR_FEATURE_1]; + + if (opcode_2 == 2) + return CP15[CP15_DEBUG_FEATURE_0]; + + if (opcode_2 == 4) + return CP15[CP15_MEMORY_MODEL_FEATURE_0]; + + if (opcode_2 == 5) + return CP15[CP15_MEMORY_MODEL_FEATURE_1]; + + if (opcode_2 == 6) + return CP15[CP15_MEMORY_MODEL_FEATURE_2]; + + if (opcode_2 == 7) + return CP15[CP15_MEMORY_MODEL_FEATURE_3]; + } + else if (crm == 2) + { + if (opcode_2 == 0) + return CP15[CP15_ISA_FEATURE_0]; + + if (opcode_2 == 1) + return CP15[CP15_ISA_FEATURE_1]; + + if (opcode_2 == 2) + return CP15[CP15_ISA_FEATURE_2]; + + if (opcode_2 == 3) + return CP15[CP15_ISA_FEATURE_3]; + + if (opcode_2 == 4) + return CP15[CP15_ISA_FEATURE_4]; + } + } + + if (crn == 1 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + return CP15[CP15_CONTROL]; + + if (opcode_2 == 1) + return CP15[CP15_AUXILIARY_CONTROL]; + + if (opcode_2 == 2) + return CP15[CP15_COPROCESSOR_ACCESS_CONTROL]; + } + + if (crn == 2 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + return CP15[CP15_TRANSLATION_BASE_TABLE_0]; + + if (opcode_2 == 1) + return CP15[CP15_TRANSLATION_BASE_TABLE_1]; + + if (opcode_2 == 2) + return CP15[CP15_TRANSLATION_BASE_CONTROL]; + } + + if (crn == 3 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) + return CP15[CP15_DOMAIN_ACCESS_CONTROL]; + + if (crn == 5 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + return CP15[CP15_FAULT_STATUS]; + + if (opcode_2 == 1) + return CP15[CP15_INSTR_FAULT_STATUS]; + } + + if (crn == 6 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + return CP15[CP15_FAULT_ADDRESS]; + + if (opcode_2 == 1) + return CP15[CP15_WFAR]; + } + + if (crn == 7 && opcode_1 == 0 && crm == 4 && opcode_2 == 0) + return CP15[CP15_PHYS_ADDRESS]; + + if (crn == 9 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) + return CP15[CP15_DATA_CACHE_LOCKDOWN]; + + if (crn == 10 && opcode_1 == 0) + { + if (crm == 0 && opcode_2 == 0) + return CP15[CP15_TLB_LOCKDOWN]; + + if (crm == 2) + { + if (opcode_2 == 0) + return CP15[CP15_PRIMARY_REGION_REMAP]; + + if (opcode_2 == 1) + return CP15[CP15_NORMAL_REGION_REMAP]; + } + } + + if (crn == 13 && crm == 0) + { + if (opcode_2 == 0) + return CP15[CP15_PID]; + + if (opcode_2 == 1) + return CP15[CP15_CONTEXT_ID]; + + if (opcode_2 == 4) + return CP15[CP15_THREAD_PRW]; + } + + if (crn == 15) + { + if (opcode_1 == 0 && crm == 12) + { + if (opcode_2 == 0) + return CP15[CP15_PERFORMANCE_MONITOR_CONTROL]; + + if (opcode_2 == 1) + return CP15[CP15_CYCLE_COUNTER]; + + if (opcode_2 == 2) + return CP15[CP15_COUNT_0]; + + if (opcode_2 == 3) + return CP15[CP15_COUNT_1]; + } + + if (opcode_1 == 5 && opcode_2 == 2) + { + if (crm == 5) + return CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS]; + + if (crm == 6) + return CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS]; + + if (crm == 7) + return CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE]; + } + + if (opcode_1 == 7 && crm == 1 && opcode_2 == 0) + return CP15[CP15_TLB_DEBUG_CONTROL]; + } + } + + LOG_ERROR(Core_ARM11, "MRC CRn=%u, CRm=%u, OP1=%u OP2=%u is not implemented. Returning zero.", crn, crm, opcode_1, opcode_2); + return 0; +} + +// Write to the CP15 registers. Used with implementation of the MCR instruction. +// Note that since the 3DS does not have the hypervisor extensions, these registers +// are not implemented. +void ARMul_State::WriteCP15Register(u32 value, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2) +{ + if (InAPrivilegedMode()) + { + if (crn == 1 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + CP15[CP15_CONTROL] = value; + else if (opcode_2 == 1) + CP15[CP15_AUXILIARY_CONTROL] = value; + else if (opcode_2 == 2) + CP15[CP15_COPROCESSOR_ACCESS_CONTROL] = value; + } + else if (crn == 2 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + CP15[CP15_TRANSLATION_BASE_TABLE_0] = value; + else if (opcode_2 == 1) + CP15[CP15_TRANSLATION_BASE_TABLE_1] = value; + else if (opcode_2 == 2) + CP15[CP15_TRANSLATION_BASE_CONTROL] = value; + } + else if (crn == 3 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) + { + CP15[CP15_DOMAIN_ACCESS_CONTROL] = value; + } + else if (crn == 5 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + CP15[CP15_FAULT_STATUS] = value; + else if (opcode_2 == 1) + CP15[CP15_INSTR_FAULT_STATUS] = value; + } + else if (crn == 6 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + CP15[CP15_FAULT_ADDRESS] = value; + else if (opcode_2 == 1) + CP15[CP15_WFAR] = value; + } + else if (crn == 7 && opcode_1 == 0) + { + if (crm == 0 && opcode_2 == 4) + { + CP15[CP15_WAIT_FOR_INTERRUPT] = value; + } + else if (crm == 4 && opcode_2 == 0) + { + // NOTE: Not entirely accurate. This should do permission checks. + CP15[CP15_PHYS_ADDRESS] = Memory::VirtualToPhysicalAddress(value); + } + else if (crm == 5) + { + if (opcode_2 == 0) + CP15[CP15_INVALIDATE_INSTR_CACHE] = value; + else if (opcode_2 == 1) + CP15[CP15_INVALIDATE_INSTR_CACHE_USING_MVA] = value; + else if (opcode_2 == 2) + CP15[CP15_INVALIDATE_INSTR_CACHE_USING_INDEX] = value; + else if (opcode_2 == 6) + CP15[CP15_FLUSH_BRANCH_TARGET_CACHE] = value; + else if (opcode_2 == 7) + CP15[CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY] = value; + } + else if (crm == 6) + { + if (opcode_2 == 0) + CP15[CP15_INVALIDATE_DATA_CACHE] = value; + else if (opcode_2 == 1) + CP15[CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA] = value; + else if (opcode_2 == 2) + CP15[CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX] = value; + } + else if (crm == 7 && opcode_2 == 0) + { + CP15[CP15_INVALIDATE_DATA_AND_INSTR_CACHE] = value; + } + else if (crm == 10) + { + if (opcode_2 == 0) + CP15[CP15_CLEAN_DATA_CACHE] = value; + else if (opcode_2 == 1) + CP15[CP15_CLEAN_DATA_CACHE_LINE_USING_MVA] = value; + else if (opcode_2 == 2) + CP15[CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX] = value; + } + else if (crm == 14) + { + if (opcode_2 == 0) + CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE] = value; + else if (opcode_2 == 1) + CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA] = value; + else if (opcode_2 == 2) + CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX] = value; + } + } + else if (crn == 8 && opcode_1 == 0) + { + if (crm == 5) + { + if (opcode_2 == 0) + CP15[CP15_INVALIDATE_ITLB] = value; + else if (opcode_2 == 1) + CP15[CP15_INVALIDATE_ITLB_SINGLE_ENTRY] = value; + else if (opcode_2 == 2) + CP15[CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH] = value; + else if (opcode_2 == 3) + CP15[CP15_INVALIDATE_ITLB_ENTRY_ON_MVA] = value; + } + else if (crm == 6) + { + if (opcode_2 == 0) + CP15[CP15_INVALIDATE_DTLB] = value; + else if (opcode_2 == 1) + CP15[CP15_INVALIDATE_DTLB_SINGLE_ENTRY] = value; + else if (opcode_2 == 2) + CP15[CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH] = value; + else if (opcode_2 == 3) + CP15[CP15_INVALIDATE_DTLB_ENTRY_ON_MVA] = value; + } + else if (crm == 7) + { + if (opcode_2 == 0) + CP15[CP15_INVALIDATE_UTLB] = value; + else if (opcode_2 == 1) + CP15[CP15_INVALIDATE_UTLB_SINGLE_ENTRY] = value; + else if (opcode_2 == 2) + CP15[CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH] = value; + else if (opcode_2 == 3) + CP15[CP15_INVALIDATE_UTLB_ENTRY_ON_MVA] = value; + } + } + else if (crn == 9 && opcode_1 == 0 && crm == 0 && opcode_2 == 0) + { + CP15[CP15_DATA_CACHE_LOCKDOWN] = value; + } + else if (crn == 10 && opcode_1 == 0) + { + if (crm == 0 && opcode_2 == 0) + { + CP15[CP15_TLB_LOCKDOWN] = value; + } + else if (crm == 2) + { + if (opcode_2 == 0) + CP15[CP15_PRIMARY_REGION_REMAP] = value; + else if (opcode_2 == 1) + CP15[CP15_NORMAL_REGION_REMAP] = value; + } + } + else if (crn == 13 && opcode_1 == 0 && crm == 0) + { + if (opcode_2 == 0) + CP15[CP15_PID] = value; + else if (opcode_2 == 1) + CP15[CP15_CONTEXT_ID] = value; + else if (opcode_2 == 3) + CP15[CP15_THREAD_URO] = value; + else if (opcode_2 == 4) + CP15[CP15_THREAD_PRW] = value; + } + else if (crn == 15) + { + if (opcode_1 == 0 && crm == 12) + { + if (opcode_2 == 0) + CP15[CP15_PERFORMANCE_MONITOR_CONTROL] = value; + else if (opcode_2 == 1) + CP15[CP15_CYCLE_COUNTER] = value; + else if (opcode_2 == 2) + CP15[CP15_COUNT_0] = value; + else if (opcode_2 == 3) + CP15[CP15_COUNT_1] = value; + } + else if (opcode_1 == 5) + { + if (crm == 4) + { + if (opcode_2 == 2) + CP15[CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY] = value; + else if (opcode_2 == 4) + CP15[CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY] = value; + } + else if (crm == 5 && opcode_2 == 2) + { + CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS] = value; + } + else if (crm == 6 && opcode_2 == 2) + { + CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS] = value; + } + else if (crm == 7 && opcode_2 == 2) + { + CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE] = value; + } + } + else if (opcode_1 == 7 && crm == 1 && opcode_2 == 0) + { + CP15[CP15_TLB_DEBUG_CONTROL] = value; + } + } + } + + // Unprivileged registers + if (crn == 7 && opcode_1 == 0 && crm == 5 && opcode_2 == 4) + { + CP15[CP15_FLUSH_PREFETCH_BUFFER] = value; + } + else if (crn == 7 && opcode_1 == 0 && crm == 10) + { + if (opcode_2 == 4) + CP15[CP15_DATA_SYNC_BARRIER] = value; + else if (opcode_2 == 5) + CP15[CP15_DATA_MEMORY_BARRIER] = value; + } + else if (crn == 13 && opcode_1 == 0 && crm == 0 && opcode_2 == 2) + { + CP15[CP15_THREAD_UPRW] = value; + } +} diff --git a/src/core/arm/skyeye_common/armstate.h b/src/core/arm/skyeye_common/armstate.h new file mode 100644 index 0000000000..b364e26210 --- /dev/null +++ b/src/core/arm/skyeye_common/armstate.h @@ -0,0 +1,252 @@ +/* armdefs.h -- ARMulator common definitions: ARM6 Instruction Emulator. + Copyright (C) 1994 Advanced RISC Machines Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#pragma once + +#include <array> +#include <unordered_map> + +#include "common/common_types.h" +#include "core/arm/skyeye_common/arm_regformat.h" + +// Signal levels +enum { + LOW = 0, + HIGH = 1, + LOWHIGH = 1, + HIGHLOW = 2 +}; + +// Cache types +enum { + NONCACHE = 0, + DATACACHE = 1, + INSTCACHE = 2, +}; + +// ARM privilege modes +enum PrivilegeMode { + USER32MODE = 16, + FIQ32MODE = 17, + IRQ32MODE = 18, + SVC32MODE = 19, + ABORT32MODE = 23, + UNDEF32MODE = 27, + SYSTEM32MODE = 31 +}; + +// ARM privilege mode register banks +enum { + USERBANK = 0, + FIQBANK = 1, + IRQBANK = 2, + SVCBANK = 3, + ABORTBANK = 4, + UNDEFBANK = 5, + DUMMYBANK = 6, + SYSTEMBANK = 7 +}; + +// Hardware vector addresses +enum { + ARMResetV = 0, + ARMUndefinedInstrV = 4, + ARMSWIV = 8, + ARMPrefetchAbortV = 12, + ARMDataAbortV = 16, + ARMAddrExceptnV = 20, + ARMIRQV = 24, + ARMFIQV = 28, + ARMErrorV = 32, // This is an offset, not an address! + + ARMul_ResetV = ARMResetV, + ARMul_UndefinedInstrV = ARMUndefinedInstrV, + ARMul_SWIV = ARMSWIV, + ARMul_PrefetchAbortV = ARMPrefetchAbortV, + ARMul_DataAbortV = ARMDataAbortV, + ARMul_AddrExceptnV = ARMAddrExceptnV, + ARMul_IRQV = ARMIRQV, + ARMul_FIQV = ARMFIQV +}; + +// Coprocessor status values +enum { + ARMul_FIRST = 0, + ARMul_TRANSFER = 1, + ARMul_BUSY = 2, + ARMul_DATA = 3, + ARMul_INTERRUPT = 4, + ARMul_DONE = 0, + ARMul_CANT = 1, + ARMul_INC = 3 +}; + +// Instruction condition codes +enum ConditionCode { + EQ = 0, + NE = 1, + CS = 2, + CC = 3, + MI = 4, + PL = 5, + VS = 6, + VC = 7, + HI = 8, + LS = 9, + GE = 10, + LT = 11, + GT = 12, + LE = 13, + AL = 14, + NV = 15, +}; + +// Flags for use with the APSR. +enum : u32 { + NBIT = (1U << 31U), + ZBIT = (1 << 30), + CBIT = (1 << 29), + VBIT = (1 << 28), + QBIT = (1 << 27), + JBIT = (1 << 24), + EBIT = (1 << 9), + ABIT = (1 << 8), + IBIT = (1 << 7), + FBIT = (1 << 6), + TBIT = (1 << 5), + + // Masks for groups of bits in the APSR. + MODEBITS = 0x1F, + INTBITS = 0x1C0, +}; + +// Values for Emulate. +enum { + STOP = 0, // Stop + CHANGEMODE = 1, // Change mode + ONCE = 2, // Execute just one iteration + RUN = 3 // Continuous execution +}; + + +struct ARMul_State final +{ +public: + explicit ARMul_State(PrivilegeMode initial_mode); + + void ChangePrivilegeMode(u32 new_mode); + void Reset(); + + // Reads/writes data in big/little endian format based on the + // state of the E (endian) bit in the APSR. + u16 ReadMemory16(u32 address) const; + u32 ReadMemory32(u32 address) const; + u64 ReadMemory64(u32 address) const; + void WriteMemory16(u32 address, u16 data); + void WriteMemory32(u32 address, u32 data); + void WriteMemory64(u32 address, u64 data); + + u32 ReadCP15Register(u32 crn, u32 opcode_1, u32 crm, u32 opcode_2) const; + void WriteCP15Register(u32 value, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2); + + // Exclusive memory access functions + bool IsExclusiveMemoryAccess(u32 address) const { + return exclusive_state && exclusive_tag == (address & RESERVATION_GRANULE_MASK); + } + void SetExclusiveMemoryAddress(u32 address) { + exclusive_tag = address & RESERVATION_GRANULE_MASK; + exclusive_state = true; + } + void UnsetExclusiveMemoryAddress() { + exclusive_tag = 0xFFFFFFFF; + exclusive_state = false; + } + + // Whether or not the given CPU is in big endian mode (E bit is set) + bool InBigEndianMode() const { + return (Cpsr & (1 << 9)) != 0; + } + // Whether or not the given CPU is in a mode other than user mode. + bool InAPrivilegedMode() const { + return (Mode != USER32MODE); + } + // Note that for the 3DS, a Thumb instruction will only ever be + // two bytes in size. Thus we don't need to worry about ThumbEE + // or Thumb-2 where instructions can be 4 bytes in length. + u32 GetInstructionSize() const { + return TFlag ? 2 : 4; + } + + std::array<u32, 16> Reg; // The current register file + std::array<u32, 2> Reg_usr; + std::array<u32, 2> Reg_svc; // R13_SVC R14_SVC + std::array<u32, 2> Reg_abort; // R13_ABORT R14_ABORT + std::array<u32, 2> Reg_undef; // R13 UNDEF R14 UNDEF + std::array<u32, 2> Reg_irq; // R13_IRQ R14_IRQ + std::array<u32, 7> Reg_firq; // R8---R14 FIRQ + std::array<u32, 7> Spsr; // The exception psr's + std::array<u32, CP15_REGISTER_COUNT> CP15; + + // FPSID, FPSCR, and FPEXC + std::array<u32, VFP_SYSTEM_REGISTER_COUNT> VFP; + + // VFPv2 and VFPv3-D16 has 16 doubleword registers (D0-D16 or S0-S31). + // VFPv3-D32/ASIMD may have up to 32 doubleword registers (D0-D31), + // and only 32 singleword registers are accessible (S0-S31). + std::array<u32, 64> ExtReg; + + u32 Emulate; // To start and stop emulation + u32 Cpsr; // The current PSR + u32 Spsr_copy; + u32 phys_pc; + + u32 Mode; // The current mode + u32 Bank; // The current register bank + + u32 NFlag, ZFlag, CFlag, VFlag, IFFlags; // Dummy flags for speed + unsigned int shifter_carry_out; + + u32 TFlag; // Thumb state + + unsigned long long NumInstrs; // The number of instructions executed + unsigned NumInstrsToExecute; + + unsigned NresetSig; // Reset the processor + unsigned NfiqSig; + unsigned NirqSig; + + unsigned abortSig; + unsigned NtransSig; + unsigned bigendSig; + unsigned syscallSig; + + // TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per + // process for our purposes), not per ARMul_State (which tracks CPU core state). + std::unordered_map<u32, int> instruction_cache; + +private: + void ResetMPCoreCP15Registers(); + + // Defines a reservation granule of 2 words, which protects the first 2 words starting at the tag. + // This is the smallest granule allowed by the v7 spec, and is coincidentally just large enough to + // support LDR/STREXD. + static const u32 RESERVATION_GRANULE_MASK = 0xFFFFFFF8; + + u32 exclusive_tag; // The address for which the local monitor is in exclusive access mode + u32 exclusive_result; + bool exclusive_state; +}; diff --git a/src/core/arm/skyeye_common/armsupp.cpp b/src/core/arm/skyeye_common/armsupp.cpp new file mode 100644 index 0000000000..d31fb94490 --- /dev/null +++ b/src/core/arm/skyeye_common/armsupp.cpp @@ -0,0 +1,208 @@ +/* armsupp.c -- ARMulator support code: ARM6 Instruction Emulator. + Copyright (C) 1994 Advanced RISC Machines Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "common/logging/log.h" + +#include "core/mem_map.h" +#include "core/arm/skyeye_common/arm_regformat.h" +#include "core/arm/skyeye_common/armstate.h" +#include "core/arm/skyeye_common/armsupp.h" + +// Unsigned sum of absolute difference +u8 ARMul_UnsignedAbsoluteDifference(u8 left, u8 right) +{ + if (left > right) + return left - right; + + return right - left; +} + +// Add with carry, indicates if a carry-out or signed overflow occurred. +u32 AddWithCarry(u32 left, u32 right, u32 carry_in, bool* carry_out_occurred, bool* overflow_occurred) +{ + u64 unsigned_sum = (u64)left + (u64)right + (u64)carry_in; + s64 signed_sum = (s64)(s32)left + (s64)(s32)right + (s64)carry_in; + u64 result = (unsigned_sum & 0xFFFFFFFF); + + if (carry_out_occurred) + *carry_out_occurred = (result != unsigned_sum); + + if (overflow_occurred) + *overflow_occurred = ((s64)(s32)result != signed_sum); + + return (u32)result; +} + +// Compute whether an addition of A and B, giving RESULT, overflowed. +bool AddOverflow(u32 a, u32 b, u32 result) +{ + return ((NEG(a) && NEG(b) && POS(result)) || + (POS(a) && POS(b) && NEG(result))); +} + +// Compute whether a subtraction of A and B, giving RESULT, overflowed. +bool SubOverflow(u32 a, u32 b, u32 result) +{ + return ((NEG(a) && POS(b) && POS(result)) || + (POS(a) && NEG(b) && NEG(result))); +} + +// Returns true if the Q flag should be set as a result of overflow. +bool ARMul_AddOverflowQ(u32 a, u32 b) +{ + u32 result = a + b; + if (((result ^ a) & (u32)0x80000000) && ((a ^ b) & (u32)0x80000000) == 0) + return true; + + return false; +} + +// 8-bit signed saturated addition +u8 ARMul_SignedSaturatedAdd8(u8 left, u8 right) +{ + u8 result = left + right; + + if (((result ^ left) & 0x80) && ((left ^ right) & 0x80) == 0) { + if (left & 0x80) + result = 0x80; + else + result = 0x7F; + } + + return result; +} + +// 8-bit signed saturated subtraction +u8 ARMul_SignedSaturatedSub8(u8 left, u8 right) +{ + u8 result = left - right; + + if (((result ^ left) & 0x80) && ((left ^ right) & 0x80) != 0) { + if (left & 0x80) + result = 0x80; + else + result = 0x7F; + } + + return result; +} + +// 16-bit signed saturated addition +u16 ARMul_SignedSaturatedAdd16(u16 left, u16 right) +{ + u16 result = left + right; + + if (((result ^ left) & 0x8000) && ((left ^ right) & 0x8000) == 0) { + if (left & 0x8000) + result = 0x8000; + else + result = 0x7FFF; + } + + return result; +} + +// 16-bit signed saturated subtraction +u16 ARMul_SignedSaturatedSub16(u16 left, u16 right) +{ + u16 result = left - right; + + if (((result ^ left) & 0x8000) && ((left ^ right) & 0x8000) != 0) { + if (left & 0x8000) + result = 0x8000; + else + result = 0x7FFF; + } + + return result; +} + +// 8-bit unsigned saturated addition +u8 ARMul_UnsignedSaturatedAdd8(u8 left, u8 right) +{ + u8 result = left + right; + + if (result < left) + result = 0xFF; + + return result; +} + +// 16-bit unsigned saturated addition +u16 ARMul_UnsignedSaturatedAdd16(u16 left, u16 right) +{ + u16 result = left + right; + + if (result < left) + result = 0xFFFF; + + return result; +} + +// 8-bit unsigned saturated subtraction +u8 ARMul_UnsignedSaturatedSub8(u8 left, u8 right) +{ + if (left <= right) + return 0; + + return left - right; +} + +// 16-bit unsigned saturated subtraction +u16 ARMul_UnsignedSaturatedSub16(u16 left, u16 right) +{ + if (left <= right) + return 0; + + return left - right; +} + +// Signed saturation. +u32 ARMul_SignedSatQ(s32 value, u8 shift, bool* saturation_occurred) +{ + const u32 max = (1 << shift) - 1; + const s32 top = (value >> shift); + + if (top > 0) { + *saturation_occurred = true; + return max; + } + else if (top < -1) { + *saturation_occurred = true; + return ~max; + } + + *saturation_occurred = false; + return (u32)value; +} + +// Unsigned saturation +u32 ARMul_UnsignedSatQ(s32 value, u8 shift, bool* saturation_occurred) +{ + const u32 max = (1 << shift) - 1; + + if (value < 0) { + *saturation_occurred = true; + return 0; + } else if ((u32)value > max) { + *saturation_occurred = true; + return max; + } + + *saturation_occurred = false; + return (u32)value; +} diff --git a/src/core/arm/skyeye_common/armsupp.h b/src/core/arm/skyeye_common/armsupp.h new file mode 100644 index 0000000000..391309fa81 --- /dev/null +++ b/src/core/arm/skyeye_common/armsupp.h @@ -0,0 +1,32 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +#define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1)) +#define BIT(s, n) ((s >> (n)) & 1) + +#define POS(i) ( (~(i)) >> 31 ) +#define NEG(i) ( (i) >> 31 ) + +bool AddOverflow(u32, u32, u32); +bool SubOverflow(u32, u32, u32); + +u32 AddWithCarry(u32, u32, u32, bool*, bool*); +bool ARMul_AddOverflowQ(u32, u32); + +u8 ARMul_SignedSaturatedAdd8(u8, u8); +u8 ARMul_SignedSaturatedSub8(u8, u8); +u16 ARMul_SignedSaturatedAdd16(u16, u16); +u16 ARMul_SignedSaturatedSub16(u16, u16); + +u8 ARMul_UnsignedSaturatedAdd8(u8, u8); +u16 ARMul_UnsignedSaturatedAdd16(u16, u16); +u8 ARMul_UnsignedSaturatedSub8(u8, u8); +u16 ARMul_UnsignedSaturatedSub16(u16, u16); +u8 ARMul_UnsignedAbsoluteDifference(u8, u8); +u32 ARMul_SignedSatQ(s32, u8, bool*); +u32 ARMul_UnsignedSatQ(s32, u8, bool*); diff --git a/src/core/arm/skyeye_common/vfp/vfp.cpp b/src/core/arm/skyeye_common/vfp/vfp.cpp index 571d6c2f27..26f303de42 100644 --- a/src/core/arm/skyeye_common/vfp/vfp.cpp +++ b/src/core/arm/skyeye_common/vfp/vfp.cpp @@ -20,39 +20,30 @@ /* Note: this file handles interface with arm core and vfp registers */ +#include "common/common_funcs.h" #include "common/logging/log.h" -#include "core/arm/skyeye_common/armdefs.h" +#include "core/arm/skyeye_common/armstate.h" #include "core/arm/skyeye_common/vfp/asm_vfp.h" #include "core/arm/skyeye_common/vfp/vfp.h" -unsigned VFPInit(ARMul_State* state) +void VFPInit(ARMul_State* state) { state->VFP[VFP_FPSID] = VFP_FPSID_IMPLMEN<<24 | VFP_FPSID_SW<<23 | VFP_FPSID_SUBARCH<<16 | VFP_FPSID_PARTNUM<<8 | VFP_FPSID_VARIANT<<4 | VFP_FPSID_REVISION; state->VFP[VFP_FPEXC] = 0; state->VFP[VFP_FPSCR] = 0; + // ARM11 MPCore instruction register reset values. + state->VFP[VFP_FPINST] = 0xEE000A00; + state->VFP[VFP_FPINST2] = 0; + // ARM11 MPCore feature register values. state->VFP[VFP_MVFR0] = 0x11111111; state->VFP[VFP_MVFR1] = 0; - - return 0; -} - -void VMSR(ARMul_State* state, ARMword reg, ARMword Rt) -{ - if (reg == 1) - { - state->VFP[VFP_FPSCR] = state->Reg[Rt]; - } - else if (reg == 8) - { - state->VFP[VFP_FPEXC] = state->Reg[Rt]; - } } -void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value) +void VMOVBRS(ARMul_State* state, u32 to_arm, u32 t, u32 n, u32* value) { if (to_arm) { @@ -64,7 +55,7 @@ void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* } } -void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2) +void VMOVBRRD(ARMul_State* state, u32 to_arm, u32 t, u32 t2, u32 n, u32* value1, u32* value2) { if (to_arm) { @@ -77,7 +68,7 @@ void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword state->ExtReg[n*2] = *value1; } } -void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2) +void VMOVBRRSS(ARMul_State* state, u32 to_arm, u32 t, u32 t2, u32 n, u32* value1, u32* value2) { if (to_arm) { @@ -91,7 +82,7 @@ void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMwor } } -void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm) +void VMOVI(ARMul_State* state, u32 single, u32 d, u32 imm) { if (single) { @@ -104,7 +95,7 @@ void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm) state->ExtReg[d*2] = 0; } } -void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword m) +void VMOVR(ARMul_State* state, u32 single, u32 d, u32 m) { if (single) { @@ -153,9 +144,8 @@ void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpsc LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x\n", exceptions); if (exceptions == VFP_EXCEPTION_ERROR) { - LOG_TRACE(Core_ARM11, "unhandled bounce %x\n", inst); - exit(-1); - return; + LOG_CRITICAL(Core_ARM11, "unhandled bounce %x\n", inst); + Crash(); } /* diff --git a/src/core/arm/skyeye_common/vfp/vfp.h b/src/core/arm/skyeye_common/vfp/vfp.h index acefae9bbf..88908da9f3 100644 --- a/src/core/arm/skyeye_common/vfp/vfp.h +++ b/src/core/arm/skyeye_common/vfp/vfp.h @@ -26,7 +26,7 @@ #define CHECK_VFP_ENABLED #define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]); -unsigned VFPInit(ARMul_State* state); +void VFPInit(ARMul_State* state); s32 vfp_get_float(ARMul_State* state, u32 reg); void vfp_put_float(ARMul_State* state, s32 val, u32 reg); @@ -36,10 +36,8 @@ void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpsc u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr); u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr); -void VMSR(ARMul_State* state, ARMword reg, ARMword Rt); -void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value); -void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); -void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); -void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm); -void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword imm); - +void VMOVBRS(ARMul_State* state, u32 to_arm, u32 t, u32 n, u32* value); +void VMOVBRRD(ARMul_State* state, u32 to_arm, u32 t, u32 t2, u32 n, u32* value1, u32* value2); +void VMOVBRRSS(ARMul_State* state, u32 to_arm, u32 t, u32 t2, u32 n, u32* value1, u32* value2); +void VMOVI(ARMul_State* state, u32 single, u32 d, u32 imm); +void VMOVR(ARMul_State* state, u32 single, u32 d, u32 imm); diff --git a/src/core/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h index 2007d6dc4b..91a8d4d574 100644 --- a/src/core/arm/skyeye_common/vfp/vfp_helper.h +++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h @@ -34,7 +34,7 @@ #include <cstdio> #include "common/common_types.h" -#include "core/arm/skyeye_common/armdefs.h" +#include "core/arm/skyeye_common/armstate.h" #include "core/arm/skyeye_common/vfp/asm_vfp.h" #define do_div(n, base) {n/=base;} @@ -415,7 +415,7 @@ struct op { u32 flags; }; -static inline u32 fls(ARMword x) +static inline u32 fls(u32 x) { int r = 32; diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp index f91049585c..1d844a66e8 100644 --- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp @@ -70,9 +70,9 @@ static void vfp_double_dump(const char *str, struct vfp_double *d) static void vfp_double_normalise_denormal(struct vfp_double *vd) { - int bits = 31 - fls((ARMword)(vd->significand >> 32)); + int bits = 31 - fls((u32)(vd->significand >> 32)); if (bits == 31) - bits = 63 - fls((ARMword)vd->significand); + bits = 63 - fls((u32)vd->significand); vfp_double_dump("normalise_denormal: in", vd); @@ -109,9 +109,9 @@ u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd, exponent = vd->exponent; significand = vd->significand; - shift = 32 - fls((ARMword)(significand >> 32)); + shift = 32 - fls((u32)(significand >> 32)); if (shift == 32) - shift = 64 - fls((ARMword)significand); + shift = 64 - fls((u32)significand); if (shift) { exponent -= shift; significand <<= shift; @@ -566,7 +566,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32 /* * 2^0 <= m < 2^32-2^8 */ - d = (ARMword)((vdm.significand << 1) >> shift); + d = (u32)((vdm.significand << 1) >> shift); rem = vdm.significand << (65 - shift); if (rmode == FPSCR_ROUND_NEAREST) { @@ -647,7 +647,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32 int shift = 1023 + 63 - vdm.exponent; /* 58 */ u64 rem, incr = 0; - d = (ARMword)((vdm.significand << 1) >> shift); + d = (u32)((vdm.significand << 1) >> shift); rem = vdm.significand << (65 - shift); if (rmode == FPSCR_ROUND_NEAREST) { diff --git a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp index 67fe63aa49..9b99fc5bc0 100644 --- a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp @@ -51,7 +51,7 @@ VMLA_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmla_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -100,7 +100,7 @@ VMLS_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmls_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -149,7 +149,7 @@ VNMLA_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vnmla_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -199,7 +199,7 @@ VNMLS_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vnmls_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -248,7 +248,7 @@ VNMUL_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vnmul_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -297,7 +297,7 @@ VMUL_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmul_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -346,7 +346,7 @@ VADD_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vadd_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -395,7 +395,7 @@ VSUB_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vsub_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -444,7 +444,7 @@ VDIV_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vdiv_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -492,7 +492,7 @@ VMOVI_INST: VMOVI(cpu, inst_cream->single, inst_cream->d, inst_cream->imm); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmovi_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -536,7 +536,7 @@ VMOVR_INST: VMOVR(cpu, inst_cream->single, inst_cream->d, inst_cream->m); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmovr_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -585,7 +585,7 @@ VABS_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vabs_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -635,7 +635,7 @@ VNEG_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vneg_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -684,7 +684,7 @@ VSQRT_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vsqrt_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -733,7 +733,7 @@ VCMP_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vcmp_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -782,7 +782,7 @@ VCMP2_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vcmp2_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -831,7 +831,7 @@ VCVTBDS_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vcvtbds_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -882,7 +882,7 @@ VCVTBFF_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vcvtbff_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -931,7 +931,7 @@ VCVTBFI_INST: CHECK_VFP_CDP_RET; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vcvtbfi_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -981,7 +981,7 @@ VMOVBRS_INST: VMOVBRS(cpu, inst_cream->to_arm, inst_cream->t, inst_cream->n, &(cpu->Reg[inst_cream->t])); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmovbrs_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -995,7 +995,7 @@ VMOVBRS_INST: #ifdef VFP_INTERPRETER_STRUCT struct vmsr_inst { unsigned int reg; - unsigned int Rd; + unsigned int Rt; }; #endif #ifdef VFP_INTERPRETER_TRANS @@ -1009,7 +1009,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmsr)(unsigned int inst, int index) inst_base->br = NON_BRANCH; inst_cream->reg = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->Rt = BITS(inst, 12, 15); return inst_base; } @@ -1017,17 +1017,32 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmsr)(unsigned int inst, int index) #ifdef VFP_INTERPRETER_IMPL VMSR_INST: { - if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { + if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { /* FIXME: special case for access to FPSID and FPEXC, VFP must be disabled , and in privileged mode */ /* Exceptions must be checked, according to v7 ref manual */ CHECK_VFP_ENABLED; - vmsr_inst *inst_cream = (vmsr_inst *)inst_base->component; + vmsr_inst* const inst_cream = (vmsr_inst*)inst_base->component; + + unsigned int reg = inst_cream->reg; + unsigned int rt = inst_cream->Rt; - VMSR(cpu, inst_cream->reg, inst_cream->Rd); + if (reg == 1) + { + cpu->VFP[VFP_FPSCR] = cpu->Reg[rt]; + } + else if (cpu->InAPrivilegedMode()) + { + if (reg == 8) + cpu->VFP[VFP_FPEXC] = cpu->Reg[rt]; + else if (reg == 9) + cpu->VFP[VFP_FPINST] = cpu->Reg[rt]; + else if (reg == 10) + cpu->VFP[VFP_FPINST2] = cpu->Reg[rt]; + } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmsr_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -1075,7 +1090,7 @@ VMOVBRC_INST: cpu->ExtReg[(2 * inst_cream->d) + inst_cream->index] = cpu->Reg[inst_cream->t]; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmovbrc_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -1111,19 +1126,22 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmrs)(unsigned int inst, int index) #ifdef VFP_INTERPRETER_IMPL VMRS_INST: { - if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { + if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { /* FIXME: special case for access to FPSID and FPEXC, VFP must be disabled, and in privileged mode */ /* Exceptions must be checked, according to v7 ref manual */ CHECK_VFP_ENABLED; - vmrs_inst *inst_cream = (vmrs_inst *)inst_base->component; + vmrs_inst* const inst_cream = (vmrs_inst*)inst_base->component; - if (inst_cream->reg == 1) /* FPSCR */ + unsigned int reg = inst_cream->reg; + unsigned int rt = inst_cream->Rt; + + if (reg == 1) // FPSCR { - if (inst_cream->Rt != 15) + if (rt != 15) { - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPSCR]; + cpu->Reg[rt] = cpu->VFP[VFP_FPSCR]; } else { @@ -1133,28 +1151,29 @@ VMRS_INST: cpu->VFlag = (cpu->VFP[VFP_FPSCR] >> 28) & 1; } } - else + else if (reg == 0) { - switch (inst_cream->reg) - { - case 0: - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPSID]; - break; - case 6: - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_MVFR1]; - break; - case 7: - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_MVFR0]; - break; - case 8: - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPEXC]; - break; - default: - break; - } + cpu->Reg[rt] = cpu->VFP[VFP_FPSID]; + } + else if (reg == 6) + { + cpu->Reg[rt] = cpu->VFP[VFP_MVFR1]; + } + else if (reg == 7) + { + cpu->Reg[rt] = cpu->VFP[VFP_MVFR0]; + } + else if (cpu->InAPrivilegedMode()) + { + if (reg == 8) + cpu->Reg[rt] = cpu->VFP[VFP_FPEXC]; + else if (reg == 9) + cpu->Reg[rt] = cpu->VFP[VFP_FPINST]; + else if (reg == 10) + cpu->Reg[rt] = cpu->VFP[VFP_FPINST2]; } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmrs_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -1202,7 +1221,7 @@ VMOVBCR_INST: cpu->Reg[inst_cream->t] = cpu->ExtReg[(2 * inst_cream->d) + inst_cream->index]; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmovbcr_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -1255,7 +1274,7 @@ VMOVBRRSS_INST: VMOVBRRSS(cpu, inst_cream->to_arm, inst_cream->t, inst_cream->t2, inst_cream->m, &cpu->Reg[inst_cream->t], &cpu->Reg[inst_cream->t2]); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmovbrrss_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -1303,7 +1322,7 @@ VMOVBRRD_INST: VMOVBRRD(cpu, inst_cream->to_arm, inst_cream->t, inst_cream->t2, inst_cream->m, &(cpu->Reg[inst_cream->t]), &(cpu->Reg[inst_cream->t2])); } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vmovbrrd_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -1359,23 +1378,23 @@ VSTR_INST: if (inst_cream->single) { - WriteMemory32(cpu, addr, cpu->ExtReg[inst_cream->d]); + cpu->WriteMemory32(addr, cpu->ExtReg[inst_cream->d]); } else { const u32 word1 = cpu->ExtReg[inst_cream->d*2+0]; const u32 word2 = cpu->ExtReg[inst_cream->d*2+1]; - if (InBigEndianMode(cpu)) { - WriteMemory32(cpu, addr + 0, word2); - WriteMemory32(cpu, addr + 4, word1); + if (cpu->InBigEndianMode()) { + cpu->WriteMemory32(addr + 0, word2); + cpu->WriteMemory32(addr + 4, word1); } else { - WriteMemory32(cpu, addr + 0, word1); - WriteMemory32(cpu, addr + 4, word2); + cpu->WriteMemory32(addr + 0, word1); + cpu->WriteMemory32(addr + 4, word2); } } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vstr_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -1425,7 +1444,7 @@ VPUSH_INST: { if (inst_cream->single) { - WriteMemory32(cpu, addr, cpu->ExtReg[inst_cream->d+i]); + cpu->WriteMemory32(addr, cpu->ExtReg[inst_cream->d+i]); addr += 4; } else @@ -1433,12 +1452,12 @@ VPUSH_INST: const u32 word1 = cpu->ExtReg[(inst_cream->d+i)*2+0]; const u32 word2 = cpu->ExtReg[(inst_cream->d+i)*2+1]; - if (InBigEndianMode(cpu)) { - WriteMemory32(cpu, addr + 0, word2); - WriteMemory32(cpu, addr + 4, word1); + if (cpu->InBigEndianMode()) { + cpu->WriteMemory32(addr + 0, word2); + cpu->WriteMemory32(addr + 4, word1); } else { - WriteMemory32(cpu, addr + 0, word1); - WriteMemory32(cpu, addr + 4, word2); + cpu->WriteMemory32(addr + 0, word1); + cpu->WriteMemory32(addr + 4, word2); } addr += 8; @@ -1447,7 +1466,7 @@ VPUSH_INST: cpu->Reg[R13] -= inst_cream->imm32; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vpush_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -1503,7 +1522,7 @@ VSTM_INST: /* encoding 1 */ { if (inst_cream->single) { - WriteMemory32(cpu, addr, cpu->ExtReg[inst_cream->d+i]); + cpu->WriteMemory32(addr, cpu->ExtReg[inst_cream->d+i]); addr += 4; } else @@ -1511,12 +1530,12 @@ VSTM_INST: /* encoding 1 */ const u32 word1 = cpu->ExtReg[(inst_cream->d+i)*2+0]; const u32 word2 = cpu->ExtReg[(inst_cream->d+i)*2+1]; - if (InBigEndianMode(cpu)) { - WriteMemory32(cpu, addr + 0, word2); - WriteMemory32(cpu, addr + 4, word1); + if (cpu->InBigEndianMode()) { + cpu->WriteMemory32(addr + 0, word2); + cpu->WriteMemory32(addr + 4, word1); } else { - WriteMemory32(cpu, addr + 0, word1); - WriteMemory32(cpu, addr + 4, word2); + cpu->WriteMemory32(addr + 0, word1); + cpu->WriteMemory32(addr + 4, word2); } addr += 8; @@ -1578,15 +1597,15 @@ VPOP_INST: { if (inst_cream->single) { - cpu->ExtReg[inst_cream->d+i] = ReadMemory32(cpu, addr); + cpu->ExtReg[inst_cream->d+i] = cpu->ReadMemory32(addr); addr += 4; } else { - const u32 word1 = ReadMemory32(cpu, addr + 0); - const u32 word2 = ReadMemory32(cpu, addr + 4); + const u32 word1 = cpu->ReadMemory32(addr + 0); + const u32 word2 = cpu->ReadMemory32(addr + 4); - if (InBigEndianMode(cpu)) { + if (cpu->InBigEndianMode()) { cpu->ExtReg[(inst_cream->d+i)*2+0] = word2; cpu->ExtReg[(inst_cream->d+i)*2+1] = word1; } else { @@ -1599,7 +1618,7 @@ VPOP_INST: } cpu->Reg[R13] += inst_cream->imm32; } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vpop_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -1651,14 +1670,14 @@ VLDR_INST: if (inst_cream->single) { - cpu->ExtReg[inst_cream->d] = ReadMemory32(cpu, addr); + cpu->ExtReg[inst_cream->d] = cpu->ReadMemory32(addr); } else { - const u32 word1 = ReadMemory32(cpu, addr + 0); - const u32 word2 = ReadMemory32(cpu, addr + 4); + const u32 word1 = cpu->ReadMemory32(addr + 0); + const u32 word2 = cpu->ReadMemory32(addr + 4); - if (InBigEndianMode(cpu)) { + if (cpu->InBigEndianMode()) { cpu->ExtReg[inst_cream->d*2+0] = word2; cpu->ExtReg[inst_cream->d*2+1] = word1; } else { @@ -1667,7 +1686,7 @@ VLDR_INST: } } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vldr_inst)); FETCH_INST; GOTO_NEXT_INST; @@ -1723,15 +1742,15 @@ VLDM_INST: { if (inst_cream->single) { - cpu->ExtReg[inst_cream->d+i] = ReadMemory32(cpu, addr); + cpu->ExtReg[inst_cream->d+i] = cpu->ReadMemory32(addr); addr += 4; } else { - const u32 word1 = ReadMemory32(cpu, addr + 0); - const u32 word2 = ReadMemory32(cpu, addr + 4); + const u32 word1 = cpu->ReadMemory32(addr + 0); + const u32 word2 = cpu->ReadMemory32(addr + 4); - if (InBigEndianMode(cpu)) { + if (cpu->InBigEndianMode()) { cpu->ExtReg[(inst_cream->d+i)*2+0] = word2; cpu->ExtReg[(inst_cream->d+i)*2+1] = word1; } else { @@ -1747,7 +1766,7 @@ VLDM_INST: cpu->Reg[inst_cream->n] - inst_cream->imm32); } } - cpu->Reg[15] += GET_INST_SIZE(cpu); + cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(vldm_inst)); FETCH_INST; GOTO_NEXT_INST; diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index 5a655a6f20..e5d3392520 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp @@ -53,6 +53,8 @@ #include <cinttypes> +#include "common/common_funcs.h" +#include "common/common_types.h" #include "common/logging/log.h" #include "core/arm/skyeye_common/vfp/vfp_helper.h" @@ -1246,7 +1248,7 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr) if (!fop->fn) { LOG_CRITICAL(Core_ARM11, "could not find single op %d, inst=0x%x@0x%x", FEXT_TO_IDX(inst), inst, state->Reg[15]); - exit(-1); + Crash(); goto invalid; } diff --git a/src/core/core.cpp b/src/core/core.cpp index 79038cd529..dddc16708e 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -2,15 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/settings.h" #include "core/arm/arm_interface.h" -#include "core/arm/disassembler/arm_disasm.h" #include "core/arm/dyncom/arm_dyncom.h" #include "core/hle/hle.h" #include "core/hle/kernel/thread.h" diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index e53c2e6068..20f2da0fe3 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -3,12 +3,12 @@ // Refer to the license.txt file included. #include <atomic> -#include <cstdio> #include <mutex> #include <vector> -#include "common/assert.h" #include "common/chunk_file.h" +#include "common/logging/log.h" +#include "common/string_util.h" #include "core/arm/arm_interface.h" #include "core/core.h" @@ -502,7 +502,7 @@ void Advance() { Core::g_app_core->down_count += diff; } if (advance_callback) - advance_callback(cycles_executed); + advance_callback(static_cast<int>(cycles_executed)); } void LogPendingEvents() { diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp index 45a559ce87..3f81447dfd 100644 --- a/src/core/file_sys/archive_backend.cpp +++ b/src/core/file_sys/archive_backend.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstddef> +#include <iomanip> #include <sstream> #include "common/logging/log.h" diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index e50c58a52f..92dad8e6f1 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -2,17 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <sys/stat.h> +#include <algorithm> +#include <vector> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" +#include "common/string_util.h" #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/disk_archive.h" #include "core/hle/service/fs/archive.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h index ef0b27bdee..ec8d770fc0 100644 --- a/src/core/file_sys/archive_extsavedata.h +++ b/src/core/file_sys/archive_extsavedata.h @@ -4,10 +4,13 @@ #pragma once +#include <memory> +#include <string> + #include "common/common_types.h" -#include "core/file_sys/disk_archive.h" -#include "core/loader/loader.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index d4a12ed103..696b51a943 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp @@ -2,30 +2,30 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <memory> #include "common/common_types.h" -#include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" #include "core/file_sys/archive_romfs.h" +#include "core/file_sys/ivfc_archive.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace namespace FileSys { -ArchiveFactory_RomFS::ArchiveFactory_RomFS(const Loader::AppLoader& app_loader) - : romfs_data(std::make_shared<std::vector<u8>>()) { +ArchiveFactory_RomFS::ArchiveFactory_RomFS(Loader::AppLoader& app_loader) { // Load the RomFS from the app - if (Loader::ResultStatus::Success != app_loader.ReadRomFS(*romfs_data)) { + if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { LOG_ERROR(Service_FS, "Unable to read RomFS!"); } } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) { - auto archive = Common::make_unique<IVFCArchive>(romfs_data); + auto archive = Common::make_unique<IVFCArchive>(romfs_file, data_offset, data_size); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h index 409bc670aa..2bedfa9c67 100644 --- a/src/core/file_sys/archive_romfs.h +++ b/src/core/file_sys/archive_romfs.h @@ -5,11 +5,13 @@ #pragma once #include <memory> +#include <string> #include <vector> #include "common/common_types.h" -#include "core/file_sys/ivfc_archive.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -20,14 +22,16 @@ namespace FileSys { /// File system interface to the RomFS archive class ArchiveFactory_RomFS final : public ArchiveFactory { public: - ArchiveFactory_RomFS(const Loader::AppLoader& app_loader); + ArchiveFactory_RomFS(Loader::AppLoader& app_loader); std::string GetName() const override { return "RomFS"; } ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; ResultCode Format(const Path& path) override; private: - std::shared_ptr<std::vector<u8>> romfs_data; + std::shared_ptr<FileUtil::IOFile> romfs_file; + u64 data_offset; + u64 data_size; }; } // namespace FileSys diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index a92309377b..12876899f5 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp @@ -2,18 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <sys/stat.h> +#include <algorithm> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" +#include "common/string_util.h" #include "core/file_sys/archive_savedata.h" #include "core/file_sys/disk_archive.h" #include "core/hle/kernel/process.h" #include "core/hle/service/fs/archive.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -37,7 +37,7 @@ ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directo } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) { - std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->program_id); + std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id); if (!FileUtil::Exists(concrete_mount_point)) { // When a SaveData archive is created for the first time, it is not yet formatted // and the save file/directory structure expected by the game has not yet been initialized. @@ -52,7 +52,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P } ResultCode ArchiveFactory_SaveData::Format(const Path& path) { - std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->program_id); + std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id); FileUtil::DeleteDirRecursively(concrete_mount_point); FileUtil::CreateFullPath(concrete_mount_point); return RESULT_SUCCESS; diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h index db17afc92a..1f65297ddc 100644 --- a/src/core/file_sys/archive_savedata.h +++ b/src/core/file_sys/archive_savedata.h @@ -4,10 +4,11 @@ #pragma once -#include "common/common_types.h" +#include <memory> +#include <string> -#include "core/file_sys/disk_archive.h" -#include "core/loader/loader.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp index e7e4fbf1d9..ea1dfe2c71 100644 --- a/src/core/file_sys/archive_savedatacheck.cpp +++ b/src/core/file_sys/archive_savedatacheck.cpp @@ -2,11 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <vector> + +#include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" +#include "common/string_util.h" #include "core/file_sys/archive_savedatacheck.h" +#include "core/file_sys/ivfc_archive.h" #include "core/hle/service/fs/archive.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -31,17 +37,14 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co auto vec = path.AsBinary(); const u32* data = reinterpret_cast<u32*>(vec.data()); std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]); - FileUtil::IOFile file(file_path, "rb"); + auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb"); - if (!file.IsOpen()) { + if (!file->IsOpen()) { return ResultCode(-1); // TODO(Subv): Find the right error code } - auto size = file.GetSize(); - auto raw_data = std::make_shared<std::vector<u8>>(size); - file.ReadBytes(raw_data->data(), size); - file.Close(); + auto size = file->GetSize(); - auto archive = Common::make_unique<IVFCArchive>(std::move(raw_data)); + auto archive = Common::make_unique<IVFCArchive>(file, 0, size); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_savedatacheck.h b/src/core/file_sys/archive_savedatacheck.h index f78a6f02ee..b14aefe8bc 100644 --- a/src/core/file_sys/archive_savedatacheck.h +++ b/src/core/file_sys/archive_savedatacheck.h @@ -4,12 +4,11 @@ #pragma once -#include <vector> +#include <memory> +#include <string> -#include "common/common_types.h" - -#include "core/file_sys/ivfc_archive.h" -#include "core/loader/loader.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index c1234a1862..5c825f4296 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -2,9 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <sys/stat.h> +#include <algorithm> -#include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 1becf6c0f2..10b273bdba 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h @@ -4,10 +4,11 @@ #pragma once -#include "common/common_types.h" +#include <memory> +#include <string> -#include "core/file_sys/disk_archive.h" -#include "core/loader/loader.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index 4fe785c975..896f895295 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -2,15 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <sys/stat.h> +#include <algorithm> +#include <vector> #include "common/common_types.h" #include "common/file_util.h" #include "common/make_unique.h" +#include "common/string_util.h" #include "core/file_sys/archive_systemsavedata.h" +#include "core/file_sys/disk_archive.h" #include "core/hle/service/fs/archive.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h index 3431fed880..afc689848a 100644 --- a/src/core/file_sys/archive_systemsavedata.h +++ b/src/core/file_sys/archive_systemsavedata.h @@ -4,10 +4,13 @@ #pragma once +#include <memory> +#include <string> + #include "common/common_types.h" -#include "core/file_sys/disk_archive.h" -#include "core/loader/loader.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp index 9980cced10..1096fd34da 100644 --- a/src/core/file_sys/disk_archive.cpp +++ b/src/core/file_sys/disk_archive.cpp @@ -2,7 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <sys/stat.h> +#include <algorithm> +#include <cstdio> #include "common/common_types.h" #include "common/file_util.h" @@ -10,7 +11,6 @@ #include "common/make_unique.h" #include "core/file_sys/disk_archive.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -105,12 +105,12 @@ bool DiskFile::Open() { return true; } -size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const { +size_t DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { file->Seek(offset, SEEK_SET); return file->ReadBytes(buffer, length); } -size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { +size_t DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { file->Seek(offset, SEEK_SET); size_t written = file->WriteBytes(buffer, length); if (flush) @@ -118,8 +118,8 @@ size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, cons return written; } -size_t DiskFile::GetSize() const { - return static_cast<size_t>(file->GetSize()); +u64 DiskFile::GetSize() const { + return file->GetSize(); } bool DiskFile::SetSize(const u64 size) const { diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h index a22d3837a4..c5da07508c 100644 --- a/src/core/file_sys/disk_archive.h +++ b/src/core/file_sys/disk_archive.h @@ -4,13 +4,18 @@ #pragma once +#include <cstddef> +#include <memory> +#include <string> +#include <vector> + #include "common/common_types.h" #include "common/file_util.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/directory_backend.h" #include "core/file_sys/file_backend.h" -#include "core/loader/loader.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -50,10 +55,10 @@ public: DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); bool Open() override; - size_t Read(const u64 offset, const u32 length, u8* buffer) const override; - size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; - size_t GetSize() const override; - bool SetSize(const u64 size) const override; + size_t Read(u64 offset, size_t length, u8* buffer) const override; + size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; + u64 GetSize() const override; + bool SetSize(u64 size) const override; bool Close() const override; void Flush() const override { diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h index 0fcff18454..df7165df36 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/file_backend.h @@ -4,6 +4,8 @@ #pragma once +#include <cstddef> + #include "common/common_types.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -29,7 +31,7 @@ public: * @param buffer Buffer to read data into * @return Number of bytes read */ - virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0; + virtual size_t Read(u64 offset, size_t length, u8* buffer) const = 0; /** * Write data to the file @@ -39,20 +41,20 @@ public: * @param buffer Buffer to read data from * @return Number of bytes written */ - virtual size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const = 0; + virtual size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0; /** * Get the size of the file in bytes * @return Size of the file in bytes */ - virtual size_t GetSize() const = 0; + virtual u64 GetSize() const = 0; /** * Set the size of the file in bytes * @param size New size of the file * @return true if successful */ - virtual bool SetSize(const u64 size) const = 0; + virtual bool SetSize(u64 size) const = 0; /** * Close the file diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp index 2d2509d16d..e16aa14911 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/ivfc_archive.cpp @@ -2,10 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> #include <memory> #include "common/common_types.h" -#include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" @@ -16,15 +16,12 @@ namespace FileSys { -IVFCArchive::IVFCArchive(std::shared_ptr<const std::vector<u8>> data) : data(data) { -} - std::string IVFCArchive::GetName() const { return "IVFC"; } std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { - return Common::make_unique<IVFCFile>(data); + return Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size); } bool IVFCArchive::DeleteFile(const Path& path) const { @@ -64,19 +61,21 @@ std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) c //////////////////////////////////////////////////////////////////////////////////////////////////// -size_t IVFCFile::Read(const u64 offset, const u32 length, u8* buffer) const { +size_t IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const { LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); - memcpy(buffer, data->data() + offset, length); - return length; + romfs_file->Seek(data_offset + offset, SEEK_SET); + size_t read_length = (size_t)std::min((u64)length, data_size - offset); + + return romfs_file->ReadBytes(buffer, read_length); } -size_t IVFCFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { +size_t IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); return 0; } -size_t IVFCFile::GetSize() const { - return sizeof(u8) * data->size(); +u64 IVFCFile::GetSize() const { + return data_size; } bool IVFCFile::SetSize(const u64 size) const { diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h index 10415798d5..c15a6c4ae2 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/ivfc_archive.h @@ -4,15 +4,18 @@ #pragma once +#include <cstddef> #include <memory> +#include <string> #include <vector> #include "common/common_types.h" +#include "common/file_util.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/directory_backend.h" #include "core/file_sys/file_backend.h" -#include "core/loader/loader.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -26,7 +29,8 @@ namespace FileSys { */ class IVFCArchive : public ArchiveBackend { public: - IVFCArchive(std::shared_ptr<const std::vector<u8>> data); + IVFCArchive(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) + : romfs_file(file), data_offset(offset), data_size(size) {} std::string GetName() const override; @@ -40,23 +44,28 @@ public: std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; protected: - std::shared_ptr<const std::vector<u8>> data; + std::shared_ptr<FileUtil::IOFile> romfs_file; + u64 data_offset; + u64 data_size; }; class IVFCFile : public FileBackend { public: - IVFCFile(std::shared_ptr<const std::vector<u8>> data) : data(data) {} + IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) + : romfs_file(file), data_offset(offset), data_size(size) {} bool Open() override { return true; } - size_t Read(const u64 offset, const u32 length, u8* buffer) const override; - size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; - size_t GetSize() const override; - bool SetSize(const u64 size) const override; + size_t Read(u64 offset, size_t length, u8* buffer) const override; + size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; + u64 GetSize() const override; + bool SetSize(u64 size) const override; bool Close() const override { return false; } void Flush() const override { } private: - std::shared_ptr<const std::vector<u8>> data; + std::shared_ptr<FileUtil::IOFile> romfs_file; + u64 data_offset; + u64 data_size; }; class IVFCDirectory : public DirectoryBackend { diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp new file mode 100644 index 0000000000..826f6cbb6f --- /dev/null +++ b/src/core/hle/applets/applet.cpp @@ -0,0 +1,101 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <unordered_map> + +#include "common/assert.h" +#include "common/common_types.h" + +#include "core/core_timing.h" +#include "core/hle/applets/applet.h" +#include "core/hle/applets/swkbd.h" +#include "core/hle/result.h" +#include "core/hle/service/apt/apt.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// Specializes std::hash for AppletId, so that we can use it in std::unordered_map. +// Workaround for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 +namespace std { + template <> + struct hash<Service::APT::AppletId> { + typedef Service::APT::AppletId argument_type; + typedef std::size_t result_type; + + result_type operator()(const argument_type& id_code) const { + typedef std::underlying_type<argument_type>::type Type; + return std::hash<Type>()(static_cast<Type>(id_code)); + } + }; +} + +namespace HLE { +namespace Applets { + +static std::unordered_map<Service::APT::AppletId, std::shared_ptr<Applet>> applets; +static u32 applet_update_event = -1; ///< The CoreTiming event identifier for the Applet update callback. +/// The interval at which the Applet update callback will be called, 16.6ms +static const u64 applet_update_interval_us = 16666; + +ResultCode Applet::Create(Service::APT::AppletId id) { + switch (id) { + case Service::APT::AppletId::SoftwareKeyboard1: + case Service::APT::AppletId::SoftwareKeyboard2: + applets[id] = std::make_shared<SoftwareKeyboard>(id); + break; + default: + // TODO(Subv): Find the right error code + return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotSupported, ErrorLevel::Permanent); + } + + return RESULT_SUCCESS; +} + +std::shared_ptr<Applet> Applet::Get(Service::APT::AppletId id) { + auto itr = applets.find(id); + if (itr != applets.end()) + return itr->second; + return nullptr; +} + +/// Handles updating the current Applet every time it's called. +static void AppletUpdateEvent(u64 applet_id, int cycles_late) { + Service::APT::AppletId id = static_cast<Service::APT::AppletId>(applet_id); + std::shared_ptr<Applet> applet = Applet::Get(id); + ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id=%08X", id); + + applet->Update(); + + // If the applet is still running after the last update, reschedule the event + if (applet->IsRunning()) { + CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us) - cycles_late, + applet_update_event, applet_id); + } else { + // Otherwise the applet has terminated, in which case we should clean it up + applets[id] = nullptr; + } +} + +ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter) { + ResultCode result = StartImpl(parameter); + if (result.IsError()) + return result; + // Schedule the update event + CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us), applet_update_event, static_cast<u64>(id)); + return result; +} + +void Init() { + // Register the applet update callback + applet_update_event = CoreTiming::RegisterEvent("HLE Applet Update Event", AppletUpdateEvent); +} + +void Shutdown() { +} + +} +} // namespace diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h new file mode 100644 index 0000000000..b235d0b8a4 --- /dev/null +++ b/src/core/hle/applets/applet.h @@ -0,0 +1,77 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> + +#include "core/hle/result.h" +#include "core/hle/service/apt/apt.h" + +namespace HLE { +namespace Applets { + +class Applet { +public: + virtual ~Applet() { } + Applet(Service::APT::AppletId id) : id(id) { } + + /** + * Creates an instance of the Applet subclass identified by the parameter. + * and stores it in a global map. + * @param id Id of the applet to create. + * @returns ResultCode Whether the operation was successful or not. + */ + static ResultCode Create(Service::APT::AppletId id); + + /** + * Retrieves the Applet instance identified by the specified id. + * @param id Id of the Applet to retrieve. + * @returns Requested Applet or nullptr if not found. + */ + static std::shared_ptr<Applet> Get(Service::APT::AppletId id); + + /** + * Handles a parameter from the application. + * @param parameter Parameter data to handle. + * @returns ResultCode Whether the operation was successful or not. + */ + virtual ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) = 0; + + /** + * Handles the Applet start event, triggered from the application. + * @param parameter Parameter data to handle. + * @returns ResultCode Whether the operation was successful or not. + */ + ResultCode Start(const Service::APT::AppletStartupParameter& parameter); + + /** + * Whether the applet is currently executing instead of the host application or not. + */ + virtual bool IsRunning() const = 0; + + /** + * Handles an update tick for the Applet, lets it update the screen, send commands, etc. + */ + virtual void Update() = 0; + +protected: + /** + * Handles the Applet start event, triggered from the application. + * @param parameter Parameter data to handle. + * @returns ResultCode Whether the operation was successful or not. + */ + virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0; + + Service::APT::AppletId id; ///< Id of this Applet +}; + +/// Initializes the HLE applets +void Init(); + +/// Shuts down the HLE applets +void Shutdown(); + +} +} // namespace diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp new file mode 100644 index 0000000000..1db6b5a175 --- /dev/null +++ b/src/core/hle/applets/swkbd.cpp @@ -0,0 +1,113 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include <string> + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" + +#include "core/hle/applets/swkbd.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/hid/hid.h" +#include "core/hle/service/gsp_gpu.h" +#include "core/hle/result.h" +#include "core/memory.h" + +#include "video_core/video_core.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace HLE { +namespace Applets { + +SoftwareKeyboard::SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) { + // Create the SharedMemory that will hold the framebuffer data + // TODO(Subv): What size should we use here? + using Kernel::MemoryPermission; + framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); +} + +ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { + LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); + UNIMPLEMENTED(); + // TODO(Subv): Find the right error code + return ResultCode(-1); + } + + Service::APT::MessageParameter result; + // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo + result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); + result.data = nullptr; + result.buffer_size = 0; + result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + result.sender_id = static_cast<u32>(id); + result.object = framebuffer_memory; + + Service::APT::SendParameter(result); + return RESULT_SUCCESS; +} + +ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter const& parameter) { + ASSERT_MSG(parameter.buffer_size == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong"); + + memcpy(&config, parameter.data, parameter.buffer_size); + text_memory = boost::static_pointer_cast<Kernel::SharedMemory, Kernel::Object>(parameter.object); + + // TODO(Subv): Verify if this is the correct behavior + memset(text_memory->GetPointer(), 0, text_memory->size); + + DrawScreenKeyboard(); + + started = true; + return RESULT_SUCCESS; +} + +void SoftwareKeyboard::Update() { + // TODO(Subv): Handle input using the touch events from the HID module + + // TODO(Subv): Remove this hardcoded text + std::u16string text = Common::UTF8ToUTF16("Citra"); + memcpy(text_memory->GetPointer(), text.c_str(), text.length() * sizeof(char16_t)); + + // TODO(Subv): Ask for input and write it to the shared memory + // TODO(Subv): Find out what are the possible values for the return code, + // some games seem to check for a hardcoded 2 + config.return_code = 2; + config.text_length = 6; + config.text_offset = 0; + + // TODO(Subv): We're finalizing the applet immediately after it's started, + // but we should defer this call until after all the input has been collected. + Finalize(); +} + +void SoftwareKeyboard::DrawScreenKeyboard() { + auto bottom_screen = GSP_GPU::GetFrameBufferInfo(0, 1); + auto info = bottom_screen->framebuffer_info[bottom_screen->index]; + + // TODO(Subv): Draw the HLE keyboard, for now just zero-fill the framebuffer + memset(Memory::GetPointer(info.address_left), 0, info.stride * 320); + + GSP_GPU::SetBufferSwap(1, info); +} + +void SoftwareKeyboard::Finalize() { + // Let the application know that we're closing + Service::APT::MessageParameter message; + message.buffer_size = sizeof(SoftwareKeyboardConfig); + message.data = reinterpret_cast<u8*>(&config); + message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); + message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + message.sender_id = static_cast<u32>(id); + Service::APT::SendParameter(message); + + started = false; +} + +} +} // namespace diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h new file mode 100644 index 0000000000..cb95b8d90a --- /dev/null +++ b/src/core/hle/applets/swkbd.h @@ -0,0 +1,90 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "common/common_funcs.h" + +#include "core/hle/applets/applet.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/result.h" +#include "core/hle/service/apt/apt.h" + +namespace HLE { +namespace Applets { + +struct SoftwareKeyboardConfig { + INSERT_PADDING_WORDS(0x8); + + u16 max_text_length; ///< Maximum length of the input text + + INSERT_PADDING_BYTES(0x6E); + + char16_t display_text[65]; ///< Text to display when asking the user for input + + INSERT_PADDING_BYTES(0xE); + + u32 default_text_offset; ///< Offset of the default text in the output SharedMemory + + INSERT_PADDING_WORDS(0x3); + + u32 shared_memory_size; ///< Size of the SharedMemory + + INSERT_PADDING_WORDS(0x1); + + u32 return_code; ///< Return code of the SoftwareKeyboard, usually 2, other values are unknown + + INSERT_PADDING_WORDS(0x2); + + u32 text_offset; ///< Offset in the SharedMemory where the output text starts + u16 text_length; ///< Length in characters of the output text + + INSERT_PADDING_BYTES(0x2B6); +}; + +/** + * The size of this structure (0x400) has been verified via reverse engineering of multiple games + * that use the software keyboard. + */ +static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config size is wrong"); + +class SoftwareKeyboard final : public Applet { +public: + SoftwareKeyboard(Service::APT::AppletId id); + ~SoftwareKeyboard() {} + + ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override; + ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override; + void Update() override; + bool IsRunning() const override { return started; } + + /** + * Draws a keyboard to the current bottom screen framebuffer. + */ + void DrawScreenKeyboard(); + + /** + * Sends the LibAppletClosing signal to the application, + * along with the relevant data buffers. + */ + void Finalize(); + + /// TODO(Subv): Find out what this is actually used for. + /// It is believed that the application stores the current screen image here. + Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; + + /// SharedMemory where the output text will be stored + Kernel::SharedPtr<Kernel::SharedMemory> text_memory; + + /// Configuration of this instance of the SoftwareKeyboard, as received from the application + SoftwareKeyboardConfig config; + + /// Whether this applet is currently running instead of the host application or not. + bool started; +}; + +} +} // namespace diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 5949cb4707..1a0518926d 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -87,8 +87,28 @@ template<ResultCode func(u32, s64)> void Wrap() { } } -template<ResultCode func(void*, void*, u32)> void Wrap(){ - FuncReturn(func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2)).raw); +template<ResultCode func(MemoryInfo*, PageInfo*, u32)> void Wrap() { + MemoryInfo memory_info = {}; + PageInfo page_info = {}; + u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; + Core::g_app_core->SetReg(1, memory_info.base_address); + Core::g_app_core->SetReg(2, memory_info.size); + Core::g_app_core->SetReg(3, memory_info.permission); + Core::g_app_core->SetReg(4, memory_info.state); + Core::g_app_core->SetReg(5, page_info.flags); + FuncReturn(retval); +} + +template<ResultCode func(MemoryInfo*, PageInfo*, Handle, u32)> void Wrap() { + MemoryInfo memory_info = {}; + PageInfo page_info = {}; + u32 retval = func(&memory_info, &page_info, PARAM(2), PARAM(3)).raw; + Core::g_app_core->SetReg(1, memory_info.base_address); + Core::g_app_core->SetReg(2, memory_info.size); + Core::g_app_core->SetReg(3, memory_info.permission); + Core::g_app_core->SetReg(4, memory_info.state); + Core::g_app_core->SetReg(5, page_info.flags); + FuncReturn(retval); } template<ResultCode func(s32*, u32)> void Wrap(){ @@ -113,7 +133,7 @@ template<ResultCode func(u32)> void Wrap() { FuncReturn(func(PARAM(0)).raw); } -template<ResultCode func(s64*, u32, u32*, s32)> void Wrap(){ +template<ResultCode func(s64*, u32, u32*, u32)> void Wrap(){ FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), (u32*)Memory::GetPointer(PARAM(2)), (s32)PARAM(3)).raw); } diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index fdeb9a0285..cd0a400dcf 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp @@ -10,7 +10,6 @@ #include "core/hle/hle.h" #include "core/hle/config_mem.h" #include "core/hle/shared_page.h" -#include "core/hle/kernel/thread.h" #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index f338f3266c..53feebbc05 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -21,7 +21,7 @@ SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { SharedPtr<Event> evt(new Event); evt->signaled = false; - evt->reset_type = evt->intitial_reset_type = reset_type; + evt->reset_type = reset_type; evt->name = std::move(name); return evt; diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index fba960d2a1..89d4052367 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -26,7 +26,6 @@ public: static const HandleType HANDLE_TYPE = HandleType::Event; HandleType GetHandleType() const override { return HANDLE_TYPE; } - ResetType intitial_reset_type; ///< ResetType specified at Event initialization ResetType reset_type; ///< Current ResetType bool signaled; ///< Whether the event has already been signaled diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 20e11da164..5711c0405c 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -7,8 +7,6 @@ #include "common/assert.h" #include "common/logging/log.h" -#include "core/arm/arm_interface.h" -#include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/process.h" diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 64595f7581..4d4276f7a7 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,10 +4,11 @@ #pragma once -#include <boost/intrusive_ptr.hpp> +#include <boost/smart_ptr/intrusive_ptr.hpp> +#include <algorithm> #include <array> -#include <memory> +#include <cstddef> #include <string> #include <vector> @@ -16,8 +17,6 @@ #include "core/hle/hle.h" #include "core/hle/result.h" -struct ApplicationInfo; - namespace Kernel { class Thread; @@ -48,6 +47,7 @@ enum class HandleType : u32 { Semaphore = 10, Timer = 11, ResourceLimit = 12, + CodeSet = 13, }; enum { @@ -86,6 +86,7 @@ public: case HandleType::Process: case HandleType::AddressArbiter: case HandleType::ResourceLimit: + case HandleType::CodeSet: return false; } } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index b0e75ba59b..a7892c6522 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -5,24 +5,39 @@ #include "common/assert.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/make_unique.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/vm_manager.h" +#include "core/mem_map.h" #include "core/memory.h" namespace Kernel { +SharedPtr<CodeSet> CodeSet::Create(std::string name, u64 program_id) { + SharedPtr<CodeSet> codeset(new CodeSet); + + codeset->name = std::move(name); + codeset->program_id = program_id; + + return codeset; +} + +CodeSet::CodeSet() {} +CodeSet::~CodeSet() {} + u32 Process::next_process_id; -SharedPtr<Process> Process::Create(std::string name, u64 program_id) { +SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { SharedPtr<Process> process(new Process); - process->name = std::move(name); - process->program_id = program_id; - + process->codeset = std::move(code_set); process->flags.raw = 0; process->flags.memory_region = MemoryRegion::APPLICATION; + process->address_space = Common::make_unique<VMManager>(); + Memory::InitLegacyAddressSpace(*process->address_space); return process; } @@ -87,8 +102,19 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { } } -void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { - Kernel::SetupMainThread(entry_point, main_thread_priority); +void Process::Run(s32 main_thread_priority, u32 stack_size) { + auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) { + auto vma = address_space->MapMemoryBlock(segment.addr, codeset->memory, + segment.offset, segment.size, memory_state).Unwrap(); + address_space->Reprotect(vma, permissions); + }; + + MapSegment(codeset->code, VMAPermission::ReadExecute, MemoryState::Code); + MapSegment(codeset->rodata, VMAPermission::Read, MemoryState::Code); + MapSegment(codeset->data, VMAPermission::ReadWrite, MemoryState::Private); + + address_space->LogLayout(); + Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); } Kernel::Process::Process() {} diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 7b8a686106..83d3aceae5 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -5,6 +5,9 @@ #pragma once #include <bitset> +#include <cstddef> +#include <memory> +#include <string> #include <boost/container/static_vector.hpp> @@ -12,7 +15,6 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/result.h" namespace Kernel { @@ -46,23 +48,51 @@ union ProcessFlags { }; class ResourceLimit; +class VMManager; + +struct CodeSet final : public Object { + static SharedPtr<CodeSet> Create(std::string name, u64 program_id); + + std::string GetTypeName() const override { return "CodeSet"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::CodeSet; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + /// Name of the process + std::string name; + /// Title ID corresponding to the process + u64 program_id; + + std::shared_ptr<std::vector<u8>> memory; + + struct Segment { + size_t offset = 0; + VAddr addr = 0; + u32 size = 0; + }; + + Segment code, rodata, data; + VAddr entrypoint; + +private: + CodeSet(); + ~CodeSet() override; +}; class Process final : public Object { public: - static SharedPtr<Process> Create(std::string name, u64 program_id); + static SharedPtr<Process> Create(SharedPtr<CodeSet> code_set); std::string GetTypeName() const override { return "Process"; } - std::string GetName() const override { return name; } + std::string GetName() const override { return codeset->name; } static const HandleType HANDLE_TYPE = HandleType::Process; HandleType GetHandleType() const override { return HANDLE_TYPE; } static u32 next_process_id; - /// Name of the process - std::string name; - /// Title ID corresponding to the process - u64 program_id; + SharedPtr<CodeSet> codeset; /// Resource limit descriptor for this process SharedPtr<ResourceLimit> resource_limit; @@ -80,6 +110,7 @@ public: /// Bitmask of the used TLS slots std::bitset<300> used_tls_slots; + std::unique_ptr<VMManager> address_space; /** * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them @@ -90,7 +121,7 @@ public: /** * Applies address space changes and launches the process main thread. */ - void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); + void Run(s32 main_thread_priority, u32 stack_size); private: Process(); diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 257da9105b..adaffcafe8 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -4,8 +4,14 @@ #pragma once +#include <string> + +#include "common/assert.h" +#include "common/common_types.h" + #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" +#include "core/hle/result.h" #include "core/memory.h" namespace IPC { diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 2042668969..7a29227762 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -4,9 +4,12 @@ #pragma once +#include <string> + #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 4729a7fe03..29ea6d5319 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -13,7 +13,7 @@ #include "common/thread_queue_list.h" #include "core/arm/arm_interface.h" -#include "core/arm/skyeye_common/armdefs.h" +#include "core/arm/skyeye_common/armstate.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/hle.h" @@ -37,6 +37,10 @@ void Thread::Acquire() { ASSERT_MSG(!ShouldWait(), "object unavailable!"); } +// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing +// us to simply use a pool index or similar. +static Kernel::HandleTable wakeup_callback_handle_table; + // Lists all thread ids that aren't deleted/etc. static std::vector<SharedPtr<Thread>> thread_list; @@ -93,6 +97,8 @@ void Thread::Stop() { // Cancel any outstanding wakeup events for this thread CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); + wakeup_callback_handle_table.Close(callback_handle); + callback_handle = 0; // Clean up thread from ready queue // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) @@ -108,6 +114,7 @@ void Thread::Stop() { for (auto& wait_object : wait_objects) { wait_object->RemoveWaitingThread(this); } + wait_objects.clear(); Kernel::g_current_process->used_tls_slots[tls_index] = false; @@ -210,6 +217,14 @@ static void SwitchContext(Thread* new_thread) { new_thread->context.pc -= thumb_mode ? 2 : 4; } + // Clean up the thread's wait_objects, they'll be restored if needed during + // the svcWaitSynchronization call + for (int i = 0; i < new_thread->wait_objects.size(); ++i) { + SharedPtr<WaitObject> object = new_thread->wait_objects[i]; + object->RemoveWaitingThread(new_thread); + } + new_thread->wait_objects.clear(); + ready_queue.remove(new_thread->current_priority, new_thread); new_thread->status = THREADSTATUS_RUNNING; @@ -268,10 +283,6 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { thread->status = THREADSTATUS_WAIT_ARB; } -// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing -// us to simply use a pool index or similar. -static Kernel::HandleTable wakeup_callback_handle_table; - /** * Callback that will wake up the thread it was scheduled for * @param thread_handle The handle of the thread that's been awoken @@ -503,12 +514,16 @@ void ThreadingInit() { current_thread = nullptr; next_thread_id = 1; - - thread_list.clear(); - ready_queue.clear(); } void ThreadingShutdown() { + current_thread = nullptr; + + for (auto& t : thread_list) { + t->Stop(); + } + thread_list.clear(); + ready_queue.clear(); } } // namespace diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index b8160bb2c7..1ff1d9b979 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -13,6 +13,7 @@ #include "core/core.h" +#include "core/hle/hle.h" #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index b2dd21542f..205cc7b53e 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <iterator> + #include "common/assert.h" #include "core/hle/kernel/vm_manager.h" @@ -33,6 +35,10 @@ VMManager::VMManager() { Reset(); } +VMManager::~VMManager() { + Reset(); +} + void VMManager::Reset() { vma_map.clear(); @@ -128,6 +134,16 @@ void VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) { MergeAdjacent(iter); } +void VMManager::LogLayout() const { + for (const auto& p : vma_map) { + const VirtualMemoryArea& vma = p.second; + LOG_DEBUG(Kernel, "%08X - %08X size: %8X %c%c%c", vma.base, vma.base + vma.size, vma.size, + (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', + (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', + (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-'); + } +} + VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle & iter) { // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given // non-const access to its container. diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 22b7246039..b3795a94a3 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -6,7 +6,6 @@ #include <map> #include <memory> -#include <string> #include <vector> #include "common/common_types.h" @@ -102,7 +101,7 @@ struct VirtualMemoryArea { * - http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/ * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ */ -class VMManager { +class VMManager final { // TODO(yuriks): Make page tables switchable to support multiple VMManagers public: /** @@ -122,6 +121,7 @@ public: using VMAHandle = decltype(vma_map)::const_iterator; VMManager(); + ~VMManager(); /// Clears the address space map, re-initializing with a single free area. void Reset(); @@ -169,6 +169,9 @@ public: /// Changes the permissions of the given VMA. void Reprotect(VMAHandle vma, VMAPermission new_perms); + /// Dumps the address space layout to the log, for debugging + void LogLayout() const; + private: using VMAIter = decltype(vma_map)::iterator; diff --git a/src/core/hle/result.h b/src/core/hle/result.h index ce633d8415..cb2d681e00 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -4,7 +4,7 @@ #pragma once -#include <cstddef> +#include <new> #include <type_traits> #include <utility> diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 57dc1ece72..7332478fb1 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -38,6 +38,15 @@ void GetTitleIDList(Service::Interface* self) { LOG_WARNING(Service_AM, "(STUBBED) Requested %u titles from media type %u", num_titles, media_type); } +void GetNumContentInfos(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; // Number of content infos plus one + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + void Init() { using namespace Kernel; diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 063b8bd097..0b78f53933 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -37,6 +37,19 @@ void TitleIDListGetTotal(Service::Interface* self); */ void GetTitleIDList(Service::Interface* self); +/** + * AM::GetNumContentInfos service function + * Inputs: + * 0 : Command header (0x100100C0) + * 1 : Unknown + * 2 : Unknown + * 3 : Unknown + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Number of content infos plus one + */ +void GetNumContentInfos(Service::Interface* self); + /// Initialize AM service void Init(); diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp index c6fc81bc33..f40a87cb41 100644 --- a/src/core/hle/service/am/am_app.cpp +++ b/src/core/hle/service/am/am_app.cpp @@ -9,11 +9,19 @@ namespace Service { namespace AM { -// Empty arrays are illegal -- commented out until an entry is added. -//const Interface::FunctionInfo FunctionTable[] = { }; +const Interface::FunctionInfo FunctionTable[] = { + {0x100100C0, GetNumContentInfos, "GetNumContentInfos"}, + {0x10020104, nullptr, "FindContentInfos"}, + {0x10030142, nullptr, "ListContentInfos"}, + {0x10040102, nullptr, "DeleteContents"}, + {0x10050084, nullptr, "GetDataTitleInfos"}, + {0x10070102, nullptr, "ListDataTitleTicketInfos"}, + {0x100900C0, nullptr, "IsDataTitleInUse"}, + {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"}, +}; AM_APP_Interface::AM_APP_Interface() { - //Register(FunctionTable); + Register(FunctionTable); } } // namespace AM diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index b454a2709c..7b6ab4ce01 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -6,6 +6,7 @@ #include "common/file_util.h" #include "common/logging/log.h" +#include "core/hle/applets/applet.h" #include "core/hle/service/service.h" #include "core/hle/service/apt/apt.h" #include "core/hle/service/apt/apt_a.h" @@ -34,12 +35,21 @@ static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; static Kernel::SharedPtr<Kernel::Mutex> lock; static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event -static Kernel::SharedPtr<Kernel::Event> start_event; ///< APT start event +static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event static std::vector<u8> shared_font; static u32 cpu_percent; ///< CPU time available to the running application +/// Parameter data to be returned in the next call to Glance/ReceiveParameter +static MessageParameter next_parameter; + +void SendParameter(const MessageParameter& parameter) { + next_parameter = parameter; + // Signal the event to let the application know that a new parameter is ready to be read + parameter_event->Signal(); +} + void Initialize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; @@ -47,18 +57,18 @@ void Initialize(Service::Interface* self) { cmd_buff[2] = IPC::MoveHandleDesc(2); cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(start_event).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(parameter_event).MoveFrom(); // TODO(bunnei): Check if these events are cleared every time Initialize is called. notification_event->Clear(); - start_event->Clear(); + parameter_event->Clear(); ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); lock->Release(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_TRACE(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); + LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); } void GetSharedFont(Service::Interface* self) { @@ -85,9 +95,6 @@ void GetSharedFont(Service::Interface* self) { void NotifyToWait(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; - // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further. - start_event->Signal(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); } @@ -100,7 +107,7 @@ void GetLockHandle(Service::Interface* self) { // Not sure what these parameters are used for, but retail apps check that they are 0 after // GetLockHandle has been called. - cmd_buff[2] = 0; + cmd_buff[2] = 0; // Applet Attributes, this value is passed to Enable. cmd_buff[3] = 0; cmd_buff[4] = 0; @@ -110,9 +117,10 @@ void GetLockHandle(Service::Interface* self) { void Enable(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? + u32 attributes = cmd_buff[1]; cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); + parameter_event->Signal(); // Let the application know that it has been started + LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); } void GetAppletManInfo(Service::Interface* self) { @@ -121,8 +129,8 @@ void GetAppletManInfo(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; cmd_buff[3] = 0; - cmd_buff[4] = static_cast<u32>(AppID::HomeMenu); // Home menu AppID - cmd_buff[5] = static_cast<u32>(AppID::Application); // TODO(purpasmart96): Do this correctly + cmd_buff[4] = static_cast<u32>(AppletId::HomeMenu); // Home menu AppID + cmd_buff[5] = static_cast<u32>(AppletId::Application); // TODO(purpasmart96): Do this correctly LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); } @@ -131,7 +139,13 @@ void IsRegistered(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 1; // Set to registered + /// TODO(Subv): It is currently unknown what this value (0x400) means, + /// but i believe it is used as a global "LibraryApplet" id, to verify if there's + /// any LibApplet currently running. This is not verified. + if (app_id != 0x400) + cmd_buff[2] = 1; // Set to registered + else + cmd_buff[2] = 0; // Set to not registered LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); } @@ -145,50 +159,82 @@ void InquireNotification(Service::Interface* self) { void SendParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 src_app_id = cmd_buff[1]; - u32 dst_app_id = cmd_buff[2]; - u32 signal_type = cmd_buff[3]; - u32 buffer_size = cmd_buff[4]; - u32 value = cmd_buff[5]; - u32 handle = cmd_buff[6]; - u32 size = cmd_buff[7]; - u32 in_param_buffer_ptr = cmd_buff[8]; + u32 src_app_id = cmd_buff[1]; + u32 dst_app_id = cmd_buff[2]; + u32 signal_type = cmd_buff[3]; + u32 buffer_size = cmd_buff[4]; + u32 value = cmd_buff[5]; + u32 handle = cmd_buff[6]; + u32 size = cmd_buff[7]; + u32 buffer = cmd_buff[8]; + + std::shared_ptr<HLE::Applets::Applet> dest_applet = HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id)); + + if (dest_applet == nullptr) { + LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); + cmd_buff[1] = -1; // TODO(Subv): Find the right error code + return; + } - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + MessageParameter param; + param.buffer_size = buffer_size; + param.destination_id = dst_app_id; + param.sender_id = src_app_id; + param.object = Kernel::g_handle_table.GetGeneric(handle); + param.signal = signal_type; + param.data = Memory::GetPointer(buffer); + + cmd_buff[1] = dest_applet->ReceiveParameter(param).raw; LOG_WARNING(Service_APT, "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X," "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X", - src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, in_param_buffer_ptr); + src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, buffer); } void ReceiveParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; u32 buffer_size = cmd_buff[2]; + VAddr buffer = cmd_buff[0x104 >> 2]; + cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; - cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type - cmd_buff[4] = 0x10; // Parameter buffer size (16) - cmd_buff[5] = 0; + cmd_buff[2] = next_parameter.sender_id; + cmd_buff[3] = next_parameter.signal; // Signal type + cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size + cmd_buff[5] = 0x10; cmd_buff[6] = 0; - cmd_buff[7] = 0; - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); + if (next_parameter.object != nullptr) + cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); + cmd_buff[7] = (next_parameter.buffer_size << 14) | 2; + cmd_buff[8] = buffer; + + if (next_parameter.data) + memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size)); + + LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); } void GlanceParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; u32 buffer_size = cmd_buff[2]; + VAddr buffer = cmd_buff[0x104 >> 2]; cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; - cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type - cmd_buff[4] = 0x10; // Parameter buffer size (16) - cmd_buff[5] = 0; + cmd_buff[2] = next_parameter.sender_id; + cmd_buff[3] = next_parameter.signal; // Signal type + cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size + cmd_buff[5] = 0x10; cmd_buff[6] = 0; - cmd_buff[7] = 0; + if (next_parameter.object != nullptr) + cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); + cmd_buff[7] = (next_parameter.buffer_size << 14) | 2; + cmd_buff[8] = buffer; - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); + if (next_parameter.data) + memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size)); + + LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); } void CancelParameter(Service::Interface* self) { @@ -240,7 +286,7 @@ void AppletUtility(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // These are from 3dbrew - I'm not really sure what they're used for. - u32 unk = cmd_buff[1]; + u32 command = cmd_buff[1]; u32 buffer1_size = cmd_buff[2]; u32 buffer2_size = cmd_buff[3]; u32 buffer1_addr = cmd_buff[5]; @@ -248,8 +294,8 @@ void AppletUtility(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " - "buffer1_addr=0x%08X, buffer2_addr=0x%08X", unk, buffer1_size, buffer2_size, + LOG_WARNING(Service_APT, "(STUBBED) called command=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " + "buffer1_addr=0x%08X, buffer2_addr=0x%08X", command, buffer1_size, buffer2_size, buffer1_addr, buffer2_addr); } @@ -281,11 +327,41 @@ void GetAppCpuTimeLimit(Service::Interface* self) { LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value); } +void PrepareToStartLibraryApplet(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); + cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw; + LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); +} + +void StartLibraryApplet(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); + std::shared_ptr<HLE::Applets::Applet> applet = HLE::Applets::Applet::Get(applet_id); + + LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + + if (applet == nullptr) { + LOG_ERROR(Service_APT, "unknown applet id=%08X", applet_id); + cmd_buff[1] = -1; // TODO(Subv): Find the right error code + return; + } + + AppletStartupParameter parameter; + parameter.buffer_size = cmd_buff[2]; + parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]); + parameter.data = Memory::GetPointer(cmd_buff[6]); + + cmd_buff[1] = applet->Start(parameter).raw; +} + void Init() { AddService(new APT_A_Interface); AddService(new APT_S_Interface); AddService(new APT_U_Interface); + HLE::Applets::Init(); + // Load the shared system font (if available). // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided @@ -318,7 +394,10 @@ void Init() { // TODO(bunnei): Check if these are created in Initialize or on APT process startup. notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification"); - start_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Start"); + parameter_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Start"); + + next_parameter.signal = static_cast<u32>(SignalType::AppJustStarted); + next_parameter.destination_id = 0x300; } void Shutdown() { @@ -326,7 +405,11 @@ void Shutdown() { shared_font_mem = nullptr; lock = nullptr; notification_event = nullptr; - start_event = nullptr; + parameter_event = nullptr; + + next_parameter.object = nullptr; + + HLE::Applets::Shutdown(); } } // namespace APT diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index a03e1712a3..72972d05bb 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -4,13 +4,33 @@ #pragma once -#include <array> -#include "core/hle/result.h" -#include "core/hle/service/service.h" +#include "common/common_types.h" + +#include "core/hle/kernel/kernel.h" namespace Service { + +class Interface; + namespace APT { +/// Holds information about the parameters used in Send/Glance/ReceiveParameter +struct MessageParameter { + u32 sender_id = 0; + u32 destination_id = 0; + u32 signal = 0; + u32 buffer_size = 0; + Kernel::SharedPtr<Kernel::Object> object = nullptr; + u8* data = nullptr; +}; + +/// Holds information about the parameters used in StartLibraryApplet +struct AppletStartupParameter { + u32 buffer_size = 0; + Kernel::SharedPtr<Kernel::Object> object = nullptr; + u8* data = nullptr; +}; + /// Signals used by APT functions enum class SignalType : u32 { None = 0x0, @@ -23,7 +43,7 @@ enum class SignalType : u32 { }; /// App Id's used by APT functions -enum class AppID : u32 { +enum class AppletId : u32 { HomeMenu = 0x101, AlternateMenu = 0x103, Camera = 0x110, @@ -45,6 +65,9 @@ enum class AppID : u32 { SoftwareKeyboard2 = 0x401, }; +/// Send a parameter to the currently-running application, which will read it via ReceiveParameter +void SendParameter(const MessageParameter& parameter); + /** * APT::Initialize service function * Service function that initializes the APT process for the running application @@ -249,6 +272,33 @@ void SetAppCpuTimeLimit(Service::Interface* self); */ void GetAppCpuTimeLimit(Service::Interface* self); +/** + * APT::PrepareToStartLibraryApplet service function + * Inputs: + * 0 : Command header [0x00180040] + * 1 : Id of the applet to start + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +void PrepareToStartLibraryApplet(Service::Interface* self); + +/** + * APT::StartLibraryApplet service function + * Inputs: + * 0 : Command header [0x001E0084] + * 1 : Id of the applet to start + * 2 : Buffer size + * 3 : Always 0? + * 4 : Handle passed to the applet + * 5 : (Size << 14) | 2 + * 6 : Input buffer virtual address + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +void StartLibraryApplet(Service::Interface* self); + /// Initialize the APT service void Init(); diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 8649342458..88de339f97 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -10,19 +10,24 @@ namespace Service { namespace APT { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, GetLockHandle, "GetLockHandle?"}, - {0x00020080, Initialize, "Initialize?"}, - {0x00030040, Enable, "Enable?"}, - {0x00040040, nullptr, "Finalize?"}, - {0x00050040, nullptr, "GetAppletManInfo?"}, - {0x00060040, nullptr, "GetAppletInfo?"}, - {0x000D0080, ReceiveParameter, "ReceiveParameter?"}, - {0x000E0080, GlanceParameter, "GlanceParameter?"}, - {0x003B0040, nullptr, "CancelLibraryApplet?"}, - {0x00430040, NotifyToWait, "NotifyToWait?"}, - {0x00440000, GetSharedFont, "GetSharedFont?"}, - {0x004B00C2, AppletUtility, "AppletUtility?"}, - {0x00550040, nullptr, "WriteInputToNsState?"}, + {0x00010040, GetLockHandle, "GetLockHandle?"}, + {0x00020080, Initialize, "Initialize?"}, + {0x00030040, Enable, "Enable?"}, + {0x00040040, nullptr, "Finalize?"}, + {0x00050040, nullptr, "GetAppletManInfo?"}, + {0x00060040, nullptr, "GetAppletInfo?"}, + {0x00090040, IsRegistered, "IsRegistered"}, + {0x000C0104, SendParameter, "SendParameter"}, + {0x000D0080, ReceiveParameter, "ReceiveParameter"}, + {0x000E0080, GlanceParameter, "GlanceParameter"}, + {0x000F0100, CancelParameter, "CancelParameter"}, + {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, + {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, + {0x003B0040, nullptr, "CancelLibraryApplet?"}, + {0x00430040, NotifyToWait, "NotifyToWait?"}, + {0x00440000, GetSharedFont, "GetSharedFont?"}, + {0x004B00C2, AppletUtility, "AppletUtility?"}, + {0x00550040, nullptr, "WriteInputToNsState?"}, }; APT_A_Interface::APT_A_Interface() { diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index d006b59308..b724cd72bd 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -35,13 +35,13 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00150140, nullptr, "PrepareToStartApplication"}, {0x00160040, nullptr, "PreloadLibraryApplet"}, {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, - {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, + {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"}, {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, {0x001B00C4, nullptr, "StartApplication"}, {0x001C0000, nullptr, "WakeupApplication"}, {0x001D0000, nullptr, "CancelApplication"}, - {0x001E0084, nullptr, "StartLibraryApplet"}, + {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, nullptr, "StartSystemApplet"}, {0x00200044, nullptr, "StartNewestHomeMenu"}, {0x00210000, nullptr, "OrderToCloseApplication"}, diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index af4adba84f..a329514a6f 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -11,7 +11,9 @@ namespace CFG { const Interface::FunctionInfo FunctionTable[] = { {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, - {0x00020000, nullptr, "SecureInfoGetRegion"}, + {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, + {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, + {0x00050000, GetSystemModel, "GetSystemModel"}, {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x04020082, nullptr, "SetConfigInfoBlk4"}, {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index fafb43a2fa..a8cb15d603 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -310,4 +310,9 @@ Interface::Interface() { Register(FunctionTable); } +Interface::~Interface() { + semaphore_event = nullptr; + interrupt_event = nullptr; +} + } // namespace diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index fa13bfb7c9..b6f611db5c 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h @@ -4,6 +4,8 @@ #pragma once +#include <string> + #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -14,6 +16,7 @@ namespace DSP_DSP { class Interface : public Service::Interface { public: Interface(); + ~Interface() override; std::string GetPortName() const override { return "dsp::DSP"; diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp index 439c7282eb..3a5897d062 100644 --- a/src/core/hle/service/frd/frd_u.cpp +++ b/src/core/hle/service/frd/frd_u.cpp @@ -10,15 +10,26 @@ namespace Service { namespace FRD { const Interface::FunctionInfo FunctionTable[] = { + {0x00010000, nullptr, "HasLoggedIn"}, + {0x00030000, nullptr, "Login"}, + {0x00040000, nullptr, "Logout"}, {0x00050000, nullptr, "GetFriendKey"}, {0x00080000, nullptr, "GetMyPresence"}, + {0x00090000, nullptr, "GetMyScreenName"}, {0x00100040, nullptr, "GetPassword"}, + {0x00110080, nullptr, "GetFriendKeyList"}, {0x00190042, nullptr, "GetFriendFavoriteGame"}, {0x001A00C4, nullptr, "GetFriendInfo"}, {0x001B0080, nullptr, "IsOnFriendList"}, {0x001C0042, nullptr, "DecodeLocalFriendCode"}, {0x001D0002, nullptr, "SetCurrentlyPlayingText"}, - {0x00320042, nullptr, "SetClientSdkVersion"} + {0x00230000, nullptr, "GetLastResponseResult"}, + {0x00270040, nullptr, "ResultToErrorCode"}, + {0x00280244, nullptr, "RequestGameAuthentication"}, + {0x00290000, nullptr, "GetGameAuthenticationData"}, + {0x002A0204, nullptr, "RequestServiceLocator"}, + {0x002B0000, nullptr, "GetServiceLocatorData"}, + {0x00320042, nullptr, "SetClientSdkVersion"}, }; FRD_U_Interface::FRD_U_Interface() { diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 4e275cb13a..6c0df67c32 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -2,29 +2,35 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstddef> +#include <system_error> +#include <type_traits> #include <memory> #include <unordered_map> +#include <utility> #include <boost/container/flat_map.hpp> +#include "common/assert.h" #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" -#include "common/math_util.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/archive_extsavedata.h" -#include "core/file_sys/archive_romfs.h" #include "core/file_sys/archive_savedata.h" #include "core/file_sys/archive_savedatacheck.h" #include "core/file_sys/archive_sdmc.h" #include "core/file_sys/archive_systemsavedata.h" #include "core/file_sys/directory_backend.h" +#include "core/file_sys/file_backend.h" +#include "core/hle/hle.h" #include "core/hle/service/service.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" #include "core/hle/result.h" +#include "core/memory.h" // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. // Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 @@ -110,7 +116,7 @@ ResultVal<bool> File::SyncRequest() { u32 address = cmd_buff[6]; LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); - cmd_buff[2] = static_cast<u32>(backend->Write(offset, length, flush, Memory::GetPointer(address))); + cmd_buff[2] = static_cast<u32>(backend->Write(offset, length, flush != 0, Memory::GetPointer(address))); break; } diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 357b6b0969..f611259537 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -4,22 +4,25 @@ #pragma once +#include <memory> +#include <string> + #include "common/common_types.h" #include "core/file_sys/archive_backend.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/session.h" #include "core/hle/result.h" +namespace FileSys { +class DirectoryBackend; +class FileBackend; +} + /// The unique system identifier hash, also known as ID0 extern const std::string SYSTEM_ID; /// The scrambled SD card CID, also known as ID1 extern const std::string SDCARD_ID; -namespace Kernel { - class Session; -} - namespace Service { namespace FS { diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 0ad44e55e1..ae52083f92 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -115,7 +115,8 @@ static void OpenFileDirectly(Service::Interface* self) { ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); if (archive_handle.Failed()) { - LOG_ERROR(Service_FS, "failed to get a handle for archive"); + LOG_ERROR(Service_FS, "failed to get a handle for archive archive_id=0x%08X archive_path=%s", + archive_id, archive_path.DebugStr().c_str()); cmd_buff[1] = archive_handle.Code().raw; cmd_buff[3] = 0; return; @@ -128,7 +129,8 @@ static void OpenFileDirectly(Service::Interface* self) { cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); } else { cmd_buff[3] = 0; - LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); + LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%d", + file_path.DebugStr().c_str(), mode.hex, attributes); } } @@ -347,7 +349,8 @@ static void OpenDirectory(Service::Interface* self) { if (dir_res.Succeeded()) { cmd_buff[3] = Kernel::g_handle_table.Create(*dir_res).MoveFrom(); } else { - LOG_ERROR(Service_FS, "failed to get a handle for directory"); + LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s", + dirname_type, dirname_size, dir_path.DebugStr().c_str()); } } @@ -382,7 +385,8 @@ static void OpenArchive(Service::Interface* self) { cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF; } else { cmd_buff[2] = cmd_buff[3] = 0; - LOG_ERROR(Service_FS, "failed to get a handle for archive"); + LOG_ERROR(Service_FS, "failed to get a handle for archive archive_id=0x%08X archive_path=%s", + archive_id, archive_path.DebugStr().c_str()); } } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 4b0b4229d9..e93c1b4369 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -9,14 +9,17 @@ #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" -#include "gsp_gpu.h" #include "core/hw/hw.h" #include "core/hw/gpu.h" #include "core/hw/lcd.h" #include "video_core/gpu_debugger.h" +#include "video_core/debug_utils/debug_utils.h" +#include "video_core/renderer_base.h" #include "video_core/video_core.h" +#include "gsp_gpu.h" + // Main graphics debugger object - TODO: Here is probably not the best place for this GraphicsDebugger g_debugger; @@ -40,7 +43,7 @@ static inline u8* GetCommandBuffer(u32 thread_id) { return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); } -static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { +FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { DEBUG_ASSERT_MSG(screen_index < 2, "Invalid screen index"); // For each thread there are two FrameBufferUpdate fields @@ -203,7 +206,7 @@ static void ReadHWRegs(Service::Interface* self) { } } -static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { +void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { u32 base_address = 0x400000; PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); @@ -224,6 +227,9 @@ static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { &info.format); WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), 4, &info.shown_fb); + + if (Pica::g_debug_context) + Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); } /** @@ -347,7 +353,7 @@ void SignalInterrupt(InterruptId interrupt_id) { /// Executes the next GSP command static void ExecuteCommand(const Command& command, u32 thread_id) { // Utility function to convert register ID to address - auto WriteGPURegister = [](u32 id, u32 data) { + static auto WriteGPURegister = [](u32 id, u32 data) { GPU::Write<u32>(0x1EF00000 + 4 * id, data); }; @@ -389,19 +395,24 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { case CommandId::SET_MEMORY_FILL: { auto& params = command.memory_fill; - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_start)), - Memory::VirtualToPhysicalAddress(params.start1) >> 3); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_end)), - Memory::VirtualToPhysicalAddress(params.end1) >> 3); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].value_32bit)), params.value1); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].control)), params.control1); - - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_start)), - Memory::VirtualToPhysicalAddress(params.start2) >> 3); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_end)), - Memory::VirtualToPhysicalAddress(params.end2) >> 3); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].value_32bit)), params.value2); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].control)), params.control2); + + if (params.start1 != 0) { + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_start)), + Memory::VirtualToPhysicalAddress(params.start1) >> 3); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_end)), + Memory::VirtualToPhysicalAddress(params.end1) >> 3); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].value_32bit)), params.value1); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].control)), params.control1); + } + + if (params.start2 != 0) { + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_start)), + Memory::VirtualToPhysicalAddress(params.start2) >> 3); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_end)), + Memory::VirtualToPhysicalAddress(params.end2) >> 3); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].value_32bit)), params.value2); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].control)), params.control2); + } break; } @@ -446,6 +457,9 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { default: LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value()); } + + if (Pica::g_debug_context) + Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::GSPCommandProcessed, (void*)&command); } /** @@ -582,7 +596,7 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { Register(FunctionTable); - g_interrupt_event = 0; + g_interrupt_event = nullptr; using Kernel::MemoryPermission; g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, @@ -591,4 +605,9 @@ Interface::Interface() { g_thread_id = 0; } +Interface::~Interface() { + g_interrupt_event = nullptr; + g_shared_memory = nullptr; +} + } // namespace diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index a435d418aa..c89d0a4679 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h @@ -5,8 +5,11 @@ #pragma once #include <cstddef> +#include <string> #include "common/bit_field.h" +#include "common/common_types.h" + #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -158,6 +161,7 @@ static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrec class Interface : public Service::Interface { public: Interface(); + ~Interface() override; std::string GetPortName() const override { return "gsp::Gpu"; @@ -170,4 +174,14 @@ public: */ void SignalInterrupt(InterruptId interrupt_id); +void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); + +/** + * Retrieves the framebuffer info stored in the GSP shared memory for the + * specified screen index and thread id. + * @param thread_id GSP thread id of the process that accesses the structure that we are requesting. + * @param screen_index Index of the screen we are requesting (Top = 0, Bottom = 1). + * @returns FramebufferUpdate Information about the specified framebuffer. + */ +FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); } // namespace diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index c7c1bb5ab4..c35b13b25f 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "common/emu_window.h" #include "core/hle/service/service.h" #include "core/hle/service/hid/hid.h" @@ -34,6 +35,16 @@ static Kernel::SharedPtr<Kernel::Event> event_debug_pad; static u32 next_pad_index; static u32 next_touch_index; +const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> pad_mapping = { + Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y, + Service::HID::PAD_L, Service::HID::PAD_R, Service::HID::PAD_ZL, Service::HID::PAD_ZR, + Service::HID::PAD_START, Service::HID::PAD_SELECT, Service::HID::PAD_NONE, + Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT, + Service::HID::PAD_CIRCLE_UP, Service::HID::PAD_CIRCLE_DOWN, Service::HID::PAD_CIRCLE_LEFT, Service::HID::PAD_CIRCLE_RIGHT, + Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT +}; + + // TODO(peachum): // Add a method for setting analog input from joystick device for the circle Pad. // diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 68e2bcee02..517f4f2ae6 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -6,16 +6,18 @@ #include <array> -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/service.h" +#ifndef _MSC_VER +#include <cstddef> +#endif +#include "core/settings.h" #include "common/bit_field.h" - -namespace Kernel { - class SharedMemory; - class Event; -} +#include "common/common_funcs.h" +#include "common/common_types.h" namespace Service { + +class Interface; + namespace HID { /** @@ -155,6 +157,9 @@ const PadState PAD_CIRCLE_LEFT = {{1u << 29}}; const PadState PAD_CIRCLE_UP = {{1u << 30}}; const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; + +extern const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> pad_mapping; + /** * HID::GetIPCHandles service function * Inputs: diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp index 25b01860e5..18b22956ff 100644 --- a/src/core/hle/service/nwm_uds.cpp +++ b/src/core/hle/service/nwm_uds.cpp @@ -125,4 +125,8 @@ Interface::Interface() { Register(FunctionTable); } +Interface::~Interface() { + handle_event = nullptr; +} + } // namespace diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h index 82abdff28a..0ced2359ce 100644 --- a/src/core/hle/service/nwm_uds.h +++ b/src/core/hle/service/nwm_uds.h @@ -16,6 +16,7 @@ namespace NWM_UDS { class Interface : public Service::Interface { public: Interface(); + ~Interface() override; std::string GetPortName() const override { return "nwm::UDS"; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index d681cc3dc4..0de0b13a3f 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -54,7 +54,7 @@ static std::string MakeFunctionString(const char* name, const char* port_name, c std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name); for (int i = 1; i <= num_params; ++i) { - function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); + function_string += Common::StringFromFormat(", cmd_buff[%i]=0x%X", i, cmd_buff[i]); } return function_string; } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 77bfb9ff13..f311352120 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -4,6 +4,7 @@ #pragma once +#include <cstddef> #include <string> #include <unordered_map> @@ -11,8 +12,8 @@ #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/session.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Service diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 3f0c5e3881..d768a3fc76 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -2,40 +2,47 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <cstring> +#include <unordered_map> + +#include "common/assert.h" +#include "common/bit_field.h" +#include "common/common_types.h" #include "common/logging/log.h" -#include "common/platform.h" - -#if EMU_PLATFORM == PLATFORM_WINDOWS -#include <winsock2.h> -#include <ws2tcpip.h> - -// MinGW does not define several errno constants -#ifndef _MSC_VER -#define EBADMSG 104 -#define ENODATA 120 -#define ENOMSG 122 -#define ENOSR 124 -#define ENOSTR 125 -#define ETIME 137 -#define EIDRM 2001 -#define ENOLINK 2002 -#endif // _MSC_VER +#include "common/scope_exit.h" +#include "core/hle/kernel/session.h" +#include "core/hle/result.h" +#include "core/hle/service/soc_u.h" +#include "core/memory.h" + +#ifdef _WIN32 + #include <winsock2.h> + #include <ws2tcpip.h> + + // MinGW does not define several errno constants + #ifndef _MSC_VER + #define EBADMSG 104 + #define ENODATA 120 + #define ENOMSG 122 + #define ENOSR 124 + #define ENOSTR 125 + #define ETIME 137 + #define EIDRM 2001 + #define ENOLINK 2002 + #endif // _MSC_VER #else -#include <sys/socket.h> -#include <netinet/in.h> -#include <netdb.h> -#include <arpa/inet.h> -#include <fcntl.h> -#include <poll.h> + #include <cerrno> + #include <fcntl.h> + #include <netinet/in.h> + #include <netdb.h> + #include <poll.h> + #include <sys/socket.h> + #include <unistd.h> #endif -#include "common/scope_exit.h" -#include "core/hle/hle.h" -#include "core/hle/service/soc_u.h" -#include <unordered_map> - -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 # define WSAEAGAIN WSAEWOULDBLOCK # define WSAEMULTIHOP -1 // Invalid dummy value # define ERRNO(x) WSA##x @@ -328,6 +335,7 @@ static void Socket(Service::Interface* self) { if ((s32)socket_handle == SOCKET_ERROR_VALUE) result = TranslateError(GET_ERRNO); + cmd_buffer[0] = IPC::MakeHeader(2, 2, 0); cmd_buffer[1] = result; cmd_buffer[2] = socket_handle; } @@ -351,8 +359,9 @@ static void Bind(Service::Interface* self) { if (res != 0) result = TranslateError(GET_ERRNO); - cmd_buffer[2] = res; + cmd_buffer[0] = IPC::MakeHeader(5, 2, 0); cmd_buffer[1] = result; + cmd_buffer[2] = res; } static void Fcntl(Service::Interface* self) { @@ -369,7 +378,7 @@ static void Fcntl(Service::Interface* self) { }); if (ctr_cmd == 3) { // F_GETFL -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 posix_ret = 0; auto iter = open_sockets.find(socket_handle); if (iter != open_sockets.end() && iter->second.blocking == false) @@ -386,7 +395,7 @@ static void Fcntl(Service::Interface* self) { posix_ret |= 4; // O_NONBLOCK #endif } else if (ctr_cmd == 4) { // F_SETFL -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0; int ret = ioctlsocket(socket_handle, FIONBIO, &tmp); if (ret == SOCKET_ERROR_VALUE) { @@ -434,8 +443,9 @@ static void Listen(Service::Interface* self) { if (ret != 0) result = TranslateError(GET_ERRNO); - cmd_buffer[2] = ret; + cmd_buffer[0] = IPC::MakeHeader(3, 2, 0); cmd_buffer[1] = result; + cmd_buffer[2] = ret; } static void Accept(Service::Interface* self) { @@ -460,8 +470,10 @@ static void Accept(Service::Interface* self) { Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len); } - cmd_buffer[2] = ret; + cmd_buffer[0] = IPC::MakeHeader(4, 2, 2); cmd_buffer[1] = result; + cmd_buffer[2] = ret; + cmd_buffer[3] = IPC::StaticBufferDesc(static_cast<u32>(max_addr_len), 0); } static void GetHostId(Service::Interface* self) { @@ -675,26 +687,29 @@ static void Connect(Service::Interface* self) { int result = 0; if (ret != 0) result = TranslateError(GET_ERRNO); - cmd_buffer[2] = ret; + + cmd_buffer[0] = IPC::MakeHeader(6, 2, 0); cmd_buffer[1] = result; + cmd_buffer[2] = ret; } static void InitializeSockets(Service::Interface* self) { // TODO(Subv): Implement -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); #endif u32* cmd_buffer = Kernel::GetCommandBuffer(); - cmd_buffer[1] = 0; + cmd_buffer[0] = IPC::MakeHeader(1, 1, 0); + cmd_buffer[1] = RESULT_SUCCESS.raw; } static void ShutdownSockets(Service::Interface* self) { // TODO(Subv): Implement CleanupSockets(); -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 WSACleanup(); #endif @@ -745,7 +760,7 @@ Interface::Interface() { Interface::~Interface() { CleanupSockets(); -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 WSACleanup(); #endif } diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h index 483b3111b7..a091f597cc 100644 --- a/src/core/hle/service/soc_u.h +++ b/src/core/hle/service/soc_u.h @@ -4,6 +4,8 @@ #pragma once +#include <string> + #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 6c49fa6cf0..3b8c7c0e44 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -68,4 +68,8 @@ Interface::Interface() { Register(FunctionTable); } +Interface::~Interface() { + event_handle = nullptr; +} + } // namespace diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h index 653aba5cb3..96c89b0255 100644 --- a/src/core/hle/service/srv.h +++ b/src/core/hle/service/srv.h @@ -13,6 +13,7 @@ namespace SRV { class Interface : public Service::Interface { public: Interface(); + ~Interface() override; std::string GetPortName() const override { return "srv:"; diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index ac1967da8b..6e7dafaad4 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -12,6 +12,7 @@ #include "core/hw/y2r.h" #include "core/mem_map.h" +#include "video_core/renderer_base.h" #include "video_core/utils.h" #include "video_core/video_core.h" @@ -409,4 +410,8 @@ Interface::Interface() { Register(FunctionTable); } +Interface::~Interface() { + completion_event = nullptr; +} + } // namespace diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index 7df47fcb92..3965a55456 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h @@ -5,9 +5,11 @@ #pragma once #include <array> +#include <string> #include "common/common_types.h" +#include "core/hle/result.h" #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -110,6 +112,7 @@ struct ConversionConfiguration { class Interface : public Service::Interface { public: Interface(); + ~Interface() override; std::string GetPortName() const override { return "y2r:u"; diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp index 4014eee98e..26d87c7e2f 100644 --- a/src/core/hle/shared_page.cpp +++ b/src/core/hle/shared_page.cpp @@ -4,12 +4,6 @@ #include <cstring> -#include "common/common_types.h" -#include "common/common_funcs.h" - -#include "core/core.h" -#include "core/memory.h" -#include "core/hle/config_mem.h" #include "core/hle/shared_page.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h index fd2ab66a29..db6a5340be 100644 --- a/src/core/hle/shared_page.h +++ b/src/core/hle/shared_page.h @@ -10,9 +10,12 @@ * write access, according to 3dbrew; this is not emulated) */ +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/memory.h" + //////////////////////////////////////////////////////////////////////////////////////////////////// namespace SharedPage { diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 6cde4fc87e..bb64fdfb76 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -22,6 +22,7 @@ #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" +#include "core/hle/kernel/vm_manager.h" #include "core/hle/function_wrappers.h" #include "core/hle/result.h" @@ -333,7 +334,7 @@ static ResultCode GetResourceLimit(Handle* resource_limit, Handle process_handle /// Get resource limit current values static ResultCode GetResourceLimitCurrentValues(s64* values, Handle resource_limit_handle, u32* names, - s32 name_count) { + u32 name_count) { LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", resource_limit_handle, names, name_count); @@ -349,7 +350,7 @@ static ResultCode GetResourceLimitCurrentValues(s64* values, Handle resource_lim /// Get resource limit max values static ResultCode GetResourceLimitLimitValues(s64* values, Handle resource_limit_handle, u32* names, - s32 name_count) { + u32 name_count) { LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", resource_limit_handle, names, name_count); @@ -529,12 +530,33 @@ static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) return RESULT_SUCCESS; } -/// Query memory -static ResultCode QueryMemory(void* info, void* out, u32 addr) { - LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); +/// Query process memory +static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info, Handle process_handle, u32 addr) { + using Kernel::Process; + Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle); + if (process == nullptr) + return ERR_INVALID_HANDLE; + + auto vma = process->address_space->FindVMA(addr); + + if (vma == process->address_space->vma_map.end()) + return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); + + memory_info->base_address = vma->second.base; + memory_info->permission = static_cast<u32>(vma->second.permissions); + memory_info->size = vma->second.size; + memory_info->state = static_cast<u32>(vma->second.meminfo_state); + + page_info->flags = 0; + LOG_TRACE(Kernel_SVC, "called process=0x%08X addr=0x%08X", process_handle, addr); return RESULT_SUCCESS; } +/// Query memory +static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 addr) { + return QueryProcessMemory(memory_info, page_info, Kernel::CurrentProcess, addr); +} + /// Create an event static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) { using Kernel::Event; @@ -806,13 +828,12 @@ static const FunctionDef SVC_Table[] = { {0x7A, nullptr, "AddCodeSegment"}, {0x7B, nullptr, "Backdoor"}, {0x7C, nullptr, "KernelSetState"}, - {0x7D, nullptr, "QueryProcessMemory"}, + {0x7D, HLE::Wrap<QueryProcessMemory>, "QueryProcessMemory"}, }; Common::Profiling::TimingCategory profiler_svc("SVC Calls"); -static const FunctionDef* GetSVCInfo(u32 opcode) { - u32 func_num = opcode & 0xFFFFFF; // 8 bits +static const FunctionDef* GetSVCInfo(u32 func_num) { if (func_num >= ARRAY_SIZE(SVC_Table)) { LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); return nullptr; @@ -820,10 +841,10 @@ static const FunctionDef* GetSVCInfo(u32 opcode) { return &SVC_Table[func_num]; } -void CallSVC(u32 opcode) { +void CallSVC(u32 immediate) { Common::Profiling::ScopeTimer timer_svc(profiler_svc); - const FunctionDef *info = GetSVCInfo(opcode); + const FunctionDef* info = GetSVCInfo(immediate); if (info) { if (info->func) { info->func(); diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h index 4389aa73dd..12de9ffbeb 100644 --- a/src/core/hle/svc.h +++ b/src/core/hle/svc.h @@ -41,6 +41,6 @@ enum ArbitrationType { namespace SVC { -void CallSVC(u32 opcode); +void CallSVC(u32 immediate); } // namespace diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 7471def578..3ccbc03b26 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -2,17 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> +#include <type_traits> + #include "common/color.h" #include "common/common_types.h" - -#include "core/arm/arm_interface.h" +#include "common/logging/log.h" +#include "common/vector_math.h" #include "core/settings.h" -#include "core/core.h" #include "core/memory.h" #include "core/core_timing.h" -#include "core/hle/hle.h" #include "core/hle/service/gsp_gpu.h" #include "core/hle/service/dsp_dsp.h" #include "core/hle/service/hid/hid.h" @@ -20,10 +21,17 @@ #include "core/hw/hw.h" #include "core/hw/gpu.h" +#include "core/tracer/recorder.h" + #include "video_core/command_processor.h" +#include "video_core/hwrasterizer_base.h" +#include "video_core/renderer_base.h" #include "video_core/utils.h" #include "video_core/video_core.h" +#include "video_core/debug_utils/debug_utils.h" + + namespace GPU { Regs g_regs; @@ -53,6 +61,29 @@ inline void Read(T &var, const u32 raw_addr) { var = g_regs[addr / 4]; } +static Math::Vec4<u8> DecodePixel(Regs::PixelFormat input_format, const u8* src_pixel) { + switch (input_format) { + case Regs::PixelFormat::RGBA8: + return Color::DecodeRGBA8(src_pixel); + + case Regs::PixelFormat::RGB8: + return Color::DecodeRGB8(src_pixel); + + case Regs::PixelFormat::RGB565: + return Color::DecodeRGB565(src_pixel); + + case Regs::PixelFormat::RGB5A1: + return Color::DecodeRGB5A1(src_pixel); + + case Regs::PixelFormat::RGBA4: + return Color::DecodeRGBA4(src_pixel); + + default: + LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", input_format); + return {0, 0, 0, 0}; + } +} + template <typename T> inline void Write(u32 addr, const T data) { addr -= HW::VADDR_GPU; @@ -75,39 +106,43 @@ inline void Write(u32 addr, const T data) { const bool is_second_filler = (index != GPU_REG_INDEX(memory_fill_config[0].trigger)); auto& config = g_regs.memory_fill_config[is_second_filler]; - if (config.address_start && config.trigger) { - u8* start = Memory::GetPhysicalPointer(config.GetStartAddress()); - u8* end = Memory::GetPhysicalPointer(config.GetEndAddress()); - - if (config.fill_24bit) { - // fill with 24-bit values - for (u8* ptr = start; ptr < end; ptr += 3) { - ptr[0] = config.value_24bit_r; - ptr[1] = config.value_24bit_g; - ptr[2] = config.value_24bit_b; + if (config.trigger) { + if (config.address_start) { // Some games pass invalid values here + u8* start = Memory::GetPhysicalPointer(config.GetStartAddress()); + u8* end = Memory::GetPhysicalPointer(config.GetEndAddress()); + + if (config.fill_24bit) { + // fill with 24-bit values + for (u8* ptr = start; ptr < end; ptr += 3) { + ptr[0] = config.value_24bit_r; + ptr[1] = config.value_24bit_g; + ptr[2] = config.value_24bit_b; + } + } else if (config.fill_32bit) { + // fill with 32-bit values + for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr) + *ptr = config.value_32bit; + } else { + // fill with 16-bit values + for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr) + *ptr = config.value_16bit; } - } else if (config.fill_32bit) { - // fill with 32-bit values - for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr) - *ptr = config.value_32bit; - } else { - // fill with 16-bit values - for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr) - *ptr = config.value_16bit; - } - LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); + LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); - config.trigger = 0; - config.finished = 1; + if (!is_second_filler) { + GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0); + } else { + GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); + } - if (!is_second_filler) { - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0); - } else { - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); + VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); } - VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); + // Reset "trigger" flag and set the "finish" flag + // NOTE: This was confirmed to happen on hardware even if "address_start" is zero. + config.trigger = 0; + config.finished = 1; } break; } @@ -116,6 +151,10 @@ inline void Write(u32 addr, const T data) { { const auto& config = g_regs.display_transfer_config; if (config.trigger & 1) { + + if (Pica::g_debug_context) + Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::IncomingDisplayTransfer, nullptr); + u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); @@ -125,11 +164,18 @@ inline void Write(u32 addr, const T data) { break; } - unsigned horizontal_scale = (config.scaling != config.NoScale) ? 2 : 1; - unsigned vertical_scale = (config.scaling == config.ScaleXY) ? 2 : 1; + if (config.output_tiled && + (config.scaling == config.ScaleXY || config.scaling == config.ScaleX)) { + LOG_CRITICAL(HW_GPU, "Scaling is only implemented on tiled input"); + UNIMPLEMENTED(); + break; + } - u32 output_width = config.output_width / horizontal_scale; - u32 output_height = config.output_height / vertical_scale; + bool horizontal_scale = config.scaling != config.NoScale; + bool vertical_scale = config.scaling == config.ScaleXY; + + u32 output_width = config.output_width >> horizontal_scale; + u32 output_height = config.output_height >> vertical_scale; u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); @@ -153,16 +199,14 @@ inline void Write(u32 addr, const T data) { break; } - // TODO(Subv): Implement the box filter when scaling is enabled - // right now we're just skipping the extra pixels. for (u32 y = 0; y < output_height; ++y) { for (u32 x = 0; x < output_width; ++x) { - Math::Vec4<u8> src_color = { 0, 0, 0, 0 }; + Math::Vec4<u8> src_color; // Calculate the [x,y] position of the input image // based on the current output position and the scale - u32 input_x = x * horizontal_scale; - u32 input_y = y * vertical_scale; + u32 input_x = x << horizontal_scale; + u32 input_y = y << vertical_scale; if (config.flip_vertically) { // Flip the y value of the output data, @@ -177,46 +221,49 @@ inline void Write(u32 addr, const T data) { u32 dst_offset; if (config.output_tiled) { - // Interpret the input as linear and the output as tiled - u32 coarse_y = y & ~7; - u32 stride = output_width * dst_bytes_per_pixel; - - src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; - dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; + if (!config.dont_swizzle) { + // Interpret the input as linear and the output as tiled + u32 coarse_y = y & ~7; + u32 stride = output_width * dst_bytes_per_pixel; + + src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; + } else { + // Both input and output are linear + src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } } else { - // Interpret the input as tiled and the output as linear - u32 coarse_y = input_y & ~7; - u32 stride = config.input_width * src_bytes_per_pixel; - - src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; - dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + if (!config.dont_swizzle) { + // Interpret the input as tiled and the output as linear + u32 coarse_y = input_y & ~7; + u32 stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } else { + // Both input and output are tiled + u32 out_coarse_y = y & ~7; + u32 out_stride = output_width * dst_bytes_per_pixel; + + u32 in_coarse_y = input_y & ~7; + u32 in_stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + in_coarse_y * in_stride; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + out_coarse_y * out_stride; + } } const u8* src_pixel = src_pointer + src_offset; - switch (config.input_format) { - case Regs::PixelFormat::RGBA8: - src_color = Color::DecodeRGBA8(src_pixel); - break; - - case Regs::PixelFormat::RGB8: - src_color = Color::DecodeRGB8(src_pixel); - break; - - case Regs::PixelFormat::RGB565: - src_color = Color::DecodeRGB565(src_pixel); - break; - - case Regs::PixelFormat::RGB5A1: - src_color = Color::DecodeRGB5A1(src_pixel); - break; - - case Regs::PixelFormat::RGBA4: - src_color = Color::DecodeRGBA4(src_pixel); - break; - - default: - LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", config.input_format.Value()); - break; + src_color = DecodePixel(config.input_format, src_pixel); + if (config.scaling == config.ScaleX) { + Math::Vec4<u8> pixel = DecodePixel(config.input_format, src_pixel + src_bytes_per_pixel); + src_color = ((src_color + pixel) / 2).Cast<u8>(); + } else if (config.scaling == config.ScaleXY) { + Math::Vec4<u8> pixel1 = DecodePixel(config.input_format, src_pixel + 1 * src_bytes_per_pixel); + Math::Vec4<u8> pixel2 = DecodePixel(config.input_format, src_pixel + 2 * src_bytes_per_pixel); + Math::Vec4<u8> pixel3 = DecodePixel(config.input_format, src_pixel + 3 * src_bytes_per_pixel); + src_color = (((src_color + pixel1) + (pixel2 + pixel3)) / 4).Cast<u8>(); } u8* dst_pixel = dst_pointer + dst_offset; @@ -254,6 +301,7 @@ inline void Write(u32 addr, const T data) { config.GetPhysicalOutputAddress(), output_width, output_height, config.output_format.Value(), config.flags); + g_regs.display_transfer_config.trigger = 0; GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size); @@ -268,7 +316,14 @@ inline void Write(u32 addr, const T data) { if (config.trigger & 1) { u32* buffer = (u32*)Memory::GetPhysicalPointer(config.GetPhysicalAddress()); + + if (Pica::g_debug_context && Pica::g_debug_context->recorder) { + Pica::g_debug_context->recorder->MemoryAccessed((u8*)buffer, config.size * sizeof(u32), config.GetPhysicalAddress()); + } + Pica::CommandProcessor::ProcessCommandList(buffer, config.size); + + g_regs.command_processor_config.trigger = 0; } break; } @@ -276,6 +331,13 @@ inline void Write(u32 addr, const T data) { default: break; } + + // Notify tracer about the register write + // This is happening *after* handling the write to make sure we properly catch all memory reads. + if (Pica::g_debug_context && Pica::g_debug_context->recorder) { + // addr + GPU VBase - IO VBase + IO PBase + Pica::g_debug_context->recorder->RegisterWritten<T>(addr + 0x1EF00000 - 0x1EC00000 + 0x10100000, data); + } } // Explicitly instantiate template functions because we aren't defining this in the header: diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 699bcd2a56..daad506feb 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -5,6 +5,7 @@ #pragma once #include <cstddef> +#include <type_traits> #include "common/assert.h" #include "common/bit_field.h" @@ -202,6 +203,7 @@ struct Regs { BitField< 0, 1, u32> flip_vertically; // flips input data vertically BitField< 1, 1, u32> output_tiled; // Converts from linear to tiled format BitField< 3, 1, u32> raw_copy; // Copies the data without performing any processing + BitField< 5, 1, u32> dont_swizzle; BitField< 8, 3, PixelFormat> input_format; BitField<12, 3, PixelFormat> output_format; diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index c7006a4987..b5fdbf9c14 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp @@ -15,6 +15,21 @@ template <typename T> inline void Read(T &var, const u32 addr) { switch (addr & 0xFFFFF000) { case VADDR_GPU: + case VADDR_GPU + 0x1000: + case VADDR_GPU + 0x2000: + case VADDR_GPU + 0x3000: + case VADDR_GPU + 0x4000: + case VADDR_GPU + 0x5000: + case VADDR_GPU + 0x6000: + case VADDR_GPU + 0x7000: + case VADDR_GPU + 0x8000: + case VADDR_GPU + 0x9000: + case VADDR_GPU + 0xA000: + case VADDR_GPU + 0xB000: + case VADDR_GPU + 0xC000: + case VADDR_GPU + 0xD000: + case VADDR_GPU + 0xE000: + case VADDR_GPU + 0xF000: GPU::Read(var, addr); break; case VADDR_LCD: @@ -29,6 +44,21 @@ template <typename T> inline void Write(u32 addr, const T data) { switch (addr & 0xFFFFF000) { case VADDR_GPU: + case VADDR_GPU + 0x1000: + case VADDR_GPU + 0x2000: + case VADDR_GPU + 0x3000: + case VADDR_GPU + 0x4000: + case VADDR_GPU + 0x5000: + case VADDR_GPU + 0x6000: + case VADDR_GPU + 0x7000: + case VADDR_GPU + 0x8000: + case VADDR_GPU + 0x9000: + case VADDR_GPU + 0xA000: + case VADDR_GPU + 0xB000: + case VADDR_GPU + 0xC000: + case VADDR_GPU + 0xD000: + case VADDR_GPU + 0xE000: + case VADDR_GPU + 0xF000: GPU::Write(addr, data); break; case VADDR_LCD: diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp index 963c8d9810..6f93709e32 100644 --- a/src/core/hw/lcd.cpp +++ b/src/core/hw/lcd.cpp @@ -7,11 +7,12 @@ #include "common/common_types.h" #include "common/logging/log.h" -#include "core/arm/arm_interface.h" -#include "core/hle/hle.h" #include "core/hw/hw.h" #include "core/hw/lcd.h" +#include "core/tracer/recorder.h" +#include "video_core/debug_utils/debug_utils.h" + namespace LCD { Regs g_regs; @@ -42,6 +43,13 @@ inline void Write(u32 addr, const T data) { } g_regs[index] = static_cast<u32>(data); + + // Notify tracer about the register write + // This is happening *after* handling the write to make sure we properly catch all memory reads. + if (Pica::g_debug_context && Pica::g_debug_context->recorder) { + // addr + GPU VBase - IO VBase + IO PBase + Pica::g_debug_context->recorder->RegisterWritten<T>(addr + HW::VADDR_LCD - 0x1EC00000 + 0x10100000, data); + } } // Explicitly instantiate template functions because we aren't defining this in the header: diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h index 8631eb201a..bcce6d8cfb 100644 --- a/src/core/hw/lcd.h +++ b/src/core/hw/lcd.h @@ -5,6 +5,7 @@ #pragma once #include <cstddef> +#include <type_traits> #include "common/bit_field.h" #include "common/common_funcs.h" diff --git a/src/core/hw/y2r.cpp b/src/core/hw/y2r.cpp index 5b7fb39e17..f80e26ecdb 100644 --- a/src/core/hw/y2r.cpp +++ b/src/core/hw/y2r.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <array> -#include <numeric> +#include <cstddef> +#include <memory> #include "common/assert.h" #include "common/color.h" @@ -109,7 +111,7 @@ static void SendData(const u32* input, ConversionBuffer& buf, int amount_of_data while (output < unit_end) { u32 color = *input++; Math::Vec4<u8> col_vec{ - (color >> 24) & 0xFF, (color >> 16) & 0xFF, (color >> 8) & 0xFF, alpha, + (u8)(color >> 24), (u8)(color >> 16), (u8)(color >> 8), alpha }; switch (output_format) { diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 14aeebebb3..530837d088 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -19,7 +19,7 @@ namespace Loader { -/** +/* * File layout: * - File header * - Code, rodata and data relocation table headers @@ -39,13 +39,16 @@ namespace Loader { * The entrypoint is always the start of the code segment. * The BSS section must be cleared manually by the application. */ + enum THREEDSX_Error { ERROR_NONE = 0, ERROR_READ = 1, ERROR_FILE = 2, ERROR_ALLOC = 3 }; + static const u32 RELOCBUFSIZE = 512; +static const unsigned int NUM_SEGMENTS = 3; // File header #pragma pack(1) @@ -98,7 +101,10 @@ static u32 TranslateAddr(u32 addr, const THREEloadinfo *loadinfo, u32* offsets) return loadinfo->seg_addrs[2] + addr - offsets[1]; } -static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) +using Kernel::SharedPtr; +using Kernel::CodeSet; + +static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, SharedPtr<CodeSet>* out_codeset) { if (!file.IsOpen()) return ERROR_FILE; @@ -116,15 +122,13 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) &~0xFFF; loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) &~0xFFF; u32 offsets[2] = { loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] }; - u32 data_load_size = (hdr.data_seg_size - hdr.bss_size + 0xFFF) &~0xFFF; - u32 bss_load_size = loadinfo.seg_sizes[2] - data_load_size; - u32 n_reloc_tables = hdr.reloc_hdr_size / 4; - std::vector<u8> all_mem(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2] + 3 * n_reloc_tables); + u32 n_reloc_tables = hdr.reloc_hdr_size / sizeof(u32); + std::vector<u8> program_image(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]); loadinfo.seg_addrs[0] = base_addr; loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0]; loadinfo.seg_addrs[2] = loadinfo.seg_addrs[1] + loadinfo.seg_sizes[1]; - loadinfo.seg_ptrs[0] = &all_mem[0]; + loadinfo.seg_ptrs[0] = program_image.data(); loadinfo.seg_ptrs[1] = loadinfo.seg_ptrs[0] + loadinfo.seg_sizes[0]; loadinfo.seg_ptrs[2] = loadinfo.seg_ptrs[1] + loadinfo.seg_sizes[1]; @@ -132,10 +136,9 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) file.Seek(hdr.header_size, SEEK_SET); // Read the relocation headers - u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size); - - for (unsigned current_segment : {0, 1, 2}) { - size_t size = n_reloc_tables * 4; + std::vector<u32> relocs(n_reloc_tables * NUM_SEGMENTS); + for (unsigned int current_segment = 0; current_segment < NUM_SEGMENTS; ++current_segment) { + size_t size = n_reloc_tables * sizeof(u32); if (file.ReadBytes(&relocs[current_segment * n_reloc_tables], size) != size) return ERROR_READ; } @@ -152,7 +155,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size); // Relocate the segments - for (unsigned current_segment : {0, 1, 2}) { + for (unsigned int current_segment = 0; current_segment < NUM_SEGMENTS; ++current_segment) { for (unsigned current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) { u32 n_relocs = relocs[current_segment * n_reloc_tables + current_segment_reloc_table]; if (current_segment_reloc_table >= 2) { @@ -160,7 +163,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR); continue; } - static THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; + THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; u32* pos = (u32*)loadinfo.seg_ptrs[current_segment]; const u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4); @@ -179,7 +182,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) pos += table.skip; s32 num_patches = table.patch; while (0 < num_patches && pos < end_pos) { - u32 in_addr = (char*)pos - (char*)&all_mem[0]; + u32 in_addr = (u8*)pos - program_image.data(); u32 addr = TranslateAddr(*pos, &loadinfo, offsets); LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n", base_addr + in_addr, addr, current_segment_reloc_table, *pos); @@ -188,7 +191,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) *pos = (addr); break; case 1: - *pos = (addr - in_addr); + *pos = static_cast<u32>(addr - in_addr); break; default: break; //this should never happen @@ -201,14 +204,29 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) } } - // Write the data - memcpy(Memory::GetPointer(base_addr), &all_mem[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]); + // Create the CodeSet + SharedPtr<CodeSet> code_set = CodeSet::Create("", 0); + + code_set->code.offset = loadinfo.seg_ptrs[0] - program_image.data(); + code_set->code.addr = loadinfo.seg_addrs[0]; + code_set->code.size = loadinfo.seg_sizes[0]; + + code_set->rodata.offset = loadinfo.seg_ptrs[1] - program_image.data(); + code_set->rodata.addr = loadinfo.seg_addrs[1]; + code_set->rodata.size = loadinfo.seg_sizes[1]; - LOG_DEBUG(Loader, "CODE: %u pages\n", loadinfo.seg_sizes[0] / 0x1000); - LOG_DEBUG(Loader, "RODATA: %u pages\n", loadinfo.seg_sizes[1] / 0x1000); - LOG_DEBUG(Loader, "DATA: %u pages\n", data_load_size / 0x1000); - LOG_DEBUG(Loader, "BSS: %u pages\n", bss_load_size / 0x1000); + code_set->data.offset = loadinfo.seg_ptrs[2] - program_image.data(); + code_set->data.addr = loadinfo.seg_addrs[2]; + code_set->data.size = loadinfo.seg_sizes[2]; + code_set->entrypoint = code_set->code.addr; + code_set->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); + + LOG_DEBUG(Loader, "code size: 0x%X", loadinfo.seg_sizes[0]); + LOG_DEBUG(Loader, "rodata size: 0x%X", loadinfo.seg_sizes[1]); + LOG_DEBUG(Loader, "data size: 0x%X (including 0x%X of bss)", loadinfo.seg_sizes[2], hdr.bss_size); + + *out_codeset = code_set; return ERROR_NONE; } @@ -228,19 +246,22 @@ ResultStatus AppLoader_THREEDSX::Load() { if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; - if (!file->IsOpen()) + if (!file.IsOpen()) + return ResultStatus::Error; + + SharedPtr<CodeSet> codeset; + if (Load3DSXFile(file, Memory::PROCESS_IMAGE_VADDR, &codeset) != ERROR_NONE) return ResultStatus::Error; + codeset->name = filename; - Kernel::g_current_process = Kernel::Process::Create(filename, 0); + Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); Kernel::g_current_process->svc_access_mask.set(); Kernel::g_current_process->address_mappings = default_address_mappings; // Attach the default resource limit (APPLICATION) to the process Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - Load3DSXFile(*file, Memory::PROCESS_IMAGE_VADDR); - - Kernel::g_current_process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); + Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h index 096b3ec205..a0aa0c533d 100644 --- a/src/core/loader/3dsx.h +++ b/src/core/loader/3dsx.h @@ -17,7 +17,7 @@ namespace Loader { /// Loads an 3DSX file class AppLoader_THREEDSX final : public AppLoader { public: - AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file, std::string filename) + AppLoader_THREEDSX(FileUtil::IOFile&& file, std::string filename) : AppLoader(std::move(file)), filename(std::move(filename)) {} /** diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index f00753a79d..5d7264f12a 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> #include <string> #include <memory> @@ -10,11 +11,14 @@ #include "common/logging/log.h" #include "common/symbols.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/loader/elf.h" #include "core/memory.h" +using Kernel::SharedPtr; +using Kernel::CodeSet; + //////////////////////////////////////////////////////////////////////////////////////////////////// // ELF Header Constants @@ -96,6 +100,12 @@ enum ElfSectionFlags #define PT_LOPROC 0x70000000 #define PT_HIPROC 0x7FFFFFFF +// Segment flags +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 +#define PF_MASKPROC 0xF0000000 + typedef unsigned int Elf32_Addr; typedef unsigned short Elf32_Half; typedef unsigned int Elf32_Off; @@ -192,7 +202,7 @@ public: ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } u32 GetEntryPoint() const { return entryPoint; } u32 GetFlags() const { return (u32)(header->e_flags); } - void LoadInto(u32 vaddr); + SharedPtr<CodeSet> LoadInto(u32 vaddr); bool LoadSymbols(); int GetNumSegments() const { return (int)(header->e_phnum); } @@ -248,7 +258,7 @@ const char *ElfReader::GetSectionName(int section) const { return nullptr; } -void ElfReader::LoadInto(u32 vaddr) { +SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx); // Should we relocate? @@ -263,22 +273,63 @@ void ElfReader::LoadInto(u32 vaddr) { LOG_DEBUG(Loader, "%i segments:", header->e_phnum); // First pass : Get the bits into RAM - u32 segment_addr[32]; u32 base_addr = relocate ? vaddr : 0; - for (unsigned i = 0; i < header->e_phnum; i++) { - Elf32_Phdr* p = segments + i; - LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, + u32 total_image_size = 0; + for (unsigned int i = 0; i < header->e_phnum; ++i) { + Elf32_Phdr* p = &segments[i]; + if (p->p_type == PT_LOAD) { + total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; + } + } + + std::vector<u8> program_image(total_image_size); + size_t current_image_position = 0; + + SharedPtr<CodeSet> codeset = CodeSet::Create("", 0); + + for (unsigned int i = 0; i < header->e_phnum; ++i) { + Elf32_Phdr* p = &segments[i]; + LOG_DEBUG(Loader, "Type: %i Vaddr: %08X Filesz: %8X Memsz: %8X ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); if (p->p_type == PT_LOAD) { - segment_addr[i] = base_addr + p->p_vaddr; - memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz); - LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i], - p->p_memsz); + CodeSet::Segment* codeset_segment; + u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); + if (permission_flags == (PF_R | PF_X)) { + codeset_segment = &codeset->code; + } else if (permission_flags == (PF_R)) { + codeset_segment = &codeset->rodata; + } else if (permission_flags == (PF_R | PF_W)) { + codeset_segment = &codeset->data; + } else { + LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id %u with flags %X", i, p->p_flags); + continue; + } + + if (codeset_segment->size != 0) { + LOG_ERROR(Loader, "ELF has more than one segment of the same type. Skipping extra segment (id %i)", i); + continue; + } + + u32 segment_addr = base_addr + p->p_vaddr; + u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF; + + codeset_segment->offset = current_image_position; + codeset_segment->addr = segment_addr; + codeset_segment->size = aligned_size; + + memcpy(&program_image[current_image_position], GetSegmentPtr(i), p->p_filesz); + current_image_position += aligned_size; } } + + codeset->entrypoint = base_addr + header->e_entry; + codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); + LOG_DEBUG(Loader, "Done loading."); + + return codeset; } SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { @@ -340,29 +391,29 @@ ResultStatus AppLoader_ELF::Load() { if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; - if (!file->IsOpen()) + if (!file.IsOpen()) return ResultStatus::Error; // Reset read pointer in case this file has been read before. - file->Seek(0, SEEK_SET); + file.Seek(0, SEEK_SET); - u32 size = static_cast<u32>(file->GetSize()); + size_t size = file.GetSize(); std::unique_ptr<u8[]> buffer(new u8[size]); - if (file->ReadBytes(&buffer[0], size) != size) + if (file.ReadBytes(&buffer[0], size) != size) return ResultStatus::Error; - Kernel::g_current_process = Kernel::Process::Create(filename, 0); + ElfReader elf_reader(&buffer[0]); + SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); + codeset->name = filename; + + Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); Kernel::g_current_process->svc_access_mask.set(); Kernel::g_current_process->address_mappings = default_address_mappings; // Attach the default resource limit (APPLICATION) to the process Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - ElfReader elf_reader(&buffer[0]); - elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); - // TODO: Fill application title - - Kernel::g_current_process->Run(elf_reader.GetEntryPoint(), 48, Kernel::DEFAULT_STACK_SIZE); + Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 32841606a3..c6a5ebe991 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -17,7 +17,7 @@ namespace Loader { /// Loads an ELF/AXF file class AppLoader_ELF final : public AppLoader { public: - AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file, std::string filename) + AppLoader_ELF(FileUtil::IOFile&& file, std::string filename) : AppLoader(std::move(file)), filename(std::move(filename)) { } /** diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 8b14edf00d..f5b349a776 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -2,10 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <memory> #include <string> #include "common/logging/log.h" #include "common/make_unique.h" +#include "common/string_util.h" #include "core/file_sys/archive_romfs.h" #include "core/hle/kernel/process.h" @@ -54,18 +56,18 @@ static FileType IdentifyFile(FileUtil::IOFile& file) { static FileType GuessFromExtension(const std::string& extension_) { std::string extension = Common::ToLower(extension_); - if (extension == ".elf") + if (extension == ".elf" || extension == ".axf") return FileType::ELF; - else if (extension == ".axf") - return FileType::ELF; - else if (extension == ".cxi") - return FileType::CXI; - else if (extension == ".cci") - return FileType::CCI; - else if (extension == ".3ds") + + if (extension == ".cci" || extension == ".3ds") return FileType::CCI; - else if (extension == ".3dsx") + + if (extension == ".cxi") + return FileType::CXI; + + if (extension == ".3dsx") return FileType::THREEDSX; + return FileType::Unknown; } @@ -88,8 +90,8 @@ static const char* GetFileTypeString(FileType type) { } ResultStatus LoadFile(const std::string& filename) { - std::unique_ptr<FileUtil::IOFile> file(new FileUtil::IOFile(filename, "rb")); - if (!file->IsOpen()) { + FileUtil::IOFile file(filename, "rb"); + if (!file.IsOpen()) { LOG_ERROR(Loader, "Failed to load file %s", filename.c_str()); return ResultStatus::Error; } @@ -97,7 +99,7 @@ ResultStatus LoadFile(const std::string& filename) { std::string filename_filename, filename_extension; Common::SplitPath(filename, nullptr, &filename_filename, &filename_extension); - FileType type = IdentifyFile(*file); + FileType type = IdentifyFile(file); FileType filename_type = GuessFromExtension(filename_extension); if (type != filename_type) { @@ -122,7 +124,7 @@ ResultStatus LoadFile(const std::string& filename) { case FileType::CXI: case FileType::CCI: { - AppLoader_NCCH app_loader(std::move(file)); + AppLoader_NCCH app_loader(std::move(file), filename); // Load application and RomFS if (ResultStatus::Success == app_loader.Load()) { diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 87e16fb986..a37d3348ce 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -4,12 +4,18 @@ #pragma once +#include <algorithm> +#include <initializer_list> +#include <memory> +#include <string> #include <vector> #include "common/common_types.h" #include "common/file_util.h" -#include "core/hle/kernel/process.h" +namespace Kernel { +struct AddressMapping; +} //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace @@ -46,7 +52,7 @@ static inline u32 MakeMagic(char a, char b, char c, char d) { /// Interface for loading an application class AppLoader : NonCopyable { public: - AppLoader(std::unique_ptr<FileUtil::IOFile>&& file) : file(std::move(file)) { } + AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) { } virtual ~AppLoader() { } /** @@ -60,7 +66,7 @@ public: * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - virtual ResultStatus ReadCode(std::vector<u8>& buffer) const { + virtual ResultStatus ReadCode(std::vector<u8>& buffer) { return ResultStatus::ErrorNotImplemented; } @@ -69,7 +75,7 @@ public: * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - virtual ResultStatus ReadIcon(std::vector<u8>& buffer) const { + virtual ResultStatus ReadIcon(std::vector<u8>& buffer) { return ResultStatus::ErrorNotImplemented; } @@ -78,7 +84,7 @@ public: * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - virtual ResultStatus ReadBanner(std::vector<u8>& buffer) const { + virtual ResultStatus ReadBanner(std::vector<u8>& buffer) { return ResultStatus::ErrorNotImplemented; } @@ -87,22 +93,25 @@ public: * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - virtual ResultStatus ReadLogo(std::vector<u8>& buffer) const { + virtual ResultStatus ReadLogo(std::vector<u8>& buffer) { return ResultStatus::ErrorNotImplemented; } /** * Get the RomFS of the application - * @param buffer Reference to buffer to store data + * Since the RomFS can be huge, we return a file reference instead of copying to a buffer + * @param romfs_file The file containing the RomFS + * @param offset The offset the romfs begins on + * @param size The size of the romfs * @return ResultStatus result of function */ - virtual ResultStatus ReadRomFS(std::vector<u8>& buffer) const { + virtual ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { return ResultStatus::ErrorNotImplemented; } protected: - std::unique_ptr<FileUtil::IOFile> file; - bool is_loaded = false; + FileUtil::IOFile file; + bool is_loaded = false; }; /** diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 08993c4fae..094d741007 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> +#include <cstring> #include <memory> #include "common/logging/log.h" @@ -10,7 +11,7 @@ #include "common/string_util.h" #include "common/swap.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/loader/ncch.h" #include "core/memory.h" @@ -116,7 +117,10 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { return FileType::Error; } -ResultStatus AppLoader_NCCH::LoadExec() const { +ResultStatus AppLoader_NCCH::LoadExec() { + using Kernel::SharedPtr; + using Kernel::CodeSet; + if (!is_loaded) return ResultStatus::ErrorNotLoaded; @@ -125,7 +129,30 @@ ResultStatus AppLoader_NCCH::LoadExec() const { std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( (const char*)exheader_header.codeset_info.name, 8); u64 program_id = *reinterpret_cast<u64_le const*>(&ncch_header.program_id[0]); - Kernel::g_current_process = Kernel::Process::Create(process_name, program_id); + + SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, program_id); + + codeset->code.offset = 0; + codeset->code.addr = exheader_header.codeset_info.text.address; + codeset->code.size = exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE; + + codeset->rodata.offset = codeset->code.offset + codeset->code.size; + codeset->rodata.addr = exheader_header.codeset_info.ro.address; + codeset->rodata.size = exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE; + + // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just + // to the regular size. Playing it safe for now. + u32 bss_page_size = (exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF; + code.resize(code.size() + bss_page_size, 0); + + codeset->data.offset = codeset->rodata.offset + codeset->rodata.size; + codeset->data.addr = exheader_header.codeset_info.data.address; + codeset->data.size = exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE + bss_page_size; + + codeset->entrypoint = codeset->code.addr; + codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); + + Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); // Attach a resource limit to the process based on the resource limit category Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory( @@ -136,18 +163,16 @@ ResultStatus AppLoader_NCCH::LoadExec() const { std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps)); Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); - Memory::WriteBlock(entry_point, &code[0], code.size()); - s32 priority = exheader_header.arm11_system_local_caps.priority; u32 stack_size = exheader_header.codeset_info.stack_size; - Kernel::g_current_process->Run(entry_point, priority, stack_size); + Kernel::g_current_process->Run(priority, stack_size); return ResultStatus::Success; } return ResultStatus::Error; } -ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const { - if (!file->IsOpen()) +ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) { + if (!file.IsOpen()) return ResultStatus::Error; LOG_DEBUG(Loader, "%d sections:", kMaxSections); @@ -161,7 +186,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& section.offset, section.size, section.name); s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); - file->Seek(section_offset, SEEK_SET); + file.Seek(section_offset, SEEK_SET); if (is_compressed) { // Section is compressed, read compressed .code section... @@ -172,7 +197,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& return ResultStatus::ErrorMemoryAllocationFailed; } - if (file->ReadBytes(&temp_buffer[0], section.size) != section.size) + if (file.ReadBytes(&temp_buffer[0], section.size) != section.size) return ResultStatus::Error; // Decompress .code section... @@ -183,7 +208,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& } else { // Section is uncompressed... buffer.resize(section.size); - if (file->ReadBytes(&buffer[0], section.size) != section.size) + if (file.ReadBytes(&buffer[0], section.size) != section.size) return ResultStatus::Error; } return ResultStatus::Success; @@ -196,21 +221,21 @@ ResultStatus AppLoader_NCCH::Load() { if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; - if (!file->IsOpen()) + if (!file.IsOpen()) return ResultStatus::Error; // Reset read pointer in case this file has been read before. - file->Seek(0, SEEK_SET); + file.Seek(0, SEEK_SET); - if (file->ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) + if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) return ResultStatus::Error; // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); ncch_offset = 0x4000; - file->Seek(ncch_offset, SEEK_SET); - file->ReadBytes(&ncch_header, sizeof(NCCH_Header)); + file.Seek(ncch_offset, SEEK_SET); + file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); } // Verify we are loading the correct file type... @@ -219,7 +244,7 @@ ResultStatus AppLoader_NCCH::Load() { // Read ExHeader... - if (file->ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) + if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) return ResultStatus::Error; is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; @@ -239,7 +264,6 @@ ResultStatus AppLoader_NCCH::Load() { LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size); LOG_DEBUG(Loader, "Core version: %d" , core_version); LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority); - LOG_DEBUG(Loader, "Resource limit descriptor: 0x%08X", exheader_header.arm11_system_local_caps.resource_limit_descriptor); LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category); // Read ExeFS... @@ -250,8 +274,8 @@ ResultStatus AppLoader_NCCH::Load() { LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset); LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size); - file->Seek(exefs_offset + ncch_offset, SEEK_SET); - if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) + file.Seek(exefs_offset + ncch_offset, SEEK_SET); + if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) return ResultStatus::Error; is_loaded = true; // Set state to loaded @@ -259,24 +283,24 @@ ResultStatus AppLoader_NCCH::Load() { return LoadExec(); // Load the executable into memory for booting } -ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const { +ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { return LoadSectionExeFS(".code", buffer); } -ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) const { +ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) { return LoadSectionExeFS("icon", buffer); } -ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) const { +ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) { return LoadSectionExeFS("banner", buffer); } -ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) const { +ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) { return LoadSectionExeFS("logo", buffer); } -ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { - if (!file->IsOpen()) +ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { + if (!file.IsOpen()) return ResultStatus::Error; // Check if the NCCH has a RomFS... @@ -287,12 +311,17 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); - buffer.resize(romfs_size); + if (file.GetSize () < romfs_offset + romfs_size) + return ResultStatus::Error; - file->Seek(romfs_offset, SEEK_SET); - if (file->ReadBytes(&buffer[0], romfs_size) != romfs_size) + // We reopen the file, to allow its position to be independent from file's + romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb"); + if (!romfs_file->IsOpen()) return ResultStatus::Error; + offset = romfs_offset; + size = romfs_size; + return ResultStatus::Success; } LOG_DEBUG(Loader, "NCCH has no RomFS"); diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 29e39d2c0c..b4374a4760 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -163,7 +163,8 @@ namespace Loader { /// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) class AppLoader_NCCH final : public AppLoader { public: - AppLoader_NCCH(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } + AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath) + : AppLoader(std::move(file)), filepath(filepath) { } /** * Returns the type of the file @@ -183,35 +184,35 @@ public: * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - ResultStatus ReadCode(std::vector<u8>& buffer) const override; + ResultStatus ReadCode(std::vector<u8>& buffer) override; /** * Get the icon (typically icon section) of the application * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - ResultStatus ReadIcon(std::vector<u8>& buffer) const override; + ResultStatus ReadIcon(std::vector<u8>& buffer) override; /** * Get the banner (typically banner section) of the application * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - ResultStatus ReadBanner(std::vector<u8>& buffer) const override; + ResultStatus ReadBanner(std::vector<u8>& buffer) override; /** * Get the logo (typically logo section) of the application * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - ResultStatus ReadLogo(std::vector<u8>& buffer) const override; + ResultStatus ReadLogo(std::vector<u8>& buffer) override; /** * Get the RomFS of the application * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; + ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) override; private: @@ -221,13 +222,13 @@ private: * @param buffer Vector to read data into * @return ResultStatus result of function */ - ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const; + ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer); /** * Loads .code section into memory for booting * @return ResultStatus result of function */ - ResultStatus LoadExec() const; + ResultStatus LoadExec(); bool is_compressed = false; @@ -244,6 +245,8 @@ private: NCCH_Header ncch_header; ExeFs_Header exefs_header; ExHeader_Header exheader_header; + + std::string filepath; }; } // namespace Loader diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp index bf814b945a..cbe993fbe0 100644 --- a/src/core/mem_map.cpp +++ b/src/core/mem_map.cpp @@ -3,13 +3,14 @@ // Refer to the license.txt file included. #include <map> +#include <memory> +#include <utility> +#include <vector> #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/config_mem.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/result.h" #include "core/hle/shared_page.h" @@ -31,7 +32,6 @@ struct MemoryArea { // We don't declare the IO regions in here since its handled by other means. static MemoryArea memory_areas[] = { - {PROCESS_IMAGE_VADDR, PROCESS_IMAGE_MAX_SIZE, "Process Image"}, // ExeFS:/.code is loaded here {HEAP_VADDR, HEAP_SIZE, "Heap"}, // Application heap (main memory) {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory {LINEAR_HEAP_VADDR, LINEAR_HEAP_SIZE, "Linear Heap"}, // Linear heap (main memory) @@ -131,13 +131,13 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) { return addr | 0x80000000; } -// TODO(yuriks): Move this into Process -static Kernel::VMManager address_space; - void Init() { - using namespace Kernel; - InitMemoryMap(); + LOG_DEBUG(HW_Memory, "initialized OK"); +} + +void InitLegacyAddressSpace(Kernel::VMManager& address_space) { + using namespace Kernel; for (MemoryArea& area : memory_areas) { auto block = std::make_shared<std::vector<u8>>(area.size); @@ -151,14 +151,11 @@ void Init() { auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR, (u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom(); address_space.Reprotect(shared_page_vma, VMAPermission::Read); - - LOG_DEBUG(HW_Memory, "initialized OK"); } void Shutdown() { heap_map.clear(); heap_linear_map.clear(); - address_space.Reset(); LOG_DEBUG(HW_Memory, "shutdown OK"); } diff --git a/src/core/mem_map.h b/src/core/mem_map.h index ba50914a8d..229ef82c5d 100644 --- a/src/core/mem_map.h +++ b/src/core/mem_map.h @@ -6,9 +6,14 @@ #include "common/common_types.h" +namespace Kernel { +class VMManager; +} + namespace Memory { void Init(); +void InitLegacyAddressSpace(Kernel::VMManager& address_space); void Shutdown(); /** diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 28844a9154..1f66bb27d6 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -9,9 +9,6 @@ #include "common/logging/log.h" #include "common/swap.h" -#include "core/hle/config_mem.h" -#include "core/hle/shared_page.h" -#include "core/hw/hw.h" #include "core/mem_map.h" #include "core/memory.h" #include "core/memory_setup.h" @@ -62,14 +59,12 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { while (base != end) { ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); - if (current_page_table->attributes[base] != PageType::Unmapped && type != PageType::Unmapped) { - LOG_ERROR(HW_Memory, "overlapping memory ranges at %08X", base * PAGE_SIZE); - } current_page_table->attributes[base] = type; current_page_table->pointers[base] = memory; base += 1; - memory += PAGE_SIZE; + if (memory != nullptr) + memory += PAGE_SIZE; } } diff --git a/src/core/memory.h b/src/core/memory.h index 0b8ff9ec4e..418609de07 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -4,6 +4,8 @@ #pragma once +#include <cstddef> + #include "common/common_types.h" namespace Memory { diff --git a/src/core/settings.h b/src/core/settings.h index 5a70d157a6..2775ee257c 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -5,34 +5,42 @@ #pragma once #include <string> +#include <array> namespace Settings { +namespace NativeInput { +enum Values { + A, B, X, Y, + L, R, ZL, ZR, + START, SELECT, HOME, + DUP, DDOWN, DLEFT, DRIGHT, + SUP, SDOWN, SLEFT, SRIGHT, + CUP, CDOWN, CLEFT, CRIGHT, + NUM_INPUTS +}; +static const std::array<const char*, NUM_INPUTS> Mapping = { + "pad_a", "pad_b", "pad_x", "pad_y", + "pad_l", "pad_r", "pad_zl", "pad_zr", + "pad_start", "pad_select", "pad_home", + "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", + "pad_sup", "pad_sdown", "pad_sleft", "pad_sright", + "pad_cup", "pad_cdown", "pad_cleft", "pad_cright" +}; +static const std::array<Values, NUM_INPUTS> All = { + A, B, X, Y, + L, R, ZL, ZR, + START, SELECT, HOME, + DUP, DDOWN, DLEFT, DRIGHT, + SUP, SDOWN, SLEFT, SRIGHT, + CUP, CDOWN, CLEFT, CRIGHT +}; +} + + struct Values { // Controls - int pad_a_key; - int pad_b_key; - int pad_x_key; - int pad_y_key; - int pad_l_key; - int pad_r_key; - int pad_zl_key; - int pad_zr_key; - int pad_start_key; - int pad_select_key; - int pad_home_key; - int pad_dup_key; - int pad_ddown_key; - int pad_dleft_key; - int pad_dright_key; - int pad_sup_key; - int pad_sdown_key; - int pad_sleft_key; - int pad_sright_key; - int pad_cup_key; - int pad_cdown_key; - int pad_cleft_key; - int pad_cright_key; + std::array<int, NativeInput::NUM_INPUTS> input_mappings; // Core int frame_skip; diff --git a/src/core/tracer/citrace.h b/src/core/tracer/citrace.h new file mode 100644 index 0000000000..5deb6ce9eb --- /dev/null +++ b/src/core/tracer/citrace.h @@ -0,0 +1,101 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <cstdint> + +namespace CiTrace { + +// NOTE: Things are stored in little-endian + +#pragma pack(1) + +struct CTHeader { + static const char* ExpectedMagicWord() { + return "CiTr"; + } + + static uint32_t ExpectedVersion() { + return 1; + } + + char magic[4]; + uint32_t version; + uint32_t header_size; + + struct { + // NOTE: Register range sizes are technically hardware-constants, but the actual limits + // aren't known. Hence we store the presumed limits along the offsets. + // Sizes are given in uint32_t units. + uint32_t gpu_registers; + uint32_t gpu_registers_size; + uint32_t lcd_registers; + uint32_t lcd_registers_size; + uint32_t pica_registers; + uint32_t pica_registers_size; + uint32_t default_attributes; + uint32_t default_attributes_size; + uint32_t vs_program_binary; + uint32_t vs_program_binary_size; + uint32_t vs_swizzle_data; + uint32_t vs_swizzle_data_size; + uint32_t vs_float_uniforms; + uint32_t vs_float_uniforms_size; + uint32_t gs_program_binary; + uint32_t gs_program_binary_size; + uint32_t gs_swizzle_data; + uint32_t gs_swizzle_data_size; + uint32_t gs_float_uniforms; + uint32_t gs_float_uniforms_size; + + // Other things we might want to store here: + // - Initial framebuffer data, maybe even a full copy of FCRAM/VRAM + // - Lookup tables for fragment lighting + // - Lookup tables for procedural textures + } initial_state_offsets; + + uint32_t stream_offset; + uint32_t stream_size; +}; + +enum CTStreamElementType : uint32_t { + FrameMarker = 0xE1, + MemoryLoad = 0xE2, + RegisterWrite = 0xE3, +}; + +struct CTMemoryLoad { + uint32_t file_offset; + uint32_t size; + uint32_t physical_address; + uint32_t pad; +}; + +struct CTRegisterWrite { + uint32_t physical_address; + + enum : uint32_t { + SIZE_8 = 0xD1, + SIZE_16 = 0xD2, + SIZE_32 = 0xD3, + SIZE_64 = 0xD4 + } size; + + // TODO: Make it clearer which bits of this member are used for sizes other than 32 bits + uint64_t value; +}; + +struct CTStreamElement { + CTStreamElementType type; + + union { + CTMemoryLoad memory_load; + CTRegisterWrite register_write; + }; +}; + +#pragma pack() + +} diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp new file mode 100644 index 0000000000..656706c0c5 --- /dev/null +++ b/src/core/tracer/recorder.cpp @@ -0,0 +1,187 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> + +#include "common/assert.h" +#include "common/file_util.h" +#include "common/logging/log.h" + +#include "recorder.h" + +namespace CiTrace { + +Recorder::Recorder(const InitialState& initial_state) : initial_state(initial_state) { + +} + +void Recorder::Finish(const std::string& filename) { + // Setup CiTrace header + CTHeader header; + std::memcpy(header.magic, CTHeader::ExpectedMagicWord(), 4); + header.version = CTHeader::ExpectedVersion(); + header.header_size = sizeof(CTHeader); + + // Calculate file offsets + auto& initial = header.initial_state_offsets; + + initial.gpu_registers_size = initial_state.gpu_registers.size(); + initial.lcd_registers_size = initial_state.lcd_registers.size(); + initial.pica_registers_size = initial_state.pica_registers.size(); + initial.default_attributes_size = initial_state.default_attributes.size(); + initial.vs_program_binary_size = initial_state.vs_program_binary.size(); + initial.vs_swizzle_data_size = initial_state.vs_swizzle_data.size(); + initial.vs_float_uniforms_size = initial_state.vs_float_uniforms.size(); + initial.gs_program_binary_size = initial_state.gs_program_binary.size(); + initial.gs_swizzle_data_size = initial_state.gs_swizzle_data.size(); + initial.gs_float_uniforms_size = initial_state.gs_float_uniforms.size(); + header.stream_size = stream.size(); + + initial.gpu_registers = sizeof(header); + initial.lcd_registers = initial.gpu_registers + initial.gpu_registers_size * sizeof(u32); + initial.pica_registers = initial.lcd_registers + initial.lcd_registers_size * sizeof(u32);; + initial.default_attributes = initial.pica_registers + initial.pica_registers_size * sizeof(u32); + initial.vs_program_binary = initial.default_attributes + initial.default_attributes_size * sizeof(u32); + initial.vs_swizzle_data = initial.vs_program_binary + initial.vs_program_binary_size * sizeof(u32); + initial.vs_float_uniforms = initial.vs_swizzle_data + initial.vs_swizzle_data_size * sizeof(u32); + initial.gs_program_binary = initial.vs_float_uniforms + initial.vs_float_uniforms_size * sizeof(u32); + initial.gs_swizzle_data = initial.gs_program_binary + initial.gs_program_binary_size * sizeof(u32); + initial.gs_float_uniforms = initial.gs_swizzle_data + initial.gs_swizzle_data_size * sizeof(u32); + header.stream_offset = initial.gs_float_uniforms + initial.gs_float_uniforms_size * sizeof(u32); + + // Iterate through stream elements, update relevant stream element data + for (auto& stream_element : stream) { + switch (stream_element.data.type) { + case MemoryLoad: + { + auto& file_offset = memory_regions[stream_element.hash]; + if (!stream_element.uses_existing_data) { + file_offset = header.stream_offset; + } + stream_element.data.memory_load.file_offset = file_offset; + break; + } + + default: + // Other commands don't use any extra data + DEBUG_ASSERT(stream_element.extra_data.size() == 0); + break; + } + header.stream_offset += stream_element.extra_data.size(); + } + + try { + // Open file and write header + FileUtil::IOFile file(filename, "wb"); + size_t written = file.WriteObject(header); + if (written != 1 || file.Tell() != initial.gpu_registers) + throw "Failed to write header"; + + // Write initial state + written = file.WriteArray(initial_state.gpu_registers.data(), initial_state.gpu_registers.size()); + if (written != initial_state.gpu_registers.size() || file.Tell() != initial.lcd_registers) + throw "Failed to write GPU registers"; + + written = file.WriteArray(initial_state.lcd_registers.data(), initial_state.lcd_registers.size()); + if (written != initial_state.lcd_registers.size() || file.Tell() != initial.pica_registers) + throw "Failed to write LCD registers"; + + written = file.WriteArray(initial_state.pica_registers.data(), initial_state.pica_registers.size()); + if (written != initial_state.pica_registers.size() || file.Tell() != initial.default_attributes) + throw "Failed to write Pica registers"; + + written = file.WriteArray(initial_state.default_attributes.data(), initial_state.default_attributes.size()); + if (written != initial_state.default_attributes.size() || file.Tell() != initial.vs_program_binary) + throw "Failed to write default vertex attributes"; + + written = file.WriteArray(initial_state.vs_program_binary.data(), initial_state.vs_program_binary.size()); + if (written != initial_state.vs_program_binary.size() || file.Tell() != initial.vs_swizzle_data) + throw "Failed to write vertex shader program binary"; + + written = file.WriteArray(initial_state.vs_swizzle_data.data(), initial_state.vs_swizzle_data.size()); + if (written != initial_state.vs_swizzle_data.size() || file.Tell() != initial.vs_float_uniforms) + throw "Failed to write vertex shader swizzle data"; + + written = file.WriteArray(initial_state.vs_float_uniforms.data(), initial_state.vs_float_uniforms.size()); + if (written != initial_state.vs_float_uniforms.size() || file.Tell() != initial.gs_program_binary) + throw "Failed to write vertex shader float uniforms"; + + written = file.WriteArray(initial_state.gs_program_binary.data(), initial_state.gs_program_binary.size()); + if (written != initial_state.gs_program_binary.size() || file.Tell() != initial.gs_swizzle_data) + throw "Failed to write geomtry shader program binary"; + + written = file.WriteArray(initial_state.gs_swizzle_data.data(), initial_state.gs_swizzle_data.size()); + if (written != initial_state.gs_swizzle_data.size() || file.Tell() != initial.gs_float_uniforms) + throw "Failed to write geometry shader swizzle data"; + + written = file.WriteArray(initial_state.gs_float_uniforms.data(), initial_state.gs_float_uniforms.size()); + if (written != initial_state.gs_float_uniforms.size() || file.Tell() != initial.gs_float_uniforms + sizeof(u32) * initial.gs_float_uniforms_size) + throw "Failed to write geometry shader float uniforms"; + + // Iterate through stream elements, write "extra data" + for (const auto& stream_element : stream) { + if (stream_element.extra_data.size() == 0) + continue; + + written = file.WriteBytes(stream_element.extra_data.data(), stream_element.extra_data.size()); + if (written != stream_element.extra_data.size()) + throw "Failed to write extra data"; + } + + if (file.Tell() != header.stream_offset) + throw "Unexpected end of extra data"; + + // Write actual stream elements + for (const auto& stream_element : stream) { + if (1 != file.WriteObject(stream_element.data)) + throw "Failed to write stream element"; + } + } catch(const char* str) { + LOG_ERROR(HW_GPU, "Writing CiTrace file failed: %s", str); + } +} + +void Recorder::FrameFinished() { + stream.push_back( { FrameMarker } ); +} + +void Recorder::MemoryAccessed(const u8* data, u32 size, u32 physical_address) { + StreamElement element = { MemoryLoad }; + element.data.memory_load.size = size; + element.data.memory_load.physical_address = physical_address; + + // Compute hash over given memory region to check if the contents are already stored internally + boost::crc_32_type result; + result.process_bytes(data, size); + element.hash = result.checksum(); + + element.uses_existing_data = (memory_regions.find(element.hash) != memory_regions.end()); + if (!element.uses_existing_data) { + element.extra_data.resize(size); + memcpy(element.extra_data.data(), data, size); + memory_regions.insert({element.hash, 0}); // file offset will be initialized in Finish() + } + + stream.push_back(element); +} + +template<typename T> +void Recorder::RegisterWritten(u32 physical_address, T value) { + StreamElement element = { RegisterWrite }; + element.data.register_write.size = (sizeof(T) == 1) ? CTRegisterWrite::SIZE_8 + : (sizeof(T) == 2) ? CTRegisterWrite::SIZE_16 + : (sizeof(T) == 4) ? CTRegisterWrite::SIZE_32 + : CTRegisterWrite::SIZE_64; + element.data.register_write.physical_address = physical_address; + element.data.register_write.value = value; + + stream.push_back(element); +} + +template void Recorder::RegisterWritten(u32,u8); +template void Recorder::RegisterWritten(u32,u16); +template void Recorder::RegisterWritten(u32,u32); +template void Recorder::RegisterWritten(u32,u64); + +} diff --git a/src/core/tracer/recorder.h b/src/core/tracer/recorder.h new file mode 100644 index 0000000000..6e4b700156 --- /dev/null +++ b/src/core/tracer/recorder.h @@ -0,0 +1,90 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <unordered_map> +#include <vector> + +#include <boost/crc.hpp> + +#include "common/common_types.h" + +#include "citrace.h" + +namespace CiTrace { + +class Recorder { +public: + struct InitialState { + std::vector<u32> gpu_registers; + std::vector<u32> lcd_registers; + std::vector<u32> pica_registers; + std::vector<u32> default_attributes; + std::vector<u32> vs_program_binary; + std::vector<u32> vs_swizzle_data; + std::vector<u32> vs_float_uniforms; + std::vector<u32> gs_program_binary; + std::vector<u32> gs_swizzle_data; + std::vector<u32> gs_float_uniforms; + }; + + /** + * Recorder constructor + * @param default_attributes Pointer to an array of 32-bit-aligned 24-bit floating point values. + * @param vs_float_uniforms Pointer to an array of 32-bit-aligned 24-bit floating point values. + */ + Recorder(const InitialState& initial_state); + + /// Finish recording of this Citrace and save it using the given filename. + void Finish(const std::string& filename); + + /// Mark end of a frame + void FrameFinished(); + + /** + * Store a copy of the given memory range in the recording. + * @note Use this whenever the GPU is about to access a particular memory region. + * @note The implementation will make sure to minimize redundant memory updates. + */ + void MemoryAccessed(const u8* data, u32 size, u32 physical_address); + + /** + * Record a register write. + * @note Use this whenever a GPU-related MMIO register has been written to. + */ + template<typename T> + void RegisterWritten(u32 physical_address, T value); + +private: + // Initial state of recording start + InitialState initial_state; + + // Command stream + struct StreamElement { + CTStreamElement data; + + /** + * Extra data to store along "core" data. + * This is e.g. used for data used in MemoryUpdates. + */ + std::vector<u8> extra_data; + + /// Optional CRC hash (e.g. for hashing memory regions) + boost::crc_32_type::value_type hash; + + /// If true, refer to data already written to the output file instead of extra_data + bool uses_existing_data; + }; + + std::vector<StreamElement> stream; + + /** + * Internal cache which maps hashes of memory contents to file offsets at which those memory + * contents are stored. + */ + std::unordered_map<boost::crc_32_type::value_type /*hash*/, u32 /*file_offset*/> memory_regions; +}; + +} // namespace diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 5c7f4ae181..162108301a 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -2,7 +2,6 @@ set(SRCS renderer_opengl/generated/gl_3_2_core.c renderer_opengl/gl_rasterizer.cpp renderer_opengl/gl_rasterizer_cache.cpp - renderer_opengl/gl_resource_manager.cpp renderer_opengl/gl_shader_util.cpp renderer_opengl/gl_state.cpp renderer_opengl/renderer_opengl.cpp diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp index 943f3eb35d..558b49d60e 100644 --- a/src/video_core/clipper.cpp +++ b/src/video_core/clipper.cpp @@ -94,7 +94,7 @@ void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) { // NOTE: We clip against a w=epsilon plane to guarantee that the output has a positive w value. // TODO: Not sure if this is a valid approach. Also should probably instead use the smallest // epsilon possible within float24 accuracy. - static const float24 EPSILON = float24::FromFloat32(0.00001); + static const float24 EPSILON = float24::FromFloat32(0.00001f); static const float24 f0 = float24::FromFloat32(0.0); static const float24 f1 = float24::FromFloat32(1.0); static const std::array<ClippingEdge, 7> clipping_edges = {{ @@ -153,7 +153,7 @@ void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) { "Triangle %lu/%lu at position (%.3f, %.3f, %.3f, %.3f), " "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)", - i, output_list->size(), + i + 1, output_list->size() - 2, vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(), vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(), vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(), diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index b46fadd9f1..ef9584abda 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -6,18 +6,20 @@ #include "common/profiler.h" +#include "core/hle/service/gsp_gpu.h" +#include "core/hw/gpu.h" +#include "core/settings.h" + +#include "debug_utils/debug_utils.h" + #include "clipper.h" #include "command_processor.h" #include "math.h" #include "pica.h" #include "primitive_assembly.h" +#include "renderer_base.h" #include "vertex_shader.h" #include "video_core.h" -#include "core/hle/service/gsp_gpu.h" -#include "core/hw/gpu.h" -#include "core/settings.h" - -#include "debug_utils/debug_utils.h" namespace Pica { @@ -43,12 +45,12 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { if (GPU::g_skip_frame && id != PICA_REG_INDEX(trigger_irq)) return; - // TODO: Figure out how register masking acts on e.g. vs_uniform_setup.set_value + // TODO: Figure out how register masking acts on e.g. vs.uniform_setup.set_value u32 old_value = regs[id]; regs[id] = (old_value & ~mask) | (value & mask); if (g_debug_context) - g_debug_context->OnEvent(DebugContext::Event::CommandLoaded, reinterpret_cast<void*>(&id)); + g_debug_context->OnEvent(DebugContext::Event::PicaCommandLoaded, reinterpret_cast<void*>(&id)); DebugUtils::OnPicaRegWrite(id, regs[id]); @@ -58,10 +60,50 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); break; + // Load default vertex input attributes + case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[0], 0x233): + case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[1], 0x234): + case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[2], 0x235): + { + // TODO: Does actual hardware indeed keep an intermediate buffer or does + // it directly write the values? + default_attr_write_buffer[default_attr_counter++] = value; + + // Default attributes are written in a packed format such that four float24 values are encoded in + // three 32-bit numbers. We write to internal memory once a full such vector is + // written. + if (default_attr_counter >= 3) { + default_attr_counter = 0; + + auto& setup = regs.vs_default_attributes_setup; + + if (setup.index >= 16) { + LOG_ERROR(HW_GPU, "Invalid VS default attribute index %d", (int)setup.index); + break; + } + + Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index]; + + // NOTE: The destination component order indeed is "backwards" + attribute.w = float24::FromRawFloat24(default_attr_write_buffer[0] >> 8); + attribute.z = float24::FromRawFloat24(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF)); + attribute.y = float24::FromRawFloat24(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF)); + attribute.x = float24::FromRawFloat24(default_attr_write_buffer[2] & 0xFFFFFF); + + LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index, + attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(), + attribute.w.ToFloat32()); + + // TODO: Verify that this actually modifies the register! + setup.index = setup.index + 1; + } + break; + } + case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[0], 0x23c): case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[1], 0x23d): { - unsigned index = id - PICA_REG_INDEX(command_buffer.trigger[0]); + unsigned index = static_cast<unsigned>(id - PICA_REG_INDEX(command_buffer.trigger[0])); u32* head_ptr = (u32*)Memory::GetPhysicalPointer(regs.command_buffer.GetPhysicalAddress(index)); g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = head_ptr; g_state.cmd_list.length = regs.command_buffer.GetSize(index) / sizeof(u32); @@ -74,7 +116,9 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { { Common::Profiling::ScopeTimer scope_timer(category_drawing); +#if PICA_LOG_TEV DebugUtils::DumpTevStageConfig(regs.GetTevStages()); +#endif if (g_debug_context) g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr); @@ -117,9 +161,50 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { const u16* index_address_16 = (u16*)index_address_8; bool index_u16 = index_info.format != 0; +#if PICA_DUMP_GEOMETRY DebugUtils::GeometryDumper geometry_dumper; - PrimitiveAssembler<VertexShader::OutputVertex> primitive_assembler(regs.triangle_topology.Value()); PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value()); +#endif + PrimitiveAssembler<VertexShader::OutputVertex> primitive_assembler(regs.triangle_topology.Value()); + + if (g_debug_context) { + for (int i = 0; i < 3; ++i) { + const auto texture = regs.GetTextures()[i]; + if (!texture.enabled) + continue; + + u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); + if (g_debug_context && Pica::g_debug_context->recorder) + g_debug_context->recorder->MemoryAccessed(texture_data, Pica::Regs::NibblesPerPixel(texture.format) * texture.config.width / 2 * texture.config.height, texture.config.GetPhysicalAddress()); + } + } + + class { + /// Combine overlapping and close ranges + void SimplifyRanges() { + for (auto it = ranges.begin(); it != ranges.end(); ++it) { + // NOTE: We add 32 to the range end address to make sure "close" ranges are combined, too + auto it2 = std::next(it); + while (it2 != ranges.end() && it->first + it->second + 32 >= it2->first) { + it->second = std::max(it->second, it2->first + it2->second - it->first); + it2 = ranges.erase(it2); + } + } + } + + public: + /// Record a particular memory access in the list + void AddAccess(u32 paddr, u32 size) { + // Create new range or extend existing one + ranges[paddr] = std::max(ranges[paddr], size); + + // Simplify ranges... + SimplifyRanges(); + } + + /// Map of accessed ranges (mapping start address to range size) + std::map<u32, u32> ranges; + } memory_accesses; for (unsigned int index = 0; index < regs.num_vertices; ++index) { @@ -127,6 +212,10 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { if (is_indexed) { // TODO: Implement some sort of vertex cache! + if (g_debug_context && Pica::g_debug_context->recorder) { + int size = index_u16 ? 2 : 1; + memory_accesses.AddAccess(base_address + index_info.offset + size * index, size); + } } // Initialize data for the current vertex @@ -149,7 +238,14 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { // Load per-vertex data from the loader arrays for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) { - const u8* srcdata = Memory::GetPhysicalPointer(vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i]); + u32 source_addr = vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i]; + const u8* srcdata = Memory::GetPhysicalPointer(source_addr); + + if (g_debug_context && Pica::g_debug_context->recorder) { + memory_accesses.AddAccess(source_addr, + (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::FLOAT) ? 4 + : (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? 2 : 1); + } const float srcval = (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::BYTE) ? *(s8*)srcdata : (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::UBYTE) ? *(u8*)srcdata : @@ -179,6 +275,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { if (g_debug_context) g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input); +#if PICA_DUMP_GEOMETRY // NOTE: When dumping geometry, we simply assume that the first input attribute // corresponds to the position for now. DebugUtils::GeometryDumper::Vertex dumped_vertex = { @@ -188,9 +285,10 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { dumping_primitive_assembler.SubmitVertex(dumped_vertex, std::bind(&DebugUtils::GeometryDumper::AddTriangle, &geometry_dumper, _1, _2, _3)); +#endif // Send to vertex shader - VertexShader::OutputVertex output = VertexShader::RunShader(input, attribute_config.GetNumTotalAttributes()); + VertexShader::OutputVertex output = VertexShader::RunShader(input, attribute_config.GetNumTotalAttributes(), g_state.regs.vs, g_state.vs); if (is_indexed) { // TODO: Add processed vertex to vertex cache! @@ -211,47 +309,55 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { } } + for (auto& range : memory_accesses.ranges) { + g_debug_context->recorder->MemoryAccessed(Memory::GetPhysicalPointer(range.first), + range.second, range.first); + } + if (Settings::values.use_hw_renderer) { VideoCore::g_renderer->hw_rasterizer->DrawTriangles(); } +#if PICA_DUMP_GEOMETRY geometry_dumper.Dump(); +#endif - if (g_debug_context) + if (g_debug_context) { g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr); + } break; } - case PICA_REG_INDEX(vs_bool_uniforms): + case PICA_REG_INDEX(vs.bool_uniforms): for (unsigned i = 0; i < 16; ++i) - g_state.vs.uniforms.b[i] = (regs.vs_bool_uniforms.Value() & (1 << i)) != 0; + g_state.vs.uniforms.b[i] = (regs.vs.bool_uniforms.Value() & (1 << i)) != 0; break; - case PICA_REG_INDEX_WORKAROUND(vs_int_uniforms[0], 0x2b1): - case PICA_REG_INDEX_WORKAROUND(vs_int_uniforms[1], 0x2b2): - case PICA_REG_INDEX_WORKAROUND(vs_int_uniforms[2], 0x2b3): - case PICA_REG_INDEX_WORKAROUND(vs_int_uniforms[3], 0x2b4): + case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[0], 0x2b1): + case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[1], 0x2b2): + case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[2], 0x2b3): + case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[3], 0x2b4): { - int index = (id - PICA_REG_INDEX_WORKAROUND(vs_int_uniforms[0], 0x2b1)); - auto values = regs.vs_int_uniforms[index]; + int index = (id - PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[0], 0x2b1)); + auto values = regs.vs.int_uniforms[index]; g_state.vs.uniforms.i[index] = Math::Vec4<u8>(values.x, values.y, values.z, values.w); LOG_TRACE(HW_GPU, "Set integer uniform %d to %02x %02x %02x %02x", index, values.x.Value(), values.y.Value(), values.z.Value(), values.w.Value()); break; } - case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[0], 0x2c1): - case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[1], 0x2c2): - case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[2], 0x2c3): - case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[3], 0x2c4): - case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[4], 0x2c5): - case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[5], 0x2c6): - case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[6], 0x2c7): - case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[7], 0x2c8): + case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[0], 0x2c1): + case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[1], 0x2c2): + case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[2], 0x2c3): + case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[3], 0x2c4): + case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[4], 0x2c5): + case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[5], 0x2c6): + case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[6], 0x2c7): + case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[7], 0x2c8): { - auto& uniform_setup = regs.vs_uniform_setup; + auto& uniform_setup = regs.vs.uniform_setup; // TODO: Does actual hardware indeed keep an intermediate buffer or does // it directly write the values? @@ -293,73 +399,33 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { break; } - // Load default vertex input attributes - case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[0], 0x233): - case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[1], 0x234): - case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[2], 0x235): - { - // TODO: Does actual hardware indeed keep an intermediate buffer or does - // it directly write the values? - default_attr_write_buffer[default_attr_counter++] = value; - - // Default attributes are written in a packed format such that four float24 values are encoded in - // three 32-bit numbers. We write to internal memory once a full such vector is - // written. - if (default_attr_counter >= 3) { - default_attr_counter = 0; - - auto& setup = regs.vs_default_attributes_setup; - - if (setup.index >= 16) { - LOG_ERROR(HW_GPU, "Invalid VS default attribute index %d", (int)setup.index); - break; - } - - Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index]; - - // NOTE: The destination component order indeed is "backwards" - attribute.w = float24::FromRawFloat24(default_attr_write_buffer[0] >> 8); - attribute.z = float24::FromRawFloat24(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF)); - attribute.y = float24::FromRawFloat24(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF)); - attribute.x = float24::FromRawFloat24(default_attr_write_buffer[2] & 0xFFFFFF); - - LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index, - attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(), - attribute.w.ToFloat32()); - - // TODO: Verify that this actually modifies the register! - setup.index = setup.index + 1; - } - break; - } - // Load shader program code - case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[0], 0x2cc): - case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[1], 0x2cd): - case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[2], 0x2ce): - case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[3], 0x2cf): - case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[4], 0x2d0): - case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[5], 0x2d1): - case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[6], 0x2d2): - case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[7], 0x2d3): + case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[0], 0x2cc): + case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[1], 0x2cd): + case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[2], 0x2ce): + case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[3], 0x2cf): + case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[4], 0x2d0): + case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[5], 0x2d1): + case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[6], 0x2d2): + case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[7], 0x2d3): { - g_state.vs.program_code[regs.vs_program.offset] = value; - regs.vs_program.offset++; + g_state.vs.program_code[regs.vs.program.offset] = value; + regs.vs.program.offset++; break; } // Load swizzle pattern data - case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[0], 0x2d6): - case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[1], 0x2d7): - case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[2], 0x2d8): - case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[3], 0x2d9): - case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[4], 0x2da): - case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[5], 0x2db): - case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[6], 0x2dc): - case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[7], 0x2dd): + case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[0], 0x2d6): + case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[1], 0x2d7): + case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[2], 0x2d8): + case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[3], 0x2d9): + case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[4], 0x2da): + case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[5], 0x2db): + case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[6], 0x2dc): + case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[7], 0x2dd): { - g_state.vs.swizzle_data[regs.vs_swizzle_patterns.offset] = value; - regs.vs_swizzle_patterns.offset++; + g_state.vs.swizzle_data[regs.vs.swizzle_patterns.offset] = value; + regs.vs.swizzle_patterns.offset++; break; } @@ -370,7 +436,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { VideoCore::g_renderer->hw_rasterizer->NotifyPicaRegisterChanged(id); if (g_debug_context) - g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id)); + g_debug_context->OnEvent(DebugContext::Event::PicaCommandProcessed, reinterpret_cast<void*>(&id)); } void ProcessCommandList(const u32* list, u32 size) { diff --git a/src/video_core/command_processor.h b/src/video_core/command_processor.h index bb3d4150fe..022a71f5ec 100644 --- a/src/video_core/command_processor.h +++ b/src/video_core/command_processor.h @@ -4,11 +4,11 @@ #pragma once +#include <type_traits> + #include "common/bit_field.h" #include "common/common_types.h" -#include "pica.h" - namespace Pica { namespace CommandProcessor { diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 7b8ab72b6e..e9a8584114 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -23,6 +23,7 @@ #include "common/vector_math.h" #include "video_core/pica.h" +#include "video_core/renderer_base.h" #include "video_core/utils.h" #include "video_core/video_core.h" @@ -84,15 +85,11 @@ void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) { vertices.push_back(v1); vertices.push_back(v2); - int num_vertices = vertices.size(); + int num_vertices = (int)vertices.size(); faces.push_back({ num_vertices-3, num_vertices-2, num_vertices-1 }); } void GeometryDumper::Dump() { - // NOTE: Permanently enabling this just trashes the hard disk for no reason. - // Hence, this is currently disabled. - return; - static int index = 0; std::string filename = std::string("geometry_dump") + std::to_string(++index) + ".obj"; @@ -115,10 +112,6 @@ void GeometryDumper::Dump() { void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data, u32 swizzle_size, u32 main_offset, const Regs::VSOutputAttributes* output_attributes) { - // NOTE: Permanently enabling this just trashes hard disks for no reason. - // Hence, this is currently disabled. - return; - struct StuffToWrite { u8* pointer; u32 size; @@ -240,8 +233,8 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data dvle.main_offset_words = main_offset; dvle.output_register_table_offset = write_offset - dvlb.dvle_offset; - dvle.output_register_table_size = output_info_table.size(); - QueueForWriting((u8*)output_info_table.data(), output_info_table.size() * sizeof(OutputRegisterInfo)); + dvle.output_register_table_size = static_cast<uint32_t>(output_info_table.size()); + QueueForWriting((u8*)output_info_table.data(), static_cast<u32>(output_info_table.size() * sizeof(OutputRegisterInfo))); // TODO: Create a label table for "main" @@ -496,31 +489,31 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture // Lookup base value Math::Vec3<int> ret; if (differential_mode) { - ret.r() = differential.r; - ret.g() = differential.g; - ret.b() = differential.b; + ret.r() = static_cast<int>(differential.r); + ret.g() = static_cast<int>(differential.g); + ret.b() = static_cast<int>(differential.b); if (x >= 2) { - ret.r() += differential.dr; - ret.g() += differential.dg; - ret.b() += differential.db; + ret.r() += static_cast<int>(differential.dr); + ret.g() += static_cast<int>(differential.dg); + ret.b() += static_cast<int>(differential.db); } ret.r() = Color::Convert5To8(ret.r()); ret.g() = Color::Convert5To8(ret.g()); ret.b() = Color::Convert5To8(ret.b()); } else { if (x < 2) { - ret.r() = Color::Convert4To8(separate.r1); - ret.g() = Color::Convert4To8(separate.g1); - ret.b() = Color::Convert4To8(separate.b1); + ret.r() = Color::Convert4To8(static_cast<u8>(separate.r1)); + ret.g() = Color::Convert4To8(static_cast<u8>(separate.g1)); + ret.b() = Color::Convert4To8(static_cast<u8>(separate.b1)); } else { - ret.r() = Color::Convert4To8(separate.r2); - ret.g() = Color::Convert4To8(separate.g2); - ret.b() = Color::Convert4To8(separate.b2); + ret.r() = Color::Convert4To8(static_cast<u8>(separate.r2)); + ret.g() = Color::Convert4To8(static_cast<u8>(separate.g2)); + ret.b() = Color::Convert4To8(static_cast<u8>(separate.b2)); } } // Add modifier - unsigned table_index = (x < 2) ? table_index_1.Value() : table_index_2.Value(); + unsigned table_index = static_cast<int>((x < 2) ? table_index_1.Value() : table_index_2.Value()); static const std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{ { 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 }, @@ -564,10 +557,6 @@ TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, } void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { - // NOTE: Permanently enabling this just trashes hard disks for no reason. - // Hence, this is currently disabled. - return; - #ifndef HAVE_PNG return; #else diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 7926d64ec7..81eea30a95 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -14,6 +14,8 @@ #include "common/vector_math.h" +#include "core/tracer/recorder.h" + #include "video_core/pica.h" namespace Pica { @@ -23,11 +25,14 @@ public: enum class Event { FirstEvent = 0, - CommandLoaded = FirstEvent, - CommandProcessed, + PicaCommandLoaded = FirstEvent, + PicaCommandProcessed, IncomingPrimitiveBatch, FinishedPrimitiveBatch, VertexLoaded, + IncomingDisplayTransfer, + GSPCommandProcessed, + BufferSwapped, NumEvents }; @@ -129,6 +134,8 @@ public: Event active_breakpoint; bool at_breakpoint = false; + std::shared_ptr<CiTrace::Recorder> recorder = nullptr; + private: /** * Private default constructor to make sure people always construct this through Construct() @@ -150,6 +157,11 @@ extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this g namespace DebugUtils { +#define PICA_DUMP_GEOMETRY 0 +#define PICA_DUMP_SHADERS 0 +#define PICA_DUMP_TEXTURES 0 +#define PICA_LOG_TEV 0 + // Simple utility class for dumping geometry data to an OBJ file class GeometryDumper { public: diff --git a/src/video_core/hwrasterizer_base.h b/src/video_core/hwrasterizer_base.h index dec193f8b2..c8746c608a 100644 --- a/src/video_core/hwrasterizer_base.h +++ b/src/video_core/hwrasterizer_base.h @@ -4,8 +4,13 @@ #pragma once -#include "common/emu_window.h" -#include "video_core/vertex_shader.h" +#include "common/common_types.h" + +namespace Pica { +namespace VertexShader { +struct OutputVertex; +} +} class HWRasterizer { public: diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp index 543d9c443d..17cb667805 100644 --- a/src/video_core/pica.cpp +++ b/src/video_core/pica.cpp @@ -2,7 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <string.h> +#include <cstring> +#include <unordered_map> #include "pica.h" @@ -10,6 +11,75 @@ namespace Pica { State g_state; +std::string Regs::GetCommandName(int index) { + static std::unordered_map<u32, std::string> map; + + if (map.empty()) { + #define ADD_FIELD(name) \ + map.insert({static_cast<u32>(PICA_REG_INDEX(name)), #name}); \ + /* TODO: change to Regs::name when VS2015 and other compilers support it */ \ + for (u32 i = PICA_REG_INDEX(name) + 1; i < PICA_REG_INDEX(name) + sizeof(Regs().name) / 4; ++i) \ + map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ + + ADD_FIELD(trigger_irq); + ADD_FIELD(cull_mode); + ADD_FIELD(viewport_size_x); + ADD_FIELD(viewport_size_y); + ADD_FIELD(viewport_depth_range); + ADD_FIELD(viewport_depth_far_plane); + ADD_FIELD(viewport_corner); + ADD_FIELD(texture0_enable); + ADD_FIELD(texture0); + ADD_FIELD(texture0_format); + ADD_FIELD(texture1); + ADD_FIELD(texture1_format); + ADD_FIELD(texture2); + ADD_FIELD(texture2_format); + ADD_FIELD(tev_stage0); + ADD_FIELD(tev_stage1); + ADD_FIELD(tev_stage2); + ADD_FIELD(tev_stage3); + ADD_FIELD(tev_combiner_buffer_input); + ADD_FIELD(tev_stage4); + ADD_FIELD(tev_stage5); + ADD_FIELD(tev_combiner_buffer_color); + ADD_FIELD(output_merger); + ADD_FIELD(framebuffer); + ADD_FIELD(vertex_attributes); + ADD_FIELD(index_array); + ADD_FIELD(num_vertices); + ADD_FIELD(trigger_draw); + ADD_FIELD(trigger_draw_indexed); + ADD_FIELD(vs_default_attributes_setup); + ADD_FIELD(command_buffer); + ADD_FIELD(triangle_topology); + ADD_FIELD(gs.bool_uniforms); + ADD_FIELD(gs.int_uniforms); + ADD_FIELD(gs.main_offset); + ADD_FIELD(gs.input_register_map); + ADD_FIELD(gs.uniform_setup); + ADD_FIELD(gs.program); + ADD_FIELD(gs.swizzle_patterns); + ADD_FIELD(vs.bool_uniforms); + ADD_FIELD(vs.int_uniforms); + ADD_FIELD(vs.main_offset); + ADD_FIELD(vs.input_register_map); + ADD_FIELD(vs.uniform_setup); + ADD_FIELD(vs.program); + ADD_FIELD(vs.swizzle_patterns); + +#undef ADD_FIELD + } + + // Return empty string if no match is found + auto it = map.find(index); + if (it != map.end()) { + return it->second; + } else { + return std::string(); + } +} + void Init() { } diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 9628a75896..34b02b2f83 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -5,10 +5,9 @@ #pragma once #include <array> +#include <cmath> #include <cstddef> -#include <initializer_list> -#include <map> -#include <vector> +#include <string> #include "common/assert.h" #include "common/bit_field.h" @@ -114,11 +113,22 @@ struct Regs { struct TextureConfig { enum WrapMode : u32 { ClampToEdge = 0, + ClampToBorder = 1, Repeat = 2, MirroredRepeat = 3, }; - INSERT_PADDING_WORDS(0x1); + enum TextureFilter : u32 { + Nearest = 0, + Linear = 1 + }; + + union { + BitField< 0, 8, u32> r; + BitField< 8, 8, u32> g; + BitField<16, 8, u32> b; + BitField<24, 8, u32> a; + } border_color; union { BitField< 0, 16, u32> height; @@ -126,8 +136,10 @@ struct Regs { }; union { - BitField< 8, 2, WrapMode> wrap_s; - BitField<12, 2, WrapMode> wrap_t; + BitField< 1, 1, TextureFilter> mag_filter; + BitField< 2, 1, TextureFilter> min_filter; + BitField< 8, 2, WrapMode> wrap_t; + BitField<12, 2, WrapMode> wrap_s; }; INSERT_PADDING_WORDS(0x1); @@ -194,6 +206,7 @@ struct Regs { case TextureFormat::IA8: return 4; + case TextureFormat::I4: case TextureFormat::A4: return 1; @@ -284,6 +297,7 @@ struct Regs { AddSigned = 3, Lerp = 4, Subtract = 5, + Dot3_RGB = 6, MultiplyThenAdd = 8, AddThenMultiply = 9, @@ -414,6 +428,11 @@ struct Regs { GreaterThanOrEqual = 7, }; + enum class StencilAction : u32 { + Keep = 0, + Xor = 5, + }; + struct { union { // If false, logic blending is used @@ -448,15 +467,35 @@ struct Regs { BitField< 8, 8, u32> ref; } alpha_test; - union { - BitField< 0, 1, u32> stencil_test_enable; - BitField< 4, 3, CompareFunc> stencil_test_func; - BitField< 8, 8, u32> stencil_replacement_value; - BitField<16, 8, u32> stencil_reference_value; - BitField<24, 8, u32> stencil_mask; - } stencil_test; + struct { + union { + // If true, enable stencil testing + BitField< 0, 1, u32> enable; - INSERT_PADDING_WORDS(0x1); + // Comparison operation for stencil testing + BitField< 4, 3, CompareFunc> func; + + // Value to calculate the new stencil value from + BitField< 8, 8, u32> replacement_value; + + // Value to compare against for stencil testing + BitField<16, 8, u32> reference_value; + + // Mask to apply on stencil test inputs + BitField<24, 8, u32> mask; + }; + + union { + // Action to perform when the stencil test fails + BitField< 0, 3, StencilAction> action_stencil_fail; + + // Action to perform when stencil testing passed but depth testing fails + BitField< 4, 3, StencilAction> action_depth_fail; + + // Action to perform when both stencil and depth testing pass + BitField< 8, 3, StencilAction> action_depth_pass; + }; + } stencil_test; union { BitField< 0, 1, u32> depth_test_enable; @@ -506,7 +545,7 @@ struct Regs { struct { INSERT_PADDING_WORDS(0x6); - DepthFormat depth_format; + DepthFormat depth_format; // TODO: Should be a BitField! BitField<16, 3, ColorFormat> color_format; INSERT_PADDING_WORDS(0x4); @@ -752,171 +791,123 @@ struct Regs { INSERT_PADDING_WORDS(0x20); enum class TriangleTopology : u32 { - List = 0, - Strip = 1, - Fan = 2, - ListIndexed = 3, // TODO: No idea if this is correct + List = 0, + Strip = 1, + Fan = 2, + Shader = 3, // Programmable setup unit implemented in a geometry shader }; BitField<8, 2, TriangleTopology> triangle_topology; - INSERT_PADDING_WORDS(0x51); + INSERT_PADDING_WORDS(0x21); - BitField<0, 16, u32> vs_bool_uniforms; - union { - BitField< 0, 8, u32> x; - BitField< 8, 8, u32> y; - BitField<16, 8, u32> z; - BitField<24, 8, u32> w; - } vs_int_uniforms[4]; + struct ShaderConfig { + BitField<0, 16, u32> bool_uniforms; - INSERT_PADDING_WORDS(0x5); + union { + BitField< 0, 8, u32> x; + BitField< 8, 8, u32> y; + BitField<16, 8, u32> z; + BitField<24, 8, u32> w; + } int_uniforms[4]; - // Offset to shader program entry point (in words) - BitField<0, 16, u32> vs_main_offset; + INSERT_PADDING_WORDS(0x5); - union { - BitField< 0, 4, u64> attribute0_register; - BitField< 4, 4, u64> attribute1_register; - BitField< 8, 4, u64> attribute2_register; - BitField<12, 4, u64> attribute3_register; - BitField<16, 4, u64> attribute4_register; - BitField<20, 4, u64> attribute5_register; - BitField<24, 4, u64> attribute6_register; - BitField<28, 4, u64> attribute7_register; - BitField<32, 4, u64> attribute8_register; - BitField<36, 4, u64> attribute9_register; - BitField<40, 4, u64> attribute10_register; - BitField<44, 4, u64> attribute11_register; - BitField<48, 4, u64> attribute12_register; - BitField<52, 4, u64> attribute13_register; - BitField<56, 4, u64> attribute14_register; - BitField<60, 4, u64> attribute15_register; - - int GetRegisterForAttribute(int attribute_index) const { - u64 fields[] = { - attribute0_register, attribute1_register, attribute2_register, attribute3_register, - attribute4_register, attribute5_register, attribute6_register, attribute7_register, - attribute8_register, attribute9_register, attribute10_register, attribute11_register, - attribute12_register, attribute13_register, attribute14_register, attribute15_register, + // Offset to shader program entry point (in words) + BitField<0, 16, u32> main_offset; + + union { + BitField< 0, 4, u64> attribute0_register; + BitField< 4, 4, u64> attribute1_register; + BitField< 8, 4, u64> attribute2_register; + BitField<12, 4, u64> attribute3_register; + BitField<16, 4, u64> attribute4_register; + BitField<20, 4, u64> attribute5_register; + BitField<24, 4, u64> attribute6_register; + BitField<28, 4, u64> attribute7_register; + BitField<32, 4, u64> attribute8_register; + BitField<36, 4, u64> attribute9_register; + BitField<40, 4, u64> attribute10_register; + BitField<44, 4, u64> attribute11_register; + BitField<48, 4, u64> attribute12_register; + BitField<52, 4, u64> attribute13_register; + BitField<56, 4, u64> attribute14_register; + BitField<60, 4, u64> attribute15_register; + + int GetRegisterForAttribute(int attribute_index) const { + u64 fields[] = { + attribute0_register, attribute1_register, attribute2_register, attribute3_register, + attribute4_register, attribute5_register, attribute6_register, attribute7_register, + attribute8_register, attribute9_register, attribute10_register, attribute11_register, + attribute12_register, attribute13_register, attribute14_register, attribute15_register, + }; + return (int)fields[attribute_index]; + } + } input_register_map; + + // OUTMAP_MASK, 0x28E, CODETRANSFER_END + INSERT_PADDING_WORDS(0x3); + + struct { + enum Format : u32 + { + FLOAT24 = 0, + FLOAT32 = 1 }; - return (int)fields[attribute_index]; - } - } vs_input_register_map; - INSERT_PADDING_WORDS(0x3); + bool IsFloat32() const { + return format == FLOAT32; + } - struct { - enum Format : u32 - { - FLOAT24 = 0, - FLOAT32 = 1 - }; + union { + // Index of the next uniform to write to + // TODO: ctrulib uses 8 bits for this, however that seems to yield lots of invalid indices + // TODO: Maybe the uppermost index is for the geometry shader? Investigate! + BitField<0, 7, u32> index; - bool IsFloat32() const { - return format == FLOAT32; - } + BitField<31, 1, Format> format; + }; - union { - // Index of the next uniform to write to - // TODO: ctrulib uses 8 bits for this, however that seems to yield lots of invalid indices - BitField<0, 7, u32> index; + // Writing to these registers sets the current uniform. + u32 set_value[8]; - BitField<31, 1, Format> format; - }; + } uniform_setup; - // Writing to these registers sets the "current" uniform. - // TODO: It's not clear how the hardware stores what the "current" uniform is. - u32 set_value[8]; + INSERT_PADDING_WORDS(0x2); - } vs_uniform_setup; + struct { + // Offset of the next instruction to write code to. + // Incremented with each instruction write. + u32 offset; - INSERT_PADDING_WORDS(0x2); + // Writing to these registers sets the "current" word in the shader program. + u32 set_word[8]; + } program; - struct { - // Offset of the next instruction to write code to. - // Incremented with each instruction write. - u32 offset; + INSERT_PADDING_WORDS(0x1); - // Writing to these registers sets the "current" word in the shader program. - // TODO: It's not clear how the hardware stores what the "current" word is. - u32 set_word[8]; - } vs_program; + // This register group is used to load an internal table of swizzling patterns, + // which are indexed by each shader instruction to specify vector component swizzling. + struct { + // Offset of the next swizzle pattern to write code to. + // Incremented with each instruction write. + u32 offset; - INSERT_PADDING_WORDS(0x1); + // Writing to these registers sets the current swizzle pattern in the table. + u32 set_word[8]; + } swizzle_patterns; - // This register group is used to load an internal table of swizzling patterns, - // which are indexed by each shader instruction to specify vector component swizzling. - struct { - // Offset of the next swizzle pattern to write code to. - // Incremented with each instruction write. - u32 offset; + INSERT_PADDING_WORDS(0x2); + }; - // Writing to these registers sets the "current" swizzle pattern in the table. - // TODO: It's not clear how the hardware stores what the "current" swizzle pattern is. - u32 set_word[8]; - } vs_swizzle_patterns; + ShaderConfig gs; + ShaderConfig vs; - INSERT_PADDING_WORDS(0x22); + INSERT_PADDING_WORDS(0x20); // Map register indices to names readable by humans // Used for debugging purposes, so performance is not an issue here - static std::string GetCommandName(int index) { - std::map<u32, std::string> map; - - #define ADD_FIELD(name) \ - do { \ - map.insert({PICA_REG_INDEX(name), #name}); \ - /* TODO: change to Regs::name when VS2015 and other compilers support it */ \ - for (u32 i = PICA_REG_INDEX(name) + 1; i < PICA_REG_INDEX(name) + sizeof(Regs().name) / 4; ++i) \ - map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ - } while(false) - - ADD_FIELD(trigger_irq); - ADD_FIELD(cull_mode); - ADD_FIELD(viewport_size_x); - ADD_FIELD(viewport_size_y); - ADD_FIELD(viewport_depth_range); - ADD_FIELD(viewport_depth_far_plane); - ADD_FIELD(viewport_corner); - ADD_FIELD(texture0_enable); - ADD_FIELD(texture0); - ADD_FIELD(texture0_format); - ADD_FIELD(texture1); - ADD_FIELD(texture1_format); - ADD_FIELD(texture2); - ADD_FIELD(texture2_format); - ADD_FIELD(tev_stage0); - ADD_FIELD(tev_stage1); - ADD_FIELD(tev_stage2); - ADD_FIELD(tev_stage3); - ADD_FIELD(tev_combiner_buffer_input); - ADD_FIELD(tev_stage4); - ADD_FIELD(tev_stage5); - ADD_FIELD(tev_combiner_buffer_color); - ADD_FIELD(output_merger); - ADD_FIELD(framebuffer); - ADD_FIELD(vertex_attributes); - ADD_FIELD(index_array); - ADD_FIELD(num_vertices); - ADD_FIELD(trigger_draw); - ADD_FIELD(trigger_draw_indexed); - ADD_FIELD(vs_default_attributes_setup); - ADD_FIELD(command_buffer); - ADD_FIELD(triangle_topology); - ADD_FIELD(vs_bool_uniforms); - ADD_FIELD(vs_int_uniforms); - ADD_FIELD(vs_main_offset); - ADD_FIELD(vs_input_register_map); - ADD_FIELD(vs_uniform_setup); - ADD_FIELD(vs_program); - ADD_FIELD(vs_swizzle_patterns); - - #undef ADD_FIELD - - // Return empty string if no match is found - return map[index]; - } + static std::string GetCommandName(int index); static inline size_t NumIds() { return sizeof(Regs) / sizeof(u32); @@ -982,17 +973,14 @@ ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232); ASSERT_REG_POSITION(command_buffer, 0x238); ASSERT_REG_POSITION(triangle_topology, 0x25e); -ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); -ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1); -ASSERT_REG_POSITION(vs_main_offset, 0x2ba); -ASSERT_REG_POSITION(vs_input_register_map, 0x2bb); -ASSERT_REG_POSITION(vs_uniform_setup, 0x2c0); -ASSERT_REG_POSITION(vs_program, 0x2cb); -ASSERT_REG_POSITION(vs_swizzle_patterns, 0x2d5); +ASSERT_REG_POSITION(gs, 0x280); +ASSERT_REG_POSITION(vs, 0x2b0); #undef ASSERT_REG_POSITION #endif // !defined(_MSC_VER) +static_assert(sizeof(Regs::ShaderConfig) == 0x30 * sizeof(u32), "ShaderConfig structure has incorrect size"); + // The total number of registers is chosen arbitrarily, but let's make sure it's not some odd value anyway. static_assert(sizeof(Regs) <= 0x300 * sizeof(u32), "Register set structure larger than it should be"); static_assert(sizeof(Regs) >= 0x300 * sizeof(u32), "Register set structure smaller than it should be"); @@ -1014,7 +1002,7 @@ struct float24 { u32 mantissa = hex & 0xFFFF; u32 exponent = (hex >> 16) & 0x7F; u32 sign = hex >> 23; - ret.value = powf(2.0f, (float)exponent-63.0f) * (1.0f + mantissa * powf(2.0f, -16.f)); + ret.value = std::pow(2.0f, (float)exponent-63.0f) * (1.0f + mantissa * std::pow(2.0f, -16.f)); if (sign) ret.value = -ret.value; } @@ -1102,7 +1090,7 @@ struct State { Regs regs; /// Vertex shader memory - struct { + struct ShaderSetup { struct { Math::Vec4<float24> f[96]; std::array<bool, 16> b; @@ -1113,7 +1101,10 @@ struct State { std::array<u32, 1024> program_code; std::array<u32, 1024> swizzle_data; - } vs; + }; + + ShaderSetup vs; + ShaderSetup gs; /// Current Pica command list struct { diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp index 0120f2896b..2f22bdccef 100644 --- a/src/video_core/primitive_assembly.cpp +++ b/src/video_core/primitive_assembly.cpp @@ -20,8 +20,9 @@ template<typename VertexType> void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler) { switch (topology) { + // TODO: Figure out what's different with TriangleTopology::Shader. case Regs::TriangleTopology::List: - case Regs::TriangleTopology::ListIndexed: + case Regs::TriangleTopology::Shader: if (buffer_index < 2) { buffer[buffer_index++] = vtx; } else { diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 59d156ee78..68b7cc05db 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -126,6 +126,30 @@ static u32 GetDepth(int x, int y) { } } +static u8 GetStencil(int x, int y) { + const auto& framebuffer = g_state.regs.framebuffer; + const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress(); + u8* depth_buffer = Memory::GetPhysicalPointer(addr); + + y = framebuffer.height - y; + + const u32 coarse_y = y & ~7; + u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(framebuffer.depth_format); + u32 stride = framebuffer.width * bytes_per_pixel; + + u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; + u8* src_pixel = depth_buffer + src_offset; + + switch (framebuffer.depth_format) { + case Regs::DepthFormat::D24S8: + return Color::DecodeD24S8(src_pixel).y; + + default: + LOG_WARNING(HW_GPU, "GetStencil called for function which doesn't have a stencil component (format %u)", framebuffer.depth_format); + return 0; + } +} + static void SetDepth(int x, int y, u32 value) { const auto& framebuffer = g_state.regs.framebuffer; const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress(); @@ -144,13 +168,15 @@ static void SetDepth(int x, int y, u32 value) { case Regs::DepthFormat::D16: Color::EncodeD16(value, dst_pixel); break; + case Regs::DepthFormat::D24: Color::EncodeD24(value, dst_pixel); break; + case Regs::DepthFormat::D24S8: - // TODO(Subv): Implement the stencil buffer - Color::EncodeD24S8(value, 0, dst_pixel); + Color::EncodeD24X8(value, dst_pixel); break; + default: LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); UNIMPLEMENTED(); @@ -158,6 +184,53 @@ static void SetDepth(int x, int y, u32 value) { } } +static void SetStencil(int x, int y, u8 value) { + const auto& framebuffer = g_state.regs.framebuffer; + const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress(); + u8* depth_buffer = Memory::GetPhysicalPointer(addr); + + y = framebuffer.height - y; + + const u32 coarse_y = y & ~7; + u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(framebuffer.depth_format); + u32 stride = framebuffer.width * bytes_per_pixel; + + u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; + u8* dst_pixel = depth_buffer + dst_offset; + + switch (framebuffer.depth_format) { + case Pica::Regs::DepthFormat::D16: + case Pica::Regs::DepthFormat::D24: + // Nothing to do + break; + + case Pica::Regs::DepthFormat::D24S8: + Color::EncodeX24S8(value, dst_pixel); + break; + + default: + LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); + UNIMPLEMENTED(); + break; + } +} + +// TODO: Should the stencil mask be applied to the "dest" or "ref" operands? Most likely not! +static u8 PerformStencilAction(Regs::StencilAction action, u8 dest, u8 ref) { + switch (action) { + case Regs::StencilAction::Keep: + return dest; + + case Regs::StencilAction::Xor: + return dest ^ ref; + + default: + LOG_CRITICAL(HW_GPU, "Unknown stencil action %x", (int)action); + UNIMPLEMENTED(); + return 0; + } +} + // NOTE: Assuming that rasterizer coordinates are 12.4 fixed-point values struct Fix12P4 { Fix12P4() {} @@ -276,6 +349,9 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, auto textures = regs.GetTextures(); auto tev_stages = regs.GetTevStages(); + bool stencil_action_enable = g_state.regs.output_merger.stencil_test.enable && g_state.regs.framebuffer.depth_format == Regs::DepthFormat::D24S8; + const auto stencil_test = g_state.regs.output_merger.stencil_test; + // Enter rasterization loop, starting at the center of the topleft bounding box corner. // TODO: Not sure if looping through x first might be faster for (u16 y = min_y + 8; y < max_y; y += 0x10) { @@ -349,6 +425,9 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, val = std::min(val, (int)size - 1); return val; + case Regs::TextureConfig::ClampToBorder: + return val; + case Regs::TextureConfig::Repeat: return (int)((unsigned)val % size); @@ -367,17 +446,26 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, } }; - // Textures are laid out from bottom to top, hence we invert the t coordinate. - // NOTE: This may not be the right place for the inversion. - // TODO: Check if this applies to ETC textures, too. - s = GetWrappedTexCoord(texture.config.wrap_s, s, texture.config.width); - t = texture.config.height - 1 - GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height); - - u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); - auto info = DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format); - - texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info); - DebugUtils::DumpTexture(texture.config, texture_data); + if ((texture.config.wrap_s == Regs::TextureConfig::ClampToBorder && (s < 0 || s >= texture.config.width)) + || (texture.config.wrap_t == Regs::TextureConfig::ClampToBorder && (t < 0 || t >= texture.config.height))) { + auto border_color = texture.config.border_color; + texture_color[i] = { border_color.r, border_color.g, border_color.b, border_color.a }; + } else { + // Textures are laid out from bottom to top, hence we invert the t coordinate. + // NOTE: This may not be the right place for the inversion. + // TODO: Check if this applies to ETC textures, too. + s = GetWrappedTexCoord(texture.config.wrap_s, s, texture.config.width); + t = texture.config.height - 1 - GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height); + + u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); + auto info = DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format); + + // TODO: Apply the min and mag filters to the texture + texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info); +#if PICA_DUMP_TEXTURES + DebugUtils::DumpTexture(texture.config, texture_data); +#endif + } } // Texture environment - consists of 6 stages of color and alpha combining. @@ -556,7 +644,18 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, result = (result * input[2].Cast<int>()) / 255; return result.Cast<u8>(); } - + case Operation::Dot3_RGB: + { + // Not fully accurate. + // Worst case scenario seems to yield a +/-3 error + // Some HW results indicate that the per-component computation can't have a higher precision than 1/256, + // while dot3_rgb( (0x80,g0,b0),(0x7F,g1,b1) ) and dot3_rgb( (0x80,g0,b0),(0x80,g1,b1) ) give different results + int result = ((input[0].r() * 2 - 255) * (input[1].r() * 2 - 255) + 128) / 256 + + ((input[0].g() * 2 - 255) * (input[1].g() * 2 - 255) + 128) / 256 + + ((input[0].b() * 2 - 255) * (input[1].b() * 2 - 255) + 128) / 256; + result = std::max(0, std::min(255, result)); + return { (u8)result, (u8)result, (u8)result }; + } default: LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); UNIMPLEMENTED(); @@ -638,6 +737,7 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, } const auto& output_merger = regs.output_merger; + // TODO: Does alpha testing happen before or after stencil? if (output_merger.alpha_test.enable) { bool pass = false; @@ -679,6 +779,54 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, continue; } + u8 old_stencil = 0; + if (stencil_action_enable) { + old_stencil = GetStencil(x >> 4, y >> 4); + u8 dest = old_stencil & stencil_test.mask; + u8 ref = stencil_test.reference_value & stencil_test.mask; + + bool pass = false; + switch (stencil_test.func) { + case Regs::CompareFunc::Never: + pass = false; + break; + + case Regs::CompareFunc::Always: + pass = true; + break; + + case Regs::CompareFunc::Equal: + pass = (ref == dest); + break; + + case Regs::CompareFunc::NotEqual: + pass = (ref != dest); + break; + + case Regs::CompareFunc::LessThan: + pass = (ref < dest); + break; + + case Regs::CompareFunc::LessThanOrEqual: + pass = (ref <= dest); + break; + + case Regs::CompareFunc::GreaterThan: + pass = (ref > dest); + break; + + case Regs::CompareFunc::GreaterThanOrEqual: + pass = (ref >= dest); + break; + } + + if (!pass) { + u8 new_stencil = PerformStencilAction(stencil_test.action_stencil_fail, old_stencil, stencil_test.replacement_value); + SetStencil(x >> 4, y >> 4, new_stencil); + continue; + } + } + // TODO: Does depth indeed only get written even if depth testing is enabled? if (output_merger.depth_test_enable) { unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format); @@ -723,11 +871,22 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, break; } - if (!pass) + if (!pass) { + if (stencil_action_enable) { + u8 new_stencil = PerformStencilAction(stencil_test.action_depth_fail, old_stencil, stencil_test.replacement_value); + SetStencil(x >> 4, y >> 4, new_stencil); + } continue; + } if (output_merger.depth_write_enable) SetDepth(x >> 4, y >> 4, z); + + if (stencil_action_enable) { + // TODO: What happens if stencil testing is enabled, but depth testing is not? Will stencil get updated anyway? + u8 new_stencil = PerformStencilAction(stencil_test.action_depth_pass, old_stencil, stencil_test.replacement_value); + SetStencil(x >> 4, y >> 4, new_stencil); + } } auto dest = GetPixel(x >> 4, y >> 4); diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 5757ac75d6..6587bcf271 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -4,10 +4,14 @@ #pragma once +#include <memory> + #include "common/common_types.h" #include "video_core/hwrasterizer_base.h" +class EmuWindow; + class RendererBase : NonCopyable { public: diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 518f79331e..2db845da65 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -2,10 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> +#include <memory> + #include "common/color.h" +#include "common/math_util.h" -#include "core/settings.h" #include "core/hw/gpu.h" +#include "core/memory.h" +#include "core/settings.h" #include "video_core/pica.h" #include "video_core/utils.h" @@ -16,8 +21,6 @@ #include "generated/gl_3_2_core.h" -#include <memory> - static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace && stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace && @@ -813,12 +816,16 @@ void RasterizerOpenGL::ReloadColorBuffer() { } void RasterizerOpenGL::ReloadDepthBuffer() { + PAddr depth_buffer_addr = Pica::g_state.regs.framebuffer.GetDepthBufferPhysicalAddress(); + + if (depth_buffer_addr == 0) + return; + // TODO: Appears to work, but double-check endianness of depth values and order of depth-stencil - u8* depth_buffer = Memory::GetPhysicalPointer(Pica::g_state.regs.framebuffer.GetDepthBufferPhysicalAddress()); + u8* depth_buffer = Memory::GetPhysicalPointer(depth_buffer_addr); - if (depth_buffer == nullptr) { + if (depth_buffer == nullptr) return; - } u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index d7d422b1f2..ae7b26fc66 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -4,7 +4,12 @@ #pragma once +#include <vector> + +#include "common/common_types.h" + #include "video_core/hwrasterizer_base.h" +#include "video_core/vertex_shader.h" #include "gl_state.h" #include "gl_rasterizer_cache.h" diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 2e4110a88c..dc3ffdf228 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -31,12 +31,18 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text state.texture_units[texture_unit].texture_2d = new_texture->texture.handle; state.Apply(); - // TODO: Need to choose filters that correspond to PICA once register is declared - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(config.config.mag_filter)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(config.config.min_filter)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(config.config.wrap_s)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, PicaToGL::WrapMode(config.config.wrap_t)); + GLenum wrap_s = PicaToGL::WrapMode(config.config.wrap_s); + GLenum wrap_t = PicaToGL::WrapMode(config.config.wrap_t); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t); + + if (wrap_s == GL_CLAMP_TO_BORDER || wrap_t == GL_CLAMP_TO_BORDER) { + auto border_color = PicaToGL::ColorRGBA8((u8*)&config.config.border_color.r); + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color.data()); + } const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format); diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp deleted file mode 100644 index 8f4ae28a4c..0000000000 --- a/src/video_core/renderer_opengl/gl_resource_manager.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "video_core/renderer_opengl/gl_resource_manager.h" -#include "video_core/renderer_opengl/gl_shader_util.h" - -// Textures -OGLTexture::OGLTexture() : handle(0) { -} - -OGLTexture::~OGLTexture() { - Release(); -} - -void OGLTexture::Create() { - if (handle != 0) { - return; - } - - glGenTextures(1, &handle); -} - -void OGLTexture::Release() { - glDeleteTextures(1, &handle); - handle = 0; -} - -// Shaders -OGLShader::OGLShader() : handle(0) { -} - -OGLShader::~OGLShader() { - Release(); -} - -void OGLShader::Create(const char* vert_shader, const char* frag_shader) { - if (handle != 0) { - return; - } - - handle = ShaderUtil::LoadShaders(vert_shader, frag_shader); -} - -void OGLShader::Release() { - glDeleteProgram(handle); - handle = 0; -} - -// Buffer objects -OGLBuffer::OGLBuffer() : handle(0) { -} - -OGLBuffer::~OGLBuffer() { - Release(); -} - -void OGLBuffer::Create() { - if (handle != 0) { - return; - } - - glGenBuffers(1, &handle); -} - -void OGLBuffer::Release() { - glDeleteBuffers(1, &handle); - handle = 0; -} - -// Vertex array objects -OGLVertexArray::OGLVertexArray() : handle(0) { -} - -OGLVertexArray::~OGLVertexArray() { - Release(); -} - -void OGLVertexArray::Create() { - if (handle != 0) { - return; - } - - glGenVertexArrays(1, &handle); -} - -void OGLVertexArray::Release() { - glDeleteVertexArrays(1, &handle); - handle = 0; -} - -// Framebuffers -OGLFramebuffer::OGLFramebuffer() : handle(0) { -} - -OGLFramebuffer::~OGLFramebuffer() { - Release(); -} - -void OGLFramebuffer::Create() { - if (handle != 0) { - return; - } - - glGenFramebuffers(1, &handle); -} - -void OGLFramebuffer::Release() { - glDeleteFramebuffers(1, &handle); - handle = 0; -} diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 975720d0ab..6f9dc012d6 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h @@ -4,76 +4,124 @@ #pragma once +#include <utility> + #include "common/common_types.h" -#include "generated/gl_3_2_core.h" +#include "video_core/renderer_opengl/generated/gl_3_2_core.h" +#include "video_core/renderer_opengl/gl_shader_util.h" -class OGLTexture : public NonCopyable { +class OGLTexture : private NonCopyable { public: - OGLTexture(); - ~OGLTexture(); + OGLTexture() = default; + OGLTexture(OGLTexture&& o) { std::swap(handle, o.handle); } + ~OGLTexture() { Release(); } + OGLTexture& operator=(OGLTexture&& o) { std::swap(handle, o.handle); return *this; } /// Creates a new internal OpenGL resource and stores the handle - void Create(); + void Create() { + if (handle != 0) return; + glGenTextures(1, &handle); + } /// Deletes the internal OpenGL resource - void Release(); + void Release() { + if (handle == 0) return; + glDeleteTextures(1, &handle); + handle = 0; + } - GLuint handle; + GLuint handle = 0; }; -class OGLShader : public NonCopyable { +class OGLShader : private NonCopyable { public: - OGLShader(); - ~OGLShader(); + OGLShader() = default; + OGLShader(OGLShader&& o) { std::swap(handle, o.handle); } + ~OGLShader() { Release(); } + OGLShader& operator=(OGLShader&& o) { std::swap(handle, o.handle); return *this; } /// Creates a new internal OpenGL resource and stores the handle - void Create(const char* vert_shader, const char* frag_shader); + void Create(const char* vert_shader, const char* frag_shader) { + if (handle != 0) return; + handle = ShaderUtil::LoadShaders(vert_shader, frag_shader); + } /// Deletes the internal OpenGL resource - void Release(); + void Release() { + if (handle == 0) return; + glDeleteProgram(handle); + handle = 0; + } - GLuint handle; + GLuint handle = 0; }; -class OGLBuffer : public NonCopyable { +class OGLBuffer : private NonCopyable { public: - OGLBuffer(); - ~OGLBuffer(); + OGLBuffer() = default; + OGLBuffer(OGLBuffer&& o) { std::swap(handle, o.handle); } + ~OGLBuffer() { Release(); } + OGLBuffer& operator=(OGLBuffer&& o) { std::swap(handle, o.handle); return *this; } /// Creates a new internal OpenGL resource and stores the handle - void Create(); + void Create() { + if (handle != 0) return; + glGenBuffers(1, &handle); + } /// Deletes the internal OpenGL resource - void Release(); + void Release() { + if (handle == 0) return; + glDeleteBuffers(1, &handle); + handle = 0; + } - GLuint handle; + GLuint handle = 0; }; -class OGLVertexArray : public NonCopyable { +class OGLVertexArray : private NonCopyable { public: - OGLVertexArray(); - ~OGLVertexArray(); + OGLVertexArray() = default; + OGLVertexArray(OGLVertexArray&& o) { std::swap(handle, o.handle); } + ~OGLVertexArray() { Release(); } + OGLVertexArray& operator=(OGLVertexArray&& o) { std::swap(handle, o.handle); return *this; } /// Creates a new internal OpenGL resource and stores the handle - void Create(); + void Create() { + if (handle != 0) return; + glGenVertexArrays(1, &handle); + } /// Deletes the internal OpenGL resource - void Release(); + void Release() { + if (handle == 0) return; + glDeleteVertexArrays(1, &handle); + handle = 0; + } - GLuint handle; + GLuint handle = 0; }; -class OGLFramebuffer : public NonCopyable { +class OGLFramebuffer : private NonCopyable { public: - OGLFramebuffer(); - ~OGLFramebuffer(); + OGLFramebuffer() = default; + OGLFramebuffer(OGLFramebuffer&& o) { std::swap(handle, o.handle); } + ~OGLFramebuffer() { Release(); } + OGLFramebuffer& operator=(OGLFramebuffer&& o) { std::swap(handle, o.handle); return *this; } /// Creates a new internal OpenGL resource and stores the handle - void Create(); + void Create() { + if (handle != 0) return; + glGenFramebuffers(1, &handle); + } /// Deletes the internal OpenGL resource - void Release(); + void Release() { + if (handle == 0) return; + glDeleteFramebuffers(1, &handle); + handle = 0; + } - GLuint handle; + GLuint handle = 0; }; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 3526e16d50..9efc153373 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -147,20 +147,17 @@ void OpenGLState::Apply() { // Textures for (unsigned texture_index = 0; texture_index < ARRAY_SIZE(texture_units); ++texture_index) { - if (texture_units[texture_index].enabled_2d != cur_state.texture_units[texture_index].enabled_2d) { + if (texture_units[texture_index].enabled_2d != cur_state.texture_units[texture_index].enabled_2d || + texture_units[texture_index].texture_2d != cur_state.texture_units[texture_index].texture_2d) { + glActiveTexture(GL_TEXTURE0 + texture_index); if (texture_units[texture_index].enabled_2d) { - glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture_units[texture_index].texture_2d); } else { - glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); } } - - if (texture_units[texture_index].texture_2d != cur_state.texture_units[texture_index].texture_2d) { - glActiveTexture(GL_TEXTURE0 + texture_index); - glBindTexture(GL_TEXTURE_2D, texture_units[texture_index].texture_2d); - } } // Framebuffer diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index e566f9f7af..3b562da868 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -12,10 +12,37 @@ namespace PicaToGL { +inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) { + static const GLenum filter_mode_table[] = { + GL_NEAREST, // TextureFilter::Nearest + GL_LINEAR // TextureFilter::Linear + }; + + // Range check table for input + if (mode >= ARRAY_SIZE(filter_mode_table)) { + LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode); + UNREACHABLE(); + + return GL_LINEAR; + } + + GLenum gl_mode = filter_mode_table[mode]; + + // Check for dummy values indicating an unknown mode + if (gl_mode == 0) { + LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode); + UNIMPLEMENTED(); + + return GL_LINEAR; + } + + return gl_mode; +} + inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) { static const GLenum wrap_mode_table[] = { GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge - 0, // Unknown + GL_CLAMP_TO_BORDER,// WrapMode::ClampToBorder GL_REPEAT, // WrapMode::Repeat GL_MIRRORED_REPEAT // WrapMode::MirroredRepeat }; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 3399ca1230..96e12839a4 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -2,22 +2,27 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <cstddef> +#include <cstdlib> + +#include "common/assert.h" +#include "common/emu_window.h" +#include "common/logging/log.h" +#include "common/profiler_reporting.h" + #include "core/hw/gpu.h" #include "core/hw/hw.h" #include "core/hw/lcd.h" #include "core/memory.h" #include "core/settings.h" -#include "common/emu_window.h" -#include "common/logging/log.h" -#include "common/profiler_reporting.h" - #include "video_core/video_core.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_shaders.h" -#include <algorithm> +#include "video_core/debug_utils/debug_utils.h" /** * Vertex structure that the drawn screen rectangles are composed of. @@ -126,6 +131,10 @@ void RendererOpenGL::SwapBuffers() { hw_rasterizer->Reset(); } } + + if (Pica::g_debug_context && Pica::g_debug_context->recorder) { + Pica::g_debug_context->recorder->FrameFinished(); + } } /** diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp index 87006a832a..960ae57796 100644 --- a/src/video_core/vertex_shader.cpp +++ b/src/video_core/vertex_shader.cpp @@ -2,8 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <stack> - +#include <boost/container/static_vector.hpp> #include <boost/range/algorithm.hpp> #include <common/file_util.h> @@ -27,7 +26,7 @@ namespace Pica { namespace VertexShader { struct VertexShaderState { - const u32* program_counter; + u32 program_counter; const float24* input_register_table[16]; Math::Vec4<float24> output_registers[16]; @@ -53,7 +52,7 @@ struct VertexShaderState { }; // TODO: Is there a maximal size for this? - std::stack<CallStackElement> call_stack; + boost::container::static_vector<CallStackElement, 16> call_stack; struct { u32 max_offset; // maximum program counter ever reached @@ -71,15 +70,15 @@ static void ProcessShaderCode(VertexShaderState& state) { while (true) { if (!state.call_stack.empty()) { - auto& top = state.call_stack.top(); - if (state.program_counter - program_code.data() == top.final_address) { + auto& top = state.call_stack.back(); + if (state.program_counter == top.final_address) { state.address_registers[2] += top.loop_increment; if (top.repeat_counter-- == 0) { - state.program_counter = &program_code[top.return_address]; - state.call_stack.pop(); + state.program_counter = top.return_address; + state.call_stack.pop_back(); } else { - state.program_counter = &program_code[top.loop_address]; + state.program_counter = top.loop_address; } // TODO: Is "trying again" accurate to hardware? @@ -88,17 +87,16 @@ static void ProcessShaderCode(VertexShaderState& state) { } bool exit_loop = false; - const Instruction& instr = *(const Instruction*)state.program_counter; - const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.common.operand_desc_id]; + const Instruction instr = { program_code[state.program_counter] }; + const SwizzlePattern swizzle = { swizzle_data[instr.common.operand_desc_id] }; - static auto call = [&program_code](VertexShaderState& state, u32 offset, u32 num_instructions, + static auto call = [](VertexShaderState& state, u32 offset, u32 num_instructions, u32 return_offset, u8 repeat_count, u8 loop_increment) { - state.program_counter = &program_code[offset] - 1; // -1 to make sure when incrementing the PC we end up at the correct offset - state.call_stack.push({ offset + num_instructions, return_offset, repeat_count, loop_increment, offset }); + state.program_counter = offset - 1; // -1 to make sure when incrementing the PC we end up at the correct offset + ASSERT(state.call_stack.size() < state.call_stack.capacity()); + state.call_stack.push_back({ offset + num_instructions, return_offset, repeat_count, loop_increment, offset }); }; - u32 binary_offset = state.program_counter - program_code.data(); - - state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + binary_offset); + state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + state.program_counter); auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* { switch (source_reg.GetRegisterType()) { @@ -221,7 +219,7 @@ static void ProcessShaderCode(VertexShaderState& state) { for (int i = 0; i < num_components; ++i) dot = dot + src1[i] * src2[i]; - for (int i = 0; i < num_components; ++i) { + for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -442,13 +440,13 @@ static void ProcessShaderCode(VertexShaderState& state) { case OpCode::Id::JMPC: if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) { - state.program_counter = &program_code[instr.flow_control.dest_offset] - 1; + state.program_counter = instr.flow_control.dest_offset - 1; } break; case OpCode::Id::JMPU: if (uniforms.b[instr.flow_control.bool_uniform_id]) { - state.program_counter = &program_code[instr.flow_control.dest_offset] - 1; + state.program_counter = instr.flow_control.dest_offset - 1; } break; @@ -456,7 +454,7 @@ static void ProcessShaderCode(VertexShaderState& state) { call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, - binary_offset + 1, 0, 0); + state.program_counter + 1, 0, 0); break; case OpCode::Id::CALLU: @@ -464,7 +462,7 @@ static void ProcessShaderCode(VertexShaderState& state) { call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, - binary_offset + 1, 0, 0); + state.program_counter + 1, 0, 0); } break; @@ -473,7 +471,7 @@ static void ProcessShaderCode(VertexShaderState& state) { call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, - binary_offset + 1, 0, 0); + state.program_counter + 1, 0, 0); } break; @@ -483,8 +481,8 @@ static void ProcessShaderCode(VertexShaderState& state) { case OpCode::Id::IFU: if (uniforms.b[instr.flow_control.bool_uniform_id]) { call(state, - binary_offset + 1, - instr.flow_control.dest_offset - binary_offset - 1, + state.program_counter + 1, + instr.flow_control.dest_offset - state.program_counter - 1, instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0); } else { call(state, @@ -501,8 +499,8 @@ static void ProcessShaderCode(VertexShaderState& state) { if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) { call(state, - binary_offset + 1, - instr.flow_control.dest_offset - binary_offset - 1, + state.program_counter + 1, + instr.flow_control.dest_offset - state.program_counter - 1, instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0); } else { call(state, @@ -519,8 +517,8 @@ static void ProcessShaderCode(VertexShaderState& state) { state.address_registers[2] = uniforms.i[instr.flow_control.int_uniform_id].y; call(state, - binary_offset + 1, - instr.flow_control.dest_offset - binary_offset + 1, + state.program_counter + 1, + instr.flow_control.dest_offset - state.program_counter + 1, instr.flow_control.dest_offset + 1, uniforms.i[instr.flow_control.int_uniform_id].x, uniforms.i[instr.flow_control.int_uniform_id].z); @@ -546,20 +544,17 @@ static void ProcessShaderCode(VertexShaderState& state) { static Common::Profiling::TimingCategory shader_category("Vertex Shader"); -OutputVertex RunShader(const InputVertex& input, int num_attributes) { +OutputVertex RunShader(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const State::ShaderSetup& setup) { Common::Profiling::ScopeTimer timer(shader_category); - const auto& regs = g_state.regs; - const auto& vs = g_state.vs; VertexShaderState state; - const u32* main = &vs.program_code[regs.vs_main_offset]; - state.program_counter = (u32*)main; + state.program_counter = config.main_offset; state.debug.max_offset = 0; state.debug.max_opdesc_id = 0; // Setup input register table - const auto& attribute_register_map = regs.vs_input_register_map; + const auto& attribute_register_map = config.input_register_map; float24 dummy_register; boost::fill(state.input_register_table, &dummy_register); @@ -584,16 +579,18 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) { state.conditional_code[1] = false; ProcessShaderCode(state); - DebugUtils::DumpShader(vs.program_code.data(), state.debug.max_offset, vs.swizzle_data.data(), - state.debug.max_opdesc_id, regs.vs_main_offset, - regs.vs_output_attributes); +#if PICA_DUMP_SHADERS + DebugUtils::DumpShader(setup.program_code.data(), state.debug.max_offset, setup.swizzle_data.data(), + state.debug.max_opdesc_id, config.main_offset, + g_state.regs.vs_output_attributes); // TODO: Don't hardcode VS here +#endif // Setup output data OutputVertex ret; // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to // figure out what those circumstances are and enable the remaining outputs then. for (int i = 0; i < 7; ++i) { - const auto& output_register_map = regs.vs_output_attributes[i]; + const auto& output_register_map = g_state.regs.vs_output_attributes[i]; // TODO: Don't hardcode VS here u32 semantics[4] = { output_register_map.map_x, output_register_map.map_y, diff --git a/src/video_core/vertex_shader.h b/src/video_core/vertex_shader.h index 7471a6de8e..97f9250ddf 100644 --- a/src/video_core/vertex_shader.h +++ b/src/video_core/vertex_shader.h @@ -4,11 +4,10 @@ #pragma once -#include <initializer_list> +#include <type_traits> -#include <common/common_types.h> +#include "common/vector_math.h" -#include "math.h" #include "pica.h" namespace Pica { @@ -66,7 +65,7 @@ struct OutputVertex { static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD"); static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size"); -OutputVertex RunShader(const InputVertex& input, int num_attributes); +OutputVertex RunShader(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const State::ShaderSetup& setup); } // namespace diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index 3f24df7bd6..14b33c9ddf 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -4,12 +4,11 @@ #pragma once -#include "common/emu_window.h" - -#include "renderer_base.h" - #include <atomic> +class EmuWindow; +class RendererBase; + //////////////////////////////////////////////////////////////////////////////////////////////////// // Video Core namespace |