diff options
287 files changed, 4909 insertions, 2888 deletions
diff --git a/.gitmodules b/.gitmodules index f498a60de..dbb1b0dd3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "dynarmic"] path = externals/dynarmic url = https://github.com/MerryMage/dynarmic.git +[submodule "xbyak"] + path = externals/xbyak + url = https://github.com/herumi/xbyak.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 25631b32e..52a1fd492 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ # CMake 3.2 required for cmake to know the right flags for CXX standard on OSX cmake_minimum_required(VERSION 3.2) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) function(download_bundled_external remote_path lib_name prefix_var) set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}") @@ -183,8 +184,10 @@ IF (APPLE) FIND_LIBRARY(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++") + if (CMAKE_CXX_COMPILER_ID STREQUAL Clang) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++") + endif() ELSEIF (WIN32) # WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista) add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600) @@ -254,6 +257,8 @@ set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih") include_directories(${INI_PREFIX}) add_subdirectory(${INI_PREFIX}) +add_subdirectory(externals) + option(DYNARMIC_TESTS OFF) add_subdirectory(externals/dynarmic) @@ -289,3 +294,15 @@ if(ENABLE_QT AND UNIX AND NOT APPLE) install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.xml" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages") endif() + +if(UNIX) + if(ENABLE_SDL2) + install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.6" + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man6") + endif() + + if (ENABLE_QT) + install(FILES "${CMAKE_SOURCE_DIR}/dist/citra-qt.6" + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man6") + endif() +endif() diff --git a/CMakeModules/CopyCitraQt5Deps.cmake b/CMakeModules/CopyCitraQt5Deps.cmake new file mode 100644 index 000000000..05f58cf9a --- /dev/null +++ b/CMakeModules/CopyCitraQt5Deps.cmake @@ -0,0 +1,17 @@ +function(copy_citra_Qt5_deps target_dir) + include(WindowsCopyFiles) + set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/") + set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") + set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") + set(PLATFORMS ${DLL_DEST}platforms/) + windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} + icudt*.dll + icuin*.dll + icuuc*.dll + Qt5Core$<$<CONFIG:Debug>:d>.* + Qt5Gui$<$<CONFIG:Debug>:d>.* + Qt5OpenGL$<$<CONFIG:Debug>:d>.* + Qt5Widgets$<$<CONFIG:Debug>:d>.* + ) + windows_copy_files(citra-qt ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) +endfunction(copy_citra_Qt5_deps) diff --git a/CMakeModules/CopyCitraSDLDeps.cmake b/CMakeModules/CopyCitraSDLDeps.cmake new file mode 100644 index 000000000..4f9e4aeb9 --- /dev/null +++ b/CMakeModules/CopyCitraSDLDeps.cmake @@ -0,0 +1,5 @@ +function(copy_citra_SDL_deps target_dir) + include(WindowsCopyFiles) + set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/") + windows_copy_files(${target_dir} ${SDL2_DLL_DIR} ${DLL_DEST} SDL2.dll) +endfunction(copy_citra_SDL_deps) diff --git a/dist/citra-qt.6 b/dist/citra-qt.6 new file mode 100644 index 000000000..d09e96b5d --- /dev/null +++ b/dist/citra-qt.6 @@ -0,0 +1,40 @@ +.Dd November 22 2016 +.Dt citra-qt 6 +.Os +.Sh NAME +.Nm Citra-Qt +.Nd Nintendo 3DS Emulator/Debugger (Qt) +.Sh SYNOPSIS +.Nm citra-qt +.Op Ar file +.Sh DESCRIPTION +Citra is an experimental open-source Nintendo 3DS emulator/debugger. +.Pp +.Nm citra-qt +is the Qt implementation. +.Sh FILES +.Bl -tag -width Ds +.It Pa $XDG_DATA_HOME/citra-emu +Emulator storage. +.It Pa $XDG_CONFIG_HOME/citra-emu +Configuration files. +.El +.Sh AUTHORS +This document is made available to you under the CC-BY license. +.Pp +Citra is made by a team of volunteers. These contributors are listed + at <\fBhttps://github.com/citra-emu/citra/contributors\fR>. +.Pp +.Sh SEE ALSO +.Bl -tag -width Ds +.It Xr citra 6 +The SDL frontend of the application +.El +.Pp +Resources are available for this project: +.Bl -tag -width Ds +.It <\fBhttps://citra-emu.org\fR> +The main homepage of the project. +.It <\fBhttps://github.com/citra-emu/citra\fR> +The main source code repository for the Citra emulator. +.Pp diff --git a/dist/citra.6 b/dist/citra.6 new file mode 100644 index 000000000..72483b1dd --- /dev/null +++ b/dist/citra.6 @@ -0,0 +1,49 @@ +.Dd November 22 2016 +.Dt citra 6 +.Os +.Sh NAME +.Nm Citra +.Nd Nintendo 3DS Emulator/Debugger (SDL) +.Sh SYNOPSIS +.Nm citra +.Op Ar options +.Op Ar file +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl g Ar port , Fl Fl gdbport Ar port +Starts the GDB stub on the specified port +.It Fl h , Fl Fl help +Shows syntax help and exits +.It Fl v , Fl Fl version +Describes the installed version and exits +.Sh DESCRIPTION +Citra is an experimental open-source Nintendo 3DS emulator/debugger. +.Pp +.Nm citra +is the Simple DirectMedia Layer (SDL) implementation. +.Sh FILES +.Bl -tag -width Ds +.It Pa $XDG_DATA_HOME/citra-emu +Emulator storage. +.It Pa $XDG_CONFIG_HOME/citra-emu +Configuration files. +.El +.Sh AUTHORS +This document is made available to you under the CC-BY license. +.Pp +Citra is made by a team of volunteers. These contributors are listed + at <\fBhttps://github.com/citra-emu/citra/contributors\fR>. +.Pp +.Sh SEE ALSO +.Bl -tag -width Ds +.It Xr citra-qt 6 +The Qt frontend of the application +.El +.Pp +Resources are available for this project: +.Bl -tag -width Ds +.It <\fBhttps://citra-emu.org\fR> +The main homepage of the project. +.It <\fBhttps://github.com/citra-emu/citra\fR> +The main source code repository for the Citra emulator. +.Pp diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt new file mode 100644 index 000000000..7e4b05ffc --- /dev/null +++ b/externals/CMakeLists.txt @@ -0,0 +1,8 @@ +# Xbyak +if (ARCHITECTURE_x86_64) + add_library(xbyak INTERFACE) + target_include_directories(xbyak INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak) + if (NOT MSVC) + target_compile_options(xbyak INTERFACE -fno-operator-names) + endif() +endif() diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 54d051977f7a6af9c7596ba6a4e6eb467bd1e0b +Subproject 36082087ded632079b16d24137fdd0c450ce82e diff --git a/externals/xbyak b/externals/xbyak new file mode 160000 +Subproject fe4765d2fed4e990ea5e9661b6bc5fc9bf48ec1 diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index 49260de7c..ba6acf28e 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -23,9 +23,9 @@ static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles static void AudioTickCallback(u64 /*userdata*/, int cycles_late) { if (DSP::HLE::Tick()) { // TODO(merry): Signal all the other interrupts as appropriate. - DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Audio); + Service::DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Audio); // HACK(merry): Added to prevent regressions. Will remove soon. - DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Binary); + Service::DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Binary); } // Reschedule recurrent event @@ -56,22 +56,17 @@ void AddAddressSpace(Kernel::VMManager& address_space) { } void SelectSink(std::string sink_id) { - if (sink_id == "auto") { - // Auto-select. - // g_sink_details is ordered in terms of desirability, with the best choice at the front. - const auto& sink_detail = g_sink_details.front(); - DSP::HLE::SetSink(sink_detail.factory()); - return; - } - auto iter = std::find_if(g_sink_details.begin(), g_sink_details.end(), [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); - if (iter == g_sink_details.end()) { - LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id"); - DSP::HLE::SetSink(std::make_unique<NullSink>()); - return; + if (sink_id == "auto" || iter == g_sink_details.end()) { + if (sink_id != "auto") { + LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id %s", sink_id.c_str()); + } + // Auto-select. + // g_sink_details is ordered in terms of desirability, with the best choice at the front. + iter = g_sink_details.begin(); } DSP::HLE::SetSink(iter->factory()); diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp index bc69acbc2..24074a514 100644 --- a/src/audio_core/hle/pipe.cpp +++ b/src/audio_core/hle/pipe.cpp @@ -104,7 +104,7 @@ static void AudioPipeWriteStructAddresses() { WriteU16(DspPipe::Audio, addr); } // Signal that we have data on this pipe. - DSP_DSP::SignalPipeInterrupt(DspPipe::Audio); + Service::DSP_DSP::SignalPipeInterrupt(DspPipe::Audio); } void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h index fa81718ed..e3e4dc353 100644 --- a/src/audio_core/time_stretch.h +++ b/src/audio_core/time_stretch.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <cstddef> #include <memory> #include <vector> diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index f9c488a1a..ecb5d2dfe 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -1,3 +1,5 @@ +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) + set(SRCS emu_window/emu_window_sdl2.cpp citra.cpp @@ -28,11 +30,6 @@ if(UNIX AND NOT APPLE) endif() if (MSVC) - include(WindowsCopyFiles) - - set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/") - - windows_copy_files(citra ${SDL2_DLL_DIR} ${DLL_DEST} SDL2.dll) - - unset(DLL_DEST) + include(CopyCitraSDLDeps) + copy_citra_SDL_deps(citra) endif() diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index f9387e61c..99c096ac7 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -18,7 +18,7 @@ #endif #ifdef _WIN32 -#include <Windows.h> +#include <windows.h> #endif #include "citra/config.h" @@ -33,7 +33,6 @@ #include "core/gdbstub/gdbstub.h" #include "core/loader/loader.h" #include "core/settings.h" -#include "core/system.h" #include "video_core/video_core.h" static void PrintHelp(const char* argv0) { @@ -64,7 +63,7 @@ int main(int argc, char** argv) { return -1; } #endif - std::string boot_filename; + std::string filepath; static struct option long_options[] = { {"gdbport", required_argument, 0, 'g'}, @@ -97,9 +96,9 @@ int main(int argc, char** argv) { } } else { #ifdef _WIN32 - boot_filename = Common::UTF16ToUTF8(argv_w[optind]); + filepath = Common::UTF16ToUTF8(argv_w[optind]); #else - boot_filename = argv[optind]; + filepath = argv[optind]; #endif optind++; } @@ -115,7 +114,7 @@ int main(int argc, char** argv) { MicroProfileOnThreadCreate("EmuThread"); SCOPE_EXIT({ MicroProfileShutdown(); }); - if (boot_filename.empty()) { + if (filepath.empty()) { LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); return -1; } @@ -127,32 +126,25 @@ int main(int argc, char** argv) { Settings::values.use_gdbstub = use_gdbstub; Settings::Apply(); - std::unique_ptr<EmuWindow_SDL2> emu_window = std::make_unique<EmuWindow_SDL2>(); + std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>()}; - std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(boot_filename); - if (!loader) { - LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", boot_filename.c_str()); - return -1; - } - - boost::optional<u32> system_mode = loader->LoadKernelSystemMode(); + Core::System& system{Core::System::GetInstance()}; - if (!system_mode) { - LOG_CRITICAL(Frontend, "Failed to load ROM (Could not determine system mode)!"); - return -1; - } + SCOPE_EXIT({ system.Shutdown(); }); - System::Init(emu_window.get(), system_mode.get()); - SCOPE_EXIT({ System::Shutdown(); }); + const Core::System::ResultStatus load_result{system.Load(emu_window.get(), filepath)}; - Loader::ResultStatus load_result = loader->Load(); - if (Loader::ResultStatus::Success != load_result) { - LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result); + switch (load_result) { + case Core::System::ResultStatus::ErrorGetLoader: + LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filepath.c_str()); + return -1; + case Core::System::ResultStatus::ErrorLoader: + LOG_CRITICAL(Frontend, "Failed to load ROM!"); return -1; } while (emu_window->IsOpen()) { - Core::RunLoop(); + system.RunLoop(); } return 0; diff --git a/src/citra/citra.rc b/src/citra/citra.rc index b0edb2e6b..fea603004 100644 --- a/src/citra/citra.rc +++ b/src/citra/citra.rc @@ -5,5 +5,5 @@ // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -GLFW_ICON ICON "..\\..\\dist\\citra.ico" +CITRA_ICON ICON "../../dist/citra.ico" diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index 8abe48984..b0d82b670 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -9,10 +9,10 @@ #include <SDL.h> #include <glad/glad.h> #include "citra/emu_window/emu_window_sdl2.h" -#include "common/key_map.h" #include "common/logging/log.h" #include "common/scm_rev.h" #include "common/string_util.h" +#include "core/frontend/key_map.h" #include "core/hle/service/hid/hid.h" #include "core/settings.h" #include "video_core/video_core.h" diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h index e4d14ef12..c8cd919c6 100644 --- a/src/citra/emu_window/emu_window_sdl2.h +++ b/src/citra/emu_window/emu_window_sdl2.h @@ -5,7 +5,7 @@ #pragma once #include <utility> -#include "common/emu_window.h" +#include "core/frontend/emu_window.h" struct SDL_Window; diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index a9dacd5f1..93f1c339d 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -1,17 +1,18 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) set(SRCS config.cpp debugger/callstack.cpp debugger/disassembler.cpp - debugger/graphics.cpp - debugger/graphics_breakpoint_observer.cpp - debugger/graphics_breakpoints.cpp - debugger/graphics_cmdlists.cpp - debugger/graphics_surface.cpp - debugger/graphics_tracing.cpp - debugger/graphics_vertex_shader.cpp + debugger/graphics/graphics.cpp + debugger/graphics/graphics_breakpoint_observer.cpp + debugger/graphics/graphics_breakpoints.cpp + debugger/graphics/graphics_cmdlists.cpp + debugger/graphics/graphics_surface.cpp + debugger/graphics/graphics_tracing.cpp + debugger/graphics/graphics_vertex_shader.cpp debugger/profiler.cpp debugger/ramview.cpp debugger/registers.cpp @@ -38,14 +39,14 @@ set(HEADERS config.h debugger/callstack.h debugger/disassembler.h - debugger/graphics.h - debugger/graphics_breakpoint_observer.h - debugger/graphics_breakpoints.h - debugger/graphics_breakpoints_p.h - debugger/graphics_cmdlists.h - debugger/graphics_surface.h - debugger/graphics_tracing.h - debugger/graphics_vertex_shader.h + debugger/graphics/graphics.h + debugger/graphics/graphics_breakpoint_observer.h + debugger/graphics/graphics_breakpoints.h + debugger/graphics/graphics_breakpoints_p.h + debugger/graphics/graphics_cmdlists.h + debugger/graphics/graphics_surface.h + debugger/graphics/graphics_tracing.h + debugger/graphics/graphics_vertex_shader.h debugger/profiler.h debugger/ramview.h debugger/registers.h @@ -107,27 +108,9 @@ if(UNIX AND NOT APPLE) install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") endif() -if (Qt5_FOUND AND MSVC) - include(WindowsCopyFiles) - - set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") - set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") - set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/") - set(PLATFORMS ${DLL_DEST}platforms/) - - windows_copy_files(citra-qt ${Qt5_DLL_DIR} ${DLL_DEST} - icudt*.dll - icuin*.dll - icuuc*.dll - Qt5Core$<$<CONFIG:Debug>:d>.* - Qt5Gui$<$<CONFIG:Debug>:d>.* - Qt5OpenGL$<$<CONFIG:Debug>:d>.* - Qt5Widgets$<$<CONFIG:Debug>:d>.* - ) - windows_copy_files(citra-qt ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) - - unset(Qt5_DLL_DIR) - unset(Qt5_PLATFORMS_DIR) - unset(DLL_DEST) - unset(PLATFORMS) +if (MSVC) + include(CopyCitraQt5Deps) + include(CopyCitraSDLDeps) + copy_citra_Qt5_deps(citra-qt) + copy_citra_SDL_deps(citra-qt) endif() diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 7699ca8d0..57fde6caa 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -9,21 +9,14 @@ #endif #include "citra_qt/bootmanager.h" -#include "common/key_map.h" #include "common/microprofile.h" #include "common/scm_rev.h" #include "common/string_util.h" #include "core/core.h" -#include "core/settings.h" -#include "core/system.h" +#include "core/frontend/key_map.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/video_core.h" -#define APP_NAME "citra" -#define APP_VERSION "0.1-" VERSION -#define APP_TITLE APP_NAME " " APP_VERSION -#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" - EmuThread::EmuThread(GRenderWindow* render_window) : exec_step(false), running(false), stop_run(false), render_window(render_window) {} @@ -43,7 +36,7 @@ void EmuThread::run() { if (!was_active) emit DebugModeLeft(); - Core::RunLoop(); + Core::System::GetInstance().RunLoop(); was_active = running || exec_step; if (!was_active && !stop_run) @@ -53,7 +46,7 @@ void EmuThread::run() { emit DebugModeLeft(); exec_step = false; - Core::SingleStep(); + Core::System::GetInstance().SingleStep(); emit DebugModeEntered(); yieldCurrentThread(); @@ -65,7 +58,7 @@ void EmuThread::run() { } // Shutdown the core emulation - System::Shutdown(); + Core::System::GetInstance().Shutdown(); #if MICROPROFILE_ENABLED MicroProfileOnThreadExit(); diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 67228b94f..43015390b 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -2,13 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <atomic> #include <condition_variable> #include <mutex> #include <QGLWidget> #include <QThread> -#include "common/emu_window.h" #include "common/thread.h" +#include "core/frontend/emu_window.h" class QKeyEvent; class QScreen; @@ -21,7 +23,7 @@ class EmuThread : public QThread { Q_OBJECT public: - EmuThread(GRenderWindow* render_window); + explicit EmuThread(GRenderWindow* render_window); /** * Start emulation (on new thread) diff --git a/src/citra_qt/citra-qt.rc b/src/citra_qt/citra-qt.rc index 3c7239853..fea603004 100644 --- a/src/citra_qt/citra-qt.rc +++ b/src/citra_qt/citra-qt.rc @@ -5,5 +5,5 @@ // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_ICON1 ICON "..\\..\\dist\\citra.ico" +CITRA_ICON ICON "../../dist/citra.ico" diff --git a/src/citra_qt/configure_general.cpp b/src/citra_qt/configure_general.cpp index 0b802d081..ac90a6df4 100644 --- a/src/citra_qt/configure_general.cpp +++ b/src/citra_qt/configure_general.cpp @@ -4,8 +4,8 @@ #include "citra_qt/configure_general.h" #include "citra_qt/ui_settings.h" +#include "core/core.h" #include "core/settings.h" -#include "core/system.h" #include "ui_configure_general.h" ConfigureGeneral::ConfigureGeneral(QWidget* parent) @@ -14,7 +14,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) ui->setupUi(this); this->setConfiguration(); - ui->toggle_cpu_jit->setEnabled(!System::IsPoweredOn()); + ui->toggle_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); } ConfigureGeneral::~ConfigureGeneral() {} diff --git a/src/citra_qt/configure_graphics.cpp b/src/citra_qt/configure_graphics.cpp index 36f10c8d7..cea7db388 100644 --- a/src/citra_qt/configure_graphics.cpp +++ b/src/citra_qt/configure_graphics.cpp @@ -3,8 +3,8 @@ // Refer to the license.txt file included. #include "citra_qt/configure_graphics.h" +#include "core/core.h" #include "core/settings.h" -#include "core/system.h" #include "ui_configure_graphics.h" ConfigureGraphics::ConfigureGraphics(QWidget* parent) @@ -13,7 +13,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) ui->setupUi(this); this->setConfiguration(); - ui->toggle_vsync->setEnabled(!System::IsPoweredOn()); + ui->toggle_vsync->setEnabled(!Core::System::GetInstance().IsPoweredOn()); } ConfigureGraphics::~ConfigureGraphics() {} diff --git a/src/citra_qt/configure_input.cpp b/src/citra_qt/configure_input.cpp index d321db71f..3e6803b8a 100644 --- a/src/citra_qt/configure_input.cpp +++ b/src/citra_qt/configure_input.cpp @@ -5,15 +5,33 @@ #include <memory> #include <utility> #include <QTimer> +#include "citra_qt/config.h" #include "citra_qt/configure_input.h" +static QString getKeyName(Qt::Key key_code) { + switch (key_code) { + case Qt::Key_Shift: + return QObject::tr("Shift"); + case Qt::Key_Control: + return QObject::tr("Ctrl"); + case Qt::Key_Alt: + return QObject::tr("Alt"); + case Qt::Key_Meta: + case -1: + return ""; + default: + return QKeySequence(key_code).toString(); + } +} + ConfigureInput::ConfigureInput(QWidget* parent) - : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { + : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), + timer(std::make_unique<QTimer>()) { ui->setupUi(this); + setFocusPolicy(Qt::ClickFocus); - // Initialize mapping of input enum to UI button. - input_mapping = { + button_map = { {Settings::NativeInput::Values::A, ui->buttonA}, {Settings::NativeInput::Values::B, ui->buttonB}, {Settings::NativeInput::Values::X, ui->buttonX}, @@ -40,114 +58,89 @@ ConfigureInput::ConfigureInput(QWidget* parent) {Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod}, }; - // Attach handle click method to each button click. - for (const auto& entry : input_mapping) { - connect(entry.second, SIGNAL(released()), this, SLOT(handleClick())); + for (const auto& entry : button_map) { + const Settings::NativeInput::Values input_id = entry.first; + connect(entry.second, &QPushButton::released, + [this, input_id]() { handleClick(input_id); }); } - connect(ui->buttonRestoreDefaults, SIGNAL(released()), this, SLOT(restoreDefaults())); - setFocusPolicy(Qt::ClickFocus); - timer = new QTimer(this); + + connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); + timer->setSingleShot(true); - connect(timer, &QTimer::timeout, this, [&]() { - key_pressed = Qt::Key_Escape; - setKey(); + connect(timer.get(), &QTimer::timeout, [this]() { + releaseKeyboard(); + releaseMouse(); + current_input_id = boost::none; + updateButtonLabels(); }); - this->setConfiguration(); -} -void ConfigureInput::handleClick() { - QPushButton* sender = qobject_cast<QPushButton*>(QObject::sender()); - previous_mapping = sender->text(); - sender->setText(tr("[waiting]")); - sender->setFocus(); - grabKeyboard(); - grabMouse(); - changing_button = sender; - timer->start(5000); // Cancel after 5 seconds + this->loadConfiguration(); } void ConfigureInput::applyConfiguration() { - for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - int value = getKeyValue(input_mapping[Settings::NativeInput::Values(i)]->text()); - Settings::values.input_mappings[Settings::NativeInput::All[i]] = value; + for (const auto& input_id : Settings::NativeInput::All) { + const size_t index = static_cast<size_t>(input_id); + Settings::values.input_mappings[index] = static_cast<int>(key_map[input_id]); } Settings::Apply(); } -void ConfigureInput::setConfiguration() { - for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - QString keyValue = getKeyName(Settings::values.input_mappings[i]); - input_mapping[Settings::NativeInput::Values(i)]->setText(keyValue); +void ConfigureInput::loadConfiguration() { + for (const auto& input_id : Settings::NativeInput::All) { + const size_t index = static_cast<size_t>(input_id); + key_map[input_id] = static_cast<Qt::Key>(Settings::values.input_mappings[index]); } + updateButtonLabels(); } -void ConfigureInput::keyPressEvent(QKeyEvent* event) { - if (!changing_button) - return; - if (!event || event->key() == Qt::Key_unknown) - return; - key_pressed = event->key(); - timer->stop(); - setKey(); +void ConfigureInput::restoreDefaults() { + for (const auto& input_id : Settings::NativeInput::All) { + const size_t index = static_cast<size_t>(input_id); + key_map[input_id] = static_cast<Qt::Key>(Config::defaults[index].toInt()); + } + updateButtonLabels(); + applyConfiguration(); } -void ConfigureInput::setKey() { - const QString key_value = getKeyName(key_pressed); - if (key_pressed == Qt::Key_Escape) - changing_button->setText(previous_mapping); - else - changing_button->setText(key_value); - removeDuplicates(key_value); - key_pressed = Qt::Key_unknown; - releaseKeyboard(); - releaseMouse(); - changing_button = nullptr; - previous_mapping = nullptr; +void ConfigureInput::updateButtonLabels() { + for (const auto& input_id : Settings::NativeInput::All) { + button_map[input_id]->setText(getKeyName(key_map[input_id])); + } } -QString ConfigureInput::getKeyName(int key_code) const { - if (key_code == Qt::Key_Shift) - return tr("Shift"); - if (key_code == Qt::Key_Control) - return tr("Ctrl"); - if (key_code == Qt::Key_Alt) - return tr("Alt"); - if (key_code == Qt::Key_Meta) - return ""; - if (key_code == -1) - return ""; +void ConfigureInput::handleClick(Settings::NativeInput::Values input_id) { + QPushButton* button = button_map[input_id]; + button->setText(tr("[press key]")); + button->setFocus(); - return QKeySequence(key_code).toString(); -} + current_input_id = input_id; -Qt::Key ConfigureInput::getKeyValue(const QString& text) const { - if (text == "Shift") - return Qt::Key_Shift; - if (text == "Ctrl") - return Qt::Key_Control; - if (text == "Alt") - return Qt::Key_Alt; - if (text == "Meta") - return Qt::Key_unknown; - if (text == "") - return Qt::Key_unknown; - - return Qt::Key(QKeySequence(text)[0]); + grabKeyboard(); + grabMouse(); + timer->start(5000); // Cancel after 5 seconds } -void ConfigureInput::removeDuplicates(const QString& newValue) { - for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - if (changing_button != input_mapping[Settings::NativeInput::Values(i)]) { - const QString oldValue = input_mapping[Settings::NativeInput::Values(i)]->text(); - if (newValue == oldValue) - input_mapping[Settings::NativeInput::Values(i)]->setText(""); - } - } +void ConfigureInput::keyPressEvent(QKeyEvent* event) { + releaseKeyboard(); + releaseMouse(); + + if (!current_input_id || !event) + return; + + if (event->key() != Qt::Key_Escape) + setInput(*current_input_id, static_cast<Qt::Key>(event->key())); + + updateButtonLabels(); + current_input_id = boost::none; + timer->stop(); } -void ConfigureInput::restoreDefaults() { - for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - const QString keyValue = getKeyName(Config::defaults[i].toInt()); - input_mapping[Settings::NativeInput::Values(i)]->setText(keyValue); +void ConfigureInput::setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed) { + // Remove duplicates + for (auto& pair : key_map) { + if (pair.second == key_pressed) + pair.second = Qt::Key_unknown; } + + key_map[input_id] = key_pressed; } diff --git a/src/citra_qt/configure_input.h b/src/citra_qt/configure_input.h index 5183b904d..bc343db83 100644 --- a/src/citra_qt/configure_input.h +++ b/src/citra_qt/configure_input.h @@ -7,7 +7,7 @@ #include <memory> #include <QKeyEvent> #include <QWidget> -#include "citra_qt/config.h" +#include <boost/optional.hpp> #include "core/settings.h" #include "ui_configure_input.h" @@ -30,35 +30,28 @@ public: private: std::unique_ptr<Ui::ConfigureInput> ui; - std::map<Settings::NativeInput::Values, QPushButton*> input_mapping; - int key_pressed; - QPushButton* changing_button = nullptr; ///< button currently waiting for key press. - QString previous_mapping; - QTimer* timer; - /// Load configuration settings into button text - void setConfiguration(); + /// This input is currently awaiting configuration. + /// (i.e.: its corresponding QPushButton has been pressed.) + boost::optional<Settings::NativeInput::Values> current_input_id; + std::unique_ptr<QTimer> timer; - /// Check all inputs for duplicate keys. Clears out any other button with the same value as this - /// button's new value. - void removeDuplicates(const QString& newValue); - - /// Handle key press event for input tab when a button is 'waiting'. - void keyPressEvent(QKeyEvent* event) override; - - /// Convert key ASCII value to its' letter/name - QString getKeyName(int key_code) const; - - /// Convert letter/name of key to its ASCII value. - Qt::Key getKeyValue(const QString& text) const; - - /// Set button text to name of key pressed. - void setKey(); - -private slots: - /// Event handler for all button released() event. - void handleClick(); + /// Each input is represented by a QPushButton. + std::map<Settings::NativeInput::Values, QPushButton*> button_map; + /// Each input is configured to respond to the press of a Qt::Key. + std::map<Settings::NativeInput::Values, Qt::Key> key_map; + /// Load configuration settings. + void loadConfiguration(); /// Restore all buttons to their default values. void restoreDefaults(); + /// Update UI to reflect current configuration. + void updateButtonLabels(); + + /// Called when the button corresponding to input_id was pressed. + void handleClick(Settings::NativeInput::Values input_id); + /// Handle key press events. + void keyPressEvent(QKeyEvent* event) override; + /// Configure input input_id to respond to key key_pressed. + void setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed); }; diff --git a/src/citra_qt/configure_system.cpp b/src/citra_qt/configure_system.cpp index 873d314ec..eb1276ef3 100644 --- a/src/citra_qt/configure_system.cpp +++ b/src/citra_qt/configure_system.cpp @@ -6,7 +6,6 @@ #include "citra_qt/ui_settings.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/fs/archive.h" -#include "core/system.h" #include "ui_configure_system.h" static const std::array<int, 12> days_in_month = {{ @@ -24,7 +23,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui:: ConfigureSystem::~ConfigureSystem() {} void ConfigureSystem::setConfiguration() { - enabled = !System::IsPoweredOn(); + enabled = !Core::System::GetInstance().IsPoweredOn(); if (!enabled) { ReadSystemSettings(); diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp index c66f2b96a..c1db93583 100644 --- a/src/citra_qt/debugger/callstack.cpp +++ b/src/citra_qt/debugger/callstack.cpp @@ -25,7 +25,7 @@ CallstackWidget::CallstackWidget(QWidget* parent) : QDockWidget(parent) { void CallstackWidget::OnDebugModeEntered() { // Stack pointer - const u32 sp = Core::g_app_core->GetReg(13); + const u32 sp = Core::CPU().GetReg(13); Clear(); diff --git a/src/citra_qt/debugger/callstack.h b/src/citra_qt/debugger/callstack.h index 765757986..f04ab9c7e 100644 --- a/src/citra_qt/debugger/callstack.h +++ b/src/citra_qt/debugger/callstack.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <QDockWidget> #include "ui_callstack.h" @@ -11,7 +13,7 @@ class CallstackWidget : public QDockWidget { Q_OBJECT public: - CallstackWidget(QWidget* parent = nullptr); + explicit CallstackWidget(QWidget* parent = nullptr); public slots: void OnDebugModeEntered(); diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp index 1ee6bbd6a..e9c8ad858 100644 --- a/src/citra_qt/debugger/disassembler.cpp +++ b/src/citra_qt/debugger/disassembler.cpp @@ -185,13 +185,13 @@ DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) } void DisassemblerWidget::Init() { - model->ParseFromAddress(Core::g_app_core->GetPC()); + model->ParseFromAddress(Core::CPU().GetPC()); disasm_ui.treeView->resizeColumnToContents(0); disasm_ui.treeView->resizeColumnToContents(1); disasm_ui.treeView->resizeColumnToContents(2); - QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::g_app_core->GetPC()); + QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::CPU().GetPC()); disasm_ui.treeView->scrollTo(model_index); disasm_ui.treeView->selectionModel()->setCurrentIndex( model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); @@ -214,8 +214,8 @@ void DisassemblerWidget::OnPause() { emu_thread->SetRunning(false); // TODO: By now, the CPU might not have actually stopped... - if (Core::g_app_core) { - model->SetNextInstruction(Core::g_app_core->GetPC()); + if (Core::System::GetInstance().IsPoweredOn()) { + model->SetNextInstruction(Core::CPU().GetPC()); } } @@ -224,7 +224,7 @@ void DisassemblerWidget::OnToggleStartStop() { } void DisassemblerWidget::OnDebugModeEntered() { - u32 next_instr = Core::g_app_core->GetPC(); + u32 next_instr = Core::CPU().GetPC(); if (model->GetBreakPoints().IsAddressBreakPoint(next_instr)) emu_thread->SetRunning(false); diff --git a/src/citra_qt/debugger/disassembler.h b/src/citra_qt/debugger/disassembler.h index 2ca6c2bd4..a6e59515c 100644 --- a/src/citra_qt/debugger/disassembler.h +++ b/src/citra_qt/debugger/disassembler.h @@ -17,7 +17,7 @@ class DisassemblerModel : public QAbstractListModel { Q_OBJECT public: - DisassemblerModel(QObject* parent); + explicit DisassemblerModel(QObject* parent); int columnCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -38,9 +38,7 @@ private: unsigned int program_counter; QModelIndex selection; - - // TODO: Make BreakPoints less crappy (i.e. const-correct) so that this needn't be mutable. - mutable BreakPoints breakpoints; + BreakPoints breakpoints; }; class DisassemblerWidget : public QDockWidget { diff --git a/src/citra_qt/debugger/graphics.cpp b/src/citra_qt/debugger/graphics/graphics.cpp index ef6712bfa..6a76adeae 100644 --- a/src/citra_qt/debugger/graphics.cpp +++ b/src/citra_qt/debugger/graphics/graphics.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <QListView> -#include "citra_qt/debugger/graphics.h" +#include "citra_qt/debugger/graphics/graphics.h" #include "citra_qt/util/util.h" extern GraphicsDebugger g_debugger; @@ -22,15 +22,15 @@ QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) con return QVariant(); int command_index = index.row(); - const GSP_GPU::Command& command = GetDebugger()->ReadGXCommandHistory(command_index); + const Service::GSP::Command& command = GetDebugger()->ReadGXCommandHistory(command_index); if (role == Qt::DisplayRole) { - std::map<GSP_GPU::CommandId, const char*> command_names = { - {GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA"}, - {GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST"}, - {GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL"}, - {GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER"}, - {GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY"}, - {GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH"}, + std::map<Service::GSP::CommandId, const char*> command_names = { + {Service::GSP::CommandId::REQUEST_DMA, "REQUEST_DMA"}, + {Service::GSP::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST"}, + {Service::GSP::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL"}, + {Service::GSP::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER"}, + {Service::GSP::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY"}, + {Service::GSP::CommandId::CACHE_FLUSH, "CACHE_FLUSH"}, }; const u32* command_data = reinterpret_cast<const u32*>(&command); QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9") diff --git a/src/citra_qt/debugger/graphics.h b/src/citra_qt/debugger/graphics/graphics.h index bedf3e596..8837fb792 100644 --- a/src/citra_qt/debugger/graphics.h +++ b/src/citra_qt/debugger/graphics/graphics.h @@ -13,7 +13,7 @@ class GPUCommandStreamItemModel : public QAbstractListModel, Q_OBJECT public: - GPUCommandStreamItemModel(QObject* parent); + explicit GPUCommandStreamItemModel(QObject* parent); int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; diff --git a/src/citra_qt/debugger/graphics_breakpoint_observer.cpp b/src/citra_qt/debugger/graphics/graphics_breakpoint_observer.cpp index e01d3440e..dc6070dea 100644 --- a/src/citra_qt/debugger/graphics_breakpoint_observer.cpp +++ b/src/citra_qt/debugger/graphics/graphics_breakpoint_observer.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <QMetaType> -#include "citra_qt/debugger/graphics_breakpoint_observer.h" +#include "citra_qt/debugger/graphics/graphics_breakpoint_observer.h" BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title, QWidget* parent) diff --git a/src/citra_qt/debugger/graphics_breakpoint_observer.h b/src/citra_qt/debugger/graphics/graphics_breakpoint_observer.h index e77df4f5b..e77df4f5b 100644 --- a/src/citra_qt/debugger/graphics_breakpoint_observer.h +++ b/src/citra_qt/debugger/graphics/graphics_breakpoint_observer.h diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics/graphics_breakpoints.cpp index d2a036dfa..030828ba8 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.cpp +++ b/src/citra_qt/debugger/graphics/graphics_breakpoints.cpp @@ -7,8 +7,8 @@ #include <QPushButton> #include <QTreeView> #include <QVBoxLayout> -#include "citra_qt/debugger/graphics_breakpoints.h" -#include "citra_qt/debugger/graphics_breakpoints_p.h" +#include "citra_qt/debugger/graphics/graphics_breakpoints.h" +#include "citra_qt/debugger/graphics/graphics_breakpoints_p.h" #include "common/assert.h" BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) diff --git a/src/citra_qt/debugger/graphics_breakpoints.h b/src/citra_qt/debugger/graphics/graphics_breakpoints.h index 5fc40c916..bec72a2db 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.h +++ b/src/citra_qt/debugger/graphics/graphics_breakpoints.h @@ -20,8 +20,8 @@ class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakP using Event = Pica::DebugContext::Event; public: - GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, - QWidget* parent = nullptr); + explicit GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, + QWidget* parent = nullptr); void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; void OnPicaResume() override; diff --git a/src/citra_qt/debugger/graphics_breakpoints_p.h b/src/citra_qt/debugger/graphics/graphics_breakpoints_p.h index dc64706bd..dc64706bd 100644 --- a/src/citra_qt/debugger/graphics_breakpoints_p.h +++ b/src/citra_qt/debugger/graphics/graphics_breakpoints_p.h diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp index 8a784d108..dab529e3a 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp @@ -13,7 +13,7 @@ #include <QSpinBox> #include <QTreeView> #include <QVBoxLayout> -#include "citra_qt/debugger/graphics_cmdlists.h" +#include "citra_qt/debugger/graphics/graphics_cmdlists.h" #include "citra_qt/util/spinbox.h" #include "citra_qt/util/util.h" #include "common/vector_math.h" @@ -21,7 +21,8 @@ #include "video_core/pica.h" #include "video_core/pica_state.h" -QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { +namespace { +QImage LoadTexture(const u8* src, const Pica::DebugUtils::TextureInfo& info) { QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); for (int y = 0; y < info.height; ++y) { for (int x = 0; x < info.width; ++x) { @@ -35,7 +36,8 @@ QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { class TextureInfoWidget : public QWidget { public: - TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) + TextureInfoWidget(const u8* src, const Pica::DebugUtils::TextureInfo& info, + QWidget* parent = nullptr) : QWidget(parent) { QLabel* image_widget = new QLabel; QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info)); @@ -47,6 +49,7 @@ public: setLayout(layout); } }; +} // Anonymous namespace GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {} @@ -65,7 +68,6 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const { const auto& write = pica_trace.writes[index.row()]; if (role == Qt::DisplayRole) { - QString content; switch (index.column()) { case 0: return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str()); @@ -122,19 +124,21 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) || COMMAND_IN_RANGE(command_id, texture2)) { - unsigned index; + unsigned texture_index; if (COMMAND_IN_RANGE(command_id, texture0)) { - index = 0; + texture_index = 0; } else if (COMMAND_IN_RANGE(command_id, texture1)) { - index = 1; + texture_index = 1; } else if (COMMAND_IN_RANGE(command_id, texture2)) { - index = 2; + texture_index = 2; } else { UNREACHABLE_MSG("Unknown texture command"); } - auto config = Pica::g_state.regs.GetTextures()[index].config; - auto format = Pica::g_state.regs.GetTextures()[index].format; - auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); + + const auto texture = Pica::g_state.regs.GetTextures()[texture_index]; + const auto config = texture.config; + const auto format = texture.format; + const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); // TODO: Open a surface debugger } @@ -148,19 +152,21 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) || COMMAND_IN_RANGE(command_id, texture2)) { - unsigned index; + unsigned texture_index; if (COMMAND_IN_RANGE(command_id, texture0)) { - index = 0; + texture_index = 0; } else if (COMMAND_IN_RANGE(command_id, texture1)) { - index = 1; + texture_index = 1; } else { - index = 2; + texture_index = 2; } - auto config = Pica::g_state.regs.GetTextures()[index].config; - auto format = Pica::g_state.regs.GetTextures()[index].format; - auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); - u8* src = Memory::GetPhysicalPointer(config.GetPhysicalAddress()); + const auto texture = Pica::g_state.regs.GetTextures()[texture_index]; + const auto config = texture.config; + const auto format = texture.format; + + const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); + const u8* src = Memory::GetPhysicalPointer(config.GetPhysicalAddress()); new_info_widget = new TextureInfoWidget(src, info); } if (command_info_widget) { diff --git a/src/citra_qt/debugger/graphics_cmdlists.h b/src/citra_qt/debugger/graphics/graphics_cmdlists.h index fa2b9122b..8f40b94c5 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.h +++ b/src/citra_qt/debugger/graphics/graphics_cmdlists.h @@ -20,7 +20,7 @@ public: CommandIdRole = Qt::UserRole, }; - GPUCommandListModel(QObject* parent); + explicit GPUCommandListModel(QObject* parent); int columnCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -39,7 +39,7 @@ class GPUCommandListWidget : public QDockWidget { Q_OBJECT public: - GPUCommandListWidget(QWidget* parent = nullptr); + explicit GPUCommandListWidget(QWidget* parent = nullptr); public slots: void OnToggleTracing(); diff --git a/src/citra_qt/debugger/graphics_surface.cpp b/src/citra_qt/debugger/graphics/graphics_surface.cpp index 906daaa50..4efd95d3c 100644 --- a/src/citra_qt/debugger/graphics_surface.cpp +++ b/src/citra_qt/debugger/graphics/graphics_surface.cpp @@ -11,7 +11,7 @@ #include <QPushButton> #include <QScrollArea> #include <QSpinBox> -#include "citra_qt/debugger/graphics_surface.h" +#include "citra_qt/debugger/graphics/graphics_surface.h" #include "citra_qt/util/spinbox.h" #include "common/color.h" #include "core/hw/gpu.h" diff --git a/src/citra_qt/debugger/graphics_surface.h b/src/citra_qt/debugger/graphics/graphics_surface.h index 21e6b5b8b..28f5650a7 100644 --- a/src/citra_qt/debugger/graphics_surface.h +++ b/src/citra_qt/debugger/graphics/graphics_surface.h @@ -6,7 +6,7 @@ #include <QLabel> #include <QPushButton> -#include "citra_qt/debugger/graphics_breakpoint_observer.h" +#include "citra_qt/debugger/graphics/graphics_breakpoint_observer.h" class QComboBox; class QSpinBox; @@ -18,7 +18,8 @@ class SurfacePicture : public QLabel { Q_OBJECT public: - SurfacePicture(QWidget* parent = 0, GraphicsSurfaceWidget* surface_widget = nullptr); + explicit SurfacePicture(QWidget* parent = nullptr, + GraphicsSurfaceWidget* surface_widget = nullptr); ~SurfacePicture(); protected slots: @@ -71,8 +72,8 @@ class GraphicsSurfaceWidget : public BreakPointObserverDock { static unsigned int NibblesPerPixel(Format format); public: - GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, - QWidget* parent = nullptr); + explicit GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, + QWidget* parent = nullptr); void Pick(int x, int y); public slots: diff --git a/src/citra_qt/debugger/graphics_tracing.cpp b/src/citra_qt/debugger/graphics/graphics_tracing.cpp index 5c6b74034..716ed50b8 100644 --- a/src/citra_qt/debugger/graphics_tracing.cpp +++ b/src/citra_qt/debugger/graphics/graphics_tracing.cpp @@ -12,7 +12,7 @@ #include <QMessageBox> #include <QPushButton> #include <boost/range/algorithm/copy.hpp> -#include "citra_qt/debugger/graphics_tracing.h" +#include "citra_qt/debugger/graphics/graphics_tracing.h" #include "common/common_types.h" #include "core/hw/gpu.h" #include "core/hw/lcd.h" diff --git a/src/citra_qt/debugger/graphics_tracing.h b/src/citra_qt/debugger/graphics/graphics_tracing.h index e04a3dac3..3f73bcd2e 100644 --- a/src/citra_qt/debugger/graphics_tracing.h +++ b/src/citra_qt/debugger/graphics/graphics_tracing.h @@ -4,7 +4,7 @@ #pragma once -#include "citra_qt/debugger/graphics_breakpoint_observer.h" +#include "citra_qt/debugger/graphics/graphics_breakpoint_observer.h" class EmuThread; @@ -12,8 +12,8 @@ class GraphicsTracingWidget : public BreakPointObserverDock { Q_OBJECT public: - GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, - QWidget* parent = nullptr); + explicit GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, + QWidget* parent = nullptr); private slots: void StartRecording(); diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp index 96b40db1e..b75b94ef8 100644 --- a/src/citra_qt/debugger/graphics_vertex_shader.cpp +++ b/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp @@ -14,7 +14,7 @@ #include <QSignalMapper> #include <QSpinBox> #include <QTreeView> -#include "citra_qt/debugger/graphics_vertex_shader.h" +#include "citra_qt/debugger/graphics/graphics_vertex_shader.h" #include "citra_qt/util/util.h" #include "video_core/pica.h" #include "video_core/pica_state.h" diff --git a/src/citra_qt/debugger/graphics_vertex_shader.h b/src/citra_qt/debugger/graphics/graphics_vertex_shader.h index ec42f24bb..bedea0bed 100644 --- a/src/citra_qt/debugger/graphics_vertex_shader.h +++ b/src/citra_qt/debugger/graphics/graphics_vertex_shader.h @@ -6,7 +6,7 @@ #include <QAbstractTableModel> #include <QTreeView> -#include "citra_qt/debugger/graphics_breakpoint_observer.h" +#include "citra_qt/debugger/graphics/graphics_breakpoint_observer.h" #include "nihstro/parser_shbin.h" #include "video_core/shader/shader.h" @@ -19,7 +19,7 @@ class GraphicsVertexShaderModel : public QAbstractTableModel { Q_OBJECT public: - GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent); + explicit GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent); int columnCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; diff --git a/src/citra_qt/debugger/profiler.h b/src/citra_qt/debugger/profiler.h index d8c6487aa..c8912fd5a 100644 --- a/src/citra_qt/debugger/profiler.h +++ b/src/citra_qt/debugger/profiler.h @@ -15,7 +15,7 @@ class ProfilerModel : public QAbstractItemModel { Q_OBJECT public: - ProfilerModel(QObject* parent); + explicit ProfilerModel(QObject* parent); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; @@ -37,7 +37,7 @@ class ProfilerWidget : public QDockWidget { Q_OBJECT public: - ProfilerWidget(QWidget* parent = nullptr); + explicit ProfilerWidget(QWidget* parent = nullptr); private slots: void setProfilingInfoUpdateEnabled(bool enable); @@ -53,7 +53,7 @@ class MicroProfileDialog : public QWidget { Q_OBJECT public: - MicroProfileDialog(QWidget* parent = nullptr); + explicit MicroProfileDialog(QWidget* parent = nullptr); /// Returns a QAction that can be used to toggle visibility of this dialog. QAction* toggleViewAction(); diff --git a/src/citra_qt/debugger/ramview.h b/src/citra_qt/debugger/ramview.h index 8043c59e8..d01cea93b 100644 --- a/src/citra_qt/debugger/ramview.h +++ b/src/citra_qt/debugger/ramview.h @@ -2,13 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include "qhexedit.h" class GRamView : public QHexEdit { Q_OBJECT public: - GRamView(QWidget* parent = nullptr); + explicit GRamView(QWidget* parent = nullptr); public slots: void OnCPUStepped(); diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp index 4c529d3c3..b982bc58b 100644 --- a/src/citra_qt/debugger/registers.cpp +++ b/src/citra_qt/debugger/registers.cpp @@ -58,16 +58,16 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { } void RegistersWidget::OnDebugModeEntered() { - if (!Core::g_app_core) + if (!Core::System::GetInstance().IsPoweredOn()) return; for (int i = 0; i < core_registers->childCount(); ++i) core_registers->child(i)->setText( - 1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0'))); + 1, QString("0x%1").arg(Core::CPU().GetReg(i), 8, 16, QLatin1Char('0'))); for (int i = 0; i < vfp_registers->childCount(); ++i) vfp_registers->child(i)->setText( - 1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0'))); + 1, QString("0x%1").arg(Core::CPU().GetVFPReg(i), 8, 16, QLatin1Char('0'))); UpdateCPSRValues(); UpdateVFPSystemRegisterValues(); @@ -127,7 +127,7 @@ void RegistersWidget::CreateCPSRChildren() { } void RegistersWidget::UpdateCPSRValues() { - const u32 cpsr_val = Core::g_app_core->GetCPSR(); + const u32 cpsr_val = Core::CPU().GetCPSR(); cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0'))); cpsr->child(0)->setText( @@ -191,10 +191,10 @@ void RegistersWidget::CreateVFPSystemRegisterChildren() { } void RegistersWidget::UpdateVFPSystemRegisterValues() { - const u32 fpscr_val = Core::g_app_core->GetVFPSystemReg(VFP_FPSCR); - const u32 fpexc_val = Core::g_app_core->GetVFPSystemReg(VFP_FPEXC); - const u32 fpinst_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST); - const u32 fpinst2_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST2); + const u32 fpscr_val = Core::CPU().GetVFPSystemReg(VFP_FPSCR); + const u32 fpexc_val = Core::CPU().GetVFPSystemReg(VFP_FPEXC); + const u32 fpinst_val = Core::CPU().GetVFPSystemReg(VFP_FPINST); + const u32 fpinst2_val = Core::CPU().GetVFPSystemReg(VFP_FPINST2); QTreeWidgetItem* const fpscr = vfp_system_registers->child(0); fpscr->setText(1, QString("0x%1").arg(fpscr_val, 8, 16, QLatin1Char('0'))); diff --git a/src/citra_qt/debugger/registers.h b/src/citra_qt/debugger/registers.h index 54c9a8155..55bda5b59 100644 --- a/src/citra_qt/debugger/registers.h +++ b/src/citra_qt/debugger/registers.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <QDockWidget> #include "ui_registers.h" @@ -13,7 +15,7 @@ class RegistersWidget : public QDockWidget { Q_OBJECT public: - RegistersWidget(QWidget* parent = nullptr); + explicit RegistersWidget(QWidget* parent = nullptr); public slots: void OnDebugModeEntered(); diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp index be5a51e52..1d2de5185 100644 --- a/src/citra_qt/debugger/wait_tree.cpp +++ b/src/citra_qt/debugger/wait_tree.cpp @@ -8,7 +8,6 @@ #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/semaphore.h" -#include "core/hle/kernel/session.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" @@ -230,7 +229,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes)); } if (thread.status == THREADSTATUS_WAIT_SYNCH) { - list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, thread.wait_all)); + list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, + thread.IsSleepingOnWaitAll())); } return list; @@ -391,7 +391,7 @@ WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), p } void WaitTreeWidget::OnDebugModeEntered() { - if (!Core::g_app_core) + if (!Core::System::GetInstance().IsPoweredOn()) return; model->InitItems(); view->setModel(model); diff --git a/src/citra_qt/debugger/wait_tree.h b/src/citra_qt/debugger/wait_tree.h index 5d1d964d1..ee9708fc1 100644 --- a/src/citra_qt/debugger/wait_tree.h +++ b/src/citra_qt/debugger/wait_tree.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <boost/container/flat_set.hpp> #include <QAbstractItemModel> @@ -49,7 +51,7 @@ private: class WaitTreeText : public WaitTreeItem { Q_OBJECT public: - WaitTreeText(const QString& text); + explicit WaitTreeText(const QString& text); QString GetText() const override; private: @@ -65,7 +67,7 @@ public: class WaitTreeWaitObject : public WaitTreeExpandableItem { Q_OBJECT public: - WaitTreeWaitObject(const Kernel::WaitObject& object); + explicit WaitTreeWaitObject(const Kernel::WaitObject& object); static std::unique_ptr<WaitTreeWaitObject> make(const Kernel::WaitObject& object); QString GetText() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; @@ -92,7 +94,7 @@ private: class WaitTreeThread : public WaitTreeWaitObject { Q_OBJECT public: - WaitTreeThread(const Kernel::Thread& thread); + explicit WaitTreeThread(const Kernel::Thread& thread); QString GetText() const override; QColor GetColor() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; @@ -101,35 +103,37 @@ public: class WaitTreeEvent : public WaitTreeWaitObject { Q_OBJECT public: - WaitTreeEvent(const Kernel::Event& object); + explicit WaitTreeEvent(const Kernel::Event& object); std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; }; class WaitTreeMutex : public WaitTreeWaitObject { Q_OBJECT public: - WaitTreeMutex(const Kernel::Mutex& object); + explicit WaitTreeMutex(const Kernel::Mutex& object); std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; }; class WaitTreeSemaphore : public WaitTreeWaitObject { Q_OBJECT public: - WaitTreeSemaphore(const Kernel::Semaphore& object); + explicit WaitTreeSemaphore(const Kernel::Semaphore& object); std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; }; class WaitTreeTimer : public WaitTreeWaitObject { Q_OBJECT public: - WaitTreeTimer(const Kernel::Timer& object); + explicit WaitTreeTimer(const Kernel::Timer& object); std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; }; class WaitTreeMutexList : public WaitTreeExpandableItem { Q_OBJECT public: - WaitTreeMutexList(const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list); + explicit WaitTreeMutexList( + const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list); + QString GetText() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; @@ -140,7 +144,7 @@ private: class WaitTreeThreadList : public WaitTreeExpandableItem { Q_OBJECT public: - WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list); + explicit WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list); QString GetText() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; @@ -152,7 +156,7 @@ class WaitTreeModel : public QAbstractItemModel { Q_OBJECT public: - WaitTreeModel(QObject* parent = nullptr); + explicit WaitTreeModel(QObject* parent = nullptr); QVariant data(const QModelIndex& index, int role) const override; QModelIndex index(int row, int column, const QModelIndex& parent) const override; @@ -171,7 +175,7 @@ class WaitTreeWidget : public QDockWidget { Q_OBJECT public: - WaitTreeWidget(QWidget* parent = nullptr); + explicit WaitTreeWidget(QWidget* parent = nullptr); public slots: void OnDebugModeEntered(); diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index 07bc35308..09469f3c5 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <QHeaderView> +#include <QMenu> #include <QThreadPool> #include <QVBoxLayout> #include "common/common_paths.h" @@ -13,7 +14,7 @@ #include "game_list_p.h" #include "ui_settings.h" -GameList::GameList(QWidget* parent) { +GameList::GameList(QWidget* parent) : QWidget{parent} { QVBoxLayout* layout = new QVBoxLayout; tree_view = new QTreeView; @@ -28,18 +29,18 @@ GameList::GameList(QWidget* parent) { tree_view->setSortingEnabled(true); tree_view->setEditTriggers(QHeaderView::NoEditTriggers); tree_view->setUniformRowHeights(true); + tree_view->setContextMenuPolicy(Qt::CustomContextMenu); item_model->insertColumns(0, COLUMN_COUNT); item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); - connect(tree_view, SIGNAL(activated(const QModelIndex&)), this, - SLOT(ValidateEntry(const QModelIndex&))); + connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); + connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); // We must register all custom types with the Qt Automoc system so that we are able to use it - // with - // signals/slots. In this case, QList falls under the umbrells of custom types. + // with signals/slots. In this case, QList falls under the umbrells of custom types. qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); layout->addWidget(tree_view); @@ -50,7 +51,7 @@ GameList::~GameList() { emit ShouldCancelWorker(); } -void GameList::AddEntry(QList<QStandardItem*> entry_items) { +void GameList::AddEntry(const QList<QStandardItem*>& entry_items) { item_model->invisibleRootItem()->appendRow(entry_items); } @@ -72,6 +73,23 @@ void GameList::DonePopulating() { tree_view->setEnabled(true); } +void GameList::PopupContextMenu(const QPoint& menu_location) { + QModelIndex item = tree_view->indexAt(menu_location); + if (!item.isValid()) + return; + + int row = item_model->itemFromIndex(item)->row(); + QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); + u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong(); + + QMenu context_menu; + QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); + open_save_location->setEnabled(program_id != 0); + connect(open_save_location, &QAction::triggered, + [&]() { emit OpenSaveFolderRequested(program_id); }); + context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); +} + void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) { @@ -86,12 +104,13 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { emit ShouldCancelWorker(); GameListWorker* worker = new GameListWorker(dir_path, deep_scan); - connect(worker, SIGNAL(EntryReady(QList<QStandardItem*>)), this, - SLOT(AddEntry(QList<QStandardItem*>)), Qt::QueuedConnection); - connect(worker, SIGNAL(Finished()), this, SLOT(DonePopulating()), Qt::QueuedConnection); + connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); + connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, + Qt::QueuedConnection); // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel // without delay. - connect(this, SIGNAL(ShouldCancelWorker()), worker, SLOT(Cancel()), Qt::DirectConnection); + connect(this, &GameList::ShouldCancelWorker, worker, &GameListWorker::Cancel, + Qt::DirectConnection); QThreadPool::globalInstance()->start(worker); current_worker = std::move(worker); @@ -128,8 +147,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign std::vector<u8> smdh; loader->ReadIcon(smdh); + u64 program_id = 0; + loader->ReadProgramId(program_id); + emit EntryReady({ - new GameListItemPath(QString::fromStdString(physical_name), smdh), + new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id), new GameListItem( QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItemSize(FileUtil::GetSize(physical_name)), @@ -151,6 +173,6 @@ void GameListWorker::run() { } void GameListWorker::Cancel() { - disconnect(this, 0, 0, 0); + disconnect(this, nullptr, nullptr, nullptr); stop_processing = true; } diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h index a22e9bc60..1abf10051 100644 --- a/src/citra_qt/game_list.h +++ b/src/citra_qt/game_list.h @@ -25,7 +25,7 @@ public: COLUMN_COUNT, // Number of columns }; - GameList(QWidget* parent = nullptr); + explicit GameList(QWidget* parent = nullptr); ~GameList() override; void PopulateAsync(const QString& dir_path, bool deep_scan); @@ -33,18 +33,18 @@ public: void SaveInterfaceLayout(); void LoadInterfaceLayout(); -public slots: - void AddEntry(QList<QStandardItem*> entry_items); - -private slots: - void ValidateEntry(const QModelIndex& item); - void DonePopulating(); - signals: void GameChosen(QString game_path); void ShouldCancelWorker(); + void OpenSaveFolderRequested(u64 program_id); private: + void AddEntry(const QList<QStandardItem*>& entry_items); + void ValidateEntry(const QModelIndex& item); + void DonePopulating(); + + void PopupContextMenu(const QPoint& menu_location); + QTreeView* tree_view = nullptr; QStandardItemModel* item_model = nullptr; GameListWorker* current_worker = nullptr; diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h index 5ca3fe991..a15f06c5f 100644 --- a/src/citra_qt/game_list_p.h +++ b/src/citra_qt/game_list_p.h @@ -71,10 +71,13 @@ class GameListItemPath : public GameListItem { public: static const int FullPathRole = Qt::UserRole + 1; static const int TitleRole = Qt::UserRole + 2; + static const int ProgramIdRole = Qt::UserRole + 3; GameListItemPath() : GameListItem() {} - GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data) : GameListItem() { + GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) + : GameListItem() { setData(game_path, FullPathRole); + setData(qulonglong(program_id), ProgramIdRole); if (!Loader::IsValidSMDH(smdh_data)) { // SMDH is not valid, set a default icon diff --git a/src/citra_qt/hotkeys.h b/src/citra_qt/hotkeys.h index 350103c6f..46f48c2d8 100644 --- a/src/citra_qt/hotkeys.h +++ b/src/citra_qt/hotkeys.h @@ -55,7 +55,7 @@ class GHotkeysDialog : public QWidget { Q_OBJECT public: - GHotkeysDialog(QWidget* parent = nullptr); + explicit GHotkeysDialog(QWidget* parent = nullptr); private: Ui::hotkeys ui; diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index a3887f9ab..6d59cf640 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> #include <clocale> #include <memory> #include <thread> @@ -16,12 +17,12 @@ #include "citra_qt/configure_dialog.h" #include "citra_qt/debugger/callstack.h" #include "citra_qt/debugger/disassembler.h" -#include "citra_qt/debugger/graphics.h" -#include "citra_qt/debugger/graphics_breakpoints.h" -#include "citra_qt/debugger/graphics_cmdlists.h" -#include "citra_qt/debugger/graphics_surface.h" -#include "citra_qt/debugger/graphics_tracing.h" -#include "citra_qt/debugger/graphics_vertex_shader.h" +#include "citra_qt/debugger/graphics/graphics.h" +#include "citra_qt/debugger/graphics/graphics_breakpoints.h" +#include "citra_qt/debugger/graphics/graphics_cmdlists.h" +#include "citra_qt/debugger/graphics/graphics_surface.h" +#include "citra_qt/debugger/graphics/graphics_tracing.h" +#include "citra_qt/debugger/graphics/graphics_vertex_shader.h" #include "citra_qt/debugger/profiler.h" #include "citra_qt/debugger/ramview.h" #include "citra_qt/debugger/registers.h" @@ -41,10 +42,10 @@ #include "common/string_util.h" #include "core/arm/disassembler/load_symbol_map.h" #include "core/core.h" +#include "core/file_sys/archive_source_sd_savedata.h" #include "core/gdbstub/gdbstub.h" #include "core/loader/loader.h" #include "core/settings.h" -#include "core/system.h" #include "qhexedit.h" #include "video_core/video_core.h" @@ -58,6 +59,36 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { ui.setupUi(this); statusBar()->hide(); + InitializeWidgets(); + InitializeDebugMenuActions(); + InitializeRecentFileMenuActions(); + InitializeHotkeys(); + + SetDefaultUIGeometry(); + RestoreUIState(); + + ConnectWidgetEvents(); + + setWindowTitle(QString("Citra | %1-%2").arg(Common::g_scm_branch, Common::g_scm_desc)); + show(); + + game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + + QStringList args = QApplication::arguments(); + if (args.length() >= 2) { + BootGame(args[1].toStdString()); + } +} + +GMainWindow::~GMainWindow() { + // will get automatically deleted otherwise + if (render_window->parent() == nullptr) + delete render_window; + + Pica::g_debug_context.reset(); +} + +void GMainWindow::InitializeWidgets() { render_window = new GRenderWindow(this, emu_thread.get()); render_window->hide(); @@ -93,25 +124,27 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); graphicsCommandsWidget->hide(); - auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this); + graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this); addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget); graphicsBreakpointsWidget->hide(); - auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this); + graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this); addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); graphicsVertexShaderWidget->hide(); - auto graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this); + graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this); addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget); graphicsTracingWidget->hide(); - auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica Surface Viewer"), this); - connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, - SLOT(OnCreateGraphicsSurfaceViewer())); - waitTreeWidget = new WaitTreeWidget(this); addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); waitTreeWidget->hide(); +} + +void GMainWindow::InitializeDebugMenuActions() { + auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica Surface Viewer"), this); + connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, + SLOT(OnCreateGraphicsSurfaceViewer())); QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); debug_menu->addAction(graphicsSurfaceViewerAction); @@ -129,19 +162,47 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); debug_menu->addAction(graphicsTracingWidget->toggleViewAction()); debug_menu->addAction(waitTreeWidget->toggleViewAction()); +} - // Set default UI state +void GMainWindow::InitializeRecentFileMenuActions() { + for (int i = 0; i < max_recent_files_item; ++i) { + actions_recent_files[i] = new QAction(this); + actions_recent_files[i]->setVisible(false); + connect(actions_recent_files[i], SIGNAL(triggered()), this, SLOT(OnMenuRecentFile())); + + ui.menu_recent_files->addAction(actions_recent_files[i]); + } + + UpdateRecentFiles(); +} + +void GMainWindow::InitializeHotkeys() { + RegisterHotkey("Main Window", "Load File", QKeySequence::Open); + RegisterHotkey("Main Window", "Swap Screens", QKeySequence::NextChild); + RegisterHotkey("Main Window", "Start Emulation"); + LoadHotkeys(); + + connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, + SLOT(OnMenuLoadFile())); + connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, + SLOT(OnStartGame())); + connect(GetHotkey("Main Window", "Swap Screens", render_window), SIGNAL(activated()), this, + SLOT(OnSwapScreens())); +} + +void GMainWindow::SetDefaultUIGeometry() { // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half - QDesktopWidget* desktop = ((QApplication*)QApplication::instance())->desktop(); - QRect screenRect = desktop->screenGeometry(this); - int x, y, w, h; - w = screenRect.width() * 2 / 3; - h = screenRect.height() / 2; - x = (screenRect.x() + screenRect.width()) / 2 - w / 2; - y = (screenRect.y() + screenRect.height()) / 2 - h * 55 / 100; + const QRect screenRect = QApplication::desktop()->screenGeometry(this); + + const int w = screenRect.width() * 2 / 3; + const int h = screenRect.height() / 2; + const int x = (screenRect.x() + screenRect.width()) / 2 - w / 2; + const int y = (screenRect.y() + screenRect.height()) / 2 - h * 55 / 100; + setGeometry(x, y, w, h); +} - // Restore UI state +void GMainWindow::RestoreUIState() { restoreGeometry(UISettings::values.geometry); restoreState(UISettings::values.state); render_window->restoreGeometry(UISettings::values.renderwindow_geometry); @@ -157,20 +218,13 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { ui.actionDisplay_widget_title_bars->setChecked(UISettings::values.display_titlebar); OnDisplayTitleBars(ui.actionDisplay_widget_title_bars->isChecked()); +} - // Prepare actions for recent files - for (int i = 0; i < max_recent_files_item; ++i) { - actions_recent_files[i] = new QAction(this); - actions_recent_files[i]->setVisible(false); - connect(actions_recent_files[i], SIGNAL(triggered()), this, SLOT(OnMenuRecentFile())); - - ui.menu_recent_files->addAction(actions_recent_files[i]); - } - UpdateRecentFiles(); - - // Setup connections +void GMainWindow::ConnectWidgetEvents() { connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), Qt::DirectConnection); + connect(game_list, SIGNAL(OpenSaveFolderRequested(u64)), this, + SLOT(OnGameListOpenSaveFolder(u64)), Qt::DirectConnection); connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure())); connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()), Qt::DirectConnection); @@ -197,40 +251,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { connect(this, SIGNAL(EmulationStarting(EmuThread*)), waitTreeWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStopping()), waitTreeWidget, SLOT(OnEmulationStopping())); - - // Setup hotkeys - RegisterHotkey("Main Window", "Load File", QKeySequence::Open); - RegisterHotkey("Main Window", "Swap Screens", QKeySequence::NextChild); - RegisterHotkey("Main Window", "Start Emulation"); - LoadHotkeys(); - - connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, - SLOT(OnMenuLoadFile())); - connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, - SLOT(OnStartGame())); - connect(GetHotkey("Main Window", "Swap Screens", this), SIGNAL(activated()), this, - SLOT(OnSwapScreens())); - - std::string window_title = - Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); - setWindowTitle(window_title.c_str()); - - show(); - - game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); - - QStringList args = QApplication::arguments(); - if (args.length() >= 2) { - BootGame(args[1].toStdString()); - } -} - -GMainWindow::~GMainWindow() { - // will get automatically deleted otherwise - if (render_window->parent() == nullptr) - delete render_window; - - Pica::g_debug_context.reset(); } void GMainWindow::OnDisplayTitleBars(bool show) { @@ -253,7 +273,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) { } } -bool GMainWindow::InitializeSystem(u32 system_mode) { +bool GMainWindow::LoadROM(const std::string& filename) { // Shutdown previous session if the emu thread is still active... if (emu_thread != nullptr) ShutdownGame(); @@ -269,54 +289,25 @@ bool GMainWindow::InitializeSystem(u32 system_mode) { return false; } - // Initialize the core emulation - System::Result system_result = System::Init(render_window, system_mode); - if (System::Result::Success != system_result) { - switch (system_result) { - case System::Result::ErrorInitVideoCore: - QMessageBox::critical(this, tr("Error while starting Citra!"), - tr("Failed to initialize the video core!\n\n" - "Please ensure that your GPU supports OpenGL 3.3 and that you " - "have the latest graphics driver.")); - break; - - default: - QMessageBox::critical(this, tr("Error while starting Citra!"), - tr("Unknown error (please check the log)!")); - break; - } - return false; - } - return true; -} + Core::System& system{Core::System::GetInstance()}; -bool GMainWindow::LoadROM(const std::string& filename) { - std::unique_ptr<Loader::AppLoader> app_loader = Loader::GetLoader(filename); - if (!app_loader) { - LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filename.c_str()); - QMessageBox::critical(this, tr("Error while loading ROM!"), - tr("The ROM format is not supported.")); - return false; - } + const Core::System::ResultStatus result{system.Load(render_window, filename)}; - boost::optional<u32> system_mode = app_loader->LoadKernelSystemMode(); - if (!system_mode) { - LOG_CRITICAL(Frontend, "Failed to load ROM!"); - QMessageBox::critical(this, tr("Error while loading ROM!"), - tr("Could not determine the system mode.")); - return false; - } - - if (!InitializeSystem(system_mode.get())) - return false; + if (result != Core::System::ResultStatus::Success) { + switch (result) { + case Core::System::ResultStatus::ErrorGetLoader: + LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filename.c_str()); + QMessageBox::critical(this, tr("Error while loading ROM!"), + tr("The ROM format is not supported.")); + break; - Loader::ResultStatus result = app_loader->Load(); - if (Loader::ResultStatus::Success != result) { - System::Shutdown(); - LOG_CRITICAL(Frontend, "Failed to load ROM!"); + case Core::System::ResultStatus::ErrorSystemMode: + LOG_CRITICAL(Frontend, "Failed to load ROM!"); + QMessageBox::critical(this, tr("Error while loading ROM!"), + tr("Could not determine the system mode.")); + break; - switch (result) { - case Loader::ResultStatus::ErrorEncrypted: { + case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: { // Build the MessageBox ourselves to have clickable link QMessageBox popup_error; popup_error.setTextFormat(Qt::RichText); @@ -331,11 +322,10 @@ bool GMainWindow::LoadROM(const std::string& filename) { popup_error.exec(); break; } - case Loader::ResultStatus::ErrorInvalidFormat: + case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: QMessageBox::critical(this, tr("Error while loading ROM!"), tr("The ROM format is not supported.")); break; - case Loader::ResultStatus::Error: default: QMessageBox::critical(this, tr("Error while loading ROM!"), tr("Unknown error!")); @@ -386,6 +376,7 @@ void GMainWindow::BootGame(const std::string& filename) { game_list->hide(); } render_window->show(); + render_window->setFocus(); emulation_running = true; OnStartGame(); @@ -460,6 +451,21 @@ void GMainWindow::OnGameListLoadFile(QString game_path) { BootGame(game_path.toStdString()); } +void GMainWindow::OnGameListOpenSaveFolder(u64 program_id) { + std::string sdmc_dir = FileUtil::GetUserPath(D_SDMC_IDX); + std::string path = FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(sdmc_dir, program_id); + QString qpath = QString::fromStdString(path); + + QDir dir(qpath); + if (!dir.exists()) { + QMessageBox::critical(this, tr("Error Opening Save Folder"), tr("Folder does not exist!")); + return; + } + + LOG_INFO(Frontend, "Opening save data path for program_id=%" PRIu64, program_id); + QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); +} + void GMainWindow::OnMenuLoadFile() { QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index f87178227..a2fd45c47 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -9,18 +9,21 @@ #include <QMainWindow> #include "ui_main.h" +class CallstackWidget; class Config; +class DisassemblerWidget; +class EmuThread; class GameList; class GImageInfo; +class GPUCommandStreamWidget; +class GPUCommandListWidget; +class GraphicsBreakPointsWidget; +class GraphicsTracingWidget; +class GraphicsVertexShaderWidget; class GRenderWindow; -class EmuThread; -class ProfilerWidget; class MicroProfileDialog; -class DisassemblerWidget; +class ProfilerWidget; class RegistersWidget; -class CallstackWidget; -class GPUCommandStreamWidget; -class GPUCommandListWidget; class WaitTreeWidget; class GMainWindow : public QMainWindow { @@ -60,6 +63,16 @@ signals: void EmulationStopping(); private: + void InitializeWidgets(); + void InitializeDebugMenuActions(); + void InitializeRecentFileMenuActions(); + void InitializeHotkeys(); + + void SetDefaultUIGeometry(); + void RestoreUIState(); + + void ConnectWidgetEvents(); + /** * Initializes the emulation system. * @param system_mode The system mode with which to intialize the kernel. @@ -105,6 +118,7 @@ private slots: void OnStopGame(); /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); + void OnGameListOpenSaveFolder(u64 program_id); void OnMenuLoadFile(); void OnMenuLoadSymbolMap(); /// Called whenever a user selects the "File->Select Game List Root" menu item @@ -135,6 +149,9 @@ private: CallstackWidget* callstackWidget; GPUCommandStreamWidget* graphicsWidget; GPUCommandListWidget* graphicsCommandsWidget; + GraphicsBreakPointsWidget* graphicsBreakpointsWidget; + GraphicsVertexShaderWidget* graphicsVertexShaderWidget; + GraphicsTracingWidget* graphicsTracingWidget; WaitTreeWidget* waitTreeWidget; QAction* actions_recent_files[max_recent_files_item]; diff --git a/src/citra_qt/util/spinbox.h b/src/citra_qt/util/spinbox.h index a57355cd6..2fa1db3a4 100644 --- a/src/citra_qt/util/spinbox.h +++ b/src/citra_qt/util/spinbox.h @@ -42,7 +42,7 @@ class CSpinBox : public QAbstractSpinBox { Q_OBJECT public: - CSpinBox(QWidget* parent = nullptr); + explicit CSpinBox(QWidget* parent = nullptr); void stepBy(int steps) override; StepEnabled stepEnabled() const override; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 74a271f08..5aecf6e6e 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -3,11 +3,9 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU set(SRCS break_points.cpp - emu_window.cpp file_util.cpp framebuffer_layout.cpp hash.cpp - key_map.cpp logging/filter.cpp logging/text_formatter.cpp logging/backend.cpp @@ -34,11 +32,9 @@ set(HEADERS common_funcs.h common_paths.h common_types.h - emu_window.h file_util.h framebuffer_layout.h hash.h - key_map.h linear_disk_cache.h logging/text_formatter.h logging/filter.h @@ -71,9 +67,15 @@ if(ARCHITECTURE_x86_64) set(HEADERS ${HEADERS} x64/abi.h x64/cpu_detect.h - x64/emitter.h) + x64/emitter.h + x64/xbyak_abi.h + x64/xbyak_util.h + ) endif() create_directory_groups(${SRCS} ${HEADERS}) add_library(common STATIC ${SRCS} ${HEADERS}) +if (ARCHITECTURE_x86_64) + target_link_libraries(common xbyak) +endif() diff --git a/src/common/bit_set.h b/src/common/bit_set.h index c48b3b769..3059d0cb0 100644 --- a/src/common/bit_set.h +++ b/src/common/bit_set.h @@ -16,7 +16,7 @@ namespace Common { // Helper functions: -#ifdef _WIN32 +#ifdef _MSC_VER template <typename T> static inline int CountSetBits(T v) { // from https://graphics.stanford.edu/~seander/bithacks.html diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 37304d236..b56105306 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -32,22 +32,10 @@ // Subdirs in the User dir returned by GetUserPath(D_USER_IDX) #define CONFIG_DIR "config" -#define GAMECONFIG_DIR "game_config" -#define MAPS_DIR "maps" #define CACHE_DIR "cache" #define SDMC_DIR "sdmc" #define NAND_DIR "nand" #define SYSDATA_DIR "sysdata" -#define SHADERCACHE_DIR "shader_cache" -#define STATESAVES_DIR "state_saves" -#define SCREENSHOTS_DIR "screenShots" -#define DUMP_DIR "dump" -#define DUMP_TEXTURES_DIR "textures" -#define DUMP_FRAMES_DIR "frames" -#define DUMP_AUDIO_DIR "audio" -#define LOGS_DIR "logs" -#define SHADERS_DIR "shaders" -#define SYSCONF_DIR "sysconf" // Filenames // Files in the directory returned by GetUserPath(D_CONFIG_IDX) @@ -57,9 +45,3 @@ // Sys files #define SHARED_FONT "shared_font.bin" - -// Files in the directory returned by GetUserPath(D_LOGS_IDX) -#define MAIN_LOG "emu.log" - -// Files in the directory returned by GetUserPath(D_SYSCONF_IDX) -#define SYSCONF "SYSCONF" diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index b6161f2dc..1a1f5d9b5 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -40,9 +40,20 @@ #endif #if defined(__APPLE__) +// CFURL contains __attribute__ directives that gcc does not know how to parse, so we need to just +// ignore them if we're not using clang. The macro is only used to prevent linking against +// functions that don't exist on older versions of macOS, and the worst case scenario is a linker +// error, so this is perfectly safe, just inconvenient. +#ifndef __clang__ +#define availability(...) +#endif #include <CoreFoundation/CFBundle.h> #include <CoreFoundation/CFString.h> #include <CoreFoundation/CFURL.h> +#ifdef availability +#undef availability +#endif + #endif #include <algorithm> @@ -701,24 +712,9 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; } #endif - - paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; - paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; - paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; - paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; - paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; - paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP; - paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP; - paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; - paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; - paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; - paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP; - paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; - paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; - paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; } if (!newPath.empty()) { @@ -732,48 +728,15 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new switch (DirIDX) { case D_ROOT_IDX: paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP; - paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR + DIR_SEP; - paths[F_SYSCONF_IDX] = paths[D_SYSCONF_IDX] + SYSCONF; break; case D_USER_IDX: paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP; paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; - paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; - paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; - paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; - paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; - paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; - paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP; - paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP; - paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; - paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; - paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; - paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP; - paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR DIR_SEP; - paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG; - paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; - paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; - paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; break; - - case D_CONFIG_IDX: - paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG; - paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; - paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; - break; - - case D_DUMP_IDX: - paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; - paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; - paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; - break; - - case D_LOGS_IDX: - paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; } } diff --git a/src/common/file_util.h b/src/common/file_util.h index ac58607c5..94adfcd7e 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -21,31 +21,11 @@ enum { D_USER_IDX, D_ROOT_IDX, D_CONFIG_IDX, - D_GAMECONFIG_IDX, - D_MAPS_IDX, D_CACHE_IDX, - D_SHADERCACHE_IDX, - D_SHADERS_IDX, - D_STATESAVES_IDX, - D_SCREENSHOTS_IDX, D_SDMC_IDX, D_NAND_IDX, D_SYSDATA_IDX, - D_HIRESTEXTURES_IDX, - D_DUMP_IDX, - D_DUMPFRAMES_IDX, - D_DUMPAUDIO_IDX, - D_DUMPTEXTURES_IDX, - D_DUMPDSP_IDX, D_LOGS_IDX, - D_SYSCONF_IDX, - F_EMUCONFIG_IDX, - F_DEBUGGERCONFIG_IDX, - F_LOGGERCONFIG_IDX, - F_MAINLOG_IDX, - F_RAMDUMP_IDX, - F_ARAMDUMP_IDX, - F_SYSCONF_IDX, NUM_PATH_INDICES }; diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 7fd397fe5..3ea102229 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -50,6 +50,7 @@ namespace Log { SUB(Service, CAM) \ SUB(Service, CECD) \ SUB(Service, CFG) \ + SUB(Service, CSND) \ SUB(Service, DSP) \ SUB(Service, DLP) \ SUB(Service, HID) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 96d0dfb8c..9d8c18d8e 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -67,6 +67,7 @@ enum class Class : ClassType { Service_CAM, ///< The CAM (Camera) service Service_CECD, ///< The CECD (StreetPass) service Service_CFG, ///< The CFG (Configuration) service + Service_CSND, ///< The CSND (CWAV format process) service Service_DSP, ///< The DSP (DSP control) service Service_DLP, ///< The DLP (Download Play) service Service_HID, ///< The HID (Human interface device) service diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index d61c1696b..9d423766f 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp @@ -7,7 +7,7 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN -#include <Windows.h> +#include <windows.h> #endif #include "common/assert.h" diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index 09366e801..79b404bb8 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "common/scm_rev.h" diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index df1008180..bad311793 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -14,7 +14,7 @@ #ifdef _WIN32 #include <codecvt> -#include <Windows.h> +#include <windows.h> #include "common/common_funcs.h" #else #include <iconv.h> diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 9bb2f4e1d..9e207118f 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -6,7 +6,7 @@ #ifdef __APPLE__ #include <mach/mach.h> #elif defined(_WIN32) -#include <Windows.h> +#include <windows.h> #else #if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) #include <pthread_np.h> diff --git a/src/common/timer.cpp b/src/common/timer.cpp index e843cbd9c..c9803109e 100644 --- a/src/common/timer.cpp +++ b/src/common/timer.cpp @@ -4,7 +4,8 @@ #include <time.h> #ifdef _WIN32 -#include <Windows.h> +#include <windows.h> +// windows.h needs to be included before other windows headers #include <mmsystem.h> #include <sys/timeb.h> #else diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h new file mode 100644 index 000000000..6090d93e1 --- /dev/null +++ b/src/common/x64/xbyak_abi.h @@ -0,0 +1,178 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <initializer_list> +#include <xbyak.h> +#include "common/assert.h" +#include "common/bit_set.h" + +namespace Common { +namespace X64 { + +int RegToIndex(const Xbyak::Reg& reg) { + using Kind = Xbyak::Reg::Kind; + ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0, + "RegSet only support GPRs and XMM registers."); + ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15."); + return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16); +} + +inline Xbyak::Reg64 IndexToReg64(int reg_index) { + ASSERT(reg_index < 16); + return Xbyak::Reg64(reg_index); +} + +inline Xbyak::Xmm IndexToXmm(int reg_index) { + ASSERT(reg_index >= 16 && reg_index < 32); + return Xbyak::Xmm(reg_index - 16); +} + +inline Xbyak::Reg IndexToReg(int reg_index) { + if (reg_index < 16) { + return IndexToReg64(reg_index); + } else { + return IndexToXmm(reg_index); + } +} + +inline BitSet32 BuildRegSet(std::initializer_list<Xbyak::Reg> regs) { + BitSet32 bits; + for (const Xbyak::Reg& reg : regs) { + bits[RegToIndex(reg)] = true; + } + return bits; +} + +const BitSet32 ABI_ALL_GPRS(0x0000FFFF); +const BitSet32 ABI_ALL_XMMS(0xFFFF0000); + +#ifdef _WIN32 + +// Microsoft x64 ABI +const Xbyak::Reg ABI_RETURN = Xbyak::util::rax; +const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx; +const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx; +const Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8; +const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9; + +const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({ + // GPRs + Xbyak::util::rcx, Xbyak::util::rdx, Xbyak::util::r8, Xbyak::util::r9, Xbyak::util::r10, + Xbyak::util::r11, + // XMMs + Xbyak::util::xmm0, Xbyak::util::xmm1, Xbyak::util::xmm2, Xbyak::util::xmm3, Xbyak::util::xmm4, + Xbyak::util::xmm5, +}); + +const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({ + // GPRs + Xbyak::util::rbx, Xbyak::util::rsi, Xbyak::util::rdi, Xbyak::util::rbp, Xbyak::util::r12, + Xbyak::util::r13, Xbyak::util::r14, Xbyak::util::r15, + // XMMs + Xbyak::util::xmm6, Xbyak::util::xmm7, Xbyak::util::xmm8, Xbyak::util::xmm9, Xbyak::util::xmm10, + Xbyak::util::xmm11, Xbyak::util::xmm12, Xbyak::util::xmm13, Xbyak::util::xmm14, + Xbyak::util::xmm15, +}); + +constexpr size_t ABI_SHADOW_SPACE = 0x20; + +#else + +// System V x86-64 ABI +const Xbyak::Reg ABI_RETURN = Xbyak::util::rax; +const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi; +const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi; +const Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx; +const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx; + +const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({ + // GPRs + Xbyak::util::rcx, Xbyak::util::rdx, Xbyak::util::rdi, Xbyak::util::rsi, Xbyak::util::r8, + Xbyak::util::r9, Xbyak::util::r10, Xbyak::util::r11, + // XMMs + Xbyak::util::xmm0, Xbyak::util::xmm1, Xbyak::util::xmm2, Xbyak::util::xmm3, Xbyak::util::xmm4, + Xbyak::util::xmm5, Xbyak::util::xmm6, Xbyak::util::xmm7, Xbyak::util::xmm8, Xbyak::util::xmm9, + Xbyak::util::xmm10, Xbyak::util::xmm11, Xbyak::util::xmm12, Xbyak::util::xmm13, + Xbyak::util::xmm14, Xbyak::util::xmm15, +}); + +const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({ + // GPRs + Xbyak::util::rbx, Xbyak::util::rbp, Xbyak::util::r12, Xbyak::util::r13, Xbyak::util::r14, + Xbyak::util::r15, +}); + +constexpr size_t ABI_SHADOW_SPACE = 0; + +#endif + +void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size, + s32* out_subtraction, s32* out_xmm_offset) { + int count = (regs & ABI_ALL_GPRS).Count(); + rsp_alignment -= count * 8; + size_t subtraction = 0; + int xmm_count = (regs & ABI_ALL_XMMS).Count(); + if (xmm_count) { + // If we have any XMMs to save, we must align the stack here. + subtraction = rsp_alignment & 0xF; + } + subtraction += 0x10 * xmm_count; + size_t xmm_base_subtraction = subtraction; + subtraction += needed_frame_size; + subtraction += ABI_SHADOW_SPACE; + // Final alignment. + rsp_alignment -= subtraction; + subtraction += rsp_alignment & 0xF; + + *out_subtraction = (s32)subtraction; + *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); +} + +size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, + size_t rsp_alignment, size_t needed_frame_size = 0) { + s32 subtraction, xmm_offset; + ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); + + for (int reg_index : (regs & ABI_ALL_GPRS)) { + code.push(IndexToReg64(reg_index)); + } + + if (subtraction != 0) { + code.sub(code.rsp, subtraction); + } + + for (int reg_index : (regs & ABI_ALL_XMMS)) { + code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(reg_index)); + xmm_offset += 0x10; + } + + return ABI_SHADOW_SPACE; +} + +void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, size_t rsp_alignment, + size_t needed_frame_size = 0) { + s32 subtraction, xmm_offset; + ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); + + for (int reg_index : (regs & ABI_ALL_XMMS)) { + code.movaps(IndexToXmm(reg_index), code.xword[code.rsp + xmm_offset]); + xmm_offset += 0x10; + } + + if (subtraction != 0) { + code.add(code.rsp, subtraction); + } + + // GPRs need to be popped in reverse order + for (int reg_index = 15; reg_index >= 0; reg_index--) { + if (regs[reg_index]) { + code.pop(IndexToReg64(reg_index)); + } + } +} + +} // namespace X64 +} // namespace Common diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h new file mode 100644 index 000000000..0f52f704b --- /dev/null +++ b/src/common/x64/xbyak_util.h @@ -0,0 +1,49 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <type_traits> +#include <xbyak.h> +#include "common/x64/xbyak_abi.h" + +namespace Common { +namespace X64 { + +// Constants for use with cmpps/cmpss +enum { + CMP_EQ = 0, + CMP_LT = 1, + CMP_LE = 2, + CMP_UNORD = 3, + CMP_NEQ = 4, + CMP_NLT = 5, + CMP_NLE = 6, + CMP_ORD = 7, +}; + +inline bool IsWithin2G(uintptr_t ref, uintptr_t target) { + u64 distance = target - (ref + 5); + return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL); +} + +inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) { + return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target); +} + +template <typename T> +inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { + static_assert(std::is_pointer<T>(), "Argument must be a (function) pointer."); + size_t addr = reinterpret_cast<size_t>(f); + if (IsWithin2G(code, addr)) { + code.call(f); + } else { + // ABI_RETURN is a safe temp register to use before a call + code.mov(ABI_RETURN, addr); + code.call(ABI_RETURN); + } +} + +} // namespace X64 +} // namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8ce141e6a..3621449b3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -29,15 +29,17 @@ set(SRCS file_sys/ivfc_archive.cpp file_sys/path_parser.cpp file_sys/savedata_archive.cpp + frontend/emu_window.cpp + frontend/key_map.cpp gdbstub/gdbstub.cpp hle/config_mem.cpp - hle/hle.cpp hle/applets/applet.cpp hle/applets/erreula.cpp hle/applets/mii_selector.cpp hle/applets/swkbd.cpp hle/kernel/address_arbiter.cpp hle/kernel/client_port.cpp + hle/kernel/client_session.cpp hle/kernel/event.cpp hle/kernel/kernel.cpp hle/kernel/memory.cpp @@ -46,14 +48,15 @@ set(SRCS hle/kernel/resource_limit.cpp hle/kernel/semaphore.cpp hle/kernel/server_port.cpp - hle/kernel/session.cpp + hle/kernel/server_session.cpp hle/kernel/shared_memory.cpp hle/kernel/thread.cpp hle/kernel/timer.cpp hle/kernel/vm_manager.cpp hle/service/ac_u.cpp - hle/service/act_a.cpp - hle/service/act_u.cpp + hle/service/act/act.cpp + hle/service/act/act_a.cpp + hle/service/act/act_u.cpp hle/service/am/am.cpp hle/service/am/am_app.cpp hle/service/am/am_net.cpp @@ -73,10 +76,12 @@ set(SRCS hle/service/cam/cam_s.cpp hle/service/cam/cam_u.cpp hle/service/cecd/cecd.cpp + hle/service/cecd/cecd_ndm.cpp hle/service/cecd/cecd_s.cpp hle/service/cecd/cecd_u.cpp hle/service/cfg/cfg.cpp hle/service/cfg/cfg_i.cpp + hle/service/cfg/cfg_nor.cpp hle/service/cfg/cfg_s.cpp hle/service/cfg/cfg_u.cpp hle/service/csnd_snd.cpp @@ -105,8 +110,13 @@ set(SRCS hle/service/ldr_ro/ldr_ro.cpp hle/service/ldr_ro/memory_synchronizer.cpp hle/service/mic_u.cpp + hle/service/mvd/mvd.cpp + hle/service/mvd/mvd_std.cpp hle/service/ndm/ndm.cpp hle/service/ndm/ndm_u.cpp + hle/service/nfc/nfc.cpp + hle/service/nfc/nfc_m.cpp + hle/service/nfc/nfc_u.cpp hle/service/news/news.cpp hle/service/news/news_s.cpp hle/service/news/news_u.cpp @@ -115,12 +125,25 @@ set(SRCS hle/service/nim/nim_s.cpp hle/service/nim/nim_u.cpp hle/service/ns_s.cpp - hle/service/nwm_uds.cpp + hle/service/nwm/nwm.cpp + hle/service/nwm/nwm_cec.cpp + hle/service/nwm/nwm_ext.cpp + hle/service/nwm/nwm_inf.cpp + hle/service/nwm/nwm_sap.cpp + hle/service/nwm/nwm_soc.cpp + hle/service/nwm/nwm_tst.cpp + hle/service/nwm/nwm_uds.cpp hle/service/pm_app.cpp hle/service/ptm/ptm.cpp + hle/service/ptm/ptm_gets.cpp hle/service/ptm/ptm_play.cpp + hle/service/ptm/ptm_sets.cpp hle/service/ptm/ptm_sysm.cpp hle/service/ptm/ptm_u.cpp + hle/service/qtm/qtm.cpp + hle/service/qtm/qtm_s.cpp + hle/service/qtm/qtm_sp.cpp + hle/service/qtm/qtm_u.cpp hle/service/service.cpp hle/service/soc_u.cpp hle/service/srv.cpp @@ -140,7 +163,6 @@ set(SRCS tracer/recorder.cpp memory.cpp settings.cpp - system.cpp ) set(HEADERS @@ -178,16 +200,19 @@ set(HEADERS file_sys/ivfc_archive.h file_sys/path_parser.h file_sys/savedata_archive.h + frontend/emu_window.h + frontend/key_map.h gdbstub/gdbstub.h hle/config_mem.h hle/function_wrappers.h - hle/hle.h + hle/ipc.h hle/applets/applet.h hle/applets/erreula.h hle/applets/mii_selector.h hle/applets/swkbd.h hle/kernel/address_arbiter.h hle/kernel/client_port.h + hle/kernel/client_session.h hle/kernel/event.h hle/kernel/kernel.h hle/kernel/memory.h @@ -196,15 +221,16 @@ set(HEADERS hle/kernel/resource_limit.h hle/kernel/semaphore.h hle/kernel/server_port.h - hle/kernel/session.h + hle/kernel/server_session.h hle/kernel/shared_memory.h hle/kernel/thread.h hle/kernel/timer.h hle/kernel/vm_manager.h hle/result.h hle/service/ac_u.h - hle/service/act_a.h - hle/service/act_u.h + hle/service/act/act.h + hle/service/act/act_a.h + hle/service/act/act_u.h hle/service/am/am.h hle/service/am/am_app.h hle/service/am/am_net.h @@ -224,10 +250,12 @@ set(HEADERS hle/service/cam/cam_s.h hle/service/cam/cam_u.h hle/service/cecd/cecd.h + hle/service/cecd/cecd_ndm.h hle/service/cecd/cecd_s.h hle/service/cecd/cecd_u.h hle/service/cfg/cfg.h hle/service/cfg/cfg_i.h + hle/service/cfg/cfg_nor.h hle/service/cfg/cfg_s.h hle/service/cfg/cfg_u.h hle/service/csnd_snd.h @@ -256,8 +284,13 @@ set(HEADERS hle/service/ldr_ro/ldr_ro.h hle/service/ldr_ro/memory_synchronizer.h hle/service/mic_u.h + hle/service/mvd/mvd.h + hle/service/mvd/mvd_std.h hle/service/ndm/ndm.h hle/service/ndm/ndm_u.h + hle/service/nfc/nfc.h + hle/service/nfc/nfc_m.h + hle/service/nfc/nfc_u.h hle/service/news/news.h hle/service/news/news_s.h hle/service/news/news_u.h @@ -266,12 +299,25 @@ set(HEADERS hle/service/nim/nim_s.h hle/service/nim/nim_u.h hle/service/ns_s.h - hle/service/nwm_uds.h + hle/service/nwm/nwm.h + hle/service/nwm/nwm_cec.h + hle/service/nwm/nwm_ext.h + hle/service/nwm/nwm_inf.h + hle/service/nwm/nwm_sap.h + hle/service/nwm/nwm_soc.h + hle/service/nwm/nwm_tst.h + hle/service/nwm/nwm_uds.h hle/service/pm_app.h hle/service/ptm/ptm.h + hle/service/ptm/ptm_gets.h hle/service/ptm/ptm_play.h + hle/service/ptm/ptm_sets.h hle/service/ptm/ptm_sysm.h hle/service/ptm/ptm_u.h + hle/service/qtm/qtm.h + hle/service/qtm/qtm_s.h + hle/service/qtm/qtm_sp.h + hle/service/qtm/qtm_u.h hle/service/service.h hle/service/soc_u.h hle/service/srv.h @@ -294,7 +340,6 @@ set(HEADERS memory_setup.h mmio.h settings.h - system.h ) include_directories(../../externals/dynarmic/include) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index e466b21b2..ccd43f431 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -8,15 +8,22 @@ #include "core/arm/skyeye_common/arm_regformat.h" #include "core/arm/skyeye_common/vfp/asm_vfp.h" -namespace Core { -struct ThreadContext; -} - /// Generic ARM11 CPU interface class ARM_Interface : NonCopyable { public: virtual ~ARM_Interface() {} + struct ThreadContext { + u32 cpu_registers[13]; + u32 sp; + u32 lr; + u32 pc; + u32 cpsr; + u32 fpu_registers[64]; + u32 fpscr; + u32 fpexc; + }; + /** * Runs the CPU for the given number of instructions * @param num_instructions Number of instructions to run @@ -124,13 +131,13 @@ public: * Saves the current CPU context * @param ctx Thread context to save */ - virtual void SaveContext(Core::ThreadContext& ctx) = 0; + virtual void SaveContext(ThreadContext& ctx) = 0; /** * Loads a CPU context * @param ctx Thread context to load */ - virtual void LoadContext(const Core::ThreadContext& ctx) = 0; + virtual void LoadContext(const ThreadContext& ctx) = 0; /// Prepare core for thread reschedule (if needed to correctly handle state) virtual void PrepareReschedule() = 0; diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index ca8a94ee9..9f25e3b00 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> #include <dynarmic/dynarmic.h> #include "common/assert.h" #include "common/microprofile.h" @@ -44,6 +45,7 @@ static Dynarmic::UserCallbacks GetUserCallbacks(ARMul_State* interpeter_state) { user_callbacks.user_arg = static_cast<void*>(interpeter_state); user_callbacks.CallSVC = &SVC::CallSVC; user_callbacks.IsReadOnlyMemory = &IsReadOnlyMemory; + user_callbacks.MemoryReadCode = &Memory::Read32; user_callbacks.MemoryRead8 = &Memory::Read8; user_callbacks.MemoryRead16 = &Memory::Read16; user_callbacks.MemoryRead32 = &Memory::Read32; @@ -136,7 +138,7 @@ void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { AddTicks(ticks_executed); } -void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) { +void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { memcpy(ctx.cpu_registers, jit->Regs().data(), sizeof(ctx.cpu_registers)); memcpy(ctx.fpu_registers, jit->ExtRegs().data(), sizeof(ctx.fpu_registers)); @@ -149,7 +151,7 @@ void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) { ctx.fpexc = interpreter_state->VFP[VFP_FPEXC]; } -void ARM_Dynarmic::LoadContext(const Core::ThreadContext& ctx) { +void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) { memcpy(jit->Regs().data(), ctx.cpu_registers, sizeof(ctx.cpu_registers)); memcpy(jit->ExtRegs().data(), ctx.fpu_registers, sizeof(ctx.fpu_registers)); diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index ced86d29b..87ab53d81 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -10,10 +10,6 @@ #include "core/arm/arm_interface.h" #include "core/arm/skyeye_common/armstate.h" -namespace Core { -struct ThreadContext; -} - class ARM_Dynarmic final : public ARM_Interface { public: ARM_Dynarmic(PrivilegeMode initial_mode); @@ -33,8 +29,8 @@ public: void AddTicks(u64 ticks) override; - void SaveContext(Core::ThreadContext& ctx) override; - void LoadContext(const Core::ThreadContext& ctx) override; + void SaveContext(ThreadContext& ctx) override; + void LoadContext(const ThreadContext& ctx) override; void PrepareReschedule() override; void ExecuteInstructions(int num_instructions) override; diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index 34c7f945e..81f9bf99e 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -89,7 +89,7 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) { AddTicks(ticks_executed); } -void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { +void ARM_DynCom::SaveContext(ThreadContext& ctx) { memcpy(ctx.cpu_registers, state->Reg.data(), sizeof(ctx.cpu_registers)); memcpy(ctx.fpu_registers, state->ExtReg.data(), sizeof(ctx.fpu_registers)); @@ -102,7 +102,7 @@ void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { ctx.fpexc = state->VFP[VFP_FPEXC]; } -void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) { +void ARM_DynCom::LoadContext(const ThreadContext& ctx) { memcpy(state->Reg.data(), ctx.cpu_registers, sizeof(ctx.cpu_registers)); memcpy(state->ExtReg.data(), ctx.fpu_registers, sizeof(ctx.fpu_registers)); diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 65db1f0f9..62c174f3c 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -10,10 +10,6 @@ #include "core/arm/skyeye_common/arm_regformat.h" #include "core/arm/skyeye_common/armstate.h" -namespace Core { -struct ThreadContext; -} - class ARM_DynCom final : public ARM_Interface { public: ARM_DynCom(PrivilegeMode initial_mode); @@ -36,8 +32,8 @@ public: void AddTicks(u64 ticks) override; - void SaveContext(Core::ThreadContext& ctx) override; - void LoadContext(const Core::ThreadContext& ctx) override; + void SaveContext(ThreadContext& ctx) override; + void LoadContext(const ThreadContext& ctx) override; void PrepareReschedule() override; void ExecuteInstructions(int num_instructions) override; diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 7b8616702..67c45640a 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -953,7 +953,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { #define GDB_BP_CHECK \ cpu->Cpsr &= ~(1 << 5); \ cpu->Cpsr |= cpu->TFlag << 5; \ - if (GDBStub::g_server_enabled) { \ + if (GDBStub::IsServerEnabled()) { \ if (GDBStub::IsMemoryBreak() || (breakpoint_data.type != GDBStub::BreakpointType::None && \ PC == breakpoint_data.address)) { \ GDBStub::Break(); \ @@ -1649,7 +1649,7 @@ DISPATCH : { } // Find breakpoint if one exists within the block - if (GDBStub::g_server_enabled && GDBStub::IsConnected()) { + if (GDBStub::IsConnected()) { breakpoint_data = GDBStub::GetNextBreakpointFromAddress(cpu->Reg[15], GDBStub::BreakpointType::Execute); } diff --git a/src/core/arm/dyncom/arm_dyncom_trans.h b/src/core/arm/dyncom/arm_dyncom_trans.h index b1ec90662..632ff2cd6 100644 --- a/src/core/arm/dyncom/arm_dyncom_trans.h +++ b/src/core/arm/dyncom/arm_dyncom_trans.h @@ -1,3 +1,5 @@ +#pragma once + #include <cstddef> #include "common/common_types.h" diff --git a/src/core/arm/skyeye_common/armstate.cpp b/src/core/arm/skyeye_common/armstate.cpp index 1465b074e..c36b0208f 100644 --- a/src/core/arm/skyeye_common/armstate.cpp +++ b/src/core/arm/skyeye_common/armstate.cpp @@ -182,7 +182,7 @@ void ARMul_State::ResetMPCoreCP15Registers() { } static void CheckMemoryBreakpoint(u32 address, GDBStub::BreakpointType type) { - if (GDBStub::g_server_enabled && GDBStub::CheckBreakpoint(address, type)) { + if (GDBStub::IsServerEnabled() && GDBStub::CheckBreakpoint(address, type)) { LOG_DEBUG(Debug, "Found memory breakpoint @ %08x", address); GDBStub::Break(true); } diff --git a/src/core/core.cpp b/src/core/core.cpp index 49ac8be6e..202cd332b 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include <memory> + +#include "audio_core/audio_core.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/arm/dynarmic/arm_dynarmic.h" @@ -10,19 +12,25 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/gdbstub/gdbstub.h" -#include "core/hle/hle.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/memory.h" #include "core/hle/kernel/thread.h" +#include "core/hle/service/service.h" #include "core/hw/hw.h" +#include "core/loader/loader.h" #include "core/settings.h" +#include "video_core/video_core.h" namespace Core { -std::unique_ptr<ARM_Interface> g_app_core; ///< ARM11 application core -std::unique_ptr<ARM_Interface> g_sys_core; ///< ARM11 system (OS) core +/*static*/ System System::s_instance; + +System::ResultStatus System::RunLoop(int tight_loop) { + if (!cpu_core) { + return ResultStatus::ErrorNotInitialized; + } -/// Run the core CPU loop -void RunLoop(int tight_loop) { - if (GDBStub::g_server_enabled) { + if (GDBStub::IsServerEnabled()) { GDBStub::HandlePacket(); // If the loop is halted and we want to step, use a tiny (1) number of instructions to @@ -32,7 +40,7 @@ void RunLoop(int tight_loop) { GDBStub::SetCpuStepFlag(false); tight_loop = 1; } else { - return; + return ResultStatus::Success; } } } @@ -43,48 +51,115 @@ void RunLoop(int tight_loop) { LOG_TRACE(Core_ARM11, "Idling"); CoreTiming::Idle(); CoreTiming::Advance(); - HLE::Reschedule(__func__); + PrepareReschedule(); } else { - g_app_core->Run(tight_loop); + cpu_core->Run(tight_loop); } HW::Update(); - if (HLE::IsReschedulePending()) { - Kernel::Reschedule(); - } + Reschedule(); + + return ResultStatus::Success; +} + +System::ResultStatus System::SingleStep() { + return RunLoop(1); } -/// Step the CPU one instruction -void SingleStep() { - RunLoop(1); +System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) { + if (app_loader) { + app_loader.reset(); + } + + app_loader = Loader::GetLoader(filepath); + + if (!app_loader) { + LOG_CRITICAL(Core, "Failed to obtain loader for %s!", filepath.c_str()); + return ResultStatus::ErrorGetLoader; + } + + boost::optional<u32> system_mode{app_loader->LoadKernelSystemMode()}; + if (!system_mode) { + LOG_CRITICAL(Core, "Failed to determine system mode!"); + return ResultStatus::ErrorSystemMode; + } + + ResultStatus init_result{Init(emu_window, system_mode.get())}; + if (init_result != ResultStatus::Success) { + LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result); + System::Shutdown(); + return init_result; + } + + const Loader::ResultStatus load_result{app_loader->Load()}; + if (Loader::ResultStatus::Success != load_result) { + LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result); + System::Shutdown(); + + switch (load_result) { + case Loader::ResultStatus::ErrorEncrypted: + return ResultStatus::ErrorLoader_ErrorEncrypted; + case Loader::ResultStatus::ErrorInvalidFormat: + return ResultStatus::ErrorLoader_ErrorInvalidFormat; + default: + return ResultStatus::ErrorLoader; + } + } + return ResultStatus::Success; } -/// Halt the core -void Halt(const char* msg) { - // TODO(ShizZy): ImplementMe +void System::PrepareReschedule() { + cpu_core->PrepareReschedule(); + reschedule_pending = true; } -/// Kill the core -void Stop() { - // TODO(ShizZy): ImplementMe +void System::Reschedule() { + if (!reschedule_pending) { + return; + } + + reschedule_pending = false; + Kernel::Reschedule(); } -/// Initialize the core -void Init() { +System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { + if (cpu_core) { + cpu_core.reset(); + } + + Memory::Init(); + if (Settings::values.use_cpu_jit) { - g_sys_core = std::make_unique<ARM_Dynarmic>(USER32MODE); - g_app_core = std::make_unique<ARM_Dynarmic>(USER32MODE); + cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE); } else { - g_sys_core = std::make_unique<ARM_DynCom>(USER32MODE); - g_app_core = std::make_unique<ARM_DynCom>(USER32MODE); + cpu_core = std::make_unique<ARM_DynCom>(USER32MODE); + } + + CoreTiming::Init(); + HW::Init(); + Kernel::Init(system_mode); + Service::Init(); + AudioCore::Init(); + GDBStub::Init(); + + if (!VideoCore::Init(emu_window)) { + return ResultStatus::ErrorVideoCore; } LOG_DEBUG(Core, "Initialized OK"); + + return ResultStatus::Success; } -void Shutdown() { - g_app_core.reset(); - g_sys_core.reset(); +void System::Shutdown() { + GDBStub::Shutdown(); + AudioCore::Shutdown(); + VideoCore::Shutdown(); + Service::Shutdown(); + Kernel::Shutdown(); + HW::Shutdown(); + CoreTiming::Shutdown(); + cpu_core.reset(); LOG_DEBUG(Core, "Shutdown OK"); } diff --git a/src/core/core.h b/src/core/core.h index ffbfa91c3..1015e8847 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -5,56 +5,118 @@ #pragma once #include <memory> +#include <string> + #include "common/common_types.h" +#include "core/memory.h" +class EmuWindow; class ARM_Interface; -//////////////////////////////////////////////////////////////////////////////////////////////////// +namespace Loader { +class AppLoader; +} namespace Core { -struct ThreadContext { - u32 cpu_registers[13]; - u32 sp; - u32 lr; - u32 pc; - u32 cpsr; - u32 fpu_registers[64]; - u32 fpscr; - u32 fpexc; +class System { +public: + /** + * Gets the instance of the System singleton class. + * @returns Reference to the instance of the System singleton class. + */ + static System& GetInstance() { + return s_instance; + } + + /// Enumeration representing the return values of the System Initialize and Load process. + enum class ResultStatus : u32 { + Success, ///< Succeeded + ErrorNotInitialized, ///< Error trying to use core prior to initialization + ErrorGetLoader, ///< Error finding the correct application loader + ErrorSystemMode, ///< Error determining the system mode + ErrorLoader, ///< Error loading the specified application + ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption + ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an + /// invalid format + ErrorVideoCore, ///< Error in the video core + }; + + /** + * Run the core CPU loop + * This function runs the core for the specified number of CPU instructions before trying to + * update hardware. This is much faster than SingleStep (and should be equivalent), as the CPU + * is not required to do a full dispatch with each instruction. NOTE: the number of instructions + * requested is not guaranteed to run, as this will be interrupted preemptively if a hardware + * update is requested (e.g. on a thread switch). + * @param tight_loop Number of instructions to execute. + * @return Result status, indicating whethor or not the operation succeeded. + */ + ResultStatus RunLoop(int tight_loop = 1000); + + /** + * Step the CPU one instruction + * @return Result status, indicating whethor or not the operation succeeded. + */ + ResultStatus SingleStep(); + + /// Shutdown the emulated system. + void Shutdown(); + + /** + * Load an executable application. + * @param emu_window Pointer to the host-system window used for video output and keyboard input. + * @param filepath String path to the executable application to load on the host file system. + * @returns ResultStatus code, indicating if the operation succeeded. + */ + ResultStatus Load(EmuWindow* emu_window, const std::string& filepath); + + /** + * Indicates if the emulated system is powered on (all subsystems initialized and able to run an + * application). + * @returns True if the emulated system is powered on, otherwise false. + */ + bool IsPoweredOn() const { + return cpu_core != nullptr; + } + + /// Prepare the core emulation for a reschedule + void PrepareReschedule(); + + /** + * Gets a reference to the emulated CPU. + * @returns A reference to the emulated CPU. + */ + ARM_Interface& CPU() { + return *cpu_core; + } + +private: + /** + * Initialize the emulated system. + * @param emu_window Pointer to the host-system window used for video output and keyboard input. + * @param system_mode The system mode. + * @return ResultStatus code, indicating if the operation succeeded. + */ + ResultStatus Init(EmuWindow* emu_window, u32 system_mode); + + /// Reschedule the core emulation + void Reschedule(); + + /// AppLoader used to load the current executing application + std::unique_ptr<Loader::AppLoader> app_loader; + + ///< ARM11 CPU core + std::unique_ptr<ARM_Interface> cpu_core; + + /// When true, signals that a reschedule should happen + bool reschedule_pending{}; + + static System s_instance; }; -extern std::unique_ptr<ARM_Interface> g_app_core; ///< ARM11 application core -extern std::unique_ptr<ARM_Interface> g_sys_core; ///< ARM11 system (OS) core - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// Start the core -void Start(); - -/** - * Run the core CPU loop - * This function runs the core for the specified number of CPU instructions before trying to update - * hardware. This is much faster than SingleStep (and should be equivalent), as the CPU is not - * required to do a full dispatch with each instruction. NOTE: the number of instructions requested - * is not guaranteed to run, as this will be interrupted preemptively if a hardware update is - * requested (e.g. on a thread switch). - */ -void RunLoop(int tight_loop = 1000); - -/// Step the CPU one instruction -void SingleStep(); - -/// Halt the core -void Halt(const char* msg); - -/// Kill the core -void Stop(); - -/// Initialize the core -void Init(); - -/// Shutdown the core -void Shutdown(); +static ARM_Interface& CPU() { + return System::GetInstance().CPU(); +} -} // namespace +} // namespace Core diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 5220b55ea..a437d0823 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -130,7 +130,6 @@ int RegisterEvent(const char* name, TimedCallback callback) { static void AntiCrashCallback(u64 userdata, int cycles_late) { LOG_CRITICAL(Core_Timing, "Savestate broken: an unregistered event was called."); - Core::Halt("invalid timing events"); } void RestoreRegisterEvent(int event_type, const char* name, TimedCallback callback) { @@ -147,7 +146,7 @@ void UnregisterAllEvents() { } void Init() { - Core::g_app_core->down_count = INITIAL_SLICE_LENGTH; + Core::CPU().down_count = INITIAL_SLICE_LENGTH; g_slice_length = INITIAL_SLICE_LENGTH; global_timer = 0; idled_cycles = 0; @@ -187,7 +186,7 @@ void Shutdown() { } u64 GetTicks() { - return (u64)global_timer + g_slice_length - Core::g_app_core->down_count; + return (u64)global_timer + g_slice_length - Core::CPU().down_count; } u64 GetIdleTicks() { @@ -461,18 +460,18 @@ void MoveEvents() { } void ForceCheck() { - s64 cycles_executed = g_slice_length - Core::g_app_core->down_count; + s64 cycles_executed = g_slice_length - Core::CPU().down_count; global_timer += cycles_executed; // This will cause us to check for new events immediately. - Core::g_app_core->down_count = 0; + Core::CPU().down_count = 0; // But let's not eat a bunch more time in Advance() because of this. g_slice_length = 0; } void Advance() { - s64 cycles_executed = g_slice_length - Core::g_app_core->down_count; + s64 cycles_executed = g_slice_length - Core::CPU().down_count; global_timer += cycles_executed; - Core::g_app_core->down_count = g_slice_length; + Core::CPU().down_count = g_slice_length; if (has_ts_events) MoveEvents(); @@ -481,7 +480,7 @@ void Advance() { if (!first) { if (g_slice_length < 10000) { g_slice_length += 10000; - Core::g_app_core->down_count += g_slice_length; + Core::CPU().down_count += g_slice_length; } } else { // Note that events can eat cycles as well. @@ -491,7 +490,7 @@ void Advance() { const int diff = target - g_slice_length; g_slice_length += diff; - Core::g_app_core->down_count += diff; + Core::CPU().down_count += diff; } if (advance_callback) advance_callback(static_cast<int>(cycles_executed)); @@ -507,12 +506,12 @@ void LogPendingEvents() { } void Idle(int max_idle) { - s64 cycles_down = Core::g_app_core->down_count; + s64 cycles_down = Core::CPU().down_count; if (max_idle != 0 && cycles_down > max_idle) cycles_down = max_idle; if (first && cycles_down > 0) { - s64 cycles_executed = g_slice_length - Core::g_app_core->down_count; + s64 cycles_executed = g_slice_length - Core::CPU().down_count; s64 cycles_next_event = first->time - global_timer; if (cycles_next_event < cycles_executed + cycles_down) { @@ -527,9 +526,9 @@ void Idle(int max_idle) { cycles_down / (float)(g_clock_rate_arm11 * 0.001f)); idled_cycles += cycles_down; - Core::g_app_core->down_count -= cycles_down; - if (Core::g_app_core->down_count == 0) - Core::g_app_core->down_count = -1; + Core::CPU().down_count -= cycles_down; + if (Core::CPU().down_count == 0) + Core::CPU().down_count = -1; } std::string GetScheduledEventsSummary() { diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index e1c4931ec..51ce78435 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -64,7 +64,7 @@ private: */ class ExtSaveDataArchive : public SaveDataArchive { public: - ExtSaveDataArchive(const std::string& mount_point) : SaveDataArchive(mount_point) {} + explicit ExtSaveDataArchive(const std::string& mount_point) : SaveDataArchive(mount_point) {} std::string GetName() const override { return "ExtSaveDataArchive: " + mount_point; @@ -141,11 +141,10 @@ std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) std::string GetExtDataContainerPath(const std::string& mount_point, bool shared) { if (shared) - return Common::StringFromFormat("%sdata/%s/extdata/", mount_point.c_str(), - SYSTEM_ID.c_str()); + return Common::StringFromFormat("%sdata/%s/extdata/", mount_point.c_str(), SYSTEM_ID); - return Common::StringFromFormat("%sNintendo 3DS/%s/%s/extdata/", mount_point.c_str(), - SYSTEM_ID.c_str(), SDCARD_ID.c_str()); + return Common::StringFromFormat("%sNintendo 3DS/%s/%s/extdata/", mount_point.c_str(), SYSTEM_ID, + SDCARD_ID); } Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) { diff --git a/src/core/file_sys/archive_ncch.cpp b/src/core/file_sys/archive_ncch.cpp index 6f1aadfc3..89455e39c 100644 --- a/src/core/file_sys/archive_ncch.cpp +++ b/src/core/file_sys/archive_ncch.cpp @@ -19,7 +19,7 @@ namespace FileSys { static std::string GetNCCHContainerPath(const std::string& nand_directory) { - return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID.c_str()); + return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID); } static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) { diff --git a/src/core/file_sys/archive_ncch.h b/src/core/file_sys/archive_ncch.h index 66b8ce75d..753b91f96 100644 --- a/src/core/file_sys/archive_ncch.h +++ b/src/core/file_sys/archive_ncch.h @@ -17,7 +17,7 @@ namespace FileSys { /// File system interface to the NCCH archive class ArchiveFactory_NCCH final : public ArchiveFactory { public: - ArchiveFactory_NCCH(const std::string& mount_point); + explicit ArchiveFactory_NCCH(const std::string& mount_point); std::string GetName() const override { return "NCCH"; diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h index 8a8082a05..1eaf99b54 100644 --- a/src/core/file_sys/archive_romfs.h +++ b/src/core/file_sys/archive_romfs.h @@ -20,7 +20,7 @@ namespace FileSys { /// File system interface to the RomFS archive class ArchiveFactory_RomFS final : public ArchiveFactory { public: - ArchiveFactory_RomFS(Loader::AppLoader& app_loader); + explicit ArchiveFactory_RomFS(Loader::AppLoader& app_loader); std::string GetName() const override { return "RomFS"; diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 9d99b110c..f6c70bfcc 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h @@ -17,7 +17,7 @@ namespace FileSys { /// Archive backend for SDMC archive class SDMCArchive : public ArchiveBackend { public: - SDMCArchive(const std::string& mount_point_) : mount_point(mount_point_) {} + explicit SDMCArchive(const std::string& mount_point_) : mount_point(mount_point_) {} std::string GetName() const override { return "SDMCArchive: " + mount_point; @@ -43,7 +43,7 @@ protected: /// File system interface to the SDMC archive class ArchiveFactory_SDMC final : public ArchiveFactory { public: - ArchiveFactory_SDMC(const std::string& mount_point); + explicit ArchiveFactory_SDMC(const std::string& mount_point); /** * Initialize the archive. diff --git a/src/core/file_sys/archive_sdmcwriteonly.h b/src/core/file_sys/archive_sdmcwriteonly.h index ed977485a..9cd38d96f 100644 --- a/src/core/file_sys/archive_sdmcwriteonly.h +++ b/src/core/file_sys/archive_sdmcwriteonly.h @@ -19,7 +19,7 @@ namespace FileSys { */ class SDMCWriteOnlyArchive : public SDMCArchive { public: - SDMCWriteOnlyArchive(const std::string& mount_point) : SDMCArchive(mount_point) {} + explicit SDMCWriteOnlyArchive(const std::string& mount_point) : SDMCArchive(mount_point) {} std::string GetName() const override { return "SDMCWriteOnlyArchive: " + mount_point; @@ -34,7 +34,7 @@ public: /// File system interface to the SDMC write-only archive class ArchiveFactory_SDMCWriteOnly final : public ArchiveFactory { public: - ArchiveFactory_SDMCWriteOnly(const std::string& mount_point); + explicit ArchiveFactory_SDMCWriteOnly(const std::string& mount_point); /** * Initialize the archive. diff --git a/src/core/file_sys/archive_source_sd_savedata.cpp b/src/core/file_sys/archive_source_sd_savedata.cpp index 2d8a950a3..e01357891 100644 --- a/src/core/file_sys/archive_source_sd_savedata.cpp +++ b/src/core/file_sys/archive_source_sd_savedata.cpp @@ -18,7 +18,7 @@ namespace { std::string GetSaveDataContainerPath(const std::string& sdmc_directory) { return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/", sdmc_directory.c_str(), - SYSTEM_ID.c_str(), SDCARD_ID.c_str()); + SYSTEM_ID, SDCARD_ID); } std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) { @@ -90,4 +90,9 @@ ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(u64 program return MakeResult<ArchiveFormatInfo>(info); } +std::string ArchiveSource_SDSaveData::GetSaveDataPathFor(const std::string& mount_point, + u64 program_id) { + return GetSaveDataPath(GetSaveDataContainerPath(mount_point), program_id); +} + } // namespace FileSys diff --git a/src/core/file_sys/archive_source_sd_savedata.h b/src/core/file_sys/archive_source_sd_savedata.h index b33126c31..b5fe43cc1 100644 --- a/src/core/file_sys/archive_source_sd_savedata.h +++ b/src/core/file_sys/archive_source_sd_savedata.h @@ -23,6 +23,8 @@ public: ResultCode Format(u64 program_id, const FileSys::ArchiveFormatInfo& format_info); ResultVal<ArchiveFormatInfo> GetFormatInfo(u64 program_id) const; + static std::string GetSaveDataPathFor(const std::string& mount_point, u64 program_id); + private: std::string mount_point; }; diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index 54e7793e0..8986b5c0e 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -26,7 +26,7 @@ std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& pa } std::string GetSystemSaveDataContainerPath(const std::string& mount_point) { - return Common::StringFromFormat("%sdata/%s/sysdata/", mount_point.c_str(), SYSTEM_ID.c_str()); + return Common::StringFromFormat("%sdata/%s/sysdata/", mount_point.c_str(), SYSTEM_ID); } Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low) { diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h index a24b89f2b..52eb6c630 100644 --- a/src/core/file_sys/archive_systemsavedata.h +++ b/src/core/file_sys/archive_systemsavedata.h @@ -18,7 +18,7 @@ namespace FileSys { /// File system interface to the SystemSaveData archive class ArchiveFactory_SystemSaveData final : public ArchiveFactory { public: - ArchiveFactory_SystemSaveData(const std::string& mount_point); + explicit ArchiveFactory_SystemSaveData(const std::string& mount_point); ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h index 990802579..b9f52f65d 100644 --- a/src/core/file_sys/path_parser.h +++ b/src/core/file_sys/path_parser.h @@ -17,7 +17,7 @@ namespace FileSys { */ class PathParser { public: - PathParser(const Path& path); + explicit PathParser(const Path& path); /** * Checks if the Path is valid. diff --git a/src/core/file_sys/savedata_archive.h b/src/core/file_sys/savedata_archive.h index 2fb6c452a..176d35710 100644 --- a/src/core/file_sys/savedata_archive.h +++ b/src/core/file_sys/savedata_archive.h @@ -18,7 +18,7 @@ namespace FileSys { /// Archive backend for general save data archive type (SaveData and SystemSaveData) class SaveDataArchive : public ArchiveBackend { public: - SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {} + explicit SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {} std::string GetName() const override { return "SaveDataArchive: " + mount_point; diff --git a/src/common/emu_window.cpp b/src/core/frontend/emu_window.cpp index e3a9e08e6..f6f90f9e1 100644 --- a/src/common/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -5,8 +5,8 @@ #include <algorithm> #include <cmath> #include "common/assert.h" -#include "common/key_map.h" -#include "emu_window.h" +#include "core/frontend/emu_window.h" +#include "core/frontend/key_map.h" #include "video_core/video_core.h" void EmuWindow::ButtonPressed(Service::HID::PadState pad) { diff --git a/src/common/emu_window.h b/src/core/frontend/emu_window.h index 835c4d500..835c4d500 100644 --- a/src/common/emu_window.h +++ b/src/core/frontend/emu_window.h diff --git a/src/common/key_map.cpp b/src/core/frontend/key_map.cpp index 97cafe9c9..15f0e079c 100644 --- a/src/common/key_map.cpp +++ b/src/core/frontend/key_map.cpp @@ -3,8 +3,8 @@ // Refer to the license.txt file included. #include <map> -#include "common/emu_window.h" -#include "common/key_map.h" +#include "core/frontend/emu_window.h" +#include "core/frontend/key_map.h" namespace KeyMap { diff --git a/src/common/key_map.h b/src/core/frontend/key_map.h index 040794578..040794578 100644 --- a/src/common/key_map.h +++ b/src/core/frontend/key_map.h diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index aea43e92b..d88e25073 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -5,6 +5,7 @@ // Originally written by Sven Peter <sven@fail0verflow.com> for anergistic. #include <algorithm> +#include <atomic> #include <climits> #include <csignal> #include <cstdarg> @@ -15,8 +16,8 @@ #include <fcntl.h> #ifdef _WIN32 -#include <WinSock2.h> -#include <common/x64/abi.h> +#include <winsock2.h> +// winsock2.h needs to be included first to prevent winsock.h being included by other includes #include <io.h> #include <iphlpapi.h> #include <ws2tcpip.h> @@ -34,6 +35,7 @@ #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/gdbstub/gdbstub.h" +#include "core/loader/loader.h" #include "core/memory.h" const int GDB_BUFFER_SIZE = 10000; @@ -130,7 +132,10 @@ static u16 gdbstub_port = 24689; static bool halt_loop = true; static bool step_loop = false; -std::atomic<bool> g_server_enabled(false); + +// If set to false, the server will never be started and no +// gdbstub-related functions will be executed. +static std::atomic<bool> server_enabled(false); #ifdef _WIN32 WSADATA InitData; @@ -181,11 +186,10 @@ static u8 NibbleToHex(u8 n) { /** * Converts input hex string characters into an array of equivalent of u8 bytes. * -* @param dest Pointer to buffer to store u8 bytes. * @param src Pointer to array of output hex string characters. * @param len Length of src array. */ -static u32 HexToInt(u8* src, u32 len) { +static u32 HexToInt(const u8* src, size_t len) { u32 output = 0; while (len-- > 0) { output = (output << 4) | HexCharToValue(src[0]); @@ -201,7 +205,7 @@ static u32 HexToInt(u8* src, u32 len) { * @param src Pointer to array of u8 bytes. * @param len Length of src array. */ -static void MemToGdbHex(u8* dest, u8* src, u32 len) { +static void MemToGdbHex(u8* dest, const u8* src, size_t len) { while (len-- > 0) { u8 tmp = *src++; *dest++ = NibbleToHex(tmp >> 4); @@ -216,7 +220,7 @@ static void MemToGdbHex(u8* dest, u8* src, u32 len) { * @param src Pointer to array of output hex string characters. * @param len Length of src array. */ -static void GdbHexToMem(u8* dest, u8* src, u32 len) { +static void GdbHexToMem(u8* dest, const u8* src, size_t len) { while (len-- > 0) { *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]); src += 2; @@ -240,7 +244,7 @@ static void IntToGdbHex(u8* dest, u32 v) { * * @param src Pointer to hex string. */ -static u32 GdbHexToInt(u8* src) { +static u32 GdbHexToInt(const u8* src) { u32 output = 0; for (int i = 0; i < 8; i += 2) { @@ -264,7 +268,7 @@ static u8 ReadByte() { } /// Calculate the checksum of the current command buffer. -static u8 CalculateChecksum(u8* buffer, u32 length) { +static u8 CalculateChecksum(const u8* buffer, size_t length) { return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>())); } @@ -446,9 +450,9 @@ static void SendSignal(u32 signal) { latest_signal = signal; - std::string buffer = Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;", latest_signal, 15, - htonl(Core::g_app_core->GetPC()), 13, - htonl(Core::g_app_core->GetReg(13))); + std::string buffer = + Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;", latest_signal, 15, + htonl(Core::CPU().GetPC()), 13, htonl(Core::CPU().GetReg(13))); LOG_DEBUG(Debug_GDBStub, "Response: %s", buffer.c_str()); SendReply(buffer.c_str()); } @@ -535,15 +539,15 @@ static void ReadRegister() { } if (id <= R15_REGISTER) { - IntToGdbHex(reply, Core::g_app_core->GetReg(id)); + IntToGdbHex(reply, Core::CPU().GetReg(id)); } else if (id == CPSR_REGISTER) { - IntToGdbHex(reply, Core::g_app_core->GetCPSR()); + IntToGdbHex(reply, Core::CPU().GetCPSR()); } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { - IntToGdbHex(reply, Core::g_app_core->GetVFPReg( + IntToGdbHex(reply, Core::CPU().GetVFPReg( id - CPSR_REGISTER - 1)); // VFP registers should start at 26, so one after CSPR_REGISTER } else if (id == FPSCR_REGISTER) { - IntToGdbHex(reply, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); // Get FPSCR + IntToGdbHex(reply, Core::CPU().GetVFPSystemReg(VFP_FPSCR)); // Get FPSCR IntToGdbHex(reply + 8, 0); } else { return SendReply("E01"); @@ -560,29 +564,29 @@ static void ReadRegisters() { u8* bufptr = buffer; for (int reg = 0; reg <= R15_REGISTER; reg++) { - IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetReg(reg)); + IntToGdbHex(bufptr + reg * CHAR_BIT, Core::CPU().GetReg(reg)); } bufptr += (16 * CHAR_BIT); - IntToGdbHex(bufptr, Core::g_app_core->GetCPSR()); + IntToGdbHex(bufptr, Core::CPU().GetCPSR()); bufptr += CHAR_BIT; for (int reg = 0; reg <= 31; reg++) { - IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetVFPReg(reg)); + IntToGdbHex(bufptr + reg * CHAR_BIT, Core::CPU().GetVFPReg(reg)); } bufptr += (32 * CHAR_BIT); - IntToGdbHex(bufptr, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); + IntToGdbHex(bufptr, Core::CPU().GetVFPSystemReg(VFP_FPSCR)); SendReply(reinterpret_cast<char*>(buffer)); } /// Modify data of register specified by gdb client. static void WriteRegister() { - u8* buffer_ptr = command_buffer + 3; + const u8* buffer_ptr = command_buffer + 3; u32 id = HexCharToValue(command_buffer[1]); if (command_buffer[2] != '=') { @@ -592,13 +596,13 @@ static void WriteRegister() { } if (id <= R15_REGISTER) { - Core::g_app_core->SetReg(id, GdbHexToInt(buffer_ptr)); + Core::CPU().SetReg(id, GdbHexToInt(buffer_ptr)); } else if (id == CPSR_REGISTER) { - Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr)); + Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr)); } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { - Core::g_app_core->SetVFPReg(id - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr)); + Core::CPU().SetVFPReg(id - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr)); } else if (id == FPSCR_REGISTER) { - Core::g_app_core->SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr)); + Core::CPU().SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr)); } else { return SendReply("E01"); } @@ -608,27 +612,26 @@ static void WriteRegister() { /// Modify all registers with data received from the client. static void WriteRegisters() { - u8* buffer_ptr = command_buffer + 1; + const u8* buffer_ptr = command_buffer + 1; if (command_buffer[0] != 'G') return SendReply("E01"); for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { if (reg <= R15_REGISTER) { - Core::g_app_core->SetReg(reg, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); + Core::CPU().SetReg(reg, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); } else if (reg == CPSR_REGISTER) { - Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr + i * CHAR_BIT)); + Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * CHAR_BIT)); } else if (reg == CPSR_REGISTER - 1) { // Dummy FPA register, ignore } else if (reg < CPSR_REGISTER) { // Dummy FPA registers, ignore i += 2; } else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) { - Core::g_app_core->SetVFPReg(reg - CPSR_REGISTER - 1, - GdbHexToInt(buffer_ptr + i * CHAR_BIT)); + Core::CPU().SetVFPReg(reg - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); i++; // Skip padding } else if (reg == FPSCR_REGISTER) { - Core::g_app_core->SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); + Core::CPU().SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); } } @@ -653,7 +656,7 @@ static void ReadMemory() { SendReply("E01"); } - u8* data = Memory::GetPointer(addr); + const u8* data = Memory::GetPointer(addr); if (!data) { return SendReply("E00"); } @@ -902,10 +905,10 @@ void SetServerPort(u16 port) { void ToggleServer(bool status) { if (status) { - g_server_enabled = status; + server_enabled = status; // Start server - if (!IsConnected() && Core::g_sys_core != nullptr) { + if (!IsConnected() && Core::System().GetInstance().IsPoweredOn()) { Init(); } } else { @@ -914,12 +917,12 @@ void ToggleServer(bool status) { Shutdown(); } - g_server_enabled = status; + server_enabled = status; } } static void Init(u16 port) { - if (!g_server_enabled) { + if (!server_enabled) { // Set the halt loop to false in case the user enabled the gdbstub mid-execution. // This way the CPU can still execute normally. halt_loop = false; @@ -998,7 +1001,7 @@ void Init() { } void Shutdown() { - if (!g_server_enabled) { + if (!server_enabled) { return; } @@ -1015,8 +1018,12 @@ void Shutdown() { LOG_INFO(Debug_GDBStub, "GDB stopped."); } +bool IsServerEnabled() { + return server_enabled; +} + bool IsConnected() { - return g_server_enabled && gdbserver_socket != -1; + return IsServerEnabled() && gdbserver_socket != -1; } bool GetCpuHaltFlag() { diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h index a7483bb10..38177e32c 100644 --- a/src/core/gdbstub/gdbstub.h +++ b/src/core/gdbstub/gdbstub.h @@ -5,7 +5,7 @@ // Originally written by Sven Peter <sven@fail0verflow.com> for anergistic. #pragma once -#include <atomic> + #include "common/common_types.h" namespace GDBStub { @@ -24,10 +24,6 @@ struct BreakpointAddress { BreakpointType type; }; -/// If set to false, the server will never be started and no gdbstub-related functions will be -/// executed. -extern std::atomic<bool> g_server_enabled; - /** * Set the port the gdbstub should use to listen for connections. * @@ -36,7 +32,7 @@ extern std::atomic<bool> g_server_enabled; void SetServerPort(u16 port); /** - * Set the g_server_enabled flag and start or stop the server if possible. + * Starts or stops the server if possible. * * @param status Set the server to enabled or disabled. */ @@ -48,6 +44,9 @@ void Init(); /// Stop gdbstub server. void Shutdown(); +/// Checks if the gdbstub server is enabled. +bool IsServerEnabled(); + /// Returns true if there is an active socket connection. bool IsConnected(); diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index 4311d9897..645b2d5fe 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -101,6 +101,10 @@ ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter) return result; } +bool Applet::IsRunning() const { + return is_running; +} + bool IsLibraryAppletRunning() { // Check the applets map for instances of any applet for (auto itr = applets.begin(); itr != applets.end(); ++itr) diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h index bfdcad126..ebeed9813 100644 --- a/src/core/hle/applets/applet.h +++ b/src/core/hle/applets/applet.h @@ -13,8 +13,7 @@ namespace Applets { class Applet { public: - virtual ~Applet() {} - Applet(Service::APT::AppletId id) : id(id) {} + virtual ~Applet() = default; /** * Creates an instance of the Applet subclass identified by the parameter. @@ -48,7 +47,7 @@ public: /** * Whether the applet is currently executing instead of the host application or not. */ - virtual bool IsRunning() const = 0; + bool IsRunning() const; /** * Handles an update tick for the Applet, lets it update the screen, send commands, etc. @@ -56,6 +55,8 @@ public: virtual void Update() = 0; protected: + explicit Applet(Service::APT::AppletId id) : id(id) {} + /** * Handles the Applet start event, triggered from the application. * @param parameter Parameter data to handle. @@ -65,6 +66,9 @@ protected: Service::APT::AppletId id; ///< Id of this Applet std::shared_ptr<std::vector<u8>> heap_memory; ///< Heap memory for this Applet + + /// Whether this applet is currently running instead of the host application or not. + bool is_running = false; }; /// Returns whether a library applet is currently running diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp index e1379ac4d..75d7fd9fc 100644 --- a/src/core/hle/applets/erreula.cpp +++ b/src/core/hle/applets/erreula.cpp @@ -47,7 +47,7 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param } ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parameter) { - started = true; + is_running = true; // TODO(Subv): Set the expected fields in the response buffer before resending it to the // application. @@ -62,7 +62,7 @@ ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parame message.sender_id = static_cast<u32>(id); Service::APT::SendParameter(message); - started = false; + is_running = false; return RESULT_SUCCESS; } diff --git a/src/core/hle/applets/erreula.h b/src/core/hle/applets/erreula.h index a7ec7ec01..681bbea0c 100644 --- a/src/core/hle/applets/erreula.h +++ b/src/core/hle/applets/erreula.h @@ -17,18 +17,12 @@ public: 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; - } +private: /// This SharedMemory will be created when we receive the LibAppJustStarted message. /// It holds the framebuffer info retrieved by the application with /// GSPGPU::ImportDisplayCaptureInfo Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; - -private: - /// Whether this applet is currently running instead of the host application or not. - bool started = false; }; } // namespace Applets diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index 3455b9201..07c7f5b99 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -55,7 +55,7 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p } ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& parameter) { - started = true; + is_running = true; // TODO(Subv): Set the expected fields in the response buffer before resending it to the // application. @@ -78,7 +78,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa message.sender_id = static_cast<u32>(id); Service::APT::SendParameter(message); - started = false; + is_running = false; return RESULT_SUCCESS; } diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h index e3ab9f0cd..ec00e29d2 100644 --- a/src/core/hle/applets/mii_selector.h +++ b/src/core/hle/applets/mii_selector.h @@ -65,23 +65,18 @@ ASSERT_REG_POSITION(unk_6C, 0x6C); class MiiSelector final : public Applet { public: - MiiSelector(Service::APT::AppletId id) : Applet(id), started(false) {} + MiiSelector(Service::APT::AppletId id) : Applet(id) {} 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; - } +private: /// This SharedMemory will be created when we receive the LibAppJustStarted message. /// It holds the framebuffer info retrieved by the application with /// GSPGPU::ImportDisplayCaptureInfo Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; - /// Whether this applet is currently running instead of the host application or not. - bool started; - MiiConfig config; }; } diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index 1e21337f5..059297fbc 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp @@ -70,7 +70,7 @@ ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter cons DrawScreenKeyboard(); - started = true; + is_running = true; return RESULT_SUCCESS; } @@ -94,13 +94,13 @@ void SoftwareKeyboard::Update() { } void SoftwareKeyboard::DrawScreenKeyboard() { - auto bottom_screen = GSP_GPU::GetFrameBufferInfo(0, 1); + auto bottom_screen = Service::GSP::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 Memory::ZeroBlock(info.address_left, info.stride * 320); - GSP_GPU::SetBufferSwap(1, info); + Service::GSP::SetBufferSwap(1, info); } void SoftwareKeyboard::Finalize() { @@ -113,7 +113,7 @@ void SoftwareKeyboard::Finalize() { message.sender_id = static_cast<u32>(id); Service::APT::SendParameter(message); - started = false; + is_running = false; } } } // namespace diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h index ea0b1fba9..cc92a8f19 100644 --- a/src/core/hle/applets/swkbd.h +++ b/src/core/hle/applets/swkbd.h @@ -52,14 +52,11 @@ static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config class SoftwareKeyboard final : public Applet { public: - SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) {} + SoftwareKeyboard(Service::APT::AppletId id) : Applet(id) {} 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. @@ -72,6 +69,7 @@ public: */ void Finalize(); +private: /// This SharedMemory will be created when we receive the LibAppJustStarted message. /// It holds the framebuffer info retrieved by the application with /// GSPGPU::ImportDisplayCaptureInfo @@ -82,9 +80,6 @@ public: /// 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 8ce0f6d2b..7875971ce 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -7,14 +7,14 @@ #include "common/common_types.h" #include "core/arm/arm_interface.h" #include "core/core.h" -#include "core/hle/hle.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/result.h" #include "core/hle/svc.h" #include "core/memory.h" namespace HLE { -#define PARAM(n) Core::g_app_core->GetReg(n) +#define PARAM(n) Core::CPU().GetReg(n) /// An invalid result code that is meant to be overwritten when a thread resumes from waiting static const ResultCode RESULT_INVALID(0xDEADC0DE); @@ -24,7 +24,7 @@ static const ResultCode RESULT_INVALID(0xDEADC0DE); * @param res Result to return */ static inline void FuncReturn(u32 res) { - Core::g_app_core->SetReg(0, res); + Core::CPU().SetReg(0, res); } /** @@ -33,8 +33,8 @@ static inline void FuncReturn(u32 res) { * @todo Verify that this function is correct */ static inline void FuncReturn64(u64 res) { - Core::g_app_core->SetReg(0, (u32)(res & 0xFFFFFFFF)); - Core::g_app_core->SetReg(1, (u32)((res >> 32) & 0xFFFFFFFF)); + Core::CPU().SetReg(0, (u32)(res & 0xFFFFFFFF)); + Core::CPU().SetReg(1, (u32)((res >> 32) & 0xFFFFFFFF)); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -49,7 +49,7 @@ template <ResultCode func(u32*, u32, u32, u32, u32, u32)> void Wrap() { u32 param_1 = 0; u32 retval = func(¶m_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw; - Core::g_app_core->SetReg(1, param_1); + Core::CPU().SetReg(1, param_1); FuncReturn(retval); } @@ -57,19 +57,19 @@ template <ResultCode func(u32*, s32, u32, u32, u32, s32)> void Wrap() { u32 param_1 = 0; u32 retval = func(¶m_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw; - Core::g_app_core->SetReg(1, param_1); + Core::CPU().SetReg(1, param_1); FuncReturn(retval); } template <ResultCode func(s32*, u32*, s32, bool, s64)> void Wrap() { s32 param_1 = 0; - s32 retval = func(¶m_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), + s32 retval = func(¶m_1, (Kernel::Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))) .raw; if (retval != RESULT_INVALID.raw) { - Core::g_app_core->SetReg(1, (u32)param_1); + Core::CPU().SetReg(1, (u32)param_1); FuncReturn(retval); } } @@ -84,7 +84,7 @@ template <ResultCode func(u32*)> void Wrap() { u32 param_1 = 0; u32 retval = func(¶m_1).raw; - Core::g_app_core->SetReg(1, param_1); + Core::CPU().SetReg(1, param_1); FuncReturn(retval); } @@ -102,24 +102,24 @@ 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); + Core::CPU().SetReg(1, memory_info.base_address); + Core::CPU().SetReg(2, memory_info.size); + Core::CPU().SetReg(3, memory_info.permission); + Core::CPU().SetReg(4, memory_info.state); + Core::CPU().SetReg(5, page_info.flags); FuncReturn(retval); } -template <ResultCode func(MemoryInfo*, PageInfo*, Handle, u32)> +template <ResultCode func(MemoryInfo*, PageInfo*, Kernel::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); + Core::CPU().SetReg(1, memory_info.base_address); + Core::CPU().SetReg(2, memory_info.size); + Core::CPU().SetReg(3, memory_info.permission); + Core::CPU().SetReg(4, memory_info.state); + Core::CPU().SetReg(5, page_info.flags); FuncReturn(retval); } @@ -127,7 +127,7 @@ template <ResultCode func(s32*, u32)> void Wrap() { s32 param_1 = 0; u32 retval = func(¶m_1, PARAM(1)).raw; - Core::g_app_core->SetReg(1, param_1); + Core::CPU().SetReg(1, param_1); FuncReturn(retval); } @@ -140,7 +140,7 @@ template <ResultCode func(u32*, u32)> void Wrap() { u32 param_1 = 0; u32 retval = func(¶m_1, PARAM(1)).raw; - Core::g_app_core->SetReg(1, param_1); + Core::CPU().SetReg(1, param_1); FuncReturn(retval); } @@ -160,7 +160,7 @@ template <ResultCode func(u32*, const char*)> void Wrap() { u32 param_1 = 0; u32 retval = func(¶m_1, (char*)Memory::GetPointer(PARAM(1))).raw; - Core::g_app_core->SetReg(1, param_1); + Core::CPU().SetReg(1, param_1); FuncReturn(retval); } @@ -168,7 +168,7 @@ template <ResultCode func(u32*, s32, s32)> void Wrap() { u32 param_1 = 0; u32 retval = func(¶m_1, PARAM(1), PARAM(2)).raw; - Core::g_app_core->SetReg(1, param_1); + Core::CPU().SetReg(1, param_1); FuncReturn(retval); } @@ -176,7 +176,7 @@ template <ResultCode func(s32*, u32, s32)> void Wrap() { s32 param_1 = 0; u32 retval = func(¶m_1, PARAM(1), PARAM(2)).raw; - Core::g_app_core->SetReg(1, param_1); + Core::CPU().SetReg(1, param_1); FuncReturn(retval); } @@ -184,8 +184,8 @@ template <ResultCode func(s64*, u32, s32)> void Wrap() { s64 param_1 = 0; u32 retval = func(¶m_1, PARAM(1), PARAM(2)).raw; - Core::g_app_core->SetReg(1, (u32)param_1); - Core::g_app_core->SetReg(2, (u32)(param_1 >> 32)); + Core::CPU().SetReg(1, (u32)param_1); + Core::CPU().SetReg(2, (u32)(param_1 >> 32)); FuncReturn(retval); } @@ -194,7 +194,7 @@ void Wrap() { u32 param_1 = 0; // The last parameter is passed in R0 instead of R4 u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3), PARAM(0)).raw; - Core::g_app_core->SetReg(1, param_1); + Core::CPU().SetReg(1, param_1); FuncReturn(retval); } @@ -205,30 +205,30 @@ void Wrap() { FuncReturn(func(PARAM(0), param1, param2).raw); } -template <ResultCode func(s64*, Handle, u32)> +template <ResultCode func(s64*, Kernel::Handle, u32)> void Wrap() { s64 param_1 = 0; u32 retval = func(¶m_1, PARAM(1), PARAM(2)).raw; - Core::g_app_core->SetReg(1, (u32)param_1); - Core::g_app_core->SetReg(2, (u32)(param_1 >> 32)); + Core::CPU().SetReg(1, (u32)param_1); + Core::CPU().SetReg(2, (u32)(param_1 >> 32)); FuncReturn(retval); } -template <ResultCode func(Handle, u32)> +template <ResultCode func(Kernel::Handle, u32)> void Wrap() { FuncReturn(func(PARAM(0), PARAM(1)).raw); } -template <ResultCode func(Handle*, Handle*, const char*, u32)> +template <ResultCode func(Kernel::Handle*, Kernel::Handle*, const char*, u32)> void Wrap() { - Handle param_1 = 0; - Handle param_2 = 0; + Kernel::Handle param_1 = 0; + Kernel::Handle param_2 = 0; u32 retval = func(¶m_1, ¶m_2, reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3)) .raw; // The first out parameter is moved into R2 and the second is moved into R1. - Core::g_app_core->SetReg(1, param_2); - Core::g_app_core->SetReg(2, param_1); + Core::CPU().SetReg(1, param_2); + Core::CPU().SetReg(2, param_1); FuncReturn(retval); } diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp deleted file mode 100644 index 41b772163..000000000 --- a/src/core/hle/hle.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/arm/arm_interface.h" -#include "core/core.h" -#include "core/hle/hle.h" -#include "core/hle/service/service.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -namespace { - -bool reschedule; ///< If true, immediately reschedules the CPU to a new thread -} - -namespace HLE { - -void Reschedule(const char* reason) { - DEBUG_ASSERT_MSG(reason != nullptr && strlen(reason) < 256, - "Reschedule: Invalid or too long reason."); - - // TODO(bunnei): It seems that games depend on some CPU execution time elapsing during HLE - // routines. This simulates that time by artificially advancing the number of CPU "ticks". - // The value was chosen empirically, it seems to work well enough for everything tested, but - // is likely not ideal. We should find a more accurate way to simulate timing with HLE. - Core::g_app_core->AddTicks(4000); - - Core::g_app_core->PrepareReschedule(); - - reschedule = true; -} - -bool IsReschedulePending() { - return reschedule; -} - -void DoneRescheduling() { - reschedule = false; -} - -void Init() { - Service::Init(); - - reschedule = false; - - LOG_DEBUG(Kernel, "initialized OK"); -} - -void Shutdown() { - Service::Shutdown(); - - LOG_DEBUG(Kernel, "shutdown OK"); -} - -} // namespace diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h deleted file mode 100644 index 23859e129..000000000 --- a/src/core/hle/hle.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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" - -typedef u32 Handle; -typedef s32 Result; - -const Handle INVALID_HANDLE = 0; - -namespace HLE { - -void Reschedule(const char* reason); -bool IsReschedulePending(); -void DoneRescheduling(); - -void Init(); -void Shutdown(); - -} // namespace diff --git a/src/core/hle/kernel/session.h b/src/core/hle/ipc.h index ec025f732..4e094faa7 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/ipc.h @@ -1,17 +1,31 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2016 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #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 Kernel { + +static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header + +/** + * Returns a pointer to the command buffer in the current thread's TLS + * TODO(Subv): This is not entirely correct, the command buffer should be copied from + * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to + * the service handler process' memory. + * @param offset Optional offset into command buffer + * @return Pointer to command buffer + */ +inline u32* GetCommandBuffer(const int offset = 0) { + return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + + offset); +} +} + namespace IPC { enum DescriptorType : u32 { @@ -144,75 +158,3 @@ inline DescriptorType GetDescriptorType(u32 descriptor) { } } // namespace IPC - -namespace Kernel { - -static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header - -/** - * Returns a pointer to the command buffer in the current thread's TLS - * TODO(Subv): This is not entirely correct, the command buffer should be copied from - * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to - * the service handler process' memory. - * @param offset Optional offset into command buffer - * @return Pointer to command buffer - */ -inline u32* GetCommandBuffer(const int offset = 0) { - return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + - offset); -} - -/** - * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS - * primitive for communication between different processes, and are used to implement service calls - * to the various system services. - * - * To make a service call, the client must write the command header and parameters to the buffer - * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest - * SVC call with its Session handle. The kernel will read the command header, using it to marshall - * the parameters to the process at the server endpoint of the session. After the server replies to - * the request, the response is marshalled back to the caller's TLS buffer and control is - * transferred back to it. - * - * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC - * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called - * with the session handle, this class's SyncRequest method is called, which should read the TLS - * buffer and emulate the call accordingly. Since the code can directly read the emulated memory, - * no parameter marshalling is done. - * - * In the long term, this should be turned into the full-fledged IPC mechanism implemented by - * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as - * opposed to HLE simulations. - */ -class Session : public WaitObject { -public: - Session(); - ~Session() override; - - std::string GetTypeName() const override { - return "Session"; - } - - static const HandleType HANDLE_TYPE = HandleType::Session; - HandleType GetHandleType() const override { - return HANDLE_TYPE; - } - - /** - * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls - * aren't supported yet. - */ - virtual ResultVal<bool> SyncRequest() = 0; - - // TODO(bunnei): These functions exist to satisfy a hardware test with a Session object - // passed into WaitSynchronization. Figure out the meaning of them. - - bool ShouldWait() override { - return true; - } - - void Acquire() override { - ASSERT_MSG(!ShouldWait(), "object unavailable!"); - } -}; -} diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 37eec4c84..01fab123e 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -4,7 +4,6 @@ #include "common/common_types.h" #include "common/logging/log.h" -#include "core/hle/hle.h" #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/thread.h" #include "core/memory.h" @@ -79,8 +78,6 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, ErrorSummary::WrongArgument, ErrorLevel::Usage); } - HLE::Reschedule(__func__); - // The calls that use a timeout seem to always return a Timeout error even if they did not put // the thread to sleep if (type == ArbitrationType::WaitIfLessThanWithTimeout || diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index aedc6f989..22645f4ec 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -4,12 +4,44 @@ #include "common/assert.h" #include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/server_port.h" +#include "core/hle/kernel/server_session.h" namespace Kernel { ClientPort::ClientPort() {} ClientPort::~ClientPort() {} +ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { + // Note: Threads do not wait for the server endpoint to call + // AcceptSession before returning from this call. + + if (active_sessions >= max_sessions) { + // TODO(Subv): Return an error code in this situation after session disconnection is + // implemented. + /*return ResultCode(ErrorDescription::MaxConnectionsReached, + ErrorModule::OS, ErrorSummary::WouldBlock, + ErrorLevel::Temporary);*/ + } + active_sessions++; + + // Create a new session pair, let the created sessions inherit the parent port's HLE handler. + auto sessions = + ServerSession::CreateSessionPair(server_port->GetName(), server_port->hle_handler); + auto client_session = std::get<SharedPtr<ClientSession>>(sessions); + auto server_session = std::get<SharedPtr<ServerSession>>(sessions); + + if (server_port->hle_handler) + server_port->hle_handler->ClientConnected(server_session); + + server_port->pending_sessions.push_back(std::move(server_session)); + + // Wake the threads waiting on the ServerPort + server_port->WakeupAllWaitingThreads(); + + return MakeResult<SharedPtr<ClientSession>>(std::move(client_session)); +} + } // namespace diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index d28147718..511490c7c 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -11,8 +11,9 @@ namespace Kernel { class ServerPort; +class ClientSession; -class ClientPort : public Object { +class ClientPort final : public Object { public: friend class ServerPort; std::string GetTypeName() const override { @@ -27,12 +28,20 @@ public: return HANDLE_TYPE; } + /** + * Creates a new Session pair, adds the created ServerSession to the associated ServerPort's + * list of pending sessions, and signals the ServerPort, causing any threads + * waiting on it to awake. + * @returns ClientSession The client endpoint of the created Session pair, or error code. + */ + ResultVal<SharedPtr<ClientSession>> Connect(); + SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port. u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have u32 active_sessions; ///< Number of currently open sessions to this port std::string name; ///< Name of client port (optional) -protected: +private: ClientPort(); ~ClientPort() override; }; diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp new file mode 100644 index 000000000..0331386ec --- /dev/null +++ b/src/core/hle/kernel/client_session.cpp @@ -0,0 +1,40 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" + +#include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/server_session.h" + +namespace Kernel { + +ClientSession::ClientSession() = default; +ClientSession::~ClientSession() { + // This destructor will be called automatically when the last ClientSession handle is closed by + // the emulated application. + + if (server_session->hle_handler) + server_session->hle_handler->ClientDisconnected(server_session); + + // TODO(Subv): If the session is still open, set the connection status to 2 (Closed by client), + // wake up all the ServerSession's waiting threads and set the WaitSynchronization result to + // 0xC920181A. +} + +ResultVal<SharedPtr<ClientSession>> ClientSession::Create(ServerSession* server_session, + std::string name) { + SharedPtr<ClientSession> client_session(new ClientSession); + + client_session->name = std::move(name); + client_session->server_session = server_session; + client_session->session_status = SessionStatus::Open; + return MakeResult<SharedPtr<ClientSession>>(std::move(client_session)); +} + +ResultCode ClientSession::SendSyncRequest() { + // Signal the server session that new data is available + return server_session->HandleSyncRequest(); +} + +} // namespace diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h new file mode 100644 index 000000000..ed468dec6 --- /dev/null +++ b/src/core/hle/kernel/client_session.h @@ -0,0 +1,65 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <string> + +#include "common/common_types.h" + +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +class ServerSession; + +enum class SessionStatus { + Open = 1, + ClosedByClient = 2, + ClosedBYServer = 3, +}; + +class ClientSession final : public Object { +public: + friend class ServerSession; + + std::string GetTypeName() const override { + return "ClientSession"; + } + + std::string GetName() const override { + return name; + } + + static const HandleType HANDLE_TYPE = HandleType::ClientSession; + HandleType GetHandleType() const override { + return HANDLE_TYPE; + } + + /** + * Sends an SyncRequest from the current emulated thread. + * @return ResultCode of the operation. + */ + ResultCode SendSyncRequest(); + + std::string name; ///< Name of client port (optional) + ServerSession* server_session; ///< The server session associated with this client session. + SessionStatus session_status; ///< The session's current status. + +private: + ClientSession(); + ~ClientSession() override; + + /** + * Creates a client session. + * @param server_session The server session associated with this client session + * @param name Optional name of client session + * @return The created client session + */ + static ResultVal<SharedPtr<ClientSession>> Create(ServerSession* server_session, + std::string name = "Unknown"); +}; + +} // namespace diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 0c8752670..1db8e102f 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> +#include <boost/range/algorithm_ext/erase.hpp> #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/config_mem.h" @@ -31,13 +32,61 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { waiting_threads.erase(itr); } -void WaitObject::WakeupAllWaitingThreads() { - for (auto thread : waiting_threads) - thread->ResumeFromWait(); +SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { + // Remove the threads that are ready or already running from our waitlist + boost::range::remove_erase_if(waiting_threads, [](const SharedPtr<Thread>& thread) { + return thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_READY || + thread->status == THREADSTATUS_DEAD; + }); + + // TODO(Subv): This call should be performed inside the loop below to check if an object can be + // acquired by a particular thread. This is useful for things like recursive locking of Mutexes. + if (ShouldWait()) + return nullptr; + + Thread* candidate = nullptr; + s32 candidate_priority = THREADPRIO_LOWEST + 1; + + for (const auto& thread : waiting_threads) { + if (thread->current_priority >= candidate_priority) + continue; - waiting_threads.clear(); + bool ready_to_run = + std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), + [](const SharedPtr<WaitObject>& object) { return object->ShouldWait(); }); + if (ready_to_run) { + candidate = thread.get(); + candidate_priority = thread->current_priority; + } + } + + return candidate; +} - HLE::Reschedule(__func__); +void WaitObject::WakeupAllWaitingThreads() { + while (auto thread = GetHighestPriorityReadyThread()) { + if (!thread->IsSleepingOnWaitAll()) { + Acquire(); + // Set the output index of the WaitSynchronizationN call to the index of this object. + if (thread->wait_set_output) { + thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); + thread->wait_set_output = false; + } + } else { + for (auto& object : thread->wait_objects) { + object->Acquire(); + object->RemoveWaitingThread(thread.get()); + } + // Note: This case doesn't update the output index of WaitSynchronizationN. + // Clear the thread's waitlist + thread->wait_objects.clear(); + } + + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + thread->ResumeFromWait(); + // Note: Removing the thread from the object's waitlist will be + // done by GetHighestPriorityReadyThread. + } } const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 231cf7b75..9503e7d04 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -11,11 +11,12 @@ #include <vector> #include <boost/smart_ptr/intrusive_ptr.hpp> #include "common/common_types.h" -#include "core/hle/hle.h" #include "core/hle/result.h" namespace Kernel { +using Handle = u32; + class Thread; // TODO: Verify code @@ -31,22 +32,21 @@ enum KernelHandle : Handle { }; enum class HandleType : u32 { - Unknown = 0, - - Session = 2, - Event = 3, - Mutex = 4, - SharedMemory = 5, - Redirection = 6, - Thread = 7, - Process = 8, - AddressArbiter = 9, - Semaphore = 10, - Timer = 11, - ResourceLimit = 12, - CodeSet = 13, - ClientPort = 14, - ServerPort = 15, + Unknown, + Event, + Mutex, + SharedMemory, + Thread, + Process, + AddressArbiter, + Semaphore, + Timer, + ResourceLimit, + CodeSet, + ClientPort, + ServerPort, + ClientSession, + ServerSession, }; enum { @@ -82,23 +82,23 @@ public: */ bool IsWaitable() const { switch (GetHandleType()) { - case HandleType::Session: - case HandleType::ServerPort: case HandleType::Event: case HandleType::Mutex: case HandleType::Thread: case HandleType::Semaphore: case HandleType::Timer: + case HandleType::ServerPort: + case HandleType::ServerSession: return true; case HandleType::Unknown: case HandleType::SharedMemory: - case HandleType::Redirection: case HandleType::Process: case HandleType::AddressArbiter: case HandleType::ResourceLimit: case HandleType::CodeSet: case HandleType::ClientPort: + case HandleType::ClientSession: return false; } } @@ -152,9 +152,15 @@ public: */ void RemoveWaitingThread(Thread* thread); - /// Wake up all threads waiting on this object + /** + * Wake up all threads waiting on this object that can be awoken, in priority order, + * and set the synchronization result and output of the thread. + */ void WakeupAllWaitingThreads(); + /// Obtains the highest priority thread that is ready to run from this object's waiting list. + SharedPtr<Thread> GetHighestPriorityReadyThread(); + /// Get a const reference to the waiting threads list for debug use const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 8e3ec8a14..6c19aa7c0 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -24,12 +24,14 @@ void ServerPort::Acquire() { } std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( - u32 max_sessions, std::string name) { + u32 max_sessions, std::string name, + std::shared_ptr<Service::SessionRequestHandler> hle_handler) { SharedPtr<ServerPort> server_port(new ServerPort); SharedPtr<ClientPort> client_port(new ClientPort); server_port->name = name + "_Server"; + server_port->hle_handler = std::move(hle_handler); client_port->name = name + "_Client"; client_port->server_port = server_port; client_port->max_sessions = max_sessions; diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index fa9448ca0..b0f8df62c 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -4,11 +4,16 @@ #pragma once +#include <memory> #include <string> #include <tuple> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +namespace Service { +class SessionRequestHandler; +} + namespace Kernel { class ClientPort; @@ -19,10 +24,13 @@ public: * Creates a pair of ServerPort and an associated ClientPort. * @param max_sessions Maximum number of sessions to the port * @param name Optional name of the ports + * @param hle_handler Optional HLE handler template for the port, + * ServerSessions crated from this port will inherit a reference to this handler. * @return The created port tuple */ static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair( - u32 max_sessions, std::string name = "UnknownPort"); + u32 max_sessions, std::string name = "UnknownPort", + std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); std::string GetTypeName() const override { return "ServerPort"; @@ -41,6 +49,10 @@ public: std::vector<SharedPtr<WaitObject>> pending_sessions; ///< ServerSessions waiting to be accepted by the port + /// This session's HLE request handler template (optional) + /// ServerSessions created from this port inherit a reference to this handler. + std::shared_ptr<Service::SessionRequestHandler> hle_handler; + bool ShouldWait() override; void Acquire() override; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp new file mode 100644 index 000000000..146458c1c --- /dev/null +++ b/src/core/hle/kernel/server_session.cpp @@ -0,0 +1,79 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <tuple> + +#include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +ServerSession::ServerSession() = default; +ServerSession::~ServerSession() { + // This destructor will be called automatically when the last ServerSession handle is closed by + // the emulated application. + // TODO(Subv): Reduce the ClientPort's connection count, + // if the session is still open, set the connection status to 3 (Closed by server), +} + +ResultVal<SharedPtr<ServerSession>> ServerSession::Create( + std::string name, std::shared_ptr<Service::SessionRequestHandler> hle_handler) { + SharedPtr<ServerSession> server_session(new ServerSession); + + server_session->name = std::move(name); + server_session->signaled = false; + server_session->hle_handler = std::move(hle_handler); + + return MakeResult<SharedPtr<ServerSession>>(std::move(server_session)); +} + +bool ServerSession::ShouldWait() { + return !signaled; +} + +void ServerSession::Acquire() { + ASSERT_MSG(!ShouldWait(), "object unavailable!"); + signaled = false; +} + +ResultCode ServerSession::HandleSyncRequest() { + // The ServerSession received a sync request, this means that there's new data available + // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or + // similar. + + // If this ServerSession has an associated HLE handler, forward the request to it. + if (hle_handler != nullptr) { + // Attempt to translate the incoming request's command buffer. + ResultCode result = TranslateHLERequest(this); + if (result.IsError()) + return result; + hle_handler->HandleSyncRequest(SharedPtr<ServerSession>(this)); + // TODO(Subv): Translate the response command buffer. + } + + // If this ServerSession does not have an HLE implementation, just wake up the threads waiting + // on it. + signaled = true; + WakeupAllWaitingThreads(); + return RESULT_SUCCESS; +} + +ServerSession::SessionPair ServerSession::CreateSessionPair( + const std::string& name, std::shared_ptr<Service::SessionRequestHandler> hle_handler) { + auto server_session = + ServerSession::Create(name + "_Server", std::move(hle_handler)).MoveFrom(); + // We keep a non-owning pointer to the ServerSession in the ClientSession because we don't want + // to prevent the ServerSession's destructor from being called when the emulated + // application closes the last ServerSession handle. + auto client_session = ClientSession::Create(server_session.get(), name + "_Client").MoveFrom(); + + return std::make_tuple(std::move(server_session), std::move(client_session)); +} + +ResultCode TranslateHLERequest(ServerSession* server_session) { + // TODO(Subv): Implement this function once multiple concurrent processes are supported. + return RESULT_SUCCESS; +} +} diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h new file mode 100644 index 000000000..458284a5d --- /dev/null +++ b/src/core/hle/kernel/server_session.h @@ -0,0 +1,94 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#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/hle/service/service.h" +#include "core/memory.h" + +namespace Kernel { + +class ClientSession; + +/** + * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS + * primitive for communication between different processes, and are used to implement service calls + * to the various system services. + * + * To make a service call, the client must write the command header and parameters to the buffer + * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest + * SVC call with its ClientSession handle. The kernel will read the command header, using it to + * marshall the parameters to the process at the server endpoint of the session. + * After the server replies to the request, the response is marshalled back to the caller's + * TLS buffer and control is transferred back to it. + */ +class ServerSession final : public WaitObject { +public: + std::string GetTypeName() const override { + return "ServerSession"; + } + + static const HandleType HANDLE_TYPE = HandleType::ServerSession; + HandleType GetHandleType() const override { + return HANDLE_TYPE; + } + + using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>; + + /** + * Creates a pair of ServerSession and an associated ClientSession. + * @param name Optional name of the ports + * @return The created session tuple + */ + static SessionPair CreateSessionPair( + const std::string& name = "Unknown", + std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); + + /** + * Handle a sync request from the emulated application. + * @returns ResultCode from the operation. + */ + ResultCode HandleSyncRequest(); + + bool ShouldWait() override; + + void Acquire() override; + + std::string name; ///< The name of this session (optional) + bool signaled; ///< Whether there's new data available to this ServerSession + std::shared_ptr<Service::SessionRequestHandler> + hle_handler; ///< This session's HLE request handler (optional) + +private: + ServerSession(); + ~ServerSession() override; + + /** + * Creates a server session. The server session can have an optional HLE handler, + * which will be invoked to handle the IPC requests that this session receives. + * @param name Optional name of the server session. + * @param hle_handler Optional HLE handler for this server session. + * @return The created server session + */ + static ResultVal<SharedPtr<ServerSession>> Create( + std::string name = "Unknown", + std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); +}; + +/** + * Performs command buffer translation for an HLE IPC request. + * The command buffer from the ServerSession thread's TLS is copied into a + * buffer and all descriptors in the buffer are processed. + * TODO(Subv): Implement this function, currently we do not support multiple processes running at + * once, but once that is implemented we'll need to properly translate all descriptors + * in the command buffer. + */ +ResultCode TranslateHLERequest(ServerSession* server_session); +} diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 84d6d24c6..5fb95dada 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -14,7 +14,6 @@ #include "core/arm/skyeye_common/armstate.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/hle/hle.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/mutex.h" @@ -46,7 +45,7 @@ static std::vector<SharedPtr<Thread>> thread_list; // Lists only ready thread ids. static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue; -static Thread* current_thread; +static SharedPtr<Thread> current_thread; // The first available thread id at startup static u32 next_thread_id; @@ -63,7 +62,7 @@ Thread::Thread() {} Thread::~Thread() {} Thread* GetCurrentThread() { - return current_thread; + return current_thread.get(); } /** @@ -120,8 +119,6 @@ void Thread::Stop() { u32 tls_slot = ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); - - HLE::Reschedule(__func__); } Thread* ArbitrateHighestPriorityThread(u32 address) { @@ -181,50 +178,6 @@ static void PriorityBoostStarvedThreads() { } /** - * Gets the registers for timeout parameter of the next WaitSynchronization call. - * @param thread a pointer to the thread that is ready to call WaitSynchronization - * @returns a tuple of two register pointers to low and high part of the timeout parameter - */ -static std::tuple<u32*, u32*> GetWaitSynchTimeoutParameterRegister(Thread* thread) { - bool thumb_mode = (thread->context.cpsr & TBIT) != 0; - u16 thumb_inst = Memory::Read16(thread->context.pc & 0xFFFFFFFE); - u32 inst = Memory::Read32(thread->context.pc & 0xFFFFFFFC) & 0x0FFFFFFF; - - if ((thumb_mode && thumb_inst == 0xDF24) || (!thumb_mode && inst == 0x0F000024)) { - // svc #0x24 (WaitSynchronization1) - return std::make_tuple(&thread->context.cpu_registers[2], - &thread->context.cpu_registers[3]); - } else if ((thumb_mode && thumb_inst == 0xDF25) || (!thumb_mode && inst == 0x0F000025)) { - // svc #0x25 (WaitSynchronizationN) - return std::make_tuple(&thread->context.cpu_registers[0], - &thread->context.cpu_registers[4]); - } - - UNREACHABLE(); -} - -/** - * Updates the WaitSynchronization timeout parameter according to the difference - * between ticks of the last WaitSynchronization call and the incoming one. - * @param timeout_low a pointer to the register for the low part of the timeout parameter - * @param timeout_high a pointer to the register for the high part of the timeout parameter - * @param last_tick tick of the last WaitSynchronization call - */ -static void UpdateTimeoutParameter(u32* timeout_low, u32* timeout_high, u64 last_tick) { - s64 timeout = ((s64)*timeout_high << 32) | *timeout_low; - - if (timeout != -1) { - timeout -= cyclesToUs(CoreTiming::GetTicks() - last_tick) * 1000; // in nanoseconds - - if (timeout < 0) - timeout = 0; - - *timeout_low = timeout & 0xFFFFFFFF; - *timeout_high = timeout >> 32; - } -} - -/** * Switches the CPU's active thread context to that of the specified thread * @param new_thread The thread to switch to */ @@ -234,7 +187,7 @@ static void SwitchContext(Thread* new_thread) { // Save context for previous thread if (previous_thread) { previous_thread->last_running_ticks = CoreTiming::GetTicks(); - Core::g_app_core->SaveContext(previous_thread->context); + Core::CPU().SaveContext(previous_thread->context); if (previous_thread->status == THREADSTATUS_RUNNING) { // This is only the case when a reschedule is triggered without the current thread @@ -254,40 +207,14 @@ static void SwitchContext(Thread* new_thread) { current_thread = new_thread; - // If the thread was waited by a svcWaitSynch call, step back PC by one instruction to rerun - // the SVC when the thread wakes up. This is necessary to ensure that the thread can acquire - // the requested wait object(s) before continuing. - if (new_thread->waitsynch_waited) { - // CPSR flag indicates CPU mode - bool thumb_mode = (new_thread->context.cpsr & TBIT) != 0; - - // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM - new_thread->context.pc -= thumb_mode ? 2 : 4; - - // Get the register for timeout parameter - u32 *timeout_low, *timeout_high; - std::tie(timeout_low, timeout_high) = GetWaitSynchTimeoutParameterRegister(new_thread); - - // Update the timeout parameter - UpdateTimeoutParameter(timeout_low, timeout_high, new_thread->last_running_ticks); - } - - // Clean up the thread's wait_objects, they'll be restored if needed during - // the svcWaitSynchronization call - for (size_t 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; // Restores thread to its nominal priority if it has been temporarily changed new_thread->current_priority = new_thread->nominal_priority; - Core::g_app_core->LoadContext(new_thread->context); - Core::g_app_core->SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); + Core::CPU().LoadContext(new_thread->context); + Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); } else { current_thread = nullptr; } @@ -319,17 +246,13 @@ static Thread* PopNextReadyThread() { void WaitCurrentThread_Sleep() { Thread* thread = GetCurrentThread(); thread->status = THREADSTATUS_WAIT_SLEEP; - - HLE::Reschedule(__func__); } void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, - bool wait_set_output, bool wait_all) { + bool wait_set_output) { Thread* thread = GetCurrentThread(); thread->wait_set_output = wait_set_output; - thread->wait_all = wait_all; thread->wait_objects = std::move(wait_objects); - thread->waitsynch_waited = true; thread->status = THREADSTATUS_WAIT_SYNCH; } @@ -339,6 +262,13 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { thread->status = THREADSTATUS_WAIT_ARB; } +void ExitCurrentThread() { + Thread* thread = GetCurrentThread(); + thread->Stop(); + thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), + thread_list.end()); +} + /** * Callback that will wake up the thread it was scheduled for * @param thread_handle The handle of the thread that's been awoken @@ -351,15 +281,15 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { return; } - thread->waitsynch_waited = false; - if (thread->status == THREADSTATUS_WAIT_SYNCH || thread->status == THREADSTATUS_WAIT_ARB) { + thread->wait_set_output = false; + // Remove the thread from each of its waiting objects' waitlists + for (auto& object : thread->wait_objects) + object->RemoveWaitingThread(thread.get()); + thread->wait_objects.clear(); thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, ErrorSummary::StatusChanged, ErrorLevel::Info)); - - if (thread->wait_set_output) - thread->SetWaitSynchronizationOutput(-1); } thread->ResumeFromWait(); @@ -399,6 +329,7 @@ void Thread::ResumeFromWait() { ready_queue.push_back(current_priority, this); status = THREADSTATUS_READY; + Core::System::GetInstance().PrepareReschedule(); } /** @@ -453,9 +384,9 @@ std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& t * @param entry_point Address of entry point for execution * @param arg User argument for thread */ -static void ResetThreadContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, - u32 arg) { - memset(&context, 0, sizeof(Core::ThreadContext)); +static void ResetThreadContext(ARM_Interface::ThreadContext& context, u32 stack_top, + u32 entry_point, u32 arg) { + memset(&context, 0, sizeof(ARM_Interface::ThreadContext)); context.cpu_registers[0] = arg; context.pc = entry_point; @@ -494,13 +425,11 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, thread->last_running_ticks = CoreTiming::GetTicks(); thread->processor_id = processor_id; thread->wait_set_output = false; - thread->wait_all = false; thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); thread->owner_process = g_current_process; - thread->waitsynch_waited = false; // Find the next available TLS index, and mark it as used auto& tls_slots = Kernel::g_current_process->tls_slots; @@ -555,8 +484,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, ready_queue.push_back(thread->current_priority, thread.get()); thread->status = THREADSTATUS_READY; - HLE::Reschedule(__func__); - return MakeResult<SharedPtr<Thread>>(std::move(thread)); } @@ -617,16 +544,6 @@ void Reschedule() { Thread* cur = GetCurrentThread(); Thread* next = PopNextReadyThread(); - HLE::DoneRescheduling(); - - // Don't bother switching to the same thread. - // But if the thread was waiting on objects, we still need to switch it - // to perform PC modification, change state to RUNNING, etc. - // This occurs in the case when an object the thread is waiting on immediately wakes up - // the current thread before Reschedule() is called. - if (next == cur && (next == nullptr || next->waitsynch_waited == false)) - return; - if (cur && next) { LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); } else if (cur) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index e0ffcea8a..c77ac644d 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -5,11 +5,13 @@ #pragma once #include <string> +#include <unordered_map> #include <vector> +#include <boost/container/flat_map.hpp> #include <boost/container/flat_set.hpp> #include "common/common_types.h" +#include "core/arm/arm_interface.h" #include "core/core.h" -#include "core/hle/hle.h" #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" @@ -125,6 +127,16 @@ public: void SetWaitSynchronizationOutput(s32 output); /** + * Retrieves the index that this particular object occupies in the list of objects + * that the thread passed to WaitSynchronizationN. + * It is used to set the output value of WaitSynchronizationN when the thread is awakened. + * @param object Object to query the index of. + */ + s32 GetWaitObjectIndex(const WaitObject* object) const { + return wait_objects_index.at(object->GetObjectId()); + } + + /** * Stops a thread, invalidating it from further use */ void Stop(); @@ -137,7 +149,16 @@ public: return tls_address; } - Core::ThreadContext context; + /** + * Returns whether this thread is waiting for all the objects in + * its wait list to become ready, as a result of a WaitSynchronizationN call + * with wait_all = true, or a ReplyAndReceive call. + */ + bool IsSleepingOnWaitAll() const { + return !wait_objects.empty(); + } + + ARM_Interface::ThreadContext context; u32 thread_id; @@ -154,16 +175,22 @@ public: VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread - bool waitsynch_waited; ///< Set to true if the last svcWaitSynch call caused the thread to wait - /// Mutexes currently held by this thread, which will be released when it exits. boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; - SharedPtr<Process> owner_process; ///< Process that owns this thread - std::vector<SharedPtr<WaitObject>> wait_objects; ///< Objects that the thread is waiting on - VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address - bool wait_all; ///< True if the thread is waiting on all objects before resuming - bool wait_set_output; ///< True if the output parameter should be set on thread wakeup + SharedPtr<Process> owner_process; ///< Process that owns this thread + + /// Objects that the thread is waiting on. + /// This is only populated when the thread should wait for all the objects to become ready. + std::vector<SharedPtr<WaitObject>> wait_objects; + + /// Mapping of Object ids to their position in the last waitlist that this object waited on. + boost::container::flat_map<int, s32> wait_objects_index; + + VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address + + /// True if the WaitSynchronizationN output parameter should be set on thread wakeup. + bool wait_set_output; std::string name; @@ -215,10 +242,9 @@ void WaitCurrentThread_Sleep(); * @param wait_objects Kernel objects that we are waiting on * @param wait_set_output If true, set the output parameter on thread wakeup (for * WaitSynchronizationN only) - * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) */ void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, - bool wait_set_output, bool wait_all); + bool wait_set_output); /** * Waits the current thread from an ArbitrateAddress call @@ -227,6 +253,11 @@ void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wa void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); /** + * Stops the current thread and removes it from the thread_list + */ +void ExitCurrentThread(); + +/** * Initialize threading */ void ThreadingInit(); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index eac181f4e..b50cf520d 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -60,14 +60,10 @@ void Timer::Set(s64 initial, s64 interval) { u64 initial_microseconds = initial / 1000; CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), timer_callback_event_type, callback_handle); - - HLE::Reschedule(__func__); } void Timer::Cancel() { CoreTiming::UnscheduleEvent(timer_callback_event_type, callback_handle); - - HLE::Reschedule(__func__); } void Timer::Clear() { diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 8d29117a8..53864a3a7 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -18,6 +18,7 @@ enum class ErrorDescription : u32 { Success = 0, WrongPermission = 46, OS_InvalidBufferDescriptor = 48, + MaxConnectionsReached = 52, WrongAddress = 53, FS_ArchiveNotMounted = 101, FS_FileNotFound = 112, diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index fe367aca5..36204db4d 100644 --- a/src/core/hle/service/ac_u.cpp +++ b/src/core/hle/service/ac_u.cpp @@ -2,14 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> + #include "common/logging/log.h" #include "core/hle/kernel/event.h" #include "core/hle/service/ac_u.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace AC_U - -namespace AC_U { +namespace Service { +namespace AC { struct ACConfig { std::array<u8, 0x200> data; @@ -31,7 +31,7 @@ static Kernel::SharedPtr<Kernel::Event> disconnect_event; * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void CreateDefaultConfig(Service::Interface* self) { +static void CreateDefaultConfig(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 ac_config_addr = cmd_buff[65]; @@ -56,7 +56,7 @@ static void CreateDefaultConfig(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void ConnectAsync(Service::Interface* self) { +static void ConnectAsync(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); connect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); @@ -77,7 +77,7 @@ static void ConnectAsync(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void GetConnectResult(Service::Interface* self) { +static void GetConnectResult(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -94,7 +94,7 @@ static void GetConnectResult(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void CloseAsync(Service::Interface* self) { +static void CloseAsync(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); if (ac_connected && disconnect_event) { @@ -120,7 +120,7 @@ static void CloseAsync(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void GetCloseResult(Service::Interface* self) { +static void GetCloseResult(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -134,7 +134,7 @@ static void GetCloseResult(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. */ -static void GetWifiStatus(Service::Interface* self) { +static void GetWifiStatus(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(purpasmart96): This function is only a stub, @@ -155,7 +155,7 @@ static void GetWifiStatus(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : Infra Priority */ -static void GetInfraPriority(Service::Interface* self) { +static void GetInfraPriority(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -177,7 +177,7 @@ static void GetInfraPriority(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : Infra Priority */ -static void SetRequestEulaVersion(Service::Interface* self) { +static void SetRequestEulaVersion(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 major = cmd_buff[1] & 0xFF; @@ -203,7 +203,7 @@ static void SetRequestEulaVersion(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void RegisterDisconnectEvent(Service::Interface* self) { +static void RegisterDisconnectEvent(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); disconnect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); @@ -221,7 +221,7 @@ static void RegisterDisconnectEvent(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : bool, is connected */ -static void IsConnected(Service::Interface* self) { +static void IsConnected(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -237,7 +237,7 @@ static void IsConnected(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetClientVersion(Service::Interface* self) { +static void SetClientVersion(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); const u32 version = cmd_buff[1]; @@ -271,10 +271,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00400042, SetClientVersion, "SetClientVersion"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +AC_U::AC_U() { Register(FunctionTable); ac_connected = false; @@ -284,10 +281,11 @@ Interface::Interface() { disconnect_event = nullptr; } -Interface::~Interface() { +AC_U::~AC_U() { close_event = nullptr; connect_event = nullptr; disconnect_event = nullptr; } -} // namespace +} // namespace AC +} // namespace Service diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h index 6592b21c9..573c32d7e 100644 --- a/src/core/hle/service/ac_u.h +++ b/src/core/hle/service/ac_u.h @@ -6,21 +6,18 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace AC_U +namespace Service { +namespace AC { -// socket service "ac:u" - -namespace AC_U { - -class Interface : public Service::Interface { +class AC_U final : public Interface { public: - Interface(); - ~Interface(); + AC_U(); + ~AC_U(); std::string GetPortName() const override { return "ac:u"; } }; -} // namespace +} // namespace AC +} // namespace Service diff --git a/src/core/hle/service/act/act.cpp b/src/core/hle/service/act/act.cpp new file mode 100644 index 000000000..9600036c0 --- /dev/null +++ b/src/core/hle/service/act/act.cpp @@ -0,0 +1,18 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/act/act.h" +#include "core/hle/service/act/act_a.h" +#include "core/hle/service/act/act_u.h" + +namespace Service { +namespace ACT { + +void Init() { + AddService(new ACT_A); + AddService(new ACT_U); +} + +} // namespace ACT +} // namespace Service diff --git a/src/core/hle/service/act/act.h b/src/core/hle/service/act/act.h new file mode 100644 index 000000000..1425291aa --- /dev/null +++ b/src/core/hle/service/act/act.h @@ -0,0 +1,14 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service { +namespace ACT { + +/// Initializes all ACT services +void Init(); + +} // namespace ACT +} // namespace Service diff --git a/src/core/hle/service/act/act_a.cpp b/src/core/hle/service/act/act_a.cpp new file mode 100644 index 000000000..5c523368f --- /dev/null +++ b/src/core/hle/service/act/act_a.cpp @@ -0,0 +1,30 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/act/act.h" +#include "core/hle/service/act/act_a.h" + +namespace Service { +namespace ACT { + +const Interface::FunctionInfo FunctionTable[] = { + // act:u shared commands + {0x00010084, nullptr, "Initialize"}, + {0x00020040, nullptr, "GetErrorCode"}, + {0x000600C2, nullptr, "GetAccountDataBlock"}, + {0x000B0042, nullptr, "AcquireEulaList"}, + {0x000D0040, nullptr, "GenerateUuid"}, + // act:a + {0x041300C2, nullptr, "UpdateMiiImage"}, + {0x041B0142, nullptr, "AgreeEula"}, + {0x04210042, nullptr, "UploadMii"}, + {0x04230082, nullptr, "ValidateMailAddress"}, +}; + +ACT_A::ACT_A() { + Register(FunctionTable); +} + +} // namespace ACT +} // namespace Service diff --git a/src/core/hle/service/act_a.h b/src/core/hle/service/act/act_a.h index 765cae644..e3adb03e5 100644 --- a/src/core/hle/service/act_a.h +++ b/src/core/hle/service/act/act_a.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace ACT_A +namespace Service { +namespace ACT { -namespace ACT_A { - -class Interface : public Service::Interface { +class ACT_A final : public Service::Interface { public: - Interface(); + ACT_A(); std::string GetPortName() const override { return "act:a"; } }; -} // namespace +} // namespace ACT +} // namespace Service diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act/act_u.cpp index 05de4d002..cf98aa1d6 100644 --- a/src/core/hle/service/act_u.cpp +++ b/src/core/hle/service/act/act_u.cpp @@ -2,25 +2,25 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/service/act_u.h" +#include "core/hle/service/act/act.h" +#include "core/hle/service/act/act_u.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace ACT_U - -namespace ACT_U { +namespace Service { +namespace ACT { const Interface::FunctionInfo FunctionTable[] = { + // clang-format off {0x00010084, nullptr, "Initialize"}, {0x00020040, nullptr, "GetErrorCode"}, {0x000600C2, nullptr, "GetAccountDataBlock"}, + {0x000B0042, nullptr, "AcquireEulaList"}, {0x000D0040, nullptr, "GenerateUuid"}, + // clang-format on }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +ACT_U::ACT_U() { Register(FunctionTable); } -} // namespace +} // namespace ACT +} // namespace Service diff --git a/src/core/hle/service/act_u.h b/src/core/hle/service/act/act_u.h index be41454a4..9d8538fbf 100644 --- a/src/core/hle/service/act_u.h +++ b/src/core/hle/service/act/act_u.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace ACT_U +namespace Service { +namespace ACT { -namespace ACT_U { - -class Interface : public Service::Interface { +class ACT_U final : public Interface { public: - Interface(); + ACT_U(); std::string GetPortName() const override { return "act:u"; } }; -} // namespace +} // namespace ACT +} // namespace Service diff --git a/src/core/hle/service/act_a.cpp b/src/core/hle/service/act_a.cpp deleted file mode 100644 index 3a775fa90..000000000 --- a/src/core/hle/service/act_a.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/act_a.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace ACT_A - -namespace ACT_A { - -const Interface::FunctionInfo FunctionTable[] = { - {0x041300C2, nullptr, "UpdateMiiImage"}, - {0x041B0142, nullptr, "AgreeEula"}, - {0x04210042, nullptr, "UploadMii"}, - {0x04230082, nullptr, "ValidateMailAddress"}, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { - Register(FunctionTable); -} - -} // namespace diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index f7a990d69..d344a622f 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -20,7 +20,7 @@ static std::array<u32, 3> am_titles_list_count = {0, 0, 0}; static u32 am_ticket_count = 0; static u32 am_ticket_list_count = 0; -void GetTitleCount(Service::Interface* self) { +void GetNumPrograms(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 media_type = cmd_buff[1] & 0xFF; @@ -81,7 +81,7 @@ void DeleteContents(Service::Interface* self) { media_type, title_id, am_content_count[media_type], content_ids_pointer); } -void GetTitleList(Service::Interface* self) { +void GetProgramList(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 media_type = cmd_buff[2] & 0xFF; @@ -97,7 +97,7 @@ void GetTitleList(Service::Interface* self) { media_type, am_titles_list_count[media_type], title_ids_output_pointer); } -void GetTitleInfo(Service::Interface* self) { +void GetProgramInfos(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 media_type = cmd_buff[1] & 0xFF; @@ -113,7 +113,7 @@ void GetTitleInfo(Service::Interface* self) { } void GetDataTitleInfos(Service::Interface* self) { - GetTitleInfo(self); + GetProgramInfos(self); LOG_WARNING(Service_AM, "(STUBBED) called"); } @@ -151,7 +151,7 @@ void DeleteTicket(Service::Interface* self) { LOG_WARNING(Service_AM, "(STUBBED) called title_id=0x%016" PRIx64 "", title_id); } -void GetTicketCount(Service::Interface* self) { +void GetNumTickets(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 5676cdd5f..9bc2ca305 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -11,7 +11,7 @@ class Interface; namespace AM { /** - * AM::GetTitleCount service function + * AM::GetNumPrograms service function * Gets the number of installed titles in the requested media type * Inputs: * 0 : Command header (0x00010040) @@ -20,7 +20,7 @@ namespace AM { * 1 : Result, 0 on success, otherwise error code * 2 : The number of titles in the requested media type */ -void GetTitleCount(Service::Interface* self); +void GetNumPrograms(Service::Interface* self); /** * AM::FindContentInfos service function @@ -62,7 +62,7 @@ void ListContentInfos(Service::Interface* self); void DeleteContents(Service::Interface* self); /** - * AM::GetTitleList service function + * AM::GetProgramList service function * Loads information about the desired number of titles from the desired media type into an array * Inputs: * 1 : Title count @@ -72,10 +72,10 @@ void DeleteContents(Service::Interface* self); * 1 : Result, 0 on success, otherwise error code * 2 : The number of titles loaded from the requested media type */ -void GetTitleList(Service::Interface* self); +void GetProgramList(Service::Interface* self); /** - * AM::GetTitleInfo service function + * AM::GetProgramInfos service function * Inputs: * 1 : u8 Mediatype * 2 : Total titles @@ -84,11 +84,11 @@ void GetTitleList(Service::Interface* self); * Outputs: * 1 : Result, 0 on success, otherwise error code */ -void GetTitleInfo(Service::Interface* self); +void GetProgramInfos(Service::Interface* self); /** * AM::GetDataTitleInfos service function - * Wrapper for AM::GetTitleInfo + * Wrapper for AM::GetProgramInfos * Inputs: * 1 : u8 Mediatype * 2 : Total titles @@ -135,12 +135,12 @@ void GetNumContentInfos(Service::Interface* self); void DeleteTicket(Service::Interface* self); /** - * AM::GetTicketCount service function + * AM::GetNumTickets service function * Outputs: * 1 : Result, 0 on success, otherwise error code - * 2 : Total titles + * 2 : Number of tickets */ -void GetTicketCount(Service::Interface* self); +void GetNumTickets(Service::Interface* self); /** * AM::GetTicketList service function diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp index bfc1ca6bd..218375c8f 100644 --- a/src/core/hle/service/am/am_app.cpp +++ b/src/core/hle/service/am/am_app.cpp @@ -14,9 +14,14 @@ const Interface::FunctionInfo FunctionTable[] = { {0x10030142, ListContentInfos, "ListContentInfos"}, {0x10040102, DeleteContents, "DeleteContents"}, {0x10050084, GetDataTitleInfos, "GetDataTitleInfos"}, + {0x10060080, nullptr, "GetNumDataTitleTickets"}, {0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"}, + {0x100801C2, nullptr, "GetItemRights"}, {0x100900C0, nullptr, "IsDataTitleInUse"}, {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"}, + {0x100B00C0, nullptr, "GetNumExistingContentInfos"}, + {0x100C0142, nullptr, "ListExistingContentInfos"}, + {0x100D0084, nullptr, "GetPatchTitleInfos"}, }; AM_APP_Interface::AM_APP_Interface() { diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp index 3a597a34c..f3cd1d23f 100644 --- a/src/core/hle/service/am/am_net.cpp +++ b/src/core/hle/service/am/am_net.cpp @@ -9,61 +9,116 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, GetTitleCount, "GetTitleCount"}, - {0x00020082, GetTitleList, "GetTitleList"}, - {0x00030084, GetTitleInfo, "GetTitleInfo"}, - {0x000400C0, nullptr, "DeleteApplicationTitle"}, - {0x000500C0, nullptr, "GetTitleProductCode"}, - {0x000600C0, nullptr, "GetTitleExtDataId"}, + {0x00010040, GetNumPrograms, "GetNumPrograms"}, + {0x00020082, GetProgramList, "GetProgramList"}, + {0x00030084, GetProgramInfos, "GetProgramInfos"}, + {0x000400C0, nullptr, "DeleteUserProgram"}, + {0x000500C0, nullptr, "GetProductCode"}, + {0x000600C0, nullptr, "GetStorageId"}, {0x00070080, DeleteTicket, "DeleteTicket"}, - {0x00080000, GetTicketCount, "GetTicketCount"}, + {0x00080000, GetNumTickets, "GetNumTickets"}, {0x00090082, GetTicketList, "GetTicketList"}, {0x000A0000, nullptr, "GetDeviceID"}, - {0x000D0084, nullptr, "GetPendingTitleInfo"}, - {0x000E00C0, nullptr, "DeletePendingTitle"}, - {0x00140040, nullptr, "FinalizePendingTitles"}, - {0x00150040, nullptr, "DeleteAllPendingTitles"}, + {0x000B0040, nullptr, "GetNumImportTitleContexts"}, + {0x000C0082, nullptr, "GetImportTitleContextList"}, + {0x000D0084, nullptr, "GetImportTitleContexts"}, + {0x000E00C0, nullptr, "DeleteImportTitleContext"}, + {0x000F00C0, nullptr, "GetNumImportContentContexts"}, + {0x00100102, nullptr, "GetImportContentContextList"}, + {0x00110104, nullptr, "GetImportContentContexts"}, + {0x00120102, nullptr, "DeleteImportContentContexts"}, + {0x00130040, nullptr, "NeedsCleanup"}, + {0x00140040, nullptr, "DoCleanup"}, + {0x00150040, nullptr, "DeleteAllImportContexts"}, + {0x00160000, nullptr, "DeleteAllTemporaryPrograms"}, + {0x00170044, nullptr, "ImportTwlBackupLegacy"}, {0x00180080, nullptr, "InitializeTitleDatabase"}, - {0x00190040, nullptr, "ReloadDBS"}, - {0x001A00C0, nullptr, "GetDSiWareExportSize"}, - {0x001B0144, nullptr, "ExportDSiWare"}, - {0x001C0084, nullptr, "ImportDSiWare"}, - {0x00230080, nullptr, "TitleIDListGetTotal2"}, - {0x002400C2, nullptr, "GetTitleIDList2"}, - {0x04010080, nullptr, "InstallFIRM"}, - {0x04020040, nullptr, "StartInstallCIADB0"}, - {0x04030000, nullptr, "StartInstallCIADB1"}, - {0x04040002, nullptr, "AbortCIAInstall"}, - {0x04050002, nullptr, "CloseCIAFinalizeInstall"}, - {0x04060002, nullptr, "CloseCIA"}, - {0x040700C2, nullptr, "FinalizeTitlesInstall"}, - {0x04080042, nullptr, "GetCiaFileInfo"}, - {0x040E00C2, nullptr, "InstallTitlesFinish"}, - {0x040F0000, nullptr, "InstallNATIVEFIRM"}, - {0x041000C0, nullptr, "DeleteTitle"}, - {0x04120000, nullptr, "Initialize"}, - {0x041700C0, nullptr, "MigrateAGBtoSAV"}, - {0x08010000, nullptr, "OpenTicket"}, - {0x08020002, nullptr, "TicketAbortInstall"}, - {0x08030002, nullptr, "TicketFinalizeInstall"}, - {0x08040100, nullptr, "InstallTitleBegin"}, - {0x08050000, nullptr, "InstallTitleAbort"}, - {0x080600C0, nullptr, "InstallTitleResume"}, - {0x08070000, nullptr, "InstallTitleAbortTMD"}, - {0x08080000, nullptr, "InstallTitleFinish"}, - {0x080A0000, nullptr, "OpenTMD"}, - {0x080B0002, nullptr, "TMDAbortInstall"}, - {0x080C0042, nullptr, "TMDFinalizeInstall"}, - {0x080E0040, nullptr, "OpenContentCreate"}, - {0x080F0002, nullptr, "ContentAbortInstall"}, - {0x08100040, nullptr, "OpenContentResume"}, - {0x08120002, nullptr, "ContentFinalizeInstall"}, - {0x08130000, nullptr, "GetTotalContents"}, - {0x08140042, nullptr, "GetContentIndexes"}, - {0x08150044, nullptr, "GetContentsInfo"}, - {0x08180042, nullptr, "GetCTCert"}, - {0x08190108, nullptr, "SetCertificates"}, - {0x081B00C2, nullptr, "InstallTitlesFinish"}, + {0x00190040, nullptr, "QueryAvailableTitleDatabase"}, + {0x001A00C0, nullptr, "CalcTwlBackupSize"}, + {0x001B0144, nullptr, "ExportTwlBackup"}, + {0x001C0084, nullptr, "ImportTwlBackup"}, + {0x001D0000, nullptr, "DeleteAllTwlUserPrograms"}, + {0x001E00C8, nullptr, "ReadTwlBackupInfo"}, + {0x001F0040, nullptr, "DeleteAllExpiredUserPrograms"}, + {0x00200000, nullptr, "GetTwlArchiveResourceInfo"}, + {0x00210042, nullptr, "GetPersonalizedTicketInfoList"}, + {0x00220080, nullptr, "DeleteAllImportContextsFiltered"}, + {0x00230080, nullptr, "GetNumImportTitleContextsFiltered"}, + {0x002400C2, nullptr, "GetImportTitleContextListFiltered"}, + {0x002500C0, nullptr, "CheckContentRights"}, + {0x00260044, nullptr, "GetTicketLimitInfos"}, + {0x00270044, nullptr, "GetDemoLaunchInfos"}, + {0x00280108, nullptr, "ReadTwlBackupInfoEx"}, + {0x00290082, nullptr, "DeleteUserProgramsAtomically"}, + {0x002A00C0, nullptr, "GetNumExistingContentInfosSystem"}, + {0x002B0142, nullptr, "ListExistingContentInfosSystem"}, + {0x002C0084, nullptr, "GetProgramInfosIgnorePlatform"}, + {0x002D00C0, nullptr, "CheckContentRightsIgnorePlatform"}, + {0x04010080, nullptr, "UpdateFirmwareTo"}, + {0x04020040, nullptr, "BeginImportProgram"}, + {0x04030000, nullptr, "BeginImportProgramTemporarily"}, + {0x04040002, nullptr, "CancelImportProgram"}, + {0x04050002, nullptr, "EndImportProgram"}, + {0x04060002, nullptr, "EndImportProgramWithoutCommit"}, + {0x040700C2, nullptr, "CommitImportPrograms"}, + {0x04080042, nullptr, "GetProgramInfoFromCia"}, + {0x04090004, nullptr, "GetSystemMenuDataFromCia"}, + {0x040A0002, nullptr, "GetDependencyListFromCia"}, + {0x040B0002, nullptr, "GetTransferSizeFromCia"}, + {0x040C0002, nullptr, "GetCoreVersionFromCia"}, + {0x040D0042, nullptr, "GetRequiredSizeFromCia"}, + {0x040E00C2, nullptr, "CommitImportProgramsAndUpdateFirmwareAuto"}, + {0x040F0000, nullptr, "UpdateFirmwareAuto"}, + {0x041000C0, nullptr, "DeleteProgram"}, + {0x04110044, nullptr, "GetTwlProgramListForReboot"}, + {0x04120000, nullptr, "GetSystemUpdaterMutex"}, + {0x04130002, nullptr, "GetMetaSizeFromCia"}, + {0x04140044, nullptr, "GetMetaDataFromCia"}, + {0x04150080, nullptr, "CheckDemoLaunchRights"}, + {0x041600C0, nullptr, "GetInternalTitleLocationInfo"}, + {0x041700C0, nullptr, "PerpetuateAgbSaveData"}, + {0x04180040, nullptr, "BeginImportProgramForOverWrite"}, + {0x04190000, nullptr, "BeginImportSystemProgram"}, + {0x08010000, nullptr, "BeginImportTicket"}, + {0x08020002, nullptr, "CancelImportTicket"}, + {0x08030002, nullptr, "EndImportTicket"}, + {0x08040100, nullptr, "BeginImportTitle"}, + {0x08050000, nullptr, "StopImportTitle"}, + {0x080600C0, nullptr, "ResumeImportTitle"}, + {0x08070000, nullptr, "CancelImportTitle"}, + {0x08080000, nullptr, "EndImportTitle"}, + {0x080900C2, nullptr, "CommitImportTitles"}, + {0x080A0000, nullptr, "BeginImportTmd"}, + {0x080B0002, nullptr, "CancelImportTmd"}, + {0x080C0042, nullptr, "EndImportTmd"}, + {0x080D0042, nullptr, "CreateImportContentContexts"}, + {0x080E0040, nullptr, "BeginImportContent"}, + {0x080F0002, nullptr, "StopImportContent"}, + {0x08100040, nullptr, "ResumeImportContent"}, + {0x08110002, nullptr, "CancelImportContent"}, + {0x08120002, nullptr, "EndImportContent"}, + {0x08130000, nullptr, "GetNumCurrentImportContentContexts"}, + {0x08140042, nullptr, "GetCurrentImportContentContextList"}, + {0x08150044, nullptr, "GetCurrentImportContentContexts"}, + {0x08160146, nullptr, "Sign"}, + {0x08170146, nullptr, "Verify"}, + {0x08180042, nullptr, "GetDeviceCert"}, + {0x08190108, nullptr, "ImportCertificates"}, + {0x081A0042, nullptr, "ImportCertificate"}, + {0x081B00C2, nullptr, "CommitImportTitlesAndUpdateFirmwareAuto"}, + {0x081C0100, nullptr, "DeleteTicketId"}, + {0x081D0080, nullptr, "GetNumTicketIds"}, + {0x081E0102, nullptr, "GetTicketIdList"}, + {0x081F0080, nullptr, "GetNumTicketsOfProgram"}, + {0x08200102, nullptr, "ListTicketInfos"}, + {0x08210142, nullptr, "GetRightsOnlyTicketData"}, + {0x08220000, nullptr, "GetNumCurrentContentInfos"}, + {0x08230044, nullptr, "FindCurrentContentInfos"}, + {0x08240082, nullptr, "ListCurrentContentInfos"}, + {0x08250102, nullptr, "CalculateContextRequiredSize"}, + {0x08260042, nullptr, "UpdateImportContentContexts"}, + {0x08270000, nullptr, "DeleteAllDemoLaunchInfos"}, + {0x082800C0, nullptr, "BeginImportTitleForOverWrite"}, }; AM_NET_Interface::AM_NET_Interface() { diff --git a/src/core/hle/service/am/am_sys.cpp b/src/core/hle/service/am/am_sys.cpp index a2268303c..949b3591d 100644 --- a/src/core/hle/service/am/am_sys.cpp +++ b/src/core/hle/service/am/am_sys.cpp @@ -9,27 +9,64 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, GetTitleCount, "GetTitleCount"}, - {0x00020082, GetTitleList, "GetTitleList"}, - {0x00030084, GetTitleInfo, "GetTitleInfo"}, - {0x000400C0, nullptr, "DeleteApplicationTitle"}, - {0x000500C0, nullptr, "GetTitleProductCode"}, - {0x000600C0, nullptr, "GetTitleExtDataId"}, + {0x00010040, GetNumPrograms, "GetNumPrograms"}, + {0x00020082, GetProgramList, "GetProgramList"}, + {0x00030084, GetProgramInfos, "GetProgramInfos"}, + {0x000400C0, nullptr, "DeleteUserProgram"}, + {0x000500C0, nullptr, "GetProductCode"}, + {0x000600C0, nullptr, "GetStorageId"}, {0x00070080, DeleteTicket, "DeleteTicket"}, - {0x00080000, GetTicketCount, "GetTicketCount"}, + {0x00080000, GetNumTickets, "GetNumTickets"}, {0x00090082, GetTicketList, "GetTicketList"}, {0x000A0000, nullptr, "GetDeviceID"}, - {0x000D0084, nullptr, "GetPendingTitleInfo"}, - {0x000E00C0, nullptr, "DeletePendingTitle"}, - {0x00140040, nullptr, "FinalizePendingTitles"}, - {0x00150040, nullptr, "DeleteAllPendingTitles"}, + {0x000B0040, nullptr, "GetNumImportTitleContexts"}, + {0x000C0082, nullptr, "GetImportTitleContextList"}, + {0x000D0084, nullptr, "GetImportTitleContexts"}, + {0x000E00C0, nullptr, "DeleteImportTitleContext"}, + {0x000F00C0, nullptr, "GetNumImportContentContexts"}, + {0x00100102, nullptr, "GetImportContentContextList"}, + {0x00110104, nullptr, "GetImportContentContexts"}, + {0x00120102, nullptr, "DeleteImportContentContexts"}, + {0x00130040, nullptr, "NeedsCleanup"}, + {0x00140040, nullptr, "DoCleanup"}, + {0x00150040, nullptr, "DeleteAllImportContexts"}, + {0x00160000, nullptr, "DeleteAllTemporaryPrograms"}, + {0x00170044, nullptr, "ImportTwlBackupLegacy"}, {0x00180080, nullptr, "InitializeTitleDatabase"}, - {0x00190040, nullptr, "ReloadDBS"}, - {0x001A00C0, nullptr, "GetDSiWareExportSize"}, - {0x001B0144, nullptr, "ExportDSiWare"}, - {0x001C0084, nullptr, "ImportDSiWare"}, - {0x00230080, nullptr, "GetPendingTitleCount"}, - {0x002400C2, nullptr, "GetPendingTitleList"}, + {0x00190040, nullptr, "QueryAvailableTitleDatabase"}, + {0x001A00C0, nullptr, "CalcTwlBackupSize"}, + {0x001B0144, nullptr, "ExportTwlBackup"}, + {0x001C0084, nullptr, "ImportTwlBackup"}, + {0x001D0000, nullptr, "DeleteAllTwlUserPrograms"}, + {0x001E00C8, nullptr, "ReadTwlBackupInfo"}, + {0x001F0040, nullptr, "DeleteAllExpiredUserPrograms"}, + {0x00200000, nullptr, "GetTwlArchiveResourceInfo"}, + {0x00210042, nullptr, "GetPersonalizedTicketInfoList"}, + {0x00220080, nullptr, "DeleteAllImportContextsFiltered"}, + {0x00230080, nullptr, "GetNumImportTitleContextsFiltered"}, + {0x002400C2, nullptr, "GetImportTitleContextListFiltered"}, + {0x002500C0, nullptr, "CheckContentRights"}, + {0x00260044, nullptr, "GetTicketLimitInfos"}, + {0x00270044, nullptr, "GetDemoLaunchInfos"}, + {0x00280108, nullptr, "ReadTwlBackupInfoEx"}, + {0x00290082, nullptr, "DeleteUserProgramsAtomically"}, + {0x002A00C0, nullptr, "GetNumExistingContentInfosSystem"}, + {0x002B0142, nullptr, "ListExistingContentInfosSystem"}, + {0x002C0084, nullptr, "GetProgramInfosIgnorePlatform"}, + {0x002D00C0, nullptr, "CheckContentRightsIgnorePlatform"}, + {0x100100C0, GetNumContentInfos, "GetNumContentInfos"}, + {0x10020104, FindContentInfos, "FindContentInfos"}, + {0x10030142, ListContentInfos, "ListContentInfos"}, + {0x10040102, DeleteContents, "DeleteContents"}, + {0x10050084, GetDataTitleInfos, "GetDataTitleInfos"}, + {0x10060080, nullptr, "GetNumDataTitleTickets"}, + {0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"}, + {0x100801C2, nullptr, "GetItemRights"}, + {0x100900C0, nullptr, "IsDataTitleInUse"}, + {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"}, + {0x100B00C0, nullptr, "GetNumExistingContentInfos"}, + {0x100C0142, nullptr, "ListExistingContentInfos"}, + {0x100D0084, nullptr, "GetPatchTitleInfos"}, }; AM_SYS_Interface::AM_SYS_Interface() { diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp index 151b5e42b..354d51610 100644 --- a/src/core/hle/service/am/am_u.cpp +++ b/src/core/hle/service/am/am_u.cpp @@ -9,40 +9,76 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, GetTitleCount, "GetTitleCount"}, - {0x00020082, GetTitleList, "GetTitleList"}, - {0x00030084, GetTitleInfo, "GetTitleInfo"}, - {0x000400C0, nullptr, "DeleteApplicationTitle"}, - {0x000500C0, nullptr, "GetTitleProductCode"}, - {0x000600C0, nullptr, "GetTitleExtDataId"}, + {0x00010040, GetNumPrograms, "GetNumPrograms"}, + {0x00020082, GetProgramList, "GetProgramList"}, + {0x00030084, GetProgramInfos, "GetProgramInfos"}, + {0x000400C0, nullptr, "DeleteUserProgram"}, + {0x000500C0, nullptr, "GetProductCode"}, + {0x000600C0, nullptr, "GetStorageId"}, {0x00070080, DeleteTicket, "DeleteTicket"}, - {0x00080000, GetTicketCount, "GetTicketCount"}, + {0x00080000, GetNumTickets, "GetNumTickets"}, {0x00090082, GetTicketList, "GetTicketList"}, {0x000A0000, nullptr, "GetDeviceID"}, - {0x000D0084, nullptr, "GetPendingTitleInfo"}, - {0x000E00C0, nullptr, "DeletePendingTitle"}, - {0x00140040, nullptr, "FinalizePendingTitles"}, - {0x00150040, nullptr, "DeleteAllPendingTitles"}, + {0x000B0040, nullptr, "GetNumImportTitleContexts"}, + {0x000C0082, nullptr, "GetImportTitleContextList"}, + {0x000D0084, nullptr, "GetImportTitleContexts"}, + {0x000E00C0, nullptr, "DeleteImportTitleContext"}, + {0x000F00C0, nullptr, "GetNumImportContentContexts"}, + {0x00100102, nullptr, "GetImportContentContextList"}, + {0x00110104, nullptr, "GetImportContentContexts"}, + {0x00120102, nullptr, "DeleteImportContentContexts"}, + {0x00130040, nullptr, "NeedsCleanup"}, + {0x00140040, nullptr, "DoCleanup"}, + {0x00150040, nullptr, "DeleteAllImportContexts"}, + {0x00160000, nullptr, "DeleteAllTemporaryPrograms"}, + {0x00170044, nullptr, "ImportTwlBackupLegacy"}, {0x00180080, nullptr, "InitializeTitleDatabase"}, - {0x00190040, nullptr, "ReloadDBS"}, - {0x001A00C0, nullptr, "GetDSiWareExportSize"}, - {0x001B0144, nullptr, "ExportDSiWare"}, - {0x001C0084, nullptr, "ImportDSiWare"}, - {0x00230080, nullptr, "TitleIDListGetTotal2"}, - {0x002400C2, nullptr, "GetTitleIDList2"}, - {0x04010080, nullptr, "InstallFIRM"}, - {0x04020040, nullptr, "StartInstallCIADB0"}, - {0x04030000, nullptr, "StartInstallCIADB1"}, - {0x04040002, nullptr, "AbortCIAInstall"}, - {0x04050002, nullptr, "CloseCIAFinalizeInstall"}, - {0x04060002, nullptr, "CloseCIA"}, - {0x040700C2, nullptr, "FinalizeTitlesInstall"}, - {0x04080042, nullptr, "GetCiaFileInfo"}, - {0x040E00C2, nullptr, "InstallTitlesFinish"}, - {0x040F0000, nullptr, "InstallNATIVEFIRM"}, - {0x041000C0, nullptr, "DeleteTitle"}, - {0x04120000, nullptr, "Initialize"}, - {0x041700C0, nullptr, "MigrateAGBtoSAV"}, + {0x00190040, nullptr, "QueryAvailableTitleDatabase"}, + {0x001A00C0, nullptr, "CalcTwlBackupSize"}, + {0x001B0144, nullptr, "ExportTwlBackup"}, + {0x001C0084, nullptr, "ImportTwlBackup"}, + {0x001D0000, nullptr, "DeleteAllTwlUserPrograms"}, + {0x001E00C8, nullptr, "ReadTwlBackupInfo"}, + {0x001F0040, nullptr, "DeleteAllExpiredUserPrograms"}, + {0x00200000, nullptr, "GetTwlArchiveResourceInfo"}, + {0x00210042, nullptr, "GetPersonalizedTicketInfoList"}, + {0x00220080, nullptr, "DeleteAllImportContextsFiltered"}, + {0x00230080, nullptr, "GetNumImportTitleContextsFiltered"}, + {0x002400C2, nullptr, "GetImportTitleContextListFiltered"}, + {0x002500C0, nullptr, "CheckContentRights"}, + {0x00260044, nullptr, "GetTicketLimitInfos"}, + {0x00270044, nullptr, "GetDemoLaunchInfos"}, + {0x00280108, nullptr, "ReadTwlBackupInfoEx"}, + {0x00290082, nullptr, "DeleteUserProgramsAtomically"}, + {0x002A00C0, nullptr, "GetNumExistingContentInfosSystem"}, + {0x002B0142, nullptr, "ListExistingContentInfosSystem"}, + {0x002C0084, nullptr, "GetProgramInfosIgnorePlatform"}, + {0x002D00C0, nullptr, "CheckContentRightsIgnorePlatform"}, + {0x04010080, nullptr, "UpdateFirmwareTo"}, + {0x04020040, nullptr, "BeginImportProgram"}, + {0x04030000, nullptr, "BeginImportProgramTemporarily"}, + {0x04040002, nullptr, "CancelImportProgram"}, + {0x04050002, nullptr, "EndImportProgram"}, + {0x04060002, nullptr, "EndImportProgramWithoutCommit"}, + {0x040700C2, nullptr, "CommitImportPrograms"}, + {0x04080042, nullptr, "GetProgramInfoFromCia"}, + {0x04090004, nullptr, "GetSystemMenuDataFromCia"}, + {0x040A0002, nullptr, "GetDependencyListFromCia"}, + {0x040B0002, nullptr, "GetTransferSizeFromCia"}, + {0x040C0002, nullptr, "GetCoreVersionFromCia"}, + {0x040D0042, nullptr, "GetRequiredSizeFromCia"}, + {0x040E00C2, nullptr, "CommitImportProgramsAndUpdateFirmwareAuto"}, + {0x040F0000, nullptr, "UpdateFirmwareAuto"}, + {0x041000C0, nullptr, "DeleteProgram"}, + {0x04110044, nullptr, "GetTwlProgramListForReboot"}, + {0x04120000, nullptr, "GetSystemUpdaterMutex"}, + {0x04130002, nullptr, "GetMetaSizeFromCia"}, + {0x04140044, nullptr, "GetMetaDataFromCia"}, + {0x04150080, nullptr, "CheckDemoLaunchRights"}, + {0x041600C0, nullptr, "GetInternalTitleLocationInfo"}, + {0x041700C0, nullptr, "PerpetuateAgbSaveData"}, + {0x04180040, nullptr, "BeginImportProgramForOverWrite"}, + {0x04190000, nullptr, "BeginImportSystemProgram"}, }; AM_U_Interface::AM_U_Interface() { diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 31e5e07b2..615fe31ea 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -458,12 +458,16 @@ void GetStartupArgument(Service::Interface* self) { return; } - LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x , " - "parameter_value=0x%08x", - startup_argument_type, parameter_size, Memory::Read32(cmd_buff[41])); + u32 addr = cmd_buff[65]; + if (addr && parameter_size) { + Memory::ZeroBlock(addr, parameter_size); + } + + LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x", + startup_argument_type, parameter_size); cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = (parameter_size > 0) ? 1 : 0; + cmd_buff[2] = 0; } void CheckNew3DSApp(Service::Interface* self) { diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 44dbd8757..80325361f 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -14,6 +14,9 @@ class Interface; namespace APT { +/// Each APT service can only have up to 2 sessions connected at the same time. +static const u32 MaxAPTSessions = 2; + /// Holds information about the parameters used in Send/Glance/ReceiveParameter struct MessageParameter { u32 sender_id = 0; @@ -407,9 +410,11 @@ void CancelLibraryApplet(Service::Interface* self); * Inputs: * 1 : Parameter Size (capped to 0x300) * 2 : StartupArgumentType + * 65 : Output buffer for startup argument * Outputs: * 0 : Return header - * 1 : u8, Exists (0 = does not exist, 1 = exists) + * 1 : Result of function, 0 on success, otherwise error code + * 2 : u8, Exists (0 = does not exist, 1 = exists) */ void GetStartupArgument(Service::Interface* self); diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index a7a0c8a41..62dc2d61d 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -9,37 +9,100 @@ namespace Service { namespace APT { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, GetLockHandle, "GetLockHandle?"}, - {0x00020080, Initialize, "Initialize?"}, - {0x00030040, Enable, "Enable?"}, - {0x00040040, nullptr, "Finalize?"}, + {0x00010040, GetLockHandle, "GetLockHandle"}, + {0x00020080, Initialize, "Initialize"}, + {0x00030040, Enable, "Enable"}, + {0x00040040, nullptr, "Finalize"}, {0x00050040, GetAppletManInfo, "GetAppletManInfo"}, {0x00060040, GetAppletInfo, "GetAppletInfo"}, + {0x00070000, nullptr, "GetLastSignaledAppletId"}, + {0x00080000, nullptr, "CountRegisteredApplet"}, {0x00090040, IsRegistered, "IsRegistered"}, + {0x000A0040, nullptr, "GetAttribute"}, {0x000B0040, InquireNotification, "InquireNotification"}, {0x000C0104, SendParameter, "SendParameter"}, {0x000D0080, ReceiveParameter, "ReceiveParameter"}, {0x000E0080, GlanceParameter, "GlanceParameter"}, {0x000F0100, CancelParameter, "CancelParameter"}, + {0x001000C2, nullptr, "DebugFunc"}, + {0x001100C0, nullptr, "MapProgramIdForDebug"}, + {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, + {0x00130000, nullptr, "GetPreparationState"}, + {0x00140040, nullptr, "SetPreparationState"}, {0x00150140, PrepareToStartApplication, "PrepareToStartApplication"}, {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"}, + {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, + {0x00190040, nullptr, "PrepareToStartSystemApplet"}, + {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, + {0x001B00C4, nullptr, "StartApplication"}, + {0x001C0000, nullptr, "WakeupApplication"}, + {0x001D0000, nullptr, "CancelApplication"}, {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, + {0x001F0084, nullptr, "StartSystemApplet"}, + {0x00200044, nullptr, "StartNewestHomeMenu"}, + {0x00210000, nullptr, "OrderToCloseApplication"}, + {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00230040, nullptr, "PrepareToJumpToApplication"}, + {0x00240044, nullptr, "JumpToApplication"}, + {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"}, + {0x00260000, nullptr, "PrepareToCloseSystemApplet"}, + {0x00270044, nullptr, "CloseApplication"}, + {0x00280044, nullptr, "CloseLibraryApplet"}, + {0x00290044, nullptr, "CloseSystemApplet"}, + {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, + {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, + {0x002C0044, nullptr, "JumpToHomeMenu"}, + {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, + {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, + {0x00300044, nullptr, "LeaveResidentApplet"}, + {0x00310100, nullptr, "PrepareToDoApplicationJump"}, + {0x00320084, nullptr, "DoApplicationJump"}, + {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, + {0x00340084, nullptr, "SendDeliverArg"}, + {0x00350080, nullptr, "ReceiveDeliverArg"}, + {0x00360040, nullptr, "LoadSysMenuArg"}, + {0x00370042, nullptr, "StoreSysMenuArg"}, + {0x00380040, nullptr, "PreloadResidentApplet"}, + {0x00390040, nullptr, "PrepareToStartResidentApplet"}, + {0x003A0044, nullptr, "StartResidentApplet"}, {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"}, + {0x003C0042, nullptr, "SendDspSleep"}, + {0x003D0042, nullptr, "SendDspWakeUp"}, {0x003E0080, nullptr, "ReplySleepQuery"}, - {0x00430040, NotifyToWait, "NotifyToWait?"}, - {0x00440000, GetSharedFont, "GetSharedFont?"}, - {0x004B00C2, AppletUtility, "AppletUtility?"}, + {0x003F0040, nullptr, "ReplySleepNotificationComplete"}, + {0x00400042, nullptr, "SendCaptureBufferInfo"}, + {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, + {0x00420080, nullptr, "SleepSystem"}, + {0x00430040, NotifyToWait, "NotifyToWait"}, + {0x00440000, GetSharedFont, "GetSharedFont"}, + {0x00450040, nullptr, "GetWirelessRebootInfo"}, + {0x00460104, nullptr, "Wrap"}, + {0x00470104, nullptr, "Unwrap"}, + {0x00480100, nullptr, "GetProgramInfo"}, + {0x00490180, nullptr, "Reboot"}, + {0x004A0040, nullptr, "GetCaptureInfo"}, + {0x004B00C2, AppletUtility, "AppletUtility"}, + {0x004C0000, nullptr, "SetFatalErrDispMode"}, + {0x004D0080, nullptr, "GetAppletProgramInfo"}, + {0x004E0000, nullptr, "HardwareResetAsync"}, {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, {0x00510080, GetStartupArgument, "GetStartupArgument"}, + {0x00520104, nullptr, "Wrap1"}, + {0x00530104, nullptr, "Unwrap1"}, {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"}, {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"}, + {0x00570044, nullptr, "WakeupApplication2"}, + {0x00580002, nullptr, "GetProgramID"}, {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, {0x01020000, CheckNew3DS, "CheckNew3DS"}, + {0x01040000, nullptr, "IsStandardMemoryLayout"}, + {0x01050100, nullptr, "IsTitleAllowed"}, }; -APT_A_Interface::APT_A_Interface() { +APT_A_Interface::APT_A_Interface() : Interface(MaxAPTSessions) { Register(FunctionTable); } diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index c4556a5de..effd23dce 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -94,12 +94,15 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00530104, nullptr, "Unwrap1"}, {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"}, {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"}, + {0x00570044, nullptr, "WakeupApplication2"}, {0x00580002, nullptr, "GetProgramID"}, {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, {0x01020000, CheckNew3DS, "CheckNew3DS"}, + {0x01040000, nullptr, "IsStandardMemoryLayout"}, + {0x01050100, nullptr, "IsTitleAllowed"}, }; -APT_S_Interface::APT_S_Interface() { +APT_S_Interface::APT_S_Interface() : Interface(MaxAPTSessions) { Register(FunctionTable); } diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index a731c39f6..e06084a1e 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -99,7 +99,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x01020000, CheckNew3DS, "CheckNew3DS"}, }; -APT_U_Interface::APT_U_Interface() { +APT_U_Interface::APT_U_Interface() : Interface(MaxAPTSessions) { Register(FunctionTable); } diff --git a/src/core/hle/service/boss/boss_p.cpp b/src/core/hle/service/boss/boss_p.cpp index dfee8d055..ee941e228 100644 --- a/src/core/hle/service/boss/boss_p.cpp +++ b/src/core/hle/service/boss/boss_p.cpp @@ -2,16 +2,81 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/boss/boss.h" #include "core/hle/service/boss/boss_p.h" namespace Service { namespace BOSS { -// Empty arrays are illegal -- commented out until an entry is added. -// const Interface::FunctionInfo FunctionTable[] = { }; +const Interface::FunctionInfo FunctionTable[] = { + // boss:u shared commands + {0x00010082, InitializeSession, "InitializeSession"}, + {0x00020100, RegisterStorage, "RegisterStorage"}, + {0x00030000, UnregisterStorage, "UnregisterStorage"}, + {0x00040000, GetStorageInfo, "GetStorageInfo"}, + {0x00050042, RegisterPrivateRootCa, "RegisterPrivateRootCa"}, + {0x00060084, RegisterPrivateClientCert, "RegisterPrivateClientCert"}, + {0x00070000, GetNewArrivalFlag, "GetNewArrivalFlag"}, + {0x00080002, RegisterNewArrivalEvent, "RegisterNewArrivalEvent"}, + {0x00090040, SetOptoutFlag, "SetOptoutFlag"}, + {0x000A0000, GetOptoutFlag, "GetOptoutFlag"}, + {0x000B00C2, RegisterTask, "RegisterTask"}, + {0x000C0082, UnregisterTask, "UnregisterTask"}, + {0x000D0082, ReconfigureTask, "ReconfigureTask"}, + {0x000E0000, GetTaskIdList, "GetTaskIdList"}, + {0x000F0042, GetStepIdList, "GetStepIdList"}, + {0x00100102, GetNsDataIdList, "GetNsDataIdList"}, + {0x00110102, GetOwnNsDataIdList, "GetOwnNsDataIdList"}, + {0x00120102, GetNewDataNsDataIdList, "GetNewDataNsDataIdList"}, + {0x00130102, GetOwnNewDataNsDataIdList, "GetOwnNewDataNsDataIdList"}, + {0x00140082, SendProperty, "SendProperty"}, + {0x00150042, SendPropertyHandle, "SendPropertyHandle"}, + {0x00160082, ReceiveProperty, "ReceiveProperty"}, + {0x00170082, UpdateTaskInterval, "UpdateTaskInterval"}, + {0x00180082, UpdateTaskCount, "UpdateTaskCount"}, + {0x00190042, GetTaskInterval, "GetTaskInterval"}, + {0x001A0042, GetTaskCount, "GetTaskCount"}, + {0x001B0042, GetTaskServiceStatus, "GetTaskServiceStatus"}, + {0x001C0042, StartTask, "StartTask"}, + {0x001D0042, StartTaskImmediate, "StartTaskImmediate"}, + {0x001E0042, CancelTask, "CancelTask"}, + {0x001F0000, GetTaskFinishHandle, "GetTaskFinishHandle"}, + {0x00200082, GetTaskState, "GetTaskState"}, + {0x00210042, GetTaskResult, "GetTaskResult"}, + {0x00220042, GetTaskCommErrorCode, "GetTaskCommErrorCode"}, + {0x002300C2, GetTaskStatus, "GetTaskStatus"}, + {0x00240082, GetTaskError, "GetTaskError"}, + {0x00250082, GetTaskInfo, "GetTaskInfo"}, + {0x00260040, DeleteNsData, "DeleteNsData"}, + {0x002700C2, GetNsDataHeaderInfo, "GetNsDataHeaderInfo"}, + {0x00280102, ReadNsData, "ReadNsData"}, + {0x00290080, SetNsDataAdditionalInfo, "SetNsDataAdditionalInfo"}, + {0x002A0040, GetNsDataAdditionalInfo, "GetNsDataAdditionalInfo"}, + {0x002B0080, SetNsDataNewFlag, "SetNsDataNewFlag"}, + {0x002C0040, GetNsDataNewFlag, "GetNsDataNewFlag"}, + {0x002D0040, GetNsDataLastUpdate, "GetNsDataLastUpdate"}, + {0x002E0040, GetErrorCode, "GetErrorCode"}, + {0x002F0140, RegisterStorageEntry, "RegisterStorageEntry"}, + {0x00300000, GetStorageEntryInfo, "GetStorageEntryInfo"}, + {0x00310100, SetStorageOption, "SetStorageOption"}, + {0x00320000, GetStorageOption, "GetStorageOption"}, + {0x00330042, StartBgImmediate, "StartBgImmediate"}, + {0x00340042, GetTaskActivePriority, "GetTaskActivePriority"}, + {0x003500C2, RegisterImmediateTask, "RegisterImmediateTask"}, + {0x00360084, SetTaskQuery, "SetTaskQuery"}, + {0x00370084, GetTaskQuery, "GetTaskQuery"}, + // boss:p + {0x04040080, nullptr, "GetAppNewFlag"}, + {0x04130082, nullptr, "SendPropertyPrivileged"}, + {0x041500C0, nullptr, "DeleteNsDataPrivileged"}, + {0x04160142, nullptr, "GetNsDataHeaderInfoPrivileged"}, + {0x04170182, nullptr, "ReadNsDataPrivileged"}, + {0x041A0100, nullptr, "SetNsDataNewFlagPrivileged"}, + {0x041B00C0, nullptr, "GetNsDataNewFlagPrivileged"}, +}; BOSS_P_Interface::BOSS_P_Interface() { - // Register(FunctionTable); + Register(FunctionTable); } } // namespace BOSS diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index 515b344e6..eb04273db 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -5,6 +5,7 @@ #include "common/logging/log.h" #include "core/hle/kernel/event.h" #include "core/hle/service/cecd/cecd.h" +#include "core/hle/service/cecd/cecd_ndm.h" #include "core/hle/service/cecd/cecd_s.h" #include "core/hle/service/cecd/cecd_u.h" #include "core/hle/service/service.h" @@ -43,12 +44,13 @@ void GetChangeStateEventHandle(Service::Interface* self) { } void Init() { - AddService(new CECD_S_Interface); - AddService(new CECD_U_Interface); + AddService(new CECD_NDM); + AddService(new CECD_S); + AddService(new CECD_U); - cecinfo_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD_U::cecinfo_event"); + cecinfo_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD::cecinfo_event"); change_state_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD_U::change_state_event"); + Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD::change_state_event"); } void Shutdown() { diff --git a/src/core/hle/service/cecd/cecd_ndm.cpp b/src/core/hle/service/cecd/cecd_ndm.cpp new file mode 100644 index 000000000..7baf93750 --- /dev/null +++ b/src/core/hle/service/cecd/cecd_ndm.cpp @@ -0,0 +1,23 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/cecd/cecd.h" +#include "core/hle/service/cecd/cecd_ndm.h" + +namespace Service { +namespace CECD { + +static const Interface::FunctionInfo FunctionTable[] = { + {0x00010000, nullptr, "Initialize"}, + {0x00020000, nullptr, "Deinitialize"}, + {0x00030000, nullptr, "ResumeDaemon"}, + {0x00040040, nullptr, "SuspendDaemon"}, +}; + +CECD_NDM::CECD_NDM() { + Register(FunctionTable); +} + +} // namespace CECD +} // namespace Service diff --git a/src/core/hle/service/cecd/cecd_ndm.h b/src/core/hle/service/cecd/cecd_ndm.h new file mode 100644 index 000000000..2e2e50ada --- /dev/null +++ b/src/core/hle/service/cecd/cecd_ndm.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace CECD { + +class CECD_NDM : public Interface { +public: + CECD_NDM(); + + std::string GetPortName() const override { + return "cecd:ndm"; + } +}; + +} // namespace CECD +} // namespace Service diff --git a/src/core/hle/service/cecd/cecd_s.cpp b/src/core/hle/service/cecd/cecd_s.cpp index 7477b9320..eacda7d41 100644 --- a/src/core/hle/service/cecd/cecd_s.cpp +++ b/src/core/hle/service/cecd/cecd_s.cpp @@ -2,16 +2,34 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/cecd/cecd.h" #include "core/hle/service/cecd/cecd_s.h" namespace Service { namespace CECD { -// Empty arrays are illegal -- commented out until an entry is added. -// const Interface::FunctionInfo FunctionTable[] = { }; +static const Interface::FunctionInfo FunctionTable[] = { + // cecd:u shared commands + {0x000100C2, nullptr, "OpenRawFile"}, + {0x00020042, nullptr, "ReadRawFile"}, + {0x00030104, nullptr, "ReadMessage"}, + {0x00040106, nullptr, "ReadMessageWithHMAC"}, + {0x00050042, nullptr, "WriteRawFile"}, + {0x00060104, nullptr, "WriteMessage"}, + {0x00070106, nullptr, "WriteMessageWithHMAC"}, + {0x00080102, nullptr, "Delete"}, + {0x000A00C4, nullptr, "GetSystemInfo"}, + {0x000B0040, nullptr, "RunCommand"}, + {0x000C0040, nullptr, "RunCommandAlt"}, + {0x000E0000, GetCecStateAbbreviated, "GetCecStateAbbreviated"}, + {0x000F0000, GetCecInfoEventHandle, "GetCecInfoEventHandle"}, + {0x00100000, GetChangeStateEventHandle, "GetChangeStateEventHandle"}, + {0x00110104, nullptr, "OpenAndWrite"}, + {0x00120104, nullptr, "OpenAndRead"}, +}; -CECD_S_Interface::CECD_S_Interface() { - // Register(FunctionTable); +CECD_S::CECD_S() { + Register(FunctionTable); } } // namespace CECD diff --git a/src/core/hle/service/cecd/cecd_s.h b/src/core/hle/service/cecd/cecd_s.h index df5c01849..ab6c6789a 100644 --- a/src/core/hle/service/cecd/cecd_s.h +++ b/src/core/hle/service/cecd/cecd_s.h @@ -9,9 +9,9 @@ namespace Service { namespace CECD { -class CECD_S_Interface : public Interface { +class CECD_S : public Interface { public: - CECD_S_Interface(); + CECD_S(); std::string GetPortName() const override { return "cecd:s"; diff --git a/src/core/hle/service/cecd/cecd_u.cpp b/src/core/hle/service/cecd/cecd_u.cpp index 4b747de7b..3ed864f0b 100644 --- a/src/core/hle/service/cecd/cecd_u.cpp +++ b/src/core/hle/service/cecd/cecd_u.cpp @@ -9,13 +9,26 @@ namespace Service { namespace CECD { static const Interface::FunctionInfo FunctionTable[] = { + // cecd:u shared commands + {0x000100C2, nullptr, "OpenRawFile"}, + {0x00020042, nullptr, "ReadRawFile"}, + {0x00030104, nullptr, "ReadMessage"}, + {0x00040106, nullptr, "ReadMessageWithHMAC"}, + {0x00050042, nullptr, "WriteRawFile"}, + {0x00060104, nullptr, "WriteMessage"}, + {0x00070106, nullptr, "WriteMessageWithHMAC"}, + {0x00080102, nullptr, "Delete"}, + {0x000A00C4, nullptr, "GetSystemInfo"}, + {0x000B0040, nullptr, "RunCommand"}, + {0x000C0040, nullptr, "RunCommandAlt"}, {0x000E0000, GetCecStateAbbreviated, "GetCecStateAbbreviated"}, {0x000F0000, GetCecInfoEventHandle, "GetCecInfoEventHandle"}, {0x00100000, GetChangeStateEventHandle, "GetChangeStateEventHandle"}, - {0x00120104, nullptr, "ReadSavedData"}, + {0x00110104, nullptr, "OpenAndWrite"}, + {0x00120104, nullptr, "OpenAndRead"}, }; -CECD_U_Interface::CECD_U_Interface() { +CECD_U::CECD_U() { Register(FunctionTable); } diff --git a/src/core/hle/service/cecd/cecd_u.h b/src/core/hle/service/cecd/cecd_u.h index 394030ffc..16e874ff5 100644 --- a/src/core/hle/service/cecd/cecd_u.h +++ b/src/core/hle/service/cecd/cecd_u.h @@ -9,9 +9,9 @@ namespace Service { namespace CECD { -class CECD_U_Interface : public Interface { +class CECD_U : public Interface { public: - CECD_U_Interface(); + CECD_U(); std::string GetPortName() const override { return "cecd:u"; diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 5e340f4c8..0bf59eb76 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -12,6 +12,7 @@ #include "core/hle/result.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_i.h" +#include "core/hle/service/cfg/cfg_nor.h" #include "core/hle/service/cfg/cfg_s.h" #include "core/hle/service/cfg/cfg_u.h" #include "core/hle/service/fs/archive.h" @@ -574,9 +575,10 @@ ResultCode LoadConfigNANDSaveFile() { } void Init() { - AddService(new CFG_I_Interface); - AddService(new CFG_S_Interface); - AddService(new CFG_U_Interface); + AddService(new CFG_I); + AddService(new CFG_NOR); + AddService(new CFG_S); + AddService(new CFG_U); LoadConfigNANDSaveFile(); diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index 2ff52c8b8..e8db0fc42 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp @@ -20,6 +20,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00080080, nullptr, "GoThroughTable"}, {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, + {0x000B0000, nullptr, "IsFangateSupported"}, // cfg:i {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"}, @@ -55,7 +56,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x08180042, nullptr, "SecureInfoGetSerialNo"}, }; -CFG_I_Interface::CFG_I_Interface() { +CFG_I::CFG_I() { Register(FunctionTable); } diff --git a/src/core/hle/service/cfg/cfg_i.h b/src/core/hle/service/cfg/cfg_i.h index d0a2cce39..8cfd47633 100644 --- a/src/core/hle/service/cfg/cfg_i.h +++ b/src/core/hle/service/cfg/cfg_i.h @@ -9,9 +9,9 @@ namespace Service { namespace CFG { -class CFG_I_Interface : public Service::Interface { +class CFG_I final : public Interface { public: - CFG_I_Interface(); + CFG_I(); std::string GetPortName() const override { return "cfg:i"; diff --git a/src/core/hle/service/cfg/cfg_nor.cpp b/src/core/hle/service/cfg/cfg_nor.cpp new file mode 100644 index 000000000..4ce02d115 --- /dev/null +++ b/src/core/hle/service/cfg/cfg_nor.cpp @@ -0,0 +1,23 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/cfg/cfg.h" +#include "core/hle/service/cfg/cfg_nor.h" + +namespace Service { +namespace CFG { + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010040, nullptr, "Initialize"}, + {0x00020000, nullptr, "Shutdown"}, + {0x00050082, nullptr, "ReadData"}, + {0x00060082, nullptr, "WriteData"}, +}; + +CFG_NOR::CFG_NOR() { + Register(FunctionTable); +} + +} // namespace CFG +} // namespace Service diff --git a/src/core/hle/service/cfg/cfg_nor.h b/src/core/hle/service/cfg/cfg_nor.h new file mode 100644 index 000000000..c337718e7 --- /dev/null +++ b/src/core/hle/service/cfg/cfg_nor.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace CFG { + +class CFG_NOR final : public Interface { +public: + CFG_NOR(); + + std::string GetPortName() const override { + return "cfg:nor"; + } +}; + +} // namespace CFG +} // namespace Service diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index eed26dec7..9386fe33d 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -20,6 +20,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00080080, nullptr, "GoThroughTable"}, {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, + {0x000B0000, nullptr, "IsFangateSupported"}, // cfg:s {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"}, @@ -32,7 +33,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x04090000, nullptr, "UpdateConfigBlk00040003"}, }; -CFG_S_Interface::CFG_S_Interface() { +CFG_S::CFG_S() { Register(FunctionTable); } diff --git a/src/core/hle/service/cfg/cfg_s.h b/src/core/hle/service/cfg/cfg_s.h index 5568d6485..99fea46ee 100644 --- a/src/core/hle/service/cfg/cfg_s.h +++ b/src/core/hle/service/cfg/cfg_s.h @@ -9,9 +9,9 @@ namespace Service { namespace CFG { -class CFG_S_Interface : public Service::Interface { +class CFG_S final : public Interface { public: - CFG_S_Interface(); + CFG_S(); std::string GetPortName() const override { return "cfg:s"; diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index f28217134..7b66fee22 100644 --- a/src/core/hle/service/cfg/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp @@ -20,9 +20,10 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00080080, nullptr, "GoThroughTable"}, {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, + {0x000B0000, nullptr, "IsFangateSupported"}, }; -CFG_U_Interface::CFG_U_Interface() { +CFG_U::CFG_U() { Register(FunctionTable); } diff --git a/src/core/hle/service/cfg/cfg_u.h b/src/core/hle/service/cfg/cfg_u.h index 5303d8ac6..fc7844714 100644 --- a/src/core/hle/service/cfg/cfg_u.h +++ b/src/core/hle/service/cfg/cfg_u.h @@ -9,9 +9,9 @@ namespace Service { namespace CFG { -class CFG_U_Interface : public Service::Interface { +class CFG_U final : public Interface { public: - CFG_U_Interface(); + CFG_U(); std::string GetPortName() const override { return "cfg:u"; diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 20c759ad7..6cf62f9bc 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp @@ -4,85 +4,137 @@ #include <cstring> #include "common/alignment.h" -#include "core/hle/hle.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/csnd_snd.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace CSND_SND +namespace Service { +namespace CSND { -namespace CSND_SND { - -const Interface::FunctionInfo FunctionTable[] = { - {0x00010140, Initialize, "Initialize"}, - {0x00020000, Shutdown, "Shutdown"}, - {0x00030040, ExecuteType0Commands, "ExecuteType0Commands"}, - {0x00040080, nullptr, "ExecuteType1Commands"}, - {0x00050000, AcquireSoundChannels, "AcquireSoundChannels"}, - {0x00060000, nullptr, "ReleaseSoundChannels"}, - {0x00070000, nullptr, "AcquireCaptureDevice"}, - {0x00080040, nullptr, "ReleaseCaptureDevice"}, - {0x00090082, nullptr, "FlushDataCache"}, - {0x000A0082, nullptr, "StoreDataCache"}, - {0x000B0082, nullptr, "InvalidateDataCache"}, - {0x000C0000, nullptr, "Reset"}, +struct Type0Command { + // command id and next command offset + u32 command_id; + u32 finished; + u32 flags; + u8 parameters[20]; }; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { - Register(FunctionTable); -} +static_assert(sizeof(Type0Command) == 0x20, "Type0Command structure size is wrong"); static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr; static Kernel::SharedPtr<Kernel::Mutex> mutex = nullptr; -void Initialize(Service::Interface* self) { +/** + * CSND_SND::Initialize service function + * Inputs: + * 0 : Header Code[0x00010140] + * 1 : Shared memory block size, for mem-block creation + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Handle-list header + * 3 : Mutex handle + * 4 : Shared memory block handle + */ +static void Initialize(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 size = Common::AlignUp(cmd_buff[1], Memory::PAGE_SIZE); + using Kernel::MemoryPermission; shared_memory = Kernel::SharedMemory::Create(nullptr, size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "CSND:SharedMemory"); - mutex = Kernel::Mutex::Create(false); + mutex = Kernel::Mutex::Create(false, "CSND:mutex"); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = IPC::CopyHandleDesc(2); cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom(); cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom(); + + LOG_WARNING(Service_CSND, "(STUBBED) called"); } -void ExecuteType0Commands(Service::Interface* self) { - u32* const cmd_buff = Kernel::GetCommandBuffer(); - u8* const ptr = shared_memory->GetPointer(cmd_buff[1]); +/** + * CSND_SND::Shutdown service function + * Inputs: + * 0 : Header Code[0x00020000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void Shutdown(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); - if (shared_memory != nullptr && ptr != nullptr) { - Type0Command command; - std::memcpy(&command, ptr, sizeof(Type0Command)); + shared_memory = nullptr; + mutex = nullptr; - LOG_WARNING(Service, "(STUBBED) CSND_SND::ExecuteType0Commands"); - command.finished |= 1; - cmd_buff[1] = 0; + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_CSND, "(STUBBED) called"); +} - std::memcpy(ptr, &command, sizeof(Type0Command)); - } else { +/** + * CSND_SND::ExecuteCommands service function + * Inputs: + * 0 : Header Code[0x00030040] + * 1 : Command offset in shared memory. + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Available channel bit mask + */ +static void ExecuteCommands(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + if (shared_memory == nullptr) { cmd_buff[1] = 1; + LOG_ERROR(Service_CSND, "called, shared memory not allocated"); + return; } + + VAddr addr = cmd_buff[1]; + u8* ptr = shared_memory->GetPointer(addr); + + Type0Command command; + std::memcpy(&command, ptr, sizeof(Type0Command)); + command.finished |= 1; + std::memcpy(ptr, &command, sizeof(Type0Command)); + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CSND, "(STUBBED) called, addr=0x%08X", addr); } -void AcquireSoundChannels(Service::Interface* self) { +/** + * CSND_SND::AcquireSoundChannels service function + * Inputs: + * 0 : Header Code[0x00050000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Available channel bit mask + */ +static void AcquireSoundChannels(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - cmd_buff[1] = 0; + cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0xFFFFFF00; + LOG_WARNING(Service_CSND, "(STUBBED) called"); } -void Shutdown(Service::Interface* self) { - shared_memory = nullptr; - mutex = nullptr; +const Interface::FunctionInfo FunctionTable[] = { + {0x00010140, Initialize, "Initialize"}, + {0x00020000, Shutdown, "Shutdown"}, + {0x00030040, ExecuteCommands, "ExecuteCommands"}, + {0x00040080, nullptr, "ExecuteType1Commands"}, + {0x00050000, AcquireSoundChannels, "AcquireSoundChannels"}, + {0x00060000, nullptr, "ReleaseSoundChannels"}, + {0x00070000, nullptr, "AcquireCaptureDevice"}, + {0x00080040, nullptr, "ReleaseCaptureDevice"}, + {0x00090082, nullptr, "FlushDataCache"}, + {0x000A0082, nullptr, "StoreDataCache"}, + {0x000B0082, nullptr, "InvalidateDataCache"}, + {0x000C0000, nullptr, "Reset"}, +}; + +CSND_SND::CSND_SND() { + Register(FunctionTable); } -} // namespace +} // namespace CSND +} // namespace Service diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h index a146d116b..ca6d4513e 100644 --- a/src/core/hle/service/csnd_snd.h +++ b/src/core/hle/service/csnd_snd.h @@ -6,31 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace CSND_SND +namespace Service { +namespace CSND { -namespace CSND_SND { - -class Interface : public Service::Interface { +class CSND_SND final : public Interface { public: - Interface(); + CSND_SND(); std::string GetPortName() const override { return "csnd:SND"; } }; -struct Type0Command { - // command id and next command offset - u32 command_id; - u32 finished; - u32 flags; - u8 parameters[20]; -}; - -void Initialize(Service::Interface* self); -void ExecuteType0Commands(Service::Interface* self); -void AcquireSoundChannels(Service::Interface* self); -void Shutdown(Service::Interface* self); - -} // namespace +} // namespace CSND +} // namespace Service diff --git a/src/core/hle/service/dlp/dlp.h b/src/core/hle/service/dlp/dlp.h index ec2fe46e8..3185fe322 100644 --- a/src/core/hle/service/dlp/dlp.h +++ b/src/core/hle/service/dlp/dlp.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + namespace Service { namespace DLP { diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp index 49d5b8d1c..25c07f401 100644 --- a/src/core/hle/service/dlp/dlp_srvr.cpp +++ b/src/core/hle/service/dlp/dlp_srvr.cpp @@ -22,7 +22,14 @@ static void unk_0x000E0040(Interface* self) { const Interface::FunctionInfo FunctionTable[] = { {0x00010183, nullptr, "Initialize"}, {0x00020000, nullptr, "Finalize"}, + {0x00030000, nullptr, "GetServerState"}, + {0x00050080, nullptr, "StartAccepting"}, + {0x00070000, nullptr, "StartDistribution"}, {0x000800C0, nullptr, "SendWirelessRebootPassphrase"}, + {0x00090040, nullptr, "AcceptClient"}, + {0x000B0042, nullptr, "GetConnectingClients"}, + {0x000C0040, nullptr, "GetClientInfo"}, + {0x000D0040, nullptr, "GetClientState"}, {0x000E0040, unk_0x000E0040, "unk_0x000E0040"}, }; diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index a15aa3696..fe8a6c2d6 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -12,9 +12,7 @@ using DspPipe = DSP::HLE::DspPipe; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace DSP_DSP - +namespace Service { namespace DSP_DSP { static Kernel::SharedPtr<Kernel::Event> semaphore_event; @@ -582,4 +580,5 @@ Interface::~Interface() { interrupt_events = {}; } -} // namespace +} // namespace DSP_DSP +} // namespace Service diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index 3e97da6eb..691d6f716 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h @@ -13,12 +13,10 @@ enum class DspPipe; } } -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace DSP_DSP - +namespace Service { namespace DSP_DSP { -class Interface : public Service::Interface { +class Interface final : public Service::Interface { public: Interface(); ~Interface() override; @@ -35,3 +33,4 @@ public: void SignalPipeInterrupt(DSP::HLE::DspPipe pipe); } // namespace DSP_DSP +} // namespace Service diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 9905757c7..cd0a1a598 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -13,10 +13,8 @@ #include "core/hle/result.h" #include "core/hle/service/err_f.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace ERR_F - -namespace ERR_F { +namespace Service { +namespace ERR { enum class FatalErrType : u32 { Generic = 0, @@ -167,7 +165,7 @@ static void LogGenericInfo(const ErrInfo::ErrInfoCommon& errinfo_common) { * 0 : Header code * 1 : Result code */ -static void ThrowFatalError(Service::Interface* self) { +static void ThrowFatalError(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); LOG_CRITICAL(Service_ERR, "Fatal error"); @@ -256,11 +254,9 @@ const Interface::FunctionInfo FunctionTable[] = { // clang-format on }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +ERR_F::ERR_F() { Register(FunctionTable); } -} // namespace +} // namespace ERR +} // namespace Service diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h index 892d8af9b..5b27fc871 100644 --- a/src/core/hle/service/err_f.h +++ b/src/core/hle/service/err_f.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace ERR_F +namespace Service { +namespace ERR { -namespace ERR_F { - -class Interface : public Service::Interface { +class ERR_F final : public Interface { public: - Interface(); + ERR_F(); std::string GetPortName() const override { return "err:f"; } }; -} // namespace +} // namespace ERR +} // namespace Service diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index bef75f5df..6cddc1fdb 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -23,7 +23,7 @@ #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/kernel/client_session.h" #include "core/hle/result.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" @@ -45,9 +45,7 @@ struct hash<Service::FS::ArchiveIdCode> { }; } -/// TODO(Subv): Confirm length of these strings -const std::string SYSTEM_ID = "00000000000000000000000000000000"; -const std::string SDCARD_ID = "00000000000000000000000000000000"; +static constexpr Kernel::Handle INVALID_HANDLE{}; namespace Service { namespace FS { @@ -93,7 +91,7 @@ File::File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& File::~File() {} -ResultVal<bool> File::SyncRequest() { +void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { u32* cmd_buff = Kernel::GetCommandBuffer(); FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); switch (cmd) { @@ -103,8 +101,8 @@ ResultVal<bool> File::SyncRequest() { u64 offset = cmd_buff[1] | ((u64)cmd_buff[2]) << 32; u32 length = cmd_buff[3]; u32 address = cmd_buff[5]; - LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", - GetTypeName().c_str(), GetName().c_str(), offset, length, address); + LOG_TRACE(Service_FS, "Read %s: offset=0x%llx length=%d address=0x%x", GetName().c_str(), + offset, length, address); if (offset + length > backend->GetSize()) { LOG_ERROR(Service_FS, @@ -116,7 +114,7 @@ ResultVal<bool> File::SyncRequest() { ResultVal<size_t> read = backend->Read(offset, data.size(), data.data()); if (read.Failed()) { cmd_buff[1] = read.Code().raw; - return read.Code(); + return; } Memory::WriteBlock(address, data.data(), *read); cmd_buff[2] = static_cast<u32>(*read); @@ -129,22 +127,22 @@ ResultVal<bool> File::SyncRequest() { u32 length = cmd_buff[3]; u32 flush = cmd_buff[4]; 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); + LOG_TRACE(Service_FS, "Write %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", + GetName().c_str(), offset, length, address, flush); std::vector<u8> data(length); Memory::ReadBlock(address, data.data(), data.size()); ResultVal<size_t> written = backend->Write(offset, data.size(), flush != 0, data.data()); if (written.Failed()) { cmd_buff[1] = written.Code().raw; - return written.Code(); + return; } cmd_buff[2] = static_cast<u32>(*written); break; } case FileCommand::GetSize: { - LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); + LOG_TRACE(Service_FS, "GetSize %s", GetName().c_str()); u64 size = backend->GetSize(); cmd_buff[2] = (u32)size; cmd_buff[3] = size >> 32; @@ -153,14 +151,13 @@ ResultVal<bool> File::SyncRequest() { case FileCommand::SetSize: { u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - LOG_TRACE(Service_FS, "SetSize %s %s size=%llu", GetTypeName().c_str(), GetName().c_str(), - size); + LOG_TRACE(Service_FS, "SetSize %s size=%llu", GetName().c_str(), size); backend->SetSize(size); break; } case FileCommand::Close: { - LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); + LOG_TRACE(Service_FS, "Close %s", GetName().c_str()); backend->Close(); break; } @@ -173,7 +170,11 @@ ResultVal<bool> File::SyncRequest() { case FileCommand::OpenLinkFile: { LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile %s", GetName().c_str()); - cmd_buff[3] = Kernel::g_handle_table.Create(this).ValueOr(INVALID_HANDLE); + auto sessions = Kernel::ServerSession::CreateSessionPair(GetName(), shared_from_this()); + ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); + cmd_buff[3] = Kernel::g_handle_table + .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) + .ValueOr(INVALID_HANDLE); break; } @@ -194,10 +195,9 @@ ResultVal<bool> File::SyncRequest() { LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); ResultCode error = UnimplementedFunction(ErrorModule::FS); cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. - return error; + return; } cmd_buff[1] = RESULT_SUCCESS.raw; // No error - return MakeResult<bool>(false); } Directory::Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, @@ -206,18 +206,16 @@ Directory::Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, Directory::~Directory() {} -ResultVal<bool> Directory::SyncRequest() { +void Directory::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { u32* cmd_buff = Kernel::GetCommandBuffer(); DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); switch (cmd) { - // Read from directory... case DirectoryCommand::Read: { u32 count = cmd_buff[1]; u32 address = cmd_buff[3]; std::vector<FileSys::Entry> entries(count); - LOG_TRACE(Service_FS, "Read %s %s: count=%d", GetTypeName().c_str(), GetName().c_str(), - count); + LOG_TRACE(Service_FS, "Read %s: count=%d", GetName().c_str(), count); // Number of entries actually read u32 read = backend->Read(entries.size(), entries.data()); @@ -227,7 +225,7 @@ ResultVal<bool> Directory::SyncRequest() { } case DirectoryCommand::Close: { - LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); + LOG_TRACE(Service_FS, "Close %s", GetName().c_str()); backend->Close(); break; } @@ -237,10 +235,9 @@ ResultVal<bool> Directory::SyncRequest() { LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); ResultCode error = UnimplementedFunction(ErrorModule::FS); cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. - return MakeResult<bool>(false); + return; } cmd_buff[1] = RESULT_SUCCESS.raw; // No error - return MakeResult<bool>(false); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -307,9 +304,9 @@ ResultCode RegisterArchiveType(std::unique_ptr<FileSys::ArchiveFactory>&& factor return RESULT_SUCCESS; } -ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, - const FileSys::Path& path, - const FileSys::Mode mode) { +ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, + const FileSys::Path& path, + const FileSys::Mode mode) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) return ERR_INVALID_ARCHIVE_HANDLE; @@ -318,8 +315,8 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han if (backend.Failed()) return backend.Code(); - auto file = Kernel::SharedPtr<File>(new File(backend.MoveFrom(), path)); - return MakeResult<Kernel::SharedPtr<File>>(std::move(file)); + auto file = std::shared_ptr<File>(new File(backend.MoveFrom(), path)); + return MakeResult<std::shared_ptr<File>>(std::move(file)); } ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { @@ -398,8 +395,8 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, } } -ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, - const FileSys::Path& path) { +ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, + const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) return ERR_INVALID_ARCHIVE_HANDLE; @@ -408,8 +405,8 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a if (backend.Failed()) return backend.Code(); - auto directory = Kernel::SharedPtr<Directory>(new Directory(backend.MoveFrom(), path)); - return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory)); + auto directory = std::shared_ptr<Directory>(new Directory(backend.MoveFrom(), path)); + return MakeResult<std::shared_ptr<Directory>>(std::move(directory)); } ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) { diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 87089bd92..519c1f3a9 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -8,7 +8,7 @@ #include <string> #include "common/common_types.h" #include "core/file_sys/archive_backend.h" -#include "core/hle/kernel/session.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/result.h" namespace FileSys { @@ -17,9 +17,9 @@ class FileBackend; } /// The unique system identifier hash, also known as ID0 -extern const std::string SYSTEM_ID; +static constexpr char SYSTEM_ID[]{"00000000000000000000000000000000"}; /// The scrambled SD card CID, also known as ID1 -extern const std::string SDCARD_ID; +static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"}; namespace Service { namespace FS { @@ -43,33 +43,37 @@ enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 }; typedef u64 ArchiveHandle; -class File : public Kernel::Session { +class File final : public SessionRequestHandler, public std::enable_shared_from_this<File> { public: File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path); ~File(); - std::string GetName() const override { + std::string GetName() const { return "Path: " + path.DebugStr(); } - ResultVal<bool> SyncRequest() override; FileSys::Path path; ///< Path of the file u32 priority; ///< Priority of the file. TODO(Subv): Find out what this means std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface + +protected: + void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; }; -class Directory : public Kernel::Session { +class Directory final : public SessionRequestHandler { public: Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path); ~Directory(); - std::string GetName() const override { + std::string GetName() const { return "Directory: " + path.DebugStr(); } - ResultVal<bool> SyncRequest() override; FileSys::Path path; ///< Path of the directory std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface + +protected: + void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; }; /** @@ -99,11 +103,11 @@ ResultCode RegisterArchiveType(std::unique_ptr<FileSys::ArchiveFactory>&& factor * @param archive_handle Handle to an open Archive object * @param path Path to the File inside of the Archive * @param mode Mode under which to open the File - * @return The opened File object as a Session + * @return The opened File object */ -ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, - const FileSys::Path& path, - const FileSys::Mode mode); +ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, + const FileSys::Path& path, + const FileSys::Mode mode); /** * Delete a File from an Archive @@ -178,10 +182,10 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, * Open a Directory from an Archive * @param archive_handle Handle to an open Archive object * @param path Path to the Directory inside of the Archive - * @return The opened Directory object as a Session + * @return The opened Directory object */ -ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, - const FileSys::Path& path); +ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, + const FileSys::Path& path); /** * Get the free space in an Archive diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 9ec17b395..337da1387 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -8,6 +8,7 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/string_util.h" +#include "core/hle/kernel/client_session.h" #include "core/hle/result.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" @@ -17,7 +18,7 @@ // Namespace FS_User using Kernel::SharedPtr; -using Kernel::Session; +using Kernel::ServerSession; namespace Service { namespace FS { @@ -67,10 +68,16 @@ static void OpenFile(Service::Interface* self) { LOG_DEBUG(Service_FS, "path=%s, mode=%u attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes); - ResultVal<SharedPtr<File>> file_res = OpenFileFromArchive(archive_handle, file_path, mode); + ResultVal<std::shared_ptr<File>> file_res = + OpenFileFromArchive(archive_handle, file_path, mode); cmd_buff[1] = file_res.Code().raw; if (file_res.Succeeded()) { - cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); + std::shared_ptr<File> file = *file_res; + auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); + file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); + cmd_buff[3] = Kernel::g_handle_table + .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) + .MoveFrom(); } else { cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); @@ -127,10 +134,16 @@ static void OpenFileDirectly(Service::Interface* self) { } SCOPE_EXIT({ CloseArchive(*archive_handle); }); - ResultVal<SharedPtr<File>> file_res = OpenFileFromArchive(*archive_handle, file_path, mode); + ResultVal<std::shared_ptr<File>> file_res = + OpenFileFromArchive(*archive_handle, file_path, mode); cmd_buff[1] = file_res.Code().raw; if (file_res.Succeeded()) { - cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); + std::shared_ptr<File> file = *file_res; + auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); + file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); + cmd_buff[3] = Kernel::g_handle_table + .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) + .MoveFrom(); } else { cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%u", @@ -388,10 +401,16 @@ static void OpenDirectory(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%u size=%u data=%s", static_cast<u32>(dirname_type), dirname_size, dir_path.DebugStr().c_str()); - ResultVal<SharedPtr<Directory>> dir_res = OpenDirectoryFromArchive(archive_handle, dir_path); + ResultVal<std::shared_ptr<Directory>> dir_res = + OpenDirectoryFromArchive(archive_handle, dir_path); cmd_buff[1] = dir_res.Code().raw; if (dir_res.Succeeded()) { - cmd_buff[3] = Kernel::g_handle_table.Create(*dir_res).MoveFrom(); + std::shared_ptr<Directory> directory = *dir_res; + auto sessions = ServerSession::CreateSessionPair(directory->GetName(), directory); + directory->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); + cmd_buff[3] = Kernel::g_handle_table + .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) + .MoveFrom(); } else { 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()); @@ -1003,6 +1022,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x08680000, nullptr, "GetMediaType"}, {0x08690000, nullptr, "GetNandEraseCount"}, {0x086A0082, nullptr, "ReadNandReport"}, + {0x087A0180, nullptr, "AddSeed"}, + {0x088600C0, nullptr, "CheckUpdatedDat"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 78cb761be..947958703 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -18,13 +18,11 @@ // Main graphics debugger object - TODO: Here is probably not the best place for this GraphicsDebugger g_debugger; -// Beginning address of HW regs -const static u32 REGS_BEGIN = 0x1EB00000; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace GSP_GPU +namespace Service { +namespace GSP { -namespace GSP_GPU { +// Beginning address of HW regs +const u32 REGS_BEGIN = 0x1EB00000; const ResultCode ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED( ErrorDescription::OutofRangeOrMisalignedAddress, ErrorModule::GX, ErrorSummary::InvalidArgument, @@ -179,7 +177,7 @@ static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr * 2 : number of registers to write sequentially * 4 : pointer to source data array */ -static void WriteHWRegs(Service::Interface* self) { +static void WriteHWRegs(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 reg_addr = cmd_buff[1]; u32 size = cmd_buff[2]; @@ -199,7 +197,7 @@ static void WriteHWRegs(Service::Interface* self) { * 4 : pointer to source data array * 6 : pointer to mask array */ -static void WriteHWRegsWithMask(Service::Interface* self) { +static void WriteHWRegsWithMask(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 reg_addr = cmd_buff[1]; u32 size = cmd_buff[2]; @@ -211,7 +209,7 @@ static void WriteHWRegsWithMask(Service::Interface* self) { } /// Read a GSP GPU hardware register -static void ReadHWRegs(Service::Interface* self) { +static void ReadHWRegs(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 reg_addr = cmd_buff[1]; u32 size = cmd_buff[2]; @@ -298,7 +296,7 @@ ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { * Outputs: * 1: Result code */ -static void SetBufferSwap(Service::Interface* self) { +static void SetBufferSwap(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 screen_id = cmd_buff[1]; FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; @@ -319,7 +317,7 @@ static void SetBufferSwap(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void FlushDataCache(Service::Interface* self) { +static void FlushDataCache(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 address = cmd_buff[1]; u32 size = cmd_buff[2]; @@ -340,7 +338,7 @@ static void FlushDataCache(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetAxiConfigQoSMode(Service::Interface* self) { +static void SetAxiConfigQoSMode(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 mode = cmd_buff[1]; @@ -359,7 +357,7 @@ static void SetAxiConfigQoSMode(Service::Interface* self) { * 2 : Thread index into GSP command buffer * 4 : Handle to GSP shared memory */ -static void RegisterInterruptRelayQueue(Service::Interface* self) { +static void RegisterInterruptRelayQueue(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 flags = cmd_buff[1]; @@ -391,7 +389,7 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void UnregisterInterruptRelayQueue(Service::Interface* self) { +static void UnregisterInterruptRelayQueue(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); g_thread_id = 0; @@ -592,7 +590,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { * Outputs: * 1: Result code */ -static void SetLcdForceBlack(Service::Interface* self) { +static void SetLcdForceBlack(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); bool enable_black = cmd_buff[1] != 0; @@ -609,7 +607,7 @@ static void SetLcdForceBlack(Service::Interface* self) { } /// This triggers handling of the GX command written to the command buffer in shared memory. -static void TriggerCmdReqQueue(Service::Interface* self) { +static void TriggerCmdReqQueue(Interface* self) { // Iterate through each thread's command queue... for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id); @@ -638,6 +636,7 @@ static void TriggerCmdReqQueue(Service::Interface* self) { * Inputs: * 0: Header 0x00180000 * Outputs: + * 0: Header Code[0x00180240] * 1: Result code * 2: Left framebuffer virtual address for the main screen * 3: Right framebuffer virtual address for the main screen @@ -648,7 +647,7 @@ static void TriggerCmdReqQueue(Service::Interface* self) { * 8: Bottom screen framebuffer format * 9: Bottom screen framebuffer width */ -static void ImportDisplayCaptureInfo(Service::Interface* self) { +static void ImportDisplayCaptureInfo(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(Subv): We're always returning the framebuffer structures for thread_id = 0, @@ -660,18 +659,19 @@ static void ImportDisplayCaptureInfo(Service::Interface* self) { FrameBufferUpdate* top_screen = GetFrameBufferInfo(thread_id, 0); FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(thread_id, 1); + cmd_buff[0] = IPC::MakeHeader(0x18, 0x9, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + // Top Screen cmd_buff[2] = top_screen->framebuffer_info[top_screen->index].address_left; cmd_buff[3] = top_screen->framebuffer_info[top_screen->index].address_right; cmd_buff[4] = top_screen->framebuffer_info[top_screen->index].format; cmd_buff[5] = top_screen->framebuffer_info[top_screen->index].stride; - + // Bottom Screen cmd_buff[6] = bottom_screen->framebuffer_info[bottom_screen->index].address_left; cmd_buff[7] = bottom_screen->framebuffer_info[bottom_screen->index].address_right; cmd_buff[8] = bottom_screen->framebuffer_info[bottom_screen->index].format; cmd_buff[9] = bottom_screen->framebuffer_info[bottom_screen->index].stride; - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_GSP, "called"); } @@ -680,7 +680,7 @@ static void ImportDisplayCaptureInfo(Service::Interface* self) { * Outputs: * 1: Result code */ -static void AcquireRight(Service::Interface* self) { +static void AcquireRight(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); gpu_right_acquired = true; @@ -695,7 +695,7 @@ static void AcquireRight(Service::Interface* self) { * Outputs: * 1: Result code */ -static void ReleaseRight(Service::Interface* self) { +static void ReleaseRight(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); gpu_right_acquired = false; @@ -739,10 +739,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x001F0082, nullptr, "StoreDataCache"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +GSP_GPU::GSP_GPU() { Register(FunctionTable); g_interrupt_event = nullptr; @@ -757,10 +754,11 @@ Interface::Interface() { first_initialization = true; } -Interface::~Interface() { +GSP_GPU::~GSP_GPU() { g_interrupt_event = nullptr; g_shared_memory = nullptr; gpu_right_acquired = false; } -} // namespace +} // namespace GSP +} // namespace Service diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index 79a72f77d..c6e24073b 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h @@ -11,10 +11,8 @@ #include "core/hle/result.h" #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace GSP_GPU - -namespace GSP_GPU { +namespace Service { +namespace GSP { /// GSP interrupt ID enum class InterruptId : u8 { @@ -176,11 +174,10 @@ struct CommandBuffer { }; static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrect size"); -/// Interface to "srv:" service -class Interface : public Service::Interface { +class GSP_GPU final : public Interface { public: - Interface(); - ~Interface() override; + GSP_GPU(); + ~GSP_GPU() override; std::string GetPortName() const override { return "gsp::Gpu"; @@ -203,4 +200,6 @@ ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); * @returns FramebufferUpdate Information about the specified framebuffer. */ FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); -} // namespace + +} // namespace GSP +} // namespace Service diff --git a/src/core/hle/service/gsp_lcd.cpp b/src/core/hle/service/gsp_lcd.cpp index b916dd759..89cb4a3cc 100644 --- a/src/core/hle/service/gsp_lcd.cpp +++ b/src/core/hle/service/gsp_lcd.cpp @@ -4,26 +4,26 @@ #include "core/hle/service/gsp_lcd.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace GSP_LCD - -namespace GSP_LCD { +namespace Service { +namespace GSP { const Interface::FunctionInfo FunctionTable[] = { // clang-format off + {0x000A0080, nullptr, "SetBrightnessRaw"}, + {0x000B0080, nullptr, "SetBrightness"}, {0x000F0000, nullptr, "PowerOnAllBacklights"}, {0x00100000, nullptr, "PowerOffAllBacklights"}, {0x00110040, nullptr, "PowerOnBacklight"}, {0x00120040, nullptr, "PowerOffBacklight"}, {0x00130040, nullptr, "SetLedForceOff"}, + {0x00140000, nullptr, "GetVendor"}, + {0x00150040, nullptr, "GetBrightness"}, // clang-format on }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +GSP_LCD::GSP_LCD() { Register(FunctionTable); } -} // namespace +} // namespace GSP +} // namespace Service diff --git a/src/core/hle/service/gsp_lcd.h b/src/core/hle/service/gsp_lcd.h index 56b3cfe86..e9686a5e7 100644 --- a/src/core/hle/service/gsp_lcd.h +++ b/src/core/hle/service/gsp_lcd.h @@ -6,19 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace GSP_LCD +namespace Service { +namespace GSP { -namespace GSP_LCD { - -/// Interface to "gsp::Lcd" service -class Interface : public Service::Interface { +class GSP_LCD final : public Interface { public: - Interface(); + GSP_LCD(); std::string GetPortName() const override { return "gsp::Lcd"; } }; -} // namespace +} // namespace GSP +} // namespace Service diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 99baded11..676154bd4 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -3,9 +3,9 @@ // Refer to the license.txt file included. #include <cmath> -#include "common/emu_window.h" #include "common/logging/log.h" #include "core/core_timing.h" +#include "core/frontend/emu_window.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" @@ -37,7 +37,8 @@ static int enable_gyroscope_count = 0; // positive means enabled static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { // 30 degree and 60 degree are angular thresholds for directions - constexpr float TAN30 = 0.577350269, TAN60 = 1 / TAN30; + constexpr float TAN30 = 0.577350269f; + constexpr float TAN60 = 1 / TAN30; // a circle pad radius greater than 40 will trigger circle pad direction constexpr int CIRCLE_PAD_THRESHOLD_SQUARE = 40 * 40; PadState state; diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp index 3cf62a4b8..b01d6e031 100644 --- a/src/core/hle/service/http_c.cpp +++ b/src/core/hle/service/http_c.cpp @@ -4,10 +4,8 @@ #include "core/hle/service/http_c.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace HTTP_C - -namespace HTTP_C { +namespace Service { +namespace HTTP { const Interface::FunctionInfo FunctionTable[] = { {0x00010044, nullptr, "Initialize"}, @@ -55,6 +53,10 @@ const Interface::FunctionInfo FunctionTable[] = { {0x002E0040, nullptr, "DestroyRootCertChain"}, {0x002F0082, nullptr, "RootCertChainAddCert"}, {0x00300080, nullptr, "RootCertChainAddDefaultCert"}, + {0x00310080, nullptr, "RootCertChainRemoveCert"}, + {0x00320084, nullptr, "OpenClientCertContext"}, + {0x00330040, nullptr, "OpenDefaultClientCertContext"}, + {0x00340040, nullptr, "CloseClientCertContext"}, {0x00350186, nullptr, "SetDefaultProxy"}, {0x00360000, nullptr, "ClearDNSCache"}, {0x00370080, nullptr, "SetKeepAlive"}, @@ -62,11 +64,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00390000, nullptr, "Finalize"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +HTTP_C::HTTP_C() { Register(FunctionTable); } -} // namespace +} // namespace HTTP +} // namespace Service diff --git a/src/core/hle/service/http_c.h b/src/core/hle/service/http_c.h index 5ea3d1df3..cff279c02 100644 --- a/src/core/hle/service/http_c.h +++ b/src/core/hle/service/http_c.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace HTTP_C +namespace Service { +namespace HTTP { -namespace HTTP_C { - -class Interface : public Service::Interface { +class HTTP_C final : public Interface { public: - Interface(); + HTTP_C(); std::string GetPortName() const override { return "http:C"; } }; -} // namespace +} // namespace HTTP +} // namespace Service diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp index 4d6639ded..7f1731a50 100644 --- a/src/core/hle/service/ir/ir.cpp +++ b/src/core/hle/service/ir/ir.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_rst.h" @@ -36,7 +37,7 @@ void InitializeIrNopShared(Interface* self) { u32 send_buff_size = cmd_buff[4]; u32 unk2 = cmd_buff[5]; u8 baud_rate = cmd_buff[6] & 0xFF; - Handle handle = cmd_buff[8]; + Kernel::Handle handle = cmd_buff[8]; if (Kernel::g_handle_table.IsValid(handle)) { transfer_shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); diff --git a/src/core/hle/service/ldr_ro/cro_helper.cpp b/src/core/hle/service/ldr_ro/cro_helper.cpp index 4f0aa77eb..f78545f37 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.cpp +++ b/src/core/hle/service/ldr_ro/cro_helper.cpp @@ -7,10 +7,8 @@ #include "common/scope_exit.h" #include "core/hle/service/ldr_ro/cro_helper.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace LDR_RO - -namespace LDR_RO { +namespace Service { +namespace LDR { static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument, @@ -1493,4 +1491,5 @@ std::tuple<VAddr, u32> CROHelper::GetExecutablePages() const { return std::make_tuple(0, 0); } -} // namespace +} // namespace LDR +} // namespace Service diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h index 6a0d0d3bf..060d5a55f 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.h +++ b/src/core/hle/service/ldr_ro/cro_helper.h @@ -11,10 +11,8 @@ #include "core/hle/result.h" #include "core/memory.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace LDR_RO - -namespace LDR_RO { +namespace Service { +namespace LDR { // GCC versions < 5.0 do not implement std::is_trivially_copyable. // Excluding MSVC because it has weird behaviour for std::is_trivially_copyable. @@ -710,4 +708,5 @@ private: ResultCode ApplyExitRelocations(VAddr crs_address); }; -} // namespace +} // namespace LDR +} // namespace Service diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index ec183d1f5..8d00a7577 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -12,10 +12,8 @@ #include "core/hle/service/ldr_ro/ldr_ro.h" #include "core/hle/service/ldr_ro/memory_synchronizer.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace LDR_RO - -namespace LDR_RO { +namespace Service { +namespace LDR { static const ResultCode ERROR_ALREADY_INITIALIZED = // 0xD9612FF9 ResultCode(ErrorDescription::AlreadyInitialized, ErrorModule::RO, ErrorSummary::Internal, @@ -71,7 +69,7 @@ static bool VerifyBufferState(VAddr buffer_ptr, u32 size) { * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ -static void Initialize(Service::Interface* self) { +static void Initialize(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); VAddr crs_buffer_ptr = cmd_buff[1]; u32 crs_size = cmd_buff[2]; @@ -196,7 +194,7 @@ static void Initialize(Service::Interface* self) { * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ -static void LoadCRR(Service::Interface* self) { +static void LoadCRR(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 crr_buffer_ptr = cmd_buff[1]; u32 crr_size = cmd_buff[2]; @@ -229,7 +227,7 @@ static void LoadCRR(Service::Interface* self) { * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ -static void UnloadCRR(Service::Interface* self) { +static void UnloadCRR(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 crr_buffer_ptr = cmd_buff[1]; u32 descriptor = cmd_buff[2]; @@ -276,7 +274,7 @@ static void UnloadCRR(Service::Interface* self) { * unified one of two, with an additional parameter link_on_load_bug_fix. * There is a dispatcher template below. */ -static void LoadCRO(Service::Interface* self, bool link_on_load_bug_fix) { +static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { u32* cmd_buff = Kernel::GetCommandBuffer(); VAddr cro_buffer_ptr = cmd_buff[1]; VAddr cro_address = cmd_buff[2]; @@ -459,7 +457,7 @@ static void LoadCRO(Service::Interface* self, bool link_on_load_bug_fix) { } } - Core::g_app_core->ClearInstructionCache(); + Core::CPU().ClearInstructionCache(); LOG_INFO(Service_LDR, "CRO \"%s\" loaded at 0x%08X, fixed_end=0x%08X", cro.ModuleName().data(), cro_address, cro_address + fix_size); @@ -469,7 +467,7 @@ static void LoadCRO(Service::Interface* self, bool link_on_load_bug_fix) { } template <bool link_on_load_bug_fix> -static void LoadCRO(Service::Interface* self) { +static void LoadCRO(Interface* self) { LoadCRO(self, link_on_load_bug_fix); } @@ -486,7 +484,7 @@ static void LoadCRO(Service::Interface* self) { * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ -static void UnloadCRO(Service::Interface* self) { +static void UnloadCRO(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); VAddr cro_address = cmd_buff[1]; u32 zero = cmd_buff[2]; @@ -564,7 +562,7 @@ static void UnloadCRO(Service::Interface* self) { memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr); } - Core::g_app_core->ClearInstructionCache(); + Core::CPU().ClearInstructionCache(); cmd_buff[1] = result.raw; } @@ -580,7 +578,7 @@ static void UnloadCRO(Service::Interface* self) { * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ -static void LinkCRO(Service::Interface* self) { +static void LinkCRO(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); VAddr cro_address = cmd_buff[1]; u32 descriptor = cmd_buff[2]; @@ -626,7 +624,7 @@ static void LinkCRO(Service::Interface* self) { } memory_synchronizer.SynchronizeOriginalMemory(); - Core::g_app_core->ClearInstructionCache(); + Core::CPU().ClearInstructionCache(); cmd_buff[1] = result.raw; } @@ -642,7 +640,7 @@ static void LinkCRO(Service::Interface* self) { * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ -static void UnlinkCRO(Service::Interface* self) { +static void UnlinkCRO(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); VAddr cro_address = cmd_buff[1]; u32 descriptor = cmd_buff[2]; @@ -688,7 +686,7 @@ static void UnlinkCRO(Service::Interface* self) { } memory_synchronizer.SynchronizeOriginalMemory(); - Core::g_app_core->ClearInstructionCache(); + Core::CPU().ClearInstructionCache(); cmd_buff[1] = result.raw; } @@ -704,7 +702,7 @@ static void UnlinkCRO(Service::Interface* self) { * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ -static void Shutdown(Service::Interface* self) { +static void Shutdown(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); VAddr crs_buffer_ptr = cmd_buff[1]; u32 descriptor = cmd_buff[2]; @@ -762,14 +760,12 @@ const Interface::FunctionInfo FunctionTable[] = { // clang-format on }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +LDR_RO::LDR_RO() { Register(FunctionTable); loaded_crs = 0; memory_synchronizer.Clear(); } -} // namespace +} // namespace LDR +} // namespace Service diff --git a/src/core/hle/service/ldr_ro/ldr_ro.h b/src/core/hle/service/ldr_ro/ldr_ro.h index 331637cde..0f6fe7b60 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.h +++ b/src/core/hle/service/ldr_ro/ldr_ro.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace LDR_RO +namespace Service { +namespace LDR { -namespace LDR_RO { - -class Interface : public Service::Interface { +class LDR_RO final : public Interface { public: - Interface(); + LDR_RO(); std::string GetPortName() const override { return "ldr:ro"; } }; -} // namespace +} // namespace LDR +} // namespace Service diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.cpp b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp index 989887264..0d44bf6bd 100644 --- a/src/core/hle/service/ldr_ro/memory_synchronizer.cpp +++ b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp @@ -6,10 +6,8 @@ #include "common/assert.h" #include "core/hle/service/ldr_ro/memory_synchronizer.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace LDR_RO - -namespace LDR_RO { +namespace Service { +namespace LDR { auto MemorySynchronizer::FindMemoryBlock(VAddr mapping, VAddr original) { auto block = std::find_if(memory_blocks.begin(), memory_blocks.end(), @@ -40,4 +38,5 @@ void MemorySynchronizer::SynchronizeOriginalMemory() { } } -} // namespace +} // namespace LDR +} // namespace Service diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.h b/src/core/hle/service/ldr_ro/memory_synchronizer.h index 883ee4acf..438293a58 100644 --- a/src/core/hle/service/ldr_ro/memory_synchronizer.h +++ b/src/core/hle/service/ldr_ro/memory_synchronizer.h @@ -7,10 +7,8 @@ #include <vector> #include "core/memory.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace LDR_RO - -namespace LDR_RO { +namespace Service { +namespace LDR { /** * This is a work-around before we implement memory aliasing. @@ -40,4 +38,5 @@ private: auto FindMemoryBlock(VAddr mapping, VAddr original); }; -} // namespace +} // namespace LDR +} // namespace Service diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index 1f851d328..4f1dd2fce 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -4,13 +4,12 @@ #include "common/logging/log.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/mic_u.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace MIC_U - -namespace MIC_U { +namespace Service { +namespace MIC { enum class Encoding : u8 { PCM8 = 0, @@ -49,10 +48,10 @@ static bool audio_buffer_loop; * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void MapSharedMem(Service::Interface* self) { +static void MapSharedMem(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 size = cmd_buff[1]; - Handle mem_handle = cmd_buff[3]; + Kernel::Handle mem_handle = cmd_buff[3]; shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(mem_handle); if (shared_memory) { shared_memory->name = "MIC_U:shared_memory"; @@ -68,7 +67,7 @@ static void MapSharedMem(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void UnmapSharedMem(Service::Interface* self) { +static void UnmapSharedMem(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -87,7 +86,7 @@ static void UnmapSharedMem(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void StartSampling(Service::Interface* self) { +static void StartSampling(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); encoding = static_cast<Encoding>(cmd_buff[1] & 0xFF); @@ -111,7 +110,7 @@ static void StartSampling(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void AdjustSampling(Service::Interface* self) { +static void AdjustSampling(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -125,7 +124,7 @@ static void AdjustSampling(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void StopSampling(Service::Interface* self) { +static void StopSampling(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error is_sampling = false; @@ -140,7 +139,7 @@ static void StopSampling(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : 0 = sampling, non-zero = sampling */ -static void IsSampling(Service::Interface* self) { +static void IsSampling(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = is_sampling; @@ -155,7 +154,7 @@ static void IsSampling(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 3 : Event handle */ -static void GetBufferFullEvent(Service::Interface* self) { +static void GetBufferFullEvent(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom(); @@ -170,7 +169,7 @@ static void GetBufferFullEvent(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetGain(Service::Interface* self) { +static void SetGain(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); mic_gain = cmd_buff[1] & 0xFF; cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -185,7 +184,7 @@ static void SetGain(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : Gain */ -static void GetGain(Service::Interface* self) { +static void GetGain(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = mic_gain; @@ -200,7 +199,7 @@ static void GetGain(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetPower(Service::Interface* self) { +static void SetPower(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); mic_power = static_cast<bool>(cmd_buff[1] & 0xFF); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -215,7 +214,7 @@ static void SetPower(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : Power */ -static void GetPower(Service::Interface* self) { +static void GetPower(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = mic_power; @@ -232,7 +231,7 @@ static void GetPower(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetIirFilterMic(Service::Interface* self) { +static void SetIirFilterMic(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 size = cmd_buff[1]; @@ -250,7 +249,7 @@ static void SetIirFilterMic(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetClamp(Service::Interface* self) { +static void SetClamp(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); clamp = static_cast<bool>(cmd_buff[1] & 0xFF); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -265,7 +264,7 @@ static void SetClamp(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : Clamp (0 = don't clamp, non-zero = clamp) */ -static void GetClamp(Service::Interface* self) { +static void GetClamp(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = clamp; @@ -280,7 +279,7 @@ static void GetClamp(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetAllowShellClosed(Service::Interface* self) { +static void SetAllowShellClosed(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); allow_shell_closed = static_cast<bool>(cmd_buff[1] & 0xFF); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -294,7 +293,7 @@ static void SetAllowShellClosed(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetClientVersion(Service::Interface* self) { +static void SetClientVersion(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); const u32 version = cmd_buff[1]; @@ -324,10 +323,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00100040, SetClientVersion, "SetClientVersion"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +MIC_U::MIC_U() { Register(FunctionTable); shared_memory = nullptr; buffer_full_event = @@ -338,9 +334,10 @@ Interface::Interface() { clamp = false; } -Interface::~Interface() { +MIC_U::~MIC_U() { shared_memory = nullptr; buffer_full_event = nullptr; } -} // namespace +} // namespace MIC +} // namespace Service diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h index 1cff7390e..ec2b67ab8 100644 --- a/src/core/hle/service/mic_u.h +++ b/src/core/hle/service/mic_u.h @@ -6,21 +6,18 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace MIC_U +namespace Service { +namespace MIC { -// mic service - -namespace MIC_U { - -class Interface : public Service::Interface { +class MIC_U final : public Interface { public: - Interface(); - ~Interface(); + MIC_U(); + ~MIC_U(); std::string GetPortName() const override { return "mic:u"; } }; -} // namespace +} // namespace MIC +} // namespace Service diff --git a/src/core/hle/service/mvd/mvd.cpp b/src/core/hle/service/mvd/mvd.cpp new file mode 100644 index 000000000..9416fe5d6 --- /dev/null +++ b/src/core/hle/service/mvd/mvd.cpp @@ -0,0 +1,17 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/mvd/mvd.h" +#include "core/hle/service/mvd/mvd_std.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace MVD { + +void Init() { + AddService(new MVD_STD()); +} + +} // namespace MVD +} // namespace Service diff --git a/src/core/hle/service/mvd/mvd.h b/src/core/hle/service/mvd/mvd.h new file mode 100644 index 000000000..7b212e839 --- /dev/null +++ b/src/core/hle/service/mvd/mvd.h @@ -0,0 +1,14 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service { +namespace MVD { + +/// Initializes all MVD services. +void Init(); + +} // namespace MVD +} // namespace Service diff --git a/src/core/hle/service/mvd/mvd_std.cpp b/src/core/hle/service/mvd/mvd_std.cpp new file mode 100644 index 000000000..fd7ca87d3 --- /dev/null +++ b/src/core/hle/service/mvd/mvd_std.cpp @@ -0,0 +1,32 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/mvd/mvd_std.h" + +namespace Service { +namespace MVD { + +const Interface::FunctionInfo FunctionTable[] = { + // clang-format off + {0x00010082, nullptr, "Initialize"}, + {0x00020000, nullptr, "Shutdown"}, + {0x00030300, nullptr, "CalculateWorkBufSize"}, + {0x000400C0, nullptr, "CalculateImageSize"}, + {0x00080142, nullptr, "ProcessNALUnit"}, + {0x00090042, nullptr, "ControlFrameRendering"}, + {0x000A0000, nullptr, "GetStatus"}, + {0x000B0000, nullptr, "GetStatusOther"}, + {0x001D0042, nullptr, "GetConfig"}, + {0x001E0044, nullptr, "SetConfig"}, + {0x001F0902, nullptr, "SetOutputBuffer"}, + {0x00210100, nullptr, "OverrideOutputBuffers"} + // clang-format on +}; + +MVD_STD::MVD_STD() { + Register(FunctionTable); +} + +} // namespace MVD +} // namespace Service diff --git a/src/core/hle/service/mvd/mvd_std.h b/src/core/hle/service/mvd/mvd_std.h new file mode 100644 index 000000000..7db9e2e50 --- /dev/null +++ b/src/core/hle/service/mvd/mvd_std.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace MVD { + +class MVD_STD final : public Interface { +public: + MVD_STD(); + + std::string GetPortName() const override { + return "mvd:std"; + } +}; + +} // namespace MVD +} // namespace Service diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp new file mode 100644 index 000000000..d9738c6a1 --- /dev/null +++ b/src/core/hle/service/nfc/nfc.cpp @@ -0,0 +1,18 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nfc/nfc.h" +#include "core/hle/service/nfc/nfc_m.h" +#include "core/hle/service/nfc/nfc_u.h" + +namespace Service { +namespace NFC { + +void Init() { + AddService(new NFC_M()); + AddService(new NFC_U()); +} + +} // namespace NFC +} // namespace Service diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h new file mode 100644 index 000000000..cd65a5fdc --- /dev/null +++ b/src/core/hle/service/nfc/nfc.h @@ -0,0 +1,14 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service { +namespace NFC { + +/// Initialize all NFC services. +void Init(); + +} // namespace NFC +} // namespace Service diff --git a/src/core/hle/service/nfc/nfc_m.cpp b/src/core/hle/service/nfc/nfc_m.cpp new file mode 100644 index 000000000..717335c11 --- /dev/null +++ b/src/core/hle/service/nfc/nfc_m.cpp @@ -0,0 +1,44 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nfc/nfc_m.h" + +namespace Service { +namespace NFC { + +const Interface::FunctionInfo FunctionTable[] = { + // clang-format off + // nfc:u shared commands + {0x00010040, nullptr, "Initialize"}, + {0x00020040, nullptr, "Shutdown"}, + {0x00030000, nullptr, "StartCommunication"}, + {0x00040000, nullptr, "StopCommunication"}, + {0x00050040, nullptr, "StartTagScanning"}, + {0x00060000, nullptr, "StopTagScanning"}, + {0x00070000, nullptr, "LoadAmiiboData"}, + {0x00080000, nullptr, "ResetTagScanState"}, + {0x00090002, nullptr, "UpdateStoredAmiiboData"}, + {0x000D0000, nullptr, "GetTagState"}, + {0x000F0000, nullptr, "CommunicationGetStatus"}, + {0x00100000, nullptr, "GetTagInfo2"}, + {0x00110000, nullptr, "GetTagInfo"}, + {0x00120000, nullptr, "CommunicationGetResult"}, + {0x00130040, nullptr, "OpenAppData"}, + {0x00140384, nullptr, "InitializeWriteAppData"}, + {0x00150040, nullptr, "ReadAppData"}, + {0x00160242, nullptr, "WriteAppData"}, + {0x00170000, nullptr, "GetAmiiboSettings"}, + {0x00180000, nullptr, "GetAmiiboConfig"}, + {0x00190000, nullptr, "GetAppDataInitStruct"}, + // nfc:m + {0x04040A40, nullptr, "SetAmiiboSettings"} + // clang-format on +}; + +NFC_M::NFC_M() { + Register(FunctionTable); +} + +} // namespace NFC +} // namespace Service diff --git a/src/core/hle/service/nfc/nfc_m.h b/src/core/hle/service/nfc/nfc_m.h new file mode 100644 index 000000000..fae75535b --- /dev/null +++ b/src/core/hle/service/nfc/nfc_m.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NFC { + +class NFC_M final : public Interface { +public: + NFC_M(); + + std::string GetPortName() const override { + return "nfc:m"; + } +}; + +} // namespace NFC +} // namespace Service diff --git a/src/core/hle/service/nfc/nfc_u.cpp b/src/core/hle/service/nfc/nfc_u.cpp new file mode 100644 index 000000000..deffb0b4f --- /dev/null +++ b/src/core/hle/service/nfc/nfc_u.cpp @@ -0,0 +1,41 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nfc/nfc_u.h" + +namespace Service { +namespace NFC { + +const Interface::FunctionInfo FunctionTable[] = { + // clang-format off + {0x00010040, nullptr, "Initialize"}, + {0x00020040, nullptr, "Shutdown"}, + {0x00030000, nullptr, "StartCommunication"}, + {0x00040000, nullptr, "StopCommunication"}, + {0x00050040, nullptr, "StartTagScanning"}, + {0x00060000, nullptr, "StopTagScanning"}, + {0x00070000, nullptr, "LoadAmiiboData"}, + {0x00080000, nullptr, "ResetTagScanState"}, + {0x00090002, nullptr, "UpdateStoredAmiiboData"}, + {0x000D0000, nullptr, "GetTagState"}, + {0x000F0000, nullptr, "CommunicationGetStatus"}, + {0x00100000, nullptr, "GetTagInfo2"}, + {0x00110000, nullptr, "GetTagInfo"}, + {0x00120000, nullptr, "CommunicationGetResult"}, + {0x00130040, nullptr, "OpenAppData"}, + {0x00140384, nullptr, "InitializeWriteAppData"}, + {0x00150040, nullptr, "ReadAppData"}, + {0x00160242, nullptr, "WriteAppData"}, + {0x00170000, nullptr, "GetAmiiboSettings"}, + {0x00180000, nullptr, "GetAmiiboConfig"}, + {0x00190000, nullptr, "GetAppDataInitStruct"}, + // clang-format on +}; + +NFC_U::NFC_U() { + Register(FunctionTable); +} + +} // namespace NFC +} // namespace Service diff --git a/src/core/hle/service/nfc/nfc_u.h b/src/core/hle/service/nfc/nfc_u.h new file mode 100644 index 000000000..eb7507314 --- /dev/null +++ b/src/core/hle/service/nfc/nfc_u.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NFC { + +class NFC_U final : public Interface { +public: + NFC_U(); + + std::string GetPortName() const override { + return "nfc:u"; + } +}; + +} // namespace NFC +} // namespace Service diff --git a/src/core/hle/service/nim/nim_s.cpp b/src/core/hle/service/nim/nim_s.cpp index e2ba693c9..28b87e6f7 100644 --- a/src/core/hle/service/nim/nim_s.cpp +++ b/src/core/hle/service/nim/nim_s.cpp @@ -10,6 +10,7 @@ namespace NIM { const Interface::FunctionInfo FunctionTable[] = { {0x000A0000, nullptr, "CheckSysupdateAvailableSOAP"}, {0x0016020A, nullptr, "ListTitles"}, + {0x00290000, nullptr, "AccountCheckBalanceSOAP"}, {0x002D0042, nullptr, "DownloadTickets"}, {0x00420240, nullptr, "StartDownload"}, }; diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp index 7e07d02e8..7664bad60 100644 --- a/src/core/hle/service/nim/nim_u.cpp +++ b/src/core/hle/service/nim/nim_u.cpp @@ -15,6 +15,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00050000, nullptr, "CheckForSysUpdateEvent"}, {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, {0x000A0000, nullptr, "GetState"}, + {0x000B0000, nullptr, "GetSystemTitleHash"}, }; NIM_U_Interface::NIM_U_Interface() { diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp index 6693f7c08..215c9aacc 100644 --- a/src/core/hle/service/ns_s.cpp +++ b/src/core/hle/service/ns_s.cpp @@ -4,10 +4,8 @@ #include "core/hle/service/ns_s.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace NS_S - -namespace NS_S { +namespace Service { +namespace NS { const Interface::FunctionInfo FunctionTable[] = { {0x000100C0, nullptr, "LaunchFIRM"}, @@ -27,11 +25,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00160000, nullptr, "RebootSystemClean"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +NS_S::NS_S() { Register(FunctionTable); } -} // namespace +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns_s.h b/src/core/hle/service/ns_s.h index 8d8e849b8..90288a521 100644 --- a/src/core/hle/service/ns_s.h +++ b/src/core/hle/service/ns_s.h @@ -6,19 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace NS_S +namespace Service { +namespace NS { -namespace NS_S { - -/// Interface to "NS:S" service -class Interface : public Service::Interface { +class NS_S final : public Interface { public: - Interface(); + NS_S(); std::string GetPortName() const override { return "ns:s"; } }; -} // namespace +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm.cpp b/src/core/hle/service/nwm/nwm.cpp new file mode 100644 index 000000000..9f1994dc3 --- /dev/null +++ b/src/core/hle/service/nwm/nwm.cpp @@ -0,0 +1,28 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nwm/nwm.h" +#include "core/hle/service/nwm/nwm_cec.h" +#include "core/hle/service/nwm/nwm_ext.h" +#include "core/hle/service/nwm/nwm_inf.h" +#include "core/hle/service/nwm/nwm_sap.h" +#include "core/hle/service/nwm/nwm_soc.h" +#include "core/hle/service/nwm/nwm_tst.h" +#include "core/hle/service/nwm/nwm_uds.h" + +namespace Service { +namespace NWM { + +void Init() { + AddService(new NWM_CEC); + AddService(new NWM_EXT); + AddService(new NWM_INF); + AddService(new NWM_SAP); + AddService(new NWM_SOC); + AddService(new NWM_TST); + AddService(new NWM_UDS); +} + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm.h b/src/core/hle/service/nwm/nwm.h new file mode 100644 index 000000000..6926b29a6 --- /dev/null +++ b/src/core/hle/service/nwm/nwm.h @@ -0,0 +1,14 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service { +namespace NWM { + +/// Initialize all NWM services +void Init(); + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_cec.cpp b/src/core/hle/service/nwm/nwm_cec.cpp new file mode 100644 index 000000000..7f03987df --- /dev/null +++ b/src/core/hle/service/nwm/nwm_cec.cpp @@ -0,0 +1,19 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nwm/nwm_cec.h" + +namespace Service { +namespace NWM { + +const Interface::FunctionInfo FunctionTable[] = { + {0x000D0082, nullptr, "SendProbeRequest"}, +}; + +NWM_CEC::NWM_CEC() { + Register(FunctionTable); +} + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_cec.h b/src/core/hle/service/nwm/nwm_cec.h new file mode 100644 index 000000000..07b6addb5 --- /dev/null +++ b/src/core/hle/service/nwm/nwm_cec.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NWM { + +class NWM_CEC final : public Interface { +public: + NWM_CEC(); + + std::string GetPortName() const override { + return "nwm::CEC"; + } +}; + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_ext.cpp b/src/core/hle/service/nwm/nwm_ext.cpp new file mode 100644 index 000000000..605640a13 --- /dev/null +++ b/src/core/hle/service/nwm/nwm_ext.cpp @@ -0,0 +1,19 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nwm/nwm_ext.h" + +namespace Service { +namespace NWM { + +const Interface::FunctionInfo FunctionTable[] = { + {0x00080040, nullptr, "ControlWirelessEnabled"}, +}; + +NWM_EXT::NWM_EXT() { + Register(FunctionTable); +} + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_ext.h b/src/core/hle/service/nwm/nwm_ext.h new file mode 100644 index 000000000..51d39d9ea --- /dev/null +++ b/src/core/hle/service/nwm/nwm_ext.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NWM { + +class NWM_EXT final : public Interface { +public: + NWM_EXT(); + + std::string GetPortName() const override { + return "nwm::EXT"; + } +}; + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_inf.cpp b/src/core/hle/service/nwm/nwm_inf.cpp new file mode 100644 index 000000000..c8470589b --- /dev/null +++ b/src/core/hle/service/nwm/nwm_inf.cpp @@ -0,0 +1,21 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nwm/nwm_inf.h" + +namespace Service { +namespace NWM { + +const Interface::FunctionInfo FunctionTable[] = { + {0x000603C4, nullptr, "RecvBeaconBroadcastData"}, + {0x00070742, nullptr, "ConnectToEncryptedAP"}, + {0x00080302, nullptr, "ConnectToAP"}, +}; + +NWM_INF::NWM_INF() { + Register(FunctionTable); +} + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_inf.h b/src/core/hle/service/nwm/nwm_inf.h new file mode 100644 index 000000000..0043d769c --- /dev/null +++ b/src/core/hle/service/nwm/nwm_inf.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NWM { + +class NWM_INF final : public Interface { +public: + NWM_INF(); + + std::string GetPortName() const override { + return "nwm::INF"; + } +}; + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_sap.cpp b/src/core/hle/service/nwm/nwm_sap.cpp new file mode 100644 index 000000000..fd29ed761 --- /dev/null +++ b/src/core/hle/service/nwm/nwm_sap.cpp @@ -0,0 +1,20 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nwm/nwm_sap.h" + +namespace Service { +namespace NWM { + +/* +const Interface::FunctionInfo FunctionTable[] = { +}; +*/ + +NWM_SAP::NWM_SAP() { + // Register(FunctionTable); +} + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_sap.h b/src/core/hle/service/nwm/nwm_sap.h new file mode 100644 index 000000000..f692e06d4 --- /dev/null +++ b/src/core/hle/service/nwm/nwm_sap.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NWM { + +class NWM_SAP final : public Interface { +public: + NWM_SAP(); + + std::string GetPortName() const override { + return "nwm::SAP"; + } +}; + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_soc.cpp b/src/core/hle/service/nwm/nwm_soc.cpp new file mode 100644 index 000000000..fdffcb925 --- /dev/null +++ b/src/core/hle/service/nwm/nwm_soc.cpp @@ -0,0 +1,20 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nwm/nwm_soc.h" + +namespace Service { +namespace NWM { + +/* +const Interface::FunctionInfo FunctionTable[] = { +}; +*/ + +NWM_SOC::NWM_SOC() { + // Register(FunctionTable); +} + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_soc.h b/src/core/hle/service/nwm/nwm_soc.h new file mode 100644 index 000000000..594941d7e --- /dev/null +++ b/src/core/hle/service/nwm/nwm_soc.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NWM { + +class NWM_SOC final : public Interface { +public: + NWM_SOC(); + + std::string GetPortName() const override { + return "nwm::SOC"; + } +}; + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_tst.cpp b/src/core/hle/service/nwm/nwm_tst.cpp new file mode 100644 index 000000000..5f292e5db --- /dev/null +++ b/src/core/hle/service/nwm/nwm_tst.cpp @@ -0,0 +1,20 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nwm/nwm_tst.h" + +namespace Service { +namespace NWM { + +/* +const Interface::FunctionInfo FunctionTable[] = { +}; +*/ + +NWM_TST::NWM_TST() { + // Register(FunctionTable); +} + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_tst.h b/src/core/hle/service/nwm/nwm_tst.h new file mode 100644 index 000000000..8deca3216 --- /dev/null +++ b/src/core/hle/service/nwm/nwm_tst.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NWM { + +class NWM_TST final : public Interface { +public: + NWM_TST(); + + std::string GetPortName() const override { + return "nwm::TST"; + } +}; + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 80081aae2..08fade320 100644 --- a/src/core/hle/service/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -5,14 +5,12 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/kernel/event.h" -#include "core/hle/service/nwm_uds.h" +#include "core/hle/service/nwm/nwm_uds.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace NWM_UDS +namespace Service { +namespace NWM { -namespace NWM_UDS { - -static Kernel::SharedPtr<Kernel::Event> handle_event; +static Kernel::SharedPtr<Kernel::Event> uds_handle_event; /** * NWM_UDS::Shutdown service function @@ -22,7 +20,7 @@ static Kernel::SharedPtr<Kernel::Event> handle_event; * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ -static void Shutdown(Service::Interface* self) { +static void Shutdown(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(purpasmart): Verify return header on HW @@ -50,7 +48,7 @@ static void Shutdown(Service::Interface* self) { * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ -static void RecvBeaconBroadcastData(Service::Interface* self) { +static void RecvBeaconBroadcastData(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 out_buffer_size = cmd_buff[1]; u32 unk1 = cmd_buff[2]; @@ -90,7 +88,7 @@ static void RecvBeaconBroadcastData(Service::Interface* self) { * 2 : Value 0 * 3 : Output handle */ -static void Initialize(Service::Interface* self) { +static void InitializeWithVersion(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 unk1 = cmd_buff[1]; u32 unk2 = cmd_buff[12]; @@ -103,7 +101,7 @@ static void Initialize(Service::Interface* self) { /* cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; - cmd_buff[3] = Kernel::g_handle_table.Create(handle_event) + cmd_buff[3] = Kernel::g_handle_table.Create(uds_handle_event) .MoveFrom(); // TODO(purpasmart): Verify if this is a event handle */ cmd_buff[0] = IPC::MakeHeader(0x1B, 1, 2); @@ -118,26 +116,29 @@ static void Initialize(Service::Interface* self) { } const Interface::FunctionInfo FunctionTable[] = { + {0x00010442, nullptr, "Initialize (deprecated)"}, {0x00020000, nullptr, "Scrap"}, {0x00030000, Shutdown, "Shutdown"}, - {0x00040402, nullptr, "CreateNetwork"}, + {0x00040402, nullptr, "CreateNetwork (deprecated)"}, {0x00050040, nullptr, "EjectClient"}, {0x00060000, nullptr, "EjectSpectator"}, {0x00070080, nullptr, "UpdateNetworkAttribute"}, {0x00080000, nullptr, "DestroyNetwork"}, + {0x00090442, nullptr, "ConnectNetwork (deprecated)"}, {0x000A0000, nullptr, "DisconnectNetwork"}, {0x000B0000, nullptr, "GetConnectionStatus"}, {0x000D0040, nullptr, "GetNodeInformation"}, + {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"}, {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"}, - {0x00100042, nullptr, "SetBeaconAdditionalData"}, + {0x00100042, nullptr, "SetApplicationData"}, {0x00110040, nullptr, "GetApplicationData"}, {0x00120100, nullptr, "Bind"}, {0x00130040, nullptr, "Unbind"}, - {0x001400C0, nullptr, "RecvBroadcastDataFrame"}, + {0x001400C0, nullptr, "PullPacket"}, {0x00150080, nullptr, "SetMaxSendDelay"}, {0x00170182, nullptr, "SendTo"}, {0x001A0000, nullptr, "GetChannel"}, - {0x001B0302, Initialize, "Initialize"}, + {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, {0x001D0044, nullptr, "BeginHostingNetwork"}, {0x001E0084, nullptr, "ConnectToNetwork"}, {0x001F0006, nullptr, "DecryptBeaconData"}, @@ -146,17 +147,15 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00220402, nullptr, "ScanOnConnection"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { - handle_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM_UDS::handle_event"); +NWM_UDS::NWM_UDS() { + uds_handle_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::uds_handle_event"); Register(FunctionTable); } -Interface::~Interface() { - handle_event = nullptr; +NWM_UDS::~NWM_UDS() { + uds_handle_event = nullptr; } -} // namespace +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h index 0ced2359c..55db748f6 100644 --- a/src/core/hle/service/nwm_uds.h +++ b/src/core/hle/service/nwm/nwm_uds.h @@ -6,21 +6,20 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace NWM_UDS +// Local-WLAN service -// local-WLAN service +namespace Service { +namespace NWM { -namespace NWM_UDS { - -class Interface : public Service::Interface { +class NWM_UDS final : public Interface { public: - Interface(); - ~Interface() override; + NWM_UDS(); + ~NWM_UDS() override; std::string GetPortName() const override { return "nwm::UDS"; } }; -} // namespace +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp index 7d91694f6..caa16f952 100644 --- a/src/core/hle/service/pm_app.cpp +++ b/src/core/hle/service/pm_app.cpp @@ -4,31 +4,30 @@ #include "core/hle/service/pm_app.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace PM_APP - -namespace PM_APP { +namespace Service { +namespace PM { const Interface::FunctionInfo FunctionTable[] = { + // clang-format off {0x00010140, nullptr, "LaunchTitle"}, - {0x00020082, nullptr, "LaunchFIRMSetParams"}, - {0x00030080, nullptr, "TerminateProcesse"}, - {0x00040100, nullptr, "TerminateProcessTID"}, - {0x000500C0, nullptr, "TerminateProcessTID_unknown"}, + {0x00020082, nullptr, "LaunchFIRM"}, + {0x00030080, nullptr, "TerminateApplication"}, + {0x00040100, nullptr, "TerminateTitle"}, + {0x000500C0, nullptr, "TerminateProcess"}, + {0x00060082, nullptr, "PrepareForReboot"}, {0x00070042, nullptr, "GetFIRMLaunchParams"}, {0x00080100, nullptr, "GetTitleExheaderFlags"}, {0x00090042, nullptr, "SetFIRMLaunchParams"}, - {0x000A0140, nullptr, "SetResourceLimit"}, - {0x000B0140, nullptr, "GetResourceLimitMax"}, + {0x000A0140, nullptr, "SetAppResourceLimit"}, + {0x000B0140, nullptr, "GetAppResourceLimit"}, {0x000C0080, nullptr, "UnregisterProcess"}, {0x000D0240, nullptr, "LaunchTitleUpdate"}, + // clang-format on }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +PM_APP::PM_APP() { Register(FunctionTable); } -} // namespace +} // namespace PM +} // namespace Service diff --git a/src/core/hle/service/pm_app.h b/src/core/hle/service/pm_app.h index c1fb1f9da..151c69f3d 100644 --- a/src/core/hle/service/pm_app.h +++ b/src/core/hle/service/pm_app.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace PM_APP +namespace Service { +namespace PM { -namespace PM_APP { - -class Interface : public Service::Interface { +class PM_APP final : public Interface { public: - Interface(); + PM_APP(); std::string GetPortName() const override { return "pm:app"; } }; -} // namespace +} // namespace PM +} // namespace Service diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index cc859c14c..8ff808fd9 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp @@ -6,7 +6,9 @@ #include "core/file_sys/file_backend.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/ptm/ptm.h" +#include "core/hle/service/ptm/ptm_gets.h" #include "core/hle/service/ptm/ptm_play.h" +#include "core/hle/service/ptm/ptm_sets.h" #include "core/hle/service/ptm/ptm_sysm.h" #include "core/hle/service/ptm/ptm_u.h" #include "core/hle/service/service.h" @@ -81,7 +83,7 @@ void GetTotalStepCount(Service::Interface* self) { LOG_WARNING(Service_PTM, "(STUBBED) called"); } -void IsLegacyPowerOff(Service::Interface* self) { +void GetSoftwareClosedFlag(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; @@ -106,9 +108,12 @@ void CheckNew3DS(Service::Interface* self) { } void Init() { - AddService(new PTM_Play_Interface); - AddService(new PTM_Sysm_Interface); - AddService(new PTM_U_Interface); + AddService(new PTM_Gets); + AddService(new PTM_Play); + AddService(new PTM_S); + AddService(new PTM_Sets); + AddService(new PTM_Sysm); + AddService(new PTM_U); shell_open = true; battery_is_charging = true; @@ -137,7 +142,7 @@ void Init() { Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode); if (gamecoin_result.Succeeded()) { auto gamecoin = gamecoin_result.MoveFrom(); - gamecoin->backend->Write(0, sizeof(GameCoin), 1, + gamecoin->backend->Write(0, sizeof(GameCoin), true, reinterpret_cast<const u8*>(&default_game_coin)); gamecoin->backend->Close(); } diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h index 6e163a6f9..a1a628012 100644 --- a/src/core/hle/service/ptm/ptm.h +++ b/src/core/hle/service/ptm/ptm.h @@ -82,12 +82,13 @@ void GetBatteryChargeState(Interface* self); void GetTotalStepCount(Interface* self); /** - * PTM::IsLegacyPowerOff service function + * PTM::GetSoftwareClosedFlag service function * Outputs: * 1: Result code, 0 on success, otherwise error code - * 2: Whether the system is going through a power off + * 2: Whether or not the "software closed" dialog was requested by the last FIRM + * and should be displayed. */ -void IsLegacyPowerOff(Interface* self); +void GetSoftwareClosedFlag(Interface* self); /** * PTM::CheckNew3DS service function diff --git a/src/core/hle/service/ptm/ptm_gets.cpp b/src/core/hle/service/ptm/ptm_gets.cpp new file mode 100644 index 000000000..b23e508d6 --- /dev/null +++ b/src/core/hle/service/ptm/ptm_gets.cpp @@ -0,0 +1,37 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ptm/ptm.h" +#include "core/hle/service/ptm/ptm_gets.h" + +namespace Service { +namespace PTM { + +const Interface::FunctionInfo FunctionTable[] = { + // ptm:u common commands + {0x00010002, nullptr, "RegisterAlarmClient"}, + {0x00020080, nullptr, "SetRtcAlarm"}, + {0x00030000, nullptr, "GetRtcAlarm"}, + {0x00040000, nullptr, "CancelRtcAlarm"}, + {0x00050000, GetAdapterState, "GetAdapterState"}, + {0x00060000, GetShellState, "GetShellState"}, + {0x00070000, GetBatteryLevel, "GetBatteryLevel"}, + {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"}, + {0x00090000, nullptr, "GetPedometerState"}, + {0x000A0042, nullptr, "GetStepHistoryEntry"}, + {0x000B00C2, nullptr, "GetStepHistory"}, + {0x000C0000, GetTotalStepCount, "GetTotalStepCount"}, + {0x000D0040, nullptr, "SetPedometerRecordingMode"}, + {0x000E0000, nullptr, "GetPedometerRecordingMode"}, + {0x000F0084, nullptr, "GetStepHistoryAll"}, + // ptm:gets + {0x04010000, nullptr, "GetSystemTime"}, +}; + +PTM_Gets::PTM_Gets() { + Register(FunctionTable); +} + +} // namespace PTM +} // namespace Service diff --git a/src/core/hle/service/ptm/ptm_gets.h b/src/core/hle/service/ptm/ptm_gets.h new file mode 100644 index 000000000..5552c9eff --- /dev/null +++ b/src/core/hle/service/ptm/ptm_gets.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace PTM { + +class PTM_Gets final : public Interface { +public: + PTM_Gets(); + + std::string GetPortName() const override { + return "ptm:gets"; + } +}; + +} // namespace PTM +} // namespace Service diff --git a/src/core/hle/service/ptm/ptm_play.cpp b/src/core/hle/service/ptm/ptm_play.cpp index 2e0c6e1a3..bcb00e0d4 100644 --- a/src/core/hle/service/ptm/ptm_play.cpp +++ b/src/core/hle/service/ptm/ptm_play.cpp @@ -2,19 +2,37 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/ptm/ptm.h" #include "core/hle/service/ptm/ptm_play.h" namespace Service { namespace PTM { const Interface::FunctionInfo FunctionTable[] = { + // ptm:u common commands + {0x00010002, nullptr, "RegisterAlarmClient"}, + {0x00020080, nullptr, "SetRtcAlarm"}, + {0x00030000, nullptr, "GetRtcAlarm"}, + {0x00040000, nullptr, "CancelRtcAlarm"}, + {0x00050000, GetAdapterState, "GetAdapterState"}, + {0x00060000, GetShellState, "GetShellState"}, + {0x00070000, GetBatteryLevel, "GetBatteryLevel"}, + {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"}, + {0x00090000, nullptr, "GetPedometerState"}, + {0x000A0042, nullptr, "GetStepHistoryEntry"}, + {0x000B00C2, nullptr, "GetStepHistory"}, + {0x000C0000, GetTotalStepCount, "GetTotalStepCount"}, + {0x000D0040, nullptr, "SetPedometerRecordingMode"}, + {0x000E0000, nullptr, "GetPedometerRecordingMode"}, + {0x000F0084, nullptr, "GetStepHistoryAll"}, + // ptm:play {0x08070082, nullptr, "GetPlayHistory"}, {0x08080000, nullptr, "GetPlayHistoryStart"}, {0x08090000, nullptr, "GetPlayHistoryLength"}, {0x080B0080, nullptr, "CalcPlayHistoryStart"}, }; -PTM_Play_Interface::PTM_Play_Interface() { +PTM_Play::PTM_Play() { Register(FunctionTable); } diff --git a/src/core/hle/service/ptm/ptm_play.h b/src/core/hle/service/ptm/ptm_play.h index 47f229581..663faabee 100644 --- a/src/core/hle/service/ptm/ptm_play.h +++ b/src/core/hle/service/ptm/ptm_play.h @@ -9,9 +9,9 @@ namespace Service { namespace PTM { -class PTM_Play_Interface : public Service::Interface { +class PTM_Play final : public Interface { public: - PTM_Play_Interface(); + PTM_Play(); std::string GetPortName() const override { return "ptm:play"; diff --git a/src/core/hle/service/ptm/ptm_sets.cpp b/src/core/hle/service/ptm/ptm_sets.cpp new file mode 100644 index 000000000..a8c6cf227 --- /dev/null +++ b/src/core/hle/service/ptm/ptm_sets.cpp @@ -0,0 +1,20 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ptm/ptm_sets.h" + +namespace Service { +namespace PTM { + +const Interface::FunctionInfo FunctionTable[] = { + // Note that this service does not have access to ptm:u's common commands + {0x00010080, nullptr, "SetSystemTime"}, +}; + +PTM_Sets::PTM_Sets() { + Register(FunctionTable); +} + +} // namespace PTM +} // namespace Service diff --git a/src/core/hle/service/ptm/ptm_sets.h b/src/core/hle/service/ptm/ptm_sets.h new file mode 100644 index 000000000..d33b047e5 --- /dev/null +++ b/src/core/hle/service/ptm/ptm_sets.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace PTM { + +class PTM_Sets final : public Interface { +public: + PTM_Sets(); + + std::string GetPortName() const override { + return "ptm:sets"; + } +}; + +} // namespace PTM +} // namespace Service diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp index 693158dbf..f95dfdbb1 100644 --- a/src/core/hle/service/ptm/ptm_sysm.cpp +++ b/src/core/hle/service/ptm/ptm_sysm.cpp @@ -9,6 +9,23 @@ namespace Service { namespace PTM { const Interface::FunctionInfo FunctionTable[] = { + // ptm:u common commands + {0x00010002, nullptr, "RegisterAlarmClient"}, + {0x00020080, nullptr, "SetRtcAlarm"}, + {0x00030000, nullptr, "GetRtcAlarm"}, + {0x00040000, nullptr, "CancelRtcAlarm"}, + {0x00050000, GetAdapterState, "GetAdapterState"}, + {0x00060000, GetShellState, "GetShellState"}, + {0x00070000, GetBatteryLevel, "GetBatteryLevel"}, + {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"}, + {0x00090000, nullptr, "GetPedometerState"}, + {0x000A0042, nullptr, "GetStepHistoryEntry"}, + {0x000B00C2, nullptr, "GetStepHistory"}, + {0x000C0000, GetTotalStepCount, "GetTotalStepCount"}, + {0x000D0040, nullptr, "SetPedometerRecordingMode"}, + {0x000E0000, nullptr, "GetPedometerRecordingMode"}, + {0x000F0084, nullptr, "GetStepHistoryAll"}, + // ptm:sysm {0x040100C0, nullptr, "SetRtcAlarmEx"}, {0x04020042, nullptr, "ReplySleepQuery"}, {0x04030042, nullptr, "NotifySleepPreparationComplete"}, @@ -33,8 +50,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x080C0080, nullptr, "SetUserTime"}, {0x080D0000, nullptr, "InvalidateSystemTime"}, {0x080E0140, nullptr, "NotifyPlayEvent"}, - {0x080F0000, IsLegacyPowerOff, "IsLegacyPowerOff"}, - {0x08100000, nullptr, "ClearLegacyPowerOff"}, + {0x080F0000, GetSoftwareClosedFlag, "GetSoftwareClosedFlag"}, + {0x08100000, nullptr, "ClearSoftwareClosedFlag"}, {0x08110000, GetShellState, "GetShellState"}, {0x08120000, nullptr, "IsShutdownByBatteryEmpty"}, {0x08130000, nullptr, "FormatSavedata"}, @@ -42,7 +59,11 @@ const Interface::FunctionInfo FunctionTable[] = { {0x08180040, nullptr, "ConfigureNew3DSCPU"}, }; -PTM_Sysm_Interface::PTM_Sysm_Interface() { +PTM_S::PTM_S() { + Register(FunctionTable); +} + +PTM_Sysm::PTM_Sysm() { Register(FunctionTable); } diff --git a/src/core/hle/service/ptm/ptm_sysm.h b/src/core/hle/service/ptm/ptm_sysm.h index e37f20546..8afcebbba 100644 --- a/src/core/hle/service/ptm/ptm_sysm.h +++ b/src/core/hle/service/ptm/ptm_sysm.h @@ -9,9 +9,18 @@ namespace Service { namespace PTM { -class PTM_Sysm_Interface : public Interface { +class PTM_S final : public Interface { public: - PTM_Sysm_Interface(); + PTM_S(); + + std::string GetPortName() const override { + return "ptm:s"; + } +}; + +class PTM_Sysm final : public Interface { +public: + PTM_Sysm(); std::string GetPortName() const override { return "ptm:sysm"; diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp index 65e868393..e0b65ba89 100644 --- a/src/core/hle/service/ptm/ptm_u.cpp +++ b/src/core/hle/service/ptm/ptm_u.cpp @@ -26,7 +26,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000F0084, nullptr, "GetStepHistoryAll"}, }; -PTM_U_Interface::PTM_U_Interface() { +PTM_U::PTM_U() { Register(FunctionTable); } diff --git a/src/core/hle/service/ptm/ptm_u.h b/src/core/hle/service/ptm/ptm_u.h index bf132f610..7b75d6e49 100644 --- a/src/core/hle/service/ptm/ptm_u.h +++ b/src/core/hle/service/ptm/ptm_u.h @@ -9,9 +9,9 @@ namespace Service { namespace PTM { -class PTM_U_Interface : public Interface { +class PTM_U final : public Interface { public: - PTM_U_Interface(); + PTM_U(); std::string GetPortName() const override { return "ptm:u"; diff --git a/src/core/hle/service/qtm/qtm.cpp b/src/core/hle/service/qtm/qtm.cpp new file mode 100644 index 000000000..f11542263 --- /dev/null +++ b/src/core/hle/service/qtm/qtm.cpp @@ -0,0 +1,21 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/qtm/qtm.h" +#include "core/hle/service/qtm/qtm_s.h" +#include "core/hle/service/qtm/qtm_sp.h" +#include "core/hle/service/qtm/qtm_u.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace QTM { + +void Init() { + AddService(new QTM_S()); + AddService(new QTM_SP()); + AddService(new QTM_U()); +} + +} // namespace QTM +} // namespace Service diff --git a/src/core/hle/service/qtm/qtm.h b/src/core/hle/service/qtm/qtm.h new file mode 100644 index 000000000..33b774c79 --- /dev/null +++ b/src/core/hle/service/qtm/qtm.h @@ -0,0 +1,14 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service { +namespace QTM { + +/// Initializes all QTM services. +void Init(); + +} // namespace QTM +} // namespace Service diff --git a/src/core/hle/service/qtm/qtm_s.cpp b/src/core/hle/service/qtm/qtm_s.cpp new file mode 100644 index 000000000..ad7df24a0 --- /dev/null +++ b/src/core/hle/service/qtm/qtm_s.cpp @@ -0,0 +1,23 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/qtm/qtm_s.h" + +namespace Service { +namespace QTM { + +const Interface::FunctionInfo FunctionTable[] = { + // clang-format off + // qtm common commands + {0x00010080, nullptr, "GetHeadtrackingInfoRaw"}, + {0x00020080, nullptr, "GetHeadtrackingInfo"}, + // clang-format on +}; + +QTM_S::QTM_S() { + Register(FunctionTable); +} + +} // namespace QTM +} // namespace Service diff --git a/src/core/hle/service/qtm/qtm_s.h b/src/core/hle/service/qtm/qtm_s.h new file mode 100644 index 000000000..e66138ed0 --- /dev/null +++ b/src/core/hle/service/qtm/qtm_s.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace QTM { + +class QTM_S final : public Interface { +public: + QTM_S(); + + std::string GetPortName() const override { + return "qtm:s"; + } +}; + +} // namespace QTM +} // namespace Service diff --git a/src/core/hle/service/qtm/qtm_sp.cpp b/src/core/hle/service/qtm/qtm_sp.cpp new file mode 100644 index 000000000..6e0695d34 --- /dev/null +++ b/src/core/hle/service/qtm/qtm_sp.cpp @@ -0,0 +1,23 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/qtm/qtm_sp.h" + +namespace Service { +namespace QTM { + +const Interface::FunctionInfo FunctionTable[] = { + // clang-format off + // qtm common commands + {0x00010080, nullptr, "GetHeadtrackingInfoRaw"}, + {0x00020080, nullptr, "GetHeadtrackingInfo"}, + // clang-format on +}; + +QTM_SP::QTM_SP() { + Register(FunctionTable); +} + +} // namespace QTM +} // namespace Service diff --git a/src/core/hle/service/qtm/qtm_sp.h b/src/core/hle/service/qtm/qtm_sp.h new file mode 100644 index 000000000..0ae0618fc --- /dev/null +++ b/src/core/hle/service/qtm/qtm_sp.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace QTM { + +class QTM_SP final : public Interface { +public: + QTM_SP(); + + std::string GetPortName() const override { + return "qtm:sp"; + } +}; + +} // namespace QTM +} // namespace Service diff --git a/src/core/hle/service/qtm/qtm_u.cpp b/src/core/hle/service/qtm/qtm_u.cpp new file mode 100644 index 000000000..a0f808432 --- /dev/null +++ b/src/core/hle/service/qtm/qtm_u.cpp @@ -0,0 +1,23 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/qtm/qtm_u.h" + +namespace Service { +namespace QTM { + +const Interface::FunctionInfo FunctionTable[] = { + // clang-format off + // qtm common commands + {0x00010080, nullptr, "GetHeadtrackingInfoRaw"}, + {0x00020080, nullptr, "GetHeadtrackingInfo"}, + // clang-format on +}; + +QTM_U::QTM_U() { + Register(FunctionTable); +} + +} // namespace QTM +} // namespace Service diff --git a/src/core/hle/service/qtm/qtm_u.h b/src/core/hle/service/qtm/qtm_u.h new file mode 100644 index 000000000..1ed4c0adc --- /dev/null +++ b/src/core/hle/service/qtm/qtm_u.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace QTM { + +class QTM_U final : public Interface { +public: + QTM_U(); + + std::string GetPortName() const override { + return "qtm:u"; + } +}; + +} // namespace QTM +} // namespace Service diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index ca7eeac8a..7e52a05d9 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -2,11 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <boost/range/algorithm_ext/erase.hpp> + #include "common/logging/log.h" #include "common/string_util.h" + +#include "core/hle/kernel/server_port.h" #include "core/hle/service/ac_u.h" -#include "core/hle/service/act_a.h" -#include "core/hle/service/act_u.h" +#include "core/hle/service/act/act.h" #include "core/hle/service/am/am.h" #include "core/hle/service/apt/apt.h" #include "core/hle/service/boss/boss.h" @@ -26,13 +29,16 @@ #include "core/hle/service/ir/ir.h" #include "core/hle/service/ldr_ro/ldr_ro.h" #include "core/hle/service/mic_u.h" +#include "core/hle/service/mvd/mvd.h" #include "core/hle/service/ndm/ndm.h" #include "core/hle/service/news/news.h" +#include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/ns_s.h" -#include "core/hle/service/nwm_uds.h" +#include "core/hle/service/nwm/nwm.h" #include "core/hle/service/pm_app.h" #include "core/hle/service/ptm/ptm.h" +#include "core/hle/service/qtm/qtm.h" #include "core/hle/service/service.h" #include "core/hle/service/soc_u.h" #include "core/hle/service/srv.h" @@ -41,8 +47,8 @@ namespace Service { -std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports; -std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services; +std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; +std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_srv_services; /** * Creates a function string for logging, complete with the name (or header code, depending @@ -61,7 +67,23 @@ static std::string MakeFunctionString(const char* name, const char* port_name, return function_string; } -ResultVal<bool> Interface::SyncRequest() { +void SessionRequestHandler::ClientConnected( + Kernel::SharedPtr<Kernel::ServerSession> server_session) { + connected_sessions.push_back(server_session); +} + +void SessionRequestHandler::ClientDisconnected( + Kernel::SharedPtr<Kernel::ServerSession> server_session) { + boost::range::remove_erase(connected_sessions, server_session); +} + +Interface::Interface(u32 max_sessions) : max_sessions(max_sessions) {} +Interface::~Interface() = default; + +void Interface::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { + // TODO(Subv): Make use of the server_session in the HLE service handlers to distinguish which + // session triggered each command. + u32* cmd_buff = Kernel::GetCommandBuffer(); auto itr = m_functions.find(cmd_buff[0]); @@ -75,14 +97,12 @@ ResultVal<bool> Interface::SyncRequest() { // TODO(bunnei): Hack - ignore error cmd_buff[1] = 0; - return MakeResult<bool>(false); + return; } LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); itr->second.func(this); - - return MakeResult<bool>(false); // TODO: Implement return from actual function } void Interface::Register(const FunctionInfo* functions, size_t n) { @@ -97,72 +117,81 @@ void Interface::Register(const FunctionInfo* functions, size_t n) { // Module interface static void AddNamedPort(Interface* interface_) { - g_kernel_named_ports.emplace(interface_->GetPortName(), interface_); + auto ports = + Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName(), + std::shared_ptr<Interface>(interface_)); + auto client_port = std::get<Kernel::SharedPtr<Kernel::ClientPort>>(ports); + g_kernel_named_ports.emplace(interface_->GetPortName(), std::move(client_port)); } void AddService(Interface* interface_) { - g_srv_services.emplace(interface_->GetPortName(), interface_); + auto ports = + Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName(), + std::shared_ptr<Interface>(interface_)); + auto client_port = std::get<Kernel::SharedPtr<Kernel::ClientPort>>(ports); + g_srv_services.emplace(interface_->GetPortName(), std::move(client_port)); } /// Initialize ServiceManager void Init() { - AddNamedPort(new SRV::Interface); - AddNamedPort(new ERR_F::Interface); - - Service::FS::ArchiveInit(); - Service::AM::Init(); - Service::APT::Init(); - Service::BOSS::Init(); - Service::CAM::Init(); - Service::CECD::Init(); - Service::CFG::Init(); - Service::DLP::Init(); - Service::FRD::Init(); - Service::HID::Init(); - Service::IR::Init(); - Service::NEWS::Init(); - Service::NDM::Init(); - Service::NIM::Init(); - Service::PTM::Init(); - - AddService(new AC_U::Interface); - AddService(new ACT_A::Interface); - AddService(new ACT_U::Interface); - AddService(new CSND_SND::Interface); + AddNamedPort(new SRV::SRV); + AddNamedPort(new ERR::ERR_F); + + FS::ArchiveInit(); + ACT::Init(); + AM::Init(); + APT::Init(); + BOSS::Init(); + CAM::Init(); + CECD::Init(); + CFG::Init(); + DLP::Init(); + FRD::Init(); + HID::Init(); + IR::Init(); + MVD::Init(); + NDM::Init(); + NEWS::Init(); + NFC::Init(); + NIM::Init(); + NWM::Init(); + PTM::Init(); + QTM::Init(); + + AddService(new AC::AC_U); + AddService(new CSND::CSND_SND); AddService(new DSP_DSP::Interface); - AddService(new GSP_GPU::Interface); - AddService(new GSP_LCD::Interface); - AddService(new HTTP_C::Interface); - AddService(new LDR_RO::Interface); - AddService(new MIC_U::Interface); - AddService(new NS_S::Interface); - AddService(new NWM_UDS::Interface); - AddService(new PM_APP::Interface); - AddService(new SOC_U::Interface); - AddService(new SSL_C::Interface); - AddService(new Y2R_U::Interface); + AddService(new GSP::GSP_GPU); + AddService(new GSP::GSP_LCD); + AddService(new HTTP::HTTP_C); + AddService(new LDR::LDR_RO); + AddService(new MIC::MIC_U); + AddService(new NS::NS_S); + AddService(new PM::PM_APP); + AddService(new SOC::SOC_U); + AddService(new SSL::SSL_C); + AddService(new Y2R::Y2R_U); LOG_DEBUG(Service, "initialized OK"); } /// Shutdown ServiceManager void Shutdown() { - - Service::PTM::Shutdown(); - Service::NDM::Shutdown(); - Service::NIM::Shutdown(); - Service::NEWS::Shutdown(); - Service::IR::Shutdown(); - Service::HID::Shutdown(); - Service::FRD::Shutdown(); - Service::DLP::Shutdown(); - Service::CFG::Shutdown(); - Service::CECD::Shutdown(); - Service::CAM::Shutdown(); - Service::BOSS::Shutdown(); - Service::APT::Shutdown(); - Service::AM::Shutdown(); - Service::FS::ArchiveShutdown(); + PTM::Shutdown(); + NIM::Shutdown(); + NEWS::Shutdown(); + NDM::Shutdown(); + IR::Shutdown(); + HID::Shutdown(); + FRD::Shutdown(); + DLP::Shutdown(); + CFG::Shutdown(); + CECD::Shutdown(); + CAM::Shutdown(); + BOSS::Shutdown(); + APT::Shutdown(); + AM::Shutdown(); + FS::ArchiveShutdown(); g_srv_services.clear(); g_kernel_named_ports.clear(); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 29daacfc4..a7ba7688f 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -9,8 +9,15 @@ #include <unordered_map> #include <boost/container/flat_map.hpp> #include "common/common_types.h" -#include "core/hle/kernel/session.h" +#include "core/hle/ipc.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/thread.h" #include "core/hle/result.h" +#include "core/memory.h" + +namespace Kernel { +class ServerSession; +} //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Service @@ -18,14 +25,63 @@ namespace Service { static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) +/// Arbitrary default number of maximum connections to an HLE service. +static const u32 DefaultMaxSessions = 10; + +/** + * Interface implemented by HLE Session handlers. + * This can be provided to a ServerSession in order to hook into several relevant events + * (such as a new connection or a SyncRequest) so they can be implemented in the emulator. + */ +class SessionRequestHandler { +public: + /** + * Handles a sync request from the emulated application. + * @param server_session The ServerSession that was triggered for this sync request, + * it should be used to differentiate which client (As in ClientSession) we're answering to. + * TODO(Subv): Use a wrapper structure to hold all the information relevant to + * this request (ServerSession, Originator thread, Translated command buffer, etc). + * @returns ResultCode the result code of the translate operation. + */ + virtual void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) = 0; + + /** + * Signals that a client has just connected to this HLE handler and keeps the + * associated ServerSession alive for the duration of the connection. + * @param server_session Owning pointer to the ServerSession associated with the connection. + */ + void ClientConnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); + + /** + * Signals that a client has just disconnected from this HLE handler and releases the + * associated ServerSession. + * @param server_session ServerSession associated with the connection. + */ + void ClientDisconnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); -/// Interface to a CTROS service -class Interface : public Kernel::Session { - // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be - // just something that encapsulates a session and acts as a helper to implement service - // processes. +protected: + /// List of sessions that are connected to this handler. + /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list + // for the duration of the connection. + std::vector<Kernel::SharedPtr<Kernel::ServerSession>> connected_sessions; +}; + +/** + * Framework for implementing HLE service handlers which dispatch incoming SyncRequests based on a + * table mapping header ids to handler functions. + */ +class Interface : public SessionRequestHandler { public: - std::string GetName() const override { + /** + * Creates an HLE interface with the specified max sessions. + * @param max_sessions Maximum number of sessions that can be + * connected to this service at the same time. + */ + Interface(u32 max_sessions = DefaultMaxSessions); + + virtual ~Interface(); + + std::string GetName() const { return GetPortName(); } @@ -33,6 +89,15 @@ public: version.raw = raw_version; } + /** + * Gets the maximum allowed number of sessions that can be connected to this service + * at the same time. + * @returns The maximum number of connections allowed. + */ + u32 GetMaxSessions() const { + return max_sessions; + } + typedef void (*Function)(Interface*); struct FunctionInfo { @@ -49,9 +114,9 @@ public: return "[UNKNOWN SERVICE PORT]"; } - ResultVal<bool> SyncRequest() override; - protected: + void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; + /** * Registers the functions in the service */ @@ -71,6 +136,7 @@ protected: } version = {}; private: + u32 max_sessions; ///< Maximum number of concurrent sessions that this service can handle. boost::container::flat_map<u32, FunctionInfo> m_functions; }; @@ -81,9 +147,9 @@ void Init(); void Shutdown(); /// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC. -extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports; +extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; /// Map of services registered with the "srv:" service, retrieved using GetServiceHandle. -extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services; +extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_srv_services; /// Adds a service to the services table void AddService(Interface* interface_); diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 46b75db25..c3918cdd0 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -11,7 +11,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/scope_exit.h" -#include "core/hle/kernel/session.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/result.h" #include "core/hle/service/soc_u.h" #include "core/memory.h" @@ -53,12 +53,10 @@ #define closesocket(x) close(x) #endif -static const s32 SOCKET_ERROR_VALUE = -1; +namespace Service { +namespace SOC { -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace SOC_U - -namespace SOC_U { +const s32 SOCKET_ERROR_VALUE = -1; /// Holds the translation from system network errors to 3DS network errors static const std::unordered_map<int, int> error_map = {{ @@ -339,7 +337,7 @@ static void CleanupSockets() { open_sockets.clear(); } -static void Socket(Service::Interface* self) { +static void Socket(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 domain = cmd_buffer[1]; // Address family u32 type = cmd_buffer[2]; @@ -378,7 +376,7 @@ static void Socket(Service::Interface* self) { cmd_buffer[2] = socket_handle; } -static void Bind(Service::Interface* self) { +static void Bind(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 socket_handle = cmd_buffer[1]; u32 len = cmd_buffer[2]; @@ -406,7 +404,7 @@ static void Bind(Service::Interface* self) { cmd_buffer[2] = res; } -static void Fcntl(Service::Interface* self) { +static void Fcntl(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 socket_handle = cmd_buffer[1]; u32 ctr_cmd = cmd_buffer[2]; @@ -475,7 +473,7 @@ static void Fcntl(Service::Interface* self) { } } -static void Listen(Service::Interface* self) { +static void Listen(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 socket_handle = cmd_buffer[1]; u32 backlog = cmd_buffer[2]; @@ -490,7 +488,7 @@ static void Listen(Service::Interface* self) { cmd_buffer[2] = ret; } -static void Accept(Service::Interface* self) { +static void Accept(Interface* self) { // TODO(Subv): Calling this function on a blocking socket will block the emu thread, // preventing graceful shutdown when closing the emulator, this can be fixed by always // performing nonblocking operations and spinlock until the data is available @@ -518,7 +516,7 @@ static void Accept(Service::Interface* self) { cmd_buffer[3] = IPC::StaticBufferDesc(static_cast<u32>(max_addr_len), 0); } -static void GetHostId(Service::Interface* self) { +static void GetHostId(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); char name[128]; @@ -536,7 +534,7 @@ static void GetHostId(Service::Interface* self) { freeaddrinfo(res); } -static void Close(Service::Interface* self) { +static void Close(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 socket_handle = cmd_buffer[1]; @@ -553,7 +551,7 @@ static void Close(Service::Interface* self) { cmd_buffer[1] = result; } -static void SendTo(Service::Interface* self) { +static void SendTo(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 socket_handle = cmd_buffer[1]; u32 len = cmd_buffer[2]; @@ -597,7 +595,7 @@ static void SendTo(Service::Interface* self) { cmd_buffer[1] = result; } -static void RecvFrom(Service::Interface* self) { +static void RecvFrom(Interface* self) { // TODO(Subv): Calling this function on a blocking socket will block the emu thread, // preventing graceful shutdown when closing the emulator, this can be fixed by always // performing nonblocking operations and spinlock until the data is available @@ -654,7 +652,7 @@ static void RecvFrom(Service::Interface* self) { cmd_buffer[3] = total_received; } -static void Poll(Service::Interface* self) { +static void Poll(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 nfds = cmd_buffer[1]; int timeout = cmd_buffer[2]; @@ -692,7 +690,7 @@ static void Poll(Service::Interface* self) { cmd_buffer[2] = ret; } -static void GetSockName(Service::Interface* self) { +static void GetSockName(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 socket_handle = cmd_buffer[1]; socklen_t ctr_len = cmd_buffer[2]; @@ -720,7 +718,7 @@ static void GetSockName(Service::Interface* self) { cmd_buffer[1] = result; } -static void Shutdown(Service::Interface* self) { +static void Shutdown(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 socket_handle = cmd_buffer[1]; int how = cmd_buffer[2]; @@ -733,7 +731,7 @@ static void Shutdown(Service::Interface* self) { cmd_buffer[1] = result; } -static void GetPeerName(Service::Interface* self) { +static void GetPeerName(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 socket_handle = cmd_buffer[1]; socklen_t len = cmd_buffer[2]; @@ -761,7 +759,7 @@ static void GetPeerName(Service::Interface* self) { cmd_buffer[1] = result; } -static void Connect(Service::Interface* self) { +static void Connect(Interface* self) { // TODO(Subv): Calling this function on a blocking socket will block the emu thread, // preventing graceful shutdown when closing the emulator, this can be fixed by always // performing nonblocking operations and spinlock until the data is available @@ -790,7 +788,7 @@ static void Connect(Service::Interface* self) { cmd_buffer[2] = ret; } -static void InitializeSockets(Service::Interface* self) { +static void InitializeSockets(Interface* self) { // TODO(Subv): Implement #ifdef _WIN32 WSADATA data; @@ -802,7 +800,7 @@ static void InitializeSockets(Service::Interface* self) { cmd_buffer[1] = RESULT_SUCCESS.raw; } -static void ShutdownSockets(Service::Interface* self) { +static void ShutdownSockets(Interface* self) { // TODO(Subv): Implement CleanupSockets(); @@ -814,7 +812,7 @@ static void ShutdownSockets(Service::Interface* self) { cmd_buffer[1] = 0; } -static void GetSockOpt(Service::Interface* self) { +static void GetSockOpt(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 socket_handle = cmd_buffer[1]; u32 level = cmd_buffer[2]; @@ -849,7 +847,7 @@ static void GetSockOpt(Service::Interface* self) { cmd_buffer[3] = optlen; } -static void SetSockOpt(Service::Interface* self) { +static void SetSockOpt(Interface* self) { u32* cmd_buffer = Kernel::GetCommandBuffer(); u32 socket_handle = cmd_buffer[1]; u32 level = cmd_buffer[2]; @@ -916,18 +914,16 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00230040, nullptr, "AddGlobalSocket"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +SOC_U::SOC_U() { Register(FunctionTable); } -Interface::~Interface() { +SOC_U::~SOC_U() { CleanupSockets(); #ifdef _WIN32 WSACleanup(); #endif } -} // namespace +} // namespace SOC +} // namespace Service diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h index 8d02ed30f..5f829fc1c 100644 --- a/src/core/hle/service/soc_u.h +++ b/src/core/hle/service/soc_u.h @@ -7,19 +7,18 @@ #include <string> #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace SOC_U +namespace Service { +namespace SOC { -namespace SOC_U { - -class Interface : public Service::Interface { +class SOC_U final : public Interface { public: - Interface(); - ~Interface(); + SOC_U(); + ~SOC_U(); std::string GetPortName() const override { return "soc:U"; } }; -} // namespace +} // namespace SOC +} // namespace Service diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index b25be413a..3bd787147 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -2,14 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <tuple> + #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/kernel/client_session.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/service/srv.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace SRV - +namespace Service { namespace SRV { static Kernel::SharedPtr<Kernel::Event> event_handle; @@ -23,7 +25,7 @@ static Kernel::SharedPtr<Kernel::Event> event_handle; * 0: 0x00010040 * 1: ResultCode */ -static void RegisterClient(Service::Interface* self) { +static void RegisterClient(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); if (cmd_buff[1] != IPC::CallingPidDesc()) { @@ -48,7 +50,7 @@ static void RegisterClient(Service::Interface* self) { * 2: Translation descriptor: 0x20 * 3: Handle to semaphore signaled on process notification */ -static void EnableNotification(Service::Interface* self) { +static void EnableNotification(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(bunnei): Change to a semaphore once these have been implemented @@ -73,7 +75,7 @@ static void EnableNotification(Service::Interface* self) { * 1: ResultCode * 3: Service handle */ -static void GetServiceHandle(Service::Interface* self) { +static void GetServiceHandle(Interface* self) { ResultCode res = RESULT_SUCCESS; u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -81,7 +83,15 @@ static void GetServiceHandle(Service::Interface* self) { auto it = Service::g_srv_services.find(port_name); if (it != Service::g_srv_services.end()) { - cmd_buff[3] = Kernel::g_handle_table.Create(it->second).MoveFrom(); + auto client_port = it->second; + + auto client_session = client_port->Connect(); + res = client_session.Code(); + + if (client_session.Succeeded()) { + // Return the client session + cmd_buff[3] = Kernel::g_handle_table.Create(*client_session).MoveFrom(); + } LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); } else { LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); @@ -99,7 +109,7 @@ static void GetServiceHandle(Service::Interface* self) { * 0: 0x00090040 * 1: ResultCode */ -static void Subscribe(Service::Interface* self) { +static void Subscribe(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 notification_id = cmd_buff[1]; @@ -118,7 +128,7 @@ static void Subscribe(Service::Interface* self) { * 0: 0x000A0040 * 1: ResultCode */ -static void Unsubscribe(Service::Interface* self) { +static void Unsubscribe(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 notification_id = cmd_buff[1]; @@ -138,7 +148,7 @@ static void Unsubscribe(Service::Interface* self) { * 0: 0x000C0040 * 1: ResultCode */ -static void PublishToSubscriber(Service::Interface* self) { +static void PublishToSubscriber(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 notification_id = cmd_buff[1]; @@ -167,16 +177,14 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000E00C0, nullptr, "IsServiceRegistered"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +SRV::SRV() { Register(FunctionTable); event_handle = nullptr; } -Interface::~Interface() { +SRV::~SRV() { event_handle = nullptr; } } // namespace SRV +} // namespace Service diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h index 96c89b025..d3a9de879 100644 --- a/src/core/hle/service/srv.h +++ b/src/core/hle/service/srv.h @@ -2,22 +2,23 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/service/service.h" +#pragma once -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace SRV +#include "core/hle/service/service.h" +namespace Service { namespace SRV { /// Interface to "srv:" service -class Interface : public Service::Interface { +class SRV final : public Interface { public: - Interface(); - ~Interface() override; + SRV(); + ~SRV() override; std::string GetPortName() const override { return "srv:"; } }; -} // namespace +} // namespace SRV +} // namespace Service diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp index abab1d271..09ced9d7a 100644 --- a/src/core/hle/service/ssl_c.cpp +++ b/src/core/hle/service/ssl_c.cpp @@ -6,15 +6,13 @@ #include "common/common_types.h" #include "core/hle/service/ssl_c.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace SSL_C - -namespace SSL_C { +namespace Service { +namespace SSL { // TODO: Implement a proper CSPRNG in the future when actual security is needed static std::mt19937 rand_gen; -static void Initialize(Service::Interface* self) { +static void Initialize(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // Seed random number generator when the SSL service is initialized @@ -25,7 +23,7 @@ static void Initialize(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; } -static void GenerateRandomData(Service::Interface* self) { +static void GenerateRandomData(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 size = cmd_buff[1]; @@ -66,6 +64,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00050082, nullptr, "AddTrustedRootCA"}, {0x00060080, nullptr, "RootCertChainAddDefaultCert"}, {0x00070080, nullptr, "RootCertChainRemoveCert"}, + {0x000D0084, nullptr, "OpenClientCertContext"}, {0x000E0040, nullptr, "OpenDefaultClientCertContext"}, {0x000F0040, nullptr, "CloseClientCertContext"}, {0x00110042, GenerateRandomData, "GenerateRandomData"}, @@ -73,19 +72,19 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00130040, nullptr, "StartConnection"}, {0x00140040, nullptr, "StartConnectionGetOut"}, {0x00150082, nullptr, "Read"}, + {0x00160082, nullptr, "ReadPeek"}, {0x00170082, nullptr, "Write"}, {0x00180080, nullptr, "ContextSetRootCertChain"}, {0x00190080, nullptr, "ContextSetClientCert"}, {0x001B0080, nullptr, "ContextClearOpt"}, + {0x001C00C4, nullptr, "ContextGetProtocolCipher"}, {0x001E0040, nullptr, "DestroyContext"}, {0x001F0082, nullptr, "ContextInitSharedmem"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +SSL_C::SSL_C() { Register(FunctionTable); } -} // namespace +} // namespace SSL_C +} // namespace Service diff --git a/src/core/hle/service/ssl_c.h b/src/core/hle/service/ssl_c.h index 58e87c1cb..fc50a2eb2 100644 --- a/src/core/hle/service/ssl_c.h +++ b/src/core/hle/service/ssl_c.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace SSL_C +namespace Service { +namespace SSL { -namespace SSL_C { - -class Interface : public Service::Interface { +class SSL_C final : public Interface { public: - Interface(); + SSL_C(); std::string GetPortName() const override { return "ssl:C"; } }; -} // namespace +} // namespace SSL +} // namespace Service diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 097e09d28..a20194107 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -11,10 +11,8 @@ #include "core/hle/service/y2r_u.h" #include "core/hw/y2r.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace Y2R_U - -namespace Y2R_U { +namespace Service { +namespace Y2R { struct ConversionParameters { InputFormat input_format; @@ -83,7 +81,7 @@ ResultCode ConversionConfiguration::SetStandardCoefficient( return RESULT_SUCCESS; } -static void SetInputFormat(Service::Interface* self) { +static void SetInputFormat(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.input_format = static_cast<InputFormat>(cmd_buff[1]); @@ -94,7 +92,7 @@ static void SetInputFormat(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); } -static void GetInputFormat(Service::Interface* self) { +static void GetInputFormat(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); @@ -104,7 +102,7 @@ static void GetInputFormat(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); } -static void SetOutputFormat(Service::Interface* self) { +static void SetOutputFormat(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.output_format = static_cast<OutputFormat>(cmd_buff[1]); @@ -115,7 +113,7 @@ static void SetOutputFormat(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); } -static void GetOutputFormat(Service::Interface* self) { +static void GetOutputFormat(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x4, 2, 0); @@ -125,7 +123,7 @@ static void GetOutputFormat(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); } -static void SetRotation(Service::Interface* self) { +static void SetRotation(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.rotation = static_cast<Rotation>(cmd_buff[1]); @@ -136,7 +134,7 @@ static void SetRotation(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); } -static void GetRotation(Service::Interface* self) { +static void GetRotation(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x6, 2, 0); @@ -146,7 +144,7 @@ static void GetRotation(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); } -static void SetBlockAlignment(Service::Interface* self) { +static void SetBlockAlignment(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]); @@ -157,7 +155,7 @@ static void SetBlockAlignment(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); } -static void GetBlockAlignment(Service::Interface* self) { +static void GetBlockAlignment(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0); @@ -174,7 +172,7 @@ static void GetBlockAlignment(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetSpacialDithering(Service::Interface* self) { +static void SetSpacialDithering(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); spacial_dithering_enabled = cmd_buff[1] & 0xF; @@ -190,7 +188,7 @@ static void SetSpacialDithering(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : u8, 0 = Disabled, 1 = Enabled */ -static void GetSpacialDithering(Service::Interface* self) { +static void GetSpacialDithering(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); @@ -207,7 +205,7 @@ static void GetSpacialDithering(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetTemporalDithering(Service::Interface* self) { +static void SetTemporalDithering(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); temporal_dithering_enabled = cmd_buff[1] & 0xF; @@ -223,7 +221,7 @@ static void SetTemporalDithering(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : u8, 0 = Disabled, 1 = Enabled */ -static void GetTemporalDithering(Service::Interface* self) { +static void GetTemporalDithering(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); @@ -240,7 +238,7 @@ static void GetTemporalDithering(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void SetTransferEndInterrupt(Service::Interface* self) { +static void SetTransferEndInterrupt(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); transfer_end_interrupt_enabled = cmd_buff[1] & 0xf; @@ -256,7 +254,7 @@ static void SetTransferEndInterrupt(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : u8, 0 = Disabled, 1 = Enabled */ -static void GetTransferEndInterrupt(Service::Interface* self) { +static void GetTransferEndInterrupt(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0xE, 2, 0); @@ -272,7 +270,7 @@ static void GetTransferEndInterrupt(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 3 : The handle of the completion event */ -static void GetTransferEndEvent(Service::Interface* self) { +static void GetTransferEndEvent(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); @@ -282,7 +280,7 @@ static void GetTransferEndEvent(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called"); } -static void SetSendingY(Service::Interface* self) { +static void SetSendingY(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.src_Y.address = cmd_buff[1]; @@ -299,7 +297,7 @@ static void SetSendingY(Service::Interface* self) { cmd_buff[6]); } -static void SetSendingU(Service::Interface* self) { +static void SetSendingU(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.src_U.address = cmd_buff[1]; @@ -316,7 +314,7 @@ static void SetSendingU(Service::Interface* self) { cmd_buff[6]); } -static void SetSendingV(Service::Interface* self) { +static void SetSendingV(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.src_V.address = cmd_buff[1]; @@ -333,7 +331,7 @@ static void SetSendingV(Service::Interface* self) { cmd_buff[6]); } -static void SetSendingYUYV(Service::Interface* self) { +static void SetSendingYUYV(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.src_YUYV.address = cmd_buff[1]; @@ -356,7 +354,7 @@ static void SetSendingYUYV(Service::Interface* self) { * 1 : Result of the function, 0 on success, otherwise error code * 2 : u8, 0 = Not Finished, 1 = Finished */ -static void IsFinishedSendingYuv(Service::Interface* self) { +static void IsFinishedSendingYuv(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x14, 2, 0); @@ -372,7 +370,7 @@ static void IsFinishedSendingYuv(Service::Interface* self) { * 1 : Result of the function, 0 on success, otherwise error code * 2 : u8, 0 = Not Finished, 1 = Finished */ -static void IsFinishedSendingY(Service::Interface* self) { +static void IsFinishedSendingY(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x15, 2, 0); @@ -388,7 +386,7 @@ static void IsFinishedSendingY(Service::Interface* self) { * 1 : Result of the function, 0 on success, otherwise error code * 2 : u8, 0 = Not Finished, 1 = Finished */ -static void IsFinishedSendingU(Service::Interface* self) { +static void IsFinishedSendingU(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); @@ -404,7 +402,7 @@ static void IsFinishedSendingU(Service::Interface* self) { * 1 : Result of the function, 0 on success, otherwise error code * 2 : u8, 0 = Not Finished, 1 = Finished */ -static void IsFinishedSendingV(Service::Interface* self) { +static void IsFinishedSendingV(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x17, 2, 0); @@ -414,7 +412,7 @@ static void IsFinishedSendingV(Service::Interface* self) { LOG_WARNING(Service_Y2R, "(STUBBED) called"); } -static void SetReceiving(Service::Interface* self) { +static void SetReceiving(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.dst.address = cmd_buff[1]; @@ -437,7 +435,7 @@ static void SetReceiving(Service::Interface* self) { * 1 : Result of the function, 0 on success, otherwise error code * 2 : u8, 0 = Not Finished, 1 = Finished */ -static void IsFinishedReceiving(Service::Interface* self) { +static void IsFinishedReceiving(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x19, 2, 0); @@ -447,7 +445,7 @@ static void IsFinishedReceiving(Service::Interface* self) { LOG_WARNING(Service_Y2R, "(STUBBED) called"); } -static void SetInputLineWidth(Service::Interface* self) { +static void SetInputLineWidth(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x1A, 1, 0); @@ -456,7 +454,7 @@ static void SetInputLineWidth(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); } -static void GetInputLineWidth(Service::Interface* self) { +static void GetInputLineWidth(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x1B, 2, 0); @@ -466,7 +464,7 @@ static void GetInputLineWidth(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called input_line_width=%u", conversion.input_line_width); } -static void SetInputLines(Service::Interface* self) { +static void SetInputLines(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x1C, 1, 0); @@ -475,7 +473,7 @@ static void SetInputLines(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]); } -static void GetInputLines(Service::Interface* self) { +static void GetInputLines(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x1D, 2, 0); @@ -485,7 +483,7 @@ static void GetInputLines(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called input_lines=%u", conversion.input_lines); } -static void SetCoefficient(Service::Interface* self) { +static void SetCoefficient(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); const u16* coefficients = reinterpret_cast<const u16*>(&cmd_buff[1]); @@ -499,7 +497,7 @@ static void SetCoefficient(Service::Interface* self) { coefficients[5], coefficients[6], coefficients[7]); } -static void GetCoefficient(Service::Interface* self) { +static void GetCoefficient(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x1F, 5, 0); @@ -509,7 +507,7 @@ static void GetCoefficient(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called"); } -static void SetStandardCoefficient(Service::Interface* self) { +static void SetStandardCoefficient(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 index = cmd_buff[1]; @@ -520,7 +518,7 @@ static void SetStandardCoefficient(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", index); } -static void GetStandardCoefficient(Service::Interface* self) { +static void GetStandardCoefficient(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 index = cmd_buff[1]; @@ -539,7 +537,7 @@ static void GetStandardCoefficient(Service::Interface* self) { } } -static void SetAlpha(Service::Interface* self) { +static void SetAlpha(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.alpha = cmd_buff[1]; @@ -550,7 +548,7 @@ static void SetAlpha(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); } -static void GetAlpha(Service::Interface* self) { +static void GetAlpha(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x23, 2, 0); @@ -560,7 +558,7 @@ static void GetAlpha(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); } -static void SetDitheringWeightParams(Service::Interface* self) { +static void SetDitheringWeightParams(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); std::memcpy(&dithering_weight_params, &cmd_buff[1], sizeof(DitheringWeightParams)); @@ -570,7 +568,7 @@ static void SetDitheringWeightParams(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called"); } -static void GetDitheringWeightParams(Service::Interface* self) { +static void GetDitheringWeightParams(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x25, 9, 0); @@ -580,7 +578,7 @@ static void GetDitheringWeightParams(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called"); } -static void StartConversion(Service::Interface* self) { +static void StartConversion(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( @@ -599,7 +597,7 @@ static void StartConversion(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called"); } -static void StopConversion(Service::Interface* self) { +static void StopConversion(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x27, 1, 0); @@ -614,7 +612,7 @@ static void StopConversion(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code * 2 : 1 if there's a conversion running, otherwise 0. */ -static void IsBusyConversion(Service::Interface* self) { +static void IsBusyConversion(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x28, 2, 0); @@ -627,7 +625,7 @@ static void IsBusyConversion(Service::Interface* self) { /** * Y2R_U::SetPackageParameter service function */ -static void SetPackageParameter(Service::Interface* self) { +static void SetPackageParameter(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]); @@ -668,7 +666,7 @@ cleanup: params->padding, params->alpha); } -static void PingProcess(Service::Interface* self) { +static void PingProcess(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x2A, 2, 0); @@ -678,7 +676,7 @@ static void PingProcess(Service::Interface* self) { LOG_WARNING(Service_Y2R, "(STUBBED) called"); } -static void DriverInitialize(Service::Interface* self) { +static void DriverInitialize(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.input_format = InputFormat::YUV422_Indiv8; @@ -704,7 +702,7 @@ static void DriverInitialize(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called"); } -static void DriverFinalize(Service::Interface* self) { +static void DriverFinalize(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x2C, 1, 0); @@ -713,7 +711,7 @@ static void DriverFinalize(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called"); } -static void GetPackageParameter(Service::Interface* self) { +static void GetPackageParameter(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x2D, 4, 0); @@ -771,18 +769,16 @@ const Interface::FunctionInfo FunctionTable[] = { {0x002D0000, GetPackageParameter, "GetPackageParameter"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +Y2R_U::Y2R_U() { completion_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "Y2R:Completed"); std::memset(&conversion, 0, sizeof(conversion)); Register(FunctionTable); } -Interface::~Interface() { +Y2R_U::~Y2R_U() { completion_event = nullptr; } -} // namespace +} // namespace Y2R +} // namespace Service
\ No newline at end of file diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index 1b47b5322..dddeed0be 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h @@ -10,10 +10,8 @@ #include "core/hle/result.h" #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace Y2R_U - -namespace Y2R_U { +namespace Service { +namespace Y2R { enum class InputFormat : u8 { /// 8-bit input, with YUV components in separate planes and 4:2:2 subsampling. @@ -127,14 +125,15 @@ struct DitheringWeightParams { u16 w3_xOdd_yOdd; }; -class Interface : public Service::Interface { +class Y2R_U final : public Interface { public: - Interface(); - ~Interface() override; + Y2R_U(); + ~Y2R_U() override; std::string GetPortName() const override { return "y2r:u"; } }; -} // namespace +} // namespace Y2R +} // namespace Service diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index c6b80dc50..2ca270de3 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -13,6 +13,7 @@ #include "core/hle/function_wrappers.h" #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/mutex.h" @@ -20,6 +21,7 @@ #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/server_port.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" @@ -41,6 +43,9 @@ const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E +const ResultCode ERR_SYNC_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS, + ErrorSummary::StatusChanged, ErrorLevel::Info); + const ResultCode ERR_MISALIGNED_ADDRESS{// 0xE0E01BF1 ErrorDescription::MisalignedAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage}; @@ -161,7 +166,8 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add } /// Maps a memory block to specified address -static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { +static ResultCode MapMemoryBlock(Kernel::Handle handle, u32 addr, u32 permissions, + u32 other_permissions) { using Kernel::SharedMemory; using Kernel::MemoryPermission; @@ -193,7 +199,7 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o ErrorSummary::InvalidArgument, ErrorLevel::Usage); } -static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) { +static ResultCode UnmapMemoryBlock(Kernel::Handle handle, u32 addr) { using Kernel::SharedMemory; LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X", handle, addr); @@ -208,7 +214,7 @@ static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) { } /// Connect to an OS service given the port name, returns the handle to the port to out -static ResultCode ConnectToPort(Handle* out_handle, const char* port_name) { +static ResultCode ConnectToPort(Kernel::Handle* out_handle, const char* port_name) { if (port_name == nullptr) return ERR_NOT_FOUND; if (std::strlen(port_name) > 11) @@ -222,54 +228,66 @@ static ResultCode ConnectToPort(Handle* out_handle, const char* port_name) { return ERR_NOT_FOUND; } - CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(it->second)); + auto client_port = it->second; + + SharedPtr<Kernel::ClientSession> client_session; + CASCADE_RESULT(client_session, client_port->Connect()); + + // Return the client session + CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(client_session)); return RESULT_SUCCESS; } -/// Synchronize to an OS service -static ResultCode SendSyncRequest(Handle handle) { - SharedPtr<Kernel::Session> session = Kernel::g_handle_table.Get<Kernel::Session>(handle); +/// Makes a blocking IPC call to an OS service. +static ResultCode SendSyncRequest(Kernel::Handle handle) { + SharedPtr<Kernel::ClientSession> session = + Kernel::g_handle_table.Get<Kernel::ClientSession>(handle); if (session == nullptr) { return ERR_INVALID_HANDLE; } LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); - return session->SyncRequest().Code(); + // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server + // responds and cause a reschedule. + return session->SendSyncRequest(); } /// Close a handle -static ResultCode CloseHandle(Handle handle) { +static ResultCode CloseHandle(Kernel::Handle handle) { LOG_TRACE(Kernel_SVC, "Closing handle 0x%08X", handle); return Kernel::g_handle_table.Close(handle); } /// Wait for a handle to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { +static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) { auto object = Kernel::g_handle_table.GetWaitObject(handle); Kernel::Thread* thread = Kernel::GetCurrentThread(); - thread->waitsynch_waited = false; - if (object == nullptr) return ERR_INVALID_HANDLE; LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); - HLE::Reschedule(__func__); - - // Check for next thread to schedule if (object->ShouldWait()) { + if (nano_seconds == 0) + return ERR_SYNC_TIMEOUT; + object->AddWaitingThread(thread); - Kernel::WaitCurrentThread_WaitSynchronization({object}, false, false); + // TODO(Subv): Perform things like update the mutex lock owner's priority to + // prevent priority inversion. Currently this is done in Mutex::ShouldWait, + // but it should be moved to a function that is called from here. + thread->status = THREADSTATUS_WAIT_SYNCH; // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); - // NOTE: output of this SVC will be set later depending on how the thread resumes - return HLE::RESULT_INVALID; + // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread + // resumes due to a signal in its wait objects. + // Otherwise we retain the default value of timeout. + return ERR_SYNC_TIMEOUT; } object->Acquire(); @@ -278,13 +296,9 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { } /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, - s64 nano_seconds) { - bool wait_thread = !wait_all; - int handle_index = 0; +static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 handle_count, + bool wait_all, s64 nano_seconds) { Kernel::Thread* thread = Kernel::GetCurrentThread(); - bool was_waiting = thread->waitsynch_waited; - thread->waitsynch_waited = false; // Check if 'handles' is invalid if (handles == nullptr) @@ -300,94 +314,117 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); - // If 'handle_count' is non-zero, iterate through each handle and wait the current thread if - // necessary - if (handle_count != 0) { - bool selected = false; // True once an object has been selected - - Kernel::SharedPtr<Kernel::WaitObject> wait_object; - - for (int i = 0; i < handle_count; ++i) { - auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); - if (object == nullptr) - return ERR_INVALID_HANDLE; - - // Check if the current thread should wait on this object... - if (object->ShouldWait()) { - - // Check we are waiting on all objects... - if (wait_all) - // Wait the thread - wait_thread = true; - } else { - // Do not wait on this object, check if this object should be selected... - if (!wait_all && (!selected || (wait_object == object && was_waiting))) { - // Do not wait the thread - wait_thread = false; - handle_index = i; - wait_object = object; - selected = true; - } - } - } - } else { - // If no handles were passed in, put the thread to sleep only when 'wait_all' is false - // NOTE: This should deadlock the current thread if no timeout was specified - if (!wait_all) { - wait_thread = true; - } + using ObjectPtr = Kernel::SharedPtr<Kernel::WaitObject>; + std::vector<ObjectPtr> objects(handle_count); + + for (int i = 0; i < handle_count; ++i) { + auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); + if (object == nullptr) + return ERR_INVALID_HANDLE; + objects[i] = object; } - SCOPE_EXIT({ - HLE::Reschedule("WaitSynchronizationN"); - }); // Reschedule after putting the threads to sleep. + // Clear the mapping of wait object indices. + // We don't want any lingering state in this map. + // It will be repopulated later in the wait_all = false case. + thread->wait_objects_index.clear(); + + if (wait_all) { + bool all_available = + std::all_of(objects.begin(), objects.end(), + [](const ObjectPtr& object) { return !object->ShouldWait(); }); + if (all_available) { + // We can acquire all objects right now, do so. + for (auto& object : objects) + object->Acquire(); + // Note: In this case, the `out` parameter is not set, + // and retains whatever value it had before. + return RESULT_SUCCESS; + } + + // Not all objects were available right now, prepare to suspend the thread. - // If thread should wait, then set its state to waiting - if (wait_thread) { + // If a timeout value of 0 was provided, just return the Timeout error code instead of + // suspending the thread. + if (nano_seconds == 0) + return ERR_SYNC_TIMEOUT; - // Actually wait the current thread on each object if we decided to wait... - std::vector<SharedPtr<Kernel::WaitObject>> wait_objects; - wait_objects.reserve(handle_count); + // Put the thread to sleep + thread->status = THREADSTATUS_WAIT_SYNCH; - for (int i = 0; i < handle_count; ++i) { - auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); - object->AddWaitingThread(Kernel::GetCurrentThread()); - wait_objects.push_back(object); + // Add the thread to each of the objects' waiting threads. + for (auto& object : objects) { + object->AddWaitingThread(thread); + // TODO(Subv): Perform things like update the mutex lock owner's priority to + // prevent priority inversion. Currently this is done in Mutex::ShouldWait, + // but it should be moved to a function that is called from here. } - Kernel::WaitCurrentThread_WaitSynchronization(std::move(wait_objects), true, wait_all); + // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN + thread->wait_objects = std::move(objects); // Create an event to wake the thread up after the specified nanosecond delay has passed - Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); - - // NOTE: output of this SVC will be set later depending on how the thread resumes - return HLE::RESULT_INVALID; - } + thread->WakeAfterDelay(nano_seconds); - // Acquire objects if we did not wait... - for (int i = 0; i < handle_count; ++i) { - auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); + // This value gets set to -1 by default in this case, it is not modified after this. + *out = -1; + // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to + // a signal in one of its wait objects. + return ERR_SYNC_TIMEOUT; + } else { + // Find the first object that is acquirable in the provided list of objects + auto itr = std::find_if(objects.begin(), objects.end(), + [](const ObjectPtr& object) { return !object->ShouldWait(); }); - // Acquire the object if it is not waiting... - if (!object->ShouldWait()) { + if (itr != objects.end()) { + // We found a ready object, acquire it and set the result value + Kernel::WaitObject* object = itr->get(); object->Acquire(); + *out = std::distance(objects.begin(), itr); + return RESULT_SUCCESS; + } + + // No objects were ready to be acquired, prepare to suspend the thread. + + // If a timeout value of 0 was provided, just return the Timeout error code instead of + // suspending the thread. + if (nano_seconds == 0) + return ERR_SYNC_TIMEOUT; + + // Put the thread to sleep + thread->status = THREADSTATUS_WAIT_SYNCH; - // If this was the first non-waiting object and 'wait_all' is false, don't acquire - // any other objects - if (!wait_all) - break; + // Clear the thread's waitlist, we won't use it for wait_all = false + thread->wait_objects.clear(); + + // Add the thread to each of the objects' waiting threads. + for (size_t i = 0; i < objects.size(); ++i) { + Kernel::WaitObject* object = objects[i].get(); + // Set the index of this object in the mapping of Objects -> index for this thread. + thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i); + object->AddWaitingThread(thread); + // TODO(Subv): Perform things like update the mutex lock owner's priority to + // prevent priority inversion. Currently this is done in Mutex::ShouldWait, + // but it should be moved to a function that is called from here. } - } - // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does - // not seem to set it to any meaningful value. - *out = handle_count != 0 ? (wait_all ? -1 : handle_index) : 0; + // Note: If no handles and no timeout were given, then the thread will deadlock, this is + // consistent with hardware behavior. - return RESULT_SUCCESS; + // Create an event to wake the thread up after the specified nanosecond delay has passed + thread->WakeAfterDelay(nano_seconds); + + // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a + // signal in one of its wait objects. + // Otherwise we retain the default value of timeout, and -1 in the out parameter + thread->wait_set_output = true; + *out = -1; + return ERR_SYNC_TIMEOUT; + } } /// Create an address arbiter (to allocate access to shared resources) -static ResultCode CreateAddressArbiter(Handle* out_handle) { +static ResultCode CreateAddressArbiter(Kernel::Handle* out_handle) { using Kernel::AddressArbiter; SharedPtr<AddressArbiter> arbiter = AddressArbiter::Create(); @@ -397,7 +434,7 @@ static ResultCode CreateAddressArbiter(Handle* out_handle) { } /// Arbitrate address -static ResultCode ArbitrateAddress(Handle handle, u32 address, u32 type, u32 value, +static ResultCode ArbitrateAddress(Kernel::Handle handle, u32 address, u32 type, u32 value, s64 nanoseconds) { using Kernel::AddressArbiter; @@ -440,7 +477,7 @@ static void OutputDebugString(const char* string) { } /// Get resource limit -static ResultCode GetResourceLimit(Handle* resource_limit, Handle process_handle) { +static ResultCode GetResourceLimit(Kernel::Handle* resource_limit, Kernel::Handle process_handle) { LOG_TRACE(Kernel_SVC, "called process=0x%08X", process_handle); SharedPtr<Kernel::Process> process = @@ -454,7 +491,7 @@ static ResultCode GetResourceLimit(Handle* resource_limit, Handle process_handle } /// Get resource limit current values -static ResultCode GetResourceLimitCurrentValues(s64* values, Handle resource_limit_handle, +static ResultCode GetResourceLimitCurrentValues(s64* values, Kernel::Handle resource_limit_handle, u32* names, u32 name_count) { LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", resource_limit_handle, names, name_count); @@ -471,8 +508,8 @@ static ResultCode GetResourceLimitCurrentValues(s64* values, Handle resource_lim } /// Get resource limit max values -static ResultCode GetResourceLimitLimitValues(s64* values, Handle resource_limit_handle, u32* names, - u32 name_count) { +static ResultCode GetResourceLimitLimitValues(s64* values, Kernel::Handle resource_limit_handle, + u32* names, u32 name_count) { LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", resource_limit_handle, names, name_count); @@ -488,7 +525,7 @@ static ResultCode GetResourceLimitLimitValues(s64* values, Handle resource_limit } /// Creates a new thread -static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point, u32 arg, +static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { using Kernel::Thread; @@ -546,13 +583,13 @@ static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point /// Called when a thread exits static void ExitThread() { - LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); + LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); - Kernel::GetCurrentThread()->Stop(); + Kernel::ExitCurrentThread(); } /// Gets the priority for the specified thread -static ResultCode GetThreadPriority(s32* priority, Handle handle) { +static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); if (thread == nullptr) return ERR_INVALID_HANDLE; @@ -562,7 +599,7 @@ static ResultCode GetThreadPriority(s32* priority, Handle handle) { } /// Sets the priority for the specified thread -static ResultCode SetThreadPriority(Handle handle, s32 priority) { +static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); if (thread == nullptr) return ERR_INVALID_HANDLE; @@ -572,11 +609,11 @@ static ResultCode SetThreadPriority(Handle handle, s32 priority) { } /// Create a mutex -static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) { +static ResultCode CreateMutex(Kernel::Handle* out_handle, u32 initial_locked) { using Kernel::Mutex; SharedPtr<Mutex> mutex = Mutex::Create(initial_locked != 0); - mutex->name = Common::StringFromFormat("mutex-%08x", Core::g_app_core->GetReg(14)); + mutex->name = Common::StringFromFormat("mutex-%08x", Core::CPU().GetReg(14)); CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(mutex))); LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X", @@ -586,7 +623,7 @@ static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) { } /// Release a mutex -static ResultCode ReleaseMutex(Handle handle) { +static ResultCode ReleaseMutex(Kernel::Handle handle) { using Kernel::Mutex; LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle); @@ -601,7 +638,7 @@ static ResultCode ReleaseMutex(Handle handle) { } /// Get the ID of the specified process -static ResultCode GetProcessId(u32* process_id, Handle process_handle) { +static ResultCode GetProcessId(u32* process_id, Kernel::Handle process_handle) { LOG_TRACE(Kernel_SVC, "called process=0x%08X", process_handle); const SharedPtr<Kernel::Process> process = @@ -614,7 +651,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) { } /// Get the ID of the process that owns the specified thread -static ResultCode GetProcessIdOfThread(u32* process_id, Handle thread_handle) { +static ResultCode GetProcessIdOfThread(u32* process_id, Kernel::Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x%08X", thread_handle); const SharedPtr<Kernel::Thread> thread = @@ -631,7 +668,7 @@ static ResultCode GetProcessIdOfThread(u32* process_id, Handle thread_handle) { } /// Get the ID for the specified thread. -static ResultCode GetThreadId(u32* thread_id, Handle handle) { +static ResultCode GetThreadId(u32* thread_id, Kernel::Handle handle) { LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle); const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); @@ -643,11 +680,11 @@ static ResultCode GetThreadId(u32* thread_id, Handle handle) { } /// Creates a semaphore -static ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) { +static ResultCode CreateSemaphore(Kernel::Handle* out_handle, s32 initial_count, s32 max_count) { using Kernel::Semaphore; CASCADE_RESULT(SharedPtr<Semaphore> semaphore, Semaphore::Create(initial_count, max_count)); - semaphore->name = Common::StringFromFormat("semaphore-%08x", Core::g_app_core->GetReg(14)); + semaphore->name = Common::StringFromFormat("semaphore-%08x", Core::CPU().GetReg(14)); CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(semaphore))); LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X", @@ -656,7 +693,7 @@ static ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max } /// Releases a certain number of slots in a semaphore -static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { +static ResultCode ReleaseSemaphore(s32* count, Kernel::Handle handle, s32 release_count) { using Kernel::Semaphore; LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, handle); @@ -672,7 +709,7 @@ static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) /// Query process memory static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info, - Handle process_handle, u32 addr) { + Kernel::Handle process_handle, u32 addr) { using Kernel::Process; Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle); if (process == nullptr) @@ -700,11 +737,11 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 } /// Create an event -static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) { +static ResultCode CreateEvent(Kernel::Handle* out_handle, u32 reset_type) { using Kernel::Event; SharedPtr<Event> evt = Event::Create(static_cast<Kernel::ResetType>(reset_type)); - evt->name = Common::StringFromFormat("event-%08x", Core::g_app_core->GetReg(14)); + evt->name = Common::StringFromFormat("event-%08x", Core::CPU().GetReg(14)); CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(evt))); LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", reset_type, @@ -713,14 +750,14 @@ static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) { } /// Duplicates a kernel handle -static ResultCode DuplicateHandle(Handle* out, Handle handle) { +static ResultCode DuplicateHandle(Kernel::Handle* out, Kernel::Handle handle) { CASCADE_RESULT(*out, Kernel::g_handle_table.Duplicate(handle)); LOG_TRACE(Kernel_SVC, "duplicated 0x%08X to 0x%08X", handle, *out); return RESULT_SUCCESS; } /// Signals an event -static ResultCode SignalEvent(Handle handle) { +static ResultCode SignalEvent(Kernel::Handle handle) { using Kernel::Event; LOG_TRACE(Kernel_SVC, "called event=0x%08X", handle); @@ -734,7 +771,7 @@ static ResultCode SignalEvent(Handle handle) { } /// Clears an event -static ResultCode ClearEvent(Handle handle) { +static ResultCode ClearEvent(Kernel::Handle handle) { using Kernel::Event; LOG_TRACE(Kernel_SVC, "called event=0x%08X", handle); @@ -747,11 +784,11 @@ static ResultCode ClearEvent(Handle handle) { } /// Creates a timer -static ResultCode CreateTimer(Handle* out_handle, u32 reset_type) { +static ResultCode CreateTimer(Kernel::Handle* out_handle, u32 reset_type) { using Kernel::Timer; SharedPtr<Timer> timer = Timer::Create(static_cast<Kernel::ResetType>(reset_type)); - timer->name = Common::StringFromFormat("timer-%08x", Core::g_app_core->GetReg(14)); + timer->name = Common::StringFromFormat("timer-%08x", Core::CPU().GetReg(14)); CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(timer))); LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", reset_type, @@ -760,7 +797,7 @@ static ResultCode CreateTimer(Handle* out_handle, u32 reset_type) { } /// Clears a timer -static ResultCode ClearTimer(Handle handle) { +static ResultCode ClearTimer(Kernel::Handle handle) { using Kernel::Timer; LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); @@ -774,7 +811,7 @@ static ResultCode ClearTimer(Handle handle) { } /// Starts a timer -static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { +static ResultCode SetTimer(Kernel::Handle handle, s64 initial, s64 interval) { using Kernel::Timer; LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); @@ -789,7 +826,7 @@ static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { } /// Cancels a timer -static ResultCode CancelTimer(Handle handle) { +static ResultCode CancelTimer(Kernel::Handle handle) { using Kernel::Timer; LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); @@ -818,14 +855,13 @@ static void SleepThread(s64 nanoseconds) { static s64 GetSystemTick() { s64 result = CoreTiming::GetTicks(); // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. - Core::g_app_core->AddTicks( - 150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b + Core::CPU().AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b return result; } /// Creates a memory block at the specified address with the specified permissions and size -static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission, - u32 other_permission) { +static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 size, + u32 my_permission, u32 other_permission) { using Kernel::SharedMemory; if (size % Memory::PAGE_SIZE != 0) @@ -876,8 +912,8 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 return RESULT_SUCCESS; } -static ResultCode CreatePort(Handle* server_port, Handle* client_port, const char* name, - u32 max_sessions) { +static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client_port, + const char* name, u32 max_sessions) { // TODO(Subv): Implement named ports. ASSERT_MSG(name == nullptr, "Named ports are currently unimplemented"); @@ -942,7 +978,7 @@ static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { return RESULT_SUCCESS; } -static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) { +static ResultCode GetProcessInfo(s64* out, Kernel::Handle process_handle, u32 type) { LOG_TRACE(Kernel_SVC, "called process=0x%08X type=%u", process_handle, type); using Kernel::Process; @@ -1148,6 +1184,8 @@ void CallSVC(u32 immediate) { if (info) { if (info->func) { info->func(); + // TODO(Subv): Not all service functions should cause a reschedule in all cases. + Core::System::GetInstance().PrepareReschedule(); } else { LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); } diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index cfba82e51..1a1ee90b2 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -430,9 +430,9 @@ inline void Write(u32 addr, const T data) { // TODO: hwtest this if (config.GetStartAddress() != 0) { if (!is_second_filler) { - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0); + Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PSC0); } else { - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); + Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PSC1); } } @@ -473,7 +473,7 @@ inline void Write(u32 addr, const T data) { } g_regs.display_transfer_config.trigger = 0; - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); + Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PPF); } break; } @@ -487,8 +487,8 @@ inline void Write(u32 addr, const T data) { 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::g_debug_context->recorder->MemoryAccessed((u8*)buffer, config.size, + config.GetPhysicalAddress()); } Pica::CommandProcessor::ProcessCommandList(buffer, config.size); @@ -548,8 +548,8 @@ static void VBlankCallback(u64 userdata, int cycles_late) { // screen, or if both use the same interrupts and these two instead determine the // beginning and end of the VBlank period. If needed, split the interrupt firing into // two different intervals. - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0); - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1); + Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0); + Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); // Check for user input updates Service::HID::Update(); diff --git a/src/core/hw/y2r.cpp b/src/core/hw/y2r.cpp index 6a6c707a2..e697f84b3 100644 --- a/src/core/hw/y2r.cpp +++ b/src/core/hw/y2r.cpp @@ -18,7 +18,7 @@ namespace HW { namespace Y2R { -using namespace Y2R_U; +using namespace Service::Y2R; static const size_t MAX_TILES = 1024 / 8; static const size_t TILE_SIZE = 8 * 8; diff --git a/src/core/hw/y2r.h b/src/core/hw/y2r.h index 6b6e71bec..25fcd781c 100644 --- a/src/core/hw/y2r.h +++ b/src/core/hw/y2r.h @@ -2,13 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -namespace Y2R_U { +#pragma once + +namespace Service { +namespace Y2R { struct ConversionConfiguration; } +} namespace HW { namespace Y2R { - -void PerformConversion(Y2R_U::ConversionConfiguration& cvt); +void PerformConversion(Service::Y2R::ConversionConfiguration& cvt); } } diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h index cfcc21cc4..3f376778a 100644 --- a/src/core/loader/3dsx.h +++ b/src/core/loader/3dsx.h @@ -27,34 +27,14 @@ public: */ static FileType IdentifyType(FileUtil::IOFile& file); - /** - * Returns the type of this file - * @return FileType corresponding to the loaded file - */ FileType GetFileType() override { return IdentifyType(file); } - /** - * Load the bootable file - * @return ResultStatus result of function - */ ResultStatus Load() 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) override; - /** - * Get the RomFS of the application - * @param romfs_file Reference to buffer to store data - * @param offset Offset in the file to the RomFS - * @param size Size of the RomFS in bytes - * @return ResultStatus result of function - */ ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) override; diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 584bf6e27..862aa90d8 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -26,18 +26,10 @@ public: */ static FileType IdentifyType(FileUtil::IOFile& file); - /** - * Returns the type of this file - * @return FileType corresponding to the loaded file - */ FileType GetFileType() override { return IdentifyType(file); } - /** - * Load the bootable file - * @return ResultStatus result of function - */ ResultStatus Load() override; private: diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 5e3d46638..a6c2a745f 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -144,6 +144,15 @@ public: } /** + * Get the program id of the application + * @param out_program_id Reference to store program id into + * @return ResultStatus result of function + */ + virtual ResultStatus ReadProgramId(u64& out_program_id) { + return ResultStatus::ErrorNotImplemented; + } + + /** * Get the RomFS of the application * 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 diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index dce7f50f9..a204dc336 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -366,6 +366,18 @@ ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) { return LoadSectionExeFS("logo", buffer); } +ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) { + if (!file.IsOpen()) + return ResultStatus::Error; + + ResultStatus result = LoadExeFS(); + if (result != ResultStatus::Success) + return result; + + out_program_id = ncch_header.program_id; + return ResultStatus::Success; +} + ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { if (!file.IsOpen()) diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index e9e11727b..fe08f5b45 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -171,18 +171,10 @@ public: */ static FileType IdentifyType(FileUtil::IOFile& file); - /** - * Returns the type of this file - * @return FileType corresponding to the loaded file - */ FileType GetFileType() override { return IdentifyType(file); } - /** - * Load the application - * @return ResultStatus result of function - */ ResultStatus Load() override; /** @@ -191,33 +183,20 @@ public: */ boost::optional<u32> LoadKernelSystemMode(); - /** - * Get the code (typically .code section) of the application - * @param buffer Reference to buffer to store data - * @return ResultStatus result of function - */ 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) 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) override; + ResultStatus ReadLogo(std::vector<u8>& buffer) override; + /** - * Get the logo (typically logo section) of the application - * @param buffer Reference to buffer to store data + * Get the program id of the application + * @param out_program_id Reference to store program id into * @return ResultStatus result of function */ - ResultStatus ReadLogo(std::vector<u8>& buffer) override; + ResultStatus ReadProgramId(u64& out_program_id) override; /** * Get the RomFS of the application diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 626e06cd9..5d23c52f9 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -7,7 +7,7 @@ #include "settings.h" #include "video_core/video_core.h" -#include "common/emu_window.h" +#include "core/frontend/emu_window.h" namespace Settings { diff --git a/src/core/system.cpp b/src/core/system.cpp deleted file mode 100644 index a5f763805..000000000 --- a/src/core/system.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "audio_core/audio_core.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/gdbstub/gdbstub.h" -#include "core/hle/hle.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/memory.h" -#include "core/hw/hw.h" -#include "core/system.h" -#include "video_core/video_core.h" - -namespace System { - -static bool is_powered_on{false}; - -Result Init(EmuWindow* emu_window, u32 system_mode) { - Core::Init(); - CoreTiming::Init(); - Memory::Init(); - HW::Init(); - Kernel::Init(system_mode); - HLE::Init(); - if (!VideoCore::Init(emu_window)) { - return Result::ErrorInitVideoCore; - } - AudioCore::Init(); - GDBStub::Init(); - - is_powered_on = true; - - return Result::Success; -} - -bool IsPoweredOn() { - return is_powered_on; -} - -void Shutdown() { - GDBStub::Shutdown(); - AudioCore::Shutdown(); - VideoCore::Shutdown(); - HLE::Shutdown(); - Kernel::Shutdown(); - HW::Shutdown(); - CoreTiming::Shutdown(); - Core::Shutdown(); - - is_powered_on = false; -} - -} // namespace diff --git a/src/core/system.h b/src/core/system.h deleted file mode 100644 index b41fc088a..000000000 --- a/src/core/system.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -class EmuWindow; - -namespace System { - -enum class Result { - Success, ///< Everything is fine - Error, ///< Something went wrong (no module specified) - ErrorInitCore, ///< Something went wrong during core init - ErrorInitVideoCore, ///< Something went wrong during video core init -}; - -Result Init(EmuWindow* emu_window, u32 system_mode); -bool IsPoweredOn(); -void Shutdown(); -} diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 89237e477..b47156ca4 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -13,6 +13,6 @@ include_directories(../../externals/catch/single_include/) add_executable(tests ${SRCS} ${HEADERS}) target_link_libraries(tests core video_core audio_core common) -target_link_libraries(tests ${PLATFORM_LIBRARIES}) +target_link_libraries(tests ${PLATFORM_LIBRARIES} Threads::Threads) add_test(NAME tests COMMAND $<TARGET_FILE:tests>) diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 581a37897..6ca319b59 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -39,6 +39,7 @@ set(HEADERS rasterizer.h rasterizer_interface.h renderer_base.h + shader/debug_data.h shader/shader.h shader/shader_interpreter.h swrasterizer.h @@ -59,6 +60,9 @@ create_directory_groups(${SRCS} ${HEADERS}) add_library(video_core STATIC ${SRCS} ${HEADERS}) target_link_libraries(video_core glad) +if (ARCHITECTURE_x86_64) + target_link_libraries(video_core xbyak) +endif() if (PNG_FOUND) target_link_libraries(video_core ${PNG_LIBRARIES}) diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index c80c96762..ea58e9f54 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -59,7 +59,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { regs[id] = (old_value & ~write_mask) | (value & write_mask); - DebugUtils::OnPicaRegWrite({(u16)id, (u16)mask, regs[id]}); + // Double check for is_pica_tracing to avoid call overhead + if (DebugUtils::IsPicaTracing()) { + DebugUtils::OnPicaRegWrite({(u16)id, (u16)mask, regs[id]}); + } if (g_debug_context) g_debug_context->OnEvent(DebugContext::Event::PicaCommandLoaded, @@ -68,7 +71,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { switch (id) { // Trigger IRQ case PICA_REG_INDEX(trigger_irq): - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); + Service::GSP::SignalInterrupt(Service::GSP::InterruptId::P3D); break; case PICA_REG_INDEX_WORKAROUND(triangle_topology, 0x25E): @@ -136,9 +139,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { immediate_input.attr[immediate_attribute_id++] = attribute; if (immediate_attribute_id >= regs.vs.num_input_attributes + 1) { + MICROPROFILE_SCOPE(GPU_Drawing); immediate_attribute_id = 0; - Shader::UnitState<false> shader_unit; + Shader::UnitState shader_unit; g_state.vs.Setup(); // Send to vertex shader @@ -165,6 +169,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { case PICA_REG_INDEX(gpu_mode): if (regs.gpu_mode == Regs::GPUMode::Configuring) { + MICROPROFILE_SCOPE(GPU_Drawing); + // Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring VideoCore::g_renderer->Rasterizer()->DrawTriangles(); @@ -237,7 +243,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { unsigned int vertex_cache_pos = 0; vertex_cache_ids.fill(-1); - Shader::UnitState<false> shader_unit; + Shader::UnitState shader_unit; g_state.vs.Setup(); for (unsigned int index = 0; index < regs.num_vertices; ++index) { @@ -251,7 +257,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { ASSERT(vertex != -1); bool vertex_cache_hit = false; - Shader::OutputRegisters output_registers; if (is_indexed) { if (g_debug_context && Pica::g_debug_context->recorder) { @@ -279,10 +284,9 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation, (void*)&input); g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes()); - output_registers = shader_unit.output_registers; // Retrieve vertex from register data - output_vertex = output_registers.ToVertex(regs.vs); + output_vertex = shader_unit.output_registers.ToVertex(regs.vs); if (is_indexed) { vertex_cache[vertex_cache_pos] = output_vertex; diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 8806464d9..c44b3d95a 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -276,10 +276,10 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, static std::unique_ptr<PicaTrace> pica_trace; static std::mutex pica_trace_mutex; -static int is_pica_tracing = false; +bool g_is_pica_tracing = false; void StartPicaTracing() { - if (is_pica_tracing) { + if (g_is_pica_tracing) { LOG_WARNING(HW_GPU, "StartPicaTracing called even though tracing already running!"); return; } @@ -287,34 +287,26 @@ void StartPicaTracing() { std::lock_guard<std::mutex> lock(pica_trace_mutex); pica_trace = std::make_unique<PicaTrace>(); - is_pica_tracing = true; -} - -bool IsPicaTracing() { - return is_pica_tracing != 0; + g_is_pica_tracing = true; } void OnPicaRegWrite(PicaTrace::Write write) { - // Double check for is_pica_tracing to avoid pointless locking overhead - if (!is_pica_tracing) - return; - std::lock_guard<std::mutex> lock(pica_trace_mutex); - if (!is_pica_tracing) + if (!g_is_pica_tracing) return; pica_trace->writes.push_back(write); } std::unique_ptr<PicaTrace> FinishPicaTracing() { - if (!is_pica_tracing) { + if (!g_is_pica_tracing) { LOG_WARNING(HW_GPU, "FinishPicaTracing called even though tracing isn't running!"); return {}; } // signalize that no further tracing should be performed - is_pica_tracing = false; + g_is_pica_tracing = false; // Wait until running tracing is finished std::lock_guard<std::mutex> lock(pica_trace_mutex); diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 189c93abb..46ea8d9c7 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -196,8 +196,12 @@ struct PicaTrace { std::vector<Write> writes; }; +extern bool g_is_pica_tracing; + void StartPicaTracing(); -bool IsPicaTracing(); +inline bool IsPicaTracing() { + return g_is_pica_tracing; +} void OnPicaRegWrite(PicaTrace::Write write); std::unique_ptr<PicaTrace> FinishPicaTracing(); diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h index 3c6636d66..c1f9b43c2 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h @@ -28,7 +28,8 @@ public: * @note All methods in this class are called from the GSP thread */ virtual void GXCommandProcessed(int total_command_count) { - const GSP_GPU::Command& cmd = observed->ReadGXCommandHistory(total_command_count - 1); + const Service::GSP::Command& cmd = + observed->ReadGXCommandHistory(total_command_count - 1); LOG_TRACE(Debug_GPU, "Received command: id=%x", (int)cmd.id.Value()); } @@ -48,16 +49,16 @@ public: return; gx_command_history.emplace_back(); - GSP_GPU::Command& cmd = gx_command_history.back(); + Service::GSP::Command& cmd = gx_command_history.back(); - memcpy(&cmd, command_data, sizeof(GSP_GPU::Command)); + memcpy(&cmd, command_data, sizeof(Service::GSP::Command)); ForEachObserver([this](DebuggerObserver* observer) { observer->GXCommandProcessed(static_cast<int>(this->gx_command_history.size())); }); } - const GSP_GPU::Command& ReadGXCommandHistory(int index) const { + const Service::GSP::Command& ReadGXCommandHistory(int index) const { // TODO: Is this thread-safe? return gx_command_history[index]; } @@ -80,5 +81,5 @@ private: std::vector<DebuggerObserver*> observers; - std::vector<GSP_GPU::Command> gx_command_history; + std::vector<Service::GSP::Command> gx_command_history; }; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index ba65db419..5a306a5c8 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -11,6 +11,7 @@ #include "common/color.h" #include "common/logging/log.h" #include "common/math_util.h" +#include "common/microprofile.h" #include "common/vector_math.h" #include "core/hw/gpu.h" #include "video_core/pica.h" @@ -21,6 +22,10 @@ #include "video_core/renderer_opengl/pica_to_gl.h" #include "video_core/renderer_opengl/renderer_opengl.h" +MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); +MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); +MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); + 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 && @@ -168,6 +173,7 @@ void RasterizerOpenGL::DrawTriangles() { if (vertex_batch.empty()) return; + MICROPROFILE_SCOPE(OpenGL_Drawing); const auto& regs = Pica::g_state.regs; // Sync and bind the framebuffer surfaces @@ -189,10 +195,6 @@ void RasterizerOpenGL::DrawTriangles() { GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, (has_stencil && depth_surface != nullptr) ? depth_surface->texture.handle : 0, 0); - if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return; - } - // Sync the viewport // These registers hold half-width and half-height, so must be multiplied by 2 GLsizei viewport_width = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_x).ToFloat32() * 2; @@ -698,18 +700,22 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { } void RasterizerOpenGL::FlushAll() { + MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushAll(); } void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) { + MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushRegion(addr, size, nullptr, false); } void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) { + MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushRegion(addr, size, nullptr, true); } bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) { + MICROPROFILE_SCOPE(OpenGL_Blits); using PixelFormat = CachedSurface::PixelFormat; using SurfaceType = CachedSurface::SurfaceType; @@ -782,6 +788,7 @@ bool RasterizerOpenGL::AccelerateTextureCopy(const GPU::Regs::DisplayTransferCon } bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) { + MICROPROFILE_SCOPE(OpenGL_Blits); using PixelFormat = CachedSurface::PixelFormat; using SurfaceType = CachedSurface::SurfaceType; @@ -807,10 +814,6 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return false; - } - GLfloat color_values[4] = {0.0f, 0.0f, 0.0f, 0.0f}; // TODO: Handle additional pixel format and fill value size combinations to accelerate more @@ -883,10 +886,10 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) } } - cur_state.color_mask.red_enabled = true; - cur_state.color_mask.green_enabled = true; - cur_state.color_mask.blue_enabled = true; - cur_state.color_mask.alpha_enabled = true; + cur_state.color_mask.red_enabled = GL_TRUE; + cur_state.color_mask.green_enabled = GL_TRUE; + cur_state.color_mask.blue_enabled = GL_TRUE; + cur_state.color_mask.alpha_enabled = GL_TRUE; cur_state.Apply(); glClearBufferfv(GL_COLOR, 0, color_values); } else if (dst_type == SurfaceType::Depth) { @@ -895,10 +898,6 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) dst_surface->texture.handle, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return false; - } - GLfloat value_float; if (dst_surface->pixel_format == CachedSurface::PixelFormat::D16) { value_float = config.value_32bit / 65535.0f; // 2^16 - 1 @@ -914,10 +913,6 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dst_surface->texture.handle, 0); - if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return false; - } - GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 GLint value_int = (config.value_32bit >> 24); @@ -942,6 +937,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con if (framebuffer_addr == 0) { return false; } + MICROPROFILE_SCOPE(OpenGL_CacheManagement); CachedSurface src_params; src_params.addr = framebuffer_addr; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 61f6e767f..85aa06cd5 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -11,11 +11,11 @@ #include <vector> #include <glad/glad.h> #include "common/bit_field.h" -#include "common/emu_window.h" #include "common/logging/log.h" #include "common/math_util.h" #include "common/microprofile.h" #include "common/vector_math.h" +#include "core/frontend/emu_window.h" #include "core/memory.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/pica_state.h" @@ -103,7 +103,7 @@ static void MortonCopyPixels(CachedSurface::PixelFormat pixel_format, u32 width, } } -bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, +void RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, const MathUtil::Rectangle<int>& src_rect, const MathUtil::Rectangle<int>& dst_rect) { @@ -158,21 +158,14 @@ bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; } - bool can_blit = OpenGLState::CheckFBStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE && - OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; - - if (can_blit) { - glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, - dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, buffers, - buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST); - } + glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left, + dst_rect.top, dst_rect.right, dst_rect.bottom, buffers, + buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST); // Restore previous framebuffer bindings cur_state.draw.read_framebuffer = old_fbs[0]; cur_state.draw.draw_framebuffer = old_fbs[1]; cur_state.Apply(); - - return can_blit; } bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface, @@ -186,9 +179,9 @@ bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface, return false; } - return BlitTextures(src_surface->texture.handle, dst_surface->texture.handle, - CachedSurface::GetFormatType(src_surface->pixel_format), src_rect, - dst_rect); + BlitTextures(src_surface->texture.handle, dst_surface->texture.handle, + CachedSurface::GetFormatType(src_surface->pixel_format), src_rect, dst_rect); + return true; } static void AllocateSurfaceTexture(GLuint texture, CachedSurface::PixelFormat pixel_format, diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 32abfbaf5..b50e8292b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -187,7 +187,7 @@ public: ~RasterizerCacheOpenGL(); /// Blits one texture to another - bool BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, + void BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, const MathUtil::Rectangle<int>& src_rect, const MathUtil::Rectangle<int>& dst_rect); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 8f278722d..4c4f98ac9 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -293,7 +293,7 @@ static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) { case CompareFunc::GreaterThanOrEqual: { static const char* op[] = {"!=", "==", ">=", ">", "<=", "<"}; unsigned index = (unsigned)func - (unsigned)CompareFunc::Equal; - out += "int(last_tex_env_out.a * 255.0f) " + std::string(op[index]) + " alphatest_ref"; + out += "int(last_tex_env_out.a * 255.0) " + std::string(op[index]) + " alphatest_ref"; break; } @@ -422,16 +422,13 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { if (abs) { // LUT index is in the range of (0.0, 1.0) index = lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" - : "max(" + index + ", 0.f)"; - return "(FLOAT_255 * clamp(" + index + ", 0.0, 1.0))"; + : "max(" + index + ", 0.0)"; } else { // LUT index is in the range of (-1.0, 1.0) - index = "clamp(" + index + ", -1.0, 1.0)"; - return "(FLOAT_255 * ((" + index + " < 0) ? " + index + " + 2.0 : " + index + - ") / 2.0)"; + index = "((" + index + " < 0) ? " + index + " + 2.0 : " + index + ") / 2.0"; } - return std::string(); + return "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; }; // Gets the lighting lookup table value given the specified sampler and index @@ -462,7 +459,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { if (light_config.dist_atten_enable) { std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " + light_src + ".position) + " + light_src + ".dist_atten_bias)"; - index = "((clamp(" + index + ", 0.0, FLOAT_255)))"; + index = "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; const unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + light_config.num); dist_atten = GetLutValue((Regs::LightingSampler)lut_num, index); @@ -580,8 +577,10 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) { #version 330 core #define NUM_TEV_STAGES 6 #define NUM_LIGHTS 8 -#define LIGHTING_LUT_SIZE 256 -#define FLOAT_255 (255.0 / 256.0) + +// Texture coordinate offsets and scales +#define OFFSET_256 (0.5 / 256.0) +#define SCALE_256 (255.0 / 256.0) in vec4 primary_color; in vec2 texcoord[3]; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 2a731f483..3c03b424a 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -232,19 +232,6 @@ void OpenGLState::Apply() const { cur_state = *this; } -GLenum OpenGLState::CheckFBStatus(GLenum target) { - GLenum fb_status = glCheckFramebufferStatus(target); - if (fb_status != GL_FRAMEBUFFER_COMPLETE) { - const char* fb_description = - (target == GL_READ_FRAMEBUFFER ? "READ" - : (target == GL_DRAW_FRAMEBUFFER ? "DRAW" : "UNK")); - LOG_CRITICAL(Render_OpenGL, "OpenGL %s framebuffer check failed, status %X", fb_description, - fb_status); - } - - return fb_status; -} - void OpenGLState::ResetTexture(GLuint handle) { for (auto& unit : cur_state.texture_units) { if (unit.texture_2d == handle) { diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 01dead883..aee3c2946 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -90,9 +90,6 @@ public: /// Apply this state as the current OpenGL state void Apply() const; - /// Check the status of the current OpenGL read or draw framebuffer configuration - static GLenum CheckFBStatus(GLenum target); - /// Resets and unbinds any references to the given resource in the current OpenGL state static void ResetTexture(GLuint handle); static void ResetSampler(GLuint handle); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 93f0ac105..2aa90e5c1 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -9,10 +9,10 @@ #include <glad/glad.h> #include "common/assert.h" #include "common/bit_field.h" -#include "common/emu_window.h" #include "common/logging/log.h" #include "common/profiler_reporting.h" #include "common/synchronized_wrapper.h" +#include "core/frontend/emu_window.h" #include "core/hw/gpu.h" #include "core/hw/hw.h" #include "core/hw/lcd.h" diff --git a/src/video_core/shader/debug_data.h b/src/video_core/shader/debug_data.h new file mode 100644 index 000000000..9e82122e1 --- /dev/null +++ b/src/video_core/shader/debug_data.h @@ -0,0 +1,186 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include "common/common_types.h" +#include "common/vector_math.h" +#include "video_core/pica_types.h" + +namespace Pica { +namespace Shader { + +/// Helper structure used to keep track of data useful for inspection of shader emulation +template <bool full_debugging> +struct DebugData; + +template <> +struct DebugData<false> { + // TODO: Hide these behind and interface and move them to DebugData<true> + u32 max_offset = 0; ///< maximum program counter ever reached + u32 max_opdesc_id = 0; ///< maximum swizzle pattern index ever used +}; + +template <> +struct DebugData<true> { + /// Records store the input and output operands of a particular instruction. + struct Record { + enum Type { + // Floating point arithmetic operands + SRC1 = 0x1, + SRC2 = 0x2, + SRC3 = 0x4, + + // Initial and final output operand value + DEST_IN = 0x8, + DEST_OUT = 0x10, + + // Current and next instruction offset (in words) + CUR_INSTR = 0x20, + NEXT_INSTR = 0x40, + + // Output address register value + ADDR_REG_OUT = 0x80, + + // Result of a comparison instruction + CMP_RESULT = 0x100, + + // Input values for conditional flow control instructions + COND_BOOL_IN = 0x200, + COND_CMP_IN = 0x400, + + // Input values for a loop + LOOP_INT_IN = 0x800, + }; + + Math::Vec4<float24> src1; + Math::Vec4<float24> src2; + Math::Vec4<float24> src3; + + Math::Vec4<float24> dest_in; + Math::Vec4<float24> dest_out; + + s32 address_registers[2]; + bool conditional_code[2]; + bool cond_bool; + bool cond_cmp[2]; + Math::Vec4<u8> loop_int; + + u32 instruction_offset; + u32 next_instruction; + + /// set of enabled fields (as a combination of Type flags) + unsigned mask = 0; + }; + + u32 max_offset = 0; ///< maximum program counter ever reached + u32 max_opdesc_id = 0; ///< maximum swizzle pattern index ever used + + /// List of records for each executed shader instruction + std::vector<DebugData<true>::Record> records; +}; + +/// Type alias for better readability +using DebugDataRecord = DebugData<true>::Record; + +/// Helper function to set a DebugData<true>::Record field based on the template enum parameter. +template <DebugDataRecord::Type type, typename ValueType> +inline void SetField(DebugDataRecord& record, ValueType value); + +template <> +inline void SetField<DebugDataRecord::SRC1>(DebugDataRecord& record, float24* value) { + record.src1.x = value[0]; + record.src1.y = value[1]; + record.src1.z = value[2]; + record.src1.w = value[3]; +} + +template <> +inline void SetField<DebugDataRecord::SRC2>(DebugDataRecord& record, float24* value) { + record.src2.x = value[0]; + record.src2.y = value[1]; + record.src2.z = value[2]; + record.src2.w = value[3]; +} + +template <> +inline void SetField<DebugDataRecord::SRC3>(DebugDataRecord& record, float24* value) { + record.src3.x = value[0]; + record.src3.y = value[1]; + record.src3.z = value[2]; + record.src3.w = value[3]; +} + +template <> +inline void SetField<DebugDataRecord::DEST_IN>(DebugDataRecord& record, float24* value) { + record.dest_in.x = value[0]; + record.dest_in.y = value[1]; + record.dest_in.z = value[2]; + record.dest_in.w = value[3]; +} + +template <> +inline void SetField<DebugDataRecord::DEST_OUT>(DebugDataRecord& record, float24* value) { + record.dest_out.x = value[0]; + record.dest_out.y = value[1]; + record.dest_out.z = value[2]; + record.dest_out.w = value[3]; +} + +template <> +inline void SetField<DebugDataRecord::ADDR_REG_OUT>(DebugDataRecord& record, s32* value) { + record.address_registers[0] = value[0]; + record.address_registers[1] = value[1]; +} + +template <> +inline void SetField<DebugDataRecord::CMP_RESULT>(DebugDataRecord& record, bool* value) { + record.conditional_code[0] = value[0]; + record.conditional_code[1] = value[1]; +} + +template <> +inline void SetField<DebugDataRecord::COND_BOOL_IN>(DebugDataRecord& record, bool value) { + record.cond_bool = value; +} + +template <> +inline void SetField<DebugDataRecord::COND_CMP_IN>(DebugDataRecord& record, bool* value) { + record.cond_cmp[0] = value[0]; + record.cond_cmp[1] = value[1]; +} + +template <> +inline void SetField<DebugDataRecord::LOOP_INT_IN>(DebugDataRecord& record, Math::Vec4<u8> value) { + record.loop_int = value; +} + +template <> +inline void SetField<DebugDataRecord::CUR_INSTR>(DebugDataRecord& record, u32 value) { + record.instruction_offset = value; +} + +template <> +inline void SetField<DebugDataRecord::NEXT_INSTR>(DebugDataRecord& record, u32 value) { + record.next_instruction = value; +} + +/// Helper function to set debug information on the current shader iteration. +template <DebugDataRecord::Type type, typename ValueType> +inline void Record(DebugData<false>& debug_data, u32 offset, ValueType value) { + // Debugging disabled => nothing to do +} + +template <DebugDataRecord::Type type, typename ValueType> +inline void Record(DebugData<true>& debug_data, u32 offset, ValueType value) { + if (offset >= debug_data.records.size()) + debug_data.records.resize(offset + 1); + + SetField<type, ValueType>(debug_data.records[offset], value); + debug_data.records[offset].mask |= type; +} + +} // namespace Shader +} // namespace Pica diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp index 3febe739c..a4aa3c9e0 100644 --- a/src/video_core/shader/shader.cpp +++ b/src/video_core/shader/shader.cpp @@ -25,7 +25,7 @@ namespace Pica { namespace Shader { -OutputVertex OutputRegisters::ToVertex(const Regs::ShaderConfig& config) { +OutputVertex OutputRegisters::ToVertex(const Regs::ShaderConfig& config) const { // Setup output data OutputVertex ret; // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to @@ -109,15 +109,12 @@ void ShaderSetup::Setup() { MICROPROFILE_DEFINE(GPU_Shader, "GPU", "Shader", MP_RGB(50, 50, 240)); -void ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, int num_attributes) { +void ShaderSetup::Run(UnitState& state, const InputVertex& input, int num_attributes) { auto& config = g_state.regs.vs; auto& setup = g_state.vs; MICROPROFILE_SCOPE(GPU_Shader); - state.debug.max_offset = 0; - state.debug.max_opdesc_id = 0; - // Setup input register table const auto& attribute_register_map = config.input_register_map; @@ -128,22 +125,23 @@ void ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, int num state.conditional_code[1] = false; #ifdef ARCHITECTURE_x86_64 - if (VideoCore::g_shader_jit_enabled) + if (VideoCore::g_shader_jit_enabled) { jit_shader->Run(setup, state, config.main_offset); - else - RunInterpreter(setup, state, config.main_offset); + } else { + DebugData<false> dummy_debug_data; + RunInterpreter(setup, state, dummy_debug_data, config.main_offset); + } #else - RunInterpreter(setup, state, config.main_offset); + DebugData<false> dummy_debug_data; + RunInterpreter(setup, state, dummy_debug_data, config.main_offset); #endif // ARCHITECTURE_x86_64 } DebugData<true> ShaderSetup::ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const ShaderSetup& setup) { - UnitState<true> state; - - state.debug.max_offset = 0; - state.debug.max_opdesc_id = 0; + UnitState state; + DebugData<true> debug_data; // Setup input register table boost::fill(state.registers.input, Math::Vec4<float24>::AssignToAll(float24::Zero())); @@ -154,8 +152,8 @@ DebugData<true> ShaderSetup::ProduceDebugInfo(const InputVertex& input, int num_ state.conditional_code[0] = false; state.conditional_code[1] = false; - RunInterpreter(setup, state, config.main_offset); - return state.debug; + RunInterpreter(setup, state, debug_data, config.main_offset); + return debug_data; } } // namespace Shader diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h index 8858d67f8..2b07759b9 100644 --- a/src/video_core/shader/shader.h +++ b/src/video_core/shader/shader.h @@ -8,8 +8,6 @@ #include <cstddef> #include <memory> #include <type_traits> -#include <vector> -#include <boost/container/static_vector.hpp> #include <nihstro/shader_bytecode.h> #include "common/assert.h" #include "common/common_funcs.h" @@ -17,6 +15,7 @@ #include "common/vector_math.h" #include "video_core/pica.h" #include "video_core/pica_types.h" +#include "video_core/shader/debug_data.h" using nihstro::RegisterType; using nihstro::SourceRegister; @@ -85,187 +84,16 @@ struct OutputRegisters { alignas(16) Math::Vec4<float24> value[16]; - OutputVertex ToVertex(const Regs::ShaderConfig& config); + OutputVertex ToVertex(const Regs::ShaderConfig& config) const; }; static_assert(std::is_pod<OutputRegisters>::value, "Structure is not POD"); -// Helper structure used to keep track of data useful for inspection of shader emulation -template <bool full_debugging> -struct DebugData; - -template <> -struct DebugData<false> { - // TODO: Hide these behind and interface and move them to DebugData<true> - u32 max_offset; // maximum program counter ever reached - u32 max_opdesc_id; // maximum swizzle pattern index ever used -}; - -template <> -struct DebugData<true> { - // Records store the input and output operands of a particular instruction. - struct Record { - enum Type { - // Floating point arithmetic operands - SRC1 = 0x1, - SRC2 = 0x2, - SRC3 = 0x4, - - // Initial and final output operand value - DEST_IN = 0x8, - DEST_OUT = 0x10, - - // Current and next instruction offset (in words) - CUR_INSTR = 0x20, - NEXT_INSTR = 0x40, - - // Output address register value - ADDR_REG_OUT = 0x80, - - // Result of a comparison instruction - CMP_RESULT = 0x100, - - // Input values for conditional flow control instructions - COND_BOOL_IN = 0x200, - COND_CMP_IN = 0x400, - - // Input values for a loop - LOOP_INT_IN = 0x800, - }; - - Math::Vec4<float24> src1; - Math::Vec4<float24> src2; - Math::Vec4<float24> src3; - - Math::Vec4<float24> dest_in; - Math::Vec4<float24> dest_out; - - s32 address_registers[2]; - bool conditional_code[2]; - bool cond_bool; - bool cond_cmp[2]; - Math::Vec4<u8> loop_int; - - u32 instruction_offset; - u32 next_instruction; - - // set of enabled fields (as a combination of Type flags) - unsigned mask = 0; - }; - - u32 max_offset; // maximum program counter ever reached - u32 max_opdesc_id; // maximum swizzle pattern index ever used - - // List of records for each executed shader instruction - std::vector<DebugData<true>::Record> records; -}; - -// Type alias for better readability -using DebugDataRecord = DebugData<true>::Record; - -// Helper function to set a DebugData<true>::Record field based on the template enum parameter. -template <DebugDataRecord::Type type, typename ValueType> -inline void SetField(DebugDataRecord& record, ValueType value); - -template <> -inline void SetField<DebugDataRecord::SRC1>(DebugDataRecord& record, float24* value) { - record.src1.x = value[0]; - record.src1.y = value[1]; - record.src1.z = value[2]; - record.src1.w = value[3]; -} - -template <> -inline void SetField<DebugDataRecord::SRC2>(DebugDataRecord& record, float24* value) { - record.src2.x = value[0]; - record.src2.y = value[1]; - record.src2.z = value[2]; - record.src2.w = value[3]; -} - -template <> -inline void SetField<DebugDataRecord::SRC3>(DebugDataRecord& record, float24* value) { - record.src3.x = value[0]; - record.src3.y = value[1]; - record.src3.z = value[2]; - record.src3.w = value[3]; -} - -template <> -inline void SetField<DebugDataRecord::DEST_IN>(DebugDataRecord& record, float24* value) { - record.dest_in.x = value[0]; - record.dest_in.y = value[1]; - record.dest_in.z = value[2]; - record.dest_in.w = value[3]; -} - -template <> -inline void SetField<DebugDataRecord::DEST_OUT>(DebugDataRecord& record, float24* value) { - record.dest_out.x = value[0]; - record.dest_out.y = value[1]; - record.dest_out.z = value[2]; - record.dest_out.w = value[3]; -} - -template <> -inline void SetField<DebugDataRecord::ADDR_REG_OUT>(DebugDataRecord& record, s32* value) { - record.address_registers[0] = value[0]; - record.address_registers[1] = value[1]; -} - -template <> -inline void SetField<DebugDataRecord::CMP_RESULT>(DebugDataRecord& record, bool* value) { - record.conditional_code[0] = value[0]; - record.conditional_code[1] = value[1]; -} - -template <> -inline void SetField<DebugDataRecord::COND_BOOL_IN>(DebugDataRecord& record, bool value) { - record.cond_bool = value; -} - -template <> -inline void SetField<DebugDataRecord::COND_CMP_IN>(DebugDataRecord& record, bool* value) { - record.cond_cmp[0] = value[0]; - record.cond_cmp[1] = value[1]; -} - -template <> -inline void SetField<DebugDataRecord::LOOP_INT_IN>(DebugDataRecord& record, Math::Vec4<u8> value) { - record.loop_int = value; -} - -template <> -inline void SetField<DebugDataRecord::CUR_INSTR>(DebugDataRecord& record, u32 value) { - record.instruction_offset = value; -} - -template <> -inline void SetField<DebugDataRecord::NEXT_INSTR>(DebugDataRecord& record, u32 value) { - record.next_instruction = value; -} - -// Helper function to set debug information on the current shader iteration. -template <DebugDataRecord::Type type, typename ValueType> -inline void Record(DebugData<false>& debug_data, u32 offset, ValueType value) { - // Debugging disabled => nothing to do -} - -template <DebugDataRecord::Type type, typename ValueType> -inline void Record(DebugData<true>& debug_data, u32 offset, ValueType value) { - if (offset >= debug_data.records.size()) - debug_data.records.resize(offset + 1); - - SetField<type, ValueType>(debug_data.records[offset], value); - debug_data.records[offset].mask |= type; -} - /** * This structure contains the state information that needs to be unique for a shader unit. The 3DS * has four shader units that process shaders in parallel. At the present, Citra only implements a * single shader unit that processes all shaders serially. Putting the state information in a struct * here will make it easier for us to parallelize the shader processing later. */ -template <bool Debug> struct UnitState { struct Registers { // The registers are accessed by the shader JIT using SSE instructions, and are therefore @@ -283,8 +111,6 @@ struct UnitState { // TODO: How many bits do these actually have? s32 address_registers[3]; - DebugData<Debug> debug; - static size_t InputOffset(const SourceRegister& reg) { switch (reg.GetRegisterType()) { case RegisterType::Input: @@ -332,21 +158,16 @@ struct ShaderSetup { std::array<Math::Vec4<u8>, 4> i; } uniforms; - static size_t UniformOffset(RegisterType type, unsigned index) { - switch (type) { - case RegisterType::FloatUniform: - return offsetof(ShaderSetup, uniforms.f) + index * sizeof(Math::Vec4<float24>); - - case RegisterType::BoolUniform: - return offsetof(ShaderSetup, uniforms.b) + index * sizeof(bool); + static size_t GetFloatUniformOffset(unsigned index) { + return offsetof(ShaderSetup, uniforms.f) + index * sizeof(Math::Vec4<float24>); + } - case RegisterType::IntUniform: - return offsetof(ShaderSetup, uniforms.i) + index * sizeof(Math::Vec4<u8>); + static size_t GetBoolUniformOffset(unsigned index) { + return offsetof(ShaderSetup, uniforms.b) + index * sizeof(bool); + } - default: - UNREACHABLE(); - return 0; - } + static size_t GetIntUniformOffset(unsigned index) { + return offsetof(ShaderSetup, uniforms.i) + index * sizeof(Math::Vec4<u8>); } std::array<u32, 1024> program_code; @@ -364,7 +185,7 @@ struct ShaderSetup { * @param input Input vertex into the shader * @param num_attributes The number of vertex shader attributes */ - void Run(UnitState<false>& state, const InputVertex& input, int num_attributes); + void Run(UnitState& state, const InputVertex& input, int num_attributes); /** * Produce debug information based on the given shader and input vertex diff --git a/src/video_core/shader/shader_interpreter.cpp b/src/video_core/shader/shader_interpreter.cpp index 6abb6761f..70db4167e 100644 --- a/src/video_core/shader/shader_interpreter.cpp +++ b/src/video_core/shader/shader_interpreter.cpp @@ -6,6 +6,7 @@ #include <array> #include <cmath> #include <numeric> +#include <boost/container/static_vector.hpp> #include <nihstro/shader_bytecode.h> #include "common/assert.h" #include "common/common_types.h" @@ -38,12 +39,42 @@ struct CallStackElement { }; template <bool Debug> -void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned offset) { +void RunInterpreter(const ShaderSetup& setup, UnitState& state, DebugData<Debug>& debug_data, + unsigned offset) { // TODO: Is there a maximal size for this? boost::container::static_vector<CallStackElement, 16> call_stack; - u32 program_counter = offset; + auto call = [&program_counter, &call_stack](u32 offset, u32 num_instructions, u32 return_offset, + u8 repeat_count, u8 loop_increment) { + // -1 to make sure when incrementing the PC we end up at the correct offset + program_counter = offset - 1; + ASSERT(call_stack.size() < call_stack.capacity()); + call_stack.push_back( + {offset + num_instructions, return_offset, repeat_count, loop_increment, offset}); + }; + + auto evaluate_condition = [&state](Instruction::FlowControlType flow_control) { + using Op = Instruction::FlowControlType::Op; + + bool result_x = flow_control.refx.Value() == state.conditional_code[0]; + bool result_y = flow_control.refy.Value() == state.conditional_code[1]; + + switch (flow_control.op) { + case Op::Or: + return result_x || result_y; + case Op::And: + return result_x && result_y; + case Op::JustX: + return result_x; + case Op::JustY: + return result_y; + default: + UNREACHABLE(); + return false; + } + }; + const auto& uniforms = g_state.vs.uniforms; const auto& swizzle_data = g_state.vs.swizzle_data; const auto& program_code = g_state.vs.program_code; @@ -74,20 +105,11 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned const Instruction instr = {program_code[program_counter]}; const SwizzlePattern swizzle = {swizzle_data[instr.common.operand_desc_id]}; - auto call = [&program_counter, &call_stack](UnitState<Debug>& state, u32 offset, - u32 num_instructions, u32 return_offset, - u8 repeat_count, u8 loop_increment) { - // -1 to make sure when incrementing the PC we end up at the correct offset - program_counter = offset - 1; - ASSERT(call_stack.size() < call_stack.capacity()); - call_stack.push_back( - {offset + num_instructions, return_offset, repeat_count, loop_increment, offset}); - }; - Record<DebugDataRecord::CUR_INSTR>(state.debug, iteration, program_counter); + Record<DebugDataRecord::CUR_INSTR>(debug_data, iteration, program_counter); if (iteration > 0) - Record<DebugDataRecord::NEXT_INSTR>(state.debug, iteration - 1, program_counter); + Record<DebugDataRecord::NEXT_INSTR>(debug_data, iteration - 1, program_counter); - state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + program_counter); + debug_data.max_offset = std::max<u32>(debug_data.max_offset, 1 + program_counter); auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* { switch (source_reg.GetRegisterType()) { @@ -155,54 +177,54 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned ? &state.registers.temporary[instr.common.dest.Value().GetIndex()][0] : dummy_vec4_float24; - state.debug.max_opdesc_id = - std::max<u32>(state.debug.max_opdesc_id, 1 + instr.common.operand_desc_id); + debug_data.max_opdesc_id = + std::max<u32>(debug_data.max_opdesc_id, 1 + instr.common.operand_desc_id); switch (instr.opcode.Value().EffectiveOpCode()) { case OpCode::Id::ADD: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; dest[i] = src1[i] + src2[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } case OpCode::Id::MUL: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; dest[i] = src1[i] * src2[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } case OpCode::Id::FLR: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; dest[i] = float24::FromFloat32(std::floor(src1[i].ToFloat32())); } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; case OpCode::Id::MAX: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -212,13 +234,13 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned // max(NaN, 0) -> 0 dest[i] = (src1[i] > src2[i]) ? src1[i] : src2[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; case OpCode::Id::MIN: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -228,16 +250,16 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned // min(NaN, 0) -> 0 dest[i] = (src1[i] < src2[i]) ? src1[i] : src2[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; case OpCode::Id::DP3: case OpCode::Id::DP4: case OpCode::Id::DPH: case OpCode::Id::DPHI: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); OpCode::Id opcode = instr.opcode.Value().EffectiveOpCode(); if (opcode == OpCode::Id::DPH || opcode == OpCode::Id::DPHI) @@ -253,14 +275,14 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = dot; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } // Reciprocal case OpCode::Id::RCP: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); float24 rcp_res = float24::FromFloat32(1.0f / src1[0].ToFloat32()); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) @@ -268,14 +290,14 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = rcp_res; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } // Reciprocal Square Root case OpCode::Id::RSQ: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); float24 rsq_res = float24::FromFloat32(1.0f / std::sqrt(src1[0].ToFloat32())); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) @@ -283,12 +305,12 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = rsq_res; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } case OpCode::Id::MOVA: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); for (int i = 0; i < 2; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -296,29 +318,29 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned // TODO: Figure out how the rounding is done on hardware state.address_registers[i] = static_cast<s32>(src1[i].ToFloat32()); } - Record<DebugDataRecord::ADDR_REG_OUT>(state.debug, iteration, + Record<DebugDataRecord::ADDR_REG_OUT>(debug_data, iteration, state.address_registers); break; } case OpCode::Id::MOV: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; dest[i] = src1[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } case OpCode::Id::SGE: case OpCode::Id::SGEI: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -326,14 +348,14 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = (src1[i] >= src2[i]) ? float24::FromFloat32(1.0f) : float24::FromFloat32(0.0f); } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; case OpCode::Id::SLT: case OpCode::Id::SLTI: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -341,12 +363,12 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = (src1[i] < src2[i]) ? float24::FromFloat32(1.0f) : float24::FromFloat32(0.0f); } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; case OpCode::Id::CMP: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); for (int i = 0; i < 2; ++i) { // TODO: Can you restrict to one compare via dest masking? @@ -383,12 +405,12 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned break; } } - Record<DebugDataRecord::CMP_RESULT>(state.debug, iteration, state.conditional_code); + Record<DebugDataRecord::CMP_RESULT>(debug_data, iteration, state.conditional_code); break; case OpCode::Id::EX2: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); // EX2 only takes first component exp2 and writes it to all dest components float24 ex2_res = float24::FromFloat32(std::exp2(src1[0].ToFloat32())); @@ -399,13 +421,13 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = ex2_res; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } case OpCode::Id::LG2: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); // LG2 only takes the first component log2 and writes it to all dest components float24 lg2_res = float24::FromFloat32(std::log2(src1[0].ToFloat32())); @@ -416,7 +438,7 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = lg2_res; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } @@ -498,17 +520,17 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned ? &state.registers.temporary[instr.mad.dest.Value().GetIndex()][0] : dummy_vec4_float24; - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::SRC3>(state.debug, iteration, src3); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::SRC3>(debug_data, iteration, src3); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; dest[i] = src1[i] * src2[i] + src3[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); } else { LOG_ERROR(HW_GPU, "Unhandled multiply-add instruction: 0x%02x (%s): 0x%08x", (int)instr.opcode.Value().EffectiveOpCode(), @@ -518,26 +540,6 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned } default: { - static auto evaluate_condition = [](const UnitState<Debug>& state, bool refx, bool refy, - Instruction::FlowControlType flow_control) { - bool results[2] = {refx == state.conditional_code[0], - refy == state.conditional_code[1]}; - - switch (flow_control.op) { - case flow_control.Or: - return results[0] || results[1]; - - case flow_control.And: - return results[0] && results[1]; - - case flow_control.JustX: - return results[0]; - - case flow_control.JustY: - return results[1]; - } - }; - // Handle each instruction on its own switch (instr.opcode.Value()) { case OpCode::Id::END: @@ -545,17 +547,15 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned break; case OpCode::Id::JMPC: - Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, - state.conditional_code); - if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, - instr.flow_control)) { + Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code); + if (evaluate_condition(instr.flow_control)) { program_counter = instr.flow_control.dest_offset - 1; } break; case OpCode::Id::JMPU: Record<DebugDataRecord::COND_BOOL_IN>( - state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); + debug_data, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); if (uniforms.b[instr.flow_control.bool_uniform_id] == !(instr.flow_control.num_instructions & 1)) { @@ -564,25 +564,23 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned break; case OpCode::Id::CALL: - call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, + call(instr.flow_control.dest_offset, instr.flow_control.num_instructions, program_counter + 1, 0, 0); break; case OpCode::Id::CALLU: Record<DebugDataRecord::COND_BOOL_IN>( - state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); + debug_data, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); if (uniforms.b[instr.flow_control.bool_uniform_id]) { - call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, + call(instr.flow_control.dest_offset, instr.flow_control.num_instructions, program_counter + 1, 0, 0); } break; case OpCode::Id::CALLC: - Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, - state.conditional_code); - if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, - instr.flow_control)) { - call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, + Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code); + if (evaluate_condition(instr.flow_control)) { + call(instr.flow_control.dest_offset, instr.flow_control.num_instructions, program_counter + 1, 0, 0); } break; @@ -592,14 +590,13 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned case OpCode::Id::IFU: Record<DebugDataRecord::COND_BOOL_IN>( - state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); + debug_data, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); if (uniforms.b[instr.flow_control.bool_uniform_id]) { - call(state, program_counter + 1, - instr.flow_control.dest_offset - program_counter - 1, + call(program_counter + 1, instr.flow_control.dest_offset - program_counter - 1, instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0); } else { - call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, + call(instr.flow_control.dest_offset, instr.flow_control.num_instructions, instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0); } @@ -609,16 +606,13 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned case OpCode::Id::IFC: { // TODO: Do we need to consider swizzlers here? - Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, - state.conditional_code); - if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, - instr.flow_control)) { - call(state, program_counter + 1, - instr.flow_control.dest_offset - program_counter - 1, + Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code); + if (evaluate_condition(instr.flow_control)) { + call(program_counter + 1, instr.flow_control.dest_offset - program_counter - 1, instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0); } else { - call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, + call(instr.flow_control.dest_offset, instr.flow_control.num_instructions, instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0); } @@ -633,9 +627,8 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned uniforms.i[instr.flow_control.int_uniform_id].w); state.address_registers[2] = loop_param.y; - Record<DebugDataRecord::LOOP_INT_IN>(state.debug, iteration, loop_param); - call(state, program_counter + 1, - instr.flow_control.dest_offset - program_counter + 1, + Record<DebugDataRecord::LOOP_INT_IN>(debug_data, iteration, loop_param); + call(program_counter + 1, instr.flow_control.dest_offset - program_counter + 1, instr.flow_control.dest_offset + 1, loop_param.x, loop_param.z); break; } @@ -657,8 +650,8 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned } // Explicit instantiation -template void RunInterpreter(const ShaderSetup& setup, UnitState<false>& state, unsigned offset); -template void RunInterpreter(const ShaderSetup& setup, UnitState<true>& state, unsigned offset); +template void RunInterpreter(const ShaderSetup&, UnitState&, DebugData<false>&, unsigned offset); +template void RunInterpreter(const ShaderSetup&, UnitState&, DebugData<true>&, unsigned offset); } // namespace diff --git a/src/video_core/shader/shader_interpreter.h b/src/video_core/shader/shader_interpreter.h index 48ede0a2e..d31dcd7a6 100644 --- a/src/video_core/shader/shader_interpreter.h +++ b/src/video_core/shader/shader_interpreter.h @@ -8,11 +8,14 @@ namespace Pica { namespace Shader { -template <bool Debug> struct UnitState; template <bool Debug> -void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned offset); +struct DebugData; + +template <bool Debug> +void RunInterpreter(const ShaderSetup& setup, UnitState& state, DebugData<Debug>& debug_data, + unsigned offset); } // namespace diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index c96110bb2..c588b778b 100644 --- a/src/video_core/shader/shader_jit_x64.cpp +++ b/src/video_core/shader/shader_jit_x64.cpp @@ -6,24 +6,30 @@ #include <cmath> #include <cstdint> #include <nihstro/shader_bytecode.h> +#include <smmintrin.h> #include <xmmintrin.h> #include "common/assert.h" #include "common/logging/log.h" #include "common/vector_math.h" -#include "common/x64/abi.h" #include "common/x64/cpu_detect.h" -#include "common/x64/emitter.h" -#include "shader.h" -#include "shader_jit_x64.h" +#include "common/x64/xbyak_abi.h" +#include "common/x64/xbyak_util.h" #include "video_core/pica_state.h" #include "video_core/pica_types.h" +#include "video_core/shader/shader.h" +#include "video_core/shader/shader_jit_x64.h" + +using namespace Common::X64; +using namespace Xbyak::util; +using Xbyak::Label; +using Xbyak::Reg32; +using Xbyak::Reg64; +using Xbyak::Xmm; namespace Pica { namespace Shader { -using namespace Gen; - typedef void (JitShader::*JitFunction)(Instruction instr); const JitFunction instr_table[64] = { @@ -98,44 +104,47 @@ const JitFunction instr_table[64] = { // purposes, as documented below: /// Pointer to the uniform memory -static const X64Reg SETUP = R9; +static const Reg64 SETUP = r9; /// The two 32-bit VS address offset registers set by the MOVA instruction -static const X64Reg ADDROFFS_REG_0 = R10; -static const X64Reg ADDROFFS_REG_1 = R11; +static const Reg64 ADDROFFS_REG_0 = r10; +static const Reg64 ADDROFFS_REG_1 = r11; /// VS loop count register (Multiplied by 16) -static const X64Reg LOOPCOUNT_REG = R12; +static const Reg32 LOOPCOUNT_REG = r12d; /// Current VS loop iteration number (we could probably use LOOPCOUNT_REG, but this quicker) -static const X64Reg LOOPCOUNT = RSI; +static const Reg32 LOOPCOUNT = esi; /// Number to increment LOOPCOUNT_REG by on each loop iteration (Multiplied by 16) -static const X64Reg LOOPINC = RDI; +static const Reg32 LOOPINC = edi; /// Result of the previous CMP instruction for the X-component comparison -static const X64Reg COND0 = R13; +static const Reg64 COND0 = r13; /// Result of the previous CMP instruction for the Y-component comparison -static const X64Reg COND1 = R14; +static const Reg64 COND1 = r14; /// Pointer to the UnitState instance for the current VS unit -static const X64Reg STATE = R15; +static const Reg64 STATE = r15; /// SIMD scratch register -static const X64Reg SCRATCH = XMM0; +static const Xmm SCRATCH = xmm0; /// Loaded with the first swizzled source register, otherwise can be used as a scratch register -static const X64Reg SRC1 = XMM1; +static const Xmm SRC1 = xmm1; /// Loaded with the second swizzled source register, otherwise can be used as a scratch register -static const X64Reg SRC2 = XMM2; +static const Xmm SRC2 = xmm2; /// Loaded with the third swizzled source register, otherwise can be used as a scratch register -static const X64Reg SRC3 = XMM3; +static const Xmm SRC3 = xmm3; /// Additional scratch register -static const X64Reg SCRATCH2 = XMM4; +static const Xmm SCRATCH2 = xmm4; /// Constant vector of [1.0f, 1.0f, 1.0f, 1.0f], used to efficiently set a vector to one -static const X64Reg ONE = XMM14; +static const Xmm ONE = xmm14; /// Constant vector of [-0.f, -0.f, -0.f, -0.f], used to efficiently negate a vector with XOR -static const X64Reg NEGBIT = XMM15; +static const Xmm NEGBIT = xmm15; // State registers that must not be modified by external functions calls // Scratch registers, e.g., SRC1 and SCRATCH, have to be saved on the side if needed -static const BitSet32 persistent_regs = { - SETUP, STATE, // Pointers to register blocks - ADDROFFS_REG_0, ADDROFFS_REG_1, LOOPCOUNT_REG, COND0, COND1, // Cached registers - ONE + 16, NEGBIT + 16, // Constants -}; +static const BitSet32 persistent_regs = BuildRegSet({ + // Pointers to register blocks + SETUP, STATE, + // Cached registers + ADDROFFS_REG_0, ADDROFFS_REG_1, LOOPCOUNT_REG, COND0, COND1, + // Constants + ONE, NEGBIT, +}); /// Raw constant for the source register selector that indicates no swizzling is performed static const u8 NO_SRC_REG_SWIZZLE = 0x1b; @@ -157,7 +166,8 @@ static void LogCritical(const char* msg) { void JitShader::Compile_Assert(bool condition, const char* msg) { if (!condition) { - ABI_CallFunctionP(reinterpret_cast<const void*>(LogCritical), const_cast<char*>(msg)); + mov(ABI_PARAM1, reinterpret_cast<size_t>(msg)); + CallFarFunction(*this, LogCritical); } } @@ -169,16 +179,16 @@ void JitShader::Compile_Assert(bool condition, const char* msg) { * @param dest Destination XMM register to store the loaded, swizzled source register */ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, - X64Reg dest) { - X64Reg src_ptr; + Xmm dest) { + Reg64 src_ptr; size_t src_offset; if (src_reg.GetRegisterType() == RegisterType::FloatUniform) { src_ptr = SETUP; - src_offset = ShaderSetup::UniformOffset(RegisterType::FloatUniform, src_reg.GetIndex()); + src_offset = ShaderSetup::GetFloatUniformOffset(src_reg.GetIndex()); } else { src_ptr = STATE; - src_offset = UnitState<false>::InputOffset(src_reg); + src_offset = UnitState::InputOffset(src_reg); } int src_offset_disp = (int)src_offset; @@ -206,13 +216,13 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe if (src_num == offset_src && address_register_index != 0) { switch (address_register_index) { case 1: // address offset 1 - MOVAPS(dest, MComplex(src_ptr, ADDROFFS_REG_0, SCALE_1, src_offset_disp)); + movaps(dest, xword[src_ptr + ADDROFFS_REG_0 + src_offset_disp]); break; case 2: // address offset 2 - MOVAPS(dest, MComplex(src_ptr, ADDROFFS_REG_1, SCALE_1, src_offset_disp)); + movaps(dest, xword[src_ptr + ADDROFFS_REG_1 + src_offset_disp]); break; case 3: // address offset 3 - MOVAPS(dest, MComplex(src_ptr, LOOPCOUNT_REG, SCALE_1, src_offset_disp)); + movaps(dest, xword[src_ptr + LOOPCOUNT_REG.cvt64() + src_offset_disp]); break; default: UNREACHABLE(); @@ -220,7 +230,7 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe } } else { // Load the source - MOVAPS(dest, MDisp(src_ptr, src_offset_disp)); + movaps(dest, xword[src_ptr + src_offset_disp]); } SwizzlePattern swiz = {g_state.vs.swizzle_data[operand_desc_id]}; @@ -232,17 +242,17 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe sel = ((sel & 0xc0) >> 6) | ((sel & 3) << 6) | ((sel & 0xc) << 2) | ((sel & 0x30) >> 2); // Shuffle inputs for swizzle - SHUFPS(dest, R(dest), sel); + shufps(dest, dest, sel); } // If the source register should be negated, flip the negative bit using XOR const bool negate[] = {swiz.negate_src1, swiz.negate_src2, swiz.negate_src3}; if (negate[src_num - 1]) { - XORPS(dest, R(NEGBIT)); + xorps(dest, NEGBIT); } } -void JitShader::Compile_DestEnable(Instruction instr, X64Reg src) { +void JitShader::Compile_DestEnable(Instruction instr, Xmm src) { DestRegister dest; unsigned operand_desc_id; if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD || @@ -256,28 +266,26 @@ void JitShader::Compile_DestEnable(Instruction instr, X64Reg src) { SwizzlePattern swiz = {g_state.vs.swizzle_data[operand_desc_id]}; - int dest_offset_disp = (int)UnitState<false>::OutputOffset(dest); - ASSERT_MSG(dest_offset_disp == UnitState<false>::OutputOffset(dest), - "Destinaton offset too large for int type"); + size_t dest_offset_disp = UnitState::OutputOffset(dest); // If all components are enabled, write the result to the destination register if (swiz.dest_mask == NO_DEST_REG_MASK) { // Store dest back to memory - MOVAPS(MDisp(STATE, dest_offset_disp), src); + movaps(xword[STATE + dest_offset_disp], src); } else { // Not all components are enabled, so mask the result when storing to the destination // register... - MOVAPS(SCRATCH, MDisp(STATE, dest_offset_disp)); + movaps(SCRATCH, xword[STATE + dest_offset_disp]); if (Common::GetCPUCaps().sse4_1) { u8 mask = ((swiz.dest_mask & 1) << 3) | ((swiz.dest_mask & 8) >> 3) | ((swiz.dest_mask & 2) << 1) | ((swiz.dest_mask & 4) >> 1); - BLENDPS(SCRATCH, R(src), mask); + blendps(SCRATCH, src, mask); } else { - MOVAPS(SCRATCH2, R(src)); - UNPCKHPS(SCRATCH2, R(SCRATCH)); // Unpack X/Y components of source and destination - UNPCKLPS(SCRATCH, R(src)); // Unpack Z/W components of source and destination + movaps(SCRATCH2, src); + unpckhps(SCRATCH2, SCRATCH); // Unpack X/Y components of source and destination + unpcklps(SCRATCH, src); // Unpack Z/W components of source and destination // Compute selector to selectively copy source components to destination for SHUFPS // instruction @@ -285,62 +293,61 @@ void JitShader::Compile_DestEnable(Instruction instr, X64Reg src) { ((swiz.DestComponentEnabled(1) ? 3 : 2) << 2) | ((swiz.DestComponentEnabled(2) ? 0 : 1) << 4) | ((swiz.DestComponentEnabled(3) ? 2 : 3) << 6); - SHUFPS(SCRATCH, R(SCRATCH2), sel); + shufps(SCRATCH, SCRATCH2, sel); } // Store dest back to memory - MOVAPS(MDisp(STATE, dest_offset_disp), SCRATCH); + movaps(xword[STATE + dest_offset_disp], SCRATCH); } } -void JitShader::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch) { - MOVAPS(scratch, R(src1)); - CMPPS(scratch, R(src2), CMP_ORD); +void JitShader::Compile_SanitizedMul(Xmm src1, Xmm src2, Xmm scratch) { + movaps(scratch, src1); + cmpordps(scratch, src2); - MULPS(src1, R(src2)); + mulps(src1, src2); - MOVAPS(src2, R(src1)); - CMPPS(src2, R(src2), CMP_UNORD); + movaps(src2, src1); + cmpunordps(src2, src2); - XORPS(scratch, R(src2)); - ANDPS(src1, R(scratch)); + xorps(scratch, src2); + andps(src1, scratch); } void JitShader::Compile_EvaluateCondition(Instruction instr) { // Note: NXOR is used below to check for equality switch (instr.flow_control.op) { case Instruction::FlowControlType::Or: - MOV(32, R(RAX), R(COND0)); - MOV(32, R(RBX), R(COND1)); - XOR(32, R(RAX), Imm32(instr.flow_control.refx.Value() ^ 1)); - XOR(32, R(RBX), Imm32(instr.flow_control.refy.Value() ^ 1)); - OR(32, R(RAX), R(RBX)); + mov(eax, COND0); + mov(ebx, COND1); + xor(eax, (instr.flow_control.refx.Value() ^ 1)); + xor(ebx, (instr.flow_control.refy.Value() ^ 1)); + or (eax, ebx); break; case Instruction::FlowControlType::And: - MOV(32, R(RAX), R(COND0)); - MOV(32, R(RBX), R(COND1)); - XOR(32, R(RAX), Imm32(instr.flow_control.refx.Value() ^ 1)); - XOR(32, R(RBX), Imm32(instr.flow_control.refy.Value() ^ 1)); - AND(32, R(RAX), R(RBX)); + mov(eax, COND0); + mov(ebx, COND1); + xor(eax, (instr.flow_control.refx.Value() ^ 1)); + xor(ebx, (instr.flow_control.refy.Value() ^ 1)); + and(eax, ebx); break; case Instruction::FlowControlType::JustX: - MOV(32, R(RAX), R(COND0)); - XOR(32, R(RAX), Imm32(instr.flow_control.refx.Value() ^ 1)); + mov(eax, COND0); + xor(eax, (instr.flow_control.refx.Value() ^ 1)); break; case Instruction::FlowControlType::JustY: - MOV(32, R(RAX), R(COND1)); - XOR(32, R(RAX), Imm32(instr.flow_control.refy.Value() ^ 1)); + mov(eax, COND1); + xor(eax, (instr.flow_control.refy.Value() ^ 1)); break; } } void JitShader::Compile_UniformCondition(Instruction instr) { - int offset = - ShaderSetup::UniformOffset(RegisterType::BoolUniform, instr.flow_control.bool_uniform_id); - CMP(sizeof(bool) * 8, MDisp(SETUP, offset), Imm8(0)); + size_t offset = ShaderSetup::GetBoolUniformOffset(instr.flow_control.bool_uniform_id); + cmp(byte[SETUP + offset], 0); } BitSet32 JitShader::PersistentCallerSavedRegs() { @@ -350,7 +357,7 @@ BitSet32 JitShader::PersistentCallerSavedRegs() { void JitShader::Compile_ADD(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); - ADDPS(SRC1, R(SRC2)); + addps(SRC1, SRC2); Compile_DestEnable(instr, SRC1); } @@ -360,15 +367,15 @@ void JitShader::Compile_DP3(Instruction instr) { Compile_SanitizedMul(SRC1, SRC2, SCRATCH); - MOVAPS(SRC2, R(SRC1)); - SHUFPS(SRC2, R(SRC2), _MM_SHUFFLE(1, 1, 1, 1)); + movaps(SRC2, SRC1); + shufps(SRC2, SRC2, _MM_SHUFFLE(1, 1, 1, 1)); - MOVAPS(SRC3, R(SRC1)); - SHUFPS(SRC3, R(SRC3), _MM_SHUFFLE(2, 2, 2, 2)); + movaps(SRC3, SRC1); + shufps(SRC3, SRC3, _MM_SHUFFLE(2, 2, 2, 2)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 0, 0, 0)); - ADDPS(SRC1, R(SRC2)); - ADDPS(SRC1, R(SRC3)); + shufps(SRC1, SRC1, _MM_SHUFFLE(0, 0, 0, 0)); + addps(SRC1, SRC2); + addps(SRC1, SRC3); Compile_DestEnable(instr, SRC1); } @@ -379,13 +386,13 @@ void JitShader::Compile_DP4(Instruction instr) { Compile_SanitizedMul(SRC1, SRC2, SCRATCH); - MOVAPS(SRC2, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(2, 3, 0, 1)); // XYZW -> ZWXY - ADDPS(SRC1, R(SRC2)); + movaps(SRC2, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(2, 3, 0, 1)); // XYZW -> ZWXY + addps(SRC1, SRC2); - MOVAPS(SRC2, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 1, 2, 3)); // XYZW -> WZYX - ADDPS(SRC1, R(SRC2)); + movaps(SRC2, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(0, 1, 2, 3)); // XYZW -> WZYX + addps(SRC1, SRC2); Compile_DestEnable(instr, SRC1); } @@ -401,50 +408,50 @@ void JitShader::Compile_DPH(Instruction instr) { if (Common::GetCPUCaps().sse4_1) { // Set 4th component to 1.0 - BLENDPS(SRC1, R(ONE), 0x8); // 0b1000 + blendps(SRC1, ONE, 0b1000); } else { // Set 4th component to 1.0 - MOVAPS(SCRATCH, R(SRC1)); - UNPCKHPS(SCRATCH, R(ONE)); // XYZW, 1111 -> Z1__ - UNPCKLPD(SRC1, R(SCRATCH)); // XYZW, Z1__ -> XYZ1 + movaps(SCRATCH, SRC1); + unpckhps(SCRATCH, ONE); // XYZW, 1111 -> Z1__ + unpcklpd(SRC1, SCRATCH); // XYZW, Z1__ -> XYZ1 } Compile_SanitizedMul(SRC1, SRC2, SCRATCH); - MOVAPS(SRC2, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(2, 3, 0, 1)); // XYZW -> ZWXY - ADDPS(SRC1, R(SRC2)); + movaps(SRC2, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(2, 3, 0, 1)); // XYZW -> ZWXY + addps(SRC1, SRC2); - MOVAPS(SRC2, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 1, 2, 3)); // XYZW -> WZYX - ADDPS(SRC1, R(SRC2)); + movaps(SRC2, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(0, 1, 2, 3)); // XYZW -> WZYX + addps(SRC1, SRC2); Compile_DestEnable(instr, SRC1); } void JitShader::Compile_EX2(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); - MOVSS(XMM0, R(SRC1)); + movss(xmm0, SRC1); // ABI_PARAM1 - ABI_PushRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0); - ABI_CallFunction(reinterpret_cast<const void*>(exp2f)); - ABI_PopRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0); + ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); + CallFarFunction(*this, exp2f); + ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); - SHUFPS(XMM0, R(XMM0), _MM_SHUFFLE(0, 0, 0, 0)); - MOVAPS(SRC1, R(XMM0)); + shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); // ABI_RETURN + movaps(SRC1, xmm0); Compile_DestEnable(instr, SRC1); } void JitShader::Compile_LG2(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); - MOVSS(XMM0, R(SRC1)); + movss(xmm0, SRC1); // ABI_PARAM1 - ABI_PushRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0); - ABI_CallFunction(reinterpret_cast<const void*>(log2f)); - ABI_PopRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0); + ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); + CallFarFunction(*this, log2f); + ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); - SHUFPS(XMM0, R(XMM0), _MM_SHUFFLE(0, 0, 0, 0)); - MOVAPS(SRC1, R(XMM0)); + shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); // ABI_RETURN + movaps(SRC1, xmm0); Compile_DestEnable(instr, SRC1); } @@ -464,8 +471,8 @@ void JitShader::Compile_SGE(Instruction instr) { Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); } - CMPPS(SRC2, R(SRC1), CMP_LE); - ANDPS(SRC2, R(ONE)); + cmpleps(SRC2, SRC1); + andps(SRC2, ONE); Compile_DestEnable(instr, SRC2); } @@ -479,8 +486,8 @@ void JitShader::Compile_SLT(Instruction instr) { Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); } - CMPPS(SRC1, R(SRC2), CMP_LT); - ANDPS(SRC1, R(ONE)); + cmpltps(SRC1, SRC2); + andps(SRC1, ONE); Compile_DestEnable(instr, SRC1); } @@ -489,10 +496,10 @@ void JitShader::Compile_FLR(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); if (Common::GetCPUCaps().sse4_1) { - ROUNDFLOORPS(SRC1, R(SRC1)); + roundps(SRC1, SRC1, _MM_FROUND_FLOOR); } else { - CVTTPS2DQ(SRC1, R(SRC1)); - CVTDQ2PS(SRC1, R(SRC1)); + cvttps2dq(SRC1, SRC1); + cvtdq2ps(SRC1, SRC1); } Compile_DestEnable(instr, SRC1); @@ -502,7 +509,7 @@ void JitShader::Compile_MAX(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned. - MAXPS(SRC1, R(SRC2)); + maxps(SRC1, SRC2); Compile_DestEnable(instr, SRC1); } @@ -510,7 +517,7 @@ void JitShader::Compile_MIN(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned. - MINPS(SRC1, R(SRC2)); + minps(SRC1, SRC2); Compile_DestEnable(instr, SRC1); } @@ -524,37 +531,37 @@ void JitShader::Compile_MOVA(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); // Convert floats to integers using truncation (only care about X and Y components) - CVTTPS2DQ(SRC1, R(SRC1)); + cvttps2dq(SRC1, SRC1); // Get result - MOVQ_xmm(R(RAX), SRC1); + movq(rax, SRC1); // Handle destination enable if (swiz.DestComponentEnabled(0) && swiz.DestComponentEnabled(1)) { // Move and sign-extend low 32 bits - MOVSX(64, 32, ADDROFFS_REG_0, R(RAX)); + movsxd(ADDROFFS_REG_0, eax); // Move and sign-extend high 32 bits - SHR(64, R(RAX), Imm8(32)); - MOVSX(64, 32, ADDROFFS_REG_1, R(RAX)); + shr(rax, 32); + movsxd(ADDROFFS_REG_1, eax); // Multiply by 16 to be used as an offset later - SHL(64, R(ADDROFFS_REG_0), Imm8(4)); - SHL(64, R(ADDROFFS_REG_1), Imm8(4)); + shl(ADDROFFS_REG_0, 4); + shl(ADDROFFS_REG_1, 4); } else { if (swiz.DestComponentEnabled(0)) { // Move and sign-extend low 32 bits - MOVSX(64, 32, ADDROFFS_REG_0, R(RAX)); + movsxd(ADDROFFS_REG_0, eax); // Multiply by 16 to be used as an offset later - SHL(64, R(ADDROFFS_REG_0), Imm8(4)); + shl(ADDROFFS_REG_0, 4); } else if (swiz.DestComponentEnabled(1)) { // Move and sign-extend high 32 bits - SHR(64, R(RAX), Imm8(32)); - MOVSX(64, 32, ADDROFFS_REG_1, R(RAX)); + shr(rax, 32); + movsxd(ADDROFFS_REG_1, eax); // Multiply by 16 to be used as an offset later - SHL(64, R(ADDROFFS_REG_1), Imm8(4)); + shl(ADDROFFS_REG_1, 4); } } } @@ -569,8 +576,8 @@ void JitShader::Compile_RCP(Instruction instr) { // TODO(bunnei): RCPSS is a pretty rough approximation, this might cause problems if Pica // performs this operation more accurately. This should be checked on hardware. - RCPSS(SRC1, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 0, 0, 0)); // XYWZ -> XXXX + rcpss(SRC1, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(0, 0, 0, 0)); // XYWZ -> XXXX Compile_DestEnable(instr, SRC1); } @@ -580,8 +587,8 @@ void JitShader::Compile_RSQ(Instruction instr) { // TODO(bunnei): RSQRTSS is a pretty rough approximation, this might cause problems if Pica // performs this operation more accurately. This should be checked on hardware. - RSQRTSS(SRC1, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 0, 0, 0)); // XYWZ -> XXXX + rsqrtss(SRC1, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(0, 0, 0, 0)); // XYWZ -> XXXX Compile_DestEnable(instr, SRC1); } @@ -589,34 +596,35 @@ void JitShader::Compile_RSQ(Instruction instr) { void JitShader::Compile_NOP(Instruction instr) {} void JitShader::Compile_END(Instruction instr) { - ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8); - RET(); + ABI_PopRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8); + ret(); } void JitShader::Compile_CALL(Instruction instr) { // Push offset of the return - PUSH(64, Imm32(instr.flow_control.dest_offset + instr.flow_control.num_instructions)); + push(qword, (instr.flow_control.dest_offset + instr.flow_control.num_instructions)); // Call the subroutine - FixupBranch b = CALL(); - fixup_branches.push_back({b, instr.flow_control.dest_offset}); + call(instruction_labels[instr.flow_control.dest_offset]); // Skip over the return offset that's on the stack - ADD(64, R(RSP), Imm32(8)); + add(rsp, 8); } void JitShader::Compile_CALLC(Instruction instr) { Compile_EvaluateCondition(instr); - FixupBranch b = J_CC(CC_Z, true); + Label b; + jz(b); Compile_CALL(instr); - SetJumpTarget(b); + L(b); } void JitShader::Compile_CALLU(Instruction instr) { Compile_UniformCondition(instr); - FixupBranch b = J_CC(CC_Z, true); + Label b; + jz(b); Compile_CALL(instr); - SetJumpTarget(b); + L(b); } void JitShader::Compile_CMP(Instruction instr) { @@ -633,33 +641,33 @@ void JitShader::Compile_CMP(Instruction instr) { static const u8 cmp[] = {CMP_EQ, CMP_NEQ, CMP_LT, CMP_LE, CMP_LT, CMP_LE}; bool invert_op_x = (op_x == Op::GreaterThan || op_x == Op::GreaterEqual); - Gen::X64Reg lhs_x = invert_op_x ? SRC2 : SRC1; - Gen::X64Reg rhs_x = invert_op_x ? SRC1 : SRC2; + Xmm lhs_x = invert_op_x ? SRC2 : SRC1; + Xmm rhs_x = invert_op_x ? SRC1 : SRC2; if (op_x == op_y) { // Compare X-component and Y-component together - CMPPS(lhs_x, R(rhs_x), cmp[op_x]); - MOVQ_xmm(R(COND0), lhs_x); + cmpps(lhs_x, rhs_x, cmp[op_x]); + movq(COND0, lhs_x); - MOV(64, R(COND1), R(COND0)); + mov(COND1, COND0); } else { bool invert_op_y = (op_y == Op::GreaterThan || op_y == Op::GreaterEqual); - Gen::X64Reg lhs_y = invert_op_y ? SRC2 : SRC1; - Gen::X64Reg rhs_y = invert_op_y ? SRC1 : SRC2; + Xmm lhs_y = invert_op_y ? SRC2 : SRC1; + Xmm rhs_y = invert_op_y ? SRC1 : SRC2; // Compare X-component - MOVAPS(SCRATCH, R(lhs_x)); - CMPSS(SCRATCH, R(rhs_x), cmp[op_x]); + movaps(SCRATCH, lhs_x); + cmpss(SCRATCH, rhs_x, cmp[op_x]); // Compare Y-component - CMPPS(lhs_y, R(rhs_y), cmp[op_y]); + cmpps(lhs_y, rhs_y, cmp[op_y]); - MOVQ_xmm(R(COND0), SCRATCH); - MOVQ_xmm(R(COND1), lhs_y); + movq(COND0, SCRATCH); + movq(COND1, lhs_y); } - SHR(32, R(COND0), Imm8(31)); - SHR(64, R(COND1), Imm8(63)); + shr(COND0.cvt32(), 31); // ignores upper 32 bits in source + shr(COND1, 63); } void JitShader::Compile_MAD(Instruction instr) { @@ -674,7 +682,7 @@ void JitShader::Compile_MAD(Instruction instr) { } Compile_SanitizedMul(SRC1, SRC2, SCRATCH); - ADDPS(SRC1, R(SRC3)); + addps(SRC1, SRC3); Compile_DestEnable(instr, SRC1); } @@ -682,6 +690,7 @@ void JitShader::Compile_MAD(Instruction instr) { void JitShader::Compile_IF(Instruction instr) { Compile_Assert(instr.flow_control.dest_offset >= program_counter, "Backwards if-statements not supported"); + Label l_else, l_endif; // Evaluate the "IF" condition if (instr.opcode.Value() == OpCode::Id::IFU) { @@ -689,26 +698,25 @@ void JitShader::Compile_IF(Instruction instr) { } else if (instr.opcode.Value() == OpCode::Id::IFC) { Compile_EvaluateCondition(instr); } - FixupBranch b = J_CC(CC_Z, true); + jz(l_else, T_NEAR); // Compile the code that corresponds to the condition evaluating as true Compile_Block(instr.flow_control.dest_offset); // If there isn't an "ELSE" condition, we are done here if (instr.flow_control.num_instructions == 0) { - SetJumpTarget(b); + L(l_else); return; } - FixupBranch b2 = J(true); - - SetJumpTarget(b); + jmp(l_endif, T_NEAR); + L(l_else); // This code corresponds to the "ELSE" condition // Comple the code that corresponds to the condition evaluating as false Compile_Block(instr.flow_control.dest_offset + instr.flow_control.num_instructions); - SetJumpTarget(b2); + L(l_endif); } void JitShader::Compile_LOOP(Instruction instr) { @@ -721,25 +729,25 @@ void JitShader::Compile_LOOP(Instruction instr) { // This decodes the fields from the integer uniform at index instr.flow_control.int_uniform_id. // The Y (LOOPCOUNT_REG) and Z (LOOPINC) component are kept multiplied by 16 (Left shifted by // 4 bits) to be used as an offset into the 16-byte vector registers later - int offset = - ShaderSetup::UniformOffset(RegisterType::IntUniform, instr.flow_control.int_uniform_id); - MOV(32, R(LOOPCOUNT), MDisp(SETUP, offset)); - MOV(32, R(LOOPCOUNT_REG), R(LOOPCOUNT)); - SHR(32, R(LOOPCOUNT_REG), Imm8(4)); - AND(32, R(LOOPCOUNT_REG), Imm32(0xFF0)); // Y-component is the start - MOV(32, R(LOOPINC), R(LOOPCOUNT)); - SHR(32, R(LOOPINC), Imm8(12)); - AND(32, R(LOOPINC), Imm32(0xFF0)); // Z-component is the incrementer - MOVZX(32, 8, LOOPCOUNT, R(LOOPCOUNT)); // X-component is iteration count - ADD(32, R(LOOPCOUNT), Imm8(1)); // Iteration count is X-component + 1 - - auto loop_start = GetCodePtr(); + size_t offset = ShaderSetup::GetIntUniformOffset(instr.flow_control.int_uniform_id); + mov(LOOPCOUNT, dword[SETUP + offset]); + mov(LOOPCOUNT_REG, LOOPCOUNT); + shr(LOOPCOUNT_REG, 4); + and(LOOPCOUNT_REG, 0xFF0); // Y-component is the start + mov(LOOPINC, LOOPCOUNT); + shr(LOOPINC, 12); + and(LOOPINC, 0xFF0); // Z-component is the incrementer + movzx(LOOPCOUNT, LOOPCOUNT.cvt8()); // X-component is iteration count + add(LOOPCOUNT, 1); // Iteration count is X-component + 1 + + Label l_loop_start; + L(l_loop_start); Compile_Block(instr.flow_control.dest_offset + 1); - ADD(32, R(LOOPCOUNT_REG), R(LOOPINC)); // Increment LOOPCOUNT_REG by Z-component - SUB(32, R(LOOPCOUNT), Imm8(1)); // Increment loop count by 1 - J_CC(CC_NZ, loop_start); // Loop if not equal + add(LOOPCOUNT_REG, LOOPINC); // Increment LOOPCOUNT_REG by Z-component + sub(LOOPCOUNT, 1); // Increment loop count by 1 + jnz(l_loop_start); // Loop if not equal looping = false; } @@ -755,8 +763,12 @@ void JitShader::Compile_JMP(Instruction instr) { bool inverted_condition = (instr.opcode.Value() == OpCode::Id::JMPU) && (instr.flow_control.num_instructions & 1); - FixupBranch b = J_CC(inverted_condition ? CC_Z : CC_NZ, true); - fixup_branches.push_back({b, instr.flow_control.dest_offset}); + Label& b = instruction_labels[instr.flow_control.dest_offset]; + if (inverted_condition) { + jz(b, T_NEAR); + } else { + jnz(b, T_NEAR); + } } void JitShader::Compile_Block(unsigned end) { @@ -767,13 +779,14 @@ void JitShader::Compile_Block(unsigned end) { void JitShader::Compile_Return() { // Peek return offset on the stack and check if we're at that offset - MOV(64, R(RAX), MDisp(RSP, 8)); - CMP(32, R(RAX), Imm32(program_counter)); + mov(rax, qword[rsp + 8]); + cmp(eax, (program_counter)); // If so, jump back to before CALL - FixupBranch b = J_CC(CC_NZ, true); - RET(); - SetJumpTarget(b); + Label b; + jnz(b); + ret(); + L(b); } void JitShader::Compile_NextInstr() { @@ -781,9 +794,7 @@ void JitShader::Compile_NextInstr() { Compile_Return(); } - ASSERT_MSG(code_ptr[program_counter] == nullptr, - "Tried to compile already compiled shader location!"); - code_ptr[program_counter] = GetCodePtr(); + L(instruction_labels[program_counter]); Instruction instr = GetVertexShaderInstruction(program_counter++); @@ -824,64 +835,53 @@ void JitShader::FindReturnOffsets() { void JitShader::Compile() { // Reset flow control state - program = (CompiledShader*)GetCodePtr(); + program = (CompiledShader*)getCurr(); program_counter = 0; looping = false; - code_ptr.fill(nullptr); - fixup_branches.clear(); + instruction_labels.fill(Xbyak::Label()); // Find all `CALL` instructions and identify return locations FindReturnOffsets(); // The stack pointer is 8 modulo 16 at the entry of a procedure - ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8); + ABI_PushRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8); - MOV(PTRBITS, R(SETUP), R(ABI_PARAM1)); - MOV(PTRBITS, R(STATE), R(ABI_PARAM2)); + mov(SETUP, ABI_PARAM1); + mov(STATE, ABI_PARAM2); // Zero address/loop registers - XOR(64, R(ADDROFFS_REG_0), R(ADDROFFS_REG_0)); - XOR(64, R(ADDROFFS_REG_1), R(ADDROFFS_REG_1)); - XOR(64, R(LOOPCOUNT_REG), R(LOOPCOUNT_REG)); + xor(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); + xor(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); + xor(LOOPCOUNT_REG, LOOPCOUNT_REG); // Used to set a register to one static const __m128 one = {1.f, 1.f, 1.f, 1.f}; - MOV(PTRBITS, R(RAX), ImmPtr(&one)); - MOVAPS(ONE, MatR(RAX)); + mov(rax, reinterpret_cast<size_t>(&one)); + movaps(ONE, xword[rax]); // Used to negate registers static const __m128 neg = {-0.f, -0.f, -0.f, -0.f}; - MOV(PTRBITS, R(RAX), ImmPtr(&neg)); - MOVAPS(NEGBIT, MatR(RAX)); + mov(rax, reinterpret_cast<size_t>(&neg)); + movaps(NEGBIT, xword[rax]); // Jump to start of the shader program - JMPptr(R(ABI_PARAM3)); + jmp(ABI_PARAM3); // Compile entire program Compile_Block(static_cast<unsigned>(g_state.vs.program_code.size())); - // Set the target for any incomplete branches now that the entire shader program has been - // emitted - for (const auto& branch : fixup_branches) { - SetJumpTarget(branch.first, code_ptr[branch.second]); - } - // Free memory that's no longer needed return_offsets.clear(); return_offsets.shrink_to_fit(); - fixup_branches.clear(); - fixup_branches.shrink_to_fit(); - uintptr_t size = - reinterpret_cast<uintptr_t>(GetCodePtr()) - reinterpret_cast<uintptr_t>(program); - ASSERT_MSG(size <= MAX_SHADER_SIZE, "Compiled a shader that exceeds the allocated size!"); + ready(); + uintptr_t size = reinterpret_cast<uintptr_t>(getCurr()) - reinterpret_cast<uintptr_t>(program); + ASSERT_MSG(size <= MAX_SHADER_SIZE, "Compiled a shader that exceeds the allocated size!"); LOG_DEBUG(HW_GPU, "Compiled shader size=%lu", size); } -JitShader::JitShader() { - AllocCodeSpace(MAX_SHADER_SIZE); -} +JitShader::JitShader() : Xbyak::CodeGenerator(MAX_SHADER_SIZE) {} } // namespace Shader diff --git a/src/video_core/shader/shader_jit_x64.h b/src/video_core/shader/shader_jit_x64.h index 98de5ecef..f37548306 100644 --- a/src/video_core/shader/shader_jit_x64.h +++ b/src/video_core/shader/shader_jit_x64.h @@ -9,6 +9,7 @@ #include <utility> #include <vector> #include <nihstro/shader_bytecode.h> +#include <xbyak.h> #include "common/bit_set.h" #include "common/common_types.h" #include "common/x64/emitter.h" @@ -29,12 +30,12 @@ constexpr size_t MAX_SHADER_SIZE = 1024 * 64; * This class implements the shader JIT compiler. It recompiles a Pica shader program into x86_64 * code that can be executed on the host machine directly. */ -class JitShader : public Gen::XCodeBlock { +class JitShader : public Xbyak::CodeGenerator { public: JitShader(); - void Run(const ShaderSetup& setup, UnitState<false>& state, unsigned offset) const { - program(&setup, &state, code_ptr[offset]); + void Run(const ShaderSetup& setup, UnitState& state, unsigned offset) const { + program(&setup, &state, instruction_labels[offset].getAddress()); } void Compile(); @@ -71,14 +72,14 @@ private: void Compile_NextInstr(); void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, - Gen::X64Reg dest); - void Compile_DestEnable(Instruction instr, Gen::X64Reg dest); + Xbyak::Xmm dest); + void Compile_DestEnable(Instruction instr, Xbyak::Xmm dest); /** * Compiles a `MUL src1, src2` operation, properly handling the PICA semantics when multiplying * zero by inf. Clobbers `src2` and `scratch`. */ - void Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch); + void Compile_SanitizedMul(Xbyak::Xmm src1, Xbyak::Xmm src2, Xbyak::Xmm scratch); void Compile_EvaluateCondition(Instruction instr); void Compile_UniformCondition(Instruction instr); @@ -103,7 +104,7 @@ private: void FindReturnOffsets(); /// Mapping of Pica VS instructions to pointers in the emitted code - std::array<const u8*, 1024> code_ptr; + std::array<Xbyak::Label, 1024> instruction_labels; /// Offsets in code where a return needs to be inserted std::vector<unsigned> return_offsets; @@ -111,9 +112,6 @@ private: unsigned program_counter = 0; ///< Offset of the next instruction to decode bool looping = false; ///< True if compiling a loop, used to check for nested loops - /// Branches that need to be fixed up once the entire shader program is compiled - std::vector<std::pair<Gen::FixupBranch, unsigned>> fixup_branches; - using CompiledShader = void(const void* setup, void* state, const u8* start_addr); CompiledShader* program = nullptr; }; |