diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..4c9c7975a --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,25 @@ +<!--- + +Please read the FAQ: +https://citra-emu.org/wiki/FAQ + +THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO: +http://discuss.citra-emu.org/ + +If the FAQ does not answer your question, please go to: +http://discuss.citra-emu.org/ + +==================================================== + +When submitting an issue, please check the following: + +- You have read the above. +- You have provided the version (commit hash) of Citra you are using. +- You have provided sufficient detail for the issue to be reproduced. +- You have provided system specs (if relevant). +- Please also provide: + - For crashes, a backtrace. + - For graphical issues, comparison screenshots with real hardware. + - For emulation inaccuracies, a test-case (if able). + +---> \ No newline at end of file diff --git a/.travis-build.sh b/.travis-build.sh index 511df04ac..8440b4f5c 100755 --- a/.travis-build.sh +++ b/.travis-build.sh @@ -11,12 +11,12 @@ fi #if OS is linux or is not set if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then - export CC=gcc-5 - export CXX=g++-5 + export CC=gcc-6 + export CXX=g++-6 export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH mkdir build && cd build - cmake -DCITRA_FORCE_QT4=ON .. + cmake .. make -j4 ctest -VV -C Release diff --git a/.travis-deps.sh b/.travis-deps.sh index 4a79feb70..aad9074bf 100755 --- a/.travis-deps.sh +++ b/.travis-deps.sh @@ -5,11 +5,11 @@ set -x #if OS is linux or is not set if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then - export CC=gcc-5 - export CXX=g++-5 + export CC=gcc-6 + export CXX=g++-6 mkdir -p $HOME/.local - curl -L http://www.cmake.org/files/v3.1/cmake-3.1.0-Linux-i386.tar.gz \ + curl -L http://www.cmake.org/files/v3.2/cmake-3.2.0-Linux-i386.tar.gz \ | tar -xz -C $HOME/.local --strip-components=1 ( @@ -21,6 +21,6 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then elif [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update > /dev/null # silence the very verbose output brew unlink cmake - brew install cmake31 qt5 sdl2 dylibbundler + brew install cmake qt5 sdl2 dylibbundler gem install xcpretty fi diff --git a/.travis-upload.sh b/.travis-upload.sh index d86775cb9..7838bf079 100755 --- a/.travis-upload.sh +++ b/.travis-upload.sh @@ -23,8 +23,103 @@ if [ "$TRAVIS_BRANCH" = "master" ]; then # move SDL2 libs into folder for deployment dylibbundler -b -x "${REV_NAME}/citra" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/" + + # Make the changes to make the citra-qt app standalone (i.e. not dependent on the current brew installation). + # To do this, the absolute references to each and every QT framework must be re-written to point to the local frameworks + # (in the Contents/Frameworks folder). + # The "install_name_tool" is used to do so. + + # Coreutils is a hack to coerce Homebrew to point to the absolute Cellar path (symlink dereferenced). i.e: + # ls -l /usr/local/opt/qt5:: /usr/local/opt/qt5 -> ../Cellar/qt5/5.6.1-1 + # grealpath ../Cellar/qt5/5.6.1-1:: /usr/local/Cellar/qt5/5.6.1-1 + brew install coreutils + + REV_NAME_ALT=$REV_NAME/ + # grealpath is located in coreutils, there is no "realpath" for OS X :( + QT_BREWS_PATH=$(grealpath "$(brew --prefix qt5)") + BREW_PATH=$(brew --prefix) + QT_VERSION_NUM=5 + + $BREW_PATH/opt/qt5/bin/macdeployqt "${REV_NAME_ALT}citra-qt.app" \ + -executable="${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt" + + # These are the files that macdeployqt packed into Contents/Frameworks/ - we don't want those, so we replace them. + declare -a macos_libs=("QtCore" "QtWidgets" "QtGui" "QtOpenGL" "QtPrintSupport") + + for macos_lib in "${macos_libs[@]}" + do + SC_FRAMEWORK_PART=$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib + # Replace macdeployqt versions of the Frameworks with our own (from /usr/local/opt/qt5/lib/) + cp "$BREW_PATH/opt/qt5/lib/$SC_FRAMEWORK_PART" "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART" + + # Replace references within the embedded Framework files with "internal" versions. + for macos_lib2 in "${macos_libs[@]}" + do + # Since brew references both the non-symlinked and symlink paths of QT5, it needs to be duplicated. + # /usr/local/Cellar/qt5/5.6.1-1/lib and /usr/local/opt/qt5/lib both resolve to the same files. + # So the two lines below are effectively duplicates when resolved as a path, but as strings, they aren't. + RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2 + install_name_tool -change \ + $QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \ + @executable_path/../Frameworks/$RM_FRAMEWORK_PART \ + "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART" + install_name_tool -change \ + "$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \ + @executable_path/../Frameworks/$RM_FRAMEWORK_PART \ + "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART" + done + done + + # Handles `This application failed to start because it could not find or load the Qt platform plugin "cocoa"` + # Which manifests itself as: + # "Exception Type: EXC_CRASH (SIGABRT) | Exception Codes: 0x0000000000000000, 0x0000000000000000 | Exception Note: EXC_CORPSE_NOTIFY" + # There may be more dylibs needed to be fixed... + declare -a macos_plugins=("Plugins/platforms/libqcocoa.dylib") + + for macos_lib in "${macos_plugins[@]}" + do + install_name_tool -id @executable_path/../$macos_lib "${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib" + for macos_lib2 in "${macos_libs[@]}" + do + RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2 + install_name_tool -change \ + $QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \ + @executable_path/../Frameworks/$RM_FRAMEWORK_PART \ + "${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib" + install_name_tool -change \ + "$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \ + @executable_path/../Frameworks/$RM_FRAMEWORK_PART \ + "${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib" + done + done + + for macos_lib in "${macos_libs[@]}" + do + # Debugging info for Travis-CI + otool -L "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib" + done + + # Make the citra-qt.app application launch a debugging terminal. + # Store away the actual binary + mv ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt-bin + + cat > ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt <<EOL +#!/usr/bin/env bash +cd "\`dirname "\$0"\`" +chmod +x citra-qt-bin +open citra-qt-bin --args "\$@" +EOL + # Content that will serve as the launching script for citra (within the .app folder) + + # Make the launching script executable + chmod +x ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt + fi + # Copy documentation + cp license.txt "$REV_NAME" + cp README.md "$REV_NAME" + ARCHIVE_NAME="${REV_NAME}.tar.xz" tar -cJvf "$ARCHIVE_NAME" "$REV_NAME" lftp -c "open -u citra-builds,$BUILD_PASSWORD sftp://builds.citra-emu.org; set sftp:auto-confirm yes; put -O '$UPLOAD_DIR' '$ARCHIVE_NAME'" diff --git a/.travis.yml b/.travis.yml index 8d86baece..8be395770 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,26 @@ -os: - - linux - - osx - language: cpp +matrix: + include: + - os: linux + sudo: true + dist: trusty + - os: osx + sudo: false + env: global: - secure: "AXHFIafTmbGDsHD3mUVj5a4I397DQjti/WoqAJGUp2PglxTcc04BwxZ9Z+xLuf5N2Hs5r9ojAJLT8OGxJCLBDXzneQTNSqXbFuYSLbqrEAiIRlA9eRIotWCg+wYcO+5e8MKX+cHVKwiIWasUB21AtCdq6msh6Y3pUshZp212VPg=" -sudo: false - addons: apt: sources: - ubuntu-toolchain-r-test packages: - - gcc-5 - - g++-5 + - gcc-6 + - g++-6 + - qt5-default + - libqt5opengl5-dev - xorg-dev - lib32stdc++6 # For CMake - lftp # To upload builds diff --git a/CMakeLists.txt b/CMakeLists.txt index f7b0af115..6ac3df0e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ -# CMake 3.1 required for Qt5 settings to be applied automatically on -# dependent libraries and IMPORTED targets. -cmake_minimum_required(VERSION 3.1) +# CMake 3.2 required for cmake to know the right flags for CXX standard on OSX +cmake_minimum_required(VERSION 3.2) function(download_bundled_external remote_path lib_name prefix_var) set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}") @@ -40,7 +39,6 @@ option(CITRA_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" OFF) option(ENABLE_QT "Enable the Qt frontend" ON) option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF) -option(CITRA_FORCE_QT4 "Use Qt4 even if Qt5 is available." OFF) if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit) message(STATUS "Copying pre-commit hook") @@ -64,14 +62,12 @@ if (NOT DEFINED ARCHITECTURE) endif() message(STATUS "Target architecture: ${ARCHITECTURE}") -if (NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) - if (ARCHITECTURE_x86_64) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.1") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse4.1") - endif() +if (NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") else() # Silence "deprecation" warnings add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS) @@ -185,7 +181,7 @@ ENDIF (APPLE) if (ENABLE_QT) if (CITRA_USE_BUNDLED_QT) if (MSVC14 AND ARCHITECTURE_x86_64) - set(QT_VER qt-5.5-msvc2015_64) + set(QT_VER qt-5.7-msvc2015_64) else() message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.") endif() @@ -201,16 +197,8 @@ if (ENABLE_QT) set(QT_PREFIX_HINT) endif() - if (NOT CITRA_FORCE_QT4) - find_package(Qt5 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) - set(CITRA_QT_LIBS Qt5::Widgets Qt5::OpenGL) - endif() - - if (CITRA_FORCE_QT4 OR NOT Qt5_FOUND) - # Try to fallback to Qt4 - find_package(Qt4 REQUIRED COMPONENTS QtGui QtOpenGL ${QT_PREFIX_HINT}) - set(CITRA_QT_LIBS Qt4::QtGui Qt4::QtOpenGL) - endif() + find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) + set(CITRA_QT_LIBS Qt5::Widgets Qt5::OpenGL) endif() # This function should be passed a list of all files in a target. It will automatically generate diff --git a/README.md b/README.md index a27acbc15..5463763ad 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ For development discussion, please join us @ #citra on freenode. ### Development +Most of the development happens on GitHub. It's also where [our central repository](https://github.com/citra-emu/citra) is hosted. + If you want to contribute please take a look at the [Contributor's Guide](CONTRIBUTING.md), [TODO list](https://docs.google.com/document/d/1SWIop0uBI9IW8VGg97TAtoT_CHNoP42FzYmvG1F4QDA) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information). You should as well contact any of the developers in the forum in order to know about the current state of the emulator. ### Building diff --git a/appveyor.yml b/appveyor.yml index fa4134384..0ffb680ff 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,6 +5,10 @@ environment: BUILD_PASSWORD: secure: EXGNlWKJsCtbeImEJ5EP9qrxZ+EqUFfNy+CP61nDOMA= +cache: + - C:\ProgramData\chocolatey\bin -> appveyor.yml + - C:\ProgramData\chocolatey\lib -> appveyor.yml + os: Visual Studio 2015 platform: @@ -38,22 +42,30 @@ on_success: $GITREV = $(git show -s --format='%h') # Where are these spaces coming from? Regardless, let's remove them $BUILD_NAME = "citra-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ","" + $BUILD_NAME_PDB = "citra-${GITDATE}-${GITREV}-windows-amd64-debugsymbols.7z" -replace " ","" $BUILD_NAME_NOQT = "citra-noqt-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ","" - # Zip up the build folder - 7z a $BUILD_NAME .\build\bin\release\* - # Do a second archive with only the binaries - 7z a $BUILD_NAME_NOQT .\build\bin\release\*.exe - # Download winscp - Invoke-WebRequest "http://iweb.dl.sourceforge.net/project/winscp/WinSCP/5.7.3/winscp573.zip" -OutFile "winscp573.zip" - 7z e -y winscp573.zip + # Remove unnecessary files + rm .\build\bin\release\*tests* - # Upload to server - .\WinSCP.com /command ` + # Put the pdb files in a separate archive and remove them from the main download + 7z a $BUILD_NAME_PDB .\build\bin\release\*.pdb + rm .\build\bin\release\*.pdb + + # Zip up the build folder and documentation + 7z a $BUILD_NAME .\build\bin\release\* .\license.txt .\README.md + # Do a second archive with only the binaries (excludes dlls) and documentation + 7z a $BUILD_NAME_NOQT .\build\bin\release\*.exe .\license.txt .\README.md + + + # Download WinSCP and upload to server + choco install winscp.portable + WinSCP.exe /command ` "option batch abort" ` "option confirm off" ` "open sftp://citra-builds:${env:BUILD_PASSWORD}@builds.citra-emu.org -hostkey=*" ` "put $BUILD_NAME /citra/nightly/windows-amd64/" ` "put $BUILD_NAME_NOQT /citra/nightly/windows-noqt-amd64/" ` + "put $BUILD_NAME_PDB /citra/nightly/windows-amd64-debugsymbols/" ` "exit" } diff --git a/externals/cmake-modules/FindSDL2.cmake b/externals/cmake-modules/FindSDL2.cmake index 9b8daa0d1..22ce752c5 100644 --- a/externals/cmake-modules/FindSDL2.cmake +++ b/externals/cmake-modules/FindSDL2.cmake @@ -3,6 +3,7 @@ # SDL2_LIBRARY, the name of the library to link against # SDL2_FOUND, if false, do not try to link to SDL2 # SDL2_INCLUDE_DIR, where to find SDL.h +# SDL2_DLL_DIR, where to find SDL2.dll if it exists # # This module responds to the the flag: # SDL2_BUILDING_LIBRARY @@ -149,6 +150,14 @@ FIND_LIBRARY(SDL2_LIBRARY_TEMP ) IF(SDL2_LIBRARY_TEMP) + if(MSVC) + get_filename_component(SDL2_DLL_DIR_TEMP ${SDL2_LIBRARY_TEMP} DIRECTORY) + if(EXISTS ${SDL2_DLL_DIR_TEMP}/SDL2.dll) + set(SDL2_DLL_DIR ${SDL2_DLL_DIR_TEMP}) + unset(SDL2_DLL_DIR_TEMP) + endif() + endif() + FIND_PATH(SDL2_INCLUDE_DIR SDL.h HINTS $ENV{SDL2DIR} diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index d42249ebd..8e19ec0c4 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -71,6 +71,10 @@ void SelectSink(std::string sink_id) { DSP::HLE::SetSink(iter->factory()); } +void EnableStretching(bool enable) { + DSP::HLE::EnableStretching(enable); +} + void Shutdown() { CoreTiming::UnscheduleEvent(tick_event, 0); DSP::HLE::Shutdown(); diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index f618361f3..7e678aba5 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h @@ -23,6 +23,9 @@ void AddAddressSpace(Kernel::VMManager& vm_manager); /// Select the sink to use based on sink id. void SelectSink(std::string sink_id); +/// Enable/Disable stretching. +void EnableStretching(bool enable); + /// Shutdown Audio Core void Shutdown(); diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp index 0640e1eff..0cddeb82a 100644 --- a/src/audio_core/hle/dsp.cpp +++ b/src/audio_core/hle/dsp.cpp @@ -85,12 +85,45 @@ static StereoFrame16 GenerateCurrentFrame() { // Audio output +static bool perform_time_stretching = true; static std::unique_ptr<AudioCore::Sink> sink; static AudioCore::TimeStretcher time_stretcher; +static void FlushResidualStretcherAudio() { + time_stretcher.Flush(); + while (true) { + std::vector<s16> residual_audio = time_stretcher.Process(sink->SamplesInQueue()); + if (residual_audio.empty()) + break; + sink->EnqueueSamples(residual_audio.data(), residual_audio.size() / 2); + } +} + static void OutputCurrentFrame(const StereoFrame16& frame) { - time_stretcher.AddSamples(&frame[0][0], frame.size()); - sink->EnqueueSamples(time_stretcher.Process(sink->SamplesInQueue())); + if (perform_time_stretching) { + time_stretcher.AddSamples(&frame[0][0], frame.size()); + std::vector<s16> stretched_samples = time_stretcher.Process(sink->SamplesInQueue()); + sink->EnqueueSamples(stretched_samples.data(), stretched_samples.size() / 2); + } else { + constexpr size_t maximum_sample_latency = 1024; // about 32 miliseconds + if (sink->SamplesInQueue() > maximum_sample_latency) { + // This can occur if we're running too fast and samples are starting to back up. + // Just drop the samples. + return; + } + + sink->EnqueueSamples(&frame[0][0], frame.size()); + } +} + +void EnableStretching(bool enable) { + if (perform_time_stretching == enable) + return; + + if (!enable) { + FlushResidualStretcherAudio(); + } + perform_time_stretching = enable; } // Public Interface @@ -111,12 +144,8 @@ void Init() { } void Shutdown() { - time_stretcher.Flush(); - while (true) { - std::vector<s16> residual_audio = time_stretcher.Process(sink->SamplesInQueue()); - if (residual_audio.empty()) - break; - sink->EnqueueSamples(residual_audio); + if (perform_time_stretching) { + FlushResidualStretcherAudio(); } } diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h index 9275cd7de..565f20b6f 100644 --- a/src/audio_core/hle/dsp.h +++ b/src/audio_core/hle/dsp.h @@ -544,5 +544,13 @@ bool Tick(); */ void SetSink(std::unique_ptr<AudioCore::Sink> sink); +/** + * Enables/Disables audio-stretching. + * Audio stretching is an enhancement that stretches audio to match emulation + * speed to prevent stuttering at the cost of some audio latency. + * @param enable true to enable, false to disable. + */ +void EnableStretching(bool enable); + } // namespace HLE } // namespace DSP diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h index faf0ee4e1..9931c4778 100644 --- a/src/audio_core/null_sink.h +++ b/src/audio_core/null_sink.h @@ -19,7 +19,7 @@ public: return native_sample_rate; } - void EnqueueSamples(const std::vector<s16>&) override {} + void EnqueueSamples(const s16*, size_t) override {} size_t SamplesInQueue() const override { return 0; diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp index dc75c04ee..311dd5b59 100644 --- a/src/audio_core/sdl2_sink.cpp +++ b/src/audio_core/sdl2_sink.cpp @@ -71,14 +71,12 @@ unsigned int SDL2Sink::GetNativeSampleRate() const { return impl->sample_rate; } -void SDL2Sink::EnqueueSamples(const std::vector<s16>& samples) { +void SDL2Sink::EnqueueSamples(const s16* samples, size_t sample_count) { if (impl->audio_device_id <= 0) return; - ASSERT_MSG(samples.size() % 2 == 0, "Samples must be in interleaved stereo PCM16 format (size must be a multiple of two)"); - SDL_LockAudioDevice(impl->audio_device_id); - impl->queue.emplace_back(samples); + impl->queue.emplace_back(samples, samples + sample_count * 2); SDL_UnlockAudioDevice(impl->audio_device_id); } diff --git a/src/audio_core/sdl2_sink.h b/src/audio_core/sdl2_sink.h index 0f296b673..b13827214 100644 --- a/src/audio_core/sdl2_sink.h +++ b/src/audio_core/sdl2_sink.h @@ -18,7 +18,7 @@ public: unsigned int GetNativeSampleRate() const override; - void EnqueueSamples(const std::vector<s16>& samples) override; + void EnqueueSamples(const s16* samples, size_t sample_count) override; size_t SamplesInQueue() const override; diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h index 1c881c3d2..a06fc3dcc 100644 --- a/src/audio_core/sink.h +++ b/src/audio_core/sink.h @@ -23,9 +23,10 @@ public: /** * Feed stereo samples to sink. - * @param samples Samples in interleaved stereo PCM16 format. Size of vector must be multiple of two. + * @param samples Samples in interleaved stereo PCM16 format. + * @param sample_count Number of samples. */ - virtual void EnqueueSamples(const std::vector<s16>& samples) = 0; + virtual void EnqueueSamples(const s16* samples, size_t sample_count) = 0; /// Samples enqueued that have not been played yet. virtual std::size_t SamplesInQueue() const = 0; diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index e01216734..128b9a16d 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -17,11 +17,16 @@ #include <getopt.h> #endif +#ifdef _WIN32 +#include <Windows.h> +#endif + #include "common/logging/log.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/scm_rev.h" #include "common/scope_exit.h" +#include "common/string_util.h" #include "core/settings.h" #include "core/system.h" @@ -55,6 +60,15 @@ int main(int argc, char **argv) { bool use_gdbstub = Settings::values.use_gdbstub; u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); char *endarg; +#ifdef _WIN32 + int argc_w; + auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); + + if (argv_w == nullptr) { + LOG_CRITICAL(Frontend, "Failed to get command line arguments"); + return -1; + } +#endif std::string boot_filename; static struct option long_options[] = { @@ -86,11 +100,19 @@ int main(int argc, char **argv) { return 0; } } else { +#ifdef _WIN32 + boot_filename = Common::UTF16ToUTF8(argv_w[optind]); +#else boot_filename = argv[optind]; +#endif optind++; } } +#ifdef _WIN32 + LocalFree(argv_w); +#endif + Log::Filter log_filter(Log::Level::Debug); Log::SetFilter(&log_filter); diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 22cb51ea8..110b883fb 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -68,9 +68,10 @@ void Config::ReadValues() { Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0); // Renderer - Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", false); + Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); + Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false); Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); @@ -78,6 +79,7 @@ void Config::ReadValues() { // Audio Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); + Settings::values.enable_audio_stretching = sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); // Data Storage Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 4e63f3206..2031620a5 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -44,17 +44,21 @@ frame_skip = [Renderer] # Whether to use software or hardware rendering. -# 0 (default): Software, 1: Hardware +# 0: Software, 1 (default): Hardware use_hw_renderer = # Whether to use the Just-In-Time (JIT) compiler for shader emulation -# 0 : Interpreter (slow), 1 (default): JIT (fast) +# 0: Interpreter (slow), 1 (default): JIT (fast) use_shader_jit = # Whether to use native 3DS screen resolution or to scale rendering resolution to the displayed screen size. # 0 (default): Native, 1: Scaled use_scaled_resolution = +# Whether to enable V-Sync (caps the framerate at 60FPS) or not. +# 0 (default): Off, 1: On +use_vsync = + # The clear color for the renderer. What shows up on the sides of the bottom screen. # Must be in range of 0.0-1.0. Defaults to 1.0 for all. bg_red = @@ -66,6 +70,12 @@ bg_green = # auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available) output_engine = +# Whether or not to enable the audio-stretching post-processing effect. +# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter, +# at the cost of increasing audio latency. +# 0: No, 1 (default): Yes +enable_audio_stretching = + [Data Storage] # Whether to create a virtual SD card. # 1 (default): Yes, 0: No diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index 591f68aa4..da12307b7 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -108,6 +108,7 @@ EmuWindow_SDL2::EmuWindow_SDL2() { OnResize(); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); SDL_PumpEvents(); + SDL_GL_SetSwapInterval(Settings::values.use_vsync); DoneCurrent(); } diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 43a766053..e97d33da4 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -22,6 +22,9 @@ set(SRCS configure_debug.cpp configure_dialog.cpp configure_general.cpp + configure_graphics.cpp + configure_system.cpp + configure_input.cpp game_list.cpp hotkeys.cpp main.cpp @@ -52,6 +55,9 @@ set(HEADERS configure_debug.h configure_dialog.h configure_general.h + configure_graphics.h + configure_system.h + configure_input.h game_list.h game_list_p.h hotkeys.h @@ -69,6 +75,9 @@ set(UIS configure_audio.ui configure_debug.ui configure_general.ui + configure_graphics.ui + configure_system.ui + configure_input.ui hotkeys.ui main.ui ) diff --git a/src/citra_qt/Info.plist b/src/citra_qt/Info.plist index 4c89e128b..7d46b39d1 100644 --- a/src/citra_qt/Info.plist +++ b/src/citra_qt/Info.plist @@ -5,7 +5,7 @@ <key>CFBundleDevelopmentRegion</key> <string>English</string> <key>CFBundleExecutable</key> - <string>$(EXECUTABLE_NAME)</string> + <string>${EXECUTABLE_NAME}</string> <key>CFBundleGetInfoString</key> <string></string> <key>CFBundleIconFile</key> diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 414b2f8af..6dddde9ba 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -107,36 +107,13 @@ private: }; GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : - QWidget(parent), keyboard_id(0), emu_thread(emu_thread) { + QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) { std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); setWindowTitle(QString::fromStdString(window_title)); keyboard_id = KeyMap::NewDeviceId(); ReloadSetKeymaps(); - - // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, WA_DontShowOnScreen, WA_DeleteOnClose - QGLFormat fmt; - fmt.setVersion(3,3); - fmt.setProfile(QGLFormat::CoreProfile); - // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X - fmt.setOption(QGL::NoDeprecatedFunctions); - - child = new GGLWidgetInternal(fmt, this); - QBoxLayout* layout = new QHBoxLayout(this); - - resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); - layout->addWidget(child); - layout->setMargin(0); - setLayout(layout); - - OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); - - OnFramebufferSizeChanged(); - NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(child->width(), child->height())); - - BackupGeometry(); - } void GRenderWindow::moveContext() @@ -281,6 +258,40 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) NotifyClientAreaSizeChanged(std::make_pair(width, height)); } +void GRenderWindow::InitRenderTarget() { + if (child) { + delete child; + } + + if (layout()) { + delete layout(); + } + + // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, WA_DontShowOnScreen, WA_DeleteOnClose + QGLFormat fmt; + fmt.setVersion(3, 3); + fmt.setProfile(QGLFormat::CoreProfile); + fmt.setSwapInterval(Settings::values.use_vsync); + + // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X + fmt.setOption(QGL::NoDeprecatedFunctions); + + child = new GGLWidgetInternal(fmt, this); + QBoxLayout* layout = new QHBoxLayout(this); + + resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); + layout->addWidget(child); + layout->setMargin(0); + setLayout(layout); + + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); + + OnFramebufferSizeChanged(); + NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height())); + + BackupGeometry(); +} + void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { setMinimumSize(minimal_size.first, minimal_size.second); } diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 0dcf3e5eb..c1da2bc5f 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -126,6 +126,8 @@ public: void OnClientAreaResized(unsigned width, unsigned height); + void InitRenderTarget(); + public slots: void moveContext(); // overridden diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index ba7edaff9..fa3fa210c 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -3,14 +3,11 @@ // Refer to the license.txt file included. #include <QSettings> -#include <QString> -#include <QStringList> #include "citra_qt/config.h" #include "citra_qt/ui_settings.h" #include "common/file_util.h" -#include "core/settings.h" Config::Config() { // TODO: Don't hardcode the path; let the frontend decide where to put the config files. @@ -21,7 +18,7 @@ Config::Config() { Reload(); } -static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults = { +const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = { // directly mapped keys Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, @@ -48,9 +45,10 @@ void Config::ReadValues() { qt_config->endGroup(); qt_config->beginGroup("Renderer"); - Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", false).toBool(); + Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool(); Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool(); Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).toBool(); + Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); @@ -59,6 +57,7 @@ void Config::ReadValues() { qt_config->beginGroup("Audio"); Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); + Settings::values.enable_audio_stretching = qt_config->value("enable_audio_stretching", true).toBool(); qt_config->endGroup(); qt_config->beginGroup("Data Storage"); @@ -109,7 +108,7 @@ void Config::ReadValues() { UISettings::values.shortcuts.emplace_back( UISettings::Shortcut(group + "/" + hotkey, UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), - qt_config->value("Context").toInt()))); + qt_config->value("Context").toInt()))); qt_config->endGroup(); } @@ -142,6 +141,7 @@ void Config::SaveValues() { qt_config->setValue("use_hw_renderer", Settings::values.use_hw_renderer); qt_config->setValue("use_shader_jit", Settings::values.use_shader_jit); qt_config->setValue("use_scaled_resolution", Settings::values.use_scaled_resolution); + qt_config->setValue("use_vsync", Settings::values.use_vsync); // Cast to double because Qt's written float values are not human-readable qt_config->setValue("bg_red", (double)Settings::values.bg_red); @@ -151,6 +151,7 @@ void Config::SaveValues() { qt_config->beginGroup("Audio"); qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); + qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching); qt_config->endGroup(); qt_config->beginGroup("Data Storage"); @@ -191,7 +192,7 @@ void Config::SaveValues() { qt_config->endGroup(); qt_config->beginGroup("Shortcuts"); - for (auto shortcut : UISettings::values.shortcuts ) { + for (auto shortcut : UISettings::values.shortcuts) { qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first); qt_config->setValue(shortcut.first + "/Context", shortcut.second.second); } diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h index dd0b2ef0b..0cbdb707f 100644 --- a/src/citra_qt/config.h +++ b/src/citra_qt/config.h @@ -1,10 +1,13 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include <string> +#include <QVariant> + +#include "core/settings.h" class QSettings; @@ -20,4 +23,5 @@ public: void Reload(); void Save(); + static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults; }; diff --git a/src/citra_qt/configure.ui b/src/citra_qt/configure.ui index e1624bbef..28b4a3b90 100644 --- a/src/citra_qt/configure.ui +++ b/src/citra_qt/configure.ui @@ -24,16 +24,26 @@ <string>General</string> </attribute> </widget> - <widget class="QWidget" name="inputTab"> + <widget class="ConfigureSystem" name="systemTab"> + <attribute name="title"> + <string>System</string> + </attribute> + </widget> + <widget class="ConfigureInput" name="inputTab"> <attribute name="title"> <string>Input</string> </attribute> </widget> - <widget class="ConfigureAudio" name="audioTab"> + <widget class="ConfigureGraphics" name="graphicsTab"> <attribute name="title"> - <string>Audio</string> + <string>Graphics</string> </attribute> </widget> + <widget class="ConfigureAudio" name="audioTab"> + <attribute name="title"> + <string>Audio</string> + </attribute> + </widget> <widget class="ConfigureDebug" name="debugTab"> <attribute name="title"> <string>Debug</string> @@ -57,6 +67,12 @@ <header>configure_general.h</header> <container>1</container> </customwidget> + <customwidget> + <class>ConfigureSystem</class> + <extends>QWidget</extends> + <header>configure_system.h</header> + <container>1</container> + </customwidget> <customwidget> <class>ConfigureAudio</class> <extends>QWidget</extends> @@ -69,6 +85,18 @@ <header>configure_debug.h</header> <container>1</container> </customwidget> + <customwidget> + <class>ConfigureInput</class> + <extends>QWidget</extends> + <header>configure_input.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>ConfigureGraphics</class> + <extends>QWidget</extends> + <header>configure_graphics.h</header> + <container>1</container> + </customwidget> </customwidgets> <resources/> <connections> diff --git a/src/citra_qt/configure_audio.cpp b/src/citra_qt/configure_audio.cpp index cedfa2f2a..7100be158 100644 --- a/src/citra_qt/configure_audio.cpp +++ b/src/citra_qt/configure_audio.cpp @@ -36,9 +36,12 @@ void ConfigureAudio::setConfiguration() { } } ui->output_sink_combo_box->setCurrentIndex(new_sink_index); + + ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); } void ConfigureAudio::applyConfiguration() { Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()).toStdString(); + Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); Settings::Apply(); } diff --git a/src/citra_qt/configure_audio.ui b/src/citra_qt/configure_audio.ui index d7f6946ca..3e2b4635f 100644 --- a/src/citra_qt/configure_audio.ui +++ b/src/citra_qt/configure_audio.ui @@ -25,6 +25,16 @@ </item> </layout> </item> + <item> + <widget class="QCheckBox" name="toggle_audio_stretching"> + <property name="text"> + <string>Enable audio stretching</string> + </property> + <property name="toolTip"> + <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/src/citra_qt/configure_debug.cpp b/src/citra_qt/configure_debug.cpp index dc3d7b906..fa57a7f72 100644 --- a/src/citra_qt/configure_debug.cpp +++ b/src/citra_qt/configure_debug.cpp @@ -19,13 +19,13 @@ ConfigureDebug::~ConfigureDebug() { } void ConfigureDebug::setConfiguration() { - ui->toogle_gdbstub->setChecked(Settings::values.use_gdbstub); + ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub); ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port); } void ConfigureDebug::applyConfiguration() { - Settings::values.use_gdbstub = ui->toogle_gdbstub->isChecked(); + Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked(); Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); Settings::Apply(); } diff --git a/src/citra_qt/configure_debug.ui b/src/citra_qt/configure_debug.ui index 3ba7f44da..bbbb0e3f4 100644 --- a/src/citra_qt/configure_debug.ui +++ b/src/citra_qt/configure_debug.ui @@ -25,7 +25,7 @@ <item> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> - <widget class="QCheckBox" name="toogle_gdbstub"> + <widget class="QCheckBox" name="toggle_gdbstub"> <property name="text"> <string>Enable GDB Stub</string> </property> @@ -83,7 +83,7 @@ <resources/> <connections> <connection> - <sender>toogle_gdbstub</sender> + <sender>toggle_gdbstub</sender> <signal>toggled(bool)</signal> <receiver>gdbport_spinbox</receiver> <slot>setEnabled(bool)</slot> diff --git a/src/citra_qt/configure_dialog.cpp b/src/citra_qt/configure_dialog.cpp index 2f0317fe0..7da8ad067 100644 --- a/src/citra_qt/configure_dialog.cpp +++ b/src/citra_qt/configure_dialog.cpp @@ -9,9 +9,10 @@ #include "core/settings.h" -ConfigureDialog::ConfigureDialog(QWidget *parent) : +ConfigureDialog::ConfigureDialog(QWidget *parent, bool running) : QDialog(parent), - ui(new Ui::ConfigureDialog) + ui(new Ui::ConfigureDialog), + emulation_running(running) { ui->setupUi(this); this->setConfiguration(); @@ -21,10 +22,16 @@ ConfigureDialog::~ConfigureDialog() { } void ConfigureDialog::setConfiguration() { + // System tab needs set manually + // depending on whether emulation is running + ui->systemTab->setConfiguration(emulation_running); } void ConfigureDialog::applyConfiguration() { ui->generalTab->applyConfiguration(); + ui->systemTab->applyConfiguration(); + ui->inputTab->applyConfiguration(); + ui->graphicsTab->applyConfiguration(); ui->audioTab->applyConfiguration(); ui->debugTab->applyConfiguration(); } diff --git a/src/citra_qt/configure_dialog.h b/src/citra_qt/configure_dialog.h index 89020eeb4..305b33bdf 100644 --- a/src/citra_qt/configure_dialog.h +++ b/src/citra_qt/configure_dialog.h @@ -16,7 +16,7 @@ class ConfigureDialog : public QDialog Q_OBJECT public: - explicit ConfigureDialog(QWidget *parent = nullptr); + explicit ConfigureDialog(QWidget *parent, bool emulation_running); ~ConfigureDialog(); void applyConfiguration(); @@ -26,4 +26,5 @@ private: private: std::unique_ptr<Ui::ConfigureDialog> ui; + bool emulation_running; }; diff --git a/src/citra_qt/configure_general.cpp b/src/citra_qt/configure_general.cpp index 62648e665..95aab9f2e 100644 --- a/src/citra_qt/configure_general.cpp +++ b/src/citra_qt/configure_general.cpp @@ -20,20 +20,14 @@ ConfigureGeneral::~ConfigureGeneral() { } void ConfigureGeneral::setConfiguration() { - ui->toogle_deepscan->setChecked(UISettings::values.gamedir_deepscan); - ui->toogle_check_exit->setChecked(UISettings::values.confirm_before_closing); + ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); + ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->region_combobox->setCurrentIndex(Settings::values.region_value); - ui->toogle_hw_renderer->setChecked(Settings::values.use_hw_renderer); - ui->toogle_shader_jit->setChecked(Settings::values.use_shader_jit); - ui->toogle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution); } void ConfigureGeneral::applyConfiguration() { - UISettings::values.gamedir_deepscan = ui->toogle_deepscan->isChecked(); - UISettings::values.confirm_before_closing = ui->toogle_check_exit->isChecked(); + UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); + UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); Settings::values.region_value = ui->region_combobox->currentIndex(); - Settings::values.use_hw_renderer = ui->toogle_hw_renderer->isChecked(); - Settings::values.use_shader_jit = ui->toogle_shader_jit->isChecked(); - Settings::values.use_scaled_resolution = ui->toogle_scaled_resolution->isChecked(); Settings::Apply(); } diff --git a/src/citra_qt/configure_general.ui b/src/citra_qt/configure_general.ui index 5eb309793..343f804c0 100644 --- a/src/citra_qt/configure_general.ui +++ b/src/citra_qt/configure_general.ui @@ -25,14 +25,14 @@ <item> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="QCheckBox" name="toogle_deepscan"> + <widget class="QCheckBox" name="toggle_deepscan"> <property name="text"> <string>Recursive scan for game folder</string> </property> </widget> </item> <item> - <widget class="QCheckBox" name="toogle_check_exit"> + <widget class="QCheckBox" name="toggle_check_exit"> <property name="text"> <string>Confirm exit while emulation is running</string> </property> @@ -106,40 +106,6 @@ </layout> </widget> </item> - <item> - <widget class="QGroupBox" name="groupBox_2"> - <property name="title"> - <string>Performance</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <widget class="QCheckBox" name="toogle_hw_renderer"> - <property name="text"> - <string>Enable hardware renderer</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="toogle_shader_jit"> - <property name="text"> - <string>Enable shader JIT</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="toogle_scaled_resolution"> - <property name="text"> - <string>Enable scaled resolution</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> <item> <widget class="QGroupBox" name="groupBox_3"> <property name="title"> diff --git a/src/citra_qt/configure_graphics.cpp b/src/citra_qt/configure_graphics.cpp new file mode 100644 index 000000000..5a8101795 --- /dev/null +++ b/src/citra_qt/configure_graphics.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 "citra_qt/configure_graphics.h" +#include "ui_configure_graphics.h" + +#include "core/settings.h" +#include "core/system.h" + +ConfigureGraphics::ConfigureGraphics(QWidget *parent) : + QWidget(parent), + ui(new Ui::ConfigureGraphics) +{ + ui->setupUi(this); + this->setConfiguration(); + + ui->toggle_vsync->setEnabled(!System::IsPoweredOn()); +} + +ConfigureGraphics::~ConfigureGraphics() { +} + +void ConfigureGraphics::setConfiguration() { + ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer); + ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); + ui->toggle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution); + ui->toggle_vsync->setChecked(Settings::values.use_vsync); +} + +void ConfigureGraphics::applyConfiguration() { + Settings::values.use_hw_renderer = ui->toggle_hw_renderer->isChecked(); + Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); + Settings::values.use_scaled_resolution = ui->toggle_scaled_resolution->isChecked(); + Settings::values.use_vsync = ui->toggle_vsync->isChecked(); + Settings::Apply(); +} diff --git a/src/citra_qt/configure_graphics.h b/src/citra_qt/configure_graphics.h new file mode 100644 index 000000000..dfb0c0461 --- /dev/null +++ b/src/citra_qt/configure_graphics.h @@ -0,0 +1,29 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QWidget> + +namespace Ui { +class ConfigureGraphics; +} + +class ConfigureGraphics : public QWidget +{ + Q_OBJECT + +public: + explicit ConfigureGraphics(QWidget *parent = nullptr); + ~ConfigureGraphics(); + + void applyConfiguration(); + +private: + void setConfiguration(); + +private: + std::unique_ptr<Ui::ConfigureGraphics> ui; +}; diff --git a/src/citra_qt/configure_graphics.ui b/src/citra_qt/configure_graphics.ui new file mode 100644 index 000000000..da6e19ce1 --- /dev/null +++ b/src/citra_qt/configure_graphics.ui @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureGraphics</class> + <widget class="QWidget" name="ConfigureGraphics"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Graphics</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="toggle_hw_renderer"> + <property name="text"> + <string>Enable hardware renderer</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="toggle_shader_jit"> + <property name="text"> + <string>Enable shader JIT</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="toggle_scaled_resolution"> + <property name="text"> + <string>Enable scaled resolution</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="toggle_vsync"> + <property name="text"> + <string>Enable V-Sync</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>toggle_gdbstub</sender> + <signal>toggled(bool)</signal> + <receiver>gdbport_spinbox</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>84</x> + <y>157</y> + </hint> + <hint type="destinationlabel"> + <x>342</x> + <y>158</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/citra_qt/configure_input.cpp b/src/citra_qt/configure_input.cpp new file mode 100644 index 000000000..9c7a67174 --- /dev/null +++ b/src/citra_qt/configure_input.cpp @@ -0,0 +1,149 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> +#include <utility> +#include <QTimer> + +#include "citra_qt/configure_input.h" + +ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { + ui->setupUi(this); + + // Initialize mapping of input enum to UI button. + input_mapping = { + { std::make_pair(Settings::NativeInput::Values::A, ui->buttonA) }, + { std::make_pair(Settings::NativeInput::Values::B, ui->buttonB) }, + { std::make_pair(Settings::NativeInput::Values::X, ui->buttonX) }, + { std::make_pair(Settings::NativeInput::Values::Y, ui->buttonY) }, + { std::make_pair(Settings::NativeInput::Values::L, ui->buttonL) }, + { std::make_pair(Settings::NativeInput::Values::R, ui->buttonR) }, + { std::make_pair(Settings::NativeInput::Values::ZL, ui->buttonZL) }, + { std::make_pair(Settings::NativeInput::Values::ZR, ui->buttonZR) }, + { std::make_pair(Settings::NativeInput::Values::START, ui->buttonStart) }, + { std::make_pair(Settings::NativeInput::Values::SELECT, ui->buttonSelect) }, + { std::make_pair(Settings::NativeInput::Values::HOME, ui->buttonHome) }, + { std::make_pair(Settings::NativeInput::Values::DUP, ui->buttonDpadUp) }, + { std::make_pair(Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown) }, + { std::make_pair(Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft) }, + { std::make_pair(Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight) }, + { std::make_pair(Settings::NativeInput::Values::CUP, ui->buttonCStickUp) }, + { std::make_pair(Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown) }, + { std::make_pair(Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft) }, + { std::make_pair(Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight) }, + { std::make_pair(Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp) }, + { std::make_pair(Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown) }, + { std::make_pair(Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft) }, + { std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight) }, + { std::make_pair(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())); + } + connect(ui->buttonRestoreDefaults, SIGNAL(released()), this, SLOT(restoreDefaults())); + setFocusPolicy(Qt::ClickFocus); + timer = new QTimer(this); + timer->setSingleShot(true); + connect(timer, &QTimer::timeout, this, [&]() { key_pressed = Qt::Key_Escape; setKey(); }); + 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 +} + +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; + } + 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::keyPressEvent(QKeyEvent* event) { + if (!changing_button) + return; + if (!event || event->key() == Qt::Key_unknown) + return; + key_pressed = event->key(); + timer->stop(); + setKey(); +} + +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; +} + +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 ""; + + return QKeySequence(key_code).toString(); +} + +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]); +} + +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::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); + } +} diff --git a/src/citra_qt/configure_input.h b/src/citra_qt/configure_input.h new file mode 100644 index 000000000..fe8ea5580 --- /dev/null +++ b/src/citra_qt/configure_input.h @@ -0,0 +1,63 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <QWidget> +#include <QKeyEvent> + +#include "citra_qt/config.h" +#include "core/settings.h" +#include "ui_configure_input.h" + +class QPushButton; +class QString; +class QTimer; + +namespace Ui { + class ConfigureInput; +} + +class ConfigureInput : public QWidget { + Q_OBJECT + +public: + explicit ConfigureInput(QWidget* parent = nullptr); + + /// Save all button configurations to settings file + void applyConfiguration(); + +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(); + + /// 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(); + + /// Restore all buttons to their default values. + void restoreDefaults(); +}; diff --git a/src/citra_qt/configure_input.ui b/src/citra_qt/configure_input.ui new file mode 100644 index 000000000..a040d4df4 --- /dev/null +++ b/src/citra_qt/configure_input.ui @@ -0,0 +1,593 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureInput</class> + <widget class="QWidget" name="ConfigureInput"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>370</width> + <height>534</height> + </rect> + </property> + <property name="windowTitle"> + <string>ConfigureInput</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QGridLayout" name="gridLayout_7"> + <item row="0" column="0"> + <widget class="QGroupBox" name="faceButtons"> + <property name="title"> + <string>Face Buttons</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>A:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonA"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>B:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonB"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>X:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonX"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Y:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonY"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="0" column="1"> + <widget class="QGroupBox" name="faceButtons_2"> + <property name="title"> + <string>Directional Pad</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_12"> + <item> + <widget class="QLabel" name="label_34"> + <property name="text"> + <string>Up:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonDpadUp"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_9"> + <item> + <widget class="QLabel" name="label_35"> + <property name="text"> + <string>Down:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonDpadDown"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_10"> + <item> + <widget class="QLabel" name="label_32"> + <property name="text"> + <string>Left:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonDpadLeft"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_11"> + <item> + <widget class="QLabel" name="label_33"> + <property name="text"> + <string>Right:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonDpadRight"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="1" column="0"> + <widget class="QGroupBox" name="faceButtons_3"> + <property name="title"> + <string>Shoulder Buttons</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_13"> + <item> + <widget class="QLabel" name="label_17"> + <property name="text"> + <string>L:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonL"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_14"> + <item> + <widget class="QLabel" name="label_19"> + <property name="text"> + <string>R:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonR"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_15"> + <item> + <widget class="QLabel" name="label_20"> + <property name="text"> + <string>ZL:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonZL"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_16"> + <item> + <widget class="QLabel" name="label_18"> + <property name="text"> + <string>ZR:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonZR"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + <zorder></zorder> + </widget> + </item> + <item row="1" column="1"> + <widget class="QGroupBox" name="faceButtons_4"> + <property name="title"> + <string>Circle Pad</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_17"> + <item> + <widget class="QLabel" name="label_21"> + <property name="text"> + <string>Left:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCircleLeft"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_18"> + <item> + <widget class="QLabel" name="label_23"> + <property name="text"> + <string>Right:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCircleRight"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_19"> + <item> + <widget class="QLabel" name="label_24"> + <property name="text"> + <string>Up:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCircleUp"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_20"> + <item> + <widget class="QLabel" name="label_22"> + <property name="text"> + <string>Down:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCircleDown"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="2" column="0"> + <widget class="QGroupBox" name="faceButtons_5"> + <property name="title"> + <string>C-Stick</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_21"> + <item> + <widget class="QLabel" name="label_25"> + <property name="text"> + <string>Left:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCStickLeft"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_22"> + <item> + <widget class="QLabel" name="label_27"> + <property name="text"> + <string>Right:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCStickRight"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_23"> + <item> + <widget class="QLabel" name="label_28"> + <property name="text"> + <string>Up:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCStickUp"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_24"> + <item> + <widget class="QLabel" name="label_26"> + <property name="text"> + <string>Down:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCStickDown"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="2" column="1"> + <widget class="QGroupBox" name="faceButtons_6"> + <property name="title"> + <string>Misc.</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_6"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_25"> + <item> + <widget class="QLabel" name="label_29"> + <property name="text"> + <string>Start:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonStart"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_26"> + <item> + <widget class="QLabel" name="label_30"> + <property name="text"> + <string>Select:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonSelect"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_27"> + <item> + <widget class="QLabel" name="label_31"> + <property name="text"> + <string>Home:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonHome"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_28"> + <item> + <widget class="QLabel" name="label_34"> + <property name="text"> + <string>Circle Mod:</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCircleMod"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="buttonRestoreDefaults"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="sizeIncrement"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="text"> + <string>Restore Defaults</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/citra_qt/configure_system.cpp b/src/citra_qt/configure_system.cpp new file mode 100644 index 000000000..4f0d4dbfe --- /dev/null +++ b/src/citra_qt/configure_system.cpp @@ -0,0 +1,136 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "citra_qt/configure_system.h" +#include "citra_qt/ui_settings.h" +#include "ui_configure_system.h" + +#include "core/hle/service/fs/archive.h" +#include "core/hle/service/cfg/cfg.h" + +static const std::array<int, 12> days_in_month = {{ + 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}}; + +ConfigureSystem::ConfigureSystem(QWidget *parent) : + QWidget(parent), + ui(new Ui::ConfigureSystem) { + ui->setupUi(this); + + connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)), SLOT(updateBirthdayComboBox(int))); +} + +ConfigureSystem::~ConfigureSystem() { +} + +void ConfigureSystem::setConfiguration(bool emulation_running) { + enabled = !emulation_running; + + if (!enabled) { + ReadSystemSettings(); + ui->group_system_settings->setEnabled(false); + } else { + // This tab is enabled only when game is not running (i.e. all service are not initialized). + // Temporarily register archive types and load the config savegame file to memory. + Service::FS::RegisterArchiveTypes(); + ResultCode result = Service::CFG::LoadConfigNANDSaveFile(); + Service::FS::UnregisterArchiveTypes(); + + if (result.IsError()) { + ui->label_disable_info->setText(tr("Failed to load system settings data.")); + ui->group_system_settings->setEnabled(false); + enabled = false; + return; + } + + ReadSystemSettings(); + ui->label_disable_info->hide(); + } +} + +void ConfigureSystem::ReadSystemSettings() { + // set username + username = Service::CFG::GetUsername(); + // ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this when we move to Qt 5.5 + ui->edit_username->setText(QString::fromUtf16(reinterpret_cast<const ushort*>(username.data()))); + + // set birthday + std::tie(birthmonth, birthday) = Service::CFG::GetBirthday(); + ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); + ui->combo_birthday->setCurrentIndex(birthday - 1); + + // set system language + language_index = Service::CFG::GetSystemLanguage(); + ui->combo_language->setCurrentIndex(language_index); + + // set sound output mode + sound_index = Service::CFG::GetSoundOutputMode(); + ui->combo_sound->setCurrentIndex(sound_index); +} + +void ConfigureSystem::applyConfiguration() { + if (!enabled) + return; + + bool modified = false; + + // apply username + // std::u16string new_username = ui->edit_username->text().toStdU16String(); // TODO(wwylele): Use this when we move to Qt 5.5 + std::u16string new_username(reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16())); + if (new_username != username) { + Service::CFG::SetUsername(new_username); + modified = true; + } + + // apply birthday + int new_birthmonth = ui->combo_birthmonth->currentIndex() + 1; + int new_birthday = ui->combo_birthday->currentIndex() + 1; + if (birthmonth != new_birthmonth || birthday != new_birthday) { + Service::CFG::SetBirthday(new_birthmonth, new_birthday); + modified = true; + } + + // apply language + int new_language = ui->combo_language->currentIndex(); + if (language_index != new_language) { + Service::CFG::SetSystemLanguage(static_cast<Service::CFG::SystemLanguage>(new_language)); + modified = true; + } + + // apply sound + int new_sound = ui->combo_sound->currentIndex(); + if (sound_index != new_sound) { + Service::CFG::SetSoundOutputMode(static_cast<Service::CFG::SoundOutputMode>(new_sound)); + modified = true; + } + + // update the config savegame if any item is modified. + if (modified) + Service::CFG::UpdateConfigNANDSavegame(); +} + +void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { + if (birthmonth_index < 0 || birthmonth_index >= 12) + return; + + // store current day selection + int birthday_index = ui->combo_birthday->currentIndex(); + + // get number of days in the new selected month + int days = days_in_month[birthmonth_index]; + + // if the selected day is out of range, + // reset it to 1st + if (birthday_index < 0 || birthday_index >= days) + birthday_index = 0; + + // update the day combo box + ui->combo_birthday->clear(); + for (int i = 1; i <= days; ++i) { + ui->combo_birthday->addItem(QString::number(i)); + } + + // restore the day selection + ui->combo_birthday->setCurrentIndex(birthday_index); +} diff --git a/src/citra_qt/configure_system.h b/src/citra_qt/configure_system.h new file mode 100644 index 000000000..1f5577070 --- /dev/null +++ b/src/citra_qt/configure_system.h @@ -0,0 +1,38 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QWidget> + +namespace Ui { +class ConfigureSystem; +} + +class ConfigureSystem : public QWidget +{ + Q_OBJECT + +public: + explicit ConfigureSystem(QWidget *parent = nullptr); + ~ConfigureSystem(); + + void applyConfiguration(); + void setConfiguration(bool emulation_running); + +public slots: + void updateBirthdayComboBox(int birthmonth_index); + +private: + void ReadSystemSettings(); + + std::unique_ptr<Ui::ConfigureSystem> ui; + bool enabled; + + std::u16string username; + int birthmonth, birthday; + int language_index; + int sound_index; +}; diff --git a/src/citra_qt/configure_system.ui b/src/citra_qt/configure_system.ui new file mode 100644 index 000000000..6a906b61b --- /dev/null +++ b/src/citra_qt/configure_system.ui @@ -0,0 +1,252 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureSystem</class> + <widget class="QWidget" name="ConfigureSystem"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>360</width> + <height>377</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="group_system_settings"> + <property name="title"> + <string>System Settings</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_username"> + <property name="text"> + <string>Username</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="edit_username"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maxLength"> + <number>10</number> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_birthday"> + <property name="text"> + <string>Birthday</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> + <item> + <widget class="QComboBox" name="combo_birthmonth"> + <item> + <property name="text"> + <string>January</string> + </property> + </item> + <item> + <property name="text"> + <string>February</string> + </property> + </item> + <item> + <property name="text"> + <string>March</string> + </property> + </item> + <item> + <property name="text"> + <string>April</string> + </property> + </item> + <item> + <property name="text"> + <string>May</string> + </property> + </item> + <item> + <property name="text"> + <string>June</string> + </property> + </item> + <item> + <property name="text"> + <string>July</string> + </property> + </item> + <item> + <property name="text"> + <string>August</string> + </property> + </item> + <item> + <property name="text"> + <string>September</string> + </property> + </item> + <item> + <property name="text"> + <string>October</string> + </property> + </item> + <item> + <property name="text"> + <string>November</string> + </property> + </item> + <item> + <property name="text"> + <string>December</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QComboBox" name="combo_birthday"/> + </item> + </layout> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_language"> + <property name="text"> + <string>Language</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="combo_language"> + <item> + <property name="text"> + <string>Japanese (日本語)</string> + </property> + </item> + <item> + <property name="text"> + <string>English</string> + </property> + </item> + <item> + <property name="text"> + <string>French (français)</string> + </property> + </item> + <item> + <property name="text"> + <string>German (Deutsch)</string> + </property> + </item> + <item> + <property name="text"> + <string>Italian (italiano)</string> + </property> + </item> + <item> + <property name="text"> + <string>Spanish (español)</string> + </property> + </item> + <item> + <property name="text"> + <string>Simplified Chinese (简体中文)</string> + </property> + </item> + <item> + <property name="text"> + <string>Korean (한국어)</string> + </property> + </item> + <item> + <property name="text"> + <string>Dutch (Nederlands)</string> + </property> + </item> + <item> + <property name="text"> + <string>Portuguese (português)</string> + </property> + </item> + <item> + <property name="text"> + <string>Russian (Русский)</string> + </property> + </item> + <item> + <property name="text"> + <string>Traditional Chinese (正體中文)</string> + </property> + </item> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_sound"> + <property name="text"> + <string>Sound output mode</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QComboBox" name="combo_sound"> + <item> + <property name="text"> + <string>Mono</string> + </property> + </item> + <item> + <property name="text"> + <string>Stereo</string> + </property> + </item> + <item> + <property name="text"> + <string>Surround</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="label_disable_info"> + <property name="text"> + <string>System settings are available only when game is not running.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp index 585ac049a..17898f54b 100644 --- a/src/citra_qt/debugger/profiler.cpp +++ b/src/citra_qt/debugger/profiler.cpp @@ -151,8 +151,8 @@ private: /// This timer is used to redraw the widget's contents continuously. To save resources, it only /// runs while the widget is visible. QTimer update_timer; - /// Scale the coordinate system appropriately when physical DPI != logical DPI. - qreal x_scale, y_scale; + /// Scale the coordinate system appropriately when dpi != 96. + qreal x_scale = 1.0, y_scale = 1.0; }; #endif @@ -222,15 +222,14 @@ MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) { MicroProfileInitUI(); connect(&update_timer, SIGNAL(timeout()), SLOT(update())); - - QPainter painter(this); - x_scale = qreal(painter.device()->physicalDpiX()) / qreal(painter.device()->logicalDpiX()); - y_scale = qreal(painter.device()->physicalDpiY()) / qreal(painter.device()->logicalDpiY()); } void MicroProfileWidget::paintEvent(QPaintEvent* ev) { QPainter painter(this); + // The units used by Microprofile for drawing are based in pixels on a 96 dpi display. + x_scale = qreal(painter.device()->logicalDpiX()) / 96.0; + y_scale = qreal(painter.device()->logicalDpiY()) / 96.0; painter.scale(x_scale, y_scale); painter.setBackground(Qt::black); @@ -241,7 +240,7 @@ void MicroProfileWidget::paintEvent(QPaintEvent* ev) { painter.setFont(font); mp_painter = &painter; - MicroProfileDraw(rect().width(), rect().height()); + MicroProfileDraw(rect().width() / x_scale, rect().height() / y_scale); mp_painter = nullptr; } diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index 15484fae3..1910da3ac 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -120,11 +120,9 @@ void GameList::LoadInterfaceLayout() void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { - const auto callback = [&](unsigned* num_entries_out, - const std::string& directory, - const std::string& virtual_name, - unsigned int recursion) -> bool { - + const auto callback = [this, recursion](unsigned* num_entries_out, + const std::string& directory, + const std::string& virtual_name) -> bool { std::string physical_name = directory + DIR_SEP + virtual_name; if (stop_processing) diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 0ed1ffa5a..9fd4482f6 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -243,7 +243,9 @@ bool GMainWindow::InitializeSystem() { if (emu_thread != nullptr) ShutdownGame(); + render_window->InitRenderTarget(); render_window->MakeCurrent(); + if (!gladLoadGL()) { QMessageBox::critical(this, tr("Error while starting Citra!"), tr("Failed to initialize the video core!\n\n" @@ -508,11 +510,12 @@ void GMainWindow::ToggleWindowMode() { } void GMainWindow::OnConfigure() { - ConfigureDialog configureDialog(this); + ConfigureDialog configureDialog(this, emulation_running); auto result = configureDialog.exec(); if (result == QDialog::Accepted) { configureDialog.applyConfiguration(); + render_window->ReloadSetKeymaps(); config->Save(); } } diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp index 08270dd88..fd728c109 100644 --- a/src/common/emu_window.cpp +++ b/src/common/emu_window.cpp @@ -51,7 +51,6 @@ static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsi } std::tuple<unsigned,unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) { - new_x = std::max(new_x, framebuffer_layout.bottom_screen.left); new_x = std::min(new_x, framebuffer_layout.bottom_screen.right-1); @@ -92,9 +91,9 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { } EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) { - - ASSERT(width > 0); - ASSERT(height > 0); + // When hiding the widget, the function receives a size of 0 + if (width == 0) width = 1; + if (height == 0) height = 1; EmuWindow::FramebufferLayout res = { width, height, {}, {} }; diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 17af7c385..84fe95c8c 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -434,7 +434,7 @@ bool CreateEmptyFile(const std::string &filename) } -bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback, unsigned int recursion) +bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback) { LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str()); @@ -472,7 +472,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo continue; unsigned ret_entries = 0; - if (!callback(&ret_entries, directory, virtual_name, recursion)) { + if (!callback(&ret_entries, directory, virtual_name)) { callback_error = true; break; } @@ -497,10 +497,9 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, unsigned int recursion) { - const auto callback = [&parent_entry](unsigned* num_entries_out, - const std::string& directory, - const std::string& virtual_name, - unsigned int recursion) -> bool { + const auto callback = [recursion, &parent_entry](unsigned* num_entries_out, + const std::string& directory, + const std::string& virtual_name) -> bool { FSTEntry entry; entry.virtualName = virtual_name; entry.physicalName = directory + DIR_SEP + virtual_name; @@ -526,16 +525,15 @@ unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, }; unsigned num_entries; - return ForeachDirectoryEntry(&num_entries, directory, callback, recursion) ? num_entries : 0; + return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0; } bool DeleteDirRecursively(const std::string &directory, unsigned int recursion) { - const static auto callback = [](unsigned* num_entries_out, - const std::string& directory, - const std::string& virtual_name, - unsigned int recursion) -> bool { + const auto callback = [recursion](unsigned* num_entries_out, + const std::string& directory, + const std::string& virtual_name) -> bool { std::string new_path = directory + DIR_SEP_CHR + virtual_name; if (IsDirectory(new_path)) { @@ -546,7 +544,7 @@ bool DeleteDirRecursively(const std::string &directory, unsigned int recursion) return Delete(new_path); }; - if (!ForeachDirectoryEntry(nullptr, directory, callback, recursion)) + if (!ForeachDirectoryEntry(nullptr, directory, callback)) return false; // Delete the outermost directory diff --git a/src/common/file_util.h b/src/common/file_util.h index 32ae2dc57..7ad7ee829 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -105,13 +105,11 @@ bool CreateEmptyFile(const std::string &filename); * @param num_entries_out to be assigned by the callable with the number of iterated directory entries, never null * @param directory the path to the enclosing directory * @param virtual_name the entry name, without any preceding directory info - * @param recursion Number of children directory to read before giving up * @return whether handling the entry succeeded */ using DirectoryEntryCallable = std::function<bool(unsigned* num_entries_out, const std::string& directory, - const std::string& virtual_name, - unsigned int recursion)>; + const std::string& virtual_name)>; /** * Scans a directory, calling the callback for each file/directory contained within. @@ -119,10 +117,9 @@ using DirectoryEntryCallable = std::function<bool(unsigned* num_entries_out, * @param num_entries_out assigned by the function with the number of iterated directory entries, can be null * @param directory the directory to scan * @param callback The callback which will be called for each entry - * @param recursion Number of children directories to read before giving up * @return whether scanning the directory succeeded */ -bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback, unsigned int recursion = 0); +bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback); /** * Scans the directory tree, storing the results. diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index d7008fc66..0b2fabec9 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -117,7 +117,7 @@ Entry CreateEntry(Class log_class, Level log_level, vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args); entry.message = std::string(formatting_buffer.data()); - return std::move(entry); + return entry; } static Filter* filter = nullptr; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e9b04098b..174e9dc79 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -5,6 +5,7 @@ set(SRCS arm/dyncom/arm_dyncom_dec.cpp arm/dyncom/arm_dyncom_interpreter.cpp arm/dyncom/arm_dyncom_thumb.cpp + arm/dyncom/arm_dyncom_trans.cpp arm/skyeye_common/armstate.cpp arm/skyeye_common/armsupp.cpp arm/skyeye_common/vfp/vfp.cpp @@ -26,9 +27,11 @@ set(SRCS 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/event.cpp hle/kernel/kernel.cpp hle/kernel/memory.cpp @@ -36,6 +39,7 @@ set(SRCS hle/kernel/process.cpp hle/kernel/resource_limit.cpp hle/kernel/semaphore.cpp + hle/kernel/server_port.cpp hle/kernel/session.cpp hle/kernel/shared_memory.cpp hle/kernel/thread.cpp @@ -91,7 +95,9 @@ set(SRCS hle/service/ir/ir_rst.cpp hle/service/ir/ir_u.cpp hle/service/ir/ir_user.cpp - hle/service/ldr_ro.cpp + hle/service/ldr_ro/cro_helper.cpp + hle/service/ldr_ro/ldr_ro.cpp + hle/service/ldr_ro/memory_synchronizer.cpp hle/service/mic_u.cpp hle/service/ndm/ndm.cpp hle/service/ndm/ndm_u.cpp @@ -140,6 +146,7 @@ set(HEADERS arm/dyncom/arm_dyncom_interpreter.h arm/dyncom/arm_dyncom_run.h arm/dyncom/arm_dyncom_thumb.h + arm/dyncom/arm_dyncom_trans.h arm/skyeye_common/arm_regformat.h arm/skyeye_common/armstate.h arm/skyeye_common/armsupp.h @@ -164,9 +171,11 @@ set(HEADERS hle/function_wrappers.h hle/hle.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/event.h hle/kernel/kernel.h hle/kernel/memory.h @@ -174,6 +183,7 @@ set(HEADERS hle/kernel/process.h hle/kernel/resource_limit.h hle/kernel/semaphore.h + hle/kernel/server_port.h hle/kernel/session.h hle/kernel/shared_memory.h hle/kernel/thread.h @@ -230,7 +240,9 @@ set(HEADERS hle/service/ir/ir_rst.h hle/service/ir/ir_u.h hle/service/ir/ir_user.h - hle/service/ldr_ro.h + hle/service/ldr_ro/cro_helper.h + hle/service/ldr_ro/ldr_ro.h + hle/service/ldr_ro/memory_synchronizer.h hle/service/mic_u.h hle/service/ndm/ndm.h hle/service/ndm/ndm_u.h diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index d8abe5aeb..de5e9c8fa 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -32,6 +32,9 @@ public: Run(1); } + /// Clear all instruction cache + virtual void ClearInstructionCache() = 0; + /** * Set the Program Counter to an address * @param addr Address to set PC to diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index 13492a08b..ab77da965 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -12,6 +12,7 @@ #include "core/arm/dyncom/arm_dyncom.h" #include "core/arm/dyncom/arm_dyncom_interpreter.h" #include "core/arm/dyncom/arm_dyncom_run.h" +#include "core/arm/dyncom/arm_dyncom_trans.h" #include "core/core.h" #include "core/core_timing.h" @@ -23,6 +24,11 @@ ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { ARM_DynCom::~ARM_DynCom() { } +void ARM_DynCom::ClearInstructionCache() { + state->instruction_cache.clear(); + trans_cache_buf_top = 0; +} + void ARM_DynCom::SetPC(u32 pc) { state->Reg[15] = pc; } diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 3664fd728..e763abc24 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -21,6 +21,8 @@ public: ARM_DynCom(PrivilegeMode initial_mode); ~ARM_DynCom(); + void ClearInstructionCache() override; + void SetPC(u32 pc) override; u32 GetPC() const override; u32 GetReg(int index) const override; diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index cfc67287f..c8d45c6db 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -17,6 +17,7 @@ #include "core/arm/dyncom/arm_dyncom_dec.h" #include "core/arm/dyncom/arm_dyncom_interpreter.h" #include "core/arm/dyncom/arm_dyncom_thumb.h" +#include "core/arm/dyncom/arm_dyncom_trans.h" #include "core/arm/dyncom/arm_dyncom_run.h" #include "core/arm/skyeye_common/armstate.h" #include "core/arm/skyeye_common/armsupp.h" @@ -24,18 +25,6 @@ #include "core/gdbstub/gdbstub.h" -enum { - COND = (1 << 0), - NON_BRANCH = (1 << 1), - DIRECT_BRANCH = (1 << 2), - INDIRECT_BRANCH = (1 << 3), - CALL = (1 << 4), - RET = (1 << 5), - END_OF_PAGE = (1 << 6), - THUMB = (1 << 7), - SINGLE_STEP = (1 << 8) -}; - #define RM BITS(sht_oper, 0, 3) #define RS BITS(sht_oper, 8, 11) @@ -46,8 +35,6 @@ enum { #define ROTATE_RIGHT_32(n, i) ROTATE_RIGHT(n, i, 32) #define ROTATE_LEFT_32(n, i) ROTATE_LEFT(n, i, 32) -typedef unsigned int (*shtop_fp_t)(ARMul_State* cpu, unsigned int sht_oper); - static bool CondPassed(const ARMul_State* cpu, unsigned int cond) { const bool n_flag = cpu->NFlag != 0; const bool z_flag = cpu->ZFlag != 0; @@ -245,12 +232,6 @@ static unsigned int DPO(RotateRightByRegister)(ARMul_State* cpu, unsigned int sh return shifter_operand; } -typedef void (*get_addr_fp_t)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr); - -struct ldst_inst { - unsigned int inst; - get_addr_fp_t get_addr; -}; #define DEBUG_MSG LOG_DEBUG(Core_ARM11, "inst is %x", inst); CITRA_IGNORE_EXIT(0) #define LnSWoUB(s) glue(LnSWoUB, s) @@ -668,479 +649,7 @@ static void LnSWoUB(ScaledRegisterOffset)(ARMul_State* cpu, unsigned int inst, u virt_addr = addr; } -struct arm_inst { - unsigned int idx; - unsigned int cond; - int br; - char component[0]; -}; - -struct generic_arm_inst { - u32 Ra; - u32 Rm; - u32 Rn; - u32 Rd; - u8 op1; - u8 op2; -}; - -struct adc_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct add_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct orr_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct and_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct eor_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct bbl_inst { - unsigned int L; - int signed_immed_24; - unsigned int next_addr; - unsigned int jmp_addr; -}; - -struct bx_inst { - unsigned int Rm; -}; - -struct blx_inst { - union { - s32 signed_immed_24; - u32 Rm; - } val; - unsigned int inst; -}; - -struct clz_inst { - unsigned int Rm; - unsigned int Rd; -}; - -struct cps_inst { - unsigned int imod0; - unsigned int imod1; - unsigned int mmod; - unsigned int A, I, F; - unsigned int mode; -}; - -struct clrex_inst { -}; - -struct cpy_inst { - unsigned int Rm; - unsigned int Rd; -}; - -struct bic_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct sub_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct tst_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct cmn_inst { - unsigned int I; - unsigned int Rn; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct teq_inst { - unsigned int I; - unsigned int Rn; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct stm_inst { - unsigned int inst; -}; - -struct bkpt_inst { - u32 imm; -}; - -struct stc_inst { -}; - -struct ldc_inst { -}; - -struct swi_inst { - unsigned int num; -}; - -struct cmp_inst { - unsigned int I; - unsigned int Rn; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct mov_inst { - unsigned int I; - unsigned int S; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct mvn_inst { - unsigned int I; - unsigned int S; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct rev_inst { - unsigned int Rd; - unsigned int Rm; - unsigned int op1; - unsigned int op2; -}; - -struct rsb_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct rsc_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct sbc_inst { - unsigned int I; - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int shifter_operand; - shtop_fp_t shtop_func; -}; - -struct mul_inst { - unsigned int S; - unsigned int Rd; - unsigned int Rs; - unsigned int Rm; -}; - -struct smul_inst { - unsigned int Rd; - unsigned int Rs; - unsigned int Rm; - unsigned int x; - unsigned int y; -}; - -struct umull_inst { - unsigned int S; - unsigned int RdHi; - unsigned int RdLo; - unsigned int Rs; - unsigned int Rm; -}; - -struct smlad_inst { - unsigned int m; - unsigned int Rm; - unsigned int Rd; - unsigned int Ra; - unsigned int Rn; - unsigned int op1; - unsigned int op2; -}; - -struct smla_inst { - unsigned int x; - unsigned int y; - unsigned int Rm; - unsigned int Rd; - unsigned int Rs; - unsigned int Rn; -}; - -struct smlalxy_inst { - unsigned int x; - unsigned int y; - unsigned int RdLo; - unsigned int RdHi; - unsigned int Rm; - unsigned int Rn; -}; - -struct ssat_inst { - unsigned int Rn; - unsigned int Rd; - unsigned int imm5; - unsigned int sat_imm; - unsigned int shift_type; -}; - -struct umaal_inst { - unsigned int Rn; - unsigned int Rm; - unsigned int RdHi; - unsigned int RdLo; -}; - -struct umlal_inst { - unsigned int S; - unsigned int Rm; - unsigned int Rs; - unsigned int RdHi; - unsigned int RdLo; -}; - -struct smlal_inst { - unsigned int S; - unsigned int Rm; - unsigned int Rs; - unsigned int RdHi; - unsigned int RdLo; -}; - -struct smlald_inst { - unsigned int RdLo; - unsigned int RdHi; - unsigned int Rm; - unsigned int Rn; - unsigned int swap; - unsigned int op1; - unsigned int op2; -}; - -struct mla_inst { - unsigned int S; - unsigned int Rn; - unsigned int Rd; - unsigned int Rs; - unsigned int Rm; -}; - -struct mrc_inst { - unsigned int opcode_1; - unsigned int opcode_2; - unsigned int cp_num; - unsigned int crn; - unsigned int crm; - unsigned int Rd; - unsigned int inst; -}; - -struct mcr_inst { - unsigned int opcode_1; - unsigned int opcode_2; - unsigned int cp_num; - unsigned int crn; - unsigned int crm; - unsigned int Rd; - unsigned int inst; -}; - -struct mcrr_inst { - unsigned int opcode_1; - unsigned int cp_num; - unsigned int crm; - unsigned int rt; - unsigned int rt2; -}; - -struct mrs_inst { - unsigned int R; - unsigned int Rd; -}; - -struct msr_inst { - unsigned int field_mask; - unsigned int R; - unsigned int inst; -}; - -struct pld_inst { -}; - -struct sxtb_inst { - unsigned int Rd; - unsigned int Rm; - unsigned int rotate; -}; - -struct sxtab_inst { - unsigned int Rd; - unsigned int Rn; - unsigned int Rm; - unsigned rotate; -}; - -struct sxtah_inst { - unsigned int Rd; - unsigned int Rn; - unsigned int Rm; - unsigned int rotate; -}; - -struct sxth_inst { - unsigned int Rd; - unsigned int Rm; - unsigned int rotate; -}; - -struct uxtab_inst { - unsigned int Rn; - unsigned int Rd; - unsigned int rotate; - unsigned int Rm; -}; - -struct uxtah_inst { - unsigned int Rn; - unsigned int Rd; - unsigned int rotate; - unsigned int Rm; -}; - -struct uxth_inst { - unsigned int Rd; - unsigned int Rm; - unsigned int rotate; -}; - -struct cdp_inst { - unsigned int opcode_1; - unsigned int CRn; - unsigned int CRd; - unsigned int cp_num; - unsigned int opcode_2; - unsigned int CRm; - unsigned int inst; -}; - -struct uxtb_inst { - unsigned int Rd; - unsigned int Rm; - unsigned int rotate; -}; - -struct swp_inst { - unsigned int Rn; - unsigned int Rd; - unsigned int Rm; -}; - -struct setend_inst { - unsigned int set_bigend; -}; - -struct b_2_thumb { - unsigned int imm; -}; -struct b_cond_thumb { - unsigned int imm; - unsigned int cond; -}; - -struct bl_1_thumb { - unsigned int imm; -}; -struct bl_2_thumb { - unsigned int imm; -}; -struct blx_1_thumb { - unsigned int imm; - unsigned int instr; -}; - -struct pkh_inst { - unsigned int Rm; - unsigned int Rn; - unsigned int Rd; - unsigned char imm; -}; - -typedef arm_inst * ARM_INST_PTR; - -#define CACHE_BUFFER_SIZE (64 * 1024 * 2000) -static char inst_buf[CACHE_BUFFER_SIZE]; -static int top = 0; -static inline void *AllocBuffer(unsigned int size) { - int start = top; - top += size; - if (top > CACHE_BUFFER_SIZE) { - LOG_ERROR(Core_ARM11, "inst_buf is full"); - CITRA_IGNORE_EXIT(-1); - } - return (void *)&inst_buf[start]; -} - -static shtop_fp_t get_shtop(unsigned int inst) { +shtop_fp_t GetShifterOp(unsigned int inst) { if (BIT(inst, 25)) { return DPO(Immediate); } else if (BITS(inst, 4, 11) == 0) { @@ -1165,7 +674,7 @@ static shtop_fp_t get_shtop(unsigned int inst) { return nullptr; } -static get_addr_fp_t get_calc_addr_op(unsigned int inst) { +get_addr_fp_t GetAddressingOp(unsigned int inst) { if (BITS(inst, 24, 27) == 5 && BIT(inst, 21) == 0) { return LnSWoUB(ImmediateOffset); } else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 0 && BITS(inst, 4, 11) == 0) { @@ -1208,2200 +717,20 @@ static get_addr_fp_t get_calc_addr_op(unsigned int inst) { return nullptr; } -#define INTERPRETER_TRANSLATE(s) glue(InterpreterTranslate_, s) - -static ARM_INST_PTR INTERPRETER_TRANSLATE(adc)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(adc_inst)); - adc_inst *inst_cream = (adc_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(add)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(add_inst)); - add_inst *inst_cream = (add_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(and)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(and_inst)); - and_inst *inst_cream = (and_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(bbl)(unsigned int inst, int index) -{ - #define POSBRANCH ((inst & 0x7fffff) << 2) - #define NEGBRANCH ((0xff000000 |(inst & 0xffffff)) << 2) - - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(bbl_inst)); - bbl_inst *inst_cream = (bbl_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = DIRECT_BRANCH; - - if (BIT(inst, 24)) - inst_base->br = CALL; - if (BITS(inst, 28, 31) <= 0xe) - inst_base->br |= COND; - - inst_cream->L = BIT(inst, 24); - inst_cream->signed_immed_24 = BIT(inst, 23) ? NEGBRANCH : POSBRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(bic)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(bic_inst)); - bic_inst *inst_cream = (bic_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(bkpt_inst)); - bkpt_inst* const inst_cream = (bkpt_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->imm = (BITS(inst, 8, 19) << 4) | BITS(inst, 0, 3); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(blx)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(blx_inst)); - blx_inst *inst_cream = (blx_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = INDIRECT_BRANCH; - - inst_cream->inst = inst; - if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) { - inst_cream->val.Rm = BITS(inst, 0, 3); - } else { - inst_cream->val.signed_immed_24 = BITS(inst, 0, 23); - } - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(bx)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(bx_inst)); - bx_inst *inst_cream = (bx_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = INDIRECT_BRANCH; - - inst_cream->Rm = BITS(inst, 0, 3); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(bx)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index) { - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cdp_inst)); - cdp_inst *inst_cream = (cdp_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->CRm = BITS(inst, 0, 3); - inst_cream->CRd = BITS(inst, 12, 15); - inst_cream->CRn = BITS(inst, 16, 19); - inst_cream->cp_num = BITS(inst, 8, 11); - inst_cream->opcode_2 = BITS(inst, 5, 7); - inst_cream->opcode_1 = BITS(inst, 20, 23); - inst_cream->inst = inst; - - LOG_TRACE(Core_ARM11, "inst %x index %x", inst, index); - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(clrex)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(clrex_inst)); - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(clz)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(clz_inst)); - clz_inst *inst_cream = (clz_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rd = BITS(inst, 12, 15); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(cmn)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cmn_inst)); - cmn_inst *inst_cream = (cmn_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(cmp)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cmp_inst)); - cmp_inst *inst_cream = (cmp_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(cps)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cps_inst)); - cps_inst *inst_cream = (cps_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->imod0 = BIT(inst, 18); - inst_cream->imod1 = BIT(inst, 19); - inst_cream->mmod = BIT(inst, 17); - inst_cream->A = BIT(inst, 8); - inst_cream->I = BIT(inst, 7); - inst_cream->F = BIT(inst, 6); - inst_cream->mode = BITS(inst, 0, 4); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(cpy)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mov_inst)); - mov_inst *inst_cream = (mov_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) { - inst_base->br = INDIRECT_BRANCH; - } - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(eor)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(eor_inst)); - eor_inst *inst_cream = (eor_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldc)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldc_inst)); - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldm)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - if (BIT(inst, 15)) { - inst_base->br = INDIRECT_BRANCH; - } - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(sxth)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtb_inst)); - sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->rotate = BITS(inst, 10, 11); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldr)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - if (BITS(inst, 12, 15) == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrcond)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - if (BITS(inst, 12, 15) == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(uxth)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(uxth_inst)); - uxth_inst *inst_cream = (uxth_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->rotate = BITS(inst, 10, 11); - inst_cream->Rm = BITS(inst, 0, 3); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtah)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(uxtah_inst)); - uxtah_inst *inst_cream = (uxtah_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->rotate = BITS(inst, 10, 11); - inst_cream->Rm = BITS(inst, 0, 3); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrb)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(unsigned int inst, int index) -{ - arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst* inst_cream = (ldst_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; +// Specialized for LDRT, LDRBT, STRT, and STRBT, which have specific addressing mode requirements +get_addr_fp_t GetAddressingOpLoadStoreT(unsigned int inst) { if (BITS(inst, 25, 27) == 2) { - inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed); + return LnSWoUB(ImmediatePostIndexed); } else if (BITS(inst, 25, 27) == 3) { - inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed); - } else { - DEBUG_MSG; + return LnSWoUB(ScaledRegisterPostIndexed); } - - return inst_base; + // Reaching this would indicate the thumb version + // of this instruction, however the 3DS CPU doesn't + // support this variant (the 3DS CPU is only ARMv6K, + // while this variant is added in ARMv6T2). + // So it's sufficient for citra to not implement this. + return nullptr; } -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrd)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrex)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst *inst_cream = (generic_arm_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = (BITS(inst, 12, 15) == 15) ? INDIRECT_BRANCH : NON_BRANCH; // Branch if dest is R15 - - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrexb)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(ldrex)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrexh)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(ldrex)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrexd)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(ldrex)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrh)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsb)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrt)(unsigned int inst, int index) -{ - arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst* inst_cream = (ldst_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - if (BITS(inst, 25, 27) == 2) { - inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed); - } else if (BITS(inst, 25, 27) == 3) { - inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed); - } else { - // Reaching this would indicate the thumb version - // of this instruction, however the 3DS CPU doesn't - // support this variant (the 3DS CPU is only ARMv6K, - // while this variant is added in ARMv6T2). - // So it's sufficient for citra to not implement this. - DEBUG_MSG; - } - - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(mcr)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mcr_inst)); - mcr_inst *inst_cream = (mcr_inst *)inst_base->component; - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->crn = BITS(inst, 16, 19); - inst_cream->crm = BITS(inst, 0, 3); - inst_cream->opcode_1 = BITS(inst, 21, 23); - inst_cream->opcode_2 = BITS(inst, 5, 7); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->cp_num = BITS(inst, 8, 11); - inst_cream->inst = inst; - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(mcrr_inst)); - mcrr_inst* const inst_cream = (mcrr_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->crm = BITS(inst, 0, 3); - inst_cream->opcode_1 = BITS(inst, 4, 7); - inst_cream->cp_num = BITS(inst, 8, 11); - inst_cream->rt = BITS(inst, 12, 15); - inst_cream->rt2 = BITS(inst, 16, 19); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(mla)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mla_inst)); - mla_inst *inst_cream = (mla_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 12, 15); - inst_cream->Rd = BITS(inst, 16, 19); - inst_cream->Rs = BITS(inst, 8, 11); - inst_cream->Rm = BITS(inst, 0, 3); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(mov)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mov_inst)); - mov_inst *inst_cream = (mov_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) { - inst_base->br = INDIRECT_BRANCH; - } - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(mrc)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mrc_inst)); - mrc_inst *inst_cream = (mrc_inst *)inst_base->component; - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->crn = BITS(inst, 16, 19); - inst_cream->crm = BITS(inst, 0, 3); - inst_cream->opcode_1 = BITS(inst, 21, 23); - inst_cream->opcode_2 = BITS(inst, 5, 7); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->cp_num = BITS(inst, 8, 11); - inst_cream->inst = inst; - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(mcrr)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(mrs)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mrs_inst)); - mrs_inst *inst_cream = (mrs_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->R = BIT(inst, 22); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(msr)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(msr_inst)); - msr_inst *inst_cream = (msr_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->field_mask = BITS(inst, 16, 19); - inst_cream->R = BIT(inst, 22); - inst_cream->inst = inst; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(mul)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mul_inst)); - mul_inst *inst_cream = (mul_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->S = BIT(inst, 20); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rs = BITS(inst, 8, 11); - inst_cream->Rd = BITS(inst, 16, 19); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(mvn)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mvn_inst)); - mvn_inst *inst_cream = (mvn_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) { - inst_base->br = INDIRECT_BRANCH; - } - return inst_base; - -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(orr)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(orr_inst)); - orr_inst *inst_cream = (orr_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} - -// NOP introduced in ARMv6K. -static ARM_INST_PTR INTERPRETER_TRANSLATE(nop)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst)); - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(pkh_inst)); - pkh_inst *inst_cream = (pkh_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->imm = BITS(inst, 7, 11); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(pkhbt)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(pld_inst)); - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->op1 = BITS(inst, 21, 22); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(qadd)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(qadd)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(qadd)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->op1 = BITS(inst, 20, 21); - inst_cream->op2 = BITS(inst, 5, 7); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(qadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(qadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(qadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(qadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(qadd8)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(rev_inst)); - rev_inst* const inst_cream = (rev_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->op1 = BITS(inst, 20, 22); - inst_cream->op2 = BITS(inst, 5, 7); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(rev16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(rev)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(rev)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst* const inst_cream = (ldst_inst*)inst_base->component; - - inst_base->cond = AL; - inst_base->idx = index; - inst_base->br = INDIRECT_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(rsb)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rsb_inst)); - rsb_inst *inst_cream = (rsb_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(rsc)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rsc_inst)); - rsc_inst *inst_cream = (rsc_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->op1 = BITS(inst, 20, 21); - inst_cream->op2 = BITS(inst, 5, 7); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(sadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(sadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(sadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(sadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(sadd8)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sbc_inst)); - sbc_inst *inst_cream = (sbc_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->op1 = BITS(inst, 20, 22); - inst_cream->op2 = BITS(inst, 5, 7); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(setend_inst)); - setend_inst* const inst_cream = (setend_inst*)inst_base->component; - - inst_base->cond = AL; - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->set_bigend = BIT(inst, 9); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(sev)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst)); - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->op1 = BITS(inst, 20, 21); - inst_cream->op2 = BITS(inst, 5, 7); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(shadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(shadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(shadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(shadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(shadd8)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(smla)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smla_inst)); - smla_inst *inst_cream = (smla_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->x = BIT(inst, 5); - inst_cream->y = BIT(inst, 6); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rs = BITS(inst, 8, 11); - inst_cream->Rd = BITS(inst, 16, 19); - inst_cream->Rn = BITS(inst, 12, 15); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(smlad)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlad_inst)); - smlad_inst* const inst_cream = (smlad_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->m = BIT(inst, 5); - inst_cream->Rn = BITS(inst, 0, 3); - inst_cream->Rm = BITS(inst, 8, 11); - inst_cream->Rd = BITS(inst, 16, 19); - inst_cream->Ra = BITS(inst, 12, 15); - inst_cream->op1 = BITS(inst, 20, 22); - inst_cream->op2 = BITS(inst, 5, 7); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(smlad)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(smlad)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(smlad)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(smlal)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); - umlal_inst *inst_cream = (umlal_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->S = BIT(inst, 20); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rs = BITS(inst, 8, 11); - inst_cream->RdHi = BITS(inst, 16, 19); - inst_cream->RdLo = BITS(inst, 12, 15); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlalxy_inst)); - smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->x = BIT(inst, 5); - inst_cream->y = BIT(inst, 6); - inst_cream->RdLo = BITS(inst, 12, 15); - inst_cream->RdHi = BITS(inst, 16, 19); - inst_cream->Rn = BITS(inst, 0, 4); - inst_cream->Rm = BITS(inst, 8, 11); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlad_inst)); - smlad_inst* const inst_cream = (smlad_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Ra = BITS(inst, 12, 15); - inst_cream->Rm = BITS(inst, 8, 11); - inst_cream->Rn = BITS(inst, 0, 3); - inst_cream->Rd = BITS(inst, 16, 19); - inst_cream->m = BIT(inst, 6); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlald_inst)); - smlald_inst* const inst_cream = (smlald_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rm = BITS(inst, 8, 11); - inst_cream->Rn = BITS(inst, 0, 3); - inst_cream->RdLo = BITS(inst, 12, 15); - inst_cream->RdHi = BITS(inst, 16, 19); - inst_cream->swap = BIT(inst, 5); - inst_cream->op1 = BITS(inst, 20, 22); - inst_cream->op2 = BITS(inst, 5, 7); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(smlald)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlad_inst)); - smlad_inst* const inst_cream = (smlad_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->m = BIT(inst, 5); - inst_cream->Ra = BITS(inst, 12, 15); - inst_cream->Rm = BITS(inst, 8, 11); - inst_cream->Rn = BITS(inst, 0, 3); - inst_cream->Rd = BITS(inst, 16, 19); - inst_cream->op1 = BITS(inst, 20, 22); - inst_cream->op2 = BITS(inst, 5, 7); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(smmla)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(smmla)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(smul)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smul_inst)); - smul_inst *inst_cream = (smul_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rd = BITS(inst, 16, 19); - inst_cream->Rs = BITS(inst, 8, 11); - inst_cream->Rm = BITS(inst, 0, 3); - - inst_cream->x = BIT(inst, 5); - inst_cream->y = BIT(inst, 6); - - return inst_base; - -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(smull)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umull_inst)); - umull_inst *inst_cream = (umull_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->S = BIT(inst, 20); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rs = BITS(inst, 8, 11); - inst_cream->RdHi = BITS(inst, 16, 19); - inst_cream->RdLo = BITS(inst, 12, 15); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(smulw)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smlad_inst)); - smlad_inst *inst_cream = (smlad_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->m = BIT(inst, 6); - inst_cream->Rm = BITS(inst, 8, 11); - inst_cream->Rn = BITS(inst, 0, 3); - inst_cream->Rd = BITS(inst, 16, 19); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst* const inst_cream = (ldst_inst*)inst_base->component; - - inst_base->cond = AL; - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ssat_inst)); - ssat_inst* const inst_cream = (ssat_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rn = BITS(inst, 0, 3); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->imm5 = BITS(inst, 7, 11); - inst_cream->sat_imm = BITS(inst, 16, 20); - inst_cream->shift_type = BIT(inst, 6); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ssat_inst)); - ssat_inst* const inst_cream = (ssat_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rn = BITS(inst, 0, 3); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->sat_imm = BITS(inst, 16, 19); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(stc_inst)); - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(stm)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtb_inst)); - sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->rotate = BITS(inst, 10, 11); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(str)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(uxth_inst)); - uxth_inst *inst_cream = (uxth_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->rotate = BITS(inst, 10, 11); - inst_cream->Rm = BITS(inst, 0, 3); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(uxtab_inst)); - uxtab_inst *inst_cream = (uxtab_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->rotate = BITS(inst, 10, 11); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(strb)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(unsigned int inst, int index) -{ - arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst* inst_cream = (ldst_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - - if (BITS(inst, 25, 27) == 2) { - inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed); - } else if (BITS(inst, 25, 27) == 3) { - inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed); - } else { - DEBUG_MSG; - } - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(strd)(unsigned int inst, int index){ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(strex)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst *inst_cream = (generic_arm_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->Rm = BITS(inst, 0, 3); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(strexb)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(strex)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(strexh)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(strex)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(strexd)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(strex)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(strh)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst *inst_cream = (ldst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - inst_cream->get_addr = get_calc_addr_op(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(unsigned int inst, int index) -{ - arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); - ldst_inst* inst_cream = (ldst_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->inst = inst; - if (BITS(inst, 25, 27) == 2) { - inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed); - } else if (BITS(inst, 25, 27) == 3) { - inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed); - } else { - // Reaching this would indicate the thumb version - // of this instruction, however the 3DS CPU doesn't - // support this variant (the 3DS CPU is only ARMv6K, - // while this variant is added in ARMv6T2). - // So it's sufficient for citra to not implement this. - DEBUG_MSG; - } - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(sub)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sub_inst)); - sub_inst *inst_cream = (sub_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(swi)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(swi_inst)); - swi_inst *inst_cream = (swi_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->num = BITS(inst, 0, 23); - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(swp)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(swp_inst)); - swp_inst *inst_cream = (swp_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->Rm = BITS(inst, 0, 3); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(swpb)(unsigned int inst, int index){ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(swp_inst)); - swp_inst *inst_cream = (swp_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->Rm = BITS(inst, 0, 3); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab)(unsigned int inst, int index){ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtab_inst)); - sxtab_inst *inst_cream = (sxtab_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->rotate = BITS(inst, 10, 11); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(sxtab_inst)); - sxtab_inst* const inst_cream = (sxtab_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->rotate = BITS(inst, 10, 11); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(sxtab16)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index) { - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtah_inst)); - sxtah_inst *inst_cream = (sxtah_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->rotate = BITS(inst, 10, 11); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(teq)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(teq_inst)); - teq_inst *inst_cream = (teq_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(tst)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(tst_inst)); - tst_inst *inst_cream = (tst_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->I = BIT(inst, 25); - inst_cream->S = BIT(inst, 20); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->shifter_operand = BITS(inst, 0, 11); - inst_cream->shtop_func = get_shtop(inst); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->op1 = BITS(inst, 20, 21); - inst_cream->op2 = BITS(inst, 5, 7); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uadd8)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->op1 = BITS(inst, 20, 21); - inst_cream->op2 = BITS(inst, 5, 7); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uhadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uhadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uhadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uhadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uhadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(umaal_inst)); - umaal_inst* const inst_cream = (umaal_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rm = BITS(inst, 8, 11); - inst_cream->Rn = BITS(inst, 0, 3); - inst_cream->RdLo = BITS(inst, 12, 15); - inst_cream->RdHi = BITS(inst, 16, 19); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); - umlal_inst *inst_cream = (umlal_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->S = BIT(inst, 20); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rs = BITS(inst, 8, 11); - inst_cream->RdHi = BITS(inst, 16, 19); - inst_cream->RdLo = BITS(inst, 12, 15); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(umull)(unsigned int inst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umull_inst)); - umull_inst *inst_cream = (umull_inst *)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->S = BIT(inst, 20); - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rs = BITS(inst, 8, 11); - inst_cream->RdHi = BITS(inst, 16, 19); - inst_cream->RdLo = BITS(inst, 12, 15); - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(b_2_thumb)(unsigned int tinst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(b_2_thumb)); - b_2_thumb *inst_cream = (b_2_thumb *)inst_base->component; - - inst_cream->imm = ((tinst & 0x3FF) << 1) | ((tinst & (1 << 10)) ? 0xFFFFF800 : 0); - - inst_base->idx = index; - inst_base->br = DIRECT_BRANCH; - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(b_cond_thumb)(unsigned int tinst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(b_cond_thumb)); - b_cond_thumb *inst_cream = (b_cond_thumb *)inst_base->component; - - inst_cream->imm = (((tinst & 0x7F) << 1) | ((tinst & (1 << 7)) ? 0xFFFFFF00 : 0)); - inst_cream->cond = ((tinst >> 8) & 0xf); - inst_base->idx = index; - inst_base->br = DIRECT_BRANCH; - - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(bl_1_thumb)(unsigned int tinst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(bl_1_thumb)); - bl_1_thumb *inst_cream = (bl_1_thumb *)inst_base->component; - - inst_cream->imm = (((tinst & 0x07FF) << 12) | ((tinst & (1 << 10)) ? 0xFF800000 : 0)); - - inst_base->idx = index; - inst_base->br = NON_BRANCH; - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(bl_2_thumb)(unsigned int tinst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(bl_2_thumb)); - bl_2_thumb *inst_cream = (bl_2_thumb *)inst_base->component; - - inst_cream->imm = (tinst & 0x07FF) << 1; - - inst_base->idx = index; - inst_base->br = DIRECT_BRANCH; - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(blx_1_thumb)(unsigned int tinst, int index) -{ - arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(blx_1_thumb)); - blx_1_thumb *inst_cream = (blx_1_thumb *)inst_base->component; - - inst_cream->imm = (tinst & 0x07FF) << 1; - inst_cream->instr = tinst; - - inst_base->idx = index; - inst_base->br = DIRECT_BRANCH; - return inst_base; -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->op1 = BITS(inst, 20, 21); - inst_cream->op2 = BITS(inst, 5, 7); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uqadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uqadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uqadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uqadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uqadd8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); - generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->op1 = BITS(inst, 20, 24); - inst_cream->op2 = BITS(inst, 5, 7); - inst_cream->Rd = BITS(inst, 16, 19); - inst_cream->Rm = BITS(inst, 8, 11); - inst_cream->Rn = BITS(inst, 0, 3); - inst_cream->Ra = BITS(inst, 12, 15); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(usada8)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(ssat)(inst, index); -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(ssat16)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(uxtab_inst)); - uxtab_inst* const inst_cream = (uxtab_inst*)inst_base->component; - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - inst_cream->Rm = BITS(inst, 0, 3); - inst_cream->Rn = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); - inst_cream->rotate = BITS(inst, 10, 11); - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(unsigned int inst, int index) -{ - return INTERPRETER_TRANSLATE(uxtab16)(inst, index); -} - -static ARM_INST_PTR INTERPRETER_TRANSLATE(wfe)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst)); - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(wfi)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst)); - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - return inst_base; -} -static ARM_INST_PTR INTERPRETER_TRANSLATE(yield)(unsigned int inst, int index) -{ - arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst)); - - inst_base->cond = BITS(inst, 28, 31); - inst_base->idx = index; - inst_base->br = NON_BRANCH; - - return inst_base; -} - -// Floating point VFPv3 structures and instructions - -#define VFP_INTERPRETER_STRUCT -#include "core/arm/skyeye_common/vfp/vfpinstr.cpp" -#undef VFP_INTERPRETER_STRUCT - -#define VFP_INTERPRETER_TRANS -#include "core/arm/skyeye_common/vfp/vfpinstr.cpp" -#undef VFP_INTERPRETER_TRANS - -typedef ARM_INST_PTR (*transop_fp_t)(unsigned int, int); - -const transop_fp_t arm_instruction_trans[] = { - INTERPRETER_TRANSLATE(vmla), - INTERPRETER_TRANSLATE(vmls), - INTERPRETER_TRANSLATE(vnmla), - INTERPRETER_TRANSLATE(vnmls), - INTERPRETER_TRANSLATE(vnmul), - INTERPRETER_TRANSLATE(vmul), - INTERPRETER_TRANSLATE(vadd), - INTERPRETER_TRANSLATE(vsub), - INTERPRETER_TRANSLATE(vdiv), - INTERPRETER_TRANSLATE(vmovi), - INTERPRETER_TRANSLATE(vmovr), - INTERPRETER_TRANSLATE(vabs), - INTERPRETER_TRANSLATE(vneg), - INTERPRETER_TRANSLATE(vsqrt), - INTERPRETER_TRANSLATE(vcmp), - INTERPRETER_TRANSLATE(vcmp2), - INTERPRETER_TRANSLATE(vcvtbds), - INTERPRETER_TRANSLATE(vcvtbff), - INTERPRETER_TRANSLATE(vcvtbfi), - INTERPRETER_TRANSLATE(vmovbrs), - INTERPRETER_TRANSLATE(vmsr), - INTERPRETER_TRANSLATE(vmovbrc), - INTERPRETER_TRANSLATE(vmrs), - INTERPRETER_TRANSLATE(vmovbcr), - INTERPRETER_TRANSLATE(vmovbrrss), - INTERPRETER_TRANSLATE(vmovbrrd), - INTERPRETER_TRANSLATE(vstr), - INTERPRETER_TRANSLATE(vpush), - INTERPRETER_TRANSLATE(vstm), - INTERPRETER_TRANSLATE(vpop), - INTERPRETER_TRANSLATE(vldr), - INTERPRETER_TRANSLATE(vldm), - - INTERPRETER_TRANSLATE(srs), - INTERPRETER_TRANSLATE(rfe), - INTERPRETER_TRANSLATE(bkpt), - INTERPRETER_TRANSLATE(blx), - INTERPRETER_TRANSLATE(cps), - INTERPRETER_TRANSLATE(pld), - INTERPRETER_TRANSLATE(setend), - INTERPRETER_TRANSLATE(clrex), - INTERPRETER_TRANSLATE(rev16), - INTERPRETER_TRANSLATE(usad8), - INTERPRETER_TRANSLATE(sxtb), - INTERPRETER_TRANSLATE(uxtb), - INTERPRETER_TRANSLATE(sxth), - INTERPRETER_TRANSLATE(sxtb16), - INTERPRETER_TRANSLATE(uxth), - INTERPRETER_TRANSLATE(uxtb16), - INTERPRETER_TRANSLATE(cpy), - INTERPRETER_TRANSLATE(uxtab), - INTERPRETER_TRANSLATE(ssub8), - INTERPRETER_TRANSLATE(shsub8), - INTERPRETER_TRANSLATE(ssubaddx), - INTERPRETER_TRANSLATE(strex), - INTERPRETER_TRANSLATE(strexb), - INTERPRETER_TRANSLATE(swp), - INTERPRETER_TRANSLATE(swpb), - INTERPRETER_TRANSLATE(ssub16), - INTERPRETER_TRANSLATE(ssat16), - INTERPRETER_TRANSLATE(shsubaddx), - INTERPRETER_TRANSLATE(qsubaddx), - INTERPRETER_TRANSLATE(shaddsubx), - INTERPRETER_TRANSLATE(shadd8), - INTERPRETER_TRANSLATE(shadd16), - INTERPRETER_TRANSLATE(sel), - INTERPRETER_TRANSLATE(saddsubx), - INTERPRETER_TRANSLATE(sadd8), - INTERPRETER_TRANSLATE(sadd16), - INTERPRETER_TRANSLATE(shsub16), - INTERPRETER_TRANSLATE(umaal), - INTERPRETER_TRANSLATE(uxtab16), - INTERPRETER_TRANSLATE(usubaddx), - INTERPRETER_TRANSLATE(usub8), - INTERPRETER_TRANSLATE(usub16), - INTERPRETER_TRANSLATE(usat16), - INTERPRETER_TRANSLATE(usada8), - INTERPRETER_TRANSLATE(uqsubaddx), - INTERPRETER_TRANSLATE(uqsub8), - INTERPRETER_TRANSLATE(uqsub16), - INTERPRETER_TRANSLATE(uqaddsubx), - INTERPRETER_TRANSLATE(uqadd8), - INTERPRETER_TRANSLATE(uqadd16), - INTERPRETER_TRANSLATE(sxtab), - INTERPRETER_TRANSLATE(uhsubaddx), - INTERPRETER_TRANSLATE(uhsub8), - INTERPRETER_TRANSLATE(uhsub16), - INTERPRETER_TRANSLATE(uhaddsubx), - INTERPRETER_TRANSLATE(uhadd8), - INTERPRETER_TRANSLATE(uhadd16), - INTERPRETER_TRANSLATE(uaddsubx), - INTERPRETER_TRANSLATE(uadd8), - INTERPRETER_TRANSLATE(uadd16), - INTERPRETER_TRANSLATE(sxtah), - INTERPRETER_TRANSLATE(sxtab16), - INTERPRETER_TRANSLATE(qadd8), - INTERPRETER_TRANSLATE(bxj), - INTERPRETER_TRANSLATE(clz), - INTERPRETER_TRANSLATE(uxtah), - INTERPRETER_TRANSLATE(bx), - INTERPRETER_TRANSLATE(rev), - INTERPRETER_TRANSLATE(blx), - INTERPRETER_TRANSLATE(revsh), - INTERPRETER_TRANSLATE(qadd), - INTERPRETER_TRANSLATE(qadd16), - INTERPRETER_TRANSLATE(qaddsubx), - INTERPRETER_TRANSLATE(ldrex), - INTERPRETER_TRANSLATE(qdadd), - INTERPRETER_TRANSLATE(qdsub), - INTERPRETER_TRANSLATE(qsub), - INTERPRETER_TRANSLATE(ldrexb), - INTERPRETER_TRANSLATE(qsub8), - INTERPRETER_TRANSLATE(qsub16), - INTERPRETER_TRANSLATE(smuad), - INTERPRETER_TRANSLATE(smmul), - INTERPRETER_TRANSLATE(smusd), - INTERPRETER_TRANSLATE(smlsd), - INTERPRETER_TRANSLATE(smlsld), - INTERPRETER_TRANSLATE(smmla), - INTERPRETER_TRANSLATE(smmls), - INTERPRETER_TRANSLATE(smlald), - INTERPRETER_TRANSLATE(smlad), - INTERPRETER_TRANSLATE(smlaw), - INTERPRETER_TRANSLATE(smulw), - INTERPRETER_TRANSLATE(pkhtb), - INTERPRETER_TRANSLATE(pkhbt), - INTERPRETER_TRANSLATE(smul), - INTERPRETER_TRANSLATE(smlalxy), - INTERPRETER_TRANSLATE(smla), - INTERPRETER_TRANSLATE(mcrr), - INTERPRETER_TRANSLATE(mrrc), - INTERPRETER_TRANSLATE(cmp), - INTERPRETER_TRANSLATE(tst), - INTERPRETER_TRANSLATE(teq), - INTERPRETER_TRANSLATE(cmn), - INTERPRETER_TRANSLATE(smull), - INTERPRETER_TRANSLATE(umull), - INTERPRETER_TRANSLATE(umlal), - INTERPRETER_TRANSLATE(smlal), - INTERPRETER_TRANSLATE(mul), - INTERPRETER_TRANSLATE(mla), - INTERPRETER_TRANSLATE(ssat), - INTERPRETER_TRANSLATE(usat), - INTERPRETER_TRANSLATE(mrs), - INTERPRETER_TRANSLATE(msr), - INTERPRETER_TRANSLATE(and), - INTERPRETER_TRANSLATE(bic), - INTERPRETER_TRANSLATE(ldm), - INTERPRETER_TRANSLATE(eor), - INTERPRETER_TRANSLATE(add), - INTERPRETER_TRANSLATE(rsb), - INTERPRETER_TRANSLATE(rsc), - INTERPRETER_TRANSLATE(sbc), - INTERPRETER_TRANSLATE(adc), - INTERPRETER_TRANSLATE(sub), - INTERPRETER_TRANSLATE(orr), - INTERPRETER_TRANSLATE(mvn), - INTERPRETER_TRANSLATE(mov), - INTERPRETER_TRANSLATE(stm), - INTERPRETER_TRANSLATE(ldm), - INTERPRETER_TRANSLATE(ldrsh), - INTERPRETER_TRANSLATE(stm), - INTERPRETER_TRANSLATE(ldm), - INTERPRETER_TRANSLATE(ldrsb), - INTERPRETER_TRANSLATE(strd), - INTERPRETER_TRANSLATE(ldrh), - INTERPRETER_TRANSLATE(strh), - INTERPRETER_TRANSLATE(ldrd), - INTERPRETER_TRANSLATE(strt), - INTERPRETER_TRANSLATE(strbt), - INTERPRETER_TRANSLATE(ldrbt), - INTERPRETER_TRANSLATE(ldrt), - INTERPRETER_TRANSLATE(mrc), - INTERPRETER_TRANSLATE(mcr), - INTERPRETER_TRANSLATE(msr), - INTERPRETER_TRANSLATE(msr), - INTERPRETER_TRANSLATE(msr), - INTERPRETER_TRANSLATE(msr), - INTERPRETER_TRANSLATE(msr), - INTERPRETER_TRANSLATE(ldrb), - INTERPRETER_TRANSLATE(strb), - INTERPRETER_TRANSLATE(ldr), - INTERPRETER_TRANSLATE(ldrcond), - INTERPRETER_TRANSLATE(str), - INTERPRETER_TRANSLATE(cdp), - INTERPRETER_TRANSLATE(stc), - INTERPRETER_TRANSLATE(ldc), - INTERPRETER_TRANSLATE(ldrexd), - INTERPRETER_TRANSLATE(strexd), - INTERPRETER_TRANSLATE(ldrexh), - INTERPRETER_TRANSLATE(strexh), - INTERPRETER_TRANSLATE(nop), - INTERPRETER_TRANSLATE(yield), - INTERPRETER_TRANSLATE(wfe), - INTERPRETER_TRANSLATE(wfi), - INTERPRETER_TRANSLATE(sev), - INTERPRETER_TRANSLATE(swi), - INTERPRETER_TRANSLATE(bbl), - - // All the thumb instructions should be placed the end of table - INTERPRETER_TRANSLATE(b_2_thumb), - INTERPRETER_TRANSLATE(b_cond_thumb), - INTERPRETER_TRANSLATE(bl_1_thumb), - INTERPRETER_TRANSLATE(bl_2_thumb), - INTERPRETER_TRANSLATE(blx_1_thumb) -}; enum { FETCH_SUCCESS, @@ -3413,7 +742,7 @@ static ThumbDecodeStatus DecodeThumbInstruction(u32 inst, u32 addr, u32* arm_ins ThumbDecodeStatus ret = TranslateThumbInstruction (addr, inst, arm_inst, inst_size); if (ret == ThumbDecodeStatus::BRANCH) { int inst_index; - int table_length = sizeof(arm_instruction_trans) / sizeof(transop_fp_t); + int table_length = arm_instruction_trans_len; u32 tinstr = GetThumbInstruction(inst, addr); switch ((tinstr & 0xF800) >> 11) { @@ -3499,14 +828,14 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) // Go on next, until terminal instruction // Save start addr of basicblock in CreamCache ARM_INST_PTR inst_base = nullptr; - int ret = NON_BRANCH; + TransExtData ret = TransExtData::NON_BRANCH; int size = 0; // instruction size of basic block - bb_start = top; + bb_start = trans_cache_buf_top; u32 phys_addr = addr; u32 pc_start = cpu->Reg[15]; - while (ret == NON_BRANCH) { + while (ret == TransExtData::NON_BRANCH) { unsigned int inst_size = InterpreterTranslateInstruction(cpu, phys_addr, inst_base); size++; @@ -3514,7 +843,7 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) phys_addr += inst_size; if ((phys_addr & 0xfff) == 0) { - inst_base->br = END_OF_PAGE; + inst_base->br = TransExtData::END_OF_PAGE; } ret = inst_base->br; }; @@ -3528,15 +857,15 @@ static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) MICROPROFILE_SCOPE(DynCom_Decode); ARM_INST_PTR inst_base = nullptr; - bb_start = top; + bb_start = trans_cache_buf_top; u32 phys_addr = addr; u32 pc_start = cpu->Reg[15]; InterpreterTranslateInstruction(cpu, phys_addr, inst_base); - if (inst_base->br == NON_BRANCH) { - inst_base->br = SINGLE_STEP; + if (inst_base->br == TransExtData::NON_BRANCH) { + inst_base->br = TransExtData::SINGLE_STEP; } cpu->instruction_cache[pc_start] = bb_start; @@ -3581,8 +910,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { #define SET_PC (cpu->Reg[15] = cpu->Reg[15] + 8 + inst_cream->signed_immed_24) #define SHIFTER_OPERAND inst_cream->shtop_func(cpu, inst_cream->shifter_operand) - #define FETCH_INST if (inst_base->br != NON_BRANCH) goto DISPATCH; \ - inst_base = (arm_inst *)&inst_buf[ptr] + #define FETCH_INST if (inst_base->br != TransExtData::NON_BRANCH) goto DISPATCH; \ + inst_base = (arm_inst *)&trans_cache_buf[ptr] #define INC_PC(l) ptr += sizeof(arm_inst) + l #define INC_PC_STUB ptr += sizeof(arm_inst) @@ -3905,7 +1234,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { breakpoint_data = GDBStub::GetNextBreakpointFromAddress(cpu->Reg[15], GDBStub::BreakpointType::Execute); } - inst_base = (arm_inst *)&inst_buf[ptr]; + inst_base = (arm_inst *)&trans_cache_buf[ptr]; GOTO_NEXT_INST; } ADC_INST: @@ -5491,10 +2820,12 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { operand2 = (BIT(RS, 15)) ? (BITS(RS, 0, 15) | 0xffff0000) : BITS(RS, 0, 15); else operand2 = (BIT(RS, 31)) ? (BITS(RS, 16, 31) | 0xffff0000) : BITS(RS, 16, 31); - RD = operand1 * operand2 + RN; - if (AddOverflow(operand1 * operand2, RN, RD)) + u32 product = operand1 * operand2; + u32 result = product + RN; + if (AddOverflow(product, RN, result)) cpu->Cpsr |= (1 << 27); + RD = result; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(smla_inst)); @@ -5899,7 +3230,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { addr += 4; } if (BIT(inst_cream->inst, 15)) { - cpu->WriteMemory32(addr, cpu->Reg_usr[1] + 8); + cpu->WriteMemory32(addr, cpu->Reg[15] + 8); } } else { for (int i = 0; i < 15; i++) { @@ -5914,8 +3245,9 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } // Check PC reg - if (BIT(inst_cream->inst, 15)) - cpu->WriteMemory32(addr, cpu->Reg_usr[1] + 8); + if (BIT(inst_cream->inst, 15)) { + cpu->WriteMemory32(addr, cpu->Reg[15] + 8); + } } } cpu->Reg[15] += cpu->GetInstructionSize(); diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.cpp b/src/core/arm/dyncom/arm_dyncom_thumb.cpp index 29272fd5d..3576370d1 100644 --- a/src/core/arm/dyncom/arm_dyncom_thumb.cpp +++ b/src/core/arm/dyncom/arm_dyncom_thumb.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstddef> + // We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding // ARM instruction, and using the existing ARM simulator. @@ -293,15 +295,22 @@ ThumbDecodeStatus TranslateThumbInstruction(u32 addr, u32 instr, u32* ainstr, u3 | (BIT(tinstr, 4) << 18); // enable bit } } else if ((tinstr & 0x0F00) == 0x0a00) { - static const u32 subset[3] = { + static const u32 subset[4] = { 0xE6BF0F30, // REV 0xE6BF0FB0, // REV16 + 0, // undefined 0xE6FF0FB0, // REVSH }; - *ainstr = subset[BITS(tinstr, 6, 7)] // base - | (BITS(tinstr, 0, 2) << 12) // Rd - | BITS(tinstr, 3, 5); // Rm + size_t subset_index = BITS(tinstr, 6, 7); + + if (subset_index == 2) { + valid = ThumbDecodeStatus::UNDEFINED; + } else { + *ainstr = subset[subset_index] // base + | (BITS(tinstr, 0, 2) << 12) // Rd + | BITS(tinstr, 3, 5); // Rm + } } else { static const u32 subset[4] = { 0xE92D0000, // STMDB sp!,{rlist} diff --git a/src/core/arm/dyncom/arm_dyncom_trans.cpp b/src/core/arm/dyncom/arm_dyncom_trans.cpp new file mode 100644 index 000000000..00b42c246 --- /dev/null +++ b/src/core/arm/dyncom/arm_dyncom_trans.cpp @@ -0,0 +1,2178 @@ +#include <cstdlib> + +#include "common/assert.h" +#include "common/common_types.h" + +#include "core/arm/dyncom/arm_dyncom_interpreter.h" +#include "core/arm/dyncom/arm_dyncom_trans.h" +#include "core/arm/skyeye_common/armstate.h" +#include "core/arm/skyeye_common/armsupp.h" +#include "core/arm/skyeye_common/vfp/vfp.h" + +char trans_cache_buf[TRANS_CACHE_SIZE]; +size_t trans_cache_buf_top = 0; + +static void* AllocBuffer(size_t size) { + size_t start = trans_cache_buf_top; + trans_cache_buf_top += size; + ASSERT_MSG(trans_cache_buf_top <= TRANS_CACHE_SIZE, "Translation cache is full!"); + return static_cast<void*>(&trans_cache_buf[start]); +} + +#define glue(x, y) x ## y +#define INTERPRETER_TRANSLATE(s) glue(InterpreterTranslate_, s) + +shtop_fp_t GetShifterOp(unsigned int inst); +get_addr_fp_t GetAddressingOp(unsigned int inst); +get_addr_fp_t GetAddressingOpLoadStoreT(unsigned int inst); + +static ARM_INST_PTR INTERPRETER_TRANSLATE(adc)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(adc_inst)); + adc_inst *inst_cream = (adc_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(add)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(add_inst)); + add_inst *inst_cream = (add_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(and)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(and_inst)); + and_inst *inst_cream = (and_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(bbl)(unsigned int inst, int index) +{ + #define POSBRANCH ((inst & 0x7fffff) << 2) + #define NEGBRANCH ((0xff000000 |(inst & 0xffffff)) << 2) + + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(bbl_inst)); + bbl_inst *inst_cream = (bbl_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::DIRECT_BRANCH; + + if (BIT(inst, 24)) + inst_base->br = TransExtData::CALL; + + inst_cream->L = BIT(inst, 24); + inst_cream->signed_immed_24 = BIT(inst, 23) ? NEGBRANCH : POSBRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(bic)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(bic_inst)); + bic_inst *inst_cream = (bic_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(bkpt_inst)); + bkpt_inst* const inst_cream = (bkpt_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->imm = (BITS(inst, 8, 19) << 4) | BITS(inst, 0, 3); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(blx)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(blx_inst)); + blx_inst *inst_cream = (blx_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::INDIRECT_BRANCH; + + inst_cream->inst = inst; + if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) { + inst_cream->val.Rm = BITS(inst, 0, 3); + } else { + inst_cream->val.signed_immed_24 = BITS(inst, 0, 23); + } + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(bx)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(bx_inst)); + bx_inst *inst_cream = (bx_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::INDIRECT_BRANCH; + + inst_cream->Rm = BITS(inst, 0, 3); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(bx)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index) { + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cdp_inst)); + cdp_inst *inst_cream = (cdp_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->CRm = BITS(inst, 0, 3); + inst_cream->CRd = BITS(inst, 12, 15); + inst_cream->CRn = BITS(inst, 16, 19); + inst_cream->cp_num = BITS(inst, 8, 11); + inst_cream->opcode_2 = BITS(inst, 5, 7); + inst_cream->opcode_1 = BITS(inst, 20, 23); + inst_cream->inst = inst; + + LOG_TRACE(Core_ARM11, "inst %x index %x", inst, index); + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(clrex)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(clrex_inst)); + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(clz)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(clz_inst)); + clz_inst *inst_cream = (clz_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rd = BITS(inst, 12, 15); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(cmn)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cmn_inst)); + cmn_inst *inst_cream = (cmn_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(cmp)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cmp_inst)); + cmp_inst *inst_cream = (cmp_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(cps)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cps_inst)); + cps_inst *inst_cream = (cps_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->imod0 = BIT(inst, 18); + inst_cream->imod1 = BIT(inst, 19); + inst_cream->mmod = BIT(inst, 17); + inst_cream->A = BIT(inst, 8); + inst_cream->I = BIT(inst, 7); + inst_cream->F = BIT(inst, 6); + inst_cream->mode = BITS(inst, 0, 4); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(cpy)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mov_inst)); + mov_inst *inst_cream = (mov_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) { + inst_base->br = TransExtData::INDIRECT_BRANCH; + } + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(eor)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(eor_inst)); + eor_inst *inst_cream = (eor_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldc)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldc_inst)); + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldm)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + if (BIT(inst, 15)) { + inst_base->br = TransExtData::INDIRECT_BRANCH; + } + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(sxth)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtb_inst)); + sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->rotate = BITS(inst, 10, 11); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldr)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + if (BITS(inst, 12, 15) == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrcond)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + if (BITS(inst, 12, 15) == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(uxth)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(uxth_inst)); + uxth_inst *inst_cream = (uxth_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->rotate = BITS(inst, 10, 11); + inst_cream->Rm = BITS(inst, 0, 3); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtah)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(uxtah_inst)); + uxtah_inst *inst_cream = (uxtah_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->rotate = BITS(inst, 10, 11); + inst_cream->Rm = BITS(inst, 0, 3); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrb)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(unsigned int inst, int index) +{ + arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst* inst_cream = (ldst_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOpLoadStoreT(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrd)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrex)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst *inst_cream = (generic_arm_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = (BITS(inst, 12, 15) == 15) ? TransExtData::INDIRECT_BRANCH : TransExtData::NON_BRANCH; // Branch if dest is R15 + + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrexb)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(ldrex)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrexh)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(ldrex)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrexd)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(ldrex)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrh)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsb)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrt)(unsigned int inst, int index) +{ + arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst* inst_cream = (ldst_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOpLoadStoreT(inst); + + if (BITS(inst, 12, 15) == 15) { + inst_base->br = TransExtData::INDIRECT_BRANCH; + } + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(mcr)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mcr_inst)); + mcr_inst *inst_cream = (mcr_inst *)inst_base->component; + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->crn = BITS(inst, 16, 19); + inst_cream->crm = BITS(inst, 0, 3); + inst_cream->opcode_1 = BITS(inst, 21, 23); + inst_cream->opcode_2 = BITS(inst, 5, 7); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->cp_num = BITS(inst, 8, 11); + inst_cream->inst = inst; + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(mcrr_inst)); + mcrr_inst* const inst_cream = (mcrr_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->crm = BITS(inst, 0, 3); + inst_cream->opcode_1 = BITS(inst, 4, 7); + inst_cream->cp_num = BITS(inst, 8, 11); + inst_cream->rt = BITS(inst, 12, 15); + inst_cream->rt2 = BITS(inst, 16, 19); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(mla)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mla_inst)); + mla_inst *inst_cream = (mla_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 12, 15); + inst_cream->Rd = BITS(inst, 16, 19); + inst_cream->Rs = BITS(inst, 8, 11); + inst_cream->Rm = BITS(inst, 0, 3); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(mov)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mov_inst)); + mov_inst *inst_cream = (mov_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) { + inst_base->br = TransExtData::INDIRECT_BRANCH; + } + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(mrc)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mrc_inst)); + mrc_inst *inst_cream = (mrc_inst *)inst_base->component; + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->crn = BITS(inst, 16, 19); + inst_cream->crm = BITS(inst, 0, 3); + inst_cream->opcode_1 = BITS(inst, 21, 23); + inst_cream->opcode_2 = BITS(inst, 5, 7); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->cp_num = BITS(inst, 8, 11); + inst_cream->inst = inst; + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(mcrr)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(mrs)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mrs_inst)); + mrs_inst *inst_cream = (mrs_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->R = BIT(inst, 22); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(msr)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(msr_inst)); + msr_inst *inst_cream = (msr_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->field_mask = BITS(inst, 16, 19); + inst_cream->R = BIT(inst, 22); + inst_cream->inst = inst; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(mul)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mul_inst)); + mul_inst *inst_cream = (mul_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->S = BIT(inst, 20); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rs = BITS(inst, 8, 11); + inst_cream->Rd = BITS(inst, 16, 19); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(mvn)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mvn_inst)); + mvn_inst *inst_cream = (mvn_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) { + inst_base->br = TransExtData::INDIRECT_BRANCH; + } + return inst_base; + +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(orr)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(orr_inst)); + orr_inst *inst_cream = (orr_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} + +// NOP introduced in ARMv6K. +static ARM_INST_PTR INTERPRETER_TRANSLATE(nop)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst)); + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(pkh_inst)); + pkh_inst *inst_cream = (pkh_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->imm = BITS(inst, 7, 11); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(pkhbt)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(pld_inst)); + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->op1 = BITS(inst, 21, 22); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(qadd)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(qadd)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(qadd)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->op1 = BITS(inst, 20, 21); + inst_cream->op2 = BITS(inst, 5, 7); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(qadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(qadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(qadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(qadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(qadd8)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(rev_inst)); + rev_inst* const inst_cream = (rev_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->op1 = BITS(inst, 20, 22); + inst_cream->op2 = BITS(inst, 5, 7); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(rev16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(rev)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(rev)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst* const inst_cream = (ldst_inst*)inst_base->component; + + inst_base->cond = AL; + inst_base->idx = index; + inst_base->br = TransExtData::INDIRECT_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(rsb)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rsb_inst)); + rsb_inst *inst_cream = (rsb_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(rsc)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rsc_inst)); + rsc_inst *inst_cream = (rsc_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->op1 = BITS(inst, 20, 21); + inst_cream->op2 = BITS(inst, 5, 7); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(sadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(sadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(sadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(sadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(sadd8)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sbc_inst)); + sbc_inst *inst_cream = (sbc_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->op1 = BITS(inst, 20, 22); + inst_cream->op2 = BITS(inst, 5, 7); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(setend_inst)); + setend_inst* const inst_cream = (setend_inst*)inst_base->component; + + inst_base->cond = AL; + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->set_bigend = BIT(inst, 9); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(sev)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst)); + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->op1 = BITS(inst, 20, 21); + inst_cream->op2 = BITS(inst, 5, 7); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(shadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(shadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(shadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(shadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(shadd8)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(smla)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smla_inst)); + smla_inst *inst_cream = (smla_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->x = BIT(inst, 5); + inst_cream->y = BIT(inst, 6); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rs = BITS(inst, 8, 11); + inst_cream->Rd = BITS(inst, 16, 19); + inst_cream->Rn = BITS(inst, 12, 15); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(smlad)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlad_inst)); + smlad_inst* const inst_cream = (smlad_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->m = BIT(inst, 5); + inst_cream->Rn = BITS(inst, 0, 3); + inst_cream->Rm = BITS(inst, 8, 11); + inst_cream->Rd = BITS(inst, 16, 19); + inst_cream->Ra = BITS(inst, 12, 15); + inst_cream->op1 = BITS(inst, 20, 22); + inst_cream->op2 = BITS(inst, 5, 7); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(smlad)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(smlad)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(smlad)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(smlal)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); + umlal_inst *inst_cream = (umlal_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->S = BIT(inst, 20); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rs = BITS(inst, 8, 11); + inst_cream->RdHi = BITS(inst, 16, 19); + inst_cream->RdLo = BITS(inst, 12, 15); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlalxy_inst)); + smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->x = BIT(inst, 5); + inst_cream->y = BIT(inst, 6); + inst_cream->RdLo = BITS(inst, 12, 15); + inst_cream->RdHi = BITS(inst, 16, 19); + inst_cream->Rn = BITS(inst, 0, 4); + inst_cream->Rm = BITS(inst, 8, 11); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlad_inst)); + smlad_inst* const inst_cream = (smlad_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Ra = BITS(inst, 12, 15); + inst_cream->Rm = BITS(inst, 8, 11); + inst_cream->Rn = BITS(inst, 0, 3); + inst_cream->Rd = BITS(inst, 16, 19); + inst_cream->m = BIT(inst, 6); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlald_inst)); + smlald_inst* const inst_cream = (smlald_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rm = BITS(inst, 8, 11); + inst_cream->Rn = BITS(inst, 0, 3); + inst_cream->RdLo = BITS(inst, 12, 15); + inst_cream->RdHi = BITS(inst, 16, 19); + inst_cream->swap = BIT(inst, 5); + inst_cream->op1 = BITS(inst, 20, 22); + inst_cream->op2 = BITS(inst, 5, 7); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(smlald)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlad_inst)); + smlad_inst* const inst_cream = (smlad_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->m = BIT(inst, 5); + inst_cream->Ra = BITS(inst, 12, 15); + inst_cream->Rm = BITS(inst, 8, 11); + inst_cream->Rn = BITS(inst, 0, 3); + inst_cream->Rd = BITS(inst, 16, 19); + inst_cream->op1 = BITS(inst, 20, 22); + inst_cream->op2 = BITS(inst, 5, 7); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(smmla)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(smmla)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(smul)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smul_inst)); + smul_inst *inst_cream = (smul_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rd = BITS(inst, 16, 19); + inst_cream->Rs = BITS(inst, 8, 11); + inst_cream->Rm = BITS(inst, 0, 3); + + inst_cream->x = BIT(inst, 5); + inst_cream->y = BIT(inst, 6); + + return inst_base; + +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(smull)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umull_inst)); + umull_inst *inst_cream = (umull_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->S = BIT(inst, 20); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rs = BITS(inst, 8, 11); + inst_cream->RdHi = BITS(inst, 16, 19); + inst_cream->RdLo = BITS(inst, 12, 15); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(smulw)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smlad_inst)); + smlad_inst *inst_cream = (smlad_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->m = BIT(inst, 6); + inst_cream->Rm = BITS(inst, 8, 11); + inst_cream->Rn = BITS(inst, 0, 3); + inst_cream->Rd = BITS(inst, 16, 19); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst* const inst_cream = (ldst_inst*)inst_base->component; + + inst_base->cond = AL; + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ssat_inst)); + ssat_inst* const inst_cream = (ssat_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rn = BITS(inst, 0, 3); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->imm5 = BITS(inst, 7, 11); + inst_cream->sat_imm = BITS(inst, 16, 20); + inst_cream->shift_type = BIT(inst, 6); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ssat_inst)); + ssat_inst* const inst_cream = (ssat_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rn = BITS(inst, 0, 3); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->sat_imm = BITS(inst, 16, 19); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(stc_inst)); + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(stm)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtb_inst)); + sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->rotate = BITS(inst, 10, 11); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(str)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(uxth_inst)); + uxth_inst *inst_cream = (uxth_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->rotate = BITS(inst, 10, 11); + inst_cream->Rm = BITS(inst, 0, 3); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(uxtab_inst)); + uxtab_inst *inst_cream = (uxtab_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->rotate = BITS(inst, 10, 11); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(strb)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(unsigned int inst, int index) +{ + arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst* inst_cream = (ldst_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOpLoadStoreT(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(strd)(unsigned int inst, int index){ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(strex)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst *inst_cream = (generic_arm_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->Rm = BITS(inst, 0, 3); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(strexb)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(strex)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(strexh)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(strex)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(strexd)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(strex)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(strh)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst *inst_cream = (ldst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(unsigned int inst, int index) +{ + arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst)); + ldst_inst* inst_cream = (ldst_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->inst = inst; + inst_cream->get_addr = GetAddressingOpLoadStoreT(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(sub)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sub_inst)); + sub_inst *inst_cream = (sub_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + if (inst_cream->Rd == 15) + inst_base->br = TransExtData::INDIRECT_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(swi)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(swi_inst)); + swi_inst *inst_cream = (swi_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->num = BITS(inst, 0, 23); + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(swp)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(swp_inst)); + swp_inst *inst_cream = (swp_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->Rm = BITS(inst, 0, 3); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(swpb)(unsigned int inst, int index){ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(swp_inst)); + swp_inst *inst_cream = (swp_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->Rm = BITS(inst, 0, 3); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab)(unsigned int inst, int index){ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtab_inst)); + sxtab_inst *inst_cream = (sxtab_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->rotate = BITS(inst, 10, 11); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(sxtab_inst)); + sxtab_inst* const inst_cream = (sxtab_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->rotate = BITS(inst, 10, 11); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(sxtab16)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index) { + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtah_inst)); + sxtah_inst *inst_cream = (sxtah_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->rotate = BITS(inst, 10, 11); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(teq)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(teq_inst)); + teq_inst *inst_cream = (teq_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(tst)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(tst_inst)); + tst_inst *inst_cream = (tst_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->I = BIT(inst, 25); + inst_cream->S = BIT(inst, 20); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->shifter_operand = BITS(inst, 0, 11); + inst_cream->shtop_func = GetShifterOp(inst); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->op1 = BITS(inst, 20, 21); + inst_cream->op2 = BITS(inst, 5, 7); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uadd8)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->op1 = BITS(inst, 20, 21); + inst_cream->op2 = BITS(inst, 5, 7); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uhadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uhadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uhadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uhadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uhadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(umaal_inst)); + umaal_inst* const inst_cream = (umaal_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rm = BITS(inst, 8, 11); + inst_cream->Rn = BITS(inst, 0, 3); + inst_cream->RdLo = BITS(inst, 12, 15); + inst_cream->RdHi = BITS(inst, 16, 19); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); + umlal_inst *inst_cream = (umlal_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->S = BIT(inst, 20); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rs = BITS(inst, 8, 11); + inst_cream->RdHi = BITS(inst, 16, 19); + inst_cream->RdLo = BITS(inst, 12, 15); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(umull)(unsigned int inst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umull_inst)); + umull_inst *inst_cream = (umull_inst *)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->S = BIT(inst, 20); + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rs = BITS(inst, 8, 11); + inst_cream->RdHi = BITS(inst, 16, 19); + inst_cream->RdLo = BITS(inst, 12, 15); + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(b_2_thumb)(unsigned int tinst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(b_2_thumb)); + b_2_thumb *inst_cream = (b_2_thumb *)inst_base->component; + + inst_cream->imm = ((tinst & 0x3FF) << 1) | ((tinst & (1 << 10)) ? 0xFFFFF800 : 0); + + inst_base->idx = index; + inst_base->br = TransExtData::DIRECT_BRANCH; + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(b_cond_thumb)(unsigned int tinst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(b_cond_thumb)); + b_cond_thumb *inst_cream = (b_cond_thumb *)inst_base->component; + + inst_cream->imm = (((tinst & 0x7F) << 1) | ((tinst & (1 << 7)) ? 0xFFFFFF00 : 0)); + inst_cream->cond = ((tinst >> 8) & 0xf); + inst_base->idx = index; + inst_base->br = TransExtData::DIRECT_BRANCH; + + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(bl_1_thumb)(unsigned int tinst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(bl_1_thumb)); + bl_1_thumb *inst_cream = (bl_1_thumb *)inst_base->component; + + inst_cream->imm = (((tinst & 0x07FF) << 12) | ((tinst & (1 << 10)) ? 0xFF800000 : 0)); + + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(bl_2_thumb)(unsigned int tinst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(bl_2_thumb)); + bl_2_thumb *inst_cream = (bl_2_thumb *)inst_base->component; + + inst_cream->imm = (tinst & 0x07FF) << 1; + + inst_base->idx = index; + inst_base->br = TransExtData::DIRECT_BRANCH; + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(blx_1_thumb)(unsigned int tinst, int index) +{ + arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(blx_1_thumb)); + blx_1_thumb *inst_cream = (blx_1_thumb *)inst_base->component; + + inst_cream->imm = (tinst & 0x07FF) << 1; + inst_cream->instr = tinst; + + inst_base->idx = index; + inst_base->br = TransExtData::DIRECT_BRANCH; + return inst_base; +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->op1 = BITS(inst, 20, 21); + inst_cream->op2 = BITS(inst, 5, 7); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uqadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uqadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uqadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uqadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uqadd8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); + generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->op1 = BITS(inst, 20, 24); + inst_cream->op2 = BITS(inst, 5, 7); + inst_cream->Rd = BITS(inst, 16, 19); + inst_cream->Rm = BITS(inst, 8, 11); + inst_cream->Rn = BITS(inst, 0, 3); + inst_cream->Ra = BITS(inst, 12, 15); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(usada8)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(ssat)(inst, index); +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(ssat16)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(uxtab_inst)); + uxtab_inst* const inst_cream = (uxtab_inst*)inst_base->component; + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + inst_cream->Rm = BITS(inst, 0, 3); + inst_cream->Rn = BITS(inst, 16, 19); + inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->rotate = BITS(inst, 10, 11); + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(unsigned int inst, int index) +{ + return INTERPRETER_TRANSLATE(uxtab16)(inst, index); +} + +static ARM_INST_PTR INTERPRETER_TRANSLATE(wfe)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst)); + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(wfi)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst)); + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + return inst_base; +} +static ARM_INST_PTR INTERPRETER_TRANSLATE(yield)(unsigned int inst, int index) +{ + arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst)); + + inst_base->cond = BITS(inst, 28, 31); + inst_base->idx = index; + inst_base->br = TransExtData::NON_BRANCH; + + return inst_base; +} + +// Floating point VFPv3 instructions +#define VFP_INTERPRETER_TRANS +#include "core/arm/skyeye_common/vfp/vfpinstr.cpp" +#undef VFP_INTERPRETER_TRANS + +const transop_fp_t arm_instruction_trans[] = { + INTERPRETER_TRANSLATE(vmla), + INTERPRETER_TRANSLATE(vmls), + INTERPRETER_TRANSLATE(vnmla), + INTERPRETER_TRANSLATE(vnmls), + INTERPRETER_TRANSLATE(vnmul), + INTERPRETER_TRANSLATE(vmul), + INTERPRETER_TRANSLATE(vadd), + INTERPRETER_TRANSLATE(vsub), + INTERPRETER_TRANSLATE(vdiv), + INTERPRETER_TRANSLATE(vmovi), + INTERPRETER_TRANSLATE(vmovr), + INTERPRETER_TRANSLATE(vabs), + INTERPRETER_TRANSLATE(vneg), + INTERPRETER_TRANSLATE(vsqrt), + INTERPRETER_TRANSLATE(vcmp), + INTERPRETER_TRANSLATE(vcmp2), + INTERPRETER_TRANSLATE(vcvtbds), + INTERPRETER_TRANSLATE(vcvtbff), + INTERPRETER_TRANSLATE(vcvtbfi), + INTERPRETER_TRANSLATE(vmovbrs), + INTERPRETER_TRANSLATE(vmsr), + INTERPRETER_TRANSLATE(vmovbrc), + INTERPRETER_TRANSLATE(vmrs), + INTERPRETER_TRANSLATE(vmovbcr), + INTERPRETER_TRANSLATE(vmovbrrss), + INTERPRETER_TRANSLATE(vmovbrrd), + INTERPRETER_TRANSLATE(vstr), + INTERPRETER_TRANSLATE(vpush), + INTERPRETER_TRANSLATE(vstm), + INTERPRETER_TRANSLATE(vpop), + INTERPRETER_TRANSLATE(vldr), + INTERPRETER_TRANSLATE(vldm), + + INTERPRETER_TRANSLATE(srs), + INTERPRETER_TRANSLATE(rfe), + INTERPRETER_TRANSLATE(bkpt), + INTERPRETER_TRANSLATE(blx), + INTERPRETER_TRANSLATE(cps), + INTERPRETER_TRANSLATE(pld), + INTERPRETER_TRANSLATE(setend), + INTERPRETER_TRANSLATE(clrex), + INTERPRETER_TRANSLATE(rev16), + INTERPRETER_TRANSLATE(usad8), + INTERPRETER_TRANSLATE(sxtb), + INTERPRETER_TRANSLATE(uxtb), + INTERPRETER_TRANSLATE(sxth), + INTERPRETER_TRANSLATE(sxtb16), + INTERPRETER_TRANSLATE(uxth), + INTERPRETER_TRANSLATE(uxtb16), + INTERPRETER_TRANSLATE(cpy), + INTERPRETER_TRANSLATE(uxtab), + INTERPRETER_TRANSLATE(ssub8), + INTERPRETER_TRANSLATE(shsub8), + INTERPRETER_TRANSLATE(ssubaddx), + INTERPRETER_TRANSLATE(strex), + INTERPRETER_TRANSLATE(strexb), + INTERPRETER_TRANSLATE(swp), + INTERPRETER_TRANSLATE(swpb), + INTERPRETER_TRANSLATE(ssub16), + INTERPRETER_TRANSLATE(ssat16), + INTERPRETER_TRANSLATE(shsubaddx), + INTERPRETER_TRANSLATE(qsubaddx), + INTERPRETER_TRANSLATE(shaddsubx), + INTERPRETER_TRANSLATE(shadd8), + INTERPRETER_TRANSLATE(shadd16), + INTERPRETER_TRANSLATE(sel), + INTERPRETER_TRANSLATE(saddsubx), + INTERPRETER_TRANSLATE(sadd8), + INTERPRETER_TRANSLATE(sadd16), + INTERPRETER_TRANSLATE(shsub16), + INTERPRETER_TRANSLATE(umaal), + INTERPRETER_TRANSLATE(uxtab16), + INTERPRETER_TRANSLATE(usubaddx), + INTERPRETER_TRANSLATE(usub8), + INTERPRETER_TRANSLATE(usub16), + INTERPRETER_TRANSLATE(usat16), + INTERPRETER_TRANSLATE(usada8), + INTERPRETER_TRANSLATE(uqsubaddx), + INTERPRETER_TRANSLATE(uqsub8), + INTERPRETER_TRANSLATE(uqsub16), + INTERPRETER_TRANSLATE(uqaddsubx), + INTERPRETER_TRANSLATE(uqadd8), + INTERPRETER_TRANSLATE(uqadd16), + INTERPRETER_TRANSLATE(sxtab), + INTERPRETER_TRANSLATE(uhsubaddx), + INTERPRETER_TRANSLATE(uhsub8), + INTERPRETER_TRANSLATE(uhsub16), + INTERPRETER_TRANSLATE(uhaddsubx), + INTERPRETER_TRANSLATE(uhadd8), + INTERPRETER_TRANSLATE(uhadd16), + INTERPRETER_TRANSLATE(uaddsubx), + INTERPRETER_TRANSLATE(uadd8), + INTERPRETER_TRANSLATE(uadd16), + INTERPRETER_TRANSLATE(sxtah), + INTERPRETER_TRANSLATE(sxtab16), + INTERPRETER_TRANSLATE(qadd8), + INTERPRETER_TRANSLATE(bxj), + INTERPRETER_TRANSLATE(clz), + INTERPRETER_TRANSLATE(uxtah), + INTERPRETER_TRANSLATE(bx), + INTERPRETER_TRANSLATE(rev), + INTERPRETER_TRANSLATE(blx), + INTERPRETER_TRANSLATE(revsh), + INTERPRETER_TRANSLATE(qadd), + INTERPRETER_TRANSLATE(qadd16), + INTERPRETER_TRANSLATE(qaddsubx), + INTERPRETER_TRANSLATE(ldrex), + INTERPRETER_TRANSLATE(qdadd), + INTERPRETER_TRANSLATE(qdsub), + INTERPRETER_TRANSLATE(qsub), + INTERPRETER_TRANSLATE(ldrexb), + INTERPRETER_TRANSLATE(qsub8), + INTERPRETER_TRANSLATE(qsub16), + INTERPRETER_TRANSLATE(smuad), + INTERPRETER_TRANSLATE(smmul), + INTERPRETER_TRANSLATE(smusd), + INTERPRETER_TRANSLATE(smlsd), + INTERPRETER_TRANSLATE(smlsld), + INTERPRETER_TRANSLATE(smmla), + INTERPRETER_TRANSLATE(smmls), + INTERPRETER_TRANSLATE(smlald), + INTERPRETER_TRANSLATE(smlad), + INTERPRETER_TRANSLATE(smlaw), + INTERPRETER_TRANSLATE(smulw), + INTERPRETER_TRANSLATE(pkhtb), + INTERPRETER_TRANSLATE(pkhbt), + INTERPRETER_TRANSLATE(smul), + INTERPRETER_TRANSLATE(smlalxy), + INTERPRETER_TRANSLATE(smla), + INTERPRETER_TRANSLATE(mcrr), + INTERPRETER_TRANSLATE(mrrc), + INTERPRETER_TRANSLATE(cmp), + INTERPRETER_TRANSLATE(tst), + INTERPRETER_TRANSLATE(teq), + INTERPRETER_TRANSLATE(cmn), + INTERPRETER_TRANSLATE(smull), + INTERPRETER_TRANSLATE(umull), + INTERPRETER_TRANSLATE(umlal), + INTERPRETER_TRANSLATE(smlal), + INTERPRETER_TRANSLATE(mul), + INTERPRETER_TRANSLATE(mla), + INTERPRETER_TRANSLATE(ssat), + INTERPRETER_TRANSLATE(usat), + INTERPRETER_TRANSLATE(mrs), + INTERPRETER_TRANSLATE(msr), + INTERPRETER_TRANSLATE(and), + INTERPRETER_TRANSLATE(bic), + INTERPRETER_TRANSLATE(ldm), + INTERPRETER_TRANSLATE(eor), + INTERPRETER_TRANSLATE(add), + INTERPRETER_TRANSLATE(rsb), + INTERPRETER_TRANSLATE(rsc), + INTERPRETER_TRANSLATE(sbc), + INTERPRETER_TRANSLATE(adc), + INTERPRETER_TRANSLATE(sub), + INTERPRETER_TRANSLATE(orr), + INTERPRETER_TRANSLATE(mvn), + INTERPRETER_TRANSLATE(mov), + INTERPRETER_TRANSLATE(stm), + INTERPRETER_TRANSLATE(ldm), + INTERPRETER_TRANSLATE(ldrsh), + INTERPRETER_TRANSLATE(stm), + INTERPRETER_TRANSLATE(ldm), + INTERPRETER_TRANSLATE(ldrsb), + INTERPRETER_TRANSLATE(strd), + INTERPRETER_TRANSLATE(ldrh), + INTERPRETER_TRANSLATE(strh), + INTERPRETER_TRANSLATE(ldrd), + INTERPRETER_TRANSLATE(strt), + INTERPRETER_TRANSLATE(strbt), + INTERPRETER_TRANSLATE(ldrbt), + INTERPRETER_TRANSLATE(ldrt), + INTERPRETER_TRANSLATE(mrc), + INTERPRETER_TRANSLATE(mcr), + INTERPRETER_TRANSLATE(msr), + INTERPRETER_TRANSLATE(msr), + INTERPRETER_TRANSLATE(msr), + INTERPRETER_TRANSLATE(msr), + INTERPRETER_TRANSLATE(msr), + INTERPRETER_TRANSLATE(ldrb), + INTERPRETER_TRANSLATE(strb), + INTERPRETER_TRANSLATE(ldr), + INTERPRETER_TRANSLATE(ldrcond), + INTERPRETER_TRANSLATE(str), + INTERPRETER_TRANSLATE(cdp), + INTERPRETER_TRANSLATE(stc), + INTERPRETER_TRANSLATE(ldc), + INTERPRETER_TRANSLATE(ldrexd), + INTERPRETER_TRANSLATE(strexd), + INTERPRETER_TRANSLATE(ldrexh), + INTERPRETER_TRANSLATE(strexh), + INTERPRETER_TRANSLATE(nop), + INTERPRETER_TRANSLATE(yield), + INTERPRETER_TRANSLATE(wfe), + INTERPRETER_TRANSLATE(wfi), + INTERPRETER_TRANSLATE(sev), + INTERPRETER_TRANSLATE(swi), + INTERPRETER_TRANSLATE(bbl), + + // All the thumb instructions should be placed the end of table + INTERPRETER_TRANSLATE(b_2_thumb), + INTERPRETER_TRANSLATE(b_cond_thumb), + INTERPRETER_TRANSLATE(bl_1_thumb), + INTERPRETER_TRANSLATE(bl_2_thumb), + INTERPRETER_TRANSLATE(blx_1_thumb) +}; + +const size_t arm_instruction_trans_len = sizeof(arm_instruction_trans) / sizeof(transop_fp_t); diff --git a/src/core/arm/dyncom/arm_dyncom_trans.h b/src/core/arm/dyncom/arm_dyncom_trans.h new file mode 100644 index 000000000..7af71f4e3 --- /dev/null +++ b/src/core/arm/dyncom/arm_dyncom_trans.h @@ -0,0 +1,493 @@ +struct ARMul_State; +typedef unsigned int (*shtop_fp_t)(ARMul_State* cpu, unsigned int sht_oper); + +enum class TransExtData { + COND = (1 << 0), + NON_BRANCH = (1 << 1), + DIRECT_BRANCH = (1 << 2), + INDIRECT_BRANCH = (1 << 3), + CALL = (1 << 4), + RET = (1 << 5), + END_OF_PAGE = (1 << 6), + THUMB = (1 << 7), + SINGLE_STEP = (1 << 8) +}; + +struct arm_inst { + unsigned int idx; + unsigned int cond; + TransExtData br; + char component[0]; +}; + +struct generic_arm_inst { + u32 Ra; + u32 Rm; + u32 Rn; + u32 Rd; + u8 op1; + u8 op2; +}; + +struct adc_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct add_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct orr_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct and_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct eor_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct bbl_inst { + unsigned int L; + int signed_immed_24; + unsigned int next_addr; + unsigned int jmp_addr; +}; + +struct bx_inst { + unsigned int Rm; +}; + +struct blx_inst { + union { + s32 signed_immed_24; + u32 Rm; + } val; + unsigned int inst; +}; + +struct clz_inst { + unsigned int Rm; + unsigned int Rd; +}; + +struct cps_inst { + unsigned int imod0; + unsigned int imod1; + unsigned int mmod; + unsigned int A, I, F; + unsigned int mode; +}; + +struct clrex_inst { +}; + +struct cpy_inst { + unsigned int Rm; + unsigned int Rd; +}; + +struct bic_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct sub_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct tst_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct cmn_inst { + unsigned int I; + unsigned int Rn; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct teq_inst { + unsigned int I; + unsigned int Rn; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct stm_inst { + unsigned int inst; +}; + +struct bkpt_inst { + u32 imm; +}; + +struct stc_inst { +}; + +struct ldc_inst { +}; + +struct swi_inst { + unsigned int num; +}; + +struct cmp_inst { + unsigned int I; + unsigned int Rn; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct mov_inst { + unsigned int I; + unsigned int S; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct mvn_inst { + unsigned int I; + unsigned int S; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct rev_inst { + unsigned int Rd; + unsigned int Rm; + unsigned int op1; + unsigned int op2; +}; + +struct rsb_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct rsc_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct sbc_inst { + unsigned int I; + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int shifter_operand; + shtop_fp_t shtop_func; +}; + +struct mul_inst { + unsigned int S; + unsigned int Rd; + unsigned int Rs; + unsigned int Rm; +}; + +struct smul_inst { + unsigned int Rd; + unsigned int Rs; + unsigned int Rm; + unsigned int x; + unsigned int y; +}; + +struct umull_inst { + unsigned int S; + unsigned int RdHi; + unsigned int RdLo; + unsigned int Rs; + unsigned int Rm; +}; + +struct smlad_inst { + unsigned int m; + unsigned int Rm; + unsigned int Rd; + unsigned int Ra; + unsigned int Rn; + unsigned int op1; + unsigned int op2; +}; + +struct smla_inst { + unsigned int x; + unsigned int y; + unsigned int Rm; + unsigned int Rd; + unsigned int Rs; + unsigned int Rn; +}; + +struct smlalxy_inst { + unsigned int x; + unsigned int y; + unsigned int RdLo; + unsigned int RdHi; + unsigned int Rm; + unsigned int Rn; +}; + +struct ssat_inst { + unsigned int Rn; + unsigned int Rd; + unsigned int imm5; + unsigned int sat_imm; + unsigned int shift_type; +}; + +struct umaal_inst { + unsigned int Rn; + unsigned int Rm; + unsigned int RdHi; + unsigned int RdLo; +}; + +struct umlal_inst { + unsigned int S; + unsigned int Rm; + unsigned int Rs; + unsigned int RdHi; + unsigned int RdLo; +}; + +struct smlal_inst { + unsigned int S; + unsigned int Rm; + unsigned int Rs; + unsigned int RdHi; + unsigned int RdLo; +}; + +struct smlald_inst { + unsigned int RdLo; + unsigned int RdHi; + unsigned int Rm; + unsigned int Rn; + unsigned int swap; + unsigned int op1; + unsigned int op2; +}; + +struct mla_inst { + unsigned int S; + unsigned int Rn; + unsigned int Rd; + unsigned int Rs; + unsigned int Rm; +}; + +struct mrc_inst { + unsigned int opcode_1; + unsigned int opcode_2; + unsigned int cp_num; + unsigned int crn; + unsigned int crm; + unsigned int Rd; + unsigned int inst; +}; + +struct mcr_inst { + unsigned int opcode_1; + unsigned int opcode_2; + unsigned int cp_num; + unsigned int crn; + unsigned int crm; + unsigned int Rd; + unsigned int inst; +}; + +struct mcrr_inst { + unsigned int opcode_1; + unsigned int cp_num; + unsigned int crm; + unsigned int rt; + unsigned int rt2; +}; + +struct mrs_inst { + unsigned int R; + unsigned int Rd; +}; + +struct msr_inst { + unsigned int field_mask; + unsigned int R; + unsigned int inst; +}; + +struct pld_inst { +}; + +struct sxtb_inst { + unsigned int Rd; + unsigned int Rm; + unsigned int rotate; +}; + +struct sxtab_inst { + unsigned int Rd; + unsigned int Rn; + unsigned int Rm; + unsigned rotate; +}; + +struct sxtah_inst { + unsigned int Rd; + unsigned int Rn; + unsigned int Rm; + unsigned int rotate; +}; + +struct sxth_inst { + unsigned int Rd; + unsigned int Rm; + unsigned int rotate; +}; + +struct uxtab_inst { + unsigned int Rn; + unsigned int Rd; + unsigned int rotate; + unsigned int Rm; +}; + +struct uxtah_inst { + unsigned int Rn; + unsigned int Rd; + unsigned int rotate; + unsigned int Rm; +}; + +struct uxth_inst { + unsigned int Rd; + unsigned int Rm; + unsigned int rotate; +}; + +struct cdp_inst { + unsigned int opcode_1; + unsigned int CRn; + unsigned int CRd; + unsigned int cp_num; + unsigned int opcode_2; + unsigned int CRm; + unsigned int inst; +}; + +struct uxtb_inst { + unsigned int Rd; + unsigned int Rm; + unsigned int rotate; +}; + +struct swp_inst { + unsigned int Rn; + unsigned int Rd; + unsigned int Rm; +}; + +struct setend_inst { + unsigned int set_bigend; +}; + +struct b_2_thumb { + unsigned int imm; +}; +struct b_cond_thumb { + unsigned int imm; + unsigned int cond; +}; + +struct bl_1_thumb { + unsigned int imm; +}; +struct bl_2_thumb { + unsigned int imm; +}; +struct blx_1_thumb { + unsigned int imm; + unsigned int instr; +}; + +struct pkh_inst { + unsigned int Rm; + unsigned int Rn; + unsigned int Rd; + unsigned char imm; +}; + +// Floating point VFPv3 structures +#define VFP_INTERPRETER_STRUCT +#include "core/arm/skyeye_common/vfp/vfpinstr.cpp" +#undef VFP_INTERPRETER_STRUCT + +typedef void (*get_addr_fp_t)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr); + +struct ldst_inst { + unsigned int inst; + get_addr_fp_t get_addr; +}; + +typedef arm_inst* ARM_INST_PTR; +typedef ARM_INST_PTR (*transop_fp_t)(unsigned int, int); + +extern const transop_fp_t arm_instruction_trans[]; +extern const size_t arm_instruction_trans_len; + +#define TRANS_CACHE_SIZE (64 * 1024 * 2000) +extern char trans_cache_buf[TRANS_CACHE_SIZE]; +extern size_t trans_cache_buf_top; diff --git a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp index 4f9083515..1a98d0114 100644 --- a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp @@ -26,7 +26,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmla)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -75,7 +75,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmls)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -124,7 +124,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vnmla)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -174,7 +174,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vnmls)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -223,7 +223,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vnmul)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -272,7 +272,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmul)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -321,7 +321,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vadd)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -370,7 +370,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vsub)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -419,7 +419,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vdiv)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -470,7 +470,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovi)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->single = BIT(inst, 8) == 0; inst_cream->d = (inst_cream->single ? BITS(inst,12,15)<<1 | BIT(inst,22) : BITS(inst,12,15) | BIT(inst,22)<<4); @@ -518,7 +518,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovr)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->single = BIT(inst, 8) == 0; inst_cream->d = (inst_cream->single ? BITS(inst,12,15)<<1 | BIT(inst,22) : BITS(inst,12,15) | BIT(inst,22)<<4); @@ -560,7 +560,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vabs)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -610,7 +610,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vneg)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -659,7 +659,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vsqrt)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -708,7 +708,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vcmp)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -757,7 +757,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vcmp2)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -806,7 +806,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vcvtbds)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -857,7 +857,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vcvtbff)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -906,7 +906,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vcvtbfi)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->dp_operation = BIT(inst, 8); inst_cream->instr = inst; @@ -962,7 +962,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrs)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->to_arm = BIT(inst, 20) == 1; inst_cream->t = BITS(inst, 12, 15); @@ -1006,7 +1006,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmsr)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->reg = BITS(inst, 16, 19); inst_cream->Rt = BITS(inst, 12, 15); @@ -1069,7 +1069,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrc)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->d = BITS(inst, 16, 19)|BIT(inst, 7)<<4; inst_cream->t = BITS(inst, 12, 15); @@ -1115,7 +1115,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmrs)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->reg = BITS(inst, 16, 19); inst_cream->Rt = BITS(inst, 12, 15); @@ -1200,7 +1200,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbcr)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->d = BITS(inst, 16, 19)|BIT(inst, 7)<<4; inst_cream->t = BITS(inst, 12, 15); @@ -1253,7 +1253,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrrss)(unsigned int inst, int inde inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->to_arm = BIT(inst, 20) == 1; inst_cream->t = BITS(inst, 12, 15); @@ -1301,7 +1301,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrrd)(unsigned int inst, int index inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->to_arm = BIT(inst, 20) == 1; inst_cream->t = BITS(inst, 12, 15); @@ -1354,7 +1354,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vstr)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->single = BIT(inst, 8) == 0; inst_cream->add = BIT(inst, 23); @@ -1420,7 +1420,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vpush)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->single = BIT(inst, 8) == 0; inst_cream->d = (inst_cream->single ? BITS(inst, 12, 15)<<1|BIT(inst, 22) : BITS(inst, 12, 15)|BIT(inst, 22)<<4); @@ -1495,7 +1495,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vstm)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->single = BIT(inst, 8) == 0; inst_cream->add = BIT(inst, 23); @@ -1580,7 +1580,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vpop)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->single = BIT(inst, 8) == 0; inst_cream->d = (inst_cream->single ? (BITS(inst, 12, 15)<<1)|BIT(inst, 22) : BITS(inst, 12, 15)|(BIT(inst, 22)<<4)); @@ -1653,7 +1653,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vldr)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->single = BIT(inst, 8) == 0; inst_cream->add = BIT(inst, 23); @@ -1722,7 +1722,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vldm)(unsigned int inst, int index) inst_base->cond = BITS(inst, 28, 31); inst_base->idx = index; - inst_base->br = NON_BRANCH; + inst_base->br = TransExtData::NON_BRANCH; inst_cream->single = BIT(inst, 8) == 0; inst_cream->add = BIT(inst, 23); diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 64f5b06d9..3d8a7d0c0 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -26,7 +26,7 @@ extern int g_clock_rate_arm11; inline s64 msToCycles(int ms) { - return g_clock_rate_arm11 / 1000 * ms; + return (s64)g_clock_rate_arm11 / 1000 * ms; } inline s64 msToCycles(float ms) { diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index 90e134437..ccf35fa07 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -12,6 +12,7 @@ #include "core/core_timing.h" #include "core/hle/applets/applet.h" +#include "core/hle/applets/erreula.h" #include "core/hle/applets/mii_selector.h" #include "core/hle/applets/swkbd.h" #include "core/hle/result.h" @@ -52,6 +53,10 @@ ResultCode Applet::Create(Service::APT::AppletId id) { case Service::APT::AppletId::Ed2: applets[id] = std::make_shared<MiiSelector>(id); break; + case Service::APT::AppletId::Error: + case Service::APT::AppletId::Error2: + applets[id] = std::make_shared<ErrEula>(id); + break; default: LOG_ERROR(Service_APT, "Could not create applet %u", id); // TODO(Subv): Find the right error code diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp new file mode 100644 index 000000000..92a4b2323 --- /dev/null +++ b/src/core/hle/applets/erreula.cpp @@ -0,0 +1,72 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/string_util.h" + +#include "core/hle/applets/erreula.h" +#include "core/hle/service/apt/apt.h" + +namespace HLE { +namespace Applets { + +ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { + LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); + UNIMPLEMENTED(); + // TODO(Subv): Find the right error code + return ResultCode(-1); + } + + // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory. + // Create the SharedMemory that will hold the framebuffer data + Service::APT::CaptureBufferInfo capture_info; + ASSERT(sizeof(capture_info) == parameter.buffer.size()); + + memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info)); + + // TODO: allocated memory never released + using Kernel::MemoryPermission; + // Allocate a heap block of the required size for this applet. + heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); + // Create a SharedMemory that directly points to this heap block. + framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(), + MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + "ErrEula Memory"); + + // Send the response message with the newly created SharedMemory + Service::APT::MessageParameter result; + result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); + result.buffer.clear(); + result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + result.sender_id = static_cast<u32>(id); + result.object = framebuffer_memory; + + Service::APT::SendParameter(result); + return RESULT_SUCCESS; +} + +ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parameter) { + started = true; + + // TODO(Subv): Set the expected fields in the response buffer before resending it to the application. + // TODO(Subv): Reverse the parameter format for the ErrEula applet + + // Let the application know that we're closing + Service::APT::MessageParameter message; + message.buffer.resize(parameter.buffer.size()); + std::fill(message.buffer.begin(), message.buffer.end(), 0); + message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); + message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + message.sender_id = static_cast<u32>(id); + Service::APT::SendParameter(message); + + started = false; + return RESULT_SUCCESS; +} + +void ErrEula::Update() { +} + +} // namespace Applets +} // namespace HLE diff --git a/src/core/hle/applets/erreula.h b/src/core/hle/applets/erreula.h new file mode 100644 index 000000000..9fe72ae07 --- /dev/null +++ b/src/core/hle/applets/erreula.h @@ -0,0 +1,31 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/applets/applet.h" +#include "core/hle/kernel/shared_memory.h" + +namespace HLE { +namespace Applets { + +class ErrEula final : public Applet { +public: + explicit ErrEula(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; } + + /// 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 +} // namespace HLE diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index bf7f875b6..8839ce482 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -194,6 +194,16 @@ template<ResultCode func(Handle, u32)> void Wrap() { FuncReturn(func(PARAM(0), PARAM(1)).raw); } +template<ResultCode func(Handle*, Handle*, const char*, u32)> void Wrap() { + Handle param_1 = 0; + 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); + FuncReturn(retval); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type u32 diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp new file mode 100644 index 000000000..444ce8d45 --- /dev/null +++ b/src/core/hle/kernel/client_port.cpp @@ -0,0 +1,16 @@ +// 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_port.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/server_port.h" + +namespace Kernel { + +ClientPort::ClientPort() {} +ClientPort::~ClientPort() {} + +} // namespace diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h new file mode 100644 index 000000000..480b6ddae --- /dev/null +++ b/src/core/hle/kernel/client_port.h @@ -0,0 +1,36 @@ +// 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/common_types.h" + +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +class ServerPort; + +class ClientPort : public Object { +public: + friend class ServerPort; + std::string GetTypeName() const override { return "ClientPort"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::ClientPort; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + 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: + ClientPort(); + ~ClientPort() override; +}; + +} // namespace diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 4d4276f7a..27ba3f912 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -35,7 +35,7 @@ enum KernelHandle : Handle { enum class HandleType : u32 { Unknown = 0, - Port = 1, + Session = 2, Event = 3, Mutex = 4, @@ -48,6 +48,8 @@ enum class HandleType : u32 { Timer = 11, ResourceLimit = 12, CodeSet = 13, + ClientPort = 14, + ServerPort = 15, }; enum { @@ -72,6 +74,7 @@ public: bool IsWaitable() const { switch (GetHandleType()) { case HandleType::Session: + case HandleType::ServerPort: case HandleType::Event: case HandleType::Mutex: case HandleType::Thread: @@ -80,13 +83,13 @@ public: return true; case HandleType::Unknown: - case HandleType::Port: case HandleType::SharedMemory: case HandleType::Redirection: case HandleType::Process: case HandleType::AddressArbiter: case HandleType::ResourceLimit: case HandleType::CodeSet: + case HandleType::ClientPort: return false; } } diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp new file mode 100644 index 000000000..fcc684a20 --- /dev/null +++ b/src/core/hle/kernel/server_port.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 <tuple> + +#include "common/assert.h" + +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/server_port.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +ServerPort::ServerPort() {} +ServerPort::~ServerPort() {} + +bool ServerPort::ShouldWait() { + // If there are no pending sessions, we wait until a new one is added. + return pending_sessions.size() == 0; +} + +void ServerPort::Acquire() { + ASSERT_MSG(!ShouldWait(), "object unavailable!"); +} + +std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair(u32 max_sessions, std::string name) { + SharedPtr<ServerPort> server_port(new ServerPort); + SharedPtr<ClientPort> client_port(new ClientPort); + + server_port->name = name + "_Server"; + client_port->name = name + "_Client"; + client_port->server_port = server_port; + client_port->max_sessions = max_sessions; + client_port->active_sessions = 0; + + return std::make_tuple(std::move(server_port), std::move(client_port)); +} + +} // namespace diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h new file mode 100644 index 000000000..e9c972ce6 --- /dev/null +++ b/src/core/hle/kernel/server_port.h @@ -0,0 +1,46 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <tuple> + +#include "common/common_types.h" + +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +class ClientPort; + +class ServerPort final : public WaitObject { +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 + * @return The created port tuple + */ + static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair(u32 max_sessions, std::string name = "UnknownPort"); + + std::string GetTypeName() const override { return "ServerPort"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::ServerPort; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + std::string name; ///< Name of port (optional) + + std::vector<SharedPtr<WaitObject>> pending_sessions; ///< ServerSessions waiting to be accepted by the port + + bool ShouldWait() override; + void Acquire() override; + +private: + ServerPort(); + ~ServerPort() override; +}; + +} // namespace diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 6ddaf970e..8ec889967 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -16,38 +16,130 @@ namespace IPC { -constexpr u32 MakeHeader(u16 command_id, unsigned int regular_params, unsigned int translate_params) { - return ((u32)command_id << 16) | (((u32)regular_params & 0x3F) << 6) | (((u32)translate_params & 0x3F) << 0); +enum DescriptorType : u32 { + // Buffer related desciptors types (mask : 0x0F) + StaticBuffer = 0x02, + PXIBuffer = 0x04, + MappedBuffer = 0x08, + // Handle related descriptors types (mask : 0x30, but need to check for buffer related descriptors first ) + CopyHandle = 0x00, + MoveHandle = 0x10, + CallingPid = 0x20, +}; + +/** + * @brief Creates a command header to be used for IPC + * @param command_id ID of the command to create a header for. + * @param normal_params Size of the normal parameters in words. Up to 63. + * @param translate_params_size Size of the translate parameters in words. Up to 63. + * @return The created IPC header. + * + * Normal parameters are sent directly to the process while the translate parameters might go through modifications and checks by the kernel. + * The translate parameters are described by headers generated with the IPC::*Desc functions. + * + * @note While #normal_params is equivalent to the number of normal parameters, #translate_params_size includes the size occupied by the translate parameters headers. + */ +constexpr u32 MakeHeader(u16 command_id, unsigned int normal_params, unsigned int translate_params_size) { + return (u32(command_id) << 16) | ((u32(normal_params) & 0x3F) << 6) | (u32(translate_params_size) & 0x3F); } -constexpr u32 MoveHandleDesc(unsigned int num_handles = 1) { - return 0x0 | ((num_handles - 1) << 26); +union Header { + u32 raw; + BitField< 0, 6, u32> translate_params_size; + BitField< 6, 6, u32> normal_params; + BitField<16, 16, u32> command_id; +}; + +inline Header ParseHeader(u32 header) { + return{ header }; } -constexpr u32 CopyHandleDesc(unsigned int num_handles = 1) { - return 0x10 | ((num_handles - 1) << 26); +constexpr u32 MoveHandleDesc(u32 num_handles = 1) { + return MoveHandle | ((num_handles - 1) << 26); +} + +constexpr u32 CopyHandleDesc(u32 num_handles = 1) { + return CopyHandle | ((num_handles - 1) << 26); } constexpr u32 CallingPidDesc() { - return 0x20; + return CallingPid; } -constexpr u32 StaticBufferDesc(u32 size, unsigned int buffer_id) { - return 0x2 | (size << 14) | ((buffer_id & 0xF) << 10); +constexpr bool isHandleDescriptor(u32 descriptor) { + return (descriptor & 0xF) == 0x0; +} + +constexpr u32 HandleNumberFromDesc(u32 handle_descriptor) { + return (handle_descriptor >> 26) + 1; +} + +constexpr u32 StaticBufferDesc(u32 size, u8 buffer_id) { + return StaticBuffer | (size << 14) | ((buffer_id & 0xF) << 10); +} + +union StaticBufferDescInfo { + u32 raw; + BitField< 10, 4, u32> buffer_id; + BitField< 14, 18, u32> size; +}; + +inline StaticBufferDescInfo ParseStaticBufferDesc(const u32 desc) { + return{ desc }; +} + +/** + * @brief Creates a header describing a buffer to be sent over PXI. + * @param size Size of the buffer. Max 0x00FFFFFF. + * @param buffer_id The Id of the buffer. Max 0xF. + * @param is_read_only true if the buffer is read-only. If false, the buffer is considered to have read-write access. + * @return The created PXI buffer header. + * + * The next value is a phys-address of a table located in the BASE memregion. + */ +inline u32 PXIBufferDesc(u32 size, unsigned buffer_id, bool is_read_only) { + u32 type = PXIBuffer; + if (is_read_only) type |= 0x2; + return type | (size << 8) | ((buffer_id & 0xF) << 4); } enum MappedBufferPermissions { - R = 2, - W = 4, + R = 1, + W = 2, RW = R | W, }; constexpr u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { - return 0x8 | (size << 4) | (u32)perms; + return MappedBuffer | (size << 4) | (u32(perms) << 1); } +union MappedBufferDescInfo { + u32 raw; + BitField< 4, 28, u32> size; + BitField< 1, 2, MappedBufferPermissions> perms; +}; + +inline MappedBufferDescInfo ParseMappedBufferDesc(const u32 desc) { + return{ desc }; } +inline DescriptorType GetDescriptorType(u32 descriptor) { + // Note: Those checks must be done in this order + if (isHandleDescriptor(descriptor)) + return (DescriptorType)(descriptor & 0x30); + + // handle the fact that the following descriptors can have rights + if (descriptor & MappedBuffer) + return MappedBuffer; + + if (descriptor & PXIBuffer) + return PXIBuffer; + + return StaticBuffer; +} + +} // namespace IPC + namespace Kernel { static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header @@ -60,7 +152,7 @@ static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of * @param offset Optional offset into command buffer * @return Pointer to command buffer */ -inline static u32* GetCommandBuffer(const int offset = 0) { +inline u32* GetCommandBuffer(const int offset = 0) { return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + offset); } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 3f6bec5fa..9dea995f4 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -181,6 +181,48 @@ 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 paramter 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 @@ -219,6 +261,13 @@ static void SwitchContext(Thread* new_thread) { // 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 @@ -542,8 +591,12 @@ void Reschedule() { HLE::DoneRescheduling(); - // Don't bother switching to the same thread - if (next == cur) + // 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) { diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 57dedcb22..268a8dad2 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -20,6 +20,7 @@ enum class ErrorDescription : u32 { WrongPermission = 46, OS_InvalidBufferDescriptor = 48, WrongAddress = 53, + FS_ArchiveNotMounted = 101, FS_NotFound = 120, FS_AlreadyExists = 190, FS_InvalidOpenFlags = 230, @@ -135,15 +136,28 @@ enum class ErrorModule : u32 { MCU = 72, NS = 73, News = 74, - RO_1 = 75, + RO = 75, GD = 76, CardSPI = 77, EC = 78, - RO_2 = 79, - WebBrowser = 80, - Test = 81, - ENC = 82, - PIA = 83, + WebBrowser = 79, + Test = 80, + ENC = 81, + PIA = 82, + ACT = 83, + VCTL = 84, + OLV = 85, + NEIA = 86, + NPNS = 87, + + AVD = 90, + L2B = 91, + MVD = 92, + NFC = 93, + UART = 94, + SPM = 95, + QTM = 96, + NFP = 97, Application = 254, InvalidResult = 255 diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 1e54a53dd..4d2956638 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -37,6 +37,8 @@ static u32 cpu_percent; ///< CPU time available to the running application // APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode static u8 unknown_ns_state_field; +static ScreencapPostPermission screen_capture_post_permission; + /// Parameter data to be returned in the next call to Glance/ReceiveParameter static MessageParameter next_parameter; @@ -51,7 +53,7 @@ void Initialize(Service::Interface* self) { u32 app_id = cmd_buff[1]; u32 flags = cmd_buff[2]; - cmd_buff[2] = IPC::MoveHandleDesc(2); + cmd_buff[2] = IPC::CopyHandleDesc(2); cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); cmd_buff[4] = Kernel::g_handle_table.Create(parameter_event).MoveFrom(); @@ -70,15 +72,17 @@ void Initialize(Service::Interface* self) { void GetSharedFont(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + if (!shared_font_mem) { + LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); + cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); + cmd_buff[1] = -1; // TODO: Find the right error code + return; + } + // The shared font has to be relocated to the new address before being passed to the application. VAddr target_address = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address); - // The shared font dumped by 3dsutils (https://github.com/citra-emu/3dsutils) uses this address as base, - // so we relocate it from there to our real address. - // TODO(Subv): This address is wrong if the shared font is dumped from a n3DS, - // we need a way to automatically calculate the original address of the font from the file. - static const VAddr SHARED_FONT_VADDR = 0x18000000; if (!shared_font_relocated) { - BCFNT::RelocateSharedFont(shared_font_mem, SHARED_FONT_VADDR, target_address); + BCFNT::RelocateSharedFont(shared_font_mem, target_address); shared_font_relocated = true; } cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); @@ -87,7 +91,7 @@ void GetSharedFont(Service::Interface* self) { // the real APT service calculates this address by scanning the entire address space (using svcQueryMemory) // and searches for an allocation of the same size as the Shared Font. cmd_buff[2] = target_address; - cmd_buff[3] = IPC::MoveHandleDesc(); + cmd_buff[3] = IPC::CopyHandleDesc(); cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); } @@ -382,23 +386,23 @@ void StartLibraryApplet(Service::Interface* self) { cmd_buff[1] = applet->Start(parameter).raw; } -void SetNSStateField(Service::Interface* self) { +void SetScreenCapPostPermission(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - unknown_ns_state_field = cmd_buff[1]; + screen_capture_post_permission = static_cast<ScreencapPostPermission>(cmd_buff[1] & 0xF); cmd_buff[0] = IPC::MakeHeader(0x55, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_APT, "(STUBBED) unknown_ns_state_field=%u", unknown_ns_state_field); + LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", screen_capture_post_permission); } -void GetNSStateField(Service::Interface* self) { +void GetScreenCapPostPermission(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[0] = IPC::MakeHeader(0x56, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[8] = unknown_ns_state_field; - LOG_WARNING(Service_APT, "(STUBBED) unknown_ns_state_field=%u", unknown_ns_state_field); + cmd_buff[2] = static_cast<u32>(screen_capture_post_permission); + LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", screen_capture_post_permission); } void GetAppletInfo(Service::Interface* self) { @@ -493,6 +497,7 @@ void Init() { cpu_percent = 0; unknown_ns_state_field = 0; + screen_capture_post_permission = ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value // TODO(bunnei): Check if these are created in Initialize or on APT process startup. notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 76b3a3807..077a6a316 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -66,6 +66,8 @@ enum class AppletId : u32 { InstructionManual = 0x115, Notifications = 0x116, Miiverse = 0x117, + MiiversePost = 0x118, + AmiiboSettings = 0x119, SoftwareKeyboard1 = 0x201, Ed1 = 0x202, PnoteApp = 0x204, @@ -78,6 +80,12 @@ enum class AppletId : u32 { AnyLibraryApplet = 0x400, SoftwareKeyboard2 = 0x401, Ed2 = 0x402, + PnoteApp2 = 0x404, + SnoteApp2 = 0x405, + Error2 = 0x406, + Mint2 = 0x407, + Extrapad2 = 0x408, + Memolib2 = 0x409, }; enum class StartupArgumentType : u32 { @@ -86,6 +94,13 @@ enum class StartupArgumentType : u32 { OtherMedia = 2, }; +enum class ScreencapPostPermission : u32 { + CleanThePermission = 0, //TODO(JamePeng): verify what "zero" means + NoExplicitSetting = 1, + EnableScreenshotPostingToMiiverse = 2, + DisableScreenshotPostingToMiiverse = 3 +}; + /// Send a parameter to the currently-running application, which will read it via ReceiveParameter void SendParameter(const MessageParameter& parameter); @@ -375,25 +390,24 @@ void StartLibraryApplet(Service::Interface* self); void GetStartupArgument(Service::Interface* self); /** - * APT::SetNSStateField service function + * APT::SetScreenCapPostPermission service function * Inputs: - * 1 : u8 NS state field + * 0 : Header Code[0x00550040] + * 1 : u8 The screenshot posting permission * Outputs: * 1 : Result of function, 0 on success, otherwise error code - * Note: - * This writes the input u8 to a NS state field. */ -void SetNSStateField(Service::Interface* self); +void SetScreenCapPostPermission(Service::Interface* self); /** - * APT::GetNSStateField service function + * APT::GetScreenCapPostPermission service function + * Inputs: + * 0 : Header Code[0x00560000] * Outputs: * 1 : Result of function, 0 on success, otherwise error code - * 8 : u8 NS state field - * Note: - * This returns a u8 NS state field(which can be set by cmd 0x00550040), at cmdreply+8. + * 2 : u8 The screenshot posting permission */ -void GetNSStateField(Service::Interface* self); +void GetScreenCapPostPermission(Service::Interface* self); /** * APT::CheckNew3DSApp service function diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 223c0a8bd..6c44c491c 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -33,8 +33,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, {0x00510080, GetStartupArgument, "GetStartupArgument"}, - {0x00550040, SetNSStateField, "SetNSStateField?"}, - {0x00560000, GetNSStateField, "GetNSStateField?"}, + {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"}, + {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"}, {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, {0x01020000, CheckNew3DS, "CheckNew3DS"} }; diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index f5c52fa3d..c70f2201f 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -92,8 +92,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00520104, nullptr, "Wrap1"}, {0x00530104, nullptr, "Unwrap1"}, - {0x00550040, SetNSStateField, "SetNSStateField?" }, - {0x00560000, GetNSStateField, "GetNSStateField?" }, + {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"}, + {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"}, {0x00580002, nullptr, "GetProgramID"}, {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, {0x01020000, CheckNew3DS, "CheckNew3DS"} diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index 0e60bd34f..7bb804ffa 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -92,8 +92,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00520104, nullptr, "Wrap1"}, {0x00530104, nullptr, "Unwrap1"}, - {0x00550040, SetNSStateField, "SetNSStateField?"}, - {0x00560000, GetNSStateField, "GetNSStateField?"}, + {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"}, + {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"}, {0x00580002, nullptr, "GetProgramID"}, {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, {0x01020000, CheckNew3DS, "CheckNew3DS"} diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.cpp b/src/core/hle/service/apt/bcfnt/bcfnt.cpp index b0d39d4a5..57eb39d75 100644 --- a/src/core/hle/service/apt/bcfnt/bcfnt.cpp +++ b/src/core/hle/service/apt/bcfnt/bcfnt.cpp @@ -9,60 +9,97 @@ namespace Service { namespace APT { namespace BCFNT { -void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address) { +void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr new_address) { static const u32 SharedFontStartOffset = 0x80; - u8* data = shared_font->GetPointer(SharedFontStartOffset); + const u8* cfnt_ptr = shared_font->GetPointer(SharedFontStartOffset); CFNT cfnt; - memcpy(&cfnt, data, sizeof(cfnt)); + memcpy(&cfnt, cfnt_ptr, sizeof(cfnt)); - // Advance past the header - data = shared_font->GetPointer(SharedFontStartOffset + cfnt.header_size); + u32 assumed_cmap_offset = 0; + u32 assumed_cwdh_offset = 0; + u32 assumed_tglp_offset = 0; + u32 first_cmap_offset = 0; + u32 first_cwdh_offset = 0; + u32 first_tglp_offset = 0; + // First discover the location of sections so that the rebase offset can be auto-detected + u32 current_offset = SharedFontStartOffset + cfnt.header_size; for (unsigned block = 0; block < cfnt.num_blocks; ++block) { + const u8* data = shared_font->GetPointer(current_offset); - u32 section_size = 0; - if (memcmp(data, "FINF", 4) == 0) { + SectionHeader section_header; + memcpy(§ion_header, data, sizeof(section_header)); + + if (first_cmap_offset == 0 && memcmp(section_header.magic, "CMAP", 4) == 0) { + first_cmap_offset = current_offset; + } else if (first_cwdh_offset == 0 && memcmp(section_header.magic, "CWDH", 4) == 0) { + first_cwdh_offset = current_offset; + } else if (first_tglp_offset == 0 && memcmp(section_header.magic, "TGLP", 4) == 0) { + first_tglp_offset = current_offset; + } else if (memcmp(section_header.magic, "FINF", 4) == 0) { + BCFNT::FINF finf; + memcpy(&finf, data, sizeof(finf)); + + assumed_cmap_offset = finf.cmap_offset - sizeof(SectionHeader); + assumed_cwdh_offset = finf.cwdh_offset - sizeof(SectionHeader); + assumed_tglp_offset = finf.tglp_offset - sizeof(SectionHeader); + } + + current_offset += section_header.section_size; + } + + u32 previous_base = assumed_cmap_offset - first_cmap_offset; + ASSERT(previous_base == assumed_cwdh_offset - first_cwdh_offset); + ASSERT(previous_base == assumed_tglp_offset - first_tglp_offset); + + u32 offset = new_address - previous_base; + + // Reset pointer back to start of sections and do the actual rebase + current_offset = SharedFontStartOffset + cfnt.header_size; + for (unsigned block = 0; block < cfnt.num_blocks; ++block) { + u8* data = shared_font->GetPointer(current_offset); + + SectionHeader section_header; + memcpy(§ion_header, data, sizeof(section_header)); + + if (memcmp(section_header.magic, "FINF", 4) == 0) { BCFNT::FINF finf; memcpy(&finf, data, sizeof(finf)); - section_size = finf.section_size; // Relocate the offsets in the FINF section - finf.cmap_offset += new_address - previous_address; - finf.cwdh_offset += new_address - previous_address; - finf.tglp_offset += new_address - previous_address; + finf.cmap_offset += offset; + finf.cwdh_offset += offset; + finf.tglp_offset += offset; memcpy(data, &finf, sizeof(finf)); - } else if (memcmp(data, "CMAP", 4) == 0) { + } else if (memcmp(section_header.magic, "CMAP", 4) == 0) { BCFNT::CMAP cmap; memcpy(&cmap, data, sizeof(cmap)); - section_size = cmap.section_size; // Relocate the offsets in the CMAP section - cmap.next_cmap_offset += new_address - previous_address; + cmap.next_cmap_offset += offset; memcpy(data, &cmap, sizeof(cmap)); - } else if (memcmp(data, "CWDH", 4) == 0) { + } else if (memcmp(section_header.magic, "CWDH", 4) == 0) { BCFNT::CWDH cwdh; memcpy(&cwdh, data, sizeof(cwdh)); - section_size = cwdh.section_size; // Relocate the offsets in the CWDH section - cwdh.next_cwdh_offset += new_address - previous_address; + cwdh.next_cwdh_offset += offset; memcpy(data, &cwdh, sizeof(cwdh)); - } else if (memcmp(data, "TGLP", 4) == 0) { + } else if (memcmp(section_header.magic, "TGLP", 4) == 0) { BCFNT::TGLP tglp; memcpy(&tglp, data, sizeof(tglp)); - section_size = tglp.section_size; // Relocate the offsets in the TGLP section - tglp.sheet_data_offset += new_address - previous_address; + tglp.sheet_data_offset += offset; memcpy(data, &tglp, sizeof(tglp)); } - data += section_size; + current_offset += section_header.section_size; } } diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.h b/src/core/hle/service/apt/bcfnt/bcfnt.h index 388c6bea0..8936dcf63 100644 --- a/src/core/hle/service/apt/bcfnt/bcfnt.h +++ b/src/core/hle/service/apt/bcfnt/bcfnt.h @@ -22,6 +22,11 @@ struct CFNT { u32_le num_blocks; }; +struct SectionHeader { + u8 magic[4]; + u32_le section_size; +}; + struct FINF { u8 magic[4]; u32_le section_size; @@ -75,12 +80,13 @@ struct CWDH { }; /** - * Relocates the internal addresses of the BCFNT Shared Font to the new base. + * Relocates the internal addresses of the BCFNT Shared Font to the new base. The current base will + * be auto-detected based on the file headers. + * * @param shared_font SharedMemory object that contains the Shared Font - * @param previous_address Previous address at which the offsets in the structure were based. * @param new_address New base for the offsets in the structure. */ -void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address); +void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr new_address); } // namespace BCFNT } // namespace APT diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index 9df48a650..6edcf9610 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -51,7 +51,7 @@ void GetVsyncInterruptEvent(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x5, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::MoveHandleDesc(); + cmd_buff[2] = IPC::CopyHandleDesc(); cmd_buff[3] = Kernel::g_handle_table.Create(vsync_interrupt_error_event).MoveFrom(); LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); @@ -64,7 +64,7 @@ void GetBufferErrorInterruptEvent(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x6, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::MoveHandleDesc(); + cmd_buff[2] = IPC::CopyHandleDesc(); cmd_buff[3] = Kernel::g_handle_table.Create(interrupt_error_event).MoveFrom(); LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); @@ -85,7 +85,7 @@ void SetReceiving(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x7, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::MoveHandleDesc(); + cmd_buff[2] = IPC::CopyHandleDesc(); cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); LOG_WARNING(Service_CAM, "(STUBBED) called, addr=0x%X, port=%d, image_size=%d, trans_unit=%d", diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index e067db645..a5dc47322 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -40,6 +40,20 @@ struct SaveFileConfig { }; static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes"); +enum ConfigBlockID { + StereoCameraSettingsBlockID = 0x00050005, + SoundOutputModeBlockID = 0x00070001, + ConsoleUniqueIDBlockID = 0x00090001, + UsernameBlockID = 0x000A0000, + BirthdayBlockID = 0x000A0001, + LanguageBlockID = 0x000A0002, + CountryInfoBlockID = 0x000B0000, + CountryNameBlockID = 0x000B0001, + StateNameBlockID = 0x000B0002, + EULAVersionBlockID = 0x000D0000, + ConsoleModelBlockID = 0x000F0004, +}; + struct UsernameBlock { char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary u32 zero; @@ -73,8 +87,7 @@ static const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } }; static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 }; static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014 -/// TODO(Subv): Find out what this actually is -static const u8 SOUND_OUTPUT_MODE = 2; +static const u8 SOUND_OUTPUT_MODE = SOUND_SURROUND; static const u8 UNITED_STATES_COUNTRY_ID = 49; /// TODO(Subv): Find what the other bytes are static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID }; @@ -224,6 +237,22 @@ void GetConfigInfoBlk8(Service::Interface* self) { Memory::WriteBlock(data_pointer, data.data(), data.size()); } +void SetConfigInfoBlk4(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 block_id = cmd_buff[1]; + u32 size = cmd_buff[2]; + VAddr data_pointer = cmd_buff[4]; + + if (!Memory::IsValidVirtualAddress(data_pointer)) { + cmd_buff[1] = -1; // TODO(Subv): Find the right error code + return; + } + + std::vector<u8> data(size); + Memory::ReadBlock(data_pointer, data.data(), data.size()); + cmd_buff[1] = Service::CFG::SetConfigInfoBlock(block_id, size, 0x4, data.data()).raw; +} + void UpdateConfigNANDSavegame(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = Service::CFG::UpdateConfigNANDSavegame().raw; @@ -234,13 +263,13 @@ void FormatConfig(Service::Interface* self) { cmd_buff[1] = Service::CFG::FormatConfig().raw; } -ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { +static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 flag) { // Read the header SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries), [&](const SaveConfigBlockEntry& entry) { - return entry.block_id == block_id && (entry.flags & flag); + return entry.block_id == block_id; }); if (itr == std::end(config->block_entries)) { @@ -248,17 +277,38 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); } + if ((itr->flags & flag) == 0) { + LOG_ERROR(Service_CFG, "Invalid flag %u for config block 0x%X with size %u", flag, block_id, size); + return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); + } + if (itr->size != size) { LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag); return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); } + void* pointer; + // The data is located in the block header itself if the size is less than 4 bytes if (itr->size <= 4) - memcpy(output, &itr->offset_or_data, itr->size); + pointer = &itr->offset_or_data; else - memcpy(output, &cfg_config_file_buffer[itr->offset_or_data], itr->size); + pointer = &cfg_config_file_buffer[itr->offset_or_data]; + return MakeResult<void*>(pointer); +} + +ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) { + void* pointer; + CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag)); + memcpy(output, pointer, size); + return RESULT_SUCCESS; +} + +ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input) { + void* pointer; + CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag)); + memcpy(pointer, input, size); return RESULT_SUCCESS; } @@ -336,25 +386,25 @@ ResultCode FormatConfig() { res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer); if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data()); + res = CreateConfigInfoBlk(StereoCameraSettingsBlockID, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data()); if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE); + res = CreateConfigInfoBlk(SoundOutputModeBlockID, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE); if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID); + res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID); if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK); + res = CreateConfigInfoBlk(UsernameBlockID, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK); if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(0x000A0001, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY); + res = CreateConfigInfoBlk(BirthdayBlockID, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY); if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE); + res = CreateConfigInfoBlk(LanguageBlockID, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE); if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO); + res = CreateConfigInfoBlk(CountryInfoBlockID, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO); if (!res.IsSuccess()) return res; u16_le country_name_buffer[16][0x40] = {}; @@ -363,10 +413,10 @@ ResultCode FormatConfig() { std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]); } // 0x000B0001 - Localized names for the profile Country - res = CreateConfigInfoBlk(0x000B0001, sizeof(country_name_buffer), 0xE, country_name_buffer); + res = CreateConfigInfoBlk(CountryNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer); if (!res.IsSuccess()) return res; // 0x000B0002 - Localized names for the profile State/Province - res = CreateConfigInfoBlk(0x000B0002, sizeof(country_name_buffer), 0xE, country_name_buffer); + res = CreateConfigInfoBlk(StateNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer); if (!res.IsSuccess()) return res; // 0x000B0003 - Unknown, related to country/address (zip code?) @@ -382,10 +432,10 @@ ResultCode FormatConfig() { if (!res.IsSuccess()) return res; // 0x000D0000 - Accepted EULA version - res = CreateConfigInfoBlk(0x000D0000, 0x4, 0xE, zero_buffer); + res = CreateConfigInfoBlk(EULAVersionBlockID, 0x4, 0xE, zero_buffer); if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL); + res = CreateConfigInfoBlk(ConsoleModelBlockID, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL); if (!res.IsSuccess()) return res; // 0x00170000 - Unknown @@ -399,11 +449,7 @@ ResultCode FormatConfig() { return RESULT_SUCCESS; } -void Init() { - AddService(new CFG_I_Interface); - AddService(new CFG_S_Interface); - AddService(new CFG_U_Interface); - +ResultCode LoadConfigNANDSaveFile() { // Open the SystemSaveData archive 0x00010017 FileSys::Path archive_path(cfg_system_savedata_id); auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); @@ -431,14 +477,75 @@ void Init() { if (config_result.Succeeded()) { auto config = config_result.MoveFrom(); config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); - return; + return RESULT_SUCCESS; } - FormatConfig(); + return FormatConfig(); +} + +void Init() { + AddService(new CFG_I_Interface); + AddService(new CFG_S_Interface); + AddService(new CFG_U_Interface); + + LoadConfigNANDSaveFile(); } void Shutdown() { } +void SetUsername(const std::u16string& name) { + ASSERT(name.size() <= 10); + UsernameBlock block{}; + name.copy(block.username, name.size()); + SetConfigInfoBlock(UsernameBlockID, sizeof(block), 4, &block); +} + +std::u16string GetUsername() { + UsernameBlock block; + GetConfigInfoBlock(UsernameBlockID, sizeof(block), 8, &block); + + // the username string in the block isn't null-terminated, + // so we need to find the end manually. + std::u16string username(block.username, ARRAY_SIZE(block.username)); + const size_t pos = username.find(u'\0'); + if (pos != std::u16string::npos) + username.erase(pos); + return username; +} + +void SetBirthday(u8 month, u8 day) { + BirthdayBlock block = { month, day }; + SetConfigInfoBlock(BirthdayBlockID, sizeof(block), 4, &block); +} + +std::tuple<u8, u8> GetBirthday() { + BirthdayBlock block; + GetConfigInfoBlock(BirthdayBlockID, sizeof(block), 8, &block); + return std::make_tuple(block.month, block.day); +} + +void SetSystemLanguage(SystemLanguage language) { + u8 block = language; + SetConfigInfoBlock(LanguageBlockID, sizeof(block), 4, &block); +} + +SystemLanguage GetSystemLanguage() { + u8 block; + GetConfigInfoBlock(LanguageBlockID, sizeof(block), 8, &block); + return static_cast<SystemLanguage>(block); +} + +void SetSoundOutputMode(SoundOutputMode mode) { + u8 block = mode; + SetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 4, &block); +} + +SoundOutputMode GetSoundOutputMode() { + u8 block; + GetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 8, &block); + return static_cast<SoundOutputMode>(block); +} + } // namespace CFG } // namespace Service diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index c01806836..18f60f4ca 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h @@ -5,6 +5,7 @@ #pragma once #include <array> +#include <string> #include "common/common_types.h" @@ -35,7 +36,14 @@ enum SystemLanguage { LANGUAGE_KO = 7, LANGUAGE_NL = 8, LANGUAGE_PT = 9, - LANGUAGE_RU = 10 + LANGUAGE_RU = 10, + LANGUAGE_TW = 11 +}; + +enum SoundOutputMode { + SOUND_MONO = 0, + SOUND_STEREO = 1, + SOUND_SURROUND = 2 }; /// Block header in the config savedata file @@ -177,6 +185,22 @@ void GetConfigInfoBlk2(Service::Interface* self); */ void GetConfigInfoBlk8(Service::Interface* self); +/** + * CFG::SetConfigInfoBlk4 service function + * Inputs: + * 0 : 0x04020082 / 0x08020082 + * 1 : Block ID + * 2 : Size + * 3 : Descriptor for the output buffer + * 4 : Output buffer pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * Note: + * The parameters order is different from GetConfigInfoBlk2/8's, + * where Block ID and Size are switched. + */ +void SetConfigInfoBlk4(Service::Interface* self); + /** * CFG::UpdateConfigNANDSavegame service function * Inputs: @@ -205,7 +229,19 @@ void FormatConfig(Service::Interface* self); * @param output A pointer where we will write the read data * @returns ResultCode indicating the result of the operation, 0 on success */ -ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output); +ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output); + +/** + * Reads data from input and writes to a block with the specified id and flag + * in the Config savegame buffer. + * The input size must match exactly the size of the target block + * @param block_id The id of the block we want to write + * @param size The size of the block we want to write + * @param flag The target block must have this flag set + * @param input A pointer where we will read data and write to Config savegame buffer + * @returns ResultCode indicating the result of the operation, 0 on success + */ +ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input); /** * Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory. @@ -236,11 +272,70 @@ ResultCode UpdateConfigNANDSavegame(); */ ResultCode FormatConfig(); +/** + * Open the config savegame file and load it to the memory buffer + * @returns ResultCode indicating the result of the operation, 0 on success + */ +ResultCode LoadConfigNANDSaveFile(); + /// Initialize the config service void Init(); /// Shutdown the config service void Shutdown(); +// Utilities for frontend to set config data. +// Note: before calling these functions, LoadConfigNANDSaveFile should be called, +// and UpdateConfigNANDSavegame should be called after making changes to config data. + +/** + * Sets the username in config savegame. + * @param name the username to set. The maximum size is 10 in char16_t. + */ +void SetUsername(const std::u16string& name); + +/** + * Gets the username from config savegame. + * @returns the username + */ +std::u16string GetUsername(); + +/** + * Sets the profile birthday in config savegame. + * @param month the month of birthday. + * @param day the day of the birthday. + */ +void SetBirthday(u8 month, u8 day); + +/** + * Gets the profile birthday from the config savegame. + * @returns a tuple of (month, day) of birthday + */ +std::tuple<u8, u8> GetBirthday(); + +/** + * Sets the system language in config savegame. + * @param language the system language to set. + */ +void SetSystemLanguage(SystemLanguage language); + +/** + * Gets the system language from config savegame. + * @returns the system language + */ +SystemLanguage GetSystemLanguage(); + +/** + * Sets the sound output mode in config savegame. + * @param mode the sound output mode to set + */ +void SetSoundOutputMode(SoundOutputMode mode); + +/** + * Gets the sound output mode from config savegame. + * @returns the sound output mode + */ +SoundOutputMode GetSoundOutputMode(); + } // namespace CFG } // namespace Service diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index b18060f6d..8b0db785f 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp @@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, // cfg:i {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, - {0x04020082, nullptr, "SetConfigInfoBlk4"}, + {0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"}, {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, @@ -31,7 +31,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x04080042, nullptr, "SecureInfoGetSerialNo"}, {0x04090000, nullptr, "UpdateConfigBlk00040003"}, {0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, - {0x08020082, nullptr, "SetConfigInfoBlk4"}, + {0x08020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"}, {0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, {0x080400C2, nullptr, "CreateConfigInfoBlk"}, {0x08050000, nullptr, "DeleteConfigNANDSavefile"}, diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index e001f7687..12b458783 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, // cfg:s {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, - {0x04020082, nullptr, "SetConfigInfoBlk4"}, + {0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"}, {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index d2bb8941c..913c8dc09 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp @@ -51,7 +51,7 @@ void Initialize(Service::Interface* self) { mutex = Kernel::Mutex::Create(false); cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::MoveHandleDesc(2); + 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(); } diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 81b9abe4c..4c7aaa7f2 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -58,6 +58,10 @@ namespace FS { const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::FS, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); +/// Returned when a function is passed an invalid archive handle. +const ResultCode ERR_INVALID_ARCHIVE_HANDLE(ErrorDescription::FS_ArchiveNotMounted, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); // 0xC8804465 + // Command to access archive file enum class FileCommand : u32 { Dummy1 = 0x000100C6, @@ -255,7 +259,7 @@ using FileSys::ArchiveFactory; /** * Map of registered archives, identified by id code. Once an archive is registered here, it is - * never removed until the FS service is shut down. + * never removed until UnregisterArchiveTypes is called. */ static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map; @@ -292,7 +296,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi ResultCode CloseArchive(ArchiveHandle handle) { if (handle_map.erase(handle) == 0) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; else return RESULT_SUCCESS; } @@ -314,7 +318,7 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han const FileSys::Path& path, const FileSys::Mode mode) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; auto backend = archive->OpenFile(path, mode); if (backend.Failed()) @@ -327,7 +331,7 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; return archive->DeleteFile(path); } @@ -337,7 +341,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil ArchiveBackend* src_archive = GetArchive(src_archive_handle); ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); if (src_archive == nullptr || dest_archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; if (src_archive == dest_archive) { if (src_archive->RenameFile(src_path, dest_path)) @@ -356,7 +360,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; if (archive->DeleteDirectory(path)) return RESULT_SUCCESS; @@ -367,7 +371,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; return archive->CreateFile(path, file_size); } @@ -375,7 +379,7 @@ ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; if (archive->CreateDirectory(path)) return RESULT_SUCCESS; @@ -388,7 +392,7 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons ArchiveBackend* src_archive = GetArchive(src_archive_handle); ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); if (src_archive == nullptr || dest_archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; if (src_archive == dest_archive) { if (src_archive->RenameDirectory(src_path, dest_path)) @@ -408,7 +412,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path); if (backend == nullptr) { @@ -423,7 +427,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_HANDLE; + return ERR_INVALID_ARCHIVE_HANDLE; return MakeResult<u64>(archive->GetFreeBytes()); } @@ -516,12 +520,7 @@ ResultCode CreateSystemSaveData(u32 high, u32 low) { return RESULT_SUCCESS; } -/// Initialize archives -void ArchiveInit() { - next_handle = 1; - - AddService(new FS::Interface); - +void RegisterArchiveTypes() { // TODO(Subv): Add the other archive types (see here for the known types: // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). @@ -558,10 +557,23 @@ void ArchiveInit() { RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); } +void UnregisterArchiveTypes() { + id_code_map.clear(); +} + +/// Initialize archives +void ArchiveInit() { + next_handle = 1; + + AddService(new FS::Interface); + + RegisterArchiveTypes(); +} + /// Shutdown archives void ArchiveShutdown() { handle_map.clear(); - id_code_map.clear(); + UnregisterArchiveTypes(); } } // namespace FS diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 006606740..f7a50a3a7 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -235,5 +235,11 @@ void ArchiveInit(); /// Shutdown archives void ArchiveShutdown(); +/// Register all archive types +void RegisterArchiveTypes(); + +/// Unregister all archive types +void UnregisterArchiveTypes(); + } // namespace FS } // namespace Service diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 7df7da5a4..937868747 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -645,20 +645,19 @@ static void DeleteSystemSaveData(Service::Interface* self) { * FS_User::CreateSystemSaveData service function. * Inputs: * 0 : 0x08560240 - * 1 : High word of the SystemSaveData id to create - * 2 : Low word of the SystemSaveData id to create - * 3 : Unknown - * 4 : Unknown - * 5 : Unknown - * 6 : Unknown - * 7 : Unknown - * 8 : Unknown - * 9 : Unknown (Memory address) + * 1 : u8 MediaType of the system save data + * 2 : SystemSaveData id to create + * 3 : Total size + * 4 : Block size + * 5 : Number of directories + * 6 : Number of files + * 7 : Directory bucket count + * 8 : File bucket count + * 9 : u8 Whether to duplicate data or not * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ static void CreateSystemSaveData(Service::Interface* self) { - // TODO(Subv): Figure out the other parameters. u32* cmd_buff = Kernel::GetCommandBuffer(); u32 savedata_high = cmd_buff[1]; u32 savedata_low = cmd_buff[2]; @@ -671,6 +670,38 @@ static void CreateSystemSaveData(Service::Interface* self) { cmd_buff[1] = CreateSystemSaveData(savedata_high, savedata_low).raw; } +/** + * FS_User::CreateLegacySystemSaveData service function. + * This function appears to be obsolete and seems to have been replaced by + * command 0x08560240 (CreateSystemSaveData). + * + * Inputs: + * 0 : 0x08100200 + * 1 : SystemSaveData id to create + * 2 : Total size + * 3 : Block size + * 4 : Number of directories + * 5 : Number of files + * 6 : Directory bucket count + * 7 : File bucket count + * 8 : u8 Duplicate data + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void CreateLegacySystemSaveData(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 savedata_id = cmd_buff[1]; + + LOG_WARNING(Service_FS, "(STUBBED) savedata_id=%08X cmd_buff[3]=%08X " + "cmd_buff[4]=%08X cmd_buff[5]=%08X cmd_buff[6]=%08X cmd_buff[7]=%08X cmd_buff[8]=%08X " + "cmd_buff[9]=%08X", savedata_id, cmd_buff[3], cmd_buff[4], cmd_buff[5], + cmd_buff[6], cmd_buff[7], cmd_buff[8], cmd_buff[9]); + + cmd_buff[0] = IPC::MakeHeader(0x810, 0x1, 0); + // With this command, the SystemSaveData always has save_high = 0 (Always created in the NAND) + cmd_buff[1] = CreateSystemSaveData(0, savedata_id).raw; +} + /** * FS_User::InitializeWithSdkVersion service function. * Inputs: @@ -820,7 +851,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x080D0144, nullptr, "ControlArchive"}, {0x080E0080, CloseArchive, "CloseArchive"}, {0x080F0180, FormatThisUserSaveData, "FormatThisUserSaveData"}, - {0x08100200, nullptr, "CreateSystemSaveData"}, + {0x08100200, CreateLegacySystemSaveData, "CreateLegacySystemSaveData"}, {0x08110040, nullptr, "DeleteSystemSaveData"}, {0x08120080, GetFreeBytes, "GetFreeBytes"}, {0x08130000, nullptr, "GetCardType"}, diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp deleted file mode 100644 index ecec2ce32..000000000 --- a/src/core/hle/service/ldr_ro.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/common_types.h" -#include "common/logging/log.h" - -#include "core/hle/service/ldr_ro.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace LDR_RO - -namespace LDR_RO { - -/** - * LDR_RO::Initialize service function - * Inputs: - * 1 : CRS buffer pointer - * 2 : CRS Size - * 3 : Process memory address where the CRS will be mapped - * 4 : Value, must be zero - * 5 : KProcess handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void Initialize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 crs_buffer_ptr = cmd_buff[1]; - u32 crs_size = cmd_buff[2]; - u32 address = cmd_buff[3]; - u32 value = cmd_buff[4]; - u32 process = cmd_buff[5]; - - if (value != 0) { - LOG_ERROR(Service_LDR, "This value should be zero, but is actually %u!", value); - } - - // TODO(purpasmart96): Verify return header on HW - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - - LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, address=0x%08X, value=0x%08X, process=0x%08X", - crs_buffer_ptr, crs_size, address, value, process); -} - -/** - * LDR_RO::LoadCRR service function - * Inputs: - * 1 : CRS buffer pointer - * 2 : CRS Size - * 3 : Value, must be zero - * 4 : KProcess handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void LoadCRR(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 crs_buffer_ptr = cmd_buff[1]; - u32 crs_size = cmd_buff[2]; - u32 value = cmd_buff[3]; - u32 process = cmd_buff[4]; - - if (value != 0) { - LOG_ERROR(Service_LDR, "This value should be zero, but is actually %u!", value); - } - - // TODO(purpasmart96): Verify return header on HW - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - - LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, value=0x%08X, process=0x%08X", - crs_buffer_ptr, crs_size, value, process); -} - -const Interface::FunctionInfo FunctionTable[] = { - {0x000100C2, Initialize, "Initialize"}, - {0x00020082, LoadCRR, "LoadCRR"}, - {0x00030042, nullptr, "UnloadCCR"}, - {0x000402C2, nullptr, "LoadExeCRO"}, - {0x000500C2, nullptr, "LoadCROSymbols"}, - {0x00060042, nullptr, "CRO_Load?"}, - {0x00070042, nullptr, "LoadCROSymbols"}, - {0x00080042, nullptr, "Shutdown"}, - {0x000902C2, nullptr, "LoadExeCRO_New?"}, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { - Register(FunctionTable); -} - -} // namespace diff --git a/src/core/hle/service/ldr_ro/cro_helper.cpp b/src/core/hle/service/ldr_ro/cro_helper.cpp new file mode 100644 index 000000000..3d2a613ee --- /dev/null +++ b/src/core/hle/service/ldr_ro/cro_helper.cpp @@ -0,0 +1,1477 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/alignment.h" +#include "common/logging/log.h" +#include "common/scope_exit.h" + +#include "core/hle/service/ldr_ro/cro_helper.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace LDR_RO + +namespace LDR_RO { + +static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F + ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument, ErrorLevel::Usage); + +static ResultCode CROFormatError(u32 description) { + return ResultCode(static_cast<ErrorDescription>(description), ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent); +} + +const std::array<int, 17> CROHelper::ENTRY_SIZE {{ + 1, // code + 1, // data + 1, // module name + sizeof(SegmentEntry), + sizeof(ExportNamedSymbolEntry), + sizeof(ExportIndexedSymbolEntry), + 1, // export strings + sizeof(ExportTreeEntry), + sizeof(ImportModuleEntry), + sizeof(ExternalRelocationEntry), + sizeof(ImportNamedSymbolEntry), + sizeof(ImportIndexedSymbolEntry), + sizeof(ImportAnonymousSymbolEntry), + 1, // import strings + sizeof(StaticAnonymousSymbolEntry), + sizeof(InternalRelocationEntry), + sizeof(StaticRelocationEntry) +}}; + +const std::array<CROHelper::HeaderField, 4> CROHelper::FIX_BARRIERS {{ + Fix0Barrier, + Fix1Barrier, + Fix2Barrier, + Fix3Barrier +}}; + +VAddr CROHelper::SegmentTagToAddress(SegmentTag segment_tag) const { + u32 segment_num = GetField(SegmentNum); + + if (segment_tag.segment_index >= segment_num) + return 0; + + SegmentEntry entry; + GetEntry(segment_tag.segment_index, entry); + + if (segment_tag.offset_into_segment >= entry.size) + return 0; + + return entry.offset + segment_tag.offset_into_segment; +} + +ResultCode CROHelper::ApplyRelocation(VAddr target_address, RelocationType relocation_type, + u32 addend, u32 symbol_address, u32 target_future_address) { + + switch (relocation_type) { + case RelocationType::Nothing: + break; + case RelocationType::AbsoluteAddress: + case RelocationType::AbsoluteAddress2: + Memory::Write32(target_address, symbol_address + addend); + break; + case RelocationType::RelativeAddress: + Memory::Write32(target_address, symbol_address + addend - target_future_address); + break; + case RelocationType::ThumbBranch: + case RelocationType::ArmBranch: + case RelocationType::ModifyArmBranch: + case RelocationType::AlignedRelativeAddress: + // TODO(wwylele): implement other types + UNIMPLEMENTED(); + break; + default: + return CROFormatError(0x22); + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ClearRelocation(VAddr target_address, RelocationType relocation_type) { + switch (relocation_type) { + case RelocationType::Nothing: + break; + case RelocationType::AbsoluteAddress: + case RelocationType::AbsoluteAddress2: + case RelocationType::RelativeAddress: + Memory::Write32(target_address, 0); + break; + case RelocationType::ThumbBranch: + case RelocationType::ArmBranch: + case RelocationType::ModifyArmBranch: + case RelocationType::AlignedRelativeAddress: + // TODO(wwylele): implement other types + UNIMPLEMENTED(); + break; + default: + return CROFormatError(0x22); + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool reset) { + if (symbol_address == 0 && !reset) + return CROFormatError(0x10); + + VAddr relocation_address = batch; + while (true) { + RelocationEntry relocation; + Memory::ReadBlock(relocation_address, &relocation, sizeof(RelocationEntry)); + + VAddr relocation_target = SegmentTagToAddress(relocation.target_position); + if (relocation_target == 0) { + return CROFormatError(0x12); + } + + ResultCode result = ApplyRelocation(relocation_target, relocation.type, relocation.addend, symbol_address, relocation_target); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw); + return result; + } + + if (relocation.is_batch_end) + break; + + relocation_address += sizeof(RelocationEntry); + } + + RelocationEntry relocation; + Memory::ReadBlock(batch, &relocation, sizeof(RelocationEntry)); + relocation.is_batch_resolved = reset ? 0 : 1; + Memory::WriteBlock(batch, &relocation, sizeof(RelocationEntry)); + return RESULT_SUCCESS; +} + +VAddr CROHelper::FindExportNamedSymbol(const std::string& name) const { + if (!GetField(ExportTreeNum)) + return 0; + + std::size_t len = name.size(); + ExportTreeEntry entry; + GetEntry(0, entry); + ExportTreeEntry::Child next; + next.raw = entry.left.raw; + u32 found_id; + + while (true) { + GetEntry(next.next_index, entry); + + if (next.is_end) { + found_id = entry.export_table_index; + break; + } + + u16 test_byte = entry.test_bit >> 3; + u16 test_bit_in_byte = entry.test_bit & 7; + + if (test_byte >= len) { + next.raw = entry.left.raw; + } else if((name[test_byte] >> test_bit_in_byte) & 1) { + next.raw = entry.right.raw; + } else { + next.raw = entry.left.raw; + } + } + + u32 export_named_symbol_num = GetField(ExportNamedSymbolNum); + + if (found_id >= export_named_symbol_num) + return 0; + + u32 export_strings_size = GetField(ExportStringsSize); + ExportNamedSymbolEntry symbol_entry; + GetEntry(found_id, symbol_entry); + + if (Memory::ReadCString(symbol_entry.name_offset, export_strings_size) != name) + return 0; + + return SegmentTagToAddress(symbol_entry.symbol_position); +} + +ResultCode CROHelper::RebaseHeader(u32 cro_size) { + ResultCode error = CROFormatError(0x11); + + // verifies magic + if (GetField(Magic) != MAGIC_CRO0) + return error; + + // verifies not registered + if (GetField(NextCRO) != 0 || GetField(PreviousCRO) != 0) + return error; + + // This seems to be a hard limit set by the RO module + if (GetField(FileSize) > 0x10000000 || GetField(BssSize) > 0x10000000) + return error; + + // verifies not fixed + if (GetField(FixedSize) != 0) + return error; + + if (GetField(CodeOffset) < CRO_HEADER_SIZE) + return error; + + // verifies that all offsets are in the correct order + constexpr std::array<HeaderField, 18> OFFSET_ORDER = {{ + CodeOffset, + ModuleNameOffset, + SegmentTableOffset, + ExportNamedSymbolTableOffset, + ExportTreeTableOffset, + ExportIndexedSymbolTableOffset, + ExportStringsOffset, + ImportModuleTableOffset, + ExternalRelocationTableOffset, + ImportNamedSymbolTableOffset, + ImportIndexedSymbolTableOffset, + ImportAnonymousSymbolTableOffset, + ImportStringsOffset, + StaticAnonymousSymbolTableOffset, + InternalRelocationTableOffset, + StaticRelocationTableOffset, + DataOffset, + FileSize + }}; + + u32 prev_offset = GetField(OFFSET_ORDER[0]); + u32 cur_offset; + for (std::size_t i = 1; i < OFFSET_ORDER.size(); ++i) { + cur_offset = GetField(OFFSET_ORDER[i]); + if (cur_offset < prev_offset) + return error; + prev_offset = cur_offset; + } + + // rebases offsets + u32 offset = GetField(NameOffset); + if (offset != 0) + SetField(NameOffset, offset + module_address); + + for (int field = CodeOffset; field < Fix0Barrier; field += 2) { + HeaderField header_field = static_cast<HeaderField>(field); + offset = GetField(header_field); + if (offset != 0) + SetField(header_field, offset + module_address); + } + + // verifies everything is not beyond the buffer + u32 file_end = module_address + cro_size; + for (int field = CodeOffset, i = 0; field < Fix0Barrier; field += 2, ++i) { + HeaderField offset_field = static_cast<HeaderField>(field); + HeaderField size_field = static_cast<HeaderField>(field + 1); + if (GetField(offset_field) + GetField(size_field) * ENTRY_SIZE[i] > file_end) + return error; + } + + return RESULT_SUCCESS; +} + +ResultVal<VAddr> CROHelper::RebaseSegmentTable(u32 cro_size, + VAddr data_segment_address, u32 data_segment_size, + VAddr bss_segment_address, u32 bss_segment_size) { + + u32 prev_data_segment = 0; + u32 segment_num = GetField(SegmentNum); + for (u32 i = 0; i < segment_num; ++i) { + SegmentEntry segment; + GetEntry(i, segment); + if (segment.type == SegmentType::Data) { + if (segment.size != 0) { + if (segment.size > data_segment_size) + return ERROR_BUFFER_TOO_SMALL; + prev_data_segment = segment.offset; + segment.offset = data_segment_address; + } + } else if (segment.type == SegmentType::BSS) { + if (segment.size != 0) { + if (segment.size > bss_segment_size) + return ERROR_BUFFER_TOO_SMALL; + segment.offset = bss_segment_address; + } + } else if (segment.offset != 0) { + segment.offset += module_address; + if (segment.offset > module_address + cro_size) + return CROFormatError(0x19); + } + SetEntry(i, segment); + } + return MakeResult<u32>(prev_data_segment + module_address); +} + +ResultCode CROHelper::RebaseExportNamedSymbolTable() { + VAddr export_strings_offset = GetField(ExportStringsOffset); + VAddr export_strings_end = export_strings_offset + GetField(ExportStringsSize); + + u32 export_named_symbol_num = GetField(ExportNamedSymbolNum); + for (u32 i = 0; i < export_named_symbol_num; ++i) { + ExportNamedSymbolEntry entry; + GetEntry(i, entry); + + if (entry.name_offset != 0) { + entry.name_offset += module_address; + if (entry.name_offset < export_strings_offset + || entry.name_offset >= export_strings_end) { + return CROFormatError(0x11); + } + } + + SetEntry(i, entry); + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::VerifyExportTreeTable() const { + u32 tree_num = GetField(ExportTreeNum); + for (u32 i = 0; i < tree_num; ++i) { + ExportTreeEntry entry; + GetEntry(i, entry); + + if (entry.left.next_index >= tree_num || entry.right.next_index >= tree_num) { + return CROFormatError(0x11); + } + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::RebaseImportModuleTable() { + VAddr import_strings_offset = GetField(ImportStringsOffset); + VAddr import_strings_end = import_strings_offset + GetField(ImportStringsSize); + VAddr import_indexed_symbol_table_offset = GetField(ImportIndexedSymbolTableOffset); + VAddr index_import_table_end = import_indexed_symbol_table_offset + GetField(ImportIndexedSymbolNum) * sizeof(ImportIndexedSymbolEntry); + VAddr import_anonymous_symbol_table_offset = GetField(ImportAnonymousSymbolTableOffset); + VAddr offset_import_table_end = import_anonymous_symbol_table_offset + GetField(ImportAnonymousSymbolNum) * sizeof(ImportAnonymousSymbolEntry); + + u32 module_num = GetField(ImportModuleNum); + for (u32 i = 0; i < module_num; ++i) { + ImportModuleEntry entry; + GetEntry(i, entry); + + if (entry.name_offset != 0) { + entry.name_offset += module_address; + if (entry.name_offset < import_strings_offset + || entry.name_offset >= import_strings_end) { + return CROFormatError(0x18); + } + } + + if (entry.import_indexed_symbol_table_offset != 0) { + entry.import_indexed_symbol_table_offset += module_address; + if (entry.import_indexed_symbol_table_offset < import_indexed_symbol_table_offset + || entry.import_indexed_symbol_table_offset > index_import_table_end) { + return CROFormatError(0x18); + } + } + + if (entry.import_anonymous_symbol_table_offset != 0) { + entry.import_anonymous_symbol_table_offset += module_address; + if (entry.import_anonymous_symbol_table_offset < import_anonymous_symbol_table_offset + || entry.import_anonymous_symbol_table_offset > offset_import_table_end) { + return CROFormatError(0x18); + } + } + + SetEntry(i, entry); + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::RebaseImportNamedSymbolTable() { + VAddr import_strings_offset = GetField(ImportStringsOffset); + VAddr import_strings_end = import_strings_offset + GetField(ImportStringsSize); + VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset); + VAddr external_relocation_table_end = external_relocation_table_offset + GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry); + + u32 num = GetField(ImportNamedSymbolNum); + for (u32 i = 0; i < num ; ++i) { + ImportNamedSymbolEntry entry; + GetEntry(i, entry); + + if (entry.name_offset != 0) { + entry.name_offset += module_address; + if (entry.name_offset < import_strings_offset + || entry.name_offset >= import_strings_end) { + return CROFormatError(0x1B); + } + } + + if (entry.relocation_batch_offset != 0) { + entry.relocation_batch_offset += module_address; + if (entry.relocation_batch_offset < external_relocation_table_offset + || entry.relocation_batch_offset > external_relocation_table_end) { + return CROFormatError(0x1B); + } + } + + SetEntry(i, entry); + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::RebaseImportIndexedSymbolTable() { + VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset); + VAddr external_relocation_table_end = external_relocation_table_offset + GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry); + + u32 num = GetField(ImportIndexedSymbolNum); + for (u32 i = 0; i < num ; ++i) { + ImportIndexedSymbolEntry entry; + GetEntry(i, entry); + + if (entry.relocation_batch_offset != 0) { + entry.relocation_batch_offset += module_address; + if (entry.relocation_batch_offset < external_relocation_table_offset + || entry.relocation_batch_offset > external_relocation_table_end) { + return CROFormatError(0x14); + } + } + + SetEntry(i, entry); + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::RebaseImportAnonymousSymbolTable() { + VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset); + VAddr external_relocation_table_end = external_relocation_table_offset + GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry); + + u32 num = GetField(ImportAnonymousSymbolNum); + for (u32 i = 0; i < num ; ++i) { + ImportAnonymousSymbolEntry entry; + GetEntry(i, entry); + + if (entry.relocation_batch_offset != 0) { + entry.relocation_batch_offset += module_address; + if (entry.relocation_batch_offset < external_relocation_table_offset + || entry.relocation_batch_offset > external_relocation_table_end) { + return CROFormatError(0x17); + } + } + + SetEntry(i, entry); + } + return RESULT_SUCCESS; +} + +VAddr CROHelper::GetOnUnresolvedAddress() { + return SegmentTagToAddress(SegmentTag(GetField(OnUnresolvedSegmentTag))); +} + +ResultCode CROHelper::ResetExternalRelocations() { + u32 unresolved_symbol = GetOnUnresolvedAddress(); + u32 external_relocation_num = GetField(ExternalRelocationNum); + ExternalRelocationEntry relocation; + + // Verifies that the last relocation is the end of a batch + GetEntry(external_relocation_num - 1, relocation); + if (!relocation.is_batch_end) { + return CROFormatError(0x12); + } + + bool batch_begin = true; + for (u32 i = 0; i < external_relocation_num; ++i) { + GetEntry(i, relocation); + VAddr relocation_target = SegmentTagToAddress(relocation.target_position); + + if (relocation_target == 0) { + return CROFormatError(0x12); + } + + ResultCode result = ApplyRelocation(relocation_target, relocation.type, relocation.addend, unresolved_symbol, relocation_target); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw); + return result; + } + + if (batch_begin) { + // resets to unresolved state + relocation.is_batch_resolved = 0; + SetEntry(i, relocation); + } + + // if current is an end, then the next is a beginning + batch_begin = relocation.is_batch_end != 0; + } + + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ClearExternalRelocations() { + u32 external_relocation_num = GetField(ExternalRelocationNum); + ExternalRelocationEntry relocation; + + bool batch_begin = true; + for (u32 i = 0; i < external_relocation_num; ++i) { + GetEntry(i, relocation); + VAddr relocation_target = SegmentTagToAddress(relocation.target_position); + + if (relocation_target == 0) { + return CROFormatError(0x12); + } + + ResultCode result = ClearRelocation(relocation_target, relocation.type); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error clearing relocation %08X", result.raw); + return result; + } + + if (batch_begin) { + // resets to unresolved state + relocation.is_batch_resolved = 0; + SetEntry(i, relocation); + } + + // if current is an end, then the next is a beginning + batch_begin = relocation.is_batch_end != 0; + } + + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ApplyStaticAnonymousSymbolToCRS(VAddr crs_address) { + VAddr static_relocation_table_offset = GetField(StaticRelocationTableOffset); + VAddr static_relocation_table_end = static_relocation_table_offset + GetField(StaticRelocationNum) * sizeof(StaticRelocationEntry); + + CROHelper crs(crs_address); + u32 offset_export_num = GetField(StaticAnonymousSymbolNum); + LOG_INFO(Service_LDR, "CRO \"%s\" exports %d static anonymous symbols", ModuleName().data(), offset_export_num); + for (u32 i = 0; i < offset_export_num; ++i) { + StaticAnonymousSymbolEntry entry; + GetEntry(i, entry); + u32 batch_address = entry.relocation_batch_offset + module_address; + + if (batch_address < static_relocation_table_offset + || batch_address > static_relocation_table_end) { + return CROFormatError(0x16); + } + + u32 symbol_address = SegmentTagToAddress(entry.symbol_position); + LOG_TRACE(Service_LDR, "CRO \"%s\" exports 0x%08X to the static module", ModuleName().data(), symbol_address); + ResultCode result = crs.ApplyRelocationBatch(batch_address, symbol_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ApplyInternalRelocations(u32 old_data_segment_address) { + u32 segment_num = GetField(SegmentNum); + u32 internal_relocation_num = GetField(InternalRelocationNum); + for (u32 i = 0; i < internal_relocation_num; ++i) { + InternalRelocationEntry relocation; + GetEntry(i, relocation); + VAddr target_addressB = SegmentTagToAddress(relocation.target_position); + if (target_addressB == 0) { + return CROFormatError(0x15); + } + + VAddr target_address; + SegmentEntry target_segment; + GetEntry(relocation.target_position.segment_index, target_segment); + + if (target_segment.type == SegmentType::Data) { + // If the relocation is to the .data segment, we need to relocate it in the old buffer + target_address = old_data_segment_address + relocation.target_position.offset_into_segment; + } else { + target_address = target_addressB; + } + + if (relocation.symbol_segment >= segment_num) { + return CROFormatError(0x15); + } + + SegmentEntry symbol_segment; + GetEntry(relocation.symbol_segment, symbol_segment); + LOG_TRACE(Service_LDR, "Internally relocates 0x%08X with 0x%08X", target_address, symbol_segment.offset); + ResultCode result = ApplyRelocation(target_address, relocation.type, relocation.addend, symbol_segment.offset, target_addressB); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw); + return result; + } + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ClearInternalRelocations() { + u32 internal_relocation_num = GetField(InternalRelocationNum); + for (u32 i = 0; i < internal_relocation_num; ++i) { + InternalRelocationEntry relocation; + GetEntry(i, relocation); + VAddr target_address = SegmentTagToAddress(relocation.target_position); + + if (target_address == 0) { + return CROFormatError(0x15); + } + + ResultCode result = ClearRelocation(target_address, relocation.type); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error clearing relocation %08X", result.raw); + return result; + } + } + return RESULT_SUCCESS; +} + +void CROHelper::UnrebaseImportAnonymousSymbolTable() { + u32 num = GetField(ImportAnonymousSymbolNum); + for (u32 i = 0; i < num; ++i) { + ImportAnonymousSymbolEntry entry; + GetEntry(i, entry); + + if (entry.relocation_batch_offset != 0) { + entry.relocation_batch_offset -= module_address; + } + + SetEntry(i, entry); + } +} + +void CROHelper::UnrebaseImportIndexedSymbolTable() { + u32 num = GetField(ImportIndexedSymbolNum); + for (u32 i = 0; i < num; ++i) { + ImportIndexedSymbolEntry entry; + GetEntry(i, entry); + + if (entry.relocation_batch_offset != 0) { + entry.relocation_batch_offset -= module_address; + } + + SetEntry(i, entry); + } +} + +void CROHelper::UnrebaseImportNamedSymbolTable() { + u32 num = GetField(ImportNamedSymbolNum); + for (u32 i = 0; i < num; ++i) { + ImportNamedSymbolEntry entry; + GetEntry(i, entry); + + if (entry.name_offset != 0) { + entry.name_offset -= module_address; + } + + if (entry.relocation_batch_offset) { + entry.relocation_batch_offset -= module_address; + } + + SetEntry(i, entry); + } +} + +void CROHelper::UnrebaseImportModuleTable() { + u32 module_num = GetField(ImportModuleNum); + for (u32 i = 0; i < module_num; ++i) { + ImportModuleEntry entry; + GetEntry(i, entry); + + if (entry.name_offset != 0) { + entry.name_offset -= module_address; + } + + if (entry.import_indexed_symbol_table_offset) { + entry.import_indexed_symbol_table_offset -= module_address; + } + + if (entry.import_anonymous_symbol_table_offset) { + entry.import_anonymous_symbol_table_offset -= module_address; + } + + SetEntry(i, entry); + } +} + +void CROHelper::UnrebaseExportNamedSymbolTable() { + u32 export_named_symbol_num = GetField(ExportNamedSymbolNum); + for (u32 i = 0; i < export_named_symbol_num; ++i) { + ExportNamedSymbolEntry entry; + GetEntry(i, entry); + + if (entry.name_offset != 0) { + entry.name_offset -= module_address; + } + + SetEntry(i, entry); + } +} + +void CROHelper::UnrebaseSegmentTable() { + u32 segment_num = GetField(SegmentNum); + for (u32 i = 0; i < segment_num; ++i) { + SegmentEntry segment; + GetEntry(i, segment); + + if (segment.type == SegmentType::BSS) { + segment.offset = 0; + } else if (segment.offset != 0) { + segment.offset -= module_address; + } + + SetEntry(i, segment); + } +} + +void CROHelper::UnrebaseHeader() { + u32 offset = GetField(NameOffset); + if (offset != 0) + SetField(NameOffset, offset - module_address); + + for (int field = CodeOffset; field < Fix0Barrier; field += 2) { + HeaderField header_field = static_cast<HeaderField>(field); + offset = GetField(header_field); + if (offset != 0) + SetField(header_field, offset - module_address); + } +} + +ResultCode CROHelper::ApplyImportNamedSymbol(VAddr crs_address) { + u32 import_strings_size = GetField(ImportStringsSize); + u32 symbol_import_num = GetField(ImportNamedSymbolNum); + for (u32 i = 0; i < symbol_import_num; ++i) { + ImportNamedSymbolEntry entry; + GetEntry(i, entry); + VAddr relocation_addr = entry.relocation_batch_offset; + ExternalRelocationEntry relocation_entry; + Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); + + if (!relocation_entry.is_batch_resolved) { + ResultCode result = ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> { + std::string symbol_name = Memory::ReadCString(entry.name_offset, import_strings_size); + u32 symbol_address = source.FindExportNamedSymbol(symbol_name); + + if (symbol_address != 0) { + LOG_TRACE(Service_LDR, "CRO \"%s\" imports \"%s\" from \"%s\"", + ModuleName().data(), symbol_name.data(), source.ModuleName().data()); + + ResultCode result = ApplyRelocationBatch(relocation_addr, symbol_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + + return MakeResult<bool>(false); + } + + return MakeResult<bool>(true); + }); + if (result.IsError()) { + return result; + } + } + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ResetImportNamedSymbol() { + u32 unresolved_symbol = GetOnUnresolvedAddress(); + + u32 symbol_import_num = GetField(ImportNamedSymbolNum); + for (u32 i = 0; i < symbol_import_num; ++i) { + ImportNamedSymbolEntry entry; + GetEntry(i, entry); + VAddr relocation_addr = entry.relocation_batch_offset; + ExternalRelocationEntry relocation_entry; + Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); + + ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw); + return result; + } + + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ResetImportIndexedSymbol() { + u32 unresolved_symbol = GetOnUnresolvedAddress(); + + u32 import_num = GetField(ImportIndexedSymbolNum); + for (u32 i = 0; i < import_num; ++i) { + ImportIndexedSymbolEntry entry; + GetEntry(i, entry); + VAddr relocation_addr = entry.relocation_batch_offset; + ExternalRelocationEntry relocation_entry; + Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); + + ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw); + return result; + } + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ResetImportAnonymousSymbol() { + u32 unresolved_symbol = GetOnUnresolvedAddress(); + + u32 import_num = GetField(ImportAnonymousSymbolNum); + for (u32 i = 0; i < import_num; ++i) { + ImportAnonymousSymbolEntry entry; + GetEntry(i, entry); + VAddr relocation_addr = entry.relocation_batch_offset; + ExternalRelocationEntry relocation_entry; + Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); + + ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw); + return result; + } + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ApplyModuleImport(VAddr crs_address) { + u32 import_strings_size = GetField(ImportStringsSize); + + u32 import_module_num = GetField(ImportModuleNum); + for (u32 i = 0; i < import_module_num; ++i) { + ImportModuleEntry entry; + GetEntry(i, entry); + std::string want_cro_name = Memory::ReadCString(entry.name_offset, import_strings_size); + + ResultCode result = ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> { + if (want_cro_name == source.ModuleName()) { + LOG_INFO(Service_LDR, "CRO \"%s\" imports %d indexed symbols from \"%s\"", + ModuleName().data(), entry.import_indexed_symbol_num, source.ModuleName().data()); + for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { + ImportIndexedSymbolEntry im; + entry.GetImportIndexedSymbolEntry(j, im); + ExportIndexedSymbolEntry ex; + source.GetEntry(im.index, ex); + u32 symbol_address = source.SegmentTagToAddress(ex.symbol_position); + LOG_TRACE(Service_LDR, " Imports 0x%08X", symbol_address); + ResultCode result = ApplyRelocationBatch(im.relocation_batch_offset, symbol_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + } + LOG_INFO(Service_LDR, "CRO \"%s\" imports %d anonymous symbols from \"%s\"", + ModuleName().data(), entry.import_anonymous_symbol_num, source.ModuleName().data()); + for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { + ImportAnonymousSymbolEntry im; + entry.GetImportAnonymousSymbolEntry(j, im); + u32 symbol_address = source.SegmentTagToAddress(im.symbol_position); + LOG_TRACE(Service_LDR, " Imports 0x%08X", symbol_address); + ResultCode result = ApplyRelocationBatch(im.relocation_batch_offset, symbol_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + } + return MakeResult<bool>(false); + } + return MakeResult<bool>(true); + }); + if (result.IsError()) { + return result; + } + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ApplyExportNamedSymbol(CROHelper target) { + LOG_DEBUG(Service_LDR, "CRO \"%s\" exports named symbols to \"%s\"", + ModuleName().data(), target.ModuleName().data()); + u32 target_import_strings_size = target.GetField(ImportStringsSize); + u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum); + for (u32 i = 0; i < target_symbol_import_num; ++i) { + ImportNamedSymbolEntry entry; + target.GetEntry(i, entry); + VAddr relocation_addr = entry.relocation_batch_offset; + ExternalRelocationEntry relocation_entry; + Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); + + if (!relocation_entry.is_batch_resolved) { + std::string symbol_name = Memory::ReadCString(entry.name_offset, target_import_strings_size); + u32 symbol_address = FindExportNamedSymbol(symbol_name); + if (symbol_address != 0) { + LOG_TRACE(Service_LDR, " exports symbol \"%s\"", symbol_name.data()); + ResultCode result = target.ApplyRelocationBatch(relocation_addr, symbol_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + } + } + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ResetExportNamedSymbol(CROHelper target) { + LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports named symbols to \"%s\"", + ModuleName().data(), target.ModuleName().data()); + u32 unresolved_symbol = target.GetOnUnresolvedAddress(); + u32 target_import_strings_size = target.GetField(ImportStringsSize); + u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum); + for (u32 i = 0; i < target_symbol_import_num; ++i) { + ImportNamedSymbolEntry entry; + target.GetEntry(i, entry); + VAddr relocation_addr = entry.relocation_batch_offset; + ExternalRelocationEntry relocation_entry; + Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); + + if (relocation_entry.is_batch_resolved) { + std::string symbol_name = Memory::ReadCString(entry.name_offset, target_import_strings_size); + u32 symbol_address = FindExportNamedSymbol(symbol_name); + if (symbol_address != 0) { + LOG_TRACE(Service_LDR, " unexports symbol \"%s\"", symbol_name.data()); + ResultCode result = target.ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + } + } + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ApplyModuleExport(CROHelper target) { + std::string module_name = ModuleName(); + u32 target_import_string_size = target.GetField(ImportStringsSize); + u32 target_import_module_num = target.GetField(ImportModuleNum); + for (u32 i = 0; i < target_import_module_num; ++i) { + ImportModuleEntry entry; + target.GetEntry(i, entry); + + if (Memory::ReadCString(entry.name_offset, target_import_string_size) != module_name) + continue; + + LOG_INFO(Service_LDR, "CRO \"%s\" exports %d indexed symbols to \"%s\"", + module_name.data(), entry.import_indexed_symbol_num, target.ModuleName().data()); + for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { + ImportIndexedSymbolEntry im; + entry.GetImportIndexedSymbolEntry(j, im); + ExportIndexedSymbolEntry ex; + GetEntry(im.index, ex); + u32 symbol_address = SegmentTagToAddress(ex.symbol_position); + LOG_TRACE(Service_LDR, " exports symbol 0x%08X", symbol_address); + ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, symbol_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + } + + LOG_INFO(Service_LDR, "CRO \"%s\" exports %d anonymous symbols to \"%s\"", + module_name.data(), entry.import_anonymous_symbol_num, target.ModuleName().data()); + for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { + ImportAnonymousSymbolEntry im; + entry.GetImportAnonymousSymbolEntry(j, im); + u32 symbol_address = SegmentTagToAddress(im.symbol_position); + LOG_TRACE(Service_LDR, " exports symbol 0x%08X", symbol_address); + ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, symbol_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + } + } + + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ResetModuleExport(CROHelper target) { + u32 unresolved_symbol = target.GetOnUnresolvedAddress(); + + std::string module_name = ModuleName(); + u32 target_import_string_size = target.GetField(ImportStringsSize); + u32 target_import_module_num = target.GetField(ImportModuleNum); + for (u32 i = 0; i < target_import_module_num; ++i) { + ImportModuleEntry entry; + target.GetEntry(i, entry); + + if (Memory::ReadCString(entry.name_offset, target_import_string_size) != module_name) + continue; + + LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports indexed symbols to \"%s\"", + module_name.data(), target.ModuleName().data()); + for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { + ImportIndexedSymbolEntry im; + entry.GetImportIndexedSymbolEntry(j, im); + ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + } + + LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports anonymous symbols to \"%s\"", + module_name.data(), target.ModuleName().data()); + for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { + ImportAnonymousSymbolEntry im; + entry.GetImportAnonymousSymbolEntry(j, im); + ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + } + } + + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ApplyExitRelocations(VAddr crs_address) { + u32 import_strings_size = GetField(ImportStringsSize); + u32 symbol_import_num = GetField(ImportNamedSymbolNum); + for (u32 i = 0; i < symbol_import_num; ++i) { + ImportNamedSymbolEntry entry; + GetEntry(i, entry); + VAddr relocation_addr = entry.relocation_batch_offset; + ExternalRelocationEntry relocation_entry; + Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); + + if (Memory::ReadCString(entry.name_offset, import_strings_size) == "__aeabi_atexit"){ + ResultCode result = ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> { + u32 symbol_address = source.FindExportNamedSymbol("nnroAeabiAtexit_"); + + if (symbol_address != 0) { + LOG_DEBUG(Service_LDR, "CRO \"%s\" import exit function from \"%s\"", + ModuleName().data(), source.ModuleName().data()); + + ResultCode result = ApplyRelocationBatch(relocation_addr, symbol_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); + return result; + } + + return MakeResult<bool>(false); + } + + return MakeResult<bool>(true); + }); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying exit relocation %08X", result.raw); + return result; + } + } + } + return RESULT_SUCCESS; +} + +/** + * Verifies a string or a string table matching a predicted size (i.e. terminated by 0) + * if it is not empty. There can be many other nulls in the string table because + * they are composed by many sub strings. This function is to check whether the + * whole string (table) is terminated properly, despite that it is not actually one string. + * @param address the virtual address of the string (table) + * @param size the size of the string (table), including the terminating 0 + * @returns ResultCode RESULT_SUCCESS if the size matches, otherwise error code. + */ +static ResultCode VerifyStringTableLength(VAddr address, u32 size) { + if (size != 0) { + if (Memory::Read8(address + size - 1) != 0) + return CROFormatError(0x0B); + } + return RESULT_SUCCESS; +} + +ResultCode CROHelper::Rebase(VAddr crs_address, u32 cro_size, + VAddr data_segment_addresss, u32 data_segment_size, + VAddr bss_segment_address, u32 bss_segment_size, bool is_crs) { + + ResultCode result = RebaseHeader(cro_size); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error rebasing header %08X", result.raw); + return result; + } + + result = VerifyStringTableLength(GetField(ModuleNameOffset), GetField(ModuleNameSize)); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error verifying module name %08X", result.raw); + return result; + } + + u32 prev_data_segment_address = 0; + if (!is_crs) { + auto result_val = RebaseSegmentTable(cro_size, + data_segment_addresss, data_segment_size, + bss_segment_address, bss_segment_size); + if (result_val.Failed()) { + LOG_ERROR(Service_LDR, "Error rebasing segment table %08X", result_val.Code().raw); + return result_val.Code(); + } + prev_data_segment_address = *result_val; + } + + result = RebaseExportNamedSymbolTable(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error rebasing symbol export table %08X", result.raw); + return result; + } + + result = VerifyExportTreeTable(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error verifying export tree %08X", result.raw); + return result; + } + + result = VerifyStringTableLength(GetField(ExportStringsOffset), GetField(ExportStringsSize)); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error verifying export strings %08X", result.raw); + return result; + } + + result = RebaseImportModuleTable(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error rebasing object table %08X", result.raw); + return result; + } + + result = ResetExternalRelocations(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error resetting all external relocations %08X", result.raw); + return result; + } + + result = RebaseImportNamedSymbolTable(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error rebasing symbol import table %08X", result.raw); + return result; + } + + result = RebaseImportIndexedSymbolTable(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error rebasing index import table %08X", result.raw); + return result; + } + + result = RebaseImportAnonymousSymbolTable(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error rebasing offset import table %08X", result.raw); + return result; + } + + result = VerifyStringTableLength(GetField(ImportStringsOffset), GetField(ImportStringsSize)); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error verifying import strings %08X", result.raw); + return result; + } + + if (!is_crs) { + result = ApplyStaticAnonymousSymbolToCRS(crs_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying offset export to CRS %08X", result.raw); + return result; + } + } + + result = ApplyInternalRelocations(prev_data_segment_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying internal relocations %08X", result.raw); + return result; + } + + if (!is_crs) { + result = ApplyExitRelocations(crs_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying exit relocations %08X", result.raw); + return result; + } + } + + return RESULT_SUCCESS; +} + +void CROHelper::Unrebase(bool is_crs) { + UnrebaseImportAnonymousSymbolTable(); + UnrebaseImportIndexedSymbolTable(); + UnrebaseImportNamedSymbolTable(); + UnrebaseImportModuleTable(); + UnrebaseExportNamedSymbolTable(); + + if (!is_crs) + UnrebaseSegmentTable(); + + SetNextModule(0); + SetPreviousModule(0); + + SetField(FixedSize, 0); + + UnrebaseHeader(); +} + +ResultCode CROHelper::VerifyHash(u32 cro_size, VAddr crr) const { + // TODO(wwylele): actually verify the hash + return RESULT_SUCCESS; +} + +ResultCode CROHelper::Link(VAddr crs_address, bool link_on_load_bug_fix) { + ResultCode result = RESULT_SUCCESS; + + { + VAddr data_segment_address; + if (link_on_load_bug_fix) { + // this is a bug fix introduced by 7.2.0-17's LoadCRO_New + // The bug itself is: + // If a relocation target is in .data segment, it will relocate to the + // user-specified buffer. But if this is linking during loading, + // the .data segment hasn't been tranfer from CRO to the buffer, + // thus the relocation will be overwritten by data transfer. + // To fix this bug, we need temporarily restore the old .data segment + // offset and apply imported symbols. + + // RO service seems assuming segment_index == segment_type, + // so we do the same + if (GetField(SegmentNum) >= 2) { // means we have .data segment + SegmentEntry entry; + GetEntry(2, entry); + ASSERT(entry.type == SegmentType::Data); + data_segment_address = entry.offset; + entry.offset = GetField(DataOffset); + SetEntry(2, entry); + } + } + SCOPE_EXIT({ + // Restore the new .data segment address after importing + if (link_on_load_bug_fix) { + if (GetField(SegmentNum) >= 2) { + SegmentEntry entry; + GetEntry(2, entry); + entry.offset = data_segment_address; + SetEntry(2, entry); + } + } + }); + + // Imports named symbols from other modules + result = ApplyImportNamedSymbol(crs_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying symbol import %08X", result.raw); + return result; + } + + // Imports indexed and anonymous symbols from other modules + result = ApplyModuleImport(crs_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying module import %08X", result.raw); + return result; + } + } + + // Exports symbols to other modules + result = ForEachAutoLinkCRO(crs_address, [this](CROHelper target) -> ResultVal<bool> { + ResultCode result = ApplyExportNamedSymbol(target); + if (result.IsError()) + return result; + + result = ApplyModuleExport(target); + if (result.IsError()) + return result; + + return MakeResult<bool>(true); + }); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error applying export %08X", result.raw); + return result; + } + + return RESULT_SUCCESS; +} + +ResultCode CROHelper::Unlink(VAddr crs_address) { + + // Resets all imported named symbols + ResultCode result = ResetImportNamedSymbol(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error resetting symbol import %08X", result.raw); + return result; + } + + // Resets all imported indexed symbols + result = ResetImportIndexedSymbol(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error resetting indexed import %08X", result.raw); + return result; + } + + // Resets all imported anonymous symbols + result = ResetImportAnonymousSymbol(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error resetting anonymous import %08X", result.raw); + return result; + } + + // Resets all symbols in other modules imported from this module + // Note: the RO service seems only searching in auto-link modules + result = ForEachAutoLinkCRO(crs_address, [this](CROHelper target) -> ResultVal<bool> { + ResultCode result = ResetExportNamedSymbol(target); + if (result.IsError()) + return result; + + result = ResetModuleExport(target); + if (result.IsError()) + return result; + + return MakeResult<bool>(true); + }); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error resetting export %08X", result.raw); + return result; + } + + return RESULT_SUCCESS; +} + +ResultCode CROHelper::ClearRelocations() { + ResultCode result = ClearExternalRelocations(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error clearing external relocations %08X", result.raw); + return result; + } + + result = ClearInternalRelocations(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error clearing internal relocations %08X", result.raw); + return result; + } + return RESULT_SUCCESS; +} + +void CROHelper::InitCRS() { + SetNextModule(0); + SetPreviousModule(0); +} + +void CROHelper::Register(VAddr crs_address, bool auto_link) { + CROHelper crs(crs_address); + CROHelper head(auto_link ? crs.NextModule() : crs.PreviousModule()); + + if (head.module_address) { + // there are already CROs registered + // register as the new tail + CROHelper tail(head.PreviousModule()); + + // link with the old tail + ASSERT(tail.NextModule() == 0); + SetPreviousModule(tail.module_address); + tail.SetNextModule(module_address); + + // set previous of the head pointing to the new tail + head.SetPreviousModule(module_address); + } else { + // register as the first CRO + // set previous to self as tail + SetPreviousModule(module_address); + + // set self as head + if (auto_link) + crs.SetNextModule(module_address); + else + crs.SetPreviousModule(module_address); + } + + // the new one is the tail + SetNextModule(0); +} + +void CROHelper::Unregister(VAddr crs_address) { + CROHelper crs(crs_address); + CROHelper next_head(crs.NextModule()), previous_head(crs.PreviousModule()); + CROHelper next(NextModule()), previous(PreviousModule()); + + if (module_address == next_head.module_address || module_address == previous_head.module_address) { + // removing head + if (next.module_address) { + // the next is new head + // let its previous point to the tail + next.SetPreviousModule(previous.module_address); + } + + // set new head + if (module_address == previous_head.module_address) { + crs.SetPreviousModule(next.module_address); + } else { + crs.SetNextModule(next.module_address); + } + } else if (next.module_address) { + // link previous and next + previous.SetNextModule(next.module_address); + next.SetPreviousModule(previous.module_address); + } else { + // removing tail + // set previous as new tail + previous.SetNextModule(0); + + // let head's previous point to the new tail + if (next_head.module_address && next_head.PreviousModule() == module_address) { + next_head.SetPreviousModule(previous.module_address); + } else if (previous_head.module_address && previous_head.PreviousModule() == module_address) { + previous_head.SetPreviousModule(previous.module_address); + } else { + UNREACHABLE(); + } + } + + // unlink self + SetNextModule(0); + SetPreviousModule(0); +} + +u32 CROHelper::GetFixEnd(u32 fix_level) const { + u32 end = CRO_HEADER_SIZE; + end = std::max<u32>(end, GetField(CodeOffset) + GetField(CodeSize)); + + u32 entry_size_i = 2; + int field = ModuleNameOffset; + while (true) { + end = std::max<u32>(end, + GetField(static_cast<HeaderField>(field)) + + GetField(static_cast<HeaderField>(field + 1)) * ENTRY_SIZE[entry_size_i]); + + ++entry_size_i; + field += 2; + + if (field == FIX_BARRIERS[fix_level]) + return end; + } +} + +u32 CROHelper::Fix(u32 fix_level) { + u32 fix_end = GetFixEnd(fix_level); + + if (fix_level != 0) { + SetField(Magic, MAGIC_FIXD); + + for (int field = FIX_BARRIERS[fix_level]; field < Fix0Barrier; field += 2) { + SetField(static_cast<HeaderField>(field), fix_end); + SetField(static_cast<HeaderField>(field + 1), 0); + } + } + + fix_end = Common::AlignUp(fix_end, Memory::PAGE_SIZE); + + u32 fixed_size = fix_end - module_address; + SetField(FixedSize, fixed_size); + return fixed_size; +} + +bool CROHelper::IsLoaded() const { + u32 magic = GetField(Magic); + if (magic != MAGIC_CRO0 && magic != MAGIC_FIXD) + return false; + + // TODO(wwylele): verify memory state here after memory aliasing is implemented + + return true; +} + +std::tuple<VAddr, u32> CROHelper::GetExecutablePages() const { + u32 segment_num = GetField(SegmentNum); + for (u32 i = 0; i < segment_num; ++i) { + SegmentEntry entry; + GetEntry(i, entry); + if (entry.type == SegmentType::Code && entry.size != 0) { + VAddr begin = Common::AlignDown(entry.offset, Memory::PAGE_SIZE); + VAddr end = Common::AlignUp(entry.offset + entry.size, Memory::PAGE_SIZE); + return std::make_tuple(begin, end - begin); + } + } + return std::make_tuple(0, 0); +} + +} // namespace diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h new file mode 100644 index 000000000..34e357afd --- /dev/null +++ b/src/core/hle/service/ldr_ro/cro_helper.h @@ -0,0 +1,691 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <tuple> + +#include "common/common_types.h" +#include "common/swap.h" + +#include "core/memory.h" +#include "core/hle/result.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace LDR_RO + +namespace LDR_RO { + +// GCC versions < 5.0 do not implement std::is_trivially_copyable. +// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable. +#if (__GNUC__ >= 5) || defined(__clang__) + #define ASSERT_CRO_STRUCT(name, size) \ + static_assert(std::is_standard_layout<name>::value, "CRO structure " #name " doesn't use standard layout"); \ + static_assert(std::is_trivially_copyable<name>::value, "CRO structure " #name " isn't trivially copyable"); \ + static_assert(sizeof(name) == (size), "Unexpected struct size for CRO structure " #name) +#else + #define ASSERT_CRO_STRUCT(name, size) \ + static_assert(std::is_standard_layout<name>::value, "CRO structure " #name " doesn't use standard layout"); \ + static_assert(sizeof(name) == (size), "Unexpected struct size for CRO structure " #name) +#endif + +static constexpr u32 CRO_HEADER_SIZE = 0x138; +static constexpr u32 CRO_HASH_SIZE = 0x80; + +/// Represents a loaded module (CRO) with interfaces manipulating it. +class CROHelper final { +public: + explicit CROHelper(VAddr cro_address) : module_address(cro_address) { + } + + std::string ModuleName() const { + return Memory::ReadCString(GetField(ModuleNameOffset), GetField(ModuleNameSize)); + } + + u32 GetFileSize() const { + return GetField(FileSize); + } + + /** + * Rebases the module according to its address. + * @param crs_address the virtual address of the static module + * @param cro_size the size of the CRO file + * @param data_segment_address buffer address for .data segment + * @param data_segment_size the buffer size for .data segment + * @param bss_segment_address the buffer address for .bss segment + * @param bss_segment_size the buffer size for .bss segment + * @param is_crs true if the module itself is the static module + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode Rebase(VAddr crs_address, u32 cro_size, + VAddr data_segment_addresss, u32 data_segment_size, + VAddr bss_segment_address, u32 bss_segment_size, bool is_crs); + + /** + * Unrebases the module. + * @param is_crs true if the module itself is the static module + */ + void Unrebase(bool is_crs); + + /** + * Verifies module hash by CRR. + * @param cro_size the size of the CRO + * @param crr the virtual address of the CRR + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode VerifyHash(u32 cro_size, VAddr crr) const; + + /** + * Links this module with all registered auto-link module. + * @param crs_address the virtual address of the static module + * @param link_on_load_bug_fix true if links when loading and fixes the bug + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode Link(VAddr crs_address, bool link_on_load_bug_fix); + + /** + * Unlinks this module with other modules. + * @param crs_address the virtual address of the static module + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode Unlink(VAddr crs_address); + + /** + * Clears all relocations to zero. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ClearRelocations(); + + /// Initialize this module as the static module (CRS) + void InitCRS(); + + /** + * Registers this module and adds it to the module list. + * @param crs_address the virtual address of the static module + * @auto_link whether to register as an auto link module + */ + void Register(VAddr crs_address, bool auto_link); + + /** + * Unregisters this module and removes from the module list. + * @param crs_address the virtual address of the static module + */ + void Unregister(VAddr crs_address); + + /** + * Gets the end of reserved data according to the fix level. + * @param fix_level fix level from 0 to 3 + * @returns the end of reserved data. + */ + u32 GetFixEnd(u32 fix_level) const; + + /** + * Zeros offsets to cropped data according to the fix level and marks as fixed. + * @param fix_level fix level from 0 to 3 + * @returns page-aligned size of the module after fixing. + */ + u32 Fix(u32 fix_level); + + bool IsFixed() const { + return GetField(Magic) == MAGIC_FIXD; + } + + u32 GetFixedSize() const { + return GetField(FixedSize); + } + + bool IsLoaded() const; + + /** + * Gets the page address and size of the code segment. + * @returns a tuple of (address, size); (0, 0) if the code segment doesn't exist. + */ + std::tuple<VAddr, u32> GetExecutablePages() const; + +private: + const VAddr module_address; ///< the virtual address of this module + + /** + * Each item in this enum represents a u32 field in the header begin from address+0x80, successively. + * We don't directly use a struct here, to avoid GetPointer, reinterpret_cast, or Read/WriteBlock repeatedly. + */ + enum HeaderField { + Magic = 0, + NameOffset, + NextCRO, + PreviousCRO, + FileSize, + BssSize, + FixedSize, + UnknownZero, + UnkSegmentTag, + OnLoadSegmentTag, + OnExitSegmentTag, + OnUnresolvedSegmentTag, + + CodeOffset, + CodeSize, + DataOffset, + DataSize, + ModuleNameOffset, + ModuleNameSize, + SegmentTableOffset, + SegmentNum, + + ExportNamedSymbolTableOffset, + ExportNamedSymbolNum, + ExportIndexedSymbolTableOffset, + ExportIndexedSymbolNum, + ExportStringsOffset, + ExportStringsSize, + ExportTreeTableOffset, + ExportTreeNum, + + ImportModuleTableOffset, + ImportModuleNum, + ExternalRelocationTableOffset, + ExternalRelocationNum, + ImportNamedSymbolTableOffset, + ImportNamedSymbolNum, + ImportIndexedSymbolTableOffset, + ImportIndexedSymbolNum, + ImportAnonymousSymbolTableOffset, + ImportAnonymousSymbolNum, + ImportStringsOffset, + ImportStringsSize, + + StaticAnonymousSymbolTableOffset, + StaticAnonymousSymbolNum, + InternalRelocationTableOffset, + InternalRelocationNum, + StaticRelocationTableOffset, + StaticRelocationNum, + Fix0Barrier, + + Fix3Barrier = ExportNamedSymbolTableOffset, + Fix2Barrier = ImportModuleTableOffset, + Fix1Barrier = StaticAnonymousSymbolTableOffset, + }; + static_assert(Fix0Barrier == (CRO_HEADER_SIZE - CRO_HASH_SIZE) / 4, "CRO Header fields are wrong!"); + + enum class SegmentType : u32 { + Code = 0, + ROData = 1, + Data = 2, + BSS = 3, + }; + + /** + * Identifies a program location inside of a segment. + * Required to refer to program locations because individual segments may be relocated independently of each other. + */ + union SegmentTag { + u32_le raw; + BitField<0, 4, u32_le> segment_index; + BitField<4, 28, u32_le> offset_into_segment; + + SegmentTag() = default; + explicit SegmentTag(u32 raw_) : raw(raw_) {} + }; + + /// Information of a segment in this module. + struct SegmentEntry { + u32_le offset; + u32_le size; + SegmentType type; + + static constexpr HeaderField TABLE_OFFSET_FIELD = SegmentTableOffset; + }; + ASSERT_CRO_STRUCT(SegmentEntry, 12); + + /// Identifies a named symbol exported from this module. + struct ExportNamedSymbolEntry { + u32_le name_offset; // pointing to a substring in ExportStrings + SegmentTag symbol_position; // to self's segment + + static constexpr HeaderField TABLE_OFFSET_FIELD = ExportNamedSymbolTableOffset; + }; + ASSERT_CRO_STRUCT(ExportNamedSymbolEntry, 8); + + /// Identifies an indexed symbol exported from this module. + struct ExportIndexedSymbolEntry { + SegmentTag symbol_position; // to self's segment + + static constexpr HeaderField TABLE_OFFSET_FIELD = ExportIndexedSymbolTableOffset; + }; + ASSERT_CRO_STRUCT(ExportIndexedSymbolEntry, 4); + + /// A tree node in the symbol lookup tree. + struct ExportTreeEntry { + u16_le test_bit; // bit address into the name to test + union Child { + u16_le raw; + BitField<0, 15, u16_le> next_index; + BitField<15, 1, u16_le> is_end; + } left, right; + u16_le export_table_index; // index of an ExportNamedSymbolEntry + + static constexpr HeaderField TABLE_OFFSET_FIELD = ExportTreeTableOffset; + }; + ASSERT_CRO_STRUCT(ExportTreeEntry, 8); + + /// Identifies a named symbol imported from another module. + struct ImportNamedSymbolEntry { + u32_le name_offset; // pointing to a substring in ImportStrings + u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable + + static constexpr HeaderField TABLE_OFFSET_FIELD = ImportNamedSymbolTableOffset; + }; + ASSERT_CRO_STRUCT(ImportNamedSymbolEntry, 8); + + /// Identifies an indexed symbol imported from another module. + struct ImportIndexedSymbolEntry { + u32_le index; // index of an ExportIndexedSymbolEntry in the exporting module + u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable + + static constexpr HeaderField TABLE_OFFSET_FIELD = ImportIndexedSymbolTableOffset; + }; + ASSERT_CRO_STRUCT(ImportIndexedSymbolEntry, 8); + + /// Identifies an anonymous symbol imported from another module. + struct ImportAnonymousSymbolEntry { + SegmentTag symbol_position; // in the exporting segment + u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable + + static constexpr HeaderField TABLE_OFFSET_FIELD = ImportAnonymousSymbolTableOffset; + }; + ASSERT_CRO_STRUCT(ImportAnonymousSymbolEntry, 8); + + /// Information of a imported module and symbols imported from it. + struct ImportModuleEntry { + u32_le name_offset; // pointing to a substring in ImportStrings + u32_le import_indexed_symbol_table_offset; // pointing to a subtable in ImportIndexedSymbolTable + u32_le import_indexed_symbol_num; + u32_le import_anonymous_symbol_table_offset; // pointing to a subtable in ImportAnonymousSymbolTable + u32_le import_anonymous_symbol_num; + + static constexpr HeaderField TABLE_OFFSET_FIELD = ImportModuleTableOffset; + + void GetImportIndexedSymbolEntry(u32 index, ImportIndexedSymbolEntry& entry) { + Memory::ReadBlock(import_indexed_symbol_table_offset + index * sizeof(ImportIndexedSymbolEntry), + &entry, sizeof(ImportIndexedSymbolEntry)); + } + + void GetImportAnonymousSymbolEntry(u32 index, ImportAnonymousSymbolEntry& entry) { + Memory::ReadBlock(import_anonymous_symbol_table_offset + index * sizeof(ImportAnonymousSymbolEntry), + &entry, sizeof(ImportAnonymousSymbolEntry)); + } + }; + ASSERT_CRO_STRUCT(ImportModuleEntry, 20); + + enum class RelocationType : u8 { + Nothing = 0, + AbsoluteAddress = 2, + RelativeAddress = 3, + ThumbBranch = 10, + ArmBranch = 28, + ModifyArmBranch = 29, + AbsoluteAddress2 = 38, + AlignedRelativeAddress = 42, + }; + + struct RelocationEntry { + SegmentTag target_position; // to self's segment as an ExternalRelocationEntry; to static module segment as a StaticRelocationEntry + RelocationType type; + u8 is_batch_end; + u8 is_batch_resolved; // set at a batch beginning if the batch is resolved + INSERT_PADDING_BYTES(1); + u32_le addend; + }; + + /// Identifies a normal cross-module relocation. + struct ExternalRelocationEntry : RelocationEntry { + static constexpr HeaderField TABLE_OFFSET_FIELD = ExternalRelocationTableOffset; + }; + ASSERT_CRO_STRUCT(ExternalRelocationEntry, 12); + + /// Identifies a special static relocation (no game is known using this). + struct StaticRelocationEntry : RelocationEntry { + static constexpr HeaderField TABLE_OFFSET_FIELD = StaticRelocationTableOffset; + }; + ASSERT_CRO_STRUCT(StaticRelocationEntry, 12); + + /// Identifies a in-module relocation. + struct InternalRelocationEntry { + SegmentTag target_position; // to self's segment + RelocationType type; + u8 symbol_segment; + INSERT_PADDING_BYTES(2); + u32_le addend; + + static constexpr HeaderField TABLE_OFFSET_FIELD = InternalRelocationTableOffset; + }; + ASSERT_CRO_STRUCT(InternalRelocationEntry, 12); + + /// Identifies a special static anonymous symbol (no game is known using this). + struct StaticAnonymousSymbolEntry { + SegmentTag symbol_position; // to self's segment + u32_le relocation_batch_offset; // pointing to a relocation batch in StaticRelocationTable + + static constexpr HeaderField TABLE_OFFSET_FIELD = StaticAnonymousSymbolTableOffset; + }; + ASSERT_CRO_STRUCT(StaticAnonymousSymbolEntry, 8); + + /** + * Entry size of each table, from Code to StaticRelocationTable. + * Byte string contents (such as Code) are treated with entries of size 1. + * This is used for verifying the size of each table and calculating the fix end. + */ + static const std::array<int, 17> ENTRY_SIZE; + + /// The offset field of the table where to crop for each fix level + static const std::array<HeaderField, 4> FIX_BARRIERS; + + static constexpr u32 MAGIC_CRO0 = 0x304F5243; + static constexpr u32 MAGIC_FIXD = 0x44584946; + + VAddr Field(HeaderField field) const { + return module_address + CRO_HASH_SIZE + field * 4; + } + + u32 GetField(HeaderField field) const { + return Memory::Read32(Field(field)); + } + + void SetField(HeaderField field, u32 value) { + Memory::Write32(Field(field), value); + } + + /** + * Reads an entry in one of module tables. + * @param index index of the entry + * @param data where to put the read entry + * @note the entry type must have the static member TABLE_OFFSET_FIELD + * indicating which table the entry is in. + */ + template <typename T> + void GetEntry(std::size_t index, T& data) const { + Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); + } + + /** + * Writes an entry to one of module tables. + * @param index index of the entry + * @param data the entry data to write + * @note the entry type must have the static member TABLE_OFFSET_FIELD + * indicating which table the entry is in. + */ + template <typename T> + void SetEntry(std::size_t index, const T& data) { + Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); + } + + /** + * Converts a segment tag to virtual address in this module. + * @param segment_tag the segment tag to convert + * @returns VAddr the virtual address the segment tag points to; 0 if invalid. + */ + VAddr SegmentTagToAddress(SegmentTag segment_tag) const; + + VAddr NextModule() const { + return GetField(NextCRO); + } + + VAddr PreviousModule() const { + return GetField(PreviousCRO); + } + + void SetNextModule(VAddr next) { + SetField(NextCRO, next); + } + + void SetPreviousModule(VAddr previous) { + SetField(PreviousCRO, previous); + } + + /** + * A helper function iterating over all registered auto-link modules, including the static module. + * @param crs_address the virtual address of the static module + * @param func a function object to operate on a module. It accepts one parameter + * CROHelper and returns ResultVal<bool>. It should return true to continue the iteration, + * false to stop the iteration, or an error code (which will also stop the iteration). + * @returns ResultCode indicating the result of the operation, RESULT_SUCCESS if all iteration success, + * otherwise error code of the last iteration. + */ + template <typename FunctionObject> + static ResultCode ForEachAutoLinkCRO(VAddr crs_address, FunctionObject func) { + VAddr current = crs_address; + while (current != 0) { + CROHelper cro(current); + CASCADE_RESULT(bool next, func(cro)); + if (!next) + break; + current = cro.NextModule(); + } + return RESULT_SUCCESS; + } + + /** + * Applies a relocation + * @param target_address where to apply the relocation + * @param relocation_type the type of the relocation + * @param addend address addend applied to the relocated symbol + * @param symbol_address the symbol address to be relocated with + * @param target_future_address the future address of the target. + * Usually equals to target_address, but will be different for a target in .data segment + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ApplyRelocation(VAddr target_address, RelocationType relocation_type, + u32 addend, u32 symbol_address, u32 target_future_address); + + /** + * Clears a relocation to zero + * @param target_address where to apply the relocation + * @param relocation_type the type of the relocation + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ClearRelocation(VAddr target_address, RelocationType relocation_type); + + /** + * Applies or resets a batch of relocations + * @param batch the virtual address of the first relocation in the batch + * @param symbol_address the symbol address to be relocated with + * @param reset false to set the batch to resolved state, true to reset the batch to unresolved state + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool reset = false); + + /** + * Finds an exported named symbol in this module. + * @param name the name of the symbol to find + * @return VAddr the virtual address of the symbol; 0 if not found. + */ + VAddr FindExportNamedSymbol(const std::string& name) const; + + /** + * Rebases offsets in module header according to module address. + * @param cro_size the size of the CRO file + * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. + */ + ResultCode RebaseHeader(u32 cro_size); + + /** + * Rebases offsets in segment table according to module address. + * @param cro_size the size of the CRO file + * @param data_segment_address the buffer address for .data segment + * @param data_segment_size the buffer size for .data segment + * @param bss_segment_address the buffer address for .bss segment + * @param bss_segment_size the buffer size for .bss segment + * @returns ResultVal<VAddr> with the virtual address of .data segment in CRO. + */ + ResultVal<VAddr> RebaseSegmentTable(u32 cro_size, + VAddr data_segment_address, u32 data_segment_size, + VAddr bss_segment_address, u32 bss_segment_size); + + /** + * Rebases offsets in exported named symbol table according to module address. + * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. + */ + ResultCode RebaseExportNamedSymbolTable(); + + /** + * Verifies indices in export tree table. + * @returns ResultCode RESULT_SUCCESS if all indices are verified as valid, otherwise error code. + */ + ResultCode VerifyExportTreeTable() const; + + /** + * Rebases offsets in exported module table according to module address. + * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. + */ + ResultCode RebaseImportModuleTable(); + + /** + * Rebases offsets in imported named symbol table according to module address. + * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. + */ + ResultCode RebaseImportNamedSymbolTable(); + + /** + * Rebases offsets in imported indexed symbol table according to module address. + * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. + */ + ResultCode RebaseImportIndexedSymbolTable(); + + /** + * Rebases offsets in imported anonymous symbol table according to module address. + * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. + */ + ResultCode RebaseImportAnonymousSymbolTable(); + + /** + * Gets the address of OnUnresolved function in this module. + * Used as the applied symbol for reset relocation. + * @returns the virtual address of OnUnresolved. 0 if not provided. + */ + VAddr GetOnUnresolvedAddress(); + + /** + * Resets all external relocations to unresolved state. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ResetExternalRelocations(); + + /** + * Clears all external relocations to zero. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ClearExternalRelocations(); + + /** + * Applies all static anonymous symbol to the static module. + * @param crs_address the virtual address of the static module + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ApplyStaticAnonymousSymbolToCRS(VAddr crs_address); + + /** + * Applies all internal relocations to the module itself. + * @param old_data_segment_address the virtual address of data segment in CRO buffer + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ApplyInternalRelocations(u32 old_data_segment_address); + + /** + * Clears all internal relocations to zero. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ClearInternalRelocations(); + + /// Unrebases offsets in imported anonymous symbol table + void UnrebaseImportAnonymousSymbolTable(); + + /// Unrebases offsets in imported indexed symbol table + void UnrebaseImportIndexedSymbolTable(); + + /// Unrebases offsets in imported named symbol table + void UnrebaseImportNamedSymbolTable(); + + /// Unrebases offsets in imported module table + void UnrebaseImportModuleTable(); + + /// Unrebases offsets in exported named symbol table + void UnrebaseExportNamedSymbolTable(); + + /// Unrebases offsets in segment table + void UnrebaseSegmentTable(); + + /// Unrebases offsets in module header + void UnrebaseHeader(); + + /** + * Looks up all imported named symbols of this module in all registered auto-link modules, and resolves them if found. + * @param crs_address the virtual address of the static module + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ApplyImportNamedSymbol(VAddr crs_address); + + /** + * Resets all imported named symbols of this module to unresolved state. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ResetImportNamedSymbol(); + + /** + * Resets all imported indexed symbols of this module to unresolved state. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ResetImportIndexedSymbol(); + + /** + * Resets all imported anonymous symbols of this module to unresolved state. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ResetImportAnonymousSymbol(); + + /** + * Finds registered auto-link modules that this module imports, and resolves indexed and anonymous symbols exported by them. + * @param crs_address the virtual address of the static module + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ApplyModuleImport(VAddr crs_address); + + /** + * Resolves target module's imported named symbols that exported by this module. + * @param target the module to resolve. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ApplyExportNamedSymbol(CROHelper target); + + /** + * Resets target's named symbols imported from this module to unresolved state. + * @param target the module to reset. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ResetExportNamedSymbol(CROHelper target); + + /** + * Resolves imported indexed and anonymous symbols in the target module which imports this module. + * @param target the module to resolve. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ApplyModuleExport(CROHelper target); + + /** + * Resets target's indexed and anonymous symbol imported from this module to unresolved state. + * @param target the module to reset. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ResetModuleExport(CROHelper target); + + /** + * Resolves the exit function in this module + * @param crs_address the virtual address of the static module. + * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. + */ + ResultCode ApplyExitRelocations(VAddr crs_address); +}; + +} // namespace diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp new file mode 100644 index 000000000..8ba73ea8d --- /dev/null +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -0,0 +1,748 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/alignment.h" +#include "common/common_types.h" +#include "common/logging/log.h" + +#include "core/arm/arm_interface.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/vm_manager.h" +#include "core/hle/service/ldr_ro/cro_helper.h" +#include "core/hle/service/ldr_ro/ldr_ro.h" +#include "core/hle/service/ldr_ro/memory_synchronizer.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace LDR_RO + +namespace LDR_RO { + +static const ResultCode ERROR_ALREADY_INITIALIZED = // 0xD9612FF9 + ResultCode(ErrorDescription::AlreadyInitialized, ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Permanent); +static const ResultCode ERROR_NOT_INITIALIZED = // 0xD9612FF8 + ResultCode(ErrorDescription::NotInitialized, ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Permanent); +static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F + ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument, ErrorLevel::Usage); +static const ResultCode ERROR_MISALIGNED_ADDRESS = // 0xD9012FF1 + ResultCode(ErrorDescription::MisalignedAddress, ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent); +static const ResultCode ERROR_MISALIGNED_SIZE = // 0xD9012FF2 + ResultCode(ErrorDescription::MisalignedSize, ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent); +static const ResultCode ERROR_ILLEGAL_ADDRESS = // 0xE1612C0F + ResultCode(static_cast<ErrorDescription>(15), ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Usage); +static const ResultCode ERROR_INVALID_MEMORY_STATE = // 0xD8A12C08 + ResultCode(static_cast<ErrorDescription>(8), ErrorModule::RO, ErrorSummary::InvalidState, ErrorLevel::Permanent); +static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D + ResultCode(static_cast<ErrorDescription>(13), ErrorModule::RO, ErrorSummary::InvalidState, ErrorLevel::Permanent); +static const ResultCode ERROR_INVALID_DESCRIPTOR = // 0xD9001830 + ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent); + +static MemorySynchronizer memory_synchronizer; + +// TODO(wwylele): this should be in the per-client storage when we implement multi-process +static VAddr loaded_crs; ///< the virtual address of the static module + +static bool VerifyBufferState(VAddr buffer_ptr, u32 size) { + auto vma = Kernel::g_current_process->vm_manager.FindVMA(buffer_ptr); + return vma != Kernel::g_current_process->vm_manager.vma_map.end() + && vma->second.base + vma->second.size >= buffer_ptr + size + && vma->second.permissions == Kernel::VMAPermission::ReadWrite + && vma->second.meminfo_state == Kernel::MemoryState::Private; +} + +/** + * LDR_RO::Initialize service function + * Inputs: + * 0 : 0x000100C2 + * 1 : CRS buffer pointer + * 2 : CRS Size + * 3 : Process memory address where the CRS will be mapped + * 4 : handle translation descriptor (zero) + * 5 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void Initialize(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + VAddr crs_buffer_ptr = cmd_buff[1]; + u32 crs_size = cmd_buff[2]; + VAddr crs_address = cmd_buff[3]; + u32 descriptor = cmd_buff[4]; + u32 process = cmd_buff[5]; + + LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X, descriptor=0x%08X, process=0x%08X", + crs_buffer_ptr, crs_address, crs_size, descriptor, process); + + if (descriptor != 0) { + LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; + return; + } + + cmd_buff[0] = IPC::MakeHeader(1, 1, 0); + + if (loaded_crs != 0) { + LOG_ERROR(Service_LDR, "Already initialized"); + cmd_buff[1] = ERROR_ALREADY_INITIALIZED.raw; + return; + } + + if (crs_size < CRO_HEADER_SIZE) { + LOG_ERROR(Service_LDR, "CRS is too small"); + cmd_buff[1] = ERROR_BUFFER_TOO_SMALL.raw; + return; + } + + if (crs_buffer_ptr & Memory::PAGE_MASK) { + LOG_ERROR(Service_LDR, "CRS original address is not aligned"); + cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + return; + } + + if (crs_address & Memory::PAGE_MASK) { + LOG_ERROR(Service_LDR, "CRS mapping address is not aligned"); + cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + return; + } + + if (crs_size & Memory::PAGE_MASK) { + LOG_ERROR(Service_LDR, "CRS size is not aligned"); + cmd_buff[1] = ERROR_MISALIGNED_SIZE.raw; + return; + } + + if (!VerifyBufferState(crs_buffer_ptr, crs_size)) { + LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state"); + cmd_buff[1] = ERROR_INVALID_MEMORY_STATE.raw; + return; + } + + if (crs_address < Memory::PROCESS_IMAGE_VADDR || crs_address + crs_size > Memory::PROCESS_IMAGE_VADDR_END) { + LOG_ERROR(Service_LDR, "CRS mapping address is not in the process image region"); + cmd_buff[1] = ERROR_ILLEGAL_ADDRESS.raw; + return; + } + + ResultCode result = RESULT_SUCCESS; + + if (crs_buffer_ptr != crs_address) { + // TODO(wwylele): should be memory aliasing + std::shared_ptr<std::vector<u8>> crs_mem = std::make_shared<std::vector<u8>>(crs_size); + Memory::ReadBlock(crs_buffer_ptr, crs_mem->data(), crs_size); + result = Kernel::g_current_process->vm_manager.MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code).Code(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error mapping memory block %08X", result.raw); + cmd_buff[1] = result.raw; + return; + } + + result = Kernel::g_current_process->vm_manager.ReprotectRange(crs_address, crs_size, Kernel::VMAPermission::Read); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); + cmd_buff[1] = result.raw; + return; + } + + memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size); + } else { + // Do nothing if buffer_ptr == address + // TODO(wwylele): verify this behaviour. This is only seen in the web browser app, + // and the actual behaviour is unclear. "Do nothing" is probably an incorrect implement. + // There is also a chance that another issue causes the app passing wrong arguments. + LOG_WARNING(Service_LDR, "crs_buffer_ptr == crs_address (0x%08X)", crs_address); + } + + CROHelper crs(crs_address); + crs.InitCRS(); + + result = crs.Rebase(0, crs_size, 0, 0, 0, 0, true); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error rebasing CRS 0x%08X", result.raw); + cmd_buff[1] = result.raw; + return; + } + + memory_synchronizer.SynchronizeOriginalMemory(); + + loaded_crs = crs_address; + + cmd_buff[1] = RESULT_SUCCESS.raw; +} + +/** + * LDR_RO::LoadCRR service function + * Inputs: + * 0 : 0x00020082 + * 1 : CRR buffer pointer + * 2 : CRR Size + * 3 : handle translation descriptor (zero) + * 4 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void LoadCRR(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 crr_buffer_ptr = cmd_buff[1]; + u32 crr_size = cmd_buff[2]; + u32 descriptor = cmd_buff[3]; + u32 process = cmd_buff[4]; + + if (descriptor != 0) { + LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; + return; + } + + cmd_buff[0] = IPC::MakeHeader(2, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X, descriptor=0x%08X, process=0x%08X", + crr_buffer_ptr, crr_size, descriptor, process); +} + +/** + * LDR_RO::UnloadCRR service function + * Inputs: + * 0 : 0x00030042 + * 1 : CRR buffer pointer + * 2 : handle translation descriptor (zero) + * 3 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void UnloadCRR(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 crr_buffer_ptr = cmd_buff[1]; + u32 descriptor = cmd_buff[2]; + u32 process = cmd_buff[3]; + + if (descriptor != 0) { + LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; + return; + } + + cmd_buff[0] = IPC::MakeHeader(3, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", + crr_buffer_ptr, descriptor, process); +} + +/** + * LDR_RO::LoadCRO service function + * Inputs: + * 0 : 0x000402C2 (old) / 0x000902C2 (new) + * 1 : CRO buffer pointer + * 2 : memory address where the CRO will be mapped + * 3 : CRO Size + * 4 : .data segment buffer pointer + * 5 : must be zero + * 6 : .data segment buffer size + * 7 : .bss segment buffer pointer + * 8 : .bss segment buffer size + * 9 : (bool) register CRO as auto-link module + * 10 : fix level + * 11 : CRR address (zero if use loaded CRR) + * 12 : handle translation descriptor (zero) + * 13 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : CRO fixed size + * Note: + * This service function has two versions. The function defined here is a + * 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) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + VAddr cro_buffer_ptr = cmd_buff[1]; + VAddr cro_address = cmd_buff[2]; + u32 cro_size = cmd_buff[3]; + VAddr data_segment_address = cmd_buff[4]; + u32 zero = cmd_buff[5]; + u32 data_segment_size = cmd_buff[6]; + u32 bss_segment_address = cmd_buff[7]; + u32 bss_segment_size = cmd_buff[8]; + bool auto_link = (cmd_buff[9] & 0xFF) != 0; + u32 fix_level = cmd_buff[10]; + VAddr crr_address = cmd_buff[11]; + u32 descriptor = cmd_buff[12]; + u32 process = cmd_buff[13]; + + LOG_DEBUG(Service_LDR, "called (%s), cro_buffer_ptr=0x%08X, cro_address=0x%08X, cro_size=0x%X, " + "data_segment_address=0x%08X, zero=%d, data_segment_size=0x%X, bss_segment_address=0x%08X, bss_segment_size=0x%X, " + "auto_link=%s, fix_level=%d, crr_address=0x%08X, descriptor=0x%08X, process=0x%08X", + link_on_load_bug_fix ? "new" : "old", cro_buffer_ptr, cro_address, cro_size, + data_segment_address, zero, data_segment_size, bss_segment_address, bss_segment_size, + auto_link ? "true" : "false", fix_level, crr_address, descriptor, process + ); + + if (descriptor != 0) { + LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; + return; + } + + cmd_buff[0] = IPC::MakeHeader(link_on_load_bug_fix ? 9 : 4, 2, 0); + + if (loaded_crs == 0) { + LOG_ERROR(Service_LDR, "Not initialized"); + cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; + return; + } + + if (cro_size < CRO_HEADER_SIZE) { + LOG_ERROR(Service_LDR, "CRO too small"); + cmd_buff[1] = ERROR_BUFFER_TOO_SMALL.raw; + return; + } + + if (cro_buffer_ptr & Memory::PAGE_MASK) { + LOG_ERROR(Service_LDR, "CRO original address is not aligned"); + cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + return; + } + + if (cro_address & Memory::PAGE_MASK) { + LOG_ERROR(Service_LDR, "CRO mapping address is not aligned"); + cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + return; + } + + if (cro_size & Memory::PAGE_MASK) { + LOG_ERROR(Service_LDR, "CRO size is not aligned"); + cmd_buff[1] = ERROR_MISALIGNED_SIZE.raw; + return; + } + + if (!VerifyBufferState(cro_buffer_ptr, cro_size)) { + LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state"); + cmd_buff[1] = ERROR_INVALID_MEMORY_STATE.raw; + return; + } + + if (cro_address < Memory::PROCESS_IMAGE_VADDR + || cro_address + cro_size > Memory::PROCESS_IMAGE_VADDR_END) { + LOG_ERROR(Service_LDR, "CRO mapping address is not in the process image region"); + cmd_buff[1] = ERROR_ILLEGAL_ADDRESS.raw; + return; + } + + if (zero) { + LOG_ERROR(Service_LDR, "Zero is not zero %d", zero); + cmd_buff[1] = ResultCode(static_cast<ErrorDescription>(29), ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Usage).raw; + return; + } + + ResultCode result = RESULT_SUCCESS; + + if (cro_buffer_ptr != cro_address) { + // TODO(wwylele): should be memory aliasing + std::shared_ptr<std::vector<u8>> cro_mem = std::make_shared<std::vector<u8>>(cro_size); + Memory::ReadBlock(cro_buffer_ptr, cro_mem->data(), cro_size); + result = Kernel::g_current_process->vm_manager.MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code).Code(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error mapping memory block %08X", result.raw); + cmd_buff[1] = result.raw; + return; + } + + result = Kernel::g_current_process->vm_manager.ReprotectRange(cro_address, cro_size, Kernel::VMAPermission::Read); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); + Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); + cmd_buff[1] = result.raw; + return; + } + + memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size); + } else { + // Do nothing if buffer_ptr == address + // TODO(wwylele): verify this behaviour. + // This is derived from the case of LoadCRS with buffer_ptr==address, + // and is never seen in any game. "Do nothing" is probably an incorrect implement. + // There is also a chance that this case is just prohibited. + LOG_WARNING(Service_LDR, "cro_buffer_ptr == cro_address (0x%08X)", cro_address); + } + + CROHelper cro(cro_address); + + result = cro.VerifyHash(cro_size, crr_address); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw); + Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); + cmd_buff[1] = result.raw; + return; + } + + result = cro.Rebase(loaded_crs, cro_size, data_segment_address, data_segment_size, bss_segment_address, bss_segment_size, false); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error rebasing CRO %08X", result.raw); + Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); + cmd_buff[1] = result.raw; + return; + } + + result = cro.Link(loaded_crs, link_on_load_bug_fix); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw); + Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); + cmd_buff[1] = result.raw; + return; + } + + cro.Register(loaded_crs, auto_link); + + u32 fix_size = cro.Fix(fix_level); + + memory_synchronizer.SynchronizeOriginalMemory(); + + // TODO(wwylele): verify the behaviour when buffer_ptr == address + if (cro_buffer_ptr != cro_address) { + if (fix_size != cro_size) { + result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address + fix_size, cro_size - fix_size); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw); + Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); + cmd_buff[1] = result.raw; + return; + } + } + + // Changes the block size + memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size); + } + + VAddr exe_begin; + u32 exe_size; + std::tie(exe_begin, exe_size) = cro.GetExecutablePages(); + if (exe_begin) { + result = Kernel::g_current_process->vm_manager.ReprotectRange(exe_begin, exe_size, Kernel::VMAPermission::ReadExecute); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); + Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fix_size); + cmd_buff[1] = result.raw; + return; + } + } + + Core::g_app_core->ClearInstructionCache(); + + LOG_INFO(Service_LDR, "CRO \"%s\" loaded at 0x%08X, fixed_end=0x%08X", + cro.ModuleName().data(), cro_address, cro_address+fix_size); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = fix_size; +} + +template <bool link_on_load_bug_fix> +static void LoadCRO(Service::Interface* self) { + LoadCRO(self, link_on_load_bug_fix); +} + +/** + * LDR_RO::UnloadCRO service function + * Inputs: + * 0 : 0x000500C2 + * 1 : mapped CRO pointer + * 2 : zero? (RO service doesn't care) + * 3 : original CRO pointer + * 4 : handle translation descriptor (zero) + * 5 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void UnloadCRO(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + VAddr cro_address = cmd_buff[1]; + u32 zero = cmd_buff[2]; + VAddr cro_buffer_ptr = cmd_buff[3]; + u32 descriptor = cmd_buff[4]; + u32 process = cmd_buff[5]; + + LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", + cro_address, zero, cro_buffer_ptr, descriptor, process); + + if (descriptor != 0) { + LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; + return; + } + + CROHelper cro(cro_address); + + cmd_buff[0] = IPC::MakeHeader(5, 1, 0); + + if (loaded_crs == 0) { + LOG_ERROR(Service_LDR, "Not initialized"); + cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; + return; + } + + if (cro_address & Memory::PAGE_MASK) { + LOG_ERROR(Service_LDR, "CRO address is not aligned"); + cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + return; + } + + if (!cro.IsLoaded()) { + LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); + cmd_buff[1] = ERROR_NOT_LOADED.raw; + return; + } + + LOG_INFO(Service_LDR, "Unloading CRO \"%s\"", cro.ModuleName().data()); + + u32 fixed_size = cro.GetFixedSize(); + + cro.Unregister(loaded_crs); + + ResultCode result = cro.Unlink(loaded_crs); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); + cmd_buff[1] = result.raw; + return; + } + + // If the module is not fixed, clears all external/internal relocations + // to restore the state before loading, so that it can be loaded again(?) + if (!cro.IsFixed()) { + result = cro.ClearRelocations(); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error clearing relocations %08X", result.raw); + cmd_buff[1] = result.raw; + return; + } + } + + cro.Unrebase(false); + + memory_synchronizer.SynchronizeOriginalMemory(); + + // TODO(wwylele): verify the behaviour when buffer_ptr == address + if (cro_address != cro_buffer_ptr) { + result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fixed_size); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error unmapping CRO %08X", result.raw); + } + memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr); + } + + Core::g_app_core->ClearInstructionCache(); + + cmd_buff[1] = result.raw; +} + +/** + * LDR_RO::LinkCRO service function + * Inputs: + * 0 : 0x00060042 + * 1 : mapped CRO pointer + * 2 : handle translation descriptor (zero) + * 3 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void LinkCRO(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + VAddr cro_address = cmd_buff[1]; + u32 descriptor = cmd_buff[2]; + u32 process = cmd_buff[3]; + + LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, descriptor=0x%08X, process=0x%08X", + cro_address, descriptor, process); + + if (descriptor != 0) { + LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; + return; + } + + CROHelper cro(cro_address); + + cmd_buff[0] = IPC::MakeHeader(6, 1, 0); + + if (loaded_crs == 0) { + LOG_ERROR(Service_LDR, "Not initialized"); + cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; + return; + } + + if (cro_address & Memory::PAGE_MASK) { + LOG_ERROR(Service_LDR, "CRO address is not aligned"); + cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + return; + } + + if (!cro.IsLoaded()) { + LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); + cmd_buff[1] = ERROR_NOT_LOADED.raw; + return; + } + + LOG_INFO(Service_LDR, "Linking CRO \"%s\"", cro.ModuleName().data()); + + ResultCode result = cro.Link(loaded_crs, false); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw); + } + + memory_synchronizer.SynchronizeOriginalMemory(); + Core::g_app_core->ClearInstructionCache(); + + cmd_buff[1] = result.raw; +} + +/** + * LDR_RO::UnlinkCRO service function + * Inputs: + * 0 : 0x00070042 + * 1 : mapped CRO pointer + * 2 : handle translation descriptor (zero) + * 3 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void UnlinkCRO(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + VAddr cro_address = cmd_buff[1]; + u32 descriptor = cmd_buff[2]; + u32 process = cmd_buff[3]; + + LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, descriptor=0x%08X, process=0x%08X", + cro_address, descriptor, process); + + if (descriptor != 0) { + LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; + return; + } + + CROHelper cro(cro_address); + + cmd_buff[0] = IPC::MakeHeader(7, 1, 0); + + if (loaded_crs == 0) { + LOG_ERROR(Service_LDR, "Not initialized"); + cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; + return; + } + + if (cro_address & Memory::PAGE_MASK) { + LOG_ERROR(Service_LDR, "CRO address is not aligned"); + cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + return; + } + + if (!cro.IsLoaded()) { + LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); + cmd_buff[1] = ERROR_NOT_LOADED.raw; + return; + } + + LOG_INFO(Service_LDR, "Unlinking CRO \"%s\"", cro.ModuleName().data()); + + ResultCode result = cro.Unlink(loaded_crs); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); + } + + memory_synchronizer.SynchronizeOriginalMemory(); + Core::g_app_core->ClearInstructionCache(); + + cmd_buff[1] = result.raw; +} + +/** + * LDR_RO::Shutdown service function + * Inputs: + * 0 : 0x00080042 + * 1 : original CRS buffer pointer + * 2 : handle translation descriptor (zero) + * 3 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void Shutdown(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + VAddr crs_buffer_ptr = cmd_buff[1]; + u32 descriptor = cmd_buff[2]; + u32 process = cmd_buff[3]; + + LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", + crs_buffer_ptr, descriptor, process); + + if (descriptor != 0) { + LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; + return; + } + + if (loaded_crs == 0) { + LOG_ERROR(Service_LDR, "Not initialized"); + cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; + return; + } + + cmd_buff[0] = IPC::MakeHeader(8, 1, 0); + + CROHelper crs(loaded_crs); + crs.Unrebase(true); + + memory_synchronizer.SynchronizeOriginalMemory(); + + ResultCode result = RESULT_SUCCESS; + + // TODO(wwylele): verify the behaviour when buffer_ptr == address + if (loaded_crs != crs_buffer_ptr) { + result = Kernel::g_current_process->vm_manager.UnmapRange(loaded_crs, crs.GetFileSize()); + if (result.IsError()) { + LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw); + } + memory_synchronizer.RemoveMemoryBlock(loaded_crs, crs_buffer_ptr); + } + + loaded_crs = 0; + cmd_buff[1] = result.raw; +} + +const Interface::FunctionInfo FunctionTable[] = { + {0x000100C2, Initialize, "Initialize"}, + {0x00020082, LoadCRR, "LoadCRR"}, + {0x00030042, UnloadCRR, "UnloadCRR"}, + {0x000402C2, LoadCRO<false>, "LoadCRO"}, + {0x000500C2, UnloadCRO, "UnloadCRO"}, + {0x00060042, LinkCRO, "LinkCRO"}, + {0x00070042, UnlinkCRO, "UnlinkCRO"}, + {0x00080042, Shutdown, "Shutdown"}, + {0x000902C2, LoadCRO<true>, "LoadCRO_New"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); + + loaded_crs = 0; + memory_synchronizer.Clear(); +} + +} // namespace diff --git a/src/core/hle/service/ldr_ro.h b/src/core/hle/service/ldr_ro/ldr_ro.h similarity index 100% rename from src/core/hle/service/ldr_ro.h rename to src/core/hle/service/ldr_ro/ldr_ro.h diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.cpp b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp new file mode 100644 index 000000000..4402876e6 --- /dev/null +++ b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp @@ -0,0 +1,46 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> + +#include "common/assert.h" + +#include "core/hle/service/ldr_ro/memory_synchronizer.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace LDR_RO + +namespace LDR_RO { + +auto MemorySynchronizer::FindMemoryBlock(VAddr mapping, VAddr original) { + auto block = std::find_if(memory_blocks.begin(), memory_blocks.end(), [=](MemoryBlock& b){ + return b.original == original; + }); + ASSERT(block->mapping == mapping); + return block; +} + +void MemorySynchronizer::Clear() { + memory_blocks.clear(); +} + +void MemorySynchronizer::AddMemoryBlock(VAddr mapping, VAddr original, u32 size) { + memory_blocks.push_back(MemoryBlock{mapping, original, size}); +} + +void MemorySynchronizer::ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size) { + FindMemoryBlock(mapping, original)->size = size; +} + +void MemorySynchronizer::RemoveMemoryBlock(VAddr mapping, VAddr original) { + memory_blocks.erase(FindMemoryBlock(mapping, original)); +} + +void MemorySynchronizer::SynchronizeOriginalMemory() { + for (auto& block : memory_blocks) { + Memory::CopyBlock(block.original, block.mapping, block.size); + } +} + +} // namespace diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.h b/src/core/hle/service/ldr_ro/memory_synchronizer.h new file mode 100644 index 000000000..92f267912 --- /dev/null +++ b/src/core/hle/service/ldr_ro/memory_synchronizer.h @@ -0,0 +1,44 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> + +#include "core/memory.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace LDR_RO + +namespace LDR_RO { + +/** + * This is a work-around before we implement memory aliasing. + * CRS and CRO are mapped (aliased) to another memory when loading. Games can read + * from both the original buffer and the mapping memory. So we use this to synchronize + * all original buffers with mapping memory after modifying the content. + */ +class MemorySynchronizer { +public: + void Clear(); + + void AddMemoryBlock(VAddr mapping, VAddr original, u32 size); + void ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size); + void RemoveMemoryBlock(VAddr mapping, VAddr original); + + void SynchronizeOriginalMemory(); + +private: + struct MemoryBlock { + VAddr mapping; + VAddr original; + u32 size; + }; + + std::vector<MemoryBlock> memory_blocks; + + auto FindMemoryBlock(VAddr mapping, VAddr original); +}; + +} // namespace diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 395880843..5b8440b77 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -15,7 +15,6 @@ #include "core/hle/service/gsp_gpu.h" #include "core/hle/service/gsp_lcd.h" #include "core/hle/service/http_c.h" -#include "core/hle/service/ldr_ro.h" #include "core/hle/service/mic_u.h" #include "core/hle/service/ns_s.h" #include "core/hle/service/nwm_uds.h" @@ -36,6 +35,7 @@ #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/ir/ir.h" +#include "core/hle/service/ldr_ro/ldr_ro.h" #include "core/hle/service/ndm/ndm.h" #include "core/hle/service/news/news.h" #include "core/hle/service/nim/nim.h" diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index aae955bf8..3c05f836b 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2016 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -15,23 +15,64 @@ namespace SRV { static Kernel::SharedPtr<Kernel::Event> event_handle; -static void Initialize(Service::Interface* self) { +/** + * SRV::RegisterClient service function + * Inputs: + * 0: 0x00010002 + * 1: ProcessId Header (must be 0x20) + * Outputs: + * 0: 0x00010040 + * 1: ResultCode + */ +static void RegisterClient(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - cmd_buff[1] = 0; // No error + if (cmd_buff[1] != IPC::CallingPidDesc()) { + cmd_buff[0] = IPC::MakeHeader(0x0, 0x1, 0); //0x40 + cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, + ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; + return; + } + cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); //0x10040 + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_SRV, "(STUBBED) called"); } -static void GetProcSemaphore(Service::Interface* self) { +/** + * SRV::EnableNotification service function + * Inputs: + * 0: 0x00020000 + * Outputs: + * 0: 0x00020042 + * 1: ResultCode + * 2: Translation descriptor: 0x20 + * 3: Handle to semaphore signaled on process notification + */ +static void EnableNotification(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(bunnei): Change to a semaphore once these have been implemented event_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "SRV:Event"); event_handle->Clear(); - cmd_buff[1] = 0; // No error + cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0x2); // 0x20042 + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = IPC::CopyHandleDesc(1); cmd_buff[3] = Kernel::g_handle_table.Create(event_handle).MoveFrom(); + LOG_WARNING(Service_SRV, "(STUBBED) called"); } +/** + * SRV::GetServiceHandle service function + * Inputs: + * 0: 0x00050100 + * 1-2: 8-byte UTF-8 service name + * 3: Name length + * 4: Flags (bit0: if not set, return port-handle if session-handle unavailable) + * Outputs: + * 1: ResultCode + * 3: Service handle + */ static void GetServiceHandle(Service::Interface* self) { ResultCode res = RESULT_SUCCESS; u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -49,16 +90,80 @@ static void GetServiceHandle(Service::Interface* self) { cmd_buff[1] = res.raw; } +/** + * SRV::Subscribe service function + * Inputs: + * 0: 0x00090040 + * 1: Notification ID + * Outputs: + * 0: 0x00090040 + * 1: ResultCode + */ +static void Subscribe(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 notification_id = cmd_buff[1]; + + cmd_buff[0] = IPC::MakeHeader(0x9, 0x1, 0); // 0x90040 + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); +} + +/** + * SRV::Unsubscribe service function + * Inputs: + * 0: 0x000A0040 + * 1: Notification ID + * Outputs: + * 0: 0x000A0040 + * 1: ResultCode + */ +static void Unsubscribe(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 notification_id = cmd_buff[1]; + + cmd_buff[0] = IPC::MakeHeader(0xA, 0x1, 0); // 0xA0040 + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); +} + +/** + * SRV::PublishToSubscriber service function + * Inputs: + * 0: 0x000C0080 + * 1: Notification ID + * 2: Flags (bit0: only fire if not fired, bit1: report errors) + * Outputs: + * 0: 0x000C0040 + * 1: ResultCode + */ +static void PublishToSubscriber(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 notification_id = cmd_buff[1]; + u8 flags = cmd_buff[2] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0xC, 0x1, 0); // 0xC0040 + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id, flags); +} + const Interface::FunctionInfo FunctionTable[] = { - {0x00010002, Initialize, "Initialize"}, - {0x00020000, GetProcSemaphore, "GetProcSemaphore"}, - {0x00030100, nullptr, "RegisterService"}, - {0x000400C0, nullptr, "UnregisterService"}, - {0x00050100, GetServiceHandle, "GetServiceHandle"}, - {0x000600C2, nullptr, "RegisterHandle"}, - {0x00090040, nullptr, "Subscribe"}, - {0x000B0000, nullptr, "ReceiveNotification"}, - {0x000C0080, nullptr, "PublishToSubscriber"}, + {0x00010002, RegisterClient, "RegisterClient"}, + {0x00020000, EnableNotification, "EnableNotification"}, + {0x00030100, nullptr, "RegisterService"}, + {0x000400C0, nullptr, "UnregisterService"}, + {0x00050100, GetServiceHandle, "GetServiceHandle"}, + {0x000600C2, nullptr, "RegisterPort"}, + {0x000700C0, nullptr, "UnregisterPort"}, + {0x00080100, nullptr, "GetPort"}, + {0x00090040, Subscribe, "Subscribe"}, + {0x000A0040, Unsubscribe, "Unsubscribe"}, + {0x000B0000, nullptr, "ReceiveNotification"}, + {0x000C0080, PublishToSubscriber, "PublishToSubscriber"}, + {0x000D0040, nullptr, "PublishAndGetSubscriber"}, + {0x000E00C0, nullptr, "IsServiceRegistered"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -66,10 +171,11 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { Register(FunctionTable); + event_handle = nullptr; } Interface::~Interface() { event_handle = nullptr; } -} // namespace +} // namespace SRV diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp index 2a1caeaac..4d9272923 100644 --- a/src/core/hle/shared_page.cpp +++ b/src/core/hle/shared_page.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <chrono> #include <cstring> +#include <ctime> +#include "core/core_timing.h" #include "core/hle/shared_page.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -12,6 +15,57 @@ namespace SharedPage { SharedPageDef shared_page; +static int update_time_event; + +/// Gets system time in 3DS format. The epoch is Jan 1900, and the unit is millisecond. +static u64 GetSystemTime() { + auto now = std::chrono::system_clock::now(); + + // 3DS system does't allow user to set a time before Jan 1 2000, + // so we use it as an auxiliary epoch to calculate the console time. + std::tm epoch_tm; + epoch_tm.tm_sec = 0; + epoch_tm.tm_min = 0; + epoch_tm.tm_hour = 0; + epoch_tm.tm_mday = 1; + epoch_tm.tm_mon = 0; + epoch_tm.tm_year = 100; + epoch_tm.tm_isdst = 0; + auto epoch = std::chrono::system_clock::from_time_t(std::mktime(&epoch_tm)); + + // 3DS console time uses Jan 1 1900 as internal epoch, + // so we use the milliseconds between 1900 and 2000 as base console time + u64 console_time = 3155673600000ULL; + + // Only when system time is after 2000, we set it as 3DS system time + if (now > epoch) { + console_time += std::chrono::duration_cast<std::chrono::milliseconds>(now - epoch).count(); + } + + // If the system time is in daylight saving, we give an additional hour to console time + std::time_t now_time_t = std::chrono::system_clock::to_time_t(now); + std::tm* now_tm = std::localtime(&now_time_t); + if (now_tm && now_tm->tm_isdst > 0) + console_time += 60 * 60 * 1000; + + return console_time; +} + +static void UpdateTimeCallback(u64 userdata, int cycles_late) { + DateTime& date_time = shared_page.date_time_counter % 2 ? + shared_page.date_time_0 : shared_page.date_time_1; + + date_time.date_time = GetSystemTime(); + date_time.update_tick = CoreTiming::GetTicks(); + date_time.tick_to_second_coefficient = g_clock_rate_arm11; + date_time.tick_offset = 0; + + ++shared_page.date_time_counter; + + // system time is updated hourly + CoreTiming::ScheduleEvent(msToCycles(60 * 60 * 1000) - cycles_late, update_time_event); +} + void Init() { std::memset(&shared_page, 0, sizeof(shared_page)); @@ -19,6 +73,9 @@ void Init() { // Some games wait until this value becomes 0x1, before asking running_hw shared_page.unknown_value = 0x1; + + update_time_event = CoreTiming::RegisterEvent("SharedPage::UpdateTimeCallback", UpdateTimeCallback); + CoreTiming::ScheduleEvent(0, update_time_event); } } // namespace diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h index 35a07c685..cd9246726 100644 --- a/src/core/hle/shared_page.h +++ b/src/core/hle/shared_page.h @@ -25,13 +25,14 @@ namespace SharedPage { struct DateTime { u64_le date_time; // 0 u64_le update_tick; // 8 - INSERT_PADDING_BYTES(0x20 - 0x10); // 10 + u64_le tick_to_second_coefficient; // 10 + u64_le tick_offset; // 18 }; static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong"); struct SharedPageDef { // Most of these names are taken from the 3dbrew page linked above. - u32_le date_time_selector; // 0 + u32_le date_time_counter; // 0 u8 running_hw; // 4 /// "Microcontroller hardware info" u8 mcu_hw_info; // 5 diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 0ce72de87..5d71d5619 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -14,12 +14,14 @@ #include "core/arm/arm_interface.h" #include "core/hle/kernel/address_arbiter.h" +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/semaphore.h" +#include "core/hle/kernel/server_port.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" @@ -834,6 +836,23 @@ 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) { + // TODO(Subv): Implement named ports. + ASSERT_MSG(name == nullptr, "Named ports are currently unimplemented"); + + using Kernel::ServerPort; + using Kernel::ClientPort; + using Kernel::SharedPtr; + + auto ports = ServerPort::CreatePortPair(max_sessions); + CASCADE_RESULT(*client_port, Kernel::g_handle_table.Create(std::move(std::get<SharedPtr<ClientPort>>(ports)))); + // Note: The 3DS kernel also leaks the client port handle if the server port handle fails to be created. + CASCADE_RESULT(*server_port, Kernel::g_handle_table.Create(std::move(std::get<SharedPtr<ServerPort>>(ports)))); + + LOG_TRACE(Kernel_SVC, "called max_sessions=%u", max_sessions); + return RESULT_SUCCESS; +} + static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { using Kernel::MemoryRegion; @@ -1011,7 +1030,7 @@ static const FunctionDef SVC_Table[] = { {0x44, nullptr, "Unknown"}, {0x45, nullptr, "Unknown"}, {0x46, nullptr, "Unknown"}, - {0x47, nullptr, "CreatePort"}, + {0x47, HLE::Wrap<CreatePort>, "CreatePort"}, {0x48, nullptr, "CreateSessionToPort"}, {0x49, nullptr, "CreateSession"}, {0x4A, nullptr, "AcceptSession"}, diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 8c9e5d46d..9aa8c4e5a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -280,6 +280,20 @@ u8* GetPointer(const VAddr vaddr) { return nullptr; } +std::string ReadCString(VAddr vaddr, std::size_t max_length) { + std::string string; + string.reserve(max_length); + for (std::size_t i = 0; i < max_length; ++i) { + char c = Read8(vaddr); + if (c == '\0') + break; + string.push_back(c); + ++vaddr; + } + string.shrink_to_fit(); + return string; +} + u8* GetPhysicalPointer(PAddr address) { // TODO(Subv): This call should not go through the application's memory mapping. return GetPointer(PhysicalToVirtualAddress(address)); diff --git a/src/core/memory.h b/src/core/memory.h index ae5588dee..cad845385 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -5,6 +5,7 @@ #pragma once #include <cstddef> +#include <string> #include "common/common_types.h" @@ -130,6 +131,8 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); u8* GetPointer(VAddr virtual_address); +std::string ReadCString(VAddr virtual_address, std::size_t max_length); + /** * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical * address. This should be used by services to translate addresses for use by the hardware. diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 77261eafe..1b6733a79 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -24,6 +24,7 @@ void Apply() { VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; AudioCore::SelectSink(values.sink_id); + AudioCore::EnableStretching(values.enable_audio_stretching); } diff --git a/src/core/settings.h b/src/core/settings.h index f95e62390..fb3fbe391 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -72,6 +72,7 @@ struct Values { bool use_hw_renderer; bool use_shader_jit; bool use_scaled_resolution; + bool use_vsync; float bg_red; float bg_green; @@ -81,6 +82,7 @@ struct Values { // Audio std::string sink_id; + bool enable_audio_stretching; // Debugging bool use_gdbstub; diff --git a/src/core/system.cpp b/src/core/system.cpp index 4a4757af3..4fc266cb0 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -17,6 +17,8 @@ namespace System { +static bool is_powered_on{ false }; + Result Init(EmuWindow* emu_window) { Core::Init(); CoreTiming::Init(); @@ -30,9 +32,15 @@ Result Init(EmuWindow* emu_window) { AudioCore::Init(); GDBStub::Init(); + is_powered_on = true; + return Result::Success; } +bool IsPoweredOn() { + return is_powered_on; +} + void Shutdown() { GDBStub::Shutdown(); AudioCore::Shutdown(); @@ -42,6 +50,8 @@ void Shutdown() { HW::Shutdown(); CoreTiming::Shutdown(); Core::Shutdown(); + + is_powered_on = false; } } // namespace diff --git a/src/core/system.h b/src/core/system.h index a4a627ea9..fb0ca4e1b 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -16,6 +16,7 @@ enum class Result { }; Result Init(EmuWindow* emu_window); +bool IsPoweredOn(); void Shutdown(); } diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 19e03adf4..689859049 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -423,6 +423,20 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { break; } + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[0], 0xe8): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[1], 0xe9): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[2], 0xea): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[3], 0xeb): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[4], 0xec): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[5], 0xed): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[6], 0xee): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[7], 0xef): + { + g_state.fog.lut[regs.fog_lut_offset % 128].raw = value; + regs.fog_lut_offset.Assign(regs.fog_lut_offset + 1); + break; + } + default: break; } diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 871368323..bfa686380 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -328,7 +328,7 @@ std::unique_ptr<PicaTrace> FinishPicaTracing() std::lock_guard<std::mutex> lock(pica_trace_mutex); std::unique_ptr<PicaTrace> ret(std::move(pica_trace)); - return std::move(ret); + return ret; } const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info, bool disable_alpha) { diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 544ea037f..7099c31a0 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -115,7 +115,28 @@ struct Regs { BitField<24, 5, Semantic> map_w; } vs_output_attributes[7]; - INSERT_PADDING_WORDS(0x11); + INSERT_PADDING_WORDS(0xe); + + enum class ScissorMode : u32 { + Disabled = 0, + Exclude = 1, // Exclude pixels inside the scissor box + + Include = 3 // Exclude pixels outside the scissor box + }; + + struct { + BitField<0, 2, ScissorMode> mode; + + union { + BitField< 0, 16, u32> x1; + BitField<16, 16, u32> y1; + }; + + union { + BitField< 0, 16, u32> x2; + BitField<16, 16, u32> y2; + }; + } scissor_test; union { BitField< 0, 10, s32> x; @@ -401,22 +422,47 @@ struct Regs { TevStageConfig tev_stage3; INSERT_PADDING_WORDS(0x3); + enum class FogMode : u32 { + None = 0, + Fog = 5, + Gas = 7, + }; + union { - // Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in - // these masks are set - BitField< 8, 4, u32> update_mask_rgb; - BitField<12, 4, u32> update_mask_a; + BitField<0, 3, FogMode> fog_mode; + BitField<16, 1, u32> fog_flip; - bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { - return (stage_index < 4) && (update_mask_rgb & (1 << stage_index)); - } + union { + // Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in + // these masks are set + BitField< 8, 4, u32> update_mask_rgb; + BitField<12, 4, u32> update_mask_a; - bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { - return (stage_index < 4) && (update_mask_a & (1 << stage_index)); - } - } tev_combiner_buffer_input; + bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { + return (stage_index < 4) && (update_mask_rgb & (1 << stage_index)); + } + + bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { + return (stage_index < 4) && (update_mask_a & (1 << stage_index)); + } + } tev_combiner_buffer_input; + }; + + union { + u32 raw; + BitField< 0, 8, u32> r; + BitField< 8, 8, u32> g; + BitField<16, 8, u32> b; + } fog_color; + + INSERT_PADDING_WORDS(0x4); + + BitField<0, 16, u32> fog_lut_offset; + + INSERT_PADDING_WORDS(0x1); + + u32 fog_lut_data[8]; - INSERT_PADDING_WORDS(0xf); TevStageConfig tev_stage4; INSERT_PADDING_WORDS(0x3); TevStageConfig tev_stage5; @@ -1303,6 +1349,7 @@ ASSERT_REG_POSITION(viewport_depth_range, 0x4d); ASSERT_REG_POSITION(viewport_depth_near_plane, 0x4e); ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); +ASSERT_REG_POSITION(scissor_test, 0x65); ASSERT_REG_POSITION(viewport_corner, 0x68); ASSERT_REG_POSITION(depthmap_enable, 0x6D); ASSERT_REG_POSITION(texture0_enable, 0x80); @@ -1318,6 +1365,10 @@ ASSERT_REG_POSITION(tev_stage1, 0xc8); ASSERT_REG_POSITION(tev_stage2, 0xd0); ASSERT_REG_POSITION(tev_stage3, 0xd8); ASSERT_REG_POSITION(tev_combiner_buffer_input, 0xe0); +ASSERT_REG_POSITION(fog_mode, 0xe0); +ASSERT_REG_POSITION(fog_color, 0xe1); +ASSERT_REG_POSITION(fog_lut_offset, 0xe6); +ASSERT_REG_POSITION(fog_lut_data, 0xe8); ASSERT_REG_POSITION(tev_stage4, 0xf0); ASSERT_REG_POSITION(tev_stage5, 0xf8); ASSERT_REG_POSITION(tev_combiner_buffer_color, 0xfd); diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h index 495174c25..01f4285a8 100644 --- a/src/video_core/pica_state.h +++ b/src/video_core/pica_state.h @@ -33,10 +33,10 @@ struct State { u32 raw; // LUT value, encoded as 12-bit fixed point, with 12 fraction bits - BitField< 0, 12, u32> value; + BitField< 0, 12, u32> value; // 0.0.12 fixed point // Used by HW for efficient interpolation, Citra does not use these - BitField<12, 12, u32> difference; + BitField<12, 12, s32> difference; // 1.0.11 fixed point float ToFloat() { return static_cast<float>(value) / 4095.f; @@ -46,6 +46,18 @@ struct State { std::array<std::array<LutEntry, 256>, 24> luts; } lighting; + struct { + union LutEntry { + // Used for raw access + u32 raw; + + BitField< 0, 13, s32> difference; // 1.1.11 fixed point + BitField<13, 11, u32> value; // 0.0.11 fixed point + }; + + std::array<LutEntry, 128> lut; + } fog; + /// Current Pica command list struct { const u32* head_ptr; diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 65168f05a..6f369a00e 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -338,12 +338,26 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, return; } - // TODO: Proper scissor rect test! u16 min_x = std::min({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x}); u16 min_y = std::min({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y}); u16 max_x = std::max({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x}); u16 max_y = std::max({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y}); + // Convert the scissor box coordinates to 12.4 fixed point + u16 scissor_x1 = (u16)( regs.scissor_test.x1 << 4); + u16 scissor_y1 = (u16)( regs.scissor_test.y1 << 4); + // x2,y2 have +1 added to cover the entire sub-pixel area + u16 scissor_x2 = (u16)((regs.scissor_test.x2 + 1) << 4); + u16 scissor_y2 = (u16)((regs.scissor_test.y2 + 1) << 4); + + if (regs.scissor_test.mode == Regs::ScissorMode::Include) { + // Calculate the new bounds + min_x = std::max(min_x, scissor_x1); + min_y = std::max(min_y, scissor_y1); + max_x = std::min(max_x, scissor_x2); + max_y = std::min(max_y, scissor_y2); + } + min_x &= Fix12P4::IntMask(); min_y &= Fix12P4::IntMask(); max_x = ((max_x + Fix12P4::FracMask()) & Fix12P4::IntMask()); @@ -383,6 +397,13 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, for (u16 y = min_y + 8; y < max_y; y += 0x10) { for (u16 x = min_x + 8; x < max_x; x += 0x10) { + // Do not process the pixel if it's inside the scissor box and the scissor mode is set to Exclude + if (regs.scissor_test.mode == Regs::ScissorMode::Exclude) { + if (x >= scissor_x1 && x < scissor_x2 && + y >= scissor_y1 && y < scissor_y2) + continue; + } + // Calculate the barycentric coordinates w0, w1 and w2 int w0 = bias0 + SignedArea(vtxpos[1].xy(), vtxpos[2].xy(), {x, y}); int w1 = bias1 + SignedArea(vtxpos[2].xy(), vtxpos[0].xy(), {x, y}); @@ -398,6 +419,26 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, float24::FromFloat32(static_cast<float>(w2))); float24 interpolated_w_inverse = float24::FromFloat32(1.0f) / Math::Dot(w_inverse, baricentric_coordinates); + // interpolated_z = z / w + float interpolated_z_over_w = (v0.screenpos[2].ToFloat32() * w0 + + v1.screenpos[2].ToFloat32() * w1 + + v2.screenpos[2].ToFloat32() * w2) / wsum; + + // Not fully accurate. About 3 bits in precision are missing. + // Z-Buffer (z / w * scale + offset) + float depth_scale = float24::FromRaw(regs.viewport_depth_range).ToFloat32(); + float depth_offset = float24::FromRaw(regs.viewport_depth_near_plane).ToFloat32(); + float depth = interpolated_z_over_w * depth_scale + depth_offset; + + // Potentially switch to W-Buffer + if (regs.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) { + // W-Buffer (z * scale + w * offset = (z / w * scale + offset) * w) + depth *= interpolated_w_inverse.ToFloat32() * wsum; + } + + // Clamp the result + depth = MathUtil::Clamp(depth, 0.0f, 1.0f); + // Perspective correct attribute interpolation: // Attribute values cannot be calculated by simple linear interpolation since // they are not linear in screen space. For example, when interpolating a @@ -833,6 +874,38 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, continue; } + // Apply fog combiner + // Not fully accurate. We'd have to know what data type is used to + // store the depth etc. Using float for now until we know more + // about Pica datatypes + if (regs.fog_mode == Regs::FogMode::Fog) { + const Math::Vec3<u8> fog_color = { + static_cast<u8>(regs.fog_color.r.Value()), + static_cast<u8>(regs.fog_color.g.Value()), + static_cast<u8>(regs.fog_color.b.Value()), + }; + + // Get index into fog LUT + float fog_index; + if (g_state.regs.fog_flip) { + fog_index = (1.0f - depth) * 128.0f; + } else { + fog_index = depth * 128.0f; + } + + // Generate clamped fog factor from LUT for given fog index + float fog_i = MathUtil::Clamp(floorf(fog_index), 0.0f, 127.0f); + float fog_f = fog_index - fog_i; + const auto& fog_lut_entry = g_state.fog.lut[static_cast<unsigned int>(fog_i)]; + float fog_factor = (fog_lut_entry.value + fog_lut_entry.difference * fog_f) / 2047.0f; // This is signed fixed point 1.11 + fog_factor = MathUtil::Clamp(fog_factor, 0.0f, 1.0f); + + // Blend the fog + for (unsigned i = 0; i < 3; i++) { + combiner_output[i] = fog_factor * combiner_output[i] + (1.0f - fog_factor) * fog_color[i]; + } + } + u8 old_stencil = 0; auto UpdateStencil = [stencil_test, x, y, &old_stencil](Pica::Regs::StencilAction action) { @@ -887,27 +960,6 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, } } - // interpolated_z = z / w - float interpolated_z_over_w = (v0.screenpos[2].ToFloat32() * w0 + - v1.screenpos[2].ToFloat32() * w1 + - v2.screenpos[2].ToFloat32() * w2) / wsum; - - // Not fully accurate. About 3 bits in precision are missing. - // Z-Buffer (z / w * scale + offset) - float depth_scale = float24::FromRaw(regs.viewport_depth_range).ToFloat32(); - float depth_offset = float24::FromRaw(regs.viewport_depth_near_plane).ToFloat32(); - float depth = interpolated_z_over_w * depth_scale + depth_offset; - - // Potentially switch to W-Buffer - if (regs.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) { - - // W-Buffer (z * scale + w * offset = (z / w * scale + offset) * w) - depth *= interpolated_w_inverse.ToFloat32() * wsum; - } - - // Clamp the result - depth = MathUtil::Clamp(depth, 0.0f, 1.0f); - // Convert float to integer unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format); u32 z = (u32)(depth * ((1 << num_bits) - 1)); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 931c34a37..f8393c618 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -62,6 +62,8 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { uniform_block_data.lut_dirty[index] = true; } + uniform_block_data.fog_lut_dirty = true; + // Set vertex attributes glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION); @@ -102,6 +104,18 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } + // Setup the LUT for the fog + { + fog_lut.Create(); + state.fog_lut.texture_1d = fog_lut.handle; + } + state.Apply(); + + glActiveTexture(GL_TEXTURE9); + glTexImage1D(GL_TEXTURE_1D, 0, GL_R32UI, 128, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + // Sync fixed function OpenGL state SyncCullMode(); SyncBlendEnabled(); @@ -182,6 +196,14 @@ void RasterizerOpenGL::DrawTriangles() { (GLint)(rect.bottom + regs.viewport_corner.y * color_surface->res_scale_height), (GLsizei)(viewport_width * color_surface->res_scale_width), (GLsizei)(viewport_height * color_surface->res_scale_height)); + if (uniform_block_data.data.framebuffer_scale[0] != color_surface->res_scale_width || + uniform_block_data.data.framebuffer_scale[1] != color_surface->res_scale_height) { + + uniform_block_data.data.framebuffer_scale[0] = color_surface->res_scale_width; + uniform_block_data.data.framebuffer_scale[1] = color_surface->res_scale_height; + uniform_block_data.dirty = true; + } + // Sync and bind the texture surfaces const auto pica_textures = regs.GetTextures(); for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { @@ -215,6 +237,12 @@ void RasterizerOpenGL::DrawTriangles() { } } + // Sync the fog lut + if (uniform_block_data.fog_lut_dirty) { + SyncFogLUT(); + uniform_block_data.fog_lut_dirty = false; + } + // Sync the uniform data if (uniform_block_data.dirty) { glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, GL_STATIC_DRAW); @@ -280,6 +308,21 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { SyncBlendColor(); break; + // Fog state + case PICA_REG_INDEX(fog_color): + SyncFogColor(); + break; + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[0], 0xe8): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[1], 0xe9): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[2], 0xea): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[3], 0xeb): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[4], 0xec): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[5], 0xed): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[6], 0xee): + case PICA_REG_INDEX_WORKAROUND(fog_lut_data[7], 0xef): + uniform_block_data.fog_lut_dirty = true; + break; + // Alpha test case PICA_REG_INDEX(output_merger.alpha_test): SyncAlphaTest(); @@ -318,6 +361,15 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { SyncColorWriteMask(); break; + // Scissor test + case PICA_REG_INDEX(scissor_test.mode): + shader_dirty = true; + break; + case PICA_REG_INDEX(scissor_test.x1): // and y1 + case PICA_REG_INDEX(scissor_test.x2): // and y2 + SyncScissorTest(); + break; + // Logic op case PICA_REG_INDEX(output_merger.logic_op): SyncLogicOp(); @@ -329,6 +381,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { break; // TEV stages + // (This also syncs fog_mode and fog_flip which are part of tev_combiner_buffer_input) case PICA_REG_INDEX(tev_stage0.color_source1): case PICA_REG_INDEX(tev_stage0.color_modifier1): case PICA_REG_INDEX(tev_stage0.color_op): @@ -950,9 +1003,15 @@ void RasterizerOpenGL::SetShader() { uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[5]"); if (uniform_lut != -1) { glUniform1i(uniform_lut, 8); } + GLuint uniform_fog_lut = glGetUniformLocation(shader->shader.handle, "fog_lut"); + if (uniform_fog_lut != -1) { glUniform1i(uniform_fog_lut, 9); } + current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); unsigned int block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data"); + GLint block_size; + glGetActiveUniformBlockiv(current_shader->shader.handle, block_index, GL_UNIFORM_BLOCK_DATA_SIZE, &block_size); + ASSERT_MSG(block_size == sizeof(UniformData), "Uniform block size did not match!"); glUniformBlockBinding(current_shader->shader.handle, block_index, 0); // Update uniforms @@ -960,6 +1019,7 @@ void RasterizerOpenGL::SetShader() { SyncDepthOffset(); SyncAlphaTest(); SyncCombinerColor(); + SyncScissorTest(); auto& tev_stages = Pica::g_state.regs.GetTevStages(); for (int index = 0; index < tev_stages.size(); ++index) SyncTevConstColor(index, tev_stages[index]); @@ -974,6 +1034,8 @@ void RasterizerOpenGL::SetShader() { SyncLightDistanceAttenuationBias(light_index); SyncLightDistanceAttenuationScale(light_index); } + + SyncFogColor(); } } @@ -1040,6 +1102,30 @@ void RasterizerOpenGL::SyncBlendColor() { state.blend.color.alpha = blend_color[3]; } +void RasterizerOpenGL::SyncFogColor() { + const auto& regs = Pica::g_state.regs; + uniform_block_data.data.fog_color = { + regs.fog_color.r.Value() / 255.0f, + regs.fog_color.g.Value() / 255.0f, + regs.fog_color.b.Value() / 255.0f + }; + uniform_block_data.dirty = true; +} + +void RasterizerOpenGL::SyncFogLUT() { + std::array<GLuint, 128> new_data; + + std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(), [](const auto& entry) { + return entry.raw; + }); + + if (new_data != fog_lut_data) { + fog_lut_data = new_data; + glActiveTexture(GL_TEXTURE9); + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 128, GL_RED_INTEGER, GL_UNSIGNED_INT, fog_lut_data.data()); + } +} + void RasterizerOpenGL::SyncAlphaTest() { const auto& regs = Pica::g_state.regs; if (regs.output_merger.alpha_test.ref != uniform_block_data.data.alphatest_ref) { @@ -1098,6 +1184,22 @@ void RasterizerOpenGL::SyncDepthTest() { PicaToGL::CompareFunc(regs.output_merger.depth_test_func) : GL_ALWAYS; } +void RasterizerOpenGL::SyncScissorTest() { + const auto& regs = Pica::g_state.regs; + + if (uniform_block_data.data.scissor_x1 != regs.scissor_test.x1 || + uniform_block_data.data.scissor_y1 != regs.scissor_test.y1 || + uniform_block_data.data.scissor_x2 != regs.scissor_test.x2 || + uniform_block_data.data.scissor_y2 != regs.scissor_test.y2) { + + uniform_block_data.data.scissor_x1 = regs.scissor_test.x1; + uniform_block_data.data.scissor_y1 = regs.scissor_test.y1; + uniform_block_data.data.scissor_x2 = regs.scissor_test.x2; + uniform_block_data.data.scissor_y2 = regs.scissor_test.y2; + uniform_block_data.dirty = true; + } +} + void RasterizerOpenGL::SyncCombinerColor() { auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index bb7f20161..c5029432b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -56,6 +56,8 @@ union PicaShaderConfig { const auto& regs = Pica::g_state.regs; + state.scissor_test_mode = regs.scissor_test.mode; + state.depthmap_enable = regs.depthmap_enable; state.alpha_test_func = regs.output_merger.alpha_test.enable ? @@ -76,6 +78,9 @@ union PicaShaderConfig { state.tev_stages[i].scales_raw = tev_stage.scales_raw; } + state.fog_mode = regs.fog_mode; + state.fog_flip = regs.fog_flip; + state.combiner_buffer_input = regs.tev_combiner_buffer_input.update_mask_rgb.Value() | regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; @@ -168,13 +173,15 @@ union PicaShaderConfig { }; struct State { - Pica::Regs::CompareFunc alpha_test_func; + Pica::Regs::ScissorMode scissor_test_mode; Pica::Regs::TextureConfig::TextureType texture0_type; std::array<TevStageConfigRaw, 6> tev_stages; u8 combiner_buffer_input; Pica::Regs::DepthBuffering depthmap_enable; + Pica::Regs::FogMode fog_mode; + bool fog_flip; struct { struct { @@ -316,19 +323,27 @@ private: GLfloat dist_atten_scale; }; - /// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned + /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned + // NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at + // the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. + // Not following that rule will cause problems on some AMD drivers. struct UniformData { - // A vec4 color for each of the six tev stages - GLvec4 const_color[6]; - GLvec4 tev_combiner_buffer_color; + alignas(8) GLvec2 framebuffer_scale; GLint alphatest_ref; GLfloat depth_scale; GLfloat depth_offset; + GLint scissor_x1; + GLint scissor_y1; + GLint scissor_x2; + GLint scissor_y2; + alignas(16) GLvec3 fog_color; alignas(16) GLvec3 lighting_global_ambient; LightSrc light_src[8]; + alignas(16) GLvec4 const_color[6]; // A vec4 color for each of the six tev stages + alignas(16) GLvec4 tev_combiner_buffer_color; }; - static_assert(sizeof(UniformData) == 0x390, "The size of the UniformData structure has changed, update the structure in the shader"); + static_assert(sizeof(UniformData) == 0x3C0, "The size of the UniformData structure has changed, update the structure in the shader"); static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec"); /// Sets the OpenGL shader in accordance with the current PICA register state @@ -352,6 +367,10 @@ private: /// Syncs the blend color to match the PICA register void SyncBlendColor(); + /// Syncs the fog states to match the PICA register + void SyncFogColor(); + void SyncFogLUT(); + /// Syncs the alpha test states to match the PICA register void SyncAlphaTest(); @@ -373,6 +392,9 @@ private: /// Syncs the depth test states to match the PICA register void SyncDepthTest(); + /// Syncs the scissor test state to match the PICA register + void SyncScissorTest(); + /// Syncs the TEV combiner color buffer to match the PICA register void SyncCombinerColor(); @@ -419,6 +441,7 @@ private: struct { UniformData data; bool lut_dirty[6]; + bool fog_lut_dirty; bool dirty; } uniform_block_data = {}; @@ -430,4 +453,7 @@ private: std::array<OGLTexture, 6> lighting_luts; std::array<std::array<GLvec4, 256>, 6> lighting_lut_data{}; + + OGLTexture fog_lut; + std::array<GLuint, 128> fog_lut_data{}; }; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 8332e722d..3de372f67 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -400,6 +400,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { default: LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); UNIMPLEMENTED(); + index = "0.0"; break; } @@ -539,6 +540,8 @@ in float texcoord0_w; in vec4 normquat; in vec3 view; +in vec4 gl_FragCoord; + out vec4 color; struct LightSrc { @@ -552,17 +555,24 @@ struct LightSrc { }; layout (std140) uniform shader_data { - vec4 const_color[NUM_TEV_STAGES]; - vec4 tev_combiner_buffer_color; + vec2 framebuffer_scale; int alphatest_ref; float depth_scale; float depth_offset; + int scissor_x1; + int scissor_y1; + int scissor_x2; + int scissor_y2; + vec3 fog_color; vec3 lighting_global_ambient; LightSrc light_src[NUM_LIGHTS]; + vec4 const_color[NUM_TEV_STAGES]; + vec4 tev_combiner_buffer_color; }; uniform sampler2D tex[3]; uniform sampler1D lut[6]; +uniform usampler1D fog_lut; // Rotate the vector v by the quaternion q vec3 quaternion_rotate(vec4 q, vec3 v) { @@ -580,6 +590,25 @@ vec4 secondary_fragment_color = vec4(0.0); return out; } + // Append the scissor test + if (state.scissor_test_mode != Regs::ScissorMode::Disabled) { + out += "if ("; + // Negate the condition if we have to keep only the pixels outside the scissor box + if (state.scissor_test_mode == Regs::ScissorMode::Include) + out += "!"; + // x2,y2 have +1 added to cover the entire pixel area + out += "(gl_FragCoord.x >= scissor_x1 * framebuffer_scale.x && " + "gl_FragCoord.y >= scissor_y1 * framebuffer_scale.y && " + "gl_FragCoord.x < (scissor_x2 + 1) * framebuffer_scale.x && " + "gl_FragCoord.y < (scissor_y2 + 1) * framebuffer_scale.y)) discard;\n"; + } + + out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n"; + out += "float depth = z_over_w * depth_scale + depth_offset;\n"; + if (state.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) { + out += "depth /= gl_FragCoord.w;\n"; + } + if (state.lighting.enable) WriteLighting(out, config); @@ -596,14 +625,30 @@ vec4 secondary_fragment_color = vec4(0.0); out += ") discard;\n"; } - out += "color = last_tex_env_out;\n"; + // Append fog combiner + if (state.fog_mode == Regs::FogMode::Fog) { + // Get index into fog LUT + if (state.fog_flip) { + out += "float fog_index = (1.0 - depth) * 128.0;\n"; + } else { + out += "float fog_index = depth * 128.0;\n"; + } - out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n"; - out += "float depth = z_over_w * depth_scale + depth_offset;\n"; - if (state.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) { - out += "depth /= gl_FragCoord.w;\n"; + // Generate clamped fog factor from LUT for given fog index + out += "float fog_i = clamp(floor(fog_index), 0.0, 127.0);\n"; + out += "float fog_f = fog_index - fog_i;\n"; + out += "uint fog_lut_entry = texelFetch(fog_lut, int(fog_i), 0).r;\n"; + out += "float fog_lut_entry_difference = float(int((fog_lut_entry & 0x1FFFU) << 19U) >> 19);\n"; // Extract signed difference + out += "float fog_lut_entry_value = float((fog_lut_entry >> 13U) & 0x7FFU);\n"; + out += "float fog_factor = (fog_lut_entry_value + fog_lut_entry_difference * fog_f) / 2047.0;\n"; + out += "fog_factor = clamp(fog_factor, 0.0, 1.0);\n"; + + // Blend the fog + out += "last_tex_env_out.rgb = mix(fog_color.rgb, last_tex_env_out.rgb, fog_factor);\n"; } + out += "gl_FragDepth = depth;\n"; + out += "color = last_tex_env_out;\n"; out += "}"; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index fa141fc9a..13ee986b9 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -58,6 +58,8 @@ OpenGLState::OpenGLState() { lut.texture_1d = 0; } + fog_lut.texture_1d = 0; + draw.read_framebuffer = 0; draw.draw_framebuffer = 0; draw.vertex_array = 0; @@ -195,6 +197,12 @@ void OpenGLState::Apply() const { } } + // Fog LUT + if (fog_lut.texture_1d != cur_state.fog_lut.texture_1d) { + glActiveTexture(GL_TEXTURE9); + glBindTexture(GL_TEXTURE_1D, fog_lut.texture_1d); + } + // Framebuffer if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 228727054..13c71b0a6 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -67,6 +67,10 @@ public: GLuint texture_1d; // GL_TEXTURE_BINDING_1D } lighting_luts[6]; + struct { + GLuint texture_1d; // GL_TEXTURE_BINDING_1D + } fog_lut; + struct { GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 6dc2758c5..d9b9c9cc2 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -17,6 +17,7 @@ #include "video_core/pica.h" +using GLvec2 = std::array<GLfloat, 2>; using GLvec3 = std::array<GLfloat, 3>; using GLvec4 = std::array<GLfloat, 4>; diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index c9975876d..bd6e5eb6b 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -22,6 +22,7 @@ std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin std::atomic<bool> g_hw_renderer_enabled; std::atomic<bool> g_shader_jit_enabled; std::atomic<bool> g_scaled_resolution_enabled; +std::atomic<bool> g_vsync_enabled; /// Initialize the video core bool Init(EmuWindow* emu_window) {