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(&param_1, &param_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(&section_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(&section_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) {